/* ====== Sistem RSVP Partai Demokrat — gaya global ====== */
:root {
  --biru: #12308f;
  --biru-tua: #0a1c56;
  --biru-terang: #2a56d6;
  --biru-muda: #e8edfa;
  --merah: #c8102e;
  --hijau: #1b7f3b;
  --abu: #64708a;
  --garis: #dfe5f2;
  --kartu: #ffffff;
  --latar: #f2f5fc;
  --radius: 16px;
  --bayang: 0 10px 30px rgba(10, 28, 86, .10);
  --bayang-kuat: 0 18px 48px rgba(10, 28, 86, .18);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: "Segoe UI", Inter, Roboto, system-ui, -apple-system, sans-serif;
  background: var(--latar);
  color: #16204a;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--biru); }

.wrap { width: min(1140px, 92vw); margin: 0 auto; }

/* ---------- Topbar ---------- */
.topbar {
  background: linear-gradient(90deg, var(--biru-tua), var(--biru) 55%, var(--biru-terang));
  color: #fff;
  padding: 12px 0;
  box-shadow: 0 2px 14px rgba(10, 28, 86, .25);
  position: sticky; top: 0; z-index: 20;
}
.topbar .wrap { display: flex; align-items: center; gap: 14px; }
.topbar img { height: 38px; width: auto; }
.topbar .judul { font-weight: 700; letter-spacing: .3px; font-size: 1rem; }
.topbar .sub { font-size: .78rem; opacity: .82; }
.topbar .kanan { margin-left: auto; display: flex; gap: 10px; align-items: center; }
.topbar a.tautan {
  color: #fff; text-decoration: none; font-size: .85rem; font-weight: 600;
  border: 1px solid rgba(255, 255, 255, .45); padding: 7px 14px; border-radius: 999px;
  transition: background .2s;
}
.topbar a.tautan:hover { background: rgba(255, 255, 255, .16); }

/* ---------- Hero ---------- */
.hero {
  position: relative;
  background: linear-gradient(115deg, var(--biru-tua), var(--biru) 45%, var(--biru-terang));
  color: #fff;
  padding: 54px 0 92px;
  overflow: hidden;
}
.hero .bg {
  position: absolute; inset: 0;
  background-image: url('/assets/img/banner.jpg');
  background-size: cover; background-position: center;
  opacity: .34;
}
.hero::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 8px;
  background: linear-gradient(90deg, var(--merah) 0 33%, #fff 33% 66%, var(--biru-terang) 66%);
}
.hero .wrap { position: relative; z-index: 2; text-align: center; }
.hero .lencana {
  display: inline-flex; align-items: center; gap: 9px;
  background: rgba(255, 255, 255, .16); border: 1px solid rgba(255, 255, 255, .32);
  padding: 7px 16px; border-radius: 999px; font-size: .8rem; font-weight: 600;
  letter-spacing: 1.4px; text-transform: uppercase; backdrop-filter: blur(4px);
}
.hero h1 {
  margin: 18px 0 10px;
  font-size: clamp(1.8rem, 4.4vw, 3rem);
  font-weight: 800; letter-spacing: -.5px; line-height: 1.15;
  text-shadow: 0 4px 24px rgba(0, 0, 0, .28);
}
.hero p.desk {
  margin: 0 auto; max-width: 640px; font-size: 1.02rem; opacity: .93;
}
/* Logo HUT berlatar putih (JPEG), jadi dibingkai sebagai lencana putih. */
.hero .logo-utama {
  height: 96px; width: auto; display: block; margin: 0 auto 16px;
  background: #fff; padding: 10px 14px; border-radius: 14px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .28);
}

/* ---------- Kartu acara ---------- */
.grid-acara {
  display: grid; gap: 22px;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  margin-top: -58px; position: relative; z-index: 5; padding-bottom: 10px;
}
.kartu {
  background: var(--kartu); border-radius: var(--radius); box-shadow: var(--bayang);
  border: 1px solid var(--garis); overflow: hidden;
  display: flex; flex-direction: column;
  transition: transform .22s ease, box-shadow .22s ease;
}
.kartu:hover { transform: translateY(-6px); box-shadow: var(--bayang-kuat); }
.kartu .pita { height: 6px; background: var(--biru); }
.kartu .isi { padding: 24px 24px 20px; flex: 1; display: flex; flex-direction: column; }
.kartu .ikon {
  width: 54px; height: 54px; border-radius: 14px; display: grid; place-items: center;
  background: var(--biru-muda); color: var(--biru); font-size: 1.5rem; margin-bottom: 14px;
}
.kartu h3 { margin: 0 0 8px; font-size: 1.16rem; line-height: 1.35; color: var(--biru-tua); }
.kartu .kecil { color: var(--abu); font-size: .88rem; margin: 0 0 16px; }
.kartu .meta { font-size: .84rem; color: var(--abu); display: grid; gap: 5px; margin-bottom: 18px; }
.kartu .tombol-baris { margin-top: auto; }

/* ---------- Tombol ---------- */
.tbl {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  border: 0; cursor: pointer; font: inherit; font-weight: 700; font-size: .95rem;
  padding: 12px 22px; border-radius: 10px; text-decoration: none;
  transition: filter .18s, transform .18s, box-shadow .18s;
}
.tbl:hover { filter: brightness(1.07); transform: translateY(-1px); }
.tbl:active { transform: translateY(0); }
.tbl-utama { background: var(--biru); color: #fff; box-shadow: 0 6px 16px rgba(18, 48, 143, .28); }
.tbl-merah { background: var(--merah); color: #fff; box-shadow: 0 6px 16px rgba(200, 16, 46, .28); }
.tbl-hijau { background: var(--hijau); color: #fff; box-shadow: 0 6px 16px rgba(27, 127, 59, .26); }
.tbl-garis { background: #fff; color: var(--biru); border: 1.5px solid var(--biru); }
.tbl-lebar { width: 100%; }
.tbl[disabled] { opacity: .6; cursor: not-allowed; transform: none; }

/* ---------- Bagian umum ---------- */
.bagian { padding: 56px 0; }
.bagian h2 {
  text-align: center; font-size: 1.6rem; color: var(--biru-tua); margin: 0 0 6px;
}
.bagian .sub-judul { text-align: center; color: var(--abu); margin: 0 0 34px; }

/* ---------- Form ---------- */
.panel {
  background: #fff; border: 1px solid var(--garis); border-radius: var(--radius);
  box-shadow: var(--bayang); padding: 30px;
}
.form-baris { margin-bottom: 18px; }
/* `.label-baris` untuk baris yang isinya BUKAN kolom (mis. nilai yang dipatok
   server): tampilannya sama, tapi elemennya bukan <label> — label tanpa
   kontrol yang ditunjuk tidak punya arti bagi pembaca layar. */
.form-baris label, .form-baris .label-baris {
  display: block; font-weight: 600; font-size: .92rem; margin-bottom: 7px; color: var(--biru-tua);
}
.form-baris label .wajib { color: var(--merah); }
.form-baris label .opsional { color: var(--abu); font-weight: 500; font-size: .85rem; }
/* Berlaku untuk semua jenis input teks (termasuk password & search) supaya
   tidak ada kolom yang luput dan tampil dengan gaya bawaan peramban. */
.form-baris input:not([type=radio]):not([type=checkbox]),
.form-baris textarea,
.form-baris select {
  width: 100%; padding: 12px 14px; border: 1.5px solid var(--garis); border-radius: 10px;
  font: inherit; background: #fbfcff; transition: border-color .18s, box-shadow .18s;
}
.form-baris input:focus, .form-baris textarea:focus, .form-baris select:focus {
  outline: 0; border-color: var(--biru-terang); box-shadow: 0 0 0 4px rgba(42, 86, 214, .13); background: #fff;
}
.form-baris textarea { min-height: 110px; resize: vertical; }

/* Nilai yang dipatok server dan tidak bisa diubah dari panel. Sengaja
   SEBENTUK kolom formulir (tinggi, radius, garis yang sama) supaya barisnya
   duduk persis seperti kolom lain, tapi latarnya abu dan tanpa fokus — beda
   yang cukup untuk membaca "ini bukan kolom yang menunggu diisi". */
.acara-terkunci {
  display: flex; align-items: center; gap: 10px;
  width: 100%; padding: 12px 14px; border: 1.5px solid var(--garis); border-radius: 10px;
  background: var(--latar); color: var(--biru-tua);
}
.acara-terkunci .gembok { flex: 0 0 auto; font-size: .95rem; opacity: .75; }
.acara-terkunci strong { min-width: 0; overflow-wrap: anywhere; }
.form-baris .bantuan { font-size: .8rem; color: var(--abu); margin-top: 6px; }

/* Label + kotak centang dalam satu baris. Dipakai untuk penanda yang
   keterangannya panjang, jadi teksnya boleh membungkus tanpa menarik
   kotaknya ke tengah baris. */
.pilih-satu { display: flex; align-items: flex-start; gap: 9px; cursor: pointer; margin-bottom: 0; }
.pilih-satu input[type=checkbox] { width: 18px; height: 18px; margin-top: 1px; flex: 0 0 auto; accent-color: var(--merah); }
.pilih-satu span { font-weight: 600; font-size: .92rem; line-height: 1.35; color: var(--biru-tua); }
.form-baris .galat { color: var(--merah); font-size: .82rem; margin-top: 6px; display: none; font-weight: 600; }
.form-baris.error input, .form-baris.error textarea { border-color: var(--merah); background: #fff6f7; }
.form-baris.error .galat { display: block; }

/* pilihan kehadiran */
.pilihan { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.pilihan label {
  border: 1.8px solid var(--garis); border-radius: 12px; padding: 16px; cursor: pointer;
  display: flex; align-items: center; gap: 12px; background: #fbfcff; margin: 0;
  transition: all .18s; font-weight: 600;
}
.pilihan label:hover { border-color: var(--biru-terang); background: #fff; }
.pilihan input { accent-color: var(--biru); width: 18px; height: 18px; }
.pilihan label.aktif-hadir { border-color: var(--hijau); background: #eefaf1; color: var(--hijau); }
.pilihan label.aktif-tidak { border-color: var(--merah); background: #fdeef0; color: var(--merah); }
.pilihan .tanda { font-size: 1.15rem; }

/* ---------- Kolom kata sandi ---------- */
.kotak-sandi { position: relative; }
.kotak-sandi input { padding-right: 46px !important; letter-spacing: .04em; }
.kotak-sandi .lihat {
  position: absolute; top: 50%; right: 6px; transform: translateY(-50%);
  width: 34px; height: 34px; border: 0; border-radius: 8px; cursor: pointer;
  background: transparent; color: var(--abu); font-size: 1rem; line-height: 1;
  display: grid; place-items: center; transition: background .16s, color .16s;
}
.kotak-sandi .lihat:hover { background: var(--biru-muda); color: var(--biru); }
.kotak-sandi .lihat:focus-visible { outline: 2px solid var(--biru-terang); outline-offset: 1px; }

/* ---------- Daftar syarat kata sandi ---------- */
.syarat { list-style: none; margin: 10px 0 0; padding: 0; display: grid; gap: 6px; }
.syarat li {
  display: flex; align-items: center; gap: 8px;
  font-size: .83rem; color: var(--abu); transition: color .18s;
}
.syarat li::before {
  content: '○'; width: 16px; text-align: center; font-size: .95rem;
  color: var(--garis); transition: content .18s, color .18s;
}
.syarat li.ok { color: var(--hijau); font-weight: 600; }
.syarat li.ok::before { content: '✓'; color: var(--hijau); }

/* ---------- Kartu form terpusat (masuk / ganti sandi) ---------- */
.kartu-form { max-width: 440px; }
.kartu-form .kepala-form { text-align: center; margin-bottom: 24px; }
.kartu-form .lambang {
  width: 54px; height: 54px; margin: 0 auto 14px; border-radius: 15px;
  display: grid; place-items: center; font-size: 1.5rem;
  background: var(--biru-muda); color: var(--biru);
}
.kartu-form .kepala-form h2 { margin: 0 0 6px; color: var(--biru-tua); font-size: 1.25rem; }
.kartu-form .kepala-form p { margin: 0; color: var(--abu); font-size: .9rem; }

.kotak-alasan { display: none; }
.kotak-alasan.tampil { display: block; animation: turun .25s ease; }
@keyframes turun { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }

.pesan { padding: 14px 16px; border-radius: 10px; font-size: .92rem; margin-bottom: 18px; display: none; }
.pesan.tampil { display: block; }
.pesan.galat { background: #fdeef0; color: var(--merah); border: 1px solid #f6c9d1; }
.pesan.info { background: var(--biru-muda); color: var(--biru); border: 1px solid #c9d5f3; }

/* catatan "data diisi otomatis" pada formulir RSVP */
.pesan.catatan-isi { display: none; }
.pesan.catatan-isi.tampil { display: block; }


/* sukses */
.sukses { text-align: center; padding: 40px 24px; }
.sukses .centang {
  width: 84px; height: 84px; border-radius: 50%; margin: 0 auto 20px;
  display: grid; place-items: center; font-size: 2.6rem; color: #fff;
  background: var(--hijau); box-shadow: 0 10px 26px rgba(27, 127, 59, .35);
}
.sukses.tidak .centang { background: var(--merah); box-shadow: 0 10px 26px rgba(200, 16, 46, .3); }
.sukses h2 { color: var(--biru-tua); margin: 0 0 8px; }
.sukses p { color: var(--abu); margin: 0 auto 22px; max-width: 460px; }
.ringkas-data {
  text-align: left; background: var(--latar); border: 1px solid var(--garis);
  border-radius: 12px; padding: 18px; max-width: 460px; margin: 0 auto 24px; font-size: .92rem;
}
.ringkas-data div { display: flex; justify-content: space-between; gap: 14px; padding: 6px 0; }
.ringkas-data div + div { border-top: 1px dashed var(--garis); }
.ringkas-data span:first-child { color: var(--abu); }
.ringkas-data span:last-child { font-weight: 600; text-align: right; }

/* ---------- Statistik ---------- */
.grid-stat { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.stat {
  background: #fff; border: 1px solid var(--garis); border-radius: 14px; padding: 18px 20px;
  box-shadow: var(--bayang);
}
.stat .label { font-size: .8rem; color: var(--abu); text-transform: uppercase; letter-spacing: .6px; font-weight: 700; }
.stat .angka { font-size: 2rem; font-weight: 800; color: var(--biru-tua); line-height: 1.2; }
.stat.hadir .angka { color: var(--hijau); }
.stat.tidak .angka { color: var(--merah); }

/* ---------- Tabel ---------- */
.tabel-bungkus { overflow-x: auto; border: 1px solid var(--garis); border-radius: 12px; background: #fff; }
table { border-collapse: collapse; width: 100%; font-size: .9rem; min-width: 860px; }
thead th {
  background: var(--biru); color: #fff; text-align: left; padding: 12px 14px;
  font-weight: 600; white-space: nowrap; position: sticky; top: 0;
}
tbody td { padding: 11px 14px; border-top: 1px solid var(--garis); vertical-align: top; }
tbody tr:nth-child(even) { background: #f8faff; }
.pil {
  display: inline-block; padding: 3px 11px; border-radius: 999px; font-size: .78rem; font-weight: 700;
  white-space: nowrap;
}
.pil.hadir { background: #e4f6ea; color: var(--hijau); }
.pil.tidak { background: #fdeaed; color: var(--merah); }
/* Penanda nomor luar negeri di tabel target WA Blast. Warna netral, bukan
   merah: ini keterangan, bukan peringatan — nomornya memang akan dikirimi. */
.pil.luar { background: #eef2fb; color: var(--biru); border: 1px solid #d3ddf3; }
.hapus {
  background: none; border: 1px solid var(--garis); color: var(--merah); border-radius: 8px;
  padding: 5px 10px; cursor: pointer; font-size: .8rem; font-weight: 600;
}
.hapus:hover { background: #fdeef0; border-color: var(--merah); }
.kosong { text-align: center; padding: 44px 20px; color: var(--abu); }

/* ---------- Popup formulir (modal) ---------- */
.tirai {
  position: fixed; inset: 0; z-index: 60;
  background: rgba(8, 20, 60, .62); backdrop-filter: blur(3px);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 28px 16px; overflow-y: auto;
  animation: pudar .2s ease;
}
.tirai[hidden] { display: none; }
@keyframes pudar { from { opacity: 0; } to { opacity: 1; } }

.modal {
  background: #fff; border-radius: var(--radius); width: min(620px, 100%);
  box-shadow: 0 28px 70px rgba(5, 15, 45, .45); overflow: hidden;
  margin: auto; animation: naik .26s cubic-bezier(.2, .8, .25, 1);
}
@keyframes naik { from { opacity: 0; transform: translateY(22px) scale(.985); } to { opacity: 1; transform: none; } }

.modal .kepala {
  background: linear-gradient(115deg, var(--biru-tua), var(--biru) 60%, var(--biru-terang));
  color: #fff; padding: 22px 26px; position: relative;
}
.modal .kepala .lencana-kecil {
  display: inline-block; font-size: .7rem; font-weight: 700; letter-spacing: 1.3px;
  text-transform: uppercase; background: rgba(255, 255, 255, .18);
  border: 1px solid rgba(255, 255, 255, .3); padding: 4px 12px; border-radius: 999px;
  margin-bottom: 10px;
}
.modal .kepala h2 { margin: 0; font-size: 1.24rem; line-height: 1.3; padding-right: 42px; }
.modal .kepala .meta { font-size: .84rem; opacity: .9; margin-top: 7px; }
.modal .tutup {
  position: absolute; top: 16px; right: 16px; width: 34px; height: 34px;
  border-radius: 50%; border: 1px solid rgba(255, 255, 255, .4);
  background: rgba(255, 255, 255, .14); color: #fff; font-size: 1.25rem; line-height: 1;
  cursor: pointer; display: grid; place-items: center; transition: background .18s;
}
.modal .tutup:hover { background: rgba(255, 255, 255, .3); }
.modal .badan { padding: 26px; }
.modal .badan .sukses { padding: 14px 0 6px; }

body.terkunci { overflow: hidden; }

/* ---------- Tab panel admin ---------- */
.tab {
  display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 26px;
  border-bottom: 2px solid var(--garis); padding-bottom: 0;
}
.tab-tbl {
  border: 0; background: none; font: inherit; font-weight: 600; font-size: .93rem;
  color: var(--abu); padding: 11px 18px; cursor: pointer; border-radius: 10px 10px 0 0;
  border-bottom: 3px solid transparent; margin-bottom: -2px; transition: color .18s, border-color .18s;
}
.tab-tbl:hover { color: var(--biru); background: #fff; }
.tab-tbl.aktif { color: var(--biru); border-bottom-color: var(--biru); background: #fff; }

/* ---------- Label peran ---------- */
.pil-peran {
  display: inline-block; padding: 3px 11px; border-radius: 999px;
  font-size: .76rem; font-weight: 700; white-space: nowrap;
  background: var(--biru-muda); color: var(--biru); border: 1px solid #c9d5f3;
}
.pil-peran.super { background: #fdeaed; color: var(--merah); border-color: #f6c9d1; }
.topbar .pil-peran { background: rgba(255, 255, 255, .18); color: #fff; border-color: rgba(255, 255, 255, .4); }
.pil-aksi {
  display: inline-block; padding: 2px 9px; border-radius: 6px; font-size: .76rem;
  font-weight: 700; background: var(--latar); color: var(--biru-tua); border: 1px solid var(--garis);
}

/* ---------- Tombol kecil di tabel ---------- */
.mini {
  background: #fff; border: 1px solid var(--garis); color: var(--biru); border-radius: 8px;
  padding: 5px 10px; cursor: pointer; font-size: .78rem; font-weight: 600; margin: 2px 2px 2px 0;
  transition: background .16s, border-color .16s;
}
.mini:hover { background: var(--biru-muda); border-color: var(--biru); }
.mini.merah { color: var(--merah); }
.mini.merah:hover { background: #fdeef0; border-color: var(--merah); }

/* ---------- Form berkolom ---------- */
.grid-form {
  display: grid; gap: 0 18px;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
}

/* ---------- Tabel rekap publik (DataTables) ---------- */
table.tabel-rekap { border-collapse: collapse; width: 100%; font-size: .9rem; }
table.tabel-rekap thead th {
  background: var(--biru); color: #fff; text-align: left; padding: 12px 14px;
  font-weight: 600; white-space: nowrap; border: 0;
}
table.tabel-rekap thead th:first-child { border-radius: 8px 0 0 0; }
table.tabel-rekap thead th:last-child { border-radius: 0 8px 0 0; }
table.tabel-rekap tbody td { padding: 11px 14px; border-top: 1px solid var(--garis); vertical-align: top; }
table.tabel-rekap tbody tr:nth-child(even) { background: #f8faff; }
table.tabel-rekap tbody tr:hover { background: var(--biru-muda); }

/* panah pengurutan DataTables agar terlihat di header biru */
table.dataTable thead th.sorting:after,
table.dataTable thead th.sorting_asc:after,
table.dataTable thead th.sorting_desc:after { color: #fff !important; opacity: .85; }
table.dataTable thead th { border-bottom: 0 !important; }

/* Daftar nama yang dibuka tombol "Lihat Nama" di papan /rekap-kehadiran.
   Sejak divisi digambar sebagai kartu (bukan tabel enam kolom), daftar ini
   blok di DALAM kartunya sendiri — dulu ia baris <tr> tambahan yang menumpang
   di bawah baris divisinya. Latar dan bingkainya karena itu dipasang di
   .kartu-divisi .daftar-nama, bukan di sel tabel. */
.daftar-nama { padding: 14px 18px 16px; }
.judul-nama {
  font-weight: 600; font-size: .86rem; color: var(--biru-tua);
  margin-bottom: 8px; text-transform: uppercase; letter-spacing: .3px;
}
/* min-width dinolkan: aturan umum `table { min-width: 860px }` dipakai tabel
   utama yang berkolom banyak, sedangkan tabel nama ini menumpang di dalamnya
   dan akan mendorong induknya bila ikut dipaksa selebar itu. */
.tabel-nama { width: 100%; min-width: 0; border-collapse: collapse; font-size: .86rem; }
.tabel-nama td { padding: 7px 10px; border-bottom: 1px solid #e6ecf7; }
.tabel-nama tr:last-child td { border-bottom: 0; }
.tabel-nama td.kosong { text-align: center; color: var(--abu); padding: 14px; }

/* Kotak "Akses acara" di formulir pengguna: daftar acara yang bisa dicentang,
   dibingkai supaya terbaca sebagai satu kelompok pilihan. */
.kotak-akses .daftar-acara {
  margin: 10px 0 0; padding: 12px 14px;
  background: var(--latar); border: 1px solid var(--garis); border-radius: 10px;
}
.kotak-akses .pilih-satu input[type=radio] { width: 18px; height: 18px; margin-top: 1px; accent-color: var(--merah); }

/* Panel pembungkus tabel-kartu. Paddingnya di sini, bukan di atribut style,
   supaya media query di bawah bisa merapatkannya di layar sempit. */
.panel-kartu { padding: 20px; }

/* ============================================================
   Tabel yang berubah jadi kartu di layar sempit  (.tabel-kartu)

   Di ponsel, tabel enam kolom hanya menyisakan ±40% baris yang terlihat;
   sisanya harus digeser ke samping — dan kolom yang paling dicari ("Sudah
   Masuk", "Jam Masuk") justru yang tersembunyi di kanan. Karena itu di bawah
   640px tiap baris ditampilkan sebagai kartu bertumpuk: <thead> disembunyikan
   dan tiap sel memunculkan nama kolomnya sendiri lewat data-label.

   Pendekatan CSS murni, bukan menggambar ulang markup lewat JavaScript:
   DataTables tetap memegang tabel yang sama, jadi pencarian, pengurutan, dan
   penomoran halaman tidak perlu ditulis dua kali untuk ponsel.
   ============================================================ */
@media (max-width: 640px) {
  table.tabel-kartu { min-width: 0; display: block; }
  table.tabel-kartu thead { display: none; }            /* judul kolom pindah ke tiap sel */
  table.tabel-kartu > tbody { display: block; }

  table.tabel-kartu > tbody > tr {
    display: block; background: #fff;
    border: 1px solid var(--garis); border-radius: 14px;
    padding: 12px 14px 14px; margin-bottom: 10px;
  }
  table.tabel-kartu > tbody > tr:last-child { margin-bottom: 0; }

  table.tabel-kartu > tbody > tr > td {
    display: flex; align-items: center; justify-content: space-between;
    gap: 14px; padding: 5px 0; border: 0; font-size: .92rem;
  }
  table.tabel-kartu > tbody > tr > td::before {
    content: attr(data-label);
    flex: 0 0 auto; font-weight: 600; font-size: .8rem;
    color: var(--abu); text-transform: uppercase; letter-spacing: .3px;
  }

  /* Nomor urut dibuang: pada daftar kartu ia hanya menambah baris tanpa arti. */
  table.tabel-kartu > tbody > tr > td.kolom-nomor { display: none; }

  /* Sel penanda kartu: nama divisi / nama peserta jadi kepala kartu. */
  table.tabel-kartu > tbody > tr > td.kolom-judul {
    display: block; font-weight: 700; font-size: 1.02rem; line-height: 1.3;
    color: var(--biru-tua); padding: 0 0 4px;
    border-bottom: 1px solid var(--garis); margin-bottom: 8px;
  }
  table.tabel-kartu > tbody > tr > td.kolom-judul::before { content: none; }

  /* Jabatan: keterangan di bawah nama, bukan baris berlabel tersendiri.
     Bila kosong, barisnya dihilangkan sama sekali — tanda "-" yang berdiri
     sendiri di bawah nama terbaca seperti kesalahan tampilan. */
  table.tabel-kartu > tbody > tr > td.kolom-sekunder {
    display: block; color: var(--abu); font-size: .86rem;
    padding: 0 0 6px; margin-top: -4px;
  }
  table.tabel-kartu > tbody > tr > td.kolom-sekunder::before { content: none; }
  table.tabel-kartu > tbody > tr > td.sekunder-kosong { display: none; }

  /* Baris "kosong" tetap satu blok utuh, bukan kartu berlabel. */
  table.tabel-kartu > tbody > tr > td.kosong { display: block; text-align: center; }
  table.tabel-kartu > tbody > tr > td.kosong::before { content: none; }

  /* Daftar nama di dalam kartu divisi: paddingnya dirapatkan di layar sempit. */
  .daftar-nama { padding: 12px 14px 14px; }
  /* Daftar nama di dalam kartu: satu orang satu blok — nama tebal, jabatan
     abu-abu di bawahnya, status di baris sendiri. Tabelnya tetap <table>
     supaya markup desktop dan ponsel sama. */
  table.tabel-nama, table.tabel-nama tbody,
  table.tabel-nama tbody tr, table.tabel-nama tbody td { display: block; width: 100%; }
  table.tabel-nama tbody tr { padding: 9px 0; border-bottom: 1px solid #e6ecf7; }
  table.tabel-nama tbody tr:first-child { padding-top: 0; }
  table.tabel-nama tbody tr:last-child { border-bottom: 0; padding-bottom: 0; }
  table.tabel-nama tbody td { padding: 1px 0; border: 0; }
  table.tabel-nama tbody td.kolom-nomor { display: none; }
  table.tabel-nama tbody td.kolom-judul { font-weight: 700; color: var(--biru-tua); }
  table.tabel-nama tbody td.kolom-sekunder { color: var(--abu); font-size: .84rem; }
  table.tabel-nama tbody td.sekunder-kosong { display: none; }
  table.tabel-nama tbody td .pil { margin-top: 3px; }
  .judul-nama { letter-spacing: 0; font-size: .82rem; }

  .panel-kartu { padding: 14px 12px; }

  /* Kendali DataTables: tinggi sentuh dinaikkan agar tidak meleset ditekan.
     Selektornya menyebut elemen (a/input/select) supaya lebih kuat daripada
     CSS bawaan DataTables, yang dimuat SESUDAH berkas ini. */
  .dataTables_wrapper .dataTables_filter input[type=search],
  .dataTables_wrapper .dataTables_length select { min-height: 44px; font-size: .95rem; }
  .dataTables_wrapper .dataTables_paginate a.paginate_button {
    min-height: 44px; min-width: 44px; padding: 11px 14px; margin: 4px 2px 0;
    display: inline-flex; align-items: center; justify-content: center;
  }
  .dataTables_wrapper .dataTables_info { padding-top: 10px; }

  /* Baris alat DataTables dirapatkan ke kiri. Selektornya menyebut `div`
     supaya menang atas CSS bawaan DataTables yang dimuat sesudah berkas ini
     dan menaruh kendali ini di tengah. */
  .dataTables_wrapper div.dataTables_length,
  .dataTables_wrapper div.dataTables_filter,
  .dataTables_wrapper div.dataTables_info { text-align: left; float: none; width: 100%; }
  .dataTables_wrapper div.dataTables_length { margin-bottom: 4px; }
  .dataTables_wrapper div.dataTables_paginate { text-align: center; float: none; width: 100%; }

  /* Kotak pencarian: label dibuat blok supaya lebar inputnya 100% dari wadah,
     bukan 100% + lebar teks "Cari:" yang membuatnya melewati tepi 3px. */
  .dataTables_wrapper .dataTables_filter label { display: block; }
  .dataTables_wrapper .dataTables_filter input[type=search] {
    display: block; width: 100%; margin: 6px 0 0;
  }

  /* Tautan di topbar ikut dinaikkan tingginya — di halaman ini ia satu-satunya
     jalan kembali, dan 35px terlalu tipis untuk jempol. Berlaku juga di
     halaman publik lain yang memakai topbar sama. */
  .topbar a.tautan { min-height: 44px; display: inline-flex; align-items: center; }
}

/* Tabel DataTables di dalam .panel diberi wadah yang bisa digulir sendiri.
   Aturan `table { min-width: 860px }` di atas membuat tabel lebar tetap
   terbaca, tapi tanpa wadah ini kelebihannya mendorong SELURUH halaman —
   terasa di layar 721–950px (tablet mendatar, laptop kecil), yang lolos dari
   media query ponsel di bawah. */
.panel > .dataTables_wrapper { overflow-x: auto; }

/* baris alat DataTables */
.dataTables_wrapper .dataTables_length,
.dataTables_wrapper .dataTables_filter,
.dataTables_wrapper .dataTables_info,
.dataTables_wrapper .dataTables_paginate,
.dataTables_wrapper .dataTables_saring { font-size: .86rem; color: var(--abu); }
.dataTables_wrapper .dataTables_filter { margin-bottom: 10px; }
.dataTables_wrapper .dataTables_filter input,
.dataTables_wrapper .dataTables_length select,
.dataTables_wrapper .dataTables_saring select {
  border: 1.5px solid var(--garis); border-radius: 8px; padding: 7px 10px;
  font: inherit; font-size: .88rem; background: #fbfcff; margin-left: 6px;
}
.dataTables_wrapper .dataTables_filter input:focus,
.dataTables_wrapper .dataTables_saring select:focus {
  outline: 0; border-color: var(--biru-terang); box-shadow: 0 0 0 3px rgba(42, 86, 214, .13);
}
.dataTables_wrapper .dataTables_saring { display: inline-block; margin-left: 18px; }
.dataTables_wrapper .dataTables_paginate .paginate_button {
  border-radius: 8px !important; border: 1px solid var(--garis) !important;
  padding: 6px 12px !important; margin: 0 2px !important; color: var(--biru) !important;
}
.dataTables_wrapper .dataTables_paginate .paginate_button.current {
  background: var(--biru) !important; border-color: var(--biru) !important; color: #fff !important;
}
.dataTables_wrapper .dataTables_paginate .paginate_button:hover {
  background: var(--biru-muda) !important; border-color: var(--biru) !important; color: var(--biru) !important;
}
.dataTables_wrapper .dataTables_paginate .paginate_button.disabled,
.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover {
  color: var(--garis) !important; background: transparent !important; border-color: var(--garis) !important;
}

@media (max-width: 640px) {
  .dataTables_wrapper .dataTables_saring { display: block; margin: 8px 0 0; }
  table.tabel-rekap { font-size: .84rem; }
}

/* ============================================================
   Panel admin di ponsel

   Diukur di layar 390px: sebelum blok ini, SETIAP tab bisa digulir ke samping
   12px karena blok kanan topbar (nama pengguna + tombol Keluar) tidak muat,
   dan barisan 9 tab menumpuk jadi empat baris yang memakan hampir sepertiga
   layar. Tabel yang lebar tetap digulir di dalam .tabel-bungkus — itu memang
   disengaja, karena memaksa 9 kolom masuk 390px membuat isinya tak terbaca.
   ============================================================ */
@media (max-width: 720px) {
  /* Topbar: biarkan turun baris daripada mendorong halaman melebar. */
  .topbar .wrap { flex-wrap: wrap; row-gap: 8px; }
  .topbar .kanan { flex-wrap: wrap; justify-content: flex-end; gap: 8px; }
  .topbar .pil-peran {
    max-width: 52vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .topbar a.tautan { padding: 6px 12px; }

  /* Tab: satu baris yang bisa digeser, bukan empat baris bertumpuk. */
  .tab {
    flex-wrap: nowrap; overflow-x: auto; margin-bottom: 20px;
    scroll-snap-type: x proximity; -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .tab::-webkit-scrollbar { display: none; }
  .tab-tbl { flex: 0 0 auto; scroll-snap-align: start; padding: 10px 14px; font-size: .88rem; }

  /* Dua kolom jadi satu di layar sempit. */
  .baris-kembar { grid-template-columns: 1fr; }

  /* Alat di atas tabel: tiap kendali selebar layar supaya tidak ada yang
     terpotong di tepi kanan. */
  .baris-alat { gap: 10px; }
  .baris-alat > * { flex: 1 1 100%; }
  .baris-alat select, .baris-alat input, .baris-alat .tbl { width: 100%; }
  .baris-alat input[type=search] { min-width: 0; }

  /* Statistik: dua kartu per baris masih terbaca di 390px. */
  .grid-stat { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px; }
  .stat { padding: 14px 15px; }
  .stat .angka { font-size: 1.6rem; }

  /* Tabel: lebar minimumnya diturunkan dan paddingnya dirapatkan, jadi
     gulirnya lebih pendek — bukan dihilangkan. */
  table { min-width: 620px; font-size: .84rem; }
  thead th { padding: 10px; }
  tbody td { padding: 9px 10px; }
  .tabel-bungkus { border-radius: 10px; }

  /* Tombol aksi per baris tabel boleh turun baris daripada melebarkan kolom. */
  tbody td .mini { margin: 2px 0; }

  /* Panel & judul dirapatkan agar isi utamanya lebih cepat terlihat. */
  section[data-panel] > h2 { font-size: 1.25rem; }
  section[data-panel] > h3 { font-size: 1.05rem; }
  .panel { padding: 20px 16px; }

  /* SCAN MASUK: kolom kode dan barisan printer/antrean menumpuk rapi. */
  .kotak-scan { padding: 18px 16px; }
  .baris-printer { align-items: flex-start; }
  .baris-printer button { margin-left: 0; width: 100%; }
  .kartu-hasil { flex-direction: column; text-align: center; gap: 10px; }

  /* Master Cetak & Generate QR: editor dan pratinjau sudah bertumpuk di
     bawah 900px; di sini tinggal merapatkan isinya. */
  .atur-qr textarea { font-size: .82rem; }
  .pratinjau-qr { padding: 12px; }
  .struk { padding: 12px 8px; }
  .wa-contoh { font-size: .84rem; padding: 12px; }

  /* Kendali DataTables (cari / jumlah baris / halaman) menumpuk. */
  .dataTables_wrapper .dataTables_length,
  .dataTables_wrapper .dataTables_filter,
  .dataTables_wrapper .dataTables_info,
  .dataTables_wrapper .dataTables_paginate { float: none; text-align: left; width: 100%; }
  .dataTables_wrapper .dataTables_filter input { width: 100%; margin-left: 0; }
  /* Penyaring acara yang disuntikkan DataTables di /rekap-rsvp. */
  .dataTables_wrapper .saring-acara { width: 100%; margin: 8px 0 0; }

  /* Tabel yang dibungkus DataTables tidak berada di dalam .tabel-bungkus,
     jadi wrapper-nya sendiri yang digulir — tanpa ini /rekap-rsvp melebar
     sampai 745px di layar 390px. */
  .dataTables_wrapper .dataTables_scrollBody,
  .panel > .dataTables_wrapper { overflow-x: auto; }
}

/* ---------- Footer ---------- */
.footer {
  background: var(--biru-tua); color: rgba(255, 255, 255, .8); text-align: center;
  padding: 30px 0; margin-top: 56px; font-size: .86rem;
}
.footer img { height: 44px; margin-bottom: 10px; }
.footer .garis-warna { height: 4px; background: linear-gradient(90deg, var(--merah) 0 33%, #fff 33% 66%, var(--biru-terang) 66%); }

/* ---------- Utilitas ---------- */
.tengah { text-align: center; }
.baris-alat { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; margin-bottom: 22px; }
.baris-alat select, .baris-alat input {
  padding: 10px 14px; border: 1.5px solid var(--garis); border-radius: 10px; font: inherit; background: #fff;
}
.baris-alat .dorong { margin-left: auto; }
.sembunyi { display: none !important; }

@media (max-width: 640px) {
  .pilihan { grid-template-columns: 1fr; }
  .hero { padding: 36px 0 76px; }
  .topbar .sub { display: none; }
  .panel { padding: 22px 18px; }
  .baris-alat .dorong { margin-left: 0; width: 100%; }
  .tirai { padding: 0; }
  .modal { border-radius: 0; min-height: 100%; }
  .modal .kepala { padding: 18px 20px; }
  .modal .badan { padding: 20px 18px 28px; }
}

/* ---------- Tab Generate QR ---------- */
.kotak-qr { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 380px); gap: 28px; align-items: start; }
.atur-qr .form-baris { margin-bottom: 16px; }
.atur-qr textarea { min-height: 64px; }
.baris-kembar { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }

.pratinjau-qr {
  background: var(--biru-muda); border: 1px solid var(--garis);
  border-radius: 14px; padding: 16px; position: sticky; top: 16px;
}
.pratinjau-qr .label-pratinjau {
  font-size: .78rem; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--biru); margin-bottom: 10px;
}
/* Kartu selalu digambar di atas kertas putih, jadi kanvasnya diberi bayangan
   agar tepinya tetap terlihat di dalam kotak pratinjau. */
.pratinjau-qr canvas {
  display: block; width: 100%; height: auto; background: #fff;
  border-radius: 6px; box-shadow: 0 6px 18px rgba(10, 28, 86, .16);
}
.pratinjau-qr .bantuan { font-size: .8rem; color: var(--abu); margin-top: 10px; text-align: center; }

.kabar-qr { font-size: .85rem; color: var(--abu); margin: 10px 0 0; min-height: 1.2em; }
.kabar-qr.ok { color: var(--hijau); font-weight: 600; }
.kabar-qr.salah { color: var(--merah); font-weight: 600; }

@media (max-width: 900px) {
  .kotak-qr { grid-template-columns: 1fr; }
  .pratinjau-qr { position: static; max-width: 420px; }
}

/* ---------- Tab SCAN MASUK ---------- */
.kotak-scan { padding: 22px 24px; }
/* Rata atas: keterangan di bawah kolom kode bisa dua baris, kalau rata bawah
   pemilih acara ikut terdorong turun dan barisnya terlihat miring. */
.baris-scan { display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-start; }
.baris-scan .form-baris .bantuan { margin-top: 6px; }
/* Lebar kolom ditulis di CSS, bukan inline: saat barisnya ditumpuk di
   ponsel, flex-basis berubah arti menjadi TINGGI — sempat menghasilkan
   celah kosong 320px di antara pemilih acara dan kolom kode. */
.baris-scan .form-baris { margin: 0; }
.baris-scan .kolom-acara { flex: 1 1 320px; }
.baris-scan .kolom-kode { flex: 2 1 460px; }
#scanKode { font-family: ui-monospace, Consolas, monospace; font-size: 1.15rem; letter-spacing: .06em; text-transform: uppercase; }

/* Baris status printer WebUSB di atas kartu hasil pemindaian. */
.baris-printer {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-top: 18px; padding-top: 14px; border-top: 1px dashed var(--garis);
  font-size: .88rem; color: var(--abu);
}
.baris-printer .lampu {
  width: 9px; height: 9px; flex: 0 0 9px; border-radius: 50%;
  background: #cbd5e1; box-shadow: 0 0 0 3px rgba(203, 213, 225, .35);
}
.baris-printer.siap .lampu { background: var(--hijau); box-shadow: 0 0 0 3px rgba(22, 163, 74, .18); }
.baris-printer.gagal .lampu { background: var(--merah); box-shadow: 0 0 0 3px rgba(200, 16, 46, .18); }
.baris-printer.siap { color: #14532d; }
.baris-printer button { margin-left: auto; }
.baris-printer .lampu.biru { background: var(--biru); box-shadow: 0 0 0 3px rgba(18, 48, 143, .15); }

/* Formulir tamu tanpa RSVP di tab SCAN MASUK. Dibedakan latarnya supaya jelas
   ini jalur terpisah dari pemindaian QR di atasnya — bukan kolom tambahan
   dari formulir yang sama. */
.kotak-tambah {
  margin-top: 12px; padding: 16px; border-radius: 12px;
  background: var(--biru-muda); border: 1px solid #d3ddf3;
}
.kotak-tambah .form-baris { margin-bottom: 12px; }

/* Baris antrean offline: kuning agar terbaca sebagai "perlu diperhatikan",
   bukan galat dan bukan sudah beres. */
.baris-antrean { color: #6b4c00; border-top-color: #e2c86a; }
.baris-antrean .lampu { background: #b8860b; box-shadow: 0 0 0 3px rgba(184, 134, 11, .2); }
.pil.tunggu { background: #fff8e6; color: #6b4c00; border: 1px solid #e2c86a; }

/* Penanda "sedang diproses": pemutar kecil yang mewarisi warna teks induknya,
   jadi bisa dipakai di kartu hasil maupun di dalam tombol baris tabel. */
@keyframes putarTerus { to { transform: rotate(360deg); } }
.putar {
  display: inline-block; width: 11px; height: 11px; vertical-align: -1px;
  border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%;
  animation: putarTerus .6s linear infinite;
}
.kartu-hasil .tanda .putar { width: 20px; height: 20px; border-width: 3px; vertical-align: 0; }
.mini[disabled] { opacity: .65; cursor: progress; }
#scanKode:disabled { background: #f1f5f9; cursor: progress; }

.hasil-scan:empty { display: none; }
.kartu-hasil {
  display: flex; align-items: center; gap: 16px;
  margin-top: 20px; padding: 16px 18px; border-radius: 12px; border: 1.5px solid;
  animation: naikHasil .22s ease;
}
@keyframes naikHasil { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
.kartu-hasil .tanda {
  width: 46px; height: 46px; flex: 0 0 46px; border-radius: 50%;
  display: grid; place-items: center; font-size: 1.5rem; font-weight: 700; color: #fff;
}
.kartu-hasil .judul { font-weight: 700; font-size: 1.05rem; }
.kartu-hasil .rincian { font-size: .88rem; margin-top: 4px; opacity: .9; }
.kartu-hasil.ok    { background: #eefaf1; border-color: var(--hijau); color: #14532d; }
.kartu-hasil.ok    .tanda { background: var(--hijau); }
.kartu-hasil.ulang { background: #fff8e6; border-color: #b8860b; color: #6b4c00; }
.kartu-hasil.ulang .tanda { background: #b8860b; }
.kartu-hasil.gagal { background: #fdeef0; border-color: var(--merah); color: #7a0a1c; }
.kartu-hasil.gagal .tanda { background: var(--merah); }
.kartu-hasil.proses { background: var(--biru-muda); border-color: var(--biru); color: var(--biru-tua); }
.kartu-hasil.proses .tanda { background: var(--biru); }

.baris-scan kbd {
  font: inherit; font-size: .78rem; font-weight: 600;
  background: #fff; border: 1px solid var(--garis); border-bottom-width: 2px;
  border-radius: 5px; padding: 1px 6px; color: var(--biru-tua);
}

@media (max-width: 640px) {
  .baris-scan { flex-direction: column; align-items: stretch; }
  /* Arah kolom membuat flex-basis jadi tinggi — dinolkan di sini. */
  .baris-scan .kolom-acara, .baris-scan .kolom-kode { flex: 0 0 auto; }
}

/* ---------- Tab WA Blast ---------- */
.wa-bar { height: 10px; border-radius: 999px; background: var(--garis); overflow: hidden; }
.wa-bar span {
  display: block; height: 100%; width: 0; border-radius: 999px;
  background: linear-gradient(90deg, var(--biru), var(--biru-terang));
  transition: width .4s ease;
}
/* Pesan WhatsApp mengandalkan baris baru, jadi contohnya ditampilkan apa
   adanya — bukan dirapikan ulang oleh HTML. */
.wa-contoh {
  margin: 0; padding: 14px 16px; border-radius: 10px;
  background: var(--biru-muda); border: 1px solid var(--garis);
  white-space: pre-wrap; word-break: break-word;
  font-family: inherit; font-size: .9rem; line-height: 1.55; color: var(--biru-tua);
}


/* Kotak berkas Excel: dibuat setinggi tombol di sebelahnya supaya barisan
   "pilih berkas - impor - contoh" tetap sejajar di layar lebar. */
.wac-berkas {
  padding: 9px 12px; border: 1px dashed var(--garis); border-radius: 10px;
  background: #fff; font-size: .9rem; max-width: 100%;
}

/* ---------- Tab Master Cetak: pratinjau struk ---------- */
/* Pratinjau memakai huruf monospace dan latar kertas putih supaya proporsi
   barisnya mendekati struk sungguhan; bentuk hurufnya tentu tetap berbeda
   dari font printer. */
.struk {
  background: #fff; border: 1px solid var(--garis); border-radius: 6px;
  padding: 14px 10px; font-family: ui-monospace, Consolas, monospace;
  color: #111; overflow-x: auto;
}
.struk-baris { white-space: pre; line-height: 1.25; }
/* Ukuran huruf per baris dipasang inline oleh master-cetak.js, karena
   pengalinya kini 1x–8x dan lebar/tinggi bisa berbeda. */
.struk-baris.tebal { font-weight: 700; }
.struk-baris.garis { overflow: hidden; }
.struk-baris.kiri { text-align: left; }
.struk-baris.tengah { text-align: center; }
.struk-baris.kanan { text-align: right; }
.struk-kosong { height: .7rem; }
.struk-logo, .struk-qr {
  text-align: center; font-size: .75rem; color: var(--biru);
  background: var(--biru-muda); border-radius: 4px; padding: 6px 0; margin: 4px 0;
}
.struk-potong {
  border-top: 2px dashed #9ca3af; margin-top: 10px; padding-top: 6px;
  text-align: center; font-size: .68rem; color: var(--abu);
}

/* ---------- Halaman /rekap: papan angka kehadiran ---------- */
.kartu-total { text-align: center; padding: 30px 24px; }
.kartu-total .label-total {
  font-size: .82rem; font-weight: 700; letter-spacing: 1.2px;
  text-transform: uppercase; color: var(--abu);
}
.kartu-total .angka-total {
  font-size: clamp(3rem, 14vw, 5.5rem); font-weight: 800; line-height: 1.05;
  color: var(--biru-tua); font-variant-numeric: tabular-nums;
}
/* Dua angka penyusunnya selalu dipampang berdampingan: bila totalnya
   berselisih dengan hitungan manual panitia, sumbernya langsung kelihatan. */
.kartu-total .pecahan-total {
  display: grid; grid-template-columns: 1fr 1fr; gap: 14px;
  margin-top: 18px; padding-top: 18px; border-top: 1px dashed var(--garis);
}
.kartu-total .pecahan-total .label { display: block; font-size: .8rem; color: var(--abu); }
.kartu-total .pecahan-total strong {
  font-size: 1.6rem; color: var(--biru); font-variant-numeric: tabular-nums;
}

.grid-warna { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); }
.kartu-warna {
  display: flex; align-items: center; gap: 14px;
  background: #fff; border: 1px solid var(--garis); border-radius: 14px;
  padding: 16px 18px; box-shadow: var(--bayang);
}
.kartu-warna .titik {
  width: 34px; height: 34px; flex: 0 0 34px; border-radius: 50%;
  border: 2px solid rgba(0, 0, 0, .12);
}
.kartu-warna .nama { font-size: .85rem; color: var(--abu); font-weight: 600; }
.kartu-warna .jumlah {
  font-size: 1.7rem; font-weight: 800; color: var(--biru-tua); line-height: 1.1;
  font-variant-numeric: tabular-nums;
}

@media (max-width: 720px) {
  .kartu-total { padding: 22px 16px; }
  .kartu-total .pecahan-total { grid-template-columns: 1fr; gap: 10px; }
  .grid-warna { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 10px; }
}

/* ---------- Tab Seating: kotak nomor kursi ---------- */
/* Bentuk kolom formulir proyek ini, dipendekkan agar sebaris dengan isi tabel.
   Kotak yang nomornya dipakai peserta lain diberi bingkai kuning — bukan
   merah: nomor kembar itu SAH kalau penomorannya per meja, jadi ia peringatan,
   bukan galat. */
.kursi {
  width: 100%; min-height: 40px; padding: 7px 10px;
  border: 1.5px solid var(--garis); border-radius: 10px; background: #fbfcff;
  font: inherit; font-weight: 700; color: var(--biru-tua);
  transition: border-color .18s, box-shadow .18s, background .18s;
}
.kursi::placeholder { color: #b6c0d6; font-weight: 600; }
.kursi:focus {
  outline: 0; background: #fff; border-color: var(--biru-terang);
  box-shadow: 0 0 0 4px rgba(42, 86, 214, .13);
}
.kursi.kembar { border-color: #e2c86a; background: #fff8e6; }
/* "menyimpan…" di kolom Status: teks biasa, bukan pil. Pil di kolom itu berarti
   KEADAAN yang bertahan (terisi / nomor kembar / gagal); proses yang sekejap
   tidak boleh terbaca sebagai salah satunya. */
.kursi-menyimpan { color: var(--abu); font-size: .78rem; font-weight: 600; }

/* Kepala kelompok divisi: satu baris melintang penuh, bukan sel biasa. */
.baris-grup > td {
  background: var(--biru-muda); color: var(--biru-tua);
  font-weight: 800; font-size: .84rem; letter-spacing: .4px; text-transform: uppercase;
  border-top: 1px solid #c9d5f4;
}
.baris-grup .jumlah { font-weight: 600; text-transform: none; letter-spacing: 0; color: var(--abu); }
.keadaan-kursi { white-space: nowrap; }

@media (max-width: 640px) {
  /* Di mode kartu, kepala kelompok tetap satu blok penuh — bukan kartu
     berlabel seperti baris peserta. */
  table.tabel-kartu > tbody > tr.baris-grup { padding: 0; border: 0; background: transparent; }
  table.tabel-kartu > tbody > tr.baris-grup > td { display: block; padding: 10px 14px; border-radius: 10px; }
  table.tabel-kartu > tbody > tr.baris-grup > td::before { content: none; }
}

/* ============================================================
   Papan kehadiran terbuka  /rekap-kehadiran

   Papan ini dipajang di layar besar selama acara berjalan dan menyegarkan
   dirinya tiap 15 detik, jadi yang diutamakan bukan kerapatan informasi
   melainkan keterbacaan dari jauh. Tiga keputusan yang membentuk seluruh blok
   ini:

   1. SATU ANGKA DOMINAN. Sebelumnya empat kartu statistik sebesar yang sama,
      dan mata harus memilih dulu mana yang dicari. Sekarang "Sudah Masuk"
      dibuat 2-3x lebih besar dari tiga pendampingnya.
   2. PERBANDINGAN DIGAMBAR, BUKAN DIHITUNG. Rasio hadir/terdaftar dan
      perbandingan antar divisi/warna dipajang sebagai bar. Bilangan tetap ada
      di sebelahnya untuk yang butuh nilai pastinya, tapi bentuk bar itulah
      yang terbaca dalam satu pandang dari 10 meter.
   3. KEADAAN "MASIH HIDUP" HARUS KELIHATAN. Papan yang tidak disentuh siapa
      pun tidak punya cara lain memberi tahu bahwa angkanya membeku, jadi
      penanda kesegaran di kop dibuat memeriksa keadaan sungguhan — bukan
      menjanjikan "disegarkan tiap 15 detik" di kaki halaman.
   ============================================================ */

/* ---------- Kop: identitas acara + pemilih + penanda live ---------- */
/* Menggantikan hero 54/92px + satu panel penuh untuk pemilih acara. Keduanya
   memakan ±280px sebelum satu angka pun terlihat — di layar 1080p itu
   sepertiga tinggi papan yang isinya tidak pernah berubah. */
.papan-kop {
  position: relative; overflow: hidden; color: #fff;
  background: linear-gradient(115deg, var(--biru-tua), var(--biru) 45%, var(--biru-terang));
  padding: 22px 0 26px;
}
.papan-kop .bg {
  position: absolute; inset: 0;
  background-image: url('/assets/img/banner.jpg');
  background-size: cover; background-position: center;
  opacity: .2;
}
/* Garis tiga warna yang dulu dibawa hero, dipertahankan dalam 4px: tanpa itu
   band ini kehilangan penanda bahwa papannya milik partai. */
.papan-kop::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 4px;
  background: linear-gradient(90deg, var(--merah) 0 33%, #fff 33% 66%, var(--biru-terang) 66%);
}
.papan-kop .wrap { position: relative; z-index: 2; }
.papan-kop-isi {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 18px; flex-wrap: wrap;
}
.papan-identitas { min-width: 0; }
.papan-kop .lencana {
  display: inline-flex; align-items: center; gap: 9px;
  background: rgba(255, 255, 255, .16); border: 1px solid rgba(255, 255, 255, .32);
  padding: 5px 14px; border-radius: 999px; font-size: .72rem; font-weight: 700;
  letter-spacing: 1.4px; text-transform: uppercase;
}
/* Nama ACARA yang jadi h1 — bukan "Rekap Kehadiran", yang sudah disebut
   lencana di atasnya dan topbar di atasnya lagi. Yang perlu dipastikan
   penonton adalah papan ini milik acara mana. */
.papan-kop h1 {
  margin: 10px 0 4px; font-size: clamp(1.35rem, 2.8vw, 2.1rem);
  font-weight: 800; letter-spacing: -.4px; line-height: 1.18;
  text-shadow: 0 3px 18px rgba(0, 0, 0, .3);
}
.papan-meta { margin: 0; font-size: .92rem; opacity: .9; }

.papan-kendali { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

/* Penanda kesegaran + keterangan jedanya, bertumpuk dan dirapatkan ke kanan:
   yang berubah tiap detik di atas, yang tetap di bawahnya. Digabung jadi satu
   baris teks akan membuat pil itu melebar dua kali dan berbunyi aneh saat
   keadaannya galat ("Gagal memperbarui - diperbarui tiap 60 detik"). */
.papan-kesegaran { display: flex; flex-direction: column; align-items: flex-end; gap: 5px; }
.papan-jeda { font-size: .72rem; opacity: .78; white-space: nowrap; }
.papan-kendali select {
  min-height: 44px; min-width: 250px; max-width: 100%; padding: 8px 12px;
  border: 1px solid rgba(255, 255, 255, .5); border-radius: 10px;
  background: #fff; color: var(--biru-tua);
  font: inherit; font-weight: 700; font-size: .92rem; cursor: pointer;
}
.papan-kendali select:focus-visible { outline: 3px solid #fff; outline-offset: 2px; }

/* Titiknya hijau selama pembaruan terakhir masih segar, menguning bila lewat
   45 detik (3x jeda penyegaran) tidak ada yang berhasil, memerah bila
   permintaan terakhir gagal. Warna SAJA tidak dipakai sebagai pembeda — teks
   di sebelahnya ikut berubah, karena papan ini juga dilihat orang yang tidak
   membedakan hijau dari kuning. */
.papan-hidup {
  display: inline-flex; align-items: center; gap: 8px; padding: 8px 14px;
  border-radius: 999px; background: rgba(255, 255, 255, .14);
  border: 1px solid rgba(255, 255, 255, .3);
  font-size: .82rem; font-weight: 600; white-space: nowrap;
}
.papan-hidup .titik {
  width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto;
  background: #6ee7a0; box-shadow: 0 0 0 4px rgba(110, 231, 160, .25);
}
.papan-hidup.basi .titik { background: #ffc857; box-shadow: 0 0 0 4px rgba(255, 200, 87, .25); }
.papan-hidup.galat { background: rgba(200, 16, 46, .32); border-color: rgba(255, 255, 255, .45); }
.papan-hidup.galat .titik { background: #ff8ba0; box-shadow: 0 0 0 4px rgba(255, 139, 160, .22); }

/* ---------- Badan papan ---------- */
.papan-isi { padding: 26px 0 44px; }

/* Galat jaringan/server. Sebelumnya menumpang di baris keterangan paling
   bawah — pesan yang muncul 1.500px di bawah angka yang sedang salah tidak
   pernah terbaca. */
.papan-galat {
  margin: 0 0 18px; padding: 13px 16px; border-radius: 12px;
  background: #fdeaed; border: 1px solid #f3c3cd; color: #8d1023;
  font-weight: 600; font-size: .9rem;
}
.papan-kosong {
  margin: 0; padding: 40px 24px; text-align: center; color: var(--abu);
  background: #fff; border: 1px dashed var(--garis); border-radius: var(--radius);
}
.papan-kosong.rapat { padding: 22px; border: 0; background: transparent; }

/* ---------- Angka utama ---------- */
.papan-angka { display: grid; gap: 16px; align-items: stretch; }
.angka-utama {
  background: linear-gradient(135deg, var(--biru-tua), var(--biru) 70%, var(--biru-terang));
  color: #fff; border-radius: var(--radius); padding: 22px 26px 24px;
  box-shadow: var(--bayang-kuat);
  display: flex; flex-direction: column; justify-content: center;
}
.angka-utama .label {
  font-size: .82rem; font-weight: 700; letter-spacing: 1.2px;
  text-transform: uppercase; opacity: .85;
}
/* tabular-nums: angka yang berubah tiap 15 detik tidak boleh menggeser
   lebarnya sendiri — "1.111" dan "1.999" harus sama panjang. */
.angka-utama .nilai {
  font-size: clamp(2.8rem, 6.5vw, 4.6rem); font-weight: 800; line-height: 1.05;
  letter-spacing: -2px; font-variant-numeric: tabular-nums; margin: 2px 0 14px;
}
.angka-utama .kaki { margin: 12px 0 0; font-size: .9rem; opacity: .92; }

/* Bar progres dipakai tiga tempat (angka utama, tiap divisi), jadi warna
   dasarnya untuk latar putih; di dalam kartu biru ia dibalik di bawah. */
.bar-progres { height: 10px; border-radius: 999px; background: var(--biru-muda); overflow: hidden; }
/* min-width: nilai 0% tetap digambar setipis 3px — bar yang benar-benar hilang
   terbaca seperti baris yang gagal digambar, bukan seperti angka nol. */
.bar-progres > span {
  display: block; height: 100%; min-width: 3px; border-radius: 999px;
  background: var(--biru-terang); transition: width .6s cubic-bezier(.22, .61, .36, 1);
}
.bar-progres.kecil { height: 8px; }
.angka-utama .bar-progres { height: 12px; background: rgba(255, 255, 255, .22); }
.angka-utama .bar-progres > span { background: #fff; }

/* ---------- Kop tiap bagian ---------- */
.papan-bagian { margin-top: 34px; }
.kop-bagian { margin-bottom: 14px; }
.kop-bagian h2 {
  margin: 0 0 2px; font-size: clamp(1.05rem, 2vw, 1.3rem);
  color: var(--biru-tua); letter-spacing: -.2px;
}
.kop-bagian p { margin: 0; color: var(--abu); font-size: .88rem; }
.papan-panel { padding: 18px 20px; }

/* ---------- Presensi warna lanyard ---------- */
/* Baris, bukan kartu berdampingan: lima warna sebagai kartu membuat angkanya
   tersebar di lima titik yang harus dibaca satu-satu, sedangkan sebagai baris
   berbar semuanya berbagi satu sumbu — dan "warna mana yang paling banyak"
   terjawab tanpa membaca satu bilangan pun. */
.daftar-warna { display: grid; gap: 2px; }
.baris-warna {
  display: grid; gap: 14px; align-items: center;
  grid-template-columns: 16px minmax(120px, 1.1fr) minmax(0, 2fr) auto;
  padding: 11px 4px; border-bottom: 1px solid var(--garis);
}
.baris-warna:last-child { border-bottom: 0; }
.baris-warna .chip {
  width: 16px; height: 16px; border-radius: 5px; background: var(--warna);
  border: 1px solid rgba(0, 0, 0, .16);
}
.baris-warna .nama {
  font-weight: 800; color: var(--biru-tua); font-size: .88rem;
  letter-spacing: .4px; line-height: 1.3;
}
.baris-warna .nama .ket {
  display: block; font-weight: 600; font-size: .74rem;
  color: var(--abu); letter-spacing: 0;
}
.baris-warna .bar { height: 10px; border-radius: 999px; background: var(--latar); overflow: hidden; }
/* Isian bar memakai warna talinya sendiri: itu satu-satunya tempat di papan
   ini yang boleh memakai warna sebagai data, karena namanya tetap tertulis. */
.baris-warna .bar > span {
  display: block; height: 100%; min-width: 3px; border-radius: 999px;
  background: var(--warna); transition: width .6s cubic-bezier(.22, .61, .36, 1);
}
.baris-warna .angka {
  font-size: 1.5rem; font-weight: 800; color: var(--biru-tua);
  font-variant-numeric: tabular-nums; min-width: 3.2ch; text-align: right;
}

.kaki-lanyard {
  display: flex; flex-wrap: wrap; gap: 10px;
  margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--garis);
}
.kaki-lanyard .butir {
  flex: 1 1 220px; background: var(--latar); border: 1px solid var(--garis);
  border-radius: 12px; padding: 12px 16px;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
.kaki-lanyard .butir.sorot { background: var(--biru-muda); border-color: #c9d5f4; }
.kaki-lanyard .label {
  font-size: .78rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .6px; color: var(--abu);
}
.kaki-lanyard .label em {
  display: block; font-style: normal; font-weight: 600;
  text-transform: none; letter-spacing: 0; font-size: .74rem;
}
.kaki-lanyard strong {
  font-size: 1.5rem; font-weight: 800; color: var(--biru-tua);
  font-variant-numeric: tabular-nums;
}

/* ---------- Kehadiran per divisi ---------- */
/* Dulu tabel enam kolom (No, Divisi, Peserta, Sudah Masuk, Belum Masuk,
   Nama). Empat angka berjajar memaksa pembaca membandingkan bilangan satu per
   satu, padahal yang dicari selalu "divisi mana yang paling banyak belum
   masuk" — pertanyaan bentuk, bukan pertanyaan angka. */
.daftar-divisi { display: grid; gap: 12px; }
.kartu-divisi {
  background: #fff; border: 1px solid var(--garis); border-radius: 14px;
  padding: 14px 16px;
}
.kartu-divisi.terbuka { border-color: #c9d5f4; background: #fbfcff; }
.kartu-divisi .kepala {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 14px; margin-bottom: 9px;
}
.kartu-divisi h3 { margin: 0; font-size: 1rem; color: var(--biru-tua); line-height: 1.3; }
.kartu-divisi .rasio { display: flex; align-items: baseline; gap: 7px; white-space: nowrap; }
.kartu-divisi .rasio strong {
  font-size: 1.35rem; font-weight: 800; color: var(--biru-tua);
  font-variant-numeric: tabular-nums;
}
.kartu-divisi .rasio .dari { color: var(--abu); font-weight: 700; }
.kartu-divisi .kaki {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-top: 10px; flex-wrap: wrap;
}
.kartu-divisi .angka-kaki { display: flex; gap: 6px; flex-wrap: wrap; }
.kartu-divisi .mini { min-height: 38px; margin: 0; }
/* Daftar nama sebagai laci di dalam kartunya sendiri — dulu ia baris <tr>
   tambahan di bawah baris divisinya, dan latarnya dipasang di sel tabel. */
.kartu-divisi .daftar-nama {
  margin-top: 12px; background: var(--latar);
  border: 1px solid var(--garis); border-radius: 12px;
}

/* ---------- Papan di layar sempit ---------- */
@media (max-width: 640px) {
  .papan-kop { padding: 18px 0 22px; }
  .papan-kop-isi { align-items: stretch; }
  .papan-kendali { width: 100%; }
  /* Kedua kendali selebar layar: pemilih acara ditekan dengan jempol, dan
     penanda live yang menggantung setengah lebar terbaca seperti tombol. */
  .papan-kendali select { flex: 1 1 100%; min-width: 0; }
  .papan-kesegaran { flex: 1 1 100%; align-items: stretch; text-align: center; }
  .papan-hidup { justify-content: center; }

  .papan-isi { padding: 20px 0 34px; }
  .angka-utama { padding: 18px 18px 20px; }
  .papan-bagian { margin-top: 26px; }
  .papan-panel { padding: 14px 12px; }

  /* Baris warna jadi dua baris: chip + nama + angka di atas, bar melintang di
     bawahnya. Bar dan angka yang dipaksa satu baris di 360px menyisakan ±60px
     untuk barnya — terlalu pendek untuk bisa dibandingkan. */
  .baris-warna { grid-template-columns: 16px minmax(0, 1fr) auto; row-gap: 9px; }
  .baris-warna .chip { grid-column: 1; grid-row: 1; }
  .baris-warna .nama { grid-column: 2; grid-row: 1; }
  .baris-warna .angka { grid-column: 3; grid-row: 1; font-size: 1.3rem; }
  .baris-warna .bar { grid-column: 2 / -1; grid-row: 2; }

  .kaki-lanyard .butir { padding: 11px 14px; }
  .kartu-divisi .kepala { gap: 10px; }
  /* Sasaran tap 44px sesuai pedoman sentuh, selebar kartunya. */
  .kartu-divisi .kaki { flex-direction: column; align-items: stretch; }
  .kartu-divisi .mini { width: 100%; min-height: 44px; font-size: .92rem; }
}

/* Bar yang melompat ke lebar barunya tiap 15 detik adalah gerakan yang tidak
   diminta siapa pun; yang menyetel preferensi ini mendapat papan yang diam. */
@media (prefers-reduced-motion: reduce) {
  .bar-progres > span, .baris-warna .bar > span { transition: none; }
}

/* Keterangan kecil di dalam label "Total Hadir" pada baris statistik tab
   Presensi Warna. Diperlukan sejak kartu "Check-in RSVP" dibuang: tanpa
   penyebutan itu, Total Hadir yang lebih besar dari Lanyard jadi angka yang
   tidak bisa dijelaskan dari apa pun yang terlihat di layar.

   Diselektor lewat #tapTotal, bukan `.stat .label`, supaya tidak menyentuh
   baris statistik di tab lain yang memakai komponen yang sama. Text-transform
   dan letter-spacing induknya dinolkan — label induknya huruf besar berjarak,
   dan keterangan dalam tanda kurung tidak boleh ikut begitu. */
#tapTotal .label .ket-total {
  display: block; margin-top: 2px;
  font-size: .66rem; font-weight: 600; color: var(--abu);
  text-transform: none; letter-spacing: 0;
}

/* ---------- Tab Presensi Warna: tombol tap ---------- */
/* Kartu dibuat besar dan tinggi minimalnya dipatok: petugas menekan ini
   ratusan kali sambil berdiri, sering di ponsel, jadi sasaran tapnya harus
   lebar — bukan tombol kecil yang mudah salah tekan. */
.grid-tap { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
.kartu-tap {
  background: #fff; border: 2px solid var(--warna, var(--garis)); border-radius: 16px;
  padding: 16px 16px 14px; box-shadow: var(--bayang); text-align: center;
}
.kartu-tap .kepala { display: flex; align-items: center; justify-content: center; gap: 8px; }
.kartu-tap .titik {
  width: 16px; height: 16px; border-radius: 50%; background: var(--warna);
  border: 1px solid rgba(0, 0, 0, .15);
}
.kartu-tap .nama { font-weight: 700; color: var(--biru-tua); font-size: .95rem; }

/* Angka besar = SETORAN PERANGKAT INI yang belum masuk server, jadi nilainya
   kembali 0 tiap sinkron (±5 detik sesudah tap terakhir). Karena itu keadaan
   0 sengaja diredupkan: layar berisi angka nol tegas hitam terbaca seolah
   hitungannya hilang, padahal justru artinya sudah tercatat di server. Yang
   menonjol adalah keadaan yang menuntut perhatian — masih menunggu dikirim. */
.kartu-tap .angka {
  font-size: 2.6rem; font-weight: 800; line-height: 1.15; margin: 6px 0 2px;
  color: var(--biru-tua); font-variant-numeric: tabular-nums;
}
.kartu-tap .angka.beres { color: var(--abu); opacity: .5; }
.kartu-tap .angka.menunggu { color: var(--biru-tua); }
.kartu-tap .angka.koreksi { color: var(--merah); }

/* ANGKANYA SENDIRI SEBUAH FIELD: selain tombol "−"/"+", petugas boleh mengetik
   jumlahnya langsung (mis. satu bus berisi 25 orang). Bukan kotak terpisah
   dengan tombol -/+ sendiri — itu berarti dua pasang tombol yang sama di satu
   kartu, dan dua angka yang harus dijaga tetap sejalan.

   BENTUKNYA SENGAJA BENTUK KOLOM FORMULIR PROYEK INI, bukan angka polos dengan
   garis tipis: percobaan pertama memakai latar transparan + garis putus-putus,
   dan hasilnya terbaca sebagai angka yang hanya bisa dilihat. Di sistem ini
   yang menandai "boleh diisi" adalah latar #fbfcff, garis 1.5px, dan cincin
   fokus — sama seperti .form-baris input — sedangkan LATAR ABU justru penanda
   read-only (lihat .acara-terkunci). Jadi dipakai bahasa yang sama, hanya
   dibesarkan: ia tetap angka paling menonjol di kartu.

   Bayangan dalam (inset) itu yang paling banyak menolong: kotak berbayang ke
   dalam terbaca sebagai wadah yang menunggu isi, bukan sebagai kartu. */
.kartu-tap input.angka {
  display: block; width: 100%; padding: 8px 10px; text-align: center;
  font-size: 2.1rem; line-height: 1.15; letter-spacing: -1px; font-family: inherit;
  border: 1.5px solid var(--garis); border-radius: 10px; background: #fbfcff;
  box-shadow: inset 0 2px 5px rgba(10, 28, 86, .07);
  caret-color: var(--warna); cursor: text;
  transition: border-color .18s, box-shadow .18s, background .18s;
  -webkit-appearance: none; appearance: none;
}
.kartu-tap input.angka::placeholder { color: #b6c0d6; }
.kartu-tap input.angka:hover { border-color: var(--warna); }
.kartu-tap input.angka:focus {
  outline: 0; background: #fff; border-color: var(--warna);
  box-shadow: 0 0 0 4px rgba(42, 86, 214, .13);
}
/* Keadaan "beres" (nol, sudah disetor) meredupkan WARNA ANGKANYA saja — bukan
   `opacity` elemen seperti pada versi teks. Opacity ikut meredupkan bingkai,
   latar, dan bayangan dalamnya, dan justru itu yang membuat kolom ini terbaca
   sebagai kolom mati. */
.kartu-tap input.angka.beres { color: var(--abu); opacity: 1; }

/* Kaki kartu: keadaan setoran + angka gabungan seluruh perangkat.
   DUA baris yang selalu ada dan masing-masing dipaksa satu baris — tinggi
   kartu tidak boleh berubah saat keadaannya berganti, karena kartu inilah
   sasaran tap petugas (alasan yang sama dengan baris status sinkron). */
.kartu-tap .kaki {
  display: flex; flex-direction: column; gap: 1px; margin: 0 0 10px;
  font-size: .76rem; line-height: 1.35; color: var(--abu);
}
.kartu-tap .kaki > span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.kartu-tap .keadaan {
  font-size: .68rem; font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
}
.kartu-tap .keadaan.beres { color: var(--hijau); }
.kartu-tap .keadaan.menunggu { color: #8a6400; }
.kartu-tap .keadaan.koreksi { color: var(--merah); }
.kartu-tap .server strong { color: var(--biru-tua); font-variant-numeric: tabular-nums; }

.kartu-tap .aksi { display: grid; grid-template-columns: 1fr 2fr; gap: 10px; }
.kartu-tap .aksi button {
  border: 0; cursor: pointer; font: inherit; font-weight: 800; border-radius: 12px;
  min-height: 56px; font-size: 1.4rem; transition: filter .15s, transform .1s;
}
.kartu-tap .aksi button:active { transform: scale(.97); }
.kartu-tap .kurang { background: var(--latar); color: var(--abu); border: 1.5px solid var(--garis); }
.kartu-tap .tambah { background: var(--warna); color: #fff; }
.kartu-tap .tambah:hover, .kartu-tap .kurang:hover { filter: brightness(1.06); }

.titik-warna {
  display: inline-block; width: 14px; height: 14px; border-radius: 50%;
  vertical-align: -2px; border: 1px solid rgba(0, 0, 0, .15);
}

@media (max-width: 720px) {
  .grid-tap { grid-template-columns: 1fr 1fr; gap: 10px; }
  .kartu-tap { padding: 12px 10px; }
  .kartu-tap .angka { font-size: 2rem; }
  .kartu-tap input.angka { font-size: 1.75rem; padding: 7px 8px; }
  .kartu-tap.rangka .angka { height: 49px; }
  .kartu-tap .kaki { font-size: .72rem; margin-bottom: 8px; }
  .kartu-tap.rangka .kaki { height: 30px; }
  .kartu-tap .keadaan { font-size: .64rem; }
  .kartu-tap .aksi button { min-height: 52px; }

}

/* ---------- Status sinkron tab Presensi Warna ----------
   Satu baris untuk empat keadaan: tersinkron, menunggu, mengirim, gagal.

   Tinggi dan lebarnya dipatok supaya tata letak tidak pernah bergeser saat
   keadaannya berganti — kartu warna di bawahnya adalah sasaran tap petugas,
   dan sebelumnya baris ini muncul/hilang sehingga kartunya melompat 81px.
   Yang berubah hanya warna lampu, teks, dan label tombolnya. */
/* Strukturnya dipatok grid dengan DUA baris teks yang masing-masing satu
   baris (nowrap + ellipsis) dan slot aksi berlebar tetap. Percobaan pertama
   memakai satu kalimat panjang: kalimatnya membungkus jadi 2–3 baris tergantung
   keadaan, dan tinggi baris status ikut berubah 52→87→66px — pergeserannya
   pindah tempat, bukan hilang. Teksnya karena itu dibuat pendek dan berlapis:
   baris pertama keadaannya, baris kedua penjelasannya. */
.status-sinkron {
  display: grid; align-items: center;
  grid-template-columns: 10px minmax(0, 1fr) 118px;
  grid-template-areas: "lampu teks aksi";
  column-gap: 10px;
  min-height: 58px; padding: 10px 14px;
  border: 1px solid var(--garis); border-radius: 12px; background: #fff;
  font-size: .88rem; color: var(--abu);
  transition: background .25s ease, border-color .25s ease, color .25s ease;
}
.status-sinkron .lampu { grid-area: lampu; }
.status-sinkron .aksi { grid-area: aksi; }
.status-sinkron .teks {
  grid-area: teks; display: flex; flex-direction: column; min-width: 0; line-height: 1.35;
}
.status-sinkron .utama, .status-sinkron .petunjuk {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.status-sinkron .utama { font-weight: 700; }
.status-sinkron .petunjuk { font-size: .8rem; opacity: .8; }
.status-sinkron .lampu {
  width: 10px; height: 10px; flex: 0 0 10px; border-radius: 50%;
  background: var(--abu); box-shadow: 0 0 0 3px rgba(100, 112, 138, .16);
  transition: background .25s ease, box-shadow .25s ease;
}
/* Slot aksi berlebar tetap: label tombol berganti ("Kirim sekarang" ->
   "Coba lagi") tanpa menggeser teks di sebelahnya. Saat tidak ada aksi,
   tombolnya disembunyikan dengan visibility — bukan display:none, yang akan
   mengubah ukuran barisnya. */
.status-sinkron .aksi { text-align: right; }
.status-sinkron .aksi button { width: 100%; }
.status-sinkron.beres .aksi button,
.status-sinkron.mengirim .aksi button { visibility: hidden; }

.status-sinkron.beres { border-color: #cfe8d7; background: #f4fbf6; color: #14532d; }
.status-sinkron.beres .lampu { background: var(--hijau); box-shadow: 0 0 0 3px rgba(27, 127, 59, .18); }

.status-sinkron.menunggu { border-color: #e2c86a; background: #fffdf5; color: #6b4c00; }
.status-sinkron.menunggu .lampu { background: #b8860b; box-shadow: 0 0 0 3px rgba(184, 134, 11, .2); }

.status-sinkron.mengirim { border-color: #bcccf5; background: var(--biru-muda); color: var(--biru-tua); }
.status-sinkron.mengirim .lampu {
  background: var(--biru); box-shadow: 0 0 0 3px rgba(18, 48, 143, .18);
  animation: denyutSinkron 1s ease-in-out infinite;
}
@keyframes denyutSinkron {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(.72); opacity: .55; }
}

.status-sinkron.gagal { border-color: #f6c9d1; background: #fdf3f5; color: #7a0a1c; }
.status-sinkron.gagal .lampu { background: var(--merah); box-shadow: 0 0 0 3px rgba(200, 16, 46, .18); }

/* Umpan balik satu tap: kartunya berdenyut sebentar supaya petugas yakin
   tekanannya terbaca, tanpa menambah elemen apa pun ke layar. */
.kartu-tap.berdenyut { animation: denyutKartu .22s ease; }
@keyframes denyutKartu {
  0% { transform: scale(1); }
  45% { transform: scale(1.035); }
  100% { transform: scale(1); }
}

/* Rangka sementara selagi data warna diambil: tinggi kartunya sudah dipesan
   lebih dulu, jadi kedatangan data tidak mendorong isi halaman. */
.kartu-tap.rangka { border-color: var(--garis); box-shadow: none; }
.kartu-tap.rangka .kepala, .kartu-tap.rangka .angka,
.kartu-tap.rangka .kaki, .kartu-tap.rangka .aksi button {
  background: linear-gradient(90deg, #eef1f8 25%, #f7f9fd 37%, #eef1f8 63%);
  background-size: 400% 100%; animation: kilauRangka 1.3s ease infinite;
  border-radius: 8px; color: transparent; border: 0;
}
.kartu-tap.rangka .titik { background: transparent; border-color: transparent; }
.kartu-tap.rangka .kepala { height: 20px; }
/* ±58px = 2,1rem x 1,15 + padding 16 + garis 3, setinggi kotak angka setelah
   data datang. Tanpa penyesuaian ini kartu tumbuh ~12px begitu warnanya masuk. */
.kartu-tap.rangka .angka { height: 58px; margin: 8px 0 2px; }
/* Kaki ikut dipesan tingginya: tanpa ini kartu tumbuh ~32px begitu data warna
   datang, dan isi halaman di bawahnya terdorong persis saat petugas mulai
   menekan. */
.kartu-tap.rangka .kaki { height: 32px; margin: 0 0 10px; }
/* Rangka memakai <div class="angka"> biasa, bukan <input>: yang dipesan hanya
   tingginya, dan input kosong yang berkilau terbaca seperti kolom yang menunggu
   diisi padahal datanya belum ada. */
@keyframes kilauRangka {
  0% { background-position: 100% 0; }
  100% { background-position: 0 0; }
}

/* Layar sempit: tombol turun ke barisnya sendiri supaya teks status tidak
   terpotong ellipsis. Ruangnya tetap dipesan di semua keadaan (tombolnya
   disembunyikan dengan visibility), jadi tingginya tidak berubah. */
@media (max-width: 520px) {
  .status-sinkron {
    grid-template-columns: 10px minmax(0, 1fr);
    grid-template-areas: "lampu teks" ". aksi";
    row-gap: 8px; align-items: start;
  }
  .status-sinkron .lampu { margin-top: 6px; }
  .status-sinkron .aksi { text-align: left; }
  .status-sinkron .aksi button { width: auto; min-width: 140px; }
}

@media (prefers-reduced-motion: reduce) {
  .status-sinkron.mengirim .lampu, .kartu-tap.berdenyut, .kartu-tap.rangka .angka { animation: none; }
}
