/* =====================================================================
   KUBICSA — screens.css
   Tata letak tiap layar. Mobile-first, potret.
   ===================================================================== */

/* ---------------------------------------------------------------------
   1. Wadah layar & transisi (§10.6)
   --------------------------------------------------------------------- */
/* ---------------------------------------------------------------------
   Tinggi satu layar penuh, ditulis bertingkat.
   ---------------------------------------------------------------------
   Tiap baris menimpa baris sebelumnya HANYA kalau browser memahaminya;
   yang tidak dipahami akan dibuang saat parsing dan baris sebelumnya
   tetap berlaku. Jadi urutannya: paling aman dulu, paling tepat terakhir.

     100vh   — browser lama yang belum kenal satuan dvh
     100dvh  — mengikuti bilah alamat yang muncul-hilang
     calc()  — dikurangi safe-area

   Pengurangan safe-area itu WAJIB, bukan penghalusan. <body> sudah diberi
   padding sebesar safe-area di base.css. Layar setinggi 100dvh DI DALAM
   body itu berarti tinggi totalnya 100dvh + takik atas + bilah gestur
   bawah — di HP modern kelebihannya 60–90 px. Di layar gameplay,
   kelebihan itu persis mendorong baris kartu opsi C/D dan tombol "Kunci
   Jawaban" ke bawah lipatan layar. Kartu yang keluar viewport juga
   dilewati renderer (lihat render() di renderer.js), jadi selain harus
   digulir, isinya memang belum digambar — ikut memperkuat keluhan
   "gambarnya tidak muncul di HP".
   --------------------------------------------------------------------- */
#aplikasi {
  position: relative;
  overflow-x: hidden;
  min-height: 100vh;
  min-height: 100dvh;
  min-height: calc(100dvh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px));
}

/*
 * JANGAN memberi .layar z-index.
 * ---------------------------------------------------------------------
 * Dulu ada `z-index: 1` di sini dengan maksud "konten di atas canvas 3D".
 * Efeknya justru kebalikannya, dan rapuh:
 *
 * z-index pada elemen berposisi membuat STACKING CONTEXT. Seluruh isi
 * .layar lalu terkurung di dalamnya, dan z-index anak-anaknya
 * (.panggung-label 2, .huruf-opsi 2, .petunjuk-putar 2, .tombol-sticky 3)
 * tidak lagi bisa dibandingkan dengan canvas — mereka semua ikut naik
 * turun bersama .layar sebagai satu lapisan.
 *
 * Karena canvas juga z-index:1, keduanya seri, dan yang menang cuma
 * ditentukan urutan DOM: canvas kebetulan di-append belakangan. Artinya
 * seluruh tata lapis gameplay bergantung pada KAPAN dapatkanRenderer()
 * pertama kali dipanggil — satu elemen yang disisipkan ke <body> pada
 * urutan berbeda sudah cukup membuat semua bentuk 3D lenyap.
 *
 * Tanpa z-index (position:relative saja TIDAK membuat stacking context),
 * susunannya jadi eksplisit dan tidak lagi bergantung urutan DOM:
 *   latar .panggung/.kartu-opsi (z-index auto) < canvas (1)
 *     < label & huruf opsi (2) < tombol kunci (3) < modal (50).
 * position:relative tetap dipertahankan sebagai jangkar .stiker.
 */
.layar {
  display: none;
  position: relative;
  padding: var(--sp-4);
  /* Tanpa tambahan safe-area di sini: <body> sudah memberi paddingnya. */
  padding-bottom: var(--sp-6);
  min-height: 100vh;
  min-height: 100dvh;
  min-height: calc(100dvh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px));
}

.layar[data-aktif='true'] { display: block; }

.layar[data-transisi='masuk']  { animation: geser-masuk 250ms cubic-bezier(.34,1.32,.64,1) both; }
.layar[data-transisi='mundur'] { animation: geser-mundur 250ms cubic-bezier(.34,1.32,.64,1) both; }

@keyframes geser-masuk {
  from { opacity: 0; transform: translateX(22px); }
  to   { opacity: 1; transform: none; }
}
@keyframes geser-mundur {
  from { opacity: 0; transform: translateX(-22px); }
  to   { opacity: 1; transform: none; }
}

.wadah {
  width: 100%;
  max-width: 520px;    /* nyaman dibaca di tablet tanpa melebar berlebihan */
  margin: 0 auto;
}

.tumpuk    { display: flex; flex-direction: column; gap: var(--sp-4); }
.tumpuk-rapat { display: flex; flex-direction: column; gap: var(--sp-2); }
.tengah    { text-align: center; }

