/* ============================================================
   rank.css — Papan Peringkat Mingguan.
   Butuh: tokens.css, base.css, components.css, overlay.css

   Bentuknya OVERLAY PENUH LAYAR (.fh-ov), bukan bottom sheet —
   dan itu keputusan yang sempat berat, karena papan skor per-ikan
   di Bestiary justru sheet.

   Bedanya isi, bukan selera. Sheet dibatasi 78vh, dan setelah
   dipotong pegangan, judul, panggung hadiah, segmen kategori, dan
   kaki peringkat-sendiri, yang tersisa untuk daftar di layar
   390x800 cuma sekitar lima baris. Papan per-ikan sanggup begitu
   karena dia lanjutan dari kartu ikan yang sedang dibaca — sheet
   memang bacaan sekunder dari sesuatu yang masih terlihat di
   belakangnya. Papan mingguan tidak punya "sesuatu di belakangnya":
   dia dibuka dari rail, isinya tidak ada hubungannya dengan panel
   karakter yang kebetulan ada di bawahnya, dan pemain datang ke
   sini untuk TINGGAL — menggulung 50 nama, berpindah kategori,
   menghitung selisih. Itu mode, dan HANDOFF bagian 4 menaruh mode
   di overlay penuh.

   Tetangganya di baris atas sudah begitu duluan: Chat juga dibuka
   dari .fh-topbar dan juga .fh-ov penuh.

   Yang TIDAK dipakai: .fh-ov--sisip. Nav yang tetap berdiri berguna
   untuk daftar yang isinya masih milik tabnya sendiri (anggota
   klan); papan mingguan bukan milik Home, dan header LV/koin di
   atasnya tidak menjawab satu pun pertanyaan yang dibawa pemain ke
   layar ini.
   ============================================================ */


/* ============================================================
   PANGGUNG HADIAH
   Anak langsung .fh-ov, di antara judul dan badan yang menggulung:
   dia tidak boleh ikut hanyut ke atas saat daftar digulung.

   TOTAL HADIAH yang jadi kepala, bukan jam mundurnya. Angka itu
   yang menjawab "kenapa aku harus peduli"; jam mundur menjawab
   "kapan", dan itu pertanyaan kedua.

   Ini satu-satunya tempat di app yang sengaja dibuat megah, dan
   megahnya dijaga tetap murah: satu gradien, satu pendar radial,
   satu garis emas. Tidak ada animasi. Panggung yang berdenyut di
   atas daftar yang digulung membuat mata tidak pernah bisa diam di
   angka mana pun.
   ============================================================ */

/* Padding atas 14px dan sisi 52px. Sisi yang 52 itu bukan estetika:
   panah mengambang memakan 12 + 32 = 44px di kiri, dan tanpa ruang
   yang sama di KEDUA sisi, kalimat "Total hadiah minggu ini" yang
   ditengahkan akan menabrak panah begitu font sistem dinaikkan.
   Angka hadiahnya sendiri jauh lebih pendek dari lebar yang tersisa,
   jadi tidak ada yang dikorbankan. */
.fh-rkhero{
  flex:none;
  position:relative;overflow:hidden;
  padding:14px 52px 11px;
  text-align:center;
  background:linear-gradient(180deg,
    var(--pixel-surface),var(--pixel-bg));
  border-bottom:1px solid var(--pixel-border);
}

/* Pendar emas di belakang angka. rgba dari --primary-rgb, bukan
   hex baru — aturan "nol hex di luar tokens.css" berlaku juga
   untuk yang cuma jadi latar.

   Ditaruh di ::before dan bukan di background elemennya supaya
   gradien dasar di atas tetap utuh; dua background pada satu
   elemen berarti setiap penyetelan salah satunya harus menulis
   ulang keduanya. */
.fh-rkhero::before{
  content:'';position:absolute;
  left:50%;top:14px;width:260px;height:96px;
  transform:translateX(-50%);
  background:radial-gradient(ellipse at center,
    rgba(var(--primary-rgb),.16),rgba(var(--primary-rgb),0) 70%);
  pointer-events:none;
}

/* Garis emas tipis di bawah panggung, di atas garis pemisah biasa.
   Dia yang membuat panggung terbaca sebagai satu bidang utuh dan
   bukan sekadar bagian atas daftar yang kebetulan berwarna lain. */
.fh-rkhero::after{
  content:'';position:absolute;left:0;right:0;bottom:0;height:1px;
  background:linear-gradient(90deg,
    rgba(var(--primary-rgb),0),
    rgba(var(--primary-rgb),.45) 50%,
    rgba(var(--primary-rgb),0));
}

/* PANAH KEMBALI, mengambang di sudut kiri panggung.

   Absolut dan bukan bagian dari alirannya: panggung ditengahkan
   seluruhnya, dan satu tombol di kiri yang ikut mengalir akan
   menggeser semua yang ditengahkan itu ke kanan sebesar lebarnya.
   Pilihan lain — pengganjal 32px di kanan untuk menyeimbangkan —
   berarti dua elemen kosong hanya untuk meluruskan satu tombol.

   z-index 2 karena ::before (pendar emas) dan ::after (garis emas)
   punya konteks susun sendiri; tanpa itu tombolnya tergambar DI
   BAWAH pendar dan tepinya tampak berkabut.

   Sudut atas, bukan tengah vertikal: di sudut dia terbaca sebagai
   navigasi layar, di tengah dia terbaca sebagai tombol yang ada
   hubungannya dengan angka hadiah di sebelahnya. */
.fh-rkhero__back{
  position:absolute;z-index:2;
  left:var(--sp-md);top:var(--sp-md);
  display:grid;place-items:center;
}

/* Panah pixel, sumber yang sama dengan tombol kembali di sheet
   papan skor per-ikan (.fh-fishhd__x--kiri) dan tombol paginasi.
   shape-rendering:crispEdges WAJIB: tanpa itu panah 5x9 unit
   di-antialias jadi bercak abu-abu. */
.fh-rkhero__back svg{
  width:6px;height:11px;
  fill:currentColor;shape-rendering:crispEdges;
}

/* PODIUM DI SUDUT KANAN, cermin tombol kembali di kiri. Jaraknya
   dari tepi sama persis supaya keduanya duduk di garis yang sama —
   dua sudut yang membingkai panggung.

   ⚠ GETARNYA BERHENTI SENDIRI: 3 putaran, lalu diam.

   Ini satu-satunya jalan masuk ke papan sepanjang masa di seluruh
   app, dan ikon diam di sudut panggung yang sudah ramai angka besar
   tidak akan pernah diketuk. Tapi isyarat yang berdenyut SELAMANYA
   berhenti jadi isyarat dan berubah jadi gangguan — apalagi di layar
   yang dibaca sambil menggulung lima puluh nama.

   Jeda 1,2 detik sebelum mulai: papan mingguan dibuka untuk melihat
   hadiah dan peringkat, dan getaran yang menyambut di frame pertama
   menarik mata menjauh dari yang sedang dituju pemain.

   translate, BUKAN margin/left: yang lain memaksa perhitungan tata
   letak ulang tiap frame, dan ini berjalan di atas daftar yang
   sedang digulung. */
.fh-rkhero__abadi{
  position:absolute;z-index:2;
  right:var(--sp-md);top:var(--sp-md);
  display:grid;place-items:center;
}

/* ⚠ YANG BERGETAR IKONNYA, BUKAN TOMBOLNYA. Sempat dipasang di
   tombolnya, dan yang bergoyang seluruh kotak berbingkai — di sudut
   panggung itu terbaca seperti tombol yang lepas, bukan seperti
   isyarat. Kotaknya diam; isinya yang memanggil. */
.fh-rkhero__abadi svg{
  width:11px;height:11px;
  fill:currentColor;shape-rendering:crispEdges;
}

