/* ============================================================
   FIEZEL Motion System — token gerak + animasi maskot + micro-interaction
   Bahasa gerak: pegas kecil, overshoot halus, cepat (90–450 ms UI).

   Sumber: /fiezel-motion/fiezel-motion.css (paket motion system <fiezel-mascot>).
   Berkas ini dimuat SETELAH style.css, jadi empat penyesuaian wajib dilakukan
   supaya paket ini tidak mengubah tampilan aplikasi di luar maskot. Semuanya
   ditandai [ADAPTASI] dan tidak menyentuh satu pun aturan gerak maskot:

   [ADAPTASI 1] Token TIDAK lagi dipasang di :root. Paket asli memakai nama
     --fz-gold / --fz-ink / --fz-line yang SUDAH dipakai style.css dengan nilai
     berbeda (#d9a441, #2c1b1c, #ecdec0). Memasangnya di :root akan mengganti
     palet seluruh aplikasi, bukan cuma maskot. Token kini hidup di dua kelas
     pembawa: .fz-mascot (dipakai komponen) dan .fz-motion (wadah opt-in untuk
     kelas micro-interaction di bawah).
   [ADAPTASI 2] Blok prefers-reduced-motion dibatasi ke subtree maskot. Aplikasi
     sudah punya jalur reduced-motion sendiri (style.css + .reduce-motion, dan
     prefersReducedMotion() di app.js); menimpanya dengan selektor * akan
     mematikan koreografi splash/nav yang sudah diatur di tempat lain.
   [ADAPTASI 3] Aturan :focus-visible global dihapus. Cincin fokus aplikasi
     sudah ditetapkan style.css dan dijaga a11y-test.js.
   [ADAPTASI 4] fzPop → fzMascotPop dan .fz-pop → .fz-motion-pop. style.css
     sudah punya @keyframes fzPop dengan isi berbeda; nama yang sama membuat
     definisi terakhir menang secara global.

   ============================================================
   [PAW 2026-08] Sistem gerak PAW (pau-redesign systems/10-motion, mengikat):

   ATURAN KERAS — TUBUH BEBAS ROTASI (10 §0, gerbang: keyframe-rotation-gate).
     rotate() hanya legal pada anggota badan berporos terdokumentasi: telinga,
     tulang ekor, lengan, alis, dan aksesori lepas (bintang/not/konfeti/Zz/bohlam).
     DILARANG pada .fz-all/.fz-body/.fz-head/.fz-face/.fz-eyes. Miring, condong,
     groove, angguk, goyang = translate + scale saja.
     Keyframe lama yang melanggar sudah DIHAPUS dan diganti (10 §0.1):
       fzTilt→fzGreetShift · fzLean→fzHeadLean · fzGroove→fzGrooveSway ·
       fzConf→fzConfShift · fzDrowse→fzDrowseCalm · fzLoveWiggle→fzLoveSway ·
       fzJumpBig (nama tetap, suku rotate dibuang) ·
       fzTailProud/Sway/Droop→fzTailTipFlick/fzTailBaseSway/fzTailTipSettle.

   [PAW-RIG] Rig produksi hari ini SATU bagian: belum ada .fz-head, .fz-tail-base,
     .fz-tail-tip, .fz-pupil, .fz-lid-up. Aturan di bawah sudah menyapa kelas rig
     Direction C itu, dengan fallback :where(..., .fz-svg:not(:has(...)) ...) ke
     elemen rig lama, sehingga:
       - hari ini gerak tetap hidup (face = wakil kepala, .fz-tail = satu tulang);
       - saat rig Direction C mendarat, selektor bone/pupil menang otomatis dan
         fallback-nya mati sendiri, tanpa aplikasi ganda.
     Catatan tanda ekor (17 R-3): pada rig 2-tulang, + = ujung naik. Pada fallback
     satu-jalur berporos 20% 95%, arah NAIK justru sudut NEGATIF — nilai di bawah
     ditulis untuk fallback; konversikan tandanya saat rig 2-tulang mendarat.

   [PAW-R5] Kedip di bawah reduced-motion: DILARANG KERAS (17 R-5). Loop kedip
     hidup di JS (fiezel-mascot.js _blinkLoop); blok CSS di bawah hanya jaring
     pengaman. Perubahan komponen ±2 baris yang WAJIB menyertai berkas ini
     (milik subagent states, JANGAN di berkas ini):
       _blinkLoop() { if (matchMedia('(prefers-reduced-motion: reduce)').matches
         || document.body.classList.contains('reduce-motion')) return; ... }
   ============================================================ */
.fz-mascot, .fz-motion {
  /* motion tokens
     [i15 2026-08-29, O4 §9.3 / audit 03-002] --fz-spring/--fz-out DIGANTI NAMA jadi
     --paw-spring/--paw-out: nama lama membayangi alias :root style.css (--fz-spring:
     var(--ease-spring) dst.) dengan KURVA BERBEDA (.34,1.56,.64,1 vs .34,1.4,.4,1) —
     jebakan shadowing. Tidak ada JS yang membaca nama lama (grep 2026-08-29); micro-ui.css
     (belum dimuat index.html) masih menyebut nama lama dan kini akan jatuh ke alias :root
     bila suatu saat dimuat — catatan untuk agen maskot. */
  --paw-spring: cubic-bezier(.34, 1.56, .64, 1);   /* overshoot lembut — reaksi/gestur/entrance */
  --paw-out:    cubic-bezier(.22, 1, .36, 1);       /* ease-out expo-ish — settle, gaze, akhir hold */
  --fz-calm:   cubic-bezier(.45, .05, .35, 1);     /* [PAW baru] loop ambient tak berujung (nafas, sway, drowse) */
  --fz-settle: cubic-bezier(.34, 1.12, .52, 1);    /* [PAW baru] settle serah-terima antar layar (rasa fzPawSettle) */
  --fz-fast: 120ms; --fz-base: 240ms; --fz-slow: 420ms;
  /* [PAW baru] token gerak karakter (10 §5.2) — TIDAK di :root, sesuai ADAPTASI 1 */
  --fz-beat: 140ms;        /* interval dasar viseme mulut saat speaking (dibaca JS, jitter ±20ms) */
  --fz-enter-dur: 320ms;   /* durasi bersama varian enter §2 */
  --fz-exit-dur: 200ms;    /* durasi bersama varian exit §2 (exit ≈ 0.6× enter) */
  /* --lx/--ly  : kanal lookAt (P9, JS lama) — dibaca .fz-eyes/.fz-face di bawah.
     --px/--py  : kanal micro-gaze (P8, JS baru) — dibaca .fz-pupil.
     --fz-lidup : occluder kelopak rig baru (P5/P6) — dibaca .fz-lid-up.
     Ketiganya di-set inline per instance oleh JS; default 0 lewat var(). */
  /* palet */
  --fz-yel:#FFD94F; --fz-yel-d:#EDB93A; --fz-cream:#FDFAF3; --fz-cream2:#FFF4DA;
  --fz-maroon:#8C2233; --fz-maroon-d:#6D1926; --fz-red:#D9536A; --fz-gold:#D8B36B;
  --fz-cocoa:#33201F; --fz-ink:#1d1114; --fz-muted:#6b5a60; --fz-panel:#fffdfc;
  --fz-line:#e9dfe1; --fz-green:#278268; --fz-green-soft:#e8f4ef; --fz-red-soft:#faecef;
}
/* [ADAPTASI 2] hanya subtree maskot; jalur reduced-motion aplikasi tidak disentuh.
   [PAW] Diperluas: semua keyframe baru (enter/exit, speak-bob, tulang ekor) otomatis
   ternetralkan karena hidup di bawah .fz-mascot; kelas enter/exit yang menempel di
   wadah NON-maskot (modal, strip) diikutkan eksplisit di bawah. */
