/* ============================================================================
   InggrisinYuk Kids — design system
   ----------------------------------------------------------------------------
   IDENTITAS WARNA (rationale singkat, dipakai konsisten di seluruh app):

   - Primary "Lagoon" teal (--brand-*): hue ~178°, sengaja jauh dari indigo-ungu
     (~253°) yang jadi warna utama app pembanding. Teal tua terbaca tenang &
     bisa dipercaya untuk orang tua, tapi tetap segar (bukan biru korporat).
   - Accent "Mango" (--sun-*): hue ~38°, komplemen hangat dari teal. Dipakai
     untuk hal yang menyenangkan: CTA di panel gelap, state aktif navigasi,
     bintang/penghargaan. Kombinasi laut + matahari terasa cerah & lokal,
     bukan gradasi ungu-biru generik.
   - Ink (--ink*): teal-slate #0F2E38, BUKAN navy keunguan. Karena warna teks
     ada di setiap pixel, ini pembeda paling murah & paling terasa.
   - Tanah "pasir hangat" (--paper #FCF4E6). Sebelumnya netral bertint mint
     (#F2F7F7) — satu keluarga hue dengan warna merek, dan itu persis yang
     bikin app-nya kebaca dingin/korporat. Pasir adalah tanah tempat lagoon
     duduk: teal-nya jadi kebaca "air", bukan "warna primer perusahaan", dan
     kehangatannya datang TANPA mengubah keputusan hue di PRD §11.
   - Warna kategori skill (--c-*) cuma dipakai DI DALAM konteks skill-nya,
     tidak pernah jadi warna chrome. Vocabulary dipindah dari indigo #6D4FE0
     (terlalu mirip pembanding) ke grape #8E3FA8.
   - Warna "tanah" Peta Level (--t1..--t6) juga sekunder: cuma hidup di dalam
     peta. Urutannya menceritakan jarak perjalanan (fajar → padang → lagoon →
     sungai → ngarai senja → puncak), bukan sekadar 6 warna berbeda.

   TIPOGRAFI: Baloo 2 (judul, ramah anak) + Nunito (body, x-height tinggi &
   mudah dibaca) — pasangan yang sudah dipakai di seluruh keluarga produk ini.
   ============================================================================ */

:root{
  /* --- Brand: Lagoon teal --- */
  --brand-900:#06333A;
  --brand-800:#0A4A4E;
  --brand-700:#0B6E6B;   /* aksi utama; teks putih di atasnya lolos AA */
  --brand-600:#0E8079;
  --brand-500:#12A398;   /* ikon/ilustrasi cerah */
  --brand-300:#7FD3CC;
  --brand-100:#D6F0EC;
  --brand-50:#EDF8F6;

  /* --- Accent: Mango --- */
  --sun-600:#8A4B04;     /* teks di atas latar mango muda */
  --sun-500:#FFB53D;
  --sun-400:#FFC862;
  --sun-100:#FFF1D8;

  --sun-300:#FFE0AE;

  /* --- Ink & netral (tanah pasir hangat, ink tetap teal-slate) --- */
  --ink:#0F2E38;
  --ink-2:#3C5C66;
  --ink-3:#6A868E;
  --ink-sand:#7A5A2E;     /* teks kecil di atas latar pasir/tanah peta */
  /* Permintaan user: cream muda-cerah (referensi warna dari luar app),
     dipakai utk background app (.viewport) & navbar (.tabbar). --nav-cream
     sengaja sedikit lebih dalam dari --paper (bukan token sama persis)
     supaya kartu navbar yang melayang tetap kebaca terpisah dari halaman
     di baliknya, walau keduanya 1 keluarga cream yang sama. */
  --paper:#FDF8ED;
  --nav-cream:#F8F1DF;
  --surface:#FFFFFF;
  --surface-2:#FFFBF3;    /* dasar kartu — putih hangat, bukan putih layar */
  --line:#EBDCC4;
  --muted:#F3E9D8;

  /* --- Semantik (non-punitive: "coba lagi", bukan "salah") --- */
  --ok:#1F7A43;
  --ok-bg:#E4F5EA;
  --try:#C7455B;
  --try-bg:#FCE9EC;

  /* --- Kategori skill --- */
  --c-vocab:#9B2FA8;  --c-vocab-bg:#F7E7FB;
  --c-listen:#1E6FB8; --c-listen-bg:#E3F0FC;
  --c-speak:#1F7A43;  --c-speak-bg:#E4F5EA;
  --c-gram:#B45B0C;   --c-gram-bg:#FDEEDC;
  /* Reading cuma dipakai First Placement Test (belum jadi skill Belajar
     tersendiri) — warna baru, beda dari 4 di atas. */
  --c-read:#C4488A;   --c-read-bg:#FBE7F1;

  /* --- Tanah Peta Level: 6 perhentian, dibaca sebagai perjalanan --- */
  --t1:#FFD9B0; --t1-soft:#FFF1E0; --t1-deep:#E09A55;  /* padang fajar     */
  --t2:#C9EBA8; --t2-soft:#EDF9E1; --t2-deep:#6FAE52;  /* kebun tunas      */
  --t3:#A6E5DB; --t3-soft:#E2F7F4; --t3-deep:#2F9F94;  /* tepi lagoon      */
  --t4:#B7DCF7; --t4-soft:#E6F3FD; --t4-deep:#4E97CE;  /* sungai berliku   */
  --t5:#FFCB90; --t5-soft:#FFEDD6; --t5-deep:#D98B3C;  /* ngarai senja     */
  --t6:#CEDBF2; --t6-soft:#EAF0FB; --t6-deep:#8194BD;  /* puncak berbintang*/

  /* --accent diganti per-skill lewat JS; default = brand */
  --accent:var(--brand-700);
  --accent-bg:var(--brand-100);

  /* --- Bentuk & elevasi (bayangan ikut dihangatkan: coklat pasir, bukan
         abu kebiruan — bayangan dingin di atas tanah hangat = kesan "layar",
         bayangan hangat = kesan "benda yang ditaruh di atas kertas") --- */
  --r-sm:12px; --r-md:18px; --r-lg:24px; --r-xl:30px; --r-pill:999px;
  --sh-1:0 1px 2px rgba(90,62,26,.06), 0 10px 22px -14px rgba(90,62,26,.34);
  --sh-2:0 2px 6px rgba(90,62,26,.08), 0 22px 40px -22px rgba(90,62,26,.45);
  --sh-lift:0 10px 22px -12px rgba(90,62,26,.55);
  --sh-rail:0 0 40px -10px rgba(6,51,58,.55);

  /* --- Skala tipografi --- */
  --fs-display:clamp(1.5rem, 1.18rem + 1.4vw, 2.125rem);
  --fs-h2:clamp(1.125rem, 1.02rem + .45vw, 1.375rem);
  --fs-h3:1.0625rem;
  --fs-body:0.9375rem;
  --fs-meta:0.8125rem;
  --fs-eyebrow:0.75rem;

  --font-head:'Baloo 2','Trebuchet MS',system-ui,sans-serif;
  --font-body:'Nunito',system-ui,-apple-system,'Segoe UI',sans-serif;

  /* --- Spasi (4-based) --- */
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:24px; --s6:32px; --s7:48px;
}

@media (min-width:768px){
  :root{ --fs-body:1rem; --fs-meta:0.875rem; }
}

/* ---------------------------------------------------------------- base ---- */
*{ box-sizing:border-box; }

html,body{ height:100%; }

body{
  margin:0;
  background:var(--paper);
  color:var(--ink);
  font-family:var(--font-body);
  font-size:var(--fs-body);
  font-weight:600;
  line-height:1.5;
  -webkit-font-smoothing:antialiased;
  -webkit-text-size-adjust:100%;
}

h1,h2,h3,h4,.baloo{ font-family:var(--font-head); font-weight:700; line-height:1.15; margin:0; }
p{ margin:0; }
button{ font-family:var(--font-body); font-size:1rem; cursor:pointer; border:none; background:none; color:inherit; }
[role="button"]{ cursor:pointer; }
ol,ul{ margin:0; padding:0; }
svg.ico{ width:24px; height:24px; flex:none; fill:none; stroke:currentColor; stroke-width:2; stroke-linecap:round; stroke-linejoin:round; }
svg.ico-sm{ width:18px; height:18px; }

.display{ font-family:var(--font-head); font-size:var(--fs-display); letter-spacing:-.01em; }
.h2{ font-family:var(--font-head); font-size:var(--fs-h2); }
.lede{ color:var(--ink-2); font-size:var(--fs-body); max-width:52ch; }
.eyebrow{
  display:inline-block; font-size:var(--fs-eyebrow); font-weight:800;
  letter-spacing:.1em; text-transform:uppercase; color:var(--ink-3);
}
/* Sub-judul di DALAM 1 kartu (Rapor: "Skor Belajar & Game" digabung dari 2
   kartu terpisah, permintaan user — kurangi jumlah kartu biar rapor tidak
   kepanjangan discroll, tapi grup "Skill Belajar"/"Game" tetap kebeda). */
.eyebrow.sub{ display:block; font-size:.75rem; }
.meta{ font-size:var(--fs-meta); color:var(--ink-3); font-weight:700; }

.hidden{ display:none !important; }
[hidden]{ display:none !important; }

.skip-link{
  position:absolute; left:8px; top:-60px; z-index:99;
  background:var(--brand-700); color:#fff; font-weight:800;
  padding:10px 16px; border-radius:var(--r-pill); text-decoration:none;
  transition:top .15s ease;
}
.skip-link:focus{ top:8px; }

/* ------------------------------------------------------------- shell ------ */
.shell{ display:flex; min-height:100vh; min-height:100dvh; }

/* Latar app = "siang di tepi lagoon": langit teal tipis di atas, cahaya
   matahari mango di kanan, lalu tanah pasir hangat. Butiran halus (lapisan
   dot terakhir) menahan gradasi besar supaya tidak kebaca seperti header SaaS
   — teksturnya nyaris tidak kelihatan, tapi bikin permukaannya terasa kertas. */
.viewport{
  flex:1; min-width:0; display:flex; flex-direction:column;
  background-color:var(--paper);
  background-image:
    radial-gradient(1000px 360px at 18% -10%, rgba(146,222,212,.55) 0%, rgba(146,222,212,0) 70%),
    radial-gradient(760px 300px at 104% -2%, rgba(255,197,98,.42) 0%, rgba(255,197,98,0) 66%),
    radial-gradient(rgba(163,127,74,.11) 1.3px, transparent 1.4px);
  background-size:auto, auto, 24px 24px;
}

.main{
  flex:1; width:100%; max-width:1180px; margin:0 auto;
  padding:var(--s4) var(--s4) 122px;
}
.main:focus{ outline:none; }

/* Layar login = halaman tersendiri (pola inggrisinyuk dewasa, PRD §16) —
   tanpa rail/topline/tabbar & tanpa header nama+level, full-bleed. Class
   ditoggle di app.ts (render()) berdasar state.screen. */
body.is-login .topline,
body.is-login .rail,
body.is-login .tabbar{ display:none; }
body.is-login .main{ max-width:none; padding:0; margin:0; }

/* Homepage marketing — sama pola dgn is-login (halaman berdiri sendiri,
   full-bleed), lihat `.landing-*` (bawah file) & `renderLandingPage` (app.ts). */
body.is-landing .topline,
body.is-landing .rail,
body.is-landing .tabbar{ display:none; }
body.is-landing .main{ max-width:none; padding:0; margin:0; }

/* First Placement Test — halaman berdiri sendiri juga (permintaan user):
   tanpa rail/topline/tabbar (tidak ada jalan keluar diam-diam lewat nav),
   TAPI beda dari is-login, `.main` TETAP pakai padding/max-width normal
   karena layar ini masih pakai shell .act-head + .card standar (activity),
   bukan layout full-bleed sendiri kayak login. */
body.is-placement-test .topline,
body.is-placement-test .rail,
body.is-placement-test .tabbar{ display:none; }

/* Main 1 Raja Game Hub (screen 'gamePlay') — halaman berdiri sendiri juga
   (permintaan user: "ketika klik icon game maka ke halaman baru... sehingga
   navbar dibawahnya hilang"), pola SAMA PERSIS is-placement-test. Class
   ditoggle app.ts (render()) berdasar state.screen. */
body.is-game-play .topline,
body.is-game-play .rail,
body.is-game-play .tabbar,
/* Markas Raja / Tantangan Bos (screen 'boss') — halaman berdiri sendiri jg
   (permintaan user: "ketika test di markas raja maka open halaman baru
   seperti fitur game"), pola SAMA PERSIS is-game-play/is-placement-test. */
body.is-boss-play .topline,
body.is-boss-play .rail,
body.is-boss-play .tabbar{ display:none; }

/* 🔒 Latar kelap-kelip bintang Map Kerajaan tiap Raja (`GAME_STAR_FIELD`,
   scenery.ts) — permintaan user "analisis /game/raja-kata... background
   nya ditambahkan bintang, emot lucu... yang menarik anak". **Koreksi**:
   awalnya disisipkan di `app.ts renderGamePlay()` SEBELUM `.raja-stage`,
   TAPI `.raja-stage`/`#rajaStage` adalah `.card` OPAQUE yang membungkus
   SEMUA isi Map Kerajaan (note-card+grid+footer) — bintang di luar kartu
   itu ketutup total, TIDAK PERNAH kelihatan. SEKARANG disisipkan DI DALAM
   `renderMap()` tiap `games/*.ts`, dibungkus `<div class="raja-map-wrap">`
   BARU (bukan `.raja-stage` langsung, lihat alasan di bawah) supaya
   `.game-star-field`'s `inset:0` sizing pas ke area Map SAJA. Konten asli
   (note-card/grid/footer) dikasih `z-index:1` eksplisit supaya SELALU di
   atas bintang (pola SAMA `.trail-scene`+`.raja-stop-inner` di tempat lain
   — elemen positioned TANPA z-index eksplisit tetap kelukis DI ATAS elemen
   non-positioned biasa walau urutan DOM-nya lebih dulu, jadi z-index:1
   wajib bukan opsional). */
/* `.raja-map-wrap` (BUKAN `.raja-stage` langsung — `.raja-stage`/#rajaStage
   dipakai lintas SEMUA state game termasuk ronde main aktif, sengaja TIDAK
   disentuh sama sekali biar 0 risiko ke layar lain) — wrapper lokal SEKALI
   PAKAI, cuma ada selama `renderMap()` sedang dirender. */
.raja-map-wrap{ position:relative; }
.raja-map-wrap > .game-progress-card,
.raja-map-wrap > .raja-grid,
.raja-map-wrap > .journey-map,
.raja-map-wrap > .game-howto-title,
.raja-map-wrap > .game-howto-card{ position:relative; z-index:1; }
.game-star-field{
  position:absolute; inset:0; overflow:hidden; border-radius:var(--r-lg);
  z-index:0; pointer-events:none;
}
.game-star{
  position:absolute; display:block; opacity:.4;
  animation:starTwinkle 2.6s ease-in-out infinite;
}
.game-star svg{ width:100%; height:100%; display:block; }
@keyframes starTwinkle{
  0%,100%{ transform:scale(.8) rotate(-6deg); opacity:.28; }
  50%{ transform:scale(1.15) rotate(6deg); opacity:.75; }
}

/* --------------------------------------------------------- brand mark ---- */
.brand{ display:flex; align-items:center; gap:10px; text-decoration:none; color:inherit; }
.brand-mark{ width:38px; height:38px; flex:none; border-radius:13px; }
.brand-word{ font-family:var(--font-head); font-weight:800; font-size:1.0625rem; line-height:1.05; letter-spacing:-.01em; }
.brand-word small{ display:block; font-family:var(--font-body); font-size:.625rem; font-weight:800; letter-spacing:.16em; text-transform:uppercase; opacity:.7; }

/* ------------------------------------------------- topline (mobile) ------ */
.topline{
  position:sticky; top:0; z-index:20;
  display:flex; align-items:center; justify-content:space-between; gap:var(--s3);
  padding:10px var(--s4);
  background:rgba(255,255,255,.86);
  backdrop-filter:blur(10px);
  border-bottom:1px solid var(--line);
}

.level-chip{
  display:inline-flex; align-items:center; gap:8px;
  background:var(--brand-50); border:1px solid var(--brand-100);
  color:var(--brand-700); border-radius:var(--r-pill);
  padding:6px 12px; font-weight:800; font-size:var(--fs-meta);
}
.level-chip b{ font-family:var(--font-head); }

/* ------------------------------------------------------ rail (desktop) --- */
.rail{ display:none; }

.rail-nav{ display:flex; flex-direction:column; gap:6px; }

.nav-item{
  display:flex; align-items:center; gap:12px;
  width:100%; text-align:left;
  padding:11px 12px; border-radius:var(--r-md);
  color:#B6DED9; font-weight:800; font-size:.9375rem;
  transition:background .15s ease, color .15s ease;
}
.nav-item .nav-ico{
  width:36px; height:36px; border-radius:12px; flex:none;
  display:grid; place-items:center;
  background:rgba(255,255,255,.08);
  transition:background .15s ease, color .15s ease, transform .18s ease;
}
/* Ikon navigasi dibesarkan dikit dari ukuran viewBox asli 24px (permintaan
   user) — di-scope ke dalam badge nav SAJA (bukan svg.ico global) supaya
   ikon utility lain (back/chevron/check/dst) tidak ikut membesar. */