/* ⚠ GETARNYA DIPASANG LEWAT KELAS, bukan langsung di aturan ikon.

   Animasi CSS berjalan SEKALI seumur elemen: begitu tiga putarannya
   habis, dia tidak pernah hidup lagi selama elemennya tidak
   dibuat ulang. Papan mingguan tidak pernah membuat ulang tombol
   ini, jadi getarnya cuma ada satu kali sejak app dimuat — dan
   pemain yang kembali dari papan sepanjang masa melihat ikon diam.

   Dengan kelas, JS bisa melepas lalu memasangnya kembali untuk
   menyalakan ulang (lihat getarkan() di rank.js). */
.fh-rkhero__abadi.is-getar svg{
  animation:fh-getar 1.1s ease-in-out 1.2s 3;
}

/* Sudut miringnya kecil dan geserannya 1px: yang dicari mata GERAK,
   bukan besarnya. Getaran lebar di sudut layar terbaca seperti
   sesuatu yang rusak. */
@keyframes fh-getar{
  0%,100%   {transform:translateX(0) rotate(0)}
  15%       {transform:translateX(-1px) rotate(-7deg)}
  30%       {transform:translateX(1px) rotate(7deg)}
  45%       {transform:translateX(-1px) rotate(-5deg)}
  60%       {transform:translateX(1px) rotate(4deg)}
  75%       {transform:translateX(0) rotate(0)}
}

@media (prefers-reduced-motion:reduce){
  .fh-rkhero__abadi.is-getar svg{animation:none}
}

.fh-rkhero__cap{
  position:relative;
  display:block;margin-bottom:7px;
  font-size:10px;letter-spacing:.6px;text-transform:uppercase;
  color:var(--pixel-muted);
}

.fh-rkhero__n{
  position:relative;
  display:flex;align-items:center;justify-content:center;gap:9px;
}

/* Koin diperbesar dari 16px bawaannya. Di sebelah angka setinggi
   16px, koin 16px tampil sebagai titik; ukuran ini yang membuatnya
   terbaca sebagai keping. */
.fh-rkhero__n .fh-coin{width:22px;height:22px}

/* ANGKA PENUH, bukan "3,6 M". Ini angka yang dipamerkan, dan
   pembulatan menghapus persis bagian yang bikin dia terasa besar.
   13 digit + pemisah di font pixel 16px ≈ 208px, muat di 390px
   dengan sisa lega.

   text-shadow memakai --pixel-primary-dim, warna yang memang sudah
   dipesan untuk bayangan tombol emas — bukan hitam, yang di atas
   latar biru-gelap terbaca kotor. */
.fh-rkhero__n b{
  font-family:var(--font-pixel);font-size:16px;font-weight:400;
  color:var(--pixel-primary);
  font-variant-numeric:tabular-nums;
  text-shadow:0 2px 0 var(--pixel-primary-dim);
}

/* Baris keterangan: rentang minggu di kiri, jam mundur di kanan,
   dipisah titik tengah. Keduanya keterangan DARI hadiah di atas,
   bukan berita tersendiri — jadi satu baris kecil, bukan dua. */
.fh-rkhero__sub{
  position:relative;
  margin-top:8px;
  display:flex;align-items:baseline;justify-content:center;
  gap:8px;flex-wrap:wrap;
  font-size:11px;color:var(--pixel-muted);
}
.fh-rkhero__sub em{font-style:normal}
.fh-rkhero__sub em::before{
  content:'·';margin-right:8px;color:var(--pixel-border);
}

/* Jam mundurnya emas dan tabular: dia satu-satunya angka di baris
   ini yang berubah, dan digit yang berdetak tanpa tabular-nums
   menggeser teks di sebelahnya tiap detik. */
.fh-rkhero__sub b{
  font-weight:600;color:var(--pixel-primary);
  font-variant-numeric:tabular-nums;
}

/* Minggu yang sudah lewat batas tapi hadiahnya belum masuk.
   Emas dicabut: tidak ada lagi yang bisa dikejar, dan jam mundur
   yang tetap menyala di angka nol terbaca seperti layar macet. */
.fh-rkhero.is-tutup .fh-rkhero__sub b{color:var(--pixel-muted)}

/* Layar pendek: panggung dipadatkan, bukan dibiarkan memakan
   tinggi daftar. Media query ditulis body:not(.fh-scaled) — di
   mode terskala media query mengukur jendela asli, bukan stage,
   jadi hasilnya menyesatkan (HANDOFF bagian 5). */
@media (max-height:680px){
  body:not(.fh-scaled) .fh-rkhero{padding-top:9px;padding-bottom:9px}
  body:not(.fh-scaled) .fh-rkhero__cap{margin-bottom:4px}
  body:not(.fh-scaled) .fh-rkhero__n b{font-size:14px}
}

/* Layar sempit. Hitungannya: 13 digit berpemisah di font pixel 16px
   ≈ 208px, plus koin 22 dan gap 9 = 239px. Di 390px ruang yang
   tersisa setelah padding 52+52 adalah 286px — cukup. Di 320px
   tinggal 216px, dan angkanya akan tertabrak overflow:hidden milik
   panggung tanpa gejala apa pun selain digit yang hilang di kanan.

   Yang dikecilkan angkanya DAN paddingnya, bukan salah satu:
   memangkas padding saja masih kurang 16px, mengecilkan angka saja
   membuang ruang yang sebenarnya ada. */
@media (max-width:359px){
  body:not(.fh-scaled) .fh-rkhero{padding-left:44px;padding-right:44px}
  body:not(.fh-scaled) .fh-rkhero__n{gap:7px}
  body:not(.fh-scaled) .fh-rkhero__n b{font-size:13px}
  body:not(.fh-scaled) .fh-rkhero__n .fh-coin{width:18px;height:18px}
}


/* ============================================================
   DAFTAR
   Bentuk barisnya milik components.css. Yang di sini cuma tiga
   hal yang khusus layar ini.
   ============================================================ */

/* SEGMEN IKUT MENGGULUNG di layar ini — sticky bawaan .fh-lbtop
   dimatikan.

   Di papan skor per-ikan dia menempel, dan alasannya masih benar di
   sana: sheet 78vh cuma memuat lima baris, jadi segmen yang hanyut
   berarti pemain kehilangan satu-satunya kontrol layar itu hampir
   seketika. Di sini badannya setinggi layar penuh — belasan baris
   sekaligus — dan panggung hadiah di atasnya sudah menetap. Pita
   kedua yang ikut menetap membuat daftar terasa mengintip dari
   celah di antara dua bidang yang tidak bergerak.

   ⚠ Konsekuensinya nyata dan disengaja: pemain yang sudah di
   peringkat 40 harus menggulung balik ke atas untuk berpindah
   kategori. Dengan DUA kategori itu ongkos yang wajar; kalau nanti
   kategorinya bertambah jadi tiga atau empat, timbang ulang —
   sticky-nya tinggal dinyalakan lagi dengan menghapus blok ini.

   Latarnya ikut dicabut: dia ada untuk menutupi baris yang lewat di
   belakang pita yang menempel, dan tidak ada lagi yang lewat. */
#fh-rank-ov .fh-lbtop{position:static;background:none}

/* RUANG UNTUK SEMBULAN KARTU PERTAMA. .fh-lblist bawaannya
   padding-atas nol — benar untuk Bestiary yang barisnya rata — dan
   di sini ilustrasi kartu teratas menyembul 12px ke atas, tepat ke
   tempat tombol Poin/Terlangka berdiri. Tanpa ini mahkota
   peringkat 1 memotong segmen kategori. */
#fh-rank-ov .fh-lblist{padding-top:12px}

/* ============================================================
   KARTU PEMAIN

   Bukan baris satu-baris seperti papan skor per-ikan, dan itu
   sengaja. Overlay penuh layar punya tinggi untuk memberi tiap
   pemain wajah, karakternya, dan poinnya sendiri-sendiri; sheet
   78vh milik Bestiary tidak. Karena itu bentuk barisnya turun ke
   berkas layarnya masing-masing — lihat kepala bagian papan skor
   di components.css.

   Susunannya, dari kiri: keping peringkat menempel di sudut kiri
   atas foto, foto profil, lalu nama dengan poin DI BAWAHNYA, dan
   ilustrasi karakter pemain di kanan yang MENYEMBUL ke atas
   melewati tepi kartu.

   Ongkosnya: satu baris jadi ~88px (kartu 70 + jarak 18), jadi
   satu layar memuat delapan pemain, bukan dua belas. Itu yang
   dibeli dengan wajah dan karakter.
   ============================================================ */

