.mr-0,
.mx-0 {
  margin-right: 0 !important;
}

.mr-1,
.mx-1 {
  margin-right: 0.25rem !important;
}

.mr-2,
.mx-2 {
  margin-right: 0.5rem !important;
}

.mr-3,
.mx-3 {
  margin-right: 1rem !important;
}

.mr-4,
.mx-4 {
  margin-right: 1.5rem !important;
}

.mr-5,
.mx-5 {
  margin-right: 3rem !important;
}

.ml-0,
.mx-0 {
  margin-left: 0 !important;
}

.ml-1,
.mx-1 {
  margin-left: 0.25rem !important;
}

.ml-2,
.mx-2 {
  margin-left: 0.5rem !important;
}

.ml-3,
.mx-3 {
  margin-left: 1rem !important;
}

.ml-4,
.mx-4 {
  margin-left: 1.5rem !important;
}

.ml-5,
.mx-5 {
  margin-left: 3rem !important;
}

/* =====================================================================
   Tata letak sidebar
   =====================================================================

   Tabler menggeser konten dengan selektor saudara
   (.navbar-vertical.navbar-expand-lg ~ .page-wrapper), tapi setiap view
   di sini membungkus isinya dengan <div class="wrapper"> lebih dulu —
   dan satu view (soal/view-soal.php) bahkan menambah satu lapis lagi.
   Karena itu pergeserannya dipasang langsung pada .wrapper.

   .wrapper hanya ada di halaman yang memuat sidebar; halaman login
   memakai header-signin.php dan tidak punya elemen ini, jadi aturan
   global di bawah tidak mengenainya.
   ===================================================================== */
@media (min-width: 992px) {
  .wrapper {
    padding-left: 15rem;
  }
}

/* Label pemisah kelompok di sidebar.
   Warnanya diatur di bagian palet paling bawah berkas ini — label ini
   memakai kelas .nav-link juga, jadi keduanya harus diputuskan di satu
   tempat supaya tidak saling menimpa tanpa disengaja. */
.navbar-vertical .sidebar-label {
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  pointer-events: none;
  padding-bottom: 0.25rem;
}

/* Isi sidebar didorong ke atas, blok identitas admin menempel ke bawah */
.navbar-vertical .navbar-collapse {
  display: flex;
  flex-direction: column;
}

.navbar-vertical .navbar-nav {
  width: 100%;
}

/* Judul halaman di bilah atas */
.navbar-brand-title .h3 {
  color: #354052;
}

/* Tabel lebar tidak boleh memaksa seluruh halaman menggulir mendatar */
.card-table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

/* =====================================================================
   Tabel admin yang menggulir sendiri
   =====================================================================

   Tabel Hasil punya 13 kolom bertanda text-nowrap. Sebelumnya ia meluber
   keluar kartu, membuat SELURUH halaman bisa digulir mendatar — dan
   karena sidebar position:fixed diam di tepi layar, tabelnya lewat di
   bawah sidebar saat digulir. Wadah di bawah mengurung gulirnya di dalam
   kartu, sehingga halamannya sendiri tidak pernah bergeser.
   ===================================================================== */
.tabel-gulir {
  overflow-x: auto;
  overflow-y: auto;
  max-height: 70vh;
  -webkit-overflow-scrolling: touch;
}

/* Judul kolom tetap terlihat saat isinya digulir. Latarnya WAJIB buram —
   dengan latar bening, baris yang lewat di belakangnya akan menembus. */
.tabel-gulir thead th {
  position: -webkit-sticky;
  position: sticky;
  top: 0;
  z-index: 2;
  background: #f8fafc;
}

/* Kolom aksi dipatok di tepi kanan, supaya tombolnya tetap terjangkau
   berapa pun tabel digulir ke kanan. */
.tabel-gulir th.kolom-aksi,
.tabel-gulir td.kolom-aksi {
  position: -webkit-sticky;
  position: sticky;
  right: 0;
  z-index: 1;
  background: #fff;
  box-shadow: -6px 0 6px -6px rgba(0, 0, 0, .18);
}

/* Sel sudut : menempel ke atas DAN ke kanan sekaligus, jadi z-index-nya
   harus di atas keduanya. Tanpa ini ia tertimpa salah satu. */
.tabel-gulir thead th.kolom-aksi {
  z-index: 3;
  background: #f8fafc;
}

/* Baris belang tetap terbaca di kolom yang menempel */
.tabel-gulir tbody tr:hover td.kolom-aksi {
  background: #f8fafc;
}

/* =====================================================================
   Tombol aksi pada tabel
   =====================================================================

   Kolom aksi sengaja TIDAK memakai dropdown. Wadah .tabel-gulir memotong
   apa pun yang keluar dari kotaknya, dan sel .kolom-aksi baris di bawahnya
   — sticky, z-index sama, lebih belakang di DOM — menggambar di atas menu
   yang sedang terbuka. Deretan tombol tanpa popup menghapus kedua persoalan
   itu sekaligus, dan menghemat satu klik.
   ===================================================================== */
.aksi-sel {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: .25rem;
}

.aksi-baris {
  display: flex;
  align-items: center;
  gap: .25rem;
  flex-wrap: nowrap;
}

/* Bidang tekan minimal 34px : ikon 20px saja terlalu kecil untuk jari. */
.btn-aksi {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 34px;
  height: 34px;
  padding: .25rem .4rem;
  line-height: 1;
}

.btn-aksi .ikon-aksi {
  width: 20px;
  height: 20px;
  vertical-align: middle;
}

/* Aksi merusak diberi jarak, supaya tidak tertekan karena meleset */
.aksi-baris .aksi-pisah {
  margin-left: .35rem;
}

/* Tombol catatan pada baris yang catatannya kosong : terlihat ada, jelas
   tidak bisa ditekan. */
.btn-aksi:disabled {
  opacity: .35;
  cursor: default;
}

/* =====================================================================
   Menu aksi — SATU BENTUK DI SEGALA LEBAR LAYAR
   =====================================================================

   Seluruh isi sel aksi diganti SATU tombol hamburger yang memunculkan
   popup berisi daftar aksi tegak, lengkap dengan tulisannya. Tombolnya
   disisipkan ke setiap .aksi-sel oleh myjs/function.js pada peristiwa
   draw.dt, jadi kedelapan tabel admin mendapatkannya tanpa satu pun
   berkas load_data perlu disentuh.

   DULU INI HANYA BERLAKU DI BAWAH 768px. Media query itu sudah dilepas:
   deretan lima ikon tanpa tulisan di kolom selebar 150px menuntut artinya
   dihafal, sementara popup yang sama menuliskannya. Mesinnya memang sudah
   berjalan di segala lebar layar sejak awal — yang menyembunyikannya cuma
   media query tadi, jadi menyeragamkannya tidak menyentuh JS sama sekali.

   Karena pemicunya selalu ada di DOM berapa pun lebar layarnya, memutar
   layar atau mengubah ukuran jendela tidak bisa meninggalkan baris dalam
   keadaan setengah jadi.

   DUA WUJUD, SATU MESIN

     ponsel   (< 768px)  kotak melayang di tengah layar, berkepala nama
                         baris, latar halaman digelapkan dan diburamkan
     desktop  (>= 768px) menu ramping menempel di samping tombolnya,
                         tanpa kepala, latar halaman tetap terlihat jelas

   Yang membedakan cuma posisi dan wujud kotaknya. Posisinya dihitung
   myjs/function.js pada posisikanSamping(); sisanya blok "Menu aksi
   menempel di samping" di bawah.

   KARTUNYA MENGIKUTI POPUP SKOR, dan itu disengaja.
   Keduanya muncul di ponsel pada halaman yang berdekatan, jadi butirnya
   harus terbaca sebagai satu keluarga. Nilai bingkai, radius, latar, dan
   padding .lembar-aksi-butir diambil dari .skor-popup-baris di bawah
   berkas ini — kalau salah satunya diubah, ubah keduanya.

   Yang dibagi HANYA kartunya, bukan posisi kotak pembungkusnya:
   #skorPeserta tetap di tengah di segala lebar layar, sementara menu ini
   menempel di samping begitu layarnya cukup lebar.

   CATATAN NAMA : kelas di sini masih berawalan "lembar-aksi" karena
   versi pertamanya memang lembar yang menempel di tepi bawah. Bentuk itu
   sudah ditinggalkan, tapi namanya sengaja tidak diganti — penggantian
   menyentuh lima berkas termasuk dua rangkaian uji tanpa satu pun
   perubahan yang terlihat pemakai.
   ===================================================================== */