/* ---------------------------------------------------------------------
   2. Splash
   --------------------------------------------------------------------- */
/*
 * Pemusatan dilakukan oleh .layar-nya sendiri, bukan oleh .wadah dengan
 * min-height:100dvh. Mengukur ulang satu layar penuh DI DALAM padding
 * .layar selalu menghasilkan kelebihan sebesar paddingnya, dan splash
 * jadi bisa digulir. Pola [data-aktif='true'] sama seperti #layar-main:
 * spesifisitas ID hanya boleh menang saat layar ini memang aktif.
 */
#layar-splash[data-aktif='true'] {
  display: grid;
  place-content: center;
  justify-items: center;
}

#layar-splash .wadah {
  display: grid;
  justify-items: center;
  gap: var(--sp-5);
  text-align: center;
}

.bar-muat {
  width: min(240px, 70vw);
  height: 12px;
  border-radius: var(--radius-pil);
  background: #E4DEE6;
  background: color-mix(in srgb, var(--netral) 50%, var(--panggung));
  border: 2px solid var(--primer);
  overflow: hidden;
}

.bar-muat > i {
  display: block;
  height: 100%;
  width: 0%;
  background: var(--primer);
  transition: width 240ms ease;
}

/* ---------------------------------------------------------------------
   3. Portal masuk
   --------------------------------------------------------------------- */
/* Pemusatan dipegang layarnya, sama seperti #layar-splash. */
#layar-portal[data-aktif='true'] {
  display: grid;
  place-content: center;
  justify-items: center;
}

#layar-portal .wadah {
  /* Tinggi satu layar penuh sudah dijamin .layar. Mengukurnya lagi di
     dalam padding .layar cuma bikin halaman bisa digulir tanpa alasan. */
  display: grid;
  place-content: center;
  justify-items: center;
  gap: var(--sp-4);
  text-align: center;
}

.tautan-kecil {
  font-size: var(--f-body);
  font-weight: 700;
  color: var(--teks-lembut);
  text-decoration: underline;
  text-underline-offset: 3px;
  min-height: var(--sentuh);
  display: inline-flex;
  align-items: center;
}

/* ---------------------------------------------------------------------
   4. Pilih tema (§10.2)
   --------------------------------------------------------------------- */
.pilihan-tema {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-3);
}

.kartu-tema {
  padding: var(--sp-4) var(--sp-3);
  border-radius: var(--radius-kartu);
  border: var(--tebal-stiker) solid transparent;
  min-height: 132px;
  display: grid;
  place-content: center;
  gap: var(--sp-2);
  justify-items: center;
  font-weight: 700;
  color: var(--tinta);
  transition: transform var(--transisi);
}

.kartu-tema[data-tema='manis'] {
  background: #FFF0F6;
  border-color: #FF6FA5;
  box-shadow: 0 var(--angkat) 0 #D94A80;
}

.kartu-tema[data-tema='sejuk'] {
  background: #EFFBF6;
  border-color: #3FC8A8;
  box-shadow: 0 var(--angkat) 0 #2A9E84;
}

.kartu-tema[aria-pressed='true'] { transform: translateY(-3px) rotate(-1.5deg); }

.contoh-warna { display: flex; gap: 6px; }

/* Tepi tipis wajib: contoh warna latar hampir sama dengan latar kartunya
   sendiri, jadi tanpa tepi ia tampak hilang dan siswa hanya melihat dua
   warna dari tiga. */
.contoh-warna i {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  display: block;
  border: 1.5px solid rgba(58, 32, 48, 0.28);
  border: 1.5px solid color-mix(in srgb, var(--tinta) 28%, transparent);
}

/* ---------------------------------------------------------------------
   5. Kartu kode peserta
   --------------------------------------------------------------------- */
.kode-besar {
  font-family: var(--font-pixel);
  font-size: 34px;
  letter-spacing: 0.1em;
  color: var(--teks);
  padding: var(--sp-4);
  border-radius: var(--radius-kartu);
  background: var(--panggung);
  border: var(--tebal-stiker) dashed var(--primer);
  text-align: center;
  user-select: all;   /* sekali ketuk memilih seluruh kode */
}

/* ---------------------------------------------------------------------
   6. Main menu
   --------------------------------------------------------------------- */
/* Sapaan sebagai "kartu pemain", bukan sekadar baris teks — ini elemen
   pertama yang dilihat siswa tiap kali membuka menu, dan satu-satunya
   tempat kodenya tampil. Kode itu yang dipakai melanjutkan di hari lain,
   jadi ia harus mudah ditemukan, bukan tenggelam sebagai label abu-abu. */
