/* =====================================================================
   Website Ucapan Ulang Tahun — tampilan publik
   Tema: romantis & hangat
   ===================================================================== */

:root{
  --accent:#c96a7f;
  --accent2:#e8a87c;
  --ink:#3a2430;
  --ink-soft:#6d5260;
  --cream:#fff8f4;
  --cream-2:#fdeee8;
  --card:#fffdfb;
  --line:rgba(201,106,127,.18);
  --shadow-sm:0 2px 10px rgba(90,40,60,.07);
  --shadow-md:0 14px 40px rgba(90,40,60,.12);
  --shadow-lg:0 30px 80px rgba(90,40,60,.18);
  --radius:22px;
  --serif:"Cormorant Garamond",Georgia,"Times New Roman",serif;
  --display:"Playfair Display",Georgia,serif;
  --script:"Great Vibes","Segoe Script",cursive;
  --sans:"Plus Jakarta Sans",system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
}

*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{
  margin:0;
  font-family:var(--sans);
  color:var(--ink);
  background:var(--cream);
  line-height:1.7;
  overflow-x:hidden;
}
img{max-width:100%;display:block}
a{color:var(--accent)}

/* --------------------------------------------------------------
   Latar dekoratif
   -------------------------------------------------------------- */
/* height WAJIB, tidak cukup inset:0. Elemen position:fixed diukur
   terhadap layout viewport, yang di Chrome Android LEBIH PENDEK dari
   layar terlihat saat bilah URL menyusut. Selisihnya muncul sebagai
   pita gelap di bawah begitu pengunjung menggulir — latar terlihat
   "terangkat" dan menyisakan celah.

   100lvh = tinggi viewport saat bilah URL tersembunyi, yaitu yang
   terbesar. Dengan itu ia menutupi kedua keadaan. 100vh ditulis lebih
   dulu sebagai cadangan untuk peramban yang belum mengenal lvh; di
   Chrome Android keduanya bernilai sama. */
.bg-wrap{
  position:fixed;inset:0;
  height:100vh;height:100lvh;
  z-index:0;pointer-events:none;overflow:hidden;
}
.bg-wrap::before{
  content:"";position:absolute;inset:-20%;
  background:
    radial-gradient(46% 38% at 12% 8%,  color-mix(in srgb,var(--accent) 26%,transparent) 0%, transparent 62%),
    radial-gradient(44% 40% at 88% 16%, color-mix(in srgb,var(--accent2) 34%,transparent) 0%, transparent 60%),
    radial-gradient(60% 50% at 50% 100%,color-mix(in srgb,var(--accent) 16%,transparent) 0%, transparent 66%),
    linear-gradient(170deg,var(--cream) 0%,var(--cream-2) 48%,var(--cream) 100%);
  /* filter:saturate(1.05) dihapus: 5% saturasi tidak terlihat mata, tapi
     memaksa lapisan komposit terpisah seluas 140% layar (inset:-20%). */
}
.bg-grain{
  position:absolute;inset:0;opacity:.35;mix-blend-mode:multiply;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.32'/%3E%3C/svg%3E");
}
/* width & height WAJIB ditulis. <canvas> adalah replaced element:
   dengan width:auto, inset:0 TIDAK meregangkannya — ia memakai ukuran
   intrinsiknya, yaitu 300x150 bawaan canvas. Selama bertahun-tahun
   efek partikel ini karena itu terkurung di kotak 300x150 di pojok
   kiri atas, tersamarkan oleh lapisan hiasan yang kebetulan mirip.

   Lebih buruk lagi: atribut width/height SEBUAH canvas adalah ukuran
   intrinsiknya. Tanpa ukuran CSS, menyetel cv.width dari clientWidth
   mengubah clientWidth itu sendiri — dan resize berikutnya membaca
   angka yang sudah berlipat lalu melipatnya lagi. Terukur: 1200px
   menjadi 76.800px hanya dalam enam event resize, menuntut 11 GB.
   Chrome Android memicu resize tiap kali bilah URL bergerak, jadi
   menggulir cepat cukup untuk memicunya. */
#petals{position:fixed;inset:0;width:100%;height:100%;z-index:1;pointer-events:none}

/* --------------------------------------------------------------
   Halaman sampul (cover)
   -------------------------------------------------------------- */
.cover{
  position:relative;z-index:3;
  min-height:100vh;min-height:100svh;
  display:grid;place-items:center;
  padding:32px 20px calc(32px + env(safe-area-inset-bottom));
  text-align:center;
}
.cover-inner{width:min(560px,100%);animation:riseIn 1.1s cubic-bezier(.2,.8,.25,1) both}
@keyframes riseIn{from{opacity:0;transform:translateY(28px)}to{opacity:1;transform:none}}

.eyebrow{
  font-family:var(--sans);font-size:.72rem;letter-spacing:.32em;text-transform:uppercase;
  color:var(--accent);font-weight:600;margin:0 0 18px;
}
.cover h1{
  font-family:var(--display);font-weight:600;
  font-size:clamp(2rem,7.6vw,3.7rem);line-height:1.1;margin:0 0 6px;overflow-wrap:break-word;
  letter-spacing:-.015em;
}
.cover .name-script{
  display:block;font-family:var(--script);font-weight:400;
  font-size:clamp(2.5rem,11vw,5.2rem);line-height:1.3;overflow-wrap:break-word;
  background:linear-gradient(115deg,var(--accent),var(--accent2) 78%);
  -webkit-background-clip:text;background-clip:text;color:transparent;
  padding:.06em .08em .22em;   /* ruang untuk ekor huruf sambung */
}
.cover .sub{color:var(--ink-soft);font-size:1.02rem;margin:14px auto 0;max-width:34ch}

/* Saat masih terkunci tidak ada yang bisa diketuk, jadi objek pembukanya
   dikecilkan dan judulnya direndahkan. Yang perlu terbaca lebih dulu di
   layar pertama adalah namanya dan hitung mundurnya. */
body[data-state="locked"] .cover h1{font-size:clamp(1.5rem,4.2vw,2.15rem)}
/* Ditulis dengan .cover h1 .name-script, bukan .cover .name-script:
   themes.css menyetel ukuran ini per jenis huruf dengan kekhususan
   yang sama dan dimuat belakangan, jadi aturan yang lebih pendek
   kalah diam-diam. */
