*{box-sizing:border-box;margin:0;padding:0}
/*
 * Tema.
 *
 * Tiga keadaan, sama seperti menu Theme AnyLabeling (label_widget.py:715-750):
 * mengikuti sistem, terang, gelap. Pilihan eksplisit ditandai atribut
 * data-theme di <html>; tanpa atribut itu, prefers-color-scheme yang menentukan.
 *
 * SETIAP warna didefinisikan di :root polos. Warna yang hanya hidup di dalam
 * blok media atau [data-theme] tidak pernah berlaku pada keadaan "ikut sistem"
 * yang tidak menstempel apa pun — itu jalur paling mudah menghasilkan teks tema
 * satu di atas latar tema lain.
 */
:root{
  --ground:#f7f8fa; --panel:#ffffff; --line:#e3e7ec; --line2:#cdd4dc;
  --ink:#1b2430; --ink-dim:#5f6d7e; --ink-faint:#93a0ae;
  --ok:#1f9d8f; --warn:#c47a10; --stop:#d64a2c;
  --warn-bg:rgba(196,122,16,.10);   /* latar kotak peringatan */
  --aksi:#177f73; --aksi-ink:#ffffff;   /* tombol aksi utama, kontras di dua tema */
  --raised:#fbfcfd;        /* permukaan sedikit terangkat: tombol, kotak isian */
  --sunk:#eef1f5;          /* permukaan cekung: latar gambar, alur bilah progres */
  --hover:#f3f6f9;
  --on-ink:#ffffff;        /* teks di ATAS permukaan --ink */
  --lab-bg:#eaeef3; --lab-ink:#4a5a6b;
  --split-bg:#dbe7f5; --split-ink:#2a5b8f;
  --latar:#7398c4; --latar-lembut:#b9c9dc; --ok-lembut:#f2faf9; --ok-pudar:#bcd8d4;
  --bayang:20,30,45;       /* dipakai di dalam rgba() */
  --header:247,248,250;
  --pilih:#e7edf5;         /* baris terpilih di daftar objek & berkas */
  --bahaya-lembut:#fdecea;
  --tirai:12,16,22;        /* latar gelap di belakang dialog */
  /* Kanvas anotasi sengaja tetap gelap di KEDUA tema: latar terang membuat
     poligon berwarna sulit dinilai, dan AnyLabeling pun memakai latar gelap. */
  --kanvas:#2a2f36;
  /* Kontrol native (dropdown <select>, scrollbar, pemintal angka) ikut tema,
     bukan putih bawaan browser di atas aplikasi gelap -> daftar opsi select
     terbaca. */
  color-scheme:light;
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --ground:#12161c; --panel:#1a1f27; --line:#28303a; --line2:#39434f;
    --ink:#e6ebf2; --ink-dim:#a3aebd; --ink-faint:#788493;
    --ok:#43b3a4; --warn:#d59a3a; --stop:#e2705a;
    --warn-bg:rgba(213,154,58,.14);
    --aksi:#4ec2b2; --aksi-ink:#0c1a18;
    --raised:#212832; --sunk:#161b22; --hover:#252d38;
    --on-ink:#12161c;
    --lab-bg:#2a323d; --lab-ink:#b3bece;
    --split-bg:#233246; --split-ink:#9cc0e6;
    --latar:#5b7ba8; --latar-lembut:#3a4a5e; --ok-lembut:#16302d; --ok-pudar:#2f5b55;
    --bayang:0,0,0;
    --header:18,22,28;
    --pilih:#243040; --bahaya-lembut:#33201c; --tirai:0,0,0; --kanvas:#20242a;
    color-scheme:dark;
  }
}
:root[data-theme="dark"]{
  --ground:#12161c; --panel:#1a1f27; --line:#28303a; --line2:#39434f;
  --ink:#e6ebf2; --ink-dim:#a3aebd; --ink-faint:#788493;
  --ok:#43b3a4; --warn:#d59a3a; --stop:#e2705a;
  --warn-bg:rgba(213,154,58,.14);
  --aksi:#4ec2b2; --aksi-ink:#0c1a18;
  --raised:#212832; --sunk:#161b22; --hover:#252d38;
  --on-ink:#12161c;
  --lab-bg:#2a323d; --lab-ink:#b3bece;
  --split-bg:#233246; --split-ink:#9cc0e6;
  --latar:#5b7ba8; --latar-lembut:#3a4a5e; --ok-lembut:#16302d; --ok-pudar:#2f5b55;
  --bayang:0,0,0;
  --header:18,22,28;
  --pilih:#243040; --bahaya-lembut:#33201c; --tirai:0,0,0; --kanvas:#20242a;
  color-scheme:dark;
}
/* color-scheme:dark saja TIDAK cukup di sebagian Chrome/Linux: daftar opsi
   popup <select> native tetap mengikuti tema sistem (putih di atas aplikasi
   gelap, tak terbaca). Mewarnai <option> secara eksplisit adalah satu-satunya
   properti popup native yang dihormati Chrome. Pakai token tema supaya benar di
   terang (panel putih, teks gelap) maupun gelap (panel gelap, teks terang). */
option{background-color:var(--panel);color:var(--ink)}

body{background:var(--ground);color:var(--ink);
  font:14px/1.5 ui-sans-serif,-apple-system,"Segoe UI",Roboto,sans-serif}
.mono{font-family:ui-monospace,"JetBrains Mono","SF Mono",Menlo,monospace}

header{position:sticky;top:0;z-index:20;background:rgba(var(--header),.94);
  backdrop-filter:blur(8px);border-bottom:1px solid var(--line);
  /* safe-area: 0 di desktop & potret; hanya menambah di notch/landscape iPhone */
  padding:calc(14px + env(safe-area-inset-top,0px))
    calc(22px + env(safe-area-inset-right,0px)) 0
    calc(22px + env(safe-area-inset-left,0px))}
.top{display:flex;align-items:baseline;gap:14px;flex-wrap:wrap}

/* ---------------------------------------------------------------- brand mark
   Dulu kepala halaman cuma teks "HIGOLAB-DEV" menggantung sendiri di pojok.
   Sekarang didahului satu lambang: ubin teal dengan bingkai tipis di dalamnya
   — motif kotak anotasi, inti pekerjaan alat ini. Digambar murni dari token
   lewat ::before, jadi SATU aturan memasangnya di SEMUA halaman tanpa menyentuh
   tiap templat, dan ikut berganti warna di tema gelap tanpa penyesuaian. */
header h1{display:inline-flex;align-items:center;gap:9px}
header h1::before{content:"";flex:0 0 auto;width:25px;height:25px;
  border-radius:8px;background:var(--aksi);
  box-shadow:inset 0 0 0 2px var(--aksi), inset 0 0 0 4px var(--aksi-ink)}

/* Judul yang HANYA untuk pembaca layar: menjaga dokumen tetap punya <h1> dan
   urutan heading, tanpa menampilkan apa pun. Dipakai halaman /view, yang
   nama berkasnya sudah dieja utuh di kartu "Berkas" sehingga mengulanginya di
   kepala cuma menuh-menuhi. Lambang brand ::before ikut dimatikan di sini. */
.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}
header h1.sr-only::before{display:none}

/* ------------------------------------------------------------- gugus akun
   "masuk sebagai devuser / Tema / Kelola akun / Keluar" dulu EMPAT potong lepas
   sejajar baseline, yang di layar sempit melebar lalu membungkus ke baris
   kedua. Kini SATU tombol beridentitas (.whoami di dalam .akun-tombol) yang
   membuka menu .akun-menu berisi sisanya — meminjam bahasa titik-tiga kartu
   projek (.pmenu). Hook /logout, /akun, dan persistensi tema tak berubah.
   .whoami tetap namespace sendiri — BUKAN .akun-* milik daftar "kelola akun"
   (nama yang sama akan saling mewarisi gaya di dua tempat berbeda); karena itu
   kelas baru di sini memakai nama yang TIDAK ada di daftar itu (akun-tombol,
   akun-menu, akun-item, akun-tema, akun-cek, akun-kepala, akun-kepala-nama,
   akun-ident, akun-peran, akun-caret), bukan mendaur ulang akun-nama/akun-baris
   dan kawannya. */
.whoami{display:inline-flex;align-items:center;gap:8px;
  padding:3px 11px 3px 4px;border:1px solid var(--line);border-radius:999px;
  background:var(--panel)}
.whoami-ava{flex:0 0 auto;width:23px;height:23px;border-radius:50%;
  display:grid;place-items:center;background:var(--aksi);color:var(--aksi-ink);
  font-size:11px;font-weight:700;line-height:1;overflow:hidden}
/* Foto profil mengisi lingkaran tanpa gepeng (crop tengah sudah kotak di
   server, cover di sini menjaga tetap rapi apa pun ukuran render-nya). */