.sapaan {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3);
  border-radius: var(--radius-kartu);
  background: var(--panggung);
  border: var(--tebal-stiker) solid var(--primer);
  box-shadow: 0 var(--angkat) 0 var(--primer-tua);
}

.sapaan .maskot-kecil { flex: 0 0 auto; }

/* Kode peserta dibuat menonjol dan mudah disalin dengan sekali tekan
   lama — sama seperti .kode-besar di layar kartu kode. */
#kode-peserta-kecil {
  font-family: var(--font-pixel);
  letter-spacing: 0.08em;
  color: var(--primer-tua);
  user-select: all;
}

.tombol-sesi {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  padding: var(--sp-4);
  text-align: left;
  border-radius: var(--radius-kartu);
  background: var(--panggung);
  border: var(--tebal-stiker) solid var(--primer);
  box-shadow: 0 var(--angkat) 0 var(--primer-tua);
  min-height: 84px;
  transition: transform 120ms ease, box-shadow 120ms ease;
}

.tombol-sesi:active:not([disabled]) {
  transform: translateY(3px);
  box-shadow: 0 1px 0 var(--primer-tua);
}

.tombol-sesi[data-sorot='true'] {
  border-color: var(--aksen);
  box-shadow: 0 var(--angkat) 0 #C9A62E;
}

/* Tombol sesi yang masih terkunci TIDAK BOLEH terasa seperti hukuman
   (§4). Redup boleh, muram tidak — ini titik paling rawan peserta
   berhenti sebelum post-test. */
.tombol-sesi[disabled] {
  opacity: 0.62;
  cursor: not-allowed;
  border-style: dashed;
  box-shadow: none;
}

.ikon-sesi { width: 44px; height: 44px; flex: 0 0 auto; }

/* ---------------------------------------------------------------------
   Petak aksi sekunder di Main Menu
   ---------------------------------------------------------------------
   Menggantikan empat tautan teks bergaris bawah yang bertumpuk. Selain
   terbaca seperti footer situs, tautan itu punya masalah nyata: target
   sentuhnya hanya setinggi satu baris teks, dan empat baris beruntun
   dengan jarak 8px membuat "Akhiri Sesi" gampang tertekan padahal yang
   dituju "Ganti suasana" — dan aksi itu mengeluarkan siswa dari akunnya.

   Dua kolom, bukan satu baris berisi empat: di layar 360px, empat petak
   sebaris menyisakan lebar 78px per petak dan labelnya harus disingkat
   sampai tidak terbaca.
   --------------------------------------------------------------------- */
.petak-aksi {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-2);
}

.aksi {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 60px;              /* jauh di atas ambang 44px (§10.8) */
  padding: 10px 12px;
  text-align: left;
  font-size: var(--f-label);
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: 0.02em;
  color: var(--teks-lembut);
  border-radius: var(--radius-kecil);
  border: 2px solid var(--netral);
  background: #FCFAF4;
  background: color-mix(in srgb, var(--panggung) 92%, var(--netral));
  transition: transform 120ms ease, border-color 150ms ease, color 150ms ease;
  touch-action: manipulation;
}

.aksi:active {
  transform: translateY(2px);
  border-color: var(--primer);
  color: var(--teks);
}

.aksi-ikon {
  font-size: 20px;
  line-height: 1;
  flex: 0 0 auto;
}

/* "Akhiri sesi" mengeluarkan siswa dari akunnya dan mengembalikan
   perangkat ke pemain berikutnya. Ia tidak boleh tampak sama persis
   dengan tiga aksi lain yang semuanya tidak merusak apa pun. */
.aksi-keluar {
  border-style: dashed;
  border-color: var(--salah);
  color: var(--salah);
}

.aksi-keluar:active { border-color: var(--salah); color: var(--salah); }

/* ---------------------------------------------------------------------
   Jejak tiga tahap (§4 layar 6)
   ---------------------------------------------------------------------
   Menu sebelumnya hanya menampilkan dua tombol, jadi siswa tidak pernah
   melihat posisinya di dalam program: tombol Uji Diri yang nonaktif
   terbaca sebagai "rusak" atau "aku dilarang", bukan "belum gilirannya".
   Modal pengantar memang menjelaskan urutannya, tapi hanya muncul sekali.

   Vertikal, bukan horizontal: di layar 360px, tiga label berdampingan
   memaksa teks 10px atau singkatan yang harus ditebak sendiri.
   --------------------------------------------------------------------- */
.jejak {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}