/* LATARNYA TIGA LAPIS, bukan satu warna rata.

   Versi pertama memakai --pixel-surface polos dan hasilnya lima
   puluh persegi panjang identik: tidak ada yang membedakan juara
   satu dari peringkat 40 selain angka kecil di sudutnya, dan
   ilustrasi karakter tampak ditempel di atas bidang datar.

   Dari bawah ke atas:
     1. --pixel-surface            bidang dasar
     2. kilau atas                 satu sapuan putih 4,5% yang habis
                                   di 46% tinggi. Ini yang membuat
                                   kartu terbaca sebagai permukaan
                                   yang kena cahaya, bukan lubang.
     3. --wash                     semburat dari kiri, warnanya
                                   ditentukan keadaan kartu: medali
                                   untuk tiga teratas, emas untuk
                                   kartu sendiri, tidak ada untuk
                                   sisanya.

   --wash dipakai sebagai VARIABEL dan bukan menulis ulang seluruh
   `background`: tiga lapis itu harus tetap utuh di semua keadaan,
   dan menulis ulang shorthand-nya di tiap varian berarti tiap
   penyetelan lapisan 1 atau 2 harus diulang di tiga tempat.

   box-shadow inset 1px di puncak menutup kartu dengan garis terang
   setipis rambut — pemisah antara kilau dan tepi kartu, yang tanpa
   itu terbaca sebagai gradien yang bocor keluar. */
.fh-rkrow{
  --wash:transparent;
  position:relative;
  display:flex;align-items:center;gap:11px;
  padding:10px 12px;
  min-height:70px;
  border:1px solid var(--pixel-border);
  border-radius:var(--r-xl);
  background:
    linear-gradient(90deg,var(--wash),transparent 64%),
    linear-gradient(180deg,rgba(255,255,255,.045),rgba(255,255,255,0) 46%),
    var(--pixel-surface);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.05);
}

/* Jarak antar kartu lewat margin, BUKAN gap di .fh-lblist: daftar
   itu dipakai bersama Bestiary, yang barisnya justru harus rapat.

   overflow kartu TIDAK di-hidden, dan itu syarat seluruh efeknya:
   gambar di kanan menyembul 14px di atas tepi kartu. Yang
   menjaga dia tidak menabrak kartu di atasnya adalah margin 18px
   ini — sisa 4px. Kalau margin dikecilkan, yang pertama terlihat
   rusak adalah mahkota karakter baris berikutnya.

   Urutan tumpuknya kebetulan sudah benar tanpa z-index: kartu
   position:relative, dan saudara yang lebih belakang digambar di
   atas yang lebih depan, jadi ilustrasi baris N selalu menimpa
   kartu baris N-1 dan bukan sebaliknya. Jangan menambahkan
   z-index ke .fh-rkrow tanpa memeriksa ini lagi. */
.fh-rkrow + .fh-rkrow{margin-top:18px}


/* ---------- WARNA TIER DI KARTU: DICABUT, SENGAJA ----------
   Kartu Terlangka sekarang POLOS, sama persis dengan kartu Poin.

   ⚠ Ini bukan berarti belum pernah dicoba. ENAM pendekatan sudah
   dijalankan sampai terlihat di layar dan semuanya ditolak: pendar
   dari warna teks, dua pendar sepusat, garis tepi, badge
   bertulisan, petak warna di kartu, dan pendar berbentuk siluet
   ikan yang di-mask dari isi slot.

   Riwayat lengkapnya — kenapa masing-masing gagal, dan angka yang
   sudah diukur — ada di IMPLEMENTASI.md bagian "Papan Peringkat
   Mingguan". BACA DULU sebelum mencoba lagi: beberapa di antaranya
   gagal karena alasan yang tidak kelihatan sampai dilihat di
   browser sungguhan, bukan di simulasi.

   Yang sengaja DITINGGALKAN untuk percobaan berikutnya, walau
   sekarang tidak dipakai siapa pun:

     - token --t-*-glow-rgb di tokens.css dan FH.tierGlowRgb().
       Nilainya diambil dari inti gradient slot dan sudah diperiksa;
       menurunkannya ulang berisiko salah. Hanya untuk GARIS dan
       cahaya tipis — bukan bidang.
     - FH.tierFill(), satu-satunya sumber warna tier yang benar
       untuk BIDANG, karena tiga tier teratas adalah permukaan dan
       bukan satu warna.
   ---------------------------------------------------------- */


/* ---------- KEPING PERINGKAT ----------
   Menempel di sudut kiri atas foto, setengah keluar dari
   lingkarannya. Warna medali datang dari .fh-lbrank di
   components.css — yang ditimpa di sini cuma bentuk dan posisinya,
   supaya pemetaan emas/perak/perunggu tetap tinggal di satu
   tempat.

   Pil, bukan lingkaran: peringkat di sini bisa tiga digit (kaki
   papan menggambar peringkat sebenarnya, bukan cuma 1..50), dan
   lingkaran berdiameter tetap memotong digit ketiga tanpa gejala
   apa pun. min-width menjaga "1" tetap bulat. */
.fh-rkrow .fh-lbrank{
  position:absolute;left:-7px;top:-6px;
  width:auto;min-width:24px;height:21px;
  padding:0 6px;
  border-radius:11px;
  background:var(--pixel-raised);
  box-shadow:inset 0 0 0 1px var(--pixel-border);
  color:var(--pixel-text);
  font-size:9px;
}
.fh-rkrow .fh-lbrank.is-medal{
  border-radius:11px;
  background:var(--m);
  color:var(--ink-on-tier);
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.22);
}


/* ---------- FOTO PROFIL ----------
   .fh-av dari components.css dipakai apa adanya, cuma diberi
   ukuran lewat --fh-av. Pembungkusnya ada karena keping peringkat
   butuh sesuatu yang position:relative dan seukuran fotonya. */

.fh-rkav{position:relative;flex:none;width:46px;height:46px}
.fh-rkav .fh-av{--fh-av:46px}

/* Huruf cadangan saat pemain tidak punya foto Telegram. Warnanya
   digelapkan dari bawaan .fh-av: latarnya bukan --pixel-raised
   lagi melainkan salah satu --av-* yang pekat, dan --pixel-muted
   di atasnya nyaris tidak terbaca. Aturan yang sama dengan
   .fh-mav di chat.

   Ini bukan kasus tepi. Telegram tidak mewajibkan foto profil,
   jadi di papan 50 nama sebagian besar baris justru memakai
   cabang ini. */
.fh-rkav .fh-av{color:var(--pixel-sunken)}


/* ---------- NAMA DAN NILAI ----------
   Bertumpuk, bukan berdampingan: kolom kanan kartu sudah dipakai
   ilustrasi karakter, dan nilai yang tetap dipaksa ke kanan akan
   duduk di atas wajahnya. */