@media (prefers-reduced-motion: reduce) {
  .fz-mascot, .fz-mascot *, .fz-mascot *::before, .fz-mascot *::after {
    animation-duration:.01ms !important; animation-delay:0ms !important;
    animation-iteration-count:1 !important; transition-duration:.01ms !important; }
  .fz-m-enter-pop, .fz-m-enter-rise, .fz-m-enter-slide-l, .fz-m-enter-slide-r,
  .fz-m-enter-peek, .fz-m-enter-fade, .fz-m-exit-sink, .fz-m-exit-fade,
  .fz-m-exit-slide-l, .fz-m-exit-slide-r {
    animation-duration:.01ms !important; animation-delay:0ms !important;
    animation-iteration-count:1 !important; }
  /* [PAW-R5] jaring pengaman anti-kedip: tanpa gate JS, tween 70ms runtuh jadi
     SNAP kelopak tiap 1.8–5.6 dtk — kebalikan niat reduced-motion. Kelas .blink
     dari JS tidak boleh menutup mata. Pose kelopak state (sleepy/speaking/thinking)
     tetap dipertahankan agar pose beku tetap terbaca (10 §5.5 lapis 3). */
  .fz-mascot.blink .fz-lid { transform: scale(1,0) !important; }
  .fz-mascot.st-sleepy .fz-lid { transform: scale(1,.62) !important; }
  .fz-mascot.st-thinking .fz-lid { transform: scale(1,.3) !important; }
  .fz-mascot.st-speaking .fz-lid { transform: scale(1,.12) !important; }
}
/* Murid yang mematikan gerak dari Pengaturan aplikasi (state.preferences.motion=false)
   membuat enhanceUI() memasang .reduce-motion di <body> (app.js). Aturan style.css
   sudah menjinakkan seluruh keturunannya; baris ini hanya menegaskannya untuk maskot.
   [PAW] Backstop yang sama menutup semua keyframe baru + anti-kedip R-5. */
body.reduce-motion .fz-mascot, body.reduce-motion .fz-mascot * {
  animation-duration:.01ms !important; animation-delay:0ms !important;
  animation-iteration-count:1 !important; transition-duration:.01ms !important; }
body.reduce-motion .fz-m-enter-pop, body.reduce-motion .fz-m-enter-rise,
body.reduce-motion .fz-m-enter-slide-l, body.reduce-motion .fz-m-enter-slide-r,
body.reduce-motion .fz-m-enter-peek, body.reduce-motion .fz-m-enter-fade,
body.reduce-motion .fz-m-exit-sink, body.reduce-motion .fz-m-exit-fade,
body.reduce-motion .fz-m-exit-slide-l, body.reduce-motion .fz-m-exit-slide-r {
  animation-duration:.01ms !important; animation-delay:0ms !important;
  animation-iteration-count:1 !important; }
body.reduce-motion .fz-mascot.blink .fz-lid { transform: scale(1,0) !important; }
body.reduce-motion .fz-mascot.st-sleepy .fz-lid { transform: scale(1,.62) !important; }
body.reduce-motion .fz-mascot.st-thinking .fz-lid { transform: scale(1,.3) !important; }
body.reduce-motion .fz-mascot.st-speaking .fz-lid { transform: scale(1,.12) !important; }

/* ================= MASKOT ================= */
.fz-mascot { position:relative; display:inline-block; width:200px; aspect-ratio:320/300; }
.fz-mascot .fz-svg { width:100%; height:100%; display:block; overflow:visible; }
.fz-mascot * { transform-box: fill-box; }
.fz-all, .fz-body, .fz-head, .fz-face, .fz-arm, .fz-ear, .fz-eyes, .fz-acc { transform-origin:center; }
/* [PAW] Poros terdokumentasi (10 §0.2, Direction C C6/C9; koordinat viewBox 320×300):
   - lengan dijangkarkan ulang dari 50% 8% bbox ke poros bahu C9 (115,185)/(198,182)
     supaya gestur terangkat bebas dari siluet kepala;
   - telinga dijangkarkan ulang ke poros pangkal-dalam (108,52)/(212,52);
   - ekor: fallback satu-tulang tetap 20% 95%; rig 2-tulang memakai
     base @(212,244) ±8° dan tip @(296,200) ±20° (cincin + mask ikut grup tip, C7). */
.fz-arm-l { transform-box: view-box; transform-origin: 115px 185px; }
.fz-arm-r { transform-box: view-box; transform-origin: 198px 182px; }
.fz-ear-l { transform-box: view-box; transform-origin: 108px 52px; }
.fz-ear-r { transform-box: view-box; transform-origin: 212px 52px; }
.fz-tail  { transform-origin: 20% 95%; }
.fz-tail-base { transform-box: view-box; transform-origin: 212px 244px; }
.fz-tail-tip  { transform-box: view-box; transform-origin: 296px 200px; }

/* --- idle: "hidup, tidak berisik" (10 §1.1) —
   anggaran: maks 4 node teranimasi (nafas, 2 telinga, ujung ekor) + 2 timer JS. --- */
/* nafas: SATU-SATUNYA loop yang selalu hidup; periode 2.6s, easing --fz-calm */
.fz-all { animation: fzBreathe 2.6s var(--fz-calm) infinite; transform-origin:50% 88%; }
@keyframes fzBreathe { 0%,100%{transform:scale(1,1)} 50%{transform:scale(1.015,.985)} }
/* kedutan telinga: 8% dari siklus 7.4s/9.1s, L/R tak sinkron (R telat 3s) —
   terbaca "sesekali", 92% waktunya diam */