.jejak li {
  display: grid;
  grid-template-columns: 26px 1fr auto;
  align-items: center;
  gap: var(--sp-3);
  padding: 6px 0;
}

/* Garis penghubung antar tahap, digambar dari nomornya sendiri supaya
   tidak perlu elemen tambahan yang harus ikut dijaga panjangnya. */
.jejak li:not(:last-child) .tahap-nomor::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 100%;
  width: 2px;
  height: 14px;
  transform: translateX(-50%);
  background: var(--netral);
}

.tahap-nomor {
  position: relative;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-family: var(--font-pixel);
  font-size: 12px;
  background: #E4DEE6;
  background: color-mix(in srgb, var(--netral) 50%, var(--panggung));
  color: var(--teks-lembut);
  border: 2px solid var(--netral);
}

.tahap-teks { font-weight: 700; color: var(--teks-lembut); }
.tahap-ket  { font-size: var(--f-label); color: var(--teks-samar); }

/* Keadaan dibedakan lewat BENTUK dan ISI lambang, bukan warna saja
   (§10.8): selesai memakai centang, berjalan memakai titik penuh,
   terkunci memakai gembok. */
.jejak li[data-tahap='selesai'] .tahap-nomor {
  background: var(--benar);
  border-color: var(--benar);
  color: var(--tinta);
}
.jejak li[data-tahap='selesai'] .tahap-teks { color: var(--teks); }

.jejak li[data-tahap='kini'] .tahap-nomor {
  background: var(--aksen);
  border-color: var(--aksen);
  color: var(--tinta);
  transform: scale(1.12);
}
.jejak li[data-tahap='kini'] .tahap-teks { color: var(--teks); }

.jejak li[data-tahap='terkunci'] { opacity: 0.7; }

/* ---------------------------------------------------------------------
   7. Gameplay (§10.5)
   ---------------------------------------------------------------------
   Proporsi: header ±10%, panggung target ±36%, grid opsi ±40%,
   tombol kunci jawaban melekat di bawah.
   --------------------------------------------------------------------- */
/*
 * Selektor ID (#layar-main) punya spesifisitas lebih tinggi daripada
 * selektor kelas (.layar[data-aktif]) TERLEPAS dari urutan penulisan.
 * Ditulis sebagai "#layar-main { display:flex }" polos, aturan ini akan
 * SELALU menang atas ".layar { display:none }" milik router — layar
 * gameplay jadi tampil terus-menerus di belakang layar lain, apa pun
 * data-aktif-nya. Wajib disatukan dengan [data-aktif='true'] supaya
 * spesifisitas tinggi itu hanya berlaku saat layar ini memang aktif;
 * saat tidak aktif, aturan dasar .layar{display:none} yang berlaku.
 */
#layar-main[data-aktif='true'] {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding-bottom: var(--sp-3);
  height: 100vh;
  height: 100dvh;
  height: calc(100dvh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px));
}

.kepala-main {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  flex: 0 0 auto;
  min-height: 44px;
}

/* Penanda langkah tutorial — pil kecil, sejajar tangga titik. */
.pil-langkah {
  flex: 0 0 auto;
  padding: 4px 10px;
  border-radius: var(--radius-pil);
  background: #E7E1E7;
  background: color-mix(in srgb, var(--netral) 40%, var(--panggung));
  color: var(--teks-lembut);
  white-space: nowrap;
}

/* ---------------------------------------------------------------------
   Maskot pendamping sesi
   ---------------------------------------------------------------------
   Diletakkan di HEADER, bukan di dalam atau di tepi panggung. Dua alasan:

   1. §10.3 — tidak boleh ada hiasan di belakang atau di atas objek 3D.
      Elemen .view mengisi panggung sampai inset 10px, jadi apa pun yang
      ditaruh di sana pasti menutupi sebagian bentuk yang sedang dinilai.
   2. Tata letak gameplay memakai tinggi viewport penuh dan sudah ketat;
      header adalah satu-satunya tempat yang tidak mencuri tinggi dari
      panggung maupun kartu opsi.

   Reaksinya (senang/belajar) HANYA muncul di sesi Lab. ui.js yang
   menegakkannya, lihat setMaskot().
   --------------------------------------------------------------------- */