body[data-state="locked"] .cover h1 .name-script{font-size:clamp(2.1rem,8vw,3.2rem);line-height:1.2}
body[data-state="locked"][data-font="soft"] .cover h1 .name-script,
body[data-state="locked"][data-font="modern"] .cover h1 .name-script{font-size:clamp(2.3rem,8.8vw,3.6rem)}
/* 26vh ikut menjaga jendela yang pendek: objeknya mengecil sendiri
   supaya hitung mundurnya tidak terdorong keluar layar. */
body[data-state="locked"] .opener-stage{width:min(190px,46vw,26vh);margin:20px auto 18px}
body[data-state="locked"] .countdown{margin-top:18px}

/* Animasi pembuka kini ada di assets/css/openers.css */

/* Countdown */
.countdown{margin:26px auto 6px;max-width:460px}
.countdown-label{
  font-size:.74rem;letter-spacing:.24em;text-transform:uppercase;
  color:var(--ink-soft);font-weight:600;margin-bottom:14px;
}
.cd-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:10px}
.cd-cell{
  background:rgba(255,255,255,.72);backdrop-filter:blur(8px);
  border:1px solid var(--line);border-radius:16px;padding:14px 6px 11px;
  box-shadow:var(--shadow-sm);
}
.cd-num{
  font-family:var(--display);font-size:clamp(1.5rem,6.5vw,2.1rem);
  font-weight:600;line-height:1;color:var(--accent);font-variant-numeric:tabular-nums;
}
.cd-unit{font-size:.62rem;letter-spacing:.16em;text-transform:uppercase;color:var(--ink-soft);margin-top:7px}
.cd-target{margin-top:16px;font-size:.9rem;color:var(--ink-soft)}
.cd-target b{color:var(--ink);font-weight:600}

/* Tombol */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:9px;
  font-family:var(--sans);font-weight:600;font-size:.95rem;
  padding:15px 30px;border-radius:999px;border:1px solid transparent;
  cursor:pointer;text-decoration:none;transition:transform .2s,box-shadow .25s,background .25s;
}
.btn-primary{
/* --accent-ink: aksen yang digelapkan supaya teks putih di atasnya lolos
   WCAG AA (accent_ink() di includes/themes.php). Fallback ke --accent
   supaya halaman yang belum menyuntikkannya tetap berwarna. Bayangan
   tetap pakai --accent asli: tidak ada teks di atasnya. */
  background:linear-gradient(120deg,var(--accent-ink,var(--accent)),var(--accent2-ink,var(--accent2)));
  color:#fff;box-shadow:0 12px 30px color-mix(in srgb,var(--accent) 38%,transparent);
}
.btn-primary:hover{transform:translateY(-2px);box-shadow:0 18px 40px color-mix(in srgb,var(--accent) 46%,transparent)}
.btn-ghost{background:rgba(255,255,255,.7);color:var(--accent);border-color:var(--line)}
.btn:disabled{opacity:.55;cursor:not-allowed;transform:none}

.hint{margin-top:16px;font-size:.84rem;color:var(--ink-soft)}

/* --------------------------------------------------------------
   Halaman isi
   -------------------------------------------------------------- */
.stage{position:relative;z-index:3;display:none}
.stage.is-active{display:block;animation:fadeUp .9s ease both}
@keyframes fadeUp{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:none}}

.wrap{width:min(880px,100% - 40px);margin-inline:auto}
section.block{padding:clamp(56px,10vw,104px) 0}

.sec-head{text-align:center;margin-bottom:38px}
.sec-kicker{
  font-size:.7rem;letter-spacing:.3em;text-transform:uppercase;
  color:var(--accent);font-weight:700;margin-bottom:12px;
}
.sec-title{font-family:var(--display);font-weight:600;font-size:clamp(1.7rem,5.2vw,2.5rem);margin:0;letter-spacing:-.01em}
.sec-sub{color:var(--ink-soft);margin:12px auto 0;max-width:46ch;font-size:.98rem}

/* Hero */
.hero{padding:clamp(70px,14vw,130px) 0 clamp(30px,6vw,60px);text-align:center}
.hero .greet{font-family:var(--script);font-size:clamp(2rem,8.6vw,4.4rem);line-height:1.25;color:var(--accent);margin:0 0 .12em;padding-bottom:.1em}
.hero .who{
  font-family:var(--display);font-size:clamp(1.9rem,7vw,3.2rem);font-weight:600;
  margin:.28em 0 0;letter-spacing:-.02em;line-height:1.2;
}
.hero .who .cursor{display:inline-block;width:3px;height:.92em;translate:0 .08em;background:var(--accent);animation:blink 1s steps(2) infinite}
@keyframes blink{50%{opacity:0}}
.age-badge{
  display:inline-flex;align-items:center;gap:10px;margin-top:22px;
  padding:9px 20px;border-radius:999px;font-size:.86rem;font-weight:600;
  background:rgba(255,255,255,.75);border:1px solid var(--line);color:var(--accent);
  box-shadow:var(--shadow-sm);
}
.hero .date{margin-top:14px;color:var(--ink-soft);font-size:.94rem}

.ornament{display:flex;align-items:center;gap:16px;justify-content:center;margin:34px auto 0;max-width:280px;color:var(--accent)}
.ornament::before,.ornament::after{content:"";height:1px;flex:1;background:linear-gradient(90deg,transparent,var(--line),transparent)}

/* Surat */
.letter{
  position:relative;background:var(--card);border:1px solid var(--line);
  border-radius:var(--radius);padding:clamp(28px,6vw,54px);
  box-shadow:var(--shadow-md);
}
.letter::before{
  content:"❝";position:absolute;top:-6px;left:24px;font-family:var(--display);
  font-size:4.6rem;color:color-mix(in srgb,var(--accent) 22%,transparent);line-height:1;
}
.letter-body{
  font-family:var(--serif);font-size:clamp(1.12rem,2.6vw,1.35rem);
  line-height:1.85;color:var(--ink);position:relative;
}
.letter-sign{margin-top:30px;text-align:right;font-family:var(--script);font-size:1.85rem;color:var(--accent)}
.letter-closing{margin-top:22px;font-style:italic;color:var(--ink-soft);text-align:center}

/* Galeri polaroid */
/* Susunan & bentuk album foto ada di assets/css/gallery.css */

