/* Trianguler — sistem desain.
 *
 * Dasar tetap dipertahankan dari versi peta-tunggal sebelumnya: navy gelap,
 * aksen biru langit, angka dalam monospace. Redesign ini menambahkan shell
 * SPA (sidebar/bottombar + 3 halaman) dan form Config di atas fondasi yang
 * sama, bukan menggantinya — lihat catatan desain di laporan designer.
 */

:root {
  --bg:        #0b1017;
  --bg-2:      #121a24;
  --bg-3:      #1a2431;
  --bg-4:      #212d3d;
  --line:      #24303f;
  --fg:        #e6edf5;
  --fg-dim:    #8fa1b6;
  --fg-faint:  #5d6f85;
  --accent:    #38bdf8;
  --accent-rgb: 56,189,248;
  --ok:        #34d399;
  --ok-rgb:    52,211,153;
  --warn:      #fbbf24;
  --warn-rgb:  251,191,36;
  --bad:       #f87171;
  --bad-rgb:   248,113,113;
  /* alias semantik dari --warn: nilainya sengaja SAMA (satu sumber, tak
   * dobel hex) tapi namanya beda krn artinya beda — ini identitas visual
   * "penanda anchor/receiver di peta", bukan status "warning" (dipakai jg
   * di kpi/pill/toast). Dipisah supaya kalau nanti warna warning berubah
   * sendiri, marker anchor tak ikut geser tanpa sengaja (dan sebaliknya). */
  --anchor:     var(--warn);
  --anchor-rgb: var(--warn-rgb);
  --radius:    10px;
  --radius-sm: 7px;
  --radius-lg: 14px;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;

  /* skala spasi 8px — cocok utk kepadatan dashboard/teknikal (ui-ux-pro-max, density tinggi) */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;
  --sp-6: 32px;

  --sidebar-w: 216px;
  --bottombar-h: 60px;
  --topbar-h: 52px;
}

* { box-sizing: border-box; }

/* Jaring pengaman: elemen ber-class yg mengatur `display` sendiri (mis. .btn
 * pakai inline-flex) bisa mengalahkan default `[hidden]{display:none}` milik
 * browser krn kedua aturan sama-sama origin "author" dgn spesifisitas setara —
 * yg belakangan menang, bukan yg [hidden]. Kunci di sini spy `hidden` SELALU
 * berarti tersembunyi, di mana pun dipakai. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.5;
  overscroll-behavior: none;
  -webkit-tap-highlight-color: transparent;
  /* color-scheme dark GLOBAL (properti ini inherited) -- browser gambar
   * sendiri kontrol form native (popup <select>, date/datetime-local picker,
   * scrollbar) pakai chrome GELAP alih-alih default terang bawaan OS. Tanpa
   * ini, <select> yang tak diberi background eksplisit (mis. #tl-asset,
   * #af-type, #borrow-user -- lihat .select di bawah) berakhir teks terang
   * (diwarisi dari `color: inherit` di bawah) DI ATAS popup opsi putih bawaan
   * browser = tak terbaca. Dulu cuma diset lokal di .field input date/
   * datetime-local, sekarang satu titik utk SEMUA kontrol native di app ini. */
  color-scheme: dark;
}

h1, h2, h3 { font-family: inherit; }
button, input, select, textarea { font-family: inherit; color: inherit; }
/* Select yang TIDAK diberi class .select / aturan spesifik lain (mis.
 * .opt-row select, .pagination select) -- jaring pengaman supaya tak ada
 * <select> polos di app ini yang jatuh ke background terang bawaan browser
 * (lihat catatan color-scheme di atas). Class/selector lebih spesifik di
 * bawah tetap menang lewat cascade normal. */
select {
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 0 11px;
  min-height: 40px;
  font-size: 13px;
  cursor: pointer;
}

/* ------------------------------------------------------------- accessibility */

:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}
a { color: var(--accent); }

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

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

/* ------------------------------------------------------------------- shell */

#app { display: flex; height: 100%; }

#content-wrap { flex: 1 1 auto; display: flex; flex-direction: column; min-width: 0; height: 100%; }

#topbar { display: none; } /* muncul <860px, lihat media query di bawah */

#content { flex: 1 1 auto; min-height: 0; position: relative; }

.view { height: 100%; }
.view[hidden] { display: none; }

.view-page { overflow-y: auto; padding: var(--sp-5) var(--sp-6) var(--sp-6); }
.view-full { display: flex; flex-direction: column; }

.view-head { margin-bottom: var(--sp-5); max-width: 1180px; }
.view-head h1 { margin: 0 0 3px; font-size: 21px; font-weight: 700; letter-spacing: -.2px; }
.view-sub { margin: 0; color: var(--fg-faint); font-size: 12.5px; }

/* -------------------------------------------------------------------- brand */

.brand { display: flex; align-items: center; gap: 11px; min-width: 0; }

.mark {
  width: 26px; height: 26px; flex: 0 0 auto;
  background: linear-gradient(160deg, var(--accent), #6366f1);
  clip-path: polygon(50% 4%, 96% 92%, 4% 92%);
}

.brand-text { display: flex; flex-direction: column; min-width: 0; }
.brand-text strong { font-size: 14.5px; font-weight: 650; letter-spacing: .1px; }
.brand-text span   { font-size: 11px; color: var(--fg-faint); font-family: var(--mono); }

.brand-sm strong { font-size: 14px; font-weight: 650; }

/* ----------------------------------------------------------------- sidebar */

#sidebar {
  flex: 0 0 var(--sidebar-w);
  display: flex;
  flex-direction: column;
  background: var(--bg-2);
  border-right: 1px solid var(--line);
  padding: var(--sp-4) var(--sp-3) var(--sp-3);
  gap: var(--sp-5);
}

.nav-list { display: flex; flex-direction: column; gap: 2px; }

.nav-item {
  display: flex; align-items: center; gap: 11px;
  padding: 9px 11px;
  min-height: 44px;
  border-radius: var(--radius-sm);
  color: var(--fg-dim);
  text-decoration: none;
  font-size: 13px; font-weight: 560;
  transition: background .15s ease, color .15s ease;
}
.nav-item:hover { background: var(--bg-3); color: var(--fg); }
.nav-item.active {
  background: rgba(var(--accent-rgb), .13);
  color: var(--accent);
}
.nav-icon { width: 19px; height: 19px; flex: 0 0 auto; }

.nav-icon, .bnav-item svg, .mode-btn svg, #panel-toggle svg, .btn svg,
.icon-btn svg, .search-box svg, .tl-play svg, .follow-toggle svg {
  fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}

.sidebar-foot { margin-top: auto; display: flex; flex-direction: column; gap: 9px; }

.foot-meta { margin: 0; display: flex; flex-direction: column; gap: 3px; }
.foot-meta div { display: flex; justify-content: space-between; gap: 8px; font-size: 11px; }
.foot-meta dt { color: var(--fg-faint); }
.foot-meta dd { margin: 0; font-family: var(--mono); color: var(--fg-dim); }

/* ------------------------------------------------------------------ session */

.session { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding-top: 9px; border-top: 1px solid var(--line); }
.session-user { display: flex; align-items: center; gap: 8px; min-width: 0; }
.session-avatar {
  width: 26px; height: 26px; flex: 0 0 auto; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--bg-3); border: 1px solid var(--line);
  font: 700 10.5px/1 var(--mono); color: var(--accent);
}
.session-name-wrap { min-width: 0; display: flex; flex-direction: column; }
.session-name { font-size: 12px; font-weight: 650; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 108px; }
.session-role { font-size: 10.5px; color: var(--fg-faint); text-transform: capitalize; }
.session-btn {
  flex: 0 0 auto;
  background: none; border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: 7px 10px; min-height: 32px;
  color: var(--accent); font-size: 11.5px; font-weight: 650; cursor: pointer;
}
.session-btn:hover { background: var(--bg-3); }

.topbar-session { display: flex; align-items: center; gap: 8px; }

/* ------------------------------------------------------------------- pills */

.pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px;
  background: var(--bg-3);
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 11.5px;
  font-family: var(--mono);
  color: var(--fg-dim);
  white-space: nowrap;
  align-self: flex-start;
}
.pill i { width: 7px; height: 7px; border-radius: 50%; background: var(--fg-faint); flex: 0 0 auto; }
.pill.ok   i { background: var(--ok);   box-shadow: 0 0 7px var(--ok); }
.pill.warn i { background: var(--warn); box-shadow: 0 0 7px var(--warn); }
.pill.bad  i { background: var(--bad);  box-shadow: 0 0 7px var(--bad); }

/* --------------------------------------------------------------- bottombar */

#bottombar { display: none; }

.bnav-item {
  flex: 1 1 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; padding: 6px 2px;
  min-height: 44px;
  color: var(--fg-faint);
  text-decoration: none;
  font-size: 10.5px; font-weight: 600;
}
.bnav-item svg { width: 21px; height: 21px; }
.bnav-item.active { color: var(--accent); }

/* -------------------------------------------------------------- kpi & dash */

.kpi-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--sp-4);
  margin-bottom: var(--sp-5);
  /* Konvensi kolom konten file ini 1180px (.view-head, .dash-grid, dst).
   * Tanpa batas yang sama di sini, baris KPI menjulur lebih lebar daripada
   * baris kartu di jendela >1180px konten — tepi kanannya tidak sejajar. */
  max-width: 1180px;
}

.kpi {
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--sp-4);
  display: flex; flex-direction: column; gap: 4px;
}
.kpi-label { font-size: 10.5px; text-transform: uppercase; letter-spacing: .08em; color: var(--fg-faint); font-weight: 650; }
.kpi-value { font-size: 26px; font-weight: 700; font-family: var(--mono); line-height: 1.15; }
.kpi-sub { font-size: 11.5px; color: var(--fg-dim); }
.kpi.ok .kpi-value   { color: var(--ok); }
.kpi.warn .kpi-value { color: var(--warn); }
.kpi.bad .kpi-value  { color: var(--bad); }

.dash-grid {
  display: grid;
  /* Dua kolom PERSIS sama lebar. auto-fit sengaja tidak dipakai: dengan 2
   * kartu di container yang muat >2 track, sisa track kosongnya membuat baris
   * ini berhenti sebelum tepi kanan — tidak sejajar dengan baris KPI di atas.
   * `stretch` menyamakan tinggi kedua kartu dalam baris yang sama. */
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-4);
  align-items: stretch;
  max-width: 1180px;
}

/* ------------------------------------------------------------------- cards */

.card {
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--sp-4);
  /* grid item polos punya minimum otomatis = min-content anak; tanpa ini,
   * konten yg tak bisa membungkus (mis. .tag-name/.tag-mac panjang) bisa
   * memaksa track grid lebih lebar drpd jatah 1fr-nya. Pola sama dgn
   * .kpi-grid/.ac-grid — lihat catatan di situ. */
  min-width: 0;
}
.card h2 {
  margin: 0 0 10px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .09em;
  color: var(--fg-faint);
  font-weight: 650;
}
.card-head-row { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); margin-bottom: var(--sp-3); }
.card-head-row h2 { margin: 0; }

.empty { margin: 0; color: var(--fg-faint); font-size: 12.5px; }

/* -------------------------------------------------------------------- tags */

.tag {
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 10px;
  margin-bottom: 8px;
  background: var(--bg-3);
  border-left: 3px solid var(--tag-color, var(--accent));
}
.tag:last-child { margin-bottom: 0; }

.tag-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; min-width: 0; }
/* nama tag bebas diketik user (bisa panjang) — dipotong ellipsis drpd
 * mendorong lebar kartu; MAC di sebelahnya tetap penuh & tak menyusut
 * (pola sama dgn .recv-name/.recv-coords di editor receiver). */
.tag-name { font-weight: 650; font-size: 13.5px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tag-mac  { font-family: var(--mono); font-size: 10.5px; color: var(--fg-faint); flex: 0 0 auto; white-space: nowrap; }

/* Badge sumber posisi non-solver (fix #11 proximity, atau mobile receiver) --
 * amber = "degradasi", warna yang SAMA dipakai marker anchor (--anchor,
 * alias --warn) supaya bahasanya konsisten dgn peta. */
.tag-src {
  font-size: 10px; font-weight: 650; padding: 1px 6px; border-radius: 999px;
  white-space: nowrap; flex: 0 0 auto;
  color: var(--anchor); background: rgba(var(--anchor-rgb), .14);
  border: 1px solid rgba(var(--anchor-rgb), .35);
}

.tag-stale { opacity: .45; }

.acc { display: flex; align-items: baseline; gap: 6px; margin: 8px 0 2px; }
.acc b { font-size: 24px; font-weight: 650; font-family: var(--mono); line-height: 1; }
.acc span { font-size: 11px; color: var(--fg-faint); }

.tag-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 4px 10px;
  margin-top: 8px;
  font-size: 11.5px;
  font-family: var(--mono);
}
.tag-grid div { display: flex; justify-content: space-between; gap: 6px; }
.tag-grid span { color: var(--fg-faint); }

/* --------------------------------------------------------- anchor & ranges */

table.rng { width: 100%; border-collapse: collapse; font-family: var(--mono); font-size: 11.5px; margin-top: 9px; }
table.rng th {
  text-align: right; font-weight: 500; color: var(--fg-faint);
  padding: 3px 0 5px; border-bottom: 1px solid var(--line); font-size: 10px;
  text-transform: uppercase; letter-spacing: .05em;
}
table.rng th:first-child, table.rng td:first-child { text-align: left; }
table.rng td { text-align: right; padding: 3px 0; color: var(--fg-dim); }
table.rng td.k { color: var(--fg); font-weight: 600; }
table.rng tr.stale td { color: var(--fg-faint); text-decoration: line-through; }

.bar { height: 3px; border-radius: 2px; background: var(--bg-3); overflow: hidden; margin-top: 2px; }
.bar > i { display: block; height: 100%; background: var(--accent); }

.anchor-row {
  display: flex; align-items: center; gap: 9px;
  padding: 6px 0; border-bottom: 1px solid var(--line);
  font-size: 12px;
}
.anchor-row:last-child { border-bottom: 0; }
.anchor-row .status-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--fg-faint); flex: 0 0 auto; }
.anchor-row .status-dot.ok { background: var(--ok); box-shadow: 0 0 6px var(--ok); }
.anchor-row .status-dot.bad { background: var(--bad); }
.anchor-badge {
  width: 22px; height: 22px; flex: 0 0 auto;
  display: grid; place-items: center;
  background: var(--bg-3); border: 1px solid var(--accent);
  color: var(--accent); border-radius: 5px;
  font-weight: 700; font-size: 11px;
}
.anchor-row .xy { margin-left: auto; font-family: var(--mono); font-size: 11px; color: var(--fg-faint); text-align: right; }
.anchor-row .xy small { display: block; color: var(--fg-faint); font-size: 10px; }

/* ----------------------------------------------------------------- options */

.opt {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 0; min-height: 32px;
  font-size: 12.5px; cursor: pointer; color: var(--fg-dim);
}
.opt input { accent-color: var(--accent); width: 15px; height: 15px; cursor: pointer; flex: 0 0 auto; }

.opt-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; margin-top: 8px; font-size: 12.5px; color: var(--fg-dim);
}
.opt-row select {
  background: var(--bg-3); color: var(--fg);
  border: 1px solid var(--line); border-radius: 6px;
  padding: 5px 7px; font-size: 11.5px; font-family: var(--mono);
  min-height: 30px;
}

.btn-row { display: flex; gap: 8px; margin-top: 11px; }
.btn-row button {
  flex: 1; padding: 9px 8px; min-height: 38px;
  background: var(--bg-3); color: var(--fg-dim);
  border: 1px solid var(--line); border-radius: 7px;
  font-size: 11.5px; cursor: pointer; font-family: inherit;
}
.btn-row button:hover { background: var(--line); color: var(--fg); }
.btn-row button:active { transform: translateY(1px); }

/* ------------------------------------------------------------- diagnostics */