/* Ukurannya sengaja 40px, bukan 34px seperti .btn-aksi : ini satu-satunya
   tombol yang tersisa di sel aksi, jadi bidang tekannya bidang tekan jari. */
.btn-menu-aksi {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 40px;
  height: 40px;
  padding: .25rem .4rem;
  line-height: 1;
  border-radius: .5rem;
}

.btn-menu-aksi .ikon-aksi {
  width: 20px;
  height: 20px;
  vertical-align: middle;
}

/* Posisi ponselnya TIDAK diatur di sini sama sekali : ia datang dari
   modal-dialog-centered + modal-sm di markup-nya, sama persis dengan
   #skorPeserta. Aturan lembar bawah yang dulu ada di tempat ini —
   position: fixed, translateY(100%), radius hanya di atas,
   safe-area-inset-bottom — sudah dibuang seluruhnya.

   Peringatan yang menyertainya masih berlaku penuh dan sekarang justru
   makin penting: menyisakan sebagian berarti dua aturan posisi saling
   menimpa. Blok "Menu aksi menempel di samping" di bawah karena itu
   membatalkan KEEMPAT sifat pemusatnya sekaligus — display, margin,
   min-height, dan max-width — bukan sebagian. */

.lembar-aksi-judul {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Pembungkusnya sama dengan .skor-popup : kolom, berjarak .5rem.
   Jarak itulah yang memisahkan butir — bukan garis. */
.lembar-aksi-daftar {
  display: flex;
  flex-direction: column;
  gap: .5rem;
}

/* Kartu, bukan baris. Bingkai, radius, dan latarnya SAMA PERSIS dengan
   .skor-popup-baris — lihat catatan di kepala blok ini.

   Ikon lalu tulisan, keduanya rata kiri pada satu garis tegak, supaya
   daftarnya terbaca lurus dan tidak berundak. */
.lembar-aksi-butir {
  display: flex;
  gap: .85rem;
  align-items: center;
  width: 100%;
  padding: .65rem .85rem;
  border: 1px solid var(--ui-garis);
  border-radius: .5rem;
  background: var(--ui-permukaan);
  color: var(--ui-navy-2);
  font-size: .9375rem;
  text-align: left;
  text-decoration: none;
}

.lembar-aksi-butir:hover,
.lembar-aksi-butir:focus {
  border-color: var(--ui-aksen);
  background: var(--ui-latar);
  color: var(--ui-navy-2);
  text-decoration: none;
}

.lembar-aksi-butir:disabled {
  opacity: .4;
  cursor: default;
}

/* Lebar ikonnya dipatok supaya seluruh tulisan mulai di kolom yang sama.
   Tanpa flex-shrink: 0 ikon yang lebih lebar akan menggeser tulisannya dan
   daftarnya berhenti terlihat lurus. */
.lembar-aksi-ikon {
  display: inline-flex;
  flex: 0 0 24px;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  color: var(--ui-aksen);
}

/* SVG-nya salinan dari tombol di baris, jadi masih membawa .ikon-aksi yang
   berukuran 20px. Di dalam popup ukurannya ditentukan di sini. */
.lembar-aksi-ikon svg {
  width: 24px;
  height: 24px;
}

.lembar-aksi-teks {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Aksi merusak : kartu bernada merah, ditaruh paling bawah.
   Tidak ada garis pemisah — popup skor juga tidak punya, dan jaraknya
   sudah cukup memisahkan. Jarak tambahan di atasnya yang menjaga maksud
   "jangan sampai salah tekan" dari .aksi-baris .aksi-pisah. */
.lembar-aksi-butir.lembar-aksi-merusak {
  margin-top: .35rem;
  border-color: var(--ui-bahaya);
  background: #fef2f2;
  color: var(--ui-bahaya);
}

.lembar-aksi-butir.lembar-aksi-merusak .lembar-aksi-ikon {
  color: var(--ui-bahaya);
}

.lembar-aksi-butir.lembar-aksi-merusak:hover,
.lembar-aksi-butir.lembar-aksi-merusak:focus {
  border-color: var(--ui-bahaya);
  background: #fee2e2;
  color: var(--ui-bahaya);
}

/* SELURUH isi sel disembunyikan, bukan hanya .btn-aksi : tombol Salin
   Link di halaman Tes memakai .copy .btn-warning dan tidak pernah membawa
   .btn-aksi, jadi menyaring kelas akan meninggalkannya sendirian di baris.
   Kekhususan 0,2,0 mengalahkan .btn-aksi dan .aksi-baris yang masing-masing
   cuma 0,1,0.

   AMAN untuk semua tabel karena kedelapan pembuat .aksi-sel ada di dalam
   fungsi render DataTables — tes_reload, soal_reload, subsoal_reload,
   admin_reload, audio_reload, login_reload, galat_reload, dan
   hasil_tes_toefl_reload — sehingga semuanya melewati draw.dt dan pasti
   kebagian tombol hamburgernya. Kalau suatu saat ada .aksi-sel yang
   dirakit di luar DataTables, aturan ini akan menyembunyikan seluruh
   aksinya tanpa memberi gantinya. */
.aksi-sel > *:not(.btn-menu-aksi) {
  display: none;
}

.aksi-sel .btn-menu-aksi {
  display: inline-flex;
}

/* =====================================================================
   Menu aksi menempel di samping (desktop)
   =====================================================================

   Kelas .lembar-aksi-samping dipasang myjs/function.js pada #menuAksi DAN
   pada <body> saat menunya dibuka di layar >= 768px, lalu dilepas lagi
   pada hidden.bs.modal. Nilai top/bottom/left/right-nya properti sebaris
   dari posisikanSamping(); yang di sini hanya sifat yang tidak bergantung
   pada letak tombolnya.

   KENAPA DUA TEMPAT. Latar gelapnya elemen SAUDARA yang ditempelkan
   Bootstrap langsung ke <body>, di luar #menuAksi — tidak ada selektor
   dari dalam modal yang bisa menjangkaunya. Karena itu penandanya juga
   dipasang di <body>.

   SELURUHNYA DIKURUNG #menuAksi. Ada 13 dialog lain di aplikasi ini yang
   memakai modal-dialog-centered; aturan tanpa kurungan akan menggeser
   semuanya.
   ===================================================================== */

/* Keempat pembatal pemusatnya ditulis BERSAMA — lihat peringatan di atas.
   Kekhususan (1,2,1) mengalahkan .modal-dialog-centered dan aturan media
   query Bootstrap yang masing-masing cuma (0,1,0), jadi tidak ada
   !important yang diperlukan. */
#menuAksi.lembar-aksi-samping .modal-dialog {
  position: absolute;   /* .modal induknya fixed → koordinat viewport 1:1 */
  display: block;       /* batal: flex centring .modal-dialog-centered */
  margin: 0;            /* batal: margin 1.75rem auto pada >= 576px */
  min-height: 0;        /* batal: min-height calc(100% - 3.5rem) */
  max-width: none;      /* batal: max-width 380px milik .modal-sm */
  width: 240px;         /* harus sama dengan LEBAR_MENU di function.js */
}

/* Kepala dibuang : nama pesertanya sudah terbaca di baris tepat di sebelah
   menunya, jadi mengulanginya tidak menambah kejelasan.

   display:none, BUKAN elemennya dibuang dari markup. Tombol tutup di
   dalamnya load-bearing — jalur butir bertautan di function.js menutup
   popup dengan menekan [data-bs-dismiss] ini, dan HTMLElement.click()
   tetap bekerja pada elemen tersembunyi. Membuangnya akan memutus
   penutupan setiap butir bertautan tanpa satu pun galat. */
#menuAksi.lembar-aksi-samping .modal-header {
  display: none;
}