.maskot-main {
  /* Kotak tetap + object-fit:contain, JANGAN width/height keras saja.
     ---------------------------------------------------------------------
     Tidak satu pun berkas maskot bujur sangkar: rey-kecil 252×447 (tegak),
     nara-belajar 398×231 (melebar), nara-piala 512×492. Memaksa 40×40
     membuat semuanya teregang — dan karena gambarnya berganti tiap kali
     siswa menjawab, distorsinya ikut berubah-ubah di tengah sesi.

     contain menjaga rasio aslinya, dan kotak tetap menjaga lebar header
     tidak melonjak saat gambar bertukar antara yang tegak dan yang
     melebar — pergeseran tata letak tepat di sebelah tangga progres
     akan sangat mengganggu. */
  width: 46px;
  height: 46px;
  object-fit: contain;
  object-position: center;
  flex: 0 0 auto;
  image-rendering: auto;
  /* Header ini di dalam .layar; canvas 3D ada di z-index 1 dan menutupi
     seluruh viewport, jadi maskot butuh z-index 2 seperti label panggung
     dan huruf opsi — kalau tidak, ia tertimpa canvas. */
  position: relative;
  z-index: 2;
}

/* Sentakan kecil saat keadaannya berganti. Tidak ada animasi berulang:
   apa pun yang bergerak terus-menerus di dekat panggung akan menarik
   perhatian dari tugasnya sendiri. */
.maskot-main[data-keadaan='benar'],
.maskot-main[data-keadaan='salah'] {
  animation: maskot-sentak 420ms cubic-bezier(.34, 1.32, .64, 1) both;
}

@keyframes maskot-sentak {
  0%   { transform: scale(1) rotate(0); }
  45%  { transform: scale(1.22) rotate(-7deg); }
  100% { transform: scale(1) rotate(0); }
}

@media (prefers-reduced-motion: reduce) {
  .maskot-main[data-keadaan='benar'],
  .maskot-main[data-keadaan='salah'] { animation: none; }
}

.panggung-target {
  flex: 1 1 36%;
  min-height: 168px;
}

.area-opsi { flex: 1 1 40%; }

.area-opsi .grid-opsi { height: 100%; }

/*
 * Di-SCOPE ke .area-opsi — sama seperti baris di atas. Ditulis tanpa
 * scope sebelumnya (ditemukan lewat pengujian visual nyata pada tutorial,
 * bukan pembacaan kode): ".kartu-opsi { aspect-ratio: auto; ... }" polos
 * menimpa aspect-ratio:1/1 dari components.css untuk SEMUA .kartu-opsi
 * di halaman mana pun screens.css dimuat — termasuk grid latihan
 * tutorial yang sengaja hanya 2 kartu dan TIDAK dibungkus .area-opsi.
 * Akibatnya kartu opsi tutorial gepeng jadi rasio 254:96 alih-alih
 * bujur sangkar, dan objek 3D di dalamnya terlihat jauh lebih kecil dari
 * seharusnya. Ini pola bug spesifisitas yang sama seperti #layar-main
 * dan .pil-timer/[hidden] sebelumnya: aturan ditulis untuk SATU konteks
 * tapi lupa di-scope, lalu bocor ke konteks lain.
 */
.area-opsi .kartu-opsi { aspect-ratio: auto; height: 100%; min-height: 96px; }

/* ---------------------------------------------------------------------
   Layar lebar — gameplay dua kolom
   ---------------------------------------------------------------------
   Di potret, tumpukan vertikal sudah benar. Di laptop ia berubah jadi
   masalah pengukuran, bukan sekadar kurang cantik: panggung menjadi
   sekitar 520×155 (rasio 3,35:1), dan karena auto-fit kamera dibatasi
   sisi terpendek, bentuk 3D-nya tampil kecil di tengah lautan krem.
   Bentuk yang kecil lebih sulit dibedakan — siswa bisa salah karena
   tidak bisa melihatnya, bukan karena kemampuan spasialnya (§10.3).

   Dua kolom membuat panggung dan grid opsi sama-sama mendekati bujur
   sangkar, jadi keduanya memakai tinggi yang tersedia sepenuhnya.

   Ambangnya 900px DAN orientasi lanskap: tablet 800px potret tetap
   memakai tumpukan vertikal, yang memang lebih tepat di sana.
   --------------------------------------------------------------------- */