.fz-ear-l { animation: fzEarTwitch 7.4s var(--fz-calm) infinite; }
.fz-ear-r { animation: fzEarTwitch 9.1s var(--fz-calm) infinite 3s; }
@keyframes fzEarTwitch { 0%,92%,100%{transform:rotate(0)} 94%{transform:rotate(-7deg)} 97%{transform:rotate(4deg)} }
/* sway ekor pelan ±4°, periode 5.2s — amplitudo kecil supaya tak menyaingi nafas.
   :where = spesifisitas nol, jadi state mana pun bebas menimpanya. */
.fz-mascot :where(.fz-tail-tip, .fz-svg:not(:has(.fz-tail-tip)) .fz-tail) {
  animation: fzTailIdleSway 5.2s var(--fz-calm) infinite; }
@keyframes fzTailIdleSway { 0%,100%{transform:rotate(0)} 50%{transform:rotate(-4deg)} }

/* --- mata: kedip + dua kanal pandang terpisah (10 §1.4) ---
   Kanal lookAt (P9): --lx/--ly di-set JS lama, digeser seluruh grup mata + paralaks wajah.
   Kanal micro-gaze (P8): --px/--py di-set JS micro-gaze/preset state, digeser PUPIL saja
   (rig baru; tanpa .fz-pupil kanal ini tidur, fallback per-state ada di st-thinking). */
.fz-eyes { transform: translate(var(--lx,0), var(--ly,0)); transition: transform .3s var(--paw-out); }
.fz-face { transform: translate(calc(var(--lx,0)*.4), calc(var(--ly,0)*.4)); transition: transform .35s var(--paw-out); }
.fz-pupil { transform: translate(var(--px,0), var(--py,0)); transition: transform .26s var(--paw-out); }
.fz-lid { transform: scale(1,0); transform-origin:50% 0%; transition: transform 70ms ease-in; }
.blink .fz-lid { transform: scale(1,1); }
/* occluder kelopak rig baru (P5/P6): turun 0…34px lewat --fz-lidup */
.fz-lid-up { transform: translateY(var(--fz-lidup, 0)); transition: transform .3s var(--paw-out); }

/* ====== STATE ======
   Semua kelas st-* yang dipasang komponen tetap terdefinisi di sini.
   Gerak kepala ditulis ke .fz-head (rig Direction C); fallback rig lama = .fz-face
   (preseden lama: fzNod/fzChinUp memang di wajah), kecuali confused yang wajahnya
   sudah dipakai fzShakeX — fallback-nya .fz-body (translate legal, hanya rotate yang haram). */

/* greeting: lambaian + geser kepala settle (pengganti fzTilt — badan TIDAK miring lagi) */
.st-greeting :is(.fz-head, .fz-svg:not(:has(.fz-head)) .fz-face) {
  animation: fzGreetShift 1.5s var(--paw-spring); }
@keyframes fzGreetShift { 0%{transform:translate(0,0)} 25%{transform:translate(-5px,1px)}
  70%{transform:translate(-4px,1px)} 100%{transform:translate(0,0)} }
.st-greeting .fz-arm-r { animation: fzWave 1.5s var(--paw-spring) 1; }
.st-greeting .fz-pads { animation: fzPadsShow 1.5s ease-out; }
@keyframes fzPadsShow { 0%{opacity:0} 12%{opacity:1} 86%{opacity:1} 100%{opacity:0} }
@keyframes fzWave { 0%{transform:rotate(0)} 20%{transform:rotate(-128deg)}
  36%{transform:rotate(-104deg)} 52%{transform:rotate(-130deg)} 68%{transform:rotate(-104deg)}
  82%{transform:rotate(-126deg)} 100%{transform:rotate(0)} }

/* curious: kepala condong ke depan (pengganti fzLean) + perk telinga asimetris
   (−10°/+4°, 300ms) + satu alis + kibas ujung ekor +14 */
.st-curious :is(.fz-head, .fz-svg:not(:has(.fz-head)) .fz-face) {
  animation: fzHeadLean .5s var(--paw-spring) forwards; }
@keyframes fzHeadLean { to { transform: translate(5px,2px); } }
.st-curious .fz-ear-l { animation:none; transform:rotate(-10deg); transition: transform .3s var(--paw-out); }
.st-curious .fz-ear-r { animation:none; transform:rotate(4deg);  transition: transform .3s var(--paw-out); }
.st-curious .fz-brows { opacity:1; }
.st-curious .fz-brow-l { transform: translateY(-4px) rotate(-8deg); }
.st-curious :where(.fz-tail-tip, .fz-svg:not(:has(.fz-tail-tip)) .fz-tail) {
  animation: fzTailTipFlick .6s var(--paw-spring) 1; }

/* thinking: pupil ke sudut atas (kanal --px/--py; fallback grup mata utk rig tanpa pupil)
   + kelopak setengah + kepala (2,−1) + lengan kanan +96° ke dagu (literal, 17 R-3)
   + titik-titik bergiliran. Persisten sampai caller mengganti state. Kedip boleh. */
.st-thinking { --px:6px; --py:-5px; --fz-lidup:10px; }
.st-thinking .fz-eyes:not(:has(.fz-pupil)) { transform: translate(6px,-5px); }
.st-thinking :is(.fz-head, .fz-svg:not(:has(.fz-head)) .fz-face) { transform: translate(2px,-1px); }
.st-thinking .fz-lid { transform: scale(1,.3); transition: transform .3s var(--paw-out); }
.st-thinking.blink .fz-lid { transform: scale(1,1); }
.st-thinking .fz-arm-r { transform: rotate(96deg); transition: transform .7s var(--paw-spring); }
.st-thinking .fz-brows { opacity:1; }
.st-thinking .fz-dots { opacity:1; }
.st-thinking .fz-dots circle { animation: fzDot 1.1s ease-in-out infinite; }
.st-thinking .fz-dots .d2 { animation-delay:.18s } .st-thinking .fz-dots .d3 { animation-delay:.36s }
@keyframes fzDot { 0%,100%{opacity:.25; transform:translateY(0)} 40%{opacity:1; transform:translateY(-5px)} }

/* listening: headphone + sway kepala translate-only (pengganti fzGroove yang memutar badan)
   + kontra-sway pangkal ekor ∓4° (poros ekor = legal) + not balok */
.st-listening .fz-headphones { opacity:1; animation: fzMascotPop .4s var(--paw-spring); }
.st-listening :is(.fz-head, .fz-svg:not(:has(.fz-head)) .fz-face) {
  animation: fzGrooveSway .62s var(--fz-calm) infinite alternate; }
@keyframes fzGrooveSway { from{transform:translateX(-3px)} to{transform:translateX(3px)} }
.st-listening :where(.fz-tail-base, .fz-svg:not(:has(.fz-tail-base)) .fz-tail) {
  animation: fzTailBaseSway .62s var(--fz-calm) infinite alternate-reverse; }
