/* ============================================================
   CCTV Monitoring — antarmuka ruang kontrol, mobile-first.

   Arah desain
   -----------
   Paletnya diambil dari isi frame kamera itu sendiri: langit malam yang
   biru-kehitaman (bukan hitam netral), kolam cahaya lampu sodium yang
   hangat, beton yang tersorot, dan pantulan IR yang kebiruan. Tujuannya
   antarmuka terasa menyambung dengan gambar di dalamnya, bukan dashboard
   umum yang dibungkus di sekelilingnya.

   Aturan warna yang dipegang di seluruh berkas ini:
   keadaan NORMAL adalah yang paling sunyi. Warna hanya dibelanjakan untuk
   hal yang butuh tindakan. Kalau "terhubung" ikut berteriak, satu kanal
   yang mati jadi susah ditemukan — padahal justru itu pekerjaannya.

   Tipografi
   ---------
   Atkinson Hyperlegible untuk teks: dirancang Braille Institute agar tiap
   karakter sesulit mungkin tertukar. Antarmuka ini dibaca sekilas, di
   layar kecil, dalam gelap — keterbacaan memang briefnya.
   IBM Plex Mono untuk data: timecode, nomor kanal, IP, status. Angkanya
   tabular supaya jam tidak bergoyang tiap detik.
   ============================================================ */

/* ---------- font lokal (tanpa CDN; lihat fonts/LICENSE.txt) ---------- */
@font-face {
  font-family: 'Atkinson Hyperlegible';
  font-style: normal; font-weight: 400; font-display: swap;
  src: url('fonts/atkinson-400.woff2') format('woff2');
}
@font-face {
  font-family: 'Atkinson Hyperlegible';
  font-style: normal; font-weight: 700; font-display: swap;
  src: url('fonts/atkinson-700.woff2') format('woff2');
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal; font-weight: 400; font-display: swap;
  src: url('fonts/plexmono-400.woff2') format('woff2');
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal; font-weight: 600; font-display: swap;
  src: url('fonts/plexmono-600.woff2') format('woff2');
}

/* ---------- token ---------- */
:root {
  color-scheme: dark;

  --ink:      #0B1016;   /* langit malam */
  --plate:    #131A23;   /* panel */
  --plate-2:  #1A2430;   /* panel terangkat */
  --edge:     #24303D;   /* garis rambut */
  --edge-lit: #35485C;   /* garis rambut saat hover/fokus */

  --vapor:    #CDD9E5;   /* teks utama */
  --haze:     #8093A5;   /* teks sekunder */

  --halo:     #F2A93B;   /* lampu sodium: aksi utama & perlu perhatian */
  --halo-ink: #241703;
  --beam:     #5EC8F2;   /* pantulan IR: fokus & tautan */
  --ok:       #58C69A;   /* hanya sebagai titik/bilah kecil, tak pernah blok */
  --tally:    #FF4438;   /* lampu REC: hanya untuk yang gagal */

  --scrim:    rgba(4, 8, 12, .72);
  --lift:     0 10px 30px rgba(0, 0, 0, .45);

  --r:    12px;
  --r-sm: 8px;
  --tap:  44px;
  --grid-gap: 10px;   /* dipakai juga di calc tinggi .live-row — jangan dipisah */

  --sans: 'Atkinson Hyperlegible', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --mono: 'IBM Plex Mono', ui-monospace, 'Cascadia Mono', Consolas, monospace;

  /* Menempel di ATAS gambar kamera, jadi tetap sama di kedua tema. */
  --osd-inkwell: rgba(6, 10, 14, .62);
  --osd-text:    rgba(255, 255, 255, .92);
}

/* Mode terang: siang hari — beton dan langit pucat. Apa pun yang menempel
   di atas video tetap gelap, karena itu benar di kedua tema. */
:root[data-theme="light"] {
  color-scheme: light;
  --ink:      #EDF1F6;
  --plate:    #FFFFFF;
  --plate-2:  #F4F7FB;
  --edge:     #D7E0EA;
  --edge-lit: #A9BACC;
  --vapor:    #16202B;
  --haze:     #566878;
  --halo:     #9A5D06;
  --halo-ink: #FFFFFF;
  --beam:     #0A6699;
  --ok:       #0B7A55;
  --tally:    #C42B20;
  --scrim:    rgba(16, 26, 36, .45);
  --lift:     0 10px 30px rgba(20, 32, 45, .14);
}