@media (min-width: 900px) and (orientation: landscape) {
  #layar-main[data-aktif='true'] {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr) auto;
    grid-template-areas:
      'kepala  kepala'
      'panggung opsi'
      'tombol  tombol';
    column-gap: var(--sp-5);
    align-items: stretch;
    max-width: 1080px;
    margin: 0 auto;
  }

  #layar-main[data-aktif='true'] > .kepala-main { grid-area: kepala; }
  #layar-main[data-aktif='true'] > .panggung-target { grid-area: panggung; }
  #layar-main[data-aktif='true'] > .area-opsi { grid-area: opsi; }
  #layar-main[data-aktif='true'] > .tombol-sticky { grid-area: tombol; }

  /* SEMUA di-scope ke #layar-main. Ditulis polos, ".panggung-target
     { min-height: 0 }" akan ikut mengenai #panggung-tutorial — yang juga
     memakai kelas itu, tapi berada di aliran blok biasa dengan seluruh
     anaknya berposisi absolut. Tanpa min-height, panggung tutorial runtuh
     ke tinggi nol dan objek 3D-nya hilang total. Ini pola bug yang sama
     persis dengan #layar-main dan .kartu-opsi sebelumnya (lihat catatan
     di bawah): aturan ditulis untuk satu konteks, lupa di-scope, bocor.

     flex-basis dari tata letak potret tidak berlaku lagi di grid; tinggi
     baris tengah yang menentukan, dan keduanya berbagi baris yang sama. */
  #layar-main[data-aktif='true'] > .panggung-target { min-height: 0; }
  #layar-main[data-aktif='true'] .area-opsi .kartu-opsi { min-height: 0; }

  /* Tombol kunci tidak perlu melekat di layar lebar — seluruh tata letak
     sudah muat tanpa digulir, dan sticky di dalam baris grid justru bisa
     menempel di tempat yang tidak diharapkan. */
  #layar-main[data-aktif='true'] > .tombol-sticky {
    position: static;
    max-width: 420px;
    justify-self: center;
  }
}

/* ---------------------------------------------------------------------
   Layar lebar — layar non-gameplay
   ---------------------------------------------------------------------
   Di laptop, kolom 520px yang menempel di atas menyisakan ruang kosong
   besar di bawahnya dan terbaca seperti formulir yang belum jadi.

   Dipusatkan memakai AUTO-MARGIN FLEX, bukan place-content:center. Pada
   konten yang lebih tinggi dari wadahnya, place-content:center meluber ke
   DUA arah dan bagian atasnya tidak bisa dijangkau gulir sama sekali —
   layar registrasi dengan tujuh isian dan papan ketik terbuka persis
   masuk kasus itu. Auto-margin flex runtuh menjadi 0 saat kekurangan
   ruang, jadi konten tinggi tetap mulai dari atas dengan aman.

   Mobile sengaja tidak disentuh: di sanalah instrumen ini benar-benar
   dipakai, dan perilakunya sekarang sudah benar.
   --------------------------------------------------------------------- */
@media (min-width: 720px) {
  .layar[data-aktif='true'] {
    display: flex;
    flex-direction: column;
  }

  /* #layar-main punya tata letaknya sendiri di atas; #layar-splash dan
     #layar-portal sudah memusatkan lewat grid. Ketiganya memakai
     selektor ID sehingga tetap menang atas aturan di atas. */
  .layar[data-aktif='true'] > .wadah { margin: auto; }
}

.petunjuk-putar {
  position: absolute;
  left: 50%;
  bottom: 8px;
  transform: translateX(-50%);
  z-index: 2;
  pointer-events: none;
  font-size: var(--f-label);
  color: var(--teks-lembut);
  background: rgba(255, 253, 247, 0.85);
  background: color-mix(in srgb, var(--panggung) 85%, transparent);
  padding: 3px 10px;
  border-radius: var(--radius-pil);
}

/* Layar sangat pendek (mis. ponsel lama dengan keyboard terbuka):
   kecilkan panggung target lebih dulu, jangan opsinya. */
@media (max-height: 620px) {
  .panggung-target { min-height: 130px; }
  .area-opsi .kartu-opsi { min-height: 78px; }
  :root { --f-judul: 22px; }
}

/* ---------------------------------------------------------------------
   8. Umpan balik Lab (§8) — TIDAK PERNAH dipakai di sesi Test
   --------------------------------------------------------------------- */
.umpan-balik { text-align: center; }

/* --cad-lencana-hasil = hasil color-mix(warna-hasil 30%, panggung) yang
   sudah dihitung, untuk browser tanpa color-mix(). Ditaruh di sini, bukan
   di .lencana-hasil, karena nilainya bergantung benar/salah. */
.umpan-balik[data-hasil='benar'] {
  --warna-hasil: var(--benar);
  --cad-lencana-hasil: #CAEECF;
}
.umpan-balik[data-hasil='salah'] {
  --warna-hasil: var(--salah);
  --cad-lencana-hasil: #FFD6D2;
}