#diag { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 3px 12px; font-size: 11.5px; }
#diag dt { color: var(--fg-faint); }
#diag dd { margin: 0; text-align: right; font-family: var(--mono); color: var(--fg-dim); word-break: break-all; }

/* --------------------------------------------------------------------- maps */

#maps-shell { flex: 1 1 auto; display: flex; min-height: 0; }

#map-stage { flex: 1 1 auto; min-width: 0; position: relative; background: #0d1420; }
#map-indoor, #map-outdoor { position: absolute; inset: 0; }
#map-indoor[hidden], #map-outdoor[hidden] { display: none; }
#map-indoor { background: var(--bg); }

.mode-switch {
  position: absolute; top: 12px; left: 12px; z-index: 700;
  display: flex; gap: 2px;
  background: rgba(18,26,36,.88);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 3px;
  backdrop-filter: blur(6px);
}
.mode-btn {
  display: flex; align-items: center; gap: 6px;
  padding: 7px 13px; min-height: 36px;
  border: 0; border-radius: 999px;
  background: transparent; color: var(--fg-dim);
  font-size: 12px; font-weight: 600; cursor: pointer;
}
.mode-btn svg { width: 15px; height: 15px; }
.mode-btn:hover { color: var(--fg); }
.mode-btn.active { background: var(--accent); color: #05202e; }

/* Tombol kecil "Ikuti tag" (maps.js:setFollow) -- sama bahasa visual dgn
 * .mode-switch/.mode-btn (pill blur, warna aktif accent) tapi berdiri
 * sendiri di kanan-atas #map-stage, simetris dgn .mode-switch di kiri. */
.follow-toggle {
  position: absolute; top: 12px; right: 12px; z-index: 700;
  display: flex; align-items: center; gap: 6px;
  padding: 7px 13px; min-height: 36px;
  border: 1px solid var(--line); border-radius: 999px;
  background: rgba(18,26,36,.88); color: var(--fg-dim);
  font-size: 12px; font-weight: 600; cursor: pointer;
  backdrop-filter: blur(6px);
}
.follow-toggle svg { width: 15px; height: 15px; }
.follow-toggle:hover { color: var(--fg); }
.follow-toggle.active { background: var(--accent); border-color: var(--accent); color: #05202e; }

#indoor-empty {
  position: absolute; inset: 0; z-index: 600;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--sp-4); text-align: center; padding: var(--sp-5);
  background: var(--bg);
  color: var(--fg-dim); font-size: 13px;
}
#indoor-empty[hidden] { display: none; }
#indoor-empty p { margin: 0; max-width: 320px; }

#panel {
  flex: 0 0 330px;
  background: var(--bg-2);
  border-left: 1px solid var(--line);
  overflow-y: auto;
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
#panel.mode-indoor .opt-outdoor-only { display: none; }

#panel-toggle {
  display: none;
  position: absolute; right: 12px; bottom: 12px; z-index: 900;
  width: 46px; height: 46px; border-radius: 50%;
  background: var(--bg-2); color: var(--fg);
  border: 1px solid var(--line); cursor: pointer;
  box-shadow: 0 4px 16px rgba(0,0,0,.45);
}
#panel-toggle svg { width: 19px; height: 19px; }

/* --------------------------------------------------------------- map bits */

.leaflet-container { background: #0d1420; font-family: var(--sans); }
#map-indoor.leaflet-container { background: var(--bg); }
.leaflet-control-attribution { background: rgba(11,16,23,.82) !important; color: var(--fg-faint) !important; font-size: 10px !important; }
.leaflet-control-attribution a { color: var(--fg-dim) !important; }
.leaflet-bar a {
  background: var(--bg-2) !important; color: var(--fg) !important;
  border-bottom-color: var(--line) !important;
}
.leaflet-bar a:hover { background: var(--bg-3) !important; }
.leaflet-control-scale-line {
  background: rgba(11,16,23,.82) !important; color: var(--fg-dim) !important;
  border-color: var(--fg-faint) !important;
}

.tile-dim { filter: brightness(.62) saturate(.72) contrast(1.06); }

.anchor-icon {
  display: grid; place-items: center;
  width: 28px; height: 28px;
  background: var(--anchor);
  border: 2px solid #0b1017;
  border-radius: 6px;
  color: #0b1017;
  font: 800 12px/1 var(--mono);
  box-shadow: 0 0 0 1px rgba(var(--anchor-rgb),.5), 0 2px 10px rgba(0,0,0,.55);
}

.tag-icon {
  width: 14px; height: 14px; border-radius: 50%;
  border: 2.5px solid #0b1017;
  box-shadow: 0 0 0 1.5px currentColor, 0 0 14px currentColor;
  background: currentColor;
}
/* Fallback proximity (fix #11, lihat tracker._solve_tag_proximity) -- marker
 * semi-transparan + tepi putus-putus, beda yang JUJUR drpd fix solver biasa
 * (dipasangkan dgn lingkaran galat dashArray, lihat animate() di maps.js). */
.tag-icon-proximity {
  opacity: .58;
  border-style: dashed;
  box-shadow: 0 0 0 1.5px currentColor, 0 0 8px currentColor;
}

/* --------------------------------------------------- editor receiver: peta */

.recv-marker {
  display: grid; place-items: center;
  width: 28px; height: 28px;
  /* satu warna amber tetap utk SEMUA receiver — sama seperti .anchor-icon
   * di Maps (lihat --anchor di :root). Sebelumnya tiap id dpt warna sendiri
   * dari PALETTE (receiver-map.js), yg justru membuat bahasa visual "anchor"
   * beda antara Maps (biru polos) dan editor Config (pelangi per-id).
   * Pembeda marker individual saat drag/pilih dipindah ke state .selected
   * di bawah (ring putih + scale), bukan hue — lihat catatan desain di
   * laporan designer. */
  background: var(--anchor);
  border: 2px solid #0b1017;
  border-radius: 50%;
  color: #0b1017;
  font: 800 10px/1 var(--mono);
  box-shadow: 0 0 0 1px rgba(var(--anchor-rgb),.5), 0 2px 8px rgba(0,0,0,.5);
  cursor: grab;
}
.recv-marker.selected {
  box-shadow: 0 0 0 3px rgba(255,255,255,.85), 0 0 14px var(--anchor);
  transform: scale(1.08);
}

.recv-ghost {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  width: 22px; height: 22px;
  border: 2px dashed var(--fg-faint);
  border-radius: 50%;
  cursor: pointer;
  transition: border-color .15s ease, background .15s ease;
}
.recv-ghost:hover { border-color: var(--accent); background: rgba(var(--accent-rgb), .12); }
.recv-ghost span {
  position: absolute; top: 100%; margin-top: 2px;
  font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--fg-faint); white-space: nowrap;
  background: rgba(11,16,23,.75); padding: 1px 4px; border-radius: 3px;
}
.recv-ghost:hover span { color: var(--accent); }

.map-label {
  background: rgba(11,16,23,.86);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 2px 6px;
  font: 600 11px/1.4 var(--mono);
  color: var(--fg);
  white-space: nowrap;
}
.leaflet-tooltip.map-label::before { display: none; }
.leaflet-popup-content-wrapper { background: var(--bg-2); color: var(--fg); border: 1px solid var(--line); }
.leaflet-popup-tip { background: var(--bg-2); }
.leaflet-popup-content { font-size: 12.5px; margin: 9px 12px; }

/* Popup klik tag (lihat maps.js popupHtml) -- baris "~dekat <anchor>"/"mobile
 * receiver" khusus fallback fix #11, amber sama dgn .tag-src di panel. */
.tag-popup .pop-near { color: var(--anchor); font-size: 11px; }

/* ----------------------------------------------------------------- toasts */