@keyframes fzTailBaseSway { from{transform:rotate(-4deg)} to{transform:rotate(4deg)} }
.st-listening .fz-eye-open { opacity:0 } .st-listening .fz-eye-happy { opacity:1 }
.st-listening .fz-notes { opacity:1 }
.st-listening .fz-note { animation: fzFloat 1.4s ease-in-out infinite; }
.st-listening .fz-note.n2 { animation-delay:.5s }
@keyframes fzFloat { 0%{transform:translate(var(--tx,0),8px); opacity:0} 25%{opacity:1}
  100%{transform:translate(var(--tx,0),-26px); opacity:0} }
.fz-note.n1{ --tx:264px } .fz-note.n2{ --tx:44px }
.st-listening .fz-note.n1{ transform-box:view-box }

/* encouraging: menunjuk + angguk ≤3px (angguk, bukan membungkuk — 10 §1.2) + alis naik */
.st-encouraging .fz-arm-r { animation: fzPoint 1.4s var(--paw-spring); }
@keyframes fzPoint { 0%{transform:rotate(0)} 25%{transform:rotate(-94deg)}
  72%{transform:rotate(-88deg)} 100%{transform:rotate(0)} }
.st-encouraging .fz-pads { animation: fzPadsShow 1.4s ease-out; }
.st-encouraging .fz-face { animation: fzNod .5s ease-in-out 2; }
@keyframes fzNod { 0%,100%{transform:translateY(0)} 50%{transform:translateY(3px)} }
.st-encouraging .fz-brows { opacity:1; transform: translateY(-2px); }

/* celebrating: lompat squash-stretch, intensitas lv-1..3 (10 §1.3) —
   telinga perk −10/+10 (literal), ujung ekor +18 flick, lv-3 lengan ±110–120 + pads */
.st-celebrating .fz-all { animation: fzJump .85s var(--paw-spring) 1; transform-origin:50% 95%; }
.st-celebrating.lv-2 .fz-all { animation: fzJump .8s var(--paw-spring) 2; }
.st-celebrating.lv-3 .fz-all { animation: fzJumpBig .9s var(--paw-spring) 2; }
@keyframes fzJump {
  0%{transform:scale(1,1) translateY(0)} 18%{transform:scale(1.1,.82) translateY(4px)}
  45%{transform:scale(.94,1.08) translateY(-40px)} 70%{transform:scale(1.05,.92) translateY(0)}
  100%{transform:scale(1,1) translateY(0)} }
/* [PAW] suku rotate(-4deg) di frame 45% DIBUANG (10 §0.1) — squash/stretch +
   translateY sudah membawa bacaan lompatnya, siluet tidak boleh miring */
@keyframes fzJumpBig {
  0%{transform:scale(1,1) translateY(0)} 16%{transform:scale(1.14,.78) translateY(6px)}
  45%{transform:scale(.92,1.1) translateY(-58px)}
  70%{transform:scale(1.06,.9) translateY(0)} 100%{transform:scale(1,1)} }
.st-celebrating .fz-arm-l { animation: fzArmsUpL .85s var(--paw-spring); }
.st-celebrating .fz-arm-r { animation: fzArmsUpR .85s var(--paw-spring); }
@keyframes fzArmsUpL { 30%,70%{transform:rotate(96deg)} }
@keyframes fzArmsUpR { 30%,70%{transform:rotate(-96deg)} }
.st-celebrating.lv-2 .fz-arm-l, .st-celebrating.lv-2 .fz-arm-r {
  animation-duration:.8s; animation-iteration-count:2; }
/* lv-3: sorak dua lengan literal L +115 / R −115 dengan pads (17 R-3) */
.st-celebrating.lv-3 .fz-arm-l { animation: fzArmsUpHiL .8s var(--paw-spring) 2; }
.st-celebrating.lv-3 .fz-arm-r { animation: fzArmsUpHiR .8s var(--paw-spring) 2; }
@keyframes fzArmsUpHiL { 30%,70%{transform:rotate(115deg)} }
@keyframes fzArmsUpHiR { 30%,70%{transform:rotate(-115deg)} }
.st-celebrating.lv-3 .fz-pads { animation: fzPadsShow .8s ease-out 2; }
.st-celebrating .fz-ear-l { animation:none; transform:rotate(-10deg); transition: transform .3s var(--paw-out); }
.st-celebrating .fz-ear-r { animation:none; transform:rotate(10deg);  transition: transform .3s var(--paw-out); }
.st-celebrating :where(.fz-tail-tip, .fz-svg:not(:has(.fz-tail-tip)) .fz-tail) {
  animation: fzTailTipFlick .85s var(--paw-spring) 1; }
.st-celebrating .fz-eye-open { opacity:0 } .st-celebrating .fz-eye-happy { opacity:1 }

/* confused: geser kepala (pengganti fzConf yang memutar badan; fallback rig lama = badan,
   karena wajahnya sedang dipakai fzShakeX) + alis turun + keringat + geleng ±6px */
.st-confused :is(.fz-head, .fz-svg:not(:has(.fz-head)) .fz-body) {
  animation: fzConfShift .5s var(--paw-spring) forwards; }
@keyframes fzConfShift { to{ transform: translate(-4px,1px); } }
.st-confused .fz-face { animation: fzShakeX .55s ease-in-out .25s 1; }
@keyframes fzShakeX { 0%,100%{transform:translateX(0)} 25%{transform:translateX(-6px)} 75%{transform:translateX(6px)} }
.st-confused .fz-brows { opacity:1 }
.st-confused .fz-brow-l { transform: rotate(14deg) translateY(2px); }
.st-confused .fz-brow-r { transform: rotate(-14deg) translateY(2px); }
.st-confused .fz-sweat { opacity:1; animation: fzSweat 1.6s ease-in forwards; }
@keyframes fzSweat { 0%{transform:translate(0,0) scale(.4); opacity:0}
  25%{opacity:1} 100%{transform:translate(0,26px) scale(1); opacity:0} }
.st-confused .fz-blush { opacity:.5 }

/* hinting: bohlam + kedip sebelah + condong kepala (memakai fzHeadLean, bukan fzLean) */
.st-hinting .fz-bulb { opacity:1; animation: fzMascotPop .45s var(--paw-spring); }
.st-hinting .fz-lid:not(.fz-lid-r) { transform:scale(1,1); }
.st-hinting :is(.fz-head, .fz-svg:not(:has(.fz-head)) .fz-face) {
  animation: fzHeadLean .5s var(--paw-spring) forwards; }
@keyframes fzMascotPop { 0%{transform:scale(0)} 70%{transform:scale(1.15)} 100%{transform:scale(1)} }