.lencana-hasil {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 8px 18px;
  border-radius: var(--radius-pil);
  background: var(--cad-lencana-hasil, #E4DEE6);
  background: color-mix(in srgb, var(--warna-hasil) 30%, var(--panggung));
  border: 2px solid var(--warna-hasil);
  color: var(--tinta);
  font-weight: 800;
}

/* Ghost replay: target dan pilihan siswa berputar berdampingan. */
.ghost-replay {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-3);
}

.ghost-replay .panggung { min-height: 132px; }

/* ---------------------------------------------------------------------
   9. Hasil sesi
   --------------------------------------------------------------------- */
/* Catatan skor terkoreksi — sengaja kecil dan tenang. Ia informasi untuk
   transparansi, bukan capaian yang perlu dirayakan atau disesali. */
.catatan-skor {
  margin: 0;
  font-size: var(--f-label);
  line-height: 1.6;
  color: var(--teks-lembut);
  text-align: left;
  padding: 10px 14px;
  border-radius: var(--radius-kecil);
  background: #E7E1E7;
  background: color-mix(in srgb, var(--netral) 35%, var(--panggung));
}

/* ---------------------------------------------------------------------
   Narasi hasil + kamus istilah (§4 layar 9)
   ---------------------------------------------------------------------
   Narasi sengaja lebih besar dan lebih pekat daripada .catatan-skor: ini
   kalimat yang benar-benar dibaca peserta untuk tahu hasilnya bagus atau
   tidak, bukan catatan kaki. .catatan-skor tetap abu-abu redup karena
   isinya memang keterangan teknis.
   --------------------------------------------------------------------- */
.narasi-hasil {
  margin: 0;
  font-size: var(--f-teks);
  line-height: 1.65;
  color: var(--teks);
  text-align: left;
  padding: 14px 16px;
  border-radius: var(--radius-kecil);
  background: color-mix(in srgb, var(--primer) 10%, var(--panggung));
  border-left: 4px solid var(--primer);
}

.istilah-hasil {
  width: 100%;
  text-align: left;
  font-size: var(--f-label);
  color: var(--teks-lembut);
  border-radius: var(--radius-kecil);
  background: color-mix(in srgb, var(--netral) 22%, var(--panggung));
  padding: 4px 14px;
}

.istilah-hasil > summary {
  cursor: pointer;
  padding: 10px 0;
  font-weight: 700;
  color: var(--teks);
  /* Penanda bawaan disembunyikan lalu digambar ulang di ::after supaya
     posisinya konsisten di semua browser (Safari menaruhnya berbeda). */
  list-style: none;
}
.istilah-hasil > summary::-webkit-details-marker { display: none; }
.istilah-hasil > summary::after {
  content: '＋';
  float: right;
  color: var(--primer);
  font-weight: 700;
}
.istilah-hasil[open] > summary::after { content: '−'; }

/* Sasaran sentuh minimal 44px (§10.8) — ringkasan ini satu-satunya kontrol
   di blok ini dan sering ditekan dengan ibu jari di layar kecil. */
.istilah-hasil > summary { min-height: 44px; display: flex; align-items: center; }
.istilah-hasil > summary::after { margin-left: auto; float: none; }

.istilah-hasil dl { margin: 0 0 10px; }
.istilah-hasil dt {
  font-weight: 700;
  color: var(--teks);
  margin-top: 12px;
}
.istilah-hasil dd {
  margin: 2px 0 0;
  line-height: 1.6;
}

.skor-besar {
  font-family: var(--font-pixel);
  font-size: 56px;
  line-height: 1;
  color: var(--teks);
  text-shadow: 0 3px 0 var(--panggung),
               0 0 16px var(--cad-primer-45);
}

.baris-statistik {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-2);
  text-align: center;
}

.baris-statistik .angka { font-size: 22px; }

/* ---------------------------------------------------------------------
   Perkembangan pretest → posttest (§4 layar 9)
   ---------------------------------------------------------------------
   Dua batang bersanding, bukan angka berdampingan. Untuk siswa SMP,
   panjang batang terbaca sekali lihat sementara "12 → 16" masih harus
   dihitung dulu. Angkanya tetap ditulis di sebelahnya karena panjang
   batang saja tidak cukup presisi — dan karena panjang batang tidak bisa
   dibacakan pembaca layar.
   --------------------------------------------------------------------- */
.banding {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  margin: var(--sp-2) 0;
}

.baris-banding {
  display: grid;
  grid-template-columns: 88px 1fr 46px;
  gap: var(--sp-2);
  align-items: center;
}

.bar-banding {
  height: 18px;
  border-radius: var(--radius-pil);
  background: #E4DEE6;
  background: color-mix(in srgb, var(--netral) 50%, var(--panggung));
  border: 2px solid var(--primer);
  overflow: hidden;
}