#toast-root {
  position: fixed; left: 50%; bottom: 22px; transform: translateX(-50%);
  z-index: 2000; display: flex; flex-direction: column-reverse; gap: 8px;
  align-items: center; pointer-events: none; width: min(92vw, 420px);
}
.toast {
  pointer-events: auto;
  background: var(--bg-3); color: var(--fg);
  border: 1px solid var(--line); border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  padding: 10px 14px; font-size: 12.5px;
  box-shadow: 0 8px 24px rgba(0,0,0,.4);
  opacity: 0; transform: translateY(8px);
  transition: opacity .2s ease, transform .2s ease;
  max-width: 100%;
}
.toast.show { opacity: 1; transform: translateY(0); }
.toast-ok   { border-left-color: var(--ok); }
.toast-bad  { border-left-color: var(--bad); }
.toast-warn { border-left-color: var(--warn); }

/* ------------------------------------------------------------------ banner */

.banner {
  padding: 11px 14px; border-radius: var(--radius-sm);
  background: rgba(var(--warn-rgb), .1); border: 1px solid rgba(var(--warn-rgb), .35);
  color: var(--warn); font-size: 12.5px; margin-bottom: var(--sp-4); max-width: 1180px;
}
.banner[hidden] { display: none; }
.banner.bad { background: rgba(var(--bad-rgb), .1); border-color: rgba(var(--bad-rgb), .35); color: var(--bad); }

/* Banner sesi berakhir (di luar #content, lihat index.html) -- beda dari
 * .banner per-halaman biasa: perlu tombol aksi di sampingnya & melebar penuh
 * di atas konten, bukan max-width 1180px yg ikut lebar kartu. */
.session-expired-banner {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  max-width: none; margin: 0; border-radius: 0; border-width: 0 0 1px;
  flex-wrap: wrap;
}
.session-expired-banner .btn { flex: 0 0 auto; min-height: 34px; padding: 0 14px; }

/* ------------------------------------------------------------------ config */

.config-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-4);
  max-width: 1180px;
}
.cfg-card-wide { grid-column: 1 / -1; }

.field { display: flex; flex-direction: column; gap: 5px; margin-bottom: var(--sp-3); }
.field:last-child { margin-bottom: 0; }
.field label { font-size: 11.5px; color: var(--fg-dim); font-weight: 600; }
.field input[type="text"], .field input[type="number"],
.field input[type="date"], .field input[type="datetime-local"] {
  background: var(--bg-3); color: var(--fg);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: 9px 11px; font-size: 13px; min-height: 40px;
  font-family: var(--mono);
  /* color-scheme dark: browser gambar sendiri ikon kalender/jam & popover
   * date-picker bawaan -- tanpa ini keduanya render putih terang di atas
   * kartu gelap (dipakai jg oleh #tl-from/#tl-to di panel Maps). */
  color-scheme: dark;
}
.field input:focus-visible { border-color: var(--accent); }
.field input[type="range"] { accent-color: var(--accent); min-height: 32px; cursor: pointer; }
/* field terkunci (mis. #dr-username utk role driver) -- warna teks di atas
 * sengaja override default dim bawaan browser utk :disabled, jadi affordance
 * "tak bisa diedit" perlu ditulis ulang di sini drpd cuma teks pendamping. */
.field input:disabled { opacity: .55; cursor: not-allowed; }

/* auto-fit + minmax(min(100%,Npx),1fr) ("RAM" -- Repeat/Auto/Minmax): berlaku
 * spt grid-template-columns:1fr 1fr di kontainer lebar biasa (mis. Config),
 * TAPI otomatis jatuh ke SATU kolom kalau kontainernya sendiri lebih sempit
 * dari 130px*2+gap -- ini penting krn #card-timeline (panel "Riwayat aset")
 * cuma lebar ~330px TERLEPAS dari lebar viewport halaman (bukan cuma <860px),
 * jadi breakpoint @media global saja tak cukup: input datetime-local "Sampai"
 * di situ overflow/terpotong walau layar 1280px lebar krn KONTAINERnya yg
 * sempit, bukan viewport-nya. min-width:0 di field wajib supaya track grid
 * beneran boleh menyusut (default select/input min-width:auto = lebar
 * konten, itu yg sebelumnya memaksa track lebih lebar dari jatah 1fr). */
.field-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 130px), 1fr));
  gap: var(--sp-3);
  margin-bottom: var(--sp-3);
}
.field-row .field { min-width: 0; }
.field-row .field input,
.field-row .field select { min-width: 0; max-width: 100%; }

.hint { margin: 6px 0 0; font-size: 11px; color: var(--fg-faint); }

#cfg-origin-map {
  margin-top: var(--sp-3);
  height: 190px; border-radius: var(--radius-sm);
  border: 1px solid var(--line); overflow: hidden;
}