/* completion: lompat besar (tanpa rotate) + bintang + cincin ekor berkilau + perk telinga */
.st-completion .fz-all { animation: fzJumpBig .95s var(--paw-spring) 2; transform-origin:50% 95%; }
.st-completion .fz-arm-l { animation: fzArmsUpL 1s var(--paw-spring) 2; }
.st-completion .fz-arm-r { animation: fzArmsUpR 1s var(--paw-spring) 2; }
.st-completion .fz-ear-l { animation:none; transform:rotate(-10deg); transition: transform .3s var(--paw-out); }
.st-completion .fz-ear-r { animation:none; transform:rotate(10deg);  transition: transform .3s var(--paw-out); }
.st-completion :where(.fz-tail-tip, .fz-svg:not(:has(.fz-tail-tip)) .fz-tail) {
  animation: fzTailTipFlick .95s var(--paw-spring) 1; }
.st-completion .fz-eye-open { opacity:0 } .st-completion .fz-eye-happy { opacity:1 }
.st-completion .fz-stars { opacity:1 }
.st-completion .fz-stars path { animation: fzStar 1.2s var(--paw-spring) infinite; }
.st-completion .fz-stars .s2 { animation-delay:.2s } .st-completion .fz-stars .s3 { animation-delay:.4s }
@keyframes fzStar { 0%{transform:scale(0) rotate(0)} 50%{transform:scale(1.2) rotate(18deg)} 100%{transform:scale(0) rotate(36deg)} }
.st-completion .fz-ring { animation: fzRingGlow 1s ease-in-out infinite alternate;
  transform-box:fill-box; transform-origin:50% 50%; }
@keyframes fzRingGlow { from{transform:scale(1); opacity:1} to{transform:scale(1.3); opacity:.72} }

/* confetti dari JS */
.fz-confetti-layer { position:absolute; inset:0; pointer-events:none; overflow:visible; }
.fz-confetti-layer i { position:absolute; border-radius:2px; opacity:0;
  animation: fzConfetti 1.25s var(--paw-out) forwards; }
@keyframes fzConfetti {
  0%{opacity:1; transform:translate(0,0) rotate(0)}
  55%{opacity:1; transform:translate(calc(var(--cx) * .75), var(--cy)) rotate(calc(var(--cr) * .6))}
  100%{opacity:0; transform:translate(var(--cx), calc(var(--cy) + 130px)) rotate(var(--cr))} }

/* ===== 4 STATE TAMBAHAN: proud | sleepy | sad | love ===== */

/* proud: dada membusung + dagu naik + mata tertutup senang
   + tangan kiri menepuk dada. Transient (2200ms). */
.st-proud .fz-body { animation: fzPuff .6s var(--paw-spring) forwards; transform-origin:50% 92%; }
@keyframes fzPuff { 0%{transform:scale(1,1) translateY(0)}
  55%{transform:scale(1.09,1.04) translateY(-6px)}
  100%{transform:scale(1.06,1.02) translateY(-4px)} }
.st-proud .fz-eye-open { opacity:0 } .st-proud .fz-eye-happy { opacity:1 }
.st-proud .fz-arm-l { animation: fzChest .65s var(--paw-spring) forwards; }
/* -40deg = telapak kiri (ujung bawah lengan, ~122,236) mendarat tepat
   di tengah dada krem (~160,226); overshoot -46deg dulu ala pegas */
@keyframes fzChest { 0%{transform:rotate(0)} 60%{transform:rotate(-46deg)} 100%{transform:rotate(-40deg)} }
.st-proud .fz-face { animation: fzChinUp .6s var(--paw-out) forwards; }
@keyframes fzChinUp { to{ transform: translateY(-3px); } }
/* kibas ekor bangga (pengganti fzTailProud; nilai negatif = ujung naik pada fallback) */
.st-proud :where(.fz-tail-tip, .fz-svg:not(:has(.fz-tail-tip)) .fz-tail) {
  animation: fzTailTipFlick 1s var(--paw-spring) 1; }
@keyframes fzTailTipFlick { 0%,100%{transform:rotate(0)} 40%{transform:rotate(-14deg)}
  70%{transform:rotate(-8deg)} }

/* sleepy: oleng pelan TANPA rotasi (pengganti fzDrowse — beat "kepala ngedrop"
   dipertahankan lewat translateY + scale saja) + kelopak setengah turun + telinga
   lemas + Zz mengambang. Persisten — diakhiri event 'wake' / setState lain. */
.st-sleepy .fz-all { animation: fzDrowseCalm 3.4s var(--fz-calm) infinite; transform-origin:50% 88%; }
@keyframes fzDrowseCalm {
  0%,100%{transform:scale(1,1) translateY(0)}
  30%{transform:scale(1.012,.988) translateY(1px)}
  48%{transform:scale(1,1) translateY(3px)}  /* kepala ngedrop */
  62%{transform:scale(1,1) translateY(0)}
  80%{transform:scale(1.012,.988) translateY(1px)} }
/* drift kepala 0→2px (10 §0.1) — di rig lama dibawa wajah, transisi pelan */
.st-sleepy :is(.fz-head, .fz-svg:not(:has(.fz-head)) .fz-face) {
  transform: translateY(2px); transition: transform 1.2s var(--fz-calm); }
/* kelopak (lid) kuning turun 62% dari atas = mata setengah tertutup */
.st-sleepy .fz-lid { transform: scale(1,.62); transition: transform .45s var(--paw-out); }
.st-sleepy.blink .fz-lid { transform: scale(1,1); }  /* kedip penuh tetap jalan */
.st-sleepy .fz-eyes { transform: translate(0,2px); } /* pandangan turun */
.st-sleepy .fz-ear-l, .st-sleepy .fz-ear-r { animation:none; transition: transform .45s var(--paw-out); }
.st-sleepy .fz-ear-l { transform: rotate(-7deg); }
.st-sleepy .fz-ear-r { transform: rotate(7deg); }
.st-sleepy .fz-zzz { opacity:1 }
.st-sleepy .fz-z { animation: fzZzz 2.6s ease-in-out infinite; }
.st-sleepy .fz-z.z2 { animation-delay:1.3s }
@keyframes fzZzz { 0%{opacity:0; transform:translate(0,8px) scale(.55)}
  30%{opacity:1} 70%{opacity:.9}
  100%{opacity:0; transform:translate(10px,-24px) scale(1.08)} }
/* sway ekor tidur (pengganti fzTailSway satu-tulang) */
.st-sleepy :where(.fz-tail-base, .fz-svg:not(:has(.fz-tail-base)) .fz-tail) {
  animation: fzTailBaseSway 3.4s var(--fz-calm) infinite alternate; }