/* ---------- dasar ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
html, body { margin: 0; min-height: 100%; }
body {
  background: var(--ink);
  color: var(--vapor);
  font: 400 1rem/1.6 var(--sans);
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3 { margin: 0; line-height: 1.25; font-weight: 700; letter-spacing: -.01em; }
h1 { font-size: 1.25rem; }
h2 { font-size: 1.0625rem; }
a { color: var(--beam); text-underline-offset: 2px; }
:focus-visible { outline: 2px solid var(--beam); outline-offset: 2px; border-radius: 4px; }

.hidden { display: none !important; }
.mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.muted { color: var(--haze); }

/* Label seksi bergaya pelat OSD kamera. */
.eyebrow {
  font-family: var(--mono); font-size: .6875rem; font-weight: 600;
  letter-spacing: .16em; text-transform: uppercase; color: var(--haze);
}
.eyebrow b { color: var(--vapor); font-weight: 600; }

/* Tanda tally: kotak merah kecil ala lampu REC, dipakai sebagai logo. */
.tally-mark {
  width: 9px; height: 9px; border-radius: 2px; flex-shrink: 0;
  background: var(--tally);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--tally) 18%, transparent);
}

/* ---------- layar masuk ---------- */
#login-view {
  display: flex; align-items: center; justify-content: center;
  min-height: 100vh; min-height: 100dvh; padding: 20px;
}
.login-card {
  width: 100%; max-width: 380px;
  background: var(--plate); border: 1px solid var(--edge); border-radius: var(--r);
  padding: 32px 24px calc(24px + env(safe-area-inset-bottom));
  display: flex; flex-direction: column; gap: 16px;
}
.login-brand {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  font-family: var(--mono); font-size: 1.0625rem; font-weight: 600;
  letter-spacing: .2em; text-transform: uppercase;
}
.login-sub { margin: -8px 0 4px; text-align: center; color: var(--haze); font-size: .875rem; }

/* ---------- kerangka ---------- */
#app-view { display: flex; flex-direction: column; min-height: 100vh; min-height: 100dvh; }

/* Sengaja TANPA backdrop-filter: blur pada bilah lengket memaksa compositing
   ulang tiap frame, dan di halaman ini ada beberapa video yang jalan
   bersamaan. Warna solid jauh lebih murah dan hasilnya sama terbacanya. */
#topbar {
  position: sticky; top: 0; z-index: 50;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px 16px; padding-top: calc(10px + env(safe-area-inset-top));
  background: var(--ink); border-bottom: 1px solid var(--edge);
}
.brand {
  display: flex; align-items: center; gap: 9px;
  font-family: var(--mono); font-size: .875rem; font-weight: 600;
  letter-spacing: .18em; text-transform: uppercase; white-space: nowrap;
}
.dot-sep { color: var(--haze); }
.top-right { display: flex; align-items: center; gap: 8px; }
#clock {
  font-family: var(--mono); font-size: .8125rem; font-variant-numeric: tabular-nums;
  color: var(--haze); letter-spacing: .04em;
}

#shell { flex: 1; display: flex; min-height: 0; }
#side-nav { display: none; }
#content { flex: 1; min-width: 0; padding: 18px 16px calc(88px + env(safe-area-inset-bottom)); }
/* Mode publik tidak punya menu, jadi tab bar disembunyikan — jangan sisakan
   ruang kosong setinggi tab bar di bawah halaman. */
#app-view:has(#tab-bar.hidden-bar) #content { padding-bottom: calc(24px + env(safe-area-inset-bottom)); }

/* tab bar bawah (ponsel) */
#tab-bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
  display: flex; justify-content: space-around; align-items: stretch;
  background: var(--plate); border-top: 1px solid var(--edge);
  padding: 4px 4px calc(4px + env(safe-area-inset-bottom));
}
#tab-bar a, #tab-bar button {
  flex: 1; min-height: 54px; max-width: 100px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  border: 0; background: none; cursor: pointer; color: var(--haze); text-decoration: none;
  font-family: var(--mono); font-size: .625rem; font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase; border-radius: var(--r-sm);
}
#tab-bar .ico { font-size: 1.125rem; line-height: 1; letter-spacing: 0; }
#tab-bar a.active { color: var(--vapor); }
#tab-bar a.active .ico { color: var(--halo); }
#tab-bar a:active, #tab-bar button:active { background: var(--plate-2); }
#tab-bar.hidden-bar { display: none; }