/* buttons ------------------------------------------------------------------ */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  min-height: 40px; padding: 0 15px;
  border-radius: var(--radius-sm); border: 1px solid var(--line);
  background: var(--bg-3); color: var(--fg);
  font-size: 12.5px; font-weight: 600; cursor: pointer;
  transition: background .15s ease, border-color .15s ease;
}
.btn svg { width: 16px; height: 16px; }
.btn:hover { background: var(--line); }
.btn-ghost { background: transparent; }
.btn-ghost:hover { background: var(--bg-3); }
.btn-accent { background: var(--accent); border-color: var(--accent); color: #052232; }
.btn-accent:hover { background: #5cc9fa; }
.btn-lg { min-height: 44px; padding: 0 22px; font-size: 13.5px; }
.btn-danger-ghost { color: var(--bad); }
.btn-danger-ghost:hover { background: rgba(var(--bad-rgb), .12); }

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

.upload-row { display: flex; align-items: center; gap: 10px; margin-bottom: 4px; flex-wrap: wrap; }

/* receiver editor — map-first ------------------------------------------------ */

.recv-editor { display: grid; grid-template-columns: 1.5fr 1fr; gap: var(--sp-4); align-items: start; }

.recv-map-stage {
  position: relative;
  min-height: 420px; height: 46vh; max-height: 560px;
  background: var(--bg-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
#recv-map-outdoor, #recv-map-indoor { position: absolute; inset: 0; }
#recv-map-indoor[hidden] { display: none; }
#recv-map-indoor.leaflet-container { background: var(--bg); }

.mode-switch.small { padding: 2px; }
.mode-switch.small .mode-btn { padding: 5px 10px; min-height: 30px; font-size: 11px; }
.mode-switch.small .mode-btn svg { width: 13px; height: 13px; }

.recv-list { display: flex; flex-direction: column; gap: 7px; max-height: 560px; overflow-y: auto; padding-right: 2px; }

/* `flex-shrink` default = 1: di kolom flex ber-`max-height`, kartu DIPERAS agar
 * muat 560px alih-alih membuat list menggulung — dan karena kartu ber-`overflow:
 * hidden`, isi yang keluar hilang diam-diam (kartu terpilih kehilangan ~81px:
 * seluruh baris path-loss). Kunci tinggi kartu ke tinggi alaminya supaya yang
 * menggulung adalah list, sesuai maksud `overflow-y: auto` di atas. */
.recv-card {
  flex: 0 0 auto;
  background: var(--bg-3); border: 1px solid var(--line); border-radius: var(--radius-sm);
  overflow: hidden;
}
.recv-card.selected { border-color: var(--c, var(--accent)); box-shadow: 0 0 0 1px var(--c, var(--accent)); }

.recv-card-head {
  display: flex; align-items: center; gap: 9px;
  padding: 9px 10px; min-height: 44px; cursor: pointer;
}
.recv-card-head:hover { background: var(--bg-4); }
.recv-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--c, var(--accent)); flex: 0 0 auto; box-shadow: 0 0 6px var(--c, var(--accent)); }
.recv-name { font-size: 13px; font-weight: 650; flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.recv-coords { font-family: var(--mono); font-size: 10.5px; color: var(--fg-faint); flex: 0 0 auto; white-space: nowrap; }

.recv-del {
  flex: 0 0 auto; width: 30px; height: 30px; min-height: 30px;
  display: grid; place-items: center;
  background: transparent; border: 1px solid transparent; border-radius: 7px;
  color: var(--fg-faint); cursor: pointer;
}
.recv-del:hover { color: var(--bad); border-color: var(--bad); background: rgba(var(--bad-rgb), .1); }
.recv-del svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; }

.recv-card-detail { padding: 0 10px 11px; border-top: 1px solid var(--line); padding-top: 10px; }
.recv-card-detail .field { margin-bottom: 8px; }

/* `1fr` polos punya minimum otomatis = min-content anak (form control punya
 * min-content bawaan browser ~165px, TERLEPAS dari lebar/isi sebenarnya) —
 * itu bikin grid meluber lalu terpotong diam-diam oleh `.recv-card{overflow:
 * hidden}`. `minmax(0, 1fr)` + `min-width:0` berantai ke label & input
 * memutus rantai itu; pola sama dgn .kpi-grid di atas yg sudah benar. */
.ac-grid { display: grid; gap: 8px; min-width: 0; }
.ac-grid-coords { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.ac-grid-adv    { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.ac-grid label { display: flex; flex-direction: column; gap: 3px; min-width: 0; font-size: 10px; color: var(--fg-faint); text-transform: uppercase; letter-spacing: .04em; }
.ac-grid input {
  width: 100%; min-width: 0;
  background: var(--bg-4); color: var(--fg); border: 1px solid var(--line);
  border-radius: 6px; padding: 6px 7px; font-size: 12px; font-family: var(--mono); min-height: 32px;
}
.ac-grid input:focus-visible { border-color: var(--accent); }

.ac-advanced { margin-top: 9px; }
.ac-advanced summary {
  cursor: pointer; font-size: 11px; color: var(--fg-faint); font-weight: 600;
  padding: 4px 0; list-style: none;
}
.ac-advanced summary::-webkit-details-marker { display: none; }
.ac-advanced summary::before { content: '▸ '; }
.ac-advanced[open] summary::before { content: '▾ '; }
.ac-advanced .ac-grid { margin-top: 8px; }

.config-actions {
  position: sticky; bottom: 0;
  display: flex; align-items: center; justify-content: flex-end; gap: var(--sp-3);
  margin-top: var(--sp-5); padding: var(--sp-3) 0; max-width: 1180px;
  /* bar ini melayang di atas daftar receiver yg panjang; gradien yg terlalu
   * lembut bikin kartu di bawahnya setengah kelihatan spt terpotong. Tutup
   * rapat dulu, baru luruh di ~2 baris terakhir. */
  background: linear-gradient(to top, var(--bg) 82%, rgba(11, 16, 23, 0));
}
.save-status { font-size: 12px; color: var(--fg-dim); }
.save-status.ok { color: var(--ok); }
.save-status.bad { color: var(--bad); }

/* =========================================== dialog sync perangkat (QR) */

.head-actions { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }

.qr-dialog {
  width: min(520px, calc(100vw - var(--sp-6)));
  max-height: min(88vh, 780px);
  margin: auto;                      /* <dialog> di top layer: pusatkan sendiri */
  padding: var(--sp-5);
  overflow-y: auto;
  background: var(--bg-2);
  color: var(--fg);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: 0 24px 64px rgba(0, 0, 0, .55);
}
.qr-dialog::backdrop { background: rgba(4, 8, 13, .68); }

.qr-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-3); }
.qr-head h2 { margin: 0; font-size: 16px; font-weight: 700; letter-spacing: -.01em; }

.qr-close {
  flex: 0 0 auto; width: 44px; height: 44px; margin: -10px -10px 0 0;
  display: grid; place-items: center;
  background: transparent; border: 1px solid transparent; border-radius: var(--radius-sm);
  color: var(--fg-dim); cursor: pointer;
}
.qr-close:hover { background: var(--bg-3); color: var(--fg); }
.qr-close svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; }

.qr-sub { margin-top: var(--sp-2); }

/* --- pindai ------------------------------------------------------------- */

.qr-cam {
  position: relative; margin-top: var(--sp-4);
  aspect-ratio: 4 / 3; overflow: hidden;
  background: #05090e; border: 1px solid var(--line); border-radius: var(--radius-sm);
}
.qr-cam video { width: 100%; height: 100%; object-fit: cover; display: block; }

/* bingkai bidik: memberi tahu di mana QR harus diletakkan tanpa menutupi gambar */
.qr-reticle {
  position: absolute; inset: 50% auto auto 50%; transform: translate(-50%, -50%);
  width: 58%; aspect-ratio: 1; border: 2px dashed rgba(var(--accent-rgb), .85);
  border-radius: var(--radius-sm); box-shadow: 0 0 0 100vmax rgba(4, 8, 13, .34);
}
.qr-cam-status {
  position: absolute; left: 0; right: 0; bottom: 0; margin: 0;
  padding: 7px 10px; font-size: 11.5px; color: var(--fg-dim);
  background: linear-gradient(to top, rgba(4, 8, 13, .88), transparent);
}

.qr-nocam {
  margin: var(--sp-4) 0 0; padding: 10px 12px;
  font-size: 12.5px; line-height: 1.55; color: var(--fg-dim);
  background: var(--bg-3); border: 1px solid var(--line);
  border-left: 3px solid var(--warn); border-radius: var(--radius-sm);
}

.qr-file-path { margin-top: var(--sp-4); }
.qr-file-btn { width: 100%; min-height: 44px; }
.qr-file-btn:disabled { opacity: .6; cursor: progress; }
#qr-file-hint { text-align: center; }

.qr-error {
  margin: var(--sp-4) 0 0; padding: 10px 12px;
  font-size: 12.5px; line-height: 1.55; color: var(--fg);
  background: rgba(var(--bad-rgb), .1);
  border: 1px solid rgba(var(--bad-rgb), .4);
  border-left: 3px solid var(--bad); border-radius: var(--radius-sm);
}

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

.qr-found {
  margin-top: var(--sp-4); padding: var(--sp-4);
  background: var(--bg-3); border: 1px solid var(--line);
  border-left: 3px solid var(--ok); border-radius: var(--radius-sm);
}
.qr-found-badge {
  display: inline-block; font-size: 10px; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--ok);
}
.qr-found-role { margin: 6px 0 0; font-size: 15px; font-weight: 700; letter-spacing: .01em; }
.qr-found-mac { margin: 2px 0 0; font-family: var(--mono); font-size: 14px; color: var(--accent); }
.qr-found-via { margin: 4px 0 0; font-size: 11px; color: var(--fg-faint); }

.qr-targets { margin: var(--sp-4) 0 0; padding: 0; border: 0; }
.qr-targets legend {
  padding: 0; margin-bottom: var(--sp-2);
  font-size: 11.5px; font-weight: 600; color: var(--fg-dim);
}

.qr-opt {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 11px 12px; margin-bottom: var(--sp-2); min-height: 44px;
  background: var(--bg-3); border: 1px solid var(--line);
  border-radius: var(--radius-sm); cursor: pointer;
  transition: border-color .15s ease, background .15s ease;
}
.qr-opt:hover { background: var(--bg-4); }
.qr-opt input { accent-color: var(--accent); width: 17px; height: 17px; margin: 1px 0 0; flex: 0 0 auto; cursor: pointer; }
.qr-opt:has(input:checked) { border-color: var(--accent); background: rgba(var(--accent-rgb), .07); }
.qr-opt:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
.qr-opt.is-disabled { opacity: .55; cursor: not-allowed; }
.qr-opt.is-disabled input { cursor: not-allowed; }

.qr-opt-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.qr-opt-title { font-size: 12.5px; font-weight: 600; }
.qr-opt-sub { font-size: 11px; color: var(--fg-faint); line-height: 1.5; }

.qr-check { margin-top: var(--sp-3); }
.qr-geo-note { margin-top: var(--sp-3); }

.qr-actions {
  display: flex; align-items: center; justify-content: flex-end;
  gap: var(--sp-2); margin-top: var(--sp-5);
}
/* `.btn` dasar setinggi 40px; di dalam dialog ini semua aksi dipakai sambil
 * memegang perangkat, jadi naikkan ke minimum sentuh 44px. */
.qr-actions .btn { min-height: 44px; }

/* ==================================================== halaman Aset & Audit */

.view-head-row { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-3); flex-wrap: wrap; }

.btn-block { width: 100%; }