.whoami-ava img{width:100%;height:100%;object-fit:cover;display:block}
.whoami-nama{font-size:12.5px;font-weight:600;color:var(--ink-dim);
  max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* Pembungkus menjangkarkan menu absolut ke tombolnya; ia sendiri item flex di
   .top dan ikut didorong ke ujung kanan oleh .spacer. */
.akun{position:relative;display:inline-flex}
/* Tombol = pil .whoami yang kini bisa diklik. Reset bawaan <button> (font &
   warna diwarisi), lalu isyarat "bisa diklik": kursor, hover, dan garis fokus
   papan ketik. Saat menu terbuka tombolnya tetap menyala agar jelas sumber
   menunya yang mana. */
.akun-tombol{cursor:pointer;font:inherit;color:inherit;transition:.12s}
.akun-tombol:hover{border-color:var(--ink-faint);background:var(--hover)}
.akun-tombol:focus-visible{outline:2px solid var(--ok);outline-offset:1px}
.akun-tombol[aria-expanded="true"]{border-color:var(--ink-faint);
  background:var(--hover)}
.akun-caret{flex:0 0 auto;color:var(--ink-faint);font-size:10px;line-height:1;
  transition:transform .12s}
.akun-tombol[aria-expanded="true"] .akun-caret{transform:rotate(180deg);
  color:var(--ink-dim)}
/* .akun-menu = .pmenu (panel, tepi, radius, bayang, max-width/-height mobile)
   DIJANGKARKAN ke bawah-kanan tombol. Selektor turunan (.akun > .akun-menu)
   sengaja lebih spesifik dari .pmenu agar top/right-nya menang walau blok ini
   mendahului .pmenu di berkas. */
.akun > .akun-menu{top:calc(100% + 6px);right:0}
/* Kepala menu non-interaktif: mengeja nama utuh (yang di tombol bisa terpotong)
   plus lencana peran. .pmenu hanya menata a/button, jadi blok ini dibentuk
   sendiri. */
.akun-kepala{padding:6px 12px 7px;line-height:1.3}
.akun-kepala-nama{display:block;font-size:11.5px;color:var(--ink-dim);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.akun-ident{color:var(--ink);font-weight:600}
/* Peran = lencana kecil di bawah nama: info "kamu siapa", bukan aksi. Admin
   diberi warna aksi supaya hak istimewanya terbaca sekilas (relevan tepat saat
   "Kelola member" muncul di bawah); member tetap redup netral. */
.akun-peran{display:inline-block;margin-top:5px;padding:1px 7px;border-radius:999px;
  font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;
  border:1px solid var(--line2);color:var(--ink-faint);background:var(--sunk)}
.akun-peran[data-peran="admin"]{color:var(--aksi);border-color:var(--ok-pudar);
  background:var(--ok-lembut)}
/* Item tema berbaris: kolom penanda tetap + label. Penanda centang muncul pada
   yang aktif (aria-checked) — visibility, bukan display, supaya kolomnya tetap
   selebar itu dan labelnya tak bergeser saat pindah pilihan. */
.akun-item.akun-tema{display:flex;align-items:center;gap:8px}
.akun-cek{flex:0 0 auto;width:14px;height:14px;display:grid;place-items:center;
  color:var(--aksi);font-size:12px;line-height:1;visibility:hidden}
.akun-tema[aria-checked="true"]{color:var(--ink);font-weight:600}
.akun-tema[aria-checked="true"] .akun-cek{visibility:visible}
.akun-tema[aria-checked="true"] .akun-cek::before{content:"\2713"}
/* Item menu dapat fokus papan ketik langsung (navigasi panah), jadi butuh
   garis fokus sendiri; .pmenu hanya memberi latar hover. */
.akun-item:focus-visible{outline:2px solid var(--ok);outline-offset:-2px;
  background:var(--hover)}
/* iOS Safari mem-ZOOM otomatis saat fokus input ber-font <16px (dan tak zoom-out
   lagi). Di layar sentuh/sempit, paksa semua input ke 16px. Hanya berlaku
   mobile — tampilan desktop tak tersentuh. */
@media (max-width:900px){
  input,select,textarea{font-size:16px !important}
  /* Target sentuh lebih besar di layar sentuh (anjuran ≥44px). Pakai padding,
     bukan height, supaya aman untuk tombol inline-block. */
  .chip{padding-top:8px;padding-bottom:8px}
  .acts .btn{padding-top:8px;padding-bottom:8px}
  .hal-n{height:36px;min-width:36px}
}
/* Dropdown saringan kelas: min-width tetapnya bisa lebih lebar dari layar
   sangat sempit sehingga menggeser mendatar. Di HP kecil, biarkan menyempit. */
@media (max-width:360px){
  #kelas-isi{min-width:0}
}
/* Kepala halaman projek hanya memuat .top, jadi ia butuh padding bawahnya
   sendiri: di grid, ruang itu dulu datang dari bilah saringan terakhir yang
   tinggal di dalamnya. Dibatasi ke halaman projek supaya tinggi kepala di
   halaman Lihat — yang seluruh tata letaknya dihitung dari sisa tinggi layar
   — tidak ikut berubah. */
body.berprojek header{padding-bottom:13px}
/* Kumpulan alat di atas grid. Bilah kemajuan tidak perlu jarak atas lagi —
   yang di atasnya sekarang tepi kolom, bukan judul halaman. */
.alat{margin-bottom:2px}
/* Pendorong di baris saringan: memisahkan saringan (kiri) dari tindakan atas
   dataset (kanan) tanpa menuntut keduanya tahu ada berapa isi di sisi lain.
   Boleh menyusut sampai nol supaya baris ini tidak pernah membungkus hanya
   karena pendorongnya menuntut lebar. */
.saring-dorong{flex:1 1 0;min-width:0}

/* Tautan ke kanvas pada tiap ubin job. Dibuat kecil dan di bawah gambarnya,
   bukan menutupi gambar: kotak centang di atasnya yang dipakai berulang kali,
   dan tautan yang menutupi setengah ubin membuat memilih jadi salah klik. */
.jb-buka{display:block;text-align:center;font-size:11.5px;color:var(--aksi);
  text-decoration:none;padding:3px 0 1px}
.jb-buka:hover{text-decoration:underline}

/* Tombol borongan di kolom "Belum ditugaskan". Sengaja tidak sewarna aksi
   utama: memasukkan ke dataset itu keputusan, dan "Bagi tugas" tetap jalan
   yang lebih sering benar untuk gambar yang belum dikerjakan. */
.an-siap{display:block;width:100%;margin:0 0 6px;padding:7px 9px;
  border:1px solid var(--line);border-radius:7px;background:var(--panel);
  color:var(--ink-dim);font-size:12px;cursor:pointer;transition:.12s}
.an-siap:hover{background:var(--hover);color:var(--ink);border-color:var(--aksi)}
.an-siap[disabled]{opacity:.55;cursor:default}
.an-siap-semua{margin-bottom:10px}

/* Anggota yang belum kebagian tugas. Sewarna keterangan, bukan peringatan:
   ini keadaan yang wajar sebentar sesudah mengundang, bukan kesalahan. */
.an-tanpa-tugas{font-size:12.5px;color:var(--ink-dim);margin:0 0 12px;
  padding:8px 11px;border:1px solid var(--line);border-radius:8px;
  background:var(--sunk)}
.an-tanpa-tugas a{color:var(--aksi);text-decoration:none;margin-left:4px}
.an-tanpa-tugas a:hover{text-decoration:underline}
/* Peringatan lembut di kartu versi: bukan kesalahan, tetapi angka yang
   berbeda dari isi ZIP-nya harus disebut sebelum orang mengunduhnya. */
.v-hilang{font-style:normal;color:var(--warn)}
.an-hilang{margin:6px 0 0;font-size:11.5px;color:var(--warn);line-height:1.45}
/* Chip yang menunjuk KELUAR halaman ini. Dibedakan dari chip saringan supaya
   tidak terbaca sebagai saringan yang bisa dinyalakan di sini. */
.chip-lain{border-style:dashed;color:var(--ink-dim)}
.chip-lain:hover{border-style:solid;color:var(--aksi);border-color:var(--aksi)}

/* Penanda jatah di kartu projek yang mengundang kita. Yang punya jatah diberi
   warna aksi; yang belum tetap terbaca, karena "belum ada jatahmu" adalah
   keterangan yang justru paling dicari orang yang baru diundang. */
.plabel-tugas{color:var(--aksi);border-color:var(--aksi)}
h1{font-size:15px;font-weight:600;letter-spacing:.02em}
.path{font-size:12px;color:var(--ink-faint)}
/* Path folder boleh menyusut, dan itu yang menjaga baris kepala tetap satu
   baris. Ia satu-satunya isi kepala yang panjangnya tak terduga — di dev bisa
   90 karakter — dan sebagai teks yang tidak bisa menyusut ia mendorong tombol
   terakhir turun ke baris kedua.
//
   Yang dipotong ujung DEPANnya: yang penting dari sebuah path adalah ujung
   belakangnya. Kelasnya sendiri, bukan .path, karena .path juga dipakai teks
   pencacah seperti "38 dari 38 gambar tampil" — dan direction:rtl di sana
   membalik urutan katanya. */
.path-src{flex:1 1 120px;min-width:60px;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap;direction:rtl;text-align:left}

/* Bilah kemajuan: satu bilah membulat, satu warna per keadaan. Warnanya sama
   dengan garis tepi kartu di grid (.card .rail), supaya warna yang sama
   berarti hal yang sama di dua tempat.

   Dipakai halaman ANOTASI (.an-lajur), tempat pekerjaan pelabelan benar-benar
   diukur. Grid tidak lagi memakainya: di sana isinya justru gambar yang sudah
   selesai, jadi bilahnya selalu penuh. */
.lajur{flex:1 1 auto;display:flex;height:10px;border-radius:999px;
  overflow:hidden;background:var(--sunk);box-shadow:inset 0 0 0 1px var(--line)}
/* flex-basis:0 supaya lebarnya murni dari flex-grow, yaitu jumlah gambarnya.
   min-width supaya satu gambar di antara sepuluh ribu tetap terlihat: tanpa
   itu potongan sekecil itu dibulatkan menjadi nol piksel dan keadaannya lenyap
   dari bilah tanpa jejak. */
.lajur i{display:block;flex-basis:0;min-width:4px}
.lajur .p-ok{background:var(--ok)}
.lajur .p-warn{background:var(--warn)}
.lajur .p-bg{background:var(--latar)}
.lajur .p-stop{background:var(--stop)}

/* Titik warna keadaan. Inilah yang menghubungkan sebuah baris saringan ke
   potongan bilah kemajuan di atasnya, sehingga angkanya tidak perlu ditulis
   dua kali. Selektornya tidak lagi diikat ke .chip: keadaan kini juga muncul
   sebagai baris di dalam dropdown, dan di sana titiknya sama-sama diperlukan. */
.titik{display:inline-block;width:7px;height:7px;border-radius:50%;
  flex:0 0 auto;vertical-align:middle;position:relative;top:-1px}
.chip .titik{margin-right:6px}
.titik.t-ok{background:var(--ok)}
.titik.t-warn{background:var(--warn)}
.titik.t-bg{background:var(--latar)}
.titik.t-stop{background:var(--stop)}
/* Chip terpilih berlatar tinta; titiknya perlu tepian supaya tidak melebur. */
.chip[data-on] .titik{box-shadow:0 0 0 1.5px var(--on-ink)}

/* Tombol pembuka menu saringan (Semua keadaan, kelas, Tag, Bagian). Ikon
   status, label, angka, dan caret diberi jarak yang SAMA lewat flex-gap —
   bukan margin ad-hoc. Tanpa ini titik status menempel ke teks: titik oranye
   di "Semua keadaan" tampak dipaksakan, menempel di "keadaan" tanpa spasi. */
.menu > .chip{display:inline-flex; align-items:center; gap:7px}
.menu > .chip .titik{margin:0; top:0}
.kd-caret{font-size:9px; line-height:1; flex:0 0 auto; color:var(--ink-faint);
  margin-left:1px}
.menu > .chip[data-on] .kd-caret{color:var(--on-ink); opacity:.75}

.bar{display:flex;gap:8px;flex-wrap:wrap;align-items:center;padding:12px 0 13px}
/* Aturan display milik kita sendiri lebih kuat daripada [hidden] bawaan
   peramban, jadi `el.hidden = true` pada .chip atau .prog-baris tidak
   menyembunyikan apa pun — tombol "Hentikan" tetap terpampang walau
   pembelahannya sudah selesai. Ditegaskan sekali di sini, bukan ditambal
   satu per satu di tiap kelas. */
[hidden]{display:none !important}

.chip{border:1px solid var(--line2);background:transparent;color:var(--ink-dim);
  padding:5px 11px;border-radius:999px;font-size:12.5px;cursor:pointer;
  text-decoration:none;display:inline-block;transition:.12s}
.chip:hover{border-color:var(--ink-faint);color:var(--ink)}
.chip[data-on]{background:var(--ink);color:var(--on-ink);border-color:var(--ink);font-weight:600}
/* Chip yang tidak bisa dipakai. Dihilangkan sama sekali akan menyisakan
   teka-teki: tombolnya ada tadi, sekarang tidak, dan tidak ada yang
   menjelaskan kenapa. Yang menjelaskan justru title-nya. */
.chip[data-mati]{opacity:.45;cursor:not-allowed}
.chip[data-mati]:hover{border-color:var(--line2);color:var(--ink-dim)}
.chip b{font-variant-numeric:tabular-nums}
/* Keterangan kecil di samping label isian. Dulu hanya ada di label.css, yang
   cuma dimuat halaman kanvas, padahal kelas ini dipakai juga di halaman kelola
   akun. Di sana delapan potong teks yang mestinya kecil dan redup tampil
   sebagai teks biasa, dan tidak ada yang tampak salah sampai keduanya
   dibandingkan berdampingan. */
.halus{font-size:10.5px;color:var(--ink-faint);margin-top:10px;display:block;
  text-transform:none;letter-spacing:0;font-weight:400}
.spacer{flex:1}

main{padding:20px 22px 60px}
.grid{display:grid;gap:18px;
  grid-template-columns:repeat(auto-fill,minmax(210px,1fr))}
/* Grid Dataset: ADAPTIF. Kontrol "Per baris" tidak mengunci jumlah kolom —
   ia menetapkan LEBAR MINIMUM kartu (--kartu-min, dihitung app.js dari jumlah
   yang dipilih pada lebar grid saat itu), lalu auto-fill mengisi sebanyak yang
   muat. Akibatnya memperbesar/mengecilkan tampilan (Ctrl +/-) mengubah jumlah
   per baris SENDIRI: diperbesar -> kartu lebih sedikit & tampak besar,
   dikecilkan -> lebih banyak. Bawaan ~10. Lebar minimum itu dipatok JS supaya
   di layar sempit kartunya tidak jadi terlalu kecil. Bawaan ~7. */
#grid{grid-template-columns:repeat(auto-fill,minmax(var(--kartu-min,128px),1fr))}
/* Kartu TANPA kotak. Gaya Roboflow menaruh thumbnail sebagai tokoh utama di
   atas latar halaman yang tenang, bukan di dalam kartu berbingkai tebal dengan
   bayangan dan efek terangkat saat disorot. Jadi .card sendiri transparan tanpa
   garis/bayangan; yang terbaca sebagai objek adalah gambarnya. Jarak antarbaris
   kini datang dari gap grid (18px), menggantikan padding bawah kartu yang dulu. */
.card{background:transparent;border:0;border-radius:12px;
  position:relative;transition:.14s}
/* Bingkai gambar: sudut membulat + cincin tipis sebagai pengganti bingkai
   kartu, sekaligus acuan posisi semua hamparan (kotak centang, lencana).
   overflow memotong gambar ke sudut membulat; --sunk mengisi area letterbox
   yang disisakan object-fit:contain. Sorot cuma menegaskan cincinnya, tanpa
   mengangkat kartu. */
.kartu-gbr{position:relative;border-radius:12px;overflow:hidden;
  background:var(--sunk);box-shadow:inset 0 0 0 1px var(--line);
  transition:box-shadow .14s}
.card:hover .kartu-gbr{box-shadow:inset 0 0 0 1px var(--line2)}
.kartu-gbr a{display:block}
/* object-fit:contain DIPERTAHANKAN: cover memotong foto potret sampai isinya
   berubah arti (lihat .ug-gambar). Latar gambar pindah ke .kartu-gbr. */
.card img{width:100%;aspect-ratio:1;object-fit:contain;display:block}
/* Lencana keadaan. Dulu .rail adalah rel warna 3px di tepi KIRI kartu; kini
   chip kecil membulat di kiri-BAWAH gambar, gerak khas Roboflow. Backing beku
   senada chip kotak-centang supaya kontras di atas gambar terang MAUPUN gelap;
   titik di dalamnya yang membawa warna severity (ok/warn/stop/bg). Kata-katanya
   dibawa title & aria di templat -- rel lama tak punya teks sama sekali. */
.card .rail{position:absolute;left:7px;bottom:7px;z-index:2;
  width:20px;height:20px;border-radius:7px;display:grid;place-items:center;
  background:rgba(var(--header),.86);backdrop-filter:blur(4px);
  box-shadow:0 0 0 1px rgba(var(--bayang),.14)}
.card .rail::before{content:"";width:9px;height:9px;border-radius:50%;
  background:var(--ok)}
.card[data-s="warn"] .rail::before{background:var(--warn)}
.card[data-s="stop"] .rail::before{background:var(--stop)}
.card[data-s="bg"]   .rail::before{background:var(--latar)}
/* Nama berkas di bawah thumbnail, gaya Roboflow: SATU baris, di tengah, teduh.
   Tingginya TETAP (16px, ellipsis, tak membungkus) supaya menambah nama tidak
   merusak disiplin sama-tinggi per baris -- nama sepanjang apa pun memakan satu
   baris yang sama. Nama penuh tetap di title saat terpotong. */
.kartu-nama{height:16px;margin-top:8px;padding:0 4px;font-size:11px;
  line-height:16px;color:var(--ink-dim);text-align:center;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* Tinggi .meta DIKUNCI, bukan mengikuti isinya.
   Sebelumnya isi kotak ini boleh tumbuh: chip kelas membungkus ke baris kedua
   dan ketiga, baris temuan kadang dua baris, cap pelabel hanya ada di sebagian
   gambar. Di kisi, satu kartu yang tumbuh menaikkan SELURUH barisnya, jadi
   satu gambar berlabel lima kelas membuat empat tetangganya ikut menganga.
   Diukur di projek botol-kaleng-tetra-mlp-cup-1 dan paragon-kahf: tinggi kartu
   berbeda-beda 314/336/358 px pada jendela 1400px. Dengan tinggi tetap, setiap
   kartu 310,5 px berapa pun jumlah kelasnya.
   48 = 9 (padding atas) + 19 (satu baris chip) + 5 + 15 (satu baris temuan). */
/* Tinggi kotak keterangan DIHITUNG dari bagiannya, bukan diketik sebagai satu
   angka. Saat jalur label dinaikkan dari satu baris ke dua, angka tetapnya
   tertinggal dua kali berturut-turut (52px lalu 58px) dan baris temuan
   terpotong separuh di bawah tanpa ada yang menyadarinya sampai diukur.
   Dengan token ini, mengubah tinggi jalur label otomatis membawa serta tinggi
   kotaknya. */
.card{--pad-meta:7px;--h-labs:17px;--h-iss:20px;--h-jejak:14px}
/* Padding mendatar DIRAPATKAN ke tepi gambar (4px, dari 11px): tanpa kotak
   kartu, keterangan sejajar lurus dengan thumbnail di atasnya. Tinggi TETAP
   dari tokennya -- disiplin sama-tinggi per baris tak berubah. */
.meta{height:calc(var(--pad-meta) + var(--h-labs) + var(--h-iss) + var(--h-jejak));
  padding:var(--pad-meta) 4px 0;overflow:hidden}
/* Jejak edit: "kapan" lalu "oleh siapa", satu baris kecil, tinggi dicadangkan
   (--h-jejak) di SETIAP kartu supaya barisnya tetap sama tinggi walau sebagian
   gambar belum punya jejak. */
.kartu-jejak{height:var(--h-jejak);margin-top:3px;display:flex;gap:6px;
  align-items:baseline;font-size:9.5px;color:var(--ink-faint);
  white-space:nowrap;overflow:hidden}
.kartu-jejak .kj-wkt{font-variant-numeric:tabular-nums;flex:0 0 auto}
.kartu-jejak .kj-oleh{overflow:hidden;text-overflow:ellipsis;font-weight:600}
/* direction:rtl sengaja tidak dipakai: ia memindahkan titik ke depan sehingga
   "000036.jpg" terbaca "jpg.000036". */
/* SATU baris, ringkas: split (bila ada) + sampai dua nama kelas + pil "+N".
   Tingginya dikunci (17px) dan dicadangkan SELALU -- juga untuk gambar tanpa
   kelas -- supaya kartu di baris yang sama tetap sama tinggi. Dulu DUA baris
   (satu nama per baris); itu memakan tinggi kartu tanpa menambah informasi. */
.labs{margin-top:0;height:var(--h-labs);display:flex;flex-direction:row;
  align-items:center;gap:4px;overflow:hidden}
/* Di labs, jarak antarchip dari gap; margin kanan bawaan .split (untuk halaman
   Lihat) dinolkan, dan split tak ikut menyusut. */
.labs .split{flex:0 0 auto;margin-right:0}
/* Dibatasi ke dalam .labs. Tanpa pembatasan itu selektornya juga mengenai
   <body class="lab"> di halaman anotasi: badan halaman mewarisi latar dan
   warna teks pil kecil ini, dan itu sudah terjadi diam-diam sejak awal. */
.labs .lab{font-size:9.5px;line-height:1.5;padding:0 5px;border-radius:3px;
  background:var(--lab-bg);color:var(--lab-ink);
  flex:0 1 auto;min-width:0;white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis}
/* Pil penghitung tidak boleh ikut menyusut: kalau ia yang dipotong, orang
   tidak pernah tahu masih ada kelas lain. Yang mengalah nama kelasnya. */
.lab-sisa{flex:0 0 auto;font-size:9.5px;line-height:1.5;padding:0 4px;
  border-radius:3px;background:var(--sunk);color:var(--ink-dim);
  font-variant-numeric:tabular-nums;cursor:default}
/* Asal split pada ekspor bersplit. Perlu terlihat karena memperbaiki anotasi
   di `valid` punya arti berbeda dari memperbaikinya di `train`. */
.split{font-size:9.5px;font-weight:600;letter-spacing:.03em;text-transform:uppercase;
  padding:1px 5px;margin-right:5px;border-radius:3px;background:var(--split-bg);color:var(--split-ink)}
/* Satu baris juga, dengan alasan yang sama. Dua temuan yang digabung titik
   tengah gampang melewati lebar kartu; teks penuhnya ada di title. */
/* margin + height = --h-iss. Keduanya disebut di sini supaya jumlahnya bisa
   ditelusuri dari satu tempat kalau salah satunya diubah. */
.iss{margin-top:5px;height:15px;font-size:10.5px;color:var(--warn);
  line-height:1.35;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.iss.stop{color:var(--stop)}
/* Halaman tanpa SATU pun gambar bertemuan: baris temuan tak perlu dicadangkan,
   jadi tingginya dinolkan dan kartu merapat. Disetel di grid (dari ada_temuan),
   bukan per kartu, supaya kartu sebaris tetap sama tinggi. */
#grid.grid-rapat .card{--h-iss:0}
/* Dipisahkan tipis dari gambar lewat jarak atas; jarak bawah kini dari gap
   grid, bukan padding kartu. Mendatar dirapatkan ke tepi gambar. */
.acts{display:flex;gap:6px;padding:8px 4px 0}
.btn{flex:1;text-align:center;border:1px solid var(--line2);background:var(--raised);
  color:var(--ink-dim);border-radius:5px;padding:5px 0;font-size:11.5px;cursor:pointer;
  text-decoration:none;transition:.12s}
.btn:hover{border-color:var(--ok);color:var(--ok)}
.btn.pri:hover{border-color:var(--warn);color:var(--warn)}
/* Tombolnya pun tidak boleh membungkus. Kartu yang ditandai latar punya empat
   tombol, dan "Batal latar" pecah dua baris di kartu sempit — satu-satunya
   sisa penyebab kartu tidak sama tinggi setelah .meta dikunci. Teks yang
   terpotong tetap terbaca lewat title tombolnya. */
/* Gaya hantu yang tenang untuk SEMUA tombol kartu (Edit label, Lihat,
   Latar/Batal, ikon): transparan, garis tipis --line, sudut agak membulat.
   Menimpa latar --raised & garis --line2 dari .btn dasar supaya deret kartu
   terbaca ringan dan rapi, bukan deretan tombol padat -- tanpa menyentuh .btn
   di halaman kanvas yang memakai gaya dasarnya. */
.acts .btn{min-width:0;padding:6px 2px;white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis;background:transparent;border-color:var(--line);
  color:var(--ink-dim);border-radius:7px}
.acts .btn:hover{background:var(--hover);border-color:var(--line2);color:var(--ink)}
/* "Edit label" = tindakan pokok, dibuat MENONJOL: tombol TERISI penuh beraksen
   --aksi (teks --aksi-ink yang kontras di dua tema), berpasangan dengan "Lihat"
   yang tetap hantu netral -- pola primer/sekunder yang lazim dan langsung
   terbaca. Hanya SATU tombol terisi per kartu, jadi deret kartu tetap ringan.
   Latar/border DINYATAKAN ULANG di :hover supaya menang atas `.acts .btn:hover`
   (yang akan menyetel latar --hover); hover cukup digelapkan tipis. */
.acts .btn.pri{background:var(--aksi);border-color:var(--aksi);
  color:var(--aksi-ink);font-weight:600}
.acts .btn.pri:hover{background:var(--aksi);border-color:var(--aksi);
  color:var(--aksi-ink);filter:brightness(.94)}
/* Tombol teks DIBAGI RATA, termasuk yang utama.

   Sebelumnya yang utama mengambil seluruh sisa ruang, jadi "Edit label"
   tampil jauh lebih lebar daripada "Lihat" di sebelahnya padahal keduanya
   sama-sama satu tindakan atas gambar yang sama. Lebar yang berbeda terbaca
   sebagai tingkatan yang tidak ada: yang lebar tampak tombol utama dan yang
   sempit tampak pelengkap. Yang membedakan keduanya sudah cukup lewat warna
   .pri, dan itu tidak perlu diulang dengan ukuran.

   flex-basis 0, bukan auto: dengan auto pembagiannya masih mengikuti panjang
   teks masing-masing, dan "Edit label" tetap lebih lebar daripada "Lihat". */
.acts .btn{flex:1 1 0}
/* Tombol ikon selebar isinya saja, bukan ikut dibagi rata. */
.acts .btn-ikon{flex:0 0 auto;padding:6px 8px;display:grid;place-items:center}
.acts .btn-ikon .ik{display:block}

.hero{max-width:520px;margin:16vh auto 0;text-align:center;padding:0 24px}
.hero h2{font-size:22px;font-weight:600;letter-spacing:.01em}
.hero p{margin-top:10px;color:var(--ink-dim);font-size:13.5px;line-height:1.6}
.cta{margin-top:26px;background:var(--ink);color:var(--on-ink);border:0;
  padding:11px 26px;border-radius:7px;font-size:14px;font-weight:600;cursor:pointer;
  transition:.14s}
.cta:hover{background:var(--ok);color:var(--on-ink)}
.empty{padding:70px 0;text-align:center;color:var(--ink-faint)}
.empty p{margin-top:8px;font-size:13px}

/* ======================================================== halaman Lihat
 *
 * Halaman ini menjawab satu pertanyaan: "anotasi gambar ini benar atau
 * tidak?". Karena itu aturannya satu — gambar dan keterangannya WAJIB
 * terlihat bersamaan, tanpa digulir.
 *
 * Bentuk lamanya menaruh Temuan, Objek per kelas, dan Berkas DI BAWAH
 * gambar. Pada foto potret 2296x4080 yang memenuhi tinggi layar, ketiganya
 * berada di luar layar — satu-satunya hal yang mau diperiksa justru harus
 * dicari dengan menggulir.
 *
 * Tingginya diatur flexbox, bukan calc(100vh - 210px) seperti dulu. Angka
 * 210 itu tinggi kepala yang ditebak dengan tangan, dan langsung meleset
 * begitu nama berkas panjang membuat kepalanya membungkus jadi dua baris.
 */
/* height, BUKAN min-height. Tingginya harus PASTI dari atas ke bawah:
   .lh-panggung memakai aspect-ratio dengan height:100%, dan di rantai yang
   tingginya belum pasti itu melingkar — lebar menentukan tinggi lewat
   aspect-ratio, tinggi menentukan lebar lewat height:100%. Hasilnya
   panggung memilih tingginya sendiri dan halaman meluber 79 piksel.
   dvh dipakai karena 100vh di peramban ponsel lebih tinggi daripada layar
   yang benar-benar terlihat; baris vh ditinggal sebagai cadangan. */
body.lihat{display:flex;flex-direction:column;height:100vh;height:100dvh;
  overflow:hidden}
/* Kepala tidak perlu lengket di halaman yang memang tidak menggulir. */
body.lihat header{position:static;flex:0 0 auto}
/* flex-basis 0, bukan auto: dengan auto, tinggi isinya ikut menentukan
   pembagian ruang, dan itu persis kebiasaan yang mau dihilangkan. */
body.lihat main{flex:1 1 0;min-height:0;display:flex;padding:13px 22px 16px}

.lh-tata{height:100%;width:100%;
  display:flex;gap:14px;align-items:stretch;justify-content:center}

/* ------------------------------------------------------------ panggung */
/* aspect-ratio + height:100% membuat lebarnya mengikuti rasio fotonya
   sendiri. Kotak gelap selebar 1000px berisi foto selebar 530px terbaca
   sebagai kesalahan tata letak; kotak yang tepat seukuran fotonya terbaca
   sebagai bingkai. Rasionya ditanam dari templat lewat --arw/--arh. */
.lh-panggung{flex:0 1 auto;height:100%;aspect-ratio:var(--arw)/var(--arh);
  min-width:min(260px,100%);max-width:100%;
  display:grid;place-items:center;overflow:hidden;
  /* Latar gelap di KEDUA tema, alasannya sama dengan kanvas anotasi:
     poligon berwarna tidak bisa dinilai di atas latar terang, dan halaman
     ini pekerjaannya persis menilai poligon. */
  background:var(--kanvas);border:1px solid var(--line);border-radius:10px}
.lh-panggung img{max-width:100%;max-height:100%;width:auto;height:auto;
  display:block}

/* --------------------------------------------------------- kolom kanan */
.lh-sisi{flex:0 0 320px;max-width:100%;min-width:0;
  display:flex;flex-direction:column;gap:10px;min-height:0;
  overflow-y:auto;overscroll-behavior:contain}
.lh-kotak{flex:0 0 auto;background:var(--panel);border:1px solid var(--line);
  border-radius:10px;padding:11px 13px;
  box-shadow:0 1px 2px rgba(var(--bayang),.05)}
.lh-kotak h3{display:flex;justify-content:space-between;align-items:baseline;
  gap:8px;font-size:10.5px;text-transform:uppercase;letter-spacing:.09em;
  color:var(--ink-faint);font-weight:600;margin-bottom:8px}
.lh-kotak h3 span{text-transform:none;letter-spacing:0;font-size:11px;
  color:var(--ink-dim);font-variant-numeric:tabular-nums}

/* Vonis. Rel 3px meminjam bahasa .card .rail di grid, jadi warna yang sama
   berarti hal yang sama di dua halaman. */
.lh-vonis{position:relative;overflow:hidden;padding-left:16px}
.lh-vonis::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;
  background:var(--ok)}
.lh-vonis[data-s="warn"]::before{background:var(--warn)}
.lh-vonis[data-s="stop"]::before{background:var(--stop)}
.lh-vonis[data-s="bg"]::before{background:var(--latar)}
.lh-vonis b{display:block;font-size:14px;font-weight:600;color:var(--ink);
  line-height:1.35}
/* Tiap temuan jadi pilnya sendiri. Digabung " · " jadi satu kalimat
   panjang, jumlahnya baru ketahuan setelah dibaca seluruhnya. */
.lh-temuan{display:flex;flex-wrap:wrap;gap:5px;margin-top:8px}
.lh-temuan i{font-style:normal;font-size:11px;padding:2px 8px;
  border-radius:999px;background:var(--warn-bg);color:var(--warn)}
.lh-vonis[data-s="stop"] .lh-temuan i{background:var(--bahaya-lembut);
  color:var(--stop)}
/* Gambar yang SENGAJA dikosongkan bukan cacat. Bentuk lama mewarnainya
   oranye semata karena daftar temuannya tidak kosong, sehingga pekerjaan
   yang sudah benar tampak seperti peringatan. */
.lh-vonis[data-s="bg"] .lh-temuan i{background:var(--lab-bg);color:var(--lab-ink)}
.lh-sepi{margin-top:6px;font-size:11.5px;color:var(--ink-faint);line-height:1.5}

/* Objek per kelas. Petak warnanya dari filter |warnakelas — rumus yang sama
   dengan yang dipakai menggambar mask, jadi ia jembatan antara "ada bercak
   warna di foto" dan "namanya apa". */
.lh-kelas{list-style:none}
.lh-kelas li{display:flex;align-items:center;gap:8px;padding:5px 0;
  font-size:12.5px;border-bottom:1px solid var(--line)}
.lh-kelas li:last-child{border-bottom:0}
.lh-kelas .petak{flex:0 0 auto;width:11px;height:11px;border-radius:3px;
  background:var(--c,var(--line2));
  box-shadow:0 0 0 1px rgba(var(--bayang),.22)}
.lh-kelas .nm{flex:1 1 auto;min-width:0;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.lh-kelas .n{flex:0 0 auto;color:var(--ink-dim);font-variant-numeric:tabular-nums}

/* Berkas. overflow-wrap:anywhere, BUKAN word-break:break-all — break-all
   memotong "paragon" jadi "para|gon" walau barisnya masih muat. */
.lh-berkas{display:grid;grid-template-columns:auto minmax(0,1fr);gap:5px 10px;
  font-size:11.5px;line-height:1.45}
.lh-berkas dt{color:var(--ink-faint);white-space:nowrap}
.lh-berkas dd{color:var(--ink-dim);min-width:0;overflow-wrap:anywhere}
.lh-berkas dd.nm{color:var(--ink)}

/* --------------------------------------------------------------- kepala */
/* 12,5px, bukan 15px tebal. Yang panjang di sini 24 karakter hash Roboflow
   yang tidak pernah dibaca; nama utuhnya tetap ada di title dan di panel
   Berkas, jadi tidak ada yang hilang — ia hanya berhenti berteriak. */
.lh-nama{flex:0 1 auto;min-width:0;max-width:46ch;font-size:12.5px;
  font-weight:600;letter-spacing:0;color:var(--ink);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lh-nav{display:flex;align-items:center;gap:6px}
.lh-posisi{font-size:12px;color:var(--ink-dim);font-variant-numeric:tabular-nums;
  min-width:9ch;text-align:center}
.lh-posisi b{color:var(--ink);font-weight:600}
.lh-ringkas{font-size:12px;color:var(--ink-dim)}
.lh-ringkas b{color:var(--ink);font-weight:600;font-variant-numeric:tabular-nums}
/* Menggantikan style="opacity:.35" sebaris, yang memudarkan tombol tapi
   tidak pernah mematikan kliknya. */
.chip.mati{opacity:.38;pointer-events:none}
/* Meter posisi selebar kepala: satu-satunya cara membaca "masih jauh atau
   hampir habis" tanpa membagi 6/476 di kepala sendiri. */
.lh-meter{height:2px;margin:0;background:var(--sunk)}
.lh-meter i{display:block;height:100%;background:var(--aksi)}

/* -------------------------------------------- toolbar Lihat: SATU baris ringkas
   Dulu dua baris pil besar (.top + .bar) yang memakan tempat. Kini satu bilah:
   navigasi jadi tombol IKON, aksi UTAMA (Edit/Labeli) beraksen, sisanya tenang,
   keterangan jadi teks halus sebelum menu akun. Di sempit, barisnya menggeser
   (overflow-x) — bukan membungkus jadi dua baris lagi. */
.lh-head{position:sticky;top:0;z-index:20;background:var(--ground);
  border-bottom:1px solid var(--line)}
.lh-bar{display:flex;align-items:center;gap:7px;padding:9px 16px;
  overflow-x:auto;scrollbar-width:none}
.lh-bar::-webkit-scrollbar{height:0}
.lh-btn{display:inline-flex;align-items:center;gap:6px;height:32px;padding:0 11px;
  border-radius:8px;border:1px solid var(--line);background:var(--raised);
  color:var(--ink-dim);font-size:13px;font-weight:500;line-height:1;cursor:pointer;
  text-decoration:none;white-space:nowrap;flex:none;
  transition:background .12s,color .12s,border-color .12s}
.lh-btn:hover{background:var(--hover);color:var(--ink);border-color:var(--line2)}
.lh-btn:focus-visible{outline:2px solid var(--aksi);outline-offset:2px}
.lh-btn svg{width:15px;height:15px;flex:none}
.lh-ikon{padding:0;width:32px;justify-content:center}
.lh-ghost{border-color:transparent;background:transparent}
.lh-ghost:hover{background:var(--hover);border-color:transparent}
/* Aksi UTAMA = satu-satunya tombol beraksen (teori: satu aksi primer per layar). */
.lh-utama{background:var(--aksi);color:var(--aksi-ink);border-color:transparent;
  font-weight:600}
.lh-utama:hover{background:var(--aksi);color:var(--aksi-ink);border-color:transparent;
  filter:brightness(1.08)}
.lh-btn.mati{opacity:.4;pointer-events:none}
.lh-sep{width:1px;height:20px;background:var(--line);flex:none;margin:0 1px}
.lh-bar .lh-posisi{min-width:0;padding:0 5px;white-space:nowrap}
.lh-dim{font-size:12px;color:var(--ink-faint);font-variant-numeric:tabular-nums;
  flex:none}
.lh-split{flex:none}
/* Di layar sempit, label teks tombol navigasi + ukuran piksel menepi lebih dulu;
   ikon & aksi utama tetap. */
@media (max-width:640px){
  .lh-back span,.lh-dim{display:none}
  .lh-bar{gap:6px;padding:8px 12px}
}

/* ----------------------------------------------------------- layar sempit
   Dua kolom di bawah 900px menghasilkan dua kolom yang sama-sama tidak
   terbaca; di sini halaman kembali boleh menggulir dengan sadar. */
@media (max-width:900px),(max-height:520px){
  body.lihat{height:auto;min-height:0;overflow:visible}
  body.lihat main{display:block;flex:0 0 auto}
  .lh-tata{flex-direction:column;height:auto}
  .lh-panggung{width:100%;height:auto;aspect-ratio:auto;max-height:60vh;
    max-height:60dvh;flex:0 0 auto}
  .lh-panggung img{max-height:60vh;max-height:60dvh}
  .lh-sisi{flex:1 1 auto;width:100%;overflow:visible}
  .lh-nama{max-width:100%}
  .lh-meter{margin:0}
}

/* ------------------------------------------------------------- tema gelap
   Seluruh warnanya token; yang perlu diulang hanya bayangan, karena
   bayangan halus di atas --panel gelap tidak terlihat dan kotaknya
   kehilangan pemisah dari latar. */
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .lh-kotak{box-shadow:none}
}
:root[data-theme="dark"] .lh-kotak{box-shadow:none}
table{width:100%;border-collapse:collapse;font-size:12.5px}
td{padding:3px 0;border-bottom:1px solid var(--line)}
td:last-child{text-align:right;color:var(--ink-dim);font-variant-numeric:tabular-nums}
.toast{position:fixed;left:50%;bottom:calc(26px + env(safe-area-inset-bottom,0px));transform:translateX(-50%);
  background:var(--ink);color:var(--on-ink);padding:9px 18px;border-radius:6px;
  font-size:13px;font-weight:500;opacity:0;pointer-events:none;transition:.2s;z-index:50;
  box-shadow:0 4px 14px rgba(var(--bayang),.22)}
.toast[data-on]{opacity:1}

/* login, pemilih dataset, upload */
.who{font-size:12px;color:var(--ink-faint)}
.who b{color:var(--ink-dim);font-weight:600}
.card-form{max-width:390px;margin:14vh auto 0;background:var(--panel);
  border:1px solid var(--line);border-radius:10px;padding:26px 26px 22px;
  box-shadow:0 4px 20px rgba(var(--bayang),.06)}
.card-form h2{font-size:18px;font-weight:600;margin-bottom:4px}
.card-form .sub{color:var(--ink-dim);font-size:13px;margin-bottom:18px}
.login-ingat{display:flex;align-items:center;gap:7px;font-size:13px;
  color:var(--ink-dim);margin:4px 0 12px;cursor:pointer}
.login-ingat input{margin:0}
/* Gaya label FORMULIR (login, nama dataset). Dulu ini aturan `label{}` polos,
   sehingga mengenai SETIAP <label> di seluruh aplikasi — termasuk centang di
   menu View, baris flag, dan saringan kelas, yang semuanya jadi huruf besar
   tanpa diminta. `.menu-isi label` bahkan sudah lama memasang penawar
   `text-transform:none` untuk melawannya. Sekarang dibatasi ke yang memang
   menginginkannya. */
.form-lbl{display:block;font-size:11.5px;text-transform:uppercase;letter-spacing:.07em;
  color:var(--ink-faint);font-weight:600;margin:12px 0 5px}
/* Tipe input didaftar satu per satu, dan itu jebakannya: menambah
   <input type="email"> di halaman daftar membuatnya tampil sebagai kotak
   bawaan peramban di tengah kotak-kotak lain yang sudah bergaya.
   `search` dan `number` sengaja TIDAK ikut. Keduanya sudah punya gaya
   sendiri lewat kelas (.cari-nama, .split-atur input), dan pemilih
   [type=...] mengalahkan pemilih kelas — memasukkannya ke sini membuat
   kotak cari melar memenuhi barisnya. */
input[type=text],input[type=password],input[type=email],
input[type=url]{width:100%;border:1px solid var(--line2);
  background:var(--raised);color:var(--ink);border-radius:6px;padding:9px 11px;font-size:13.5px;
  font-family:inherit}
input:focus{outline:0;border-color:var(--ok)}
.card-form .cta{width:100%;margin-top:20px}
.bad{margin-top:14px;color:var(--stop);font-size:12.5px}

.pick{max-width:1360px;margin:0 auto;padding:0}
.pick h2{font-size:20px;font-weight:600;text-align:center}
.pick .sub{text-align:center;color:var(--ink-dim);font-size:13.5px;margin-top:8px}
.panel{background:var(--panel);border:1px solid var(--line);border-radius:9px;
  padding:16px 18px;margin-top:16px}
.panel h3{font-size:11px;text-transform:uppercase;letter-spacing:.09em;
  color:var(--ink-faint);font-weight:600;margin-bottom:11px}
.dslist{display:flex;flex-direction:column;gap:1px}
.ds{display:flex;align-items:center;gap:10px;padding:9px 11px;border-radius:6px;
  border:1px solid transparent;cursor:pointer;text-decoration:none;color:var(--ink);
  font-size:13.5px;transition:.12s}
.ds:hover{background:var(--hover);border-color:var(--line)}
.ds .n{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ds .c{font-size:11.5px;color:var(--ink-faint);font-variant-numeric:tabular-nums}
.row{display:flex;gap:8px;align-items:flex-end}
.row input{flex:1}
.row .cta{width:auto;margin-top:0;padding:9px 18px;font-size:13px}
.drop{border:1.5px dashed var(--line2);border-radius:8px;padding:22px 16px;
  text-align:center;color:var(--ink-dim);font-size:13px;cursor:pointer;transition:.14s}
.drop:hover,.drop[data-over]{border-color:var(--ok);background:var(--ok-lembut);color:var(--ink)}
/* Bilah progres dipakai bersama seluruh aplikasi. Tebal dan membulat penuh
   supaya terbaca sebagai "sedang berjalan" dari sudut mata, bukan garis tipis
   yang mudah terlewat. */
.prog{height:10px;background:var(--sunk);border-radius:99px;overflow:hidden;
  margin-top:12px;display:none}
.prog[data-on]{display:block}
.prog i{display:block;height:100%;width:0;background:var(--aksi);
  border-radius:99px;transition:width .18s}
/* Tanpa persentase yang bisa dihitung, bilahnya tetap harus bergerak. Diam
   sama saja dengan mati, dan orang menutup tabnya karena mengira gagal. */
.prog[data-tak-tentu] i{width:35% !important;animation:meluncur 1.15s ease-in-out infinite}
@keyframes meluncur{0%{margin-left:-35%}100%{margin-left:100%}}
/* Setelah penyalinan selesai, isi salinannya masih dipindai — bilahnya sudah
   penuh dan berhenti bergerak, dan itu terbaca sebagai macet. Denyut menandai
   bahwa yang berhenti hanya angkanya, bukan pekerjaannya. */
.prog[data-tahap="pindai"] i{animation:denyut 1.1s ease-in-out infinite}
@keyframes denyut{0%,100%{opacity:1}50%{opacity:.35}}
.upnote{margin-top:9px;font-size:12px;color:var(--ink-dim);text-align:center}

/* menu Ekspor di grid */
.menu{position:relative;display:inline-block}
/* Menu dijangkarkan ke tepi KANAN tombol. Tombol Ekspor duduk di ujung kanan
   bar, jadi dengan left:0 menunya meluber keluar layar dan terpotong. max-width
   menjaganya tetap masuk pada jendela sempit. */
.menu-isi{display:none;position:absolute;right:0;top:28px;z-index:40;
  background:var(--panel);border:1px solid var(--line2);border-radius:8px;
  padding:7px 0;min-width:250px;max-width:calc(100vw - 28px);
  /* Menu Ekspor memuat format, rasio, ringkasan, dan hasil pembelahan
     sekaligus; pada layar pendek isinya melewati bawah jendela dan bagian
     akhirnya tidak bisa dijangkau sama sekali. */
  /* 150px = tinggi header + bar tempat tombolnya duduk (~118px) plus
     sedikit jarak ke tepi bawah. Diukur dengan e2e, bukan dikira. */
  max-height:calc(100vh - 150px);max-height:calc(100dvh - 150px);overflow-y:auto;overscroll-behavior:contain;
  box-shadow:0 8px 24px rgba(var(--bayang),.16)}
.menu-isi a{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.menu[data-buka] .menu-isi{display:block}
.menu-isi a{display:block;padding:5px 12px;font-size:12.5px;color:var(--ink);
  text-decoration:none}
/* Menu yang tombolnya duduk di tepi KIRI bar harus dijangkarkan ke kiri juga.
   Dengan right:0 milik menu Ekspor, ia terbuka ke luar layar dan isinya
   terpotong — persis yang terjadi pada saringan kelas. */
.menu-kiri > .menu-isi{right:auto;left:0}
.menu-isi a:hover{background:var(--hover)}
.menu-judul{display:block;font-size:10px;text-transform:uppercase;
  letter-spacing:.09em;color:var(--ink-faint);font-weight:600;padding:3px 12px}
.menu-pisah{display:block;height:1px;background:var(--line);margin:5px 0}
.menu-catatan{display:block;padding:4px 12px;font-size:11.5px;
  color:var(--ink-dim);line-height:1.5;white-space:normal}
.split-atur{display:flex;align-items:center;gap:6px;padding:4px 12px;
  font-size:12.5px;color:var(--ink-dim);flex-wrap:wrap}
.split-atur input{width:56px;border:1px solid var(--line2);background:var(--raised);
  border-radius:5px;padding:3px 6px;font-size:12.5px;font-family:inherit;
  color:var(--ink);text-align:center}
.menu-catatan b{color:var(--ink);font-weight:600}

/* --------------------------------------------------------- baris saringan
   Satu baris untuk semua yang menyempitkan daftar: cari nama, keadaan, kelas,
   tag, lalu urutan. Sebelumnya isinya tersebar di tiga baris — deretan chip
   keadaan, baris urut/cari, dan baris dropdown — sehingga tidak ada yang
   terbaca sebagai satu kelompok, dan tiga baris kontrol mendorong kartu
   pertama 80 piksel lebih jauh ke bawah. */
.bar-saring{gap:8px;padding:10px 0 12px}
/* Baris kontrol dataset-lewat-path. Rapat ke baris saringan di atasnya —
   keduanya kontrol atas daftar yang sama — dan tidak pernah muncul di projek. */
.ds-luar{gap:8px;padding:0 0 12px;margin-top:-4px}
/* Kalimat "Tidak bisa ditambahi: ..." memuat alasan sepanjang apa pun, dan
   terukur memakan 1329 px dari 1441: sisanya membungkus ke baris kedua,
   pendorong ikut turun, dan menu Ekspor yang mendarat di kiri membuka ke luar
   tepi layar (left=-48).

   flex-basis KECIL, bukan auto. Dengan flex-wrap:wrap sebuah baris membungkus
   LEBIH DULU daripada menyusut, jadi `flex:0 1 auto` tidak menolong sama
   sekali: basis-nya tetap selebar teksnya, tombolnya tetap terdorong turun,
   dan penyusutan baru terjadi di dalam baris yang sudah telanjur pecah.
   Dengan basis 120 px ia tidak pernah menuntut lebih dulu, lalu tumbuh
   mengisi sisa ruang. Pola yang sama dipakai .path-src di bar atas, dan
   alasannya sama persis. Kalimat utuhnya tetap ada di title. */
.ds-luar .path{flex:1 1 120px;min-width:60px;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.saring-cari{display:flex;align-items:center;gap:8px;flex:0 1 auto}
.cari-nama{border:1px solid var(--line2);background:var(--raised);color:var(--ink);
  border-radius:999px;padding:5px 12px;font-size:12.5px;
  flex:0 1 280px;min-width:160px;font-family:inherit}
.cari-nama:focus{outline:2px solid var(--ok);outline-offset:-1px}

/* Pemisah kelompok: garis 1px, bukan jarak besar — jaraknya sudah dipakai
   .bar untuk memisah kontrol satu sama lain. */
.saring-pisah{flex:0 0 auto;width:1px;height:18px;background:var(--line)}
/* Tombol "Filter" (lipat saringan) hanya untuk HP; di desktop seluruh saringan
   muat satu baris, jadi tak perlu. Dinyalakan di blok @media (max-width:560px). */
.saring-lipat{display:none}

/* Pil urut: label statis + select tanpa tepi, satu bentuk dengan .chip.
   Ia sengaja TIDAK pernah menyala: mengurutkan bukan menyaring. */
.saring-urut{display:inline-flex;align-items:center;gap:6px;height:31px;
  padding:0 9px 0 11px;border:1px solid var(--line2);border-radius:999px;
  transition:.12s}
.saring-urut:hover{border-color:var(--ink-faint)}
.saring-urut:focus-within{border-color:var(--ok)}
.saring-urut .halus{margin:0;font-size:11.5px}
.saring-urut select{border:0;background:transparent;color:var(--ink-dim);
  font-family:inherit;font-size:12.5px;appearance:none;cursor:pointer;
  max-width:200px;padding:0 13px 0 0}
.saring-urut select:focus{outline:0}
.saring-urut::after{content:"\25be";margin-left:-11px;font-size:11px;
  color:var(--ink-faint);pointer-events:none}

/* Satu tindakan untuk membatalkan semuanya sekaligus. Tanpa ini, saringan yang
   berlaku hanya terbaca dari tombol yang tertutup, dan mengosongkannya berarti
   membuka tiga dropdown satu per satu. --aksi, bukan --stop: membatalkan
   saringan bukan tindakan berbahaya. */
.saring-bersih{margin-left:auto;color:var(--aksi);border-color:var(--aksi)}
.saring-bersih:hover{background:var(--ok-lembut);color:var(--aksi);
  border-color:var(--aksi)}

/* ------------------------------------------------------- dropdown Keadaan
   Isinya TAUTAN, bukan centang: keadaan saling eksklusif, jadi satu klik sudah
   satu keputusan dan tombol "Terapkan" di situ cuma klik mubazir. */
#keadaan-isi{min-width:258px;padding:6px 0}
.menu-isi a.kd-baris{display:flex;align-items:center;gap:9px;margin:0 6px;
  padding:7px 8px;border-radius:7px;font-size:12.5px;color:var(--ink-dim);
  white-space:normal}
.menu-isi a.kd-baris:hover{background:var(--hover);color:var(--ink)}
.kd-baris span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.kd-baris b{flex:0 0 auto;font-size:11px;font-weight:600;min-width:24px;
  text-align:center;padding:1px 6px;border-radius:99px;color:var(--ink-faint);
  background:var(--sunk);font-variant-numeric:tabular-nums}
.menu-isi a.kd-baris[data-on]{background:var(--pilih);color:var(--ink);
  font-weight:600}
.kd-baris[data-on] b{color:var(--ink);background:var(--panel)}
/* Titik kosong untuk baris tanpa warna keadaan (Semua, Tugasku): tempatnya
   tetap dipakai supaya nama-namanya rata satu garis. */
.titik.t-kosong{background:transparent;box-shadow:inset 0 0 0 1px var(--line2)}

/* ------------------------------------------------------------- paginasi
   Lengket di bawah: pada 1000 per halaman, bar ini berada puluhan layar dari
   puncak, dan satu-satunya jalan ke halaman berikutnya adalah menggulir
   sampai habis. */
.hal{position:sticky;bottom:0;z-index:15;
  display:flex;align-items:center;gap:14px;flex-wrap:wrap;
  margin:18px -8px 0;padding:11px 8px;
  background:var(--ground);border-top:1px solid var(--line)}
.hal-pp{display:flex;align-items:center;gap:7px;font-size:12px;
  color:var(--ink-faint)}
.hal-pp select{border:1px solid var(--line2);background:var(--raised);
  color:var(--ink-dim);border-radius:999px;padding:4px 9px;
  font-family:inherit;font-size:12.5px;cursor:pointer}
.hal-pp select:focus{outline:2px solid var(--ok);outline-offset:1px}
.hal-info{font-size:12px;color:var(--ink-dim);font-variant-numeric:tabular-nums}
.hal-info em{font-style:normal;color:var(--ink-faint)}
/* Jumlah objek: keterangan yang sama pentingnya dengan jumlah gambar di
   sebelahnya, tetapi bukan yang dicari lebih dulu — jadi diredupkan, bukan
   diberi baris sendiri. */
.hal-objek{color:var(--ink-faint)}
.hal-nav{display:flex;align-items:center;gap:4px;margin-left:auto}
/* Radius 7px, bukan 999px: nomor halaman bukan saringan, dan bentuk yang
   berbeda mencegahnya terbaca sebagai chip yang bisa dinyalakan. */
.hal-n{min-width:30px;height:28px;padding:0 7px;display:inline-grid;
  place-items:center;border:1px solid var(--line2);border-radius:7px;
  font-size:12.5px;color:var(--ink-dim);text-decoration:none;
  font-variant-numeric:tabular-nums;transition:.12s}
.hal-n:hover{border-color:var(--ink-faint);color:var(--ink)}
.hal-n[data-on]{background:var(--ink);color:var(--on-ink);border-color:var(--ink);
  font-weight:600;pointer-events:none}
/* Panah di ujung DIREDUPKAN, bukan dihilangkan: barisnya harus tetap sepanjang
   itu supaya tombol "1" tidak bergeser tiap kali pindah halaman. */
.hal-n[data-mati]{opacity:.38;pointer-events:none}
.hal-gap{padding:0 1px;color:var(--ink-faint);font-size:12.5px;user-select:none}
.hal-loncat{display:flex;align-items:center;gap:6px;font-size:12px;
  color:var(--ink-faint)}
.hal-loncat input{width:62px;height:28px;text-align:center;
  border:1px solid var(--line2);background:var(--raised);color:var(--ink);
  border-radius:7px;padding:0 6px;font-family:inherit;font-size:12.5px;
  font-variant-numeric:tabular-nums}
.hal-loncat input:focus{outline:0;border-color:var(--ok)}
/* Tautan halaman membawa #grid. Tanpa scroll-margin, baris kartu pertama
   mendarat di bawah header yang lengket. */
.grid{scroll-margin-top:72px}

/* Di HP, kontrol "Per baris" TIDAK berlaku: grid mengabaikan --kartu-min (yang
   dipatok JS) dan memakai minimum tetap, lalu "Per baris" disembunyikan karena
   grid menyesuaikan diri sendiri. Patokannya dirapatkan ke ~102px (dulu 150px):
   pemakai merasa 2 ubin besar boros ruang dan mau yang padat, jadi di HP biasa
   (~360-400px) kini muat 3 ubin. Ubin tetap PERSEGI (aspect-ratio 1); object-
   fit:contain menjaga seluruh foto terlihat. */
@media (max-width:560px){
  /* Grid HP: padat. Permintaan pemakai 3x3 (3 baris x 3 kolom) terlihat, dan
     4x4 kalau muat. minmax ~78px memberi 4 kolom di HP biasa (~360-414px), dan
     gap 6px; dengan nav + toolbar yang sudah diringkas, 4-6 baris terlihat
     tanpa gulir -> 4x4 tercapai. aspect-ratio 1 menjaga ubin persegi; object-
     fit:contain tetap. */
  #grid{grid-template-columns:repeat(auto-fill,minmax(78px,1fr))}
  .grid{gap:6px}
  .card img{aspect-ratio:1;height:auto}
  /* Kartu HP = GAMBAR SAJA (gaya Roboflow, sesuai tampilan prod): nama berkas,
     chip kelas/isu, dan baris tombol disembunyikan supaya grid padat & bersih
     di 3 kolom. Thumbnail sudah jadi tautan ke /view (lihat index.html), jadi
     mengetuk gambar tetap membukanya; tombol Labeli ada di halaman Lihat.
     Kotak centang borongan + titik keadaan tetap (keduanya di dalam gambar). */
  .card .kartu-nama,.card .meta,.card .acts{display:none}
  /* "Per baris" -- cuma span ini, bukan pil Urut yang berbagi kelas. */
  .saring-urut:has(#kartu-kolom){display:none}
  /* Baris saringan dirapatkan: jarak & padding lebih kecil, chip membungkus
     (flex-wrap sudah dari .bar). Semua kontrol tetap terjangkau. */
  .bar-saring{gap:6px;padding:8px 0 10px}
  .bar-saring .chip{padding:5px 9px}
  .saring-urut{height:29px;padding:0 7px 0 9px}
  /* Toolbar saringan HP: tombol "Filter" melipat kontrol sekunder. Default
     (terlipat): hanya pencarian + Filter (+ Bersihkan, kalau ada saringan
     aktif) yang tampil -> toolbar cuma 1-2 baris, grid naik cepat. Ditekan ->
     .bar-saring.terbuka, semua chip muncul (membungkus seperti biasa). "Per
     baris" tetap tersembunyi (aturan :has(#kartu-kolom) di atas). */
  .saring-lipat{display:inline-flex;align-items:center;gap:5px}
  .bar-saring:not(.terbuka) .menu,
  .bar-saring:not(.terbuka) .saring-urut,
  .bar-saring:not(.terbuka) #unduh-asli,
  .bar-saring:not(.terbuka) .saring-pisah,
  .bar-saring:not(.terbuka) .saring-dorong{display:none}
  .bar-saring.terbuka{align-items:flex-start}
  .saring-lipat .kd-caret{transition:transform .15s}
  .bar-saring.terbuka .saring-lipat .kd-caret{transform:rotate(180deg)}
  /* Bar navigasi projek (Unggah/Anotasi/Dataset/Versi/Training): di 900px ia
     membungkus ke 2-3 baris dan makan tinggi besar ("nav bar gede banget"). Di
     HP dijadikan SATU baris pil yang digeser ke samping, seperti tab mobile
     Roboflow -- jauh lebih pendek. Ikon disembunyikan (label + angka sudah
     cukup, dan menghematnya memuat lebih banyak pil per layar). Kepala projek
     (sampul + nama) ikut dirapatkan. Prefiks body.berprojek menaikkan
     spesifisitas supaya menang atas blok 900px yang ditulis SETELAH blok ini. */
  body.berprojek .sisi{padding:9px 12px;gap:8px}
  body.berprojek .sisi-balik{font-size:11px}
  body.berprojek .sisi-sampul{width:34px;height:34px}
  body.berprojek .sisi-nama{font-size:12.5px}
  body.berprojek .sisi-sub{font-size:10px}
  body.berprojek .sisi-menu{flex-wrap:nowrap;overflow-x:auto;gap:6px;
    padding-bottom:2px;scrollbar-width:none;-webkit-overflow-scrolling:touch}
  body.berprojek .sisi-menu::-webkit-scrollbar{display:none}
  body.berprojek .sisi-item{flex:0 0 auto;white-space:nowrap;gap:5px;
    padding:6px 11px;font-size:12.5px;border:1px solid var(--line);
    border-radius:999px}
  body.berprojek .sisi-item[data-on]{border-color:transparent}
  body.berprojek .sisi-ikon{display:none}
  body.berprojek .sisi-angka{margin-left:3px}
  /* Isi (filter + grid) diberi padding pinggir lebih kecil -> lebih banyak
     lebar untuk ubin grid. */
  .sisi-isi{padding:12px}
  /* Kepala dirapatkan di HP: padding pinggir/atas lebih kecil & jarak antar
     kontrol di .top dirapatkan, supaya baris judul + akun makan sedikit tinggi.
     safe-area tetap dihormati (notch iPhone). padding-bawah projek ikut turun. */
  header{padding:calc(10px + env(safe-area-inset-top,0px))
    calc(14px + env(safe-area-inset-right,0px)) 0
    calc(14px + env(safe-area-inset-left,0px))}
  body.berprojek header{padding-bottom:10px}
  .top{gap:8px 10px}
}

/* Daftar saringan di kotak "tidak ada yang cocok" — di sanalah orang paling
   butuh tahu kenapa layarnya kosong. */
.empty-saring{margin-top:10px;font-size:12px;color:var(--ink-dim)}
.empty-saring b{color:var(--ink);font-weight:600}
.empty .saring-bersih{margin:0;display:inline-block}

@media (max-width:820px){
  .saring-cari{flex:1 1 100%}
  .cari-nama{flex:1 1 auto}
  .saring-pisah{display:none}
  .saring-bersih{margin-left:0}
  .hal-info{order:2;flex:1 1 100%}
  .hal-nav{margin-left:0;order:3}
  .hal-loncat{display:none}
}


/* ------------------------------------------------ saringan kelas (dropdown)
   Dirombak dari bentuk pertamanya: radio dan centang bawaan sistem, teks yang
   membungkus dua baris dengan lingkaran melayang di tengahnya, dan tombol yang
   menempel langsung ke daftar tanpa pemisah. */
#kelas-isi{min-width:302px;padding:0;overflow:hidden}
/* Selebar isinya yang terpanjang, "hasil penyeimbangan", supaya tidak satu
   pun pilihannya perlu dipotong elipsis: menu ini cuma punya enam baris, dan
   nama yang terpotong di menu sesempit itu tidak ada gunanya. */
#split-isi{min-width:246px;padding:0;overflow:hidden}
#split-isi form{display:flex;flex-direction:column}
#split-isi .menu-judul{padding:11px 14px 5px}
#tag-isi{padding:0;overflow:hidden}
#tag-isi .menu-judul{padding:11px 14px 5px}
#kelas-isi form{display:flex;flex-direction:column;max-height:min(70vh,520px)}
#kelas-isi .menu-judul{padding:12px 14px 6px}

/* Kontrol tersegmen: dua sisi dari SATU pilihan, jadi ditaruh berdampingan
   dalam satu alur — bukan dua baris radio yang tampak berdiri sendiri. */
.seg{display:flex;gap:3px;padding:3px;margin:0 14px;background:var(--sunk);
  border:1px solid var(--line);border-radius:9px}
.seg-opt{flex:1;position:relative;display:flex;align-items:center;
  justify-content:center;padding:6px 10px;border-radius:6px;font-size:12.5px;
  color:var(--ink-dim);cursor:pointer;transition:background .12s,color .12s;
  white-space:nowrap;margin:0}
/* .seg-opt kini juga dipakai sebagai <a> (tab job menautkan URL, bukan
   radio). text-decoration dibuang supaya tidak bergaris bawah. */
.seg-opt{text-decoration:none}
.seg-opt input{position:absolute;opacity:0;width:0;height:0}
.seg-opt:hover{color:var(--ink)}
.seg-opt[data-on]{background:var(--panel);color:var(--ink);font-weight:600;
  box-shadow:0 1px 3px rgba(var(--bayang),.14)}
.seg-opt:focus-within{outline:2px solid var(--ok);outline-offset:1px}
.seg-ket{margin:8px 14px 12px;font-size:11.5px;line-height:1.45;
  color:var(--ink-faint)}
.seg-ket b{color:var(--ink-dim);font-weight:600}

.kelas-daftar{max-height:230px;overflow-y:auto;padding:2px 8px;
  border-top:1px solid var(--line)}
.kelas-daftar.kelas-tanpa{max-height:none;overflow:visible;padding:4px 8px 6px}
/* Daftar kelas sungguhan diberi latar sedikit berbeda, bukan garis kedua:
   dua garis bertumpuk pada jarak 6 piksel terbaca sebagai cacat, bukan sebagai
   pemisah. */
.kelas-daftar:not(.kelas-tanpa){background:var(--raised)}
.kelas-baris{display:flex;align-items:center;gap:9px;padding:7px 8px;margin:0;
  font-size:12.5px;color:var(--ink-dim);cursor:pointer;border-radius:7px;
  transition:background .1s}
.kelas-baris:hover{background:var(--hover);color:var(--ink)}

/* Kotak centang digambar sendiri: yang bawaan sistem tampil beda-beda di tiap
   mesin dan tidak bisa ikut tema. */
.kelas-baris input[type=checkbox]{position:absolute;opacity:0;width:0;height:0}
.kelas-baris .cb{width:16px;height:16px;flex:0 0 auto;border-radius:5px;
  border:1.5px solid var(--line2);background:var(--panel);display:grid;
  place-items:center;transition:.12s}
.kelas-baris .cb::after{content:"";width:8px;height:4px;
  border-left:2px solid #fff;border-bottom:2px solid #fff;
  transform:rotate(-45deg) scale(0);transition:transform .12s;margin-top:-2px}
.kelas-baris input:checked ~ .cb{background:var(--ok);border-color:var(--ok)}
.kelas-baris input:checked ~ .cb::after{transform:rotate(-45deg) scale(1)}
.kelas-baris input:focus-visible ~ .cb{outline:2px solid var(--ok);outline-offset:2px}

.kelas-baris i{width:9px;height:9px;border-radius:3px;flex:0 0 auto}
.kelas-baris span{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.kelas-baris b{flex:0 0 auto;font-variant-numeric:tabular-nums;font-size:11px;
  font-weight:600;color:var(--ink-faint);background:var(--sunk);
  padding:1px 6px;border-radius:99px;min-width:24px;text-align:center}
.kelas-tanpa .kelas-baris span{font-style:italic}

/* Menu Tag dan Bagian memakai <span class="kelas-n"> untuk angkanya, bukan <b>
   seperti menu Kelas. Tanpa dua baris di bawah keduanya kena `.kelas-baris
   span{flex:1}` dan membelah lebarnya rata dua, sehingga namanya terpotong
   elipsis di tengah baris yang sebetulnya masih lapang: "hasil augment…  4". */
.kelas-baris .kelas-nama{flex:1 1 auto;min-width:0}
.kelas-baris .kelas-n{flex:0 0 auto;font-variant-numeric:tabular-nums;
  font-size:11px;font-weight:600;color:var(--ink-faint);background:var(--sunk);
  padding:1px 6px;border-radius:99px;min-width:24px;text-align:center}

/* Kaki menu bercentang (Tag, Bagian): Terapkan + Bersihkan.
   `.menu-isi a` menjadikan setiap tautan di dalam menu satu baris penuh —
   benar untuk menu yang isinya daftar tautan, tetapi di sini ia menimpa
   `.chip` sehingga "Bersihkan" tampil sebagai kotak selebar menu, bukan
   sebagai tombol di samping Terapkan. */
.menu-kaki{display:flex;gap:8px;padding:11px 14px;
  border-top:1px solid var(--line);background:var(--raised)}
.menu-kaki .chip{flex:0 0 auto}
.menu-isi .menu-kaki a.chip{display:inline-block;width:auto;
  padding:5px 11px;border-radius:999px}
.menu-isi .menu-kaki a.chip:hover{background:transparent;
  border-color:var(--ink-faint);color:var(--ink)}

.kelas-aksi{display:flex;gap:8px;padding:11px 14px;border-top:1px solid var(--line);
  background:var(--raised)}
.kelas-aksi .chip{flex:0 0 auto}
.chip.pri{background:var(--ok);color:var(--on-ink);border-color:var(--ok);
  font-weight:600}
.chip.pri:hover{filter:brightness(1.08);color:var(--on-ink)}
/* Tombol saringan kelas: nama kelas bisa sangat panjang, jadi dipotong dengan
   elipsis alih-alih memanjangkan barnya. Nama lengkapnya ada di tooltip. */
.chip.kelas-tombol{max-width:250px;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;vertical-align:middle}

/* Pembelahan anti-bocor di dalam menu Ekspor */
.split-aksi{display:flex;gap:6px;flex-wrap:wrap;padding:6px 12px 2px}
.prog-baris{display:block;padding:4px 12px 2px}
/* .prog bawaan dipakai untuk impor dan berupa block; di sini ia duduk di
   dalam <span>, jadi tampilannya disamakan tanpa mengubah aturan aslinya. */
.prog-baris .prog{display:block;margin-top:6px}
.prog-teks{display:flex;justify-content:space-between;gap:8px;margin-top:5px;
  font-size:11.5px;color:var(--ink-dim)}
.prog-teks b{color:var(--ink);font-variant-numeric:tabular-nums}
/* Peringatan hasil pembelahan: dibaca, bukan sekadar dilewati. */
.split-warn{display:block;margin-top:6px;padding:6px 8px;border-radius:6px;
  background:var(--warn-bg);
  border-left:2px solid var(--warn,#c87800);color:var(--ink-dim);line-height:1.45}
.split-angka{display:block;margin-top:6px;font-variant-numeric:tabular-nums}
.split-angka b{color:var(--ink)}

/* Tombol aksi utama di dalam menu. Diberi warna isi, bukan cuma garis tepi:
   di antara deretan chip bergaris ia harus terbaca sebagai "yang ditekan",
   dan --aksi dipilih supaya sama kontrasnya di tema terang maupun gelap. */
.chip.aksi{background:var(--aksi);color:var(--aksi-ink);border-color:var(--aksi);
  font-weight:600}
.chip.aksi:hover{filter:brightness(1.08);color:var(--aksi-ink)}
.chip.aksi:disabled{opacity:.55;cursor:default;filter:none}

/* Catatan hasil splitting: dilipat, bukan dibuang. Deretan kotak kuning
   membuat panelnya terbaca seperti daftar kesalahan padahal hasilnya benar —
   tapi isinya tetap perlu bisa dibuka saat angkanya terasa aneh. */
.catatan-lipat{display:block;margin-top:8px}
.catatan-lipat > summary{cursor:pointer;font-size:11.5px;color:var(--ink-dim);
  list-style:none;padding:3px 0;user-select:none}
.catatan-lipat > summary::-webkit-details-marker{display:none}
.catatan-lipat > summary::before{content:"▸ ";color:var(--ink-faint)}
.catatan-lipat[open] > summary::before{content:"▾ "}
.catatan-lipat > summary:hover{color:var(--ink)}
/* Baris ringkas lipatan yang berupa peringatan tetap berwarna, supaya yang
   penting terbaca sekilas tanpa harus membuka isinya. */
.catatan-lipat.peringatan > summary{color:var(--warn);font-weight:600}
.catatan-lipat.peringatan > summary::before{color:var(--warn)}
.catatan-lipat .lipat-isi{display:block;margin-top:5px;padding:6px 8px;
  border-radius:6px;background:var(--warn-bg);color:var(--ink-dim);line-height:1.45}
.catatan-lipat:not(.peringatan) .lipat-isi{background:var(--sunk)}

/* Daftar format itu tombol UNDUH, bukan setelan yang harus dipilih dulu.
   Tanpa tanda apa pun ia terbaca seperti daftar pilihan, dan orang menunggu
   sesuatu terjadi setelah mengkliknya. */
.menu-isi a.unduh::before{content:"↓";display:inline-block;width:14px;
  color:var(--ink-faint);font-weight:700}
.menu-isi a.unduh:hover::before{color:var(--aksi)}
#unduh-pakai{padding-top:0;padding-bottom:4px}
#unduh-pakai b{color:var(--aksi)}

/* ==================================================== halaman Pilih dataset
 *
 * Dirombak dari bentuk pertamanya: kolom 660px di tengah layar dengan
 * delapan panel bertumpuk, dan kartu menegak yang sampulnya lebih besar
 * daripada namanya.
 *
 * Dua aturan yang menyusun sisanya:
 *   1. SATU tingkat kotak. Kartu duduk langsung di atas latar halaman.
 *      Sebelumnya main -> .panel -> .pcard, dan .pcard (--raised) di atas
 *      .panel (--panel) berbeda 1,5% — kotak yang tidak terlihat tapi tetap
 *      dibayar dengan garis tepi dan padding.
 *   2. Kartu MENDATAR. Yang dicari orang adalah nama, bukan foto.
 *
 * Lebarnya 1360px, bukan 100%. Roboflow pun tidak memakai lebar penuh:
 * kolom kartunya berhenti jauh sebelum tepi jendela. Pada monitor 2560px,
 * lebar penuh memberi tujuh kartu sejajar dengan kolom nama begitu sempit
 * sampai setiap nama terpotong elipsis.
 *
 * .panel LAMA sengaja tidak disentuh: admin.html memakainya di empat tempat.
 */
.pgrid{display:grid;gap:10px;margin-top:12px;
  /* min(330px,100%) bukan 330px: di layar 360px wadahnya cuma ~316px, dan
     minmax(330px,1fr) memaksa satu jalur 330px yang meluber keluar layar. */
  grid-template-columns:repeat(auto-fill,minmax(min(330px,100%),1fr))}

.pcard{position:relative;display:flex;align-items:center;gap:12px;
  min-height:88px;padding:11px 12px;
  background:var(--panel);border:1px solid var(--line);border-radius:10px;
  box-shadow:0 1px 2px rgba(var(--bayang),.05);
  transition:background .12s,border-color .12s,box-shadow .12s}
/* Isian latar, bukan angkat. Empat baris kartu yang melompat tiap kali
   kursor lewat itu gelisah; .card di halaman grid boleh mengangkat karena
   di sana kartunya kecil dan dilihat satu per satu. */
.pcard:hover{background:var(--hover);border-color:var(--line2);
  box-shadow:0 4px 14px rgba(var(--bayang),.09)}

.psampul{position:relative;z-index:2;flex:0 0 auto;display:block;
  width:64px;height:64px;border-radius:8px;overflow:hidden;
  background:var(--sunk);border:1px solid var(--line)}
.psampul img{width:100%;height:100%;object-fit:cover;display:block}
/* Projek tanpa sampul dan dataset bersama tetap memesan kotak yang sama,
   supaya barisnya tidak bergeser sendiri. */
.psampul.kosong{display:grid;place-items:center;color:var(--ink-faint);
  font-size:19px}

.pisi{flex:1 1 auto;min-width:0;padding-right:26px}
/* Dua baris lalu elipsis. "botol-kaleng-tetra-mlp-cup-1" tidak muat satu
   baris di kolom ~204px, dan memotongnya di baris pertama membuang justru
   bagian yang membedakannya dari saudaranya. */
.pnama{display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;
  -webkit-box-orient:vertical;overflow:hidden;
  font-size:13.5px;font-weight:600;line-height:1.28;color:var(--ink);
  word-break:break-word;text-decoration:none}
/* Tautan yang direntangkan: seluruh kartu jadi sasaran klik tanpa
   menyarangkan <a> di dalam <a>. */
.pnama-link{text-decoration:none;color:inherit}
.pnama-link::after{content:"";position:absolute;inset:0;z-index:1;
  border-radius:10px}
.pbuka,.plabel{display:inline-block;margin-left:6px;padding:1px 6px;
  border-radius:99px;font-size:10px;font-weight:600;vertical-align:middle;
  white-space:nowrap}
.pbuka{background:var(--aksi);color:var(--aksi-ink)}
.plabel{background:var(--lab-bg);color:var(--lab-ink)}
/* --ink-dim, bukan --ink-faint. #93a0ae di atas putih itu 2,7:1 — di bawah
   ambang keterbacaan 4,5:1, dan yang dibawanya adalah jumlah gambar serta
   tanggal ubah. --ink-dim mencapai 5,3:1; --ink-faint untuk hiasan saja. */
.pmeta{margin-top:4px;font-size:11.5px;color:var(--ink-dim);
  font-variant-numeric:tabular-nums;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pmeta b{color:var(--ink);font-weight:600}
.pmeta.halus{margin-top:4px;font-size:11px}
/* Kemajuan pelabelan menggantikan lencana tipe milik Roboflow. Lencana yang
   selalu berbunyi sama itu derau; persentase ini satu-satunya angka di
   halaman yang menjawab "mana yang perlu kukerjakan". */
.pbar{margin-top:6px;height:3px;border-radius:99px;background:var(--sunk);
  overflow:hidden}
.pbar i{display:block;height:100%;width:0;border-radius:99px;
  background:var(--aksi);transition:width .2s}

/* Tombol titik-tiga dijangkarkan ke SUDUT KARTU. Sebelumnya top:124px —
   angka yang diturunkan dengan tangan dari .psampul{height:118px}, dua
   aturan terpisah yang wajib sinkron; mengubah tinggi sampul melepaskan
   menunya dari tombolnya. Selalu terlihat, tidak disembunyikan sampai
   hover: di layar sentuh tidak ada hover. */
.ptitik{position:absolute;z-index:2;top:8px;right:8px;width:26px;height:26px;
  display:grid;place-items:center;border:0;background:transparent;
  color:var(--ink-faint);font-size:16px;line-height:1;cursor:pointer;
  border-radius:7px;transition:.12s}
.pcard:hover .ptitik{color:var(--ink-dim)}
.ptitik:hover{background:var(--raised);color:var(--ink);
  box-shadow:0 0 0 1px var(--line2)}
.ptitik:focus-visible{outline:2px solid var(--ok);outline-offset:1px}

.pmenu{position:absolute;z-index:50;top:34px;right:8px;
  min-width:214px;max-width:calc(100vw - 32px);
  /* Kartu di baris terbawah membuka menunya melewati tepi jendela, dan
     "Buang ke tempat sampah" jadi tak terjangkau. .menu-isi sudah lama
     memakai penawar yang sama; .pmenu tidak pernah kebagian. */
  max-height:calc(100vh - 120px);max-height:calc(100dvh - 120px);overflow-y:auto;overscroll-behavior:contain;
  background:var(--panel);border:1px solid var(--line2);border-radius:9px;
  padding:5px 0;box-shadow:0 10px 28px rgba(var(--bayang),.20)}
.pmenu a,.pmenu button{display:block;width:100%;text-align:left;
  padding:6px 12px;font-size:12.5px;color:var(--ink);text-decoration:none;
  white-space:nowrap;background:transparent;border:0;cursor:pointer;
  font-family:inherit}
.pmenu a:hover,.pmenu button:hover{background:var(--hover)}
.pmenu .bahaya{color:var(--stop)}
.pmenu .bahaya:hover{background:var(--warn-bg)}

/* Sedang dibuka: rel + latar + pil, bertumpuk. Rel 3px meminjam bahasa
   .card .rail yang sudah dipakai halaman grid; konsistensi ke dalam lebih
   berharga daripada meniru Roboflow. Yang dibuang cincin
   box-shadow:0 0 0 1px var(--aksi) — cincin 1px berwarna terbaca sebagai
   penanda fokus papan ketik, bukan sebagai status. */
.pcard.dibuka{background:var(--ok-lembut);border-color:var(--ok-pudar)}
.pcard.dibuka:hover{background:var(--ok-lembut);border-color:var(--aksi)}
.pcard.dibuka::before{content:"";position:absolute;left:-1px;top:12px;
  bottom:12px;width:3px;border-radius:0 3px 3px 0;background:var(--aksi)}

/* Dataset bersama: kartu yang sama, tanpa titik-tiga dan tanpa meter.
   Perbedaannya dikatakan di kartunya sendiri ("hanya baca"), bukan
   diserahkan ke judul panel di atasnya — judul adalah hal pertama yang
   berhenti dibaca orang. */
.pcard.bersama{background:var(--raised)}
.pcard.bersama:hover{background:var(--hover)}

/* Seksi "Folder bersama (hanya baca)" di dalam Sharing → Image: subjudul +
   keterangan sebaris, lalu grid kartunya. Dipisah dari kartu projek undangan
   di atasnya dengan jarak + label — bukan garis — supaya terbaca sebagai
   lanjutan daftar yang sama, bukan panel yang lain sama sekali. */
.pbersama{margin-top:22px}
.pbagi{margin:0 2px}
.pbagi h3{display:flex;align-items:center;gap:8px;
  font-size:13.5px;font-weight:600;color:var(--ink)}
.pbagi h3 .plabel{margin-left:0}
.pbagi p{margin-top:4px;font-size:12px;line-height:1.5;
  color:var(--ink-dim);max-width:640px}
.pbersama .pgrid{margin-top:12px}

/* Dipakai dua tempat: grid kartu projek (butuh grid-column) dan halaman
   Versi (butuh dipusatkan). Batas ukurnya milik kotaknya, bukan atribut
   style di templat — di kolom isi selebar 1680px, 660px tanpa margin auto
   nemplok ke kiri dan terbaca seperti tata letak yang rusak. */
.pkosong{grid-column:1/-1;max-width:660px;margin-inline:auto;padding:34px 16px;text-align:center;
  border:1.5px dashed var(--line);border-radius:10px;
  color:var(--ink-dim);font-size:13px}
.pkosong b{display:block;color:var(--ink);font-size:14px;margin-bottom:5px}

/* ------------------------------------------------------ kepala & bilah alat */
.pkepala{display:flex;align-items:flex-end;gap:16px;flex-wrap:wrap;
  margin:16px 0 4px}
.pjudul{flex:1 1 260px;min-width:0}
.pjudul h2{font-size:19px;font-weight:600;text-align:left}
.pjudul p{margin-top:3px;font-size:12.5px;color:var(--ink-dim)}
.paksi{flex:0 0 auto;display:flex;gap:8px;align-items:center}

/* Lengket supaya cari dan urut tetap terjangkau saat projeknya banyak.
   58px = tinggi header. Ukur ulang kalau isi header berubah. */
/* Dulu DUA bilah .palat bertumpuk — tab sumber lalu tab media — masing-masing
   lengket sendiri dengan garisnya sendiri; terbaca sebagai dua kontrol kaku
   yang kebetulan berdempet. Sekarang SATU permukaan kartu lengket memuat dua
   baris: baris utama (sumber + cari + urut) dan baris anak (jenis media +
   pencacah). Satu kendali, dua tingkat. */
.palat{position:sticky;top:58px;z-index:15;
  display:flex;flex-direction:column;gap:9px;
  margin:14px 0 0;padding:10px 12px;
  background:var(--panel);border:1px solid var(--line);border-radius:12px;
  box-shadow:0 1px 2px rgba(var(--bayang),.05)}
.palat-baris{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
/* Baris anak dipisah garis tipis, bukan jarak besar: tetap satu permukaan. */
.palat-anak{padding-top:9px;border-top:1px solid var(--line)}
.palat .spacer{flex:1 1 12px}
.palat .cari-nama{flex:0 1 260px;min-width:150px}
/* .seg dibuat untuk dalam menu (margin:0 14px); di bilah alat marginnya
   harus nol dan segmennya selebar isinya, bukan 1fr. */
.ptab{margin:0;flex:0 0 auto}
.ptab .seg-opt{flex:0 0 auto;padding:5px 13px}
.ptab .seg-opt b{margin-left:6px;font-size:10.5px;font-weight:600;
  color:var(--ink-faint);font-variant-numeric:tabular-nums}
.ptab .seg-opt[data-on] b{color:var(--aksi)}

/* Tab sumber (utama): segmented control penuh, sedikit lebih berisi — inilah
   pilihan terpenting di halaman. */
.ptab-utama .seg-opt{padding:6px 16px;font-size:13px}

/* Tab media (anak): tingkat di bawah sumber, jadi sengaja lebih kalem — pil
   datar di atas latar bening, bukan segmented control kedua yang menuntut
   perhatian setara. Hierarki sumber>media langsung terbaca. Discoped ke .pick
   supaya .seg di tempat lain tak ikut berubah. */
.pick .ptab-anak{background:transparent;border:0;padding:0;gap:4px}
.pick .ptab-anak .seg-opt{padding:5px 12px;border-radius:7px;font-size:12px;
  color:var(--ink-faint)}
.pick .ptab-anak .seg-opt:hover{color:var(--ink)}
.pick .ptab-anak .seg-opt[data-on]{background:var(--sunk);color:var(--ink);
  box-shadow:inset 0 0 0 1px var(--line)}
.pick .ptab-anak .seg-opt b{color:var(--ink-faint)}
.pick .ptab-anak .seg-opt[data-on] b{color:var(--aksi)}
/* Hanya muncul saat ada saringan aktif. Angka yang selalu berbunyi
   "4 dari 4" tidak pernah dibaca. */
.phitung{flex:0 0 auto;font-size:11.5px;color:var(--ink-dim);
  font-variant-numeric:tabular-nums}
/* Jalur status: wadah Progres.mulai({di: ...}). Kosong berarti tanpa tinggi,
   jadi grid tidak bergeser tiap kali operasi mulai dan selesai. */
.jalur-status .upnote{text-align:left;margin:8px 0 0}
.jalur-status .upnote:empty{display:none}

/* --------------------------------------------------- bilah "sedang dibuka"
   Satu baris, dan SELURUHNYA bisa diklik. Sebelumnya hanya tombol di
   ujungnya, padahal seluruh baris terbaca sebagai satu sasaran. */
.lanjut{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  padding:9px 13px;border-radius:9px;text-decoration:none;color:var(--ink);
  background:var(--ok-lembut);border:1px solid var(--ok-pudar);
  transition:border-color .12s}
.lanjut:hover{border-color:var(--aksi)}
.lanjut-tag{flex:0 0 auto;font-size:9.5px;font-weight:700;letter-spacing:.08em;
  text-transform:uppercase;color:var(--aksi-ink);background:var(--aksi);
  padding:2px 7px;border-radius:99px}
.lanjut b{flex:0 0 auto;font-size:13.5px}
.lanjut .n{flex:0 0 auto;font-size:12px;color:var(--ink-dim);
  font-variant-numeric:tabular-nums}
.lanjut-path{flex:1 1 200px;min-width:0;font-size:11.5px;color:var(--ink-dim);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lanjut-cta{flex:0 0 auto;font-size:12.5px;font-weight:600;color:var(--aksi)}

/* ------------------------------------------------------- tambah dataset
   Tiga panel lama (unggah laptop, folder server, dialog desktop) jadi satu
   panel bertab. Ketiganya menjawab pertanyaan yang sama — "datanya dari
   mana?" — dan hanya satu yang dipakai dalam satu waktu. Terlipat karena
   halaman ini hampir selalu dibuka untuk MEMBUKA projek, bukan mengunggah. */
.psumber{margin-top:26px;background:var(--panel);border:1px solid var(--line);
  border-radius:11px;overflow:hidden}
.psumber > summary{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  padding:13px 16px;cursor:pointer;list-style:none;user-select:none}
.psumber > summary::-webkit-details-marker{display:none}
.psumber > summary:hover{background:var(--hover)}
.psumber > summary b{font-size:13.5px}
.psumber > summary span{font-size:12px;color:var(--ink-dim)}
.psumber > summary::after{content:"▾";margin-left:auto;flex:0 0 auto;
  font-size:11px;color:var(--ink-faint);transition:transform .15s}
.psumber[open] > summary::after{transform:rotate(180deg)}
.psumber[open] > summary{border-bottom:1px solid var(--line)}
.lipat-blok{margin-top:18px;padding-top:12px;border-top:1px solid var(--line)}

/* ------------------------------------------------------- daftar tempat sampah
   Baris rata: centang, nama+meta (mengisi), lalu tombol. SENGAJA tidak memakai
   .row — .row memberi flex:1 ke tiap <input>, jadi kotak centangnya melar dan
   mendorong nama ke tengah (itu yang bikin tata letaknya berantakan). */
#sampah-isi{max-height:min(58vh,520px);overflow:auto}
.sampah-bar{display:flex;align-items:center;gap:10px;position:sticky;top:0;
  z-index:1;background:var(--sunk);padding:6px 2px 8px;margin-bottom:2px;
  border-bottom:1px solid var(--line)}
.sampah-bar label{display:flex;align-items:center;gap:7px;font-size:12.5px;
  color:var(--ink-dim);cursor:pointer;user-select:none}
/* Centang "Pilih semua" distyle di sini, BUKAN lewat .sampah-cek — kelas itu
   penanda baris-item yang dipakai JS untuk menghitung & mengumpulkan folder;
   kalau centang ini ikut ber-.sampah-cek, ia terhitung sebagai item dan
   masuk daftar hapus borongan sebagai folder kosong. */
.sampah-bar input[type=checkbox]{flex:0 0 auto;width:15px;height:15px;margin:0;
  cursor:pointer;accent-color:var(--aksi)}
.sampah-baris{display:flex;align-items:center;gap:10px;padding:7px 2px;
  border-bottom:1px solid var(--line)}
.sampah-baris:last-of-type{border-bottom:0}
.sampah-cek{flex:0 0 auto;width:15px;height:15px;margin:0;cursor:pointer;
  accent-color:var(--aksi)}
.sampah-info{flex:1 1 auto;min-width:0;line-height:1.3}
.sampah-nama{font-size:13px;color:var(--ink);white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis}
.sampah-meta{font-size:10.5px;color:var(--ink-faint);margin-top:1px}
.sampah-baris .chip{flex:0 0 auto;margin-top:0}
.sampah-ket{font-size:10.5px;color:var(--ink-faint);margin-top:10px;
  display:block;line-height:1.5}

/* ------------------------------------------------------------ layar sempit */
@media (max-width:720px){
  /* Bilah lengket di bawah header yang juga lengket menyisakan terlalu
     sedikit tinggi untuk kartunya sendiri — di layar sempit kartunya tetap
     kartu, hanya tak lagi lengket. */
  .palat{position:static}
  .palat .cari-nama{flex:1 1 100%}
  /* Tab sumber melebar penuh; tab media tetap pil kecil berdampingan. */
  .ptab-utama{flex:1 1 100%}
  .ptab-utama .seg-opt{flex:1}
  .lanjut-path{display:none}
  .psampul{width:56px;height:56px}
  .pcard{min-height:0}
  /* Pil akun bisa menyempit; namanya yang mengalah lebih dulu, bukan tombolnya. */
  .whoami-nama{max-width:92px}
}

/* -------------------------------------------------------------- tema gelap
   Seluruh warnanya sudah token; yang perlu diulang hanya bayangan. Bayangan
   halus di atas --panel gelap tidak terlihat, jadi kartunya kehilangan
   pemisah dari latar dan menunya kehilangan ketinggiannya. */
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .pcard{box-shadow:none}
  :root:not([data-theme="light"]) .pcard:hover{
    box-shadow:0 4px 16px rgba(var(--bayang),.5)}
  :root:not([data-theme="light"]) .pmenu{
    box-shadow:0 12px 32px rgba(var(--bayang),.6)}
  :root:not([data-theme="light"]) .ptitik:hover{background:var(--sunk)}
}
:root[data-theme="dark"] .pcard{box-shadow:none}
:root[data-theme="dark"] .pcard:hover{box-shadow:0 4px 16px rgba(var(--bayang),.5)}
:root[data-theme="dark"] .pmenu{box-shadow:0 12px 32px rgba(var(--bayang),.6)}
:root[data-theme="dark"] .ptitik:hover{background:var(--sunk)}

@media (prefers-reduced-motion:reduce){
  .pcard,.lanjut,.ptitik,.pbar i,.psumber > summary::after{transition:none}
}

/* ------------------------------------------------------- progres bersama */
.pr-kotak{display:block;margin-top:10px}
.pr-teks{display:flex;justify-content:space-between;gap:8px;margin-top:6px;
  font-size:11.5px;color:var(--ink-dim)}
.pr-teks b{color:var(--ink);font-variant-numeric:tabular-nums}
.pr-kotak[data-keadaan="gagal"] .prog i{background:var(--stop)}
.pr-kotak[data-keadaan="gagal"] .pr-teks b{color:var(--stop)}
.pr-kotak[data-keadaan="selesai"] .pr-teks b{color:var(--aksi)}

/* ===================================================== penanda kerja global
 *
 * Dua keadaan: PIL (tertutup, ~30x92) dan PANEL (terbuka, 316px). Tertutup
 * itu keadaan normal; panelnya hanya ada kalau diklik.
 *
 * Ukuran pil bukan selera. Di halaman anotasi .lab-side lebarnya 260px; pil
 * terlebar (92px) + right:16px berhenti di 108px dari tepi kanan, jadi ia
 * duduk seluruhnya di dalam panel kanan dan TIDAK PERNAH menutupi kanvas.
 * Kotak 320px yang lama menggantung 76px di atas kanvas, persis tempat orang
 * menyeret simpul poligon.
 *
 * z-index 59: di atas .pmenu dan .toast (50), DI BAWAH .dlg (60). Dialog
 * modal itu interaksi sengaja dan pendek; penanda ini kembali sesudahnya.
 * Sebelumnya 900, sehingga ia duduk di atas dialog beserta tirainya.
 */
#kerja-global{
  position:fixed; right:16px; bottom:calc(16px + env(safe-area-inset-bottom,0px)); z-index:59;
  display:none; flex-direction:column-reverse; align-items:flex-end;
  /* Wadahnya tembus klik; hanya pil dan panel yang menangkap kursor, supaya
     seretan yang kebetulan lewat di dekatnya tidak pernah tersangkut. */
  pointer-events:none;
  font-size:12px; opacity:1; transition:opacity .2s ease;
}
#kerja-global[data-on]{display:flex}
#kerja-global[data-pergi]{opacity:0}

/* ------------------------------------------------------------------- pil */
.kg-pil{
  pointer-events:auto;
  display:inline-flex; align-items:center;
  height:30px; padding:0 11px 0 9px; max-width:min(60vw,320px);
  border:1px solid var(--line2); border-radius:999px;
  background:var(--panel); color:var(--ink-dim);
  box-shadow:0 4px 14px rgba(var(--bayang),.16);
  font:inherit; font-size:12px; line-height:1; cursor:pointer;
  transition:background .12s, border-color .12s, color .12s;
}
.kg-pil:hover{background:var(--hover); border-color:var(--ink-faint); color:var(--ink)}
.kg-pil:focus-visible{outline:2px solid var(--ok); outline-offset:2px}

/* Cincin kemajuan. Lubang tengahnya harus sewarna latar pil termasuk saat
   disorot, kalau tidak ia tampak seperti titik yang salah warna. */
.kg-cincin{
  flex:0 0 auto; width:15px; height:15px; margin-right:7px;
  border-radius:50%; position:relative;
  background:conic-gradient(var(--aksi) calc(var(--v,0) * 1%), var(--sunk) 0);
}
.kg-cincin::after{
  content:""; position:absolute; inset:4px; border-radius:50%; background:var(--panel);
}
.kg-pil:hover .kg-cincin::after{background:var(--hover)}
.kg-cincin[data-tak-tentu]{
  background:conic-gradient(from 0turn, var(--aksi) 0 30%, var(--sunk) 30%);
  animation:kg-putar .85s linear infinite;
}
@keyframes kg-putar{to{transform:rotate(1turn)}}

/* Nama pekerjaan melebar 3,5 detik saat ada yang baru dimulai, lalu mengecil
   sendiri. Namanya cuma perlu dibaca sekali; sisanya cukup jadi titik di
   pojok. Jaraknya dibawa margin, bukan gap, supaya ikut menciut. */
.kg-judul{
  max-width:0; margin-right:0; opacity:0; color:var(--ink); font-weight:600;
  overflow:hidden; white-space:nowrap; text-overflow:ellipsis;
  transition:max-width .26s ease, margin-right .26s ease, opacity .18s ease;
}
#kerja-global[data-kenalan] .kg-judul{max-width:190px; margin-right:8px; opacity:1}
.kg-teks{color:var(--ink); font-weight:600; font-variant-numeric:tabular-nums;
  white-space:nowrap}

#kerja-global[data-keadaan="selesai"] .kg-cincin,
#kerja-global[data-keadaan="selesai"] .kg-cincin::after{background:var(--aksi)}
#kerja-global[data-keadaan="selesai"] .kg-teks{color:var(--aksi)}
#kerja-global[data-keadaan="gagal"] .kg-pil{
  border-color:var(--stop); background:var(--bahaya-lembut)}
#kerja-global[data-keadaan="gagal"] .kg-pil:hover{background:var(--bahaya-lembut)}
#kerja-global[data-keadaan="gagal"] .kg-cincin,
#kerja-global[data-keadaan="gagal"] .kg-cincin::after{background:var(--stop)}
#kerja-global[data-keadaan="gagal"] .kg-teks{color:var(--stop)}

/* ----------------------------------------------------------------- panel */
.kg-panel{
  pointer-events:auto;
  width:316px; max-width:calc(100vw - 32px); margin-bottom:8px;
  background:var(--panel); border:1px solid var(--line2); border-radius:11px;
  box-shadow:0 12px 32px rgba(var(--bayang),.22);
  overflow:hidden; transform-origin:100% 100%;
  animation:kg-muncul .14s ease-out;
}
@keyframes kg-muncul{
  from{opacity:0; transform:translateY(6px) scale(.98)}
  to{opacity:1; transform:none}
}
.kg-kepala{
  display:flex; align-items:center; gap:8px;
  padding:8px 7px 8px 13px;
  background:var(--raised); border-bottom:1px solid var(--line);
}
.kg-kepala h4{
  flex:1; font-size:10.5px; font-weight:600; letter-spacing:.09em;
  text-transform:uppercase; color:var(--ink-faint); margin:0;
}
.kg-tutup{
  border:0; background:transparent; color:var(--ink-faint); cursor:pointer;
  font:inherit; font-size:15px; line-height:1; padding:3px 7px; border-radius:6px;
}
.kg-tutup:hover{background:var(--hover); color:var(--ink)}
.kg-tutup:focus-visible{outline:2px solid var(--ok); outline-offset:-2px}

.kg-daftar{
  list-style:none; margin:0; padding:0;
  max-height:min(58vh,380px);max-height:min(58dvh,380px); overflow-y:auto; overscroll-behavior:contain;
}
.kg-item{padding:9px 13px 10px}
.kg-item + .kg-item{border-top:1px solid var(--line)}
.kg-baris{display:flex; align-items:baseline; gap:8px}
.kg-nama{
  flex:1; min-width:0; font-size:12.5px; font-weight:600; color:var(--ink);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.kg-angka{
  flex:0 0 auto; font-size:11.5px; color:var(--ink-dim);
  font-variant-numeric:tabular-nums;
}
/* .prog dipakai bersama; di dalam baris ia lebih tipis dan rapat. */
.kg-item .prog{height:6px; margin-top:7px}
.kg-rinci{
  margin-top:5px; font-size:11.5px; color:var(--ink-faint);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.kg-buang{
  flex:0 0 auto; border:0; background:transparent; color:var(--ink-faint);
  cursor:pointer; font:inherit; font-size:13px; line-height:1;
  padding:2px 5px; margin:-2px -4px -2px 0; border-radius:5px;
}
.kg-buang:hover{background:var(--hover); color:var(--ink)}
.kg-buang:focus-visible{outline:2px solid var(--ok); outline-offset:-2px}

.kg-item[data-keadaan="selesai"] .kg-nama{color:var(--ink-dim); font-weight:500}
.kg-item[data-keadaan="selesai"] .kg-angka{color:var(--aksi); font-weight:600}
.kg-item[data-keadaan="selesai"] .prog i{background:var(--aksi)}
.kg-item[data-keadaan="gagal"]{background:var(--bahaya-lembut)}
.kg-item[data-keadaan="gagal"] .kg-angka{color:var(--stop); font-weight:600}
.kg-item[data-keadaan="gagal"] .prog i{background:var(--stop); width:100% !important}
.kg-item[data-keadaan="gagal"] .kg-rinci{color:var(--ink)}

/* Pengumuman untuk pembaca layar; tidak pernah terlihat. */
.kg-live{
  position:absolute; width:1px; height:1px; margin:-1px; padding:0; border:0;
  overflow:hidden; clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap;
}

/* Tema gelap: seluruh warnanya sudah token, jadi yang perlu diulang hanya
   bayangannya. Hitam .16 di atas --panel gelap praktis tak terlihat, dan
   pilnya kehilangan pemisah dari latar. */
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .kg-pil{box-shadow:0 4px 16px rgba(var(--bayang),.55)}
  :root:not([data-theme="light"]) .kg-panel{box-shadow:0 12px 34px rgba(var(--bayang),.6)}
}
:root[data-theme="dark"] .kg-pil{box-shadow:0 4px 16px rgba(var(--bayang),.55)}
:root[data-theme="dark"] .kg-panel{box-shadow:0 12px 34px rgba(var(--bayang),.6)}

/* Gerak yang dikurangi. Animasi tak-tentu berputar selamanya, dan bagi
   sebagian orang itu memicu pusing; keadaan "sedang berjalan" tetap harus
   tersampaikan, jadi diganti bentuk diam, bukan dihilangkan. */
@media (prefers-reduced-motion:reduce){
  #kerja-global, .kg-judul{transition:none}
  .kg-panel{animation:none}
  .kg-cincin[data-tak-tentu]{
    animation:none; background:conic-gradient(var(--aksi) 0 50%, var(--sunk) 0)}
  .prog[data-tak-tentu] i{animation:none; width:100% !important; opacity:.45}
  .prog[data-tahap="pindai"] i{animation:none}
}

/* --------------------------------------------------------- kelola akun */
.akun-daftar{display:block;margin-top:10px}
.akun-baris{display:flex;align-items:center;gap:10px;padding:9px 2px;
  border-top:1px solid var(--line)}
.akun-baris:first-child{border-top:0}
.akun-kiri{flex:1;min-width:0}
.akun-nama{font-size:13px;font-weight:600;color:var(--ink)}
.akun-email{font-size:11.5px;color:var(--ink-dim);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.akun-email.kosong{color:var(--ink-faint);font-style:italic}
.akun-meta{font-size:11px;color:var(--ink-faint);margin-top:2px}
.lencana{display:inline-block;padding:1px 7px;border-radius:99px;font-size:10px;
  font-weight:600;vertical-align:middle;margin-left:6px}
.lencana.adm{background:var(--aksi);color:var(--aksi-ink)}
.lencana.aku{background:var(--lab-bg);color:var(--lab-ink)}
.akun-aksi{flex:0 0 auto;display:flex;gap:5px}
.akun-aksi .chip{padding:4px 9px;font-size:11.5px}
.akun-aksi .chip.bahaya{color:var(--stop);border-color:var(--stop)}
.akun-aksi .chip.bahaya:hover{background:var(--bahaya-lembut)}
.lencana.tunggu{background:var(--warn-bg);color:var(--warn);
  border:1px solid var(--warn)}
.akun-baris.menunggu{background:var(--warn-bg);border-radius:8px;
  padding-left:9px;padding-right:9px}

/* ============================================================ halaman /profil
   Halaman akun DIRI SENDIRI. Dibangun dari bahasa yang sudah ada (.pick,
   .panel, .form-lbl, .cta, .chip) — yang khas di sini hanya pembatas lebarnya
   (lebih sempit dari daftar member yang dua kolom) dan baris foto. */
.profil{max-width:560px}
.profil h2{font-size:20px;font-weight:600;text-align:left}
.profil .sub{text-align:left;margin:6px 0 0}
/* Baris foto: avatar besar di kiri, tombol & keterangan di kanan. Membungkus
   ke bawah di layar sempit. */
.profil-foto{display:flex;gap:16px;align-items:flex-start;flex-wrap:wrap}
.profil-ava{flex:0 0 auto;width:72px;height:72px;border-radius:50%;
  display:grid;place-items:center;overflow:hidden;background:var(--aksi);
  color:var(--aksi-ink);font-size:30px;font-weight:700;line-height:1}
.profil-ava img{width:100%;height:100%;object-fit:cover;display:block}
.profil-foto-aksi{flex:1;min-width:200px}
/* Input berkas asli disembunyikan; tombol .chip bergaya yang menggantikannya
   (lihat profil.js). Tak pakai display:none supaya tetap bisa difokus papan
   ketik lewat label kalau perlu. */
.profil-foto-input{position:absolute;width:1px;height:1px;opacity:0;
  overflow:hidden;clip:rect(0 0 0 0)}
.profil .chip.bahaya{color:var(--stop);border-color:var(--stop)}
.profil .chip.bahaya:hover{background:var(--bahaya-lembut)}

/* Baris kaki di dalam kartu masuk/daftar: pemisah tipis lalu teks kecil
   terpusat. Sebelumnya paragraf ini berada di LUAR kartu, dan karena yang
   memusatkan tampilan adalah .card-form sendiri (max-width + margin auto),
   ia jatuh ke tepi kiri halaman sebagai tautan biru telanjang. */
.kartu-kaki{margin-top:18px;padding-top:14px;border-top:1px solid var(--line);
  text-align:center;font-size:12.5px;color:var(--ink-dim)}
.kartu-kaki a{color:var(--aksi);font-weight:600;text-decoration:none}
.kartu-kaki a:hover{text-decoration:underline}

/* =============================================================== dialog
 *
 * Dipakai bersama seluruh aplikasi. Dasarnya dulu hanya ada di label.css,
 * yang cuma dimuat di halaman anotasi — jadi halaman lain tidak punya
 * dialog sama sekali dan terpaksa memakai prompt() bawaan peramban.
 *
 * z-index 60: di atas penanda kerja global (59), menu (50), dan toast.
 */
.dlg{position:fixed;inset:0;z-index:60;display:flex;align-items:center;
  justify-content:center;background:rgba(var(--tirai),.55);padding:20px}
.dlg[hidden]{display:none}
.dlg-kotak{background:var(--panel);border:1px solid var(--line2);
  border-radius:11px;width:min(520px,100%);max-height:88vh;max-height:88dvh;overflow-y:auto;
  padding:18px 20px 16px;box-shadow:0 18px 48px rgba(var(--bayang),.34)}
.dlg-kotak h3{margin:0 0 4px;font-size:15px;color:var(--ink)}
.dlg-kotak .dlg-sub{margin:0 0 14px;font-size:12.5px;color:var(--ink-dim)}
.dlg-kotak input[type=text]{width:100%;padding:8px 10px;font-size:13px;
  background:var(--ground);color:var(--ink);border:1px solid var(--line2);
  border-radius:6px}
.dlg-kotak input[type=text]:focus{outline:2px solid var(--ok);outline-offset:-1px}
.dlg-tutup{position:absolute;top:14px;right:16px;border:0;background:transparent;
  color:var(--ink-faint);font-size:18px;line-height:1;cursor:pointer;
  padding:4px 8px;border-radius:7px}
.dlg-tutup:hover{background:var(--hover);color:var(--ink)}
.dlg-kotak{position:relative}

/* ===================================================== halaman Unggah data
 *
 * Tiga tahap, satu halaman, satu <main>. Tahap 2 dan 3 hanya disembunyikan
 * dengan atribut hidden: nama batch dan tag yang sudah diketik harus selamat
 * saat orang mundur dari tahap 2 ke tahap 1, dan pindah halaman
 * menghapusnya.
 *
 * Kolom kanan Roboflow (ponsel, Universe, cloud storage) tidak ada di sini,
 * dan ruangnya sengaja TIDAK diisi kartu pengganti. Kotak drop yang melebar
 * penuh sasaran seretnya lebih besar, dan itu memang yang dibutuhkan: orang
 * menyeret folder dari jendela lain sambil menahan tombol tetikus, dengan
 * penglihatan terbagi antara dua jendela.
 *
 * Lebarnya 1360px, sama dengan halaman Pilih projek. Dua halaman yang lebar
 * isinya berbeda membuat header dan kartunya bergeser tiap kali berpindah.
 */
.ug-halaman{max-width:1360px;margin:0 auto}

/* ------------------------------------------------------- penunjuk tahap
   Duduk di dalam header yang lengket, menggantikan pengganjal 12px milik
   pick.html. Pada tahap 2 dengan ribuan thumbnail, ini satu-satunya yang
   memberitahu bahwa masih ada tahap ketiga sesudahnya. */
.ug-langkah{display:flex;align-items:center;gap:9px;flex-wrap:wrap;
  margin-top:12px;padding-bottom:11px;font-size:12px;color:var(--ink-faint)}
.ug-langkah-item{display:flex;align-items:center;gap:7px;white-space:nowrap}
.ug-langkah-no{display:grid;place-items:center;width:18px;height:18px;
  flex:0 0 auto;border-radius:50%;background:var(--sunk);
  border:1px solid var(--line);font-size:10.5px;font-weight:700;
  color:var(--ink-faint);font-variant-numeric:tabular-nums}
.ug-langkah-item[data-on]{color:var(--ink);font-weight:600}
.ug-langkah-item[data-on] .ug-langkah-no{background:var(--aksi);
  border-color:var(--aksi);color:var(--aksi-ink)}
.ug-langkah-item[data-selesai]{color:var(--ink-dim)}
.ug-langkah-item[data-selesai] .ug-langkah-no{background:var(--ok-lembut);
  border-color:var(--ok-pudar);color:var(--aksi)}
.ug-langkah-panah{font-size:11px}

/* ------------------------------------------------------------ nama & tag
   auto-fit, bukan dua kolom mati: di bawah ~600px keduanya menumpuk sendiri
   tanpa media query, dan pada lebar berapa pun tidak ada kolom yang lebih
   sempit dari 280px. Kotak isian yang lebih sempit dari itu membuat nama
   batch tergulir mendatar di dalam kotaknya sendiri. */
.ug-isian{display:grid;gap:10px 18px;margin-top:16px;
  grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}
.ug-isian .form-lbl{margin-top:0}

/* Kotak tag tampak seperti satu <input type=text> supaya sebaris dengan nama
   batch di sebelahnya, tapi isinya chip. 40px = tinggi input yang sudah ada
   (padding 9px + baris 20px + tepi 2px); disamakan dengan tangan karena
   isinya bukan teks biasa. */
.ug-tag-kotak{display:flex;align-items:center;gap:6px;flex-wrap:wrap;
  min-height:40px;padding:5px 8px;border:1px solid var(--line2);
  border-radius:6px;background:var(--raised)}
.ug-tag-kotak:focus-within{border-color:var(--ok)}
.ug-tag{display:inline-flex;align-items:center;gap:4px;max-width:220px;
  padding:2px 4px 2px 9px;border-radius:99px;
  background:var(--lab-bg);color:var(--lab-ink);font-size:11.5px}
.ug-tag span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ug-tag button{flex:0 0 auto;border:0;background:transparent;color:inherit;
  cursor:pointer;font:inherit;line-height:1;padding:1px 4px;border-radius:99px}
.ug-tag button:hover{background:var(--hover);color:var(--ink)}
/* flex-basis 90px, bukan auto: dengan auto kolom ketiknya menyusut jadi
   beberapa piksel begitu chipnya banyak, dan kursornya menghilang. */
.ug-tag-input{flex:1 1 90px;min-width:90px;border:0;background:transparent;
  color:var(--ink);font:inherit;font-size:13.5px;padding:4px 2px}
.ug-tag-input:focus{outline:0}

/* ------------------------------------------------------- kotak drop besar
   Mewarisi .drop, jadi hover dan [data-over] sudah terurus dan penangan
   seret di app.js tidak perlu tahu ada bentuk kedua.

   Tingginya clamp, bukan angka mati. Pada laptop 1280x720 ruang di bawah
   header tinggal sekitar 600px; kotak 380px membuat panel "Format yang
   didukung" jatuh di luar layar, padahal justru itu yang dibaca orang
   SEBELUM menyeret. 34vh menjaga panel itu tetap terlihat di layar pendek
   dan tetap memberi kotak yang lapang di layar tinggi. */
.drop.ug-drop{display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:11px;
  min-height:clamp(190px,34vh,300px);margin-top:16px;padding:28px 20px;
  background:var(--panel);border-radius:11px}
.ug-drop-ikon{display:grid;place-items:center;width:46px;height:46px;
  flex:0 0 auto;border-radius:50%;background:var(--sunk);color:var(--ink-dim);
  font-size:21px;transition:background .14s,color .14s}
.drop.ug-drop:hover .ug-drop-ikon,
.drop.ug-drop[data-over] .ug-drop-ikon{background:var(--ok-pudar);color:var(--aksi)}
.ug-drop-judul{font-size:15px;font-weight:600;color:var(--ink);text-align:center}
.ug-drop-atau{font-size:12px;color:var(--ink-faint)}
.ug-drop-tombol{display:flex;gap:10px;flex-wrap:wrap;justify-content:center}
/* .cta bawaan punya margin-top:26px untuk pemakaiannya di .card-form. */
.ug-drop-tombol .cta{margin-top:0;padding:10px 22px;font-size:13.5px}
.ug-drop-batas{max-width:560px;font-size:11.5px;line-height:1.55;
  color:var(--ink-faint);text-align:center}

/* Varian bergaris untuk .cta. Sebelumnya bentuk ini ditulis sebagai style
   sebaris di pick.html tiap kali dibutuhkan, dan tiap salinannya sedikit
   berbeda. */
.cta.garis{background:transparent;color:var(--ink-dim);
  border:1px solid var(--line2)}
.cta.garis:hover{background:var(--hover);color:var(--ink);
  border-color:var(--line2)}

/* -------------------------------------------------- panel pendukung bawah
   minmax(0,...), bukan 1.3fr saja: jalur grid bawaannya min-content, dan
   satu path server sepanjang 90 karakter di dalam .ug-server memaksa
   jalurnya melebihi lebar halaman. Itu jalur paling mudah menghasilkan
   guliran mendatar yang tidak diinginkan siapa pun. */
.ug-bawah{display:grid;gap:16px;margin-top:16px;
  grid-template-columns:minmax(0,1.3fr) minmax(0,1fr)}
.ug-bawah > .panel{margin-top:0}

.ug-format-kisi{display:grid;gap:12px 18px;
  grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}
.ug-format-grup b{display:block;font-size:12px;color:var(--ink);
  margin-bottom:3px;font-weight:600}
/* anywhere, bukan break-word: deretan ekstensi itu satu "kata" panjang tanpa
   spasi yang bisa dipatahkan, dan tanpa ini ia melebarkan panelnya. */
.ug-format-grup span{display:block;font-size:11.5px;line-height:1.6;
  color:var(--ink-dim);overflow-wrap:anywhere}

.ug-server-jelajah{margin-top:10px;width:100%}
.ug-server-ket{font-size:12px;line-height:1.55;color:var(--ink-dim);
  margin-bottom:10px}
.ug-server-ket b{color:var(--ink);font-weight:600}
/* .row tidak membungkus; di kolom kanan yang sempit, tombol Salin terdorong
   keluar panel. */
.ug-server .row{flex-wrap:wrap}
.ug-server .row input{min-width:170px}
.ug-server .row .cta{flex:0 0 auto;width:auto;margin-top:0;
  padding:9px 16px;font-size:13px}

.ug-riwayat{margin-top:14px;padding-top:12px;border-top:1px solid var(--line)}
.ug-riwayat-judul{font-size:11px;color:var(--ink-faint);margin-bottom:6px}
.ug-riwayat-baris{display:flex;align-items:center;gap:4px;margin-bottom:4px}
.ug-riwayat-path{flex:1 1 auto;min-width:0;text-align:left;cursor:pointer;
  border:1px solid var(--line2);border-radius:6px;background:transparent;
  color:var(--ink-dim);font-family:inherit;font-size:11.5px;padding:6px 8px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ug-riwayat-path:hover{border-color:var(--ok);color:var(--ink)}
.ug-riwayat-path:disabled{color:var(--ink-faint);cursor:default;
  border-color:var(--line)}
.ug-riwayat-lupa{flex:0 0 auto;border:0;background:transparent;cursor:pointer;
  color:var(--ink-faint);font:inherit;font-size:14px;line-height:1;
  padding:4px 7px;border-radius:6px}
.ug-riwayat-lupa:hover{background:var(--hover);color:var(--ink)}

/* --------------------------------------------------------- tahap 2: tab
   Bentuknya sama dengan .ptab di halaman projek. Ditulis terpisah, bukan
   memakai .ptab, karena .ptab ikut aturan bilah alat lengket yang tidak ada
   di halaman ini.
   width:max-content supaya pilnya selebar isinya, bukan selebar halaman;
   flex-wrap supaya di layar sempit ketiga tab menumpuk di dalam pil, bukan
   menggulir mendatar. */
.ug-tab{margin:18px 0 0;width:max-content;max-width:100%;flex-wrap:wrap}
.ug-tab .seg-opt{flex:0 0 auto;padding:6px 14px}
.ug-tab .seg-opt b{margin-left:7px;font-size:10.5px;font-weight:600;
  color:var(--ink-faint);font-variant-numeric:tabular-nums}
.ug-tab .seg-opt[data-on] b{color:var(--aksi)}

/* ------------------------------------------------------ tahap 2: panggung */
.ug-panggung{margin-top:12px;background:var(--panel);
  border:1px solid var(--line);border-radius:11px;overflow:hidden;
  box-shadow:0 1px 2px rgba(var(--bayang),.05)}
/* Seluruh panggung jadi sasaran seret. Setelah thumbnail memenuhi layar,
   bilah setinggi 52px itu sasaran yang terlalu kecil untuk folder yang
   diseret dari jendela lain. */
.ug-panggung[data-over]{border-color:var(--ok);
  box-shadow:0 0 0 1px var(--ok),0 1px 2px rgba(var(--bayang),.05)}

/* Bilah tipis, bukan kotak drop kedua setinggi tahap 1: di tahap 2 yang
   dicari orang thumbnailnya, dan tiap 100px bilah ini memakan satu baris
   thumbnail penuh. */
/* Spanduk status ekstraksi video di latar (muncul saat halaman dibuka kalau
   ada video yang sedang/baru diekstrak). */
.ug-ekstrak{margin:12px 0;border:1px solid var(--line);border-radius:10px;
  background:var(--sunk);padding:10px 14px;display:flex;flex-direction:column;gap:6px}
.ug-ekstrak-baris{display:flex;align-items:center;gap:8px;font-size:12.5px;
  color:var(--ink)}
.ug-ekstrak-ikon{flex:none;width:16px;text-align:center}
/* Tombol sembunyikan di baris yang sudah kelar (selesai/gagal). Didorong ke
   ujung kanan; samar sampai disentuh supaya tidak merebut perhatian. */
.ug-ekstrak-tutup{margin-left:auto;flex:none;width:20px;height:20px;padding:0;
  border:none;background:transparent;color:var(--ink-faint);font-size:16px;
  line-height:1;border-radius:5px;cursor:pointer}
.ug-ekstrak-tutup:hover{background:var(--line);color:var(--ink)}
.ug-ekstrak-jalan{color:var(--ink-dim)}
.ug-ekstrak-ok{color:var(--ok)}
.ug-ekstrak-gagal{color:var(--gagal)}
.ug-ekstrak-nama{font-weight:600}
.ug-ekstrak-spin{display:inline-block;width:13px;height:13px;border:2px solid var(--line);
  border-top-color:var(--aksi);border-radius:50%;animation:ug-putar .8s linear infinite}
@keyframes ug-putar{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){.ug-ekstrak-spin{animation:none}}

.ug-bilah{display:flex;align-items:center;gap:9px;flex-wrap:wrap;
  padding:10px 14px;background:var(--raised);
  border-bottom:1px solid var(--line)}
.ug-bilah-ket{flex:1 1 240px;min-width:0}
.ug-bilah-ket b{display:block;font-size:12.5px;color:var(--ink)}
.ug-bilah-ket span{display:block;margin-top:2px;font-size:11px;
  color:var(--ink-faint);overflow-wrap:anywhere}
.ug-bilah .chip{flex:0 0 auto}
/* Tombol utama sedikit lebih besar dari chip di sebelahnya: dari sudut mata,
   ukuran yang membedakannya, bukan warnanya saja. */
.ug-bilah .chip.aksi{padding:7px 15px;font-size:13px}
/* Slider kerapatan ekstraksi video: geser kiri (sedikit) ke kanan (banyak). */
.ug-krp{flex:0 0 auto;display:flex;align-items:center;gap:9px}
.ug-krp-ujung{font-size:11px;color:var(--ink-faint);white-space:nowrap}
.ug-krp input[type=range]{width:180px;max-width:46vw;accent-color:var(--aksi);
  cursor:pointer}
@media (max-width:560px){
  .ug-krp{flex:1 1 100%}
  .ug-krp input[type=range]{flex:1 1 auto;width:auto;max-width:none}
}

/* Kisi menggulir SENDIRI, bukan ikut halaman. Pada 5.000 berkas, tombol
   "Simpan dan lanjutkan" yang ikut tergulir berada puluhan layar jauhnya
   dari thumbnail terakhir, dan orang menggulir balik ke atas mencarinya.
   Dengan wadah bergulir sendiri, bilahnya selalu di tempat tanpa
   position:sticky sama sekali.
   min(62vh,620px): 62vh supaya di layar pendek kisi tidak menelan seluruh
   jendela, 620px supaya di monitor 1440p tinggi kisinya tetap masuk akal. */
.ug-kisi-bungkus{max-height:min(62vh,620px);overflow-y:auto;overflow-x:hidden;
  overscroll-behavior:contain;padding:14px}
.ug-kisi-bungkus:focus-visible{outline:2px solid var(--ok);outline-offset:-2px}
/* auto-fill, BUKAN auto-fit. Dengan auto-fit jalur yang kosong diciutkan,
   dan saat yang di-staging cuma satu berkas, ubin tunggal itu melar
   selebar 1.300px. auto-fill menyisakan jalur kosongnya, jadi ubin
   pertama tetap seukuran ubin mana pun. */
.ug-kisi{display:grid;gap:14px 12px;
  grid-template-columns:repeat(auto-fill,minmax(112px,1fr))}

/* 5.000 ubin berarti 5.000 gambar yang harus ditata dan dilukis.
   content-visibility melewatkan yang berada di luar layar;
   contain-intrinsic-size memberinya tinggi tebakan supaya bilah gulirnya
   tidak melompat-lompat saat digulir. 152px = ubin 112px + nama 30px +
   jarak 6px + sedikit lebih. */
.ug-ubin{position:relative;margin:0;min-width:0;
  content-visibility:auto;contain-intrinsic-size:auto 152px}
.ug-gambar{position:relative;display:block;aspect-ratio:1;border-radius:10px;
  overflow:hidden;background:var(--sunk);border:1px solid var(--line);
  transition:border-color .12s}
/* contain, bukan cover: yang diperiksa di tahap ini adalah "berkasnya benar
   atau tidak", dan cover memotong sisi gambar potret sampai isinya berubah
   arti. Sama dengan .card img di halaman grid. */
.ug-gambar img,.ug-gambar canvas{width:100%;height:100%;
  object-fit:contain;display:block}
.ug-ubin:hover .ug-gambar{border-color:var(--line2)}
/* Thumbnail yang gagal digambar. Ubin kosong tanpa keterangan terbaca seperti
   berkas rusak; yang sebenarnya terjadi cuma gambarnya terlalu berat untuk
   dipratinjau, dan berkasnya tetap ikut terunggah. */
.ug-ubin[data-tanpa-gambar] .ug-gambar::after{content:"pratinjau dilewati";
  position:absolute;inset:0;display:grid;place-items:center;text-align:center;
  padding:8px;font-size:11px;color:var(--ink-faint)}
.ug-tanda{position:absolute;left:5px;bottom:5px;padding:1px 6px;
  border-radius:99px;font-size:9.5px;font-weight:600;
  background:var(--aksi);color:var(--aksi-ink)}
.ug-buang{position:absolute;top:4px;right:4px;width:20px;height:20px;
  display:grid;place-items:center;border:0;border-radius:6px;cursor:pointer;
  font:inherit;font-size:13px;line-height:1;
  background:var(--panel);color:var(--ink-faint);
  box-shadow:0 0 0 1px var(--line)}
.ug-buang:hover{color:var(--stop);box-shadow:0 0 0 1px var(--stop)}
.ug-buang:focus-visible{outline:2px solid var(--ok);outline-offset:1px}
/* Disembunyikan sampai hover HANYA pada tetikus. Di layar sentuh tidak ada
   hover, dan tombol yang butuh hover di sana sama saja dengan tidak ada. */
@media (hover:hover){
  .ug-buang{opacity:0;transition:opacity .12s}
  .ug-ubin:hover .ug-buang,.ug-buang:focus-visible{opacity:1}
}
/* Dua baris lalu terpotong, dan tingginya DIKUNCI. Kalau tiap nama memakai
   jumlah baris berbeda, tiap baris kisi punya tinggi berbeda dan ubinnya
   jadi bergerigi. Nama lengkapnya ada di atribut title.
   break-word, dan bukan direction:rtl: rtl memindahkan titik ke depan
   sehingga "000036.jpg" terbaca "jpg.000036". */
.ug-nama{margin-top:6px;height:30px;overflow:hidden;
  display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;
  -webkit-box-orient:vertical;
  font-size:10.5px;line-height:1.4;color:var(--ink-dim);
  word-break:break-word;text-align:center}

/* Baris penutup kisi saat daftarnya dipotong. Di DALAM kisi dengan kolom
   penuh, supaya ia ikut tergulir bersama thumbnail terakhir dan bukan
   menggantung di luar wadah gulir. */
.ug-lebih{grid-column:1/-1;padding:14px 4px 2px;text-align:center;
  font-size:12px;color:var(--ink-dim);font-variant-numeric:tabular-nums}

/* ---------------------------------------------------------- tahap 3
   Sempit dan di tengah. Yang terjadi di tahap ini cuma satu hal, dan
   membentangkannya selebar 1.360px membuat bilah progres terlihat seperti
   garis pemisah halaman, bukan seperti pekerjaan yang sedang berjalan. */
.ug-unggah{max-width:560px;margin:28px auto 0;background:var(--panel);
  border:1px solid var(--line);border-radius:11px;padding:20px 22px 18px;
  box-shadow:0 1px 2px rgba(var(--bayang),.05)}
.ug-unggah-judul{font-size:14px;font-weight:600;color:var(--ink)}
/* Nama batch bisa panjang; dipotong elipsis supaya judulnya tidak membungkus
   tiga baris dan mendorong bilah progres ke bawah. */
.ug-unggah-judul b{display:inline-block;max-width:100%;vertical-align:bottom;
  color:var(--aksi);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ug-unggah-ket{margin-top:12px;font-size:12px;line-height:1.55;
  color:var(--ink-dim)}
.ug-unggah-aksi{display:flex;align-items:center;gap:8px;margin-top:16px}

/* ---------------------------------------------------------- layar sempit */
@media (max-width:900px){
  .ug-bawah{grid-template-columns:1fr}
}
@media (max-width:720px){
  /* Kotak drop yang tingginya sepertiga layar menyisakan terlalu sedikit
     ruang untuk panel format di bawahnya pada layar ponsel. */
  .drop.ug-drop{min-height:clamp(160px,26vh,230px);padding:22px 14px}
  .ug-drop-tombol .cta{flex:1 1 140px}
  .ug-tab{width:auto}
  .ug-tab .seg-opt{flex:1 1 auto}
  /* Ubin 112px memberi 3 kolom di layar 360px, dan 3 kolom membuat nama
     berkas terpotong di huruf keenam. 92px memberi 4 kolom yang lebih
     lapang. */
  .ug-kisi{grid-template-columns:repeat(auto-fill,minmax(92px,1fr));gap:12px 10px}
  .ug-ubin{contain-intrinsic-size:auto 130px}
  .ug-kisi-bungkus{padding:11px}
  /* Tombol utama pindah ke barisnya sendiri, selebar bilah. */
  .ug-bilah .chip.aksi{flex:1 1 100%;text-align:center}
}

/* -------------------------------------------------------------- tema gelap
   Seluruh warnanya sudah token; yang perlu diulang hanya bayangan. Bayangan
   halus di atas --panel gelap tidak terlihat, jadi panggung dan kartu
   unggahnya kehilangan pemisah dari latar. */
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .ug-panggung,
  :root:not([data-theme="light"]) .ug-unggah{box-shadow:none}
  :root:not([data-theme="light"]) .ug-panggung[data-over]{
    box-shadow:0 0 0 1px var(--ok)}
  /* Tombol buang berlatar --panel di atas gambar gelap butuh tepi yang lebih
     tegas supaya bentuknya masih terbaca. */
  :root:not([data-theme="light"]) .ug-buang{box-shadow:0 0 0 1px var(--line2)}
}
:root[data-theme="dark"] .ug-panggung,
:root[data-theme="dark"] .ug-unggah{box-shadow:none}
:root[data-theme="dark"] .ug-panggung[data-over]{box-shadow:0 0 0 1px var(--ok)}
:root[data-theme="dark"] .ug-buang{box-shadow:0 0 0 1px var(--line2)}

@media (prefers-reduced-motion:reduce){
  .ug-drop-ikon,.ug-gambar,.ug-buang{transition:none}
}

/* ==================================================== sidebar projek
   Sepadan dengan panel kiri Roboflow: satu projek punya beberapa pekerjaan
   berurutan, dan tanpa daftar ini tiap halaman terasa berdiri sendiri tanpa
   tahu sedang berada di dalam projek apa. */
/* Halaman berprojek setinggi layar, TIDAK lebih. Sidebar dulu ikut tergulir
   karena ia flex-child yang meregang setinggi dokumen: pada 50 kartu, tinggi
   .sisi terukur 3.870px dan menu projeknya hilang ke atas begitu orang
   menggulir.

   Tingginya dihitung dari sisa, bukan dari angka tetap. Kode lama memakai
   calc(100vh - 52px) padahal kepala halaman terukur 61px, dan selisih sembilan
   piksel itu tidak pernah terlihat karena tidak ada yang benar-benar
   memakainya. Dengan body sebagai kolom flex, kepala mengambil tinggi
   alaminya dan main mendapat sisanya -- berapa pun itu nanti. */
body.berprojek{height:100dvh;overflow:hidden;display:flex;flex-direction:column}
body.berprojek > header{flex:0 0 auto}
body.berprojek main{flex:1 1 auto;min-height:0;
  display:flex;gap:0;padding:0;align-items:stretch}

.sisi{flex:0 0 224px;display:flex;flex-direction:column;gap:14px;
  padding:16px 14px 22px;background:var(--panel);
  border-right:1px solid var(--line);
  /* Menggulir SENDIRI kalau menunya lebih tinggi daripada layar; kalau tidak,
     ia diam. Yang tidak boleh adalah ikut bergerak bersama foto. */
  overflow-y:auto;overscroll-behavior:contain}
.sisi-balik{font-size:11.5px;color:var(--ink-faint);text-decoration:none}
.sisi-balik:hover{color:var(--ink-dim)}

.sisi-projek{display:flex;gap:10px;align-items:center;min-width:0}
.sisi-sampul{width:44px;height:44px;flex:0 0 auto;border-radius:8px;
  object-fit:cover;background:var(--sunk);border:1px solid var(--line)}
.sisi-sampul-kosong{display:grid;place-items:center;color:var(--ink-faint);
  font-size:18px}
.sisi-teks{display:flex;flex-direction:column;min-width:0}
/* Nama projek boleh panjang. Dipotong dengan elipsis, bukan dibungkus ke baris
   kedua: sidebar yang tingginya berubah-ubah menggeser seluruh menunya. */
.sisi-nama{font-size:13.5px;color:var(--ink);white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}
.sisi-sub{font-size:11px;color:var(--ink-faint);font-variant-numeric:tabular-nums}

.sisi-menu{display:flex;flex-direction:column;gap:1px}
.sisi-judul{font-size:10px;letter-spacing:.09em;color:var(--ink-faint);
  margin:2px 0 5px;padding-left:9px}
.sisi-item{display:flex;align-items:center;gap:9px;padding:8px 9px;
  border-radius:7px;font-size:13px;color:var(--ink-dim);text-decoration:none;
  transition:.12s}
.sisi-item:hover{background:var(--hover);color:var(--ink)}
.sisi-item[data-on]{background:var(--ok-lembut);color:var(--aksi);font-weight:600}
.sisi-ikon{flex:0 0 16px;text-align:center;font-size:12px;opacity:.85}
.sisi-angka{margin-left:auto;font-size:11px;font-weight:500;
  color:var(--ink-faint);font-variant-numeric:tabular-nums}
.sisi-item[data-on] .sisi-angka{color:var(--aksi)}

/* Kolom kanan menggulir sendiri. Halaman lain (Unggah, Anotasi, Versi) tetap
   menggulir seluruh isinya di sini; halaman grid menimpanya di bawah supaya
   yang bergerak hanya fotonya. */
.sisi-isi{flex:1 1 0;min-width:0;min-height:0;overflow-y:auto;
  overscroll-behavior:contain;padding:18px 22px 60px}

/* ------------------------------------------- pilih borongan di grid
   Kotak centang duduk di sudut kartu, DI LUAR tautan gambarnya: menaruhnya
   menimpa gambar membuat klik-untuk-membuka dan klik-untuk-memilih berebut
   area yang sama, dan orang yang ingin melihat foto malah memilihnya. */
.kartu-pilih{position:absolute;top:6px;left:6px;z-index:2;display:grid;
  place-items:center;width:26px;height:26px;border-radius:7px;
  background:rgba(var(--header),.86);backdrop-filter:blur(4px);cursor:pointer}
.kartu-pilih input{width:15px;height:15px;cursor:pointer;accent-color:var(--aksi)}
.card{position:relative}
/* Kartu yang bisa dicentang (grid dataset): Shift+klik memilih rentang, jadi
   teksnya tidak boleh ikut tersorot biru. Kartu tanpa centang — mis. daftar
   projek — tidak kena, jadi namanya tetap bisa disalin. */
.card:has(.kartu-pilih){user-select:none}
/* Terpilih ditandai di kartunya sendiri, bukan cuma di bilah: pada 50 kartu,
   yang sudah dicentang berada jauh dari angka penghitungnya. Cincin teal
   mengelilingi THUMBNAIL (bukan garis sekeliling seluruh kartu): sejak kartu
   tak lagi berupa kotak, cincin di gambarnya yang terbaca sebagai "terpilih".
   Datang setelah aturan :hover .kartu-gbr supaya menang pada spesifisitas seri. */
.card.kartu-terpilih .kartu-gbr{box-shadow:0 0 0 2px var(--aksi)}

/* Mengambang, muncul hanya saat ada yang terpilih. Sepadan dengan bilah
   tindakan Roboflow: ia mendatangi orangnya, bukan menunggu ditemukan. */
.pilih-bar{position:fixed;left:50%;bottom:calc(76px + env(safe-area-inset-bottom,0px));transform:translateX(-50%);
  z-index:40;display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  max-width:min(880px,calc(100vw - 40px));
  padding:10px 14px;border-radius:12px;background:var(--panel);
  border:1px solid var(--line2);box-shadow:0 8px 28px rgba(0,0,0,.22)}
.pilih-bar .spacer{flex:1 1 auto}
.pb-n{font-size:12.5px;color:var(--ink-dim);white-space:nowrap}
.pb-n b{color:var(--ink);font-variant-numeric:tabular-nums}

/* --------------------------------------------- halaman grid: hanya foto
   Bilah kemajuan dan seluruh saringan DIAM; yang menggulir cuma kisi
   fotonya. Sebelumnya semuanya ikut naik, dan pada 11.319 gambar kotak cari
   berada puluhan layar dari kartu yang sedang dilihat. */
body.grid-tetap .sisi-isi{overflow:hidden;display:flex;flex-direction:column;
  padding-bottom:0}
body.grid-tetap .grid-atas{flex:0 0 auto}
body.grid-tetap .grid-gulung{flex:1 1 auto;min-height:0;overflow-y:auto;
  overscroll-behavior:contain;
  /* Ruang napas di bawah kartu terakhir, menggantikan padding .sisi-isi yang
     dilepas supaya bilah pager menempel di dasar kolom. */
  padding-bottom:18px}
body.grid-tetap .grid-gulung:focus-visible{outline:2px solid var(--ok);
  outline-offset:-2px}
/* Pager tidak perlu lengket lagi: ia sudah jadi dasar kolom yang tidak
   bergerak. position:sticky di dalam wadah bergulir justru membuatnya
   melayang di tengah daftar. */
body.grid-tetap .hal{position:static;flex:0 0 auto}

/* Di layar sempit sidebar jadi baris mendatar di atas isinya. Menyembunyikannya
   sama sekali akan memutus satu-satunya jalan berpindah bagian. */
@media (max-width:900px){
  body.berprojek main{flex-direction:column}
  .sisi{flex:0 0 auto;min-height:0;border-right:0;
    border-bottom:1px solid var(--line);padding:12px 16px}
  .sisi-menu{flex-direction:row;flex-wrap:wrap;gap:4px}
  .sisi-judul{display:none}
  .sisi-isi{padding:16px}
}

/* ==================================================== halaman bagi tugas */
.bg-tata{display:flex;gap:18px;align-items:flex-start}
.bg-media{flex:1 1 0;min-width:0}
.bg-kepala{display:flex;gap:12px;align-items:flex-start;justify-content:space-between;
  margin-bottom:12px}
.bg-judul{margin:0;font-size:16px}
.bg-sub{font-size:12px;color:var(--ink-dim)}

.bg-kisi{display:grid;gap:10px;
  grid-template-columns:repeat(auto-fill,minmax(110px,1fr));
  max-height:min(64vh,660px);overflow-y:auto;padding:2px}
.bg-ubin{margin:0}
.bg-gambar{display:block;aspect-ratio:1;border-radius:10px;overflow:hidden;
  background:var(--sunk);border:1px solid var(--line)}
.bg-gambar img{width:100%;height:100%;object-fit:contain;display:block}
.bg-nama{font-size:10.5px;color:var(--ink-faint);margin-top:4px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* Jejak edit per ubin: "kapan" lalu "oleh siapa", kecil dan satu baris.
   Hanya muncul kalau gambar memang punya jejak simpan. */
.jb-jejak{display:flex;gap:6px;align-items:baseline;margin-top:1px;
  font-size:9.5px;color:var(--ink-faint);white-space:nowrap;overflow:hidden}
.jb-jejak-wkt{font-variant-numeric:tabular-nums;flex:0 0 auto}
.jb-jejak-oleh{color:var(--ink-muted,var(--ink-faint));overflow:hidden;
  text-overflow:ellipsis;font-weight:600}
/* Yang di luar jatah diredupkan, bukan disembunyikan: melihat sisa yang
   tertinggal itu bagian dari keputusannya. */
.bg-ubin[data-luar]{opacity:.25}

.bg-panel{flex:0 0 330px;background:var(--panel);border:1px solid var(--line);
  border-radius:12px;padding:17px 17px 19px;
  box-shadow:0 1px 2px rgba(var(--bayang),.05);
  position:sticky;top:64px}
/* Judul bagian diberi garis bawah tipis supaya "Anggota projek" / "Bagi tugas"
   terbaca sebagai kepala bagian yang berdiri sendiri — bukan baris pertama
   yang melebur ke daftar di bawahnya. */
.bg-panel-judul{margin:0 0 13px;padding-bottom:11px;font-size:14px;
  border-bottom:1px solid var(--line)}
.bg-jumlah{display:flex;gap:9px;align-items:center}
.bg-jumlah input{width:96px}
.bg-dari{font-size:12px;color:var(--ink-faint);white-space:nowrap}
.bg-panel input[type=range]{width:100%;margin:8px 0 2px}
.bg-centang{display:flex;gap:8px;align-items:center;margin:10px 0 4px;
  font-size:12.5px;color:var(--ink-dim);cursor:pointer}
.bg-panel textarea{width:100%;resize:vertical}
.bg-pisah{height:1px;background:var(--line);margin:14px 0 12px}

.bg-orang-kepala{display:flex;align-items:center;justify-content:space-between;
  margin-bottom:7px}
.bg-orang{display:flex;flex-direction:column;gap:3px;max-height:220px;
  overflow-y:auto}
.bg-orang-baris{display:flex;gap:9px;align-items:center;width:100%;
  padding:7px 8px;border:1px solid transparent;border-radius:8px;
  background:transparent;cursor:pointer;text-align:left;color:var(--ink-dim)}
.bg-orang-baris:hover{background:var(--hover)}
.bg-orang-baris[data-on]{border-color:var(--aksi);background:var(--ok-lembut);
  color:var(--ink)}
.bg-avatar{flex:0 0 26px;height:26px;display:grid;place-items:center;
  border-radius:50%;background:var(--sunk);color:var(--ink-dim);
  font-size:11px;font-weight:600}
.bg-orang-teks{display:flex;flex-direction:column;min-width:0;flex:1 1 auto}
.bg-orang-teks b{font-size:12.5px;color:var(--ink);white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}
.bg-orang-teks .halus{margin:0;font-size:10.5px}
.bg-orang-n{flex:0 0 auto;font-size:11px;color:var(--aksi);font-weight:600;
  white-space:nowrap}

/* Kotak undang/atur: diberi tepi sendiri supaya terbaca sebagai formulir yang
   dibuka DI DALAM panel, bukan sekadar blok latar yang menyatu dengannya. */
.bg-undang{margin-top:12px;padding:13px 13px 14px;background:var(--sunk);
  border:1px solid var(--line);border-radius:10px}
.bg-undang .row{gap:6px}
.bg-tautan{display:flex;gap:6px;align-items:center;margin-top:8px}
.bg-tautan .mono{flex:1 1 auto;min-width:0;font-size:10.5px;
  color:var(--ink-dim);overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.bg-mulai{width:100%;margin-top:14px}

@media (max-width:1080px){
  .bg-tata{flex-direction:column}
  .bg-panel{flex:1 1 auto;width:100%;position:static}
  .bg-kisi{max-height:42vh;max-height:42dvh}
}
.bg-acak-ket{margin:10px 0 0;line-height:1.5}

/* ==================================================== papan anotasi */
.an-orang{display:grid;gap:10px;margin-bottom:16px;
  grid-template-columns:repeat(auto-fill,minmax(240px,1fr))}
.an-kartu-orang{background:var(--panel);border:1px solid var(--line);
  border-radius:9px;padding:11px 12px 10px}
.an-kartu-orang.an-aku{border-color:var(--aksi)}
/* Kartu "Tambah/kelola anggota" di samping kartu pemilik. */
.an-tambah-anggota{display:flex;align-items:center;gap:11px;text-decoration:none;
  border-style:dashed;color:var(--ink-dim);transition:border-color .12s,color .12s}
.an-tambah-anggota:hover{border-color:var(--aksi);color:var(--ink)}
.an-tambah-plus{display:inline-flex;align-items:center;justify-content:center;
  width:34px;height:34px;flex:none;border-radius:50%;background:var(--sunk);
  font-size:20px;line-height:1;color:var(--aksi)}
.an-tambah-teks{font-size:13px;font-weight:600;line-height:1.25}
.an-orang-atas{display:flex;gap:9px;align-items:center;margin-bottom:8px}
.an-orang-teks{display:flex;flex-direction:column;min-width:0;flex:1 1 auto}
.an-orang-teks b{font-size:12.5px;color:var(--ink)}
.an-orang-teks .halus{margin:0;font-size:10.5px}
.an-persen{font-size:13px;color:var(--aksi);font-variant-numeric:tabular-nums}
.an-orang-kaki{display:block;margin-top:6px;font-size:10.5px;
  color:var(--ink-faint)}

/* Bilahnya lebih tipis daripada di grid: di sini ia muncul berkali-kali dalam
   satu layar, dan setinggi 10px berjajar sepuluh kali jadi berisik. */
.an-lajur{height:6px}

.an-papan{display:grid;gap:14px;align-items:start;
  grid-template-columns:repeat(3,minmax(0,1fr))}
.an-kolom{background:var(--ground);border:1px solid var(--line);
  border-radius:10px;padding:12px;display:flex;flex-direction:column;gap:10px;
  min-height:180px}
.an-kolom-kepala{display:flex;align-items:center;justify-content:space-between;
  font-size:13px;color:var(--ink)}
.an-hitung{font-size:11px;color:var(--ink-faint);background:var(--sunk);
  border-radius:999px;padding:2px 8px;font-variant-numeric:tabular-nums}

.an-kartu{background:var(--panel);border:1px solid var(--line);
  border-radius:9px;padding:11px 12px 12px;display:flex;flex-direction:column;
  gap:6px}
.an-kartu.an-aku{border-color:var(--aksi)}
.an-kartu.an-selesai{opacity:.9}
.an-kartu-atas{display:flex;align-items:center;justify-content:space-between;
  gap:8px}
.an-kartu-judul{font-size:13px;font-weight:600;color:var(--ink);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.an-kartu-waktu{margin:0;font-size:10.5px}
.an-kartu-angka{display:flex;gap:10px;align-items:baseline;font-size:11.5px;
  color:var(--ink-dim);flex-wrap:wrap}
.an-kartu-angka b{color:var(--ink);font-variant-numeric:tabular-nums}
.an-kartu-angka .an-persen{margin-left:auto}
.an-catatan{margin:2px 0 0;font-size:11.5px;color:var(--ink-dim);
  background:var(--sunk);border-radius:6px;padding:7px 9px;line-height:1.5}
.an-mulai{align-self:flex-start;margin-top:2px;font-size:12px;
  color:var(--aksi);text-decoration:none;font-weight:600}
.an-mulai:hover{text-decoration:underline}
.an-buang{flex:0 0 auto;background:transparent;border:0;cursor:pointer;
  color:var(--ink-faint);font-size:15px;line-height:1;padding:2px 4px}
.an-buang:hover{color:var(--stop)}

.an-kosong{display:flex;flex-direction:column;gap:7px;padding:14px 12px;
  text-align:center;color:var(--ink-dim);font-size:12px}
.an-kosong b{color:var(--ink);font-size:12.5px}
.an-kosong .halus{margin:0}
.an-kaki{margin-top:14px}

@media (max-width:1000px){
  .an-papan{grid-template-columns:1fr}
}

/* ------------------------------------------- Anotasi: papan mengisi layar
   Dengan banyak batch, kolom "Dikerjakan" dulu menjulur jauh ke bawah dan
   SELURUH halaman ikut menggulir, sehingga bagian atas (ringkasan anggota,
   Format anotasi, Urutkan) tertutup begitu digulir. Sekarang papannya dikunci
   setinggi layar dan tiap KOLOM menggulir sendiri di dalamnya; bagian atas
   tetap terlihat. Sepola dengan body.grid-tetap pada halaman grid. */
@media (min-width:1001px){
  body.an-tetap .sisi-isi{overflow:hidden;display:flex;flex-direction:column;
    padding-bottom:0}
  /* Bagian atas dan ringkasan kaki diam; hanya papan di tengah yang menggulir. */
  body.an-tetap .an-orang,
  body.an-tetap .an-tanpa-tugas,
  body.an-tetap .an-kendali,
  body.an-tetap .an-kaki{flex:0 0 auto}
  body.an-tetap .an-kaki{margin-top:12px}
  /* Papan mengambil sisa tinggi; kolomnya diregangkan setinggi papan (bukan
     align-items:start bawaan) supaya masing-masing punya ruang menggulir. */
  body.an-tetap .an-papan{flex:1 1 auto;min-height:0;align-items:stretch;
    padding-bottom:18px}
  body.an-tetap .an-kolom{min-height:0;padding-top:0;overflow-y:auto;
    overscroll-behavior:contain}
  /* Kepala kolom tetap terlihat saat kartunya digulir. padding-top kolom
     dilepas supaya header melekat di tepi atas tanpa menyisakan strip tempat
     kartu mengintip. */
  body.an-tetap .an-kolom-kepala{position:sticky;top:0;z-index:1;
    padding-top:12px;background:var(--ground)}
}
/* Layar sempit (kolom menumpuk): batasi tinggi tiap kolom supaya satu kolom
   panjang tak lagi menjulurkan seluruh halaman; kepala kolom tetap terlihat. */
@media (max-width:1000px){
  body.an-tetap .an-kolom{max-height:68vh;padding-top:0;overflow-y:auto;
    overscroll-behavior:contain}
  body.an-tetap .an-kolom-kepala{position:sticky;top:0;z-index:1;
    padding-top:12px;background:var(--ground)}
}

/* ------------------------------------------- dialog Pre-label dengan AI */
.pl-baris{display:flex;gap:16px;flex-wrap:wrap;margin-top:10px}
.pl-baris span{display:flex;flex-direction:column;gap:4px}
.pl-baris input.chip{width:130px}
#pl-model.chip{width:100%;margin-top:4px}
.pl-bar{height:8px;border-radius:999px;background:var(--sunk);overflow:hidden;
  margin-top:8px}
.pl-bar i{display:block;height:100%;background:var(--aksi);transition:width .3s}

/* ==================================================== rincian job */
.jb-balik{display:inline-block;margin-bottom:12px}
.jb-tata{display:flex;gap:18px;align-items:flex-start}
.jb-samping{flex:0 0 264px;background:var(--panel);border:1px solid var(--line);
  border-radius:10px;padding:14px 15px 16px;position:sticky;top:64px}
.jb-judul{margin:14px 0 7px;font-size:11px;letter-spacing:.07em;
  text-transform:uppercase;color:var(--ink-faint)}
.jb-judul:first-child{margin-top:0}
.jb-angka{margin:9px 0 6px;font-size:12.5px;color:var(--ink-dim)}
.jb-angka b{color:var(--ink);font-size:15px}
.jb-daftar{list-style:none;margin:0;padding:0;display:flex;
  flex-direction:column;gap:5px;font-size:12px;color:var(--ink-dim)}
.jb-daftar li{display:flex;align-items:center;gap:8px}
.jb-titik{width:8px;height:8px;border-radius:50%;flex:0 0 auto}
.jb-titik.t-ok{background:var(--ok)}
.jb-titik.t-stop{background:var(--stop)}
/* Dataset memakai warna aksi, BUKAN --latar. Di seluruh aplikasi --latar
   berarti "gambar tanpa objek": bilah kemajuan, chip grid, dan garis tepi
   kartu semuanya memakainya begitu. Sejak ubin job juga menampilkan cap
   latar, dataset yang ikut memakainya membuat dua hal berlawanan tampak
   sama persis pada gambar yang sama. */
.jb-titik.t-ds{background:var(--aksi)}
.jb-titik.t-bg{background:var(--latar)}
.jb-orang{display:flex;gap:9px;align-items:center}

.jb-utama{flex:1 1 0;min-width:0}
.jb-tab{margin-bottom:10px}


.jb-aksi{display:flex;gap:8px;align-items:center;flex-wrap:wrap;
  padding:9px 11px;margin-bottom:11px;background:var(--panel);
  border:1px solid var(--line);border-radius:9px}
.jb-semua{display:flex;gap:8px;align-items:center;font-size:12.5px;
  color:var(--ink-dim);cursor:pointer}
.jb-baca{margin:0 0 11px}

.jb-kisi{display:grid;gap:10px;
  /* 9 gambar per baris (ubin lebih besar). minmax(0,1fr) supaya kolom tetap
     muat & mengecil di layar sempit, bukan meluber. */
  grid-template-columns:repeat(9,minmax(0,1fr));
  max-height:min(62vh,640px);overflow-y:auto;padding:2px}
/* Layar sempit: jatuh ke auto-fill supaya ubin tak jadi terlalu kecil. */
@media (max-width:1100px){
  .jb-kisi{grid-template-columns:repeat(auto-fill,minmax(130px,1fr))}
}
/* user-select:none supaya Shift+klik memilih ubin, bukan menyorot teks nama
   berkas & tombol jadi biru. Ubin memang untuk dicentang, bukan disalin. */
.jb-ubin{margin:0; user-select:none}
.jb-gambar{position:relative;display:block;aspect-ratio:1;border-radius:10px;
  overflow:hidden;background:var(--sunk);border:1px solid var(--line);
  cursor:pointer}
.jb-gambar img{width:100%;height:100%;object-fit:contain;display:block}
/* Kotak centangnya sendiri disembunyikan; yang terlihat penandanya, supaya
   sasaran kliknya seluruh gambar, bukan kotak 13 piksel di pojok. */
.jb-pilih{position:absolute;opacity:0;width:0;height:0}
.jb-tanda-pilih{position:absolute;top:6px;left:6px;width:18px;height:18px;
  display:grid;place-items:center;border-radius:5px;font-size:12px;
  background:var(--panel);border:1px solid var(--line2);color:transparent}
.jb-pilih:checked ~ .jb-tanda-pilih{background:var(--aksi);
  border-color:var(--aksi);color:var(--aksi-ink)}
.jb-pilih:checked ~ img{opacity:.82}
.jb-gambar:has(.jb-pilih:checked){border-color:var(--aksi);
  box-shadow:0 0 0 2px var(--ok-lembut)}
.jb-cap{position:absolute;left:6px;bottom:6px;font-size:9.5px;
  padding:1px 6px;border-radius:4px;color:var(--on-ink)}
.jb-cap-ok{background:var(--ok)}
/* Baris saringan: tab keadaan di kiri, saringan kelas di kanannya. Keduanya
   menyaring daftar yang sama dan harus terbaca sebagai satu baris kendali. */
.jb-baris-saring{display:flex;gap:10px;align-items:center;flex-wrap:wrap;
  margin-bottom:10px}
.jb-cap-ds{background:var(--aksi)}
.jb-cap-bg{background:var(--latar)}

@media (max-width:1000px){
  .jb-tata{flex-direction:column}
  .jb-samping{flex:1 1 auto;width:100%;position:static}
}
/* HP: baris tahap jadi satu baris penuh yang digulir mendatar, supaya tombol
   tahap tak berdesakan dengan saringan kelas dan tetap terbaca utuh. */
@media (max-width:560px){
  .seg.jb-tab{flex:1 1 100%;overflow-x:auto;-webkit-overflow-scrolling:touch}
}
/* Angka di kontrol tersegmen menempel ke katanya: "Semua50". Diberi jarak di
   sini, bukan spasi di templat, supaya tidak ada spasi yang ikut tersalin saat
   teksnya dibaca alat bantu. */
.seg-opt b{margin-left:5px}
.bg-cabut{flex:0 0 auto;background:transparent;border:0;cursor:pointer;
  color:var(--ink-faint);font-size:15px;line-height:1;padding:2px 5px}
.bg-cabut:hover{color:var(--stop)}
.bg-menunggu{cursor:default;opacity:.85}
.bg-menunggu:hover{background:transparent}

/* Atur peran anggota + scope batch (RBAC). */
.bg-atur{flex:0 0 auto;background:transparent;border:1px solid var(--garis);
  border-radius:7px;cursor:pointer;color:var(--ink-faint);font-size:11px;
  line-height:1;padding:3px 7px}
.bg-atur:hover{color:var(--ink);border-color:var(--aksi)}
.bg-scope-list{display:flex;flex-direction:column;gap:3px;margin:4px 0 2px;
  max-height:160px;overflow:auto;padding:2px}
.bg-scope-opt{font-size:12px}
#bg-labeler-opsi{margin-top:4px}
#bg-undang-nama{color:var(--ink);font-weight:600}

/* Pelabel di halaman Lihat. Sebelumnya ia cap di sudut gambar tiap kartu
   grid; di sana ia menutupi bagian foto yang justru sedang diperiksa orang,
   dan kartu jadi ramai. Satu gambar satu halaman rinci, dan di situ ia punya
   ruang untuk disebut utuh tanpa dipotong. */
.lh-pelabel{margin-top:4px;font-size:13px;color:var(--ink)}
.lh-pelabel-aku{color:var(--aksi);font-weight:600}

/* ==================================================== halaman versi */
.vs-buat{background:var(--panel);border:1px solid var(--line);border-radius:10px;
  padding:14px 15px 15px;margin-bottom:16px;max-width:760px}
.vs-buat-teks b{display:block;font-size:13.5px;margin-bottom:4px}
.vs-buat-teks .halus{margin:0;line-height:1.55}
.vs-buat-alat{display:flex;gap:8px;align-items:center;margin-top:11px;
  flex-wrap:wrap}
.vs-buat-alat #vs-rasio{width:92px;text-align:center}
.vs-buat-alat #vs-catatan{flex:1 1 220px;min-width:0}
.vs-buat-alat .cta{margin-top:0;flex:0 0 auto;padding:9px 18px;font-size:13px}

.vs-daftar{display:flex;flex-direction:column;gap:11px;max-width:760px}
.vs-kartu{background:var(--panel);border:1px solid var(--line);border-radius:10px;
  padding:12px 14px 13px;display:flex;flex-direction:column;gap:8px}
.vs-atas{display:flex;align-items:center;gap:9px}
.vs-nomor{font-size:15px;color:var(--aksi)}
.vs-waktu{margin:0}
.vs-angka{display:flex;gap:14px;flex-wrap:wrap;font-size:12px;
  color:var(--ink-dim)}
.vs-angka b{color:var(--ink);font-variant-numeric:tabular-nums}
.vs-unduh{display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin-top:2px}
/* Warna ketiga split di bilah versi, sama dengan warna yang dipakai di tempat
   lain: teal untuk train, kuning valid, biru test. */
.lajur .p-warn{background:var(--warn)}
.lajur .p-bg{background:var(--latar)}

/* Tag pada kartu grid. Dipisah dari .labs (kelas objek) karena keduanya
   menjawab pertanyaan berbeda: kelas itu ISI gambarnya, tag itu keterangan
   yang dipasang orang tentang gambarnya. */
.tg-pil{font-size:10px;padding:1px 7px;border-radius:999px;
  background:var(--warn-bg);color:var(--ink-dim);max-width:100%;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lh-tag{display:flex;gap:5px;flex-wrap:wrap;margin-bottom:8px}
.tg-pil.tg-sunting{display:inline-flex;align-items:center;gap:3px;
  font-size:11px;padding:2px 4px 2px 8px}
.tg-buang{background:transparent;border:0;cursor:pointer;color:var(--ink-faint);
  font-size:13px;line-height:1;padding:0 3px}
.tg-buang:hover{color:var(--stop)}
.lh-tag-baris input{width:100%;font-size:12px}
.lh-batch{margin:8px 0 0}

/* Baris unggahan di kolom "Belum ditugaskan". */
.an-batch{background:var(--panel);border:1px solid var(--line);border-radius:9px;
  padding:10px 12px 11px;display:flex;flex-direction:column;gap:6px}
.an-batch-atas{display:flex;align-items:center;gap:9px}
.an-batch-nama{font-size:12.5px;color:var(--ink);white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;flex:1 1 auto}
.an-batch-n{flex:0 0 auto;font-size:11px;color:var(--ink-faint);
  background:var(--sunk);border-radius:999px;padding:2px 8px;
  font-variant-numeric:tabular-nums}
.an-lihat-semua{font-size:12px;color:var(--aksi);text-decoration:none;
  font-weight:600;padding:0 2px 2px}
.an-lihat-semua:hover{text-decoration:underline}

/* Menu kelola di papan anotasi. Tata letak baris "Urutkan" sendiri ada di
   blok .an-kendali, sebaris dengan "Format anotasi". */
.an-menu{position:relative;flex:0 0 auto}
.an-titik{background:transparent;border:0;cursor:pointer;color:var(--ink-faint);
  font-size:16px;line-height:1;padding:2px 6px;border-radius:6px}
.an-titik:hover{background:var(--hover);color:var(--ink)}
.an-menu .menu-isi{display:none;position:absolute;right:0;top:26px;z-index:30;
  min-width:180px;flex-direction:column;gap:1px;padding:5px;
  background:var(--panel);border:1px solid var(--line2);border-radius:9px;
  box-shadow:0 8px 22px rgba(var(--bayang),.14)}
.an-menu[data-buka] .menu-isi{display:flex}
.menu-aksi{display:block;width:100%;text-align:left;padding:7px 9px;
  border:0;border-radius:6px;background:transparent;cursor:pointer;
  font-size:12.5px;color:var(--ink-dim);text-decoration:none}
.menu-aksi:hover{background:var(--hover);color:var(--ink)}
.menu-aksi.menu-bahaya:hover{background:var(--bahaya-lembut);color:var(--stop)}

/* ==================================================== panduan
   Cangkang bersama: kanvas menjelaskan pintasan, grid menjelaskan arti tiap
   chip dan saringan. Gayanya di sini, bukan di label.css, karena halaman
   grid tidak memuat berkas itu — dan panduan tanpa tata letak terbaca
   sebagai satu kolom teks yang justru lebih sulit dari chip yang
   dijelaskannya. */
.panduan-kotak{width:min(860px,100%); max-height:86vh;max-height:86dvh; display:flex;
  flex-direction:column; padding:0}
.panduan-atas{display:flex; align-items:center; gap:12px;
  padding:15px 18px 12px; border-bottom:1px solid var(--line)}
.panduan-atas h3{margin:0; flex:1; font-size:15px}
.panduan-cari{padding:11px 18px 0}
.panduan-cari input{width:100%; padding:7px 10px; font-size:13px;
  border:1px solid var(--line2); border-radius:6px;
  background:var(--raised); color:var(--ink)}
.panduan-cari input:focus{outline:2px solid var(--ok); outline-offset:-1px}

.panduan-isi{overflow-y:auto; padding:14px 18px 18px;
  display:grid; gap:18px 26px; align-content:start}
@media (min-width:660px){ .panduan-isi{grid-template-columns:1fr 1fr} }
.panduan-isi section[hidden]{display:none}
.panduan-isi h4{margin:0 0 7px; font-size:11px; font-weight:600;
  letter-spacing:.08em; text-transform:uppercase; color:var(--ok)}
.panduan-isi dl{margin:0; display:grid; grid-template-columns:auto 1fr;
  gap:5px 12px; align-items:baseline}
.panduan-isi dt{font-size:11.5px; color:var(--ink-faint); white-space:nowrap;
  text-align:right}
/* Istilah berupa KATA, bukan tombol: dibiarkan membungkus dan dirata-kiri.
   Nama seperti "mask memenuhi frame" yang dipaksa satu baris rata kanan
   mendorong kolom penjelasannya jadi sempit sekali. */
.panduan-isi.panduan-kata dl{grid-template-columns:minmax(96px,auto) 1fr}
.panduan-isi.panduan-kata dt{white-space:normal; text-align:left;
  color:var(--ink); font-weight:600}
.panduan-isi dd{margin:0; font-size:12.5px; color:var(--ink-dim); line-height:1.45}
.panduan-isi dd em{font-style:normal; color:var(--ink); font-weight:600}
.panduan-isi dt[hidden],.panduan-isi dd[hidden]{display:none}


/* ==================================================== wizard "Buat versi"
   Enam langkah bertumpuk, satu terbuka pada satu waktu. Bentuknya mengikuti
   Roboflow: nomor di kiri, ringkasan pilihan di kanan judul saat langkahnya
   tertutup, sehingga seluruh resep terbaca sekali pandang tanpa membuka
   apa pun. */
.vs-kepala{display:flex;justify-content:flex-end;margin-bottom:14px}
/* Wizard adalah HALAMAN, bukan panel: dipusatkan dan dibatasi lebarnya.
   Formulir selebar 1680px tidak terbaca sebagai formulir. */
.wz{max-width:900px;margin:0 auto 18px;
  border:1px solid var(--line);border-radius:12px;background:var(--panel);
  padding:16px 18px 18px}
.wz-atas{display:flex;align-items:center;gap:10px;margin-bottom:12px}
.wz-atas b{font-size:15px}
.wz-atas .chip{margin-left:auto}
.wz-nama{display:block;font-size:12px;color:var(--ink-faint);margin-bottom:14px}
.wz-nama input{display:block;margin-top:5px;width:100%;max-width:320px;
  border:1px solid var(--line2);background:var(--raised);color:var(--ink);
  border-radius:8px;padding:7px 10px;font-family:inherit;font-size:13px}
.wz-nama input[readonly]{color:var(--ink-dim);background:var(--sunk)}

.wz-langkah{list-style:none;margin:0;padding:0;border-top:1px solid var(--line)}
.wz-item{border-bottom:1px solid var(--line)}
.wz-judul{display:flex;align-items:center;gap:11px;padding:14px 2px;font-size:13.5px}
.wz-no{flex:0 0 auto;width:24px;height:24px;border-radius:7px;display:grid;
  place-items:center;font-size:12px;font-weight:600;
  background:var(--sunk);color:var(--ink-faint)}
.wz-item[data-buka] .wz-no{background:var(--ink);color:var(--on-ink)}
.wz-item[data-lewat] .wz-no{background:var(--ok-lembut);color:var(--aksi)}
.wz-ringkas{font-size:12px;color:var(--ink-dim)}
.wz-ubah{display:none}
.wz-item[data-lewat] .wz-ubah{display:inline-block}
.wz-isi{display:none;padding:0 2px 18px 35px}
.wz-item[data-buka] .wz-isi{display:block}
.wz-isi .halus{margin-top:0;margin-bottom:12px;line-height:1.5}
/* align-items:center WAJIB. Tanpanya flex meregangkan tombol setinggi
   baris, dan .chip yang radiusnya 999px berubah jadi lingkaran besar. */
.wz-aksi{display:flex;align-items:center;gap:10px;margin-top:18px}

.wz-angka{display:flex;flex-wrap:wrap;gap:8px 20px;font-size:12.5px;
  color:var(--ink-dim);margin-bottom:6px}
.wz-angka b{color:var(--ink);font-variant-numeric:tabular-nums}
.wz-angka .wz-buang{color:var(--awas,#b45309);font-weight:600}
.wz-angka .wz-buang-banyak{color:var(--bahaya,#b42318)}
.wz-filter-awas{color:var(--bahaya,#b42318);font-weight:600;
  background:var(--bahaya-lembut,#fdeceb);border-radius:6px;padding:6px 8px}
/* Filter sumber: buang batch/tag tertentu untuk versi ini (sumber tak diubah). */
.wz-filter{margin:2px 0 12px;border:1px solid var(--line);border-radius:8px;
  padding:8px 12px;background:var(--sunk)}
.wz-filter>summary{cursor:pointer;font-size:12.5px;font-weight:600;color:var(--ink-dim)}
.wz-filter .halus{margin:8px 0 10px}
.wz-filter-kat{display:flex;gap:8px;align-items:flex-start;margin:0 0 12px;
  padding:8px 10px;background:var(--panel);border:1px solid var(--line);
  border-radius:7px;font-size:12.5px;line-height:1.45;cursor:pointer}
.wz-filter-kat input{margin-top:2px;flex:0 0 auto}
.wz-filter-kat b{color:var(--ink)}
.wz-filter-grup{margin:0 0 10px}
.wz-filter-jdl{display:block;font-size:11px;text-transform:uppercase;
  letter-spacing:.04em;color:var(--ink-faint);margin:0 0 5px}
.wz-filter-baris{display:inline-flex;align-items:center;gap:6px;font-size:12.5px;
  color:var(--ink);margin:0 14px 6px 0}
.wz-filter-baris i{font-style:normal;color:var(--ink-faint);
  font-variant-numeric:tabular-nums}
.wz-rasio{display:flex;gap:14px;flex-wrap:wrap;margin-bottom:10px}
.wz-rasio label{font-size:12px;color:var(--ink-faint)}
.wz-rasio input{width:72px;margin-left:6px;border:1px solid var(--line2);
  background:var(--raised);color:var(--ink);border-radius:7px;padding:5px 8px;
  font-family:inherit;font-size:12.5px;text-align:center}
.wz-belah{display:flex;flex-direction:column;gap:8px;margin:14px 0 10px}
.wz-pilihan{display:flex;gap:9px;align-items:flex-start;font-size:12.5px;
  color:var(--ink-dim);line-height:1.5;cursor:pointer}
.wz-pilihan b{color:var(--ink)}
.wz-belah-alat{display:flex;gap:8px}
.wz-salin{display:inline-block;font-size:12px;color:var(--ink-faint);margin-bottom:12px}
.wz-salin input{width:70px;margin-left:8px;border:1px solid var(--line2);
  background:var(--raised);color:var(--ink);border-radius:7px;padding:5px 8px;
  font-family:inherit;font-size:12.5px;text-align:center}
.wz-fase{margin-top:16px;padding-top:14px;border-top:1px solid var(--line);
  display:flex;flex-direction:column;gap:9px}

/* daftar langkah yang aktif */
.op-daftar{border:1px solid var(--line);border-radius:9px;overflow:hidden}
/* Petak, bukan daftar menurun. Enam belas langkah sebagai baris bertumpuk
   memanjang 600px ke bawah dan mendorong seluruh sisa wizard keluar layar;
   sebagai petak dua-tiga kolom ia muat dalam satu pandangan. Lebar minimum
   240px: cukup untuk nama terpanjang ("Hue / saturasi / nilai") tanpa
   terpotong, dan di layar sempit ia jatuh sendiri jadi satu kolom. */
/* Garis pemisahnya dari TEPI tiap sel, bukan dari celah grid berlatar garis.
   Dengan celah, sel yang tidak terisi di baris terakhir menampilkan warna
   latar itu sebagai blok abu-abu — petak 16 isi dalam 3 kolom menyisakan dua
   sel kosong, dan keduanya terlihat seperti kotak rusak. Margin negatifnya
   menarik garis kolom terakhir dan baris terakhir ke bawah tepi wadah, supaya
   tidak jadi garis ganda. */
.op-daftar-isi{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));
  margin:0 -1px -1px 0}
.op-baris{display:flex;align-items:center;gap:10px;padding:9px 12px;
  background:var(--panel);font-size:12.5px;min-width:0;
  border-right:1px solid var(--line);border-bottom:1px solid var(--line)}
.op-nama{flex:1 1 auto;min-width:0;font-weight:600;color:var(--ink);
  display:flex;align-items:center;gap:7px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Parameter, rata kanan. Operasi tanpa parameter meninggalkannya KOSONG —
   dulu ia jatuh ke kalimat deskripsi, dan 16 baris augmentasi berubah jadi
   16 kalimat penuh. Deskripsi adalah bahan untuk MEMILIH; tempatnya di
   popup, bukan di daftar hal yang sudah dipilih. */
.op-ket{flex:0 1 auto;text-align:right;color:var(--ink-faint);font-size:11.5px;
  font-variant-numeric:tabular-nums;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.op-kosong{padding:12px;color:var(--ink-faint);font-size:12.5px;margin:0}


/* tinjauan & perkiraan */
/* Dipakai wizard DAN panel detail versi. Sebelumnya panel detail memakai
   nama kelas .vs-tinjau yang tidak pernah punya aturan apa pun, dan
   barisnya menempel jadi "Preprocessing—". */
.wz-tinjau,.vs-tinjau{border:1px solid var(--line);border-radius:9px;
  overflow:hidden;margin-bottom:14px}
.wz-tinjau div,.vs-tinjau div{display:flex;gap:14px;padding:9px 12px;font-size:12.5px;
  border-bottom:1px solid var(--line)}
.wz-tinjau div:last-child,.vs-tinjau div:last-child{border-bottom:0}
.wz-tinjau span,.vs-tinjau span{flex:0 0 170px;color:var(--ink-faint)}
.wz-tinjau b,.vs-tinjau b{color:var(--ink);font-weight:600}
/* Fase yang dimatikan tetap disebut, di barisnya sendiri: menghilangkannya
   dari daftar terbaca sebagai fitur yang memang tidak dipunyai — persis salah
   paham yang membuat orang mengira balancer belum ada di sini. */
.wz-fase-tinjau{font-weight:500}
.wz-fase-mati{display:block;margin-top:3px;font-weight:400;
  color:var(--ink-faint)}
.wz-perkira{font-size:12.5px;color:var(--ink-dim);line-height:1.6;
  background:var(--sunk);border-radius:9px;padding:11px 13px;margin-bottom:14px}
.wz-perkira b{color:var(--ink)}
.wz-perkira .halus{display:block;margin-top:6px}
/* Kelas final (hasil): daftar nama kelas yang benar-benar masuk model, dengan
   id (indeks) data.yaml. Ditaruh DI ATAS rincian supaya hal paling penting --
   "model nanti kenal kelas apa saja" -- terbaca lebih dulu. */
.wz-kf{border:1px solid var(--line);border-radius:9px;padding:11px 13px;
  margin-bottom:14px;background:var(--sunk)}
.wz-kf-judul{font-size:12.5px;color:var(--ink-faint);margin-bottom:9px}
.wz-kf-judul b{color:var(--ink);font-weight:700}
.wz-kf-chips{display:flex;flex-wrap:wrap;gap:6px}
.wz-kf-chip{display:inline-flex;align-items:center;gap:6px;font-size:12.5px;
  color:var(--ink);background:var(--bg);border:1px solid var(--line);
  border-radius:999px;padding:3px 10px 3px 4px}
.wz-kf-chip i{display:inline-flex;align-items:center;justify-content:center;
  min-width:19px;height:19px;padding:0 5px;border-radius:999px;font-style:normal;
  font-size:11px;font-weight:700;background:var(--aksi);color:var(--aksi-ink)}
.wz-kf-chips .halus{color:var(--ink-faint)}

/* panel kerja */
.wz-kerja{padding:18px 2px 4px}
.wz-kerja b{display:block;font-size:14px;margin-bottom:6px}
.wz-kerja .prog{margin:12px 0 8px}
.wz-kerja .chip{margin-top:10px}

/* Cap resep di kartu versi: yang membedakan dua versi bukan angkanya
   melainkan resepnya, jadi resepnya yang harus terbaca sekilas. */
.vs-cap{display:flex;flex-wrap:wrap;gap:6px;margin:10px 0 4px}
.vs-pil{font-size:11px;color:var(--ink-dim);background:var(--sunk);
  border-radius:999px;padding:3px 9px}
.vs-pil-ok{color:var(--aksi);background:var(--ok-lembut)}

/* ================================================ daftar versi & detail
   Dua kolom: seluruh versi terbaca sekaligus di kiri, satu terbuka di kanan.
   Sebelumnya kartu bertumpuk selebar halaman, dan membandingkan dua versi
   berarti menggulung bolak-balik. */
.vs-tata{display:flex;gap:18px;align-items:flex-start}
.vs-kolom{flex:0 0 250px;border:1px solid var(--line);border-radius:11px;
  background:var(--panel);overflow:hidden;position:sticky;top:66px}
.vs-kolom-judul{font-size:10.5px;text-transform:uppercase;letter-spacing:.09em;
  color:var(--ink-faint);font-weight:600;padding:11px 13px 8px}
.vs-ring{display:flex;flex-direction:column;gap:5px;width:100%;text-align:left;
  border:0;border-top:1px solid var(--line);background:transparent;
  padding:11px 13px;cursor:pointer;font-family:inherit}
.vs-ring:hover{background:var(--hover)}
.vs-ring[data-on]{background:var(--ok-lembut);box-shadow:inset 3px 0 0 var(--aksi)}
.vs-ring-atas{display:flex;align-items:center;gap:9px}
.vs-nomor{font-size:11px;font-weight:700;background:var(--ink);color:var(--on-ink);
  border-radius:5px;padding:2px 6px;flex:0 0 auto}
.vs-waktu{font-size:12px;color:var(--ink-dim);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.vs-ring-cap{display:flex;flex-wrap:wrap;gap:4px}
.vs-ring-oleh{font-size:11px;color:var(--ink-faint)}

.vs-detail{flex:1 1 auto;min-width:0;border:1px solid var(--line);
  border-radius:11px;background:var(--panel);padding:16px 18px 20px}
.vs-detail-muat{color:var(--ink-faint);font-size:12.5px;padding:20px 0}
.vs-detail-atas{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  padding-bottom:12px;border-bottom:1px solid var(--line)}
.vs-detail-judul{font-size:15px;font-weight:600;color:var(--ink)}
.vs-bagian{margin-top:18px}
.vs-bagian h4{font-size:10.5px;text-transform:uppercase;letter-spacing:.09em;
  color:var(--ink-faint);font-weight:600;margin-bottom:9px}
.vs-buka-hasil{float:right;text-transform:none;letter-spacing:0;font-size:12px;
  color:var(--aksi);text-decoration:none;font-weight:600}
.vs-buka-hasil:hover{text-decoration:underline}
.vs-bagian .halus{margin-top:9px}

/* tiga kotak pembagian, warnanya sama dengan bilah kemajuan di grid */
.vs-split{display:flex;gap:10px;flex-wrap:wrap}
.vs-kotak{flex:1 1 140px;border:1px solid var(--line2);border-radius:9px;
  padding:11px 13px;display:flex;flex-direction:column;gap:3px}
.vs-kotak span{font-size:10px;letter-spacing:.09em;color:var(--ink-faint);
  font-weight:600}
.vs-kotak b{font-size:19px;font-variant-numeric:tabular-nums;color:var(--ink)}
.vs-kotak em{font-style:normal;font-size:11.5px;color:var(--ink-faint)}
.vs-k-train{border-color:var(--ok-pudar)}
.vs-k-valid{border-color:var(--warn)}
.vs-k-test{border-color:var(--latar-lembut)}

/* deretan contoh gambar hasil */
.vs-strip{display:flex;gap:7px;overflow-x:auto;padding-bottom:4px}
.vs-strip img{flex:0 0 auto;width:96px;height:96px;object-fit:cover;
  border-radius:7px;background:var(--sunk)}

/* Objek per kelas: bagan batang, bukan deretan angka.
   Yang ditanyakan orang saat melihatnya bukan "berapa botol" melainkan
   "timpang tidak?", dan deretan angka diam soal itu — 12.031 lawan 1.204
   harus dibagi di kepala dulu sebelum ketimpangannya terasa. */
.vk-bagan{display:flex;flex-direction:column;gap:2px}
.vk-baris{display:grid;grid-template-columns:minmax(88px,1.1fr) 3fr auto auto;
  align-items:center;gap:10px;padding:5px 8px;border-radius:7px;
  font-size:12.5px}
.vk-baris:hover{background:var(--hover)}
.vk-nama{display:flex;align-items:center;gap:7px;color:var(--ink-dim);
  min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Titik warna memakai rumus yang sama dengan kanvas dan grid: satu kelas
   berwarna sama di mana pun, jadi warnanya penanda, bukan hiasan. */
.vk-nama i{width:9px;height:9px;flex:0 0 auto;border-radius:3px}
.vk-nama i.vk-kosong{background:var(--latar);opacity:.55}
/* Alur batangnya diberi latar samar supaya panjang nol tetap terbaca sebagai
   "nol", bukan sebagai baris yang gagal digambar. */
.vk-bar{height:9px;border-radius:99px;background:var(--sunk);overflow:hidden}
.vk-bar i{display:block;height:100%;border-radius:99px;min-width:2px;
  transition:width .18s ease-out}
.vk-n{color:var(--ink);font-variant-numeric:tabular-nums;
  min-width:52px;text-align:right}
.vk-persen{font-style:normal;color:var(--ink-faint);font-size:11.5px;
  font-variant-numeric:tabular-nums;min-width:44px;text-align:right}
/* Sampel negatif dipisah garis: ia BUKAN kelas, dan menaruhnya dalam deret
   yang sama membuat persentase seluruh kelas ikut salah. */
.vk-negatif{margin-top:5px;padding-top:8px;border-top:1px solid var(--line)}
.vk-negatif .vk-nama{font-style:italic}

/* ============================ Tab Versi VIDEO (aksi_versi)
   Kontrol wizard & detail yang khas klip. Sebagian besar kelas (wz-*, vs-*,
   vk-*, sk-*) dipinjam dari wizard gambar di atas; yang di sini hanya tambahan
   yang tidak ada padanannya di sana. */
#akv-isi .vs-kepala{justify-content:flex-start;gap:10px;flex-wrap:wrap;align-items:center}
.akv-impor-lbl{cursor:pointer}
.akv-impor-path{display:flex;gap:8px;margin:0 0 10px;max-width:640px}
.akv-impor-path input{flex:1 1 auto;border:1px solid var(--line2);border-radius:8px;
  padding:7px 10px;font:inherit;font-size:13px;background:var(--panel);color:var(--ink)}

/* langkah 1: hitung klip sumber */
.akv-sum-besar{font-size:14px;color:var(--ink)}
.akv-sum-besar b{font-size:22px;font-variant-numeric:tabular-nums}
.akv-sum-kelas-baris{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}
.akv-sum-kelas{font-size:12px;color:var(--ink-dim);background:var(--sunk);
  border-radius:999px;padding:4px 11px}
.akv-sum-kelas b{color:var(--ink);font-variant-numeric:tabular-nums}
.wz-peringatan{color:var(--warn);margin-top:10px}

/* langkah 4: pratinjau varian augmentasi + blok penyeimbang */
.akv-varian{display:flex;flex-wrap:wrap;gap:5px;margin:4px 0 14px}
.akv-varian-judul{flex:1 0 100%;margin-bottom:2px}
.akv-varian-chip{font-size:10.5px;color:var(--ink-faint);background:var(--sunk);
  border-radius:5px;padding:2px 7px;
  font-family:ui-monospace,"JetBrains Mono","SF Mono",Menlo,monospace}
.wz-penyeimbang{margin-top:16px;padding-top:14px;border-top:1px solid var(--line)}

/* detail versi: catatan + contoh klip */
.vs-contoh-nota{margin:10px 0 0;font-style:italic}
.vs-contoh{margin:9px 0 0;padding-left:18px;line-height:1.7;
  font-family:ui-monospace,"JetBrains Mono","SF Mono",Menlo,monospace;font-size:11.5px}

/* Tab Training VIDEO (aksi_latih): baris tombol Jalankan di bawah form. Sisanya
   (tr-mesin, tr-form, tr-grid, tr-kartu, tr-pil, ak-eval*) dipinjam dari jalur
   gambar + versi aksi di atas. */
.ak-tr-jalankan{margin-top:16px;display:flex;align-items:center;gap:12px;flex-wrap:wrap}
/* Kurva akurasi per-epoch di panel rincian training klip (SVG sederhana). */
.tr-kurva-grid{stroke:var(--line);stroke-width:1}
.tr-kurva-lbl{fill:var(--ink-faint);font-size:10px;
  font-family:ui-monospace,"JetBrains Mono","SF Mono",Menlo,monospace}
.tr-kurva-garis{stroke:var(--aksi);stroke-width:2;stroke-linejoin:round;stroke-linecap:round}

/* ============================ Ingest klip (panel "Tambah klip" di Unggah video) */
.ik-panel{margin-bottom:26px}
.ik-kartu{border:1px solid var(--line);border-radius:11px;background:var(--panel);
  padding:14px 16px;margin-bottom:12px}
.ik-kartu-judul{font-weight:600;font-size:14px;margin-bottom:10px;
  display:flex;align-items:center;gap:8px}
.ik-no{display:inline-grid;place-items:center;width:20px;height:20px;border-radius:50%;
  background:var(--ink);color:var(--on-ink);font-size:11px;font-weight:700;flex:0 0 auto}
.ik-tabisi{margin:10px 0}
.ik-baris{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.ik-baris input[type=url],.ik-baris input[type=text]{flex:1 1 240px;
  border:1px solid var(--line2);border-radius:8px;padding:7px 11px;font:inherit;
  font-size:13px;background:var(--raised);color:var(--ink)}
.ik-kecil{width:70px;flex:0 0 auto;border:1px solid var(--line2);border-radius:8px;
  padding:7px 9px;font:inherit;font-size:13px;background:var(--raised);color:var(--ink)}
.ik-prog{display:block;margin:8px 0}
.ik-maju{display:inline-block;margin-left:8px}
.ik-sumber-daftar{margin-top:12px}
.ik-sumber-judul{margin-bottom:6px}
.ik-sumber-baris{display:flex;align-items:center;justify-content:space-between;
  gap:10px;padding:8px 11px;border:1px solid var(--line);border-radius:8px;margin-bottom:6px}
.ik-sumber-baris.ik-on{border-color:var(--aksi);background:var(--ok-lembut)}
.ik-sumber-nama{font-size:13px;color:var(--ink-dim);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.ik-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px}
.ik-f{display:flex;flex-direction:column;gap:4px;font-size:12px;color:var(--ink-faint)}
.ik-f input,.ik-f select{border:1px solid var(--line2);border-radius:8px;padding:6px 9px;
  font:inherit;font-size:13px;background:var(--raised);color:var(--ink)}
.ik-f.ik-cek{flex-direction:row;align-items:center;gap:7px}
.ik-aksi{display:flex;align-items:center;gap:12px;margin-top:14px;flex-wrap:wrap}
/* Projek video memakai panel "Tambah klip"; bagian wizard GAMBAR (nama/tag,
   drop zone, daftar format, tahap periksa/unggah) disembunyikan — hanya impor
   dataset klip jadi dari server yang tetap. Disembunyikan via CSS (bukan Jinja)
   agar elemen tetap di DOM dan unggah.js tak null-ref. */
#ug-halaman[data-jenis="video"] .ug-isian,
#ug-halaman[data-jenis="video"] .ug-drop,
#ug-halaman[data-jenis="video"] .ug-format,
#ug-halaman[data-jenis="video"] #ug-tahap2,
#ug-halaman[data-jenis="video"] #ug-tahap3{display:none!important}
.vk-total{float:right;font-weight:400;font-size:11.5px;color:var(--ink-faint);
  text-transform:none;letter-spacing:0}
.vk-rasio{margin-top:9px;font-size:11.5px;line-height:1.5;color:var(--ink-faint)}
.vk-rasio b{color:var(--ink-dim);font-weight:600}
.vk-timpang{color:var(--warn)}
.vk-timpang b{color:var(--warn)}

@media (max-width:900px){
  .vs-tata{flex-direction:column}
  .vs-kolom{flex:1 1 auto;width:100%;position:static}
}
.vs-ring-hilang{font-size:11px;color:var(--warn)}

/* ================================================ halaman Versi: batas ukur
   Kolom isi selebar layar membuat setiap kotak di dalamnya tampak nemplok di
   salah satu sisi. Batasnya dipasang sekali di sini, bukan ditambal per kotak
   dengan atribut style. */
#vs-isi{max-width:1180px;margin-inline:auto}
#vs-isi .pkosong{margin-top:60px}

/* Wizard adalah halaman, bukan panel. Selama ia terbuka tidak ada daftar versi
   dan tidak ada kotak "Belum ada versi" di bawahnya: dua hal yang menjawab
   pertanyaan berbeda tidak boleh bertumpuk di satu layar. DOM-nya tidak
   dibongkar, cuma disembunyikan — jadi Batal lalu masuk lagi tidak menghapus
   satu pun isian. */
#vs-isi[data-mode="wizard"] .vs-kepala,
#vs-isi[data-mode="wizard"] .pkosong,
#vs-isi[data-mode="wizard"] .vs-tata{display:none}

/* ------------------------------------------- baris tombol tiap langkah
   Sengaja BUKAN .chip/.cta: .cta membawa margin-top:26px dari pemakaiannya di
   .card-form, dan .chip radiusnya 999px. Keduanya digabung di satu baris flex
   menghasilkan tombol Kembali berbentuk lingkaran setinggi 70px. */
.wz-tbl{height:36px;padding:0 20px;border-radius:8px;
  font-family:inherit;font-size:13px;font-weight:600;line-height:1;
  cursor:pointer;display:inline-flex;align-items:center;gap:7px;
  border:1px solid transparent;transition:.14s}
.wz-tbl-pri{background:var(--aksi);color:var(--aksi-ink);border-color:var(--aksi)}
.wz-tbl-pri:hover{filter:brightness(1.08)}
.wz-tbl-pri:disabled{opacity:.55;cursor:default;filter:none}
.wz-tbl-sek{background:var(--panel);color:var(--ink-dim);border-color:var(--line2)}
.wz-tbl-sek:hover{background:var(--hover);color:var(--ink);border-color:var(--ink-faint)}
.wz-tbl:focus-visible{outline:2px solid var(--ok);outline-offset:2px}

/* ------------------------------------------------------------------ saklar
   Keadaan nyala memakai pasangan --aksi/--aksi-ink: satu-satunya pasangan di
   berkas ini yang komentarnya sendiri menjanjikan kontras di kedua tema.
   Keadaan mati mengandalkan --sunk yang SELALU lebih gelap daripada --raised
   di kedua tema, jadi knobnya selalu lebih terang daripada lubangnya. */
.sk{display:flex;align-items:center;gap:12px;padding:0 4px;min-height:44px;
  cursor:pointer;position:relative}
.sk-in{position:absolute;opacity:0;width:0;height:0}
.sk-track{flex:0 0 auto;width:34px;height:20px;border-radius:999px;
  background:var(--sunk);box-shadow:inset 0 0 0 1px var(--line2);
  display:block;position:relative;transition:background .14s,box-shadow .14s}
.sk-knob{position:absolute;top:2px;left:2px;width:16px;height:16px;
  border-radius:50%;background:var(--raised);box-shadow:0 0 0 1px var(--line2);
  transition:transform .14s,background .14s,box-shadow .14s}
.sk-in:checked ~ .sk-track{background:var(--aksi);box-shadow:inset 0 0 0 1px var(--aksi)}
.sk-in:checked ~ .sk-track .sk-knob{transform:translateX(14px);
  background:var(--aksi-ink);box-shadow:none}
.sk-in:focus-visible ~ .sk-track{outline:2px solid var(--ok);outline-offset:2px}
.sk-teks{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:2px}
.sk-teks b{font-size:13px;color:var(--ink);font-weight:600}
.sk-teks em{font-style:normal;font-size:11.5px;color:var(--ink-faint);line-height:1.4}
@media (prefers-reduced-motion:reduce){.sk-track,.sk-knob{transition:none}}

/* Di popup saklarnya di KANAN: mata menyapu nama dulu, keadaan belakangan. */
.op-gulung .sk .sk-track{order:2}
.op-gulung .sk .sk-teks{order:1}

/* ------------------------------------------- daftar langkah aktif (3 & 4) */
/* Penanda hanya pada yang MENYIMPANG dari bawaan. Lencana yang muncul di
   setiap baris tidak membawa informasi apa pun — 16 kali kata yang sama tidak
   memberi tahu siapa pun sesuatu yang baru. Yang dicari orang saat menengok
   resep adalah "apa yang berbeda?", dan justru itu yang dulu tidak terlihat. */
.op-tanda{font-size:10px;letter-spacing:.04em;font-weight:600;
  color:var(--aksi);background:var(--ok-lembut);border-radius:5px;padding:1px 6px}
.op-mati{flex:0 0 auto;width:24px;height:24px;border:0;border-radius:6px;
  background:transparent;color:var(--ink-faint);font-size:15px;line-height:1;
  cursor:pointer;opacity:.45;transition:.12s;display:grid;place-items:center}
.op-baris:hover .op-mati,.op-mati:focus-visible{opacity:1}
.op-mati:hover{color:var(--stop);background:var(--bahaya-lembut)}
.op-mati:focus-visible{outline:2px solid var(--ok);outline-offset:-2px}

.op-atur{margin-top:10px;border:1px solid var(--line2);background:var(--panel);
  color:var(--aksi);border-radius:8px;padding:8px 14px;font-family:inherit;
  font-size:12.5px;font-weight:600;cursor:pointer}
.op-atur:hover{background:var(--ok-lembut);border-color:var(--ok-pudar)}
.op-atur:focus-visible{outline:2px solid var(--ok);outline-offset:2px}

/* Tanpa ini, mematikan langkah adalah pintu satu arah yang tidak menyebutkan
   jalan pulangnya. */
.op-balik{margin-top:8px;font-size:11.5px;color:var(--ink-faint)}
.op-balik button{border:0;background:transparent;color:var(--aksi);
  font:inherit;cursor:pointer;text-decoration:underline;padding:0 2px}

/* --------------------------------------------------------- popup operasi
   Kotaknya jadi kolom tiga bagian: judul tetap, isi menggulung, kaki tetap.
   .dlg-kotak biasa menggulung SELURUH kotak, jadi judul dan tombol tutup ikut
   hilang ke atas begitu isinya 21 baris. */
#op-dlg .dlg-kotak{width:min(560px,100%);max-height:min(660px,84vh);max-height:min(660px,84dvh);
  padding:0;display:flex;flex-direction:column;overflow:hidden}
.op-dlg-atas{flex:0 0 auto;padding:18px 20px 12px;border-bottom:1px solid var(--line)}
.op-dlg-atas h3{margin:0 0 4px}
.op-cari{width:100%;margin-top:12px;padding:7px 10px;font-size:13px;
  font-family:inherit;background:var(--ground);color:var(--ink);
  border:1px solid var(--line2);border-radius:7px}
.op-cari:focus{outline:2px solid var(--ok);outline-offset:-1px}

.op-cacah{float:right;font-size:11.5px;color:var(--ink-faint);
  font-variant-numeric:tabular-nums}
.op-gulung{flex:1 1 auto;overflow-y:auto;overscroll-behavior:contain;
  padding:0 20px 8px}
.op-grup{position:sticky;top:0;z-index:1;background:var(--panel);
  padding:12px 0 6px;font-size:10px;text-transform:uppercase;
  letter-spacing:.09em;color:var(--ink-faint);font-weight:600}
.op-gulung .sk{border-bottom:1px solid var(--line)}
.op-gulung .sk:last-child{border-bottom:0}
.op-gulung .sk:hover{background:var(--hover)}
.op-hampa{padding:18px 0;font-size:12.5px;color:var(--ink-faint);text-align:center}

/* ------------------------------------------------ baris pilihan + tombol angka
   Saklarnya di dalam <label>, tombol angkanya di LUAR: label meneruskan setiap
   klik ke input miliknya, jadi tombol di dalamnya akan ikut membalik saklar. */
.op-pil{display:flex;align-items:center;gap:8px;border-bottom:1px solid var(--line)}
.op-pil:last-child{border-bottom:0}
.op-pil:hover{background:var(--hover)}
.op-pil .sk{flex:1 1 auto;min-width:0;border-bottom:0}
.op-pil .sk:hover{background:transparent}
.op-angka{flex:0 0 auto;display:inline-flex;align-items:center;gap:5px;
  border:1px solid var(--line2);background:var(--panel);color:var(--ink-dim);
  border-radius:7px;padding:5px 9px;font-family:inherit;font-size:11.5px;
  font-weight:600;cursor:pointer;white-space:nowrap}
.op-angka:hover{color:var(--aksi);border-color:var(--aksi);background:var(--ok-lembut)}
.op-angka:focus-visible{outline:2px solid var(--ok);outline-offset:2px}
/* Berapa angka yang sudah digeser dari bawaan. Tanpa ini, satu-satunya cara
   tahu sebuah operasi sudah disetel adalah membuka satu per satu. */
.op-angka-n{display:inline-grid;place-items:center;min-width:15px;height:15px;
  padding:0 3px;border-radius:999px;background:var(--aksi);color:var(--aksi-ink);
  font-size:9.5px;line-height:1}

/* ---------------------------------------------------------- pengatur angka
   Menggantikan daftar di kotak yang sama. Tombol pulangnya duduk di kepala
   yang tidak ikut menggulung, supaya tidak pernah hilang ke atas. */
.op-kembali{display:inline-block;margin:0 0 8px;border:0;background:transparent;
  color:var(--aksi);font-family:inherit;font-size:11.5px;font-weight:600;
  cursor:pointer;padding:2px 0}
.op-kembali:hover{text-decoration:underline}
.op-kembali:focus-visible{outline:2px solid var(--ok);outline-offset:2px}

.pr{padding:14px 0;border-bottom:1px solid var(--line)}
.pr:last-child{border-bottom:0}
.pr-atas{display:flex;align-items:baseline;gap:10px;margin-bottom:9px}
.pr-label{flex:1 1 auto;font-size:12.5px;color:var(--ink);font-weight:600}
.pr-nilai{flex:0 0 auto;font-size:13px;font-weight:600;color:var(--ink-dim);
  font-variant-numeric:tabular-nums}
/* Angka yang sudah digeser dari bawaan diberi warna aksi: sesudah menutup dan
   membuka popup, itu satu-satunya bekas bahwa ia pernah disentuh. */
.pr-nilai.pr-geser{color:var(--aksi)}

.pr-bar{width:100%;margin:0;height:18px;background:transparent;
  -webkit-appearance:none;appearance:none;cursor:pointer;display:block}
.pr-bar:focus{outline:0}
/* Bagian kiri knob ikut berwarna. Jalur satu warna memaksa orang membaca
   angka di atasnya untuk tahu posisinya; jalur terisi menjawabnya sekilas.
   --isi ditulis JS ke elemen input, dan pseudo-element mewarisinya. */
.pr-bar::-webkit-slider-runnable-track{height:4px;border-radius:999px;
  box-shadow:inset 0 0 0 1px var(--line2);
  background:linear-gradient(to right,var(--aksi) 0 var(--isi,0%),
    var(--sunk) var(--isi,0%) 100%)}
.pr-bar::-moz-range-track{height:4px;border-radius:999px;
  box-shadow:inset 0 0 0 1px var(--line2);
  background:linear-gradient(to right,var(--aksi) 0 var(--isi,0%),
    var(--sunk) var(--isi,0%) 100%)}
.pr-bar::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;
  width:16px;height:16px;margin-top:-6px;border-radius:50%;
  background:var(--aksi);border:2px solid var(--panel);
  box-shadow:0 1px 3px rgba(0,0,0,.28)}
.pr-bar::-moz-range-thumb{width:16px;height:16px;border-radius:50%;
  background:var(--aksi);border:2px solid var(--panel);
  box-shadow:0 1px 3px rgba(0,0,0,.28)}
.pr-bar:focus-visible::-webkit-slider-thumb{outline:2px solid var(--ok);outline-offset:2px}
.pr-bar:focus-visible::-moz-range-thumb{outline:2px solid var(--ok);outline-offset:2px}

/* Petak warna selebar barisnya: contoh sekecil kotak centang tidak cukup
   untuk menilai warna yang akan mengisi tepi ribuan gambar. */
.pr-warna-in{width:100%;height:34px;padding:0;display:block;cursor:pointer;
  background:transparent;border:1px solid var(--line2);border-radius:8px}
.pr-warna-in::-webkit-color-swatch-wrapper{padding:3px}
.pr-warna-in::-webkit-color-swatch{border:0;border-radius:5px}
.pr-warna-in::-moz-color-swatch{border:0;border-radius:5px}
.pr-warna-in:focus-visible{outline:2px solid var(--ok);outline-offset:2px}

.pr-kaki{display:flex;align-items:center;justify-content:space-between;gap:8px;
  margin-top:7px;font-size:10.5px;color:var(--ink-faint);
  font-variant-numeric:tabular-nums}
.pr-bawaan{border:0;background:transparent;color:var(--ink-faint);font:inherit;
  font-size:10.5px;cursor:pointer;padding:2px 6px;border-radius:5px}
.pr-bawaan:hover{color:var(--aksi);background:var(--ok-lembut)}
.pr-bawaan:focus-visible{outline:2px solid var(--ok);outline-offset:1px}

.pr-sel{width:100%;padding:7px 10px;font-size:13px;font-family:inherit;
  background:var(--ground);color:var(--ink);border:1px solid var(--line2);
  border-radius:7px}
.pr-sel:focus{outline:2px solid var(--ok);outline-offset:-1px}

/* ------------------------------------------------------- peta kelas
   Satu baris per kelas projek. Nama dan jumlah objeknya di kiri, karena yang
   menentukan "kelas ini mau diapakan" justru seberapa banyak isinya. */
.kl{display:flex;align-items:center;flex-wrap:wrap;gap:8px 10px;padding:11px 0;
  border-bottom:1px solid var(--line)}
.kl:last-child{border-bottom:0}
/* Langkah 2 · Kelas: centang dipakai/latar + pilihan gabung/rename. */
.wz-kelas{margin-top:6px}
.kl-pakai-bungkus{flex:0 0 auto;display:grid;place-items:center;cursor:pointer}
.kl-pakai{width:16px;height:16px;cursor:pointer;accent-color:var(--aksi)}
/* Baris yang dijadikan latar: diredupkan, nama dicoret halus, tanda hijau. */
.kl.kl-latar .kl-kiri b{color:var(--ink-dim);text-decoration:line-through;
  text-decoration-color:var(--ink-faint)}
.kl.kl-latar .kl-sel{opacity:.4}
.kl-latar-tanda{flex:0 0 auto;font-size:11px;font-weight:600;color:var(--latar);
  white-space:nowrap}
.kl-kiri{flex:1 1 110px;min-width:0;display:flex;flex-direction:column;gap:2px}
.kl-kiri b{font-size:12.5px;color:var(--ink);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.kl-n{font-size:10.5px;color:var(--ink-faint);font-variant-numeric:tabular-nums}
.kl-sel{flex:0 1 auto;max-width:180px;min-width:0;padding:6px 8px;font-size:12px;
  font-family:inherit;background:var(--ground);color:var(--ink);
  border:1px solid var(--line2);border-radius:7px}
.kl-sel:focus{outline:2px solid var(--ok);outline-offset:-1px}
.kl input.kl-nama{flex:0 1 140px;width:140px;min-width:110px;padding:6px 8px;
  font-size:12px;font-family:inherit;background:var(--ground);color:var(--ink);
  border:1px solid var(--line2);border-radius:7px}
.kl input.kl-nama:focus{outline:2px solid var(--ok);outline-offset:-1px}
/* Perubahan yang tidak berlaku selama saklarnya mati harus dikatakan sebelum
   orang mengaturnya, bukan sesudah versinya jadi tanpa perubahan apa pun. */
/* Peringatan saat Anti-bocor dipilih tetapi pemeriksaannya belum dijalankan.
   Warnanya peringatan, bukan bahaya: tidak ada yang rusak, orangnya cuma
   belum menekan tombol yang membuat pilihannya berlaku. */
/* Kenapa jumlah gambar versi ini lebih sedikit daripada yang diperkirakan.
   Tanpa kalimat ini, selisihnya cuma terlihat sebagai angka yang tidak cocok
   dan tidak ada yang bisa menjelaskannya besok. */
/* Kendali papan Anotasi: "Format anotasi" dan "Urutkan". Keduanya setelan
   papan, bukan saringan tampilan, jadi mereka dikelompokkan.

   Dulu tiap baris adalah flex-nya sendiri. Akibatnya label yang panjangnya
   berbeda ("Format anotasi" 77px, "Urutkan" 37px) mendorong dropdown-nya ke
   posisi x yang berbeda, lebar dropdown mengikuti isi masing-masing (274px
   lawan 144px), dan kalimat penjelas yang panjang menempel di kanan baris
   pertama sehingga baris itu jauh lebih berat daripada baris kedua. Tiga
   ketidakrataan itu membuat sepasang kendali terbaca sebagai dua serpihan.

   Sekarang tiap baris memakai grid dengan lebar kolom yang SAMA, diambil dari
   satu tempat (--an-label dan --an-pilih di induknya), jadi kedua dropdown
   mulai dan berakhir di titik yang sama. Kalimat penjelasnya turun ke bawah
   dropdown-nya sebagai baris bantuan, tetap sejajar kolom kendali. */
.an-kendali{--an-label:96px;--an-pilih:300px;margin:14px 0 12px}
.an-kendali-baris{display:grid;align-items:center;gap:6px 10px;margin:0;
  grid-template-columns:var(--an-label) minmax(0,var(--an-pilih))
    minmax(0,1fr)}
/* Jarak antarbaris dibuat lebih besar daripada jarak kendali ke baris
   bantuannya, supaya jelas kalimat bantuan itu milik kendali di ATASnya. */
.an-kendali-baris + .an-kendali-baris{margin-top:12px}
.an-kendali-baris > .halus{margin:0;font-size:11.5px;color:var(--ink-dim)}
/* Lebar dropdown ditentukan kolomnya, bukan isinya: itu satu-satunya cara
   "Terbaru dibagi" yang pendek berakhir di garis yang sama dengan
   "Otomatis (poligon, dari bentuk terbanyak)" yang panjang. */
.an-kendali-baris > select.chip{width:100%;padding:5px 9px;font-size:12.5px}
#an-jenis-pesan{grid-column:2 / -1;max-width:62ch;color:var(--ink-faint);
  line-height:1.5}
/* Layar sempit: kolom label 96px + dropdown 300px tidak lagi muat berdampingan,
   jadi labelnya naik ke atas kendalinya. */
@media (max-width:560px){
  .an-kendali-baris{grid-template-columns:minmax(0,1fr);gap:4px}
  .an-kendali-baris > select.chip{max-width:var(--an-pilih)}
  #an-jenis-pesan{grid-column:1}
}

.vs-pulang{margin:10px 0 0;padding:10px 12px;border-radius:8px;
  background:var(--bahaya-lembut);color:var(--ink-dim);font-size:11.5px;
  line-height:1.5}

.wz-belah-pesan{margin:8px 0 0;padding:9px 11px;border-radius:8px;
  background:var(--bahaya-lembut);color:var(--ink-dim);font-size:11.5px;
  line-height:1.45}

.kl-mati{margin:12px 0 4px;padding:9px 11px;border-radius:8px;
  background:var(--bahaya-lembut);color:var(--ink-dim);font-size:11.5px;
  line-height:1.45}
.op-dlg-bawah{flex:0 0 auto;display:flex;align-items:center;gap:10px;
  padding:12px 20px;border-top:1px solid var(--line);background:var(--raised)}
.op-dlg-bawah .spacer{flex:1}

@media (max-width:900px){
  .wz{max-width:none}
  .wz-aksi{flex-wrap:wrap}
  .wz-tbl{flex:1 1 auto;justify-content:center}
  .wz-aksi .spacer{display:none}
}

/* ---------------------------------------------------------- foto latar RVM
   Formulir di langkah 4 wizard versi. Kartunya menampilkan PELAT JADI, bukan
   foto yang diunggah: yang perlu dinilai mata adalah hasil olahannya. */
.wz-latar{margin-top:14px;padding-top:12px;border-top:1px solid var(--line)}
.wz-latar .halus{margin:4px 0 10px;line-height:1.55}
.lt-daftar{display:flex;gap:10px;flex-wrap:wrap}
.lt-kartu{margin:0;width:132px;border:1px solid var(--line);border-radius:9px;
  overflow:hidden;background:var(--sunk)}
.lt-kartu img{display:block;width:132px;height:132px;object-fit:cover}
.lt-kartu figcaption{display:flex;align-items:center;gap:6px;
  padding:5px 4px 5px 8px;font-size:11px;color:var(--ink-dim);
  background:var(--panel);border-top:1px solid var(--line)}
.lt-kartu figcaption{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
.lt-buang{margin-left:auto;flex:0 0 auto;width:20px;height:20px;line-height:1;
  border:1px solid var(--line2);border-radius:5px;background:var(--raised);
  color:var(--ink-faint);cursor:pointer;font-size:13px}
.lt-buang:hover{border-color:var(--stop);color:var(--stop)}
.lt-aksi{display:flex;align-items:center;gap:10px;margin-top:10px;flex-wrap:wrap}
.lt-pilih{cursor:pointer}
/* Batas 3 foto sudah kena: tombolnya diredupkan, tidak dihilangkan — tombol
   yang lenyap menyisakan teka-teki, sedangkan yang redup dijelaskan angkanya
   di sebelahnya. */
.lt-pilih.lt-penuh{opacity:.45;cursor:not-allowed}
.lt-aksi .halus{flex:1 1 200px;line-height:1.5}
.lt-mode{margin:12px 0 0;max-width:340px}
.lt-ket{margin:7px 0 0 !important;min-height:2.6em}
.lt-mode-cap{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}

/* ============================================================
   HALAMAN TRAINING
   ============================================================
   Seluruhnya memakai token yang sudah ada, tidak satu pun warna ditulis
   mentah: halaman ini harus ikut benar di tema terang maupun gelap tanpa
   aturan tambahan, dan warna mentah adalah cara paling cepat membuat satu
   halaman tertinggal sendirian saat temanya diganti. */

/* -- bilah mesin -------------------------------------------------------- */
.tr-mesin{
  background:var(--panel); border:1px solid var(--line); border-radius:10px;
  padding:10px 14px; margin:0 0 16px;
}
.tr-mesin-isi{display:flex; gap:22px; flex-wrap:wrap; align-items:center}
.tr-m{display:flex; align-items:center; gap:8px; min-width:210px; flex:1 1 210px}
.tr-m-nama{font-size:12px; color:var(--ink-dim); min-width:52px}
.tr-m-bar{
  flex:1; height:6px; border-radius:3px; background:var(--sunk); overflow:hidden;
}
.tr-m-bar>i{display:block; height:100%; background:var(--aksi); transition:width .4s}
.tr-m-nilai{font-size:12px; color:var(--ink-dim); font-variant-numeric:tabular-nums}

/* -- kepala ------------------------------------------------------------- */
.tr-kepala{display:flex; align-items:flex-start; gap:16px; margin:0 0 14px}
.tr-h2{margin:0; font-size:19px}
.tr-h3{margin:22px 0 10px; font-size:15px; color:var(--ink-dim);
       text-transform:uppercase; letter-spacing:.05em}
.tr-sub{margin:3px 0 0; max-width:60ch}
.tr-kepala .cta{margin-left:auto; flex:none}
.tr-diam{color:var(--ink-faint); font-size:13px}

/* -- form --------------------------------------------------------------- */
.tr-form{
  background:var(--panel); border:1px solid var(--line); border-radius:12px;
  padding:16px; margin:0 0 20px;
}
.tr-form-atas{display:flex; align-items:center; gap:10px; margin:0 0 14px}
/* Dua kolom di layar lebar, satu di sempit. Yang wajib diisi di kiri, setelan
   di kanan: orang mengisi dari kiri ke kanan, dan setelan tidak boleh berdiri
   sebelum sumber datanya dipilih. */
.tr-grid{display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:20px}
@media (max-width:900px){ .tr-grid{grid-template-columns:1fr} }
.tr-kolom{display:flex; flex-direction:column; gap:12px; min-width:0}
.tr-label{display:flex; flex-direction:column; gap:4px; font-size:13px; font-weight:600}
.tr-label input,.tr-label select{
  font:inherit; font-weight:400; padding:7px 9px; border-radius:7px;
  border:1px solid var(--line2); background:var(--raised); color:var(--ink);
}
.tr-bantu{font-size:11.5px; font-weight:400; color:var(--ink-dim); line-height:1.45}

/* Panel akun Kaggle per-user di form training (tambah/test/hapus/utama). */
.tr-akun{margin:6px 0 2px; padding:10px 12px; border:1px solid var(--line2);
  border-radius:9px; background:var(--panel); display:flex; flex-direction:column; gap:8px}
.tr-akun-atas{display:flex; align-items:center; gap:8px}
.tr-akun-atas b{font-size:12.5px}
.tr-akun-atas .chip{margin-left:auto}
.tr-akun-baris{display:flex; align-items:center; gap:8px; font-size:12.5px;
  padding:5px 0; border-top:1px solid var(--line)}
.tr-akun-baris:first-child{border-top:0}
.tr-akun-nama{font-weight:600}
.tr-akun-stat{color:var(--ink-dim); font-size:11.5px; font-variant-numeric:tabular-nums}
.tr-akun-form{display:flex; flex-direction:column; gap:7px; padding:9px;
  border:1px dashed var(--line2); border-radius:8px; background:var(--ok-lembut)}
.tr-akun-aksi{display:flex; align-items:center; gap:8px; flex-wrap:wrap}
.tr-akun-aksi .tr-bantu{flex:1 1 100%}
/* "Test & simpan" dan "Batal" harus tampil sepasang: setinggi sama dan
   sejajar. .cta global membawa margin-top:26px (warisan tombol hero) yang di
   sini malah menjatuhkannya di bawah .chip, dan padding/font keduanya beda
   tinggi. Dinetralkan HANYA dalam konteks ini — aturan global .cta/.chip tak
   disentuh. height eksplisit + border-box menyerap selisih border 1px .chip,
   jadi keduanya persis setinggi 32px; inline-flex menengahkan teksnya, dan
   align-items:center pada barisnya meratakan keduanya di garis yang sama.
   .cta tetap terisi (primer), .chip tetap bergaris (sekunder). */
.tr-akun-aksi .cta,
.tr-akun-aksi .chip{margin:0; box-sizing:border-box; height:32px;
  display:inline-flex; align-items:center; justify-content:center;
  padding:0 16px; font-size:13px; line-height:1; border-radius:7px}
.tr-test-berhasil{color:var(--ok); font-weight:600}
.tr-test-gagal{color:var(--bahaya, #c0392b); font-weight:600}
/* Monitor akun Kaggle tim di halaman Admin (#kaggle-monitor). */
.akun-kaggle-baris{padding:5px 0; font-size:13px; line-height:1.5;
  border-top:1px solid var(--line)}
.akun-kaggle-baris:first-child{border-top:0}

.tr-par-atas{display:flex; align-items:center; gap:8px}
.tr-tandabawaan{
  font-size:10.5px; font-weight:700; letter-spacing:.04em; padding:2px 6px;
  border-radius:4px; background:var(--lab-bg); color:var(--lab-ink);
}
.tr-par{display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:10px}
.tr-p{display:flex; flex-direction:column; gap:3px; font-size:12px}
.tr-p-nama{font-weight:600}
.tr-p input{
  font:inherit; padding:6px 8px; border-radius:6px; border:1px solid var(--line2);
  background:var(--raised); color:var(--ink); font-variant-numeric:tabular-nums;
}
.tr-p input:disabled{background:var(--sunk); color:var(--ink-dim); cursor:not-allowed}
.tr-par-warna{
  margin-top:10px; padding:10px; border-radius:8px;
  background:var(--ok-lembut); border:1px solid var(--ok-pudar);
}
.tr-lanjut{margin-top:6px}
.tr-lanjut>summary{cursor:pointer; font-size:13px; color:var(--ink-dim); padding:4px 0}

/* Kepala halaman diredam selama form training terbuka: judul dan ajakannya
   sudah tidak berlaku, dan tombolnya benar-benar dilepas dari alur tab, bukan
   sekadar dipudarkan. */
.tr-kepala-sunyi #tr-mulai{display:none}
.tr-kepala-sunyi .tr-sub{display:none}

/* -- panel sinkronisasi warna ------------------------------------------- */
/* SENGAJA NETRAL, tanpa hijau-berceklis dan tanpa oranye-seru.
 *
 * Versi pertamanya memberi tanda centang hijau pada "bentuk" dan tanda seru
 * oranye pada "warna". Secara logika itu cuma menandai kecocokan dengan cara
 * versinya dibuat — tetapi karena versi hampir selalu dibuat dengan bawaan
 * "bentuk", hasilnya: begitu orang memilih "Warna ikut menentukan", seluruh
 * panel berubah oranye. Yang terbaca bukan "versi ini tidak menyimpan warna",
 * melainkan "pilihanmu salah". Padahal di model industri keduanya sah — ada
 * yang memang spesifik warna, ada yang general.
 *
 * Jadi panelnya selalu netral. Yang sungguh-sungguh tidak bisa dikerjakan
 * disebut di .tr-warna-catat, sebagai fakta tentang DATANYA. */
.tr-warna{
  border-radius:9px; padding:11px 13px; border:1px solid var(--line);
  background:var(--sunk);
}
.tr-warna-atas{display:flex; align-items:center; gap:8px; margin:0 0 5px}
.tr-warna-pesan{margin:0 0 8px; font-size:12.5px; line-height:1.55; color:var(--ink-dim)}
.tr-cek{display:flex; gap:8px; align-items:flex-start; font-size:12.5px; cursor:pointer}
.tr-cek input{margin-top:2px; flex:none}
.tr-cek b{display:block}

/* -- antrean percobaan --------------------------------------------------- */
.tr-batch{margin-top:18px; padding-top:14px; border-top:1px solid var(--line)}
.tr-batch-atas{display:flex; flex-direction:column; gap:2px; margin:0 0 10px}
.tr-batch-daftar{display:flex; flex-direction:column; gap:6px; min-height:26px}
.tr-antre{
  display:flex; align-items:center; gap:10px; padding:7px 10px;
  background:var(--sunk); border-radius:7px; font-size:12.5px;
}
.tr-antre-no{
  width:20px; height:20px; border-radius:50%; display:grid; place-items:center;
  background:var(--panel); border:1px solid var(--line2); font-size:11px; font-weight:700;
}
.tr-antre-nama{font-weight:600}
.tr-antre-par{color:var(--ink-dim); font-variant-numeric:tabular-nums}
.tr-batch-aksi{display:flex; align-items:center; gap:10px; margin-top:12px}
.tr-galat{
  margin:10px 0 0; padding:8px 10px; border-radius:7px; font-size:12.5px;
  background:var(--bahaya-lembut); color:var(--stop);
}
.tr-galat-kecil{font-size:12px; color:var(--stop)}

/* -- kartu training ------------------------------------------------------ */
.tr-bagian{margin:0 0 8px}
.tr-kartu{
  background:var(--panel); border:1px solid var(--line); border-radius:11px;
  padding:13px 15px; margin:0 0 10px;
}
.tr-kartu-jalan{border-color:var(--aksi)}
.tr-pil{
  font-size:10.5px; font-weight:700; letter-spacing:.03em; padding:2px 8px;
  border-radius:99px; background:var(--lab-bg); color:var(--lab-ink);
}
.tr-pil-jalan{background:var(--aksi); color:var(--aksi-ink)}
.tr-pil-antre{background:var(--split-bg); color:var(--split-ink)}
.tr-pil-selesai{background:var(--ok); color:var(--on-ink)}
.tr-pil-gagal,.tr-pil-hilang{background:var(--stop); color:var(--on-ink)}
/* Tertunda: bukan gagal — kuota Kaggle menipis, bisa dilanjutkan. Warna
   peringatan lembut, bukan merah. */
.tr-pil-tertunda{background:var(--split-bg); color:var(--split-ink)}
.tr-pil-kaggle{background:var(--lab-bg); color:var(--lab-ink); font-weight:600}
/* RF-DETR: lencana TEKNIK, bukan keadaan. Gaya outline supaya jelas berbeda
   dari pil status solid di sebelahnya dan tak terbaca sebagai keadaan. */
.tr-pil-rfdetr{background:transparent; color:var(--aksi);
  border:1px solid var(--aksi); font-weight:700}
.tr-pil-maskrcnn{background:transparent; color:#9a6cff;
  border:1px solid #9a6cff; font-weight:700}
/* Pita info Kaggle: lencana + tautan kernel + pesan poller. Satu baris kecil,
   tenang, di bawah kepala kartu. */
.tr-kaggle{display:flex; align-items:center; gap:8px; flex-wrap:wrap;
  margin:2px 0 8px; font-size:12px}
.tr-kaggle-pesan{color:var(--ink-dim)}
.tr-k-tunda{margin:2px 0 8px; padding:8px 11px; border-radius:8px;
  background:var(--split-bg); color:var(--split-ink); font-size:12.5px}

.tr-bar{height:7px; border-radius:4px; background:var(--sunk); overflow:hidden; margin:10px 0 9px}
.tr-bar>i{display:block; height:100%; background:var(--aksi); transition:width .6s}
/* Epoch pertama: durasinya belum bisa dipakai jadi persen (belum ada epoch
   selesai sebagai acuan), jadi bilahnya "meluncur" — tanda jelas SEDANG BEKERJA
   tanpa mengarang angka persen. Memakai animasi yang sama dengan .prog. */
.tr-bar-kerja>i{width:35% !important; transition:none; animation:meluncur 1.15s ease-in-out infinite}

/* Angka disejajarkan dengan tabular-nums supaya digitnya tidak bergoyang
   tiap kali diperbarui — halaman ini menyegarkan dirinya tiap 4 detik, dan
   angka yang melompat-lompat membuatnya terasa gelisah. */
.tr-kartu-metrik{display:flex; gap:18px; flex-wrap:wrap; margin:0 0 10px}
.tr-metrik{display:flex; flex-direction:column; gap:1px}
.tr-metrik i{font-style:normal; font-size:10.5px; color:var(--ink-faint)}
.tr-metrik b{font-size:13px; font-variant-numeric:tabular-nums}
.tr-skor{display:flex; align-items:baseline; gap:8px; margin:8px 0}
.tr-skor i{font-style:normal; font-size:11.5px; color:var(--ink-faint)}
.tr-skor b{font-size:24px; font-variant-numeric:tabular-nums; color:var(--ok)}
.chip-bahaya{color:var(--stop)}

/* -- panel rincian ------------------------------------------------------- */
.tr-tirai{
  position:fixed; inset:0; background:rgba(var(--tirai),.55); z-index:60;
  display:grid; place-items:center; padding:20px;
}
.tr-panel{
  background:var(--panel); border-radius:12px; width:min(860px,100%);
  max-height:88vh;max-height:88dvh; display:flex; flex-direction:column;
  box-shadow:0 18px 50px rgba(var(--bayang),.35);
}
.tr-panel-atas{
  display:flex; align-items:center; gap:10px; padding:13px 16px;
  border-bottom:1px solid var(--line);
}
.tr-panel-isi{padding:14px 16px; overflow:auto}
/* Dialog training lanjutan: kotak kecil, bukan panel lebar. */
.tr-dialog{
  background:var(--panel); border-radius:12px; width:min(440px,100%);
  padding:0 16px 16px; box-shadow:0 18px 50px rgba(var(--bayang),.35);
}
.tr-dialog .tr-panel-atas{margin:0 -16px 4px}
.tr-dialog .tr-bantu{margin:8px 0 14px; font-size:12px; color:var(--ink-dim)}
.tr-dl-baris{display:flex; flex-direction:column; gap:5px; margin:0 0 12px;
             font-size:12px; color:var(--ink-dim)}
.tr-dl-baris select,.tr-dl-baris input{
  height:34px; padding:0 10px; border:1px solid var(--line2); border-radius:7px;
  background:var(--panel); color:var(--ink); font-size:13px}
.tr-dl-aksi{display:flex; justify-content:flex-end; margin-top:4px}

/* ============================================================ Kamus parameter */
/* Badge "?" kecil di sebelah nama parameter */
.km-tanya{display:inline-flex; align-items:center; justify-content:center;
  width:15px; height:15px; margin-left:5px; padding:0; border-radius:50%;
  border:none; background:var(--line2); color:var(--ink-dim);
  font-size:10px; font-weight:700; line-height:1; cursor:pointer;
  vertical-align:middle; transition:background .12s, color .12s}
.km-tanya:hover{background:var(--aksi,#0f766e); color:#fff}
/* Popover satu parameter */
.km-pop{position:absolute; z-index:80; width:min(280px,90vw);
  background:var(--panel); border:1px solid var(--line); border-radius:10px;
  box-shadow:0 10px 34px rgba(var(--bayang),.30); padding:11px 13px;
  font-size:12.5px; animation:km-muncul .12s ease}
@keyframes km-muncul{from{opacity:0; transform:translateY(-4px)} to{opacity:1}}
.km-pop-atas{display:flex; align-items:center; gap:6px; margin-bottom:6px}
.km-pop-atas b{font-size:13px; color:var(--ink)}
.km-dot{width:9px; height:9px; border-radius:50%; background:var(--km,#607d8b);
  flex:0 0 auto}
.km-grup{margin-left:auto; font-size:10.5px; color:var(--ink-faint)}
.km-arti{margin:0 0 9px; color:var(--ink-dim); line-height:1.5}
.km-cip{display:flex; flex-wrap:wrap; gap:6px}
.km-c{display:inline-flex; flex-direction:column; gap:1px; font-size:11px;
  padding:3px 9px; background:var(--sunk); border-radius:7px; color:var(--ink);
  font-variant-numeric:tabular-nums}
.km-c i{font-style:normal; font-size:9.5px; color:var(--ink-faint);
  text-transform:uppercase; letter-spacing:.04em}
.km-tip{margin:9px 0 0; padding:6px 9px; font-size:11.5px; line-height:1.45;
  color:var(--ink-dim); background:var(--bahaya-lembut,#fdf3e0); border-radius:7px;
  border-left:3px solid var(--awas,#f9a825)}
/* Modal daftar penuh */
.km-tirai{position:fixed; inset:0; background:rgba(var(--tirai),.55); z-index:80;
  display:grid; place-items:center; padding:20px; animation:km-muncul .12s ease}
.km-panel{background:var(--panel); border-radius:14px; width:min(720px,100%);
  max-height:88vh;max-height:88dvh; display:flex; flex-direction:column;
  box-shadow:0 20px 56px rgba(var(--bayang),.4)}
.km-panel-atas{display:flex; align-items:center; gap:10px; padding:13px 16px;
  border-bottom:1px solid var(--line)}
.km-panel-atas b{font-size:14px}
.km-cari{flex:1; height:32px; padding:0 12px; border:1px solid var(--line2);
  border-radius:8px; background:var(--sunk); color:var(--ink); font-size:13px}
.km-isi{padding:14px 16px; overflow:auto}
.km-seksi{margin:0 0 18px}
.km-seksi h3{margin:0 0 9px; font-size:12px; text-transform:uppercase;
  letter-spacing:.04em; color:var(--km,#607d8b);
  border-left:3px solid var(--km,#607d8b); padding-left:8px}
.km-grid{display:grid; grid-template-columns:repeat(auto-fill,minmax(210px,1fr));
  gap:9px}
.km-kartu{border:1px solid var(--line); border-radius:9px; padding:9px 11px;
  background:var(--panel)}
.km-kartu-nama{font-size:12.5px; font-weight:600; color:var(--ink);
  margin-bottom:4px}
.km-kartu-nama code{font-size:10.5px; font-weight:400; color:var(--ink-faint);
  background:var(--sunk); padding:1px 5px; border-radius:5px}
.km-kartu-arti{font-size:11.5px; color:var(--ink-dim); line-height:1.45;
  margin-bottom:7px}
.km-catatan{margin:6px 0 0; padding:10px 12px; font-size:12px; line-height:1.55;
  color:var(--ink-dim); background:var(--sunk); border-radius:9px}
.tr-p-blok{margin:0 0 18px}
.tr-p-blok h4{margin:0 0 8px; font-size:12px; text-transform:uppercase;
              letter-spacing:.05em; color:var(--ink-faint)}
.tr-kv-grid{display:grid; grid-template-columns:repeat(auto-fit,minmax(140px,1fr)); gap:6px}
.tr-kv{display:flex; justify-content:space-between; gap:8px; font-size:12px;
       padding:3px 7px; background:var(--sunk); border-radius:5px}
.tr-kv i{font-style:normal; color:var(--ink-dim)}
.tr-kv b{font-variant-numeric:tabular-nums}
.tr-p-blok h4 small{font-size:11px; color:var(--ink-dim); text-transform:none;
                    letter-spacing:0}
/* Kelas model: cip berindeks. Indeksnya (kotak kecil di kiri) sengaja terlihat
   karena itulah angka yang keluar dari deteksi mentah. */
.tr-kdaftar{display:flex; flex-wrap:wrap; gap:6px}
.tr-kcip{display:inline-flex; align-items:center; gap:6px; font-size:12px;
         padding:3px 9px 3px 3px; background:var(--sunk); border-radius:12px;
         border:1px solid var(--line)}
.tr-kcip i{font-style:normal; font-variant-numeric:tabular-nums; font-size:11px;
           min-width:17px; text-align:center; padding:2px 4px; border-radius:9px;
           background:var(--line); color:var(--ink-dim)}
.tr-grafik{width:100%; border-radius:8px; border:1px solid var(--line)}
/* Log dibuat menggulir sendiri, bukan memanjangkan panelnya: yang dicari
   orang di log hampir selalu baris terakhir. */
.tr-log{
  margin:0; padding:10px; border-radius:7px; background:var(--sunk);
  font-size:11.5px; line-height:1.5; max-height:230px; overflow:auto;
  white-space:pre-wrap; word-break:break-word;
}
/* Lencana "sedang berjalan" di sidebar. .sisi-angka itu teks polos tanpa
   padding, jadi memberinya latar saja menghasilkan strip hijau setipis satu
   digit yang angkanya tidak terbaca. Ia harus jadi lencana utuh.

   Sengaja dibuat berbeda bentuk dari lencana lain, bukan cuma berbeda warna:
   angka di menu lain berarti "ada sekian", yang ini berarti "ada yang sedang
   berjalan sekarang" dan itu satu-satunya lencana yang menuntut perhatian. */
.sisi-angka-jalan{
  min-width:17px; padding:1px 6px; border-radius:99px; text-align:center;
  background:var(--aksi); color:var(--aksi-ink); font-weight:700;
  font-size:10.5px; line-height:1.5;
}
.sisi-item[data-on] .sisi-angka-jalan{color:var(--aksi-ink)}

/* -- hasil uji produksi -------------------------------------------------- */
/* Putusannya diberi warna penuh, bukan sekadar teks: inilah satu-satunya
   tempat di sistem ini yang berkata "jangan dikirim ke produksi", dan
   peringatan yang terlihat seperti keterangan biasa akan dilewati. */
.tr-putusan{
  display:flex; gap:10px; align-items:baseline; padding:10px 12px;
  border-radius:8px; margin:0 0 12px; background:var(--ok-lembut);
  border:1px solid var(--ok-pudar);
}
.tr-putusan>b{font-size:11px; letter-spacing:.06em; color:var(--ok)}
.tr-putusan>span{font-size:12.5px; line-height:1.55; color:var(--ink-dim)}
.tr-putusan[data-tingkat="sedang"]{background:var(--warn-bg); border-color:var(--warn)}
.tr-putusan[data-tingkat="sedang"]>b{color:var(--warn)}
.tr-putusan[data-tingkat="buruk"]{background:var(--bahaya-lembut); border-color:var(--stop)}
.tr-putusan[data-tingkat="buruk"]>b{color:var(--stop)}

.tr-uji-angka{display:flex; gap:24px; flex-wrap:wrap; margin:0 0 10px}
.tr-uji-angka>span{display:flex; flex-direction:column; gap:2px; min-width:130px}
.tr-uji-angka i{font-style:normal; font-size:11px; color:var(--ink-faint)}
.tr-uji-angka b{font-size:21px; font-variant-numeric:tabular-nums}
.tr-uji-angka u{text-decoration:none; font-size:11px; color:var(--ink-dim)}
.tr-uji-angka>span[data-tingkat="baik"] b{color:var(--ok)}
.tr-uji-angka>span[data-tingkat="sedang"] b{color:var(--warn)}
.tr-uji-angka>span[data-tingkat="buruk"] b{color:var(--stop)}

/* Tabel gambar yang goyah. Sel yang BERBEDA dari jawaban aslinya ditandai —
   itu satu-satunya hal yang perlu dicari mata di tabel ini. */
.tr-tabel{width:100%; border-collapse:collapse; font-size:11.5px; margin-top:8px}
.tr-tabel th,.tr-tabel td{
  padding:4px 7px; text-align:left; border-bottom:1px solid var(--line);
  white-space:nowrap;
}
.tr-tabel th{color:var(--ink-faint); font-weight:600}
.tr-tabel .tr-beda{color:var(--stop); font-weight:700}
.chip-uji{border-color:var(--aksi); color:var(--aksi)}
/* "tak-terukur" bukan hijau dan bukan merah: ia berarti angkanya TIDAK ADA,
   bukan bagus dan bukan buruk. Mewarnainya hijau akan mengulang persis cacat
   yang baru diperbaiki. */
.tr-uji-angka>span[data-tingkat="tak-terukur"] b{color:var(--ink-faint)}
.tr-putusan[data-tingkat="tak-terukur"]{background:var(--warn-bg); border-color:var(--warn)}

/* -- pilihan mode warna di wizard versi ---------------------------------- */
/* Dua pilihan berdampingan, bukan satu checkbox: "bergantung warna" dan
   "tidak bergantung warna" bukan hidup-mati sebuah fitur melainkan dua
   pertanyaan berbeda yang dijawab projeknya, dan keduanya harus terbaca
   sebelum dipilih. */
.wz-mode{
  border:1px solid var(--line); border-radius:9px; padding:10px 12px;
  margin:0 0 12px; background:var(--raised);
}
.wz-mode-p{display:flex; gap:9px; align-items:flex-start; padding:7px 0; cursor:pointer}
.wz-mode-p+.wz-mode-p{border-top:1px solid var(--line)}
.wz-mode-p input{margin-top:3px; flex:none}
.wz-mode-p b{display:block; font-size:13px}
.wz-mode-p em{
  display:block; font-style:normal; font-size:11.5px; color:var(--ink-dim);
  line-height:1.5; margin-top:2px;
}
.wz-mode-ket{
  margin:8px 0 0; font-size:11.5px; color:var(--ink-dim);
  padding-top:8px; border-top:1px dashed var(--line);
}
.tr-mode-baris{margin:0 0 10px; padding:6px 9px; border-radius:6px;
               background:var(--sunk)}
.tr-uji-angka>span[data-tingkat="netral"] b{color:var(--ink)}
/* Kolom perlakuan TERANG dibedakan: di tabel yang lebar, mata perlu tahu
   kolom mana yang "tidak boleh berubah di mode mana pun". */
.tr-tabel th.tr-kol-terang{border-bottom:2px solid var(--warn)}
/* Satuan ditaruh DI DALAM barisnya, kecil dan redup — bukan sebagai kalimat
   penjelasan di bawah kotak. Empat kalimat di bawah empat kotak yang berjajar
   membuat form terbaca seperti dokumen; yang dicari mata cuma angkanya. */
.tr-p-satuan{font-size:10.5px; color:var(--ink-faint); margin-top:1px}
.tr-p{cursor:help}
.tr-warna-nilai{
  margin:6px 0 0; font-size:12px; line-height:1.55; color:var(--ink-dim);
  padding-top:7px; border-top:1px dashed var(--line);
}
/* Satu-satunya aksen berwarna yang tersisa, dan ia hanya muncul untuk
   kombinasi yang benar-benar mustahil: versi yang ronanya sudah diacak tidak
   lagi menyimpan warna aslinya, jadi tidak ada setelan waktu-latih yang bisa
   mengembalikannya. Aksennya di tepi kiri saja — cukup untuk tidak terlewat,
   tidak cukup untuk terbaca sebagai kegagalan. */
.tr-warna-catat{
  margin:8px 0 0; padding:7px 10px; font-size:12px; line-height:1.55;
  color:var(--ink-dim); background:var(--panel); border-radius:0 7px 7px 0;
  border-left:3px solid var(--warn);
}
.tr-warna-catat b{color:var(--ink); font-weight:600}
.tr-mode-pilih{
  display:flex; flex-direction:column; gap:0; margin:8px 0 10px;
  background:var(--panel); border:1px solid var(--line); border-radius:8px;
}
.tr-mode-pilih .tr-mode-p{
  display:flex; gap:9px; align-items:flex-start; padding:9px 11px; cursor:pointer;
}
.tr-mode-pilih .tr-mode-p+.tr-mode-p{border-top:1px solid var(--line)}
.tr-mode-pilih input{margin-top:3px; flex:none}
.tr-mode-pilih b{display:block; font-size:12.5px}
.tr-mode-pilih em{
  display:block; font-style:normal; font-size:11.5px; color:var(--ink-dim);
  line-height:1.5; margin-top:2px;
}

/* -- kurva per epoch ----------------------------------------------------- */
.tr-kurva{width:100%; height:auto; display:block; background:var(--sunk);
          border-radius:8px; border:1px solid var(--line)}
.tr-kisi{stroke:var(--line); stroke-width:1}
.tr-sumbu{fill:var(--ink-faint); font-size:10px}
.tr-garis-map{fill:none; stroke:var(--aksi); stroke-width:2.2;
              stroke-linejoin:round; stroke-linecap:round}
.tr-garis-loss{fill:none; stroke:var(--warn); stroke-width:1.4;
               stroke-dasharray:4 3; opacity:.75}
.tr-titik{fill:var(--aksi)}
.tr-legenda{display:flex; gap:18px; flex-wrap:wrap; margin-top:7px; font-size:11.5px}
.tr-lg{display:flex; align-items:center; gap:5px; color:var(--ink-dim)}
.tr-lg::before{content:""; width:14px; height:2.5px; border-radius:2px}
.tr-lg-map::before{background:var(--aksi)}
.tr-lg-loss::before{background:var(--warn)}

/* -- akurasi per kelas --------------------------------------------------- */
/* Angka gabungan menyembunyikan kelas yang gagal. Yang ditampilkan juga KE
   MANA salahnya lari: "tetra sering jadi kaleng" bisa ditindak, "tetra 60%"
   tidak. */
.tr-kelas{margin:10px 0 12px}
.tr-kelas h5{margin:0 0 7px; font-size:11px; text-transform:uppercase;
             letter-spacing:.05em; color:var(--ink-faint); font-weight:600}
.tr-kelas-baris{
  display:grid; grid-template-columns:minmax(90px,1.4fr) minmax(60px,2fr) auto minmax(0,1.6fr);
  gap:10px; align-items:center; padding:4px 0; font-size:12px;
}
@media (max-width:760px){
  .tr-kelas-baris{grid-template-columns:1fr auto; row-gap:3px}
  .tr-kelas-bar,.tr-kelas-salah{grid-column:1 / -1}
}
.tr-kelas-nama{overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
               font-weight:600}
.tr-kelas-bar{height:7px; border-radius:4px; background:var(--sunk); overflow:hidden}
.tr-kelas-bar>i{display:block; height:100%; background:var(--ok)}
.tr-kelas-bar>i[data-tingkat="sedang"]{background:var(--warn)}
.tr-kelas-bar>i[data-tingkat="buruk"]{background:var(--stop)}
.tr-kelas-nilai{font-variant-numeric:tabular-nums; white-space:nowrap}
.tr-kelas-nilai u{text-decoration:none; color:var(--ink-faint); font-size:11px;
                  margin-left:4px}
.tr-kelas-salah{color:var(--ink-dim); font-size:11px; overflow:hidden;
                text-overflow:ellipsis; white-space:nowrap}

/* ============================================================
   KARTU HASIL TRAINING — tata letak tiga pita
   ============================================================
   Kartunya selebar ~1.300px. Versi sebelumnya menumpuk seluruh isinya di
   kiri dalam kolom sempit dan menyisakan tiga perempat kartu kosong; yang
   di bawah ini memakai lebarnya: identitas di pita atas, angka di tengah,
   tindakan di bawah. */
.tr-k-atas{display:flex; align-items:center; gap:10px; flex-wrap:wrap;
           padding-bottom:11px; border-bottom:1px solid var(--line)}
.tr-k-nama{font-size:15px}
/* Metanya dirangkai dengan KATA, bukan deretan titik-tengah. "L1 · v2 ·
   11 Sep" menuntut orang menebak arti tiap potongannya; "L1 dari v2 oleh
   claude" langsung terbaca sebagai kalimat. */
.tr-k-meta{font-size:11.5px; color:var(--ink-faint)}
.tr-k-meta i{font-style:normal; margin:0 4px; opacity:.8}

.tr-k-isi{display:flex; align-items:center; gap:34px; flex-wrap:wrap;
          padding:14px 0 13px}
.tr-skor{display:flex; flex-direction:column; gap:1px; min-width:150px}
.tr-skor b{font-size:38px; line-height:1; font-weight:700; color:var(--ok);
           font-variant-numeric:tabular-nums; letter-spacing:-.02em}
.tr-skor b span{font-size:20px; font-weight:600; margin-left:1px}
.tr-skor i{font-style:normal; font-size:11.5px; color:var(--ink-dim)}
.tr-skor-kosong b{color:var(--ink-faint)}

/* dl/dt/dd, bukan span berlapis: pasangan label-nilai memang itu artinya,
   dan pembaca layar membacakannya sebagai pasangan. */
.tr-k-angka{display:flex; gap:30px; flex-wrap:wrap; margin:0}
.tr-k-angka>div{display:flex; flex-direction:column; gap:2px}
.tr-k-angka dt{font-size:11px; color:var(--ink-faint)}
.tr-k-angka dd{margin:0; font-size:15px; font-weight:600;
               font-variant-numeric:tabular-nums}

.tr-k-aksi{display:flex; align-items:center; gap:8px; flex-wrap:wrap;
           padding-top:11px; border-top:1px solid var(--line)}
.chip-utama{border-color:var(--aksi); color:var(--aksi); font-weight:600}
/* Kedua tombol unduh dikelompokkan di bawah satu kata, bukan berdiri sebagai
   dua tombol setara bernama panjang. Yang membedakan keduanya cuma satu kata,
   dan mengulang "Unduh" dua kali membuat barisnya penuh tanpa menambah arti. */
.tr-unduh{display:inline-flex; align-items:center; gap:6px;
          font-size:11.5px; color:var(--ink-faint); margin-left:6px}
.tr-k-galat{margin:13px 0; padding:9px 11px; border-radius:7px; font-size:12.5px;
            background:var(--bahaya-lembut); color:var(--stop)}
/* Nomor training jadi label terpisah di judul panel, bukan disambung em-dash.
   "L1 — Paragon v2 — uji evaluasi" terbaca seperti potongan yang disambung
   mesin; nomornya memang identitas, bukan bagian dari namanya. */
.tr-panel-no{
  display:inline-block; margin-right:9px; padding:1px 7px; border-radius:5px;
  background:var(--lab-bg); color:var(--lab-ink); font-size:11px;
  font-weight:700; letter-spacing:.02em; vertical-align:1px;
}
/* Pita keadaan di bawah judul panel: cukup satu baris, tanpa judul bagian
   sendiri. */
.tr-p-pita{
  display:flex; gap:28px; flex-wrap:wrap; padding:11px 0 14px;
  margin:0 0 16px; border-bottom:1px solid var(--line);
}
.tr-p-pita>div{display:flex; flex-direction:column; gap:2px}
.tr-p-pita dt{font-size:11px; color:var(--ink-faint)}
.tr-p-pita dd{margin:0; font-size:14px; font-weight:600;
              font-variant-numeric:tabular-nums}
/* Panel diperlebar: grafiknya yang menentukan, dan pada 860px ia terjepit. */
.tr-panel{width:min(1020px,100%)}

/* -- galeri gambar hasil training ---------------------------------------- */
/* Deret mendatar yang digeser, bukan petak selebar panel: satu petak val
   itu mozaik 640x640 dan kalau ditaruh selebar panel ia sendirian memakan
   seluruh layar, sehingga angka-angka di bawahnya tidak pernah terlihat.
   Ubinnya cuma INDEKS — poligonnya dibaca di kaca pembesar. Ukuran dan
   bentuknya sengaja menyalin kartu foto latar di tab Versi. */
.tr-galeri{
  display:flex; gap:10px; margin-top:9px; padding-bottom:6px;
  overflow-x:auto; scroll-snap-type:x proximity; scrollbar-width:thin;
}
.tr-galeri>*{flex:0 0 auto; scroll-snap-align:start}
/* Label dan prediksi petak yang SAMA menempel jadi satu kartu: dipisah,
   tidak ada yang tahu tebakan mana milik poligon mana. */
.tr-pasang{
  display:flex; gap:1px; padding:1px; border-radius:9px; overflow:hidden;
  border:1px solid var(--line); background:var(--line);
}
.tr-pasang .tr-gbr{border:0; border-radius:0}
.tr-gbr{
  margin:0; width:132px; border:1px solid var(--line); border-radius:9px;
  overflow:hidden; background:var(--sunk);
}
.tr-gbr img{
  display:block; width:132px; height:132px; object-fit:cover;
  cursor:zoom-in; background:var(--sunk);
}
.tr-gbr figcaption{
  padding:4px 6px; font-size:11px; color:var(--ink-dim); text-align:center;
  background:var(--panel); overflow:hidden; white-space:nowrap;
  text-overflow:ellipsis;
}

/* -- kaca pembesar ------------------------------------------------------- */
/* z-index di atas .tr-tirai (60): galerinya sendiri hidup DI DALAM panel
   rincian, jadi lupanya harus menimpa panel itu, bukan terjepit di bawahnya. */
.tr-lup{
  position:fixed; inset:0; z-index:70; background:rgba(var(--tirai),.82);
  display:flex; align-items:center; justify-content:center; gap:6px;
  padding:18px;
}
.tr-lup-isi{margin:0; display:flex; flex-direction:column; gap:8px;
            align-items:center; min-width:0}
.tr-lup-isi img{
  display:block; max-width:min(86vw,1280px); max-height:82vh;
  width:auto; height:auto; object-fit:contain; border-radius:8px;
  background:var(--sunk);
}
.tr-lup-isi figcaption{color:#fff; font-size:12.5px; text-align:center;
                       font-variant-numeric:tabular-nums}
.tr-lup-nav,.tr-lup-tutup{
  border:1px solid rgba(255,255,255,.28); background:rgba(255,255,255,.10);
  color:#fff; border-radius:8px; cursor:pointer; line-height:1;
}
.tr-lup-nav{flex:0 0 auto; width:38px; height:64px; font-size:26px}
.tr-lup-nav[disabled]{opacity:.25; cursor:default}
.tr-lup-tutup{position:absolute; top:14px; right:16px;
              width:34px; height:34px; font-size:21px}
.tr-lup-nav:hover:not([disabled]),.tr-lup-tutup:hover{
  background:rgba(255,255,255,.20)}
@media (max-width:620px){ .tr-lup-nav{width:30px; height:52px} }
.tr-lipat{border:1px solid var(--line); border-radius:8px; padding:9px 11px}
.tr-lipat>summary{cursor:pointer; display:flex; flex-direction:column; gap:2px}
.tr-lipat>summary b{font-size:12.5px}
.tr-lipat>summary::marker{color:var(--ink-faint)}

/* ================================================================ LABEL AKSI
   Halaman pelabelan klip (classifier aksi video). Memakai ulang tata letak
   berprojek (header + sidebar + .sisi-isi) dan token yang sama; yang di sini
   hanya panggung <video> dan papan tombol kelas. Theme-correct: semua warna
   dari token, tak ada nilai keras yang hanya benar di satu tema. */
.ak-tata{display:flex;gap:20px;align-items:flex-start;max-width:1100px}
.ak-samping{flex:0 0 230px;display:flex;flex-direction:column;gap:10px}
.ak-utama{flex:1 1 0;min-width:0;display:flex;flex-direction:column;gap:12px}
.ak-judul{font-size:11px;letter-spacing:.04em;text-transform:uppercase;
  color:var(--ink-faint);margin:8px 0 2px;font-weight:600}
.ak-lajur{margin:0}
.ak-angka{margin:2px 0 0;font-size:12.5px;color:var(--ink-dim)}
.ak-angka b{color:var(--ink);font-size:15px}
.ak-daftar{list-style:none;margin:6px 0 0;padding:0;display:flex;
  flex-direction:column;gap:5px;font-size:12px;color:var(--ink-dim)}
.ak-daftar b{color:var(--ink)}
.ak-titik{display:inline-block;width:8px;height:8px;border-radius:50%;
  margin-right:7px;vertical-align:middle}
.ak-titik.t-ok{background:var(--ok)}
.ak-titik.t-stop{background:var(--stop)}
.ak-titik.t-ds{background:var(--aksi)}
.ak-saring{margin:6px 0 0}
.ak-ke-dataset{width:100%;margin-top:4px;justify-content:center}
/* Daftar unduhan versi terlatih: satu tautan unduh per versi + keterangan
   jumlah klip. Dibangun dari token supaya selaras kedua tema. */
.ak-versi-list{list-style:none;margin:4px 0 0;padding:0;display:flex;
  flex-direction:column;gap:6px}
.ak-versi-list li{display:flex;flex-direction:column;gap:2px}
.ak-unduh{width:100%;justify-content:center}
.ak-versi-ket{font-size:11.5px;color:var(--ink-dim)}

/* Panel evaluasi (Langkah 10): confusion matrix + vonis + pasangan tertukar.
   Vonis diberi warna dari `tingkat` supaya model buruk TAK tampak hijau. */
.ak-eval{margin-top:6px;padding:8px;border:1px solid var(--line2);
  border-radius:8px;background:var(--sunk);display:flex;flex-direction:column;
  gap:6px;font-size:11.5px;width:100%}
.ak-eval-vonis{margin:0;color:var(--ink-dim)}
.ak-eval-vonis[data-tingkat="baik"]{color:var(--ok)}
.ak-eval-vonis[data-tingkat="sedang"]{color:var(--warn)}
.ak-eval-vonis[data-tingkat="buruk"],
.ak-eval-vonis[data-tingkat="kosong"]{color:var(--stop)}
.ak-eval-pesan,.ak-eval-galat{margin:0;color:var(--ink-dim);line-height:1.5}
.ak-eval-galat{color:var(--stop)}
.ak-eval-bingung{list-style:none;margin:0;padding:0;display:flex;
  flex-direction:column;gap:2px;color:var(--ink-dim);
  font-variant-numeric:tabular-nums}
.ak-eval-img{display:block;max-width:100%;border:1px solid var(--line);
  border-radius:6px;background:#fff}
.ak-bantu{list-style:none;margin:4px 0 0;padding:0;display:flex;
  flex-direction:column;gap:4px}
.ak-bantu kbd,.ak-hot{font-family:ui-monospace,"SF Mono",Menlo,monospace;
  font-size:10.5px;background:var(--sunk);color:var(--ink-dim);
  border:1px solid var(--line2);border-radius:4px;padding:1px 5px;
  line-height:1.4}

/* Kepala pemutar: posisi, nama berkas, label kini. */
.ak-kepala{display:flex;align-items:center;gap:10px;min-height:22px}
.ak-posisi{font-size:12px;color:var(--ink-faint);font-variant-numeric:tabular-nums}
.ak-nama{font-size:12.5px;color:var(--ink-dim);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap;max-width:46%}
.ak-kini{font-size:12px;font-weight:600;color:var(--aksi)}

/* Panggung video: latar gelap di kedua tema (seperti canvas anotasi) supaya
   klip apa pun kontras, dan berbingkai rapi. */
.ak-panggung{background:var(--kanvas);border:1px solid var(--line);
  border-radius:12px;overflow:hidden;display:flex;align-items:center;
  justify-content:center;min-height:300px;position:relative}
.ak-video{display:block;max-width:100%;max-height:60vh;width:auto;height:auto;
  background:#000}
.ak-panggung .ak-kosong{margin:0;padding:48px 16px}

.ak-baris{display:flex;align-items:center;gap:8px;flex-wrap:wrap}

/* Papan tombol kelas: satu per kelas, pintasan angka di pojok, warna kelas
   sebagai aksen tepi kiri. data-on = label klip ini sekarang. */
/* Baris kelas: tombol-tombol kelas mengalir, tombol "Kelas" (editor, pemilik)
   menempel di ujung dan tak ikut menyusut. */
.ak-kelas-baris{display:flex;align-items:flex-start;gap:8px}
.ak-kelas{display:flex;flex-wrap:wrap;gap:8px;flex:1 1 auto;min-width:0}
.ak-kelas-baris #ak-set-kelas{flex:0 0 auto}
.ak-kls{position:relative;display:inline-flex;align-items:center;gap:8px;
  padding:9px 13px 9px 12px;border:1px solid var(--line2);border-radius:9px;
  background:var(--raised);color:var(--ink);font-size:13px;font-weight:500;
  cursor:pointer;border-left:4px solid var(--w,var(--line2))}
.ak-kls:hover{background:var(--hover)}
.ak-kls:focus-visible{outline:2px solid var(--aksi);outline-offset:2px}
.ak-kls[data-on]{background:var(--ok-lembut);border-color:var(--aksi);
  color:var(--aksi);font-weight:700}
.ak-kls .ak-hot{background:var(--panel)}
.ak-kls[data-on] .ak-hot{background:var(--panel);color:var(--aksi);
  border-color:var(--aksi)}
.ak-nokelas{margin-top:8px}

@media (max-width:900px){
  .ak-tata{flex-direction:column}
  .ak-samping{flex:1 1 auto;width:100%}
  .ak-nama{max-width:100%}
}

/* ============================================================
   Tab KLIP video (Dataset/Anotasi): judul halaman + grid klip + overlay pemutar.
   Grid memakai poster DIAM (/aksi/thumb); klip hanya berputar di overlay "Lihat".
   ============================================================ */
.hal-judul { margin: 0 0 4px; font-size: 20px; }
.hal-ket { margin: 0 0 16px; max-width: 72ch; }

.akg-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 12px;
  margin-bottom: 14px; }
.akg-info { font-weight: 600; }
.akg-kelas { margin-left: 4px; }
/* Cari nama berkas klip di toolbar grid/dataset klip (padanan "Cari nama
   berkas" grid gambar). */
.ak-cari { display: flex; align-items: center; gap: 6px; }
.ak-cari input { border: 1px solid var(--line2); background: var(--raised);
  color: var(--ink); border-radius: 999px; padding: 6px 12px;
  font-family: inherit; font-size: 13px; min-width: 170px; }
.ak-cari input:focus { outline: 0; border-color: var(--ok); }

.akg-grid { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
.akg-kartu { position: relative; border: 1px solid var(--garis, #2a2f3a);
  border-radius: 8px; overflow: hidden; background: var(--panel, #171b22); }
.akg-thumb { display: block; width: 100%; aspect-ratio: 1 / 1;
  object-fit: cover; background: #0c0f14; }
.akg-thumb-x { min-height: 110px; }
.akg-label { position: absolute; top: 6px; left: 6px; color: #fff;
  font-size: 11px; font-weight: 600; padding: 1px 7px; border-radius: 6px;
  max-width: calc(100% - 12px); overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
.akg-kartu-bar { display: flex; align-items: center; gap: 6px;
  justify-content: space-between; padding: 6px; }
.akg-lihat { padding: 2px 10px; font-size: 12px; }
.akg-cb { width: 16px; height: 16px; flex: none; }

.akg-hal { margin: 16px 0; display: flex; align-items: center; gap: 8px;
  justify-content: center; }
.akg-hal-on { font-weight: 700; }
.akg-kosong { margin: 20px 0; }

.akg-overlay { position: fixed; inset: 0; z-index: 50; background: rgba(0,0,0,.82);
  display: flex; align-items: center; justify-content: center; padding: 24px; }
.akg-main { max-width: 92vw; max-height: 92vh; display: flex;
  flex-direction: column; align-items: center; gap: 8px; }
.akg-tutup { align-self: flex-end; }
.akg-video { max-width: 92vw; max-height: 80vh; background: #000; border-radius: 8px; }
.akg-vnama { color: #cbd3df; }

/* Overlay pelabelan klip (Label Aksi grid): pemutar + tombol kelas + nav. */
.ak-lbl-main { width: min(720px, 94vw); }
.ak-lbl-kepala { display: flex; align-items: center; gap: 10px; width: 100%;
  color: #cbd3df; font-size: 13px; }
.ak-lbl-nama { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ak-lbl-kelas { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center;
  margin: 10px 0 4px; }
.ak-lbl-baris { display: flex; gap: 8px; align-items: center; }
.ak-lbl-ket { color: #cbd3df; min-height: 1em; margin: 2px 0 0; }
/* badge "belum" (abu) + badge dataset (hijau pojok kanan) di kartu grid */
.akg-label-x { background: #3a3f4b; color: #c7ccd6; }
.akg-ds { position: absolute; top: 6px; right: 6px; font-size: 10px;
  background: #1f6f43; color: #fff; padding: 1px 6px; border-radius: 6px; }

/* Strip sub-tab area ANOTASI klip (Grid · Papan · Bagi) — tiga permukaan
   sejajar, dirender _aksi_subnav.html. Dipakai bersama ketiga halaman klip. */
.aksi-subnav { display: flex; flex-wrap: wrap; gap: 6px; margin: 2px 0 14px;
  border-bottom: 1px solid var(--line); padding-bottom: 10px; }
.aksi-subnav a { text-decoration: none; font-size: 13px; font-weight: 600;
  color: var(--ink-dim); padding: 6px 12px; border-radius: 7px;
  border: 1px solid transparent; transition: background .12s, color .12s; }
.aksi-subnav a:hover { color: var(--ink); background: var(--sunk); }
.aksi-subnav a[data-on] { color: var(--ink); background: var(--panel);
  border-color: var(--line); box-shadow: 0 1px 3px rgba(var(--bayang), .12); }

/* Dialog "Tugaskan ulang": periksa username/email (anotasi.js) */
.tg-cari-baris { display:flex; gap:8px; align-items:center; }
.tg-cari-baris .op-cari { flex:1; }
.tg-kartu { display:flex; align-items:center; gap:10px; padding:14px 20px 6px; }
.tg-nama { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis;
  white-space:nowrap; }
.tg-badge { font-size:11px; padding:1px 8px; border-radius:999px;
  background:var(--sunk); border:1px solid var(--line); white-space:nowrap; }
.tg-badge-luar { opacity:.6; }
.tg-aksi { display:flex; gap:8px; align-items:center; padding:4px 20px 8px; }
.tg-aksi .op-cari { flex:1; }
#tg-link { font-size:12px; }

/* ======================================================= RESPONSIF HP: papan & grid
   Ditaruh di AKHIR berkas DENGAN SENGAJA: aturan .jb-kisi (<=1100) dan
   body.an-tetap .an-kolom (<=1000) juga cocok di <=560, jadi blok ini harus
   datang SESUDAH mereka agar urutan sumber memenangkannya. Selaras dengan grid
   Dataset #grid pada @media (max-width:560px). */
@media (max-width:560px){
  /* C) Papan anotasi (kolom menumpuk di <=1000): di HP tiap kolom dibuat sedikit
     lebih tinggi dan kartunya dirapatkan, supaya kolom tak terasa sempit. */
  body.an-tetap .an-kolom{max-height:72vh}
  .an-kolom{padding:10px}
  .an-kartu{padding:9px 10px 10px}
  /* D) Grid klip (Dataset/Anotasi) & grid dataset gambar: ubin lebih besar,
     lebih sedikit per baris — perlakuan yang sama dengan #grid. */
  .akg-grid,.jb-kisi{grid-template-columns:repeat(auto-fill,minmax(150px,1fr))}
}

/* Unggah dari HP: kartu QR di halaman unggah (desktop). QR-nya digambar
   qrcode.min.js sebagai SVG di #ug-hp-kode. Latar putih tetap, bukan ikut
   tema: pemindai QR ponsel butuh kontras gelap-di-terang, dan kode di atas
   latar gelap mode malam gagal terbaca. */
.ug-hp-qr{display:flex;gap:18px;align-items:flex-start;margin-top:12px;
  flex-wrap:wrap}
.ug-hp-kode{background:#fff;padding:10px;border-radius:10px;
  width:188px;height:188px;flex:none;display:flex;align-items:center;
  justify-content:center}
.ug-hp-kode svg{width:100%;height:100%;display:block}
.ug-hp-info{flex:1;min-width:220px;display:flex;flex-direction:column;gap:6px}
.ug-hp-url{word-break:break-all;font-size:13px}
.ug-hp-catatan{margin:2px 0 6px}
.ug-hp-info .chip{align-self:flex-start}