.nav-ico svg.ico{ width:28px; height:28px; }
.nav-item[aria-current="page"]{ background:rgba(255,255,255,.12); color:#fff; }
/* Ikon nav sekarang berwarna tetap (lihat icons.ts) — badge di belakangnya
   cuma "pop" halus (bukan lagi ganti warna) saat item aktif. */
.nav-item[aria-current="page"] .nav-ico{ background:rgba(255,255,255,.22); transform:scale(1.08); }

/* ------------------------------------------------- tabbar (mobile) ------- */
/* Kartu navigasi melayang, beige (permintaan user, referensi navbar app
   kompetitor) — TAPI pill aktifnya mango (--sun-100/600), BUKAN ungu/lavender
   spt referensi, biar tetap sesuai identitas warna app ini (lihat rationale
   di atas file ini). */
.tabbar{
  position:fixed; left:12px; right:12px; bottom:calc(10px + env(safe-area-inset-bottom, 0px));
  z-index:30;
  display:flex; gap:2px;
  padding:7px; border-radius:24px;
  background:var(--nav-cream);
  border:1px solid var(--line);
  box-shadow:var(--sh-2);
}
.tab{
  flex:1; display:flex; flex-direction:column; align-items:center; gap:2px;
  padding:7px 4px 6px; border-radius:17px;
  color:var(--ink-3); font-size:.75rem; font-weight:800; letter-spacing:.01em;
  transition:background .15s ease, color .15s ease;
}
.tab .nav-ico{
  width:33px; height:33px; display:grid; place-items:center;
  transition:transform .15s ease;
}
.tab[aria-current="page"]{ background:var(--sun-100); color:var(--sun-600); }
.tab[aria-current="page"] .nav-ico{ transform:scale(1.1); }

/* ---------------------------------------------------------- breadcrumb --- */
.crumb{
  display:flex; flex-wrap:wrap; align-items:center; gap:5px;
  font-size:var(--fs-meta); font-weight:800; color:var(--ink-3);
  margin-bottom:var(--s3);
}
.crumb .sep{ opacity:.45; }
.crumb .current{ color:var(--ink); }

/* --------------------------------------------------------- primitives ---- */
.card{
  background:linear-gradient(180deg,var(--surface) 0%, var(--surface-2) 100%);
  border:1px solid var(--line);
  border-radius:var(--r-lg);
  box-shadow:var(--sh-1);
  padding:var(--s5) var(--s4);
}

/* Kilau kecil di pojok kiri-atas tiap badge bulat/kotak-bulat. Ini yang bikin
   ikon terasa "benda" (mainan/stiker) bukan kotak warna — kualitas visual yang
   diambil dari referensi glossy, tapi murni CSS: tanpa aset gambar, tanpa
   metrik/fitur baru. Semua badge memakai satu resep yang sama. */
.gloss,
.jump .ic,
.skill-card .ic,
.xp-badge,
.spark-art,
.trail-medallion,
.mini-play,
.stat-ic{ position:relative; overflow:hidden; isolation:isolate; }

/* z-index:-1 + isolation:isolate = kilau duduk DI ATAS latar badge tapi DI
   BAWAH emoji/ikonnya, jadi isinya tidak pernah ikut memudar. */
.gloss::before,
.jump .ic::before,
.skill-card .ic::before,
.xp-badge::before,
.spark-art::before,
.trail-medallion::before,
.mini-play::before,
.stat-ic::before{
  content:''; position:absolute; left:9%; top:4%; width:64%; height:44%;
  border-radius:50%; pointer-events:none; z-index:-1;
  background:linear-gradient(180deg, rgba(255,255,255,.85), rgba(255,255,255,0));
}
@media (min-width:768px){ .card{ padding:var(--s5); } }

.card-head{ display:flex; align-items:center; gap:10px; margin-bottom:var(--s3); }
.card-title{ font-family:var(--font-head); font-size:var(--fs-h3); }

/* Baris label+nilai kartu "Informasi Akun" (Pengaturan) — pola sama dgn
   eyebrow+judul kartu lain, cuma diulang 2x kecil dalam 1 kartu. */
.acc-info-row{ font-size:var(--fs-meta); color:var(--ink-2); font-weight:700; }
.acc-info-row + .acc-info-row{ margin-top:6px; }
.acc-info-row b{ color:var(--ink); font-weight:800; }

.stack{ display:flex; flex-direction:column; gap:var(--s4); }
.two-col{ display:grid; gap:var(--s4); align-items:start; }

.cta{
  display:inline-flex; align-items:center; justify-content:center; gap:8px;
  background:var(--sun-500); color:var(--brand-900);
  font-family:var(--font-head); font-weight:700; font-size:1rem;
  padding:12px 22px; border-radius:var(--r-pill);
  box-shadow:0 8px 18px -10px rgba(138,75,4,.7);
  transition:transform .12s ease, filter .15s ease;
}
.cta:active{ transform:translateY(1px) scale(.985); }

.tag{
  display:inline-flex; align-items:center; gap:6px;
  background:var(--muted); color:var(--ink-2);
  border-radius:var(--r-pill); padding:4px 10px;
  font-size:var(--fs-eyebrow); font-weight:800; letter-spacing:.04em;
}
.tag.ok{ background:var(--ok-bg); color:var(--ok); }
.tag.accent{ background:var(--accent-bg); color:var(--accent); }

.iconbtn{
  width:42px; height:42px; flex:none; border-radius:14px;
  display:grid; place-items:center;
  background:var(--surface); border:1px solid var(--line);
  color:var(--ink); box-shadow:var(--sh-1);
  transition:transform .12s ease, background .15s ease;
}
.iconbtn:active{ transform:scale(.94); }

/* ------------------------------------------------------------- beranda --- */
.greet{ margin-bottom:var(--s4); }
.greet .display{ margin-bottom:6px; }

/* Kartu "lanjutkan" di Beranda = panorama kecil, bukan panel gelap: langit
   lagoon di atas, matahari mango di kanan, awan, lalu siluet bukit di kaki
   kartu. Isi & tombolnya tidak berubah — cuma latarnya yang sekarang bercerita
   "kamu lagi di suatu tempat", selaras dengan Peta Level.
   `--spark-accent` (permintaan user, Menu Belajar: "warna sesuaikan dengan
   warna modulnya") — di-set INLINE per elemen ke warna skill tujuan
   (`SKILL_META[key].accent`, app.ts), gradiennya dihitung dari 1 warna itu
   pakai color-mix() jadi tidak perlu 5 blok CSS per-skill. Fallback ke
   teal/brand lama kalau variabelnya tidak di-set (mis. Beranda, atau kartu
   "semua materi tuntas" yang tidak menunjuk skill tertentu). */
.spark{
  --spark-base: var(--spark-accent, var(--brand-600));
  position:relative; overflow:hidden;
  display:flex; align-items:center; gap:var(--s4);
  background:linear-gradient(176deg,
    color-mix(in srgb, var(--spark-base) 55%, white) 0%,
    var(--spark-base) 38%,
    color-mix(in srgb, var(--spark-base) 100%, black 32%) 100%);
  color:#fff; border-radius:var(--r-xl);
  padding:var(--s5) var(--s4) calc(var(--s5) + 10px);
  box-shadow:var(--sh-2);
}
.spark::after{
  content:''; position:absolute; right:-120px; top:-160px;
  width:420px; height:420px; border-radius:50%;
  background:radial-gradient(circle, rgba(255,181,61,.42) 0%, rgba(255,181,61,.18) 42%, rgba(255,181,61,0) 72%);
  pointer-events:none;
}
.spark .trail-hills{ height:54px; color:var(--brand-900); opacity:.3; }
/* Awan dipakai di 3 panorama (Beranda, kepala Peta Level, arena Bos): posisi &
   warnanya diatur per-tempat, bentuknya satu SVG yang sama (scenery.ts). */
.cloud{ position:absolute; pointer-events:none; line-height:0; }
.cloud .cloud-art{ width:100%; height:auto; display:block; }

.spark .cloud{ width:104px; color:#fff; opacity:.16; }
.spark .cloud.c1{ left:6%; top:14px; }
.spark .cloud.c2{ left:52%; top:-6px; width:76px; opacity:.11; }
.spark-body{ position:relative; z-index:1; flex:1; min-width:0; }
.spark .eyebrow{ color:var(--sun-300); }
.spark-title{ font-family:var(--font-head); font-size:clamp(1.25rem,1.05rem + 1vw,1.75rem); margin:4px 0 4px; }
.spark-sub{ color:rgba(255,255,255,.86); font-size:var(--fs-meta); font-weight:700; margin-bottom:var(--s4); }

/* Strip progres di dalam kartu spark (Menu Belajar, permintaan user: gabung
   progress+lanjutkan jadi 1 section, DI BAWAH tombol, & lebih slim daripada
   versi awal) — track+label sebaris (bukan bar lalu label di bawahnya lagi)
   supaya tetap ringkas, track translusen tipis di atas gradien, isi bar
   pakai warna mango yang sama dgn glow matahari di kartu ini supaya kebaca
   "satu keluarga warna", bukan tempelan komponen putih lain. */
.spark-progress{ display:flex; align-items:center; gap:8px; margin-top:14px; max-width:260px; }
.spark-progress-track{
  flex:1; height:4px; border-radius:var(--r-pill); background:rgba(255,255,255,.22); overflow:hidden;
}
.spark-progress-fill{
  height:100%; border-radius:var(--r-pill); min-width:3px;
  background:linear-gradient(90deg,var(--sun-400),var(--sun-500));
}
.spark-progress-label{ flex:none; font-size:12px; font-weight:800; color:rgba(255,255,255,.86); letter-spacing:.02em; white-space:nowrap; }
.spark-art{
  position:relative; z-index:1; flex:none;
  width:82px; height:82px; border-radius:50%;
  display:grid; place-items:center; font-size:40px;
  background:radial-gradient(circle at 34% 26%, var(--sun-400) 0%, var(--sun-500) 58%, #EF9C24 100%);
  box-shadow:0 0 0 6px rgba(255,255,255,.14), 0 14px 26px -12px rgba(6,51,58,.85);
}
/* Beranda/Menu Belajar SELALU isi `.spark-art` dgn 1 emoji polos (font-size
   di atas cukup) — kartu hero Game Hub (app.ts renderGame()) SEKARANG bisa
   isi gambar asli Raja-nya sendiri, jadi <img> butuh sizing eksplisit
   (inert di 2 layar lain krn mereka tidak pernah taruh <img> di sini). */
.spark-art img{ width:100%; height:100%; object-fit:cover; border-radius:50%; display:block; }

/* Versi slim (permintaan user) — padding & ukuran dikecilkan, dipakai di
   kartu "lanjutkan" Beranda MAUPUN kartu serupa di Menu Belajar (progress
   bar-nya, `.spark-progress`, tetap muat di bawah tombol versi compact ini). */
.spark.compact{ padding:var(--s4) var(--s4) calc(var(--s4) + 8px); gap:var(--s3); }
.spark.compact .spark-title{ font-size:1.1875rem; margin:0 0 3px; }
.spark.compact .spark-sub{ margin-bottom:var(--s3); }
.spark.compact .cta{ padding:10px 20px; font-size:.9375rem; }
/* Dibesarkan (permintaan user, samakan dgn icon hero Game Hub) — awalnya
   60px/28px cuma di-scope `.game-hero` (Game Hub), TAPI user lanjut minta
   Beranda & Menu Belajar (yang sama-sama reuse `.spark.compact` polos ini)
   ikut dibesarkan juga — jadi sekarang base rule-nya langsung, `.game-hero`
   di markup (`renderGame()`) jadi tidak perlu override lagi (boleh dibiarkan
   nempel di class list, harmless, tapi CSS-nya dihapus krn sudah redundan). */
.spark.compact .spark-art{ width:76px; height:76px; font-size:36px; box-shadow:0 0 0 4px rgba(255,255,255,.14), 0 10px 20px -10px rgba(6,51,58,.85); }

/* Board "Semua Markas" (permintaan user: "tiru yang relevan" dari section
   Jalur Petualangan Beranda) — REUSE PERSIS `.map-board`/`.map-sky` (sky
   gradien+matahari+awan) yg sudah ada, TANPA warna terrain di kartunya
   sendiri (`.raja-card` TETAP netral putih/krem — keputusan riset Giggle
   Academy yg sudah dikunci sebelumnya, lihat komentar `.raja-card.map-card`
   di atas, SENGAJA tidak dibalik). `.spark` (hero) sendiri TIDAK punya
   margin-bottom (dipakai jg di Beranda/Menu Belajar yg spacing-nya sudah pas
   lewat `.stack`/gap induk lain), jadi jarak ke board ini digantung di
   `.game-hub-board` SAJA. */
.game-hub-board{ margin-top:var(--s4); }
/* 🔒 Revisi (permintaan user "update sky... seperti semula dimana sedikit
   lebih slim") — `min-height`+flex-center (sempat dipasang saat teksnya
   cuma 1 baris, biar matahari tidak kepotong) DICABUT lagi sekarang teksnya
   balik 2 baris (h2+p, "Pilih Markasmu!"/"Banyak game seru...") — kontennya
   sendiri sudah cukup tinggi buat matahari tidak kepotong, jadi min-height
   artifisial itu SEKARANG cuma nambah ruang kosong yang bikin band-nya
   kerasa lebih tebal dari perlu. Padding jg dikurangi dikit (var(--s3) di
   ATAS, bukan var(--s4)) biar lebih ramping/slim, konsisten permintaan. */
.game-hub-board .map-sky{ padding:var(--s3) var(--s4); }
/* Band-nya sekarang pendek (68px, dulu 96px) — matahari (`.map-sun`, 104px)
   jadi relatif lebih besar drpd tinggi band, teks h2/p bisa numpuk visual
   dgn matahari di sisi kanan kalau dibiarkan selebar band penuh. `max-width`
   nahan teks di sisi kiri, jauh dari zona matahari, tanpa perlu naikkan
   tinggi band lagi (itu yang justru mau dihindari — permintaan "lebih slim"). */
.game-hub-board .map-sky h2,
.game-hub-board .map-sky p{ max-width:62%; }
.game-hub-board .raja-grid{ padding:var(--s4); }

.jump-grid{ display:grid; grid-template-columns:1fr 1fr; gap:var(--s3); }
.jump{
  display:flex; align-items:center; gap:10px;
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-md);
  padding:12px; box-shadow:var(--sh-1); text-align:left;
  transition:transform .12s ease, border-color .15s ease;
}
.jump:active{ transform:scale(.97); }
.jump .ic{
  width:42px; height:42px; border-radius:13px; flex:none;
  display:grid; place-items:center; font-size:21px;
}
.jump .txt{ min-width:0; }
.jump .txt b{ display:block; font-family:var(--font-head); font-size:.9375rem; }
.jump .txt span{ display:block; font-size:.75rem; font-weight:800; color:var(--ink-3); }

.level-card .lvl-name{ font-family:var(--font-head); font-size:1.25rem; display:flex; align-items:center; gap:8px; }
.level-card .lvl-meta{ font-size:var(--fs-meta); color:var(--ink-3); font-weight:700; margin-top:2px; }
.progress-track{
  height:10px; border-radius:var(--r-pill); background:var(--muted);
  margin-top:var(--s4); overflow:hidden;
  /* Garis tepi tipis supaya track tetap jelas kebaca sebagai "bar" walau
     isinya masih 0% (--muted nyaris menyatu dengan latar kartu tanpa ini). */
  box-shadow:inset 0 0 0 1px var(--line);
}
.progress-fill{
  height:100%; border-radius:var(--r-pill); min-width:3px;
  background:linear-gradient(90deg,var(--brand-500),var(--brand-700));
}

/* Heading "Level X ... Y% menuju Level berikutnya" di atas progress-track —
   dipisah jadi baris tersendiri (nama level besar, persentase warna brand)
   supaya progress bar-nya jelas kebaca sebagai fitur, bukan garis dekoratif
   tipis yang gampang terlewat. */
.level-progress-head{
  display:flex; align-items:baseline; justify-content:space-between; gap:var(--s2); flex-wrap:wrap;
  margin-top:var(--s3);
}
.level-progress-name{ font-family:var(--font-head); font-size:1.0625rem; color:var(--ink); }
.level-progress-pct{ font-size:var(--fs-meta); font-weight:800; color:var(--brand-700); }
.star-row{ font-size:22px; letter-spacing:3px; line-height:1.3; }

/* XP — angka pertumbuhan yang cuma pernah naik (progress.ts). Sengaja dikasih
   warna mango cerah ("energi"), BUKAN merah/hijau — supaya tidak kebaca
   seperti health bar yang bisa berkurang. */
.xp-row{
  display:flex; align-items:center; gap:10px;
  margin-top:var(--s4); padding-top:var(--s4); border-top:1px solid var(--line);
}
.xp-badge{
  width:40px; height:40px; flex:none; border-radius:50%;
  display:grid; place-items:center; font-size:19px;
  background:radial-gradient(circle at 34% 26%, var(--sun-400) 0%, var(--sun-500) 68%, #EFA22C 100%);
  color:#6B3B02;
  box-shadow:0 8px 16px -9px rgba(160,100,10,.95), inset 0 0 0 2px rgba(255,255,255,.4);
}
.xp-text{ min-width:0; display:flex; flex-direction:column; }
.xp-text b{ font-family:var(--font-head); font-size:1rem; color:var(--ink); }
.xp-text span{ font-size:.75rem; font-weight:700; color:var(--ink-3); }
.level-card .ghost-btn{ margin-top:var(--s4); }

/* Progres Harian — SENGAJA bukan streak (progress.ts): 7 kotak lepas, tanpa
   sambungan/garis "rantai" antar hari, tanpa ikon api. Hari aktif ditandai
   isi mango; hari ini dikasih outline tipis biar kebaca "sekarang" tanpa
   menonjolkan hari kosong sebagai kegagalan. */
.day-row{ display:flex; gap:6px; }
.day-chip{
  flex:1; text-align:center; padding:8px 2px; border-radius:var(--r-md);
  background:var(--muted); color:var(--ink-3);
  font-size:.75rem; font-weight:800; letter-spacing:.01em;
}
.day-chip.is-active{ background:var(--sun-400); color:#6B3B02; }
.day-chip.is-today{ box-shadow:inset 0 0 0 2px var(--brand-500); }

.note-card{ background:var(--sun-100); border-color:#F6E2BE; }
.note-card .card-title{ color:var(--sun-600); }
.note-card p{ font-size:var(--fs-meta); color:#6C4A16; font-weight:700; }

/* ---------------------------------------------------------- menu belajar - */
.skill-grid{ display:grid; gap:var(--s3); }
.skill-card{
  position:relative;
  display:flex; align-items:center; gap:var(--s3);
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-lg);
  padding:var(--s4); box-shadow:var(--sh-1); text-align:left; width:100%;
  transition:transform .12s ease, box-shadow .15s ease, border-color .15s ease;
}
.skill-card:active{ transform:scale(.985); }
/* Persentase materi selesai per modul (permintaan user) — pojok kanan atas,
   netral (bukan warna modul) supaya tidak baku hantam dgn `.ic`; hijau "ok"
   cuma begitu genap 100% (perayaan kecil, konsisten dgn .topic-card.done). */
.skill-pct{
  position:absolute; top:10px; right:12px;
  font-size:12px; font-weight:800; letter-spacing:.02em;
  padding:3px 8px; border-radius:var(--r-pill);
  background:var(--muted); color:var(--ink-3);
}
.skill-pct.done{ background:var(--ok-bg); color:var(--ok); }
/* Skor Tiap Skill di Rapor (1–5 bintang, bukan %) — pill sama posisinya
   dgn .skill-pct polos, cuma font lebih besar & padding lebih lega supaya
   5 karakter bintang tidak sesak (beda dari angka "80%" yang cuma 3-4 char). */
.skill-pct.stars{ font-size:13px; letter-spacing:1px; padding:4px 9px; }
.skill-card .ic{
  width:54px; height:54px; border-radius:17px; flex:none;
  display:grid; place-items:center; font-size:26px;
}
.skill-card .body{ flex:1; min-width:0; }
.skill-card h3{ font-size:var(--fs-h3); margin-bottom:2px; }
.skill-card p{ font-size:var(--fs-meta); color:var(--ink-3); font-weight:700; }
.skill-card .row{ display:flex; flex-wrap:wrap; gap:6px; margin-top:8px; }
.skill-card .chev{ color:var(--ink-3); flex:none; }

.howto{ list-style:none; margin:var(--s3) 0 0; padding:0; }
.howto li{
  display:flex; gap:12px; align-items:flex-start; padding:10px 0;
  border-top:1px solid var(--line); font-size:var(--fs-meta); font-weight:700; color:var(--ink-2);
}
.howto li:first-child{ border-top:none; }
.howto b{ display:block; font-family:var(--font-head); font-size:.9375rem; color:var(--ink); }
.howto .n{
  width:26px; height:26px; border-radius:50%; flex:none;
  display:grid; place-items:center; font-family:var(--font-head); font-weight:700;
  background:var(--brand-100); color:var(--brand-700); font-size:.8125rem;
}

/* Kartu ajakan Tantangan Bos di bawah grid skill — sengaja beda rupa dari
   .spark (Beranda) supaya tidak kebaca sebagai CTA utama duplikat, tapi tetap
   "besar & seru" (Mario/Pokémon gym-leader energy, bukan tegang). */
.boss-teaser{
  position:relative; overflow:hidden;
  margin-top:var(--s4); display:flex; align-items:center; gap:var(--s4);
  background:linear-gradient(168deg,#FFF6E4 0%, var(--sun-100) 48%, #FFE2B4 100%);
  border:1px solid #F3DDA8; border-radius:var(--r-xl);
  padding:var(--s5) var(--s4) calc(var(--s5) + 8px); box-shadow:var(--sh-1);
}
.boss-teaser .trail-hills{ color:#E09A38; opacity:.3; }
.boss-teaser-body{ min-width:0; position:relative; z-index:1; }
.boss-teaser-body .eyebrow{ color:var(--sun-600); }
.boss-teaser-body .cta{ margin-top:var(--s3); }

/* Sunburst = "momen besar" versi kid-friendly: sinar matahari yang berputar
   pelan di belakang wajah singa, bukan aura gelap/api. Dipakai persis di 3
   tempat puncak cerita — ajakan Tantangan Bos, kepala arena, dan layar menang —
   supaya momen itu punya bahasa visual sendiri yang konsisten. */
.sunburst{ position:relative; flex:none; display:grid; place-items:center; width:96px; height:96px; }
.sunburst::before{
  content:''; position:absolute; inset:-10px; border-radius:50%; pointer-events:none;
  background:repeating-conic-gradient(from 0deg, rgba(255,168,36,.5) 0deg 6deg, rgba(255,168,36,0) 6deg 17deg);
  -webkit-mask-image:radial-gradient(circle, rgba(0,0,0,0) 30%, #000 46%, rgba(0,0,0,0) 74%);
  mask-image:radial-gradient(circle, rgba(0,0,0,0) 30%, #000 46%, rgba(0,0,0,0) 74%);
  animation:sunSpin 26s linear infinite;
}
@keyframes sunSpin{ to{ transform:rotate(360deg); } }
.sunburst .face{
  position:relative; width:70px; height:70px; border-radius:50%;
  display:grid; place-items:center; font-size:34px; line-height:1;
  background:radial-gradient(circle at 34% 26%, #FFF2D6 0%, var(--sun-400) 58%, #F0A22C 100%);
  box-shadow:0 0 0 5px rgba(255,255,255,.75), var(--sh-lift);
}
/* Mahkota kecil di kepala singa = penanda "bos" (Mario/Pokémon gym-leader),
   bukan ikon ancaman. Tetap 🦁 — maskot yang sama di seluruh app. */
.sunburst .crown{
  position:absolute; top:0; left:50%; transform:translateX(-50%) rotate(-9deg);
  font-size:22px; line-height:1; filter:drop-shadow(0 3px 4px rgba(120,80,10,.45));
}
.sunburst.lg{ width:136px; height:136px; }
.sunburst.lg .face{ width:98px; height:98px; font-size:48px; }
.sunburst.lg .crown{ font-size:32px; }

/* Kepala arena Tantangan Bos — menempel jadi satu papan dengan panggungnya.
   Isinya jujur: siapa bosnya + 4 babak yang akan dilewati (anak tahu apa yang
   datang = lebih tenang, sesuai aturan kid-friendly, bukan kejutan menegangkan). */
.boss-arena{
  position:relative; overflow:hidden;
  display:flex; align-items:center; gap:var(--s4); flex-wrap:wrap;
  background:linear-gradient(178deg,#FFEFD1 0%, #FFDFAE 62%, #FFD296 100%);
  border:1px solid #F0D398; border-bottom:none;
  border-radius:var(--r-xl) var(--r-xl) 0 0;
  padding:var(--s5) var(--s4) calc(var(--s5) + 10px);
}
.boss-arena .trail-hills{ color:#DE9532; opacity:.32; }
.boss-arena .cloud{ color:#fff; opacity:.55; width:86px; left:-12px; top:12px; }
.boss-arena-body{ position:relative; z-index:1; flex:1; min-width:210px; }
.boss-arena-body h2{ font-family:var(--font-head); font-size:var(--fs-h2); }
.boss-arena-body p{ font-size:var(--fs-meta); font-weight:700; color:#7A4A08; margin-top:3px; }
/* ------------------------------------------------------- daftar materi --- */
.screen-head{ display:flex; align-items:center; gap:var(--s3); margin-bottom:var(--s4); }
.screen-head .txt{ min-width:0; flex:1; }
.screen-head .txt h1{ font-size:var(--fs-h2); display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.screen-head .txt p{ font-size:var(--fs-meta); color:var(--ink-3); font-weight:700; margin-top:2px; }

/* Kepala Daftar Materi (permintaan user: gabung judul+progress jadi 1
   section, bukan kartu terpisah di bawahnya). Modifier KHUSUS di sini,
   bukan ubah `.screen-head` langsung, supaya layar lain yang pakai class
   yang sama (Peta Level, Pengaturan, dst) tidak ikut kepengaruh.
   Header ini sendiri TIDAK sticky — dia scroll away seperti konten biasa;
   yang "tetap ada"/slim pas discroll adalah `.topics-compact-bar` di bawah
   (pola project inggrisinyuk: 2 elemen terpisah, bukan 1 elemen yg
   menyusut). Margin negatif = bleed ke tepi `.main` (var(--s4) di app.ts)
   biar kerasa jadi 1 strip berwarna, bukan konten biasa. */
.topics-head{
  background:var(--paper);
  margin:calc(-1 * var(--s4)) calc(-1 * var(--s4)) var(--s4);
  padding:var(--s4) var(--s4) var(--s3);
  border-bottom:1px solid var(--line);
  /* Override `align-items:center` bawaan `.screen-head` — dgn tombol Cara
     Main sekarang jadi flex child ke-3 (lihat `.topics-head .cara-main-btn`
     di bawah), tombol kembali WAJIB ikut nempel di ATAS jg, bukan lagi
     ditengahkan relatif ke tinggi `.txt` (yang bisa berubah-ubah tergantung
     panjang tagline/apakah badge "N materi" wrap ke baris baru). */
  align-items:flex-start;
}

/* Tombol "Cara Main" digabung ke header (permintaan user: "posisinya kanan
   atas sejajar dengan Vocabulary 10 materi") — flex child KETIGA di
   `.topics-head` (setelah tombol kembali & `.txt`); `align-self:flex-start`
   supaya nempel di baris ATAS (sejajar judul h1), BUKAN ikut
   `align-items:center` bawaan `.screen-head` yang akan menengahkannya ke
   seluruh tinggi blok (termasuk progress bar di bawah judul). `.txt` sudah
   `flex:1` (isi semua sisa ruang), jadi tombol ini otomatis mepet ke kanan
   tanpa perlu `margin-left:auto`. Ukuran tetap ramping (permintaan user
   sebelumnya: "slim dan rapat tapi tetap elegan"). */
.topics-head .cara-main-btn{
  align-self:flex-start; flex:none;
  display:inline-flex; align-items:center; gap:5px;
  width:auto; margin-top:0; white-space:nowrap;
  padding:7px 14px; font-size:var(--fs-meta);
}

/* Dropdown level sendirian di bawah header (Cara Main sudah pindah ke atas,
   lihat `.topics-head .cara-main-btn`) — kalau `renderLevelSwitcher()`
   balikin string kosong (cuma 1 level yang bisa dijelajahi), `app.ts` SAMA
   SEKALI TIDAK merender div ini, jadi tidak ada baris kosong yang makan
   `margin-bottom` tanpa isi. Padding pill-nya sendiri SEKARANG di base rule
   `.level-select-wrap` (bukan di-scope ke sini lagi) — permintaan user:
   dropdown Menu Belajar disamakan persis dgn yang di sini. */
.topics-toolbar{ margin-bottom:var(--s3); }

/* Bar ringkas (permintaan user: "jadi slim/collapse pas discroll", pola
   dari app/dashboard/[module]/page.tsx project inggrisinyuk) — fixed,
   disembunyikan default (translateY+opacity, BUKAN display:none — biar
   transisinya mulus), muncul begitu `.topics-head` besar sudah lewat scroll
   (app.ts `wireTopicsCompactBar`, ambang scrollY>140). `top` ikut tinggi
   `.topline` (sticky, mobile-only), di ≥768px geser ke kanan `.rail`
   (width:92px) krn topline diganti rail di situ. */
.topics-compact-bar{
  position:fixed; top:59px; left:0; right:0; z-index:25;
  background:var(--surface); border-bottom:1px solid var(--line); box-shadow:var(--sh-1);
  transform:translateY(-100%); opacity:0; pointer-events:none;
  transition:transform .18s ease, opacity .18s ease;
}
.topics-compact-bar.is-visible{ transform:translateY(0); opacity:1; pointer-events:auto; }
.topics-compact-inner{
  max-width:1180px; margin:0 auto;
  display:flex; align-items:center; gap:var(--s3);
  padding:8px var(--s4);
}
.topics-compact-label{ flex:1; font-family:var(--font-head); font-weight:800; font-size:.9375rem; text-align:center; }
.topics-compact-pct{ flex:none; font-weight:800; font-size:.875rem; color:var(--brand-700); }
@media (min-width:768px){
  .topics-compact-bar{ top:0; left:92px; }
}

.topic-grid{ display:grid; gap:var(--s3); }
.topic-card{
  display:flex; align-items:center; gap:var(--s3); text-align:left; width:100%;
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-lg);
  padding:14px var(--s4); box-shadow:var(--sh-1);
  transition:transform .12s ease, border-color .15s ease;
}
.topic-card:active{ transform:scale(.985); }
.topic-card .num{
  width:40px; height:40px; border-radius:14px; flex:none;
  display:grid; place-items:center; font-family:var(--font-head); font-weight:700; font-size:1rem;
  background:var(--accent-bg); color:var(--accent);
}
.topic-card .info{ flex:1; min-width:0; }
.topic-card .info b{ display:block; font-family:var(--font-head); font-size:var(--fs-h3); }
.topic-card .info span{ font-size:var(--fs-meta); color:var(--ink-3); font-weight:700; }
/* Persentase per materi (permintaan user) — inline di akhir judul (bukan
   block terpisah) supaya "Judul - 80%" kebaca senapas, sama palet dgn
   .skill-pct (ok/hijau cuma pas genap 100%). */
.topic-card .info .topic-pct{ font-size:var(--fs-meta); font-weight:800; color:var(--ink-3); }
.topic-card .info .topic-pct.done{ color:var(--ok); }
.topic-card .go{
  flex:none; background:var(--accent); color:#fff; border-radius:var(--r-pill);
  padding:9px 16px; font-weight:800; font-size:var(--fs-meta);
  font-family:var(--font-head);
}
/* Materi yang sudah dikerjakan (permintaan user: warna dibedakan, bukan
   cuma badge nomor) — tint kartu penuh dgn palet "ok" yang sama dipakai di
   seluruh app utk status selesai/benar. */
.topic-card.done{ background:var(--ok-bg); border-color:var(--ok); }
.topic-card.done .num{ background:#fff; color:var(--ok); }
.topic-card.done .go{ background:var(--ok); }

/* ============================== PETA LEVEL ================================ */
/* Dulu: daftar vertikal 6 kartu dengan garis penghubung — jujur, tapi kebaca
   seperti halaman Pengaturan. Sekarang: SATU papan peta yang di-scroll seperti
   pemandangan. Tiap level = 1 "perhentian" dengan warna tanahnya sendiri
   (--t1..--t6) + siluet bukit (scenery.ts), dijahit oleh jejak kaki titik-titik
   yang berkelok kiri-kanan. Medali besar memakai emoji level (PRD §7: emoji &
   nama level adalah identitas utama untuk anak), bukan nomor.
   Aturan buka/kunci-nya TIDAK berubah sedikit pun — ini murni cara menampilkan
   data yang sama dari levelUnlockMap()/isBossCleared() (PRD §12.1). */

.map-board{
  border:1px solid var(--line); border-radius:var(--r-xl);
  overflow:hidden; box-shadow:var(--sh-2); background:var(--surface);
}

/* Kepala peta: langit + matahari + awan, sekaligus tempat legenda status. */
.map-sky{
  position:relative; overflow:hidden;
  padding:var(--s5) var(--s4) var(--s4);
  background:linear-gradient(180deg,#BDEDE5 0%, #DFF6F0 46%, var(--t1-soft) 100%);
}
.map-sky h2{ position:relative; z-index:1; font-family:var(--font-head); font-size:var(--fs-h2); }
.map-sky p{ position:relative; z-index:1; font-size:var(--fs-meta); font-weight:700; color:var(--ink-2); margin-top:4px; max-width:44ch; }
.map-sun{
  position:absolute; right:18px; top:-26px; width:104px; height:104px; border-radius:50%;
  background:radial-gradient(circle at 38% 34%, #FFE3AC 0%, var(--sun-500) 60%, rgba(255,181,61,0) 72%);
  opacity:.9; pointer-events:none;
}
.map-sky .cloud{ color:#fff; width:96px; opacity:.85; }
.map-sky .cloud.c1{ left:-16px; top:14px; }
.map-sky .cloud.c2{ right:32%; top:56px; width:62px; opacity:.6; }

/* Legenda = informasi, bukan hiasan: 3 status yang benar-benar dipakai peta. */
.map-legend{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:10px; }
.map-legend li{ display:flex; align-items:center; gap:10px; font-size:var(--fs-meta); font-weight:700; color:var(--ink-2); }
.legend-dot{
  width:30px; height:30px; border-radius:50%; flex:none; display:grid; place-items:center;
  background:var(--surface); border:3px solid var(--t3-deep); font-size:14px;
}
.legend-dot.is-cleared{ border-color:var(--sun-500); }
.legend-dot.is-locked{ border-color:#D6C6AC; background:var(--muted); color:var(--ink-3); }

/* ------- perhentian ------- */
/* Tiap kelas tanah cuma memetakan 3 token warna; seluruh komponen peta di
   bawah ini menggambar lewat --band* saja, jadi menambah level baru = cukup
   1 baris di sini + 1 entri di scenery.ts. */
.t-dawn  { --band:var(--t1); --band-soft:var(--t1-soft); --band-deep:var(--t1-deep); }
.t-meadow{ --band:var(--t2); --band-soft:var(--t2-soft); --band-deep:var(--t2-deep); }
.t-lagoon{ --band:var(--t3); --band-soft:var(--t3-soft); --band-deep:var(--t3-deep); }
.t-river { --band:var(--t4); --band-soft:var(--t4-soft); --band-deep:var(--t4-deep); }
.t-canyon{ --band:var(--t5); --band-soft:var(--t5-soft); --band-deep:var(--t5-deep); }
.t-summit{ --band:var(--t6); --band-soft:var(--t6-soft); --band-deep:var(--t6-deep); }

.trail{ list-style:none; margin:0; padding:0; }
.trail-stop{
  position:relative;
  display:grid; grid-template-columns:64px minmax(0,1fr); gap:var(--s3);
  padding:var(--s5) var(--s4);
}
.trail-scene{
  position:absolute; inset:0; overflow:hidden;
  background:linear-gradient(180deg, var(--band-soft) 0%, var(--band) 100%);
}
.trail-hills{
  position:absolute; left:0; right:0; bottom:-1px; width:100%; height:56px;
  color:var(--band-deep); opacity:.4;
}
.trail-rail{ position:relative; grid-column:1; }
/* Jejak sengaja meluber ke atas & bawah sejauh padding perhentian, supaya
   titik-titiknya menyambung terus dari satu medali ke medali berikutnya —
   satu jalur utuh, bukan potongan garis per kartu. */
.trail-path{
  position:absolute; left:0; width:100%;
  top:calc(-1 * var(--s5)); height:calc(100% + var(--s5) * 2);
  color:var(--band-deep); opacity:.6;
}
.trail-medallion{
  position:relative; z-index:1; width:60px; height:60px; border-radius:50%;
  display:grid; place-items:center; font-size:30px; line-height:1;
  background:var(--surface); border:4px solid var(--band-deep);
  box-shadow:var(--sh-lift);
}
/* Penanda status kecil di bahu medali — mango untuk yang sudah ditaklukkan,
   pasir redup untuk yang masih tersegel. */
.trail-stamp{
  position:absolute; z-index:2; right:-4px; bottom:-2px;
  width:26px; height:26px; border-radius:50%; display:grid; place-items:center;
  background:var(--sun-500); color:var(--brand-900); border:2px solid var(--surface);
  box-shadow:var(--sh-1);
}
.trail-stamp svg.ico{ width:14px; height:14px; stroke-width:3; }
.trail-stamp.locked{ background:var(--muted); color:var(--ink-3); }
/* "Kamu di sini": singa mengintip di atas medali + halo mango yang berdenyut.
   Sengaja tanpa teks di medali — labelnya ada di kartu, ini penanda posisi. */
.trail-you{
  position:absolute; z-index:3; left:38px; top:-16px; font-size:24px; line-height:1;
  filter:drop-shadow(0 4px 6px rgba(90,62,26,.4));
}
/* position+z-index wajib: .trail-scene di-absolute-kan, jadi tanpa ini kartunya
   terlukis DI BAWAH lapisan pemandangan (elemen non-positioned selalu di bawah
   elemen positioned) dan hilang dari layar. */
.trail-card{
  position:relative; z-index:1;
  grid-column:2; min-width:0;
  background:linear-gradient(180deg,var(--surface) 0%, var(--surface-2) 100%);
  border-radius:var(--r-lg); padding:var(--s4);
  box-shadow:var(--sh-lift), 0 0 0 1px rgba(255,255,255,.7) inset;
}
/* Kepala kartu: teks di kiri, avatar Raja besar di kanan — supaya markas
   punya wajah yang langsung dikenali anak, bukan cuma judul teks. */
.trail-card-head{ display:flex; align-items:flex-start; justify-content:space-between; gap:var(--s3); }
.trail-card-txt{ min-width:0; flex:1; }
.trail-boss{
  flex:none; width:64px; height:64px; border-radius:50%;
  display:grid; place-items:center; font-size:32px; line-height:1;
  background:radial-gradient(circle at 34% 30%, var(--surface) 0%, var(--surface-2) 100%);
  border:3px solid var(--band-deep); box-shadow:var(--sh-1);
}
.trail-place{
  display:block; font-size:.75rem; font-weight:800; letter-spacing:.13em;
  text-transform:uppercase; color:var(--ink-sand);
}
.trail-card h3{
  font-family:var(--font-head); font-size:1.125rem; margin-top:2px;
  display:flex; align-items:center; gap:6px; flex-wrap:wrap;
}
.trail-meta{ display:flex; align-items:center; gap:6px; margin-top:6px; flex-wrap:wrap; }
/* Tombol aksi dibuat mungil & berdampingan (pil, bukan blok selebar layar) —
   supaya tidak mendominasi kartu & menyisakan ruang buat avatar Raja. */
.trail-actions{ margin-top:var(--s3); display:flex; flex-direction:row; flex-wrap:wrap; gap:8px; }
.trail-actions .primary-btn,
.trail-actions .ghost-btn{
  width:auto; flex:1 1 140px; margin-top:0;
  padding:10px 14px; font-size:.8125rem; border-radius:var(--r-pill);
  display:inline-flex; align-items:center; justify-content:center; gap:6px;
}
.trail-actions .meta{ margin-top:2px; flex-basis:100%; }

.trail-stop.is-cleared .trail-medallion{ border-color:var(--sun-500); }
.trail-stop.is-locked .trail-scene{ filter:saturate(.3); }
.trail-stop.is-locked .trail-medallion{ background:var(--muted); border-color:#D6C6AC; }
.trail-stop.is-locked .trail-medallion span{ filter:grayscale(.7); opacity:.65; }
.trail-stop.is-locked .trail-card{ background:linear-gradient(180deg,#FFFDF9,#F8F3EA); }

/* Halo denyut cuma di perhentian tempat anak berada sekarang — 1 titik fokus
   di seluruh peta, bukan animasi di mana-mana. */
@keyframes hereHalo{
  0%{ box-shadow:var(--sh-lift), 0 0 0 0 rgba(255,181,61,.65); }
  70%{ box-shadow:var(--sh-lift), 0 0 0 15px rgba(255,181,61,0); }
  100%{ box-shadow:var(--sh-lift), 0 0 0 0 rgba(255,181,61,0); }
}
.trail-stop.is-here .trail-medallion{ border-color:var(--sun-500); animation:hereHalo 2.4s ease-out infinite; }

/* ---------------------------------------------------- mini-peta (Beranda) --
   Versi ringkas & horizontal dari Peta Level (§ trail-* di atas) — dipasang
   menonjol di Beranda supaya "sedang menjalani misi ala Solo Leveling" terasa
   begitu app dibuka, bukan cuma di layar peta penuh. Pakai token warna
   perhentian yang sama (--t1..--t6 lewat .t-*) tapi tanpa siluet bukit SVG
   (di ukuran mini, medan penuh cuma jadi noise) — cukup gradasi tanahnya.
   Tap di mana saja pada strip = buka Peta Level penuh (§12 PRD). */
.mini-trail{
  background:linear-gradient(135deg,var(--t3-soft) 0%,var(--t1-soft) 55%,var(--t5-soft) 100%);
  border-radius:var(--r-xl); padding:var(--s4);
  box-shadow:var(--sh-lift), 0 0 0 1px rgba(255,255,255,.6) inset;
  cursor:pointer;
}
.mini-trail:focus-visible{ outline:3px solid var(--brand-700); outline-offset:3px; }
.mini-trail-head{ display:flex; align-items:flex-start; justify-content:space-between; gap:var(--s2); }
.mini-trail-link{
  flex:none; white-space:nowrap;
  font-size:var(--fs-meta); font-weight:800; color:var(--brand-700);
  display:inline-flex; align-items:center; gap:2px;
}
.mini-trail-link .ico{ width:14px; height:14px; }
.mini-trail-row{
  list-style:none; margin:var(--s5) 0 0; padding:10px 2px 0;
  display:flex; align-items:flex-start; justify-content:space-between; gap:4px;
  position:relative;
}
/* Garis jalur di belakang deretan medali — satu garis putus-putus lurus,
   cukup untuk kesan "ini satu jalur", tanpa perlu SVG berkelok di skala ini. */
.mini-trail-row::before{
  content:''; position:absolute; left:22px; right:22px; top:32px; height:0;
  border-top:3px dashed var(--t3-deep); opacity:.45; z-index:0;
}
.mini-stop{ position:relative; z-index:1; display:grid; place-items:center; }
.mini-medallion{
  position:relative; width:44px; height:44px; border-radius:50%;
  display:grid; place-items:center; font-size:22px; line-height:1;
  background:var(--surface); border:3px solid var(--band-deep, var(--t3-deep));
  box-shadow:var(--sh-1);
}
.mini-stop.is-cleared .mini-medallion{ border-color:var(--sun-500); }
.mini-stop.is-locked .mini-medallion{ background:var(--muted); border-color:#D6C6AC; }
.mini-stop.is-locked .mini-medallion span{ filter:grayscale(.7); opacity:.6; }
.mini-stop.is-here .mini-medallion{ border-color:var(--sun-500); animation:hereHalo 2.4s ease-out infinite; }
.mini-stop .trail-stamp{ width:18px; height:18px; right:-3px; bottom:-3px; }
.mini-stop .trail-stamp svg.ico{ width:10px; height:10px; }
.mini-you{
  position:absolute; z-index:2; top:-20px; font-size:18px; line-height:1;
  filter:drop-shadow(0 3px 5px rgba(90,62,26,.35));
}
.mini-trail-caption{
  flex:1; min-width:0; margin:0; font-size:var(--fs-meta); font-weight:700; color:var(--ink-2);
}
.mini-trail-caption b{ color:var(--brand-700); }

/* Label CEFR kecil di bawah tiap medali mini (Little Stars sengaja tanpa
   label — PRD §7, tidak punya badge CEFR sama sekali). */
.mini-cefr{
  margin-top:4px; font-size:.75rem; font-weight:800; color:var(--ink-3); letter-spacing:.02em;
}

/* Progresmu — strip stat + bar level di bawah peta mini, terinspirasi
   beranda kompetitor (bar level, streak, XP, akurasi) tapi difilter
   kid-friendly (CLAUDE.md): tanpa coin, tanpa "HP" yang bisa habis, streak
   dikasih 1 hari pelindung (progress.ts `getStreak`), akurasi dibingkai
   hangat & disembunyikan ("–") kalau belum ada percobaan (PRD §4.6). */
.stat-row{ display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s3); margin-top:var(--s3); }
.stat-tile{
  display:flex; flex-direction:column; align-items:center; gap:4px; text-align:center;
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-md);
  padding:var(--s3) var(--s2); box-shadow:var(--sh-1);
}
.stat-ic{
  width:36px; height:36px; border-radius:50%; flex:none;
  display:grid; place-items:center; font-size:17px;
  background:radial-gradient(circle at 34% 26%, var(--brand-300) 0%, var(--brand-500) 100%);
  box-shadow:0 6px 14px -8px rgba(6,51,58,.5), inset 0 0 0 2px rgba(255,255,255,.35);
}
.stat-tile:nth-child(1) .stat-ic{
  background:radial-gradient(circle at 34% 26%, var(--sun-400) 0%, var(--sun-500) 68%, #EFA22C 100%);
}
.stat-tile:nth-child(2) .stat-ic{
  background:radial-gradient(circle at 34% 26%, var(--t5) 0%, var(--t5-deep) 100%);
}
.stat-value{ font-family:var(--font-head); font-size:1.0625rem; color:var(--ink); }
.stat-label{ font-size:.75rem; font-weight:800; color:var(--ink-3); letter-spacing:.01em; }

/* Stats Singkat Rapor — list/baris (permintaan user, ganti dari grid 3-tile
   `.stat-row`/`.stat-tile` di atas) — ikon+label+angka per baris ala
   referensi kompetitor, TAPI lingkaran ikon tetap gradien radial yang sama
   dgn `.stat-ic` (bukan ikon flat kompetitor) supaya masih 1 keluarga
   visual dgn kartu lain, bukan tempelan gaya asing. */
.stat-list{ list-style:none; margin:12px 0 0; padding:0; }
.stat-list li{
  display:flex; align-items:center; gap:12px;
  padding:10px 0; border-top:1px solid var(--line);
}
.stat-list li:first-child{ border-top:none; }
.stat-list-ic{
  width:34px; height:34px; border-radius:50%; flex:none;
  display:grid; place-items:center; font-size:16px;
  background:radial-gradient(circle at 34% 26%, var(--brand-300) 0%, var(--brand-500) 100%);
  box-shadow:0 6px 14px -8px rgba(6,51,58,.5), inset 0 0 0 2px rgba(255,255,255,.35);
}
/* Siklus 3 warna (bukan cuma baris 1/2 spt `.stat-tile`) — daftar ini
   sekarang bisa 6+ baris, siklus berulang supaya tidak monoton biru semua
   dari baris ke-3 dst. */
.stat-list li:nth-child(3n+1) .stat-list-ic{
  background:radial-gradient(circle at 34% 26%, var(--sun-400) 0%, var(--sun-500) 68%, #EFA22C 100%);
}
.stat-list li:nth-child(3n+2) .stat-list-ic{
  background:radial-gradient(circle at 34% 26%, var(--t5) 0%, var(--t5-deep) 100%);
}
.stat-list-label{ flex:1; font-size:var(--fs-meta); font-weight:700; color:var(--ink-2); }
.stat-list-value{ font-family:var(--font-head); font-weight:800; font-size:1.0625rem; color:var(--ink); }

/* Progresmu dibuat lebih ringkas/slim (permintaan user) — jarak antar-baris
   & ukuran stat-tile dikecilkan, TANPA membuang info apa pun (masih level+%,
   bar, keterangan modul, 3 stat). Cuma padding & spacing yang dirapatkan. */
.progress-panel{ padding:var(--s4); }
.progress-panel .level-progress-head{ margin-top:var(--s2); }
.progress-panel .progress-track{ height:8px; margin-top:var(--s2); }
.progress-panel > p.meta{ margin-top:var(--s2) !important; font-size:.75rem; }
.progress-panel .stat-row{ margin-top:var(--s3); gap:var(--s2); }
.progress-panel .stat-tile{ padding:var(--s2) var(--s1); gap:2px; }
.progress-panel .stat-ic{ width:28px; height:28px; font-size:14px; }
.progress-panel .stat-value{ font-size:.9375rem; }
.progress-panel .stat-label{ font-size:.75rem; }

/* Papan Peringkat XP (Beranda/Rapor) — dianonimkan total (avatar hewan + XP
   saja, TANPA nama/ranking eksplisit per anak, lihat buildLeaderboardCard di
   app.ts) — baris polos, medali cuma dekorasi utk 3 besar, bukan skor. */
.leaderboard-list{ display:flex; flex-direction:column; gap:6px; }
.leaderboard-row{
  display:flex; align-items:center; gap:var(--s2);
  padding:6px var(--s2); border-radius:var(--r-md); background:var(--surface);
}
.leaderboard-rank{ width:22px; flex:none; text-align:center; font-size:.8125rem; font-weight:800; color:var(--ink-3); }
.leaderboard-avatar{ font-size:1.25rem; flex:none; }
.leaderboard-xp{ margin-left:auto; font-family:var(--font-head); font-size:.875rem; color:var(--ink); }

/* 🔒 Grid "Pilih Game" — SEKARANG dipakai DUA tempat: roster Game Hub
   (`app.ts renderGame()`, permintaan user awal "buat tampilan game nya
   mencari card seperti contoh gambar") DAN Map Kerajaan/Hutan Ajaib INTERNAL
   tiap game (`games/wordmatch.ts`/`games/balloonpop.ts`/`games/
   soundhunt.ts`, permintaan user "jadikan 1 card an seperti di halaman game
   dimana 1 row jadi 2 card"). Riwayat singkat Map Kerajaan: jalur vertikal
   trail selang-seling (`.raja-trail`/`.raja-icon`/`.raja-stop-inner`, SUDAH
   DIHAPUS TOTAL) → sempat direvisi jadi "1 kartu lebar dgn pemandangan
   terrain" (analisis Giggle Academy, JUGA SUDAH DIGANTI) → SEKARANG reuse
   PERSIS grid 2-kolom ini, TIDAK ada CSS terpisah lagi utk Map Kerajaan.
   Tiap kartu SELURUHNYA 1 tombol tap — ikon besar di atas (tint lembut warna
   Raja, `color-mix`), judul, badge kecil (XP/Baru di roster; persentase
   0%/100% di Map Kerajaan via `.skill-pct` yang SAMA — lihat komentar
   `is-locked`/`is-cleared` di bawah). */
.raja-grid{ display:grid; grid-template-columns:repeat(2,1fr); gap:var(--s4); }
@media (min-width:640px){ .raja-grid{ grid-template-columns:repeat(3,1fr); } }
/* 🔒 SENGAJA TIDAK ADA breakpoint ≥960px lagi (dulu naik ke 4 kolom, lalu
   sempat 3-kolom-dipaksa KHUSUS Map Kerajaan via `.raja-map-wrap .raja-grid`
   — override itu SEKARANG DIHAPUS, sudah tidak perlu) — permintaan user
   "untuk http://127.0.0.1:8200/game... di web desktop version buat 3 card
   dalam 1 row" (roster `/game`) + CLAUDE.md "Desain Harus Bagus di Mobile
   MAUPUN Desktop": 3 kolom TETAP dari 640px sampai lebar berapa pun. Base
   rule ini SEKARANG SUDAH PAS utk KEDUA konteks pemakainya (roster `/game`
   6-7 kartu, DAN Map Kerajaan tiap game — SELALU 6 markas, di dalam
   `.raja-map-wrap`) — keduanya kebetulan sama-sama ingin 3 kolom di
   desktop, jadi TIDAK perlu override per-konteks lagi. Kalau nanti ada
   konteks BARU yang butuh jumlah kolom BEDA (mis. daftar >9 kartu), scope
   breakpoint-nya sendiri (pola CLAUDE.md poin 2 "Desain Harus Bagus..."),
   jangan ubah base rule ini lagi begitu saja. */
.raja-card{
  position:relative;
  display:flex; flex-direction:column; align-items:center; text-align:center; gap:8px;
  background:linear-gradient(180deg,var(--surface) 0%, var(--surface-2) 100%);
  border-radius:var(--r-lg); border:1px solid var(--line);
  padding:var(--s5) var(--s3) var(--s4);
  box-shadow:var(--sh-1); cursor:pointer;
  font-family:inherit; transition:transform .12s ease, box-shadow .15s ease;
}
/* Badge persen pojok-kanan-atas — REUSE PERSIS `.skill-pct` (Menu Belajar,
   lihat komentar di situ), tanpa CSS baru; `.raja-card` cuma butuh
   `position:relative` di atas supaya `.skill-pct`'s `position:absolute`
   anchor ke kartu ini, bukan ke ancestor lain. */
.raja-card:active{ transform:translateY(1px) scale(.98); }
.raja-card-icon{
  flex:none; width:88px; height:88px; border-radius:var(--r-lg); overflow:hidden;
  display:grid; place-items:center;
  background:color-mix(in srgb, var(--band-deep) 18%, var(--surface-2));
}
.raja-card-icon img{ width:100%; height:100%; object-fit:cover; display:block; }
.raja-card-icon svg{ width:52px; height:52px; display:block; }
.raja-card h3{ font-family:var(--font-head); font-size:.9375rem; margin:0; color:var(--ink); }
@media (min-width:560px){
  .raja-card-icon{ width:108px; height:108px; }
  .raja-card-icon svg{ width:64px; height:64px; }
  .raja-card h3{ font-size:1.0625rem; }
}

/* Status markas Map Kerajaan (`is-locked`/`is-cleared`/`is-open`) — KHUSUS
   dipakai Map Kerajaan, roster `/game` TIDAK PERNAH set class ini (semua
   game roster selalu "terbuka"), jadi aman ditaruh bare tanpa scoping
   tambahan. Markas terkunci TANPA tombol "🔒 Terkunci" berulang lagi (kerasa
   spt disabled form) — kartunya sendiri jadi `disabled`, cukup redup.
   Markas berikutnya yang boleh dijelajah dapat halo denyut mango
   (`hereHalo`, REUSE PERSIS animasi "kamu di sini" Peta Level) di ikonnya —
   SATU titik fokus jelas, tanpa perlu teks "▶️ Jelajahi".
   🔒 Badge pojok-kanan-atas dulu ikon gembok/centang custom (`.raja-card-
   badge`, SUDAH DIHAPUS) — permintaan user "tambahkan percentage di setiap
   card" GANTI jadi persentase 0%/100% via `.skill-pct` yang SAMA PERSIS
   dgn roster `/game`/Menu Belajar (bukan CSS baru, lihat komentar
   `.skill-pct` sendiri) — status terkunci TETAP kebaca dari kartu yang
   diredupkan+`disabled` di atas, bukan lagi dari ikon terpisah. */
.raja-card.is-locked{ opacity:.6; cursor:default; }
.raja-card.is-locked .raja-card-icon{ filter:grayscale(.55); }
.raja-card.is-open .raja-card-icon{ animation:hereHalo 2.2s ease-out infinite; }

/* 🔒 `.map-card` — permintaan user "kasih UI lebih kids friendly seperti
   sebelumnya, dari hasil analisis giggleacademy.com/learning-course": grid
   2-kolom (`.raja-grid`/`.raja-card`) DIKONFIRMASI sudah tepat, cuma kartu
   Map Kerajaan-nya sendiri kerasa terlalu datar/putih polos (versi grid ini
   awalnya 1:1 ikut gaya kartu roster `/game`, yang waktu itu sengaja netral).
   Kartu dapat rona warna Raja-nya sendiri yang LEMBUT (`color-mix` low-%,
   bukan latar saturasi penuh spt versi terrain SEBELUMNYA yang sudah dicabut
   — temuan riset sendiri: Giggle Academy dominan putih/krem, warna cuma
   AKSEN, bukan latar utama), ikon dapat tint sedikit lebih pekat drpd base
   biar tetap menonjol di atas kartu yang sekarang tidak polos putih lagi.
   🔒 **Revisi (permintaan user, dibandingkan langsung vs versi warna KUAT
   lewat screenshot side-by-side)** — modifier ini SEKARANG JUGA dipakai di
   roster utama `/game` (dulu "TETAP putih/krem polos apa adanya, tidak ikut
   berubah" — keputusan itu DIBALIK setelah dikonfirmasi ulang, bukan lupa).
   Versi warna KUAT (dicoba sekali lagi utk perbandingan, class `terrain-
   card`) TETAP DITOLAK — user pilih rona lembut ini utk KEDUA konteks
   (Map Kerajaan & roster), jadi sekarang `.raja-card.map-card` satu-satunya
   modifier warna yang hidup, dipakai di 2 tempat. */
.raja-card.map-card{
  background:linear-gradient(180deg, color-mix(in srgb, var(--band-deep) 14%, var(--surface)) 0%, color-mix(in srgb, var(--band-deep) 6%, var(--surface-2)) 100%);
  border-color:color-mix(in srgb, var(--band-deep) 30%, var(--line));
}
.raja-card.map-card .raja-card-icon{ background:color-mix(in srgb, var(--band-deep) 26%, var(--surface)); }
.raja-card.map-card.is-locked{ background:linear-gradient(180deg,#FFFDF9,#F8F3EA); border-color:var(--line); }

/* 🔒 `.terrain-card` — permintaan user "coba variant 2" (dihidupkan lagi
   utk dicoba LIVE di roster `/game`, gantikan `.map-card` di sana). Versi
   warna KUAT yang SAMA PERSIS pernah "dicabut" (lihat komentar `.map-card`)
   & sempat dihidupkan-lalu-dihapus lagi utk perbandingan screenshot sesi
   sebelumnya — sekarang dihidupkan LAGI, kali ini beneran dipakai.
   🔒 **Revisi (permintaan user "implementasi variant 2" [lanjutan, di Map
   Kerajaan tiap game])** — SEKARANG JUGA dipakai di 6 `games/*.ts` (dulu
   "TETAP `.map-card` lembut, TIDAK ikut berubah" — sengaja dibalik). Beda
   dari roster (tiap KARTU = game beda, jadi warnanya alami bervariasi),
   Map Kerajaan 1 game py SEMUA kartu warna SAMA (`--band-deep` per-game
   di-hardcode 1 nilai di tiap file games/*.ts) — konsekuensinya diketahui &
   diterima, bukan bug. `.is-locked` dapat override SENDIRI (beda dari
   `.map-card.is-locked`, sama alasan/nilai) krn dipakai jg di konteks yg
   PUNYA status terkunci (roster tidak pernah). */
.raja-card.terrain-card{
  background:linear-gradient(180deg, color-mix(in srgb, var(--band-deep) 35%, var(--surface)) 0%, color-mix(in srgb, var(--band-deep) 65%, var(--surface)) 100%);
  border-color:var(--band-deep);
}
.raja-card.terrain-card .raja-card-icon{ background:color-mix(in srgb, var(--band-deep) 15%, var(--surface)); }
.raja-card.terrain-card.is-locked{ background:linear-gradient(180deg,#FFFDF9,#F8F3EA); border-color:var(--line); }

/* Tag tingkat kesulitan berwarna (permintaan user "lebih kids friendly" —
   sebelumnya SEMUA tingkat pakai `.tag` netral abu-abu yang sama, kurang
   terasa "naik tangga"). REUSE token warna yang SUDAH ADA (bukan palet
   baru) — pemanasan=merah muda "read", mudah=hijau "ok" (sama arti
   "aman/gampang" dgn dipakai lintas app), sedang=biru listen, sulit=oranye
   grammar, jago=ungu vocab, legendaris=mango — 6 warna yang sudah py makna
   sendiri di app ini, dipinjam murni sbg pembeda visual tingkat, bukan
   makna skill aslinya. 🔒 `diff-pemanasan` BARU (permintaan user "tambahkan
   1 sehingga ada 6... levelnya ada pemanasan, mudah, sedang, sulit, jago,
   legendaris") — dipakai SEMUA game bertingkat (Kata/Balon/Susun/Ingatan/
   Sound Hunt), bukan cuma 1 game. */
.tag.diff-pemanasan{ background:var(--c-read-bg); color:var(--c-read); }
.tag.diff-mudah{ background:var(--ok-bg); color:var(--ok); }
.tag.diff-sedang{ background:var(--c-listen-bg); color:var(--c-listen); }
.tag.diff-sulit{ background:var(--c-gram-bg); color:var(--c-gram); }
.tag.diff-jago{ background:var(--c-vocab-bg); color:var(--c-vocab); }
.tag.diff-legendaris{ background:var(--sun-100); color:var(--sun-600); }

/* 🔒 Kartu ringkasan puncak Map Kerajaan (`.game-progress-card`) — GANTIKAN
   `.card.note-card` polos sebelumnya (permintaan user, referensi screenshot
   "Tantangan Harian": ikon+label kecil di atas, judul singkat, baris titik
   progres + "Selesai X dari Y" — "buat sesimple mungkin secara text").
   Diadaptasi, BUKAN ditiru 100%: baris "Bonus koin/XP" & tombol CTA besar
   di referensi SENGAJA tidak diambil — app ini tidak pernah py coin (PRD
   §11), dan CTA terpisah jadi mubazir krn kartu markas di bawahnya SUDAH
   1 tombol tap langsung. Titik progres (`.game-progress-dot`) reuse warna
   "ok" hijau yang sama dgn status selesai di seluruh app (BUKAN ikon
   custom baru). 🔒 Baris ikon+eyebrow ("🧩 Kerajaan Kata" dst, `.game-
   progress-head`/`.game-progress-ic`) SUDAH DIHAPUS TOTAL (permintaan user
   "remove text '🧩Kerajaan Kata'" — nama kerajaan sudah kebaca dari `.act-
   head`/judul Raja di puncak layar, redundan diulang di sini). */
/* 🔒 Revisi (permintaan user: "background sublist game mirip dgn background
   di [roster] game, tp bedakan — bukan langit dan [mata]hari") — kartu ini
   sekarang jadi banner GELAP "langit malam", BUKAN langit siang (`.map-sky`
   roster `/game`, biru muda+matahari+awan) — bintang kelap-kelip yang SUDAH
   ADA (`GAME_STAR_FIELD`, sebelumnya cuma latar ambient samar di BELAKANG
   seluruh `.raja-map-wrap`, hampir tak kelihatan di atas card putih) SEKARANG
   DIRENDER LAGI di DALAM kartu ini secara khusus (`games/*.ts`, instance
   KEDUA) supaya benar-benar kelihatan sbg dekorasi utama banner, bukan cuma
   tekstur nyaris-invisible. Gradien gelap reuse `--brand-700/900` (teal tua
   yang SAMA dgn rail/tabbar gelap di tempat lain — BUKAN navy/indigo baru,
   tetap konsisten rationale warna di atas file ini). */
.game-progress-card{
  margin-bottom:var(--s5); position:relative; overflow:hidden;
  background:linear-gradient(160deg, var(--brand-700) 0%, var(--brand-900) 100%);
  border:none;
}
.game-progress-card h2{ position:relative; z-index:1; font-family:var(--font-head); font-size:1.0625rem; margin:0 0 var(--s3); color:#fff; }
.game-progress-dots{ position:relative; z-index:1; display:flex; align-items:center; gap:6px; flex-wrap:wrap; }
.game-progress-dot{
  width:28px; height:28px; flex:none; border-radius:50%; display:grid; place-items:center;
  background:rgba(255,255,255,.18); color:transparent; font-weight:800; font-size:.8125rem;
}
.game-progress-dot.done{ background:var(--ok-bg); color:var(--ok); }
/* 🔒 Permintaan user: tandai markas BERIKUTNYA yang belum ditaklukkan
   (posisi anak sekarang di perjalanan) beda dari titik "belum" polos —
   ring putih + bg sedikit lebih terang, konsisten pola ring `.quiz-dot`/
   `.day-chip.is-today` di tempat lain, cuma warnanya putih krn kartu ini
   gelap (bukan brand teal yang lebur dgn latar). */
.game-progress-dot.current{ background:rgba(255,255,255,.32); box-shadow:0 0 0 3px rgba(255,255,255,.4); }
.game-progress-label{ font-size:var(--fs-meta); font-weight:700; color:rgba(255,255,255,.78); margin-left:4px; }

/* 🔒 "Cara Main" (permintaan user, referensi screenshot: judul tengah +
   kartu daftar bernomor) — GANTIKAN pesan penutup custom "Itu semua
   markas..." (`gameMapFooterHtml()`, SUDAH DIHAPUS dari `games/*.ts`) krn
   footer brand yang SEBENARNYA sekarang ditaruh sekali di level screen
   (`.standalone-footer`, app.ts `renderGamePlay()`). Isi langkah BEDA tiap
   game (mekanik beda-beda — match/pop/dengar), diteruskan sbg parameter ke
   helper lokal `gameHowToHtml()`. Angka bulat reuse warna Raja (`var(--k)`,
   custom property yang SUDAH diisi `.raja-stage` induknya — ikut tema
   halaman, bukan warna generik). */
.game-howto-title{ font-family:var(--font-head); font-size:1.0625rem; text-align:center; margin:var(--s6) 0 var(--s4); }
.game-howto-card{ margin-bottom:0; }
.game-howto-list{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:var(--s4); }
.game-howto-list li{ display:flex; align-items:center; gap:var(--s3); font-weight:700; }
.game-howto-num{
  flex:none; width:30px; height:30px; border-radius:50%;
  display:grid; place-items:center; font-family:var(--font-head); font-weight:800; font-size:.9375rem;
  background:color-mix(in srgb, var(--k, var(--brand-700)) 16%, var(--surface-2));
  color:var(--k, var(--brand-700));
}

/* Skin "arcade" utk stage main Raja (permintaan user: "jangan kaku, ini game
   bukan materi") — CSS-ONLY, reuse fungsi generik yang sama persis (Eja Kata/
   Susun Kalimat/Kelompokkan) TANPA fork logic apa pun: cuma restyle chrome
   quiz (stage-badge/quiz-dot) dari label datar jadi lebih "game", warna ikut
   --k (warna Raja itu, diisi inline app.ts). */
.raja-stage .stage-badge{ background:var(--k); color:#fff; box-shadow:var(--sh-1); }
.raja-stage .quiz-dot.current{ background:var(--k); color:#fff; box-shadow:0 0 0 3px color-mix(in srgb, var(--k) 25%, transparent); }
.raja-stage .quiz-dot.current.done{ background:var(--ok); color:#fff; box-shadow:0 0 0 3px color-mix(in srgb, var(--k) 25%, transparent); }

/* Memory Match — grid kartu tertutup (permintaan user, contoh referensi
   "Memory Match"): flip 2 kartu/giliran, cari pasangan id↔en 1 VocabItem
   (games/vocabulary.ts runMemoryMatch) — TANPA data baru. */
.mm-head{ display:flex; align-items:center; justify-content:space-between; margin-bottom:var(--s3); }
.mm-score{ font-family:var(--font-head); font-size:.9375rem; color:var(--ink-2); }
.mm-score b{ color:var(--ink); font-size:1.375rem; }
.mm-grid{ display:grid; grid-template-columns:repeat(var(--mm-cols,3),minmax(0,1fr)); gap:10px; max-width:calc(var(--mm-cols,3) * 150px); margin-inline:auto; }
/* Kolom ikut jumlah kartu supaya baris terakhir selalu penuh (CLAUDE.md
   "Desain Harus Bagus di Mobile MAUPUN Desktop"): 4 → 2×2, 6 → 3×2,
   8 → 4×2, 12 → 3×4 di HP / 4×3 di desktop (games/memorymatch.ts). */
.mm-grid.mm-c4{ --mm-cols:2; }
.mm-grid.mm-c8{ --mm-cols:4; }
.mm-grid.mm-c8{ gap:8px; }
.mm-grid.mm-c8 .mm-card{ padding:6px 4px; min-width:0; }
.mm-grid.mm-c8 .mm-text{ font-size:.75rem; overflow-wrap:anywhere; }
@media (min-width:720px){ .mm-grid.mm-c12{ --mm-cols:4; } .mm-grid.mm-c8 .mm-text{ font-size:.875rem; } }
/* Memory Hunt — kartu teks (Sedang/Sulit/Jago/Legendaris), kartu bunyi,
   arti Indonesia (💡 Arti), & kartu kalimat rumpang Legendaris (2 kolom di
   HP, 4 di desktop, tinggi ikut isi — kalimat tidak muat di kartu persegi). */
.mm-grid.is-text .mm-text{ font-size:.9375rem; }
.mm-grid.mm-c8.is-text .mm-text{ font-size:.8125rem; }
.mm-card.is-sound .mm-emoji{ font-size:2rem; }
.mm-meaning{ font-size:.75rem; font-weight:700; color:var(--ink-2); text-align:center; line-height:1.2; }
.mm-grid.is-gap{ --mm-cols:2; max-width:520px; }
.mm-grid.is-gap .mm-card{ aspect-ratio:auto; min-height:104px; min-width:0; padding:26px 10px 10px; }
.mm-grid.is-gap .mm-card > .mm-text, .mm-grid.is-gap .mm-card > .mm-meaning{ width:100%; overflow-wrap:anywhere; }
.mm-grid.is-gap .mm-card.is-long .mm-text{ font-size:.8125rem; font-weight:700; line-height:1.3; }
@media (min-width:720px){ .mm-grid.is-gap{ --mm-cols:4; max-width:760px; } .mm-grid.mm-c8.is-text .mm-text{ font-size:.875rem; } }
.mm-card{
  aspect-ratio:1/1; border-radius:var(--r-md); border:2px solid var(--line);
  background:var(--surface); display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:4px; padding:8px; transition:transform .15s ease, background .15s ease, border-color .15s ease;
}
.mm-card:not(:disabled):hover{ transform:translateY(-2px); border-color:var(--k, var(--brand-500)); }
.mm-card:disabled{ cursor:default; }
.mm-card.is-open{ background:color-mix(in srgb, var(--k, var(--brand-500)) 10%, var(--surface)); border-color:var(--k, var(--brand-300)); }
.mm-card.is-matched{ background:var(--ok-bg); border-color:var(--ok); position:relative; }
/* 🔒 Kartu SALAH pasang (permintaan user, CLAUDE.md "🔒 Aturan Wajib:
   Notifikasi Jawaban Salah") — flash merah singkat sebelum ke-2 kartu
   ditutup lagi, pola sama `.opt-btn.wrong`. */
.mm-card.is-wrong{ animation:shake .3s ease; border-color:var(--try); background:var(--try-bg); }
.mm-mark{
  width:40px; height:40px; border-radius:50%; background:var(--k, var(--brand-700)); color:#fff;
  display:grid; place-items:center; font-size:18px; font-weight:800;
}
.mm-emoji{ font-size:1.625rem; line-height:1; }
.mm-text{ font-family:var(--font-head); font-weight:800; font-size:.875rem; color:var(--ink); text-align:center; }
.mm-check{ position:absolute; top:6px; right:6px; width:20px; height:20px; border-radius:50%; background:var(--ok); color:#fff; display:grid; place-items:center; font-size:11px; }

/* Raja Kata — Word Match (games/wordmatch.ts): dua KOLOM kartu terlihat
   sekaligus (kata di kiri, gambar di kanan, diacak independen — permintaan
   user "ubah tampilannya jadi vertikal", dulu 2 baris atas/bawah) — anak tap
   satu kata lalu satu gambar (atau sebaliknya); pasangan benar digambar garis
   penghubung SVG (overlay .wm-lines di atas .wm-board), bukan drag beneran. */
.wm-head{ display:flex; align-items:center; justify-content:space-between; gap:var(--s3); flex-wrap:wrap; margin:var(--s3) auto; max-width:520px; }
.wm-task{ margin:0; flex:1 1 180px; font-weight:700; color:var(--ink-2); font-size:.9375rem; }
.wm-board{
  position:relative; display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); column-gap:clamp(28px, 10vw, 120px);
  max-width:520px; margin:0 auto;
}
.wm-lines{ position:absolute; inset:0; pointer-events:none; overflow:visible; }
.wm-row{ position:relative; z-index:1; display:flex; flex-direction:column; gap:var(--s3); }
.wm-card{
  position:relative; z-index:1; height:62px; border-radius:var(--r-md); border:2px solid var(--line);
  background:var(--surface); padding:8px; display:flex; align-items:center; justify-content:center;
  transition:transform .15s ease, background .15s ease, border-color .15s ease;
}
.wm-card:not(:disabled):hover{ transform:translateY(-2px); border-color:var(--brand-500); }
.wm-card:disabled{ cursor:default; }
.wm-card.is-selected{ border-color:var(--accent); background:var(--accent-bg); }
.wm-card.is-matched{ border-color:var(--ok); background:var(--ok-bg); opacity:.85; }
.wm-card.is-wrong{ animation:shake .3s ease; border-color:var(--try); background:var(--try-bg); }
.wm-word-text{ font-family:var(--font-head); font-weight:800; font-size:clamp(.875rem, 3.6vw, 1.0625rem); color:var(--ink); }
.wm-emoji{ font-size:2rem; line-height:1; }
/* Pembeda tiap markas (games/wordmatch-data.ts TIER_CONFIG): Jago = kartu
   kiri cuma 🔊 bernomor (tulisan muncul lewat 💡/setelah cocok), Legendaris =
   kartu kiri petunjuk kalimat & kartu kanan kata (tanpa gambar), Sedang/Sulit
   💡 = gambar pasangan kata terpilih berdenyut (.is-hint). */
.wm-card{ flex-direction:column; gap:2px; }
.wm-check{ position:absolute; top:4px; left:6px; font-size:.75rem; line-height:1; }
.wm-audio{ font-family:var(--font-head); font-weight:800; font-size:1.25rem; color:var(--ink); }
.wm-audio-word{ font-size:.8125rem; font-weight:700; color:var(--ink-2); }
.wm-board.is-clue{ grid-template-columns:minmax(0,3fr) minmax(0,2fr); column-gap:clamp(20px, 7vw, 80px); max-width:620px; }
.wm-board.is-clue .wm-card{ height:96px; }
.wm-board.is-clue.show-id .wm-card{ height:132px; }
.wm-card.is-clue{ justify-content:center; align-items:flex-start; text-align:left; }
.wm-clue{ font-size:clamp(.8125rem, 3.4vw, .9375rem); font-weight:700; color:var(--ink); line-height:1.3; }
.wm-clue-id{ display:block; margin-top:4px; font-size:.75rem; font-weight:600; color:var(--ink-3); }
.wm-card.is-hint{ border-color:var(--sun-500); animation:ctaPulse 1.6s ease-in-out infinite; }
.wm-line{ stroke:var(--ok); stroke-width:3; stroke-linecap:round; }
.wm-line-new{ stroke-dasharray:1; stroke-dashoffset:1; animation:wmLineDraw .35s ease forwards; }
@keyframes wmLineDraw{ to{ stroke-dashoffset:0; } }
@media (prefers-reduced-motion: reduce){
  .wm-card.is-wrong, .wm-card.is-hint{ animation:none; }
  .wm-line-new{ animation:none; stroke-dashoffset:0; }
}

/* Perhentian "Selalu Terbuka" Raja Kata di Peta Level (app.ts buildRajaKataStop)
   — kartu polos spt .raja-trail (TANPA .trail-scene medan), bukan bagian
   tangga LEVELS jadi tidak butuh status locked/cleared apa pun. */
.raja-kata-stop{ background:var(--surface-2); border-radius:var(--r-lg); margin-bottom:var(--s3); }

/* Raja Balon — Balloon Pop (games/balloonpop.ts): prompt Bahasa Indonesia
   di atas, balon berisi kata Inggris naik terus-menerus dari bawah ke atas
   (CSS animation loop, TANPA JS per-frame). TIDAK ADA timer/nyawa/badge
   kesulitan (dibuang dari referensi kompetitor, CLAUDE.md kid-friendly
   wajib) — salah tap cuma goyang halus, balon TETAP naik & bisa ditap lagi.
   `.bp-balloon` (posisi+animasi rise/sway) & `.bp-balloon-body` (bentuk
   visual+shake salah) SENGAJA elemen terpisah supaya animasi rise/sway
   tidak pernah terhenti oleh shake salah tap.
   🔒 Revisi user: (1) papan "diperbesar, maksimalkan di mobile" — tinggi
   sekarang proporsional viewport (`min(vh,px)`, bukan angka tetap kecil);
   (2) balon WAJIB selalu muncul dari bawah + lebih lambat — `animation-
   delay` POSITIF bertingkat (`i*0.35s`, dari games/balloonpop.ts, BUKAN lagi
   delay negatif acak) + `animation-fill-mode:backwards` supaya selama jeda
   itu balon "menunggu" tersembunyi di keyframe 0% (bottom:-22%, di luar
   papan) — begitu delay habis baru kelihatan naik dari tepi bawah papan;
   loop berikutnya (`infinite`) otomatis "dari bawah" lagi tiap siklus. */
.bp-head{ display:flex; justify-content:space-between; gap:8px; margin-bottom:var(--s2); }
.bp-prompt{
  background:var(--sun-100); color:var(--sun-600); border-radius:var(--r-pill);
  padding:12px var(--s5); text-align:center; font-family:var(--font-head);
  font-weight:800; font-size:1.0625rem; margin:0 0 var(--s4);
}
.bp-prompt b{ color:var(--ink); }
.bp-board{
  position:relative; overflow:hidden;
  height:min(58vh, 520px); min-height:360px;
  border-radius:var(--r-xl); border:1px solid var(--line);
  background:linear-gradient(180deg, var(--c-listen-bg), var(--surface) 88%);
}
@media (min-width:560px){ .bp-board{ height:min(64vh, 620px); } }
.bp-balloon{
  position:absolute; bottom:6%; left:var(--x,10%); margin-left:0; opacity:1;
  /* Lebar RESPONSIF (bukan fixed 100px) — 4 lane @~24% papan gampang
     bertabrakan di layar sempit kalau lebar balon tetap; clamp ini jaga
     jarak aman di mobile TANPA balon jadi kekecilan di layar lebar. */
  width:clamp(58px, 18vw, 100px); padding:0; transform:translateX(-50%);
  animation:bpRise var(--dur,8s) linear infinite, bpSway var(--sway,2.6s) ease-in-out infinite;
  animation-delay:var(--delay,0s), var(--delay,0s);
  animation-fill-mode:backwards;
  filter:drop-shadow(0 6px 10px rgba(15,46,56,.2));
}
.bp-balloon:disabled{ cursor:default; }
.bp-balloon.is-pop{ animation:bpPop .38s ease forwards; pointer-events:none; }
.bp-balloon-body{
  position:relative; display:flex; align-items:center; justify-content:center;
  width:100%; aspect-ratio:5/6; padding:8px;
  background:var(--bg,var(--brand-500)); border-radius:50% 50% 50% 50% / 58% 58% 42% 42%;
}
.bp-balloon.is-wrong .bp-balloon-body{ animation:shake .32s ease; box-shadow:0 0 0 4px var(--try); }
.bp-balloon-body::before{
  content:''; position:absolute; top:16%; left:22%; width:26%; height:16%;
  background:rgba(255,255,255,.55); border-radius:50%; transform:rotate(-18deg);
}
.bp-balloon-string{ display:block; width:2px; height:22px; margin:0 auto; background:var(--ink-3); opacity:.55; }
.bp-balloon-text{
  font-family:var(--font-head); font-weight:800; font-size:clamp(.9375rem, 3.6vw, 1.0625rem); color:#fff;
  text-align:center; line-height:1.15; text-shadow:0 1px 2px rgba(0,0,0,.2);
}
@keyframes bpRise{
  0%{ bottom:-22%; opacity:1; }
  85%{ opacity:1; }
  100%{ bottom:118%; opacity:0; }
}
@keyframes bpSway{ 0%,100%{ margin-left:-12px; } 50%{ margin-left:12px; } }
@keyframes bpPop{
  0%{ transform:translateX(-50%) scale(1); opacity:1; }
  60%{ transform:translateX(-50%) scale(1.28); opacity:.7; }
  100%{ transform:translateX(-50%) scale(.2); opacity:0; }
}

/* Raja Susun — Sentence Puzzle (games/sentencepuzzle.ts), reskin "bubble
   piramida" ala referensi kompetitor (gambar+gelembung kata pengecoh+bar
   jawaban emas). Bubble sengaja PILL bulat penuh (bukan lingkaran kaku spt
   referensi) supaya kata Vocab apa pun (pendek/panjang) tetap terbaca. */
.sp-head{ display:flex; justify-content:flex-end; margin-bottom:var(--s2); }
.sp-sky{ position:relative; height:22px; margin-bottom:-2px; }
.sp-moon{ position:absolute; right:6%; top:-6px; font-size:1.375rem; opacity:.8; }
.sp-bird{ position:absolute; right:22%; top:4px; font-size:.875rem; opacity:.5; }
.sp-bird2{ right:29%; top:12px; font-size:.6875rem; opacity:.35; }
.sp-picture{
  background:linear-gradient(180deg, var(--c-listen-bg), var(--surface) 88%);
  border:1px solid var(--line); border-radius:var(--r-xl);
  padding:16px; text-align:center; max-width:220px; margin:0 auto var(--s3);
}
.sp-picture .big-emoji{ font-size:clamp(44px,11vw,60px); }
.sp-task{ margin:var(--s2) 0 var(--s2); text-align:center; font-weight:700; color:var(--ink-2); font-size:.9375rem; }
/* Arti Indonesia kalimat target (Pemanasan & Sedang ke atas, `sentencepuzzle-data.ts` TIER_CONFIG.prompt). */
.sp-meaning{
  background:var(--sun-100); color:var(--sun-600); border-radius:var(--r-pill);
  padding:10px var(--s5); text-align:center; font-family:var(--font-head);
  font-weight:800; font-size:1rem; margin:0 auto var(--s3); max-width:520px;
}
.sp-meaning b{ color:var(--ink); }
.sp-pyramid{ display:flex; flex-direction:column; gap:10px; align-items:center; margin:var(--s4) 0; }
.sp-row{ display:flex; flex-wrap:wrap; justify-content:center; gap:8px; }
.sp-bubble{
  background:radial-gradient(circle at 30% 26%, color-mix(in srgb, var(--k,var(--accent)) 16%, #fff), color-mix(in srgb, var(--k,var(--accent)) 9%, var(--surface)));
  border:2px solid color-mix(in srgb, var(--k,var(--accent)) 38%, transparent);
  color:var(--ink); border-radius:var(--r-pill);
  min-width:52px; padding:11px 16px; text-align:center;
  font-family:var(--font-head); font-weight:800; font-size:.9375rem; line-height:1.1;
  cursor:pointer; box-shadow:var(--sh-1); transition:transform .12s ease, opacity .15s ease;
}
.sp-bubble:active:not(:disabled){ transform:scale(.93); }
.sp-bubble.is-used{ opacity:.38; background:var(--muted); border-color:var(--line); box-shadow:none; cursor:default; }
.sp-bubble:disabled{ cursor:default; }
.sp-answer-bar{
  background:var(--sun-100); color:var(--sun-600); border:2px solid var(--sun-300);
  border-radius:var(--r-pill); padding:14px var(--s5); text-align:center;
  font-family:var(--font-head); font-weight:800; font-size:1.1875rem;
  margin:var(--s3) auto; max-width:520px;
}
.sp-answer-bar.empty{ color:var(--ink-3); font-size:.9375rem; font-weight:700; }
/* 🔒 Kalimat SALAH (permintaan user, CLAUDE.md "🔒 Aturan Wajib: Notifikasi
   Jawaban Salah") — flash merah singkat, pola sama `.answer-row.is-wrong`. */
.sp-answer-bar.is-wrong{ animation:shake .3s ease; background:var(--try-bg); border-color:var(--try); color:var(--try); }

/* Story Quest — Petualangan Cerita (games/storyquest.ts): halaman buku
   dibungkus kartu krem+border tipis ala buku dongeng (`.story-page`, BUKAN
   kartu quiz polos), ilustrasi lingkaran gradient (`.story-scene`) reuse
   bahasa visual `.sunburst` tapi utk ADEGAN cerita (bukan mascot). Jawaban
   SALAH cuma menonaktifkan tombol itu sendiri (opsi lain tetap bisa ditap
   langsung, TANPA tombol "Coba Lagi" terpisah) — cerita baru lanjut halaman
   berikutnya setelah jawaban BENAR (gerbang comprehension, bukan sekadar
   catatan progres spt raja lain). "💡 Petunjuk" (`.story-clue`) cuma
   kalimat penuntun baca ulang, BUKAN jawabannya langsung. */
.story-scene{
  width:104px; height:104px; margin:0 auto var(--s3); border-radius:50%;
  display:grid; place-items:center; font-size:clamp(44px,11vw,58px);
  background:radial-gradient(circle at 34% 26%, var(--sun-100) 0%, var(--c-vocab-bg) 78%);
  box-shadow:var(--sh-1);
}
.story-page{
  position:relative; background:linear-gradient(180deg,#FFFDF7,#FBF4E4);
  border:1px solid #EFE0BE; border-radius:var(--r-xl); padding:var(--s4) var(--s5);
  box-shadow:var(--sh-1); margin:var(--s3) 0 var(--s4);
}
.story-line{
  font-family:var(--font-head); font-weight:700; font-size:1.0625rem; line-height:1.55;
  color:var(--ink); text-align:center; margin:0 0 8px;
}
.story-line:last-child{ margin-bottom:0; }
.story-divider{ text-align:center; color:var(--sun-500); font-size:.9375rem; margin:var(--s3) 0; letter-spacing:.08em; }
.story-question{
  font-family:var(--font-head); font-weight:800; font-size:1.125rem; text-align:center;
  color:var(--ink); margin:0 0 var(--s3);
}
.story-clue{ text-align:center; color:var(--ink-2); }
/* Adventure map (progress halaman) reuse `.quiz-dot` APA ADANYA, tapi
   read-only (bukan quiz-dot jump-around) — `.static` matikan cursor:pointer
   warisan supaya tidak kelihatan "bisa diklik" padahal tidak. */
.quiz-dot.static{ cursor:default; }

@keyframes storyPageIn{
  from{ opacity:0; transform:translateX(18px); }
  to{ opacity:1; transform:translateX(0); }
}
.story-page-enter{ animation:storyPageIn .3s ease both; }

/* Magic Library — koleksi buku (CLAUDE.md kid-friendly: locked state tetap
   ramah "Segera Hadir", bukan placeholder rusak). MVP 1 buku terbuka. */
.library-grid{ display:grid; grid-template-columns:repeat(2,1fr); gap:var(--s3); margin:var(--s4) 0; }
@media (min-width:560px){ .library-grid{ grid-template-columns:repeat(3,1fr); } }
.library-card{
  background:var(--surface); border:2px solid var(--line); border-radius:var(--r-lg);
  padding:var(--s4) var(--s3); text-align:center; box-shadow:var(--sh-1);
}
.library-cover{ font-size:clamp(36px,9vw,48px); margin-bottom:8px; }
.library-title{ font-family:var(--font-head); font-weight:800; font-size:.875rem; color:var(--ink); margin:0 0 6px; }
.library-card.locked{ background:var(--muted); border-color:var(--line); }
.library-card.locked .library-cover{ opacity:.55; filter:grayscale(.4); }
.library-card.locked .library-title{ color:var(--ink-3); }

/* -------------------------------------------------------------- aktivitas */
.act-head{ display:flex; align-items:center; gap:var(--s3); margin-bottom:var(--s4); }
.act-head .txt{ flex:1; min-width:0; }
.act-head h1{ font-size:var(--fs-h2); }
.act-head .sub{ display:flex; align-items:center; gap:8px; margin-top:3px; }

.act-body{ display:grid; gap:var(--s4); }

.stepper{ display:flex; gap:8px; list-style:none; margin:0; padding:0; }
.stepper li{
  flex:1; display:flex; align-items:center; gap:8px; justify-content:center;
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-md);
  padding:9px 6px; font-size:var(--fs-eyebrow); font-weight:800; color:var(--ink-3);
  letter-spacing:.02em; text-align:center;
}
.stepper li .dot{
  width:20px; height:20px; border-radius:50%; flex:none;
  display:grid; place-items:center; font-size:.625rem;
  background:var(--muted); color:var(--ink-3); font-weight:800;
}
.stepper li.active{ background:var(--accent); border-color:var(--accent); color:#fff; box-shadow:var(--sh-1); }
.stepper li.active .dot{ background:rgba(255,255,255,.25); color:#fff; }
.stepper li.done{ background:var(--accent-bg); border-color:var(--accent-bg); color:var(--accent); }
.stepper li.done .dot{ background:var(--accent); color:#fff; }
.stepper li[role="button"]{ cursor:pointer; }

/* Panel suara di layar aktivitas — <details>: desktop terbuka (kolom samping
   lega), HP terlipat jadi 1 baris "🔊 Suara & kecepatan ▾" (audit UI
   2026-10: dulu selalu terbuka ±300px di bawah tiap soal). */
.voice-shown{
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-md);
  box-shadow:var(--sh-1); overflow:hidden;
}
.voice-shown-head{
  padding:12px var(--s4); min-height:44px; cursor:pointer; list-style:none;
  font-weight:800; font-size:var(--fs-meta); color:var(--ink-2);
  display:flex; align-items:center; gap:8px;
}
.voice-shown-head::-webkit-details-marker{ display:none; }
/* Terlipat di HP (panel ini sama isinya dgn Pengaturan, jangan makan layar
   di bawah soal), terbuka di desktop — `open` diset `renderActivity()`. */
.voice-shown-head::after{ content:'▾'; margin-left:auto; transition:transform .15s ease; }
.voice-shown[open] > .voice-shown-head::after{ transform:rotate(180deg); }
.voice-shown .voice-panel{ border:none; box-shadow:none; border-radius:0; margin:0; padding:0 var(--s4) var(--s4); }
/* Kolom samping desktop sempit — deretan pill mengisi lebar penuh & tiap
   tombol boleh menyusut, supaya "1.5x" tidak terpotong. */
.voice-shown .voice-panel-pills{ display:flex; width:100%; }
.voice-shown .pill-btn{ flex:1 1 0; min-width:0; padding-left:4px; padding-right:4px; white-space:nowrap; }

/* Panel suara (dipakai di Pengaturan & di layar aktivitas) */
.voice-panel{
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-md);
  box-shadow:var(--sh-1); padding:14px var(--s4);
  display:flex; flex-direction:column; gap:12px;
}
.voice-flush .voice-panel{ border:none; box-shadow:none; padding:0; border-radius:0; }
.voice-panel-row{ display:flex; flex-wrap:wrap; align-items:center; gap:10px; }
.voice-panel-label{ font-size:var(--fs-meta); font-weight:800; color:var(--ink-2); flex:none; }
.voice-panel-group{
  font-size:var(--fs-meta); font-weight:800; color:var(--ink); text-transform:uppercase; letter-spacing:.04em;
}
.voice-panel-group ~ .voice-panel-group{ margin-top:6px; padding-top:12px; border-top:1px dashed var(--line); }
.voice-panel-pills{ display:inline-flex; background:var(--muted); border-radius:var(--r-pill); padding:4px; gap:2px; }
.pill-btn{
  border:none; background:transparent; color:var(--ink-2);
  font-weight:800; font-size:var(--fs-meta); padding:7px 13px; border-radius:var(--r-pill);
  transition:background .15s ease, color .15s ease;
}
.pill-btn.active{ background:var(--brand-700); color:#fff; }
/* Target ketuk anak ≥44px di HP (audit UI 2026-10). */
@media (max-width:767px){ .pill-btn{ min-height:44px; } }
.voice-select{
  border:1px solid var(--line); background:var(--surface); border-radius:var(--r-pill);
  padding:8px 13px; font-weight:700; font-size:var(--fs-meta); color:var(--ink);
  font-family:var(--font-body); max-width:100%; flex:1; min-width:150px;
}

/* Menu Belajar — pemilih markas lain yang sudah terbuka (permintaan user:
   "gimana di menu belajar bisa pilih materi per level"), SATU baris ringkas
   (dropdown, bukan pill row 2 baris — revisi user: pill row "boros" & bikin
   konten belajar ketutup turun), konsisten dengan design awal yang tanpa
   header sama sekali kalau cuma 1 level tersedia. Ditaruh LANGSUNG sbg flex
   child `.stack` (di antara kartu "Lanjutin Yuk" & daftar skill, permintaan
   user — dulu di paling atas sebelum kartu itu) — TANPA div pembungkus/
   margin sendiri, `.stack{gap}` induknya sudah cukup ngurus jaraknya. */
.level-select-wrap{
  display:inline-flex; align-items:center; gap:6px;
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-pill);
  padding:4px 10px 4px 12px;
  /* Bug: di Menu Belajar pill ini sekarang jadi flex child LANGSUNG `.stack`
     (dulu dibungkus div block biasa) — `.stack` column-flex nge-stretch
     children ke lebar penuh secara default, jadi melebar sepanjang kartu
     (permintaan user: "terlalu panjang, samakan dgn list materi"). `align-
     self:flex-start` no-op kalau parent-nya bukan flex (mis. `.topics-
     toolbar`, block biasa) — aman dipasang di base rule, bukan cuma override
     khusus 1 halaman. */
  align-self:flex-start;
}
.level-select{
  border:none; background:transparent; color:var(--ink); font-weight:800;
  font-size:var(--fs-meta); font-family:var(--font-body); padding:2px 4px 2px 0;
  min-height:40px; /* target ketuk ≥44px bersama padding wrap */
}

/* --------------------------------------------------------- panggung game - */
/* 🔒 Permintaan user (revisi — fix sebelumnya TIDAK KEPAKAI): jarak kartu
   soal → panel "Suara & Kecepatan" dirapatkan via padding-BAWAH panggung
   yang dikecilkan (3-value shorthand: top | kiri-kanan | bawah). Rule
   `.act-stage > .card{padding-bottom:...}` sesi sebelumnya SIA-SIA krn
   selector ID `#stage` di sini spesifisitasnya lebih tinggi & override
   balik ke padding lama — HARUS diedit di ID ini langsung, bukan lewat
   class tambahan manapun. */
#stage{ padding:var(--s5) var(--s4) var(--s3); }
@media (min-width:768px){ #stage{ padding:var(--s6) var(--s5) var(--s4); } }

/* Panggung Tantangan Bos = #stage biasa, cuma dikasih rim mango supaya terasa
   "acara besar" dibanding modul biasa — tetap kartu yang sama, bukan redesain
   baru (non-scary, cuma sedikit lebih meriah). */
.boss-stage{ border-color:#F3DDA8; box-shadow:0 0 0 4px var(--sun-100), var(--sh-2); }
.boss-arena + .boss-stage{ border-top-left-radius:0; border-top-right-radius:0; border-top-color:#F3DDA8; }

.stage-badge{
  display:inline-block; font-size:var(--fs-eyebrow); font-weight:800; letter-spacing:.08em;
  padding:6px 13px; border-radius:var(--r-pill); margin-bottom:var(--s3);
  background:var(--accent-bg); color:var(--accent); text-transform:uppercase;
}

/* Baris judul soal + tombol di ujung kanan (Speaking "Ngobrol Yuk!" & "Giliranmu Bertanya": 💡 Petunjuk). */
.stage-head{ display:flex; align-items:center; justify-content:space-between; gap:10px; margin-bottom:var(--s3); }
.stage-head .stage-badge{ margin-bottom:0; white-space:nowrap; }
.stage-head .speak-btn-ghost{ padding:7px 12px; gap:6px; font-size:var(--fs-meta); flex-shrink:0; white-space:nowrap; }
@media (max-width:360px){ .stage-head .stage-badge{ letter-spacing:.03em; padding:6px 10px; } }

/* Placement test (games/placement.ts) — titik progres di atas soal, sama
   semangatnya dengan .stepper tapi lebih ringan (9 soal, bukan 3 langkah). */
.pt-progress{ display:flex; gap:6px; margin-bottom:var(--s4); }
.pt-progress span{ flex:1; height:8px; border-radius:var(--r-pill); background:var(--muted); }
.pt-progress span.done{ background:linear-gradient(90deg,var(--brand-500),var(--brand-700)); }

/* Intro placement test — 4 pil skill + kotak cerita TLDR jalur petualangan. */
.pt-skills{ display:flex; flex-wrap:wrap; gap:8px; margin-bottom:var(--s4); }
.pt-skill-pill{
  display:inline-flex; align-items:center; gap:6px;
  background:var(--muted); color:var(--ink-2); font-weight:800; font-size:var(--fs-meta);
  padding:8px 14px; border-radius:var(--r-pill);
}
/* Warna per skill sama dengan Belajar (content.ts SKILL_META) — biar 1 identitas visual. */
.pt-skill-pill.pt-vocab{ background:var(--c-vocab-bg); color:var(--c-vocab); }
.pt-skill-pill.pt-gram{ background:var(--c-gram-bg); color:var(--c-gram); }
.pt-skill-pill.pt-listen{ background:var(--c-listen-bg); color:var(--c-listen); }
.pt-skill-pill.pt-speak{ background:var(--c-speak-bg); color:var(--c-speak); }
.pt-skill-pill.pt-read{ background:var(--c-read-bg); color:var(--c-read); }

/* Konfirmasi keluar di tengah First Placement Test — muncul begitu anak
   tap balik/nav lain SAAT lagi jawab soal, biar progres tidak hilang
   tanpa sadar. Overlay custom (bukan window.confirm bawaan browser yang
   tidak bisa diatur gaya & terasa asing utk anak). */
.pt-confirm-overlay{
  position:fixed; inset:0; background:rgba(46,33,20,.45); backdrop-filter:blur(2px);
  display:flex; align-items:center; justify-content:center; padding:var(--s5); z-index:200;
}
.pt-confirm-card{
  background:var(--surface); border-radius:var(--r-lg); box-shadow:var(--sh-2);
  padding:28px 24px; max-width:340px; width:100%; text-align:center;
  animation:loginCardIn .25s ease;
}
/* Harus SETELAH .stage-badge: badge Bos memakai kedua kelas sekaligus, jadi
   yang menang adalah aturan yang ditulis belakangan (spesifisitasnya sama). */
.stage-badge.boss-badge{ background:linear-gradient(180deg,var(--sun-100),#FFE7BE); color:var(--sun-600); }
.big-emoji{ font-size:clamp(56px,14vw,76px); text-align:center; margin:var(--s2) 0 var(--s3); line-height:1; }

/* Kartu "Cara Main" (permintaan user, app.ts `renderCaraMain`) — badge
   nomor REUSE gaya `.topic-card .num` (rounded-square, warna --accent yang
   sudah di-set per-skill oleh `setAccent()`), bukan token baru. */
.cara-main-steps{ display:flex; flex-direction:column; gap:var(--s3); margin:var(--s4) 0; text-align:left; }
.cara-main-step{ display:flex; align-items:flex-start; gap:var(--s3); }
.cara-main-step .num{
  width:32px; height:32px; border-radius:10px; flex:none;
  display:grid; place-items:center; font-family:var(--font-head); font-weight:700; font-size:.9375rem;
  background:var(--accent-bg); color:var(--accent);
}
.cara-main-step p{ margin:0; }
.cara-main-step p b{ display:block; font-family:var(--font-head); font-size:var(--fs-h3); color:var(--ink); }
.cara-main-step p span{ font-size:var(--fs-meta); color:var(--ink-3); font-weight:700; }
.en-text{ font-family:var(--font-head); font-weight:700; font-size:clamp(1.25rem,1.05rem + .9vw,1.625rem); text-align:center; }
.id-text{ text-align:center; color:var(--ink-3); font-weight:700; font-size:var(--fs-meta); margin-top:3px; }

/* Reading (First Placement Test): kalimat dibaca SENDIRI, TANPA TTS — beda
   dari Listening (diucapkan). Font besar & line-height longgar khusus utk
   pembaca pemula (permintaan user, konsisten dgn .opt-btn-text Vocab).
   Warna pakai token --c-read/--c-read-bg biar identitas skill konsisten
   dgn .pt-skill-pill.pt-read. */
.reading-passage{
  background:var(--c-read-bg); border:3px solid var(--c-read); border-radius:var(--r-md);
  padding:var(--s4) var(--s5); margin:8px auto 14px; max-width:520px;
}
.reading-passage p{
  font-family:var(--font-head); font-weight:700; color:var(--ink);
  font-size:clamp(1.125rem,1rem + .6vw,1.5rem); line-height:1.5; margin:0 0 6px;
}
.reading-passage p:last-child{ margin-bottom:0; }
.reading-question{
  font-family:var(--font-head); font-weight:800; color:var(--ink); text-align:center;
  font-size:clamp(1.0625rem,.95rem + .5vw,1.3125rem); margin:0 0 14px;
}

/* Reading "Baca Kata" (Little Stars, format `ReadingWordTopic`) — kartu 1
   KATA ala flashcard, beda dari `.reading-passage` (kalimat/paragraf) di
   atas: font jauh lebih besar + letter-spacing lebar khusus menonjolkan
   BENTUK CETAK 1 kata (print awareness), bukan alur baca kalimat. */
.reading-word-card{
  background:var(--c-read-bg); border:3px solid var(--c-read); border-radius:var(--r-md);
  padding:22px var(--s5); margin:8px auto 14px; max-width:420px; text-align:center;
}
.reading-word-card b{
  display:block; font-family:var(--font-head); font-weight:800; color:var(--ink);
  font-size:clamp(1.75rem,1.35rem + 2vw,2.75rem); letter-spacing:.04em; line-height:1.2;
}

.speak-row{ display:flex; justify-content:center; align-items:center; gap:10px; margin:var(--s4) 0; flex-wrap:wrap; }
.speak-btn{
  background:var(--accent); color:#fff; border-radius:var(--r-pill);
  padding:13px 24px; font-weight:800; font-size:.9375rem;
  display:flex; align-items:center; gap:8px; box-shadow:var(--sh-1);
  transition:transform .12s ease;
}
.speak-btn:active{ transform:scale(.97); }
/* State "sedang merekam" (permintaan user: seragamkan tombol mic dgn gaya
   teks "Dengar Lagi" — `drawListenSpeakQuestion`, games/vocabulary.ts —
   dulu tombol mic bulat besar icon-only `.mic-btn`, sekarang pill teks
   `.speak-btn` biasa) — reuse PERSIS animasi `pulse`/warna `.mic-btn.
   listening` yang sudah ada, cuma dipindah ke selector `.speak-btn` juga. */
.speak-btn.listening{ animation:pulse 1s infinite; background:var(--try); }
/* Tombol sekunder di `.speak-row` (mis. "💡 Petunjuk" di sebelah "🔊 Dengar
   Lagi", modul Listening) — UKURAN sama persis dgn .speak-btn (padding
   dikurangi 2px tiap sisi krn border, supaya total box tetap sama), tapi
   ghost/outline (bukan warna solid) biar tidak tertukar sbg aksi utama. */
.speak-btn-ghost{
  background:var(--surface); color:var(--accent); border:2px solid var(--accent-bg);
  border-radius:var(--r-pill); padding:11px 22px; font-weight:800; font-size:.9375rem;
  display:flex; align-items:center; gap:8px; font-family:inherit;
}
.speak-btn-ghost:disabled{ background:var(--muted); color:var(--ink-3); border-color:var(--line); cursor:not-allowed; }

.primer-list{ display:flex; flex-direction:column; gap:10px; margin-top:var(--s3); }
.primer-item{
  display:flex; align-items:center; gap:8px;
  /* gap 12→8 & padding-x 16→12 (permintaan user "gambar lebih besar, card
     tetap"): ruang itu dipindahkan ke `.primer-ic` yg membesar 26→40px.
     Tanpa ini kolom teks kehabisan tempat & terjemahan panjang (mis. "Kebun
     Binatang" di `tempat-di-sekitar`) pecah 2 baris — tinggi barisnya ikut
     melar 74→97px & jadi tidak rata dgn baris lain. 3 tombol `.mini-play`
     SENGAJA tidak ikut dikecilkan (target tap anak, filter kid-friendly). */
  background:var(--accent-bg); border-radius:var(--r-md); padding:13px 12px;
}
/* Ikon kiri tiap baris Kenalan (permintaan user: "gambar di card kurang
   besar, buat lebih besar tapi card-nya tetap") — dulu `font-size:26px`
   inline yang diulang di 9 titik lintas 5 file game, sekarang 1 class biar
   ukurannya cukup diubah di sini. `line-height:1` + flex bikin kotak ikon
   PERSIS setinggi font-size-nya (bukan line-box ~1.5x spt sebelumnya),
   jadi ikon bisa dibesarkan TANPA ikut menaikkan tinggi `.primer-item`
   (tinggi baris tetap ditentukan blok teks di sebelahnya). */
.primer-ic{
  flex:none; display:flex; align-items:center; justify-content:center;
  font-size:40px; line-height:1;
}
/* Gambar custom (`KENALAN_ICON_IMAGE_OVERRIDES`) sedikit lebih besar dari
   emoji: emoji punya padding visual bawaan di dalam em box-nya, gambar
   `1em` tidak — tanpa selisih ini gambar terlihat lebih kecil dari emoji
   tetangganya di baris yang sama (rasio 32:26 lama dipertahankan). */
.primer-ic.is-img{ font-size:48px; }
.primer-item .txt{ flex:1; min-width:0; }
/* Kenalan Listening (kalimat utuh + 3 tombol): di HP tombol turun ke baris
   sendiri di kanan bawah — dulu kolom teks cuma ±100px & kalimat pecah
   per 2–3 kata (audit UI 2026-10). Desktop tetap 1 baris. */
@media (max-width:560px){
  .primer-item.is-sentence{ flex-wrap:wrap; row-gap:8px; }
  .primer-item.is-sentence > .txt{ flex:1 1 calc(100% - 64px); }
  .primer-item.is-sentence > .txt + .mini-play{ margin-left:auto; }
}
.primer-item .txt b{ font-family:var(--font-head); font-size:1.0625rem; display:block; }
.primer-item .txt span{ font-size:var(--fs-meta); color:var(--ink-2); font-weight:700; }
/* Baris "Lawan kata" (CLAUDE.md "Aturan Wajib: Topik Lawan Kata WAJIB
   Tampilkan Pasangannya di Kenalan") — `display:block` biar jadi baris ke-3
   sendiri (bukan nempel di span terjemahan yg inline), warna lebih pudar
   (--ink-3) drpd terjemahan (--ink-2) krn ini info suplemen, bukan konten
   utama yg diuji. */
.primer-item .txt .opp{ display:block; font-size:var(--fs-meta); color:var(--ink-3); font-weight:600; margin-top:2px; }
/* Reset `.txt b` (font besar utk kata utama) — `<b>` di dalam `.opp` cuma
   penekanan ringan pada kata lawan-nya, BUKAN kata utama baris ini. */
.primer-item .txt .opp b{ display:inline; font-family:inherit; font-size:inherit; font-weight:700; color:inherit; }
/* Grammar format kalimat (`games/grammar.ts`): kata pola disorot di Kenalan
   & hasil Tantangan, arti Indonesia 1 baris sendiri, aturan 1 baris (tier
   Lanjut), rumpang "___" di Tantangan. */
.primer-item .txt .g-id{ display:block; margin-top:2px; }
mark.g-key{ background:var(--sun-300); color:var(--ink); border-radius:6px; padding:0 4px; font-weight:800; }
.g-rule{ background:var(--surface); border:2px dashed var(--line); border-radius:var(--r-md); padding:10px 14px; margin:0 auto 12px; max-width:560px; font-weight:700; font-size:var(--fs-meta); color:var(--ink-2); line-height:1.45; }
.g-blank{ display:inline-block; min-width:3.2em; border-bottom:3px solid var(--accent); color:transparent; }
.g-meaning{ margin-top:4px; }
/* Terjemahan Indonesia di soal Grammar yang soalnya kalimat INGGRIS (Pilih
   Bentuk, Detektif Kalimat) — dibuat menonjol (permintaan user "user kurang
   aware") tapi SENGAJA lebih kecil dari `.en-text` (kalimat Inggris = soal). */
.g-trans{ font-family:var(--font-head); font-weight:700; font-size:clamp(1.0625rem,1rem + .3vw,1.1875rem); line-height:1.35; color:var(--c-gram); text-align:center; margin:6px 0 2px; }
.g-decoy-note{ text-align:center; margin:6px 0 0; }
/* Teks pendek Tantangan Achiever: kalimat konteks di atas kalimat berumpang,
   kata kunci disorot setelah menjawab. */
.g-context{ max-width:560px; margin:6px auto 4px; text-align:center; }
.g-context p{ margin:2px 0; font-weight:700; color:var(--ink-2); font-size:1rem; line-height:1.45; }
mark.g-cue{ background:var(--brand-100); color:var(--ink); border-radius:6px; padding:0 4px; font-weight:800; }
.g-text-note, .g-cue-note{ text-align:center; margin:6px 0 0; }
.mini-play{
  width:38px; height:38px; border-radius:50%; background:var(--surface); color:var(--accent);
  display:grid; place-items:center; font-size:15px; flex:none; cursor:pointer;
  box-shadow:var(--sh-1);
}
/* Mic mini di Kenalan Vocab (permintaan user) — reuse animasi pulse yang
   sama dgn .mic-btn.listening (Speaking First Placement Test), cuma versi
   kecil biar konsisten pas "sedang mendengarkan". */
.mini-play.listening{ animation:pulse 1s infinite; background:var(--try); color:#fff; }
/* Sudah ditap (dengarkan/mic/main, permintaan user) — warna beda dari status
   "listening" di atas, sama palet dgn .topic-card.done di bawah supaya
   "sudah dicoba" kebaca konsisten di semua tempat. */
.mini-play.done{ background:var(--ok-bg); color:var(--ok); }

/* Popup hasil mic Kenalan — kompak, overlay generik (pola sama dgn
   .pt-confirm-overlay First Placement Test), muncul SETELAH hasil mic ada,
   bukan menjelaskan langkah "sedang merekam" (itu tetap di tombol mic
   mini-nya sendiri lewat kelas .listening di atas). */
.mic-pop-overlay{
  position:fixed; inset:0; background:rgba(46,33,20,.45); backdrop-filter:blur(2px);
  display:flex; align-items:center; justify-content:center; padding:var(--s5); z-index:200;
}
.mic-pop-card{
  background:var(--surface); border-radius:var(--r-lg); box-shadow:var(--sh-2);
  padding:24px 22px; max-width:300px; width:100%; text-align:center;
  animation:loginCardIn .25s ease;
}

.primary-btn{
  display:block; width:100%; background:var(--accent); color:#fff; border-radius:var(--r-md);
  padding:15px; font-family:var(--font-head); font-weight:700; font-size:1.0625rem;
  margin-top:var(--s4); box-shadow:var(--sh-1);
  transition:transform .12s ease;
}
.primary-btn:active{ transform:scale(.985); }

/* CTA "Yuk Mulai" placement test — napas halus + cincin mango melebar,
   reuse warna & rasa yang sama dgn hereHalo (medali "kamu di sini" di Peta
   Level) supaya konsisten sebagai bahasa visual "lihat sini, tap ini". */
@keyframes ctaPulse{
  0%,100%{ box-shadow:var(--sh-1), 0 0 0 0 rgba(255,181,61,.55); transform:scale(1); }
  50%{ box-shadow:var(--sh-1), 0 0 0 12px rgba(255,181,61,0); transform:scale(1.015); }
}
.pt-cta{ animation:ctaPulse 1.8s ease-in-out infinite; }
.pt-cta:active{ animation:none; }
.pt-cta:disabled{ animation:none; transform:none; }
@media (prefers-reduced-motion: reduce){ .pt-cta{ animation:none; } }

/* 🔒 Revisi user: "bukan button nya yang terlihat menyala tapi icon 💡" —
   versi awal (`.pt-cta` di seluruh tombol, ring box-shadow spt "🔊 Dengar")
   DIBALIK: SEMUA tombol "💡 Petunjuk" app-wide sekarang cuma glow di ICON
   lampunya SAJA (`<span class="hint-bulb">💡</span>`), bukan lagi seluruh
   badan tombol — `filter:drop-shadow` (bukan `text-shadow`, drop-shadow
   ikut bentuk siluet emoji jadi hasilnya lebih mirip cahaya lampu beneran)
   + scale halus, warna kuning-mango konsisten `--sh-1`/mango palette app
   ini. Berhenti glow otomatis begitu tombol induknya `disabled` (hint
   sekali-pakai sudah dipakai/terungkap) via selector `button:disabled
   .hint-bulb` — berlaku baik `disabled` dari template MAUPUN dari JS
   (`wireHint`/`wireTransformHint`), sama pola dgn `.pt-cta:disabled`. */
@keyframes bulbGlow{
  0%,100%{ filter:drop-shadow(0 0 0 rgba(255,181,61,0)); transform:scale(1); }
  50%{ filter:drop-shadow(0 0 6px rgba(255,181,61,.95)); transform:scale(1.18); }
}
.hint-bulb{ display:inline-block; animation:bulbGlow 1.4s ease-in-out infinite; }
button:disabled .hint-bulb{ animation:none; filter:none; transform:none; }
@media (prefers-reduced-motion: reduce){ .hint-bulb{ animation:none; } }
.ghost-btn{
  display:block; width:100%; background:var(--surface); color:var(--accent);
  border:2px solid var(--accent-bg); border-radius:var(--r-md);
  padding:13px; font-family:var(--font-head); font-weight:700; font-size:1rem; margin-top:10px;
  text-decoration:none; text-align:center; box-sizing:border-box;
}
.ghost-btn:disabled{
  background:var(--muted); color:var(--ink-3); border-color:var(--line);
  cursor:not-allowed;
}

.opt-grid{
  display:grid; grid-template-columns:1fr 1fr; gap:var(--s3);
  margin:var(--s4) auto 0; max-width:520px;
}
.opt-grid.three{ grid-template-columns:repeat(3,minmax(0,1fr)); }
.opt-grid.three .opt-btn{ min-width:0; overflow-wrap:anywhere; }
/* Grammar Tantangan "Pilih Bentuk" — 4 opsi 2×2 (permintaan user "4 card
   atau 2 card supaya tidak ganjil"; dulu 3 kolom & opsi panjang meluber di
   HP). Opsi panjang ("is going to visit") turun baris DI DALAM kartu. */
/* Trailblazer (format transform): 4 opsi kalimat utuh → 1 kolom. */
.opt-grid.g-transform{ grid-template-columns:1fr; max-width:620px; margin-left:auto; margin-right:auto; }
.opt-grid.g-transform .opt-btn-text{ font-size:1.0625rem; padding:14px 16px; line-height:1.35; }
.opt-grid.g-choose{ grid-template-columns:repeat(2,minmax(0,1fr)); }
.opt-grid.g-choose > *:last-child:nth-child(odd){ grid-column:auto; }
.opt-grid.g-choose .opt-btn{ min-width:0; overflow-wrap:anywhere; }
.opt-grid.g-choose.long .opt-btn{ font-size:1.0625rem; padding:16px 10px; line-height:1.25; }
/* 🔒 5 opsi (Latihan Inti Achiever/Trailblazer, `latihanDistractorCount`) —
   kartu TERAKHIR yang sekaligus JUMLAH GANJIL (selector `:last-child:nth-
   child(odd)` — cuma match kalau total anak ganjil, mis. 3 atau 5) melebar
   1 baris penuh, bukan nyisa sendirian di kolom kiri dgn ruang kosong di
   kanan (CLAUDE.md "hitung kolom yang bikin jumlah item rapi"). Kasus 4
   kartu (paling umum) TIDAK kena selector ini (kartu ke-4 genap), tetap
   2×2 spt sebelumnya. */
.opt-grid > *:last-child:nth-child(odd){ grid-column:1 / -1; }
/* `.opt-grid.three` = PERSIS 3 opsi dalam 1 baris — aturan "kartu ganjil
   terakhir melebar" di atas TIDAK berlaku (kalau berlaku, kartu ke-3 turun
   sendirian ke baris 2). */
.opt-grid.three > *:last-child:nth-child(odd){ grid-column:auto; }
/* Grammar Latihan Inti pola: 4 kartu (2×2) atau 6 kartu (Starter) — HP
   2 kolom (3 baris), desktop 3 kolom (2 baris rapi). */
.opt-grid.pattern-cards .opt-btn{ padding-top:12px; padding-bottom:12px; }
@media (min-width:700px){ .opt-grid.pattern-cards.six{ grid-template-columns:repeat(3,1fr); max-width:600px; } }
.opt-btn{
  background:var(--surface); border:3px solid var(--line); border-radius:var(--r-md);
  /* 🔒 Permintaan user: gambar/kartu Kenalan "Main" WAJIB ukuran SAMA PERSIS
     dgn kartu jawaban Latihan Inti (`.opt-btn.answer-card`/`.answer-card-
     emoji` di bawah) — padding & font-size disamakan ke angka yang sama,
     TETAP 2 kartu per baris (`.opt-grid` tidak diubah). */
  /* Permintaan user lanjutan: gambarnya dibesarkan (44-62px → 56-78px) TAPI
     kartunya tetap — padding vertikal dikurangi 24px→18px supaya tambahan
     tinggi gambar terserap di situ, bukan bikin kartu ikut membesar. */
  padding:18px 14px; text-align:center; font-size:clamp(56px,14vw,78px); line-height:1.1;
  transition:transform .12s ease, border-color .15s ease;
}
.opt-btn .lbl,
.pattern-scene .lbl{
  display:block; font-family:var(--font-head); font-size:.8125rem; font-weight:700;
  color:var(--ink-2); margin-top:6px;
}
/* Opsi teks-saja (Vocab First Placement Test — permintaan user: ID→EN
   tanpa gambar) — font besar & tebal jadi konten utama tombol, bukan
   ukuran emoji (clamp 30-44px di atas kekecilan/kebesaran utk kata). */
.opt-btn.opt-btn-text{
  font-size:1.375rem; font-family:var(--font-head); font-weight:800;
  color:var(--ink); padding:20px 14px; line-height:1.3;
}
/* Grammar Kenalan 🎮 "Cocok atau Tidak?" — SATU gambar kontras yang
   dinilai (bukan tombol), ukuran sama kartu `.opt-btn`. */
.pattern-scene{
  width:min(220px,70%); margin:var(--s2) auto var(--s3); background:var(--surface);
  border:3px solid var(--line); border-radius:var(--r-md); padding:18px 14px;
  text-align:center; font-size:clamp(56px,14vw,78px); line-height:1.1;
}
/* Grammar Kenalan 🎮 "Dengar & Pilih" — 2 PANEL berwarna supaya jelas mana
   soal & mana jawaban (permintaan user): ① panel soal krem-mango (dengar
   kalimat); ② panel jawaban toska berisi pertanyaan KHUSUS TOPIK + 2 kartu
   gambar berlabel. Gaya "chunky" (bayangan padat) ala mainan anak. */
.gm-panel{
  max-width:600px; margin:var(--s3) auto 0; border-radius:24px; padding:12px 12px 14px;
}
.gm-question{ background:var(--sun-100); border:3px solid var(--sun-300); }
.gm-answer{ background:var(--brand-50); border:3px solid var(--brand-100); }
.gm-step{
  display:flex; align-items:center; justify-content:center; gap:8px; margin:0 0 10px;
  font-family:var(--font-head); font-weight:800; font-size:.9375rem; color:var(--ink); text-align:center;
}
.gm-step b{ color:var(--c-gram); }
.gm-caption{ font-weight:700; font-size:.9375rem; color:var(--ink-2); line-height:1.35; }
.gm-caption b{ color:var(--c-gram); font-weight:900; }
.gm-said{ font-family:var(--font-head); font-weight:800; font-size:1rem; color:var(--ink); line-height:1.3; }
.gm-said-id{ font-family:var(--font-body, inherit); font-weight:700; font-size:.9375rem; color:var(--ink-2); }
.gm-said-id::before{ content:'· '; color:var(--ink-3); }
.gm-answers{ margin-top:0; max-width:none; gap:12px; }
.gm-ans{
  display:flex; flex-direction:column; align-items:center; gap:4px; padding:14px 8px !important;
  border-radius:20px; transition:transform .12s ease;
}
.gm-ans:active{ transform:translateY(3px); }
.gm-ans-a:not(.correct):not(.wrong){ border-color:var(--brand-300); box-shadow:0 5px 0 var(--brand-300); }
.gm-ans-b:not(.correct):not(.wrong){ border-color:var(--sun-400); box-shadow:0 5px 0 var(--sun-400); }
.gm-ans-label{ font-family:var(--font-head); font-weight:800; font-size:1.125rem; color:var(--ink); }
/* Panel ① "Dengarkan kalimatnya" — tombol Dengar di tengah, teks/arti
   kalimat muncul di bawahnya (lewat 💡 Petunjuk atau sesudah menjawab). */
.gm-listen{ display:flex; flex-direction:column; align-items:center; gap:8px; padding:4px 0 6px; text-align:center; }
/* Kartu pilihan panel ② — gambar kontras besar + label konteks topik. */
.gm-choice .gm-choice-pic{ display:flex; flex-direction:column; align-items:center; min-height:96px; justify-content:center; overflow:hidden; max-width:100%; }
.gm-choice .gm-ans-label{ font-size:1rem; line-height:1.25; }
/* Lencana pembeda kartu kontras (✅/😊/🔍/👦 …) — dulu ikut `.lbl` 13px. */
.opt-btn .cv-badge, .cv-badge{ line-height:1.1; margin-top:4px; }
/* Label konsep di kartu kontras Grammar (Latihan Inti/Tantangan) — cuma utk lencana yang tidak langsung terbaca (LABELED_VISUALS, games/grammar.ts). */
.pattern-card-label{ display:block; margin-top:6px; font-family:var(--font-head); font-weight:800; font-size:0.9375rem; line-height:1.2; color:var(--ink); }
/* Grammar Tantangan "🕵️ Detektif Kalimat" — kata kalimat bisa di-tap. */
.dt-sentence{ gap:8px; }
.chip.dt-word{ cursor:pointer; font-size:clamp(1.25rem,1.1rem + .6vw,1.5rem); }
.chip.dt-found{ background:var(--ok-bg); border-color:var(--ok); color:var(--ok); }
.chip.dt-found s{ opacity:.6; }
.chip.dt-miss.is-wrong{ background:var(--try-bg); border-color:var(--try); color:var(--try); animation:shake .3s ease; }
.pattern-card-label.big{ font-size:1.125rem; margin-top:8px; }

/* Grammar Tantangan — ikon 🔊 pilihan A/B "bersuara" selama TTS diputar:
   ikon berdenyut + 2 gelombang suara melebar dari ikon. */
.sound-ic{ position:relative; display:inline-block; line-height:1; }
/* Tantangan Grammar Little Stars/Starter: gambar + 2 pilihan suara + tombol
   "✅ Pilih Jawaban Ini" harus muat 1 layar HP (dulu tombolnya tertutup tab
   bar bawah, audit UI 2026-10) — di HP gambar & kartu suara dipadatkan. */
.g-listen-pic{ text-align:center; margin:14px 0; }
@media (max-width:560px){
  .g-listen-pic{ margin:6px 0; }
  .opt-grid.g-listen .opt-btn{
    display:flex; flex-wrap:wrap; align-items:center; justify-content:center; gap:10px;
    padding:10px; min-height:64px; font-size:36px;
  }
  .opt-grid.g-listen .opt-btn .lbl{ margin-top:0; font-size:1.125rem; color:var(--ink); }
  .opt-grid.g-listen .opt-btn .choice-hint{ flex-basis:100%; }
}
.sound-ic.playing{ animation:soundTalk .45s ease-in-out infinite alternate; }
.sound-ic.playing::before,
.sound-ic.playing::after{
  content:''; position:absolute; left:50%; top:50%; width:100%; height:100%;
  border:3px solid var(--accent); border-radius:50%; pointer-events:none;
  transform:translate(-50%,-50%) scale(.6); opacity:0;
  animation:soundWave 1.1s ease-out infinite;
}
.sound-ic.playing::after{ animation-delay:.55s; }
@keyframes soundTalk{ from{ transform:scale(1) rotate(-4deg); } to{ transform:scale(1.14) rotate(4deg); } }
@keyframes soundWave{
  0%{ transform:translate(-50%,-50%) scale(.6); opacity:.7; }
  100%{ transform:translate(-50%,-50%) scale(1.6); opacity:0; }
}
@media (prefers-reduced-motion: reduce){
  .sound-ic.playing{ animation:none; }
  .sound-ic.playing::before, .sound-ic.playing::after{ animation:none; opacity:.5; transform:translate(-50%,-50%) scale(1.2); }
}
/* Grammar Tantangan 💡 Petunjuk — teks EN+ID di bawah 🔊 A/B. */
.opt-btn .choice-hint{
  display:block; margin-top:6px; font-family:var(--font-head); font-size:.8125rem;
  font-weight:600; color:var(--ink-2); line-height:1.35;
}
.opt-btn .choice-hint b{ display:block; font-size:.9375rem; font-weight:800; color:var(--ink); }
.opt-btn.correct{ border-color:var(--ok); background:var(--ok-bg); animation:pop .3s ease; }
/* 🔒 Permintaan user: notifikasi salah wajib SEJELAS notifikasi benar (bg
   merah muda penuh, bukan cuma border tipis) — lihat CLAUDE.md "🔒 Aturan
   Wajib: Notifikasi Jawaban Salah". Dipasangkan SELALU dgn `playWrongTone()`
   ("tetot") + `vibrateDevice()` di JS, kecuali jalur skor mic/Speaking. */
.opt-btn.wrong{ border-color:var(--try); background:var(--try-bg); color:var(--try); animation:shake .3s ease; }
/* Dipilih tapi BELUM dikonfirmi (Grammar Tantangan "Lihat & Dengar, Pilih
   yang Pas" — dengar dulu boleh berkali-kali/ganti pilihan, baru commit lewat
   tombol terpisah "Pilih Jawaban Ini") — beda dari .correct/.wrong yang
   sudah final/terkunci. */
.opt-btn.selected{ border-color:var(--accent); background:var(--accent-bg); }
/* Terkunci setelah dijawab (permintaan user: tombol Coba Lagi/Lanjut di
   bawah yang jadi jalan lanjut, bukan tap opsi lain) & opsi yang dimatikan
   hint 50/50 — dua kasus beda TAPI visualnya sama: redup, tidak bisa ditap. */
.opt-btn:disabled{ opacity:.4; pointer-events:none; }
.opt-btn.eliminated{ text-decoration:line-through; }

/* Kartu jawaban gambar+teks+lencana huruf (permintaan user: "analogi
   dibantu image", contoh kompetitor) — co-class dgn `.opt-btn` (bukan
   pengganti), jadi warisi border/padding/correct/wrong/disabled dari situ,
   cuma isi & layout dalamnya yang beda. Diramping-kan (permintaan user:
   tombol Coba Lagi/Lanjut jadi ketutup) — emoji & padding dikecilkan,
   layout HORIZONTAL (emoji kiri, label+lencana kanan) bukan lagi vertikal
   gambar-di-atas, supaya tinggi kartu jauh lebih pendek. Teks tetap besar
   & tebal (permintaan user), cuma areanya yang diperkecil, bukan font-nya. */
/* 🔒 Permintaan user (revisi lagi — BALIK ke vertikal, kebalikan dari
   catatan "Diramping-kan...horizontal" di atas yg sekarang SUDAH TIDAK
   BERLAKU): "kalau ada text maka text-nya di bawah gambar untuk semuanya,
   tinggi diperbesar sedikit lagi" — gambar/emoji SELALU di ATAS (konten
   utama, dibesarkan), label+lencana (kalau ada) SELALU di BAWAH gambar,
   BUKAN lagi di samping. `.picture-only` (tanpa label) jadi TIDAK PERLU
   override layout apa pun lagi — bentuknya SUDAH sama persis default,
   cuma beda ADA/TIDAKNYA elemen label saja. Efek samping BAGUS: kata
   panjang tanpa spasi (mis. "Grandmother"/"Kakak/Adik Perempuan") sekarang
   dapat LEBAR PENUH kartu utk wrap (tidak lagi berbagi baris sempit dgn
   emoji), jauh lebih longgar drpd layout horizontal lama. */
.opt-btn.answer-card{
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:10px;
  /* `min-width:0` WAJIB (kartu ini GRID ITEM `.opt-grid`, grid item default
     `min-width:auto` bukan 0 — tanpa ini kartu menolak menyusut ke `1fr`,
     bug yg pernah ditemukan sesi sebelumnya). */
  min-width:0; min-height:0; padding:18px 14px; text-align:center;
}
/* Ukuran WAJIB tetap sinkron dgn `.opt-btn` di atas (aturan lama), jadi
   pembesaran gambar sesi ini diterapkan ke KEDUANYA sekaligus. */
.answer-card-emoji{ font-size:clamp(56px,14vw,78px); line-height:1; }
.answer-card-bottom{ display:flex; align-items:center; justify-content:center; gap:8px; min-width:0; }
.answer-card-label{
  font-family:var(--font-head); font-weight:800; font-size:1.0625rem; line-height:1.3; color:var(--ink);
  min-width:0; overflow-wrap:break-word;
}
.answer-card-badge{
  flex:none; width:25px; height:25px; border-radius:50%; background:var(--muted); color:var(--ink-3);
  font-family:var(--font-head); font-weight:800; font-size:.75rem;
  display:flex; align-items:center; justify-content:center;
}
.opt-btn.answer-card.correct .answer-card-badge{ background:var(--ok); color:#fff; }

/* Header soal Latihan Inti (permintaan user: "button petunjuk simpan di
   atas sejajar text LENGKAPI KALIMAT") — stage-badge kiri, tombol Petunjuk
   kanan, satu baris, bukan lagi block penuh di bawah opsi. */
.latihan-head{ display:flex; align-items:center; justify-content:space-between; gap:10px; flex-wrap:wrap; }
.latihan-head .stage-badge{ margin-bottom:0; }
/* Grammar: badge panjang boleh 2 baris, Petunjuk TETAP sejajar kanan
   (bukan turun ke baris sendiri di HP). */
.latihan-head.no-wrap{ flex-wrap:nowrap; }
.latihan-head.no-wrap .stage-badge{ white-space:normal; min-width:0; line-height:1.3; }
.latihan-head.no-wrap .hint-chip{ flex-shrink:0; white-space:nowrap; }
/* Badge yang isinya sama dgn nama tab Tantangan di atasnya — disembunyikan
   HANYA saat ada tab (`#tantanganStage`), supaya judul tidak tampil 2x;
   tanpa tab (mis. Speaking Little Stars) badge tetap tampil. */
#tantanganStage .stage-badge.tab-dup{ display:none; }
#tantanganStage :is(.stage-head,.latihan-head):has(> .stage-badge.tab-dup){ justify-content:flex-end; }
.hint-chip{ display:inline-block; width:auto; margin-top:0; padding:7px 14px; font-size:.8125rem; }

/* Tombol wajib "Coba Lagi"/"Lanjut" di tiap soal (permintaan user, CLAUDE.md
   "Format Wajib Materi Vocabulary") — muncul SETELAH jawaban, ganti
   auto-advance timer lama. */
.round-actions{ display:flex; gap:10px; margin-top:14px; }
.round-actions .ghost-btn{ flex:1; margin-top:0; }
.round-actions .primary-btn{ flex:1; }

/* Hint/clue "didampingi" — Latihan Inti SAJA (permintaan user: Tantangan
   sengaja TANPA ini), tombol kecil terpisah dari opsi jawaban. */
.hint-row{ display:flex; justify-content:center; margin-top:10px; }
.hint-row .ghost-btn{ margin-top:0; padding:8px 18px; font-size:var(--fs-meta); }

/* Navigasi bebas per soal (permintaan user, Latihan Inti & Tantangan) —
   beda dari `.round-actions` (itu pasca-jawab, ini murni pindah tempat,
   boleh dipakai kapan saja & tidak pernah mengunci). Ditaruh di atas soal,
   di bawah stage-badge (kalau ada).
   Revisi #2 (permintaan user: "2 tombol Lanjut redundant") — tombol "➡️
   Lanjut" arah maju DIHAPUS dari sini (satu-satunya "Lanjut" sekarang cuma
   yang pasca-jawab, `.round-actions`); titik status jadi <button> BERNOMOR
   & DIPERBESAR jadi target tap sungguhan (CLAUDE.md "target tap besar" —
   versi lama 9-12px terlalu kecil buat jari anak).
   Revisi #3 (permintaan user: "remove button kembali ... sudah ada number
   yang bisa diklik") — tombol "⬅️ Kembali" JUGA dihapus: nomor soal
   sebelumnya sendiri sudah jadi jalan mundur, jadi sekarang nav ini CUMA
   titik-titik nomor, tidak ada tombol terpisah sama sekali. */
.quiz-nav{ margin:10px 0 4px; }
.quiz-dots{ display:flex; justify-content:center; gap:8px; flex-wrap:wrap; }
.quiz-dot{
  width:30px; height:30px; border-radius:50%; flex:none;
  display:grid; place-items:center; font-family:var(--font-head); font-weight:800; font-size:.8125rem;
  background:var(--surface); color:var(--ink-3);
  box-shadow:inset 0 0 0 1.5px var(--line); cursor:pointer;
}
.quiz-dot.done{ background:var(--ok-bg); color:var(--ok); box-shadow:inset 0 0 0 1.5px var(--ok); }
/* 🔒 Permintaan user: dot soal yang SEDANG dibuka/disinggahi wajib beda
   warna JELAS dari yang belum/sudah dijawab — bukan cuma ring tipis di
   atas bg putih yang sama dgn dot polos (gampang kelewat anak/ortu).
   Sekarang solid var(--brand-500) + ring "glow" luar dipertahankan
   sbg penguat fokus, bukan satu-satunya pembeda lagi. */
.quiz-dot.current{ background:var(--brand-500); color:#fff; box-shadow:0 0 0 3px var(--brand-100); }
.quiz-dot.current.done{ background:var(--ok); color:#fff; box-shadow:0 0 0 3px var(--brand-100); }
/* HP: 9–12 nomor tidak muat 1 baris & dulu pecah timpang (8+2) — dibagi
   rata 2 baris (5+5 / 6+6) sekaligus dibesarkan jadi target ketuk 36px
   (audit UI 2026-10). Lebar = n×36 + (n−1)×10. */
@media (max-width:560px){
  .quiz-dots:not(.dense):has(> .quiz-dot:nth-child(9)):not(:has(> .quiz-dot:nth-child(13))){
    gap:10px; margin-inline:auto;
  }
  .quiz-dots:not(.dense):has(> .quiz-dot:nth-child(9)):not(:has(> .quiz-dot:nth-child(11))){ max-width:220px; }
  .quiz-dots:not(.dense):has(> .quiz-dot:nth-child(11)):not(:has(> .quiz-dot:nth-child(13))){ max-width:266px; }
  .quiz-dots:not(.dense):has(> .quiz-dot:nth-child(9)):not(:has(> .quiz-dot:nth-child(13))) > .quiz-dot{
    width:36px; height:36px;
  }
}

/* "⌫ Hapus Huruf"/"⌫ Hapus Kata" berdampingan dgn "🔄 Ulang Susunan"/
   "🔄 Bersihkan" (permintaan user, Tantangan). */
.letter-actions{ display:flex; justify-content:center; gap:8px; margin-top:10px; flex-wrap:wrap; }
.letter-actions .ghost-btn{ margin-top:0; flex:1; max-width:180px; }
/* Versi lebih ramping (permintaan user: "Petunjuk"/"Hapus Huruf"/"Ulang
   Susunan" 3 tombol sekaligus di Eja Kata) — padding & font lebih kecil
   drpd `.ghost-btn` biasa, supaya 3 tombol tetap enteng dilihat sebaris. */
.letter-actions .ghost-btn.slim{ padding:8px 10px; font-size:.8125rem; max-width:150px; }

/* 2 tab permanen Tantangan (permintaan user: "kalau ingin kembali ke
   tantangan sebelumnya susah" — dulu Eja Kata → Contoh Penggunaan berantai
   lurus tanpa jalan balik) — pola sama dgn `.stepper` (Kenalan/Latihan
   Inti/Tantangan di atasnya), skala lebih kecil krn cuma 2 sub-bagian.
   Pindah tab kapan saja, dua-duanya resume dari cursor masing-masing. */
.tantangan-tabs{ display:flex; gap:8px; margin-bottom:var(--s4); }
.tantangan-tab{
  flex:1; background:var(--surface); border:1px solid var(--line); border-radius:var(--r-md);
  padding:10px 8px; font-family:var(--font-head); font-size:var(--fs-meta); font-weight:800;
  color:var(--ink-3); text-align:center; cursor:pointer;
}
.tantangan-tab.active{ background:var(--accent); border-color:var(--accent); color:#fff; box-shadow:var(--sh-1); }

@keyframes pop{ 0%{transform:scale(1);} 50%{transform:scale(1.1);} 100%{transform:scale(1);} }
@keyframes shake{ 0%,100%{transform:translateX(0);} 25%{transform:translateX(-6px);} 75%{transform:translateX(6px);} }

/* Burst kecil di atas tombol yang benar — First Placement Test (§ celebrate
   animasi). Pakai ::after (bukan elemen baru) supaya tidak perlu ubah JS
   struktur DOM tombol yang sudah ada di game lain. */
.opt-btn.pt-celebrate{ position:relative; overflow:visible; }
.opt-btn.pt-celebrate::after{
  content:'⭐✨⭐'; position:absolute; left:50%; top:-6px; transform:translate(-50%,0);
  font-size:20px; white-space:nowrap; pointer-events:none;
  animation:ptBurst .7s ease-out forwards;
}
@keyframes ptBurst{
  0%{ opacity:0; transform:translate(-50%,4px) scale(.5); }
  35%{ opacity:1; transform:translate(-50%,-14px) scale(1.15); }
  100%{ opacity:0; transform:translate(-50%,-34px) scale(1); }
}
@media (prefers-reduced-motion: reduce){
  .opt-btn.pt-celebrate::after{ animation:none; opacity:0; }
}

/* Celebrate "podium" (permintaan user, CLAUDE.md Aturan Wajib apresiasi) —
   dipakai di jawaban benar Latihan Inti/Tantangan Vocab, lebih ramai
   drpd .pt-celebrate (3 emoji, bounce elemennya sendiri juga) biar berasa
   lebih festive. Class generik (bukan cuma buat .opt-btn) — attach ke
   elemen mana pun yang lagi dirayakan (tombol, kartu bintang, dst). */
.win-burst{ position:relative; overflow:visible; animation:winBounce .5s ease; }
.win-burst::after{
  content:'🎉🏆✨'; position:absolute; left:50%; top:-10px; transform:translate(-50%,0);
  font-size:22px; white-space:nowrap; pointer-events:none;
  animation:winBurst .8s ease-out forwards;
}
@keyframes winBounce{
  0%{ transform:scale(1); }
  40%{ transform:scale(1.12); }
  100%{ transform:scale(1); }
}
@keyframes winBurst{
  0%{ opacity:0; transform:translate(-50%,6px) scale(.5); }
  35%{ opacity:1; transform:translate(-50%,-16px) scale(1.2); }
  100%{ opacity:0; transform:translate(-50%,-38px) scale(1); }
}
@media (prefers-reduced-motion: reduce){
  .win-burst{ animation:none; }
  .win-burst::after{ animation:none; opacity:0; }
}

/* Confetti asli (confetti.ts `fireConfetti`) — kepingan jatuh & berputar
   dari atas layar, dipicu di titik yang sama dgn .win-burst. Full-viewport
   & self-cleaning (elemennya dihapus JS sendiri sesudah animasi selesai). */
.confetti-layer{ position:fixed; inset:0; pointer-events:none; z-index:9999; overflow:hidden; }
.confetti-piece{
  position:absolute; top:-14px; border-radius:2px;
  animation-name:confettiFall; animation-timing-function:ease-in; animation-fill-mode:forwards;
}
@keyframes confettiFall{
  0%{ transform:translate(0,0) rotate(0deg); opacity:1; }
  100%{ transform:translate(var(--drift),108vh) rotate(var(--rotate)); opacity:.9; }
}

.feedback{
  text-align:center; font-family:var(--font-head); font-weight:700;
  font-size:1.0625rem; margin-top:var(--s3); min-height:26px;
}
.feedback.good{ color:var(--ok); }
.feedback.bad{ color:var(--try); }

.answer-row{
  display:flex; flex-wrap:wrap; gap:8px; min-height:58px;
  background:var(--accent-bg); border-radius:var(--r-md); padding:11px;
  margin:var(--s4) auto var(--s3); justify-content:center; align-items:center;
  max-width:560px;
}
.answer-row.empty::after{ content:'Tap kata di bawah ⤵️'; color:var(--accent); font-weight:800; font-size:var(--fs-meta); }
/* 🔒 Susunan SALAH (Eja Kata/Susun Kalimat & sejenisnya — permintaan user,
   CLAUDE.md "🔒 Aturan Wajib: Notifikasi Jawaban Salah") — flash merah
   singkat SEBELUM papan direset ke kosong lagi, dipasangkan `playWrongTone()`
   + `vibrateDevice()` di JS. Dipakai lintas skill krn `.answer-row` sendiri
   class generik (Vocab/Listening/Grammar/Reading/Boss). */
.answer-row.is-wrong{ animation:shake .3s ease; background:var(--try-bg); box-shadow:inset 0 0 0 2px var(--try); }
.bank-row{ display:flex; flex-wrap:wrap; gap:8px; justify-content:center; max-width:560px; margin:0 auto; }
.chip{
  background:var(--surface); border:2px solid var(--accent); color:var(--accent);
  border-radius:14px; padding:10px 15px; font-family:var(--font-head);
  font-weight:700; font-size:1.0625rem; cursor:pointer;
  transition:transform .12s ease;
}
.chip:active{ transform:scale(.94); }
.chip.placed{ background:var(--accent); color:#fff; }
.chip.letter{ padding:10px 14px; min-width:22px; text-align:center; text-transform:uppercase; }
/* Huruf/kata dari "💡 Petunjuk" (Eja Kata `.letter.hint`, Susun Kalimat
   `.hint` polos tanpa `.letter` krn chip KATA bukan huruf) — beda tampilan
   dari yang anak taruh sendiri (`.chip.placed` solid), supaya jelas mana
   "diberi" vs "usaha sendiri": outline aksen di atas kertas, bukan solid. */
.chip.placed.hint{ background:var(--surface); color:var(--accent); border-style:dashed; }

/* Tantangan "📝 Lengkapi Catatan" (Achiever, note completion) —
   games/listening.ts runTantanganNote. Kartu ala kertas catatan, blank
   digarisbawahi putus-putus sampai terisi (solid + aksen sesudahnya).
   🔒 Diberi nama `.listen-note-card` (BUKAN `.note-card` polos lagi) —
   permintaan user "section orang tua di Pengaturan dibuat sama lebarnya
   dengan section lain": ketauan `.note-card` di sini TABRAKAN nama dgn
   `.card.note-card` (pola "kartu tips" umum, dipakai 8+ tempat lain di
   app.ts/placement.ts, mis. "Untuk orang tua" Pengaturan) — rule INI
   (`max-width:480px`) belakangan di CSS jadi MENANG cascade & ikut
   membatasi lebar kartu "Untuk orang tua" padahal seharusnya selebar kartu
   Pengaturan lain. Class di-rename SUPAYA TERPISAH TOTAL dari `.card.
   note-card`, bukan cuma diubah `max-width`-nya — nama lama BUKAN dipakai
   di mana pun lagi, aman diganti bersih. */
.listen-note-card{
  background:var(--surface); border:2px dashed var(--accent);
  border-radius:var(--r-md); padding:var(--s3) var(--s4);
  max-width:480px; margin:var(--s3) auto; text-align:left;
}
.note-heading{ font-family:var(--font-head); font-weight:800; font-size:1.0625rem; margin-bottom:var(--s2); }
.note-line{ display:flex; gap:6px; padding:6px 0; font-size:1rem; flex-wrap:wrap; }
.note-label{ font-weight:700; color:var(--ink-soft,inherit); }
.note-blank{
  font-family:var(--font-head); font-weight:700; color:var(--accent);
  border-bottom:2px dashed var(--accent); min-width:80px; display:inline-block;
}
.note-blank.filled{ border-bottom-style:solid; }

/* Tantangan "🧩 Dengar & Simpulkan" (Trailblazer, gist/inference) —
   games/listening.ts runTantanganDialogue. Transkrip percakapan pakai
   `.listen-note-card` yg sama (kartu kertas), baris per tokoh dibedakan warna nama. */
.dialogue-line{ margin-bottom:2px; }
.dialogue-speaker{ font-weight:800; color:var(--accent); margin-right:4px; }

.mic-wrap{ text-align:center; margin:var(--s5) 0 var(--s2); }
/* Mikrofon selalu mango — "giliran kamu bicara" — supaya tidak tertukar dengan
   tombol dengar yang warnanya ikut skill (di Speaking dua-duanya akan hijau). */
.mic-btn{
  width:84px; height:84px; border-radius:50%; background:var(--sun-500); color:var(--brand-900);
  font-size:32px; box-shadow:0 12px 26px -10px rgba(180,110,10,.65);
  transition:transform .12s ease;
}
.mic-btn:active{ transform:scale(.95); }
.mic-btn.listening{ animation:pulse 1s infinite; background:var(--try); }
@keyframes pulse{ 0%{box-shadow:0 0 0 0 rgba(199,69,91,.45);} 70%{box-shadow:0 0 0 18px rgba(199,69,91,0);} 100%{box-shadow:0 0 0 0 rgba(199,69,91,0);} }
/* Tantangan Speaking "💬 Ngobrol Yuk!" (semua level) — tugas dibaca dari
   layar tanpa audio pertanyaan: kata Indonesia ➜ slot Inggris kosong. */
.talk-task{ display:flex; align-items:center; justify-content:center; gap:10px; flex-wrap:wrap; margin:6px 0 4px; }
.talk-chip{ padding:8px 16px; border-radius:999px; background:var(--surface); border:2px solid var(--line); font-weight:800; font-size:var(--fs-body); color:var(--ink-2); }
.talk-chip--en{ border-style:dashed; border-color:var(--brand-500); background:var(--brand-50); color:var(--accent); min-width:72px; text-align:center; }
.talk-arrow{ font-size:22px; color:var(--brand-500); font-weight:800; }
.talk-instruct{ text-align:center; font-weight:800; color:var(--accent); margin:6px 0 2px; }
.talk-target{ text-align:center; font-size:var(--fs-meta); font-weight:700; color:var(--ink-2); margin:2px 0 4px; }
.talk-badges{ display:flex; justify-content:center; flex-wrap:wrap; gap:6px; margin:6px 0 2px; }
.talk-badge{ padding:4px 10px; border-radius:999px; font-size:var(--fs-meta); font-weight:800; background:var(--surface); border:1px solid var(--line); color:var(--ink-2); }
.talk-badge.ok{ background:var(--brand-50); border-color:var(--brand-500); color:var(--accent); }
.talk-model{ margin:8px auto 0; padding:10px 14px; max-width:520px; border-radius:14px; background:var(--brand-50); border:1px dashed var(--brand-500); text-align:center; }
.talk-model-label{ display:block; font-size:var(--fs-meta); font-weight:800; color:var(--accent); margin-bottom:2px; }
.sp-story{ max-width:520px; margin:6px auto; }
.sp-story p{ margin:4px 0; font-weight:700; color:var(--ink-2); }
/* Baris Kenalan Speaking: baris cerita & pertanyaan kecil di atas kalimat utama. */
.primer-item .txt .sp-ctx{ display:block; font-size:var(--fs-meta); color:var(--ink-3); }
.primer-item .txt .sp-q{ display:block; font-size:var(--fs-meta); color:var(--accent); font-weight:700; margin:2px 0; }
/* Tantangan Speaking "🎁 Tebak Isi Kotak" (Starter): pilihan gambar yang boleh ditanyakan. */
.guess-box.solved{ animation:ctaPulse 1.2s ease-in-out 2; }
.guess-word{ font-size:32px; font-weight:800; color:var(--accent); }
.guess-chips{ display:flex; justify-content:center; flex-wrap:wrap; gap:8px; margin:10px 0 4px; }
.guess-chip{ padding:8px 14px; border-radius:999px; background:var(--surface); border:2px solid var(--line); font-weight:800; color:var(--ink-2); }
.guess-chip.out{ opacity:.4; text-decoration:line-through; }
.guess-chip.hit{ border-color:var(--brand-500); background:var(--brand-50); color:var(--accent); }
/* Lengkapi Kalimat Speaking: jawaban dari 💡 Petunjuk mengisi lubangnya sendiri. */
.blank-fill{ color:var(--accent); border-bottom:3px dashed var(--brand-500); padding:0 2px; }
/* Popup hasil Tantangan Speaking — lebih lebar dari popup mic Kenalan (isi
   lebih banyak: cek kata, contoh jawaban, 2 tombol), bisa di-scroll sendiri. */
.talk-result-card{ max-width:400px; max-height:calc(100vh - 40px); overflow-y:auto; }
.talk-result-card .round-actions{ margin-top:12px; }
/* Latihan Inti Speaking: 🔊 Dengar & 🎤 Coba Yuk sejajar, sama besar.
   Mic tetap mango ("giliran kamu bicara"), merah + denyut saat mendengarkan. */
.act-row{ display:flex; gap:12px; justify-content:center; max-width:440px; margin:var(--s5) auto var(--s2); }
.act-btn{
  flex:1 1 0; min-width:0; min-height:58px; display:flex; align-items:center; justify-content:center; gap:8px;
  border-radius:var(--r-pill); font-weight:800; font-size:1.0625rem; font-family:inherit;
  box-shadow:var(--sh-1); transition:transform .12s ease;
}
.act-btn:active{ transform:scale(.97); }
.act-listen{ background:var(--accent); color:#fff; }
.act-mic{ background:var(--sun-500); color:var(--brand-900); box-shadow:0 12px 24px -12px rgba(180,110,10,.7); }
.act-mic.listening{ background:var(--try); color:#fff; animation:pulse 1s infinite; }
.act-mic:disabled{ background:var(--muted); color:var(--ink-3); box-shadow:none; }
.act-hint{ text-align:center; margin-top:4px; }
/* Tantangan Speaking "Pilih & Ucapkan": bingkai kalimat + kartu pilihan jawaban. */
.choice-frame{ margin-top:6px; }
.choice-grid{ display:grid; grid-template-columns:repeat(auto-fit, minmax(140px, 1fr)); gap:10px; max-width:520px; margin:12px auto 4px; }
.choice-card{
  display:flex; align-items:center; justify-content:center; gap:8px; min-height:56px; padding:10px 14px;
  border-radius:16px; background:var(--surface); border:2px solid var(--line); color:var(--ink);
  font-weight:800; font-size:.9375rem; font-family:inherit; text-align:left; box-shadow:var(--sh-1);
  transition:transform .12s ease, border-color .12s ease;
}
.choice-card:active{ transform:scale(.97); }
.choice-grid.long{ grid-template-columns:1fr; }
.choice-grid.long .choice-card{ justify-content:flex-start; }
.choice-card.selected{ border-color:var(--brand-500); background:var(--brand-50); color:var(--accent); }
.choice-emoji{ font-size:26px; flex-shrink:0; }
.choice-text{ line-height:1.3; }
.mic-btn:disabled{ background:var(--muted); color:var(--ink-3); box-shadow:none; animation:none; }
.mic-hint{ font-size:var(--fs-meta); color:var(--ink-3); font-weight:700; margin-top:10px; }
.heard-text{ text-align:center; font-size:var(--fs-meta); color:var(--ink-2); font-weight:700; margin-top:8px; min-height:20px; }
/* Skor kata angka — pelengkap bintang, bukan pengganti (bintang tetap yang
   utama/non-evaluatif). Netral, bukan hijau/merah, supaya tidak berasa
   vonis benar/salah. */
.mic-score{ text-align:center; font-size:var(--fs-meta); color:var(--ink-2); font-weight:700; margin-top:6px; }
.mic-score-pct{ color:var(--ink-3); font-weight:600; }

/* Highlight per-kata hasil Speaking (mic) — hijau+garis bawah = kedengaran,
   abu+coret = tidak kedengaran. Referensi: backup/daily-conversation-asr
   (1).html. Non-punitive: tetap tidak ada kata "salah" di teksnya, cuma
   visual perbedaan warna. */
.word-diff{ display:flex; flex-wrap:wrap; justify-content:center; gap:8px 10px; margin-top:10px; }
.word-diff span{ font-family:var(--font-head); font-weight:800; font-size:1rem; }
.word-diff span.ok{ color:var(--ok); border-bottom:2px solid var(--ok); padding-bottom:1px; }
.word-diff span.miss{ color:var(--ink-3); text-decoration:line-through; text-decoration-color:var(--line); opacity:.7; }

/* ------------------------------------------------------------- selesai --- */
.done-wrap{ position:relative; text-align:center; padding:var(--s3) 0; }
.done-mascot{ font-size:clamp(58px,15vw,80px); line-height:1; }
.stars{ font-size:30px; letter-spacing:8px; margin:var(--s3) 0; filter:drop-shadow(0 4px 6px rgba(180,120,20,.35)); }
.done-wrap .sunburst{ margin:0 auto var(--s2); }

/* Layar menang Tantangan Bos — puncak cerita: cahaya hangat di belakang singa
   bermahkota + pita "Bos Ditaklukkan!". Tetap perayaan, tidak ada skor, tidak
   ada perbandingan, tidak ada apa pun yang bisa "hilang". */
.done-wrap.win::before{
  content:''; position:absolute; left:50%; top:-10px; transform:translateX(-50%);
  width:min(420px,100%); height:230px; border-radius:50%; pointer-events:none;
  background:radial-gradient(circle, rgba(255,181,61,.34) 0%, rgba(255,181,61,.12) 45%, rgba(255,181,61,0) 72%);
}
.win-banner{
  position:relative; display:inline-block; margin-bottom:var(--s2);
  background:linear-gradient(180deg,var(--sun-400) 0%, var(--sun-500) 100%);
  color:#5A3202; font-family:var(--font-head); font-weight:800;
  font-size:clamp(1.25rem,1.1rem + .8vw,1.625rem); line-height:1.1;
  padding:10px 26px; border-radius:var(--r-pill);
  box-shadow:var(--sh-lift), inset 0 0 0 2px rgba(255,255,255,.45);
}
.done-title{ font-size:clamp(1.375rem,1.2rem + .8vw,1.75rem); margin-bottom:6px; }
.done-sub{ color:var(--ink-3); font-weight:700; font-size:var(--fs-meta); margin-bottom:var(--s4); }
.done-wrap .primary-btn,
.done-wrap .ghost-btn{ max-width:340px; margin-left:auto; margin-right:auto; }

/* --------------------------------------------------------- animasi kecil - */
/* Sengaja restrained (polish, bukan redesain motion) — cuma 3 tempat: idle
   mascot kecil (badge XP, kartu Tantangan Bos), "pop" sekali saat layar
   selesai/menang muncul, dan confetti kecil khusus menang Bos. Semua tunduk
   ke @media (prefers-reduced-motion) di bagian bawah file ini. */
@keyframes mascotIdle{
  0%,100%{ transform:translateY(0) rotate(0deg); }
  50%{ transform:translateY(-3px) rotate(-4deg); }
}
.mascot-idle{ display:inline-block; animation:mascotIdle 2.4s ease-in-out infinite; }

@keyframes mascotPop{
  0%{ transform:scale(.6) rotate(-8deg); opacity:0; }
  60%{ transform:scale(1.12) rotate(3deg); opacity:1; }
  100%{ transform:scale(1) rotate(0deg); }
}
.mascot-pop{ animation:mascotPop .5s cubic-bezier(.34,1.56,.64,1) both; }

@keyframes starsPop{
  0%{ transform:scale(.5); opacity:0; }
  70%{ transform:scale(1.15); opacity:1; }
  100%{ transform:scale(1); }
}
.stars-pop{ animation:starsPop .45s cubic-bezier(.34,1.56,.64,1) .15s both; }

/* ----------------------------------------------------------- login page --- */
/* Halaman login tersendiri (§ .main override di atas) — langit + awan
   melayang pelan + singa mengangguk (reuse .mascot-idle) + kartu masuk
   dengan entrance halus. Kid-friendly lewat gerakan lembut, bukan ramai. */
.login-page{
  position:relative; overflow:hidden;
  min-height:100vh; min-height:100dvh;
  display:flex; flex-direction:column;
  background-color:var(--paper);
  background-image:
    radial-gradient(1000px 420px at 20% -12%, rgba(146,222,212,.5) 0%, rgba(146,222,212,0) 70%),
    radial-gradient(760px 320px at 100% -4%, rgba(255,197,98,.4) 0%, rgba(255,197,98,0) 66%);
}
.login-sky{ position:absolute; inset:0; pointer-events:none; z-index:0; }
.login-sky .cloud{ position:absolute; width:120px; color:var(--brand-300); opacity:.4; }
.login-sky .cloud.c1{ top:8%; left:6%; animation:cloudDrift 22s ease-in-out infinite; }
.login-sky .cloud.c2{ top:16%; right:8%; width:84px; opacity:.28; animation:cloudDrift 28s ease-in-out infinite reverse; }
@keyframes cloudDrift{
  0%,100%{ transform:translateX(0); }
  50%{ transform:translateX(26px); }
}

.login-main{
  position:relative; z-index:1; flex:1;
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  padding:var(--s6) var(--s4);
}
.login-hero{ text-align:center; margin-bottom:var(--s5); }
.login-mascot{ font-size:56px; margin-bottom:8px; }
.login-card{
  width:100%; max-width:400px;
  animation:loginCardIn .5s cubic-bezier(.34,1.56,.64,1) both;
}
@keyframes loginCardIn{
  0%{ opacity:0; transform:translateY(18px) scale(.97); }
  100%{ opacity:1; transform:translateY(0) scale(1); }
}

/* Halaman Daftar & /pembayaran — tata letak MENGIKUTI `/beli` &
   `/payment/success` inggrisinyuk-app (permintaan user): latar polos, kartu
   berbingkai tipis, maks 1024px; ≥1024px 2 kolom (form kiri, ringkasan
   kanan sticky), di bawahnya 1 kolom dgn ringkasan di ATAS form. */
.buy-page{ min-height:100vh; min-height:100dvh; display:flex; flex-direction:column; background:var(--paper); }
.buy-main{ flex:1; padding:32px 16px; }
.buy-wrap{ max-width:1024px; margin:0 auto; }
.buy-back{ font-size:.875rem; font-weight:600; color:var(--ink-3); margin-bottom:12px; padding:12px 0; }
.buy-back:hover{ color:var(--ink); }
.buy-grid{ display:grid; grid-template-columns:1fr; gap:24px; align-items:start; }
.buy-card{
  background:var(--surface); border:1px solid var(--line); border-radius:14px;
  box-shadow:0 1px 2px rgba(15,40,40,.06); padding:24px;
}
.buy-summary-title{ font-family:var(--font-head); font-size:1.125rem; font-weight:700; color:var(--ink); }
.buy-product{ margin-top:16px; font-weight:700; color:var(--ink); }
.buy-benefits{ list-style:none; margin:16px 0 0; padding:0; display:grid; gap:10px; }
.buy-benefits li{ display:flex; gap:10px; align-items:flex-start; font-size:.875rem; line-height:1.6; color:var(--ink-3); }
.buy-check{
  flex:none; width:16px; height:16px; margin-top:3px; border-radius:50%;
  display:grid; place-items:center; font-size:10px; font-weight:900;
  color:var(--brand-700); background:var(--brand-100);
}
.buy-divider{ height:1px; background:var(--line); margin:20px 0; }
.buy-total{ display:flex; justify-content:space-between; align-items:center; font-weight:700; color:var(--ink); }
.buy-total b{ font-family:var(--font-head); font-size:1.5rem; font-weight:800; color:var(--brand-700); }

.buy-title{ font-family:var(--font-head); font-size:1.5rem; font-weight:800; line-height:1.25; color:var(--ink); text-wrap:balance; }
.buy-lede{ margin-top:8px; font-size:.875rem; color:var(--ink-3); }
.buy-fields{ margin-top:24px; display:grid; gap:20px; }
.buy-field label{ display:block; font-size:.875rem; font-weight:700; color:var(--ink); margin-bottom:6px; }
.buy-req{ color:var(--try); margin-left:2px; }
.buy-input{
  width:100%; box-sizing:border-box; height:42px; padding:0 12px;
  background:var(--surface); border:1px solid var(--line); border-radius:9px;
  font-family:var(--font-body); font-size:1rem; font-weight:600; color:var(--ink);
}
.buy-input::placeholder{ color:var(--ink-3); font-weight:500; }
.buy-input:focus-visible{ outline:3px solid var(--brand-200, var(--brand-100)); outline-offset:0; border-color:var(--brand-500); }
.buy-input:disabled{ opacity:.6; }
.buy-hint{ margin-top:6px; font-size:.75rem; color:var(--ink-3); }
.buy-hint.is-warn{ font-weight:700; color:var(--ink); }
.buy-error{
  display:flex; gap:8px; align-items:flex-start; margin-top:20px;
  border:1px solid var(--try); background:var(--try-bg); border-radius:9px; padding:12px;
  font-size:.875rem; line-height:1.5; color:var(--ink);
}
.buy-submit{
  margin-top:24px; width:100%; height:48px; border-radius:9px;
  display:flex; align-items:center; justify-content:center; gap:8px;
  background:var(--brand-600); color:#fff;
  font-family:var(--font-head); font-size:1rem; font-weight:700;
  transition:background .15s ease;
}
.buy-submit:hover{ background:var(--brand-700); }
.buy-submit:disabled{ opacity:.7; cursor:progress; }
.buy-note{ margin-top:14px; text-align:center; font-size:.75rem; color:var(--ink-3); text-wrap:pretty; }
.buy-note .auth-link{ font-size:inherit; }
.auth-link{
  display:inline-block; padding:10px 2px; margin:-10px 0; font:inherit; font-weight:800;
  color:var(--brand-700); text-decoration:underline; text-underline-offset:2px;
}
.buy-spin{
  width:16px; height:16px; border-radius:50%;
  border:2px solid rgba(255,255,255,.45); border-top-color:#fff;
  animation:paySpin .8s linear infinite;
}
.buy-spin.is-lg{ width:40px; height:40px; border-width:4px; border-color:var(--brand-100); border-top-color:var(--brand-600); }
@keyframes paySpin{ to{ transform:rotate(360deg); } }

.buy-footer{
  border-top:1px solid var(--line); padding:16px;
  display:flex; align-items:center; justify-content:center; gap:24px;
}
.buy-footer p{ font-size:.75rem; color:var(--ink-3); }
.buy-social{ display:flex; gap:4px; }
.buy-social a{ display:grid; place-items:center; width:40px; height:40px; color:var(--ink-3); opacity:.6; }
.buy-social a:hover{ opacity:1; }
.buy-social svg{ width:16px; height:16px; display:block; }
.wa-float{
  position:fixed; right:24px; bottom:24px; z-index:50;
  width:56px; height:56px; border-radius:50%; display:grid; place-items:center;
  background:#25D366; color:#fff; box-shadow:0 8px 20px rgba(0,0,0,.18);
  transition:transform .15s ease;
}
.wa-float:hover{ transform:scale(1.08); }
.wa-float svg{ width:28px; height:28px; }

@media (min-width:640px){
  .buy-main{ padding:32px 24px; }
  .buy-card{ padding:32px; }
  .buy-summary{ padding:24px; }
}
@media (min-width:1024px){
  .buy-main{ padding:48px 24px; }
  .buy-grid{ grid-template-columns:1.1fr .9fr; gap:32px; }
  .buy-form{ order:1; }
  .buy-summary{ order:2; position:sticky; top:24px; }
}

/* Halaman Daftar bagian marketing (di atas bagian beli). */
.mk-hero{ text-align:center; padding:8px 0 16px; }
.mk-mascot{ display:inline-block; font-size:56px; line-height:1; }
.mk-eyebrow{ margin-top:12px; font-size:.8125rem; font-weight:800; letter-spacing:.04em; text-transform:uppercase; color:var(--brand-700); }
.mk-h1{ margin:8px auto 0; max-width:22ch; font-family:var(--font-head); font-size:2rem; font-weight:800; line-height:1.15; color:var(--ink); text-wrap:balance; }
.mk-lede{ margin:12px auto 0; max-width:46ch; font-size:1rem; line-height:1.55; color:var(--ink-2); text-wrap:pretty; }
.buy-note-access{ margin-top:12px; font-weight:700; color:var(--ink-2); }
/* Kotak penawaran (hero /daftar & homepage) — harga & klaim DOMINAN
   (permintaan user). Catatan "selama layanan … berjalan" WAJIB selalu ikut
   di bawah klaim "akses selamanya" (jujur ke konsumen). */
.mk-offer{
  display:inline-block; box-sizing:border-box; width:100%; max-width:420px;
  margin-top:22px; padding:16px 20px 14px;
  background:var(--sun-100); border:2px solid var(--sun-400);
  border-radius:18px; box-shadow:0 6px 18px rgba(255,170,40,.18);
}
.mk-offer-price{ font-family:var(--font-head); font-size:2.5rem; font-weight:800; line-height:1.05; color:var(--brand-700); letter-spacing:-.01em; }
.mk-offer-claim{ margin-top:4px; font-family:var(--font-head); font-size:1.1875rem; font-weight:800; color:var(--ink); text-wrap:balance; }
.mk-offer-note{ margin-top:6px; font-size:.8125rem; line-height:1.45; color:var(--ink-2); text-wrap:balance; }
@media (max-width:389px){ .mk-offer-claim{ font-size:1.0625rem; } }
@media (min-width:1024px){
  .mk-offer-price{ font-size:3rem; }
  .mk-offer-claim{ font-size:1.375rem; }
}
.mk-cta{ max-width:320px; margin:20px auto 0; }
.mk-trust{ margin-top:10px; font-size:.75rem; color:var(--ink-3); }
.mk-section{ margin-top:48px; }
.mk-h2{ font-family:var(--font-head); font-size:1.375rem; font-weight:800; line-height:1.25; color:var(--ink); text-align:center; margin-bottom:20px; text-wrap:balance; }
.mk-grid-3, .mk-grid-feat{ display:grid; grid-template-columns:1fr; gap:12px; }
.mk-pain, .mk-feature{ padding:20px; }
.mk-pain-ic, .mk-feature-ic{ font-size:28px; line-height:1; }
.mk-pain-title, .mk-feature-title{ margin-top:10px; font-weight:800; color:var(--ink); }
.mk-pain-fix{ margin-top:6px; font-size:.875rem; line-height:1.5; color:var(--ink-2); }
.mk-pain-fix span{ color:var(--brand-700); font-weight:900; }
.mk-feature-desc{ margin-top:4px; font-size:.875rem; line-height:1.5; color:var(--ink-3); }
.mk-steps{ list-style:none; margin:0 auto; padding:0; max-width:640px; display:grid; gap:12px; }
.mk-step{ display:flex; gap:14px; align-items:flex-start; background:var(--surface); border:1px solid var(--line); border-radius:14px; padding:16px 18px; }
.mk-step-num{
  flex:none; width:32px; height:32px; border-radius:50%; display:grid; place-items:center;
  background:var(--brand-600); color:#fff; font-family:var(--font-head); font-weight:800;
}
.mk-step-title{ font-weight:800; color:var(--ink); }
.mk-step-desc{ margin-top:2px; font-size:.875rem; color:var(--ink-3); }
.mk-value{ max-width:640px; margin:0 auto; text-align:center; background:var(--brand-50); border-color:var(--brand-100); }
.mk-value-title{ font-family:var(--font-head); font-size:1.125rem; font-weight:800; color:var(--ink); }
.mk-value-list{ list-style:none; margin:14px 0 0; padding:0; display:flex; flex-wrap:wrap; justify-content:center; gap:8px; }
.mk-value-list li{ background:var(--surface); border:1px solid var(--brand-100); border-radius:999px; padding:6px 12px; font-size:.8125rem; font-weight:700; color:var(--ink-2); }
.mk-value-list span{ color:var(--brand-700); font-weight:900; }
.mk-buy{ scroll-margin-top:16px; }
.mk-faqs{ max-width:720px; margin:0 auto; display:grid; gap:10px; }
.mk-faq{ padding:0; }
.mk-faq summary{ cursor:pointer; padding:16px 18px; font-weight:700; color:var(--ink); list-style:none; }
.mk-faq summary::-webkit-details-marker{ display:none; }
.mk-faq summary::after{ content:"+"; float:right; font-weight:800; color:var(--ink-3); }
.mk-faq[open] summary::after{ content:"–"; }
.mk-faq p{ padding:0 18px 16px; font-size:.875rem; line-height:1.55; color:var(--ink-2); }
.mk-sticky{
  position:fixed; left:0; right:0; bottom:0; z-index:40;
  display:flex; align-items:center; justify-content:space-between; gap:12px;
  padding:10px 16px calc(10px + env(safe-area-inset-bottom));
  background:var(--surface); border-top:1px solid var(--line); box-shadow:0 -4px 16px rgba(15,40,40,.08);
  transition:transform .2s ease;
}
.mk-sticky-text{ min-width:0; }
.mk-sticky-text p{ font-size:.875rem; line-height:1.35; color:var(--ink); font-weight:700; }
.mk-sticky-text small{ display:block; margin-top:1px; font-size:.75rem; line-height:1.3; color:var(--ink-3); }
.mk-sticky b{ font-family:var(--font-head); font-size:1rem; color:var(--brand-700); }
@media (max-width:479px){
  .mk-sticky-sep{ display:none; }
  .mk-sticky-claim{ display:block; }
  .mk-sticky b{ font-size:1.0625rem; }
}
.mk-sticky .buy-submit{ flex:none; margin:0; width:auto; height:44px; padding:0 24px; }
.mk-sticky.is-hidden{ transform:translateY(110%); pointer-events:none; }
/* Tombol WA naik saat bar tampil; halaman diberi ruang bawah supaya footer
   tidak tertutup bar. */
body:has(.mk-sticky:not(.is-hidden)) .wa-float{ bottom:96px; }
.buy-page:has(.mk-sticky), .landing-page:has(.mk-sticky){ padding-bottom:84px; }
@media (min-width:640px){
  .mk-h1{ font-size:2.5rem; }
  .mk-grid-3{ grid-template-columns:repeat(3, 1fr); }
  .mk-grid-feat{ grid-template-columns:repeat(2, 1fr); }
}
@media (min-width:1024px){
  .mk-hero{ padding-top:16px; }
  .mk-h1{ font-size:2.75rem; }
  .mk-section{ margin-top:64px; }
  .mk-grid-feat{ grid-template-columns:repeat(3, 1fr); }
  .mk-steps{ max-width:none; grid-template-columns:repeat(3, 1fr); }
  .mk-sticky{ display:none; }
  body:has(.mk-sticky) .wa-float{ bottom:24px; }
  .buy-page:has(.mk-sticky), .landing-page:has(.mk-sticky){ padding-bottom:0; }
}

/* /pembayaran — kartu kecil di tengah (pola /payment/success). */
.pay-main{ display:flex; align-items:center; justify-content:center; padding:40px 16px; }
.pay-wrap{ width:100%; max-width:400px; }
.pay-brand{ text-align:center; margin-bottom:24px; font-family:var(--font-head); font-size:1.5rem; font-weight:800; color:var(--brand-700); }
.pay-card{ display:flex; flex-direction:column; align-items:center; gap:4px; text-align:center; }
.pay-icon{ min-height:44px; display:grid; place-items:center; margin-bottom:12px; }
.pay-ok, .pay-x, .pay-wait{
  width:40px; height:40px; border-radius:50%; display:grid; place-items:center;
  font-size:20px; font-weight:900;
}
.pay-ok{ color:#fff; background:var(--good, #1f9d6b); }
.pay-x{ color:#fff; background:var(--try); }
.pay-wait{ font-size:28px; }
.pay-title{ font-weight:800; color:var(--ink); }
.pay-desc{ font-size:.875rem; color:var(--ink-3); margin-bottom:12px; }
.pay-card .auth-link{ font-size:.875rem; }

/* Dipakai di semua halaman "berdiri sendiri" (tanpa rail/topline/tabbar) —
   login DAN First Placement Test (permintaan user: footer yang sama). */
.standalone-footer{
  position:relative; z-index:1;
  border-top:1px solid var(--line);
  padding:var(--s4); text-align:center;
}
.standalone-footer p{ font-size:var(--fs-eyebrow); font-weight:700; color:var(--ink-3); }

/* --------------------------------------------------------- homepage --- */
/* Homepage marketing (pengunjung belum login, app.ts `renderLandingPage`) —
   full-bleed spt .login-page (body.is-landing sembunyikan rail/topline/
   tabbar, lihat aturan .is-login di atas). Reuse token warna/kartu yang
   sama dgn seluruh app (bukan palet baru), cuma ditata jadi bentuk homepage
   (hero → cara kerja → fitur → testimoni → CTA → footer). */
.landing-nav{
  display:flex; align-items:center; justify-content:space-between;
  padding:var(--s4); max-width:1180px; margin:0 auto;
}
/* "Masuk" (ghost) + "Daftar" (solid) berdampingan — pola navbar inggrisinyuk
   (Login ghost + Beli Sekarang solid), cuma istilah beli disesuaikan jadi
   Daftar. Keduanya menuju layar akun yang sama (satu-satunya pintu nyata
   hari ini), lihat komentar `renderLandingPage`. */
.landing-nav-actions{ display:flex; align-items:center; gap:8px; }
.landing-nav-btn{ width:auto; margin-top:0; padding:9px 16px; font-size:.875rem; }
.landing-hero{
  padding:var(--s3) var(--s4) var(--s7);
  text-align:center;
  background-image:
    radial-gradient(1000px 420px at 20% -12%, rgba(146,222,212,.5) 0%, rgba(146,222,212,0) 70%),
    radial-gradient(760px 320px at 100% -4%, rgba(255,197,98,.4) 0%, rgba(255,197,98,0) 66%);
}
.landing-hero-inner{
  max-width:600px; margin:0 auto;
  display:flex; flex-direction:column; align-items:center; gap:var(--s3);
}
.landing-mascot{ font-size:52px; }
.landing-hero-inner h1{ text-wrap:balance; }
.landing-section{ padding:var(--s6) var(--s4) 0; max-width:1180px; margin:0 auto; }
.landing-section > h2{ text-align:center; margin-bottom:var(--s5); }
.landing-steps{ display:grid; gap:var(--s5) var(--s4); grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); }
.landing-step{ display:flex; flex-direction:column; align-items:center; text-align:center; gap:6px; }
.landing-step-ic{
  position:relative;
  width:56px; height:56px; border-radius:50%; display:grid; place-items:center; font-size:26px;
  background:radial-gradient(circle at 34% 26%, var(--brand-300) 0%, var(--brand-500) 100%);
  box-shadow:0 6px 14px -8px rgba(6,51,58,.5), inset 0 0 0 2px rgba(255,255,255,.35);
}
.landing-step-num{
  position:absolute; top:-4px; right:-4px; width:20px; height:20px; border-radius:50%;
  background:var(--sun-500); color:var(--brand-900); font-family:var(--font-head); font-weight:800; font-size:.6875rem;
  display:grid; place-items:center;
}
.landing-step h3{ font-size:var(--fs-h3); margin-top:2px; }
.landing-step p{ font-size:var(--fs-meta); color:var(--ink-3); font-weight:700; }
/* Grid fitur — SENGAJA tanpa kartu/border (beda dari `.skill-card` DAN dari
   versi kotak sebelumnya di sini), pola sama dgn `.landing-step` di atasnya
   (ikon bulat + label, tanpa background/border) supaya section Level→Fitur
   yang sebelumnya kotak-kotak beruntun kebaca tidak monoton — cuma ikonnya
   yang tetap pakai warna aksen per-skill (`f.accent`/`f.accentBg`, inline
   style), bukan satu warna teal seragam spt `.landing-step-ic`. Kolom TETAP
   (bukan auto-fit) — 8 item ÷ 4 kolom = 2 baris simetris di desktop, ÷ 2
   kolom = 4 baris simetris di mobile. */
.landing-feature-grid{ display:grid; gap:var(--s5) var(--s3); grid-template-columns:repeat(2,1fr); }
@media (min-width:768px){
  .landing-feature-grid{ grid-template-columns:repeat(4,1fr); }
}
.landing-feature-item{ display:flex; flex-direction:column; align-items:center; text-align:center; gap:6px; }
.landing-feature-ic{
  width:56px; height:56px; border-radius:50%; flex:none;
  display:grid; place-items:center; font-size:26px;
  box-shadow:0 6px 14px -8px rgba(6,51,58,.35), inset 0 0 0 2px rgba(255,255,255,.5);
}
.landing-feature-item h3{ font-size:var(--fs-h3); margin-top:2px; }
.landing-feature-item p{ font-size:var(--fs-meta); color:var(--ink-3); font-weight:700; }
/* Kartu level reuse token "tanah" Peta Level asli (.t-dawn..t-summit,
   placeFor() scenery.ts) via --band/--band-deep — bukan palet baru. */
.landing-levels{ display:grid; gap:var(--s3); grid-template-columns:repeat(auto-fit,minmax(128px,1fr)); }
.landing-level{
  display:flex; flex-direction:column; align-items:center; gap:6px; text-align:center;
  background:linear-gradient(180deg,var(--band-soft) 0%, var(--surface) 72%);
  border:1px solid var(--line); border-radius:var(--r-lg); padding:var(--s4) var(--s3);
  box-shadow:var(--sh-1);
}
.landing-level-ic{
  width:52px; height:52px; border-radius:50%; flex:none; display:grid; place-items:center; font-size:26px;
  background:var(--surface); border:3px solid var(--band-deep);
}
.landing-level h3{ font-size:var(--fs-h3); margin-top:2px; }
.landing-level p{ font-size:var(--fs-eyebrow); color:var(--ink-3); font-weight:700; margin:0; }
.landing-testis{ display:grid; gap:var(--s4); grid-template-columns:repeat(auto-fit,minmax(230px,1fr)); }
.landing-testi p{ font-size:var(--fs-body); color:var(--ink-2); font-weight:700; }
.landing-testi .who{ display:flex; align-items:center; gap:8px; margin-top:auto; }
.landing-testi .who-badge{
  width:34px; height:34px; border-radius:50%; flex:none; display:grid; place-items:center;
  background:var(--brand-700); color:#fff; font-family:var(--font-head); font-weight:800; font-size:.8125rem;
}
.landing-testi .who-name{ font-family:var(--font-head); font-size:.875rem; }
.landing-testi .who-role{ font-size:.75rem; color:var(--ink-3); font-weight:700; }
.landing-bottomcta{
  background:linear-gradient(176deg,var(--brand-600) 0%, var(--brand-900) 100%);
  color:#fff; text-align:center; padding:var(--s6) var(--s4);
  border-radius:var(--r-xl); box-shadow:var(--sh-2);
  max-width:720px; margin:0 auto;
  display:flex; flex-direction:column; align-items:center; gap:var(--s3);
}
.landing-bottomcta h2{ color:#fff; margin-bottom:0; }
.landing-bottomcta .lede{ color:rgba(255,255,255,.86); }
/* Kotak penawaran (.mk-offer, sama dgn /daftar) di homepage — jarak dari
   `gap` flex parent, bukan margin sendiri. Catatan layanan WAJIB ikut. */
.landing-hero-inner .mk-offer, .landing-bottomcta .mk-offer{ margin-top:4px; margin-bottom:4px; }
.landing-bottomcta .mk-offer{ box-shadow:0 8px 22px rgba(0,0,0,.22); }
.landing-footer-links{ display:flex; justify-content:center; gap:16px; margin-bottom:var(--s3); flex-wrap:wrap; }
.landing-footer-links a{ color:var(--ink-3); font-size:var(--fs-meta); font-weight:700; text-decoration:none; display:inline-block; padding:12px 4px; }

/* FAQ — native <details>/<summary> (tanpa JS tambahan, accessible bawaan
   browser); tanda +/- lewat ::after, bukan ikon SVG baru. */
.landing-faqs{ display:flex; flex-direction:column; gap:10px; max-width:760px; margin:0 auto; }
.landing-faq{
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-md);
  padding:14px var(--s4); box-shadow:var(--sh-1);
}
.landing-faq summary{
  cursor:pointer; list-style:none;
  display:flex; align-items:center; justify-content:space-between; gap:10px;
  font-family:var(--font-head); font-weight:700; font-size:.9375rem; color:var(--ink);
}
.landing-faq summary::-webkit-details-marker{ display:none; }
.landing-faq summary::after{ content:'+'; font-size:1.25rem; color:var(--ink-3); flex:none; }
.landing-faq[open] summary::after{ content:'–'; }
.landing-faq p{ margin-top:10px; font-size:var(--fs-meta); color:var(--ink-2); font-weight:700; }

/* Confetti kecil khusus menang Bos — beberapa emoji melayang & memudar sekali. */
.boss-burst{ position:absolute; left:50%; top:58px; width:0; height:0; pointer-events:none; z-index:2; }
.boss-burst span{ position:absolute; font-size:22px; opacity:0; animation:burstFloat 1.3s ease-out both; }
.boss-burst span:nth-child(1){ left:-116px; top:22px; animation-delay:.02s; }
.boss-burst span:nth-child(2){ left:-70px; top:10px; animation-delay:.14s; }
.boss-burst span:nth-child(3){ left:-30px; top:-14px; animation-delay:.24s; font-size:26px; }
.boss-burst span:nth-child(4){ left:10px; top:-22px; animation-delay:.3s; }
.boss-burst span:nth-child(5){ left:46px; top:-6px; animation-delay:.1s; font-size:26px; }
.boss-burst span:nth-child(6){ left:86px; top:16px; animation-delay:.2s; }
.boss-burst span:nth-child(7){ left:124px; top:28px; animation-delay:.06s; }
.boss-burst span:nth-child(8){ left:-92px; top:-6px; animation-delay:.34s; }
.boss-burst span:nth-child(9){ left:104px; top:-4px; animation-delay:.38s; }
@keyframes burstFloat{
  0%{ opacity:0; transform:translateY(14px) scale(.5) rotate(0deg); }
  28%{ opacity:1; }
  100%{ opacity:0; transform:translateY(-62px) scale(1.2) rotate(20deg); }
}

/* ---------------------------------------------------------- pengaturan --- */
.set-grid{ display:grid; gap:var(--s4); }
.set-list{ list-style:none; margin:var(--s3) 0 0; padding:0; }
.set-list li{
  display:flex; gap:10px; padding:8px 0; border-top:1px solid var(--line);
  font-size:var(--fs-meta); font-weight:700; color:var(--ink-2);
}
.set-list li:first-child{ border-top:none; }
.set-list li b{ color:var(--ink); }
.danger-btn{
  margin-top:var(--s4); border:2px solid var(--try-bg); color:var(--try);
  background:var(--surface); border-radius:var(--r-md); padding:11px 18px;
  font-family:var(--font-head); font-weight:700;
}
.text-input{
  width:100%; box-sizing:border-box; background:var(--surface);
  border:1.5px solid var(--line); border-radius:var(--r-md);
  padding:11px 14px; font-family:var(--font-body); font-size:1rem;
  font-weight:700; color:var(--ink);
}
.text-input::placeholder{ color:var(--ink-3); font-weight:600; }
.text-input:focus-visible{ outline:3px solid var(--brand-500); outline-offset:1px; border-color:var(--brand-500); }

/* Avatar hewan (Pengaturan → Nama panggilan) — grid tap-besar, sama filosofi
   dgn opt-btn di game (target tap besar utk anak, PRD §4.6). 3 kolom x 3
   baris tetap (9 avatar, lihat ANIMAL_AVATARS di progress.ts). Kartu ala
   kompetitor (permintaan user, referensi screenshot "Pilih Maskot"): ikon +
   nama Inggris (ANIMAL_AVATAR_NAMES) berdampingan vertikal dalam 1 kartu
   rounded-rect, bukan cuma emoji polos di kotak kecil — pill mango dari versi
   lama diganti border navy + badge centang hijau (--ok) begitu aktif, pola
   sama persis referensi. */
.avatar-grid{ display:grid; grid-template-columns:repeat(3,1fr); gap:10px; }
.avatar-opt{
  position:relative;
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:6px;
  padding:16px 6px; border-radius:var(--r-lg);
  background:var(--surface); border:2px solid var(--line);
  cursor:pointer;
}
.avatar-opt-emoji{ font-size:34px; line-height:1; }
.avatar-opt-name{ font-size:.75rem; font-weight:800; color:var(--ink); letter-spacing:.01em; }
.avatar-opt.is-active{ border-color:var(--ink); border-width:2.5px; }
.avatar-opt-check{
  position:absolute; top:8px; right:8px; width:20px; height:20px; border-radius:50%;
  display:grid; place-items:center; background:var(--ok); color:#fff;
}
.avatar-opt-check svg.ico{ width:11px; height:11px; stroke-width:3.4; }

/* ============================ RESPONSIVE ================================== */

/* Di HP, breadcrumb mubazir — tombol kembali + judul layar sudah menjelaskan
   posisi, dan tiap baris chrome yang dihemat = panggung game naik ke atas. */
@media (max-width:767px){
  .crumb{ display:none; }
}

/* Sebelum layar cukup lebar untuk kolom samping, panel suara turun ke bawah
   panggung supaya anak langsung melihat gamenya. */
@media (max-width:1079px){
  /* 🔒 Permintaan user: jarak kartu jawaban → panel "Suara & Kecepatan"
     dirapatkan sedikit (kartu jawaban sekarang lebih tinggi/vertikal,
     jaraknya kerasa lebih longgar drpd sebelumnya) — gap flex diperkecil,
     TETAP dgn token spasi yg ada (bukan angka sembarangan). */
  .act-body{ display:flex; flex-direction:column; gap:var(--s3); }
  .act-side{ display:contents; }
  .stepper{ order:1; }
  .act-stage{ order:2; }
  .voice-shown{ order:3; }
}

/* Tablet ke atas: rail ikon ringkas menggantikan tab bar bawah. */
@media (min-width:768px){
  .topline{ display:none; }
  .tabbar{ display:none; }

  .rail{
    display:flex; flex-direction:column; gap:var(--s5);
    position:sticky; top:0; align-self:flex-start;
    height:100vh; height:100dvh; width:92px; flex:none;
    padding:var(--s5) 14px var(--s4);
    background:linear-gradient(168deg,var(--brand-800) 0%, var(--brand-900) 100%);
    box-shadow:var(--sh-rail);
    overflow-y:auto;
  }
  .rail .brand{ justify-content:center; }
  .rail .brand-word{ display:none; }
  .rail-nav{ gap:8px; }
  .nav-item{ flex-direction:column; gap:5px; padding:8px 4px; font-size:.75rem; text-align:center; letter-spacing:.02em; }
  .nav-item .nav-ico{ width:46px; height:40px; border-radius:14px; }
  .rail-foot{ margin-top:auto; }
  .rail-foot .rail-level{
    text-align:center; color:#B6DED9; font-size:.75rem; font-weight:800;
    background:rgba(255,255,255,.08); border-radius:14px; padding:10px 6px;
  }
  .rail-foot .rail-level .eyebrow{ color:#8FC8C2; }
  .rail-foot .rail-level b{ display:block; font-family:var(--font-head); font-size:.8125rem; color:#fff; }
  .rail-foot .rail-level .cefr{ display:none; }

  .main{ padding:var(--s5) var(--s5) var(--s6); }
  /* Panggung game tetap selebar tangan anak walau layarnya lebar — teks,
     tombol, dan kartu pilihan tidak ikut melar. */
  .act-stage > .card{ max-width:700px; margin-left:auto; margin-right:auto; }
  .act-stage .primary-btn,
  .act-stage .ghost-btn{ max-width:460px; margin-left:auto; margin-right:auto; }
  /* Petunjuk di header soal tetap mentok kanan sejajar badge, bukan ke tengah. */
  .act-stage .latihan-head .ghost-btn{ margin-left:0; margin-right:0; }
  .jump-grid{ grid-template-columns:repeat(4,1fr); }
  .skill-grid{ grid-template-columns:1fr 1fr; }
  .topic-grid{ grid-template-columns:repeat(auto-fill,minmax(300px,1fr)); }
  /* Game (renderGame) tidak duduk di dalam .two-col seperti Menu Belajar —
     seluruh lebar layar kepakai, jadi grid-nya boleh tetap 2 kolom di sini. */
  .skill-grid.game-grid{ grid-template-columns:1fr 1fr; }
  /* 🔒 Permintaan user: "/pengaturan... UI versi desktop web nya kurang
     bagus tapi versi mobile sudah bagus". Percobaan PERTAMA (2-kolom CSS
     Grid) bikin kartu pendek ("Untuk orang tua"/tombol "Keluar") diregangkan
     `align-items:stretch` jadi kotak putih kosong raksasa. Percobaan KEDUA
     (multi-column "masonry", `column-count:2`) SUDAH DIGANTI TOTAL lagi —
     user tegaskan "masih kurang bagus", minta TETAP 1 kolom penuh, bukan 2
     kartu berdampingan sama sekali dalam bentuk apa pun. SEKARANG: `.set-
     grid` TETAP `display:grid` 1-kolom implisit persis base rule mobile
     (TIDAK ada `grid-template-columns`/`column-count` apa pun lagi di
     breakpoint manapun) — bedanya CUMA lebar dibatasi & ditengahkan
     (`max-width`+`margin:auto`, pola SAMA `.act-stage > .card` di bawah)
     supaya di layar lebar kartu tidak melebar mentah edge-to-edge (input
     nama/tombol jadi janggal kepanjangan) TANPA membuka kolom kedua. */
  .set-grid{ max-width:700px; margin-left:auto; margin-right:auto; }
  .spark{ padding:var(--s5) var(--s5) calc(var(--s5) + 14px); }
  .spark-art{ width:100px; height:100px; font-size:50px; }
  .spark .trail-hills{ height:66px; }

  /* Peta Level: perhentian jadi lebih lapang & medalinya membesar — di layar
     lebar peta boleh benar-benar terasa seperti pemandangan, bukan daftar. */
  .map-sky{ padding:var(--s6) var(--s5) var(--s5); }
  .map-sun{ width:130px; height:130px; right:26px; top:-32px; }
  .trail-stop{ grid-template-columns:92px minmax(0,1fr); gap:var(--s4); padding:var(--s6) var(--s5); }
  .trail-path{ top:calc(-1 * var(--s6)); height:calc(100% + var(--s6) * 2); }
  .trail-medallion{ width:78px; height:78px; font-size:38px; border-width:5px; }
  .trail-you{ left:52px; top:-18px; font-size:30px; }
  .trail-hills{ height:72px; }
  .trail-card{ padding:var(--s5); }
  .trail-card h3{ font-size:1.25rem; }
  .trail-boss{ width:78px; height:78px; font-size:39px; }
  .trail-actions .primary-btn,
  .trail-actions .ghost-btn{ flex:1 1 200px; }
  .boss-arena{ padding:var(--s6) var(--s5) calc(var(--s5) + 14px); }
}

/* Desktop lebar: rail penuh + komposisi 2 kolom yang benar-benar memanfaatkan ruang. */
@media (min-width:1080px){
  .rail{ width:252px; padding:var(--s5) var(--s4) var(--s4); }
  .rail .brand{ justify-content:flex-start; padding:0 6px; }
  .rail .brand-word{ display:block; color:#fff; }
  .nav-item{ flex-direction:row; gap:12px; padding:11px 12px; font-size:.9375rem; text-align:left; }
  .nav-item .nav-ico{ width:40px; height:40px; border-radius:12px; }
  .rail-foot .rail-level{ text-align:left; padding:12px 14px; font-size:.75rem; }
  .rail-foot .rail-level b{ font-size:1rem; }
  .rail-foot .rail-level .cefr{ display:block; opacity:.8; }

  .main{ padding:var(--s6) var(--s6) var(--s7); }
  .two-col{ grid-template-columns:minmax(0,1.55fr) minmax(276px,.85fr); gap:var(--s5); }
  .jump-grid{ grid-template-columns:1fr 1fr; }
  .skill-grid{ grid-template-columns:1fr; }
  .skill-grid.game-grid{ grid-template-columns:repeat(3,1fr); }
  /* `.set-grid` TETAP 1 kolom (max-width dibatasi sejak ≥640px, lihat
     komentar lengkap di sana) — TIDAK perlu override lagi di sini. */
  .act-body{ grid-template-columns:minmax(0,1fr) 264px; gap:var(--s5); }
  .act-side{
    order:2; align-self:start; position:sticky; top:var(--s5);
    display:flex; flex-direction:column; gap:var(--s3);
  }
  .act-stage{ order:1; min-width:0; }
  .stepper{ flex-direction:column; }
  .stepper li{ justify-content:flex-start; text-align:left; padding:12px 14px; font-size:var(--fs-meta); }
}

/* ------------------------------------------------------------ interaksi -- */
@media (hover:hover){
  .nav-item:hover{ background:rgba(255,255,255,.10); color:#fff; }
  .skill-card:hover,.topic-card:hover,.jump:hover{ border-color:var(--brand-300); box-shadow:var(--sh-2); }
  .cta:hover{ filter:brightness(1.05); }
  /* 🔒 Bug ditemukan sesi "Notifikasi Jawaban Salah" — TANPA `:not(...)` ini,
     hover pointer yang lagi diam di atas tombol yang BARU dijawab (`.correct`/
     `.wrong`/`.selected`) menimpa border-color state itu jadi teal netral lagi
     (spesifisitas sama, urutan file menang) — di desktop kelihatan warna
     benar/salahnya "hilang" begitu mouse tidak digerakkan setelah klik. */
  .opt-btn:hover:not(.correct):not(.wrong):not(.selected){ border-color:var(--brand-300); }
  .chip:hover{ filter:brightness(.97); }
  .iconbtn:hover{ background:var(--brand-50); }
}

:focus-visible{
  outline:3px solid var(--brand-600);
  outline-offset:2px;
  border-radius:6px;
}
.rail :focus-visible,
.tabbar :focus-visible,
.spark :focus-visible{ outline-color:var(--sun-400); }

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

/* ================================================================
   Reading format "Baca Teks" (PILOT Explorer, `ReadingTextTopic`,
   materi/reading.md §19–§20) — kartu TEKS NYATA utuh, tampil beda per
   jenis teks (undangan / pesan chat / catatan) supaya tidak terlihat
   seperti daftar kata Vocab. Kalimat bisa ditap (pilih + dengar di
   Kenalan, 🔎 tunjuk bukti di Latihan Inti/Tantangan).
   ================================================================ */
.rt-card{
  max-width:520px; margin:8px auto 14px; padding:16px 16px 14px;
  background:var(--surface); border:3px solid var(--line); border-radius:var(--r-md);
}
.rt-heading{
  font-family:var(--font-head); font-weight:800; color:var(--ink);
  font-size:1.125rem; text-align:center; margin-bottom:8px;
}
.rt-line{
  font-family:var(--font-head); font-weight:700; color:var(--ink);
  font-size:clamp(1.0625rem,1rem + .4vw,1.25rem); line-height:1.45;
  padding:5px 10px; margin:2px 0; border-radius:12px;
  transition:background .15s ease, box-shadow .15s ease;
}
.rt-line .rt-id{ display:block; font-size:.875rem; font-weight:600; color:var(--ink-3); margin-top:1px; }
/* Undangan — pinggir putus-putus warna skill Reading */
.rt-invitation{ background:var(--c-read-bg); border:3px dashed var(--c-read); }
.rt-invitation .rt-line{ text-align:center; }
/* Pesan — nama pengirim di atas, tiap kalimat jadi gelembung chat */
.rt-message{ background:var(--brand-50); border-color:var(--brand-300); }
.rt-message .rt-heading{ text-align:left; color:var(--brand-800); }
.rt-message .rt-line{
  width:fit-content; max-width:100%; background:var(--surface);
  border-radius:16px 16px 16px 4px; margin:6px 0; box-shadow:0 1px 0 var(--line);
}
/* Catatan — kertas tempel kuning */
.rt-note{ background:var(--sun-100); border-color:var(--sun-300); }
.rt-note .rt-heading{ text-align:left; color:var(--sun-600); }
/* Status kalimat — spesifisitas 3 kelas supaya menang dari gaya genre */
.rt-card.tappable .rt-line{ cursor:pointer; }
.rt-card.tappable .rt-line:hover{ box-shadow:inset 0 0 0 2px var(--brand-300); }
.rt-card.asking .rt-line{ outline:2px dashed var(--brand-300); outline-offset:-2px; }
.rt-card .rt-line.selected{ background:var(--sun-300); box-shadow:inset 4px 0 0 var(--sun-500); }
.rt-card .rt-line.is-evidence{ background:var(--ok-bg); box-shadow:inset 0 0 0 2px var(--ok); color:var(--ok); outline:none; }
.rt-card .rt-line.is-wrong{ background:var(--try-bg); box-shadow:inset 0 0 0 2px var(--try); animation:shake .3s ease; }
.rt-page{ font-size:.8125rem; font-weight:700; color:var(--ink-3); }
/* Bullet halaman Kenalan Reading (1 deret semua halaman, mis. 2 buku × 5 =
   10) — di HP titik dikecilkan supaya muat 1 baris, bukan 8 + 2 timpang. */
@media (max-width: 480px){
  .rt-page-nav .quiz-dots{ gap:5px; }
  .rt-page-nav .quiz-dot{ width:26px; height:26px; font-size:.75rem; }
}
/* Grid 2 kolom: 4 tombol = 2×2 rapi di HP & desktop (flex-wrap sempat
   jadi 3+1 di desktop). */
/* 2–3 tombol (🔊/🎤/🎮) selalu 1 baris sama lebar — tidak ada kartu ganjil
   sendirian di baris ke-2 sejak 🌐 Arti dihapus. */
.rt-actions{ display:grid; grid-auto-flow:column; grid-auto-columns:1fr; gap:8px; margin:0 auto 14px; max-width:520px; }
.rt-act{
  font-family:var(--font-head); font-weight:700; font-size:.875rem; color:var(--ink);
  background:var(--surface); border:2px solid var(--line); border-radius:var(--r-pill);
  padding:8px 14px; cursor:pointer; box-shadow:var(--sh-1);
}
.rt-act.done{ background:var(--ok-bg); border-color:var(--ok); color:var(--ok); }
.rt-act.listening{ background:var(--try); border-color:var(--try); color:#fff; animation:pulse 1s infinite; }
/* 3 opsi teks → 1 kolom (2 kolom menyisakan 1 kartu ganjil, CLAUDE.md
   "Desain Harus Bagus di Mobile MAUPUN Desktop") */
.opt-grid.rt-opts{ grid-template-columns:1fr; max-width:520px; margin-left:auto; margin-right:auto; }
.opt-grid.rt-opts .opt-btn-text{ font-size:1.125rem; padding:14px; }
.rt-evidence-ask{ text-align:center; font-family:var(--font-head); font-weight:700; color:var(--brand-700); margin:8px 0; }
.rt-ask-word{
  display:flex; align-items:center; justify-content:center; flex-wrap:wrap; gap:8px;
  font-size:clamp(1.25rem,1.05rem + 0.9vw,1.6rem); color:var(--ink);
  background:var(--sun-100); border:2px solid var(--sun-400); border-radius:var(--r-md);
  padding:12px 16px; margin:12px auto; max-width:520px;
  animation:askPop .35s ease-out;
}
.rt-ask-word b{ color:var(--brand-700); font-weight:800; padding:0 .15em; border-bottom:3px solid var(--sun-500); }
@keyframes askPop{ from{ transform:scale(.92); opacity:0; } to{ transform:none; opacity:1; } }
@media (prefers-reduced-motion: reduce){ .rt-ask-word{ animation:none; } }

/* --- "Baca Teks" per level (materi/reading.md §21) --- */
/* Buku gambar mini (Little Stars/Starter): gambar besar dominan (Kemendikbud
   jenjang A) + 1 kalimat huruf besar tanpa kait; tiap kata = span agar bisa
   disorot saat dibacakan & ditap. */
.rt-book-page{
  max-width:520px; margin:8px auto 14px; padding:14px 16px 18px; text-align:center;
  background:var(--surface); border:3px solid var(--c-read); border-radius:var(--r-lg);
  box-shadow:var(--sh-1);
}
.rt-book-pic{ font-size:clamp(88px,26vw,140px); line-height:1.15; margin:4px 0 8px; }
.rt-book-text{
  font-family:var(--font-body); font-weight:800; color:var(--ink);
  font-size:clamp(1.75rem,1.4rem + 1.4vw,2.375rem); line-height:1.35;
}
.rt-book-id{ margin-top:6px; font-size:1rem; }
.rt-quiz-sentence{ padding:18px 16px; }
.rt-book-id{
  margin-top:10px; padding-top:10px; border-top:2px dashed var(--line);
  font-family:var(--font-body); font-weight:800; color:var(--brand-700);
  font-size:clamp(1.3rem,1.1rem + 0.9vw,1.75rem); line-height:1.35;
}
/* Tantangan buku mini (gambar → pilih kalimat): gambar lebih kecil supaya
   3 kalimat pilihan tetap kelihatan tanpa scroll di HP. */
.rt-rev-pic{ padding:8px 16px; }
.rt-rev-pic .rt-book-pic{ font-size:clamp(72px,20vw,110px); margin:0; }
.rt-word{ display:inline-block; padding:0 4px; border-radius:10px; transition:background .12s ease; cursor:pointer; }
.rt-word.lit{ background:var(--sun-300); }
/* Kata di kartu teks (Kenalan level lain): tanpa jarak ekstra & tap tetap ke baris. */
.rt-card .rt-word{ display:inline; padding:0 1px; border-radius:6px; cursor:inherit; }
.rt-card .rt-line.selected .rt-word.lit{ background:#fff; box-shadow:inset 0 0 0 2px var(--sun-500); }
.rt-book-text.asking .rt-word{ outline:2px dashed var(--brand-300); outline-offset:-1px; margin:2px 0; }
.rt-word.is-evidence{ background:var(--ok-bg); color:var(--ok); box-shadow:inset 0 0 0 2px var(--ok); outline:none !important; }
.rt-word.is-wrong{ background:var(--try-bg); box-shadow:inset 0 0 0 2px var(--try); animation:shake .3s ease; }
.rt-instr{ display:flex; align-items:center; justify-content:center; gap:6px; flex-wrap:wrap; margin-bottom:10px; }
.rt-say{
  width:40px; height:40px; border-radius:50%; border:2px solid var(--line); background:var(--surface);
  font-size:17px; cursor:pointer; display:inline-grid; place-items:center; box-shadow:var(--sh-1); flex:none;
}
.rt-pic-opt{ font-size:clamp(46px,13vw,64px); padding:16px 8px; }
.rt-pic-multi{ display:inline-flex; flex-wrap:wrap; justify-content:center; align-items:center; gap:.06em; max-width:100%; line-height:1.1; vertical-align:middle; }
.rt-pic-multi > span{ display:block; }
/* Kotak ikon kecil daftar Kenalan (.primer-ic, 40px): multi-ikon dipadatkan ke
   kotak ±40×40 yang SAMA lebarnya dgn ikon tunggal, supaya kolom teks tiap baris
   tetap rata — 2 sebaris, 3 sebaris, 4 = 2×2, 5–9 = 3 kolom, 10 = 4 kolom. */
.primer-ic{ width:48px; }
.primer-ic .rt-pic-multi{ display:inline-grid; grid-template-columns:repeat(3,auto); gap:1px; font-size:.33em !important; line-height:1; }
.primer-ic .rt-pic-multi[data-n="4"]{ grid-template-columns:repeat(2,auto); font-size:.48em !important; }
.primer-ic .rt-pic-multi[data-n="2"]{ grid-template-columns:repeat(2,auto); font-size:.56em !important; }
.primer-ic .rt-pic-multi[data-n="10"]{ grid-template-columns:repeat(4,auto); font-size:.25em !important; }
/* Kartu pilihan gambar lain (bukan .rt-pic-opt): 4 = 2×2, 5–6 = 3+3/3+2, 7–9 = 3 kolom. */
.opt-btn:not(.rt-pic-opt) .rt-pic-multi[data-n="4"]{ display:inline-grid; grid-template-columns:repeat(2,auto); }
.opt-btn:not(.rt-pic-opt) .rt-pic-multi:is([data-n="5"],[data-n="6"],[data-n="7"],[data-n="8"],[data-n="9"]){ display:inline-grid; grid-template-columns:repeat(3,auto); }
/* Kartu "Pilih & Ucapkan" (.choice-emoji, 26px): 2–5 sebaris, 6–10 = 5 kolom. */
.choice-emoji .rt-pic-multi{ display:inline-grid; grid-template-columns:repeat(5,auto); gap:1px; font-size:.5em !important; line-height:1; }
.choice-emoji .rt-pic-multi[data-n="2"],
.choice-emoji .rt-pic-multi[data-n="3"]{ grid-template-columns:repeat(3,auto); font-size:.7em !important; }
/* Kartu pilihan jawaban (sempit): susunan hitung tetap — 2/3 sebaris,
   4 = 2×2, 5 = 3+2 — bukan pecah acak 2+1. */
.rt-pic-opt .rt-pic-multi{ display:inline-grid; grid-template-columns:repeat(3,auto); justify-content:center; font-size:.58em !important; }
.rt-pic-opt .rt-pic-multi[data-n="2"],
.rt-pic-opt .rt-pic-multi[data-n="4"]{ grid-template-columns:repeat(2,auto); font-size:.72em !important; }
.rt-pic-opt .rt-pic-multi[data-n="5"] > span:nth-child(n+4){ transform:translateX(50%); }
.rt-pic-opt .rt-pic-multi[data-n="6"],.rt-pic-opt .rt-pic-multi[data-n="7"],.rt-pic-opt .rt-pic-multi[data-n="8"],.rt-pic-opt .rt-pic-multi[data-n="9"],.rt-pic-opt .rt-pic-multi[data-n="10"]{ font-size:.45em !important; }
.rt-pic-img{ height:1em; width:auto; max-width:100%; border-radius:.14em; vertical-align:middle; }
.opt-grid.rt-tf{ gap:10px; max-width:none; }
/* ✅ Cocok / ❌ Tidak Cocok: ukuran SAMA dgn "🔁 Coba Lagi"/"Lanjut ➡️"
   (.round-actions) — lebar penuh 2 kolom, tinggi 60px, teks 17px, 1 baris. */
.opt-grid.rt-tf .opt-btn.opt-btn-text{
  min-height:60px; padding:8px 10px; font-size:1.0625rem; line-height:1.2;
  display:flex; align-items:center; justify-content:center; white-space:nowrap;
}
.rt-hint-text{ margin:-4px auto 12px; max-width:520px; }
/* ✅❓ Benar / Salah / Tidak Disebut (Tantangan Achiever): 3 tombol sebaris. */
.opt-grid.rt-tfn{ grid-template-columns:repeat(3,1fr); gap:10px; max-width:640px; }
.opt-grid.rt-tfn > *:last-child:nth-child(odd){ grid-column:auto; }
.opt-grid.rt-tfn .opt-btn.opt-btn-text{ min-height:60px; padding:8px 6px; font-size:1rem; line-height:1.2; display:flex; align-items:center; justify-content:center; text-align:center; }
.rt-statement{
  max-width:620px; margin:14px auto 12px; padding:12px 16px; border-radius:16px;
  background:var(--sun-100); border:2px dashed var(--sun-400);
  font-family:var(--font-head); font-weight:700; font-size:clamp(1.0625rem,1rem + .4vw,1.25rem); color:var(--ink); text-align:center;
}
.rt-statement-label{ display:block; font-size:.75rem; letter-spacing:.06em; text-transform:uppercase; color:var(--sun-600); margin-bottom:2px; }
.rt-statement .rt-id{ display:block; font-size:.875rem; font-weight:600; color:var(--ink-3); margin-top:2px; font-family:var(--font-body, inherit); }
/* Explorer: arti soal (💡 tahap 1) langsung di bawah pertanyaan — ukuran & gaya SAMA dgn arti kalimat teks (.rt-line .rt-id). */
.rt-gap-chip{ display:inline-block; min-width:64px; padding:0 8px; margin:0 2px; border-bottom:3px dashed var(--sun-500); color:var(--sun-600); text-align:center; }
.rt-gap-chip.filled{ border-bottom-style:solid; border-color:var(--ok); color:var(--ok); background:var(--ok-bg); border-radius:8px 8px 0 0; }
.rt-card .rt-word.gap-tap{ cursor:pointer; padding:1px 3px; box-shadow:inset 0 0 0 1px var(--line); margin:1px 0; }
.rt-card .rt-word.gap-tap:hover{ box-shadow:inset 0 0 0 2px var(--brand-300); background:var(--brand-50); }
.rt-card .rt-word.gap-tap.gap-done{ cursor:default; box-shadow:none; }
.rt-hidden-line .rt-gap-chip{ min-width:120px; display:inline-flex; justify-content:center; vertical-align:bottom; }
.rt-find-ask{ text-align:center; font-family:var(--font-head); font-weight:700; color:var(--brand-700); margin:-6px 0 12px; }
.reading-question .rt-id{ display:block; font-size:.875rem; font-weight:600; color:var(--ink-3); margin-top:1px; font-family:inherit; }
/* Explorer: 2 pilihan = 2 kartu sebaris, lencana A/B berwarna. */
.opt-grid.rt-two{ grid-template-columns:1fr 1fr; gap:12px; max-width:560px; }
.opt-grid.rt-two .rt-two-card{
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:8px;
  min-height:112px; padding:14px 10px; font-size:1.0625rem; line-height:1.3; text-align:center;
  overflow-wrap:anywhere;
}
.rt-two-badge{
  width:34px; height:34px; border-radius:50%; display:flex; align-items:center; justify-content:center;
  font-family:var(--font-head); font-weight:800; font-size:1rem; color:#fff; flex:none;
  background:var(--sun-500); box-shadow:0 3px 0 rgba(0,0,0,.12);
}
.opt-grid.rt-two .rt-two-card:nth-child(2) .rt-two-badge{ background:var(--brand-500); }
.opt-grid.rt-two .rt-two-card:nth-child(3) .rt-two-badge{ background:#7C5CD6; }
.opt-grid.rt-two .rt-two-card:nth-child(4) .rt-two-badge{ background:var(--brand-800); }
.opt-grid.rt-four{ max-width:640px; }
.opt-grid.rt-four .rt-two-card{ min-height:96px; font-size:1rem; }
.opt-grid.rt-two .rt-two-card.correct .rt-two-badge{ background:var(--ok); }
.opt-grid.rt-two .rt-two-card.wrong .rt-two-badge{ background:var(--try); }
/* Kartu yang dipilih tetap terang (bukan ikut pudar tombol terkunci). */
.opt-grid.rt-two .rt-two-card.correct:disabled, .opt-grid.rt-two .rt-two-card.wrong:disabled{ opacity:1; }
.rt-card .rt-line.hinted{ background:var(--brand-100); box-shadow:inset 0 0 0 2px var(--brand-500); }
.rt-card .rt-line.rt-br{ margin-top:12px; }
.rt-speaker{ color:var(--brand-700); }
/* 🎮 Urutkan — nomor urut muncul di pojok kartu yang sudah benar */
.rt-order-card{ position:relative; }
.rt-order-card:not(.opt-btn-text){ font-size:clamp(46px,13vw,64px); padding:16px 8px; }
.rt-order-no{
  position:absolute; top:6px; left:8px; min-width:26px; height:26px; border-radius:50%;
  background:var(--ok); color:#fff; font:800 .875rem/26px var(--font-head); text-align:center; display:none;
}
.rt-order-card.correct .rt-order-no{ display:block; }
.rt-order-card.opt-btn-text{ padding-left:42px; text-align:left; }
/* Jenis teks tambahan */
.rt-story{ background:var(--surface); }
.rt-story .rt-heading, .rt-diary .rt-heading, .rt-article .rt-heading, .rt-email .rt-heading{ text-align:left; }
.rt-sign{ background:#FFF6E0; border:4px solid var(--sun-600); }
.rt-sign .rt-heading{ color:var(--sun-600); }
.rt-sign .rt-line{ text-align:center; }
.rt-dialog{ background:var(--brand-50); border-color:var(--brand-300); }
.rt-diary{ background:#FFFDF6; border-color:var(--sun-300); border-left:8px solid var(--c-read); }
.rt-article{ background:var(--surface); border-color:var(--ink-3); }
.rt-article .rt-heading{ border-bottom:2px solid var(--line); padding-bottom:6px; }
.rt-email{ background:var(--surface); border-color:var(--brand-300); }
.rt-email .rt-heading{ font-size:.9375rem; background:var(--brand-50); margin:-16px -16px 10px; padding:10px 16px; border-radius:15px 15px 0 0; color:var(--brand-800); }

/* Achiever/Trailblazer: ajakan buka Petunjuk sesudah jawaban belum tepat. */
.feedback .fb-hint-nudge{ display:block; margin-top:4px; font-size:.9375rem; font-weight:700; color:var(--sun-600); }

/* Sound Hunt (games/soundhunt.ts) — label jenis soal per markas, kartu
   gambar tanpa tulisan, & nomor urutan di markas Jago ("Tap the X, then tap
   the Y"): kartu yang sudah ditap benar diberi lencana 1. */
.sh-badge{ text-align:center; font-family:var(--font-head); font-weight:800; color:var(--ink-2); margin:var(--s3) 0 0; }
.opt-btn.sh-card{ position:relative; }
.opt-btn.sh-card .answer-card-bottom{ justify-content:center; }
.opt-btn.sh-picked{ border-color:var(--ok); background:var(--ok-bg); }
.sh-order{ position:absolute; top:8px; left:8px; width:26px; height:26px; border-radius:50%; background:var(--ok); color:#fff; display:grid; place-items:center; font-weight:800; font-size:.875rem; }

/* ------------------------------------------------------------------------
   🏰 Tantangan Raja — tes akhir level (`materi/test_level.md`,
   `games/boss.ts`). Arena = daftar 5 babak (1 kolom di semua lebar: 5 item
   di grid 2/3 kolom selalu menyisakan baris timpang), soal ber-bullet
   progress, layar hasil per babak.
   ------------------------------------------------------------------------ */
.boss-hub{ display:flex; flex-direction:column; gap:12px; max-width:640px; margin:0 auto; }
.boss-target{
  display:flex; align-items:center; gap:12px; padding:12px 14px;
  background:var(--sun-100); border:2px solid var(--sun-300); border-radius:var(--r-md);
}
.boss-target .boss-target-ic{ font-size:34px; line-height:1; flex:none; }
.boss-target b{ display:block; font-family:var(--font-head); font-size:var(--fs-h3); color:var(--ink); }
.boss-target span{ font-size:var(--fs-meta); color:var(--sun-600); font-weight:700; }
.boss-hub-dots{ display:flex; justify-content:center; gap:10px; }
.boss-hub-dots span{
  width:40px; height:40px; border-radius:50%; display:grid; place-items:center; font-size:20px;
  background:var(--surface); box-shadow:inset 0 0 0 2px var(--line); color:var(--ok); font-weight:800;
}
.boss-hub-dots span.done{ background:var(--ok-bg); box-shadow:inset 0 0 0 2px var(--ok); }
.boss-skill-list{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:10px; }
.boss-skill-row{
  width:100%; display:flex; align-items:center; gap:12px; padding:12px 14px; text-align:left;
  background:var(--surface); border:2px solid var(--line); border-radius:var(--r-md);
  font-family:inherit; cursor:pointer; box-shadow:var(--sh-1); transition:transform .12s ease, border-color .15s ease;
}
.boss-skill-row:active{ transform:scale(.98); }
.boss-skill-row:hover{ border-color:var(--brand-300); }
.boss-skill-ic{
  width:48px; height:48px; flex:none; border-radius:50%; display:grid; place-items:center; font-size:26px;
  background:radial-gradient(circle at 35% 30%, #fff, var(--brand-100));
}
.boss-skill-txt{ flex:1; min-width:0; display:flex; flex-direction:column; gap:3px; }
.boss-skill-txt b{ font-family:var(--font-head); font-size:var(--fs-h3); color:var(--ink); }
.boss-skill-status{ font-size:var(--fs-meta); color:var(--ink-2); font-weight:600; }
.boss-skill-bar{ display:block; height:6px; border-radius:var(--r-pill); background:var(--muted); overflow:hidden; margin-top:3px; }
.boss-skill-bar span{ display:block; height:100%; background:var(--brand-500); border-radius:inherit; }
.boss-skill-end{ flex:none; text-align:right; }
.boss-skill-go{ font-family:var(--font-head); font-weight:800; color:var(--brand-700); font-size:.9375rem; white-space:nowrap; }
.boss-skill.is-passed .boss-skill-row{ background:var(--ok-bg); border-color:var(--ok); }
.boss-skill.is-passed .boss-skill-ic{ background:radial-gradient(circle at 35% 30%, #fff, #BFE7CD); }
.boss-skill.is-progress .boss-skill-row{ border-color:var(--brand-500); box-shadow:0 0 0 3px var(--brand-100); }
.boss-skill.is-retry .boss-skill-row{ background:var(--sun-100); border-color:var(--sun-400); }
.boss-skill.is-retry .boss-skill-go{ color:var(--sun-600); }
.boss-stars{ font-size:15px; letter-spacing:1px; white-space:nowrap; }
.boss-mission{ display:flex; flex-wrap:wrap; align-items:center; gap:6px; margin:8px 2px 0; font-size:var(--fs-meta); color:var(--ink-2); font-weight:700; }
.boss-mission-chip{
  border:2px solid var(--sun-400); background:var(--surface); color:var(--sun-600); border-radius:var(--r-pill);
  padding:5px 12px; font-family:var(--font-head); font-weight:800; font-size:.8125rem; cursor:pointer;
}
.boss-mission-chip:hover{ background:var(--sun-100); }
.done-wrap .boss-mission{ justify-content:center; margin:6px 0; }
.boss-hub-note{ text-align:center; margin:0; }
.boss-hub > .ghost-btn{ align-self:center; }
.boss-hub > .primary-btn{ margin-top:0; }

.boss-q-head{ display:flex; align-items:center; justify-content:space-between; gap:8px; margin-bottom:8px; }
.boss-back-pill{
  border:2px solid var(--line); background:var(--surface); color:var(--brand-700); border-radius:var(--r-pill);
  padding:6px 14px; font-family:var(--font-head); font-weight:800; font-size:.875rem; cursor:pointer;
}
.boss-back-pill:hover{ border-color:var(--brand-300); }
.boss-skill-tag{
  background:var(--sun-100); color:var(--sun-600); border-radius:var(--r-pill); padding:6px 12px;
  font-family:var(--font-head); font-weight:800; font-size:.875rem; white-space:nowrap;
}
/* Layar soal: banner Arena disembunyikan (soal & pilihan muat 1 layar HP). */
.boss-playing .boss-arena{ display:none; }
.boss-playing .boss-arena + .boss-stage{ border-top-left-radius:var(--r-lg); border-top-right-radius:var(--r-lg); border-top-color:#F3DDA8; }
.quiz-dots.dense{ gap:6px; }
.quiz-dots.dense .quiz-dot{ width:26px; height:26px; font-size:.75rem; }
.boss-list{ display:flex; flex-direction:column; gap:8px; margin-top:12px; }
.opt-btn.boss-list-opt{
  display:flex; align-items:center; gap:12px; text-align:left; padding:12px 14px;
  font-size:clamp(1rem, .95rem + .3vw, 1.125rem); line-height:1.3; font-family:var(--font-body); font-weight:700;
}
.boss-letter{
  width:30px; height:30px; flex:none; border-radius:50%; display:grid; place-items:center;
  background:var(--brand-100); color:var(--brand-700); font-family:var(--font-head); font-weight:800; font-size:.875rem;
}
.boss-tf{ display:grid; grid-template-columns:1fr 1fr; gap:12px; margin-top:12px; }
.boss-tf .opt-btn{ font-size:clamp(1.125rem, 1rem + .6vw, 1.375rem); font-family:var(--font-head); font-weight:800; padding:18px 10px; }
.opt-btn.reveal{ border-color:var(--ok); box-shadow:inset 0 0 0 2px var(--ok); background:var(--ok-bg); }
.opt-btn:disabled{ cursor:default; }
.boss-answer{ display:inline-block; margin-top:4px; font-size:var(--fs-meta); color:var(--ink-2); }
.boss-pic{ text-align:center; font-size:clamp(56px, 16vw, 84px); line-height:1.1; margin:6px 0; }
.boss-bonus-note{ text-align:center; margin:6px 0 0; }
.boss-mic-stars{ font-size:22px; letter-spacing:3px; text-align:center; margin-top:8px; }
.boss-result-stars{ font-size:26px; letter-spacing:2px; text-align:center; margin:6px 0; }
.boss-result-stars .boss-stars{ font-size:inherit; }
.boss-result-actions{ display:flex; flex-direction:column; gap:10px; width:100%; max-width:420px; margin:14px auto 0; }
.boss-result-actions .primary-btn, .boss-result-actions .ghost-btn{ margin-top:0; width:100%; }
.round-actions.single .primary-btn{ flex:1; }
@media (min-width: 900px){
  .boss-skill-row{ padding:14px 18px; }
  .boss-list{ display:grid; grid-template-columns:1fr 1fr; }
  .boss-list:has(> :nth-child(5)), .boss-list:has(> :nth-child(3):last-child){ grid-template-columns:1fr; }
}

/* Story Quest (games/storyquest.ts) — 💡 Petunjuk menyorot kalimat bukti
   (bukan menyebut "kalimat ke-2"), & jawaban berupa kalimat (markas Sedang
   ke atas) tampil sbg daftar 1 kolom: teks di kiri, lencana huruf di kanan. */
.story-line.is-evidence{ background:color-mix(in srgb, var(--sun-500) 22%, transparent); border-radius:8px; padding:2px 6px; }
.opt-grid.sq-list{ grid-template-columns:1fr; max-width:560px; margin-inline:auto; }
.opt-grid.sq-list .opt-btn.answer-card{ padding:14px 16px; }
.opt-grid.sq-list .answer-card-bottom{ width:100%; justify-content:space-between; }
.opt-grid.sq-list .answer-card-label{ text-align:left; }

/* Balloon Hunt (games/balloonpop.ts, revisi tier 2026-09-29) — teks balon
   min 15px (dulu 11–13px, sulit dibaca di balon yang bergerak); kata ≥8
   huruf dapat balon lebih lebar & teks sedikit lebih kecil; balon gambar
   (Pemanasan); prompt gambar+kata (Mudah) & kalimat rumpang (Legendaris);
   💡 coret balon (.is-struck) & balon benar berkedip setelah 2x salah
   (.is-hint, Pemanasan). */
.bp-balloon.is-long{ width:clamp(76px, 22vw, 120px); }
.bp-balloon.is-long .bp-balloon-text{ font-size:clamp(.8125rem, 3.2vw, .9375rem); }
.bp-board.is-picture .bp-balloon{ width:clamp(76px, 22vw, 116px); }
.bp-balloon-pic{ font-size:clamp(34px, 10vw, 52px); line-height:1; }
.bp-balloon.is-struck{ opacity:.3; filter:grayscale(1); }
.bp-balloon.is-struck .bp-balloon-body::after{ content:'✕'; position:absolute; inset:0; display:grid; place-items:center; color:#fff; font-size:2rem; font-weight:900; }
.bp-balloon.is-hint .bp-balloon-body{ animation:ctaPulse 1.2s ease-in-out infinite; box-shadow:0 0 0 5px var(--ok); }
.bp-prompt-pic{ font-size:1.6em; vertical-align:middle; }
.bp-say{ border:0; background:transparent; font-size:1.25rem; cursor:pointer; vertical-align:middle; }
.bp-sentence{ border-radius:var(--r-lg); line-height:1.4; }
.bp-gap{ display:inline-block; width:3.5em; height:1em; vertical-align:-.15em; border-bottom:3px dashed var(--sun-600); }
.bp-meaning{ text-align:center; color:var(--ink-2); font-weight:700; margin:0 0 var(--s3); }
/* Rapor "🏰 Hasil Tantangan Raja": kalimat "Anak bisa…" di bawah babak lolos. */
.stat-list-label .can-do{ display:block; font-size:.75rem; font-weight:600; color:var(--ok); margin-top:2px; }
.stat-list-label .can-do::before{ content:'✓ '; }

/* Raja Kelompok (games/kelompok.ts) — kata/gambar yang dikelompokkan &
   keranjang berlabel Inggris. Jumlah keranjang 2 atau 3 → kolom sama
   dengan jumlahnya (1 baris rapi di HP & desktop). */
.kl-item{ display:flex; flex-direction:column; align-items:center; gap:6px; margin:var(--s3) 0; }
.kl-pic{ font-size:clamp(56px, 16vw, 84px); line-height:1; }
.kl-word{ font-family:var(--font-head); font-weight:800; font-size:clamp(1.75rem, 7vw, 2.25rem); color:var(--ink); letter-spacing:.02em; }
.kl-meaning{ font-weight:700; color:var(--ink-2); font-size:.875rem; }
.kl-baskets{ display:grid; gap:10px; margin:var(--s4) auto 0; max-width:560px; }
.kl-baskets.kl-n2{ grid-template-columns:repeat(2, minmax(0,1fr)); }
.kl-baskets.kl-n3{ grid-template-columns:repeat(3, minmax(0,1fr)); }
.opt-btn.kl-basket{ display:flex; flex-direction:column; align-items:center; justify-content:center; gap:6px; min-width:0; min-height:112px; padding:14px 8px; }
.kl-basket-icon{ font-size:clamp(34px, 9vw, 46px); line-height:1; }
.kl-basket-label{ font-family:var(--font-head); font-weight:800; font-size:clamp(.9375rem, 3.6vw, 1.0625rem); overflow-wrap:anywhere; text-align:center; }

/* ======================================================================
   Suasana game — Fase 2 (`materi/suasana_game.md`, src/game-ui.ts)
   ====================================================================== */

/* Tombol musik di header game — berlabel jelas (bukan ikon 🔇 saja), ≥44px. */
.act-head #musicToggleMount{ margin-left:auto; flex:none; }
.music-toggle{
  min-height:44px; padding:8px 14px; border-radius:var(--r-pill); border:2px solid var(--line);
  background:var(--surface); color:var(--ink-2); font-family:var(--font-head); font-weight:800; font-size:.875rem; cursor:pointer;
}
.music-toggle.is-on{ border-color:var(--k, var(--brand-500)); color:var(--ink); background:color-mix(in srgb, var(--k, var(--brand-500)) 12%, var(--surface)); }
.audio-toggles{ display:flex; flex-wrap:wrap; gap:10px; }
@media (max-width:420px){ .act-head .music-toggle{ padding:8px 10px; font-size:.75rem; } }

/* Latar dunia per game — pastel supaya teks & kartu tetap mudah dibaca;
   dekorasi emoji tipis di bawah (pseudo-element, tidak bisa diketuk). */
.raja-stage{ position:relative; overflow:hidden; }
.raja-stage > *{ position:relative; z-index:1; }
.raja-stage::after{
  content:var(--world-deco, ''); position:absolute; left:0; right:0; bottom:-6px; z-index:0;
  font-size:clamp(28px, 8vw, 44px); letter-spacing:.35em; text-align:center; white-space:nowrap; opacity:.22; pointer-events:none;
}
.raja-stage.world-kata{ background:linear-gradient(180deg,#FFF7E0 0%,#FDEFD2 60%,#F3E4C4 100%); --world-deco:'🏰 🌳 🏡 🌳 🏰'; }
.raja-stage.world-balon{ background:linear-gradient(180deg,#DDF2FF 0%,#EEF8FF 55%,#E6F7E4 100%); --world-deco:'☁️ 🎈 ☁️ 🎈 ☁️'; }
.raja-stage.world-susun{ background:linear-gradient(180deg,#F1ECFF 0%,#F8F5FF 60%,#EAE3FA 100%); --world-deco:'🧩 ✨ 🧩 ✨ 🧩'; }
.raja-stage.world-kelompok{ background:linear-gradient(180deg,#FFF1E6 0%,#FFF8F1 60%,#FBE8D6 100%); --world-deco:'🧺 🏪 🧺 🏪 🧺'; }
.raja-stage.world-ingatan{ background:linear-gradient(180deg,#EAF0FF 0%,#F4F7FF 60%,#E3E9FA 100%); --world-deco:'🕯️ 📖 🗝️ 📖 🕯️'; }
.raja-stage.world-soundhunt{ background:linear-gradient(180deg,#E3F6EA 0%,#F2FBF4 55%,#DCEFD9 100%); --world-deco:'🌲 🍄 🌳 🍄 🌲'; }
.raja-stage.world-storyquest{ background:linear-gradient(180deg,#FFF4EC 0%,#FFFAF5 60%,#F6E7D9 100%); --world-deco:'📚 🕯️ 📖 🕯️ 📚'; }

/* Peta petualangan ber-ilustrasi (src/game-ui.ts `journeyMapHtml`, referensi
   user: peta hijau + sungai + jembatan + lencana bintang). Ilustrasi = 1 SVG
   (`.jm-scene`), markas = tombol absolut di atasnya (posisi % dari kanvas
   yang sama, jadi selalu pas di lebar berapa pun). */
.journey-map{
  position:relative; margin:var(--s4) auto var(--s2); width:100%; max-width:480px;
  border-radius:24px; overflow:hidden; border:4px solid #fff; box-shadow:0 10px 26px rgba(15,46,56,.16);
}
.jm-scene{ position:absolute; inset:0; width:100%; height:100%; display:block; pointer-events:none; }
/* Tombol = lencana saja (titik tengahnya tepat di jalan); label menempel
   absolut di samping, jadi posisi lencana tidak bergeser oleh panjang label. */
.journey-node{
  --jb:clamp(58px, 17vw, 72px);
  position:absolute; z-index:1; left:var(--x); top:var(--y); transform:translate(-50%,-50%);
  width:var(--jb); height:var(--jb); display:block; padding:0; border:0; background:none; cursor:pointer; color:var(--ink);
}
.journey-node:disabled{ cursor:default; }
.jn-badge{
  position:relative; width:100%; height:100%; border-radius:22px; display:grid; place-items:center;
  background:linear-gradient(180deg,#FF7A6B,#E8453C); border:4px solid #fff;
  box-shadow:0 6px 0 #B92F28, 0 10px 18px rgba(15,46,56,.25);
  transition:transform .15s ease;
}
.jn-emoji{ font-size:clamp(28px, 8vw, 36px); line-height:1; filter:drop-shadow(0 2px 0 rgba(0,0,0,.15)); }
.journey-node:not(:disabled):active .jn-badge{ transform:translateY(4px); box-shadow:0 2px 0 #B92F28, 0 6px 12px rgba(15,46,56,.25); }
.journey-node.is-cleared .jn-badge{ background:linear-gradient(180deg,#FFD85A,#F5B521); box-shadow:0 6px 0 #C98A0E, 0 10px 18px rgba(15,46,56,.25); }
.journey-node.is-locked .jn-badge{ background:linear-gradient(180deg,#C9CFD4,#A5ADB4); box-shadow:0 6px 0 #7E868D, 0 10px 18px rgba(15,46,56,.2); }
.journey-node.is-locked .jn-emoji{ opacity:.45; filter:grayscale(1); }
.jn-lock{ position:absolute; font-size:22px; }
.jn-num{
  position:absolute; bottom:-10px; left:50%; transform:translateX(-50%); min-width:24px; height:24px; padding:0 6px; border-radius:12px;
  background:#fff; color:var(--ink); font-family:var(--font-head); font-weight:800; font-size:.8125rem; display:grid; place-items:center; box-shadow:var(--sh-1);
}
.jn-stars{ position:absolute; top:-17px; left:50%; transform:translateX(-50%); white-space:nowrap; font-size:15px; letter-spacing:-1px; }
.jn-stars i{ font-style:normal; color:#fff; opacity:.9; text-shadow:0 1px 2px rgba(0,0,0,.35); font-size:16px; }
.jn-here{ position:absolute; top:-48px; left:50%; margin-left:-.5em; font-size:26px; animation:jnBob 1.2s ease-in-out infinite; z-index:2; }
.jn-label{
  position:absolute; top:50%; left:calc(100% + 10px); transform:translateY(-50%);
  display:flex; flex-direction:column; align-items:flex-start; gap:4px; text-align:left; width:max-content;
  background:rgba(255,255,255,.94); border-radius:14px; padding:6px 10px; box-shadow:0 3px 8px rgba(15,46,56,.15);
  max-width:clamp(118px, 34vw, 160px);
}
.journey-node.is-right .jn-label{ left:auto; right:calc(100% + 10px); align-items:flex-end; text-align:right; }
.jn-label b{ font-family:var(--font-head); font-size:.8125rem; line-height:1.2; }
.journey-node.is-current .jn-badge{ animation:jnPulse 1.8s ease-in-out infinite; }
.journey-node.just-cleared .jn-badge{ animation:jnStamp .7s ease-out; }
.journey-node.just-opened .jn-badge{ animation:jnPop .6s ease-out .5s both; }
@keyframes jnPulse{ 0%,100%{ transform:scale(1); } 50%{ transform:scale(1.08); } }
@keyframes jnBob{ 0%,100%{ transform:translateY(0); } 50%{ transform:translateY(-6px); } }
@keyframes jnStamp{ 0%{ transform:scale(1.5) rotate(-12deg); } 60%{ transform:scale(.92); } 100%{ transform:scale(1); } }
@keyframes jnPop{ 0%{ transform:scale(.6); opacity:.3; } 70%{ transform:scale(1.12); opacity:1; } 100%{ transform:scale(1); } }

/* Jejak progres di dalam markas — bullet bernomor diganti langkah bergambar
   (⭐ sudah lewat, 👣 posisi sekarang), disambung garis seperti jalan. */
.raja-stage .quiz-nav .quiz-dots{ gap:6px; background:linear-gradient(var(--line),var(--line)) center/100% 3px no-repeat; }
.raja-stage .quiz-dot.static{ width:22px; height:22px; font-size:0; }
.raja-stage .quiz-dot.static.done::after{ content:'⭐'; font-size:13px; }
.raja-stage .quiz-dot.static.current:not(.done)::after{ content:'👣'; font-size:12px; }
.raja-stage .quiz-dot.static.done{ background:#FFF4C8; box-shadow:inset 0 0 0 1.5px #F5C542; }

/* Kalimat pemandu markas tampil sbg balon bicara. */
.raja-stage .latihan-head + .meta{
  position:relative; background:#fff; border-radius:16px; padding:10px 14px; box-shadow:var(--sh-1); color:var(--ink-2);
}

/* Tekan tombol di area main = sedikit mengecil (umpan balik sentuhan). */
/* Cuma tombol/kartu biasa — BUKAN tombol yang posisinya pakai transform
   (markas peta, balon): scale di sini menimpa translate-nya → tombol
   melompat saat ditekan & ketukan meleset. */
.raja-stage :is(.opt-btn, .ghost-btn, .primary-btn, .mm-card, .wm-card, .kl-basket):not(:disabled):active{ transform:scale(.97); }

/* Pengumuman masuk markas (±1,2 dtk, tidak menghalangi ketukan). */
.markas-intro{
  position:fixed; left:50%; top:38%; z-index:60; transform:translate(-50%,-50%);
  display:flex; flex-direction:column; align-items:center; gap:6px; pointer-events:none;
  padding:18px 26px; border-radius:24px; background:rgba(255,255,255,.95); box-shadow:0 16px 40px rgba(15,46,56,.22);
  animation:miShow 1.35s ease forwards;
}
.mi-emoji{ font-size:56px; line-height:1; }
.mi-name{ font-family:var(--font-head); font-weight:800; font-size:1.25rem; color:var(--ink); }
@keyframes miShow{ 0%{ opacity:0; transform:translate(-50%,-40%) scale(.8); } 18%{ opacity:1; transform:translate(-50%,-50%) scale(1); } 75%{ opacity:1; } 100%{ opacity:0; transform:translate(-50%,-60%) scale(.95); } }

@media (prefers-reduced-motion: reduce){
  .journey-node.is-current .jn-badge, .jn-here, .journey-node.just-cleared .jn-badge, .journey-node.just-opened .jn-badge{ animation:none; }
  .markas-intro{ animation:miShowStill 1.35s linear forwards; }
}
@keyframes miShowStill{ 0%,85%{ opacity:1; } 100%{ opacity:0; } }

/* Kartu "Hari Ini" Beranda (app.ts buildTodayCard) — strip 7 hari aktif di
   bawah tombol, versi mini `.day-chip` utk latar gelap `.spark`. */
.today-week{ display:flex; gap:4px; margin-top:12px; flex-wrap:wrap; }
.today-dot{
  width:30px; height:30px; border-radius:50%; display:grid; place-items:center;
  font-size:.75rem; font-weight:800; color:rgba(255,255,255,.75);
  background:rgba(255,255,255,.14); border:1.5px solid transparent;
}
.today-dot.is-active{ background:var(--sun-400); color:var(--brand-900); }
.today-dot.is-today{ border-color:#fff; }

/* Rapor: tombol "Detail Rapor" sejajar judul Progresmu + layar Detail Rapor */
.progress-panel-head{ display:flex; align-items:center; justify-content:space-between; gap:var(--s2); }
.progress-panel-head .eyebrow{ white-space:nowrap; }
.progress-panel-head .ghost-btn{ display:inline-block; width:auto; flex:none; margin-top:0; padding:6px 12px; font-size:.8125rem; }
.rule-grid{ display:grid; grid-template-columns:1fr; gap:var(--s3); }
@media (min-width: 900px){ .rule-grid{ grid-template-columns:repeat(2, 1fr); } }
.rule-card{ padding:var(--s4); }
.rule-card .rule-head{ display:flex; align-items:center; gap:var(--s2); margin-bottom:var(--s2); }
.rule-card .rule-head h3{ margin:0; font-size:1rem; }
.rule-card p{ margin:0; }
.rule-card .rule-how{ margin-top:var(--s2); }
/* Rapor: bar tipis "materi tuntas" di kartu skill (bintang = kemampuan). */
.skill-card .mini-track{ height:5px; margin-top:6px; border-radius:var(--r-pill); background:var(--muted); overflow:hidden; }
.skill-card .mini-fill{ height:100%; border-radius:inherit; background:var(--accent); }
.summary-card p{ margin:0; color:var(--ink-2); font-weight:700; }
.skill-card .skill-stars-row{ display:flex; flex-wrap:wrap; align-items:center; gap:4px 8px; margin:2px 0; font-size:var(--fs-meta); font-weight:800; color:var(--ink-2); }
.skill-card .skill-stars{ font-size:14px; letter-spacing:1px; }
.track-note{ margin-top:var(--s3); }
.track-note ul{ margin:var(--s2) 0 0; padding-left:1.1em; display:grid; gap:6px; }
.track-note li{ font-size:var(--fs-meta); font-weight:700; }
/* Rapor: Progres per Level */
.lvl-list{ list-style:none; margin:var(--s3) 0 0; padding:0; display:grid; gap:10px; }
.lvl-row{ display:grid; grid-template-columns:minmax(0, 11em) 1fr 3em; align-items:center; gap:var(--s2); }
.lvl-name{ font-family:var(--font-head); font-weight:700; font-size:.875rem; color:var(--ink); }
.lvl-cefr{ display:block; font-family:var(--font-body, inherit); font-size:.75rem; font-weight:700; color:var(--ink-3); }
.lvl-track{ height:10px; border-radius:var(--r-pill); background:var(--muted); overflow:hidden; }
.lvl-fill{ display:block; height:100%; border-radius:inherit; background:var(--brand-500); }
.lvl-row.done .lvl-fill{ background:var(--ok); }
.lvl-pct{ text-align:right; font-weight:800; font-size:.875rem; color:var(--ink); }

/* ---------------- Rapor (susunan 4 bagian, materi/rapor.md §6.3) ---------------- */
.rapor-layout{ display:grid; gap:var(--s4); }
.rapor-sec{ display:flex; flex-direction:column; gap:var(--s3); min-width:0; }
.rapor-sekilas{ display:grid; gap:var(--s4); }
.rapor-h{ font-family:var(--font-head); font-size:1.0625rem; margin:var(--s2) 0 0; color:var(--ink); }
.rapor-summary{ margin:8px 0 0; color:var(--ink-2); font-weight:700; line-height:1.5; }
.week-stats{ display:flex; flex-wrap:wrap; gap:6px 16px; margin-top:10px; font-size:var(--fs-meta); font-weight:700; color:var(--ink-3); }
.week-stats b{ font-family:var(--font-head); font-size:1rem; color:var(--ink); }
.skill-list{ list-style:none; margin:var(--s2) 0 0; padding:0; }
.skill-list li{ display:flex; align-items:center; gap:var(--s3); padding:10px 0; border-top:1px solid var(--line); }
.skill-list li:first-child{ border-top:none; }
.skill-row-ic{ width:38px; height:38px; border-radius:12px; flex:none; display:grid; place-items:center; font-size:19px; }
.skill-row-body{ flex:1; min-width:0; display:flex; flex-direction:column; gap:2px; }
.skill-row-body b{ font-family:var(--font-head); font-size:.9375rem; color:var(--ink); }
.skill-list .can-do{ display:block; font-size:.75rem; font-weight:700; color:var(--ok); }
.skill-list .skill-stars, .stat-list .skill-stars{ flex:none; font-size:12px; letter-spacing:0; }
.word-chips{ display:flex; flex-wrap:wrap; gap:6px; margin-top:8px; }
.rapor-more{ background:var(--surface); border:1px solid var(--line); border-radius:var(--r-lg); box-shadow:var(--sh-1); }
.rapor-more > summary{ list-style:none; cursor:pointer; display:flex; flex-direction:column; gap:2px; padding:var(--s4); font-family:var(--font-head); font-weight:700; color:var(--ink); }
.rapor-more > summary::-webkit-details-marker{ display:none; }
.rapor-more > summary::after{ content:'Tap untuk buka ▾'; font-family:var(--font-body, inherit); font-size:.75rem; font-weight:800; color:var(--accent); }
.rapor-more[open] > summary::after{ content:'Tutup ▴'; }
.rapor-more-grid{ display:grid; gap:var(--s3); padding:0 var(--s3) var(--s3); }
.rapor-more-grid .card{ box-shadow:none; }
@media (min-width: 900px){
  .rapor-layout{ grid-template-columns:repeat(2, minmax(0, 1fr)); align-items:start; }
  .rapor-sec.full{ grid-column:1 / -1; }
  .rapor-sekilas{ grid-template-columns:repeat(2, minmax(0, 1fr)); align-items:stretch; }
  .rapor-more-grid{ grid-template-columns:repeat(2, minmax(0, 1fr)); align-items:start; }
}
.screen-note{ margin:12px 0 0; padding:10px 12px; border-radius:var(--r-md); background:var(--sun-100); color:var(--ink-2); font-size:var(--fs-meta); font-weight:700; line-height:1.45; }
/* Rapor: Tren 4 Minggu (bar menit + kolom ketepatan) */
.trend-head, .trend-row{ display:grid; grid-template-columns:minmax(0, 7.5em) 1fr 3.6em 4.4em; align-items:center; gap:var(--s2); }
.trend-head{ margin-top:var(--s3); font-size:.75rem; font-weight:800; color:var(--ink-3); }
.trend-head span:nth-child(2){ grid-column:2 / 4; }
.trend-head span:last-child{ text-align:right; }
.trend-list{ list-style:none; margin:6px 0 0; padding:0; display:grid; gap:8px; }
.trend-label{ font-size:.8125rem; font-weight:700; color:var(--ink-2); }
.trend-row.is-now .trend-label{ color:var(--ink); font-weight:800; }
.trend-bar{ height:10px; border-radius:var(--r-pill); background:var(--muted); overflow:hidden; }
.trend-fill{ display:block; height:100%; border-radius:4px; background:var(--brand-500); }
.trend-min, .trend-acc{ text-align:right; font-size:.8125rem; font-weight:800; color:var(--ink); }
/* Rapor: Hasil Game (per game bisa dibuka → per markas) */
.game-results{ display:grid; gap:0; margin-top:var(--s2); }
.game-result{ border-top:1px solid var(--line); }
.game-result:first-child{ border-top:none; }
.game-result > summary{ list-style:none; cursor:pointer; display:flex; align-items:center; gap:var(--s3); padding:10px 0; }
.game-result > summary::-webkit-details-marker{ display:none; }
.game-result > summary .skill-row-body::after{ content:'Lihat tiap markas ▾'; font-size:.75rem; font-weight:800; color:var(--accent); }
.game-result[open] > summary .skill-row-body::after{ content:'Tutup ▴'; }
.game-result > .stat-list{ margin:0 0 var(--s2); padding-left:calc(38px + var(--s3)); }
.game-result-empty{ margin:0 0 var(--s2); padding-left:calc(38px + var(--s3)); }
@media (min-width: 900px){
  .game-results{ grid-template-columns:repeat(2, minmax(0, 1fr)); column-gap:var(--s5); }
  .game-result:nth-child(2){ border-top:none; }
}