.icon-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-width: 34px; min-height: 34px; padding: 0 10px;
  border-radius: var(--radius-sm); border: 1px solid var(--line);
  background: var(--bg-3); color: var(--fg-dim);
  font-size: 11.5px; font-weight: 600; cursor: pointer;
}
.icon-btn:hover { background: var(--line); color: var(--fg); }
.icon-btn svg { width: 14px; height: 14px; }
.icon-btn-danger:hover { color: var(--bad); border-color: rgba(var(--bad-rgb), .4); background: rgba(var(--bad-rgb), .1); }

/* ---- badge (status aset, tipe aset, aksi audit) --------------------------- */

.badge {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px; border-radius: 999px;
  font-size: 11px; font-weight: 650; white-space: nowrap;
  border: 1px solid transparent;
}
.badge i { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: 0 0 auto; }
.badge-ok     { background: rgba(var(--ok-rgb), .13);     color: var(--ok);     border-color: rgba(var(--ok-rgb), .3); }
.badge-warn   { background: rgba(var(--warn-rgb), .13);   color: var(--warn);   border-color: rgba(var(--warn-rgb), .3); }
.badge-bad    { background: rgba(var(--bad-rgb), .13);    color: var(--bad);    border-color: rgba(var(--bad-rgb), .3); }
.badge-accent { background: rgba(var(--accent-rgb), .13); color: var(--accent); border-color: rgba(var(--accent-rgb), .3); }
.badge-neutral{ background: var(--bg-3); color: var(--fg-dim); border-color: var(--line); }
/* tipe aset BUKAN status (bukan hijau/amber/merah) -- warna terpisah dari
 * palet status supaya "Engine stand"/"Forklift" tak dikira sinyal kondisi.
 * Label teks selalu menyertai warna (lihat ux: jangan andalkan warna saja). */