/* ---------- tombol ---------- */
.btn {
  border: 1px solid var(--edge); background: var(--plate-2); color: var(--vapor);
  min-height: var(--tap); padding: 10px 18px; border-radius: var(--r-sm); cursor: pointer;
  font-family: var(--sans); font-size: .9375rem; font-weight: 700;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  transition: border-color .12s ease, background-color .12s ease;
}
.btn:hover { border-color: var(--edge-lit); }
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--halo); border-color: var(--halo); color: var(--halo-ink); }
.btn-primary:hover { border-color: var(--halo); filter: brightness(1.07); }
.btn-danger { background: transparent; border-color: color-mix(in srgb, var(--tally) 45%, var(--edge)); color: var(--tally); }
.btn-ghost { background: transparent; }
.btn-sm { min-height: 36px; padding: 6px 12px; font-size: .8125rem; }
.btn-block { width: 100%; }
.btn[disabled] { opacity: .45; cursor: not-allowed; }

/* ---------- form ---------- */
label { display: flex; flex-direction: column; gap: 6px; font-size: .8125rem; font-weight: 700; color: var(--haze); }
input, select, textarea {
  background: var(--ink); color: var(--vapor);
  border: 1px solid var(--edge); border-radius: var(--r-sm);
  padding: 12px 13px;
  font-size: 16px; /* 16px persis: di bawah ini iOS ikut memperbesar halaman */
  font-family: var(--sans); font-weight: 400; min-height: var(--tap); width: 100%;
}
input:hover, select:hover, textarea:hover { border-color: var(--edge-lit); }
input:focus, select:focus, textarea:focus { outline: 2px solid var(--beam); outline-offset: -1px; border-color: transparent; }
textarea { min-height: 96px; resize: vertical; font-family: var(--mono); font-size: .875rem; }
.check { flex-direction: row; align-items: center; gap: 12px; color: var(--vapor); min-height: var(--tap); font-weight: 400; }
.check input { width: 20px; height: 20px; min-height: 0; accent-color: var(--halo); }
.form-error { color: var(--tally); font-size: .875rem; }
.form-grid { display: grid; grid-template-columns: 1fr; gap: 14px; }
.form-grid .full { grid-column: 1 / -1; }

/* ---------- kepala halaman ---------- */
.page-head { display: flex; flex-direction: column; gap: 12px; margin-bottom: 18px; }
.page-head .actions { display: flex; gap: 8px; flex-wrap: wrap; }
.page-head .actions .btn { flex: 1; }

/* ---------- badge status ---------- */
.badge {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 4px 11px; border-radius: 999px;
  font-family: var(--mono); font-size: .6875rem; font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase;
  background: var(--plate-2); border: 1px solid var(--edge); color: var(--haze);
}
.badge::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; flex-shrink: 0; }
/* "up" sengaja tetap memakai warna teks sekunder: yang normal tidak perlu
   menarik mata. Hanya titiknya yang hijau. */
.badge.up::before { background: var(--ok); }
.badge.connecting { color: var(--halo); border-color: color-mix(in srgb, var(--halo) 32%, var(--edge)); }
.badge.connecting::before { animation: pulse 1.2s ease-in-out infinite; }
.badge.degraded { color: var(--halo); border-color: color-mix(in srgb, var(--halo) 32%, var(--edge)); }
.badge.down { color: var(--tally); border-color: color-mix(in srgb, var(--tally) 38%, var(--edge)); }
@keyframes pulse { 50% { opacity: .2; } }

/* ---------- kartu ringkasan ---------- */
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 10px; margin-bottom: 20px; }
.card { background: var(--plate); border: 1px solid var(--edge); border-radius: var(--r); padding: 15px; }
.card .num { font-family: var(--mono); font-size: 1.625rem; font-weight: 600; font-variant-numeric: tabular-nums; line-height: 1.1; }
.card .label { color: var(--haze); font-size: .8125rem; margin-top: 4px; }