/* Menu sepanjang apa pun tetap muat. Patokan paruh layar di function.js
   menjamin ruang yang tersedia selalu sekitar setengah tinggi layar, jadi
   batas inilah pasangannya — tanpa ini menu berisi tujuh aksi di jendela
   pendek akan tergunting tepi layar. */
#menuAksi.lembar-aksi-samping .modal-body {
  max-height: calc(50vh - 24px);
  overflow-y: auto;
}

/* Blur dipasang .modal-blur PADA elemen modalnya sendiri, bukan pada latar
   gelapnya. Untuk menu kecil yang menempel di tombolnya, memburamkan
   seluruh halaman membawa perhatian menjauh dari baris yang sedang
   digarap — persis yang ingin dihindari. */
#menuAksi.lembar-aksi-samping {
  -webkit-backdrop-filter: none;
          backdrop-filter: none;
}

/* Latar gelapnya dibeningkan, TAPI elemennya tetap ada : itu yang membuat
   menekan di luar menu tetap menutupnya, persis seperti dropdown.
   Kekhususan (0,3,1) mengalahkan .modal-backdrop.show yang (0,2,0). */
body.lembar-aksi-samping .modal-backdrop.show {
  opacity: 0;
}

/* Kolomnya menyusut dari ±150px jadi ±46px, dan ruang itu jatuh ke kolom
   data. Patokan sticky-nya DIPERTAHANKAN justru karena sekarang sempit:
   tabelnya lebih lebar setelah kolom d-none dimunculkan, dan tanpa patokan
   tombol aksinya baru terjangkau setelah menggulir jauh ke kanan. */
.tabel-gulir th.kolom-aksi,
.tabel-gulir td.kolom-aksi {
  width: 1%;
}

/* =====================================================================
   Pratinjau sertifikat
   =====================================================================
   Gambarnya dirakit ke <canvas> lalu dipasang sebagai data URL pada <img>
   ini. width:100% supaya sertifikat mendatar 210x148mm ikut menyusut di
   layar sempit — kanvas berukuran piksel tetap dan tidak akan menyusut
   sendiri.

   Bingkai tipis dan bayangan dipasang karena sertifikatnya sendiri sering
   berlatar putih : tanpa itu tepi kertasnya lebur dengan latar modal dan
   tidak terlihat di mana gambarnya berakhir. */
.pratinjau-gambar {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--ui-garis);
  border-radius: .5rem;
  box-shadow: var(--ui-bayang);
}

/* Isi popup catatan.
   pre-wrap wajib : catatan sering diketik berbaris-baris, dan tanpa ini
   seluruhnya runtuh jadi satu paragraf panjang. break-word menahan
   tempelan URL panjang supaya tidak melebarkan modalnya. */
.catatan-isi {
  white-space: pre-wrap;
  overflow-wrap: break-word;
  word-break: break-word;
  line-height: 1.55;
}

/* =====================================================================
   Kolom nama
   =====================================================================

   Sebelumnya beberapa tabel memasang text-nowrap pada SELURUH elemen
   <table>. Akibatnya nama peserta tidak pernah boleh membungkus, jadi
   kolomnya melebar sepanjang nama terpanjang di halaman itu dan
   mendorong seluruh kolom lain keluar layar. Memberi width pada
   kolomnya tidak menolong selama nowrap masih di sana; yang dibuang
   nowrap-nya, lalu lebarnya dibatasi di sini.

   min-width menjaga kolomnya tidak menyempit sampai satu kata per baris
   ketika tabelnya padat.
   ===================================================================== */
.kolom-nama {
  max-width: 18rem;
  min-width: 9rem;
  white-space: normal;
  overflow-wrap: break-word;   /* nama tanpa spasi tetap terpotong rapi */
  word-break: break-word;
  line-height: 1.35;
}

@media (max-width: 575.98px) {
  .kolom-nama { max-width: 11rem; }
}

/* Kepala tabel bergaya acuan : kecil, kapital, redup */
.tabel-gulir thead th {
  font-size: .75rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  font-weight: 700;
  color: #6c757d;
  white-space: nowrap;
  vertical-align: middle;
}

/* Pengendali DataTables (pencarian, jumlah baris, halaman) tidak boleh
   ikut terkurung di dalam wadah yang menggulir */
.dataTables_wrapper .dataTables_filter,
.dataTables_wrapper .dataTables_length,
.dataTables_wrapper .dataTables_info,
.dataTables_wrapper .dataTables_paginate {
  padding: .5rem .25rem;
}

/* =====================================================================
   Pemilih paket soal
   ===================================================================== */

.paket-kotak {
  border: 1px solid #e6e9ee;
  border-radius: .5rem;
  overflow: hidden;
  height: 100%;
}

.paket-kepala {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .5rem .65rem;
  background: #f8f9fa;
  border-bottom: 1px solid #e6e9ee;
  font-size: .78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: #6c757d;
}

.paket-kepala .paket-cari {
  margin-left: auto;
  max-width: 9rem;
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
}

.paket-daftar,
.paket-urutan {
  max-height: 15rem;
  overflow-y: auto;
}

/* --- sisi kiri : centang --- */
.paket-tersedia {
  display: flex;
  align-items: center;
  gap: .6rem;
  width: 100%;
  min-height: 44px;              /* bidang tekan yang nyaman di layar sentuh */
  margin: 0;
  padding: .45rem .65rem;
  border-bottom: 1px solid #f1f3f6;
  cursor: pointer;
}

.paket-tersedia:hover { background: #f8fafc; }

/* Kotak centang tidak boleh ikut menyusut.
   Tanpa ini, paket bernama panjang ("L Barron 03 S Longman 05 R Longman 04")
   membungkus jadi tiga baris dan flex memampatkan kotaknya sampai gepeng —
   terlihat rusak, dan bidang tekannya jadi terlalu kecil untuk jari. */
.paket-tersedia .form-check-input {
  flex: 0 0 auto;
  margin: 0;
}

/* Nama yang membungkus tetap sejajar dengan kotaknya di baris pertama */
.paket-tersedia { align-items: flex-start; }

.paket-nama   { flex: 1 1 auto; }
.paket-jumlah {
  color: #8a94a6;
  font-size: .78rem;
  white-space: nowrap;
  flex: 0 0 auto;          /* "140 soal" tidak ikut terpotong */
}

/* --- sisi kanan : urutan --- */
.paket-baris {
  display: flex;
  align-items: center;
  gap: .5rem;
  min-height: 44px;
  padding: .35rem .5rem;
  border-bottom: 1px solid #f1f3f6;
  background: #fff;
}

.paket-baris.sedang-diseret {
  background: #eef3fb;
  border-radius: .35rem;
}

/* Gagang seret. touch-action WAJIB none — tanpa itu peramban merebut gerak
   jari untuk menggulir dan urutannya tidak pernah berpindah di layar
   sentuh, persis seperti yang pernah terjadi di perancang sertifikat. */
.paket-gagang {
  touch-action: none;
  cursor: grab;
  color: #b3bcc9;
  letter-spacing: -3px;
  padding: 0 .25rem;
  user-select: none;
}

.paket-gagang:active { cursor: grabbing; }

.paket-urut {
  flex: 0 0 auto;
  width: 1.5rem;
  height: 1.5rem;
  line-height: 1.5rem;
  text-align: center;
  border-radius: 50%;
  background: #206bc4;
  color: #fff;
  font-size: .75rem;
  font-weight: 700;
}

.paket-label  { flex: 1 1 auto; }
.paket-tombol { flex: 0 0 auto; white-space: nowrap; }

.paket-tombol .btn {
  padding: .1rem .4rem;
  line-height: 1.2;
}

.paket-kosong {
  padding: 1.25rem .65rem;
  text-align: center;
  color: #8a94a6;
  font-size: .85rem;
}

/* =====================================================================
   Panel anti-cheat pada form tes
   ===================================================================== */

/* Menjorok dengan garis tepi merah, meniru susunan acuan : opsi di
   dalamnya jelas milik sakelar induk di atasnya, bukan setelan sejajar. */
.anticheat-opsi {
  margin-left: .5rem;
  padding-left: .85rem;
  border-left: 3px solid #d63939;
  transition: opacity .15s ease;
}

.anticheat-butir {
  display: flex;
  align-items: flex-start;
  gap: .1rem;
  min-height: 34px;
  margin-bottom: .5rem;
}

/* Kotak sakelar tidak boleh menyusut saat keterangannya membungkus */
.anticheat-butir .form-check-input {
  flex: 0 0 auto;
  margin-top: .15rem;
}

.anticheat-butir .alert-icon {
  width: 18px;
  height: 18px;
  vertical-align: text-bottom;
}

/* =====================================================================
   Blok hak akses pada form admin
   ===================================================================== */

.izin-daftar {
  border: 1px solid #e6e9ee;
  border-radius: .5rem;
  padding: .5rem .75rem;
  max-height: 17rem;
  overflow-y: auto;
  transition: opacity .15s ease;
}

.izin-kelompok + .izin-kelompok {
  margin-top: .65rem;
  padding-top: .5rem;
  border-top: 1px solid #f1f3f6;
}

.izin-judul {
  font-size: .7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: #8a94a6;
  margin-bottom: .25rem;
}

/* Bidang tekan yang nyaman di layar sentuh */
.izin-butir {
  display: flex;
  align-items: center;
  min-height: 34px;
  margin-bottom: 0;
}

/* =====================================================================
   Perancang sertifikat
   ===================================================================== */

/* Tanpa ini seret di layar sentuh tidak berfungsi sama sekali.
   Peramban memperlakukan setiap gerak jari sebagai perintah menggulir
   dan membatalkan rangkaian pointer sebelum pointermove sempat berguna,
   sehingga elemen tidak pernah ikut bergerak. */
.desain-el {
  touch-action: none;
}

/* menyeret elemen tidak menyorot teks di sekitarnya */
#kanvas,
#kanvas * {
  -webkit-user-select: none;
  user-select: none;
}