.bar-banding > i {
  display: block;
  height: 100%;
  width: 0;
  border-radius: var(--radius-pil);
  /*
   * SENGAJA --teks-lembut, BUKAN --netral.
   *
   * Alur batang di atas sudah berwarna --netral (dicampur panggung), jadi
   * isian ber-warna --netral pula praktis tidak terlihat: batang "Uji Diri 1"
   * terbaca sebagai batang kosong, dan yang tampak jelas hanya batang
   * "Uji Diri 2" yang berwarna primer. Padahal SELURUH gunanya blok ini
   * adalah membandingkan keduanya — kalau yang "sebelum" tidak kelihatan,
   * tidak ada yang bisa dibandingkan.
   *
   * --teks-lembut tetap meredup dibanding --primer (jadi makna "ini yang
   * dulu, itu yang sekarang" tetap terbaca dari warnanya), tapi cukup
   * kontras terhadap alurnya untuk benar-benar terlihat.
   */
  background: var(--teks-lembut);
  /* Lebar disetel satu frame setelah dipasang, jadi batangnya tumbuh —
     bukan langsung muncul jadi. Ini satu-satunya momen "hasil akhir" di
     seluruh alur; layak diberi gerak. */
  transition: width 800ms cubic-bezier(.34, 1.32, .64, 1);
}

.baris-banding[data-sesi='sesudah'] .bar-banding > i { background: var(--primer); }

@media (prefers-reduced-motion: reduce) {
  .bar-banding > i { transition: none; }
}

.angka-banding {
  font-family: var(--font-pixel);
  font-size: 15px;
  text-align: right;
  color: var(--teks);
}

.pesan-perkembangan {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: 0;
  padding: 10px 14px;
  border-radius: var(--radius-kecil);
  font-weight: 700;
  background: #E7E1E7;
  background: color-mix(in srgb, var(--netral) 45%, var(--panggung));
  color: var(--teks);
}

/* Warna BUKAN satu-satunya penanda (§10.8): tiap keadaan juga punya ikon
   sendiri lewat ::before, dan kalimatnya sudah menyebutkan arahnya. */
.pesan-perkembangan[data-arah='naik'] {
  background: #DCF3E2;
  background: color-mix(in srgb, var(--benar) 22%, var(--panggung));
}
.pesan-perkembangan[data-arah='naik']::before  { content: '▲'; color: #2E7D46; }
.pesan-perkembangan[data-arah='tetap']::before { content: '='; }
.pesan-perkembangan[data-arah='turun']::before { content: '•'; }

/* Rincian per soal — hanya Lab (§9). Daftar ringkas satu baris per soal,
   bukan tabel: harus tetap terbaca nyaman di layar HP sempit. */
.daftar-rincian {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 280px;
  overflow-y: auto;
}

.item-rincian {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 8px 10px;
  border-radius: var(--radius-kecil);
  background: var(--cad-panggung-primer92);
  background: color-mix(in srgb, var(--panggung) 92%, var(--primer));
  font-size: 13.5px;
}

.item-rincian .no-soal {
  font-family: var(--font-pixel);
  font-size: 12px;
  color: var(--teks-lembut);
  min-width: 22px;
}

/* Ikon, bukan hanya warna, sebagai penanda benar/salah (§10.8). */
.item-rincian .ikon-rincian { font-size: 15px; line-height: 1; }
.item-rincian[data-benar='true']  .ikon-rincian { color: var(--benar); }
.item-rincian[data-benar='false'] .ikon-rincian { color: var(--salah); }

.item-rincian .ket-rincian {
  margin-left: auto;
  color: var(--teks-lembut);
  font-size: 12px;
  text-align: right;
}

/* ---------------------------------------------------------------------
   10. Indikator sinkronisasi (§8)
   ---------------------------------------------------------------------
   Peneliti lapangan harus tahu ada data yang belum terkirim.
   --------------------------------------------------------------------- */
.lencana-antrean {
  position: fixed;
  right: var(--sp-3);
  top: calc(var(--sp-3) + env(safe-area-inset-top));
  z-index: 40;
  display: none;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border-radius: var(--radius-pil);
  background: #FFDE67;
  background: color-mix(in srgb, var(--aksen) 85%, var(--panggung));
  color: var(--tinta);
  border: 2px solid #C9A62E;
  font-size: var(--f-label);
  font-weight: 700;
}

.lencana-antrean[data-tampil='true'] { display: inline-flex; }
.lencana-antrean::before { content: '⏳'; }