.fh-rkid{
  flex:1;min-width:0;
  display:flex;flex-direction:column;gap:3px;
}
.fh-rknama{
  font-size:13px;font-weight:600;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
/* ANGKANYA YANG PALING BESAR DI KARTU, lebih besar dari namanya.

   Urutan bacanya memang begitu: papan peringkat dibuka untuk
   membandingkan angka, dan nama cuma menjawab "milik siapa". Versi
   sebelumnya menyamakan keduanya di 12-13px dan hasilnya kartu
   tanpa titik masuk — mata harus memilih sendiri mana yang dibaca
   duluan. 15px semibold di atas nama 13px membalik urutan itu.

   tabular-nums WAJIB: tanpa itu digit berlebar tak sama dan kolom
   angka di lima puluh kartu tidak pernah lurus. */
.fh-rknilai{
  font-style:normal;
  font-size:15px;font-weight:700;
  letter-spacing:.2px;
  font-variant-numeric:tabular-nums;
  white-space:nowrap;
}
/* Satuan diredam dan dikecilkan: di kolom yang seluruh isinya
   berakhiran "poin", yang dibandingkan mata cuma angkanya — dan
   satuan sebesar angkanya membuat tiap kartu punya dua hal yang
   sama menonjol padahal satu di antaranya sama persis di semua
   kartu. */
.fh-rknilai i{
  font-style:normal;font-weight:400;font-size:10px;
  color:var(--pixel-muted);
  margin-left:1px;
}

/* HARGA IKAN, di kaki animasi bersama beratnya. Emas, karena ini
   uang.

   DESIGN_NOTES: satu warna aksi, dan nilai penting memakainya —
   aturan yang sama yang membuat saldo koin di header emas dan total
   hadiah di panggung emas. Poin BUKAN uang, jadi dia tetap warna
   teks biasa; membedakan keduanya lewat warna berarti pemain tahu
   mana angka yang bisa dibelanjakan tanpa membaca satuannya. */
.fh-rkharga{
  font-style:normal;
  font-size:12px;font-weight:700;
  font-variant-numeric:tabular-nums;
  color:var(--pixel-primary);
  white-space:nowrap;
}
.fh-rkharga i{
  font-style:normal;font-weight:400;font-size:10px;
  color:var(--pixel-muted);
  margin-left:2px;
}


/* BARIS IKAN, hanya di kategori Terlangka — dan di sana dia
   MENGGANTIKAN baris nilai, bukan menemaninya.

   ⚠ Skornya sempat digambar sebagai baris ketiga ("620 skor").
   Dicabut atas permintaan Ain: angka itu internal, tidak berarti
   apa-apa bagi pemain, dan peringkat di sudut kartu sudah
   menyampaikan urutannya. Server tetap MENGIRIM `value` — dia yang
   menentukan urutan — layar saja yang tidak menggambarnya.

   Ikutannya baris ini naik jadi elemen terbesar di kartu, mengambil
   peran yang di kategori Poin dipegang angka poinnya. Urutan
   bacanya jadi sama di kedua kategori: APA dulu, siapa kemudian.

   ⚠ Nama ikan sempat diwarnai TIER (--c dari FH.tierVar, sistem
   rarity yang sama yang dipakai badge tier di Home). Dicabut atas
   permintaan Ain — PUTIH semua. Alasan yang bisa dilihat di layar:
   ungu secret dan merah muda shiny berdekatan, dan di daftar yang
   sebagian besar barisnya tier atas, warnanya berhenti terbaca
   sebagai sistem dan mulai terbaca sebagai acak. Tier tetap
   tersampaikan lewat peringkatnya sendiri — di papan Terlangka,
   yang di atas memang yang lebih langka.

   Nama dan beratnya SEWARNA, dua-duanya diredam. Nama ikan sempat
   putih supaya menonjol; disamakan atas permintaan Ain, dan
   hasilnya kartu Terlangka jadi persis setinggi kartu Poin — satu
   nama pemain di atas satu baris keterangan, sama di kedua
   kategori. */
/* BARIS IKAN MENAMPUNG DUA MUKA yang bergantian tiap detik: label
   tier + nama ikan, lalu berat + harganya.

   GRID SATU SEL, bukan position:absolute. Keduanya ditaruh di
   sel yang sama (grid-area:1/1), jadi tingginya otomatis setinggi
   muka yang paling tinggi dan tidak ada angka yang perlu dipatok.
   Dengan absolute, baris ini kehilangan tingginya dan kartu
   Terlangka menyusut lebih pendek daripada kartu Poin. */
.fh-rkfish{
  display:grid;
  font-size:11px;color:var(--pixel-muted);
  min-width:0;
}

/* ⚠ SILANGANNYA BERGANTIAN, BUKAN BERSAMAAN.

   Aslinya kedua muka memudar serentak selama .3 detik, dan selama
   itu DUA TEKS BERBEDA tertumpuk di ~50% masing-masing — "1.962 kg"
   di atas "Celestial Sang Saka", tidak terbaca sama sekali.
   Sepersepuluh dari tiap putaran 3 detik, dan cukup untuk tertangkap
   di tangkapan layar.

   Yang keluar jalan dulu (.14s), yang masuk menyusul sesudahnya
   (.14s + tunda .14s) — jadi tidak pernah ada dua teks bersamaan,
   dan totalnya masih di bawah durasi lama.

   Cara kerjanya: properti transisi dibaca dari keadaan yang DITUJU.
   Saat .is-gilir dipasang, aturan di bawahnya yang berlaku — tier
   tanpa tunda (keluar), data bertunda (masuk). Saat dilepas, aturan
   dasar yang berlaku dan perannya bertukar. */
.fh-rkmuka{
  grid-area:1/1;
  display:flex;align-items:center;gap:7px;
  min-width:0;
  transition:opacity .14s ease;
}

/* Keadaan dasar = tier MASUK, data KELUAR. */
.fh-rkmuka--tier{transition-delay:.14s}
.fh-rkmuka--data{transition-delay:0s}

/* Keadaan bergilir = data MASUK, tier KELUAR. */
.is-gilir .fh-rkmuka--tier{transition-delay:0s}
.is-gilir .fh-rkmuka--data{transition-delay:.14s}

/* Muka kedua mulai tersembunyi. Dua-duanya tetap DIGAMBAR — yang
   berganti cuma opacity, jadi tidak ada teks yang ditulis ulang
   lima puluh kali per detik. */
.fh-rkmuka--data{opacity:0}
.is-gilir .fh-rkmuka--tier{opacity:0}
.is-gilir .fh-rkmuka--data{opacity:1}

/* ⚠ KARTU YANG TERBUKA BERHENTI BERGILIR. Berat dan harganya sudah
   tampil di dalam animasinya, dan baris yang tetap berganti di
   atasnya cuma menampilkan angka yang sama dua kali sambil
   berkedip.

   Kekhususan (0,3,0) lawan (0,2,0) di atas, jadi ini menang tanpa
   bergantung pada urutan penulisan. */
.fh-rkrow.is-buka .fh-rkmuka--tier{opacity:1}
.fh-rkrow.is-buka .fh-rkmuka--data{opacity:0}

/* Badge tidak boleh menyusut — kalau ruangnya sempit, yang
   dipotong nama ikannya. Label tier yang terpotong jadi "CELESTI…"
   membuang justru penanda yang paling cepat dibaca. */
.fh-rkfish .fh-tier{flex:none}

.fh-rkfish b{
  min-width:0;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}

/* Berat di baris bergilir memakai warna teks biasa, bukan emas:
   emas dipesan untuk uang, dan berat bukan uang. */
.fh-rkmuka--data .fh-rkberat{
  font-style:normal;
  font-size:12px;font-weight:700;
  font-variant-numeric:tabular-nums;
  color:var(--pixel-text);
  white-space:nowrap;
}

@media (prefers-reduced-motion:reduce){
  .fh-rkmuka{transition:none}
}
.fh-rkfish b{
  font-size:14px;font-weight:700;letter-spacing:.2px;
  color:var(--pixel-muted);
}


/* ---------- ILUSTRASI KARAKTER ----------
   <id>-half.webp: ilustrasi realistis yang dipotong TINGGINYA saja,
   lebar utuh. Rasionya lanskap ~1,56:1, jadi kotak ini lanskap juga.

   ⚠ Kalau --half di convert_character.py disetel, rasio berkasnya
   berubah dan kotak ini WAJIB ikut. object-fit:contain memang
   menjaga proporsi, tapi selisih rasio muncul sebagai ruang kosong
   di dalam kartu, bukan sebagai error.

   contain, BUKAN cover: cover mengisi kotak dan memotong sisanya,
   dan di kartu sekecil ini karakter yang terpotong bahunya terbaca
   sebagai aset yang salah ukuran. Seluruh gambar harus muat.

   Kotaknya 112x84, rasio 1,33 — lebih persegi daripada gambar
   karakternya (1,56), dan itu SENGAJA. Kategori Terlangka
   menggambar ikan, dan ikan bisa lanskap (Arunika) maupun tegak.
   Kotak selanskap 1,56 memaksa ikan tegak menciut jadi separuh
   lebar; kotak 1,33 memberi keduanya tinggi penuh. Karakter yang
   lebih lanskap tinggal menyisakan sedikit ruang di atasnya, dan
   object-position:bottom membuat sisa itu jatuh di tempat yang
   benar.

   Kotak ini lebih tinggi daripada kartunya (84 lawan 70) dan
   ditambatkan ke tepi bawah, jadi kelebihannya menyembul 14px ke
   atas. Jarak antar-kartu 18px menyisakan 4px — kalau kotak ini
   diperbesar lagi, margin itu WAJIB ikut naik.

   pointer-events:none supaya ilustrasi tidak pernah menelan
   ketukan yang ditujukan ke kartunya. */
.fh-rkchar{
  position:absolute;right:6px;bottom:0;
  width:112px;height:84px;
  pointer-events:none;

  /* MENEMBUS KE ATAS SAJA, tidak ke bawah.

     Kartu dan kotak ini sama-sama tidak punya overflow:hidden — itu
     syarat sembulannya. Tapi tanpa batas, ekor ikan dan pendarnya
     ikut jatuh MELEWATI tepi bawah kartu dan menggantung di celah
     antar-kartu, yang terbaca seperti gambar yang salah tempat.

     clip-path inset dengan nilai NEGATIF di tiga sisi: negatif
     berarti daerah kliping melebar keluar kotak, jadi atas dan
     samping tetap bebas.

     overflow:hidden tidak bisa dipakai di sini: dia memotong
     KEEMPAT sisi, jadi gambar yang menyembul ke atas ikut terpenggal
     garis lurus. Dan CSS tidak punya "overflow terlihat di satu sisi
     saja" — crop satu sisi memang satu-satunya alat untuk ini, jadi
     yang harus benar GARIS CROPNYA, bukan pendekatannya.

     ⚠ SISI BAWAH 2px, BUKAN 0, DAN ANGKANYA DITURUNKAN BUKAN
     DITEBAK. Kotak ini position:absolute, jadi `top` diukur dari
     PADDING BOX kartu — yang mulai 1px di dalam bordernya. Dengan
     top:-14px dan tinggi 84px, tepi bawah kotak jatuh di 71px dari
     tepi atas kartu, sementara kartunya sendiri berakhir di 70px dan
     sisi dalam bordernya di 69px.

       71 - 69 = 2px

     Sempat 0, dan akibatnya seni menimpa GARIS BORDER BAWAH kartu
     sepanjang lebar gambarnya. Diuji piksel di zoom 4x: bocor tepat
     1,0px CSS, terlihat cuma di baris border itu — paling kentara di
     kartu podium yang bordernya emas, dan terbaca seperti gambar
     yang tembus ke bawah.

     ⚠ Kalau tinggi kotak (84px), top (-14px), padding kartu (10px),
     atau tebal bordernya (1px) diubah, angka 2px ini ikut. */
  clip-path:inset(-40px -28px 2px -28px);
}

/* UBIN SLOT UNTUK IKAN.

   ⚠ SEBELUMNYA seni ikan digambar melayang seperti seni karakter,
   lalu sempat DIBESARKAN DAN DITURUNKAN supaya tepi bawahnya
   tertutup kartu. Dicabut: hasilnya tidak enak dilihat. Yang
   tertinggal dari percobaan itu cuma pelajarannya — ikon ikan
   berkanvas PERSEGI, dan apa pun yang dilakukan pada kotak lanskap
   dia tetap berakhir kecil dengan ruang kosong di kiri-kanan.
   Wadahnya yang salah, bukan ukurannya.

   Sekarang bentuknya UBIN, sama dengan Bestiary/Tas/Museum: kotak
   persegi berwarna tier dengan tepi, kilau, dan sudut membulat.
   Bidangnya .fh-tile dari components.css — tidak ada gaya kotak
   yang ditulis ulang di sini, jadi ubin di papan peringkat tidak
   bisa menyimpang dari ubin di layar lain.

   ⚠ INI BUKAN SALAH SATU DARI ENAM PERCOBAAN WARNA TIER YANG
   DITOLAK (lihat blok "WARNA TIER DI KARTU: DICABUT" di atas).
   Keenamnya mewarnai KARTUNYA — pendar, dua pendar sepusat, garis
   tepi, badge, petak warna, siluet ikan yang di-mask. Yang ini
   mewarnai WADAH ASETNYA dan tidak menyentuh kartunya sama sekali;
   catatan di sana justru menyisakan FH.tierFill() untuk keperluan
   persis ini.

   54px: kartu setinggi 70px dengan padding 10px menyisakan 50px,
   jadi ubin sedikit melewati padding tapi tetap jauh di dalam
   kartu. Ditengahkan tegak, BUKAN menempel bawah — ubin adalah
   benda utuh yang duduk di kartu, bukan gambar yang menyembul dari
   baliknya, dan menempelkannya ke satu sisi membuatnya terbaca
   seperti yang kedua.

   TIDAK ADA clip-path: ubin punya batas sendiri dan tidak pernah
   melewati kartunya, jadi tidak ada yang perlu dipotong. */
.fh-rkslot{
  position:absolute;right:12px;top:50%;
  transform:translateY(-50%);
  width:54px;height:54px;
  pointer-events:none;
  transition:opacity .12s ease,visibility .12s;
}
.fh-rkslot .fh-tile{width:100%;height:100%}

/* Ikut menghilang saat kartu dibuka, sama dengan seni karakter —
   panel animasi yang muncul di bawahnya menggambar ikan yang sama,
   dan dua gambar ikan yang sama di satu kartu terbaca seperti
   kesalahan. */
.fh-rkrow.is-buka .fh-rkslot{opacity:0;visibility:hidden}
.fh-rkchar img{
  display:block;width:100%;height:100%;
  object-fit:contain;object-position:bottom;
}


/* ============================================================
   TRANSFORMASI — karakter yang punya wujud kedua

   Dua ilustrasi dari pose yang SAMA, pembingkaian yang sama, cuma
   efeknya yang bertambah (aura emas Sultan, air Draco). Ditumpuk,
   lalu yang atas dinaik-turunkan opacity-nya. Tubuhnya karena itu
   diam sempurna dan yang tumbuh cuma efeknya — itu yang membuat
   mata membacanya sebagai TRANSFORMASI, bukan sebagai dua gambar
   yang bergantian.

   Kepresisiannya datang dari asetnya, bukan dari CSS: convert_character.py
   memotong KANVAS MENTAH tanpa memangkas ke isi, jadi kedua berkas
   tersusun piksel per piksel tanpa satu pun penjajaran di sini.
   ============================================================ */

/* KEDUA LAPISAN DIANIMASIKAN, dengan jadwal yang saling mengunci.

   ⚠ Versi pertama membiarkan wujud pertama menyala diam di bawah
   sepanjang waktu, alasannya "menyilangkan dua opacity menghasilkan
   kedip pucat di tengah". Alasannya benar, obatnya salah, dan
   akibatnya terukur: wujud kedua TIDAK PERNAH tampil murni.

   Aset ini 49% kosong dan 5% semi-transparan, dan di seluruh daerah
   itu aura wujud pertama tembus dari bawah. Diukur pada Sultan:
   4,3% piksel meleset dari wujud kedua yang sebenarnya, sebagian
   sampai 240/255 -- sisa aura wujud pertama menempel di kanan-bawah
   dan kiri justru pada saat puncak transformasi.

   PENYELESAIANNYA JADWAL YANG TUMPANG TINDIH, bukan menyilangkan
   di tengah. Wujud pertama tetap menyala PENUH selama wujud kedua
   masih naik, dan baru padam setelah wujud kedua hampir di puncak:

     22%      wujud kedua mulai naik, wujud pertama masih 1
     32%      wujud kedua sudah ~71%, wujud pertama mulai padam
     39-56%   wujud pertama 0, wujud kedua 1  -> MURNI wujud kedua
     56%      wujud pertama mulai naik lagi, wujud kedua masih 1
     74%      wujud kedua 0, wujud pertama 1  -> murni wujud pertama

   PANJANG TRANSISI (1,75 dtk tiap arah) BERGANTUNG PADA ASETNYA,
   dan sejarahnya perlu dibaca sebelum angkanya diubah.

   Aura kedua wujud tidak selalu berada di tempat yang sama. Selama
   keduanya sama-sama menyala, bagian aura wujud pertama yang TIDAK
   tertutup wujud kedua tampil sebagai gambar ganda -- dan makin
   lama transisinya, makin lama gambar ganda itu terlihat.

   Aset Sultan generasi pertama menyisakan bagian telanjang yang
   besar, jadi transisi panjang terlihat berantakan dan sempat
   dipercepat ke 0,5 detik untuk menutupinya. Itu menukar satu
   masalah dengan masalah lain: perubahannya jadi terlihat
   terburu-buru, bukan halus.

   Aset Sultan sekarang menutup 92% wujud pertama -- diukur: cuma
   5,3% pikselnya kuat dan telanjang, dan Draco 0,1%. Sisa sekecil
   itu tidak lagi terbaca sebagai gambar ganda, jadi transisi
   panjang aman kembali dan dikembalikan.

   ⚠ KALAU SUATU HARI TRANSISINYA TERLIHAT BERANTAKAN LAGI, yang
   berubah hampir pasti ASETNYA, bukan angka di sini. Ukur dulu
   berapa persen wujud pertama yang telanjang; kalau jauh di atas
   5%, yang perlu diperbaiki gambarnya -- aura wujud kedua harus
   menutupi tempat aura wujud pertama berada. Mempercepat transisi
   cuma menyembunyikan gejalanya.

   Tidak ada satu pun cabang khusus per karakter di sini, dan tidak
   boleh ada: gejalanya melekat pada ASET, bukan pada karakternya.

   Disimulasikan pada aset yang sebenarnya:
     puncak murni wujud 2   selisih 0/255, bertahan 1,7 dtk
     latar tembus badan     maks 4,1%, sesaat
     perubahan              1,75 dtk tiap arah */
.fh-rkchar__b{
  animation:fh-wujud1 10s ease-in-out infinite;
}
.fh-rkchar__x{
  position:absolute;inset:0;
  opacity:0;
  animation:fh-wujud2 10s ease-in-out infinite;
}

/* JEDA DIAM DI KEDUA UJUNG, bukan naik-turun terus-menerus.

   Silangan yang bergerak tanpa henti terbaca seperti gambar yang
   berkedip rusak, dan di papan berisi belasan kartu dia menarik
   perhatian terus-menerus sampai daftarnya sendiri jadi sulit
   dibaca. Dari 10 detik: 1,75s berubah, 1,7s diam di wujud kedua,
   1,75s kembali, dan 4,8s diam di wujud pertama. Jadi tiap kartu
   tetap lebih sering DIAM daripada bergerak walau transisinya
   panjang -- siklusnya yang diperpanjang, bukan jedanya yang
   dipotong. */
/* Jadwal ini KHUSUS kartu papan peringkat. Sempat dipindah ke
   components.css waktu ubin ikan dikira memakai jadwal yang sama,
   lalu dikembalikan: ubin mengikat perubahannya ke SAPUAN PITA HOLO
   dan karena itu siklusnya wajib kelipatan durasi sapuan (lihat
   @keyframes fh-ubin1/2 di components.css). Kartu rank tidak punya
   pita, jadi tidak ada yang perlu diikuti — dan memaksakan satu
   jadwal untuk keduanya berarti salah satu ikut angka yang bukan
   miliknya. */
@keyframes fh-wujud1{
  0%,32%    {opacity:1}
  39%,56%   {opacity:0}
  63%,100%  {opacity:1}
}
@keyframes fh-wujud2{
  0%,22%    {opacity:0}
  36%,60%   {opacity:1}
  74%,100%  {opacity:0}
}

/* TIAP KARTU TIDAK SEIRAMA.

   Belasan kartu yang berkedip serempak terbaca seperti seluruh
   layar yang berkelip, bukan seperti masing-masing karakter yang
   punya auranya sendiri. Offset negatif memulai animasinya di
   tengah putaran, jadi tidak ada satu pun yang menunggu.

   nth-child, bukan nilai dari JS: yang dibutuhkan cuma "tidak
   serempak", dan itu tidak layak menambah atribut style inline di
   tiap baris papan. */
/* ⚠ KEDUA LAPISAN WAJIB DAPAT OFFSET YANG SAMA. Memberi offset
   pada satu lapisan saja membuat jadwal yang saling mengunci di
   atas jadi saling berlawanan -- dan pada satu kartu itu berarti
   KEDUANYA padam bersamaan, jadi gambarnya hilang sama sekali
   selama sekian detik. */
/* Sepertiga dan dua-pertiga siklus. ⚠ Kalau durasinya diubah,
   kedua angka ini ikut -- offset yang tidak lagi sepertiga membuat
   sebagian kartu berubah nyaris bersamaan. */
.fh-rkrow:nth-child(3n+2) .fh-rkchar__b,
.fh-rkrow:nth-child(3n+2) .fh-rkchar__x{animation-delay:-3.3s}
.fh-rkrow:nth-child(3n) .fh-rkchar__b,
.fh-rkrow:nth-child(3n) .fh-rkchar__x{animation-delay:-6.6s}

/* Aset kedua BOLEH belum termuat. Sampai dia datang, opacity-nya
   memang 0 dan yang terlihat wujud pertama — tidak ada kotak
   kosong dan tidak ada gambar rusak. */

@media (prefers-reduced-motion:reduce){
  /* DIAM DI WUJUD PERTAMA, bukan di wujud kedua.

     Wujud pertama yang dipakai kartu karakter lain, jadi papan
     tetap terbaca satu keluarga. Berhenti di wujud kedua membuat
     pemain legend tampak memakai aset yang berbeda sendiri, bukan
     memakai aset yang sama tanpa animasi. */
  .fh-rkchar__b{animation:none;opacity:1}
  .fh-rkchar__x{animation:none;opacity:0}
}

.fh-rkrow--art{padding-right:124px}

/* Baris IKAN memakai ubin 54px, jauh lebih sempit dari seni
   karakter yang 112px — jadi ruang kanan yang dicadangkan ikut
   lebih sempit, dan kolom teksnya dapat 46px lebih. 78 = 54 ubin
   + 12 jarak dari tepi + 12 jarak ke teks. */
.fh-rkrow--slot{padding-right:78px}

/* Kartu TANPA ilustrasi tetap harus benar, walau sekarang jarang.
   Ketujuh karakter sudah punya berkas -half, jadi kartu kosong tidak
   lagi mayoritas — tapi dia belum hilang: papan peringkat memuat
   pemain, dan pemain yang karakternya belum diketahui tetap
   menggambar kartu tanpa ilustrasi. Kolom teksnya tidak perlu
   disetel — dia flex:1 dan mengisi sendiri. */


/* ⚠ BAR SKOR sempat ada di sini — garis 3px di bawah poin,
   panjangnya sebanding nilai peringkat 1. Dicabut atas permintaan
   Ain. Kalau ide itu muncul lagi: dia memang membuat jarak
   antar-peringkat terbaca tanpa membandingkan digit, tapi dia juga
   menambah baris ketiga ke kartu yang isinya sudah cukup, dan poin
   yang diperbesar (di bawah) mengambil alih tugas "mencolok" yang
   sebagian dipegang bar itu. */


/* ---------- KARTU YANG MELUAS KE BAWAH ----------
   Ketuk kartu Terlangka, dia tumbuh ke bawah dan mendorong kartu di
   bawahnya. Berapa pun boleh terbuka bersamaan.

   PANELNYA MEMBUNGKUS LEWAT flex-wrap, bukan lewat pembungkus baru
   di dalam kartu. Alasannya .fh-rkchar: dia position:absolute
   terhadap KARTU, jadi menyisipkan pembungkus tidak memindahkan
   jangkarnya — sementara flex-wrap membuat panelnya turun ke baris
   sendiri tanpa menyentuh satu pun elemen yang sudah ada.

   ⚠ row-gap WAJIB nol. .fh-rkrow memakai gap:11px, dan gap berlaku
   untuk kedua sumbu begitu flex-wrap menyala — 11px itu akan
   menempel di kartu SELAMANYA, juga saat tertutup, dan terbacanya
   seperti padding bawah yang salah. */
.fh-rkrow{
  flex-wrap:wrap;

  /* ⚠ row-gap WAJIB nol. .fh-rkrow memakai gap:11px, dan gap
     berlaku untuk KEDUA sumbu begitu flex-wrap menyala — 11px itu
     akan menempel di kartu selamanya, juga saat tertutup, dan
     terbacanya seperti padding bawah yang salah. */
  row-gap:0;

  /* ⚠ align-content, dan ini bukan kerapian.

     min-height:70px menyisakan ~2px ruang bebas di kartu yang
     tertutup. Bawaannya `stretch` membagi sisa itu ke SEMUA baris,
     termasuk baris panel yang seharusnya setinggi nol — kartu
     tertutup jadi menyisakan sesobek celah di bawahnya.

     `center` dipilih daripada `flex-start` supaya kartu tertutup
     tampil sama persis dengan sebelum panel ini ada. Saat terbuka
     tidak ada ruang bebas sama sekali, jadi nilainya tidak
     berpengaruh di sana. */
  align-content:center;
}

/* Isyarat bahwa kartunya bisa diketuk. Cuma kartu yang PUNYA
   animasi yang mendapatnya — kartu tanpa animasi tidak bisa dibuka
   sama sekali, dan kursor yang menjanjikan interaksi yang tidak ada
   lebih buruk daripada tidak ada isyarat. */
.fh-rkrow.is-bisa-buka{cursor:pointer}

/* TINGGINYA DIUKUR JS, BUKAN ANGKA TETAP DI SINI.

   Sempat 144px mati, dan itu SALAH: tinggi panel sekarang mengikuti
   LEBAR kartu lewat aspect-ratio, dan lebar kartu ikut lebar layar.
   Angka mati memotong animasinya di layar lebar dan menyisakan
   celah di layar sempit.

   height:auto tidak bisa dianimasikan, jadi bukaTutup() mengukur
   scrollHeight lalu menuliskannya sebagai px. Sesudah transisinya
   selesai nilainya diganti `auto`, supaya panel yang sedang terbuka
   ikut menyesuaikan kalau layarnya diputar.

   Dua jalan keluar lain sengaja tidak dipakai: max-height dengan
   angka karangan membuat easing melenceng (bagian akhir animasi
   berjalan pada tinggi yang sudah tidak berubah), dan
   grid-template-rows 0fr->1fr baru bisa dianimasikan di browser
   yang cukup baru. */
.fh-rkbuka{
  flex:0 0 100%;
  height:0;
  overflow:hidden;
  transition:height .24s ease;
}

/* ⚠ RASIONYA MENGIKUTI BERKAS ANIMASINYA — 16:9, sama dengan mp4
   yang dihasilkan (1280x720).

   Sebelumnya panel ini 136px mati sementara lebarnya ~402px, jadi
   rasionya 2,96:1 melawan video 1,78:1 — object-fit:cover membuang
   40% TINGGI animasinya. Kalau nanti pipeline animasi berganti
   rasio, angka di sini yang ikut. */
.fh-rkpanel{
  position:relative;display:block;
  aspect-ratio:16/9;
  margin-top:8px;
  border-radius:var(--r-lg);
  overflow:hidden;
  background:var(--pixel-void);
}

/* GAMBAR DIAM MENGHILANG saat animasinya muncul — dua gambar ikan
   yang sama di satu kartu cuma bersaing.

   Menghilangnya lebih cepat daripada panelnya membuka (.12s lawan
   .24s) supaya dia sudah pergi sebelum kartunya tumbuh; kalau
   waktunya sama, dia terlihat melorot ke bawah sambil memudar.

   visibility ikut supaya dia benar-benar tidak bisa diketuk saat
   tembus pandang — bukan sekadar tidak terlihat. */
/* ⚠ DIJANGKARKAN KE ATAS, BUKAN KE BAWAH.

   Dulu `bottom:0`, dan itu benar selama tinggi kartu tetap. Sekarang
   kartunya TUMBUH, dan jangkar bawah ikut turun bersamanya — gambar
   ikannya akan melorot setinggi panelnya sambil memudar, tepat di
   depan mata.

   -14px = 70px (min-height kartu) - 84px (tinggi kotak ini), jadi
   hasilnya identik dengan bottom:0 saat tertutup. ⚠ Kalau salah satu
   dari dua angka itu diubah, angka ini ikut.

   Bonusnya: clip-path di bawah memotong di tepi bawah kotak ini,
   yang sekarang berhenti di garis bawah baris depan — jadi ikannya
   tidak pernah menimpa panel animasi. */
.fh-rkchar{top:-14px;bottom:auto}

.fh-rkchar{transition:opacity .12s ease,visibility .12s}
.fh-rkrow.is-buka .fh-rkchar{opacity:0;visibility:hidden}

/* Ruang kanan yang tadinya untuk gambar diam dikembalikan ke kolom
   teks, dan beranimasi supaya namanya bergeser mulus, bukan
   melompat.

   ⚠ SETIAP KELAS YANG MENCADANGKAN RUANG KANAN WAJIB IKUT DI SINI.
   Panel animasi adalah anak .fh-rkrow, jadi lebarnya dipotong
   padding kanan kartunya. Kelas yang tidak dikembalikan ke 12px
   saat terbuka membuat panelnya tampil LEBIH SEMPIT dari kartunya
   — dan tidak ada error apa pun, karena padding yang tidak dicabut
   itu sah-sah saja.

   Persis itu yang terjadi waktu --slot ditambahkan untuk baris ikan
   tanpa membawa serta dua baris di bawah: SEMUA kartu yang bisa
   dibuka adalah baris ikan, jadi panelnya jadi 66px lebih sempit
   dari kartunya. */
.fh-rkrow--art,
.fh-rkrow--slot{transition:padding-right .24s ease}
.fh-rkrow--art.is-buka,
.fh-rkrow--slot.is-buka{padding-right:12px}

@media (prefers-reduced-motion:reduce){
  .fh-rkbuka,.fh-rkchar,
  .fh-rkrow--art,.fh-rkrow--slot{transition:none}
}

/* ANIMASI MENGISI PANEL.

   ⚠ object-fit:contain, BUKAN cover. Di sini animasinya SUBJEK,
   bukan latar — memotongnya berarti membuang bagian ikan yang
   justru jadi alasan panel ini dibuka. Panelnya sudah dibuat
   serasio dengan berkasnya, jadi dalam keadaan normal contain dan
   cover menghasilkan gambar yang sama persis; contain-lah yang
   menjaga berkas dengan rasio lain tetap tampil utuh, bukan
   terpotong diam-diam. */
.fh-rkanim{
  position:absolute;inset:0;z-index:0;
  overflow:hidden;border-radius:inherit;
  pointer-events:none;
}
.fh-rkanim video{
  display:block;width:100%;height:100%;
  object-fit:contain;
}

/* ⚠ TIDAK ADA TIRAI DI ATAS ANIMASINYA.

   Sempat ada gradient gelap setinggi 72% panel supaya harganya
   terbaca, dan hasilnya animasinya tampak tertutup separuh —
   persis yang tidak boleh terjadi di panel yang dibuka JUSTRU untuk
   melihat animasi itu.

   Yang berikutnya dicoba keping kecil di kaki panel, dan itu pun
   dicabut: angka yang berdiri di ATAS animasi tetap merebut
   perhatian dari animasinya. Sekarang harganya keluar sama sekali
   dari panel dan menempel di baris ikan. */

/* BERAT DAN HARGA DI KAKI ANIMASI.

   Dua keping terpisah di dua sudut, bukan satu baris memanjang:
   kiri ukuran tangkapan, kanan nilainya — dua hal berbeda yang
   kebetulan berdampingan, dan memisahkannya ke dua sudut membuat
   mata tahu mana yang mana tanpa membaca satuannya.

   ⚠ Kepingnya sendiri yang berlatar, BUKAN tirai selebar panel.
   Gradient gelap setinggi 72% panel sempat dipakai dan hasilnya
   animasinya tampak tertutup separuh — persis yang tidak boleh
   terjadi di panel yang dibuka justru untuk melihatnya. Luas yang
   ditutup di sini seukuran teksnya. */
.fh-rkfakta{
  position:absolute;z-index:1;
  left:10px;right:10px;bottom:10px;
  display:flex;align-items:center;justify-content:space-between;
  gap:8px;
  pointer-events:none;
}

.fh-rkfakta > *{
  padding:5px 11px;
  border-radius:999px;
  background:rgba(var(--bg-rgb),.78);
  border:1px solid rgba(255,255,255,.10);
}

.fh-rkberat{
  font-style:normal;
  font-size:12px;font-weight:700;
  font-variant-numeric:tabular-nums;
  color:var(--pixel-text);
  white-space:nowrap;
}


/* ---------- TIGA TERATAS DAN DIRI SENDIRI ---------- */

/* PODIUM BERWARNA MEDALI, bukan emas untuk ketiganya.

   Aturan yang sama yang sudah dipakai keping peringkat sejak awal:
   emas/perak/perunggu adalah arti yang dibawa pemain dari luar app
   dan tidak perlu dijelaskan, sementara tiga kartu yang ketiganya
   bersemu emas justru menghapus urutan di antara mereka.

   Warnanya menular dari SATU tempat. .m1/.m2/.m3 dipasang di
   KARTUNYA, dan --m ikut turun ke keping peringkat di dalamnya
   lewat pewarisan — jadi tidak ada kelas medali kedua yang harus
   dipasang di keping, dan tidak ada kemungkinan kartu perak berisi
   keping emas.

   :nth-child TIDAK dipakai lagi. Dia benar selama daftarnya
   dimulai dari peringkat 1, dan diam-diam salah begitu daftarnya
   dipotong atau digulung dari tengah. Peringkat datang dari server;
   yang menggambar podium harus angka itu, bukan posisi baris. */
.fh-rkrow.m1{--m:var(--medal-1);--m-rgb:var(--medal-1-rgb)}
.fh-rkrow.m2{--m:var(--medal-2);--m-rgb:var(--medal-2-rgb)}
.fh-rkrow.m3{--m:var(--medal-3);--m-rgb:var(--medal-3-rgb)}

.fh-rkrow.is-podium{
  --wash:rgba(var(--m-rgb),.15);
  border-color:rgba(var(--m-rgb),.32);
}

/* KARTU PEMAIN SENDIRI, di daftar maupun di kaki. Tepi kiri emas
   penuh plus latar setingkat lebih terang — cukup untuk ditemukan
   mata saat menggulung 50 kartu, tanpa berteriak seperti tombol.

   box-shadow inset, bukan border-left 3px: menebalkan border kiri
   menggeser seluruh isi kartu 2px ke kanan, dan kartunya terbaca
   miring di antara tetangganya. */
.fh-rkrow.is-me{
  --wash:rgba(var(--primary-rgb),.13);
  border-color:rgba(var(--primary-rgb),.45);
  /* Dua bayangan: pita emas kiri, lalu kilau puncak yang sama
     dengan kartu lain. Menulis satu saja akan menghapus yang lain,
     dan yang hilang diam-diam adalah kilau — kartu sendiri jadi
     satu-satunya yang tampil datar. */
  box-shadow:inset 3px 0 0 var(--pixel-primary),
             inset 0 1px 0 rgba(255,255,255,.05);
}

/* Di kaki papan kartunya sendirian, jadi margin antar-kartu tidak
   pernah kena — padahal sembulan 12px-nya tetap ada dan yang
   ditabraknya baris "Peringkat kamu" tepat di atasnya. Ruangnya
   diberi di sini.

   Yang ditambah jaraknya, BUKAN sembulannya yang dikecilkan:
   kartu di kaki dan kartu di daftar harus punya bentuk yang sama
   persis, kalau tidak pemain melihat dirinya digambar dengan
   aturan yang berbeda dari orang lain. */
.fh-lbme .fh-rkrow{margin-top:18px}


/* NOMOR PERINGKAT DI JUDUL KAKI. Emas dan sedikit lebih besar
   daripada labelnya: ini satu-satunya angka di baris kiri, dan
   pemain yang membuka kaki papan mencarinya lebih dulu daripada
   membaca kalimatnya.

   Emas dipakai di sini walau DESIGN_NOTES memesannya untuk uang —
   pengecualian yang sama dengan medali papan skor: peringkat
   sendiri adalah satu-satunya angka di layar ini yang benar-benar
   milik pemain, dan dia berdiri di sebelah angka hadiah yang juga
   emas, jadi dua-duanya terbaca sebagai satu baris ringkasan. */
.fh-lbme__no{
  font-size:13px;font-weight:700;
  font-variant-numeric:tabular-nums;
  color:var(--pixel-primary);
}

/* JUMLAH PESERTA di sebelah peringkat: "#4 / 3.100". Diredam dan
   dikecilkan supaya peringkatnya tetap yang dibaca duluan — yang
   dicari pemain posisinya, dan jumlah peserta cuma yang membuat
   posisi itu berarti.

   Ditaruh di sini bersama .fh-lbme__no dan bukan di alltime.css
   tempatnya lahir: keduanya dua keping dari SATU baris teks, dan
   dua papan memakainya. Aslinya .fh-abdari, dan selama kelasnya
   masih menyebut satu layar, layar kedua yang memakainya terbaca
   seperti pinjaman. */
.fh-lbme__dari{
  margin-left:5px;
  font-size:11px;font-weight:600;
  font-variant-numeric:tabular-nums;
  color:var(--pixel-muted);
}


/* ============================================================
   KAKI PERINGKAT SENDIRI
   .fh-ov__foot + .fh-lbme dari components.css sudah mengurus
   posisi dan bentuk barisnya. Yang ditambah di sini cuma baris
   keterangan ketiga: ambang minimal / perkiraan hadiah.
   ============================================================ */

/* SATU KALIMAT DI BAWAH BARIS, dan cuma satu.

   Kaki ini sudah punya dua keterangan (judul kiri, selisih kanan);
   yang ketiga ditaruh di bawah barisnya supaya tidak berebut ruang
   dengan nama pemain. Isinya salah satu dari dua, tidak pernah
   keduanya: ambang minimal kalau belum lolos, perkiraan hadiah
   kalau sudah. */
/* KETERANGAN KANAN DI KAKI = PERKIRAAN HADIAH.

   Dulu slot ini berisi "kurang N poin untuk masuk 50 besar" dan
   perkiraan hadiahnya jadi kalimat TERSENDIRI di bawah kartu — dua
   baris untuk satu topik, di kaki yang memang sudah terlalu tebal.
   Sekarang keduanya ditukar: hadiah naik ke slot ini, selisih poin
   dibuang. Yang dicari pemain di kaki papan hadiah memang berapa
   yang dia dapat, bukan berapa poin lagi ke peringkat 50.

   Angkanya ditebalkan supaya terbaca sebagai nilai, bukan sebagai
   kalimat. .fh-lbme__gap dari components.css yang mengurus posisi
   dan warnanya. */
#fh-rank-ov .fh-lbme__gap b{font-weight:700}