.badge-type-a { color: var(--accent); border-color: rgba(var(--accent-rgb), .3); background: rgba(var(--accent-rgb), .13); }
.badge-type-b { color: #a5b4fc; border-color: rgba(165, 180, 252, .35); background: rgba(165, 180, 252, .13); }

/* ---- toolbar: pencarian + filter ------------------------------------------ */

.toolbar { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; margin-bottom: var(--sp-4); max-width: 1180px; }

.search-box {
  display: flex; align-items: center; gap: 8px;
  flex: 1 1 220px; min-width: 180px;
  background: var(--bg-2); border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: 0 11px; min-height: 40px;
}
.search-box svg { width: 15px; height: 15px; color: var(--fg-faint); flex: 0 0 auto; }
.search-box input {
  border: 0; background: none; color: var(--fg); font-size: 13px;
  flex: 1 1 auto; min-width: 0; height: 38px;
}
.search-box input:focus-visible { outline: none; }
.search-box:has(input:focus-visible) { border-color: var(--accent); }
.search-box input::-webkit-search-cancel-button { cursor: pointer; }

.chip-group { display: flex; gap: 6px; flex-wrap: wrap; }
.chip {
  min-height: 40px; padding: 0 13px;
  border-radius: 999px; border: 1px solid var(--line);
  background: var(--bg-2); color: var(--fg-dim);
  font-size: 12px; font-weight: 600; cursor: pointer;
}
.chip:hover { background: var(--bg-3); color: var(--fg); }
.chip.active { background: rgba(var(--accent-rgb), .15); border-color: var(--accent); color: var(--accent); }

.select {
  min-height: 40px; padding: 0 30px 0 11px;
  background: var(--bg-2) url('data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%238fa1b6%22 stroke-width=%222%22 stroke-linecap=%22round%22%3E%3Cpath d=%22m6 9 6 6 6-6%22/%3E%3C/svg%3E') no-repeat right 8px center / 15px;
  color: var(--fg);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  font-size: 12.5px; cursor: pointer; appearance: none;
}
.select:focus-visible { border-color: var(--accent); }

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

.table-wrap { padding: 0; overflow-x: auto; max-width: 1180px; }
.data-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.data-table th, .data-table td {
  padding: 11px 13px; text-align: left; border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.data-table th {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--fg-faint); font-weight: 650;
  position: sticky; top: 0; background: var(--bg-2); z-index: 1;
}
.data-table th button {
  background: none; border: 0; padding: 0; margin: 0; cursor: pointer;
  display: inline-flex; align-items: center; gap: 4px;
  font: inherit; color: inherit; text-transform: inherit; letter-spacing: inherit;
  min-height: 20px;
}
.data-table th:not([data-sort]) button { cursor: default; }
.data-table th button:hover { color: var(--fg); }
.data-table th[aria-sort] button { color: var(--accent); }
.data-table th .sort-arrow { font-size: 9px; }
.data-table tbody tr:hover { background: var(--bg-3); }
.data-table tbody tr:last-child td { border-bottom: 0; }
.data-table td.mono { font-family: var(--mono); color: var(--fg-dim); font-size: 11.5px; }
.data-table td.cell-dim { color: var(--fg-faint); }
.data-table th.col-actions, .data-table td.col-actions { text-align: right; }
.data-table .cell-actions { display: inline-flex; gap: 6px; justify-content: flex-end; flex-wrap: wrap; }
.data-table .loc-link { font-size: 12px; }
.data-table .row-label { display: none; } /* label kolom utk mode kartu <860px, lihat media query */

.table-empty-row td { text-align: center; padding: 34px 12px; color: var(--fg-faint); white-space: normal; }

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

.pagination {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  margin-top: var(--sp-3); flex-wrap: wrap; max-width: 1180px;
  font-size: 12px; color: var(--fg-dim);
}
.pagination .pg-info { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.pagination .pg-btns { display: flex; gap: 6px; align-items: center; }
.pagination button {
  min-width: 36px; min-height: 36px; padding: 0 10px;
  border-radius: var(--radius-sm); border: 1px solid var(--line);
  background: var(--bg-2); color: var(--fg-dim); font-size: 12px; cursor: pointer;
}
.pagination button:hover:not(:disabled) { background: var(--bg-3); color: var(--fg); }
.pagination button:disabled { opacity: .4; cursor: not-allowed; }
.pagination select { min-height: 36px; padding: 0 26px 0 9px; font-size: 12px; }

/* ---- audit: baris detail kolaps -------------------------------------------- */

.audit-detail-row td { padding: 0; border-bottom: 1px solid var(--line); white-space: normal; }
.audit-detail {
  padding: 10px 13px 14px; background: var(--bg);
  font-family: var(--mono); font-size: 11.5px; color: var(--fg-dim);
  white-space: pre-wrap; word-break: break-word;
}
.audit-toggle {
  background: none; border: 0; padding: 0; color: var(--accent); font-size: 11.5px;
  cursor: pointer; display: inline-flex; align-items: center; gap: 4px; min-height: 34px;
}
.audit-toggle:hover { text-decoration: underline; }
.audit-toggle svg { width: 12px; height: 12px; transition: transform .15s ease; }
.audit-toggle.open svg { transform: rotate(180deg); }

/* ---- banner khusus akses ditolak ------------------------------------------- */

.banner.denied { background: rgba(var(--bad-rgb), .1); border-color: rgba(var(--bad-rgb), .35); color: var(--bad); }

/* ==================================================== timeline (panel Maps) */

#card-timeline .field { margin-bottom: 9px; }
#card-timeline .field:last-of-type { margin-bottom: 0; }
#tl-load { margin-top: 2px; }
#tl-player { margin-top: 12px; padding-top: 11px; border-top: 1px solid var(--line); }

.tl-transport { display: flex; align-items: center; gap: 9px; margin-top: 2px; }
.tl-play {
  flex: 0 0 auto; width: 36px; height: 36px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--accent); border: 0; color: #052232; cursor: pointer;
}
.tl-play:hover { background: #5cc9fa; }
.tl-play svg { width: 15px; height: 15px; }
.tl-play:disabled { opacity: .4; cursor: not-allowed; }

/* track scrubber "evidence": slider asli + takik insiden overlay di atasnya.
 * Slider skrg mewakili FRAKSI waktu (resolusi tetap SLIDER_RES), bukan lagi
 * 1 step = 1 titik riwayat -- lihat timeline.js utk model interpolasinya. */
.tl-track-wrap { position: relative; flex: 1 1 auto; min-height: 44px; display: flex; align-items: center; }
.tl-slider { width: 100%; accent-color: var(--accent); min-height: 44px; cursor: pointer; margin: 0; position: relative; z-index: 2; }
.tl-slider:disabled { cursor: not-allowed; opacity: .5; }

.tl-notches { position: absolute; left: 0; right: 0; top: 50%; height: 0; pointer-events: none; z-index: 3; }
.tl-notch {
  position: absolute; top: 50%; transform: translate(-50%, -50%);
  width: 3px; height: 14px; border-radius: 1.5px;
  background: var(--ic-color, var(--bad));
  border: 0; padding: 0; margin: 0; cursor: pointer; pointer-events: auto;
}
/* garis takik tampak tipis (3px) tapi target sentuhnya lebar & ≥44px tinggi
 * (spek: track tetap bisa di-drag/ditakik dgn jari di HP). */
.tl-notch::before { content: ''; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 24px; height: 44px; }
.tl-notch:hover, .tl-notch:focus-visible { width: 4px; height: 18px; box-shadow: 0 0 6px var(--ic-color, var(--bad)); }
.tl-notch:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.tl-time { margin-top: 6px; font-family: var(--mono); font-size: 11px; color: var(--fg-dim); text-align: right; }
.tl-empty { font-size: 12px; color: var(--fg-faint); margin: 8px 0 0; }

/* kartu bukti insiden -- pola sama dgn .qr-found (callout hasil, border kiri
 * berwarna) drpd bikin sistem kartu-mengambang baru; --ic-color diisi per
 * insiden yg sedang tampil (timeline.js:showEvidenceCard). */
.tl-evidence {
  margin-top: 10px; padding: 10px 12px;
  background: var(--bg-3); border: 1px solid var(--line);
  border-left: 3px solid var(--ic-color, var(--accent)); border-radius: var(--radius-sm);
}
.tl-evidence-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.tl-evidence-type { font-size: 13px; font-weight: 700; color: var(--ic-color, var(--fg)); }
.tl-evidence-nav {
  flex: 0 0 auto; width: 26px; height: 26px; min-height: 0;
  display: grid; place-items: center;
  background: var(--bg-4); border: 1px solid var(--line); border-radius: 6px;
  color: var(--fg-dim); font-size: 14px; line-height: 1; cursor: pointer;
}
.tl-evidence-nav:hover { background: var(--line); color: var(--fg); }
.tl-evidence-body { margin: 8px 0 0; display: grid; grid-template-columns: auto 1fr; gap: 3px 10px; font-size: 11.5px; }
.tl-evidence-body dt { color: var(--fg-faint); }
.tl-evidence-body dd { margin: 0; text-align: right; font-family: var(--mono); color: var(--fg-dim); }
.tl-evidence-body dd .badge { font-family: var(--sans); }

.tl-marker {
  width: 16px; height: 16px; border-radius: 50%;
  border: 2.5px solid #0b1017;
  box-shadow: 0 0 0 1.5px currentColor, 0 0 16px currentColor;
  background: currentColor;
}

/* marker insiden riwayat (timeline.js) -- lebih besar & bersudut drpd
 * .tl-marker (posisi tag) supaya tak tertukar sbg titik lintasan yg sama;
 * warna per jenis dari App.eventTypes (event-types.js), lihat --ic-color. */
.incident-marker {
  width: 16px; height: 16px; border-radius: 4px;
  background: var(--ic-color, var(--bad));
  border: 2px solid #0b1017;
  box-shadow: 0 0 0 1.5px var(--ic-color, var(--bad)), 0 2px 8px rgba(0,0,0,.55);
  transform: rotate(45deg);
  transition: transform .15s ease, box-shadow .15s ease;
}
/* insiden yg kartu buktinya sedang tampil (timeline.js:setActiveIncidentMarker)
 * -- scale bukan width/height, spy iconAnchor Leaflet (dihitung dari ukuran
 * asli) tak ikut bergeser. */
.incident-marker.active {
  transform: rotate(45deg) scale(1.45);
  box-shadow: 0 0 0 3px rgba(255,255,255,.9), 0 0 20px var(--ic-color, var(--bad));
  z-index: 5000;
}

.tl-incident-legend {
  display: flex; flex-wrap: wrap; gap: 4px 10px;
  margin-top: 10px; padding-top: 9px; border-top: 1px solid var(--line);
  font-size: 10.5px; color: var(--fg-dim);
}
.tl-legend-item { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
.tl-legend-item i { width: 8px; height: 8px; border-radius: 2px; flex: 0 0 auto; transform: rotate(45deg); }

/* ==================================================== halaman Enrollment Wajah */

.enroll-add-box { flex: 0 1 320px; }

/* Dialog ambil foto: pakai kerangka .qr-cam/.qr-dialog yg sama dgn dialog sync
 * perangkat (qr-sync.js) -- getUserMedia, tombol fallback berkas, pesan izin
 * ditolak semuanya pola yg identik, cuma bingkai bidik wajahnya oval bukan
 * kotak QR. Lihat enroll.js utk logic capture/kirim. */
.enroll-reticle {
  position: absolute; inset: 8% auto auto 50%; transform: translateX(-50%);
  width: 62%; aspect-ratio: 3 / 4;
  border: 2px dashed rgba(var(--accent-rgb), .85);
  border-radius: 50%;
  box-shadow: 0 0 0 100vmax rgba(4, 8, 13, .34);
}
.enroll-capture-btn { margin-top: var(--sp-4); }

.enroll-thumbs {
  display: flex; flex-wrap: wrap; gap: 8px;
  margin-top: var(--sp-4);
}
.enroll-thumbs:empty { margin-top: 0; }
.enroll-thumb {
  position: relative; width: 64px; height: 64px; flex: 0 0 auto;
  border-radius: var(--radius-sm); overflow: hidden;
  border: 1px solid var(--line); background: var(--bg-3);
}
.enroll-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.enroll-thumb-del {
  position: absolute; top: 2px; right: 2px;
  width: 20px; height: 20px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(4, 8, 13, .78); border: 1px solid rgba(255,255,255,.25);
  color: #fff; cursor: pointer; padding: 0;
}
.enroll-thumb-del svg { width: 11px; height: 11px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }

/* ==================================================== halaman Laporan Pengemudi */

.dr-toolbar { align-items: flex-end; }
.dr-toolbar .field { margin-bottom: 0; min-width: 150px; }
.dr-field-user { flex: 1 1 200px; }
.dr-toolbar .btn { flex: 0 0 auto; }

.dr-score-grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1.6fr;
  gap: var(--sp-4);
  margin-bottom: var(--sp-4);
  max-width: 1180px;
  align-items: stretch;
}
/* skor besar 0-100: pakai komponen .kpi yg sama dgn Dashboard (warna
 * ok/warn/bad lewat modifier class, lihat driver-report.js:scoreTier) --
 * cuma dibesarkan krn ini metrik utama halaman ini. */
.kpi-score .kpi-value { font-size: 40px; }

.dr-events-card .event-grid { margin-top: 2px; }
/* baris per jenis event: pola sama dgn .anchor-row/.anchor-badge di Maps
 * (badge bulat + label + angka rata kanan) -- diberi nama sendiri krn
 * ranahnya beda (event mengemudi, bukan anchor radio). */
.event-row {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 0; border-bottom: 1px solid var(--line);
  font-size: 12.5px;
}
.event-row:last-child { border-bottom: 0; }
.event-icon {
  width: 26px; height: 26px; flex: 0 0 auto;
  display: grid; place-items: center;
  background: var(--bg-3); border: 1px solid var(--line);
  color: var(--fg-dim); border-radius: 7px;
}
.event-icon svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.event-name { flex: 1 1 auto; color: var(--fg-dim); }
.event-count { font-family: var(--mono); font-weight: 700; font-size: 14px; color: var(--fg); }

.dr-mini-badges { display: inline-flex; gap: 5px; flex-wrap: wrap; }
.dr-mini-badges .badge { padding: 2px 7px; font-size: 10.5px; }

/* titik warna kecil di badge jenis insiden (tabel Histori Insiden) -- sama
 * spt swatch legend di panel timeline Maps (.tl-legend-item i), identitas
 * warna per jenis dari App.eventTypes konsisten di kedua tempat. */
.dr-incident-swatch { width: 7px; height: 7px; border-radius: 2px; flex: 0 0 auto; transform: rotate(45deg); }

.dr-detail-list { display: flex; flex-direction: column; gap: 5px; }
.dr-detail-row-item {
  display: flex; align-items: center; gap: var(--sp-3);
  font-size: 11.5px; font-family: var(--mono); color: var(--fg-dim);
}
.dr-detail-row-item .badge { font-family: var(--sans); }

/* ==================================================================== SPK */
/* Halaman admin SPK: reuses .qr-opt (checkbox card, dipakai dialog sync
 * perangkat) utk daftar aset bercentang, dan .audit-toggle/.audit-detail-row
 * (dipakai Audit & Laporan Pengemudi) utk baris detail lipat -- bukan dialog
 * kedua, konsisten dgn pola "detail = baris meluas" yg sudah ada. */

.spk-asset-picker-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.spk-asset-count { font-size: 11px; }
.spk-asset-search { margin: 8px 0; }
.spk-asset-list {
  max-height: 260px; overflow-y: auto; margin: 0; padding: 8px;
  border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--bg);
}
.spk-asset-list .qr-opt:last-child { margin-bottom: 0; }

/* Detail SPK dibungkus <div class="audit-detail"> (spt dr-detail-list) --
 * di sini di-override balik ke sans+kiri rata krn isinya prosa (catatan,
 * tujuan) + daftar aset terstruktur, bukan satu blok JSON mentah. */
.spk-detail-h { margin: 0 0 6px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--fg-faint); }
.spk-detail-h + .spk-detail-h,
.spk-detail-assets + .spk-detail-h,
.spk-detail-grid + .spk-detail-h { margin-top: 12px; }
.spk-detail-grid { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 4px 12px; font-size: 11.5px; font-family: var(--sans); }
.spk-detail-grid dt { color: var(--fg-faint); }
.spk-detail-grid dd { margin: 0; color: var(--fg-dim); }
.spk-detail-assets { display: flex; flex-direction: column; gap: 5px; font-family: var(--sans); }
.spk-detail-asset-row {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  font-size: 11.5px; padding: 7px 10px;
  background: var(--bg-3); border: 1px solid var(--line); border-radius: var(--radius-sm);
}