/* =====================================================================
   Halaman pemantauan langsung : kepala & kendali
   ===================================================================== */

/* deskripsi tidak melebar melewati lebar baca */
.kepala-monitor-ket { max-width: 52rem; }

/* lebar tetap supaya kepala tidak bergoyang saat teks status berganti
   ("memuat...", "diperbarui 12:34:56", "dijeda") */
#waktuSegar { min-width: 10.5rem; font-variant-numeric: tabular-nums; }

/* di ponsel kedua tombol berbagi baris penuh, status di atasnya */
@media (max-width: 575.98px) {
  #waktuSegar { min-width: 0; flex: 1 1 100%; }
  .kendali-monitor .btn { flex: 1 1 0; }
}

/* =====================================================================
   Kartu pemantauan (tampilan ponsel)
   ===================================================================== */

.kartu-pantau {
  border-left: 3px solid transparent;
}

.kartu-pantau .card-body { padding: .75rem 1rem; }

/* tombol aksi kartu ponsel : sejajar penuh di layar sangat sempit */
@media (max-width: 399.98px) {
  .kartu-pantau .d-flex.flex-wrap.gap-1 > .btn { flex: 1 1 0; }
}

.kartu-pantau-putus { border-left-color: #f59f00; }
.kartu-pantau-awas  { border-left-color: #d63939; }

/* Tiga angka utama dibagi rata : sesi, terjawab, sisa waktu */
.kartu-pantau-angka > div {
  flex: 1 1 0;
  text-align: center;
}

.kartu-pantau-angka .nilai {
  font-size: 1.05rem;
  font-weight: 700;
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
  color: #354052;
}

.kartu-pantau-angka .label {
  font-size: .72rem;
  color: #8a94a6;
}

.kartu-pantau-progres {
  height: 4px;
  border-radius: 2px;
  background: #edf1f6;
  overflow: hidden;
}

.kartu-pantau-progres > span {
  display: block;
  height: 100%;
  background: #206bc4;
  transition: width .25s ease;
}

/* Pemberitahuan pengganti kanvas di layar sempit */
.desain-sempit {
  border: 1px dashed #c7d0db;
  border-radius: .5rem;
  padding: 1.25rem;
  text-align: center;
  color: #667382;
  background: #f7f9fc;
}

/* Pesan sesaat, ditampilkan mengambang dari _partials/footer.php.

   Dipasang di KANAN BAWAH, bukan kanan atas : di kanan atas ia menimpa
   tombol aksi halaman (Tambahkan Tes) dan, di layar sempit, butir
   sidebar. Sudut kanan bawah tidak pernah berisi kendali apa pun. */
.pesan-flash {
  position: fixed;
  bottom: 1rem;
  right: 1rem;
  z-index: 1080;
  width: 24rem;
  max-width: calc(100vw - 2rem);
  transition: opacity .35s ease, transform .35s ease;
}

/* Keadaan saat menghilang sendiri setelah beberapa detik */
.pesan-flash.pudar {
  opacity: 0;
  transform: translateY(.5rem);
  pointer-events: none;
}

.pesan-flash .alert {
  margin-bottom: 0;
  box-shadow: 0 6px 20px rgba(0, 0, 0, .18);
}

/* Kartu pintasan di bawah tabel dashboard */
.card-pintasan {
  transition: border-color .15s ease, box-shadow .15s ease;
}

.card-pintasan:hover {
  border-color: #206bc4;
  box-shadow: 0 2px 10px rgba(32, 107, 196, .12);
}

.pintasan-label {
  font-weight: 600;
  line-height: 1.25;
}

/* Kartu ringkas dashboard */
.stat-card .stat-nilai {
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1.1;
}

.stat-card .stat-label {
  font-size: 0.8rem;
  color: #6c7a91;
}

/* =====================================================================
   PALET & PERMUKAAN
   =====================================================================

   Lapis ini dimuat SESUDAH seluruh berkas Tabler (lihat urutan <link> di
   _partials/header.php), jadi cukup menimpa dari sini — tabler.min.css
   tidak perlu disentuh sama sekali, dan pembaruan Tabler tidak akan
   menghapus perubahan ini.

   Token yang sama, dengan nilai yang sama, juga ada di aplikasi peserta
   (assets/mycss/tes.css). Kalau salah satu diubah, ubah keduanya —
   uji_ui_palet.js membandingkannya dan akan gagal kalau menyimpang.
   ===================================================================== */

:root {
  --ui-navy:       #0f172a;   /* sidebar, permukaan gelap */
  --ui-navy-2:     #1e293b;   /* teks utama */
  --ui-aksen:      #2563eb;   /* tombol utama, tautan, butir aktif */
  --ui-aksen-2:    #3b82f6;   /* hover */
  --ui-sukses:     #10b981;
  --ui-bahaya:     #dc2626;
  --ui-peringatan: #f59e0b;
  --ui-latar:      #f1f5f9;   /* latar halaman */
  --ui-permukaan:  #ffffff;   /* kartu, modal, form */
  --ui-garis:      #e2e8f0;
  --ui-redup:      #64748b;
  --ui-radius:     .75rem;
  --ui-bayang:     0 1px 3px rgba(15, 23, 42, .06), 0 1px 2px rgba(15, 23, 42, .04);
  --ui-bayang-lg:  0 10px 15px -3px rgba(15, 23, 42, .07), 0 4px 6px -4px rgba(15, 23, 42, .05);
  --ui-huruf:      'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

/* Tumpukan cadangan ditulis penuh : Inter dimuat dari Google Fonts, dan
   kalau CDN-nya tidak terjangkau halaman harus tetap wajar, bukan jatuh
   ke serif bawaan peramban. */
body {
  font-family: var(--ui-huruf);
  background-color: var(--ui-latar);
  color: var(--ui-navy-2);
  -webkit-font-smoothing: antialiased;
}

.page-title,
h1, h2, h3, h4, h5, h6 {
  color: var(--ui-navy-2);
  letter-spacing: -.01em;
}

.text-muted { color: var(--ui-redup) !important; }

a { color: var(--ui-aksen); }
a:hover { color: var(--ui-aksen-2); }

/* ---------------------------------------------------------------------
   Sidebar dongker
   ---------------------------------------------------------------------

   Tabler memberi .navbar-light latar putih; di sini dibalik jadi gelap.
   Selektornya dipatok ke .navbar-vertical supaya bilah atas halaman
   (navbar-header) tidak ikut menggelap.
   --------------------------------------------------------------------- */

.navbar-vertical.navbar-light {
  background: var(--ui-navy);
  border-right: 0;
  box-shadow: inset -1px 0 0 rgba(255, 255, 255, .04);
}

.navbar-vertical .navbar-brand {
  padding-top: 1.25rem;
  padding-bottom: 1.25rem;
}

/* =====================================================================
   KEKHUSUSAN DI BAWAH INI WAJIB SETINGGI ITU. JANGAN DISEDERHANAKAN.
   =====================================================================

   Tabler menyetel warna butir menu lewat:

       .navbar .navbar-nav .nav-link        { color: rgba(35,46,60,.7) }   k=30
       .navbar .navbar-nav .nav-link:hover  { color: #232e3c }             k=40

   Keduanya dirancang untuk sidebar TERANG. Di atas latar dongker kita,
   rgba(35,46,60,.7) menghasilkan rgb(29,39,55) — kontrasnya 1,19:1
   terhadap latar, yaitu praktis tidak terbaca.

   Versi pertama perombakan ini memakai `.navbar-vertical .nav-link`
   (k=20). Kekhususannya di bawah 30, jadi warnanya TIDAK PERNAH terpakai
   dan seluruh menu tenggelam ke dalam latar. Yang terlihat benar cuma
   butir aktif, karena kebetulan kekhususannya 40.

   Karena itu setiap aturan di bawah dipatok ke
   `.navbar-vertical.navbar-light .navbar-nav ...` — semua kelas itu
   memang sudah ada di markup (_partials/sidebar.php), jadi ini bukan
   kekhususan yang dibuat-buat, dan tidak perlu !important.

   uji_ui_render.js menyelesaikan kaskade yang sesungguhnya dan akan
   gagal kalau salah satu aturan ini kembali kalah.
   ===================================================================== */

/* k=40, mengalahkan .navbar .navbar-nav .nav-link (k=30) */
.navbar-vertical.navbar-light .navbar-nav .nav-link {
  color: #cbd5e1;
  border-radius: .5rem;
  margin: .1rem .5rem;
  padding-top: .5rem;
  padding-bottom: .5rem;
  transition: background-color .15s ease, color .15s ease;
}

/* k=50, mengalahkan .navbar .navbar-nav .nav-link:hover (k=40).
   Tanpa ini menyorot butir justru MENGGELAPKAN teksnya. */
.navbar-vertical.navbar-light .navbar-nav .nav-link:hover,
.navbar-vertical.navbar-light .navbar-nav .nav-link:focus {
  color: #ffffff;
  background: rgba(255, 255, 255, .06);
}

/* Butir aktif : latar aksen transparan + penanda kiri, bukan blok penuh —
   supaya tetap terbaca sebagai bagian dari sidebar, bukan tombol.

   k=50. Versi sebelumnya k=40, yang hanya menang karena seri melawan
   Tabler lalu diputus urutan berkas — benar, tapi bergantung pada
   sesuatu yang mudah berubah. */
.navbar-vertical.navbar-light .navbar-nav .nav-item.active > .nav-link,
.navbar-vertical.navbar-light .navbar-nav .nav-link.active {
  color: #ffffff;
  background: rgba(37, 99, 235, .22);
  box-shadow: inset 3px 0 0 var(--ui-aksen-2);
}

/* Ikon mewarisi currentColor, jadi ikut terang sendiri. Opacity dibuat
   penuh supaya tidak meredupkannya lagi. */
.navbar-vertical.navbar-light .navbar-nav .nav-link .nav-link-icon { opacity: 1; }

/* Label kelompok memakai kelas .nav-link juga, jadi ia ikut kalah dari
   Tabler dengan cara yang sama. k=50, dan sengaja lebih redup daripada
   butir menu supaya tetap terbaca sebagai pemisah — tapi 6,96:1, bukan
   3,75:1 seperti sebelumnya. */
.navbar-vertical.navbar-light .navbar-nav .nav-link.sidebar-label {
  color: #94a3b8;
  background: transparent;
  padding-left: 1rem;
  margin-top: .75rem;
}

.navbar-vertical .dropdown-menu {
  background: #16233b;
  border-color: rgba(255, 255, 255, .08);
}

/* =====================================================================
   Submenu Pengaturan
   =====================================================================

   Kekhususannya sengaja disamakan dengan aturan sidebar di atas (0,4,0 /
   0,5,0). Aturan Tabler `.navbar .navbar-nav .nav-link` bernilai 0,3,0 dan
   akan menang atas selektor yang lebih pendek — itu pernah membuat warna
   sidebar saya tidak pernah terpakai sama sekali.
   ===================================================================== */

/* Butir anak dilekukkan supaya hubungan induk-anaknya terbaca, dan dibuat
   sedikit lebih kecil agar tidak bersaing dengan butir menu utama. */
.navbar-vertical.navbar-light .submenu-aturan .navbar-nav .nav-link {
  padding-left: 3rem;
  font-size: .875rem;
}

/* Garis tipis di sisi kiri : penanda visual bahwa keenamnya satu kelompok,
   tanpa menambah kotak atau latar yang membuat sidebar ramai. */
.navbar-vertical .submenu-aturan {
  box-shadow: inset 2px 0 0 rgba(255, 255, 255, .10);
  margin-left: 1.15rem;
}

/* Tanda panah pemicu, berputar saat submenunya terbentang.
   aria-expanded diurus Bootstrap, jadi itu yang dipakai sebagai sumber
   kebenaran — bukan kelas tambahan yang harus disinkronkan sendiri. */
.nav-link-toggle {
  width: 0;
  height: 0;
  border-top: 5px solid currentColor;
  border-right: 4px solid transparent;
  border-left: 4px solid transparent;
  opacity: .7;
  transition: transform .15s ease-in-out;
}

.navbar-vertical .nav-link[aria-expanded="false"] .nav-link-toggle {
  transform: rotate(-90deg);
}

.navbar-vertical .dropdown-item { color: #94a3b8; }
.navbar-vertical .dropdown-item:hover {
  color: #ffffff;
  background: rgba(255, 255, 255, .06);
}

/* =====================================================================
   LACI MENU DI PONSEL
   =====================================================================

   SELURUH bagian ini terkurung di dalam @media (max-width: 991.98px).
   Di 992px ke atas tidak ada satu pun aturan di sini yang berlaku, jadi
   tampilan desktop persis seperti sebelumnya.

   992px dipilih supaya cocok dengan navbar-expand-lg milik <aside> :
   itulah titik di mana Tabler sendiri berhenti melipat sidebarnya.
   ===================================================================== */

@media (max-width: 991.98px) {

  /* Sidebar keluar dari aliran halaman dan menjadi panel yang digeser.
     transform, bukan left/width : hanya transform yang tidak memaksa
     perhitungan tata letak ulang di tiap bingkai animasi. */
  .navbar-vertical.navbar-light {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    z-index: 1045;             /* di atas .sticky-top (1020), di bawah modal (1050) */
    width: 16rem;
    max-width: 85vw;           /* di layar sangat sempit, isi di belakang tetap terlihat */
    padding: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    transform: translateX(-100%);
    transition: transform .22s ease-out;
  }

  body.laci-buka .navbar-vertical.navbar-light { transform: translateX(0); }

  /* #sidebar-menu masih membawa kelas .collapse milik Bootstrap, yang
     berarti display:none di bawah 992px. Karena tombolnya tidak lagi
     memakai mekanisme collapse, isinya dipaksa terlihat di sini —
     yang menyembunyikannya sekarang adalah transform lacinya. */
  .navbar-vertical .navbar-collapse.collapse {
    display: block !important;
  }

  .navbar-vertical .container-fluid {
    display: block;
    padding: 0;
  }

  .navbar-vertical .navbar-brand {
    display: flex;
    justify-content: center;
    padding: 1.25rem 1rem;
  }

  /* Latar gelap : menutupi isi halaman sekaligus menjadi bidang tutup. */
  .laci-latar {
    position: fixed;
    inset: 0;
    z-index: 1040;
    background: rgba(15, 23, 42, .55);
    opacity: 0;
    transition: opacity .22s ease-out;
  }

  body.laci-buka .laci-latar { opacity: 1; }

  /* Halaman di belakang tidak ikut tergulir selama laci terbuka. */
  body.laci-buka { overflow: hidden; }

  /* Butir menu diberi bidang sentuh yang layak. */
  .navbar-vertical.navbar-light .navbar-nav .nav-link {
    min-height: 44px;
    display: flex;
    align-items: center;
  }

  /* Judul halaman di ponsel : dipendekkan daripada membungkus jadi dua
     baris dan mendorong menu pengguna keluar bilah.
     =================================================================
     min-width: 0 WAJIB ADA di pembungkusnya.

     .navbar-brand-title adalah butir flex, dan butir flex bawaannya
     min-width:auto — ia MENOLAK menyusut lebih kecil daripada isinya.
     Tanpa baris ini, judul tes yang panjang ("Hasil TOEFL Online Test
     by Bahasa Inggris Net #179 | Monday, 31 August 2026") melebar
     sampai 590px di layar 390px, dan SELURUH halaman jadi bisa digulir
     mendatar. Diukur di Chrome, bukan diperkirakan.

     text-overflow hanya bekerja kalau pemotongannya benar-benar terjadi
     di sini, jadi overflow:hidden dipasang pada pembungkusnya juga.
     ================================================================= */
  .navbar-brand-title {
    min-width: 0;
    overflow: hidden;
  }

  .navbar-brand-title .h3 {
    font-size: 1.05rem;
    display: block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

/* ---------------------------------------------------------------------
   Popup skor peserta (ponsel)
   ---------------------------------------------------------------------
   Isi popupnya sendiri tidak dibatasi media query : begitu terbuka ia
   harus tampil benar berapa pun lebar layarnya. Yang dibatasi hanya
   PENANDA di sel nama — di desktop kolom L/S/R sudah tampil sendiri, jadi
   namanya memang tidak perlu terlihat bisa disentuh.
   --------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  .tabel-gulir tbody td.kolom-nama {
    position: relative;
    padding-right: 1.5rem;
    cursor: pointer;
  }

  /* Chevron menghadap KANAN : yang terbuka sebuah popup, bukan baris di
     bawahnya. Chevron ke bawah akan menjanjikan hal yang salah. */
  .tabel-gulir tbody td.kolom-nama::after {
    content: "";
    position: absolute;
    top: 50%;
    right: .55rem;
    width: .4rem;
    height: .4rem;
    margin-top: -.25rem;
    border-top: 2px solid #94a3b8;
    border-right: 2px solid #94a3b8;
    transform: rotate(45deg);
  }
}

.skor-popup {
  display: flex;
  flex-direction: column;
  gap: .5rem;
}

.skor-popup-baris {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: .65rem .85rem;
  border: 1px solid var(--ui-garis);
  border-radius: .5rem;
  background: var(--ui-permukaan);
}

.skor-popup-label {
  display: flex;
  align-items: baseline;
  gap: .5rem;
  min-width: 0;
}

/* Huruf L/S/R dibuat menonjol : itu yang tertulis di kepala kolom
   tabelnya, jadi hubungan antara popup dan tabel langsung terlihat. */
.skor-popup-huruf {
  font-size: 1rem;
  font-weight: 700;
  color: var(--ui-navy-2);
  flex: 0 0 auto;
}

.skor-popup-sesi {
  font-size: .8125rem;
  color: var(--ui-redup);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* tabular-nums : angka tidak bergeser lebarnya antar baris */
.skor-popup-nilai {
  font-size: 1.25rem;
  font-weight: 700;
  line-height: 1.2;
  color: var(--ui-aksen);
  font-variant-numeric: tabular-nums;
  flex: 0 0 auto;
}

/* Total ditonjolkan : itu angka yang paling dicari. */
.skor-popup-total {
  border-color: transparent;
  background: var(--ui-navy);
}

.skor-popup-total .skor-popup-label {
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: #94a3b8;
}

.skor-popup-total .skor-popup-nilai {
  font-size: 1.6rem;
  color: #ffffff;
}

/* Tombol kamera : menyimpan isi popup ini sebagai PNG.
   ---------------------------------------------------------------------
   SENGAJA tanpa kelas .btn — aturan .btn milik Tabler menang atas latar,
   lapisan, dan bingkai sekaligus, dan mengalahkannya butuh rentetan
   !important. Tidak memakai kelasnya sama sekali jauh lebih murah, yang
   berarti setiap sifat tombol harus ditulis sendiri di sini.

   Barisnya berlatar --ui-navy, jadi ikonnya putih. Redup saat diam supaya
   tidak bersaing dengan angka totalnya — itu yang paling dicari mata di
   baris ini — lalu pekat begitu disentuh.

   align-self dipatok center : induknya .skor-popup-label memakai
   align-items: baseline, dan tombol berisi SVG tidak punya garis dasar
   teks sehingga ikutnya jadi melayang. */
.tombol-kamera-skor {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  align-self: center;
  flex: 0 0 auto;

  padding: .1rem .2rem;
  border: 0;
  border-radius: .35rem;
  background: transparent;
  -webkit-appearance: none;
          appearance: none;

  color: #ffffff;
  line-height: 1;
  opacity: .55;
  cursor: pointer;
  transition: opacity .15s, background-color .15s;
}

.tombol-kamera-skor:hover,
.tombol-kamera-skor:focus-visible {
  opacity: 1;
  background: rgba(255, 255, 255, .14);
}

.tombol-kamera-skor:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px rgba(255, 255, 255, .25);
}

/* Ikonnya datang dari ikon_kecil() yang berukuran 20px; di sini sedikit
   dikecilkan supaya tidak setinggi tulisan TOTAL SCORE di sebelahnya. */
.tombol-kamera-skor .ikon-aksi {
  width: 18px;
  height: 18px;
  vertical-align: middle;
}

/* Nama peserta bisa panjang; dibungkus daripada dipotong, karena inilah
   satu-satunya penanda popup ini milik siapa. */
.skor-popup-nama {
  overflow-wrap: anywhere;
  line-height: 1.35;
}

/* Tombol laci. Bidang sentuhnya 44px penuh walau ikonnya 24px. */
.btn-laci {
  width: 44px;
  height: 44px;
  margin-right: .5rem;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--ui-navy-2);
  flex: 0 0 auto;
}

.btn-laci:hover,
.btn-laci:focus { background: rgba(15, 23, 42, .06); }

/* Menghormati pengguna yang mematikan animasi di sistemnya. */
@media (prefers-reduced-motion: reduce) {
  .navbar-vertical.navbar-light,
  .laci-latar { transition: none; }
}

/* ---------------------------------------------------------------------
   Kartu & modal
   --------------------------------------------------------------------- */

.card {
  border: 1px solid var(--ui-garis);
  border-radius: var(--ui-radius);
  box-shadow: var(--ui-bayang);
}

.card-header {
  border-bottom: 1px solid var(--ui-garis);
  background: transparent;
  padding-top: .9rem;
  padding-bottom: .9rem;
}

.card-title { font-weight: 600; }

.modal-content {
  border: 0;
  border-radius: var(--ui-radius);
  box-shadow: var(--ui-bayang-lg);
}

.modal-header,
.modal-footer { border-color: var(--ui-garis); }

/* Kartu angka dashboard */
.stat-card .stat-nilai { color: var(--ui-navy-2); }
.stat-card .stat-label { color: var(--ui-redup); }

/* Ikon kartu ringkas dashboard (tablerIcon() bawaan 24px, di sini 32px)
   dan baris keterangan kecil di bawah label */
.stat-card .stat-ikon svg { width: 32px; height: 32px; }
.stat-card .stat-sub { font-size: .72rem; line-height: 1.2; margin-top: .15rem; }

/* =====================================================================
   SELEKSI MASSAL (modules/seleksi_massal.js)
   Kotak centang disisipkan ke sel pertama; isi asli sel dibungkus
   .pilih-isi supaya tetap sebaris dengan kotaknya.
   ===================================================================== */
td .pilih-baris { vertical-align: middle; margin-right: .5rem; flex: 0 0 auto; }
td .pilih-isi   { display: inline-block; vertical-align: middle; max-width: calc(100% - 2rem); }
th .pilih-semua { vertical-align: middle; }

.bilah-massal {
  position: fixed;
  left: 50%;
  bottom: 1.25rem;
  transform: translateX(-50%);
  z-index: 1040;
  max-width: calc(100% - 2rem);
  padding: .5rem .75rem;
  border-radius: 999px;
  background: var(--ui-navy-2, #1e293b);
  color: #fff;
  box-shadow: 0 10px 30px rgba(15, 23, 42, .28);
}

.bilah-massal-isi { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
.bilah-massal-jumlah { font-weight: 600; padding: 0 .25rem; white-space: nowrap; }
.bilah-massal .btn-link { color: #93c5fd; text-decoration: underline; padding: 0 .25rem; }
.bilah-massal .bilah-massal-batal { background: rgba(255, 255, 255, .12); color: #fff; border-color: transparent; }
.bilah-massal-tombol { display: inline-flex; flex-wrap: wrap; gap: .4rem; }

/* tab entitas di halaman Sampah */
.tab-sampah.active { background: var(--ui-aksen, #206bc4); color: #fff; border-color: transparent; }
.tab-sampah.active .badge.bg-red { background: #fff !important; color: #d63939; }

/* Kepala kartu Tren : kendali membungkus ke baris berikutnya di layar sempit */
#kartuTren .card-header { align-items: flex-start; }
#kartuTren .card-header .btn-group .btn.active { background: var(--ui-aksen, #206bc4); color: #fff; border-color: transparent; }

.card-pintasan:hover {
  border-color: var(--ui-aksen);
  box-shadow: 0 4px 14px rgba(37, 99, 235, .14);
}

.kartu-pantau { border-left-width: 3px; }

/* ---------------------------------------------------------------------
   Form
   ---------------------------------------------------------------------

   form-floating memang ada di Tabler beta2 dan dipakai 51 kali di
   aplikasi ini, jadi yang dirapikan pola yang sudah dipakai — bukan
   diganti dengan pola lain yang harus disunting satu per satu.
   --------------------------------------------------------------------- */

.form-control,
.form-select {
  border-color: #cbd5e1;
  border-radius: .5rem;
  color: var(--ui-navy-2);
  transition: border-color .15s ease, box-shadow .15s ease;
}

.form-control:hover:not(:disabled):not([readonly]),
.form-select:hover:not(:disabled) { border-color: #94a3b8; }

/* Cincin fokus dibuat tebal dan berwarna aksen : ini penanda utama
   "kolom mana yang sedang saya isi" pada form panjang. */
.form-control:focus,
.form-select:focus {
  border-color: var(--ui-aksen);
  box-shadow: 0 0 0 3px rgba(37, 99, 235, .15);
}

.form-control[readonly] {
  background-color: #f8fafc;
  color: var(--ui-redup);
}

.form-label {
  font-weight: 600;
  font-size: .8125rem;
  color: var(--ui-navy-2);
}

.form-hint { color: var(--ui-redup); }

.form-floating > label { color: var(--ui-redup); }

/* ---------------------------------------------------------------------
   Modul Kelas / Member Area
   --------------------------------------------------------------------- */

.thumb-kelas {
  width: 64px;
  height: 36px;
  object-fit: cover;
  border-radius: .375rem;
  flex: 0 0 auto;
  display: inline-block;
}

.kepala-kelas .thumb-kelas { width: 96px; height: 54px; }
.thumb-kosong { background: var(--ui-garis); }

.thumb-yt { width: 64px; height: 36px; object-fit: cover; border-radius: .375rem; }
.thumb-yt-besar { width: 100%; max-width: 240px; border-radius: .5rem; }

/* Pratinjau gambar OG dalam rasio 1200 x 630 */
.pratinjau-og {
  width: 100%;
  max-width: 360px;
  aspect-ratio: 1200 / 630;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border: 1px dashed var(--ui-garis);
  border-radius: .5rem;
  background: #f8fafc;
}

.pratinjau-og img { width: 100%; height: 100%; object-fit: cover; }

/* Kotak tanggal Litepicker : readonly, tapi bukan kotak yang "mati" —
   kekhususannya (0,3,0) di atas .form-control[readonly] (0,2,0). */
.form-control.tgl-tampil[readonly] {
  background-color: #fff;
  color: var(--ui-navy-2);
  cursor: pointer;
}

/* Tab Member / Sesi / Materi / Menu : bergulir mendatar di layar sempit */
.tab-kelas { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.tab-kelas .nav-tabs { flex-wrap: nowrap; white-space: nowrap; border-bottom: 0; }
.tab-kelas .nav-link { display: inline-flex; align-items: center; min-height: 44px; }

.pilih-tipe .btn { display: inline-flex; align-items: center; justify-content: center; gap: .35rem; }

/* Tab di dalam modal kelas : bergulir mendatar di layar sempit */
.tab-modal-kelas {
  flex-wrap: nowrap;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border-bottom: 1px solid var(--ui-garis);
  margin: -.25rem 0 0;
}

.tab-modal-kelas .nav-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  white-space: nowrap;
  font-weight: 600;
  color: var(--ui-redup);
}

.tab-modal-kelas .nav-link.active { color: var(--ui-aksen); }

/* Tema kelas : preset warna & pratinjau hero kecil di modal */
.preset-warna {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 2px solid #fff;
  box-shadow: 0 0 0 1px #cbd5e1;
  background: linear-gradient(135deg, var(--p1), var(--p2));
  cursor: pointer;
  padding: 0;
}

.preset-warna:hover, .preset-warna:focus-visible { box-shadow: 0 0 0 2px var(--ui-aksen); outline: none; }

.pratinjau-tema {
  position: relative;
  min-height: 84px;
  border-radius: .75rem;
  padding: .85rem 1rem;
  color: #fff;
  background: linear-gradient(135deg, #2563eb, #1e3a8a);
  overflow: hidden;
}

.pratinjau-tema.terang { color: var(--ui-navy); }
.pratinjau-tema-teks { font-weight: 700; font-size: 1.05rem; }
.pratinjau-tema-bar { height: 6px; margin-top: .75rem; border-radius: 999px; background: rgba(255, 255, 255, .25); width: 70%; }
.pratinjau-tema-bar > span { display: block; height: 100%; width: 45%; border-radius: 999px; background: currentColor; opacity: .9; }
.pratinjau-tema-logo { position: absolute; top: .6rem; right: .75rem; height: 28px; width: auto; max-width: 110px; object-fit: contain; background: #fff; padding: 3px 6px; border-radius: 6px; }

.pratinjau-logo {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 64px;
  border: 1px dashed var(--ui-garis);
  border-radius: .5rem;
  background: #f8fafc;
  padding: .5rem;
}

.pratinjau-logo-gambar { max-height: 56px; max-width: 220px; object-fit: contain; }

/* pratinjau gambar sampul materi (modal materi), rasio 16:9 */
.pratinjau-sampul {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 16 / 9;
  max-width: 320px;
  border: 1px dashed #cbd5e1;
  border-radius: .5rem;
  background: #f8fafc;
  overflow: hidden;
}
.pratinjau-sampul-gambar { width: 100%; height: 100%; object-fit: cover; }

.sandi-kelas { user-select: all; }

.form-check-input:checked {
  background-color: var(--ui-aksen);
  border-color: var(--ui-aksen);
}

.form-check-input:focus {
  border-color: var(--ui-aksen);
  box-shadow: 0 0 0 3px rgba(37, 99, 235, .15);
}

.input-group-text {
  background: #f8fafc;
  border-color: #cbd5e1;
  color: var(--ui-redup);
}

/* ---------------------------------------------------------------------
   Tabel
   ---------------------------------------------------------------------
   Kepala tabel mengikuti pola yang dipakai aplikasi rujukan : huruf
   kecil berkapital dengan jarak antar huruf, di atas latar sangat muda.
   Lebar kolom nama dan sel aksi yang sudah diatur di atas TIDAK diubah.
   --------------------------------------------------------------------- */

/* Selektornya memakai kombinator anak, BUKAN ".table thead th".
   Tabler mewarnai setiap sel lewat `.table > :not(caption) > * > *`,
   dan ".table thead th" kekhususannya di bawah itu — aturannya akan
   tertulis rapi tapi tidak pernah terpakai.

   box-shadow ikut dinolkan karena Tabler tidak hanya memakai
   background-color: ia melapisi tiap sel dengan
   `inset 0 0 0 9999px var(--tblr-table-accent-bg)`. Selama accent-bg
   masih transparan lapisan itu tak terlihat, tapi begitu tabelnya
   memakai varian belang atau sorot, lapisan itu menutupi warna kepala
   yang kita pasang di sini. */
.table > thead > tr > th,
.tabel-gulir > thead > tr > th {
  background-color: #f8fafc;
  box-shadow: none;
  color: var(--ui-redup);
  font-size: .75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
  border-bottom: 1px solid var(--ui-garis);
}

.table tbody td {
  border-top-color: #eef2f7;
  vertical-align: middle;
}

/* Sel aksi yang menempel di kanan sudah punya aturan sorot sendiri di
   bagian tabel-gulir di atas, dengan warna yang sama. */
.table > tbody > tr:hover > td { background-color: #f8fafc; }

/* ---------------------------------------------------------------------
   Tombol & lencana
   --------------------------------------------------------------------- */

.btn { border-radius: .5rem; font-weight: 500; }

.btn-primary {
  background-color: var(--ui-aksen);
  border-color: var(--ui-aksen);
}

.btn-primary:hover,
.btn-primary:focus {
  background-color: var(--ui-aksen-2);
  border-color: var(--ui-aksen-2);
}

.btn-primary:focus,
.btn-outline-primary:focus { box-shadow: 0 0 0 3px rgba(37, 99, 235, .2); }

.btn-success { background-color: var(--ui-sukses); border-color: var(--ui-sukses); }
.btn-danger  { background-color: var(--ui-bahaya); border-color: var(--ui-bahaya); }

.badge {
  border-radius: .375rem;
  padding: .28em .6em;
  font-weight: 600;
}

/* Ikon aksi di kolom kanan : hanya radiusnya yang diselaraskan. Peta
   warnanya (kuning=salin, abu=detail, biru=buka, merah=hapus, ...)
   sengaja tidak disentuh — sudah dipakai dan dihafal. */
.btn-aksi { border-radius: .5rem; }

.page-item.active .page-link {
  background-color: var(--ui-aksen);
  border-color: var(--ui-aksen);
}

.alert { border-radius: .5rem; }

/* ---------------------------------------------------------------------
   Halaman masuk
   ---------------------------------------------------------------------
   Kelas .halaman-masuk dipasang di _partials/header-signin.php. Halaman
   ini tidak punya .wrapper maupun sidebar, jadi aturan tata letak di
   bagian atas berkas ini tidak mengenainya.
   --------------------------------------------------------------------- */

.halaman-masuk {
  background: var(--ui-latar);
  min-height: 100vh;
}

.halaman-masuk .card {
  border: 0;
  border-radius: 1rem;
  box-shadow: var(--ui-bayang-lg);
}

.halaman-masuk .card-body { padding: 2rem; }

.halaman-masuk .card-title {
  font-size: 1.35rem;
  font-weight: 700;
}

/* ---------------------------------------------------------------------
   Modal Edit Peserta — tombol Tt dan kotak tanggal lahir
   ---------------------------------------------------------------------
   Dipasang di _partials/modal/modal_hasil_tes.php, digerakkan oleh
   myjs/modules/edit_peserta.js.
   --------------------------------------------------------------------- */

/* Wadah penambat tombol Tt. .form-floating milik Tabler tidak position:
   relative dengan sendirinya, jadi tanpa baris ini tombolnya akan
   menambat ke .modal-content dan melayang di sudut modal. */
.medan-huruf { position: relative; }

/* Ruang supaya ekor nama yang panjang tidak menyelinap ke bawah tombol. */
.medan-huruf .form-control { padding-right: 2.75rem; }

/* SENGAJA tanpa kelas .btn : aturan .btn milik Tabler menang atas latar,
   lapisan, dan bingkai sekaligus, dan mengalahkannya butuh rentetan
   !important. Tidak memakai kelasnya sama sekali jauh lebih murah — yang
   berarti setiap sifat tombol harus dituliskan sendiri di sini, termasuk
   yang biasanya datang gratis dari .btn. */
.tombol-huruf {
  position: absolute;
  top: 50%;
  right: .5rem;
  transform: translateY(-50%);
  z-index: 3;                 /* label .form-floating berada di lapisan 2 */

  padding: .15rem .4rem;
  border: 0;
  border-radius: .35rem;
  background: transparent;
  -webkit-appearance: none;
          appearance: none;

  font-family: inherit;
  font-size: .9rem;
  font-weight: 600;
  line-height: 1;
  color: var(--ui-redup);

  opacity: .35;               /* "semi transparan" : ada, tapi tidak menuntut */
  cursor: pointer;
  transition: opacity .15s, background-color .15s;
}

.tombol-huruf:hover,
.tombol-huruf:focus-visible {
  opacity: 1;
  background: var(--ui-latar);
  color: var(--ui-aksen);
}

.tombol-huruf:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px rgba(37, 99, 235, .15);
}

/* Kotak tanggal lahir dibuat readonly supaya tidak ada tanggal ketikan
   bebas yang gagal diurai, dan supaya papan ketik ponsel tidak ikut
   muncul menutupi kalender. Tapi .form-control[readonly] di bagian Form
   mengecatnya abu-abu seperti medan mati, padahal kotak ini justru bisa
   diklik. Warna permukaannya dikembalikan di sini. */
.medan-tanggal .form-control[readonly] {
  background: var(--ui-permukaan);
  color: inherit;
  cursor: pointer;
}

/* Kalender Litepicker.
   ---------------------------------------------------------------------
   Litepicker menyuntikkan gaya dasarnya sendiri dan Tabler menimpanya
   lewat tabler-vendors.css, jadi kalendernya sudah tampil wajar tanpa
   berkas gaya tambahan. Yang tertinggal cuma dua kotak dropdown bulan
   dan tahun : keduanya <select> polos bawaan peramban, dan di tengah
   modal yang sudah dirapikan ia terlihat seperti tambalan.

   Ditulis rangkap dengan .litepicker di depannya supaya menang atas
   aturan Tabler yang menyasar selektor yang sama. */
.litepicker .container__months .month-item-name,
.litepicker .container__months .month-item-year {
  padding: .25rem 1.5rem .25rem .5rem;
  border: 1px solid #cbd5e1;
  border-radius: .375rem;
  background-color: var(--ui-permukaan);
  color: var(--ui-navy-2);
  font-family: inherit;
  font-size: .8125rem;
  cursor: pointer;
}

.litepicker .container__months .month-item-name:hover,
.litepicker .container__months .month-item-year:hover {
  border-color: #94a3b8;
}

.litepicker .container__months .month-item-name:focus,
.litepicker .container__months .month-item-year:focus {
  outline: 0;
  border-color: var(--ui-aksen);
  box-shadow: 0 0 0 3px rgba(37, 99, 235, .15);
}

/* Menyamakan lengkung kalender dengan lengkung kotak isian di atasnya;
   Tabler mematoknya di 4px. */
.litepicker .container__months {
  border-radius: .5rem;
}

/* .btn-icon milik Tabler hanya menetapkan min-width dan membiarkan padding
   samping bawaan .btn. Dengan ikon 24px hasilnya tombol lonjong selebar
   dua kali tingginya, bukan tombol ikon. Paddingnya dipangkas di sini. */
.btnKartu {
  padding-left: .5rem;
  padding-right: .5rem;
}

.sweet_loader {
	width: 140px;
	height: 140px;
	margin: 0 auto;
	animation-duration: 0.5s;
	animation-timing-function: linear;
	animation-iteration-count: infinite;
	animation-name: ro;
	transform-origin: 50% 50%;
	transform: rotate(0) translate(0,0);
}