/* sad → gentle-concern: telinga terkulai DIPERHALUS +9/−9 literal (dulu ±17 —
   10 §1.2 tabel #14) + alis sedih (ujung dalam naik) + mulut turun (.fz-m-sad via JS)
   + badan merosot ≤6px + air mata (loop hanya selama hold). Transient (2400ms). */
.st-sad .fz-ear-l { animation: fzEarSadL .55s var(--paw-out) forwards; }
.st-sad .fz-ear-r { animation: fzEarSadR .55s var(--paw-out) forwards; }
@keyframes fzEarSadL { to{ transform: rotate(9deg) translateY(3px); } }
@keyframes fzEarSadR { to{ transform: rotate(-9deg) translateY(3px); } }
.st-sad .fz-brows { opacity:1 }
.st-sad .fz-brow-l { transform: rotate(-12deg) translateY(3px); }
.st-sad .fz-brow-r { transform: rotate(12deg) translateY(3px); }
.st-sad .fz-body { animation: fzSlump .55s var(--paw-out) forwards; transform-origin:50% 92%; }
@keyframes fzSlump { to{ transform: translateY(6px) scale(1.02,.96); } }
.st-sad .fz-eyes { transform: translate(0,4px); }   /* menatap ke bawah */
.st-sad .fz-face { transform: translateY(3px); }
.st-sad .fz-blush { opacity:.4 }
.st-sad .fz-tear { opacity:1; animation: fzTearDrop 1.9s ease-in infinite; }
@keyframes fzTearDrop { 0%{transform:translate(0,0) scale(.45); opacity:0}
  22%{opacity:1} 100%{transform:translate(0,24px) scale(1); opacity:0} }
/* ekor turun settle (pengganti fzTailDroop; ujung −10° semantik = positif pada fallback) */
.st-sad :where(.fz-tail-tip, .fz-svg:not(:has(.fz-tail-tip)) .fz-tail) {
  animation: fzTailTipSettle .6s var(--paw-out) forwards; }
@keyframes fzTailTipSettle { to{ transform: rotate(11deg) translateY(3px); } }

/* love: mata jadi hati (.fz-eye-love, pop lalu berdegup) + goyang gemas
   translate-only ±3px (pengganti fzLoveWiggle yang memutar badan) + kedua tangan
   merapat + blush menguat + hati mengambang. Transient (2000ms). */
.st-love .fz-eye-open { opacity:0 }
.st-love .fz-eye-love { opacity:1; animation: fzMascotPop .4s var(--paw-spring); } /* fzMascotPop sudah ada di core */
.st-love .fz-heart-eye { animation: fzHeartBeat .9s var(--paw-spring) infinite .35s; }
@keyframes fzHeartBeat { 0%,100%{transform:scale(1)} 28%{transform:scale(1.18)}
  44%{transform:scale(.94)} 60%{transform:scale(1.1)} }
.st-love .fz-body { animation: fzLoveSway .85s ease-in-out 2; transform-origin:50% 92%; }
@keyframes fzLoveSway { 0%,100%{transform:translateX(0)} 25%{transform:translateX(3px)}
  75%{transform:translateX(-3px)} }
.st-love .fz-arm-l { animation: fzHugL .6s var(--paw-spring) forwards; }
.st-love .fz-arm-r { animation: fzHugR .6s var(--paw-spring) forwards; }
@keyframes fzHugL { to{ transform: rotate(-20deg); } }
@keyframes fzHugR { to{ transform: rotate(20deg); } }
.st-love .fz-blush { animation: fzBlushPulse .8s ease-in-out infinite alternate; }
@keyframes fzBlushPulse { from{opacity:.75; transform:scale(1)} to{opacity:1; transform:scale(1.15)} }
.st-love .fz-hearts { opacity:1 }
.st-love .fz-heart { animation: fzHeartFloat 1.5s ease-in-out infinite; }
.st-love .fz-heart.hh2 { animation-delay:.55s }
@keyframes fzHeartFloat { 0%{transform:translateY(8px) scale(.5); opacity:0}
  25%{opacity:1} 100%{transform:translateY(-26px) scale(1); opacity:0} }

/* ===== STATE BARU PAW (10 §1.5, 17 R-2) — CSS mendarat duluan; kelas st-* nya
   dipasang komponen pada gelombang states. Tanpa JS, blok ini tidur tanpa efek. ===== */

/* speaking (10 §1.5): mulut = beat viseme dari JS (interval --fz-beat, toggle display);
   badan hanya fzSpeakBob sub-piksel supaya mulut tetap jadi titik fokus — TANPA
   aktivitas lengan/telinga. Kedip boleh. Persisten (hold:0), diakhiri speak-end. */
.st-speaking { --fz-lidup: 4px; }
.st-speaking :is(.fz-head, .fz-svg:not(:has(.fz-head)) .fz-face) {
  animation: fzSpeakBob 1.6s var(--fz-calm) infinite; }
@keyframes fzSpeakBob { 0%,100%{transform:translateY(0)} 50%{transform:translateY(-1px)} }
.st-speaking .fz-lid { transform: scale(1,.12); transition: transform .3s var(--paw-out); } /* kelopak lembut 4/34 */
.st-speaking.blink .fz-lid { transform: scale(1,1); }
.st-speaking .fz-brows { opacity:0; }
.st-speaking .fz-ear-l, .st-speaking .fz-ear-r { animation:none; } /* telinga hening, fokus ke mulut */
.st-speaking :where(.fz-tail-tip, .fz-svg:not(:has(.fz-tail-tip)) .fz-tail) {
  animation:none; transform:rotate(-3deg); transition: transform .4s var(--paw-out); } /* tip +6 semantik, tahan */
/* Kelas beat viseme (dipasang JS di host per --fz-beat; sp1→sp2→open→sp1, jeda→soft).
   Rig lama belum punya bentuk sp1/sp2/soft — dipetakan ke bentuk mulut terdekat yang ada;
   !important perlu karena komponen menyembunyikan mulut via style inline display:none. */
.fz-mascot.fz-vis-sp1 .fz-m, .fz-mascot.fz-vis-sp2 .fz-m,
.fz-mascot.fz-vis-open .fz-m, .fz-mascot.fz-vis-soft .fz-m { display:none !important; }
.fz-mascot.fz-vis-sp1 .fz-m-sp1,  .fz-mascot.fz-vis-sp1 .fz-svg:not(:has(.fz-m-sp1)) .fz-m-o { display:block !important; }
.fz-mascot.fz-vis-sp2 .fz-m-sp2,  .fz-mascot.fz-vis-sp2 .fz-svg:not(:has(.fz-m-sp2)) .fz-m-wave { display:block !important; }
.fz-mascot.fz-vis-open .fz-m-open { display:block !important; }
.fz-mascot.fz-vis-soft .fz-m-soft, .fz-mascot.fz-vis-soft .fz-svg:not(:has(.fz-m-soft)) .fz-m-smile { display:block !important; }

