
      /* Empat font utama, self-hosted (dulu dari Google Fonts; pindah
         ke situs sendiri supaya tidak diblokir Brave Shields dan
         pemblokir pelacak lain — tampilannya identik, file font dan
         weight-nya sama persis dengan yang Google Fonts layani):
         Baloo 2 700/800, Caveat 600, Press Start 2P 400, VT323 400. */
      @font-face{
        font-family:"Baloo 2";
        src:url("assets/fonts/baloo-2-700.woff2") format("woff2");
        font-weight:700;
        font-style:normal;
        font-display:swap;
      }
      @font-face{
        font-family:"Baloo 2";
        src:url("assets/fonts/baloo-2-800.woff2") format("woff2");
        font-weight:800;
        font-style:normal;
        font-display:swap;
      }
      @font-face{
        font-family:"Caveat";
        src:url("assets/fonts/caveat-600.woff2") format("woff2");
        font-weight:600;
        font-style:normal;
        font-display:swap;
      }
      @font-face{
        font-family:"Press Start 2P";
        src:url("assets/fonts/press-start-2p-400.woff2") format("woff2");
        font-weight:400;
        font-style:normal;
        font-display:swap;
      }
      @font-face{
        font-family:"VT323";
        src:url("assets/fonts/vt323-400.woff2") format("woff2");
        font-weight:400;
        font-style:normal;
        font-display:swap;
      }
      /* Font piksel tebal untuk slide foto saja (pengganti sementara HAMJA):
         Handjet weight 900, self-hosted. Slide lain tidak memakai font ini. */
      @font-face{
        font-family:"Handjet";
        src:url("assets/fonts/handjet-900.woff2") format("woff2");
        font-weight:900;
        font-style:normal;
        font-display:swap;
      }
      /* Font piksel cute untuk judul "Notes" saja: Pixelify Sans 600,
         self-hosted. Hanya h2#notesTitle yang memakai font ini. */
      @font-face{
        font-family:"Pixelify Sans";
        src:url("assets/fonts/pixelify-sans-600.woff2") format("woff2");
        font-weight:600;
        font-style:normal;
        font-display:swap;
      }
      /* Font gaya logo Minecraft untuk judul slide 2 saja: Minecraft
         Ten, self-hosted (woff2, sangat kecil) supaya tampil sama
         persis saat deploy dan tidak bergantung CDN. Blok pikselnya
         tegas seperti logo Minecraft tapi bentuknya bersih dan
         membulat di sudut, jadi tetap lucu dan gampang dibaca.
         Hanya judul "Happy birthday" di slide 2 yang memakai ini;
         subjudul di bawahnya tetap Baloo 2. */
      @font-face{
        font-family:"Minecraft Ten";
        src:url("assets/fonts/minecraft-ten.woff2") format("woff2");
        font-weight:400;
        font-style:normal;
        font-display:swap;
      }
      :root{
        --ease-out:cubic-bezier(.22,1,.36,1);
        --ease-io:cubic-bezier(.45,0,.55,1);
        --ease-soft:cubic-bezier(.33,1,.68,1);
        --bg:#2b1a0c;
        --bg-2:#3a2410;
        --paper:#f7ecc9;
        --ink-dark:#2b1a12;
        --ink-light:#fdf3d0;
        --ink-soft:#e6cf9e;
        --accent:#ffcf5a;
        --accent-strong:#ffd970;
        --grass:#5fae2e;
        --grass-light:#7ec850;
        --fire:#ff7a2f;
        --fire-red:#d94f2b;
        --line:rgba(253,243,208,.22);
        --card:#3a2410;
        --px-outline:#1d1206;
        --wipe-center:#50351a;
        --wipe-mid:#3a2410;
        --wipe-edge:#2b1a0c;
        --wipe-glow:rgba(216,183,120,.17);
        --wipe-glow-soft:rgba(216,183,120,.07);
        --wipe-ray:rgba(226,203,160,.10);
        --wipe-spark:#cbb98e;
      }
      @media (prefers-color-scheme: light){
        :root{
          --bg:#d7e8b0;
          --bg-2:#f2e3bb;
          --paper:#fffdf4;
          --ink-light:#33220f;
          --ink-soft:#5d4423;
          --accent:#8a5a00;
          --accent-strong:#7a4d00;
          --line:rgba(51,34,15,.28);
          --card:#f7ecc9;
          --wipe-center:#e9f1c0;
          --wipe-mid:#dfeab2;
          --wipe-edge:#d7e8b0;
          --wipe-glow:rgba(176,148,84,.10);
          --wipe-glow-soft:rgba(176,148,84,.05);
          --wipe-ray:rgba(110,96,38,.07);
          --wipe-spark:#a08c4e;
        }
      }
      *{box-sizing:border-box}
      /* Gulir halus hanya untuk pengguna yang tidak meminta gerak dikurangi */
      @media (prefers-reduced-motion: no-preference){
        html{scroll-behavior:smooth}
      }
      body{
        margin:0;
        background:var(--bg);
        color:var(--ink-light);
        font-family:"VT323",ui-monospace,SFMono-Regular,Menlo,monospace;
        font-size:19px;
        line-height:1.55;
        overflow-x:hidden;
      }
      body::before{
        content:"";
        position:fixed; inset:0; pointer-events:none; z-index:0;
        background-image:repeating-conic-gradient(rgba(255,255,255,.045) 0% 25%, rgba(0,0,0,.05) 0% 50%);
        background-size:28px 28px;
        opacity:.38;
      }
      a{color:inherit}
      button{
        font:inherit; cursor:pointer; border:0;
        transition:transform .38s cubic-bezier(.22,1,.36,1), opacity .38s cubic-bezier(.22,1,.36,1);
      }
      button:active{transform:scale(.975); transition-duration:.12s}
      button:focus-visible, a:focus-visible{
        outline:3px solid var(--accent-strong);
        outline-offset:3px;
      }
      .wrap{
        position:relative; z-index:1;
        max-width:760px; margin:0 auto;
        padding:0 20px 64px;
      }

      /* ---------- Views (halaman) ---------- */
      .view{display:none}
      .view.active{display:block; animation:viewIn 1.35s cubic-bezier(.22,1,.36,1) both}
      @keyframes viewIn{
        from{opacity:0; transform:translateY(14px) scale(.995)}
        60%{opacity:1}
        to{opacity:1; transform:translateY(0) scale(1)}
      }

      /* ---------- Layar loading: dua lilin berwajah ---------- */
      .loading{
        position:fixed; inset:0; z-index:100;
        display:flex; flex-direction:column; align-items:center; justify-content:center;
        text-align:center; padding:24px;
        /* Latar slide 1 disamakan dengan slide 2: langit piksel biru
           lembut yang sama persis dengan .hub-bg / .page-bg, supaya
           dari adegan lilin sampai slide 2 terasa satu dunia */
        background:
          radial-gradient(circle at 85% 8%, rgba(255,238,176,.38) 0%, rgba(255,238,176,0) 24%),
          linear-gradient(180deg, #2e639b 0%, #4d8bc0 32%, #7fb2da 57%, #a9d2ee 71%, #cfeafb 73%, #6e4525 73.6%, #54381a 100%);
        transition:opacity .7s cubic-bezier(.22,1,.36,1);
      }
      @media (prefers-color-scheme: light){
        .loading{
          background:
            radial-gradient(circle at 85% 8%, rgba(255,238,176,.55) 0%, rgba(255,238,176,0) 24%),
            linear-gradient(180deg, #4aa7ec 0%, #7cc4f7 30%, #aedffb 56%, #d8f3fe 71%, #eaf9ff 73%, #a9763f 73.6%, #8a5f30 100%);
        }
      }
      .loading.done{opacity:0; pointer-events:none}
      .loading::before{
        content:"";
        position:absolute; inset:0; pointer-events:none; z-index:1;
        background-image:repeating-conic-gradient(rgba(255,255,255,.045) 0% 25%, rgba(0,0,0,.05) 0% 50%);
        background-size:28px 28px;
        opacity:.38;
      }
      /* ---------- Latar pemandangan slide 1: salinan suasana slide 2 ----------
         Lapisan pemandangan di dalam layar lilin memakai potongan yang
         persis sama dengan slide 2 dan halaman isi lainnya: langit piksel
         biru lembut di atas, bukit piksel berundak di kejauhan, lalu tanah
         piksel ala Minecraft ber-ore di bawah (bibir tanah di 73% tinggi
         layar dan punggung bukit di bottom 26,4%, angka yang sama dengan
         .hub-bg, .hub-land, dan .hub-hills di slide 2). Karena ukurannya
         sama persis, saat crossfade ke slide 2 tidak ada satu pun elemen
         latar yang melompat posisi: langit, bukit, tanah, dan ore-nya
         sudah berada di tempat yang sama sejak adegan lilin dimulai. */
      .loading-scene{
        position:absolute; inset:0; z-index:0; overflow:hidden; pointer-events:none;
      }
      /* Lapisan redup sangat halus khusus slide 1: pemandangan yang sama
         dengan slide 2 ditimpa selubung gelap tembus pandang tipis, cukup
         supaya kedua lilin dan apinya lebih menonjol sebagai bintang
         adegan, tapi langit, bukit, dan tanahnya tetap terlihat jelas dan
         tetap satu dunia dengan slide 2 (bukan kembali cokelat polos,
         tidak pula gelap). Selubung yang sama dipasang di salinan
         pemandangan tirai ledakan, jadi fase topi jerami ikut sedikit
         redup dengan suasana yang sama; topinya sendiri berada di lapisan
         di atas tirai dan tidak ikut diredupkan. Keduanya statis tanpa
         animasi, jadi tidak menambah beban repaint di HP. */
      .loading-scene::after, .blast-scene::after{
        content:""; position:absolute; inset:0; z-index:2; pointer-events:none;
        background:linear-gradient(180deg, rgba(11,17,36,.16) 0%, rgba(11,17,36,.11) 52%, rgba(11,17,36,.15) 100%);
      }
      @media (prefers-color-scheme: light){
        .loading-scene::after, .blast-scene::after{
          background:linear-gradient(180deg, rgba(11,17,36,.12) 0%, rgba(11,17,36,.08) 52%, rgba(11,17,36,.11) 100%);
        }
      }
      /* Awan dan bintang slide 1 melayang di atas pemandangannya, dan
         panggung lilin, bar, serta persentase di atas keduanya */
      .loading .sky-decor--loading{z-index:2}
      .loading .blow-stage{z-index:3}
      .loading .loading-bar, .loading .loading-pct{position:relative; z-index:3}
      /* Bar progres dan persentase tampil polos tanpa bingkai kaca.
         Persentasenya tetap cute: font Baloo 2 yang bulat dan lembut,
         warna krem, bergoyang halus pelan pelan. */
      .loading-pct{
        font-family:"Baloo 2","VT323",ui-rounded,system-ui,sans-serif;
        font-weight:700;
        letter-spacing:.05em;
        color:#fdf3d0;
        text-shadow:0 1px 3px rgba(26,15,5,.5);
      }
      /* Tirai ledakan menuju fase topi: isinya kini pemandangan yang sama
         juga, bukan lagi sapuan radial cokelat. Lingkaran tirai membesar
         menyingkap langit, bukit, dan tanah yang sama persis dengan
         slide 2, jadi fase topi jerami berdiri di dunia yang sama dan
         peleburannya ke slide 2 tidak mengganti suasana sama sekali. */
      .loading .blast-wipe .blast-wipe-inner{
        background-color:#2e639b;
        background-image:
          repeating-conic-gradient(rgba(255,255,255,.05) 0% 25%, rgba(0,0,0,.045) 0% 50%),
          radial-gradient(circle at 85% 8%, rgba(255,238,176,.38) 0%, rgba(255,238,176,0) 24%),
          linear-gradient(180deg, #2e639b 0%, #4d8bc0 32%, #7fb2da 57%, #a9d2ee 71%, #cfeafb 73%, #cfeafb 100%);
        background-size:28px 28px, cover, cover;
        background-repeat:repeat, no-repeat, no-repeat;
      }
      @media (prefers-color-scheme: light){
        .loading .blast-wipe .blast-wipe-inner{
          background-color:#4aa7ec;
          background-image:
            repeating-conic-gradient(rgba(255,255,255,.05) 0% 25%, rgba(0,0,0,.045) 0% 50%),
            radial-gradient(circle at 85% 8%, rgba(255,238,176,.55) 0%, rgba(255,238,176,0) 24%),
            linear-gradient(180deg, #4aa7ec 0%, #7cc4f7 30%, #aedffb 56%, #d8f3fe 71%, #eaf9ff 73%, #eaf9ff 100%);
          background-size:28px 28px, cover, cover;
          background-repeat:repeat, no-repeat, no-repeat;
        }
      }
      /* Lapisan pemandangan di dalam tirai: tanah dan bukitnya menempel
         di ukuran layar yang sebenarnya (bukan di kotak tirai yang
         diperbesar), jadi posisinya sejajar piksel dengan slide 2 */
      .blast-scene{
        position:absolute; inset:0; overflow:hidden; pointer-events:none;
      }
      /* ---------- Peringan transisi slide 1 ke fase topi (anti-lag HP) ----------
         Tiga aturan beban utama selama transisi menuju topi jerami:
         1) Salinan pemandangan di dalam tirai (.blast-scene) tampil
            STATIS: goyangan jelly ore, denyut kristal, kilau, dan halo
            di dalamnya dimatikan. Tirai hanya tampil sekitar 1,25 detik
            (membesar 1,5 dtk lalu melebur ke slide 2 yang ore-nya hidup),
            jadi ore diam di pita tanah bawah tidak terbaca bedanya,
            tetapi browser hemat puluhan repaint SVG per detik tepat di
            saat clip-path tirai sedang membesar.
         2) Begitu tirai menutup layar penuh (fase topi, .earth-center),
            lapisan adegan lilin di bawahnya (pemandangan slide 1 dan
            awan bintangnya) sudah tertutup rapat oleh tirai, jadi semua
            animasinya dijedakan: tidak ada gunanya me-repaint lapisan
            yang tidak terlihat sekejap pun.
         3) will-change hanya dipasang selama fase topi pada elemen yang
            benar-benar bergerak di fase itu (topi, cahayanya, bara api),
            bukan permanen sejak halaman dibuka; lapisannya lepas sendiri
            begitu layar loading disembunyikan sesudah transisi selesai. */
      .blast-scene .ore, .blast-scene .ore .oc-g, .blast-scene .ore .glint, .blast-scene .ore.rare::before{animation:none !important}
      .loading.earth-center .loading-scene .ore,
      .loading.earth-center .loading-scene .ore .oc-g,
      .loading.earth-center .loading-scene .ore .glint,
      .loading.earth-center .loading-scene .ore.rare::before,
      .loading.earth-center .sky-decor--loading .sd-cloud,
      .loading.earth-center .sky-decor--loading .sd-star{animation-play-state:paused}
      .loading.earth-center .flash-earth,
      .loading.earth-center .fe-hat-wrap,
      .loading.earth-center .fe-hat-img,
      .loading.earth-center .fe-hat-floor,
      .loading.earth-center .fe-glow,
      .loading.earth-center .fl-glow{will-change:transform, opacity}
      /* Sapuan warna solid lama di dalam tirai dimatikan: mencocokkan
         warna slide 2 kini dikerjakan lapisan pemandangan tirai di atas,
         yang memang gambarnya sama, bukan warna polos */
      .loading .blast-wipe .blast-wipe-inner::after{display:none}
      .loading.earth-center .blast-wipe-inner::after{animation:none}
      /* Semburan bintang di depan tirai: diganti emas hangat supaya
         menyatu dengan cahaya ledakan di atas langit biru, bukan cokelat */
      .loading .blast-wipe::before{background:#e8b93c}
      /* ---------- Animasi pengarah masuk ke slide 1: mengalir lanjut ke adegan lilin ---------- */
      .loading{animation:loadingIn .9s cubic-bezier(.22,1,.36,1) backwards}
      @keyframes loadingIn{from{opacity:0} to{opacity:1}}
      .loading-bar{animation:riseIn 1s .68s cubic-bezier(.22,1,.36,1) backwards}
      .loading-pct{animation:pctWiggle 2.6s cubic-bezier(.45,0,.55,1) 2s infinite alternate}
      @keyframes pctWiggle{
        from{transform:rotate(-1.3deg) translateY(0)}
        to{transform:rotate(1.3deg) translateY(-1.5px)}
      }
      @keyframes riseIn{
        from{opacity:0; transform:translateY(10px)}
        to{opacity:1; transform:translateY(0)}
      }
      .blow-stage{animation:stageIn 1.3s cubic-bezier(.22,1,.36,1) backwards}
      @keyframes stageIn{
        from{opacity:0; transform:scale(.965)}
        to{opacity:1; transform:scale(1)}
      }
      /* Lilin meluncur masuk dari dua sisi, lalu menyerahkan gerak ke goyangan halusnya.
         Masuknya sengaja tuntas sebelum ketukan pertama adegan tiup (1,18 dtk):
         selama animasi masuk masih jalan, transform dari animasi menimpa
         transform kelas condong/api menekuk, jadi tiupan pertama dan kedua
         dulu tertelan dan tidak terlihat sama sekali. */
      .load-candle.candle-a{animation:candleEnterL 1.05s cubic-bezier(.22,1,.36,1) backwards}
      .load-candle.candle-b{animation:candleEnterR 1.05s .12s cubic-bezier(.22,1,.36,1) backwards}
      @keyframes candleEnterL{
        0%{opacity:0; transform:translateX(-64px) rotate(-1.6deg)}
        55%{opacity:.8}
        100%{opacity:1; transform:translateX(0) rotate(0deg)}
      }
      @keyframes candleEnterR{
        0%{opacity:0; transform:translateX(64px) rotate(1.6deg)}
        55%{opacity:.8}
        100%{opacity:1; transform:translateX(0) rotate(0deg)}
      }
      /* Api menyala menyusul begitu lilinnya sampai: redup dan menyala perlahan.
         Sama seperti masuknya lilin, penyalaan ini juga tuntas sebelum api
         pertama kali ditekuk tiupan (1,36 dtk), supaya tekukannya terlihat. */
      .load-candle .lc-flame{animation:flameIgnite .7s .45s cubic-bezier(.22,1,.36,1) backwards}
      .load-candle.candle-b .lc-flame{animation-delay:.57s}
      @keyframes flameIgnite{
        0%{transform:scale(.35); opacity:.3}
        60%{transform:scale(.92,.97); opacity:.8}
        100%{transform:scale(1); opacity:.92}
      }
      /* Butiran piksel pengarah: berjatuhan mengerucut ke arah api lilin, lalu memudar */
      .guide-sparkles{position:absolute; inset:0; z-index:1; pointer-events:none}
      .guide-sparkles i{
        position:absolute; width:8px; height:8px;
        background:#c9a24a;
        box-shadow:2px 2px 0 rgba(0,0,0,.14);
        opacity:0;
        animation:guideFlow 2.3s cubic-bezier(.22,1,.36,1) backwards;
      }
      @keyframes guideFlow{
        0%{opacity:0; transform:translate(0,-10px) scale(.6) rotate(0deg)}
        35%{opacity:.5}
        70%{opacity:.42}
        100%{opacity:0; transform:translate(var(--dx,0px),32px) scale(.95) rotate(45deg)}
      }

      /* Panggung saling tiup dua lilin */
      .blow-stage{
        position:relative;
        width:min(360px,88vw); height:196px;
        margin-bottom:20px;
      }
      .load-candle{
        position:absolute; bottom:14px; z-index:2;
        display:flex; flex-direction:column; align-items:center;
        transition:transform .65s cubic-bezier(.22,1,.36,1), opacity .45s cubic-bezier(.22,1,.36,1);
      }
      .load-candle.candle-a{left:9%}
      .load-candle.candle-b{right:9%}
      .load-candle.lean-r{transform:rotate(3deg) translateX(3px)}
      .load-candle.lean-l{transform:rotate(-3deg) translateX(-3px)}
      /* Meledak itu sekejap, bukan meleleh: lilinnya membengkak sebentar lalu
         pecah hilang selaras kilat, partikel, dan guncangan panggung.
         Dulu kelas ini memakai transisi dasar .load-candle (.45-.65 dtk),
         jadi lilinnya menyusut dan memudar perlahan sesudah ledakannya lewat. */
      .load-candle.exploded{opacity:0; transform:scale(.45) rotate(10deg); animation:candlePop .3s cubic-bezier(.22,1,.36,1) forwards}
      @keyframes candlePop{
        0%{opacity:1; transform:scale(1) rotate(0deg)}
        30%{opacity:1; transform:scale(1.16) rotate(-3deg)}
        100%{opacity:0; transform:scale(.42) rotate(12deg)}
      }
      .load-candle.shocked{transform:translateX(-5px) rotate(-1.5deg)}
      .candle-bob{
        display:flex; flex-direction:column; align-items:center;
        animation:candleBounce 3.6s cubic-bezier(.45,0,.55,1) infinite;
        transform:translateZ(0);
      }
      .load-candle.candle-b .candle-bob{animation-delay:-1.4s}
      .load-candle.straining .candle-bob{animation:strainShake 1.15s cubic-bezier(.45,0,.55,1) infinite}
      .load-candle.panicking .candle-bob{animation:panicShake 1s cubic-bezier(.45,0,.55,1) infinite}
      @keyframes candleBounce{
        0%,100%{transform:translateY(0) rotate(0deg)}
        50%{transform:translateY(-4px) rotate(.4deg)}
      }
      @keyframes strainShake{
        0%,100%{transform:translate(0,0) rotate(0deg)}
        25%{transform:translate(.7px,-.5px) rotate(.4deg)}
        50%{transform:translate(-.7px,0) rotate(-.4deg)}
        75%{transform:translate(.5px,.4px) rotate(.2deg)}
      }
      @keyframes panicShake{
        0%,100%{transform:translate(0,0) rotate(0deg)}
        25%{transform:translate(1.1px,-.9px) rotate(.8deg)}
        50%{transform:translate(-1.1px,0) rotate(-.8deg)}
        75%{transform:translate(.8px,.7px) rotate(.5deg)}
      }
      .load-candle svg{display:block; image-rendering:pixelated}
      .load-candle .lc-flame{
        transform-box:fill-box; transform-origin:50% 100%;
        transition:transform .6s cubic-bezier(.22,1,.36,1), opacity .6s cubic-bezier(.22,1,.36,1);
        opacity:.92;
      }
      .load-candle .lc-flick{
        transform-box:fill-box; transform-origin:50% 100%;
        animation:flick 2.2s cubic-bezier(.45,0,.55,1) infinite;
      }
      .load-candle.candle-b .lc-flick{animation-delay:-1.15s}
      /* Kerlip api: goyangan kecil tidak beraturan dari pangkal api.
         Keyframes ini dulu hilang, jadi animasi flick di atas tidak pernah
         jalan dan api kedua lilin diam membeku selama seluruh adegan. */
      @keyframes flick{
        0%,100%{transform:scale(1,1) rotate(0deg)}
        28%{transform:scale(.955,1.055) rotate(-1.4deg)}
        52%{transform:scale(1.03,.975) rotate(1.1deg)}
        76%{transform:scale(.98,1.03) rotate(-.6deg)}
      }
      .load-candle.flame-bent-r .lc-flame{transform:rotate(24deg) scale(.84,1); opacity:.86}
      .load-candle.flame-bent-l .lc-flame{transform:rotate(-24deg) scale(.84,1); opacity:.86}
      .load-candle.flame-big .lc-flame{transform:scale(1.16,1.22); opacity:.94}
      .load-candle.flame-wild .lc-flame{animation:wildFlame 1.35s cubic-bezier(.45,0,.55,1) infinite}
      @keyframes wildFlame{
        0%,100%{transform:rotate(-3deg) scale(1.14,1.2)}
        33%{transform:rotate(2.5deg) scale(1.2,1.14)}
        66%{transform:rotate(-1.5deg) scale(1.17,1.22)}
      }
      /* Ekspresi wajah lilin: yang tampil diatur kelas expr-... */
      .load-candle .eyes-closed,.load-candle .eyes-wide,.load-candle .eyes-dizzy,
      .load-candle .mouth-blow,.load-candle .mouth-worried,.load-candle .mouth-open,.load-candle .mouth-flat{
        display:none;
      }
      .load-candle.expr-blow .eyes-normal{display:none}
      .load-candle.expr-blow .eyes-closed{display:block}
      .load-candle.expr-blow .mouth-smile{display:none}
      .load-candle.expr-blow .mouth-blow{display:block}
      .load-candle.expr-hit .eyes-normal{display:none}
      .load-candle.expr-hit .eyes-wide{display:block}
      .load-candle.expr-hit .mouth-smile{display:none}
      .load-candle.expr-hit .mouth-worried{display:block}
      .load-candle.expr-annoyed .mouth-smile{display:none}
      .load-candle.expr-annoyed .mouth-flat{display:block}
      .load-candle.expr-annoyed .brow-l{transform:rotate(20deg)}
      .load-candle.expr-annoyed .brow-r{transform:rotate(-20deg)}
      .load-candle.expr-surprised .eyes-normal{display:none}
      .load-candle.expr-surprised .eyes-wide{display:block}
      .load-candle.expr-surprised .mouth-smile{display:none}
      .load-candle.expr-surprised .mouth-open{display:block}
      .load-candle.expr-panic .eyes-normal{display:none}
      .load-candle.expr-panic .eyes-dizzy{display:block}
      .load-candle.expr-panic .mouth-smile{display:none}
      .load-candle.expr-panic .mouth-open{display:block}
      .load-candle .brow{transform-box:fill-box; transform-origin:center; transition:transform .55s cubic-bezier(.22,1,.36,1)}
      .load-candle .blush{transform-box:fill-box; transform-origin:center; transition:transform .55s cubic-bezier(.22,1,.36,1)}
      .load-candle.expr-blow .blush{transform:scale(1.35)}
      .load-shadow{
        width:46px; height:10px; margin-top:10px; border-radius:50%;
        background:radial-gradient(ellipse at center, rgba(0,0,0,.4), rgba(0,0,0,0) 68%);
      }
      /* Garis kayu lurus panjang di bawah KEDUA lilin sekaligus (seperti
         gambar referensi): satu papan tipis membentang lurus dari kiri ke
         kanan panggung, menjulur melewati kedua lilin, dan kedua lilin
         berdiri menempel di atas garis itu. Garis ini anak langsung
         .blow-stage, jadi otomatis ikut masuk bersama panggung (stageIn),
         ikut guncangan ledakan (boom-shake) dengan easing yang sama, dan
         ikut memudar saat transisi ke slide 2, selalu lurus tanpa animasi
         terpisah yang bisa patah. Posisinya dihitung dari geometri panggung
         360x196: dasar badan lilin A ada di y sekitar 155 dan lilin B di
         y sekitar 156, jadi tepi atas garis ditaruh di y 151 (bottom:35px,
         tinggi 10px) supaya kedua lilin menempel tumpang sedikit di
         atasnya dan tetap menyatu walau lilinnya bergoyang naik turun.
         Masuknya digambar halus: garis memanjang lembut dari tengah
         sambil memudar masuk, dengan easing mengalir yang sama. */
      .stage-plank{
        position:absolute; left:5%; right:5%; bottom:35px; z-index:1;
        height:10px; pointer-events:none;
        transform-origin:50% 50%;
        animation:plankIn 1.7s .25s cubic-bezier(.22,1,.36,1) backwards;
      }
      .stage-plank svg{
        display:block; width:100%; height:100%; image-rendering:pixelated;
        filter:drop-shadow(0 3px 2px rgba(0,0,0,.16));
      }
      @keyframes plankIn{
        from{opacity:0; transform:scaleX(.55)}
        to{opacity:1; transform:scaleX(1)}
      }
      /* Hembusan angin antar lilin */
      .gust{
        position:absolute; top:52px; width:76px; height:34px; z-index:3;
        pointer-events:none;
      }
      .gust-ab{left:34%}
      .gust-ba{right:34%}
      .gust i{
        position:absolute; left:0; width:11px; height:11px;
        background:#d7e6ef; opacity:0;
        box-shadow:2px 2px 0 rgba(0,0,0,.10);
      }
      .gust i:nth-child(1){top:2px}
      .gust i:nth-child(2){top:12px; width:14px; height:14px}
      .gust i:nth-child(3){top:23px}
      .gust i:nth-child(4){top:7px; width:8px; height:8px}
      .gust-ab.on i{animation:gustRight 1.25s cubic-bezier(.22,1,.36,1) forwards}
      .gust-ba.on i{animation:gustLeft 1.25s cubic-bezier(.22,1,.36,1) forwards}
      .gust i:nth-child(2){animation-delay:.07s !important}
      .gust i:nth-child(3){animation-delay:.14s !important}
      .gust i:nth-child(4){animation-delay:.2s !important}
      /* Hembusannya kini menjangkau sampai dekat api lilin seberang (dulu
         berhenti sekitar 100px sebelum apinya, jadi tiupan terasa tidak
         pernah sampai padahal lilin seberang sudah bereaksi). */
      @keyframes gustRight{
        0%{transform:translateX(-6px) scale(.7); opacity:0}
        30%{opacity:.55}
        65%{opacity:.48}
        100%{transform:translateX(104px) scale(1.05); opacity:0}
      }
      @keyframes gustLeft{
        0%{transform:translateX(6px) scale(.7); opacity:0}
        30%{opacity:.55}
        65%{opacity:.48}
        100%{transform:translateX(-104px) scale(1.05); opacity:0}
      }
      .clash{
        position:absolute; left:50%; top:44px; z-index:3;
        width:30px; height:30px; margin-left:-15px;
        opacity:0; pointer-events:none;
      }
      .clash.on{animation:clashPop 1.25s cubic-bezier(.22,1,.36,1) forwards}
      @keyframes clashPop{
        0%{opacity:0; transform:scale(.6) rotate(0deg)}
        35%{opacity:.65}
        70%{opacity:.45}
        100%{opacity:0; transform:scale(1.25) rotate(22deg)}
      }
      /* Ledakan: partikel beterbangan, gelombang kejut, asap, dan tirai ledakan */
      .particle-layer{position:absolute; inset:0; z-index:6; pointer-events:none}
      .px-particle{position:absolute; width:9px; height:9px; left:0; top:0}
      .boom-flash{
        position:absolute; inset:-14px; z-index:7; pointer-events:none; opacity:0;
        background:radial-gradient(circle at 68% 45%, rgba(219,203,168,.42) 0%, rgba(168,141,99,.28) 34%, rgba(125,95,61,.13) 58%, rgba(125,95,61,0) 72%);
      }
      .boom-flash.on{animation:flashPop 2.2s cubic-bezier(.22,1,.36,1) forwards}
      /* Kilat ledakan menyambar cepat di depan, lalu sisa cahayanya memudar:
         puncak terang dulu baru tercapai di 32% (sekitar 0,7 dtk) sehingga
         kilatnya terasa datang terlambat dan lemah dibanding ledakannya. */
      @keyframes flashPop{
        0%{opacity:0; transform:scale(.82)}
        9%{opacity:.55}
        34%{opacity:.18}
        100%{opacity:0; transform:scale(1.05)}
      }
      .boom-word{
        position:absolute; left:56%; top:16%; z-index:8; pointer-events:none;
        font-family:"Press Start 2P",monospace; font-size:21px; letter-spacing:.06em;
        color:#d3ba84;
        text-shadow:3px 3px 0 rgba(0,0,0,.35);
        opacity:0; transform:scale(.3) rotate(-9deg);
      }
      .boom-word.on{animation:boomPop 2.1s cubic-bezier(.22,1,.36,1) forwards}
      @keyframes boomPop{
        0%{opacity:0; transform:scale(.6) rotate(-6deg)}
        30%{opacity:.82; transform:scale(1.02) rotate(-6deg)}
        58%{opacity:.78; transform:scale(1) rotate(-6deg)}
        100%{opacity:0; transform:scale(1.01) rotate(-6deg) translateY(-5px)}
      }
      .blow-stage.boom-shake{animation:stageShake 1.7s cubic-bezier(.22,1,.36,1)}
      @keyframes stageShake{
        0%,100%{transform:translate(0,0) rotate(0deg)}
        18%{transform:translate(-2.2px,.8px) rotate(-.35deg)}
        38%{transform:translate(2.2px,-.8px) rotate(.35deg)}
        58%{transform:translate(-1.3px,-.5px)}
        78%{transform:translate(1px,.4px)}
      }
      /* Gelombang kejut dan asap dari titik ledakan lilin */
      .shockwave{
        position:absolute; left:78%; top:56px; z-index:5;
        width:34px; height:34px; margin:-17px 0 0 -17px;
        border:4px solid #b39a6d;
        box-shadow:0 0 0 3px rgba(138,109,75,.22), inset 0 0 0 3px rgba(226,211,175,.32);
        opacity:0; pointer-events:none;
      }
      .shockwave.on{animation:shockExpand 1.9s cubic-bezier(.22,1,.36,1) forwards}
      @keyframes shockExpand{
        0%{opacity:.5; transform:scale(.55) rotate(0deg)}
        55%{opacity:.3}
        100%{opacity:0; transform:scale(5.6) rotate(5deg)}
      }
      .blast-smoke{position:absolute; inset:0; z-index:9; pointer-events:none}
      .blast-smoke i{
        position:absolute; left:76%; top:48px; width:16px; height:16px;
        background:#cfc4ae; opacity:0;
        box-shadow:3px 3px 0 rgba(0,0,0,.15);
      }
      .blast-smoke i:nth-child(odd){background:#a89a80}
      .blast-smoke.on i{animation:smokeRise 3.1s cubic-bezier(.22,1,.36,1) forwards}
      @keyframes smokeRise{
        0%{opacity:0; transform:translate(0,0) scale(.65)}
        22%{opacity:.42}
        65%{opacity:.26}
        100%{opacity:0; transform:translate(var(--sx,0px),var(--sy,-40px)) scale(1.9)}
      }
      /* Tirai ledakan: membesar dari titik ledakan menutup slide 1, lalu menyusut menyingkap slide 2 */
      .blast-wipe{
        --wipe-x:108px;
        position:absolute; inset:0; z-index:30; pointer-events:none;
        opacity:0; overflow:hidden;
        clip-path:circle(0% at calc(50% + var(--wipe-x)) 44%);
      }
      .blast-wipe-inner{
        position:absolute; inset:0;
        background-image:
          repeating-conic-gradient(rgba(255,255,255,.045) 0% 25%, rgba(0,0,0,.05) 0% 50%),
          radial-gradient(circle at calc(50% + var(--wipe-x)) 44%, var(--wipe-center) 0%, var(--wipe-mid) 38%, var(--wipe-edge) 78%, var(--wipe-edge) 100%);
        background-size:28px 28px, cover;
      }
      .blast-wipe-inner::after{
        content:""; position:absolute; inset:0;
        background-color:var(--bg);
        background-image:repeating-conic-gradient(rgba(255,255,255,.045) 0% 25%, rgba(0,0,0,.05) 0% 50%);
        background-size:28px 28px;
        opacity:0;
      }
      .loading.earth-center .blast-wipe-inner::after{animation:wipeMatchSlide2 1.6s cubic-bezier(.45,0,.55,1) .15s forwards}
      @keyframes wipeMatchSlide2{
        from{opacity:0}
        to{opacity:1}
      }
      .blast-wipe::before{
        content:""; position:absolute; z-index:1;
        left:calc(50% + var(--wipe-x)); top:44%;
        width:320px; height:320px; margin:-160px 0 0 -160px;
        background:var(--wipe-mid); opacity:0;
        clip-path:polygon(50% 0%, 58% 32%, 84% 12%, 68% 40%, 100% 38%, 74% 54%, 96% 72%, 64% 66%, 76% 96%, 54% 72%, 42% 100%, 40% 68%, 10% 88%, 28% 58%, 0% 52%, 30% 42%, 12% 16%, 40% 30%);
      }
      /* Pemanasan tirai: sejak ledakan meletus (3,38 dtk, kelas .pre dari
         JS) tirai sudah dirender sebagai titik mungil di titik ledakan
         dan lapisannya sudah dipromosikan, jadi saat gilirannya membesar
         di 4,25 dtk tidak ada lapisan atau gambar yang baru dimuat dan
         diraster di tengah jalan. Titiknya tenggelam di tengah kilat,
         partikel, dan gelombang kejut ledakan, tidak terbaca sama sekali */
      .blast-wipe.pre{opacity:1; clip-path:circle(1% at calc(50% + var(--wipe-x)) 44%); will-change:clip-path, opacity}
      .blast-wipe.on{opacity:1; animation:blastCover 1.5s cubic-bezier(.45,0,.55,1) forwards; will-change:clip-path, opacity}
      .blast-wipe.on::before{animation:burstFront 2s cubic-bezier(.22,1,.36,1) forwards}
      @keyframes blastCover{
        0%{clip-path:circle(0% at calc(50% + var(--wipe-x)) 44%)}
        55%{clip-path:circle(92% at calc(50% + var(--wipe-x)) 44%)}
        100%{clip-path:circle(145% at calc(50% + var(--wipe-x)) 44%)}
      }
      @keyframes burstFront{
        0%{opacity:.16; transform:scale(.25) rotate(0deg)}
        55%{opacity:.11}
        100%{opacity:0; transform:scale(2) rotate(13deg)}
      }
      /* Saat menyingkap: isi slide 1 memudar lebih dulu supaya tidak berkedip balik,
         lalu tirai menyusut dan seluruh layar ikut memudar menyerahkan ke slide 2 */
      .loading.blast-reveal{pointer-events:none; animation:loadingLift 1.7s cubic-bezier(.45,0,.55,1) forwards}
      .loading.blast-reveal .blow-stage,
      .loading.blast-reveal .loading-bar,
      .loading.blast-reveal .loading-pct{opacity:0; transition:opacity .45s cubic-bezier(.22,1,.36,1)}
      /* Topi dan dekor seusai ledakan ikut memudar di awal reveal: mereka sudah
         tampil di depan, tidak muncul lagi di ujung transisi */
      .loading.blast-reveal .boom-decor{opacity:0; transition:opacity 1.1s cubic-bezier(.45,0,.55,1)}
      .loading.blast-reveal .flash-earth{animation:none; opacity:0; transition:opacity 1.1s cubic-bezier(.45,0,.55,1)}
      @keyframes loadingLift{
        0%{opacity:1}
        34%{opacity:1}
        100%{opacity:0}
      }
      /* Topi di tengah layar cahaya: BUKAN muncul pas lilinnya meledak,
         tapi pas tirai cahaya ledakan sudah menutup seluruh slide 1.
         Di layar penuh cahaya itu Topi piksel duduk tepat di tengah,
         bergoyang sebentar ditemani sinar, awan, dan bintang, lalu tirai
         menyusut menyerahkan ke slide 2 */
      .flash-earth{
        position:absolute; left:50%; top:47%; z-index:36;
        transform:translate(-50%,-50%);
        display:flex; flex-direction:column; align-items:center;
        opacity:0; pointer-events:none;
      }
      .loading.earth-center .flash-earth{animation:earthCenterIn .7s cubic-bezier(.22,1,.36,1) forwards}
      @keyframes earthCenterIn{
        0%{opacity:0; transform:translate(-50%,-47.5%) scale(.68)}
        60%{opacity:1; transform:translate(-50%,-50%) scale(1.006)}
        100%{opacity:1; transform:translate(-50%,-50%) scale(1)}
      }
      /* Api cahaya ledakan memenuhi seluruh layar di fase topi tengah:
         cahaya hangat berdenyut halus plus bara piksel yang naik menyebar */
      .fire-light{position:absolute; inset:0; z-index:31; pointer-events:none; opacity:0; overflow:hidden}
      .loading.earth-center .fire-light{animation:fireLightIn 1.1s cubic-bezier(.22,1,.36,1) forwards}
      @keyframes fireLightIn{from{opacity:0} to{opacity:1}}
      .fl-glow{
        position:absolute; inset:0;
        background:
          radial-gradient(circle at 50% 47%, var(--wipe-glow) 0%, var(--wipe-glow-soft) 34%, rgba(0,0,0,0) 62%),
          radial-gradient(circle at 16% 84%, var(--wipe-glow-soft) 0%, rgba(0,0,0,0) 40%),
          radial-gradient(circle at 85% 16%, var(--wipe-glow-soft) 0%, rgba(0,0,0,0) 38%);
      }
      /* Denyut cahaya dan bara naiknya hanya beranimasi selama fase topi
         (.earth-center): sebelumnya kedua animasi ini jalan terus sejak
         halaman dibuka walau lapisannya belum terlihat sama sekali,
         membuang repaint layar penuh dan 16 elemen bergerak percuma
         selama seluruh adegan lilin */
      .loading.earth-center .fl-glow{animation:flGlowPulse 4.2s cubic-bezier(.45,0,.55,1) infinite alternate}
      @keyframes flGlowPulse{
        from{opacity:.82; transform:scale(1)}
        to{opacity:1; transform:scale(1.05)}
      }
      .fire-light i{
        position:absolute; bottom:-22px; display:block; opacity:0;
        box-shadow:2px 2px 0 rgba(0,0,0,.12);
        filter:saturate(.5) brightness(.97);
      }
      .loading.earth-center .fire-light i{animation:flameRise linear infinite}
      @keyframes flameRise{
        0%{opacity:0; transform:translate(0,0) rotate(0deg)}
        10%{opacity:.3}
        55%{opacity:.19}
        100%{opacity:0; transform:translate(var(--drift,0px),-106vh) rotate(190deg)}
      }
      /* Sinar berputar pelan di belakang topi, sisa cahaya ledakan yang memudar halus */
      .fe-rays{
        position:absolute; left:50%; top:43%; width:400px; height:400px;
        transform:translate(-50%,-50%);
        background:repeating-conic-gradient(from 0deg, var(--wipe-ray) 0deg 9deg, rgba(0,0,0,0) 9deg 24deg);
        -webkit-mask-image:radial-gradient(circle, #000 0%, rgba(0,0,0,.55) 42%, transparent 68%);
        mask-image:radial-gradient(circle, #000 0%, rgba(0,0,0,.55) 42%, transparent 68%);
        opacity:0;
      }
      .loading.earth-center .fe-rays{animation:raysIn 1.1s cubic-bezier(.22,1,.36,1) forwards, raysTurn 64s linear 1.1s infinite}
      @keyframes raysIn{
        0%{opacity:0; transform:translate(-50%,-50%) rotate(-8deg) scale(.7)}
        100%{opacity:.55; transform:translate(-50%,-50%) rotate(0deg) scale(1)}
      }
      @keyframes raysTurn{
        from{transform:translate(-50%,-50%) rotate(0deg) scale(1)}
        to{transform:translate(-50%,-50%) rotate(360deg) scale(1)}
      }
      /* Dekor pengisi seusai ledakan: awan melayang masuk, bintang berkelip,
         dua kucing piksel mengintip dari bawah, semua muncul menyusul topi */
      .boom-decor{position:absolute; inset:0; z-index:34; pointer-events:none; overflow:hidden}
      .boom-decor img{position:absolute; image-rendering:pixelated; display:block; opacity:0}
      .bd-cloud{filter:drop-shadow(3px 4px 0 rgba(0,0,0,.14))}
      .bd-c1{top:9%; left:3%; width:118px}
      .bd-c2{top:16%; right:4%; width:84px}
      .bd-c3{bottom:19%; left:6%; width:66px}
      .bd-star{filter:drop-shadow(2px 3px 0 rgba(0,0,0,.14))}
      .bd-s1{top:6%; left:44%; width:34px}
      .bd-s2{top:27%; left:12%; width:26px}
      .bd-s3{top:33%; right:11%; width:30px}
      .bd-s4{bottom:27%; right:17%; width:22px}
      .bd-cat{filter:drop-shadow(4px 5px 0 rgba(0,0,0,.18))}
      .bd-cat-blue{bottom:2.5%; left:2%; width:118px}
      .bd-cat-orange{bottom:3.5%; right:3%; width:82px}
      .loading.earth-center .bd-c1{animation:bdCloudIn 2.2s .05s cubic-bezier(.22,1,.36,1) forwards}
      .loading.earth-center .bd-c2{animation:bdCloudIn 2.2s .18s cubic-bezier(.22,1,.36,1) forwards}
      .loading.earth-center .bd-c3{animation:bdCloudIn 2.2s .3s cubic-bezier(.22,1,.36,1) forwards}
      @keyframes bdCloudIn{
        0%{opacity:0; transform:translate(-22px,10px) scale(.94)}
        22%{opacity:.92; transform:translate(0,0) scale(1)}
        58%{opacity:.92; transform:translate(12px,-5px) scale(1)}
        100%{opacity:.92; transform:translate(26px,-9px) scale(1)}
      }
      .loading.earth-center .bd-s1{animation:bdStarIn 2.2s .1s cubic-bezier(.22,1,.36,1) forwards}
      .loading.earth-center .bd-s2{animation:bdStarIn 2.2s .24s cubic-bezier(.22,1,.36,1) forwards}
      .loading.earth-center .bd-s3{animation:bdStarIn 2.2s .36s cubic-bezier(.22,1,.36,1) forwards}
      .loading.earth-center .bd-s4{animation:bdStarIn 2.2s .48s cubic-bezier(.22,1,.36,1) forwards}
      @keyframes bdStarIn{
        0%{opacity:0; transform:scale(.45) rotate(-12deg)}
        14%{opacity:.85; transform:scale(1.03) rotate(2deg)}
        32%{opacity:.58; transform:scale(.93) rotate(-2deg)}
        50%{opacity:.85; transform:scale(1.01) rotate(2deg)}
        68%{opacity:.6; transform:scale(.95) rotate(-1deg)}
        84%{opacity:.82; transform:scale(1) rotate(1deg)}
        100%{opacity:.7; transform:scale(.97) rotate(0deg)}
      }
      .loading.earth-center .bd-cat-blue{animation:bdCatIn 2s .2s cubic-bezier(.22,1,.36,1) forwards}
      .loading.earth-center .bd-cat-orange{animation:bdCatIn 2s .34s cubic-bezier(.22,1,.36,1) forwards}
      @keyframes bdCatIn{
        0%{opacity:0; transform:translateY(46%) scale(.95)}
        22%{opacity:1; transform:translateY(0) scale(1)}
        52%{opacity:1; transform:translateY(-3%) scale(1)}
        78%{opacity:1; transform:translateY(-1%) scale(1)}
        100%{opacity:1; transform:translateY(-2%) scale(1)}
      }
      @media (max-width:480px){
        .bd-c1{width:92px}
        .bd-c2{width:66px}
        .bd-cat-blue{width:92px}
        .bd-cat-orange{width:64px}
      }
      .fe-glow{
        position:absolute; left:50%; top:43%; width:360px; height:360px;
        transform:translate(-50%,-50%);
        background:radial-gradient(circle, var(--wipe-glow) 0%, var(--wipe-glow-soft) 46%, rgba(0,0,0,0) 70%);
      }
      .loading.earth-center .fe-glow{animation:feGlowPulse 5.6s cubic-bezier(.45,0,.55,1) infinite alternate}
      @keyframes feGlowPulse{
        from{opacity:.85; transform:translate(-50%,-50%) scale(1)}
        to{opacity:1; transform:translate(-50%,-50%) scale(1.06)}
      }
      .fe-hat-wrap{
        position:relative; width:272px; height:196px;
        display:flex; align-items:center; justify-content:center;
      }
      /* Melayang lembut: naik turun pelan menemani goyangan topinya */
      .loading.earth-center .fe-hat-wrap{animation:hatBob 2.4s cubic-bezier(.37,0,.63,1) infinite}
      @keyframes hatBob{
        0%,100%{transform:translateY(0)}
        50%{transform:translateY(-6px)}
      }
      /* Bayangan lantai di bawah topi: berdenyut lembut seirama ayunan
         melayangnya (2,4 detik, sama dengan hatBob) */
      .fe-hat-floor{
        position:absolute; left:50%; bottom:-26px; z-index:-1;
        width:196px; height:22px; border-radius:50%;
        transform:translateX(-50%);
        background:radial-gradient(ellipse at center, rgba(30,16,4,.42) 0%, rgba(30,16,4,.18) 55%, rgba(30,16,4,0) 75%);
        opacity:.5;
      }
      .loading.earth-center .fe-hat-floor{animation:hatFloor 2.4s cubic-bezier(.37,0,.63,1) infinite alternate}
      @keyframes hatFloor{
        from{transform:translateX(-50%) scaleX(1); opacity:.5}
        to{transform:translateX(-50%) scaleX(.93); opacity:.42}
      }
      /* Topi jerami seperti semula: satu gambar piksel datar saja, tanpa
         model 3D dan tanpa tumpukan lapisan. Topinya cukup bergoyang
         miring halus bolak balik sambil melayang, persis gaya awalnya,
         dan fase tampilnya kini sebentar saja sebelum lanjut ke slide 2. */
      .fe-hat-img{
        position:relative; width:252px; height:auto; flex:0 0 auto;
        object-fit:contain; image-rendering:pixelated; display:block;
        filter:drop-shadow(5px 6px 0 rgba(0,0,0,.18));
      }
      .loading.earth-center .fe-hat-img{animation:hatSway 2.4s cubic-bezier(.37,0,.63,1) infinite}
      @keyframes hatSway{
        0%,100%{transform:rotate(-2.4deg) translateY(0)}
        50%{transform:rotate(2.4deg) translateY(-4px)}
      }
      @media (max-width:480px){
        .fe-hat-wrap{width:240px; height:172px}
        .fe-hat-img{width:218px}
        .fe-hat-floor{width:168px}
      }
      .fe-sparkle{
        position:absolute; width:8px; height:8px; background:var(--wipe-spark);
        box-shadow:2px 2px 0 rgba(0,0,0,.14); opacity:0;
      }
      .loading.earth-center .fe-sparkle{animation:feSpark 2.4s .15s cubic-bezier(.37,0,.63,1) infinite alternate}
      .loading.earth-center .fe-s2{animation-delay:-1.2s}
      .loading.earth-center .fe-s3{animation-delay:-2.1s}
      .loading.earth-center .fe-s4{animation-delay:-.7s}
      @keyframes feSpark{
        0%{opacity:.12; transform:scale(.55) rotate(0deg) translateY(0)}
        55%{opacity:.55}
        100%{opacity:.28; transform:scale(1.05) rotate(45deg) translateY(-10px)}
      }
      .fe-s1{left:2px; top:18px}
      .fe-s2{right:4px; top:6px; width:10px; height:10px}
      .fe-s3{right:0; top:118px}
      .fe-s4{left:-2px; top:148px; width:6px; height:6px}
      /* Fase topi tengah memudar halus di awal reveal, menyerahkan ke slide 2:
         tirai tidak lagi menyusut jadi lingkaran kecil, karena tepi lingkarannya
         yang beda warna itulah yang bikin lompatan terasa kasar. Sekarang tirai
         yang warnanya sudah persis background slide 2 melebur lewat crossfade
         panjang bersama seluruh layar, tanpa tepi yang menyapu layar. */
      .loading.blast-reveal .fire-light,
      .loading.blast-reveal .flash-earth{animation:none; opacity:0; transition:opacity 1.1s cubic-bezier(.45,0,.55,1)}
      .loading.blast-reveal .blast-wipe{opacity:1; animation:blastClear 1.7s cubic-bezier(.45,0,.55,1) forwards}
      @keyframes blastClear{
        0%{clip-path:circle(145% at calc(50% + var(--wipe-x)) 44%); opacity:1}
        38%{clip-path:circle(145% at calc(50% + var(--wipe-x)) 44%); opacity:1}
        100%{clip-path:circle(145% at calc(50% + var(--wipe-x)) 44%); opacity:0}
      }
      /* Bara piksel berhamburan menyambut slide 2 */
      .ember-layer{position:fixed; inset:0; z-index:125; pointer-events:none; overflow:hidden}
      .ember-layer i{
        position:absolute; top:-18px; opacity:0;
        box-shadow:2px 2px 0 rgba(0,0,0,.14);
        animation:emberFall 2.6s cubic-bezier(.22,1,.36,1) forwards;
      }
      @keyframes emberFall{
        0%{opacity:0; transform:translate(0,0) rotate(0deg)}
        14%{opacity:.34}
        72%{opacity:.2}
        100%{opacity:0; transform:translate(var(--drift,0px),106vh) rotate(160deg)}
      }
      /* Slide 2 masuk menyusul tirai ledakan: mengalir halus tanpa hentakan,
         tumpang tindih lama dengan crossfade tirai supaya tidak ada jeda kosong */
      .view.active.blast-enter{animation:blastViewIn 1.7s cubic-bezier(.45,0,.55,1) both}
      @keyframes blastViewIn{
        0%{opacity:0; transform:scale(.992) translateY(8px)}
        42%{opacity:.85}
        100%{opacity:1; transform:scale(1) translateX(0) translateY(0)}
      }
      .view.blast-enter .hub-btn{animation:hubPop 1.9s cubic-bezier(.22,1,.36,1) backwards}
      .view.blast-enter .hub-btn:nth-child(2){animation-delay:.18s}
      .view.blast-enter .hub-btn:nth-child(3){animation-delay:.36s}
      .view.blast-enter .hub-btn:nth-child(4){animation-delay:.54s}
      @keyframes hubPop{
        from{opacity:0; transform:scale(.965) translateY(7px)}
        to{opacity:1; transform:scale(1) translateY(0)}
      }
      .loading-bar{
        width:min(240px,64vw); height:16px; position:relative;
        border:3px solid var(--px-outline);
        background:rgba(0,0,0,.28);
        box-shadow:inset 0 3px 0 rgba(0,0,0,.3), 4px 4px 0 rgba(0,0,0,.2);
      }
      @media (prefers-color-scheme: light){
        .loading-bar{background:rgba(51,34,15,.14)}
      }
      .loading-bar i{
        display:block; height:100%; width:100%;
        transform:scaleX(0); transform-origin:0 50%;
        background:repeating-linear-gradient(90deg, var(--grass) 0 14px, var(--grass-light) 14px 28px);
        transition:transform .55s cubic-bezier(.22,1,.36,1);
      }
      .loading-pct{
        margin:10px 0 0; font-size:21px; position:relative;
        min-height:28px; line-height:1.3;
      }

      /* ---------- Isi halaman surat dan harapan ---------- */
      .hero{
        text-align:center;
        padding:clamp(30px,6vw,48px) 0 8px;
      }
      .hero h2{
        font-family:"Press Start 2P",monospace;
        font-size:clamp(20px,5.6vw,34px);
        line-height:1.5; letter-spacing:0; margin:0 0 12px; font-weight:400;
        text-shadow:3px 3px 0 rgba(0,0,0,.35);
      }
      .hero h2 span{color:var(--grass-light)}
      @media (prefers-color-scheme: light){
        .hero h2{text-shadow:3px 3px 0 rgba(51,34,15,.18)}
        .hero h2 span{color:#3e7d14}
      }
      .hero p{color:var(--ink-soft); max-width:52ch; margin:0 auto; font-size:21px}

      /* Cake: kue piksel pink kiriman Kevin (assets/img-1374525bfd.png), latar
         transparan, warna diredupkan menyatu dengan background halaman.
         Tiga apinya gambar terpisah (assets/cake-flame-*.png) yang digoyang
         halus dua lapis (ayunan + kedip lembut) supaya terlihat hidup. */
      .cake-zone{
        position:relative;
        margin:36px auto 8px; max-width:420px; text-align:center;
        padding-top:46px;
      }
      .cake{
        position:relative; width:min(320px,86vw); margin:0 auto;
        aspect-ratio:733/775;
      }
      /* Cahaya hangat lembut di belakang kue + bayangan lantai di bawahnya:
         keduanya menyatu dengan background, bukan tepi gambar yang keras */
      .cake-halo{
        position:absolute; left:50%; top:44%; z-index:0;
        width:118%; aspect-ratio:1; transform:translate(-50%,-50%);
        background:radial-gradient(circle, rgba(255,196,96,.14) 0%, rgba(255,196,96,.05) 46%, rgba(255,196,96,0) 70%);
        pointer-events:none;
        transition:opacity .8s cubic-bezier(.22,1,.36,1);
      }
      .cake.all-out .cake-halo{opacity:.28}
      .cake-shadow{
        position:absolute; left:50%; bottom:-4.5%; z-index:0;
        width:88%; height:7.5%; transform:translateX(-50%);
        border-radius:50%;
        background:radial-gradient(ellipse at center, rgba(12,6,2,.5) 0%, rgba(12,6,2,.22) 55%, rgba(12,6,2,0) 75%);
        pointer-events:none;
        animation:cakeShadowPulse 5.2s cubic-bezier(.45,0,.55,1) infinite alternate;
        will-change:transform, opacity;
      }
      @media (prefers-color-scheme: light){
        .cake-halo{background:radial-gradient(circle, rgba(255,178,84,.20) 0%, rgba(255,178,84,.07) 46%, rgba(255,178,84,0) 70%)}
        .cake-shadow{background:radial-gradient(ellipse at center, rgba(84,56,18,.34) 0%, rgba(84,56,18,.15) 55%, rgba(84,56,18,0) 75%)}
      }
      @keyframes cakeShadowPulse{
        from{transform:translateX(-50%) scaleX(1); opacity:1}
        to{transform:translateX(-50%) scaleX(.955); opacity:.86}
      }
      .cake-float{
        position:absolute; inset:0; z-index:1;
        animation:cakeFloat 5.2s cubic-bezier(.45,0,.55,1) infinite alternate;
        will-change:transform;
      }
      @keyframes cakeFloat{
        from{transform:translateY(2px)}
        to{transform:translateY(-3px)}
      }
      .cake-base{
        position:absolute; inset:0; width:100%; height:100%;
        object-fit:contain; image-rendering:pixelated; display:block;
        filter:drop-shadow(3px 5px 0 rgba(0,0,0,.14));
        transition:filter .7s cubic-bezier(.22,1,.36,1);
      }
      .cake.all-out .cake-base{filter:drop-shadow(3px 5px 0 rgba(0,0,0,.14)) brightness(.93) saturate(.9)}
      /* Api lilin: posisi persen mengikuti gambar aslinya (tiga lilin) */
      #candlesVisual .cake-flame{
        position:absolute; z-index:2; pointer-events:none;
        transition:opacity .55s cubic-bezier(.22,1,.36,1);
      }
      .cf-1{left:22.37%; top:2.06%; width:13.92%; height:12.9%}
      .cf-2{left:42.02%; top:-.77%; width:13.92%; height:15.74%}
      .cf-3{left:61.53%; top:2.06%; width:13.78%; height:12.9%}
      .cf-glow{
        position:absolute; left:50%; top:52%; z-index:0;
        width:290%; aspect-ratio:1; transform:translate(-50%,-50%);
        background:radial-gradient(circle, rgba(255,186,84,.34) 0%, rgba(255,150,54,.13) 42%, rgba(255,150,54,0) 68%);
        animation:cfGlow 2.9s cubic-bezier(.45,0,.55,1) infinite alternate;
        will-change:opacity, transform;
      }
      .cf-2 .cf-glow{animation-duration:3.4s; animation-delay:-1.2s}
      .cf-3 .cf-glow{animation-duration:2.6s; animation-delay:-.6s}
      @keyframes cfGlow{
        from{opacity:.55; transform:translate(-50%,-50%) scale(.94)}
        to{opacity:.95; transform:translate(-50%,-50%) scale(1.06)}
      }
      .cf-sway{
        position:absolute; inset:0; z-index:1;
        transform-origin:50% 100%;
        animation:cfSway 3.3s cubic-bezier(.45,0,.55,1) infinite alternate;
        will-change:transform;
      }
      .cf-2 .cf-sway{animation-duration:3.9s; animation-delay:-1.5s}
      .cf-3 .cf-sway{animation-duration:2.9s; animation-delay:-.8s}
      @keyframes cfSway{
        from{transform:rotate(-2.1deg) translateX(-.6px)}
        to{transform:rotate(2.1deg) translateX(.6px)}
      }
      .cf-flick{
        position:absolute; inset:0;
        transform-origin:50% 100%;
        animation:cfFlick 1.9s cubic-bezier(.45,0,.55,1) infinite alternate;
        transition:opacity .55s cubic-bezier(.22,1,.36,1), transform .55s cubic-bezier(.22,1,.36,1);
        will-change:transform, opacity;
      }
      .cf-2 .cf-flick{animation-duration:2.25s; animation-delay:-.7s}
      .cf-3 .cf-flick{animation-duration:1.7s; animation-delay:-1.1s}
      @keyframes cfFlick{
        0%{transform:scale(1,1)}
        45%{transform:scale(.965,1.055)}
        100%{transform:scale(1.03,.985)}
      }
      .cf-flick img{
        width:100%; height:100%; display:block; image-rendering:pixelated;
        filter:drop-shadow(0 0 5px rgba(255,166,66,.35));
      }
      /* Ditiup (kelas .blowing, dipasang JS ±0,52 dtk sebelum .out): api
         berkedut melawan angin — membungkuk ke samping, meliuk balik, lalu
         menyusut bergetar ke sumbu; cahayanya berkedip tak beraturan lalu mati */
      #candlesVisual .cake-flame.blowing .cf-flick{animation:cfBlowOut .52s cubic-bezier(.33,.7,.4,1) forwards}
      @keyframes cfBlowOut{
        0%{transform:scale(1,1) rotate(0deg) translateX(0); opacity:1}
        22%{transform:scale(.9,1.1) rotate(11deg) translateX(7%); opacity:1}
        42%{transform:scale(1.06,.78) rotate(-13deg) translateX(-5%); opacity:.96}
        62%{transform:scale(.68,.58) rotate(15deg) translateX(9%); opacity:.85}
        82%{transform:scale(.36,.3) rotate(6deg) translateY(10%); opacity:.38}
        100%{transform:scale(.16,.1) rotate(0deg) translateY(20%); opacity:0}
      }
      #candlesVisual .cake-flame.blowing .cf-glow{animation:cfGlowOut .52s cubic-bezier(.33,.7,.4,1) forwards}
      @keyframes cfGlowOut{
        0%{opacity:.95}
        30%{opacity:.42}
        55%{opacity:.72}
        100%{opacity:0}
      }
      /* Api padam: berhenti tepat di keadaan akhir animasi tiup (tanpa lompatan),
         ayunan berhenti, cahaya mati */
      #candlesVisual .cake-flame.out .cf-flick{opacity:0; transform:scale(.16,.1) translateY(20%); animation:none}
      #candlesVisual .cake-flame.out .cf-sway{animation-play-state:paused}
      #candlesVisual .cake-flame.out .cf-glow{opacity:0; animation:none; transition:opacity .55s cubic-bezier(.22,1,.36,1)}
      /* Dinyalakan lagi (kelas .relighting, ±0,64 dtk): api muncul dari sumbu
         dengan pantulan lembut melewati ukuran normal lalu stabil berkerlip */
      #candlesVisual .cake-flame.relighting .cf-flick{animation:cfIgnite .64s cubic-bezier(.22,1,.36,1) both}
      @keyframes cfIgnite{
        0%{opacity:0; transform:scale(.12,.06) translateY(22%)}
        45%{opacity:1; transform:scale(1.14,1.2) translateY(0)}
        68%{transform:scale(.93,.95)}
        85%{transform:scale(1.04,1.02)}
        100%{opacity:1; transform:scale(1,1)}
      }
      /* Asap: tiga gumpalan piksel kecil naik berurutan dari sumbu tepat
         setelah api padam, melebar dan memudar pelan dalam jejak melengkung */
      .cf-smoke{
        position:absolute; left:50%; bottom:80%; z-index:2;
        width:11%; aspect-ratio:1; margin-left:-5.5%;
        background:#d8cdb6; box-shadow:2px 2px 0 rgba(0,0,0,.10);
        opacity:0; pointer-events:none;
      }
      .cf-smoke::before,.cf-smoke::after{
        content:""; position:absolute; aspect-ratio:1;
        background:#e2d9c4; box-shadow:2px 2px 0 rgba(0,0,0,.08);
        opacity:0;
      }
      .cf-smoke::before{width:78%; left:-58%; bottom:34%}
      .cf-smoke::after{width:56%; right:-52%; bottom:62%; background:#cfc4ae}
      #candlesVisual .cake-flame.out .cf-smoke{animation:cfSmokeA 1.7s cubic-bezier(.22,.61,.36,1) forwards}
      #candlesVisual .cake-flame.out .cf-smoke::before{animation:cfSmokeB 1.7s .24s cubic-bezier(.22,.61,.36,1) forwards}
      #candlesVisual .cake-flame.out .cf-smoke::after{animation:cfSmokeC 1.7s .48s cubic-bezier(.22,.61,.36,1) forwards}
      @keyframes cfSmokeA{
        0%{opacity:0; transform:translate(0,3px) scale(.55)}
        16%{opacity:.55}
        48%{transform:translate(-3px,-24px) scale(1.1)}
        76%{opacity:.26}
        100%{opacity:0; transform:translate(4px,-54px) scale(1.85)}
      }
      @keyframes cfSmokeB{
        0%{opacity:0; transform:translate(0,2px) scale(.5)}
        20%{opacity:.45}
        55%{transform:translate(-5px,-20px) scale(1.05)}
        100%{opacity:0; transform:translate(-9px,-44px) scale(1.7)}
      }
      @keyframes cfSmokeC{
        0%{opacity:0; transform:translate(0,2px) scale(.5)}
        22%{opacity:.4}
        55%{transform:translate(4px,-16px) scale(1)}
        100%{opacity:0; transform:translate(8px,-36px) scale(1.55)}
      }
      /* Target ketuk tak terlihat di atas tiap lilin (dibuat lewat JS) */
      .cake-tap{
        position:absolute; z-index:3; width:17%; height:30%;
        background:transparent; border-radius:10px; padding:0;
      }
      @media (prefers-reduced-motion: reduce){
        .cake-float, .cake-shadow, .cf-sway, .cf-flick, .cf-glow{animation:none !important}
        #candlesVisual .cake-flame.out .cf-smoke,
        #candlesVisual .cake-flame.out .cf-smoke::before,
        #candlesVisual .cake-flame.out .cf-smoke::after{animation:none !important; opacity:0}
      }
      /* Ruang pesan selalu dicadangkan setinggi DUA baris teks dan isinya
         ditengahkan vertikal: pesan petunjuk awal dan ucapan saat semua
         lilin padam terbungkus 2 baris, sedangkan pesan "tinggal N lilin"
         cuma 1 baris. Tanpa cadangan ini tinggi kotak berubah tiap pesan
         berganti dan tombol di bawahnya ikut melompat naik turun. */
      .cake-msg{min-height:62px; min-height:2lh; line-height:1.55; display:flex; align-items:center; justify-content:center; text-align:center; font-weight:600; color:var(--accent); margin-top:14px; font-size:20px}
      @media (prefers-color-scheme: light){ .cake-msg{color:#8a5a00} }
      .cake-actions{display:flex; gap:12px; justify-content:center; flex-wrap:wrap; margin-top:8px}
      .btn-secondary{
        background:#7d7d7d; color:#fff;
        border:3px solid var(--px-outline); border-radius:0;
        text-shadow:2px 2px 0 rgba(0,0,0,.45);
        box-shadow:inset 0 -5px 0 rgba(0,0,0,.3), inset 0 4px 0 rgba(255,255,255,.32);
        min-height:48px; padding:12px 22px; font-weight:600; font-size:19px;
      }
      .btn-secondary:hover{background:#8f8f8f; border-color:var(--px-outline)}

      /* ---------- Hiasan pesta piksel di sekeliling kue ----------
         Lapisan dekor di belakang kue (z-index 0, kue z-index 1): bunting
         bergelombang di pita atas area kue (padding-top .cake-zone memberi
         ruang khusus supaya benderanya tidak menyentuh api lilin) dan empat
         balon piksel di sisi kiri/kanan pada area yang di gambar kue memang
         transparan. Semua pointer-events none dan aria-hidden; geraknya
         cuma transform/opacity: lapis luar untuk animasi masuk bertahap,
         lapis dalam untuk ayunan/ambang pelan tanpa henti. */
      .cake-zone .cake{z-index:1}
      .cake-decor{position:absolute; inset:0; z-index:0; pointer-events:none}
      .cake-decor svg{display:block; image-rendering:pixelated}
      .cd-bunting{
        position:absolute; left:1%; right:1%; top:0;
        animation:cdBuntingIn 1.15s .3s cubic-bezier(.22,1,.36,1) backwards;
        will-change:transform, opacity;
      }
      @keyframes cdBuntingIn{
        from{opacity:0; transform:translateY(-8px)}
        to{opacity:1; transform:translateY(0)}
      }
      .cd-bunting-sway{
        width:100%; height:auto; transform-origin:50% 0;
        animation:cdSway 6.5s cubic-bezier(.45,0,.55,1) infinite alternate;
        will-change:transform;
      }
      @keyframes cdSway{
        from{transform:rotate(-.55deg)}
        to{transform:rotate(.55deg)}
      }
      .cd-balloon{position:absolute; pointer-events:none}
      .cd-balloon svg{
        width:100%; height:auto;
        filter:drop-shadow(2px 3px 0 rgba(0,0,0,.12));
      }
      .cd-b1{left:-2px; top:60px; width:54px; animation:cdBalloonIn 1.25s .5s cubic-bezier(.22,1,.36,1) backwards}
      .cd-b2{left:4px; top:206px; width:42px; animation:cdBalloonIn 1.25s .68s cubic-bezier(.22,1,.36,1) backwards}
      .cd-b3{right:-2px; top:80px; width:50px; animation:cdBalloonIn 1.25s .86s cubic-bezier(.22,1,.36,1) backwards}
      .cd-b4{right:6px; top:220px; width:40px; animation:cdBalloonIn 1.25s 1.04s cubic-bezier(.22,1,.36,1) backwards}
      @keyframes cdBalloonIn{
        from{opacity:0; transform:translateY(16px)}
        to{opacity:1; transform:translateY(0)}
      }
      .cd-float{
        animation:cdFloat 5.4s cubic-bezier(.45,0,.55,1) infinite alternate;
        will-change:transform;
      }
      .cd-b2 .cd-float{animation-duration:6.2s; animation-delay:-2.1s}
      .cd-b3 .cd-float{animation-duration:4.9s; animation-delay:-1.3s}
      .cd-b4 .cd-float{animation-duration:5.9s; animation-delay:-3.4s}
      @keyframes cdFloat{
        from{transform:translateY(3px) rotate(-1.1deg)}
        to{transform:translateY(-5px) rotate(1.1deg)}
      }
      .cd-b1 .bl-body{fill:#d98a9e}
      .cd-b2 .bl-body{fill:#93b184}
      .cd-b3 .bl-body{fill:#e0c078}
      .cd-b4 .bl-body{fill:#8fb0c9}
      @media (max-width:480px){
        .cake-zone{padding-top:40px}
        .cd-b1{width:42px; top:54px}
        .cd-b2{width:34px; top:182px}
        .cd-b3{width:40px; top:68px}
        .cd-b4{width:32px; top:194px}
      }
      @media (prefers-reduced-motion: reduce){
        .cd-bunting, .cd-bunting-sway, .cd-balloon, .cd-float{animation:none !important}
      }

      /* Letter + photo */
      .grid{
        display:grid; gap:20px; margin-top:44px;
      }
      @media(min-width:720px){ .grid{grid-template-columns:1.25fr .85fr} }
      .card{
        background:var(--card); border:3px solid var(--px-outline); border-radius:0;
        padding:clamp(20px,4vw,32px);
        box-shadow:7px 7px 0 rgba(0,0,0,.22);
        transition:transform .5s cubic-bezier(.22,1,.36,1), box-shadow .5s cubic-bezier(.22,1,.36,1);
      }
      .card:hover{transform:translateY(-3px)}
      .card h3{
        font-family:"Press Start 2P",monospace; font-weight:400; letter-spacing:0;
        font-size:15px; line-height:1.6; margin:0 0 14px;
      }
      .letter{
        background:var(--paper); color:var(--ink-dark);
      }
      .letter p{margin:0 0 14px; font-size:20px; line-height:1.55}
      .letter .sign{font-family:"Press Start 2P",monospace; font-size:13px; line-height:1.7; margin-top:20px}
      .letter .sign small{display:block; font-family:"VT323",monospace; font-size:17px; color:#7a5a3d; margin-top:2px}
      .photo-box{
        aspect-ratio:4/5;
        border:3px dashed #8a5c22; border-radius:0;
        background:var(--bg-2);
        display:flex; flex-direction:column; align-items:center; justify-content:center;
        text-align:center; padding:24px; gap:8px;
        image-rendering:pixelated;
      }
      .photo-box .ph-title{font-family:"Press Start 2P",monospace; font-size:14px; line-height:1.6; color:var(--accent)}
      @media (prefers-color-scheme: light){
        .photo-box{background:#fff3dd}
        .photo-box .ph-title{color:#8a5a00}
      }
      .photo-box p{margin:0; color:var(--ink-soft); font-size:17px; max-width:26ch}
      @media (prefers-color-scheme: light){
        .photo-box p{color:#5d4423}
      }

      /* ---------- Slide Foto: jendela krem "your Seasons" dengan kolase foto
         gaya perangko, meniru referensi: satu jendela krem lebar bersudut
         membulat dengan bingkai tipis ganda, di dalamnya satu foto besar
         tegak di kiri dan enam foto kecil bervariasi di kanan dan tengah,
         judul "your Seasons" di kanan atas, dan teks "cheers to every
         version of you" di kiri bawah. Slot Foto 1 sampai 6 sudah diisi
         foto asli; slot Foto 7 masih placeholder menunggu kiriman
         berikutnya. Rasio aspek tiap slot terisi disamakan persis dengan
         rasio foto yang mengisinya, jadi keenam foto tampil utuh dari
         tepi ke tepi, rapih, dan wajah tidak terpotong. ---------- */
      .seasons-win{
        position:relative;
        background:var(--paper); color:var(--ink-dark);
        border:1.5px solid rgba(43,26,18,.5); border-radius:20px;
        box-shadow:0 22px 44px rgba(10,5,2,.35), 0 4px 10px rgba(10,5,2,.22), inset 0 1px 0 rgba(255,255,255,.65);
        max-width:900px; margin:0 auto;
        padding:clamp(16px,3.2vw,30px);
      }
      /* Garis bingkai tipis kedua yang masuk ke dalam, seperti referensi */
      .seasons-win::before{
        content:""; position:absolute; inset:9px; z-index:0; pointer-events:none;
        border:1.5px solid rgba(43,26,18,.38); border-radius:13px;
      }
      .seasons-stage{position:relative; z-index:1; aspect-ratio:10/7}
      @media (max-width:560px){ .seasons-stage{aspect-ratio:10/8.4} }
      /* Judul kanan atas: "your" kecil di atas "Seasons" yang besar */
      .seasons-title{position:absolute; top:0; right:1.5%; z-index:4; line-height:1; pointer-events:none}
      .seasons-title .st-your{
        display:block; margin-left:36%;
        font-size:clamp(13px,2.3vw,20px); letter-spacing:.22em; color:#7a5a3d;
      }
      .seasons-title .st-seasons{
        display:block; font-size:clamp(27px,5.6vw,46px); line-height:1.05; color:var(--ink-dark);
      }
      /* Teks penutup di kiri bawah jendela, baris kedua menjorok seperti referensi */
      .seasons-cheers{
        position:absolute; left:1%; bottom:0; z-index:4; margin:0; pointer-events:none;
        font-size:clamp(13px,2.5vw,21px); line-height:1.3; color:#5d4423;
      }
      .seasons-cheers .ch-2{display:block; margin-left:2.2em}
      /* ---------- Tulisan slide foto: Handjet 900 + kapital semua ----------
         Hanya di dalam #viewFoto: judul halaman "Galery", caption di
         bawahnya, judul "your Seasons", dan teks "cheers to every version
         of you". Teks di HTML tetap seperti semula; kapitalnya dari
         text-transform. Ukuran diperbesar dari font lama karena Handjet
         900 tebal dan rapat, tapi dijaga supaya tidak meluber keluar
         jendela dan tidak menutupi foto atau tombol. Slide lain tetap
         memakai Press Start 2P / VT323 / Caveat seperti sebelumnya. */
      #viewFoto .sub-head h2{
        font-family:"Handjet","Press Start 2P",monospace; font-weight:900;
        text-transform:uppercase;
        font-size:clamp(34px,8.6vw,50px); line-height:1.12; letter-spacing:.05em;
      }
      #viewFoto .sub-head p{
        font-family:"Handjet","VT323",monospace; font-weight:900;
        text-transform:uppercase;
        font-size:clamp(20px,4.8vw,27px); line-height:1.3; letter-spacing:.04em;
      }
      #viewFoto .seasons-title .st-your{
        font-family:"Handjet","VT323",monospace; font-weight:900;
        text-transform:uppercase;
        font-size:clamp(14px,2.4vw,22px); letter-spacing:.16em;
      }
      #viewFoto .seasons-title .st-seasons{
        font-family:"Handjet","VT323",monospace; font-weight:900;
        text-transform:uppercase;
        font-size:clamp(27px,7.6vw,54px); line-height:1.02; letter-spacing:.02em;
      }
      #viewFoto .seasons-cheers{
        font-family:"Handjet","VT323",monospace; font-weight:900;
        text-transform:uppercase;
        font-size:clamp(17px,3.4vw,27px); line-height:1.28; letter-spacing:.03em;
      }
      /* ---------- Hiasan cute 8-bit ala One Piece di jendela Seasons ----------
         Semua ditaruh hanya di area kosong panggung: topi jerami piksel
         bertengger miring di kanan atas di samping judul, bendera Jolly
         Roger piksel (tengkorak bertopi jerami) di pojok kiri atas, tiga
         camar piksel terbang melintasi tepi atas jendela, hati serta
         bintang piksel kecil di sudut bawah dekat teks cheers, dan kapal
         Thousand Sunny piksel berlayar pelan di sepanjang tepi bawah.
         Tidak ada
         yang menutupi foto, judul, teks, atau tombol: semuanya
         pointer-events none dan berada di celah antar perangko. Gaya
         pikselnya selaras tema situs (gambar dirender pixelated, SVG
         memakai crispEdges), warnanya lembut di atas krem jendela.
         Animasi masuk dan gerak halusnya diikat di aturan #viewFoto.active
         di bawah, hanya transform dan opacity plus properti translate,
         rotate, dan scale yang terpisah, dengan easing situs yang sama. */
      .op-decor{position:absolute; z-index:5; pointer-events:none}
      .op-decor svg{display:block; width:100%; height:auto}
      .op-decor img{display:block; width:100%; height:auto; image-rendering:pixelated}
      /* Topi jerami: miring bertengger di tepi atas, di samping judul.
         Kemiringan dasarnya dari transform; goyangan halusnya memakai
         properti rotate terpisah supaya tidak bentrok dengan animasi
         masuk yang menganimasikan transform. */
      .op-hat{
        top:-2%; right:30%; width:12%;
        transform:rotate(-11deg);
        filter:drop-shadow(2px 3px 0 rgba(43,26,18,.20));
      }
      /* Bendera Jolly Roger di pojok kiri atas, tiangnya di tepi kiri
         panggung, kain benderanya di strip atas yang kosong sebelum
         perangko pertama mulai */
      .op-flag{
        top:-1.5%; left:.5%; width:11.5%;
        filter:drop-shadow(2px 3px 0 rgba(43,26,18,.20));
      }
      /* Camar: mulai dari tepi kiri panggung, terbang melintasi bagian
         atas jendela sedikit di atas tepi atasnya, jadi tidak pernah
         melewati badan foto mana pun */
      .op-gull{top:-9px; left:0; z-index:6; filter:drop-shadow(1px 2px 0 rgba(43,26,18,.16))}
      .op-gull--1{width:32px}
      .op-gull--2{width:25px}
      .op-gull--3{width:20px}
      /* Hati dan bintang piksel kecil di sudut bawah, di kanan teks
         cheers yang pendek, di bawah perangko kelima yang menggantung
         lebih tinggi */
      .op-heart{color:#f28bb4; filter:drop-shadow(1px 2px 0 rgba(43,26,18,.16))}
      .op-heart--1{left:30.5%; bottom:1.5%; width:17px; rotate:-8deg}
      .op-heart--2{left:26.5%; bottom:12.5%; width:12px; rotate:9deg; color:#e879a6}
      .op-star{left:33.2%; bottom:8.5%; width:16px; rotate:10deg; color:#e9b949; filter:drop-shadow(1px 2px 0 rgba(43,26,18,.16))}
      /* Kapal Thousand Sunny di tepi bawah jendela: lambung cokelat
         berkepala singa di haluan, layar putih besar bergambar matahari,
         bendera Jolly Roger kecil di puncak tiang, dan ombak piksel biru
         kecil di bawah lambung, semuanya dalam satu SVG piksel.
         Posisinya dihitung dari geometri perangko yang ada: strip bawah
         panggung di bawah perangko 5 (tepi bawahnya di 82,9 persen
         tinggi panggung) dan perangko 6 (86,9 persen) memang kosong,
         teks cheers berakhir sekitar 22 persen lebar, dan hati serta
         bintang berakhir di 36 persen lebar. Kapal setinggi kurang lebih
         11,3 persen bertengger di dasar strip itu dan hanya berlayar
         menyamping di rentang x 38 sampai 79 persen, jadi tidak pernah
         menutupi perangko 6 di atasnya, perangko 7 di kanan (mulai 83
         persen), hati dan bintang di kiri, maupun teks cheers. Di HP
         panggung lebih tinggi (rasio 10/8,4) jadi strip kosongnya
         justru lebih lega. */
      .op-ship{
        left:39%; bottom:.4%; width:14.5%;
        filter:drop-shadow(2px 3px 0 rgba(43,26,18,.18));
      }
      /* Perangko: bingkai putih yang tepinya bergerigi karena titik titik
         sewarna jendela menembus tepi bingkai, plus bayangan lembut */
      .stamp{
        position:absolute; z-index:2; display:block; margin:0;
        background:#fffdf7;
        padding:clamp(4px,.9vw,9px);
        transform:rotate(var(--rot,0deg));
        filter:drop-shadow(2px 4px 5px rgba(43,26,18,.28));
        transition:transform .45s cubic-bezier(.22,1,.36,1);
      }
      .stamp:hover{transform:rotate(var(--rot,0deg)) translateY(-4px) scale(1.03); z-index:6}
      .stamp::after{
        content:""; position:absolute; inset:-4px; z-index:3; pointer-events:none;
        background-image:
          radial-gradient(circle 3px, var(--paper) 2.5px, rgba(0,0,0,0) 3.4px),
          radial-gradient(circle 3px, var(--paper) 2.5px, rgba(0,0,0,0) 3.4px),
          radial-gradient(circle 3px, var(--paper) 2.5px, rgba(0,0,0,0) 3.4px),
          radial-gradient(circle 3px, var(--paper) 2.5px, rgba(0,0,0,0) 3.4px);
        background-size:12px 8px, 12px 8px, 8px 12px, 8px 12px;
        background-position:6px 0, 6px 100%, 0 6px, 100% 6px;
        background-repeat:repeat-x, repeat-x, repeat-y, repeat-y;
      }
      /* Posisi dan ukuran ketujuh perangko: rapih tapi hidup, sedikit
         miring. Rasio aspek slot 1 sampai 6 disamakan persis dengan rasio
         asli fotonya masing-masing (Foto 1: 845x899, Foto 2: 902x959,
         Foto 3: 1076x590, Foto 4: 898x1600, Foto 5: 751x719, Foto 6:
         720x1280) supaya tidak ada sisi foto yang terpotong bingkai */
      .stamp--1{left:3.5%; top:13%; width:23%; aspect-ratio:845/899; --rot:-1.6deg}
      .stamp--2{left:30%; top:5%; width:27%; aspect-ratio:902/959; --rot:1.1deg}
      .stamp--3{left:59.5%; top:14%; width:17.5%; aspect-ratio:1076/590; --rot:-2deg}
      .stamp--4{left:82.5%; top:27%; width:13.5%; aspect-ratio:898/1600; --rot:1.6deg}
      .stamp--5{left:32%; top:46%; width:27%; aspect-ratio:751/719; --rot:-1.1deg}
      .stamp--6{left:63%; top:45%; width:16.5%; aspect-ratio:720/1280; --rot:2deg}
      .stamp--7{left:83%; top:68%; width:14.5%; aspect-ratio:4/3; --rot:-1.6deg}
      /* Slot yang masih kosong (Foto 7 saja): tampil benar-benar kosong
         dan polos, tanpa ikon kamera, tanpa bingkai perangko, dan tanpa
         label; bingkai perangko putih bergerigi dihilangkan dan slot
         tetap di posisi dan ukurannya semula. Slot Foto 4 kini sudah
         terisi foto Nayla menutup mulut dengan tangan, jadi bingkai
         perangkanya berlaku normal seperti slot terisi lainnya. */
      .stamp--7{background:transparent; padding:0; filter:none}
      .stamp--7::after{display:none}
      .stamp--7 .stamp-photo{background:transparent; box-shadow:none}
      /* Area foto di dalam bingkai perangko */
      .stamp-photo{
        position:relative; width:100%; height:100%; overflow:hidden;
        background:#ffeec4;
        box-shadow:inset 0 0 0 2px rgba(138,90,0,.18);
      }
      .stamp-photo img.stamp-img{
        position:absolute; inset:0; z-index:1;
        width:100%; height:100%; object-fit:cover; display:block;
      }
      /* Rasio aspek tiap slot sudah disamakan persis dengan rasio foto
         yang mengisinya (lihat .stamp--1 sampai .stamp--6 di atas), jadi
         object-fit cover tidak memotong sisi mana pun: keenam foto tampil
         utuh dan wajah tidak terpotong. Nilai object-position di bawah
         hanya pengaman halus bila ada selisih pembulatan rasio. */
      .stamp--1 .stamp-img{object-position:50% 26%}
      .stamp--2 .stamp-img{object-position:50% 68%}
      .stamp--3 .stamp-img{object-position:36% 38%}
      .stamp--5 .stamp-img{object-position:72% 32%}
      .stamp--6 .stamp-img{object-position:50% 50%}
      /* Foto di halaman ini tidak bisa ditekan: tidak bisa ditahan lama
         sampai muncul menu simpan atau bagikan, tidak bisa diseret,
         tidak bisa diseleksi, dan tidak ada sorotan ketukan. Pola yang
         sama seperti perlindungan tulisan di slide 2 dan notes. Tombol
         minus, X, dan tombol kembali tidak terpengaruh karena semuanya
         berada di luar area foto. */
      #viewFoto .stamp-photo img{
        -webkit-user-select:none;
        user-select:none;
        -webkit-touch-callout:none;
        -webkit-user-drag:none;
        -webkit-tap-highlight-color:transparent;
        pointer-events:none;
      }
      /* Wishes */
      .wishes{margin-top:26px}
      .wishes h3{
        font-family:"Press Start 2P",monospace; font-weight:400; letter-spacing:0;
        font-size:clamp(16px,4.4vw,23px); line-height:1.55;
        text-align:center; margin:0 0 8px;
      }
      .wishes .lead{text-align:center; color:var(--ink-soft); margin:0 auto 24px; max-width:48ch; font-size:20px}
      .wish-list{display:grid; gap:14px}
      @media(min-width:640px){ .wish-list{grid-template-columns:1fr 1fr} }
      .wish{
        background:var(--card); border:3px solid var(--px-outline); border-radius:0; padding:20px;
        box-shadow:7px 7px 0 rgba(0,0,0,.22);
        transition:transform .5s cubic-bezier(.22,1,.36,1), box-shadow .5s cubic-bezier(.22,1,.36,1);
      }
      .wish:hover{transform:translateY(-3px)}
      .wish strong{
        display:block; margin-bottom:10px; color:var(--accent-strong);
        font-family:"Press Start 2P",monospace; font-weight:400;
        font-size:12px; line-height:1.7;
      }
      @media (prefers-color-scheme: light){ .wish strong{color:#9a5b12} }
      .wish p{margin:0; color:var(--ink-soft); font-size:18px}
      @media (prefers-color-scheme: light){ .wish p{color:#5a4030} }

      .closing{
        text-align:center; margin-top:52px; padding-top:32px; border-top:1px solid var(--line);
      }
      .closing p{color:var(--ink-soft); margin:0 0 18px; font-size:20px}
      footer{
        text-align:center; color:var(--ink-soft); font-size:16px; margin-top:36px;
      }


      /* ---------- Slide 2: empat tombol ikon piksel tanpa tulisan ---------- */
      .hub-grid{
        display:grid; grid-template-columns:1fr 1fr; gap:16px;
        margin:30px auto 10px; max-width:520px;
      }
      @media (max-width:480px){ .hub-grid{gap:12px} }
      /* Latar tombol kini krem hangat yang sama persis dengan plakat
         judul "Happy birthday": tekstur papan catur sangat halus yang
         sama, gradien krem yang sama, garis tepi emas yang sama, dan
         garis emas kedua di bagian dalam dari bayangan inset yang
         sama, jadi keempat tombol terasa ringan dan satu keluarga
         dengan plakatnya. Sudutnya tetap kotak piksel dan bayangan
         luarnya tetap bayangan piksel keras seperti semula. Ikon di
         atas krem tetap terbaca jelas: piringan hitam, kamera krem
         (berbingkai gelap), amplop pink, dan balok tanda tanya emas
         semuanya kontras di atas krem terang. */
      .hub-btn{
        display:flex; align-items:center; justify-content:center;
        min-height:156px; padding:18px;
        background:
          repeating-conic-gradient(rgba(146,104,44,.05) 0% 25%, rgba(255,255,255,.055) 0% 50%),
          linear-gradient(180deg, #fffbe9 0%, #fdf3d0 58%, #f6e5b8 100%);
        background-size:16px 16px, cover;
        border:3px solid #c79a48; border-radius:0;
        box-shadow:
          7px 7px 0 rgba(0,0,0,.22),
          inset 0 0 0 2px #fffbe9,
          inset 0 0 0 5px rgba(178,128,52,.48),
          inset 0 2px 0 rgba(255,255,255,.85);
      }
      .hub-btn{transition:transform .5s cubic-bezier(.22,1,.36,1), box-shadow .5s cubic-bezier(.22,1,.36,1)} .hub-btn img{transition:transform .5s cubic-bezier(.22,1,.36,1)} .hub-btn:hover{transform:translateY(-3px); box-shadow:8px 10px 0 rgba(0,0,0,.22), inset 0 0 0 2px #fffbe9, inset 0 0 0 5px rgba(178,128,52,.48), inset 0 2px 0 rgba(255,255,255,.85)} .hub-btn:hover img{transform:scale(1.03)}
      /* Animasi hidup slide 2: ikon di tiap tombol melayang lembut bergantian,
         sapuan cahaya halus menyapu tombol satu per satu secara berkala,
         dan judul melayang sangat pelan. Semua murni transform dan opacity
         di GPU dengan easing mengalir, jadi terasa elegan dan tidak kaku. */
      .hub-btn{position:relative; overflow:hidden}
      .hub-btn::after{
        content:""; position:absolute; inset:0; z-index:2; pointer-events:none;
        background:linear-gradient(105deg, rgba(255,255,255,0) 32%, rgba(255,246,214,.16) 50%, rgba(255,255,255,0) 68%);
        transform:translateX(-130%);
        animation:hubSheen 8s cubic-bezier(.45,0,.55,1) infinite;
      }
      .hub-grid .hub-btn:nth-child(2)::after{animation-delay:1.6s}
      .hub-grid .hub-btn:nth-child(3)::after{animation-delay:3.2s}
      .hub-grid .hub-btn:nth-child(4)::after{animation-delay:4.8s}
      @keyframes hubSheen{
        0%{transform:translateX(-130%)}
        16%{transform:translateX(130%)}
        100%{transform:translateX(130%)}
      }
      .hub-btn img{animation:hubIconFloat 5.4s cubic-bezier(.45,0,.55,1) infinite alternate}
      .hub-grid .hub-btn:nth-child(2) img{animation-delay:-1.4s; animation-duration:5.9s}
      .hub-grid .hub-btn:nth-child(3) img{animation-delay:-2.7s; animation-duration:5.1s}
      .hub-grid .hub-btn:nth-child(4) img{animation-delay:-.8s; animation-duration:6.2s}
      @keyframes hubIconFloat{
        from{transform:translateY(2.5px) rotate(-.8deg)}
        to{transform:translateY(-3.5px) rotate(.8deg)}
      }
      .hub-btn:hover img{animation-play-state:paused}
      /* Judul bergoyang sangat pelan sebagai SATU kesatuan utuh:
         inilah satu-satunya gerakan judul, supaya komposisinya terasa
         tenang dan stabil saat pertama dilihat, bukan ramai sendiri. */
      #viewMain .hero h2{animation:riseIn 1.1s cubic-bezier(.22,1,.36,1) both, heroFloat 8.5s cubic-bezier(.37,0,.63,1) 1.3s infinite}
      /* Goyangannya kini gelombang sinus penuh (0 ke 50 ke 100)
         dengan easing sinus yang sama di kedua arah, bukan lagi
         alternate yang membalik arah di ujung: tidak ada titik
         patah sama sekali, judul naik turun seperti napas pelan.
         Murni transform translate3d di GPU, tanpa properti lain. */
      @keyframes heroFloat{
        0%,100%{transform:translate3d(0,0,0)}
        50%{transform:translate3d(0,-3.5px,0)}
      }
      #viewMain .hero p{animation:riseIn 1.1s .25s cubic-bezier(.22,1,.36,1) both}
      #viewMain footer{animation:riseIn 1.2s .7s cubic-bezier(.22,1,.36,1) both}
      /* Footer slide 2 duduk di atas pita tanah piksel, jadi baris
         teksnya diberi chip gelap tembus pandang di belakangnya plus
         bayangan teks gelap yang lembut: tulisannya selalu terbaca
         jelas di atas tekstur tanah yang ramai, di HP maupun laptop,
         skema terang maupun gelap. Chip hanya selebar teksnya dan
         terpusat, jadi tidak menutupi kucing di tepi bawah, tombol
         Kembali di atasnya, atau isi lain. */
      #viewMain footer{
        width:fit-content; max-width:92vw;
        margin-left:auto; margin-right:auto;
        padding:7px 16px 9px;
        color:#fdf3d0;
        background:rgba(26,15,5,.55);
        border-radius:999px;
        box-shadow:0 2px 10px rgba(26,15,5,.25), inset 0 1px 0 rgba(255,243,208,.14);
        text-shadow:0 1px 2px rgba(26,15,5,.65);
      }
      /* ---------- Slide 2 cute ----------
         Judul "Happy birthday" kini memakai Minecraft Ten: blok
         piksel tegas ala logo Minecraft, tapi bentuknya bersih,
         mudah dibaca, dan tetap lucu. Huruf-hurufnya DIAM sebagai satu
         kata utuh (gerakan huruf satu-satu dengan jeda berbeda terasa
         seperti template, jadi dihentikan); yang bergerak hanya
         seluruh judul bergoyang sangat pelan sebagai satu kesatuan
         (heroFloat di atas). Hati pink di kiri dan bintang emas di
         kanan juga diam, hanya miring sedikit seperti ditempel
         tangan, sebaris dengan judul jadi tidak pernah tertutup awan
         dan tidak menutupi apa pun. Bayangannya lembut membulat,
         bukan lagi bayangan piksel yang keras.
         Baris di bawah judul kini berbunyi "Hari ini
         harimu, enjoy your day." dengan Baloo 2 (subjudul sengaja
         tetap Baloo 2; hanya judul yang bergaya Minecraft).
         Keempat tombol ikon memakai gaya piksel solid seperti
         semula, tombol Kembali memakai gaya tombol biasa, dan
         chip footer memakai chip gelap tembus pandang. */
      #viewMain .hero h2{
        font-family:"Minecraft Ten","Press Start 2P",monospace;
        font-weight:400;
        font-size:clamp(21px,6.4vw,40px);
        letter-spacing:.02em;
        line-height:1.35;
        /* Tepian huruf dihaluskan: antialias penuh plus bayangan
           lembut berlapis, jadi blok pikselnya tegas tapi tidak
           terasa kasar atau bergerigi di layar HP. */
        -webkit-font-smoothing:antialiased;
        -moz-osx-font-smoothing:grayscale;
        text-rendering:optimizeLegibility;
        text-shadow:0 3px 0 rgba(43,26,12,.14), 0 10px 24px rgba(43,26,12,.30);
      }
      @media (prefers-color-scheme: light){
        #viewMain .hero h2{text-shadow:0 2px 0 rgba(255,253,244,.55), 0 10px 22px rgba(90,64,32,.22)}
      }
      /* Judul selalu satu baris utuh: lebar font Minecraft sudah
         dihitung (teks + hati + bintang sekitar 9,3em) dan ukuran
         clamp di atas menjaga jumlah itu muat dari HP kecil sampai
         laptop, jadi tidak pernah membungkus atau tertutup awan. */
      #viewMain .hero h2 .h-inner{white-space:nowrap}
      @media (prefers-reduced-motion: reduce){
        #viewMain .hero h2{animation:none}
      }
      #viewMain .hero h2 .h-inner, #viewMain .hero h2 .h-word{color:inherit}
      #viewMain .hero h2 .h-word{white-space:nowrap}
      /* Huruf judul diam sepenuhnya: tidak ada lagi animasi per huruf.
         Span huruf tetap ada di markup, tapi statis, jadi judul terasa
         seperti tulisan yang dipasang rapi, bukan efek template. */
      #viewMain .hero h2 .h-letter{
        display:inline-block; color:inherit;
      }
      #viewMain .hero h2 .h-accent{display:inline-block; width:.56em; height:.56em; vertical-align:-.05em}
      #viewMain .hero h2 .h-accent svg{display:block; width:100%; height:100%}
      /* Hati dan bintang diam juga: tidak berdenyut, tidak berkelip.
         Hanya miring statis sedikit, beda arah satu sama lain, seperti
         stiker yang ditempel tangan di samping judul. */
      #viewMain .hero h2 .h-accent-heart{margin-right:.16em; transform:rotate(-5deg)}
      #viewMain .hero h2 .h-accent-star{margin-left:.16em; transform:rotate(7deg)}
      /* Bingkai plakat untuk judul: papan krem-emas yang rapi dan
         elegan mengelilingi "Happy birthday", menyatu dengan gaya
         piksel situs (tekstur papan catur sangat halus yang sama
         seperti permukaan lain) dan palet krem-emasnya. Bingkai
         dipasang pada h2 itu sendiri, jadi judul dan bingkainya
         bergoyang sebagai SATU kesatuan mengikuti heroFloat yang
         sudah ada; tidak ada animasi baru dan tidak ada
         backdrop-filter, jadi tetap ringan di HP. Garis emas kedua
         di bagian dalam (dari bayangan inset) membuat plakatnya
         terasa seperti papan nama beneran, dan empat sudutnya
         dihias hati pink dan bintang emas mungil yang sama dengan
         hiasan di dalam judul, duduk mengangkangi garis bingkai
         seperti stiker yang ditempel tangan (miringnya beda-beda
         sedikit). Huruf judul menjadi cokelat hangat gelap di atas
         krem plakat supaya tetap terbaca jelas di skema terang
         maupun gelap; hati dan bintang di dalam judul tidak
         berubah. Padding dihitung dalam em, jadi bingkai selalu
         membungkus pas mengikuti ukuran huruf dan judul tetap satu
         baris dari HP kecil sampai laptop. */
      #viewMain .hero h2{
        position:relative;
        display:inline-block;
        padding:.52em .92em .58em;
        color:#4a2c10;
        background:
          repeating-conic-gradient(rgba(146,104,44,.05) 0% 25%, rgba(255,255,255,.055) 0% 50%),
          linear-gradient(180deg, #fffbe9 0%, #fdf3d0 58%, #f6e5b8 100%);
        background-size:16px 16px, cover;
        border:3px solid #c79a48;
        border-radius:16px;
        box-shadow:
          inset 0 0 0 2px #fffbe9,
          inset 0 0 0 5px rgba(178,128,52,.48),
          inset 0 2px 0 rgba(255,255,255,.85),
          0 12px 26px rgba(43,26,12,.28);
        text-shadow:0 2px 0 rgba(255,255,255,.6), 0 6px 14px rgba(90,64,32,.18);
      }
      /* Hiasan sudut plakat: hati pink dan bintang emas kecil di
         empat sudut, diam (hanya ikut bergoyang bersama judul),
         tidak menutupi huruf karena duduk di garis bingkai. */
      #viewMain .hero h2 .h-corner{
        position:absolute; z-index:1;
        width:.58em; height:.58em;
        pointer-events:none;
        filter:drop-shadow(0 2px 2px rgba(43,26,12,.3));
      }
      #viewMain .hero h2 .h-corner svg{display:block; width:100%; height:100%}
      #viewMain .hero h2 .h-corner-tl{top:-.3em; left:-.3em; transform:rotate(-8deg)}
      #viewMain .hero h2 .h-corner-tr{top:-.3em; right:-.3em; transform:rotate(9deg)}
      #viewMain .hero h2 .h-corner-bl{bottom:-.3em; left:-.3em; transform:rotate(6deg)}
      #viewMain .hero h2 .h-corner-br{bottom:-.3em; right:-.3em; transform:rotate(-7deg)}
      /* Di HP judul + plakat dikecilkan dan dijauhkan dari tepi layar:
         ukuran huruf turun cukup jauh dari clamp desktop supaya tidak
         mendominasi layar, dan ada margin napas di kiri-kanan plakat
         (di atas padding .wrap yang sudah ada) supaya bingkai beserta
         hiasan sudutnya yang menjorok keluar tidak menempel ke tepi.
         Judul tetap satu baris dan tetap mudah dibaca; font, plakat,
         hiasan sudut, dan subjudul tidak berubah. Tablet dan laptop
         tidak terpengaruh. */
      @media (max-width:640px){
        #viewMain .hero h2{
          font-size:clamp(16px,5vw,22px);
          margin-left:10px; margin-right:10px;
          max-width:calc(100vw - 64px);
        }
      }
      @media (max-width:380px){
        #viewMain .hero h2{font-size:clamp(15px,4.8vw,19px)}
      }
      /* Baris di bawah judul: "Hari ini harimu, enjoy your day."
         Campuran Indonesia dan Inggris yang singkat dan bermakna,
         memakai Caveat 600 (tulisan tangan, self-hosted) atas pilihan
         Kevin 2026-11-14 — rasanya lebih personal dan nyambung sama
         surat di Notes. Karena Caveat lebih tipis dan hurufnya lebih
         kecil dari Baloo 2, ukurannya dinaikkan (dasar .hero p 21px
         jadi clamp 27–32px) supaya tetap mudah dibaca di HP; teks,
         posisi, warna, dan bayangannya tetap sama seperti sebelumnya. */
      #viewMain .hero p{
        font-family:"Caveat","Baloo 2",cursive;
        font-weight:600;
        font-size:clamp(27px,6.5vw,32px);
        line-height:1.15;
        letter-spacing:.02em;
        text-shadow:0 1px 0 rgba(43,26,12,.14), 0 5px 16px rgba(43,26,12,.22);
      }
      @media (prefers-color-scheme: light){
        #viewMain .hero p{text-shadow:0 1px 0 rgba(255,253,244,.55), 0 5px 14px rgba(90,64,32,.18)}
      }
      .hub-btn:active{transform:scale(.98)}
      .hub-btn img{
        display:block; width:min(118px,32vw); height:min(118px,32vw);
        object-fit:contain; image-rendering:pixelated;
        filter:drop-shadow(4px 5px 0 rgba(0,0,0,.22));
      }
      .sub-head{text-align:center; padding:clamp(28px,6vw,44px) 0 4px}
      .sub-head h2{
        font-family:"Press Start 2P",monospace; font-weight:400;
        font-size:clamp(18px,5.2vw,28px); line-height:1.5; margin:0 0 10px;
        text-shadow:3px 3px 0 rgba(0,0,0,.35);
      }
      @media (prefers-color-scheme: light){ .sub-head h2{text-shadow:3px 3px 0 rgba(51,34,15,.18)} }
      /* Judul halaman Notes saja: kapital semua + font cute Pixelify Sans 600,
         ukuran sedang. Teks HTML tetap "Notes"; kapitalnya dari text-transform.
         Surat, kalender, wishes, dan slide lain tidak ikut berubah. */
      #viewNotes .sub-head h2#notesTitle{
        font-family:"Pixelify Sans","Press Start 2P",monospace; font-weight:600;
        text-transform:uppercase;
        font-size:clamp(24px,6.2vw,34px); line-height:1.3; letter-spacing:.04em;
      }
      .sub-head p{color:var(--ink-soft); margin:0 auto; max-width:48ch; font-size:20px}
      .back-row{display:flex; flex-wrap:wrap; gap:12px; justify-content:center; margin:30px 0 4px}

      /* ---------- Slide tanda tanya: jendela foto editan Nayla dikelilingi kata kata baik ---------- */
      /* Jendela krem seperti foto referensi: foto lingkaran di tengah, topi pesta
         piksel miring di kanan atas foto, kue piksel di kiri bawah foto, kata kata
         baik bertebaran di kiri kanan, bintang piksel warna warni berkelip. */
      .trait-win{
        background:var(--paper); color:var(--ink-dark);
        border:4px solid var(--px-outline); border-radius:0;
        box-shadow:8px 8px 0 rgba(0,0,0,.24), inset 0 0 0 3px rgba(255,255,255,.55), inset 0 0 0 6px rgba(43,26,18,.14);
        max-width:660px; margin:26px auto 0; overflow:hidden;
        animation:traitWinPop 1.15s cubic-bezier(.22,1,.36,1) both;
      }
      @keyframes traitWinPop{
        0%{opacity:0; transform:scale(.94) rotate(-.6deg) translateY(12px)}
        100%{opacity:1; transform:scale(1) rotate(0deg) translateY(0)}
      }
      .trait-bar{
        display:flex; align-items:center; justify-content:space-between;
        padding:8px 10px 8px 14px;
        border-bottom:3px solid var(--px-outline);
        background:rgba(138,90,0,.08);
      }
      .trait-bar-btns{display:flex; gap:8px}
      .trait-bar .win-btn{width:44px; height:44px; min-width:44px; font-size:14px}
      .trait-heart{
        display:block;
        transform-box:fill-box; transform-origin:center;
        animation:traitHeartBeat 2.4s cubic-bezier(.45,0,.55,1) infinite;
      }
      @keyframes traitHeartBeat{
        0%,100%{transform:scale(1)}
        18%{transform:scale(1.16)}
        36%{transform:scale(1)}
        54%{transform:scale(1.1)}
        72%{transform:scale(1)}
      }
      .trait-stage{
        position:relative;
        aspect-ratio:7/5;
        display:flex; align-items:center; justify-content:center;
        overflow:hidden;
      }
      /* Grup tengah: foto + topi + kue melayang lembut bersama sama */
      .trait-center{
        position:relative; z-index:3;
        width:44%; max-width:280px;
        animation:traitBob 5.8s cubic-bezier(.45,0,.55,1) infinite alternate;
      }
      @keyframes traitBob{
        from{transform:translateY(-4px) rotate(-.5deg)}
        to{transform:translateY(5px) rotate(.5deg)}
      }
      /* Lingkaran foto: nanti cukup ganti isinya dengan <img src="assets/foto-editan.jpg"> */
      .trait-photo{
        position:relative; aspect-ratio:1; border-radius:50%;
        border:4px solid var(--px-outline);
        background:#ffeec4;
        box-shadow:6px 7px 0 rgba(43,26,18,.18);
        display:flex; flex-direction:column; align-items:center; justify-content:center;
        text-align:center; gap:6px; padding:16px; overflow:hidden;
        animation:traitPhotoPop 1.05s .35s cubic-bezier(.22,1,.36,1) backwards;
      }
      @keyframes traitPhotoPop{
        0%{opacity:0; transform:scale(.78) rotate(-2.5deg)}
        100%{opacity:1; transform:scale(1) rotate(0deg)}
      }
      .trait-photo img{
        position:absolute; inset:0; width:100%; height:100%;
        object-fit:cover; border-radius:50%; display:block;
        animation:traitKenBurns 12s cubic-bezier(.45,0,.55,1) infinite alternate;
        will-change:transform;
      }
      /* Foto bundar di slide tanda tanya tidak bisa ditekan: tidak bisa
         ditahan lama sampai muncul menu simpan atau bagikan, tidak bisa
         diseret, tidak bisa diseleksi, dan tidak ada sorotan ketukan.
         Pola yang sama seperti foto-foto di halaman Seasons. Tombol
         minus, X, dan tombol kembali tidak terpengaruh karena semuanya
         berada di luar area foto. */
      #viewSecret .trait-photo img{
        -webkit-user-select:none;
        user-select:none;
        -webkit-touch-callout:none;
        -webkit-user-drag:none;
        -webkit-tap-highlight-color:transparent;
        pointer-events:none;
      }
      /* Zoom napas halus ala Ken Burns di dalam lingkaran: foto tetap penuh,
         wajah di tengah tidak pernah terpotong bingkai */
      @keyframes traitKenBurns{
        from{transform:scale(1)}
        to{transform:scale(1.08)}
      }
      /* Cahaya lembut krem kekuningan berdenyut pelan di belakang lingkaran */
      .trait-glow{
        position:absolute; z-index:0; inset:-22%; border-radius:50%;
        background:radial-gradient(circle, rgba(255,205,92,.5) 0%, rgba(255,183,92,.22) 46%, rgba(255,183,92,0) 70%);
        pointer-events:none;
        animation:traitGlowPulse 5.2s 1.65s cubic-bezier(.45,0,.55,1) infinite alternate backwards,
                  traitGlowIn 1.2s .45s cubic-bezier(.22,1,.36,1) backwards;
      }
      /* Masuk memudar ke titik awal denyut, lalu denyut mengambil alih tanpa lompatan */
      @keyframes traitGlowIn{from{opacity:0} to{opacity:.55}}
      @keyframes traitGlowPulse{
        from{opacity:.55; transform:scale(.96)}
        to{opacity:.9; transform:scale(1.05)}
      }
      /* Bintang piksel kecil yang berputar pelan mengorbit mengelilingi foto */
      .trait-orbit{
        position:absolute; z-index:1; inset:-8.5%;
        pointer-events:none;
        animation:traitOrbit 30s linear infinite;
        will-change:transform;
      }
      @keyframes traitOrbit{from{transform:rotate(0deg)} to{transform:rotate(360deg)}}
      .trait-orbit-star{
        position:absolute; width:15px; height:14px; margin:-7px 0 0 -8px;
        animation:traitStarTwinkle 4.6s cubic-bezier(.45,0,.55,1) infinite;
        animation-delay:var(--sd,0s);
      }
      .tos-1{left:50%; top:1.5%; --sd:-.7s}
      .tos-2{left:93.5%; top:74.5%; --sd:-2s}
      .tos-3{left:6.5%; top:74.5%; --sd:-3.2s}
      .trait-photo .ph-title{
        font-family:"Press Start 2P",monospace; font-weight:400;
        font-size:clamp(9px,2.6vw,12px); line-height:1.7; color:#8a5a00;
      }
      .trait-photo p{margin:0; font-size:clamp(13px,3.4vw,16px); line-height:1.35; color:#7a5a3d; max-width:15ch}
      /* Topi pesta piksel: jatuh masuk, lalu bergoyang lucu berkala */
      .trait-hat{
        position:absolute; z-index:4;
        top:-15%; right:-13%; width:44%;
        transform:rotate(16deg);
        animation:traitHatIn 1s .85s cubic-bezier(.22,1,.36,1) backwards;
      }
      @keyframes traitHatIn{
        0%{opacity:0; transform:translateY(-26px) rotate(-6deg) scale(.6)}
        100%{opacity:1; transform:translateY(0) rotate(16deg) scale(1)}
      }
      .trait-hat svg{
        display:block; width:100%; height:auto;
        filter:drop-shadow(3px 4px 0 rgba(43,26,18,.2));
        transform-origin:50% 90%;
        animation:traitHatWiggle 5s cubic-bezier(.45,0,.55,1) infinite;
      }
      @keyframes traitHatWiggle{
        0%,55%,100%{transform:rotate(0deg)}
        62%{transform:rotate(-7deg)}
        70%{transform:rotate(6deg)}
        78%{transform:rotate(-3deg)}
        86%{transform:rotate(0deg)}
      }
      .trait-center:hover .trait-hat svg{animation:traitHatJump .8s cubic-bezier(.22,1,.36,1)}
      @keyframes traitHatJump{
        0%{transform:translateY(0) rotate(0deg)}
        40%{transform:translateY(-12px) rotate(-8deg)}
        100%{transform:translateY(0) rotate(0deg)}
      }
      /* Kue piksel di kiri bawah foto: masuk memantul, lalu memantul halus terus */
      .trait-cake{
        position:absolute; z-index:4;
        bottom:-7%; left:-23%; width:47%;
        animation:traitCakeIn 1s 1.05s cubic-bezier(.22,1,.36,1) backwards;
      }
      @keyframes traitCakeIn{
        0%{opacity:0; transform:translate(-16px,14px) scale(.6) rotate(-8deg)}
        100%{opacity:1; transform:translate(0,0) scale(1) rotate(0deg)}
      }
      .trait-cake svg{
        display:block; width:100%; height:auto;
        filter:drop-shadow(3px 4px 0 rgba(43,26,18,.2));
        animation:traitCakeBounce 4.6s cubic-bezier(.45,0,.55,1) infinite alternate;
      }
      @keyframes traitCakeBounce{from{transform:translateY(0)} to{transform:translateY(-3.5px)}}
      .trait-cake .cake-flame{
        transform-box:fill-box; transform-origin:50% 100%;
        animation:traitFlame 1.5s cubic-bezier(.45,0,.55,1) infinite alternate;
      }
      @keyframes traitFlame{
        from{transform:scaleY(.92) skewX(-3deg)}
        to{transform:scaleY(1.08) skewX(3.5deg)}
      }
      /* Kata kata baik: muncul memantul satu per satu, lalu melayang pelan */
      .trait-word{
        position:absolute; z-index:2;
        font-size:clamp(16px,4.6vw,25px); line-height:1;
        color:#43301d; letter-spacing:.02em; white-space:nowrap;
        transform:rotate(var(--tilt,0deg));
        animation:traitWordPop .95s cubic-bezier(.22,1,.36,1) backwards;
        animation-delay:var(--d,.5s);
        cursor:default; user-select:none;
        transition:transform .45s cubic-bezier(.22,1,.36,1);
      }
      .trait-word:hover{transform:rotate(0deg) scale(1.1)}
      @keyframes traitWordPop{
        0%{opacity:0; transform:scale(.62) rotate(-4deg)}
        100%{opacity:1; transform:scale(1) rotate(var(--tilt,0deg))}
      }
      .trait-word .tw-in{
        display:inline-block;
        animation:traitWordFloat 5.6s cubic-bezier(.45,0,.55,1) infinite alternate;
        animation-delay:var(--fd,0s);
      }
      @keyframes traitWordFloat{
        from{transform:translateY(-2.5px) rotate(-1deg)}
        to{transform:translateY(2.5px) rotate(1deg)}
      }
      .tw-kind{top:15%; left:7%; --tilt:-3deg; --d:.55s; --fd:-.4s}
      .tw-gentle{top:31%; left:13%; --tilt:2deg; --d:.7s; --fd:-1.6s}
      .tw-hardworking{top:52%; left:3.5%; --tilt:-2deg; --d:.85s; --fd:-2.5s}
      .tw-humble{top:70%; left:10%; --tilt:3deg; --d:1s; --fd:-.9s}
      .tw-caring{top:13%; right:8%; --tilt:3deg; --d:.62s; --fd:-2.1s}
      .tw-adorable{top:28%; right:4.5%; --tilt:-2deg; --d:.78s; --fd:-1.2s}
      .tw-supportive{top:44%; right:9%; --tilt:2deg; --d:.93s; --fd:-3s}
      .tw-patient{top:59%; right:13%; --tilt:-3deg; --d:1.08s; --fd:-.2s}
      .tw-creative{top:75%; right:6%; --tilt:2deg; --d:1.23s; --fd:-1.9s}
      /* Bintang piksel warna warni berkelip di sekeliling foto */
      .trait-star{
        position:absolute; z-index:1; pointer-events:none;
        animation:traitStarTwinkle 4.6s cubic-bezier(.45,0,.55,1) infinite;
        animation-delay:var(--sd,0s);
      }
      @keyframes traitStarTwinkle{
        0%,100%{transform:scale(.82) rotate(-6deg); opacity:.75}
        50%{transform:scale(1.08) rotate(8deg); opacity:1}
      }
      .ts-1{top:5%; left:17%; width:22px; --sd:-.5s}
      .ts-2{top:2.5%; left:40%; width:18px; --sd:-1.8s}
      .ts-3{top:6%; right:21%; width:20px; --sd:-2.6s}
      .ts-4{top:24%; left:26%; width:17px; --sd:-1.1s}
      .ts-5{top:58%; left:22%; width:21px; --sd:-3s}
      .ts-6{top:50%; right:19%; width:19px; --sd:-.9s}
      .ts-7{top:70%; right:24%; width:16px; --sd:-2.2s}
      .ts-8{bottom:4%; left:54%; width:18px; --sd:-1.5s}
      /* Kilau piksel kecil di sekitar lingkaran foto */
      .trait-spark{
        position:absolute; z-index:1; width:7px; height:7px; pointer-events:none;
        animation:traitSpark 3.8s cubic-bezier(.45,0,.55,1) infinite;
        animation-delay:var(--sd,0s);
      }
      @keyframes traitSpark{
        0%,100%{transform:scale(.5) rotate(0deg); opacity:.35}
        50%{transform:scale(1) rotate(45deg); opacity:.95}
      }
      .tsp-1{top:17%; left:31%; background:#e8b93c; --sd:-.6s}
      .tsp-2{top:9%; right:31%; background:#e75480; --sd:-1.7s}
      .tsp-3{bottom:15%; left:34%; background:#5b8dd9; --sd:-2.4s}
      .tsp-4{bottom:21%; right:30%; background:#58a05c; --sd:-1.1s}
      .tsp-5{top:44%; left:27.5%; background:#ff8a3c; --sd:-2.9s}
      .tsp-6{top:38%; right:26%; background:#e8b93c; --sd:-.2s}
      /* Partikel lucu tambahan: bintang dan hati piksel kecil plus kilau
         cahaya lembut, melayang pelan naik turun di sudut dan tepi panggung
         dengan jeda dan durasi beda beda. Berada di lapisan paling belakang
         (z-index:0) supaya tidak menutupi foto, kata kata, topi, dan kue;
         geraknya hanya transform dan opacity. */
      .trait-particle{
        position:absolute; z-index:0; pointer-events:none;
        animation:traitParticleFloat var(--dur,6s) cubic-bezier(.45,0,.55,1) infinite alternate;
        animation-delay:var(--fd,0s);
        will-change:transform,opacity;
      }
      @keyframes traitParticleFloat{
        from{transform:translateY(3px) rotate(-3deg); opacity:.5}
        to{transform:translateY(-4px) rotate(3deg); opacity:.92}
      }
      .tp-star{height:auto}
      .tp-p1{top:3.5%; left:3%; width:14px; --dur:5.6s; --fd:-.8s}
      .tp-p2{top:4%; right:3.5%; width:12px; --dur:6.4s; --fd:-2.3s}
      .tp-p3{bottom:5%; left:4%; width:13px; --dur:5.9s; --fd:-1.5s}
      .tp-p4{bottom:6%; right:4.5%; width:15px; --dur:6.8s; --fd:-3.4s}
      .tp-p5{top:42%; left:1.8%; width:13px; --dur:6.1s; --fd:-2.8s}
      .tp-p6{top:36%; right:2%; width:12px; --dur:5.4s; --fd:-.4s}
      .tp-p7{bottom:3%; left:44%; width:12px; --dur:6.6s; --fd:-4.1s}
      /* Kilau cahaya: titik cahaya krem lembut berdenyut sangat pelan */
      .tp-glow{
        border-radius:50%;
        background:radial-gradient(circle, rgba(255,214,120,.5) 0%, rgba(255,196,110,.18) 55%, rgba(255,196,110,0) 72%);
        animation-name:traitParticleGlow;
      }
      @keyframes traitParticleGlow{
        from{transform:translateY(2px) scale(.92); opacity:.35}
        to{transform:translateY(-3px) scale(1.06); opacity:.6}
      }
      .tp-p8{top:9%; left:21%; width:28px; height:28px; --dur:7.2s; --fd:-1.2s}
      .tp-p9{top:11%; right:22%; width:24px; height:24px; --dur:6.5s; --fd:-3.8s}
      .tp-p10{bottom:9%; left:25%; width:26px; height:26px; --dur:7.6s; --fd:-2s}
      .tp-p11{bottom:11%; right:26%; width:22px; height:22px; --dur:5.8s; --fd:-4.6s}
      /* Gerak dikurangi: partikel diam tenang di posisi alaminya */
      @media (prefers-reduced-motion: reduce){
        .trait-particle{animation:none; opacity:.55}
        .tp-glow{opacity:.3}
      }
      /* Coretan love gambar tangan: hati garis tipis melengkung seperti
         digambar pena, dua garis lengkung halus, dan tulisan tangan "love"
         kecil, memenuhi ruang kosong jendela (terutama strip atas yang
         polos sejak judul dihapus, plus sudut sudut yang sepi) tanpa
         menutupi foto, sembilan kata, topi, kue, tombol, atau partikel.
         Warnanya pink pudar dan emas lembut menyatu dengan krem jendela.
         Muncul bertahap setelah kata kata tampil, lalu melayang dan
         berdenyut sangat pelan; geraknya hanya transform dan opacity
         dengan easing situs yang sama. */
      .trait-doodle{
        position:absolute; z-index:1; pointer-events:none;
        transform:rotate(var(--rot,0deg));
        animation:traitDoodleIn 1s cubic-bezier(.22,1,.36,1) backwards;
        animation-delay:var(--dd,1.1s);
      }
      @keyframes traitDoodleIn{
        from{opacity:0; transform:rotate(var(--rot,0deg)) scale(.72) translateY(6px)}
        to{opacity:1; transform:rotate(var(--rot,0deg)) scale(1) translateY(0)}
      }
      .trait-doodle svg{
        display:block; width:100%; height:auto; overflow:visible;
        animation:traitDoodleFloat var(--dur,6.5s) cubic-bezier(.45,0,.55,1) infinite alternate;
        animation-delay:var(--fd,0s);
        will-change:transform;
      }
      @keyframes traitDoodleFloat{
        from{transform:translateY(2px) rotate(-1.1deg) scale(1)}
        to{transform:translateY(-2.5px) rotate(1.2deg) scale(1.045)}
      }
      .trait-doodle svg path{
        fill:none; stroke-width:1.8; stroke-linecap:round; stroke-linejoin:round;
      }
      .td-pink svg path{stroke:rgba(214,106,152,.78); fill:rgba(231,84,128,.06)}
      .td-gold svg path{stroke:rgba(196,148,52,.82); fill:rgba(232,185,60,.07)}
      .td-squig svg path{fill:none}
      .td-h1{top:3%; left:25.5%; width:32px; --rot:-8deg; --dd:1.05s; --fd:-1.3s; --dur:6.8s}
      .td-h2{top:5%; right:30.5%; width:20px; --rot:11deg; --dd:1.2s; --fd:-3.1s; --dur:5.9s}
      .td-h3{bottom:4.5%; right:26%; width:24px; --rot:7deg; --dd:1.35s; --fd:-2.2s; --dur:7.3s}
      .td-h4{bottom:15.5%; left:18.5%; width:17px; --rot:-9deg; --dd:1.5s; --fd:-4.4s; --dur:6.2s}
      .td-s1{top:22%; left:3%; width:54px; --rot:-5deg; --dd:1.28s; --fd:-.8s; --dur:7.6s}
      .td-s2{bottom:2.8%; left:30%; width:74px; --rot:2deg; --dd:1.44s; --fd:-2.7s; --dur:8.2s}
      /* Tulisan tangan "love" kecil di tengah strip atas jendela */
      .td-love{
        top:2%; left:50%;
        font-family:"Caveat",cursive; font-weight:600;
        font-size:clamp(25px,5.4vw,36px); line-height:1;
        color:rgba(203,90,138,.88); letter-spacing:.01em;
        transform:translateX(-50%) rotate(-3deg);
        animation-name:traitLoveIn;
      }
      @keyframes traitLoveIn{
        from{opacity:0; transform:translateX(-50%) rotate(-3deg) scale(.78) translateY(6px)}
        to{opacity:1; transform:translateX(-50%) rotate(-3deg) scale(1) translateY(0)}
      }
      .td-love-in{
        display:inline-block;
        animation:traitDoodleFloat 7s cubic-bezier(.45,0,.55,1) infinite alternate;
        animation-delay:-1.8s;
        will-change:transform;
      }
      /* Gerak dikurangi: coretan tampil diam di posisi alaminya */
      @media (prefers-reduced-motion: reduce){
        .trait-doodle{animation:none}
        .trait-doodle svg, .td-love-in{animation:none}
      }
      /* Semburan confetti piksel sekali saat jendela dibuka */
      .trait-confetti{
        position:absolute; z-index:5; left:50%; top:44%;
        width:8px; height:8px; pointer-events:none; opacity:0;
        animation:traitConfetti 2.1s cubic-bezier(.22,1,.36,1) both;
      }
      @keyframes traitConfetti{
        0%{opacity:0; transform:translate(-50%,-50%) rotate(0deg) scale(.6)}
        12%{opacity:1}
        100%{opacity:0; transform:translate(calc(-50% + var(--cx,0px)), calc(-50% + var(--cy,0px))) rotate(var(--cr,220deg)) scale(1)}
      }
      /* ---------- Game di bawah jendela tanda tanya: Thousand Sunny Block Runner ----------
         Menggantikan memory match. Bingkai krem perkamen dengan border pixel
         tegas menampung HUD kue, kanvas game, deretan 10 hati, dan dua
         tombol aksi A dan B. Kapal, Luffy, item, dan rintangan digambar piksel di kanvas
         lewat JS, jadi tidak ada aset gambar baru. Semua tombol kontrol
         minimal 44px dengan jarak minimal 8px, efek tekannya scale(.96)
         selama 80ms, dan geraknya cuma transform/opacity. */
      .run-game{max-width:520px; margin:26px auto 0; padding:0 2px; user-select:none; -webkit-user-select:none; container-type:inline-size}
      #viewSecret.active .run-game{animation:openRise .5s .45s cubic-bezier(.22,1,.36,1) backwards}
      /* Judul game selalu satu baris: ukuran huruf mengikuti lebar wadah
         game (cqw) supaya 27 karakter Press Start 2P muat penuh tanpa
         terpotong, di HP sempit maupun laptop */
      .run-title{
        font-family:"Press Start 2P",monospace; font-weight:400;
        font-size:clamp(10px,3.2vw,18px);
        font-size:clamp(9px,3.5cqw,18px);
        white-space:nowrap; line-height:1.5; text-align:center;
        margin:0 0 12px; color:var(--ink-light);
        text-shadow:3px 3px 0 rgba(0,0,0,.35);
      }
      @media (prefers-color-scheme: light){ .run-title{text-shadow:3px 3px 0 rgba(51,34,15,.18)} }
      .run-frame{
        position:relative;
        background:#F5E6C8; border:3px solid #1E1B18;
        box-shadow:5px 5px 0 rgba(0,0,0,.25), inset 0 3px 0 rgba(255,255,255,.5), inset 0 -3px 0 rgba(30,27,24,.12);
        padding:10px;
      }
      /* HUD atas: ikon kue, penghitung kue menuju 17, progress bar retro, poin */
      .run-hud{display:flex; align-items:center; gap:10px; margin-bottom:8px}
      .run-cake-ico{flex:0 0 auto; display:flex}
      .run-cake-ico svg{display:block; image-rendering:pixelated}
      .run-count{
        font-family:"Press Start 2P",monospace; font-size:12px; color:#1E1B18;
        white-space:nowrap;
      }
      .run-progress{
        flex:1 1 auto; height:16px; min-width:56px;
        border:3px solid #1E1B18; background:#fff8e6;
        box-shadow:inset 0 2px 0 rgba(30,27,24,.15);
      }
      .run-progress i{
        display:block; height:100%; width:0%;
        background:repeating-linear-gradient(90deg, #FFD166 0 12px, #f0b93f 12px 24px);
        transition:width .35s cubic-bezier(.22,1,.36,1);
      }
      .run-score{
        font-family:"Press Start 2P",monospace; font-size:10px; color:#1E1B18; white-space:nowrap;
      }
      /* Tombol bisu musik 8-bit di HUD: area sentuh 44x44px, efek tekan
         scale(.96) 80ms sama seperti tombol A dan B */
      .run-snd-btn{
        flex:0 0 auto; width:44px; height:44px; min-width:44px;
        display:inline-flex; align-items:center; justify-content:center;
        background:#fff8e6; color:#1E1B18;
        border:3px solid #1E1B18; border-radius:0;
        box-shadow:inset 0 3px 0 rgba(255,255,255,.6), inset 0 -3px 0 rgba(30,27,24,.15), 2px 2px 0 rgba(0,0,0,.2);
        transition:transform 80ms cubic-bezier(.22,1,.36,1), background .2s cubic-bezier(.22,1,.36,1);
      }
      .run-snd-btn:hover{background:#fffdf2}
      .run-snd-btn:active{transform:scale(.96)}
      .run-snd-btn svg{display:block; image-rendering:pixelated}
      .run-snd-btn .snd-off-ico{display:none}
      .run-snd-btn.muted .snd-on-ico{display:none}
      .run-snd-btn.muted .snd-off-ico{display:block}
      /* Panggung kanvas dan lapisan mulai / karam di atasnya */
      .run-stage{position:relative; border:3px solid #1E1B18; background:#38B6FF; overflow:hidden}
      .run-stage canvas{display:block; width:100%; height:auto; image-rendering:pixelated; touch-action:none}
      /* Anti zoom tidak sengaja: klik/ketuk dua kali pada background tidak
         lagi men-zoom halaman (di mode Desktop slide game ini kejadian saat
         Kevin tidak sengaja klik dua kali di background). touch-action:
         manipulation mematikan zoom double-tap/double-click bawaan browser
         tapi pinch zoom dan scroll tetap jalan seperti biasa. Berlaku di
         seluruh situs, ditegaskan lagi khusus di slide game; teks di slide
         game juga tidak bisa terseleksi oleh double-click. Kontrol game
         tidak berubah: kanvas dan tombol A/B tetap touch-action:none. */
      html, body{touch-action:manipulation}
      #viewSecret{touch-action:manipulation; -webkit-user-select:none; user-select:none}
      #viewSecret .run-stage{touch-action:manipulation}
      .run-overlay{
        position:absolute; inset:0; z-index:4;
        display:flex; flex-direction:column; align-items:center; justify-content:center; gap:10px;
        text-align:center; padding:16px;
        background:rgba(245,230,200,.9);
        animation:runModalFade .3s cubic-bezier(.22,1,.36,1) both;
      }
      .run-overlay[hidden]{display:none}
      .run-ov-title{
        font-family:"Press Start 2P",monospace; font-size:clamp(11px,3.4vw,15px); line-height:1.7;
        color:#1E1B18; margin:0;
      }
      .run-ov-text{
        font-family:"VT323",monospace; font-size:19px; line-height:1.45; color:#3a2a16;
        margin:0; max-width:42ch;
      }
      .run-btn-main{
        background:#FFD166; color:#1E1B18;
        border:3px solid #1E1B18; border-radius:0;
        box-shadow:inset 0 -5px 0 rgba(30,27,24,.22), inset 0 4px 0 rgba(255,255,255,.55), 3px 3px 0 rgba(0,0,0,.25);
        min-height:48px; padding:10px 22px;
        font-family:"Press Start 2P",monospace; font-size:12px; line-height:1.5;
      }
      .run-btn-main:hover{background:#ffda85}
      .run-btn-sub{
        background:#7d7d7d; color:#fff;
        border:3px solid #1E1B18; border-radius:0;
        text-shadow:2px 2px 0 rgba(0,0,0,.45);
        box-shadow:inset 0 -4px 0 rgba(0,0,0,.3), inset 0 3px 0 rgba(255,255,255,.32), 3px 3px 0 rgba(0,0,0,.2);
        min-height:48px; padding:10px 18px;
        font-family:"Press Start 2P",monospace; font-size:11px; line-height:1.5;
      }
      .run-btn-sub:hover{background:#8f8f8f}
      /* Deretan 10 hati Minecraft di tengah bawah kanvas */
      .run-hearts{display:flex; justify-content:center; gap:3px; padding:9px 0 3px}
      .run-hearts svg{display:block}
      /* Kontrol virtual: hanya dua tombol aksi, B di kiri untuk dash
         dan A di kanan untuk lompat dan lompatan ganda */
      .run-controls{
        display:flex; justify-content:space-between; align-items:flex-start;
        gap:12px; padding:8px 2px 2px;
      }
      .run-ctrl-side{display:flex; flex-direction:column; align-items:center; gap:6px}
      .run-ctrl-label{
        font-family:"Press Start 2P",monospace; font-size:9px; line-height:1.4;
        letter-spacing:.1em; color:#5d4423;
      }
      .run-ab-btn{
        border:3px solid #1E1B18; border-radius:50%;
        display:inline-flex; align-items:center; justify-content:center;
        font-family:"Press Start 2P",monospace; line-height:1;
        touch-action:none; -webkit-tap-highlight-color:transparent;
        transition:transform 80ms cubic-bezier(.22,1,.36,1), background .2s cubic-bezier(.22,1,.36,1);
        box-shadow:inset 0 4px 0 rgba(255,255,255,.35), inset 0 -5px 0 rgba(0,0,0,.25), 3px 3px 0 rgba(0,0,0,.2);
      }
      .run-ab-btn:active{transform:scale(.96)}
      .run-btn-a{width:60px; height:60px; min-width:60px; background:#E63946; color:#fff; font-size:16px}
      .run-btn-b{width:52px; height:52px; min-width:52px; background:#FFD166; color:#1E1B18; font-size:13px}
      .run-hint{
        margin:8px 2px 0; text-align:center;
        font-size:17px; line-height:1.45; color:#5d4423;
      }
      /* Modal peti harta karun / wanted poster saat 17 kue terkumpul */
      .run-modal{
        position:fixed; inset:0; z-index:110;
        display:flex; align-items:center; justify-content:center;
        padding:14px; background:rgba(30,27,24,.55);
        animation:runModalFade .3s cubic-bezier(.22,1,.36,1) both;
      }
      /* Latar gelap modal memudar masuk lembut bersama kartunya, bukan
         muncul sekejap: hanya opacity, durasi pendek, rasa modalnya tetap */
      @keyframes runModalFade{
        from{opacity:0}
        to{opacity:1}
      }
      .run-modal[hidden]{display:none}
      /* Kartu modal tidak pernah di-scroll: tingginya mengikuti isi
         (maksimum setinggi layar), dan foto tampil pada proporsi aslinya,
         jadi bingkai, foto, teks, dan tombol muat utuh dalam satu layar */
      .run-modal-card{
        background:#F5E6C8; border:4px solid #1E1B18; border-radius:20px;
        box-shadow:6px 6px 0 rgba(0,0,0,.35), inset 0 0 0 3px rgba(160,106,53,.35);
        max-width:336px; width:100%; max-height:100%; overflow:hidden;
        display:flex; flex-direction:column; align-items:center;
        text-align:center; padding:18px 18px 16px;
        animation:runModalIn .55s cubic-bezier(.22,1,.36,1) both;
      }
      @keyframes runModalIn{
        from{opacity:0; transform:translateY(14px) scale(.94)}
        to{opacity:1; transform:translateY(0) scale(1)}
      }
      .run-modal-label{
        flex:none;
        font-family:"Press Start 2P",monospace; font-size:10px; letter-spacing:.14em;
        color:#8a5a00; margin:0 0 10px;
      }
      /* Foto kemenangan: foto grup tegak tampil utuh (tanpa crop) di dalam
         bingkai emas piksel berlapis ala wanted poster, dan bisa ditekan
         untuk dibuka penuh di lightbox */
      .run-modal-photo-btn{
        flex:none;
        display:flex; flex-direction:column; align-items:center;
        margin:0 auto 10px; padding:0; width:min(236px,86%);
        background:none; border:0; cursor:pointer;
        -webkit-tap-highlight-color:transparent;
        touch-action:manipulation;
        transition:transform .25s cubic-bezier(.22,1,.36,1);
      }
      /* Tidak ada sorotan/kilatan ketuk bawaan browser di tombol foto
         maupun isi bingkainya saat ditekan untuk membuka foto penuh */
      .run-modal-photo-btn *{-webkit-tap-highlight-color:transparent}
      .run-modal-photo-btn:hover{transform:translateY(-2px)}
      .run-modal-photo-btn:active{transform:scale(.98)}
      .run-modal-photo-btn:focus-visible{outline:3px solid #8a5a00; outline-offset:3px}
      /* Bingkai emas yang lebih cute dan elegan: emas lembut hangat dengan
         gradasi halus dari atas ke bawah, sudut lebih membulat, garis dalam
         ganda yang rapi dan seimbang, tanpa efek cahaya di sisi bingkai —
         tidak ada kilau menyusuri tepi dan tidak ada cahaya berdenyut di
         sekelilingnya; yang bergerak hanya hiasan sudutnya */
      .run-modal-frame{
        flex:none; width:100%;
        display:flex; align-items:center; justify-content:center;
        position:relative; padding:9px; border-radius:22px;
        background:linear-gradient(168deg,#E8BE63 0%,#DCAE4C 55%,#D2A03C 100%);
        border:3px solid #2A211A;
        box-shadow:
          inset 0 0 0 2px #FBEFC9,
          inset 0 0 0 4px rgba(255,255,255,.28),
          inset 0 0 0 6px #A97A2B,
          4px 5px 0 rgba(42,33,26,.28);
      }
      /* Hiasan sudut bingkai: hati dan bintang piksel mungil yang berdenyut
         dan melayang sangat pelan, jeda bergantian seperti ditempel tangan */
      .run-frame-deco{
        position:absolute; z-index:2; width:15px; pointer-events:none;
        filter:drop-shadow(1px 1px 0 rgba(42,33,26,.22));
        animation:runDecoBob 3.4s ease-in-out infinite alternate;
      }
      .run-frame-deco svg{display:block; width:100%; height:auto}
      .run-frame-deco--tl{top:-7px; left:-7px; color:#f4a3c0; rotate:-8deg}
      .run-frame-deco--tr{top:-6px; right:-6px; width:13px; color:#FFD98A; rotate:10deg; animation-delay:-1.2s; animation-duration:3.9s}
      .run-frame-deco--bl{bottom:-6px; left:-6px; width:13px; color:#FFD98A; rotate:-10deg; animation-delay:-2.1s; animation-duration:3.7s}
      .run-frame-deco--br{bottom:-7px; right:-7px; color:#ef7fa8; rotate:8deg; animation-delay:-.6s; animation-duration:4.1s}
      @keyframes runDecoBob{
        from{transform:translateY(0) scale(1)}
        to{transform:translateY(-3px) scale(1.12)}
      }
      /* Foto tampil pada proporsi aslinya (tegak 1040x1560): lebar penuh
         mengikuti bingkai, tinggi otomatis dari rasio alami, tanpa stretch.
         max-height + object-fit contain hanya pengaman di layar sangat
         pendek: foto mengecil utuh, tidak pernah gepeng */
      .run-modal-photo{
        position:relative; z-index:0;
        width:100%; height:auto; max-width:100%; max-height:54dvh;
        aspect-ratio:1040/1560;
        object-fit:contain;
        display:block; background:#f3e5c3;
        border:2px solid #2A211A; border-radius:13px;
        -webkit-user-drag:none; user-select:none;
        -webkit-tap-highlight-color:transparent;
      }
      .run-modal-hint{
        flex:none;
        display:block; margin-top:9px;
        font-family:"VT323",monospace; font-size:16px; line-height:1.2; color:#5d4423;
      }
      /* Lightbox foto penuh: latar gelap, foto utuh sebesar mungkin,
         tombol tutup besar di kanan atas. Lightbox tampil SEKETIKA tanpa
         animasi memudar: fade masuk dari transparan membuat lapisan
         terang modal di belakangnya menyapu layar sesaat, terbaca
         sebagai kilatan cahaya saat bingkai ditekan — jadi dibuka
         langsung penuh, dan tidak ada sorotan ketuk di seluruhnya */
      .run-lightbox{
        position:fixed; inset:0; z-index:120;
        display:flex; align-items:center; justify-content:center;
        padding:52px 16px 20px;
        background:rgba(24,19,14,.93);
        -webkit-tap-highlight-color:transparent;
      }
      .run-lightbox *{-webkit-tap-highlight-color:transparent}
      .run-lightbox[hidden]{display:none}
      .run-lightbox-frame{
        display:block; position:relative; max-width:min(94vw,560px); max-height:100%;
        padding:9px; border-radius:22px;
        background:linear-gradient(168deg,#E8BE63 0%,#DCAE4C 55%,#D2A03C 100%);
        border:3px solid #2A211A;
        box-shadow:
          inset 0 0 0 2px #FBEFC9,
          inset 0 0 0 4px rgba(255,255,255,.28),
          inset 0 0 0 6px #A97A2B,
          6px 6px 0 rgba(0,0,0,.45);
      }
      .run-lightbox-img{
        display:block; position:relative; z-index:0;
        max-width:100%; max-height:calc(100dvh - 96px);
        width:auto; height:auto; object-fit:contain;
        background:#f3e5c3;
        border:2px solid #2A211A; border-radius:13px;
        -webkit-user-drag:none; user-select:none;
      }
      .run-lightbox-close{
        position:absolute; top:12px; right:12px;
        width:48px; height:48px; min-width:48px;
        display:inline-flex; align-items:center; justify-content:center;
        background:#b8352a; color:#fff;
        border:3px solid #1E1B18; border-radius:0;
        box-shadow:3px 3px 0 rgba(0,0,0,.4), inset 0 3px 0 rgba(255,255,255,.25);
        font-family:"Press Start 2P",monospace; font-size:16px; line-height:1;
        cursor:pointer; -webkit-tap-highlight-color:transparent;
      }
      .run-lightbox-close:active{transform:scale(.95)}
      .run-modal-title{
        flex:none;
        font-family:"Press Start 2P",monospace; font-weight:400;
        font-size:15px; line-height:1.7; color:#1E1B18; margin:0 0 10px;
      }
      .run-modal-btns{flex:none; display:flex; gap:10px; justify-content:center; flex-wrap:wrap}
      /* Gerak dikurangi: tanpa animasi masuk dan tanpa transisi progress */
      @media (prefers-reduced-motion: reduce){
        #viewSecret.active .run-game{animation:none}
        .run-modal, .run-modal-card, .run-overlay, .run-lightbox{animation:none}
        .run-frame-deco{animation:none}
        .run-progress i{transition:none}
      }
      #viewFoto, #viewNotes, #viewSecret, #viewCake{position:relative}
      /* Fokus pindah ke section tanda tanya saat dibuka (judulnya sudah
         dihapus): tanpa garis fokus yang mengganggu tampilan */
      #viewSecret:focus{outline:none}
      #viewFoto > *:not(.sky-decor), #viewNotes > *:not(.sky-decor), #viewSecret > *:not(.sky-decor), #viewCake > *:not(.sky-decor){position:relative; z-index:1}


      /* ---------- Kontrol jendela di halaman yang dibuka: minus dan X di kanan atas ---------- */
      .win-bar{
        display:flex; align-items:center; justify-content:flex-end; gap:8px;
        margin:14px 0 0; position:relative; z-index:5;
      }
      .win-btn{
        width:46px; height:46px; min-width:46px;
        display:inline-flex; align-items:center; justify-content:center;
        background:var(--card); color:var(--ink-light);
        border:3px solid var(--px-outline); border-radius:0;
        box-shadow:4px 4px 0 rgba(0,0,0,.22), inset 0 4px 0 rgba(255,255,255,.22), inset 0 -4px 0 rgba(0,0,0,.25);
        font-family:"Press Start 2P",monospace; font-size:15px; line-height:1;
      }
      .win-btn{transition:transform .45s cubic-bezier(.22,1,.36,1), background .45s cubic-bezier(.22,1,.36,1), box-shadow .45s cubic-bezier(.22,1,.36,1)} .win-btn:hover{transform:translateY(-1.5px)}
      .win-btn.win-close-btn{background:#b8352a; color:#fff; text-shadow:2px 2px 0 rgba(0,0,0,.35)}
      .win-btn.win-min-btn{background:#7d7d7d; color:#fff; text-shadow:2px 2px 0 rgba(0,0,0,.35)}
      .pl-win-bar{margin:6px 0 8px}

      /* ---------- Slide playlist: layar hijau piksel seperti gambar ---------- */
      .to-playlist-row{display:flex; flex-wrap:wrap; gap:12px; justify-content:center; align-items:center; margin:28px 0 2px}
      .btn-playlist{
        display:inline-flex; align-items:center; gap:10px;
        background:var(--grass); color:#fff;
        border:3px solid var(--px-outline); border-radius:0;
        text-shadow:2px 2px 0 rgba(0,0,0,.45);
        box-shadow:inset 0 -5px 0 rgba(0,0,0,.3), inset 0 4px 0 rgba(255,255,255,.32), 4px 4px 0 rgba(0,0,0,.2);
        min-height:52px; padding:12px 24px; font-weight:600; font-size:20px;
      }
      .btn-playlist:hover{background:var(--grass-light)}
      .pl-screen{
        --pl-bg:#8ba653;
        --pl-ink:#131a07;
        --pl-hi:#a9c46a;
        background:var(--pl-bg);
        color:var(--pl-ink);
        border:4px solid var(--px-outline);
        box-shadow:8px 8px 0 rgba(0,0,0,.25);
        margin-top:clamp(10px,2.5vw,16px);
        padding:clamp(11px,3vw,16px) clamp(14px,4vw,24px) 12px;
      }
      #viewPlaylist.active .pl-screen{animation:plScreenIn 1.05s cubic-bezier(.22,1,.36,1) both}
      @keyframes plScreenIn{
        from{opacity:0; transform:translateY(14px) scale(.99)}
        to{opacity:1; transform:translateY(0) scale(1)}
      }
      .pl-status{
        display:flex; align-items:center; gap:14px;
        margin-bottom:2px;
      }
      .pl-status svg{display:block}
      .pl-clock{
        margin-left:auto;
        font-family:"Press Start 2P",monospace; font-size:13px; letter-spacing:.04em;
      }
      .pl-brand{
        display:flex; align-items:center; justify-content:center; gap:12px;
        margin:0 0 8px;
      }
      .pl-logo{
        width:46px; height:46px; flex:0 0 auto;
        image-rendering:pixelated;
        filter:drop-shadow(3px 3px 0 rgba(19,26,7,.28));
        animation:plLogoFloat 4.6s cubic-bezier(.45,0,.55,1) infinite alternate;
      }
      @keyframes plLogoFloat{
        from{transform:translateY(0) rotate(-2deg)}
        to{transform:translateY(-3px) rotate(2deg)}
      }
      .pl-title{
        font-family:"Press Start 2P",monospace; font-weight:400;
        font-size:clamp(14px,4.2vw,18px); letter-spacing:.14em;
        text-align:center; margin:0;
      }
      .pl-list{display:flex; flex-direction:column; gap:1px; margin-bottom:10px}
      .pl-track{
        display:flex; align-items:baseline; gap:10px; width:100%;
        background:transparent; color:var(--pl-ink);
        font-family:"VT323",monospace; font-size:clamp(18px,4.9vw,21px);
        letter-spacing:.03em; text-align:left;
        min-height:36px; padding:3px 10px;
        transition:background .35s cubic-bezier(.22,1,.36,1), color .35s cubic-bezier(.22,1,.36,1), transform .25s cubic-bezier(.22,1,.36,1);
      }
      .pl-track:hover{background:rgba(19,26,7,.12)}
      .pl-track:active{transform:scale(.985)}
      .pl-track .pl-name{flex:1 1 auto; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
      .pl-track .pl-dur{flex:0 0 auto}
      .pl-track.selected{background:var(--pl-ink); color:var(--pl-hi)}
      .pl-track.selected:hover{background:var(--pl-ink)}
      .pl-art{
        width:min(136px,36vw); aspect-ratio:1/1; margin:0 auto 10px;
        border:3px solid var(--pl-ink);
        display:flex; align-items:center; justify-content:center;
      }
      .pl-art svg{display:none; image-rendering:pixelated; max-width:84%; max-height:84%}
      .pl-art svg.on{display:block; animation:artIn .85s cubic-bezier(.22,1,.36,1) both}
      .pl-art img{display:none; width:100%; height:100%; object-fit:cover; object-position:center}
      .pl-art img.on{display:block; animation:artIn .85s cubic-bezier(.22,1,.36,1) both}
      .pl-art:has(img.on){overflow:hidden; padding:0}
      @keyframes artIn{
        from{opacity:0; transform:scale(.92)}
        to{opacity:1; transform:scale(1)}
      }
      .pl-progress{
        position:relative; height:22px; cursor:pointer;
        display:flex; align-items:center;
      }
      .pl-progress::before{
        content:""; position:absolute; left:0; right:0; top:50%;
        height:5px; transform:translateY(-50%);
        background:rgba(19,26,7,.28);
        box-shadow:0 2px 0 rgba(19,26,7,.55);
      }
      .pl-progress i{
        position:absolute; left:0; top:50%; height:5px; width:100%;
        transform:translateY(-50%) scaleX(0);
        transform-origin:left center;
        background:var(--pl-ink);
        will-change:transform;
      }
      .pl-knob{
        position:absolute; top:50%; left:0%;
        width:17px; height:17px; transform:translate(-50%,-50%);
        background:var(--pl-ink);
        will-change:transform;
      }
      /* Baris lagu yang sedang dipilih: judul lengkap + status putar di bawah artwork */
      .pl-now{
        display:flex; flex-direction:column; align-items:center; gap:6px;
        text-align:center; margin:0 auto 10px; max-width:46ch;
      }
      .pl-now-title{
        font-size:19px; line-height:1.3; letter-spacing:.04em; font-weight:600;
      }
      .pl-now-meta{
        display:flex; flex-wrap:wrap; gap:8px; align-items:center; justify-content:center;
        font-size:16px; letter-spacing:.05em;
      }
      .pl-state{opacity:.85}
      /* Indikator equalizer kecil di baris lagu terpilih, bergerak hanya saat lagu diputar */
      .pl-eq{
        display:none; align-items:flex-end; gap:2.5px; height:14px; flex:0 0 auto;
      }
      .pl-track.selected .pl-eq{display:inline-flex}
      /* Batang equalizer digerakkan dengan transform scaleY (kompositor),
         bukan height, supaya denyutnya mulus 60fps tanpa layout ulang
         di tiap frame seperti animasi height sebelumnya */
      .pl-eq i{width:3.5px; height:14px; background:var(--pl-hi); display:block; transform:scaleY(.36); transform-origin:50% 100%}
      .pl-screen.is-playing .pl-eq i{animation:plEq .72s cubic-bezier(.45,0,.55,1) infinite alternate}
      .pl-screen.is-playing .pl-eq i:nth-child(2){animation-delay:-.24s; animation-duration:.58s}
      .pl-screen.is-playing .pl-eq i:nth-child(3){animation-delay:-.48s; animation-duration:.84s}
      @keyframes plEq{
        from{transform:scaleY(.29)}
        to{transform:scaleY(1)}
      }
      @media (prefers-reduced-motion: reduce){
        .pl-screen.is-playing .pl-eq i{animation:none; transform:none; height:9px}
      }
      .pl-time{
        display:flex; justify-content:space-between;
        margin:0 0 2px; font-size:17px; letter-spacing:.06em;
      }
      .pl-controls{
        display:flex; align-items:center; justify-content:space-between;
        max-width:280px; margin:0 auto;
      }
      .pl-ctrl{
        width:50px; height:50px; min-width:50px;
        display:inline-flex; align-items:center; justify-content:center;
        background:transparent; color:var(--pl-ink);
        transition:background .3s cubic-bezier(.22,1,.36,1), transform .34s cubic-bezier(.33,1,.68,1);
        will-change:transform;
      }
      .pl-ctrl:hover{background:rgba(19,26,7,.12)}
      .pl-ctrl:active{transform:scale(.88); transition-duration:.12s}
      .pl-ctrl svg{display:block}
      /* Ikon skip meluncur pelan mengikuti arahnya saat disentuh hover */
      #plNext svg, #plPrev svg{transition:transform .3s cubic-bezier(.33,1,.68,1)}
      #plNext:hover svg{transform:translateX(2.5px)}
      #plPrev:hover svg{transform:translateX(-2.5px)}
      /* Ketukan skip: ikon meluncur kecil searah skip lalu kembali mengalir */
      #plNext.skip-tap svg{animation:skipNextTap .38s cubic-bezier(.22,1,.36,1)}
      #plPrev.skip-tap svg{animation:skipPrevTap .38s cubic-bezier(.22,1,.36,1)}
      @keyframes skipNextTap{
        0%{transform:translateX(0) rotate(0deg) scale(1)}
        35%{transform:translateX(6px) rotate(4deg) scale(.93)}
        100%{transform:translateX(0) rotate(0deg) scale(1)}
      }
      @keyframes skipPrevTap{
        0%{transform:translateX(0) rotate(0deg) scale(1)}
        35%{transform:translateX(-6px) rotate(-4deg) scale(.93)}
        100%{transform:translateX(0) rotate(0deg) scale(1)}
      }
      /* Tombol play ke pause berubah dengan morph lembut: kedua ikon bertumpuk
         di tengah tombol yang sama, lalu saling silang memudar sambil
         mengecil, membesar, dan berputar sedikit. Murni transform dan opacity. */
      #plPlay{position:relative}
      #plPlay:active{transform:scale(.9)}
      #plPlay svg{
        position:absolute; left:50%; top:50%;
        transition:opacity .26s cubic-bezier(.22,1,.36,1), transform .36s cubic-bezier(.33,1,.68,1);
        will-change:transform, opacity;
      }
      #plPlayIcon{opacity:1; transform:translate(-50%,-50%) scale(1) rotate(0deg)}
      #plPauseIcon{opacity:0; transform:translate(-50%,-50%) scale(.5) rotate(-16deg); pointer-events:none}
      #plPlay.show-pause #plPlayIcon{opacity:0; transform:translate(-50%,-50%) scale(.5) rotate(16deg); pointer-events:none}
      #plPlay.show-pause #plPauseIcon{opacity:1; transform:translate(-50%,-50%) scale(1) rotate(0deg)}
      .pl-screen.is-playing #plPlay{background:rgba(19,26,7,.10)}
      /* Pergantian lagu lewat skip: artwork dan judul mengalir masuk dari
         arah skipnya, fade plus geser halus selama 380ms */
      .pl-screen.nav-next .pl-art svg.on,
      .pl-screen.nav-next .pl-art img.on{animation:artInNext .38s cubic-bezier(.22,1,.36,1) both}
      .pl-screen.nav-prev .pl-art svg.on,
      .pl-screen.nav-prev .pl-art img.on{animation:artInPrev .38s cubic-bezier(.22,1,.36,1) both}
      @keyframes artInNext{
        from{opacity:0; transform:translateX(18px) scale(.96)}
        to{opacity:1; transform:translateX(0) scale(1)}
      }
      @keyframes artInPrev{
        from{opacity:0; transform:translateX(-18px) scale(.96)}
        to{opacity:1; transform:translateX(0) scale(1)}
      }
      .pl-screen.nav-next .pl-now-title{animation:titleInNext .38s cubic-bezier(.22,1,.36,1) both}
      .pl-screen.nav-prev .pl-now-title{animation:titleInPrev .38s cubic-bezier(.22,1,.36,1) both}
      @keyframes titleInNext{
        from{opacity:0; transform:translateX(14px)}
        to{opacity:1; transform:translateX(0)}
      }
      @keyframes titleInPrev{
        from{opacity:0; transform:translateX(-14px)}
        to{opacity:1; transform:translateX(0)}
      }
      @media (prefers-reduced-motion: reduce){
        #plPlay svg, #plNext svg, #plPrev svg{transition:none}
        #plNext.skip-tap svg, #plPrev.skip-tap svg,
        .pl-screen.nav-next .pl-art svg.on, .pl-screen.nav-next .pl-art img.on,
        .pl-screen.nav-prev .pl-art svg.on, .pl-screen.nav-prev .pl-art img.on,
        .pl-screen.nav-next .pl-now-title, .pl-screen.nav-prev .pl-now-title{animation:none}
        .pl-ctrl:active, #plPlay:active{transform:none}
      }
      .pl-back-row{text-align:center; margin-top:6px}
      .pl-back{
        background:transparent; color:var(--pl-ink);
        border:3px solid var(--pl-ink);
        min-height:44px; padding:8px 20px;
        font-family:"VT323",monospace; font-size:18px; font-weight:600; letter-spacing:.05em;
        transition:background .25s cubic-bezier(.22,1,.36,1);
      }
      .pl-back:hover{background:rgba(19,26,7,.12)}
      #viewPlaylist{padding-bottom:16px}
      .pl-audio-hidden{position:absolute; width:1px; height:1px; opacity:0; pointer-events:none; overflow:hidden}

      /* ---------- Kalender piksel: bar atas merah, halaman krem ---------- */
      .pxcal{
        width:min(230px,62vw);
        margin:46px auto 6px;
        position:relative;
        animation:calFloat 7s cubic-bezier(.22,1,.36,1) infinite;
      }
      @keyframes calFloat{
        0%,100%{transform:translateY(0) rotate(-.55deg)}
        50%{transform:translateY(-6px) rotate(.55deg)}
      }
      .pxcal::before,.pxcal::after{
        content:""; position:absolute; inset:0;
        border:4px solid #241708;
      }
      .pxcal::before{background:#8a8a4d; transform:translate(-6px,8px) rotate(-.8deg); z-index:0}
      .pxcal::after{background:#686c38; transform:translate(6px,13px) rotate(.7deg); z-index:0}
      .pxcal-rings{
        display:flex; justify-content:space-evenly;
        position:relative; z-index:3; margin-bottom:-8px; padding:0 14px;
      }
      .pxcal-rings i{
        width:11px; height:22px; display:block;
        background:linear-gradient(180deg,#c9c9c9,#7c7c7c 60%,#5f5f5f);
        border:3px solid #241708;
        box-shadow:inset 0 3px 0 rgba(255,255,255,.5);
      }
      .pxcal-body{
        position:relative; z-index:2;
        border:4px solid #241708;
        box-shadow:7px 7px 0 rgba(0,0,0,.22);
        transform:rotate(-1.4deg);
        overflow:hidden;
      }
      .pxcal-top{
        background:#b8352a; color:#fdf3d0;
        font-family:"Press Start 2P",monospace; font-size:21px;
        letter-spacing:.14em; text-align:center;
        padding:13px 8px 11px;
        border-bottom:4px solid #7e231b;
        box-shadow:inset 0 5px 0 rgba(255,255,255,.22), inset 0 -5px 0 rgba(0,0,0,.18);
        text-shadow:2px 2px 0 rgba(0,0,0,.28);
      }
      .pxcal-page{
        background:#f7ecc9; text-align:center;
        padding:16px 10px 18px;
        box-shadow:inset 0 8px 0 rgba(176,140,60,.18);
      }
      .pxcal-month{
        display:block; font-family:"Press Start 2P",monospace;
        font-size:20px; color:#b8352a; letter-spacing:.34em; text-indent:.34em;
        margin-bottom:12px;
      }
      .pxcal-day{
        display:block; font-family:"Press Start 2P",monospace;
        font-size:56px; line-height:1.1; color:#c23a2c;
        text-shadow:4px 4px 0 rgba(126,35,27,.35);
        margin-bottom:10px;
        transform-origin:center;
        animation:dayPop 5.2s cubic-bezier(.22,1,.36,1) infinite;
      }
      /* Angka 14 sesekali memantul kecil dengan lembut: diam lama, satu pop
         halus, lalu berayun sangat kecil sebelum diam lagi */
      @keyframes dayPop{
        0%,70%,100%{transform:scale(1)}
        78%{transform:scale(1.09)}
        86%{transform:scale(.985)}
        93%{transform:scale(1.02)}
      }
      /* Bintang piksel kecil berkelip di sekeliling kalender */
      .pxcal-star{
        position:absolute; z-index:4; display:block;
        image-rendering:pixelated; pointer-events:none;
        animation:calTwinkle 4.6s cubic-bezier(.22,1,.36,1) infinite;
      }
      .pxcal-star--1{width:22px; top:10px; left:-21px}
      .pxcal-star--2{width:15px; top:-9px; right:-13px; animation-delay:-1.7s; animation-duration:5.4s}
      .pxcal-star--3{width:18px; bottom:0; right:-23px; animation-delay:-3.1s; animation-duration:4s}
      @keyframes calTwinkle{
        0%,100%{opacity:.25; transform:scale(.78) rotate(-6deg)}
        50%{opacity:.95; transform:scale(1.05) rotate(8deg)}
      }
      .pxcal-dow{
        display:block; font-size:19px; color:#7a5a3d;
        letter-spacing:.28em; text-indent:.28em; text-transform:uppercase;
      }

      /* ---------- Notes Minecraft: kertas bergaris dengan tepi sobek ---------- */
      .notes{
        position:relative;
        overflow:visible;
        /* Miring alami kertas diatur lewat properti rotate sendiri, dan napas
           halusnya menganimasikan properti yang sama, jadi tidak bentrok
           dengan transform animasi masuk (noteCardIn) maupun hover */
        rotate:-.7deg;
        animation:noteBreath 9s cubic-bezier(.22,1,.36,1) infinite alternate;
        padding-left:54px;
        padding-bottom:64px;
        background:
          linear-gradient(90deg, transparent 0 36px, rgba(200,80,70,.5) 36px 38px, transparent 38px),
          repeating-linear-gradient(180deg, transparent 0 30px, rgba(96,116,150,.30) 30px 31px),
          var(--paper);
      }
      /* Kertas bergoyang pelan seperti bernapas: miring sangat kecil,
         bolak balik lambat, teks tetap nyaman dibaca */
      @keyframes noteBreath{
        from{rotate:-.95deg}
        to{rotate:-.45deg}
      }
      /* Hover kertas tidak meluruskan miring alaminya, jadi geraknya tetap halus tanpa lompatan */
      .notes:hover{transform:translateY(-3px)}
      .note-hole{
        position:absolute; left:11px; width:15px; height:15px; border-radius:50%;
        background:rgba(59,40,20,.5);
        box-shadow:inset 0 2px 3px rgba(0,0,0,.45), 0 1px 0 rgba(255,255,255,.55);
      }
      .note-hole.h1{top:22%}
      .note-hole.h2{top:50%}
      .note-hole.h3{top:78%}
      .note-bee{
        position:absolute; top:-24px; right:-8px; width:78px; height:auto;
        image-rendering:pixelated; z-index:2;
        animation:beeLoop 8s cubic-bezier(.22,1,.36,1) infinite;
      }
      /* Lebah terbang mengitari area atas kertas dalam putaran kecil yang
         melengkung halus, kembali ke titik awal tanpa lompatan */
      @keyframes beeLoop{
        0%,100%{transform:translate(0,0) rotate(4deg)}
        22%{transform:translate(-30px,-12px) rotate(-4deg)}
        45%{transform:translate(-58px,-2px) rotate(-9deg)}
        68%{transform:translate(-34px,9px) rotate(2deg)}
        85%{transform:translate(-10px,5px) rotate(6deg)}
      }
      .note-grass{
        position:absolute; bottom:-16px; left:-10px; width:58px; height:auto;
        image-rendering:pixelated; z-index:2;
        transform:rotate(-4deg);
        transform-origin:50% 100%;
        filter:drop-shadow(3px 4px 0 rgba(0,0,0,.18));
        animation:grassSway 6.5s cubic-bezier(.22,1,.36,1) infinite;
      }
      /* Blok rumput diam lama, lalu bergoyang kecil sekali, lalu diam lagi */
      @keyframes grassSway{
        0%,58%,100%{transform:rotate(-4deg)}
        68%{transform:rotate(-.5deg) translateY(-1px)}
        78%{transform:rotate(-7deg)}
        88%{transform:rotate(-2.6deg)}
      }
      /* Bintang dan hati piksel kecil muncul menghilang lembut di sekitar kertas */
      .note-spark{
        position:absolute; z-index:2; display:block;
        image-rendering:pixelated; pointer-events:none; opacity:0;
        animation:noteSpark 7s cubic-bezier(.22,1,.36,1) infinite;
      }
      .note-spark--star1{width:20px; top:-17px; left:44%}
      .note-spark--heart{width:22px; right:-10px; top:44%; animation-delay:-2.4s; animation-duration:8s}
      .note-spark--star2{width:15px; bottom:-13px; left:26%; animation-delay:-4.6s; animation-duration:6.2s}
      @keyframes noteSpark{
        0%,100%{opacity:0; transform:translateY(5px) scale(.7) rotate(-6deg)}
        50%{opacity:.9; transform:translateY(-4px) scale(1) rotate(6deg)}
      }
      @media (max-width:480px){
        .note-bee{width:64px; top:-20px; right:-4px}
        .note-grass{width:50px}
        .notes{padding-left:48px}
      }

      /* ---------- Latar pemandangan piksel Minecraft ---------- */
      .scenery{
        position:fixed; inset:0; z-index:0; pointer-events:none; overflow:hidden;
        --star:#ffe9a8;
        --sun:#c9a84a; --sun-core:#d8c690; --sun-glow:#c9902e;
        --cloud:#f3e5c0;
        --hill-far:#54401c;
        --hill-near:#3c5220;
        --leaf:#4d7a26; --leaf-light:#5fae2e; --trunk:#6b4423;
        --ground:#4c8a24; --dirt:#54381a; --dirt-dark:#3f2a12;
      }
      @media (prefers-color-scheme: light){
        .scenery{
          --star:rgba(255,233,168,0);
          --cloud:#ffffff;
          --hill-far:#a9c97a;
          --hill-near:#82b356;
          --leaf:#5da332; --leaf-light:#79c243; --trunk:#8a5f30;
          --ground:#6db33a; --dirt:#a9763f; --dirt-dark:#8a5f30;
        }
      }
      .scenery svg{width:100%; height:100%; display:block}
      .scenery .star{
        transform-box:fill-box; transform-origin:center;
        animation:twinkle 5s cubic-bezier(.45,0,.55,1) infinite;
      }
      @keyframes twinkle{
        0%,100%{opacity:.22; transform:scale(.82) rotate(0deg)}
        50%{opacity:.62; transform:scale(1) rotate(18deg)}
      }
      .scenery .cloud{animation:cloudDrift 40s cubic-bezier(.45,0,.55,1) infinite alternate}
      .scenery .cloud.c2{animation-duration:37s; animation-delay:-14s}
      .scenery .cloud.c3{animation-duration:45s; animation-delay:-22s}
      .scenery .cloud.c4{animation-duration:43s; animation-delay:-9s}
      .scenery .cloud.c5{animation-duration:49s; animation-delay:-27s}
      .scenery .cloud.c6{animation-duration:52s; animation-delay:-31s}
      .scenery .cloud.c7{animation-duration:47s; animation-delay:-18s}
      @keyframes cloudDrift{from{transform:translate(-26px,0)} to{transform:translate(40px,-6px)}}
      .scenery .sun{animation:sunGlow 9s cubic-bezier(.45,0,.55,1) infinite}
      @keyframes sunGlow{0%,100%{opacity:.68}50%{opacity:.8}}

      /* ---------- Properti awan dan bintang piksel di tiap slide ---------- */
      .sky-decor{position:absolute; inset:0; z-index:0; pointer-events:none; overflow:hidden}
      .sky-decor img{position:absolute; image-rendering:pixelated; display:block}
      .sd-cloud{animation:sdDrift 16s cubic-bezier(.45,0,.55,1) infinite alternate; filter:drop-shadow(3px 4px 0 rgba(0,0,0,.14))}
      .sd-cloud.sd-rev{animation-direction:alternate-reverse}
      @keyframes sdDrift{
        0%{transform:translate(-18px,3px)}
        55%{transform:translate(10px,-6px)}
        100%{transform:translate(32px,-10px)}
      }
      .sd-star{animation:sdTwinkle 5.6s cubic-bezier(.45,0,.55,1) infinite; filter:drop-shadow(2px 3px 0 rgba(0,0,0,.14)); opacity:.78}
      @keyframes sdTwinkle{
        0%,100%{transform:scale(.9) rotate(-3deg); opacity:.55}
        50%{transform:scale(1.02) rotate(4deg); opacity:.82}
      }
      /* Slide 1: di sekeliling panggung lilin */
      .sky-decor--loading .sd-c1{top:6%; left:4%; width:106px}
      .sky-decor--loading .sd-c2{top:11%; right:5%; width:72px; animation-duration:11s}
      .sky-decor--loading .sd-s1{top:21%; right:13%; width:40px}
      .sky-decor--loading .sd-s2{bottom:15%; left:8%; width:32px; animation-delay:-1.1s}
      .sky-decor--loading .sd-s3{bottom:23%; right:8%; width:25px; animation-delay:-2s}
      /* Slide 2: konten utama selalu di atas dekor */
      #viewMain{position:relative; min-height:100vh; min-height:100svh}
      #viewMain > *:not(.sky-decor):not(.hub-particles):not(.hub-bg){position:relative; z-index:1}
      /* Slide 2: tulisan tidak bisa dipilih atau disalin, termasuk tahan lama di HP */
      #viewMain, #viewMain *{
        -webkit-user-select:none;
        user-select:none;
        -webkit-touch-callout:none;
      }
      #viewMain img{-webkit-user-drag:none; user-select:none}
      /* Halaman Notes: tulisan tidak bisa dipilih atau disalin, sama seperti slide 2 */
      #viewNotes, #viewNotes *{
        -webkit-user-select:none;
        user-select:none;
        -webkit-touch-callout:none;
      }
      #viewNotes img{-webkit-user-drag:none; user-select:none}
      /* Semua slide: tulisan tidak bisa dipilih atau disalin, termasuk
         tahan lama di HP. Berlaku untuk slide 1 (layar lilin), slide 2,
         playlist, foto, notes, tanda tanya, dan kue. Ini hanya mematikan
         seleksi, salin, dan sorotan ketukan; klik tombol tetap jalan. */
      .view, .view *, .loading, .loading *{
        -webkit-user-select:none;
        user-select:none;
        -webkit-touch-callout:none;
        -webkit-tap-highlight-color:transparent;
      }
      /* Partikel latar slide 2: kunang kunang piksel naik perlahan, bintang kecil
         berkelip, dan debu cahaya melayang, supaya background tidak terasa kosong.
         Semua murni transform/opacity di GPU, pelan dan halus, di belakang konten. */
      .hub-particles{position:absolute; inset:0; z-index:0; overflow:hidden; pointer-events:none}
      .hub-particles i{position:absolute; display:block; opacity:0}
      .hp-rise{
        bottom:-18px;
        box-shadow:2px 2px 0 rgba(0,0,0,.12);
        animation:hpRise linear infinite;
      }
      @keyframes hpRise{
        0%{opacity:0; transform:translate(0,0) rotate(0deg)}
        12%{opacity:var(--o,.5)}
        55%{opacity:calc(var(--o,.5) * .72)}
        100%{opacity:0; transform:translate(var(--drift,18px),-104vh) rotate(160deg)}
      }
      .hp-twinkle{
        box-shadow:2px 2px 0 rgba(0,0,0,.10);
        animation:hpTwinkle cubic-bezier(.45,0,.55,1) infinite;
      }
      @keyframes hpTwinkle{
        0%,100%{opacity:.12; transform:scale(.72) rotate(0deg)}
        50%{opacity:var(--o,.62); transform:scale(1) rotate(45deg)}
      }
      .hp-drift{
        border-radius:50%;
        filter:blur(.4px);
        animation:hpDrift cubic-bezier(.45,0,.55,1) infinite alternate;
      }
      @keyframes hpDrift{
        from{opacity:.10; transform:translate(0,0)}
        to{opacity:var(--o,.34); transform:translate(var(--drift,22px),-16px)}
      }
      .hp-glow{
        position:absolute; border-radius:50%; pointer-events:none;
        background:radial-gradient(circle, rgba(255,207,90,.14) 0%, rgba(255,207,90,.05) 42%, rgba(255,207,90,0) 70%);
        animation:hpGlowPulse 7s cubic-bezier(.45,0,.55,1) infinite alternate;
      }
      @media (prefers-color-scheme: light){
        .hp-glow{background:radial-gradient(circle, rgba(138,90,0,.10) 0%, rgba(138,90,0,.04) 42%, rgba(138,90,0,0) 70%)}
        .hub-particles i{box-shadow:2px 2px 0 rgba(51,34,15,.08)}
      }
      @keyframes hpGlowPulse{
        from{opacity:.55; transform:scale(1)}
        to{opacity:1; transform:scale(1.08)}
      }
      .sky-decor--main .sd-c1{top:0; left:-4px; width:94px; animation-duration:17s}
      .sky-decor--main .sd-c2{top:33%; right:-12px; width:78px; animation-duration:14s}
      /* Tiga awan tambahan di pita langit atas dan tepi kiri, semua kecil,
         agak tembus, dan jauh dari judul serta tombol di tengah */
      .sky-decor--main .sd-c3{top:6%; left:29%; width:62px; opacity:.85; animation-duration:19s}
      .sky-decor--main .sd-c4{top:23%; left:-10px; width:58px; opacity:.8; animation-duration:15.5s}
      .sky-decor--main .sd-c5{top:10%; right:26%; width:46px; opacity:.75; animation-duration:22s}
      /* Dua awan lagi supaya langit terasa lebih hidup: satu melayang
         rendah di tepi kiri di atas punggung bukit, satu kecil di pita
         langit atas tengah kanan. Keduanya tetap di area kosong, jauh
         dari judul, tombol, dan isi halaman, dan ikut melayang dengan
         animasi sdDrift yang sama seperti awan lainnya */
      .sky-decor--main .sd-c6{top:47%; left:-14px; width:64px; opacity:.78; animation-duration:18s}
      .sky-decor--main .sd-c7{top:4%; left:54%; width:52px; opacity:.8; animation-duration:21s}
      .sky-decor--main .sd-s1{top:8px; right:0; width:44px}
      .sky-decor--main .sd-s2{top:54%; left:-6px; width:34px; animation-delay:-1.4s}
      .sky-decor--main .sd-s3{bottom:5%; right:3%; width:38px; animation-delay:-.6s}
      /* ---------- Latar slide 2: langit, bukit, dan tanah piksel ----------
         Komposisi vertikal yang tenang khusus layar tegak: langit piksel
         biru lembut di bagian atas, area tengah bersih dan lapang di
         belakang judul dan keempat tombol, lalu tanah piksel ala Minecraft
         di bagian bawah: punggung bukit rumput berundak, bibir rumput
         hijau, rumpun rumput dan bunga kecil, serta blok tanah cokelat
         bertekstur tempat kedua kucing berjalan. Lapisan ini absolut
         selebar layar di dalam #viewMain, jadi hanya tampil selama slide 2
         aktif. Semua pita memakai persen tinggi lapisan, bukan gambar
         lebar yang di-crop, jadi tampilannya utuh di layar tegak maupun
         layar lebar. */
      .hub-bg{
        position:absolute; top:0; bottom:0; left:50%; width:100vw;
        transform:translateX(-50%);
        z-index:0; overflow:hidden; pointer-events:none;
        background:
          radial-gradient(circle at 85% 8%, rgba(255,238,176,.38) 0%, rgba(255,238,176,0) 24%),
          linear-gradient(180deg, #2e639b 0%, #4d8bc0 32%, #7fb2da 57%, #a9d2ee 71%, #cfeafb 73%, rgba(207,234,251,0) 73.6%);
      }
      @media (prefers-color-scheme: light){
        .hub-bg{
          background:
            radial-gradient(circle at 85% 8%, rgba(255,238,176,.55) 0%, rgba(255,238,176,0) 24%),
            linear-gradient(180deg, #4aa7ec 0%, #7cc4f7 30%, #aedffb 56%, #d8f3fe 71%, #eaf9ff 73%, rgba(234,249,255,0) 73.6%);
        }
        /* Footer slide 2 jatuh di atas permukaan tanah yang lebih
           terang di skema terang: chip gelapnya sedikit dipertegas
           supaya kontras teksnya tetap kuat */
        #viewMain footer{background:rgba(26,15,5,.58); box-shadow:0 2px 10px rgba(26,15,5,.28), inset 0 1px 0 rgba(255,243,208,.14)}
      }
      /* Tekstur kotak piksel halus di atas seluruh latar, senada tekstur halaman */
      .hub-bg::before{
        content:""; position:absolute; inset:0; z-index:2;
        background-image:repeating-conic-gradient(rgba(255,255,255,.05) 0% 25%, rgba(0,0,0,.045) 0% 50%);
        background-size:28px 28px; opacity:.32;
      }
      /* Tanah piksel ala Minecraft: bibir rumput hijau berlapis di tepi
         atas (hijau terang, hijau rumput, hijau tua, lalu bayangan tanah),
         di bawahnya blok tanah cokelat dengan bintik piksel dan garis blok
         tipis, makin gelap ke bawah seperti tanah galian */
      .hub-land{
        position:absolute; left:0; right:0; top:73%; bottom:0; z-index:0;
        background-color:#6e4525;
        background-image:
          linear-gradient(180deg, #8ede5e 0, #7ec850 7px, #5fae2e 13px, #4c8a24 16px, rgba(30,18,6,.38) 18px, rgba(30,18,6,0) 26px),
          repeating-conic-gradient(rgba(43,26,12,.15) 0% 25%, rgba(255,226,170,.05) 0% 50%),
          repeating-linear-gradient(90deg, rgba(43,26,12,.12) 0 2px, rgba(43,26,12,0) 2px 38px),
          repeating-linear-gradient(0deg, rgba(43,26,12,.12) 0 2px, rgba(43,26,12,0) 2px 38px),
          linear-gradient(180deg, #7a4e2a 0%, #66401f 45%, #4a2b12 100%);
        background-size:100% 26px, 18px 18px, 38px 38px, 38px 38px, 100% 100%;
        background-repeat:no-repeat, repeat, repeat, repeat, no-repeat;
      }
      /* Blok ore Minecraft di dalam tanah: blok batu piksel dengan kristal
         warna ore-nya (coal, iron, gold, lapis, diamond, redstone, emerald),
         tersebar rapi di pita tanah di bawah bibir rumput, tidak ramai.
         Lapisan ini anak .hub-land, jadi otomatis tampil di slide 2 dan di
         latar bersama empat halaman isi (keduanya memakai .hub-land yang
         sama), selalu di lapisan latar z-index paling bawah: tidak pernah
         menutupi judul, tombol, footer, atau isi halaman. Bentuknya lucu
         dan lentur, jauh dari kesan kaku: sudut blok dibulatkan organik
         seperti kerikil dengan pola lengkung yang beda beda tiap blok,
         kristalnya berupa gumpalan bulat gemuk bertumpuk (permata gemuk
         membulat untuk ore langka, bukan lagi kotak kotak bersudut
         tajam), seluruh blok bergoyang jelly memipih dan memanjang
         halus (squash and stretch) dengan jeda berbeda, kristalnya
         berdenyut sambil bergoyang pelan, dan dua kilau putih per blok
         menyala bergantian lebih hidup, plus halo lembut khusus ore
         langka. Semua cuma transform dan opacity, jadi gerakannya
         halus, ringan, dan tidak bikin lag. */
      .hub-ores{position:absolute; inset:0; pointer-events:none}
      /* Goyangan jelly yang lembut dan lentur: blok naik turun sambil
         sedikit memipih lalu memanjang (squash and stretch) dan miring
         bergantian, berporos di sisi bawah blok seperti balon tanah
         yang empuk, bukan melayang kaku. Durasinya diatur per blok di
         aturan posisi di bawah */
      .ore{
        position:absolute; width:36px; height:36px;
        transform-origin:50% 88%;
        animation:oreBob 5.8s cubic-bezier(.45,0,.55,1) infinite alternate;
        animation-delay:var(--gd,0s);
      }
      @keyframes oreBob{
        0%{transform:translateY(0) rotate(-2deg) scale(1.05,.94)}
        55%{transform:translateY(-3.5px) rotate(1.6deg) scale(.97,1.045)}
        100%{transform:translateY(-1px) rotate(2.4deg) scale(1.015,.985)}
      }
      /* Sudut blok dibulatkan organik seperti kerikil, bukan kotak kaku;
         tiap blok punya pola lengkung sendiri lewat --orr supaya tidak
         ada dua blok yang bentuknya persis sama */
      .ore svg{
        position:relative; display:block; width:100%; height:100%;
        image-rendering:pixelated; overflow:hidden;
        border-radius:var(--orr, 32% 36% 30% 38% / 38% 30% 36% 32%);
      }
      .ore .ost{fill:#7d7a74}
      .ore .ost-d{fill:#5d594f}
      .ore .ost-l{fill:#98938a}
      /* Gugus kristal berdenyut membesar kecil sangat pelan, terasa hidup */
      .ore .oc-g{
        transform-box:fill-box; transform-origin:center;
        animation:orePulse 4.4s cubic-bezier(.45,0,.55,1) infinite alternate;
        animation-delay:var(--gd,0s);
      }
      @keyframes orePulse{
        from{transform:scale(1) rotate(-2.5deg)}
        to{transform:scale(1.09) rotate(2.5deg)}
      }
      .ore .oc-g rect{fill:var(--oc)}
      .ore .oc-h rect{fill:var(--ocl)}
      /* Kilau kini membesar menyala lalu menyusut, dan kilau kedua
         (.glint-b) menyusul bergantian supaya tidak seragam kaku */
      .ore .glint{
        fill:#ffffff; opacity:.2;
        transform-box:fill-box; transform-origin:center;
        animation:oreGlint 3.9s cubic-bezier(.45,0,.55,1) infinite;
        animation-delay:var(--gd,0s);
      }
      .ore .glint-b{animation-duration:4.7s; animation-delay:calc(var(--gd,0s) + 1.4s)}
      @keyframes oreGlint{
        0%,100%{opacity:.12; transform:scale(.65)}
        50%{opacity:1; transform:scale(1.6)}
      }
      /* Halo cahaya lembut khusus ore langka (gold, diamond, emerald):
         berdenyut pelan dengan opacity saja supaya tetap ringan */
      .ore.rare::before{
        content:""; position:absolute; inset:-50%;
        background:radial-gradient(circle, var(--ocg) 0%, rgba(0,0,0,0) 62%);
        opacity:.12;
        animation:oreHalo 5.2s cubic-bezier(.45,0,.55,1) infinite alternate;
        animation-delay:var(--gd,0s);
      }
      @keyframes oreHalo{
        from{opacity:.07}
        to{opacity:.19}
      }
      /* Sebaran ore di pita tanah: kolom tengah bawah dibiarkan kosong
         untuk area footer, dan semua blok mulai di bawah bibir rumput */
      .ore.o1{left:5%; top:27%; width:34px; height:34px; --gd:-.5s; animation-duration:5.2s; --orr:36% 30% 38% 32% / 32% 38% 30% 36%}
      .ore.o2{left:14.5%; top:59%; width:30px; height:30px; --gd:-1.7s; animation-duration:6.4s; --orr:30% 38% 33% 36% / 37% 31% 35% 33%}
      .ore.o3{left:24%; top:34%; --gd:-2.6s; animation-duration:5.7s; --orr:38% 33% 30% 37% / 31% 36% 38% 32%}
      .ore.o4{left:34.5%; top:67%; width:32px; height:32px; --gd:-1.1s; animation-duration:6.9s; --orr:33% 36% 39% 30% / 36% 32% 31% 37%}
      .ore.o5{left:46.5%; top:25%; width:41px; height:41px; --gd:0s; animation-duration:5.4s; --orr:35% 31% 36% 33% / 33% 37% 32% 35%}
      .ore.o6{left:59.5%; top:61%; width:32px; height:32px; --gd:-3.1s; animation-duration:6.2s; --orr:31% 37% 32% 38% / 38% 33% 36% 30%}
      .ore.o7{left:70%; top:31%; --gd:-2s; animation-duration:5.9s; --orr:37% 32% 35% 31% / 30% 35% 33% 38%}
      .ore.o8{left:81.5%; top:65%; width:28px; height:28px; --gd:-.9s; animation-duration:6.6s; --orr:32% 35% 31% 39% / 35% 30% 37% 33%}
      .ore.o9{left:90.5%; top:37%; width:28px; height:28px; --gd:-2.9s; animation-duration:5.5s; --orr:39% 34% 36% 30% / 34% 38% 31% 35%}
      @media (prefers-reduced-motion: reduce){
        .ore, .ore .oc-g, .ore .glint, .ore.rare::before, .sd-cloud, .scenery .cloud{animation:none !important}
      }
      /* Bukit piksel di bibir tanah: SVG-nya di-slice dari tengah bawah,
         jadi undakan bukit, rumpun rumput, dan bunga kecilnya tetap utuh
         di lebar layar berapa pun */
      .hub-hills{position:absolute; left:0; bottom:26.4%; z-index:1; width:100%; height:104px; display:block}
      /* Awan dan bintang slide 2 diatur ulang mengikuti komposisi baru:
         awan kedua naik ke langit (dulu menggantung di tengah kanan setinggi
         tombol), bintang kedua turun ke tepi kiri tepat di atas punggung bukit.
         Hanya di dalam #viewMain; slide lain yang memakai kelas sama tidak berubah. */
      #viewMain .sky-decor--main .sd-c2{top:15%}
      #viewMain .sky-decor--main .sd-s2{top:auto; bottom:27.5%}

      /* ---------- Latar bersama lima halaman isi: playlist, foto, notes,
         tanda tanya, dan kue. Satu lapisan latar tetap (fixed) yang persis
         meniru latar slide 2: langit piksel biru lembut bergradasi, awan
         piksel bergerak pelan, bintang berkelip, partikel halus, bukit
         piksel di kejauhan, dan tanah piksel bertekstur di bawah (bukan
         laut), supaya kelima halaman terasa satu dunia dengan slide 2
         dan tidak terasa flat. Lapisan ini menempel di layar saat halaman
         digulir, jadi komposisinya selalu sama seperti slide 2 di layar
         HP tegak, tablet, laptop, dan MacBook tanpa terpotong aneh, dan
         hanya menyala selama salah satu dari kelima halaman itu aktif
         (kelas page-bg-on di body diatur dari showView). Isi halaman
         (jendela krem, foto, surat, game, tombol) berada di lapisan
         .wrap z-index 1 di atasnya, jadi tidak ada yang tertutup. Awan
         dan bintang bawaan halaman foto, notes, tanda tanya, dan kue
         disembunyikan karena sudah digantikan salinan yang sama persis
         di lapisan ini, supaya tidak tampil ganda. */
      .page-bg{
        position:fixed; inset:0; z-index:0; overflow:hidden; pointer-events:none;
        background:
          radial-gradient(circle at 85% 8%, rgba(255,238,176,.38) 0%, rgba(255,238,176,0) 24%),
          linear-gradient(180deg, #2e639b 0%, #4d8bc0 32%, #7fb2da 57%, #a9d2ee 71%, #cfeafb 73%, rgba(207,234,251,0) 73.6%);
        opacity:0; visibility:hidden;
        transition:opacity .7s cubic-bezier(.22,1,.36,1), visibility 0s linear .7s;
      }
      body.page-bg-on .page-bg{
        opacity:1; visibility:visible;
        transition:opacity .7s cubic-bezier(.22,1,.36,1);
      }
      /* Nyala seketika khusus transisi buka: saat membuka halaman isi dari
         slide 2, JavaScript (armPageBg di openWithAnim) memasang kelas
         page-bg-snap di body bersamaan dengan ketukan tombol, mematikan
         transisi memudar di atas supaya latar bersama tampil penuh sejak
         frame pertama animasi buka. Slide 2 yang sedang memudar keluar
         berada di lapisan di atasnya dengan latar yang sama persis, jadi
         penyalaan seketika ini tidak terlihat berubah; yang hilang adalah
         latar lama cokelat di body yang dulu sempat tampil di tengah
         animasi karena latar bersama baru menyala (dan masih memudar
         masuk 0,7 detik) setelah halaman lama hilang. Kelas dicabut
         begitu urutan buka selesai, jadi perilaku memudar biasa saat
         kembali ke slide 2 tetap seperti semula. Aturan ::after ikut
         dimatikan transisinya supaya redup khusus kue juga langsung pas. */
      body.page-bg-snap .page-bg{transition:none}
      body.page-bg-snap .page-bg::after{transition:none}
      @media (prefers-color-scheme: light){
        .page-bg{
          background:
            radial-gradient(circle at 85% 8%, rgba(255,238,176,.55) 0%, rgba(255,238,176,0) 24%),
            linear-gradient(180deg, #4aa7ec 0%, #7cc4f7 30%, #aedffb 56%, #d8f3fe 71%, #eaf9ff 73%, rgba(234,249,255,0) 73.6%);
        }
      }
      /* Tekstur kotak piksel halus di atas latar bersama, sama seperti slide 2 */
      .page-bg::before{
        content:""; position:absolute; inset:0; z-index:2;
        background-image:repeating-conic-gradient(rgba(255,255,255,.05) 0% 25%, rgba(0,0,0,.045) 0% 50%);
        background-size:28px 28px; opacity:.32;
      }
      /* Redup lembut khusus halaman kue (kelas page-bg-cake di body dari
         showView): selubung gelap radial tipis yang paling terasa di
         tengah layar, di belakang kue, supaya kue pink, lilin, bunting,
         dan balon tetap jadi fokus di atas langit yang kini sama dengan
         halaman lain, melengkapi cahaya hangat .cake-halo yang sudah
         ada. Di empat halaman isi lain selubung ini mati (opacity 0),
         dan memudar seirama nyala latar bersamanya */
      .page-bg::after{
        content:""; position:absolute; inset:0; z-index:3; pointer-events:none;
        background:radial-gradient(ellipse 105% 78% at 50% 40%, rgba(12,17,36,.19) 0%, rgba(12,17,36,.11) 55%, rgba(12,17,36,.05) 100%);
        opacity:0;
        transition:opacity .7s cubic-bezier(.22,1,.36,1);
      }
      body.page-bg-cake .page-bg::after{opacity:1}
      @media (prefers-color-scheme: light){
        .page-bg::after{
          background:radial-gradient(ellipse 105% 78% at 50% 40%, rgba(12,17,36,.15) 0%, rgba(12,17,36,.08) 55%, rgba(12,17,36,.03) 100%);
        }
      }
      /* Posisi awan dan bintang di lapisan bersama disamakan persis
         dengan penataan ulang khusus slide 2 */
      .page-bg .sky-decor--main .sd-c2{top:15%}
      .page-bg .sky-decor--main .sd-s2{top:auto; bottom:27.5%}
      /* Awan dan bintang bawaan keempat halaman ini digantikan lapisan
         bersama di atas, supaya tidak tampil ganda */
      #viewFoto .sky-decor--main, #viewNotes .sky-decor--main, #viewSecret .sky-decor--main, #viewCake .sky-decor--main{display:none}

      /* Slide playlist: di dalam layar hijau, kontennya tetap di atas */
      .pl-screen{position:relative; overflow:hidden}
      .pl-screen > *:not(.sky-decor){position:relative; z-index:1}
      .sky-decor--playlist .sd-c1{top:30px; right:-16px; width:76px; opacity:.95}
      .sky-decor--playlist .sd-s1{bottom:66px; left:-8px; width:36px}
      .sky-decor--playlist .sd-s2{top:45%; right:6px; width:24px; animation-delay:-1.7s}
      @media (max-width:480px){
        .sky-decor--loading .sd-c1{width:84px}
        .sky-decor--main .sd-c1{width:76px}
        .sky-decor--main .sd-c3{width:50px}
        .sky-decor--main .sd-c4{width:48px}
        .sky-decor--main .sd-c5{width:38px}
        .sky-decor--main .sd-c6{width:52px}
        .sky-decor--main .sd-c7{width:42px}
        .sky-decor--main .sd-s1{width:36px}
      }

      /* ---------- Kucing piksel keliling: jalan halus, diam, lalu jalan lagi ---------- */
      .cat-layer{
        position:fixed; inset:0; z-index:120;
        pointer-events:none; overflow:hidden;
      }
      .cat-wander{
        position:absolute; bottom:10px; left:0;
        will-change:transform;
      }
      .cat-wander img{
        display:block; width:100%; height:auto; image-rendering:pixelated;
        filter:drop-shadow(3px 4px 0 rgba(0,0,0,.22));
        animation:catBreathe 4s cubic-bezier(.45,0,.55,1) infinite alternate;
        transform-origin:50% 100%;
      }
      @keyframes catBreathe{
        from{transform:scale(1) rotate(0deg)}
        to{transform:scale(1.025,1.04) rotate(.6deg)}
      }
      .cat-wander.walking img{animation:catWalkBob .75s cubic-bezier(.45,0,.55,1) infinite alternate}
      @keyframes catWalkBob{
        from{transform:translateY(0) rotate(-1deg)}
        to{transform:translateY(-2.5px) rotate(1deg)}
      }
      .cat-flip{transition:transform .6s cubic-bezier(.22,1,.36,1)}
      #catOrange{width:56px}
      #catBlue{width:86px}
      @media (max-width:480px){
        #catOrange{width:48px}
        #catBlue{width:74px}
      }

      /* ---------- Tempelan karakter di notes: Luffy dan Zoro jadi stiker kertas
         di surat Minecraft, bukan lagi karakter keliling. Masing masing miring
         sedikit seperti stiker yang ditempel tangan, ada selotip kertas di
         atasnya, garis tepi putih tebal khas stiker potong, bayangan lembut,
         dan goyangan melayang sangat halus supaya terasa hidup tapi elegan. */
      .note-sticker{
        position:absolute; z-index:3; pointer-events:none;
      }
      .note-sticker img{
        display:block; width:100%; height:auto; image-rendering:pixelated;
        filter:
          drop-shadow(2px 0 0 #fffdf6) drop-shadow(-2px 0 0 #fffdf6)
          drop-shadow(0 2px 0 #fffdf6) drop-shadow(0 -2px 0 #fffdf6)
          drop-shadow(5px 7px 0 rgba(43,26,18,.20));
      }
      /* Selotip kertas di atas kepala stiker, sedikit miring dan tembus cahaya */
      .note-sticker::before{
        content:""; position:absolute; z-index:2;
        top:-7px; left:50%; width:44px; height:15px;
        transform:translateX(-50%) rotate(-5deg);
        background:repeating-linear-gradient(90deg, rgba(240,214,130,.85) 0 6px, rgba(233,201,110,.85) 6px 12px);
        box-shadow:1px 2px 0 rgba(43,26,18,.14);
        opacity:.92;
      }
      /* Zoro digeser jauh ke sudut kiri atas dan sedikit diperkecil: sebagian
         besar badannya menggantung di luar tepi kertas, seperti stiker yang
         ditempel pas di sudut, jadi baris baris awal surat sepenuhnya jelas
         terbaca dan tidak ada satu kata pun yang tertutup. Selotip kertas,
         tepi putih tebal, bayangan lembut, dan goyangan melayangnya tetap. */
      .note-sticker--zoro{
        top:-42px; left:-32px; width:88px;
        transform:rotate(-5deg);
        animation:stickerSwayZ 7s cubic-bezier(.22,1,.36,1) infinite alternate;
      }
      .note-sticker--luffy{
        bottom:-24px; right:-10px; width:104px;
        transform:rotate(4deg);
        animation:stickerSwayL 7.6s cubic-bezier(.22,1,.36,1) .8s infinite alternate;
      }
      .note-sticker--luffy::before{transform:translateX(-50%) rotate(6deg)}
      @keyframes stickerSwayZ{
        from{transform:rotate(-5deg) translateY(0)}
        to{transform:rotate(-3.6deg) translateY(-4px)}
      }
      @keyframes stickerSwayL{
        from{transform:rotate(4deg) translateY(0)}
        to{transform:rotate(2.8deg) translateY(-4px)}
      }
      @media (max-width:480px){
        .note-sticker--zoro{width:66px; top:-30px; left:-18px}
        .note-sticker--luffy{width:82px; bottom:-18px; right:-5px}
        .note-sticker::before{width:36px; height:13px}
      }

      .hero h2, .sub-head h2{animation:riseIn 1.1s cubic-bezier(.22,1,.36,1) both}

      /* ---------- Animasi pembuka empat halaman dari slide 2: playlist, foto,
         notes, dan tanda tanya. Satu koreografi yang sama supaya rapih dan
         konsisten: halamannya membesar lembut dari arah tombol yang dipencet
         (titik asal diatur lewat --open-ox dan --open-oy dari tombol hub),
         lalu isinya masuk bertahap dengan ritme tetap: bar atas dulu, judul
         menyusul, konten utama, lalu isi bawahnya. Semua durasi pendek
         (tuntas sekitar satu detik), easing yang sama dengan animasi lain
         di situs ini, dan fill backwards supaya setelah masuk elemen kembali
         ke gaya alaminya (hover, melayang, dan animasi lamanya tetap jalan). */
      #viewPlaylist.active, #viewFoto.active, #viewNotes.active, #viewSecret.active{
        animation:pageOpen .55s cubic-bezier(.22,1,.36,1) both;
        transform-origin:var(--open-ox,50%) var(--open-oy,320px);
      }
      @keyframes pageOpen{
        from{opacity:0; transform:scale(.955) translateY(10px)}
        to{opacity:1; transform:scale(1) translateY(0)}
      }
      @keyframes openBarIn{
        from{opacity:0; transform:translateY(-10px)}
        to{opacity:1; transform:translateY(0)}
      }
      @keyframes openRise{
        from{opacity:0; transform:translateY(16px)}
        to{opacity:1; transform:translateY(0)}
      }
      @keyframes openPop{
        from{opacity:0; transform:translateY(18px) scale(.96)}
        to{opacity:1; transform:translateY(0) scale(1)}
      }
      @keyframes openFade{
        from{opacity:0}
        to{opacity:1}
      }
      /* Tahap 1 di semua halaman: bar atas (tombol minus dan X) masuk duluan */
      #viewPlaylist.active .win-bar, #viewFoto.active .win-bar, #viewNotes.active .win-bar{
        animation:openBarIn .45s cubic-bezier(.22,1,.36,1) backwards;
      }
      /* Tahap 2: judul halaman menyusul persis di bawah bar */
      #viewFoto.active .sub-head, #viewNotes.active .sub-head, #viewSecret.active .sub-head{
        animation:openRise .5s .06s cubic-bezier(.22,1,.36,1) backwards;
      }
      /* Tahap akhir di semua halaman: keterangan dan tombol kembali di bawah */
      #viewFoto.active .back-row, #viewNotes.active .back-row, #viewSecret.active .back-row{
        animation:openRise .5s cubic-bezier(.22,1,.36,1) backwards;
      }
      /* ----- Foto: jendela "your Seasons" membesar lembut sebagai konten
         utama, lalu ketujuh perangko masuk satu per satu sambil melayang
         naik ke miring alaminya, judul dan teks penutup menyusul ----- */
      #viewFoto.active .seasons-win{animation:openPop .55s .16s cubic-bezier(.22,1,.36,1) backwards}
      /* Perangko masuk satu per satu (stampIn), lalu terus bergoyang
         melayang pelan (stampFloat) supaya kolasenya terasa hidup.
         stampFloat menganimasikan properti translate dan rotate yang
         terpisah dari transform, jadi tidak bentrok dengan transform
         animasi masuk maupun efek hover perangko: ketiganya menyatu
         halus. Durasi dan jeda awal tiap slot dibuat beda beda lewat
         nilai kedua animation-duration dan animation-delay di bawah,
         jadi ketujuh bingkai tidak bergerak serempak. */
      #viewFoto.active .stamp{animation:stampIn .6s cubic-bezier(.22,1,.36,1) backwards, stampFloat 6.4s cubic-bezier(.22,1,.36,1) infinite alternate}
      #viewFoto.active .stamp--1{animation-delay:.3s,-1.1s}
      #viewFoto.active .stamp--2{animation-delay:.38s,-3.4s; animation-duration:.6s,7.1s}
      #viewFoto.active .stamp--3{animation-delay:.46s,-2.2s; animation-duration:.6s,5.8s}
      #viewFoto.active .stamp--4{animation-delay:.54s,-4.6s; animation-duration:.6s,6.8s}
      #viewFoto.active .stamp--5{animation-delay:.62s,-.6s; animation-duration:.6s,7.4s}
      #viewFoto.active .stamp--6{animation-delay:.7s,-2.9s; animation-duration:.6s,6.1s}
      #viewFoto.active .stamp--7{animation-delay:.78s,-5.2s; animation-duration:.6s,6.6s}
      @keyframes stampIn{
        from{opacity:0; transform:translateY(16px) scale(.9) rotate(0deg)}
        to{opacity:1; transform:translateY(0) scale(1) rotate(var(--rot,0deg))}
      }
      /* Goyangan melayang perangko: naik turun sangat pelan sambil miring
         sepersekian derajat, bolak balik lembut tanpa hentakan */
      @keyframes stampFloat{
        from{translate:0 2px; rotate:-.3deg}
        to{translate:0 -3px; rotate:.35deg}
      }
      #viewFoto.active .seasons-title{animation:openFade .55s .55s cubic-bezier(.22,1,.36,1) backwards}
      #viewFoto.active .seasons-cheers{animation:openRise .5s .88s cubic-bezier(.22,1,.36,1) backwards}
      /* ----- Hiasan One Piece Seasons: muncul bertahap menyusul teks
         cheers, lalu hidup pelan selamanya. Polanya sama seperti
         perangko: animasi masuk menganimasikan transform dan opacity,
         sedangkan gerak berulangnya memakai properti translate, rotate,
         dan scale yang terpisah, jadi keduanya menyatu tanpa bentrok
         dan semuanya ringan di GPU. ----- */
      #viewFoto.active .op-flag{animation:opDecorIn .55s 1s cubic-bezier(.22,1,.36,1) backwards, opWave 5s cubic-bezier(.22,1,.36,1) 1.6s infinite alternate}
      #viewFoto.active .op-hat{animation:opHatIn .55s 1.08s cubic-bezier(.22,1,.36,1) backwards, opSway 4.2s cubic-bezier(.22,1,.36,1) 1.7s infinite alternate}
      #viewFoto.active .op-heart--1{animation:opDecorIn .5s 1.18s cubic-bezier(.22,1,.36,1) backwards, opPulse 3s cubic-bezier(.22,1,.36,1) 1.8s infinite alternate}
      #viewFoto.active .op-heart--2{animation:opDecorIn .5s 1.3s cubic-bezier(.22,1,.36,1) backwards, opPulse 3.5s cubic-bezier(.22,1,.36,1) 2s infinite alternate}
      #viewFoto.active .op-star{animation:opDecorIn .5s 1.42s cubic-bezier(.22,1,.36,1) backwards, opTwinkle 3.8s cubic-bezier(.22,1,.36,1) 2.1s infinite alternate}
      #viewFoto.active .op-ship{animation:opShipIn .6s 1.52s cubic-bezier(.22,1,.36,1) backwards, opSail 32s cubic-bezier(.22,1,.36,1) 2.2s infinite alternate}
      #viewFoto.active .op-ship svg{animation:opShipBob 3.6s cubic-bezier(.22,1,.36,1) 2.2s infinite alternate}
      @keyframes opDecorIn{
        from{opacity:0; transform:translateY(-7px) scale(.82)}
        to{opacity:1; transform:translateY(0) scale(1)}
      }
      /* Topi masuk sambil kembali ke miring alaminya -11 derajat */
      @keyframes opHatIn{
        from{opacity:0; transform:translateY(-9px) rotate(-17deg) scale(.82)}
        to{opacity:1; transform:translateY(0) rotate(-11deg) scale(1)}
      }
      /* Bendera berkibar: miring bolak balik sepersekian derajat sambil
         naik turun sangat pelan */
      @keyframes opWave{
        from{rotate:-1.8deg; translate:0 0}
        to{rotate:1.4deg; translate:0 -2px}
      }
      /* Topi bergoyang pelan di tenggerannya */
      @keyframes opSway{
        from{rotate:-3deg}
        to{rotate:2.6deg}
      }
      /* Hati berdenyut lembut, bintang berkelip membesar mengecil */
      @keyframes opPulse{
        from{scale:1}
        to{scale:1.14}
      }
      @keyframes opTwinkle{
        from{scale:.92; rotate:4deg}
        to{scale:1.1; rotate:-5deg}
      }
      /* Kapal masuk mengambang naik dari tepi bawah, lalu berlayar sangat
         pelan menyamping sejauh 178 persen lebar badannya sendiri
         (sekitar 26 persen lebar panggung) bolak balik tanpa hentakan;
         goyangan naik turunnya seperti di atas air diatur di SVG di
         dalamnya, pola yang sama seperti camar: pembungkus yang bergerak
         jauh, isi yang mengambang */
      @keyframes opShipIn{
        from{opacity:0; transform:translateY(10px) scale(.92)}
        to{opacity:1; transform:translateY(0) scale(1)}
      }
      @keyframes opSail{
        from{translate:-2% 0}
        to{translate:178% 0}
      }
      @keyframes opShipBob{
        from{translate:0 0; rotate:-.8deg}
        to{translate:0 -3px; rotate:.9deg}
      }
      /* Camar terbang melintasi bagian atas jendela: pembungkusnya yang
         bergerak menyamping jauh (transform), sedangkan SVG di dalamnya
         mengambang naik turun (properti translate). Tiga camar beda
         ukuran, kecepatan, dan jeda awal negatif, jadi saat halaman
         dibuka mereka sudah tersebar di lintasan dan terus bergantian
         melintas tanpa pernah berkumpul. */
      #viewFoto.active .op-gull{animation:opFly 27s linear infinite, openFade .8s 1.15s cubic-bezier(.22,1,.36,1) backwards}
      #viewFoto.active .op-gull--1{animation-duration:27s,.8s; animation-delay:-7s,1.15s}
      #viewFoto.active .op-gull--2{animation-duration:34s,.8s; animation-delay:-21s,1.25s}
      #viewFoto.active .op-gull--3{animation-duration:41s,.8s; animation-delay:-33s,1.35s}
      #viewFoto.active .op-gull svg{animation:opBob 2.2s cubic-bezier(.22,1,.36,1) infinite alternate}
      #viewFoto.active .op-gull--2 svg{animation-duration:2.5s; animation-delay:-.6s}
      #viewFoto.active .op-gull--3 svg{animation-duration:2.8s; animation-delay:-1.2s}
      @keyframes opFly{
        from{transform:translateX(-64px)}
        to{transform:translateX(min(820px, calc(100vw - 8px)))}
      }
      @keyframes opBob{
        from{translate:0 0}
        to{translate:0 -5px}
      }
      #viewFoto.active .back-row{animation-delay:1s}
      /* ----- Notes: kalender memudar masuk, surat menyusul sambil kembali ke
         miring alaminya, lalu empat doa masuk satu per satu secara rapih ----- */
      /* Kalender: fade masuk berjalan berdampingan dengan melayang lamanya,
         karena keduanya mengatur properti berbeda (opacity dan transform) */
      #viewNotes.active .pxcal{animation:openFade .6s .14s cubic-bezier(.22,1,.36,1) backwards, calFloat 7s cubic-bezier(.22,1,.36,1) infinite}
      #viewNotes.active .notes{animation:noteCardIn .7s .22s cubic-bezier(.22,1,.36,1) backwards, noteBreath 9s cubic-bezier(.22,1,.36,1) infinite alternate}
      @keyframes noteCardIn{
        from{opacity:0; transform:translateY(20px) scale(.985)}
        to{opacity:1; transform:translateY(0) scale(1)}
      }
      /* Isi surat tampil utuh bersama kertasnya: tidak ada lagi efek masuk
         bertahap untuk judul, isi, dan tanda tangan surat */
      #viewNotes.active .wishes{animation:openRise .6s .34s cubic-bezier(.22,1,.36,1) backwards}
      #viewNotes.active .wish-list .wish{animation:openRise .6s cubic-bezier(.22,1,.36,1) backwards}
      #viewNotes.active .wish-list .wish:nth-child(1){animation-delay:.44s}
      #viewNotes.active .wish-list .wish:nth-child(2){animation-delay:.52s}
      #viewNotes.active .wish-list .wish:nth-child(3){animation-delay:.6s}
      #viewNotes.active .wish-list .wish:nth-child(4){animation-delay:.68s}
      #viewNotes.active .back-row{animation:openRise .6s .74s cubic-bezier(.22,1,.36,1) backwards}
      /* ----- Tanda tanya: jendela kremnya memantul masuk sebagai konten utama
         (keyframe lamanya dipakai lagi, hanya durasinya diselaraskan), isi di
         dalamnya sudah bertahap sendiri, keterangan dan tombol kembali terakhir ----- */
      #viewSecret.active .trait-win{animation:traitWinPop .65s .12s cubic-bezier(.22,1,.36,1) backwards}
      #viewSecret.active .back-row{animation-delay:.58s}
      /* ----- Playlist: layar hijau masuk, lalu status dan judul, baris lagu
         satu per satu dari kiri, artwork membesar, dan kontrol putar terakhir.
         Animasi play, pause, dan skip yang sudah ada tidak disentuh. ----- */
      #viewPlaylist.active .pl-screen{animation:plScreenIn .55s cubic-bezier(.22,1,.36,1) backwards}
      #viewPlaylist.active .pl-status{animation:openFade .45s .08s cubic-bezier(.22,1,.36,1) backwards}
      #viewPlaylist.active .pl-brand{animation:openRise .45s .12s cubic-bezier(.22,1,.36,1) backwards}
      #viewPlaylist.active .pl-track{animation:openTrackIn .45s cubic-bezier(.22,1,.36,1) backwards}
      #viewPlaylist.active .pl-track:nth-child(1){animation-delay:.17s}
      #viewPlaylist.active .pl-track:nth-child(2){animation-delay:.21s}
      #viewPlaylist.active .pl-track:nth-child(3){animation-delay:.25s}
      #viewPlaylist.active .pl-track:nth-child(4){animation-delay:.29s}
      #viewPlaylist.active .pl-track:nth-child(5){animation-delay:.33s}
      @keyframes openTrackIn{
        from{opacity:0; transform:translateX(-12px)}
        to{opacity:1; transform:translateX(0)}
      }
      #viewPlaylist.active .pl-art{animation:openPop .5s .34s cubic-bezier(.22,1,.36,1) backwards}
      #viewPlaylist.active .pl-now{animation:openRise .45s .42s cubic-bezier(.22,1,.36,1) backwards}
      #viewPlaylist.active .pl-progress, #viewPlaylist.active .pl-time{animation:openFade .45s .48s cubic-bezier(.22,1,.36,1) backwards}
      #viewPlaylist.active .pl-controls{animation:openRise .45s .54s cubic-bezier(.22,1,.36,1) backwards}
      #viewPlaylist.active .pl-back-row{animation:openRise .45s .6s cubic-bezier(.22,1,.36,1) backwards}
      /* ---------- Animasi tutup empat halaman yang sama: kebalikan rapih
         dari animasi bukanya. Saat tombol X ditekan, halaman mengecil dan
         memudar kembali ke arah tombol hub asalnya memakai titik asal yang
         sama (--open-ox/--open-oy), hanya transform dan opacity, lalu
         JavaScript baru mengganti ke slide 2 begitu animasinya selesai.
         Kelas .closing ditambah lewat JavaScript dan dicabut lagi sebelum
         pindah, jadi halaman selalu bersih saat dibuka berikutnya. ---------- */
      #viewPlaylist.closing, #viewFoto.closing, #viewNotes.closing, #viewSecret.closing{
        animation:pageClose .62s cubic-bezier(.22,1,.36,1) both;
        transform-origin:var(--open-ox,50%) var(--open-oy,320px);
        pointer-events:none;
        /* Halaman yang menutup diangkat jadi lapisan komposit sendiri:
           skala dan pudarnya digerakkan GPU, bukan dicat ulang per frame */
        will-change:transform, opacity;
      }
      @keyframes pageClose{
        from{opacity:1; transform:scale(1) translateY(0)}
        to{opacity:0; transform:scale(.955) translateY(10px)}
      }
      /* ---------- Peredam lag saat menutup halaman (tombol X) ----------
         Selama 0,62 detik animasi tutup berjalan, TIDAK ada lapisan lain
         yang boleh ikut bergerak, supaya HP hanya menggerakkan satu
         lapisan transform/opacity dan tidak ada dua tampilan yang terlihat
         bertumpuk atau berbayang:
         1) Semua animasi di dalam halaman yang sedang menutup dijeda
            (perangko melayang, hiasan, kilau kaca, Ken Burns, dan lain
            lain). Animasi tutup di elemen halamannya sendiri tidak ikut
            dijeda karena aturan ini hanya menyasar keturunannya.
         2) Latar bersama .page-bg dan pemandangan .scenery di belakang
            halaman ikut dijeda lewat kelas view-closing di body yang
            dipasang JavaScript selama animasi tutup dan dicabut tepat
            saat halaman lama disembunyikan. */
      #viewPlaylist.closing *, #viewFoto.closing *, #viewNotes.closing *, #viewSecret.closing *{
        animation-play-state:paused !important;
      }
      body.view-closing .page-bg *, body.view-closing .page-bg::before, body.view-closing .page-bg::after,
      body.view-closing .scenery, body.view-closing .scenery *{
        animation-play-state:paused !important;
      }
      /* ---------- Transisi buka yang sekuensial (anti blink/berbayang) ----------
         Dulu halaman baru langsung tampil sementara halaman lama dan
         latarnya masih beranimasi, jadi selama transisi dua tampilan
         terlihat bersamaan saling tembus (tampak di rekaman Kevin).
         Sekarang MEMBUKA halaman pun dua tahap: halaman lama memudar
         keluar sendirian (.leaving, hanya transform dan opacity),
         disembunyikan total, BARU halaman baru muncul dengan animasi
         bukanya yang sudah ada. Selama kedua tahap itu kelas
         view-transitioning di body menjeda semua animasi latar berat
         (pemandangan, latar bersama, latar slide 2, awan, partikel,
         ore), jadi HP hanya menggerakkan satu lapisan saja. */
      .view.leaving{
        animation:viewOut .24s cubic-bezier(.22,1,.36,1) both;
        pointer-events:none;
        will-change:transform, opacity;
      }
      @keyframes viewOut{
        from{opacity:1; transform:translateY(0) scale(1)}
        to{opacity:0; transform:translateY(8px) scale(.996)}
      }
      body.view-transitioning .page-bg *, body.view-transitioning .page-bg::before, body.view-transitioning .page-bg::after,
      body.view-transitioning .scenery, body.view-transitioning .scenery *,
      body.view-transitioning .hub-bg, body.view-transitioning .hub-bg *,
      body.view-transitioning .sky-decor *, body.view-transitioning .hub-particles *,
      body.view-transitioning .hub-ores *{
        animation-play-state:paused !important;
      }
      /* Selama game Block Runner dimainkan (kelas run-playing di body,
         diatur JS dari status game), animasi latar berat yang sama
         dijeda total — awan, bintang, partikel, ore, kucing, dekor
         langit di halaman tanda tanya, dan Ken Burns foto Nayla — supaya
         GPU bebas menggambar kanvas game 60fps tanpa berebut dengan
         puluhan animasi latar; gerakan game jadi mulus tidak
         patah-patah. Semua animasi lanjut lagi dari titik yang sama
         begitu permainan berhenti (kalah, menang, atau halaman ditutup),
         persis pola jeda is-scrolling dan view-transitioning di atas.
         Kanvas game sendiri digambar lewat JavaScript, bukan animasi
         CSS, jadi permainannya tidak ikut terjeda. */
      body.run-playing .page-bg *, body.run-playing .page-bg::before, body.run-playing .page-bg::after,
      body.run-playing .scenery, body.run-playing .scenery *,
      body.run-playing .hub-bg, body.run-playing .hub-bg *,
      body.run-playing .sky-decor *, body.run-playing .hub-particles *,
      body.run-playing .hub-ores *, body.run-playing .cat-layer *,
      body.run-playing #viewSecret .trait-photo img{
        animation-play-state:paused !important;
      }
      @media (prefers-reduced-motion: reduce){
        .view.leaving{animation:none}
      }
      /* Gerak dikurangi: animasi pembuka langsung tampil tanpa jeda bertahap */
      @media (prefers-reduced-motion: reduce){
        #viewPlaylist.active *, #viewFoto.active *, #viewNotes.active *, #viewSecret.active *{
          animation-delay:0s !important;
        }
        /* Animasi tutup dimatikan juga: X langsung kembali ke slide 2 */
        #viewPlaylist.closing, #viewFoto.closing, #viewNotes.closing, #viewSecret.closing{
          animation:none;
        }
        /* Goyangan melayang perangko dimatikan: perangko hanya masuk
           sekali lewat stampIn lalu diam di posisi alaminya */
        #viewFoto.active .stamp{animation-name:stampIn}
        /* Hiasan One Piece juga diam: topi, bendera, hati, dan bintang
           hanya masuk sekali tanpa goyangan berulang, dan ketiga camar
           berhenti tersebar diam di bagian atas jendela */
        #viewFoto.active .op-flag{animation-name:opDecorIn}
        #viewFoto.active .op-hat{animation-name:opHatIn}
        #viewFoto.active .op-heart, #viewFoto.active .op-star{animation-name:opDecorIn}
        #viewFoto.active .op-gull{animation:none; opacity:1}
        #viewFoto.active .op-gull svg{animation:none}
        /* Kapal juga diam: hanya masuk sekali, tanpa berlayar dan tanpa
           goyangan ombak */
        #viewFoto.active .op-ship{animation-name:opShipIn}
        #viewFoto.active .op-ship svg{animation:none}
        .op-gull--1{left:9%}
        .op-gull--2{left:44%}
        .op-gull--3{left:73%}
      }
      /* Gerak dikurangi: animasi lucu yang berulang di notes dan kalender
         dimatikan, sisanya diam di posisi alaminya */
      @media (prefers-reduced-motion: reduce){
        .notes{animation:none}
        #viewNotes.active .notes{animation:noteCardIn .7s cubic-bezier(.22,1,.36,1) backwards}
        #viewNotes.active .pxcal{animation:openFade .6s cubic-bezier(.22,1,.36,1) backwards}
        .note-bee, .note-grass, .pxcal-day, .pxcal-star, .note-spark{animation:none}
        .pxcal-star{opacity:.5}
        .note-spark{opacity:0}
      }

      /* ---------- Penyesuaian perangkat: komposisi HP yang sama persis,
         diskalakan mengikuti layar. Di HP tidak ada yang berubah; tablet,
         laptop, dan desktop mendapat panggung yang sama dengan ukuran yang
         diperbesar proporsional supaya tampilannya sama penuh seperti di HP.
         Perpindahan antar ukuran dihaluskan transisi transform dan lebar,
         jadi mengubah ukuran jendela terasa mengalir, tidak melompat. */
      html{-webkit-text-size-adjust:100%; text-size-adjust:100%}
      body{-webkit-tap-highlight-color:transparent}
      button{touch-action:manipulation}
      #viewMain{min-height:100dvh}
      .wrap{transition:max-width .7s cubic-bezier(.33,1,.68,1), padding .7s cubic-bezier(.33,1,.68,1)}
      .blow-stage{transition:transform .7s cubic-bezier(.33,1,.68,1)}
      .hub-btn{transition:transform .5s cubic-bezier(.22,1,.36,1), box-shadow .5s cubic-bezier(.22,1,.36,1), min-height .65s cubic-bezier(.33,1,.68,1)}
      .trait-win{transition:max-width .7s cubic-bezier(.33,1,.68,1)}

      /* Tablet dan laptop kecil: semua diperbesar sedikit dari versi HP */
      @media (min-width:768px){
        .wrap{max-width:800px}
        .loading-bar{width:min(280px,64vw)}
        .hero{padding-top:56px}
        .hero h2{font-size:clamp(32px,4.2vw,40px)}
        .hub-grid{max-width:600px; gap:18px}
        .hub-btn{min-height:178px}
        .hub-btn img{width:136px; height:136px}
        .sub-head h2{font-size:clamp(24px,3.6vw,32px)}
        .cake-zone{max-width:460px}
        .trait-win{max-width:720px}
      }

      /* Desktop dan laptop besar: panggung lilin diskalakan utuh (isi di
         dalamnya ikut membesar serempak), titik tirai ledakan digeser
         sejauh skala yang sama, topi, tombol, kue, dan kalender ikut
         diperbesar supaya komposisinya sama penuh seperti di HP */
      @media (min-width:1100px){
        .wrap{max-width:880px}
        .blow-stage{transform:scale(1.22); margin:14px 0 30px}
        .blast-wipe{--wipe-x:132px}
        @keyframes stageIn{
          from{opacity:0; transform:scale(1.17)}
          to{opacity:1; transform:scale(1.22)}
        }
        @keyframes stageShake{
          0%,100%{transform:scale(1.22) translate(0,0) rotate(0deg)}
          18%{transform:scale(1.22) translate(-2.2px,.8px) rotate(-.35deg)}
          38%{transform:scale(1.22) translate(2.2px,-.8px) rotate(.35deg)}
          58%{transform:scale(1.22) translate(-1.3px,-.5px)}
          78%{transform:scale(1.22) translate(1px,.4px)}
        }
        .sky-decor--loading .sd-c1{width:148px}
        .sky-decor--loading .sd-c2{width:104px}
        .sky-decor--loading .sd-s1{width:52px}
        .sky-decor--loading .sd-s2{width:42px}
        .sky-decor--loading .sd-s3{width:34px}
        .bd-c1{width:150px}
        .bd-c2{width:108px}
        .bd-c3{width:84px}
        .bd-s1{width:42px}
        .bd-s2{width:32px}
        .bd-s3{width:38px}
        .bd-s4{width:28px}
        .bd-cat-blue{width:140px}
        .bd-cat-orange{width:100px}
        .fe-hat-wrap{width:340px; height:245px}
        .fe-hat-img{width:315px}
        .fe-hat-floor{width:245px}
        .fe-rays{width:500px; height:500px}
        .fe-glow{width:450px; height:450px}
        .sky-decor--main .sd-c1{width:118px}
        .sky-decor--main .sd-c3{width:78px}
        .sky-decor--main .sd-c4{width:72px}
        .sky-decor--main .sd-c5{width:58px}
        .sky-decor--main .sd-c6{width:82px}
        .sky-decor--main .sd-c7{width:66px}
        .sky-decor--main .sd-s1{width:54px}
        .hero{padding-top:64px}
        .hero h2{font-size:46px}
        .hero p{font-size:23px}
        .hub-grid{max-width:660px}
        .hub-btn{min-height:200px}
        .hub-btn img{width:152px; height:152px}
        .cake{transform:scale(1.14); margin:10px auto 24px}
        .pxcal{margin:60px auto 20px}
        @keyframes calFloat{
          0%,100%{transform:scale(1.14) translateY(0)}
          50%{transform:scale(1.14) translateY(-5px)}
        }
        .trait-win{max-width:780px}
        .pl-title{font-size:20px}
        .pl-track{font-size:22px}
        .pl-art{width:160px}
        .pl-controls{max-width:320px}
        #catOrange{width:68px}
        #catBlue{width:104px}
      }

      /* Monitor sangat lebar: kolom tengah sedikit lebih lega dan panggung
         lilin sedikit lebih besar lagi, selebihnya tetap sama */
      @media (min-width:1700px){
        .wrap{max-width:920px}
        .blow-stage{transform:scale(1.34); margin:20px 0 40px}
        .blast-wipe{--wipe-x:145px}
        @keyframes stageIn{
          from{opacity:0; transform:scale(1.29)}
          to{opacity:1; transform:scale(1.34)}
        }
        @keyframes stageShake{
          0%,100%{transform:scale(1.34) translate(0,0) rotate(0deg)}
          18%{transform:scale(1.34) translate(-2.2px,.8px) rotate(-.35deg)}
          38%{transform:scale(1.34) translate(2.2px,-.8px) rotate(.35deg)}
          58%{transform:scale(1.34) translate(-1.3px,-.5px)}
          78%{transform:scale(1.34) translate(1px,.4px)}
        }
      }

      /* Layar pendek (HP landscape atau jendela laptop yang ceper):
         slide 1 dipadatkan supaya seluruh adegan lilin dan barnya
         tetap muat di layar tanpa harus scroll */
      @media (max-height:560px){
        .loading{padding:10px 16px}
        .blow-stage{transform:scale(.78); margin:-20px 0 -14px}
        .blast-wipe{--wipe-x:84px}
        @keyframes stageIn{
          from{opacity:0; transform:scale(.75)}
          to{opacity:1; transform:scale(.78)}
        }
        @keyframes stageShake{
          0%,100%{transform:scale(.78) translate(0,0) rotate(0deg)}
          18%{transform:scale(.78) translate(-2.2px,.8px) rotate(-.35deg)}
          38%{transform:scale(.78) translate(2.2px,-.8px) rotate(.35deg)}
          58%{transform:scale(.78) translate(-1.3px,-.5px)}
          78%{transform:scale(.78) translate(1px,.4px)}
        }
        .loading-bar{height:12px}
        .loading-pct{font-size:14px; margin-top:6px; min-height:20px}
      }

      /* ---------- Perapian performa menyeluruh (anti-lag saat deploy) ----------
         Aturan penutup yang merapikan biaya render di semua slide tanpa
         mengubah satu pun gerakan, durasi, atau timing yang sudah ada:
         1) Lapisan latar besar (hub-bg, page-bg, dekor langit, partikel,
            dan ore) di-contain, jadi repaint di dalamnya tidak pernah
            memicu hitung ulang layout halaman di HP.
         2) Salinan latar bersama (.page-bg) yang sedang tidak tampil
            (opacity 0 dan visibility hidden) animasinya DIJEDA total:
            puluhan awan, bintang, partikel, dan ore di dalamnya tidak
            lagi beranimasi percuma di belakang slide 2. Saat halamannya
            dibuka lagi animasinya lanjut dari titik yang sama.
         3) Pemandangan .scenery dijeda selama slide 1 (kelas
            scenery-paused, diatur JS) karena tertutup rapat layar
            loading yang buram, lalu jalan lagi begitu slide 2 tampil.
         4) Transisi yang menganimasikan properti layout (max-width,
            padding, min-height) dan box-shadow pada elemen besar dibuang;
            yang tersisa hanya transform dan opacity di GPU. Bayangan
            diamnya sendiri tidak berubah.
         5) Di HP kecil: kilau kedua tiap ore berhenti (kilau pertama
            tetap), dan sepertiga
            partikel latar disembunyikan supaya animasi yang berjalan
            bersamaan lebih sedikit. */
      .hub-bg, .page-bg{contain:layout paint style}
      .sky-decor, .hub-particles, .hub-ores{contain:layout paint}
      body:not(.page-bg-on) .page-bg *, body:not(.page-bg-on) .page-bg::before, body:not(.page-bg-on) .page-bg::after{animation-play-state:paused !important}
      .scenery-paused, .scenery-paused *{animation-play-state:paused !important}
      /* 6) Pemandangan .scenery ikut dijeda selama halaman isi mana pun
         terbuka (kelas page-bg-on di body). Lapisan bersama .page-bg
         adalah salinan latar slide 2 yang sama persis dan buram penuh
         dari langit sampai tanahnya, jadi selama Playlist, Galery,
         Notes, tanda tanya, atau kue terbuka, bintang berkelip, awan,
         dan matahari SVG .scenery di bawahnya tertutup rapat dan tidak
         terlihat sama sekali — tetapi dulu tetap beranimasi terus dan
         membebani GPU selama pengguna membaca atau bermain. Sekarang
         animasinya berhenti selama tertutup dan lanjut lagi dari titik
         yang sama begitu kembali ke slide 2; tidak satu piksel pun
         berubah karena yang terlihat selalu lapisan bersama di atasnya. */
      body.page-bg-on .scenery, body.page-bg-on .scenery *{animation-play-state:paused !important}
      /* 7) Kelas jeda luar layar: dipasang dan dicabut skrip
         animAwayBoot lewat IntersectionObserver pada elemen dekoratif
         beranimasi tanpa henti di halaman yang bisa digulir. Elemen
         yang sedang jauh di luar layar (di bawah lipatan Notes yang
         panjang, perangko dan hiasan foto di bawah lipatan HP kecil,
         dan sebagainya) animasinya dijeda supaya tidak ikut membebani
         GPU, lalu lanjut dari titik yang sama jauh sebelum elemennya
         masuk pandangan, jadi tidak ada gerakan yang berubah. */
      .anim-away, .anim-away *{animation-play-state:paused !important}
      .wrap, .trait-win{transition:none}
      .hub-btn{transition:transform .5s cubic-bezier(.22,1,.36,1), opacity .5s cubic-bezier(.22,1,.36,1)}
      .card, .wish{transition:transform .5s cubic-bezier(.22,1,.36,1)}
      @media (max-width:640px){
        .ore .glint-b{animation:none}
        .hub-particles i:nth-child(3n){display:none}
      }

      /* ---------- Perapian scroll di HP (anti ghosting / tearing) ----------
         Rekaman Kevin di halaman Notes menunjukkan dua gejala saat scroll:
         isi halaman berbayang ganda seperti frame sebelumnya tertinggal,
         dan latar langit/tanah robek serta patah patah. Penyebabnya:
         1) Lapisan latar fixed layar penuh (.scenery, .page-bg, dan
            tekstur kotak body::before) tidak punya lapisan komposit GPU
            sendiri, jadi Chrome HP mengecat ulang lapisan lapisan itu
            ubin demi ubin selama scroll dan selalu ketinggalan dari
            gerakan jari: itulah robek dan bayang frame lama.
         2) Di dalam .page-bg ada puluhan animasi yang jalan terus selama
            scroll (awan dan bintang ber-filter drop-shadow, partikel naik
            setinggi layar, cahaya radial berdenyut, kilau ore), ditambah
            SVG .scenery yang berbintang dan berawan: setiap frame animasi
            membatalkan seluruh lapisan latar layar penuh, jadi GPU tidak
            sempat mengecat ubin konten yang baru tersingkap.
         Perbaikannya tanpa mengubah satu pun tampilan, warna, atau isi:
         1) Ketiga lapisan latar fixed diangkat jadi lapisan komposit GPU
            yang stabil lewat translateZ(0): sekali dicat, kompositor
            tinggal menahannya di tempat selama scroll, tanpa repaint,
            jadi latar tidak robek lagi.
         2) Selama pengguna sedang menggulir, JavaScript memasang kelas
            is-scrolling di body yang menjeda SEMUA animasi latar berat
            (pola yang sama seperti jeda saat transisi dan saat menutup
            halaman): GPU hanya memindahkan lapisan konten, scroll jadi
            60fps tanpa ghosting. Kelas dicabut sesaat setelah scroll
            berhenti dan semua animasi lanjut dari titik yang sama,
            persis seperti sebelumnya. */
      .scenery, .page-bg{transform:translateZ(0)}
      body::before{transform:translateZ(0)}
      body.is-scrolling .page-bg *, body.is-scrolling .page-bg::before, body.is-scrolling .page-bg::after,
      body.is-scrolling .scenery, body.is-scrolling .scenery *,
      body.is-scrolling .hub-bg, body.is-scrolling .hub-bg *,
      body.is-scrolling .sky-decor *, body.is-scrolling .hub-particles *,
      body.is-scrolling .hub-ores *, body.is-scrolling .cat-layer *{
        animation-play-state:paused !important;
      }

      /* ---------- Latar diam mutlak saat scroll (tinggi dikunci ke 100lvh) ----------
         Kevin menguji v170 di HP-nya dan latar halaman Notes MASIH ikut
         bergerak naik mengikuti scroll, seperti menempel pada isi halaman.
         Lapisan latarnya memang sudah position:fixed, tetapi inset:0
         membuat TINGGI lapisan mengikuti viewport dinamis Chrome HP:
         bilah alamat menyembunyi saat scroll ke bawah dan muncul lagi
         saat scroll ke atas, jadi lapisan meregang dan menyusut terus
         selama jari bergerak. Seluruh komposisi latar dihitung dari
         tinggi lapisan (gradien langit berhenti di 73%, tanah mulai di
         top:73%, bukit menempel di bottom:26,4%, SVG pemandangan
         di-crop slice mengikuti tinggi, kucing di bottom lapisan), jadi
         langit, bukit, tanah, dan kucing ikut bergeser naik turun
         mengikuti scroll: persis "background ngikut" yang Kevin lihat.
         Perbaikannya tanpa mengubah satu pun warna, isi, atau susunan:
         tinggi keempat lapisan latar fixed (.scenery, .page-bg,
         .cat-layer, dan tekstur body::before) dikunci ke 100lvh, yaitu
         tinggi viewport terbesar yang TIDAK berubah saat bilah alamat
         bergerak, dan lapisan dijangkar ke tepi bawah layar (bottom:0,
         top:auto menggantikan inset:0). Hasilnya selama scroll ukuran
         dan posisi lapisan tidak berubah satu piksel pun: tanah dan
         bukit tetap menempel di tepi bawah seperti sebelumnya, dan yang
         tersingkap saat bilah alamat menyembunyi hanyalah lanjutan
         langit yang sama di tepi atas. Lapisan komposit GPU
         translateZ(0) dan jeda animasi is-scrolling dari perapian
         sebelumnya tetap berlaku penuh, jadi latar diam tanpa robek
         dan tanpa berbayang. height:100vh ditulis lebih dulu sebagai
         cadangan browser lama (di sana vh memang berarti tinggi
         terbesar), disusul 100lvh untuk browser modern. */
      .scenery, .page-bg, .cat-layer, body::before{
        left:0; right:0; top:auto; bottom:0;
        height:100vh; height:100lvh;
      }

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