/* ---------- daftar kartu ---------- */
.rows { display: flex; flex-direction: column; gap: 10px; }
.row-card {
  background: var(--plate); border: 1px solid var(--edge); border-radius: var(--r);
  padding: 14px 15px; display: flex; flex-direction: column; gap: 10px;
}
.row-main { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.row-title { font-weight: 700; }
.row-sub { color: var(--haze); font-size: .8125rem; font-family: var(--mono); overflow-wrap: anywhere; }
.row-note { color: var(--haze); font-size: .875rem; }
.row-note.err { color: var(--tally); }
.row-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.row-actions .btn { flex: 1; min-width: 0; }
.snapshot-img {
  width: 100%; aspect-ratio: 16/9; object-fit: cover;
  border-radius: var(--r-sm); background: #000; border: 1px solid var(--edge);
}

/* ============================================================
   Live view — panggung utama
   ============================================================ */
.live-head { display: flex; flex-direction: column; gap: 12px; margin-bottom: 14px; }
.live-title { display: flex; flex-direction: column; gap: 9px; min-width: 0; }

/* --- TANDA TANGAN: bilah kanal ---------------------------------------
   Diambil dari panel depan NVR sungguhan, yang punya satu LED per kanal.
   Tiap tick membaca <video> milik selnya, jadi yang ditampilkan adalah
   "frame benar-benar masuk atau tidak", bukan status yang disalin dari
   basis data. Di ponsel hanya ~1,5 sel yang terlihat sekaligus, jadi
   ringkasan seluruh kanal dalam satu baris inilah yang bikin bisa langsung
   tahu ada yang mati tanpa menggulir. Ketuk untuk melompat ke selnya. */
.rail { display: flex; align-items: flex-end; gap: 6px; overflow-x: auto; scrollbar-width: none; padding-bottom: 2px; }
.rail::-webkit-scrollbar { display: none; }
.tick {
  flex: 0 0 auto; border: 0; background: none; cursor: pointer; padding: 6px 3px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  min-width: 30px; min-height: var(--tap); border-radius: 5px;
}
.tick-bar {
  width: 100%; min-width: 20px; height: 4px; border-radius: 2px;
  background: var(--edge-lit); transition: background-color .2s ease, height .2s ease;
}
.tick-id {
  font-family: var(--mono); font-size: .625rem; font-weight: 600;
  letter-spacing: .06em; color: var(--haze); font-variant-numeric: tabular-nums;
}
.tick.live .tick-bar { background: var(--ok); height: 6px; }
.tick.live .tick-id { color: var(--vapor); }
.tick.wait .tick-bar { background: var(--halo); animation: pulse 1.2s ease-in-out infinite; }
.tick.wait .tick-id { color: var(--halo); }
.tick.lost .tick-bar { background: var(--tally); }
.tick.lost .tick-id { color: var(--tally); }
.tick:hover .tick-bar { filter: brightness(1.3); }

.live-bar { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }

/* segmented control tata letak */
.seg { display: flex; border: 1px solid var(--edge); border-radius: var(--r-sm); overflow: hidden; background: var(--plate); }
.seg button {
  border: 0; background: none; color: var(--haze); cursor: pointer;
  min-height: 38px; padding: 6px 13px;
  font-family: var(--mono); font-size: .75rem; font-weight: 600; letter-spacing: .06em;
  white-space: nowrap; transition: color .12s ease, background-color .12s ease;
}
.seg button + button { border-left: 1px solid var(--edge); }
.seg button:hover { color: var(--vapor); }
.seg button.active { background: var(--halo); color: var(--halo-ink); }

/* Mode auto: kolom menyesuaikan lebar layar, tinggi sel ikut rasio kamera.
   minmax(0,1fr) penting: tanpa itu sel video menolak menyusut dan meluber. */
.live-grid.auto { display: grid; gap: var(--grid-gap); grid-template-columns: repeat(auto-fit, minmax(min(100%, 272px), 1fr)); }
.live-grid.auto > .cam-cell { width: 100%; }

/* Mode baris tetap: JS membuat tepat --rows buah .live-row, jadi jumlah baris
   yang dipilih selalu terpenuhi. Tinggi layar dibagi rata ke tiap baris;
   LEBAR sel mengikuti rasio kamera, sehingga sisa ruang jatuh sebagai latar
   halaman, bukan bilah hitam di dalam sel. */
.live-grid:not(.auto) { display: flex; flex-direction: column; gap: var(--grid-gap); }
.live-row {
  display: flex; gap: var(--grid-gap); align-items: stretch;
  /* safe center: kalau isinya lebih lebar dari barisnya, perataan tengah bikin
     bagian kiri tak bisa digapai saat digeser — "safe" mundur ke rata kiri. */
  justify-content: safe center;
  height: calc((var(--avail, 70dvh) - (var(--rows, 1) - 1) * var(--grid-gap)) / var(--rows, 1));
  min-height: 120px;
  /* Sel TIDAK boleh diperas: rasionya harus tetap sama dengan gambar kamera.
     Kalau satu baris tidak muat (mis. "1 baris" 3 kamera di ponsel), barisnya
     digeser mendatar, bukan selnya yang dipenyet. */
  overflow-x: auto; scrollbar-width: none; overscroll-behavior-x: contain;
}
.live-row::-webkit-scrollbar { display: none; }
.live-row > .cam-cell { height: 100%; width: auto; flex: 0 0 auto; }

.cam-cell {
  position: relative; background: #000;
  border: 1px solid var(--edge); border-radius: var(--r); overflow: hidden;
  /* --ar diisi JS dari videoWidth/videoHeight sebenarnya; 16/9 hanya sementara
     sampai metadata stream tiba. */
  aspect-ratio: var(--ar, 16/9);
  scroll-margin-top: 96px;
}
.cam-cell video-stream, .cam-cell video { display: block; width: 100%; height: 100%; }
.cam-cell video { object-fit: contain; background: #000; }
.cam-cell.flash { animation: flash 1.1s ease-out; }
@keyframes flash {
  from { box-shadow: inset 0 0 0 3px var(--halo); }
  to   { box-shadow: inset 0 0 0 3px transparent; }
}

/* Pelat OSD — meniru timecode yang ter-burn di dalam frame: mono, dijarangkan,
   duduk di atas kotak gelap kecil, bukan gradien selebar sel. */
.osd-top {
  position: absolute; top: 8px; left: 8px; right: 8px; z-index: 5;
  display: flex; justify-content: space-between; align-items: flex-start; gap: 8px;
  pointer-events: none;
}
.osd-top span {
  background: var(--osd-inkwell); border-radius: 4px; padding: 3px 7px;
  font-family: var(--mono); font-size: .625rem; font-weight: 600;
  letter-spacing: .14em; color: var(--osd-text);
}
.osd-live { display: inline-flex; align-items: center; gap: 6px; }
.osd-live::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: var(--tally); animation: pulse 2s ease-in-out infinite;
}
.osd-bottom {
  position: absolute; left: 8px; right: 8px; bottom: 8px; z-index: 5;
  display: flex; justify-content: space-between; align-items: center; gap: 8px;
  pointer-events: none;
}
.osd-name {
  background: var(--osd-inkwell); border-radius: 4px; padding: 4px 8px;
  font-size: .75rem; font-weight: 700; color: var(--osd-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.osd-bottom button {
  pointer-events: auto; border: 0; border-radius: 6px; cursor: pointer;
  min-width: 34px; min-height: 32px;
  background: var(--osd-inkwell); color: var(--osd-text); font-size: .875rem;
}
.osd-bottom button:hover { background: rgba(6, 10, 14, .85); }
.cam-empty {
  display: flex; align-items: center; justify-content: center; text-align: center;
  padding: 16px; color: var(--haze);
  font-family: var(--mono); font-size: .75rem; letter-spacing: .1em; text-transform: uppercase;
}

/* ---------- modal: bottom-sheet di ponsel ---------- */
.modal-backdrop { position: fixed; inset: 0; z-index: 100; background: var(--scrim); display: flex; align-items: flex-end; justify-content: center; }
.modal {
  background: var(--plate); border: 1px solid var(--edge); border-radius: 18px 18px 0 0;
  width: 100%; max-height: 92vh; max-height: 92dvh; overflow-y: auto;
  padding: 10px 18px calc(20px + env(safe-area-inset-bottom));
  animation: sheet-up .2s ease-out;
}
.modal::before { content: ""; display: block; width: 40px; height: 4px; border-radius: 2px; background: var(--edge-lit); margin: 4px auto 14px; }
@keyframes sheet-up { from { transform: translateY(36px); opacity: .5; } }
.modal-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px; gap: 8px; }
.modal form { display: flex; flex-direction: column; gap: 14px; }
.modal-foot { display: flex; gap: 8px; margin-top: 10px; }
.modal-foot .btn { flex: 1; }
.modal pre {
  background: var(--ink); border: 1px solid var(--edge); border-radius: var(--r-sm);
  padding: 13px; overflow-x: auto;
  font-family: var(--mono); font-size: .75rem; line-height: 1.65; white-space: pre;
}
.modal .video-big { aspect-ratio: 16/9; background: #000; border-radius: var(--r-sm); overflow: hidden; }
.modal .video-big video-stream, .modal .video-big video { width: 100%; height: 100%; }

/* ---------- toast ---------- */
#toast-root {
  position: fixed; z-index: 200; left: 12px; right: 12px;
  bottom: calc(82px + env(safe-area-inset-bottom));
  display: flex; flex-direction: column; gap: 8px; pointer-events: none;
}
.toast {
  background: var(--plate-2); border: 1px solid var(--edge); border-left: 3px solid var(--haze);
  border-radius: var(--r-sm); padding: 11px 14px; font-size: .875rem; box-shadow: var(--lift);
  animation: toast-in .16s ease-out;
}
.toast.ok { border-left-color: var(--ok); }
.toast.err { border-left-color: var(--tally); }
.toast.warn { border-left-color: var(--halo); }
@keyframes toast-in { from { transform: translateY(10px); opacity: 0; } }

/* ---------- bantuan (markdown) ---------- */
.md { max-width: 68ch; }
.md pre { background: var(--plate); border: 1px solid var(--edge); border-radius: var(--r-sm); padding: 13px; overflow-x: auto; font-family: var(--mono); font-size: .75rem; line-height: 1.65; }
.md code { font-family: var(--mono); background: var(--plate-2); border-radius: 4px; padding: 1px 5px; font-size: .875em; }
.md pre code { background: none; padding: 0; }
.md blockquote { border-left: 3px solid var(--halo); margin: 14px 0; padding: 2px 16px; color: var(--haze); }
.md h1, .md h2, .md h3 { margin: 1.6em 0 .5em; }
.md h1:first-child { margin-top: 0; }
.help-list { display: flex; flex-direction: column; gap: 10px; max-width: 560px; }
.help-list a {
  background: var(--plate); border: 1px solid var(--edge); border-radius: var(--r);
  padding: 15px 16px; min-height: var(--tap);
  display: flex; align-items: center; gap: 12px; text-decoration: none; color: var(--vapor);
  transition: border-color .12s ease;
}
.help-list a:hover { border-color: var(--edge-lit); }
.help-list a:active { background: var(--plate-2); }

/* ---------- log audit ---------- */
.audit { display: flex; flex-direction: column; gap: 6px; font-family: var(--mono); font-size: .75rem; }
.audit div { display: flex; gap: 12px; flex-wrap: wrap; padding: 9px 11px; background: var(--plate); border: 1px solid var(--edge); border-radius: var(--r-sm); }
.audit .muted { flex-shrink: 0; }

/* ============================================================
   Desktop (≥960px): rail kiri menggantikan tab bar bawah
   ============================================================ */
@media (min-width: 960px) {
  #content { padding: 26px 30px 48px; }
  #tab-bar { display: none; }
  #toast-root { left: auto; right: 22px; bottom: 22px; width: 380px; }

  #side-nav {
    display: flex; flex-direction: column; gap: 2px;
    width: 216px; flex-shrink: 0; padding: 16px 12px;
    border-right: 1px solid var(--edge);
    position: sticky; top: 57px; height: calc(100vh - 57px);
  }
  #side-nav a {
    display: flex; align-items: center; gap: 12px;
    padding: 10px 12px; border-radius: var(--r-sm);
    color: var(--haze); text-decoration: none; font-size: .9375rem; font-weight: 700;
  }
  #side-nav a:hover { background: var(--plate); color: var(--vapor); }
  #side-nav a.active { background: var(--plate-2); color: var(--vapor); }
  #side-nav a.active .ico { color: var(--halo); }
  #side-nav .ico { width: 22px; text-align: center; }

  .live-head { flex-direction: row; align-items: flex-end; justify-content: space-between; gap: 20px; }
  .live-bar { flex-shrink: 0; }
  .page-head { flex-direction: row; justify-content: space-between; align-items: center; }
  .page-head .actions .btn { flex: none; }
  .form-grid { grid-template-columns: 1fr 1fr; }
  .rows.rows-wide { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); }
  .row-actions .btn { flex: none; }

  .modal-backdrop { align-items: flex-start; padding: 6vh 16px; }
  .modal { width: 560px; max-width: 100%; border-radius: var(--r); max-height: 88vh; padding: 22px 24px; animation: none; }
  .modal::before { display: none; }
  .modal.modal-lg { width: 880px; }
  .modal-foot { justify-content: flex-end; }
  .modal-foot .btn { flex: none; }
}

/* ---------- hormati preferensi gerak minimal ---------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important; animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
}