/* welcome-back (17 R-2b, 09 §2.10; transient 2200ms di JS): perk pengenalan 300ms
   (telinga −8/+8 literal) → lambaian + kibas ekor dobel */
.st-welcome-back .fz-ear-l { animation:none; transform:rotate(-8deg); transition: transform .3s var(--paw-out); }
.st-welcome-back .fz-ear-r { animation:none; transform:rotate(8deg);  transition: transform .3s var(--paw-out); }
.st-welcome-back .fz-arm-r { animation: fzWave 1.5s var(--paw-spring) .3s 1; }
.st-welcome-back .fz-pads { animation: fzPadsShow 1.5s ease-out .3s; }
.st-welcome-back :is(.fz-head, .fz-svg:not(:has(.fz-head)) .fz-face) {
  animation: fzGreetShift 1.5s var(--paw-spring) .3s; }
.st-welcome-back :where(.fz-tail-tip, .fz-svg:not(:has(.fz-tail-tip)) .fz-tail) {
  animation: fzTailTipFlick .5s var(--paw-spring) .3s 2; }
.st-welcome-back .fz-eye-open { opacity:0 } .st-welcome-back .fz-eye-happy { opacity:1 }

/* lesson-start (17 R-2c, 09 §2.11; transient 1600ms di JS): beat Curious 600ms
   (condong + perk telinga dekat + kibas ekor +14) lalu istirahat Happy → idle */
.st-lesson-start :is(.fz-head, .fz-svg:not(:has(.fz-head)) .fz-face) {
  animation: fzHeadLean .6s var(--paw-spring) 1; }
.st-lesson-start .fz-ear-l { animation:none; transform:rotate(-10deg); transition: transform .3s var(--paw-out); }
.st-lesson-start :where(.fz-tail-tip, .fz-svg:not(:has(.fz-tail-tip)) .fz-tail) {
  animation: fzTailTipFlick .6s var(--paw-spring) 1; }
.st-lesson-start .fz-eye-open { opacity:0 } .st-lesson-start .fz-eye-happy { opacity:1 }

/* ===== ENTER / EXIT (10 §2) =====
   Prinsip: tak pernah potong display:none — kemunculan/kepergian selalu dianimasikan
   (min 120ms fade bahkan di 28px); exit ≈ 0.6× enter; enter boleh overshoot, exit tidak.
   Pemakaian: kelas dipasang pada WADAH wajah efemeral (strip/modal/baris), BUKAN pada
   maskot ter-dok yang punya transform translateX(-50%) — kecuali -peek yang memang
   ditulis untuk elemen ter-dok di dalam potongan kepala (has-mascot).
   JS melepas node pada animationend DENGAN fallback setTimeout 400ms (pola [P1-1]).
   Nama sudah dicek bentrok terhadap fzPaw*/fzPop milik style.css. */
.fz-m-enter-pop  { animation: fzMEnterPop 380ms var(--paw-spring, cubic-bezier(.34,1.56,.64,1)) both; }
@keyframes fzMEnterPop { 0%{opacity:0; transform:scale(.62)} 70%{opacity:1; transform:scale(1.07)}
  100%{opacity:1; transform:scale(1)} }
.fz-m-enter-rise { animation: fzMEnterRise 240ms var(--paw-spring, cubic-bezier(.34,1.56,.64,1)) both; }
@keyframes fzMEnterRise { from{opacity:.55; transform:translateY(8px) scale(.88)}
  to{opacity:1; transform:none} }
.fz-m-enter-slide-l { --fz-sx:-24px; }
.fz-m-enter-slide-r { --fz-sx:24px; }
.fz-m-enter-slide-l, .fz-m-enter-slide-r {
  animation: fzMEnterSlideX var(--fz-enter-dur, 320ms) var(--paw-out, cubic-bezier(.22,1,.36,1)) both; }
@keyframes fzMEnterSlideX { from{opacity:0; transform:translateX(var(--fz-sx,-24px))}
  to{opacity:1; transform:none} }
/* peek: kepala naik ke dalam lingkaran potong (dok 38–56px); translateX(-50%)
   dipertahankan karena maskot ter-dok memang diposisikan begitu (lihat blok PENEMPATAN) */
.fz-m-enter-peek { animation: fzMEnterPeek 420ms var(--fz-settle, cubic-bezier(.34,1.12,.52,1)) both; }
@keyframes fzMEnterPeek { from{transform:translate(-50%,60%)} to{transform:translate(-50%,0)} }
.fz-m-enter-fade { animation: fzMEnterFade 200ms ease-out both; }
@keyframes fzMEnterFade { from{opacity:0} to{opacity:1} }
.fz-m-exit-sink  { animation: fzMExitSink var(--fz-exit-dur, 200ms) ease-in both; }
@keyframes fzMExitSink { to{opacity:0; transform:translateY(10px) scale(.92)} }
.fz-m-exit-fade  { animation: fzMExitFade 160ms ease-in both; }
@keyframes fzMExitFade { to{opacity:0} }
.fz-m-exit-slide-l { --fz-sxo:-20px; }
.fz-m-exit-slide-r { --fz-sxo:20px; }
.fz-m-exit-slide-l, .fz-m-exit-slide-r {
  animation: fzMExitSlideX var(--fz-exit-dur, 200ms) ease-in both; }
@keyframes fzMExitSlideX { to{opacity:0; transform:translateX(var(--fz-sxo,-20px))} }

/* ================= MICRO-INTERACTIONS UI ================= */
.fz-btn { font:inherit; font-weight:700; border:none; cursor:pointer; border-radius:999px;
  padding:14px 28px; background:var(--fz-maroon); color:#fff;
  transition: transform var(--fz-fast) var(--paw-spring), box-shadow var(--fz-fast) ease,
              background var(--fz-fast) ease;
  box-shadow: 0 3px 0 var(--fz-maroon-d); }