/* Indikator "data ini otomatis segar" selagi SPK in_progress & baris terbuka
 * (lihat spk.js:startPoll/pollDetail) -- titik berdenyut sama semangat dgn
 * .pill.ok i di sidebar (status tautan gateway), skala lebih kecil krn ini
 * inline di dalam teks hint, bukan badge sendiri. */
.spk-live-hint { display: flex; align-items: center; gap: 6px; }
.spk-live-dot {
  width: 6px; height: 6px; border-radius: 50%; flex: 0 0 auto;
  background: var(--ok); box-shadow: 0 0 6px var(--ok);
  animation: spk-live-pulse 1.6s ease-in-out infinite;
}
@keyframes spk-live-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

/* ============================================================ responsif <860px */

@media (max-width: 860px) {
  #sidebar { display: none; }
  #topbar {
    display: flex; align-items: center; justify-content: space-between;
    gap: 12px; padding: 0 var(--sp-4); height: var(--topbar-h); flex: 0 0 auto;
    background: var(--bg-2); border-bottom: 1px solid var(--line);
  }
  #bottombar {
    display: flex; flex: 0 0 auto; height: var(--bottombar-h);
    background: var(--bg-2); border-top: 1px solid var(--line);
    padding-bottom: env(safe-area-inset-bottom, 0);
  }

  .view-page { padding: var(--sp-4); }
  .kpi-grid { grid-template-columns: repeat(2, 1fr); gap: var(--sp-3); }
  .dash-grid { grid-template-columns: 1fr; }
  .config-grid { grid-template-columns: 1fr; }
  .dr-score-grid { grid-template-columns: 1fr; }
  .dr-toolbar .field { min-width: 0; }
  .dr-field-user { flex-basis: 100%; }
  .dr-toolbar .btn { flex: 1 1 auto; }
  .recv-editor { grid-template-columns: 1fr; }
  .recv-map-stage { height: 56vw; max-height: 380px; }
  /* satu kolom = list sudah selebar layar; kotak gulung bersarang di dalam
   * gulungan halaman cuma menjebak jempol. Biarkan halaman yang menggulung. */
  .recv-list { max-height: none; overflow-y: visible; }
  .field-row { grid-template-columns: 1fr; }

  #main, #maps-shell { position: relative; }
  #panel {
    position: absolute; inset: 0 0 0 auto;
    width: min(88vw, 340px); flex-basis: auto;
    z-index: 800; transform: translateX(100%);
    transition: transform .22s ease;
    box-shadow: -8px 0 28px rgba(0,0,0,.4);
  }
  #panel.open { transform: none; }
  #panel-toggle { display: grid; place-items: center; }

  .mode-switch { top: 10px; left: 10px; }
  .mode-btn span, .mode-btn { font-size: 11.5px; }
  .follow-toggle { top: 10px; right: 10px; padding: 7px 11px; font-size: 11.5px; }
  .follow-toggle span { display: none; } /* layar sempit: ikon crosshair saja, cukup jelas di samping mode-switch */

  /* ---- tabel Aset/Audit -> kartu: driver pakai HP sbg satu-satunya layar --- */
  .table-wrap { overflow-x: visible; }
  .data-table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); }
  .data-table, .data-table tbody, .data-table tr { display: block; width: 100%; }
  .data-table tr { border-bottom: 1px solid var(--line); padding: 10px 2px; }
  .data-table tr:last-child { border-bottom: 0; }
  .data-table tr.table-empty-row, .data-table tr.audit-detail-row { padding: 0; }
  .data-table td {
    display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
    padding: 6px 2px; border-bottom: 0; white-space: normal; text-align: right;
  }
  .data-table .row-label { display: inline; color: var(--fg-faint); font-weight: 600; font-size: 11px; text-align: left; }
  .data-table td.col-actions { justify-content: flex-start; padding-top: 8px; }
  .data-table .cell-actions { justify-content: flex-start; }
  .data-table td.table-empty-row-cell { justify-content: center; }
  .audit-detail { text-align: left; }

  .toolbar { gap: var(--sp-2); }
  .search-box { flex-basis: 100%; }
  .select { flex: 1 1 auto; }

  .pagination { justify-content: center; }
  .pagination .pg-info { justify-content: center; width: 100%; }

  .session-name { max-width: 140px; }
}

@media (max-width: 520px) {
  .kpi-grid { grid-template-columns: 1fr 1fr; }
  .kpi-value { font-size: 22px; }

  /* di layar sempit dialog jadi lembar-bawah: dekat jempol, jangkauan penuh */
  .qr-dialog {
    width: 100vw; max-width: none; max-height: 92vh;
    margin: auto auto 0; padding: var(--sp-4);
    padding-bottom: calc(var(--sp-4) + env(safe-area-inset-bottom, 0px));
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  }
  .qr-actions { flex-wrap: wrap; }
  .qr-actions .btn { flex: 1 1 auto; }
  .head-actions { width: 100%; }
  .head-actions .btn { flex: 1 1 auto; }

  /* jempol > mouse: aksi baris tabel & kontrol filter naik ke standar sentuh 44px */
  .icon-btn, .chip, .pagination button, .tl-play { min-height: 44px; min-width: 44px; }
  .data-table .cell-actions { gap: 8px; width: 100%; }
  .data-table .cell-actions .icon-btn { flex: 1 1 auto; }
  .session-btn { min-height: 44px; }

  .enroll-add-box { flex-basis: 100%; }
  .enroll-add-form .btn { width: 100%; }
  .enroll-thumb { width: 56px; height: 56px; }
}