/* Lightbox */
.lightbox{
  position:fixed;inset:0;z-index:120;display:none;place-items:center;
  background:rgba(42,20,30,.9);backdrop-filter:blur(6px);padding:24px;
}
.lightbox.is-open{display:grid;animation:fadeIn .25s ease}
@keyframes fadeIn{from{opacity:0}to{opacity:1}}
.lightbox img{max-width:min(92vw,1000px);max-height:78vh;border-radius:12px;box-shadow:var(--shadow-lg)}
.lb-cap{color:#fff;text-align:center;margin-top:16px;font-family:var(--serif);font-size:1.1rem;opacity:.92}
.lb-note{
  color:#fff;text-align:center;margin:8px auto 0;max-width:52ch;
  font-family:var(--sans);font-size:.92rem;line-height:1.65;opacity:.72;
}
.lb-btn{
  position:absolute;top:50%;translate:0 -50%;width:48px;height:48px;border-radius:50%;
  background:rgba(255,255,255,.14);border:1px solid rgba(255,255,255,.25);color:#fff;
  font-size:1.3rem;cursor:pointer;display:grid;place-items:center;transition:background .2s;
}
.lb-btn:hover{background:rgba(255,255,255,.28)}
.lb-prev{left:18px}.lb-next{right:18px}
.lb-close{position:absolute;top:20px;right:20px;translate:none}

/* Video */
.video-frame{
  position:relative;border-radius:var(--radius);overflow:hidden;
  border:1px solid var(--line);box-shadow:var(--shadow-md);background:#160c12;
  /* Rasionya ditentukan per layanan: 16/9 untuk YouTube & Drive,
     tegak untuk TikTok, Reels, dan YouTube Shorts. */
  aspect-ratio:var(--ratio,16/9);
}

/* ------------------------------------------------------------------
   Video tegak di layar sempit: lebar diturunkan dari ANGGARAN TINGGI

   Di luar blok ini, tinggi bingkai diturunkan dari lebar lewat
   aspect-ratio — dan tinggi tidak pernah dibatasi. Di layar 393px itu
   membuat TikTok jadi ±795px dan Shorts ±604px: videonya sendirian
   lebih tinggi daripada layar, jadi harus digulir.

   Di sini arahnya dibalik: ambil tinggi layar, kurangi perabot di
   sekitarnya, lalu kalikan rasio untuk mendapat lebar maksimum.
   --ratio selalu pasangan angka telanjang (9/16, 325/760) dari
   video_source(), jadi calc(panjang * var(--ratio)) sah.

   SENGAJA dibatasi ke layar sempit. Percobaan pertama memasangnya
   tanpa media query, dan di laptop 1280x800 video tegak menyusut dari
   340px jadi 201px — anggaran tingginya ikut berlaku di sana. Masalah
   ini hanya ada di HP, jadi perbaikannya juga hanya di HP.
   ------------------------------------------------------------------ */
@media (max-width:700px){
  .video-frame{
    --v-chrome:120px;                        /* judul, keterangan, padding */
    --v-cap:calc(100svh - var(--v-chrome));  /* tinggi maksimum yang boleh */
    width:min(100%, calc(var(--v-cap) * var(--ratio,16/9)));
    margin-inline:auto;
  }
}
/* Video tegak tetap dibatasi lebarnya — kalau tidak, di layar laptop
   bingkainya jadi menara setinggi 1200px. */
.video-frame.is-tall{max-width:min(340px,100%);margin-inline:auto}
.video-frame iframe,.video-frame video{position:absolute;inset:0;width:100%;height:100%;border:0}
/* Instagram & TikTok menggambar pemutarnya sendiri di atas latar putih —
   samakan supaya tidak ada kedipan gelap sewaktu memuat. */
.video-frame[data-provider="instagram"],
.video-frame[data-provider="tiktok"]{background:#fff}

/* --------------------------------------------------------------------
   LEBAR RANCANGAN — TikTok & Instagram
   --------------------------------------------------------------------
   Sematan keduanya TIDAK menyusun ulang tata letaknya di bawah lebar
   rancangan masing-masing. Isinya meluber, lalu terpangkas oleh
   overflow:hidden — terpotong kiri-kanan DAN atas-bawah sekaligus,
   sehingga videonya tampak lebih pendek dan lebar dari aslinya.

   Aturan dasar di atas menurunkan lebar dari anggaran TINGGI
   (--v-cap × --ratio). Di HP itu menghasilkan ±250px untuk TikTok,
   padahal ia dirancang 325px. Selisih 75px itulah yang terpotong.

   Di sini arahnya dibalik: lebarnya dipatok pada lebar rancangan
   (dibatasi 100% supaya tidak pernah melebihi layar), dan tingginya
   mengikuti --ratio yang memang benar PADA lebar tersebut.

   Akibatnya bingkai bisa lebih tinggi dari layar dan ruangnya sedikit
   menggulir. Itu pilihan yang disengaja: lebih baik menggulir sedikit
   daripada videonya terpotong di keempat sisi.
   -------------------------------------------------------------------- */
/* Tinggi dipangkas sampai batas bawah area video, membuang bilah
   "Watch now" dan blok keterangan di bawahnya. Isi iframe milik
   tiktok.com tidak bisa kita sentuh sama sekali — menyembunyikan
   elemen di dalamnya mustahil — jadi memendekkan bingkainya adalah
   satu-satunya cara yang tersedia.

   612px diukur dari tangkapan layar perangkat asli pada lebar 325px:
   bingkai 760px, area video berakhir di ±612px. Angkanya ditulis
   sebagai variabel karena TikTok bisa mengubah tata letaknya kapan
   saja — kalau nanti keterangannya muncul lagi atau videonya justru
   ikut terpotong, cukup angka ini yang disesuaikan.

   Lebarnya TETAP 325px. Memangkas tinggi aman; memangkas lebar
   membuat isinya meluber dan terpotong di keempat sisi. */
.video-frame[data-provider="tiktok"]{
  --v-design-w:325px;      /* lebar, dengan satuan — dipakai width */
  aspect-ratio:325 / 612;  /* rasio, TANPA satuan — aspect-ratio menolak px */
}
.video-frame[data-provider="instagram"]{--v-design-w:400px}
.video-frame[data-provider="tiktok"],
.video-frame[data-provider="instagram"]{
  width:min(100%, var(--v-design-w));
  max-width:none;                     /* .is-tall mematok 340px — terlalu sempit untuk Instagram */
}
.video-cap{text-align:center;margin-top:16px;color:var(--ink-soft);font-size:.94rem}
.video-link{
  display:block;text-align:center;padding:26px;border-radius:var(--radius);
  border:1px dashed var(--line);background:var(--card);color:var(--accent);text-decoration:none;
}

/* Buku tamu */
.gb-list{margin-top:34px;display:grid;gap:14px}
.gb-card{
  background:rgba(255,255,255,.8);border:1px solid var(--line);border-radius:18px;
  padding:18px 20px;box-shadow:var(--shadow-sm);
}
.gb-card .who{font-weight:700;font-size:.95rem;color:var(--accent)}
.gb-card .when{font-size:.75rem;color:var(--ink-soft);margin-left:8px;font-weight:400}
.gb-card p{margin:8px 0 0;color:var(--ink);font-size:.97rem;line-height:1.65}
.gb-empty{text-align:center;color:var(--ink-soft);font-style:italic;padding:26px}

/* Footer */
.site-foot{
  position:relative;z-index:3;text-align:center;padding:56px 20px 70px;
  color:var(--ink-soft);font-size:.86rem;
}
.site-foot .heart{font-size:1.5rem;color:var(--accent);display:block;margin-bottom:12px;animation:beat 1.8s ease-in-out infinite}
@keyframes beat{0%,100%{transform:scale(1)}14%{transform:scale(1.22)}28%{transform:scale(1)}42%{transform:scale(1.16)}}

/* Kontrol melayang */
.floaters{position:fixed;right:16px;bottom:calc(16px + env(safe-area-inset-bottom));z-index:60;display:grid;gap:10px}
.fbtn{
  width:48px;height:48px;border-radius:50%;border:1px solid var(--line);
  background:rgba(255,255,255,.86);backdrop-filter:blur(10px);
  color:var(--accent);font-size:1.15rem;cursor:pointer;display:grid;place-items:center;
  box-shadow:var(--shadow-md);transition:transform .2s,background .2s;
}
.fbtn:hover{transform:translateY(-3px)}
.fbtn.is-off{color:#b9a6ae}

/* Bar progres baca */
.progress{position:fixed;top:0;left:0;height:3px;z-index:80;width:0;
  background:linear-gradient(90deg,var(--accent),var(--accent2));transition:width .1s linear}

/* Reveal saat digulir */
.reveal{opacity:0;transform:translateY(26px);transition:opacity .8s cubic-bezier(.2,.8,.25,1),transform .8s cubic-bezier(.2,.8,.25,1)}
.reveal.is-in{opacity:1;transform:none}

/* Halaman pesan (404 / belum terbit) */
.notice{
  position:relative;z-index:3;min-height:100svh;display:grid;place-items:center;
  text-align:center;padding:40px 22px;
}
.notice-card{
  background:var(--card);border:1px solid var(--line);border-radius:var(--radius);
  padding:clamp(30px,7vw,54px);box-shadow:var(--shadow-md);width:min(460px,100%);
}
.notice-card h1{font-family:var(--display);font-size:1.7rem;margin:14px 0 10px}
.notice-card p{color:var(--ink-soft);margin:0}
.notice-emoji{font-size:2.6rem}

@media (max-width:520px){
  .cd-grid{gap:7px}
  .cd-cell{padding:12px 4px 9px;border-radius:13px}
  .polaroid{padding:10px 10px 40px}
  .lb-btn{width:42px;height:42px}
  .lb-prev{left:8px}.lb-next{right:8px}
}

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

@media print{
  .floaters,#petals,.progress,.lightbox{display:none!important}
  body{background:#fff}
}

/* --------------------------------------------------------------
   Sapaan sekilas (dipakai setelah PIN gerbang benar)
   -------------------------------------------------------------- */
.toast{
  position:fixed;left:50%;top:calc(18px + env(safe-area-inset-top));
  transform:translateX(-50%);
  z-index:120;max-width:min(420px,calc(100% - 32px));
  padding:12px 20px;border-radius:999px;
  background:var(--card);border:1px solid var(--line);
  box-shadow:var(--shadow-md);
  font-family:var(--sans);font-size:.9rem;font-weight:600;color:var(--ink);
  text-align:center;line-height:1.4;
  animation:toastIn .5s cubic-bezier(.2,1.3,.4,1) both, toastOut .5s ease 4.4s both;
}
@keyframes toastIn{from{opacity:0;transform:translate(-50%,-18px) scale(.92)}to{opacity:1;transform:translate(-50%,0) scale(1)}}
@keyframes toastOut{to{opacity:0;transform:translate(-50%,-14px)}}
@media (prefers-reduced-motion: reduce){.toast{animation:none}}

/* Tombol ♪ berdenyut selagi musik latar menunggu sentuhan pertama
   (browser memblokir suara sampai pengunjung menyentuh halaman). */
/* Cincin denyut digambar di pseudo-element dan dianimasikan dengan
   transform+opacity. Versi lama menganimasikan box-shadow, yang tidak
   bisa dikomposit — tiap frame melukis ulang tombol beserta sekitarnya,
   tepat di detik halaman paling sibuk. */
.fbtn.is-waiting{position:relative}
.fbtn.is-waiting::after{
  content:"";position:absolute;inset:0;border-radius:50%;pointer-events:none;
  box-shadow:0 0 0 9px color-mix(in srgb,var(--accent) 45%,transparent);
  animation:bgmWait 1.9s ease-in-out infinite;
}
@keyframes bgmWait{
  0%,100%{transform:scale(.86);opacity:.55}
  50%    {transform:scale(1);   opacity:0}
}
@media (prefers-reduced-motion: reduce){
  .fbtn.is-waiting::after{animation:none;box-shadow:none}
  /* Pengguna yang meminta gerak berkurang hampir selalu juga terbantu
     kalau efek berat ikut dimatikan. */
  .bg-grain{mix-blend-mode:normal}
  .gb-card,.polaroid,.fbtn{backdrop-filter:none;-webkit-backdrop-filter:none}
}

/* --------------------------------------------------------------
   Atribut hidden harus benar-benar menyembunyikan
   --------------------------------------------------------------
   Browser menyembunyikan [hidden] lewat display:none bawaannya,
   tapi bobotnya paling rendah — aturan sekecil .rec-row{display:flex}
   pun sudah membatalkannya, dan elemen yang mestinya tersembunyi
   tetap terlihat. Aturan ini menutup lubang itu untuk seterusnya.
   -------------------------------------------------------------- */
[hidden]{display:none !important}

/* Media di halaman ucapan tidak bisa diseret ke desktop atau disimpan
   lewat tekan lama (menu "Simpan gambar" iOS). Klik kanan ditutup di
   site.js dan kata.js. Hanya halaman bertanda data-lindungi (view.php,
   kata.php), supaya halaman jualan tidak ikut. Ini menutup jalan yang
   mudah, bukan jaminan: tangkapan layar tetap tidak bisa dicegah situs. */
[data-lindungi] img,[data-lindungi] video,[data-lindungi] canvas{
  -webkit-user-drag:none;-webkit-touch-callout:none;-webkit-user-select:none;user-select:none}

/* Spanduk pratinjau untuk ucapan yang belum dibayar */
/* sticky, BUKAN fixed. Waktu fixed, tingginya harus dikompensasi
   padding di tempat lain — dan angka tetap 46px hanya benar selama
   pitanya muat satu baris. Di kolom pratinjau editor yang selebar
   420px ia membungkus jadi tiga baris dan menutupi judul ucapannya.
   Sticky memakan ruangnya sendiri, jadi tidak ada yang perlu
   dikompensasi dan tidak ada lebar yang bisa salah. */
.pay-banner{
  position:sticky;top:0;z-index:130;
  padding:11px 18px;text-align:center;
  background:linear-gradient(135deg,#8a5a12,#c08a2a);color:#fff;
  font-family:var(--sans);font-size:.86rem;line-height:1.5;
  box-shadow:0 6px 20px rgba(90,60,10,.28);
}
.pay-banner a{color:#fff;font-weight:700;text-decoration:underline;margin-left:6px;white-space:nowrap}

/* --------------------------------------------------------------
   Tombol bagikan ke story / status
   -------------------------------------------------------------- */
.fbtn .fb-ico{display:grid;place-items:center;width:20px;height:20px;color:currentColor}
.fbtn .fb-ico svg{width:100%;height:100%}
.fbtn.is-busy{opacity:.6;pointer-events:none}
.fbtn.is-busy .fb-ico{animation:shSpin 1s linear infinite}
@keyframes shSpin{to{transform:rotate(360deg)}}

.share-note{
  position:fixed;left:50%;bottom:calc(22px + env(safe-area-inset-bottom));
  transform:translateX(-50%);z-index:125;
  max-width:min(360px,calc(100% - 40px));margin:0;
  padding:11px 20px;border-radius:999px;text-align:center;
  background:var(--card);border:1px solid var(--line);box-shadow:var(--shadow-md);
  font-family:var(--sans);font-size:.86rem;font-weight:600;color:var(--ink);
  animation:shIn .35s ease both;
}
@keyframes shIn{from{opacity:0;transform:translate(-50%,10px)}to{opacity:1;transform:translate(-50%,0)}}
@media (prefers-reduced-motion: reduce){
  .fbtn.is-busy .fb-ico{animation:none}
  .share-note{animation:none}
}

/* Kartu Kata Mereka di dalam ucapan memakai kepala, teks, dan deretan
   foto yang sama dengan halaman publiknya — supaya keduanya terasa satu
   produk. Gaya .kata-atas / .kata-pesan / .kata-fotos ada di kata.css;
   di sini hanya yang khusus untuk kartu di dalam ruang. */
.gb-card .kata-atas{display:flex;align-items:center;gap:12px;margin-bottom:14px}
.gb-card .kata-avatar{
  flex:0 0 auto;width:40px;height:40px;border-radius:50%;
  display:grid;place-items:center;
  font-family:var(--sans);font-weight:700;font-size:1rem;color:#fff;
  background:linear-gradient(135deg,var(--accent-ink,var(--accent)),var(--accent2-ink,var(--accent2)));
}
.gb-card .kata-id{display:flex;flex-direction:column;min-width:0}
.gb-card .kata-id b{font-family:var(--sans);font-size:.92rem;font-weight:700;color:var(--ink);line-height:1.3}
.gb-card .kata-pesan{margin:0;font-family:var(--serif);line-height:1.72;overflow-wrap:anywhere}
.gb-card .kata-fotos{display:flex;flex-wrap:wrap;gap:8px;margin-top:14px}
.gb-card .kata-foto{
  display:block;width:80px;height:80px;flex:0 0 auto;
  border-radius:12px;overflow:hidden;position:relative;cursor:zoom-in;
  border:1px solid var(--line);
}
.gb-card .kata-foto img{width:100%;height:100%;object-fit:cover;display:block}
/* ====================================================================
   ALBUM VIDEO — pemutar besar + strip sampul
   --------------------------------------------------------------------
   Hanya muncul kalau videonya lebih dari satu. Satu video tetap
   digambar persis seperti sebelumnya, tanpa pembungkus tambahan.
   ==================================================================== */
.vid-album{display:flex;flex-direction:column;gap:12px}

/* Panggung tidak diberi tinggi tetap: .video-frame di dalamnya yang
   menentukan, dan tingginya berubah mengikuti rasio video yang sedang
   diputar — TikTok tegak, YouTube mendatar, unggahan apa adanya. */
.vid-stage{min-width:0}

/* --------------------------------------------------------------
   Strip sampul
   --------------------------------------------------------------
   Digulir menyamping di dalam wadahnya sendiri, tidak pernah
   menyeret halaman. scroll-snap membuat tiap sampul berhenti rapi,
   pola yang sama dengan kartu Kata Mereka.
   -------------------------------------------------------------- */
.vid-strip{
  display:flex;gap:10px;
  margin:0;padding:2px 2px 8px;list-style:none;
  overflow-x:auto;overscroll-behavior-x:contain;
  scroll-snap-type:x proximity;
  -webkit-overflow-scrolling:touch;
  scrollbar-width:thin;
}
.vid-strip::-webkit-scrollbar{height:4px}
.vid-strip::-webkit-scrollbar-thumb{background:var(--line);border-radius:4px}
.vid-strip li{flex:0 0 auto;scroll-snap-align:start;list-style:none}

/* KOTAK SERAGAM. Sampul asli berbeda-beda bentuknya — YouTube 16/9,
   TikTok tegak, unggahan apa saja. Kalau tingginya dibiarkan mengikuti
   masing-masing, strip jadi bergerigi dan mata melompat saat digeser.
   aspect-ratio:1 + object-fit:cover menyamakannya tanpa menggepengkan
   gambarnya — yang terpotong hanya sisi terpanjangnya. */
.vid-cover{
  position:relative;display:block;
  width:84px;aspect-ratio:1;
  border-radius:14px;overflow:hidden;
  border:1px solid var(--line);
  /* Buram, bukan transparan: sampul melayang di atas foto latar, dan
     warna 12% transparan membuatnya nyaris tak terbaca di sana. */
  background:color-mix(in srgb,var(--card) 88%,var(--accent));
  text-decoration:none;color:var(--ink);
  transition:transform .18s ease,border-color .18s ease,box-shadow .18s ease;
}
/* Ikut position:absolute supaya urutan DOM yang menentukan siapa di
   atas. Tanpa ini, .vid-cover-mark yang absolut selalu dicat DI ATAS
   gambar yang tidak diposisikan — nama layanannya menimpa sampulnya. */
.vid-cover img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
.vid-cover:hover,.vid-cover:focus-visible{
  transform:translateY(-2px);
  border-color:color-mix(in srgb,var(--accent) 55%,transparent);
  outline:none;
}
.vid-cover.is-on{
  border-color:var(--accent);
  box-shadow:0 0 0 2px color-mix(in srgb,var(--accent) 40%,transparent);
}

/* Sampul untuk yang tidak punya gambar: TikTok, Instagram, Vimeo, dan
   berkas unggahan. Nama layanannya dicetak supaya pengunjung tahu apa
   yang akan terbuka, bukan kotak abu-abu tanpa keterangan. */
.vid-cover-mark{
  position:absolute;inset:0;display:grid;place-items:center;
  padding:6px;text-align:center;
  font-family:var(--sans);font-size:.66rem;font-weight:700;
  letter-spacing:.02em;line-height:1.2;
  color:color-mix(in srgb,var(--accent) 85%,var(--ink));
}
/* Di POJOK, bukan di tengah. Percobaan pertama menaruhnya di tengah dan
   ia menutupi nama layanan pada sampul yang tidak bergambar — justru
   satu-satunya petunjuk isi sampul itu. */
.vid-cover-play{
  position:absolute;right:5px;top:5px;
  width:22px;height:22px;border-radius:50%;
  display:grid;place-items:center;
  font-size:.54rem;color:#fff;
  background:rgba(0,0,0,.55);
  padding-left:2px;                 /* segitiga ▶ terlihat miring kalau tidak digeser */
}
.vid-cover-num{
  position:absolute;left:5px;bottom:4px;
  min-width:16px;padding:1px 4px;border-radius:6px;
  font:700 .62rem/1.4 var(--sans);text-align:center;
  color:#fff;background:rgba(0,0,0,.55);
}

.vid-count{
  margin:0;text-align:center;
  font-family:var(--sans);font-size:.78rem;color:var(--ink-soft);
  font-variant-numeric:tabular-nums;
}

@media (prefers-reduced-motion: reduce){
  .vid-cover{transition:none}
}

/* ── Ruang Perjalanan: linimasa momen ──────────────────────────────
   Seluruhnya CSS. Tidak ada satu pun bagian cerita yang menunggu
   JavaScript untuk muncul — linimasa adalah isi, bukan hiasan.

   Rel garisnya digambar dengan ::before pada <ol>, bukan border-left
   tiap <li>. Kalau tiap butir menggambar garisnya sendiri, jarak antar
   kartu memutus garis itu jadi ruas-ruas terpisah. */
.jrn{
  list-style:none;margin:0;padding:0 0 0 30px;position:relative;
  display:flex;flex-direction:column;gap:22px;
}
/* Rel digambar per butir, bukan sekali di <ol>.

   Satu garis sepanjang <ol> berhenti di DASAR kartu terakhir, bukan di
   titik terakhir — ekor menggantung beberapa ratus piksel di bawah momen
   penutup, yang terbaca seolah ceritanya belum selesai. Tinggi kartu
   tidak diketahui CSS, jadi tidak ada nilai `bottom` yang bisa ditulis.

   Tiap butir menggambar ruas dari titiknya sendiri ke titik berikutnya —
   tinggi butir ditambah jarak antar butir — dan butir terakhir tidak
   menggambar apa pun. Nilai gap di bawah HARUS sama dengan `gap` .jrn;
   kalau berbeda, garisnya putus-putus di sela kartu. */
.jrn-item{position:relative;margin:0}

.jrn-item::before{
  content:"";position:absolute;left:-23px;top:20px;width:2px;
  height:calc(100% - 20px + 22px);
  background:linear-gradient(var(--accent2),var(--line));
  border-radius:2px;
}
.jrn-item:last-child::before{content:none}

.jrn-dot{
  position:absolute;left:-30px;top:12px;
  width:16px;height:16px;border-radius:50%;
  background:var(--card);border:3px solid var(--accent);
  box-shadow:0 0 0 4px var(--cream);
}

.jrn-card{
  background:var(--card);border:1px solid var(--line);
  border-radius:var(--radius);overflow:hidden;
  box-shadow:var(--shadow-sm);
}

/* aspect-ratio memesan tempatnya sebelum gambar termuat, jadi cerita
   di bawahnya tidak melompat saat foto akhirnya datang. */
.jrn-media{margin:0;line-height:0;background:var(--cream-2)}
.jrn-photo{width:100%;height:auto;aspect-ratio:4/3;object-fit:cover;display:block}

.jrn-txt{padding:16px 18px}
.jrn-date{
  margin:0;font-family:var(--sans);font-size:.74rem;font-weight:700;
  letter-spacing:.12em;text-transform:uppercase;color:var(--accent);
}
.jrn-title{margin:4px 0 0;font-family:var(--display);font-size:1.22rem;line-height:1.25;color:var(--ink)}
.jrn-story{margin:8px 0 0;color:var(--ink-soft);line-height:1.65}

@media (min-width:640px){
  .jrn{padding-left:38px;gap:26px}
  .jrn-dot{left:-38px;width:18px;height:18px}
  .jrn-item::before{left:-30px;top:22px;height:calc(100% - 22px + 26px)}
  .jrn-txt{padding:18px 22px}
}

/* ── Amplop Buka Kalau ─────────────────────────────────────────────
   <details> bawaan HTML: isinya tercetak lengkap dan bisa dibuka tanpa
   JavaScript. Gaya di sini hanya membuat tutupnya terbaca sebagai
   amplop berlabel, bukan daftar teks yang bisa dilipat. */
.bk-rak{display:flex;flex-direction:column;gap:12px;max-width:560px;margin:18px auto 0}
.bk-karena{
  margin:0;padding:10px 14px;border-radius:var(--radius);
  background:var(--cream-2);color:var(--ink);font-size:.92rem;text-align:center;
}
.bk-amplop{
  background:var(--card);border:1px solid var(--line);
  border-radius:var(--radius);box-shadow:var(--shadow-sm);overflow:hidden;
}
.bk-amplop>summary{
  display:flex;align-items:center;gap:14px;padding:14px 16px;
  cursor:pointer;list-style:none;
}
.bk-amplop>summary::-webkit-details-marker{display:none}
.bk-amplop>summary:focus-visible{outline:2px solid var(--accent);outline-offset:-2px;border-radius:var(--radius)}

/* Amplop kecil dengan segel. Segelnya kosong setelah dibuka. */
.bk-ikon{
  flex:none;position:relative;width:42px;height:30px;border-radius:5px;
  background:color-mix(in srgb,var(--accent2) 30%,var(--card));
  border:1px solid color-mix(in srgb,var(--accent2) 55%,var(--card));
}
.bk-ikon::before{
  content:"";position:absolute;inset:0;
  clip-path:polygon(0 0,100% 0,50% 58%);
  background:color-mix(in srgb,var(--accent2) 45%,var(--card));
}
.bk-ikon::after{
  content:"";position:absolute;left:50%;top:44%;width:11px;height:11px;border-radius:50%;
  background:var(--accent);transform:translate(-50%,-50%);
}
.bk-amplop[open] .bk-ikon::after{background:transparent;box-shadow:inset 0 0 0 1.5px var(--accent)}

.bk-label{display:grid;flex:1;min-width:0}
.bk-label small{
  font-family:var(--sans);font-size:.68rem;font-weight:700;letter-spacing:.12em;
  text-transform:uppercase;color:var(--ink-soft);
}
.bk-label span{font-family:var(--display);font-size:1.08rem;line-height:1.3;color:var(--ink)}
.bk-jenis{
  flex:none;display:inline-flex;align-items:center;gap:5px;padding:3px 9px;border-radius:999px;
  background:var(--cream-2);color:var(--accent);
  font-family:var(--sans);font-size:.72rem;font-weight:700;
}
.bk-jenis svg{width:12px;height:12px;fill:none;stroke:currentColor;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round}

.bk-isi{display:flex;flex-direction:column;gap:12px;padding:0 16px 16px}
/* width/height eksplisit: pada <img>/<video>, ukuran bawaannya ukuran
   intrinsik, bukan lebar wadah. */
.bk-foto{display:block;width:100%;height:auto;border-radius:calc(var(--radius) - 8px)}
.bk-video{display:block;width:100%;height:auto;max-height:70vh;border-radius:calc(var(--radius) - 8px);background:#000}
.bk-suara{display:block;width:100%}
.bk-tulisan{margin:0;color:var(--ink);line-height:1.75;white-space:normal}
.bk-momen{
  display:grid;gap:2px;padding:10px 12px;border-radius:calc(var(--radius) - 8px);
  background:var(--cream-2);color:var(--ink);text-decoration:none;font-size:.9rem;
}
.bk-momen small{font-size:.72rem;font-weight:700;color:var(--ink-soft)}

/* ── Misi kecil ────────────────────────────────────────────────────
   Lapisan penuh layar yang menutupi isi sebentar. Ia HANYA muncul
   kalau misi.js jalan — bawaannya [hidden], jadi tanpa JavaScript
   lapisan ini tidak pernah ada dan penerimanya langsung melihat
   isinya. Itu memang perilaku yang benar: ini hiasan, bukan gerbang. */
.misi{
  position:fixed;inset:0;z-index:120;
  display:grid;place-items:center;
  padding:24px;
  background:var(--cream);
}
body.misi-on{overflow:hidden}

.misi-in{width:min(420px,100%);text-align:center;position:relative}

.misi-step{display:flex;flex-direction:column;align-items:center;gap:16px}

.misi-n{
  margin:0;font-family:var(--sans);font-size:.72rem;font-weight:700;
  letter-spacing:.14em;text-transform:uppercase;color:var(--accent);
}
.misi-ajakan{
  margin:0;font-family:var(--display);font-size:1.32rem;line-height:1.4;
  color:var(--ink);text-wrap:balance;
}
.misi-ajakan:focus{outline:none}

.misi-pesan{
  margin:0;padding:16px 18px;border-radius:var(--radius);
  background:var(--card);border:1px solid var(--line);
  color:var(--ink);line-height:1.7;box-shadow:var(--shadow-sm);
}

/* Tahan napas — lebar isian ditumbuhkan JS lewat scaleX. */
.misi-napas{
  position:relative;overflow:hidden;
  width:200px;height:56px;border-radius:999px;cursor:pointer;
  border:1px solid var(--accent);background:var(--card);
  font-family:var(--sans);font-size:.95rem;font-weight:700;color:var(--accent);
  touch-action:none;
}
.misi-napas-isi{
  position:absolute;inset:0;transform:scaleX(0);transform-origin:left;
  background:var(--accent2);opacity:.35;transition:transform .06s linear;
}
.misi-napas-teks{position:relative}
.misi-napas.is-tahan{transform:scale(.98)}

/* Pilih perasaan — tidak ada jawaban salah, jadi tidak ada gaya "benar". */
.misi-pilih{display:flex;flex-wrap:wrap;gap:8px;justify-content:center}
.misi-opsi{
  padding:10px 16px;border-radius:999px;cursor:pointer;
  border:1px solid var(--line);background:var(--card);
  font-family:var(--sans);font-size:.9rem;font-weight:600;color:var(--ink-soft);
}
.misi-opsi.is-on{border-color:var(--accent);color:var(--accent);background:var(--cream-2)}

/* Kumpulkan hati */
.misi-hati{display:flex;flex-wrap:wrap;gap:14px;justify-content:center}
.misi-h{
  width:44px;height:44px;border:0;background:none;cursor:pointer;
  font-size:1.6rem;line-height:1;color:var(--accent);
  transition:transform .2s,opacity .2s;
  animation:misiApung 2.6s ease-in-out infinite;
  animation-delay:calc(var(--h) * .18s);
}
.misi-h.is-off{opacity:.18;transform:scale(.7);animation:none;cursor:default}
@keyframes misiApung{
  0%,100%{transform:translateY(0)}
  50%    {transform:translateY(-7px)}
}

.misi-lanjut{
  padding:12px 26px;border-radius:999px;border:0;cursor:pointer;
  background:var(--accent-ink,var(--accent));color:#fff;
  font-family:var(--sans);font-size:.95rem;font-weight:700;
}

/* Selalu terlihat, di setiap langkah — lihat aturan 2 di misi.php. */
.misi-lewat{
  position:absolute;left:50%;transform:translateX(-50%);bottom:-56px;
  border:0;background:none;cursor:pointer;
  font-family:var(--sans);font-size:.84rem;color:var(--ink-soft);
  text-decoration:underline;text-underline-offset:3px;
}

@media (prefers-reduced-motion:reduce){
  .misi-h{animation:none}
}

/* ── Tanda buatan ──────────────────────────────────────────────────
   Tanda "Dibuat dengan …" cukup terlihat untuk dibaca, cukup tenang
   untuk tidak membuat orang enggan membagikan ucapannya. Tanda yang menjengkelkan tidak ikut menyebar,
   dan tanda yang tidak menyebar tidak ada gunanya. */
.wm{position:fixed;z-index:130;pointer-events:auto}

/* Sudut KIRI bawah, dipakai bersama oleh tanda gratis dan tautan lapor.
   Kanan bawah sudah ditempati .floaters (tombol bagikan & tema). Menaruhnya
   di kiri menghindari tabrakan tanpa perlu selektor :has() yang harus tahu
   nama kelas tombol itu, dan karena itu tidak ikut rusak kalau tombolnya
   suatu saat berganti nama.

   Kolom, bukan dua elemen fixed di koordinat yang sama: yang kedua akan
   menindih yang pertama, dan tiap penghuni baru harus tahu tinggi
   tetangganya. safe-area-inset supaya tidak tertutup bilah gestur iPhone. */
.wm-kiri{
  left:12px;bottom:calc(12px + env(safe-area-inset-bottom));
  display:grid;gap:7px;justify-items:start;
}
.wm-gratis{
  padding:7px 13px;border-radius:999px;text-decoration:none;
  background:rgba(255,255,255,.82);backdrop-filter:blur(8px);
  border:1px solid var(--line);
  font-family:var(--sans);font-size:.7rem;font-weight:600;color:var(--ink-soft);
  box-shadow:0 2px 10px rgba(0,0,0,.08);
  transition:background .18s,color .18s;
}
.wm-gratis:hover{background:#fff;color:var(--ink)}

/* Lebih tenang lagi daripada tanda gratis: ini bukan ajakan, cuma jalan
   keluar yang harus ada untuk orang yang mencarinya. */
.wm-lapor{
  padding:5px 11px;border-radius:999px;text-decoration:none;
  background:rgba(255,255,255,.62);backdrop-filter:blur(8px);
  border:1px solid var(--line);
  font-family:var(--sans);font-size:.64rem;font-weight:600;
  color:var(--ink-soft);opacity:.72;
  transition:opacity .18s,background .18s,color .18s;
}
.wm-lapor:hover{opacity:1;background:#fff;color:var(--ink)}

@media (prefers-reduced-motion:reduce){
  .wm-gratis,.wm-lapor{transition:none}
}

/* ── Panel obrolan halaman coba ────────────────────────────────────
   Hanya muncul di pratinjau draf, dan hanya kalau JS jalan — di HTML
   panelnya [hidden], skripnya yang membuka. Ucapannya sendiri sudah
   tercetak lengkap tanpa panel ini. */
.ck{
  position:fixed;right:12px;bottom:calc(12px + env(safe-area-inset-bottom));
  z-index:125;width:min(340px,calc(100vw - 24px));
  font-family:var(--sans);
}
.ck-toggle{
  display:flex;align-items:center;gap:8px;width:100%;
  padding:11px 16px;border-radius:999px;border:1px solid var(--line);
  background:var(--card);cursor:pointer;
  font-size:.86rem;font-weight:700;color:var(--ink);
  box-shadow:0 4px 16px rgba(0,0,0,.10);
}
.ck-body{
  margin-top:8px;border-radius:var(--radius);overflow:hidden;
  background:var(--card);border:1px solid var(--line);
  box-shadow:0 8px 30px rgba(0,0,0,.14);
}
.ck-log{max-height:230px;overflow-y:auto;padding:12px 14px;display:flex;flex-direction:column;gap:8px}
.ck-log p{margin:0;font-size:.84rem;line-height:1.55;padding:8px 11px;border-radius:12px;max-width:88%}
.ck-ai{background:var(--cream-2);color:var(--ink);align-self:flex-start}
.ck-aku{background:var(--accent-ink,var(--accent));color:#fff;align-self:flex-end}

.ck-form{display:flex;gap:6px;padding:10px;border-top:1px solid var(--line)}
.ck-form input[type=text]{
  flex:1;min-width:0;padding:9px 12px;border-radius:999px;
  border:1px solid var(--line);font-family:inherit;font-size:.86rem;
}
.ck-form button{
  padding:9px 15px;border-radius:999px;border:0;cursor:pointer;
  background:var(--accent-ink,var(--accent));color:#fff;font-family:inherit;font-size:.84rem;font-weight:700;
}
.ck-form button:disabled{opacity:.55;cursor:default}

/* Di HP panel menempel ke kedua tepi; tombol melayang digeser ke atas
   supaya tidak saling menutup. */
@media (max-width:520px){
  .ck{left:12px;right:12px;width:auto}
  .ck-log{max-height:180px}
}

/* Pratinjau kartu bagikan di editor (share.js, ?ruang=kartu). Ukuran canvas
   ditulis eksplisit: width/height auto pada canvas berarti 1080x1920 penuh. */
.kartu-pratinjau{
  position:fixed;inset:0;z-index:60;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:10px;padding:16px;
  background:rgba(20,16,26,.62);
}
.kartu-pratinjau canvas{
  height:min(calc(100vh - 70px),calc((100vw - 32px) * 16 / 9));width:auto;
  border-radius:14px;box-shadow:0 18px 50px rgba(0,0,0,.35);
}
.kartu-pratinjau p{margin:0;color:#fff;font-size:.82rem;opacity:.85}