.fz-btn:hover { transform: translateY(-2px); }
.fz-btn:active { transform: translateY(1px) scale(.97); box-shadow: 0 1px 0 var(--fz-maroon-d); }
.fz-btn.gold { background:var(--fz-gold); color:var(--fz-ink); box-shadow:0 3px 0 #b8935a; }
.fz-btn.ghost { background:var(--fz-panel); color:var(--fz-ink); box-shadow:0 0 0 2px var(--fz-line) inset; }

.fz-card { background:var(--fz-panel); border:2px solid var(--fz-line); border-radius:20px;
  padding:18px 20px; transition: transform var(--fz-base) var(--paw-spring),
  border-color var(--fz-fast) ease, box-shadow var(--fz-base) ease; }
.fz-card.clickable { cursor:pointer; }
.fz-card.clickable:hover { transform: translateY(-3px) scale(1.015); border-color:var(--fz-gold);
  box-shadow: 0 10px 24px rgba(140,34,51,.10); }
.fz-card.clickable:active { transform: scale(.98); }

/* pop-in berjenjang */
.fz-motion-pop { opacity:0; animation: fzPopIn .45s var(--paw-spring) forwards; }
@keyframes fzPopIn { from{opacity:0; transform:translateY(16px) scale(.94)}
  to{opacity:1; transform:translateY(0) scale(1)} }
.fz-stagger > * { opacity:0; animation: fzPopIn .45s var(--paw-spring) forwards; }
.fz-stagger > *:nth-child(1){animation-delay:.03s} .fz-stagger > *:nth-child(2){animation-delay:.09s}
.fz-stagger > *:nth-child(3){animation-delay:.15s} .fz-stagger > *:nth-child(4){animation-delay:.21s}
.fz-stagger > *:nth-child(5){animation-delay:.27s} .fz-stagger > *:nth-child(6){animation-delay:.33s}

/* jawaban benar / salah */
.fz-answer { position:relative; }
.fz-answer.correct, .fz-answer.wrong { opacity:1 !important; }
.fz-answer.correct { border-color:var(--fz-green); background:var(--fz-green-soft);
  animation: fzCorrect .5s var(--paw-spring); }
@keyframes fzCorrect { 30%{transform:scale(1.04)} }
.fz-answer.wrong { border-color:var(--fz-red); background:var(--fz-red-soft);
  animation: fzShake .45s ease-in-out; }
@keyframes fzShake { 0%,100%{transform:translateX(0)} 20%{transform:translateX(-8px)}
  40%{transform:translateX(8px)} 60%{transform:translateX(-5px)} 80%{transform:translateX(5px)} }

/* XP bar */
.fz-xp { height:16px; background:#e6d5d9; border:1px solid #c9b3b8; border-radius:999px; overflow:hidden; }
.fz-xp > b { display:block; height:100%; width:0; border-radius:999px;
  background: linear-gradient(90deg, var(--fz-gold), var(--fz-yel));
  transition: width .8s var(--paw-spring); }
.fz-xp.pulse > b { animation: fzXp .6s var(--paw-spring); }
@keyframes fzXp { 50%{transform:scaleY(1.3)} }

/* toast / badge */
.fz-badge-pop { animation: fzMascotPop .5s var(--paw-spring); }
.fz-float-xp { position:absolute; font-weight:800; color:var(--fz-green);
  animation: fzFloatXp 1s var(--paw-out) forwards; pointer-events:none; }
@keyframes fzFloatXp { from{opacity:1; transform:translateY(0)} to{opacity:0; transform:translateY(-34px)} }

/* equalizer kecil utk listening */
.fz-eq { display:flex; gap:4px; align-items:flex-end; height:28px; }
.fz-eq i { width:6px; border-radius:3px; background:var(--fz-maroon); height:100%;
  transform:scaleY(.22); transform-origin:50% 100%;
  animation: fzEq .5s ease-in-out infinite alternate; }
.fz-eq i:nth-child(2){animation-delay:.1s; background:var(--fz-gold)}
.fz-eq i:nth-child(3){animation-delay:.2s} .fz-eq i:nth-child(4){animation-delay:.3s; background:var(--fz-gold)}
.fz-eq i:nth-child(5){animation-delay:.15s}
@keyframes fzEq { from{transform:scaleY(.22)} to{transform:scaleY(1)} }
.fz-eq.paused i { animation:none; transform:scaleY(.22); }

/* ============================================================
   PENEMPATAN DI FIEZEL-APPS
   Tiga wadah bulat aplikasi berbagi masalah yang sama: latarnya var(--yellow)
   dan diameternya 38-58px. Maskot badan-penuh di dalam lingkaran sekecil itu
   jadi dua hal buruk sekaligus - kuning di atas kuning, dan detail yang mustahil
   dibaca. Solusinya satu pola, dipakai bertiga:

     1. wadah dapat kelas has-mascot dari JS -> latarnya jadi krem, jadi badan
        kuning maskot punya bidang untuk berdiri;
     2. wadah memotong (overflow:hidden) dan maskot dibuat lebih besar dari
        wadahnya, ditarik ke atas, sehingga yang terlihat KEPALANYA saja.
        Kepala = mata, kumis, pipi. Itu yang terbaca di 38px, bukan badan.

   Angka 178% dan -8% bukan tebakan: kepala + telinga menempati y 14..194 dari
   viewBox 320x300, yaitu 0,5625 dari lebar. Supaya kepala mengisi wadah penuh,
   maskot harus 1/0,5625 = 178% lebar wadah, dan pusat kepala (y 104) digeser
   agar duduk di tengah wadah.
   ============================================================ */

.fz-coach-bubble.has-mascot,
.fz-coach-avatar.has-mascot,
.coach-strip-face.has-mascot {
  overflow:hidden;
  background:var(--cream, #FDFAF3);
}
/* position:relative HANYA untuk dua wadah yang aslinya statis. Gelembung
   pembimbing sudah position:fixed (style.css) dan itu yang menempelkannya di
   pojok kanan bawah - menimpanya dengan relative membuatnya jatuh mengikuti
   aliran dokumen, jauh di bawah lipatan. Sudah pernah terjadi sekali di sini. */
.fz-coach-avatar.has-mascot,
.coach-strip-face.has-mascot { position:relative; }
.fz-coach-bubble.has-mascot fiezel-mascot,
.fz-coach-avatar.has-mascot fiezel-mascot,
.coach-strip-face.has-mascot fiezel-mascot {
  position:absolute; left:50%; top:-8%; width:178%; height:auto;
  transform:translateX(-50%);
  /* WAJIB: tanpa ini klik jatuh ke maskot dan tidak pernah sampai ke <button>
     induk yang membuka panel pembimbing. */
  pointer-events:none;
}
/* style.css memaku .coach-strip-face svg ke 24px untuk ikon paw. Maskot butuh
   svg yang mengisi elemennya sendiri. */
.coach-strip-face.has-mascot fiezel-mascot .fz-svg { width:100%; height:100%; }

/* Bayangan lantai dan confetti tidak berarti di dalam lingkaran terpotong:
   yang pertama tidak terlihat, yang kedua hanya menabrak tepi potongan.
   Selebrasinya tetap terbaca dari gerak kepala dan matanya. */
.has-mascot fiezel-mascot .fz-shadow,
.has-mascot fiezel-mascot .fz-confetti-layer { display:none; }
