/* ============================================================
   Orratto — styles.css   |  Panduan lengkap: README.md
   ------------------------------------------------------------
   BREAKPOINT (hanya ini, urut dari default):
     default (HP)      : tema GELAP, video full-bleed, topbar melayang,
                         rail aksi di dalam video
     >=768 & <=1023    : tablet — feed jadi kolom 560px terpusat
     >=1024            : desktop — tema TERANG, sidebar, kartu 9/16,
                         rail aksi DI LUAR kartu, topbar disembunyikan
     max-height:520px  : HP lanskap — rail & caption dirapatkan
     max-width:360px   : HP sempit — topbar anti-sesak
   Tema ikut layout, bukan preferensi OS: var di :root = gelap,
   di-override jadi terang pada >=1024.
   ------------------------------------------------------------
   --botnav-h = tinggi nav bawah, DAN sekaligus yang menjaga .feed tidak persis
   seukuran viewport. Dua peran itu sengaja dipegang satu angka; riwayatnya:

   RIWAYAT --top-gap (DICABUT 30 Agustus 2026, jangan dihidupkan lagi)
   Chrome Android mempromosikan scroller yang PERSIS mengisi viewport (posisi
   0,0 + ukuran sama persis) menjadi implicit root scroller, sehingga scroll di
   dalamnya menggerakkan chrome peramban. Dua percobaan 23 Agt 2026 di produksi:
     inset:0  -> GAGAL. Bilah alamat hilang saat swipe turun, tinggi video
                 MELOMPAT saat scroll-snap mengunci, bilah muncul lagi saat naik.
     1px      -> LULUS di satu HP (milik DimDim) saja. TIDAK diadopsi: marginnya
                 terlalu tipis terhadap zoom, skala tampilan Android, dan
                 pembulatan sub-piksel di layar DPI tinggi — dan kegagalannya
                 SENYAP (tidak ada galat; pengguna cuma merasa feed kaku).
   Dipakailah 6px, selama berbulan-bulan, nol keluhan.
   30 Agt 2026 nav bawah masuk ke feed dan MENYITA ruang, jadi .feed kini
   viewport dikurangi 50px + safe-area. Syaratnya tetap dipenuhi — kali ini oleh
   50px STRUKTURAL, bukan 6px yang berdiri di tepi. 6px jadi mubazir dan
   dicabut. Yang HARUS tetap benar supaya pencabutan itu sah:
     1. nav bawah TIDAK PERNAH absen di bawah 1024px — termasuk untuk tamu.
        Karena itu tamu menyembunyikan SATU ITEM (.bn-an), bukan seluruh nav.
     2. potongan .shell dan tampil/tidaknya nav dikemudikan MEDIA QUERY YANG
        SAMA (<1024px), bukan status login atau JS. Tidak ada keadaan di mana
        salah satu ada tanpa yang lain.
   Kalau salah satu syarat itu dilanggar, .feed kembali seukuran viewport di
   keadaan tersebut dan bug 23 Agt hidup lagi — senyap.
   BELUM ADA BUKTI LAPANGAN untuk pencabutan ini per 30 Agt 2026; tidak bisa
   diuji lewat jsdom/emulator, butuh HP fisik.
   --ui-top-pad:6px TIDAK ada hubungannya dengan itu (dan bukan sisa --top-gap):
   ia mengatur topbar/.mute/.toast. Jangan digabung.
   env(safe-area-inset-*) berfungsi karena <meta viewport-fit=cover>.
   ============================================================ */
/* THEME follows layout: narrow = dark (default) · wide = light (>=1024px) */
  :root{
    --bg:#121212;--surface:#1C1C1C;--text:#FFFFFF;--muted:#8B949E;
    --line:rgba(255,255,255,.14);--hover:rgba(255,255,255,.08);
    --bubble:rgba(255,255,255,.08);--bubble-hover:rgba(255,255,255,.16);
    --accent:#FFFFFF;--btn-bg:#FFFFFF;--btn-fg:#121212;
    --topbar-h:68px;--sidebar-w:240px;--pad-card:20px;--rail-w:44px;
    --botnav-h:50px;
    --ui-top-pad:6px;
    --top-inset:calc(var(--ui-top-pad) + env(safe-area-inset-top));
  }
  @media (min-width:1024px){
    :root{
      --bg:#FFFFFF;--surface:#FFFFFF;--text:#0F1419;--muted:#5B6875;
      --line:#E6E9EB;--hover:rgba(0,0,0,.05);
      --bubble:rgba(0,0,0,.05);--bubble-hover:rgba(0,0,0,.10);
      --accent:#0F1419;--btn-bg:#0F1419;--btn-fg:#FFFFFF;--rail-w:48px;
    }
  }
  *{box-sizing:border-box;margin:0;padding:0}

  /* ============================================================
     TRANSISI ANTAR HALAMAN (31 Agustus 2026)
     ------------------------------------------------------------
     Silang-pudar bawaan peramban saat berpindah halaman. TIDAK ada JavaScript,
     TIDAK ada router, TIDAK ada perubahan struktur — mencabutnya = menghapus
     blok ini.

     KENAPA DI styles.css: kedua dokumen (asal DAN tujuan) harus sama-sama
     memilih ikut, kalau tidak transisinya tidak menyala sama sekali.
     styles.css dimuat ke-24 halaman, jadi satu aturan menutup semua pasangan
     navigasi. JANGAN memindahkannya ke page.css — index.html tidak memuat
     page.css, dan feed justru halaman asal yang paling sering.

     DIGERBANGI no-preference, bukan dicabut di `reduce`. Polanya sengaja
     terbalik: default TIDAK ada transisi, lalu dinyalakan kalau orangnya tidak
     meminta gerak dikurangi. Kalau ditulis sebaliknya (nyala lalu dimatikan di
     `reduce`), peramban yang tidak mengerti kuerinya tetap dapat animasi.

     ANGKA YANG MENDASARINYA (diukur DimDim 31 Agt, Chrome Fast 4G, cache
     panas, 5 putaran, 3 dicatat): 575 / 590 / 556 ms feed -> /dashboard.
     Yang penting DIPAHAMI: jeda ~570 ms itu SUDAH ADA sekarang — peramban
     menahan halaman lama selama menunggu halaman baru. Transisi tidak
     menambahnya; ia hanya mengganti pergantian mendadak di ujungnya dengan
     silang-pudar. Kalau suatu saat navigasi jadi jauh lebih lambat, tinjau
     ulang: di atas 4 detik peramban MELEWATKAN transisinya sendiri
     (TimeoutError), jadi gejalanya = transisi hilang sesekali, bukan galat.

     DUKUNGAN: Chromium 126+ dan Safari 18.2+. Firefox BELUM merilis versi
     lintas-dokumennya di rilis stabil; MDN menandainya "Limited availability",
     bukan Baseline (diverifikasi 31 Agt 2026 — beberapa artikel 2026 mengklaim
     sebaliknya dan itu bertentangan dengan data kompatibilitas resmi).
     Peramban tanpa dukungan bernavigasi seperti biasa. Tidak ada yang rusak.

     TIDAK berlaku untuk: mengetik URL di bilah alamat, bookmark, dan segala
     bentuk muat ulang — hanya navigasi yang dipicu orang mengklik isi halaman.
     Jadi menguji dengan refresh TIDAK akan memperlihatkan apa pun; harus lewat
     tap nav bawah / tautan.

     SENGAJA TANPA view-transition-name di mana pun. Dua elemen terlihat yang
     berbagi nama yang sama membuat peramban MELEMPAR dan melewatkan seluruh
     transisi — dan Orratto punya banyak elemen berulang (kartu, baris, avatar).
     Silang-pudar root saja sudah seluruh ruang lingkupnya. Kalau suatu saat
     ingin memorf satu elemen lintas halaman, itu keputusan sendiri, dan
     keunikan namanya harus dibuktikan lebih dulu.
     ============================================================ */
  @media (prefers-reduced-motion:no-preference){
    @view-transition{navigation:auto}
  }

  /* ============================================================
     [hidden] HARUS MENANG — 17 Agustus 2026
     ------------------------------------------------------------
     Aturan `[hidden]{display:none}` dari peramban berasal dari UA stylesheet,
     dan SETIAP aturan author mengalahkannya. Jadi begitu sebuah kelas menyetel
     `display:` apa pun, `el.hidden = true` di JS berhenti berpengaruh —
     diam-diam, tanpa galat, tanpa satu pun tanda di konsol.

     TIGA kebocoran hidup ditemukan sekaligus saat uji produksi 17 Agustus:
       .cr-drop      -> kotak "Select a video" tetap tampil di atas komposer
                        yang sudah terbuka (ini yang terlihat di layar dan
                        memicu penyisiran ini)
       .bsheet__cta  -> tombol "Visit site" tetap tampil pada postingan yang
                        TIDAK punya tautan produk — tombol mati ber-href "#"
       .btn          -> tombol "Remove" foto tetap tampil pada akun yang belum
                        punya foto sama sekali

     Sebelumnya kelas ini ditambal satu per satu (.db-empty[hidden],
     .po-act[hidden], .pf-adm[hidden], dan empat lainnya). Menambal per
     selektor berarti kebocoran BERIKUTNYA cuma menunggu kelas baru yang
     menyetel display — dan tidak ada yang akan menyadarinya sampai seseorang
     kebetulan melihat layarnya.

     !important dipakai dengan sengaja: tanpa itu spesifisitasnya seri dengan
     selektor kelas (0,1,0) dan yang menang cuma soal urutan baris — rapuh
     persis pada hal yang sedang dijaga di sini.

     Penambal per-selektor yang lama SENGAJA DIBIARKAN: menghapusnya cuma
     menambah risiko tanpa menambah apa pun.
     Dijaga oleh tools/test-hidden-cascade.mjs. */
  [hidden]{display:none!important}
  html,body{height:100%;overflow:hidden;overscroll-behavior:none}
  body{font-family:Arial,Helvetica,sans-serif;background:var(--bg);color:var(--text);
    -webkit-font-smoothing:antialiased;overflow:hidden;transition:background-color .3s ease,color .3s ease}
  button{font-family:inherit;cursor:pointer;border:none;background:none;color:inherit}
  a{color:inherit;text-decoration:none}
  .ico{width:22px;height:22px;display:block;stroke:currentColor;fill:none;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
  .ico.fill{fill:currentColor;stroke:none}
  .logo svg{display:block;width:auto}

  /* ===== MOBILE TOPBAR ===== */
  .topbar{position:fixed;top:0;left:0;right:0;height:calc(var(--topbar-h) + var(--top-inset));z-index:40;display:flex;align-items:center;
    gap:8px;padding:var(--top-inset) var(--pad-card) 0;background:transparent;border-bottom:none;pointer-events:none}
  .topbar > *{pointer-events:auto}
  .topbar .menu{pointer-events:auto}
  .topbar .logo{filter:drop-shadow(0 1px 5px rgba(0,0,0,.45));color:#fff;display:inline-flex;align-items:center;min-height:44px}
  .topbar .iconbtn{background:rgba(0,0,0,.5);color:#fff;width:38px;height:38px}
  .topbar .iconbtn .ico{width:24px;height:24px;stroke-width:2}
  .topbar .btn-primary{background:rgba(0,0,0,.5);color:#fff;height:38px;font-weight:600}
  .topbar .logo svg{height:22px}
  .grow{flex:1}
  .iconbtn{width:38px;height:38px;border-radius:50%;display:grid;place-items:center;color:var(--text);
    background:var(--bubble);transition:background .15s;flex:none}
  .btn{display:inline-flex;align-items:center;justify-content:center;font-weight:600;font-size:14px;
    height:38px;padding:0 16px;border-radius:999px;white-space:nowrap;transition:transform .1s,opacity .15s,background .15s}
  .btn:active{transform:scale(.98)}
  .btn-primary{background:var(--btn-bg);color:var(--btn-fg)}
  .avatar{width:38px;height:38px;border-radius:50%;box-shadow:inset 0 0 0 1px var(--line);flex:none}
  /* Foto profil pemilik akun di tombol menu. background-size:cover supaya
     gambar non-persegi tidak gepeng; warna solid tetap jadi latar selama
     gambarnya belum termuat. */
  .avatar.has-pfp{background-size:cover;background-position:center;background-repeat:no-repeat}

  /* ============================================================
     AVATAR BAWAAN — SATU aturan untuk SEMUA permukaan (15 Agustus 2026)
     ------------------------------------------------------------
     Ada di styles.css, bukan page.css, karena styles.css dimuat SETIAP
     halaman termasuk feed; page.css tidak. Sebelumnya siluetnya hanya
     didefinisikan di page.css, sementara `.avatar` di topbar cuma
     `background:#5b7183` — lingkaran biru-abu POLOS tanpa siluet. Hasilnya
     dua avatar bawaan yang berbeda di satu aplikasi, dan yang di topbar tidak
     pernah ikut diperbarui waktu siluetnya dibuat 3 Agustus.

     BENTUKNYA mengikuti pola Instagram/X/Google, dan dua hal itu yang dulu
     tidak ada: (1) LATAR BERTINT, supaya lingkarannya terbaca sebagai avatar
     kosong, bukan lubang; (2) proporsi kepala+bahu yang mengisi lingkaran —
     versi lama memakai figur kecil dengan celah lebar di atas kepala dan bahu
     yang terpotong di tepi, sehingga terlihat seperti gambar yang gagal muat.

     SVG inline sebagai data URI, bukan berkas di R2: memuatnya lewat jaringan
     berarti membayar satu permintaan untuk gambar yang sama di setiap profil
     tanpa foto. CSP sudah mengizinkan img-src data:.

     Foto sungguhan menimpanya lewat el.style.backgroundImage (inline, dipasang
     Shell.paintBg) yang selalu menang atas aturan lembar gaya. */
  .avatar,.pf-pic,.pfe-pic,.po-who .po-pfp,.db-avatar,.sr-av{
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23E4E8EC'/%3E%3Ccircle cx='32' cy='22' r='10' fill='%239AA4AE'/%3E%3Cpath d='M9 64c0-14.3 10.3-26 23-26s23 11.7 23 26Z' fill='%239AA4AE'/%3E%3C/svg%3E");
    background-size:cover;background-position:center;background-repeat:no-repeat}
  body.signed-in .m-login{display:none}
  /* search hanya untuk yang sudah masuk (guest: tombolnya tidak ditampilkan) */
  body:not(.signed-in) #msearch{display:none}
  /* MENU AVATAR TOPBAR HP — DICABUT 30 Agustus 2026.
     Isinya empat item: Profile, Dashboard, Create, Settings. Tiga yang pertama
     sekarang ada di nav bawah, yang juga tampil di feed sejak hari yang sama.
     Yang tersisa cuma Settings, dan jalannya sudah ada: Profile -> Settings.

     Ini BUKAN jalur baru. page.css:45 sudah menyembunyikan .m-acct di SEMUA
     halaman non-feed sejak lama, dengan komentar yang menyebut jalur itu apa
     adanya. Feed adalah satu-satunya tempat menu ini masih hidup di HP — sisa
     dari zaman feed tidak punya nav bawah. Mencabutnya membuat feed berhenti
     jadi pengecualian, alasan yang sama dengan nav bawah itu sendiri.
     Norma pembanding: Instagram & TikTok di HP tidak punya menu avatar di
     topbar; Settings dicapai lewat tab Profile.

     KONSEKUENSI YANG DITERIMA: di HP, Settings kini HANYA bisa dicapai lewat
     halaman profil. Kalau pintu Settings di sana kelak dipindah atau
     disembunyikan, Settings jadi tak terjangkau sama sekali di HP.

     Aturannya dimatikan di sini, MARKUP-nya di shell.js TIDAK dihapus:
     #mAcctBtn masih dipakai paintMyAvatar() bersama #acctBtn, dan .m-acct
     tetap punya aturan sendiri di baris >=1024px. Menghapus markupnya berarti
     menyentuh keduanya tanpa keuntungan apa pun.
     DESKTOP TIDAK TERPENGARUH: avatar desktop memakai jalur lain sama sekali
     (.auth-in di .auth-float, #acctBtn), bukan .m-acct. */
  .m-acct{display:none}

  /* ===== SEARCH SURFACE (full-screen overlay) ===== */
  .search-surface{position:fixed;inset:0;z-index:80;background:var(--bg);display:none;flex-direction:column}
  body.search-open .search-surface{display:flex}
  body.search-open .media{animation-play-state:paused}
  .search-head{display:flex;align-items:center;gap:10px;
    padding:10px 12px;padding-top:calc(10px + env(safe-area-inset-top));border-bottom:1px solid var(--line)}
  .search-field{flex:1;display:flex;align-items:center;gap:8px;height:42px;padding:0 6px 0 14px;
    background:var(--bubble);border-radius:999px;min-width:0}
  .search-field input{flex:1;min-width:0;background:none;border:none;outline:none;color:var(--text);font-size:15px}
  .search-field input::placeholder{color:var(--muted)}
  .search-clear{width:30px;height:30px;border-radius:50%;display:none;place-items:center;color:var(--muted);flex:none}
  .search-field.has-text .search-clear{display:grid}
  .search-results{flex:1;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;padding:4px 0;scrollbar-width:none}
  .search-results::-webkit-scrollbar{display:none}
  .sr-row{display:flex;align-items:center;height:44px;padding:0 18px;font-size:14px;color:var(--text);-webkit-tap-highlight-color:transparent}
  .sr-row:active{background:var(--hover)}
  .sr-empty{padding:22px 18px;color:var(--muted);font-size:14px}
  /* pengaman: overlay search HP tak boleh muncul di desktop (pemicunya #msearch
     memang display:none >=1024px; ini jaring kedua kalau-kalau body.search-open
     ikut tersetel). Desktop pakai dropdown sidebar, bukan overlay. */
  @media (min-width:1024px){body.search-open .search-surface{display:none}}
  @media (min-width:1024px){.search-head,.search-results{max-width:640px;margin-left:auto;margin-right:auto;width:100%}}

  /* ===== DESKTOP floating auth ===== */
  .auth-float{display:none;position:fixed;top:16px;right:20px;z-index:45;align-items:center;gap:8px}
  .auth-out{display:flex;gap:8px;align-items:center}
  body.signed-in .auth-out{display:none}
  .auth-in{display:none}

  /* ===== SHELL =====
     POTONGAN NAV BAWAH — SEKARANG DI SEMUA HALAMAN BER-NAV (31 Agustus 2026).

     Sampai 30 Agustus potongan ini hanya berlaku di feed, dan halaman lain
     memberi ruang lewat padding-bottom .page-main (nav MENIMPA). Dua model
     untuk satu nav, dan model kedua punya cacat yang tidak terlihat sampai
     DimDim melaporkannya dari HP:

       .shell{inset:0} penuh viewport -> .app height:100% -> .page-main flex:1
       => .page-main PERSIS seukuran viewport => Chrome mempromosikannya jadi
       implicit root scroller => bilah alamat hilang-muncul saat scroll.

     Itu bug 23 Agustus yang sama, cuma hidup di halaman lain dan tidak pernah
     dicari di sana. Komentar lama di page.css §2 malah mengklaim sebaliknya
     ("browser HP tidak menyembunyikan bar URL-nya") — ditulis sebelum potongan
     nav ada, tidak pernah dikoreksi, dan sudah dibetulkan sekarang.

     Menaikkan potongan ke .shell umum WAJIB dibarengi pencabutan --botnav-h
     dari padding-bottom .page-main (page.css). Kalau tidak, ruang nav terhitung
     DUA KALI dan isi halaman mengambang ~100px di atas dasar layar. Keduanya
     berubah dalam serahan yang sama; jangan pernah mencabut salah satunya.

     safe-area ikut dipotong di sini, jadi .meta/.actions (feed) dan
     .page-main TIDAK boleh menambahkannya lagi — itu terhitung dua kali.

     Potongan ini juga yang menggantikan --top-gap:6px sebagai penjaga agar
     .feed tidak persis seukuran viewport — riwayat lengkap di kepala berkas. */
  .shell{position:fixed;inset:0 0 calc(var(--botnav-h) + env(safe-area-inset-bottom)) 0}
  /* HALAMAN TANPA NAV BAWAH -> tanpa potongan, kalau tidak ada pita kosong
     50px di dasar layar untuk nav yang tidak ada di sana.
     Daftarnya SATU-SATUNYA di CSS (lihat page.css §4 untuk daftar yang sama
     dipakai menyembunyikan navnya) dan dikemudikan data-page, bukan kelas yang
     ditambahkan JS: shell.js dimuat di AKHIR body, jadi kelas dari JS datang
     setelah cat pertama dan halaman ber-HTML statis (/create, /settings*)
     akan berkedip. Atribut data-page sudah ada di HTML sejak byte pertama.
     [data-page^="settings"] mencakup kelimanya: settings, settings-profile,
     settings-handle, settings-account, settings-about.
     [data-page^="dashboard-"] — TANDA HUBUNGNYA WAJIB. Ia mencakup keempat
     SUB-halaman (dashboard-followers, -following, -posts, -post) tetapi TIDAK
     mencakup data-page="dashboard" itu sendiri, yang tetap ber-nav karena ia
     tujuan nav, bukan focused task. Tanpa tanda hubung, /dashboard ikut
     kehilangan navnya dan tab Dashboard jadi tidak bisa ditandai aktif. */
  /* [data-nonav] — SATU-SATUNYA anggota daftar ini yang datang dari JS, bukan
     dari HTML (9 September 2026). Dipasang shell.js pada /@handle MILIK ORANG
     LAIN, yang navnya dicabut tapi tidak bisa dikenali dari data-page: profil
     sendiri dan profil orang lain memakai data-page="account" yang sama.
     Perangkap kedip yang dijelaskan di atas TIDAK berlaku di sana — <main>-nya
     kosong sampai profile.js mengisinya, jadi tidak ada isi yang sempat
     tergambar dengan padding lama. Alasan lengkapnya di shell.js, blok
     PROFIL ORANG LAIN. */
  body[data-page="create"] .shell,
  body[data-page^="dashboard-"] .shell,
  body[data-page^="settings"] .shell,
  body[data-nonav] .shell{inset:0}
  .app{display:flex;height:100%}
  .sidebar{display:none}

  /* ===== FEED — card + peek ===== */
  .feed{flex:1;position:relative;overflow-y:auto;scroll-snap-type:y mandatory;scrollbar-width:none}
  .feed::-webkit-scrollbar{display:none}
  .card{height:100%;scroll-snap-align:start;scroll-snap-stop:always;
    position:relative;display:flex;align-items:stretch;justify-content:center}
  .stage{position:relative;height:100%;width:100%;display:flex;align-items:stretch;justify-content:center}
  @media (min-width:1024px){.stage{width:auto}}

  .media{position:relative;height:100%;width:100%;overflow:hidden;flex:1 1 auto;background:#0a0a0a;
    background-size:cover;background-position:50% 40%}
  .media::after{content:"";position:absolute;inset:0;pointer-events:none;
    background:linear-gradient(180deg,rgba(0,0,0,.42) 0%,rgba(0,0,0,.10) 10%,rgba(0,0,0,0) 24%,rgba(0,0,0,0) 60%,rgba(0,0,0,.28) 74%,rgba(0,0,0,.60) 100%)}
  .vid{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:0;background:#000}
  /* Video mendatar/persegi DILABUHKAN, tidak dipotong (keputusan DimDim
     6 Sep 2026). Pita atas-bawah HITAM POLOS — bukan latar blur dari video
     itu sendiri: blur berarti merender video kedua di belakang tiap kartu dan
     menjalankan filter sepanjang pemutaran, dan feed ini sengaja dijaga ringan.
     #000 datang dari background .vid di baris atas, jadi tidak ada properti
     tambahan. Yang berubah HANYA cara video mengisi kartu — geometri .card /
     .stage / .media tidak disentuh sama sekali, jadi scroll-snap dan tinggi
     kartu (yang punya sejarah rapuh, lihat --top-gap) tetap utuh. */
  .media.is-wide .vid{object-fit:contain}
  .playpause{position:absolute;inset:0;z-index:2;display:none;place-items:center;pointer-events:none}
  .card.paused .playpause{display:grid}
  .playpause svg{width:76px;height:76px;fill:rgba(255,255,255,.92);filter:drop-shadow(0 2px 14px rgba(0,0,0,.5))}

  .mute{position:fixed;left:20px;top:calc(var(--topbar-h) + var(--top-inset) + 2px);z-index:36;
    height:40px;padding:0 16px 0 13px;border-radius:999px;
    display:inline-flex;align-items:center;gap:7px;
    background:#FFFFFF;color:#0F1419;font-size:14px;font-weight:600;
    box-shadow:0 2px 10px rgba(0,0,0,.22);
    transition:transform .1s,opacity .2s,visibility .2s}
  .mute:active{transform:scale(.97)}
  .mute .ico{width:19px;height:19px;stroke-width:2;flex:none}
  body.sound-on .mute{opacity:0;visibility:hidden;pointer-events:none}

  /* meta + actions INSIDE .media
     bottom TANPA env(safe-area-inset-bottom) sejak 30 Agustus 2026: .shell
     sudah berhenti di atas safe-area (lihat .shell), jadi dasar kartu tidak
     lagi menyentuhnya. Menambahkannya di sini akan menghitungnya DUA KALI dan
     menaikkan caption ~24-34px tanpa alasan di HP bergestur/berponi.
     Desktop tidak terpengaruh: aturan >=1024px di bawah sudah memakai
     bottom:var(--pad-card) polos sejak dulu. */
  .meta{position:absolute;left:var(--pad-card);right:calc(var(--pad-card) + var(--rail-w) + 14px);bottom:var(--pad-card);z-index:3;color:#fff}
  .meta .who{display:flex;align-items:center;gap:8px;margin-bottom:8px}
  /* a.who: matikan tampilan link bawaan; warisi warna putih .meta. Layout
     tak berubah — selector .who yang sama sudah flex di atas. */
  a.who{text-decoration:none;color:inherit;-webkit-tap-highlight-color:transparent;
    position:relative;width:fit-content}
  /* area sentuh 44px tanpa menggeser tata letak (pola sama dgn .more-link di
     bawah): kotak tak terlihat setinggi 44px di tengah baris .who. */
  a.who::after{content:"";position:absolute;left:0;right:0;top:50%;
    transform:translateY(-50%);height:44px}
  @media (hover:hover) and (pointer:fine){a.who:hover .u{text-decoration:underline;text-underline-offset:2px}}
  .meta .pfp{width:32px;height:32px;border-radius:50%;border:1.5px solid rgba(255,255,255,.85);flex:none;background:#2a2a2a;background-size:cover;background-position:center}
  .meta .u{font-weight:600;font-size:15px}
  /* caption = tombol: ketuk -> memanjang di tempat. Isyaratnya hanya "..."
     bawaan -webkit-line-clamp (tanpa tombol "more" — disengaja, lihat README).
     Klamp ada di .ctxt, bukan di .c: line-clamp pada <button> bermasalah di
     Safari. JS men-disable tombol kalau teks tidak terpotong. */
  .meta .c{display:block;width:100%;max-width:42ch;text-align:left;
    font-size:14px;line-height:1.4;color:rgba(255,255,255,.92);
    -webkit-tap-highlight-color:transparent}
  .meta .c:disabled{cursor:default}
  .meta .ctxt{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
  /* dibuka: klamp mati (display:block), tinggi dibatasi + bisa di-scroll
     supaya caption panjang tidak menutupi seluruh video */
  .meta .c.open .ctxt{display:block;max-height:38vh;overflow-y:auto;overscroll-behavior:contain}
  /* quiet "See more" link — reads as post content, not a shop CTA */
  .meta .more-link{display:inline-flex;align-items:center;gap:4px;width:fit-content;
    margin-top:11px;padding:7px 14px;font-size:14px;font-weight:600;color:#fff;
    background:rgba(0,0,0,.55);border-radius:999px;
    -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
    text-shadow:0 1px 3px rgba(0,0,0,.4);-webkit-tap-highlight-color:transparent}
  .meta .more-link svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:2;
    stroke-linecap:round;stroke-linejoin:round;transition:transform .15s}
  @media (hover:hover) and (pointer:fine){.meta .more-link:hover svg{transform:translateX(2px)}}

  .actions{position:absolute;right:var(--pad-card);bottom:var(--pad-card);z-index:5;display:flex;flex-direction:column;gap:15px;align-items:center}
  .act{display:flex;flex-direction:column;align-items:center;gap:4px;color:#fff}
  .act .bubble{width:44px;height:44px;border-radius:50%;display:grid;place-items:center;background:rgba(0,0,0,.55);transition:transform .1s,background .15s}
  .act:active .bubble{transform:scale(.93)}
  .act .n{font-size:13px;font-weight:500;text-shadow:0 1px 6px rgba(0,0,0,.6)}
  .act .ico{width:23px;height:23px}
  .act[aria-pressed="true"] .ico{fill:#ff4d6d;stroke:#ff4d6d}
  .act-more .ico{fill:currentColor;stroke:none}

  /* ===== BOTTOM SHEETS (mobile) / MODAL (desktop) ===== */
  .bsheet-backdrop{position:fixed;inset:0;z-index:55;background:rgba(0,0,0,.55);opacity:0;pointer-events:none;transition:opacity .25s}
  .bsheet-backdrop.open{opacity:1;pointer-events:auto}
  .bsheet{position:fixed;left:0;right:0;bottom:0;z-index:56;background:var(--surface);color:var(--text);
    padding:8px 20px calc(24px + env(safe-area-inset-bottom));border-radius:20px 20px 0 0;
    transform:translateY(100%);pointer-events:none;transition:transform .28s cubic-bezier(.4,0,.2,1),opacity .2s ease}
  .bsheet.open{transform:translateY(0);pointer-events:auto}
  .bsheet__handle{width:40px;height:4px;border-radius:2px;background:rgba(255,255,255,.3);margin:6px auto 16px}
  .bsheet__head{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px}
  .bsheet__head h3{font-size:18px;font-weight:700}
  .bsheet__close{width:34px;height:34px;display:grid;place-items:center;border-radius:50%;color:var(--muted)}
  .bsheet__close svg{width:20px;height:20px}
  .bsheet__cta{width:100%;background:#fff;color:#111;border-radius:12px;padding:14px;
    display:flex;align-items:center;justify-content:center;gap:8px;font-weight:700}
  .bsheet__cta svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:2}
  .price-row{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin-bottom:8px}
  .price-amount{font-size:30px;font-weight:800}
  .price-orig{font-size:16px;color:var(--muted);text-decoration:line-through}
  .price-badge{font-size:12px;font-weight:700;color:#fff;background:#e5484d;padding:3px 8px;border-radius:6px}
  .price-desc{font-size:14px;line-height:1.5;color:var(--muted);margin:0 0 18px}
  .qr-wrap{display:flex;flex-direction:column;align-items:center;gap:14px}
  .qr-box{width:190px;height:190px;background:#fff;border-radius:14px;padding:12px;display:grid;place-items:center}
  .qr-box img,.qr-box canvas{width:100%;height:100%;display:block}
  .qr-cap{font-size:14px;color:var(--muted);text-align:center}
  .qr-actions{display:flex;gap:12px;margin-top:18px}
  .qr-btn{flex:1;background:var(--bubble);color:var(--text);border-radius:12px;padding:12px;font-size:14px;font-weight:700;
    display:flex;align-items:center;justify-content:center;gap:8px}
  .qr-btn svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:2}
  .more-row{width:100%;display:flex;align-items:center;gap:14px;padding:16px 4px;color:var(--text);font-size:15px}
  .more-row svg{width:22px;height:22px;fill:none;stroke:currentColor;stroke-width:1.9}
  @media (min-width:1024px){
    .bsheet{left:50%;right:auto;bottom:auto;top:50%;width:380px;max-width:calc(100vw - 40px);
      border-radius:18px;padding:20px 22px 24px;transform:translate(-50%,-46%);opacity:0}
    .bsheet.open{transform:translate(-50%,-50%);opacity:1}
    .bsheet__handle{display:none}
  }

  /* chevrons (wide) */
  .scroll-nav{position:fixed;right:20px;top:50%;transform:translateY(-50%);z-index:35;display:flex;flex-direction:column;gap:12px;pointer-events:none;background:none}
  .scroll-nav > *{pointer-events:auto}
  .scroll-nav .chev-up,.scroll-nav .chev-down{display:none}
  .scroll-nav button{width:44px;height:44px;border-radius:50%;display:grid;place-items:center;background:var(--bubble);color:var(--text);transition:background .15s,transform .1s,opacity .15s}
  .scroll-nav button:active{transform:scale(.94)}
  body.at-top .chev-up,body.at-bottom .chev-down{opacity:.3;pointer-events:none}


  .toast{position:fixed;left:50%;top:calc(var(--topbar-h) + var(--top-inset) + 12px);transform:translate(-50%,-16px);z-index:70;
    background:var(--surface);color:var(--text);border:1px solid var(--line);border-radius:999px;
    padding:9px 16px;font-size:13px;opacity:0;pointer-events:none;transition:opacity .18s,transform .18s;box-shadow:0 6px 22px rgba(0,0,0,.24)}
  .toast.show{opacity:1;transform:translate(-50%,0)}



  /* profile dropdown (desktop) */
  .acct{position:relative}
  .menu{position:absolute;top:calc(100% + 10px);right:0;min-width:236px;z-index:60;
    background:var(--bg);border:1px solid var(--line);border-radius:14px;padding:6px;
    box-shadow:0 10px 34px rgba(0,0,0,.18);display:none}
  .menu.open{display:block}
  .menu hr{border:none;border-top:1px solid var(--line);margin:6px 4px}
  .menu a,.menu button{display:flex;align-items:center;gap:11px;width:100%;padding:10px 10px;border-radius:9px;
    font-size:14px;font-weight:500;color:var(--text);text-align:left;background:none}
  .menu .ico{width:19px;height:19px}
  @media (hover:hover) and (pointer:fine){.menu a:hover,.menu button:hover{background:var(--hover)}}

  /* area sentuh minimal 44px untuk tombol-tombol kecil — ukuran visual tetap */
  .iconbtn,.avatar,.bsheet__close,.search-clear{position:relative}
  .iconbtn::after,.avatar::after,.bsheet__close::after,.search-clear::after{content:"";position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:44px;height:44px}
  .btn,.meta .more-link{position:relative}
  .btn::after,.mute::after,.meta .more-link::after{content:"";position:absolute;left:0;right:0;top:50%;transform:translateY(-50%);height:44px}

  :focus-visible{outline:2px solid var(--accent);outline-offset:2px}

  /* hover only on real pointers */
  @media (hover:hover) and (pointer:fine){
    .iconbtn:hover{background:var(--bubble-hover)}
    .btn-primary:hover{opacity:.88}
    .btn-ghost:hover{background:var(--hover)}
    .scroll-nav button:hover{background:var(--bubble-hover)}
    .navitem:hover{background:var(--hover)}
    .dsearch:hover{background:var(--bubble-hover)}
  }

  /* ===== WIDE (>=1024) ===== */
  @media (min-width:1024px){
    .topbar{display:none!important}
    .m-acct{display:none!important}
    /* PC: lingkaran ikon saja, dikunci di kanan, tepat di atas grup chevron.
       Chevron tetap di 50% -> tidak ikut bergerak saat mute hilang. */
      .mute{left:auto;right:20px;top:calc(50% - 115px);
      width:48px;height:48px;padding:0;gap:0;border-radius:50%;
      justify-content:center;
      background:var(--bubble);color:var(--text);box-shadow:none}
    .mute span{display:none}
    .mute .ico{width:21px;height:21px}
    @media (hover:hover){.mute:hover{background:var(--bubble-hover)}}
    .scroll-nav{gap:14px}
    /* Nav bawah display:none di desktop, jadi potongannya harus dibatalkan —
       kalau tidak, SEMUA halaman menyisakan 50px kosong di dasar layar.
       Sejak 31 Agustus potongannya ada di `.shell` polos (0,1,0), jadi aturan
       ini juga boleh polos: spesifisitas sama, letaknya lebih bawah, menang.
       PERANGKAP YANG MASIH HIDUP: kalau kelak potongan itu ditulis ulang dengan
       selektor yang lebih spesifik (body[...] .shell = 0,2,0), aturan ini
       KALAH meskipun letaknya lebih bawah — media query tidak menambah
       spesifisitas apa pun. Kasus "aturannya ADA tapi tidak MENANG".
       Aturan tanpa-potongan untuk /create & /settings* (0,2,0) tidak perlu
       disebut di sini: nilainya sudah inset:0, sama dengan yang diinginkan. */
    .shell{inset:0}
      .scroll-nav .chev-up,.scroll-nav .chev-down{display:grid}
    .auth-float{display:flex}
    body.signed-in .auth-in{display:flex;align-items:center}
    .toast{top:20px}

    .sidebar{display:flex;flex-direction:column;width:var(--sidebar-w);flex:none;padding:18px 14px 14px;overflow-y:auto;scrollbar-width:none}
    .sidebar::-webkit-scrollbar{display:none}
    .sidebar .logo{padding:0 10px;margin-bottom:18px;color:#71A3BF}
    .sidebar .logo svg{height:30px}
    .dsearch{display:flex;align-items:center;gap:9px;height:42px;padding:0 14px;margin-bottom:16px;
      border-radius:999px;background:var(--bubble);color:var(--muted);transition:background .15s}
    .dsearch .ico{width:19px;height:19px;flex:none}
    .dsearch input{flex:1;min-width:0;background:none;border:none;outline:none;color:var(--text);font-size:15px}
    .dsearch input::placeholder{color:var(--muted)}
    .dsearch input:disabled{cursor:pointer}

    /* ----- panel saran live (sidebar desktop) — lihat shell.js SEARCH.
       HP tak pernah melihat ini: .sidebar display:none < 1024px. ----- */
    .dsuggest-wrap{position:relative;margin-bottom:16px}
    .dsuggest-wrap .dsearch{margin-bottom:0}          /* margin pindah ke wrapper */
    .dsuggest{position:absolute;left:0;right:0;top:calc(100% + 6px);z-index:45;
      background:var(--bg);border:1px solid var(--line);border-radius:14px;padding:6px;
      box-shadow:0 12px 28px rgba(0,0,0,.12);max-height:320px;overflow-y:auto;scrollbar-width:none}
    .dsuggest::-webkit-scrollbar{display:none}
    .dsuggest[hidden]{display:none}
    .ds-row{display:flex;align-items:center;height:40px;padding:0 12px;border-radius:9px;
      color:var(--text);font-size:14px}
    .ds-row.is-active{background:var(--hover)}
    .ds-n{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    /* .ds-all (baris "See all results") DIHAPUS 6 September 2026 bersama
       halaman /search yang dituju baris itu.
       Penggantinya .ds-empty: BUKAN tombol, cuma teks. Dipakai untuk nol hasil
       DAN untuk galat/rate-limit. Sebelumnya panel ini tak pernah bisa kosong
       karena baris "See all" selalu terpasang; tanpa keadaan kosong, panel
       mengambang yang hampa tidak bisa dibedakan dari "masih memuat".
       Padding & warna disamakan dengan .sr-empty di overlay HP supaya dua
       permukaan yang sama perannya tidak terlihat seperti dua produk. */
    .ds-empty{padding:14px 12px;color:var(--muted);font-size:13.5px}

    .navitem{display:flex;align-items:center;gap:15px;height:52px;padding:0 10px;border-radius:10px;
      color:var(--text);font-weight:500;font-size:17px;transition:background .15s}
    .navitem[aria-current="page"]{font-weight:700}
    .navitem .ico{width:27px;height:27px;flex:none;stroke-width:1.6}
    .nav-an{display:none}
    body.signed-in .nav-an{display:flex}
    .foot{margin-top:14px;padding:16px 10px 0;border-top:1px solid var(--line);display:flex;flex-direction:column;gap:7px}
    .foot a{color:var(--muted);font-size:13px;font-weight:500}
    a{text-decoration:none}
    .foot .cc{color:var(--muted);font-size:13px;margin-top:4px;opacity:.75}

    .card{gap:0;padding:22px 16px;justify-content:center;height:100%}
    .media{border-radius:32px;box-shadow:0 14px 40px rgba(15,20,25,.16);width:auto;flex:0 0 auto;aspect-ratio:9/16;transform:none;opacity:1}
    .meta{right:var(--pad-card);left:var(--pad-card);bottom:var(--pad-card)}
    .actions{position:static;bottom:auto;right:auto;justify-content:flex-end;padding-bottom:8px;gap:18px;margin-left:var(--pad-card)}
    .act{color:var(--text)}
    .act .bubble{width:48px;height:48px;background:var(--bubble)}
    @media (hover:hover){.mute:hover{background:var(--bubble-hover)}}
    .act .n{color:var(--muted);font-size:13px;font-weight:600;text-shadow:none}
    .act[aria-pressed="true"] .ico{fill:#ff4d6d;stroke:#ff4d6d}
    @media (hover:hover){.act:hover .bubble{background:var(--bubble-hover)}}
  }

  /* ===== LOGIN WALL — batas feed untuk guest ===== */
  .gate-card{display:none}
  body:not(.signed-in) .gate-card{display:flex}
  .gate{position:relative;flex:1 1 auto;width:100%;display:flex;flex-direction:column;
    align-items:center;justify-content:center;text-align:center;gap:12px;
    padding:40px 30px calc(40px + env(safe-area-inset-bottom));background:#0d0d0d;color:#fff}
  .gate-title{font-size:23px;font-weight:800;letter-spacing:-.015em}
  .gate-actions{display:flex;flex-direction:column;gap:10px;width:100%;max-width:280px;margin-top:14px}
  .gate-actions .btn{width:100%;height:48px;font-size:15px}
  .gate-actions .btn-ghost{background:transparent;box-shadow:inset 0 0 0 1px var(--line)}
  @media (min-width:1024px){
    .gate-card .stage{width:auto}
    .gate{flex:0 0 auto;height:100%;width:auto;aspect-ratio:9/16;max-height:100%;
      border-radius:32px;background:var(--surface);color:var(--text);box-shadow:0 14px 40px rgba(15,20,25,.16)}
  }


  /* ===== DESKTOP (>=1024) — scrim hanya di bawah (topbar hilang, aksi di luar kartu) ===== */
  @media (min-width:1024px){
    .media::after{background:linear-gradient(180deg,rgba(0,0,0,0) 0%,rgba(0,0,0,0) 52%,rgba(0,0,0,.34) 76%,rgba(0,0,0,.60) 100%)}
    .meta .c{max-width:56ch}                        /* isi lebar kartu; masih dalam 45-75ch */
    .meta .ctxt{-webkit-line-clamp:3}               /* ruang vertikal lebih lega -> 3 baris */
  }
  /* ===== TABLET (768–1023) — feed jadi kolom terpusat, bukan full-bleed melebar ===== */
  @media (min-width:768px) and (max-width:1023px){
    .stage{max-width:560px;margin:0 auto}           /* media + rail aksi ikut ke kolom ini */
    .mute{left:calc(50% - 280px + 8px)}             /* dekatkan pill mute ke kolom */
  }
  /* ===== LAYAR SANGAT PENDEK (HP lanskap) — rapatkan rail & caption ===== */
  @media (max-height:520px) and (max-width:1023px){
    .actions{gap:10px}
    .act .bubble{width:38px;height:38px}
    .act .ico{width:20px;height:20px}
    .meta .ctxt{-webkit-line-clamp:1}
    .meta .c.open .ctxt{max-height:30vh}
    .meta .more-link{margin-top:7px}
  }
  /* ===== HP SANGAT SEMPIT (<=360) — cegah topbar sesak ===== */
  @media (max-width:360px){
    .topbar{gap:6px}
    .topbar .btn-primary{padding:0 12px}
  }

  @media (prefers-reduced-motion:reduce){.media{animation:none}*{transition:none!important}}

  /* ===== NAV BAWAH — HP saja =====
     PINDAH DARI page.css 30 Agustus 2026. Alasannya: index.html (feed) memuat
     styles.css SAJA, tanpa page.css. Sejak nav bawah tampil di semua halaman
     termasuk feed, aturannya harus ada di lembar gaya yang SEMUA halaman muat —
     kalau tidak, feed menyuntik <nav> tanpa gaya sama sekali.
     Tinggi nav = var(--botnav-h): angka yang sama juga memotong .shell, jadi
     mengubahnya di satu tempat mengubah tinggi .feed sekalian. Jangan pernah
     menulis 50px langsung di sini. */
  .bottomnav{position:fixed;left:0;right:0;bottom:0;z-index:50;display:flex;
    background:var(--bg);border-top:1px solid var(--line);
    padding-bottom:env(safe-area-inset-bottom)}
  /* 50px = standar tab bar iOS (49pt). Ini LANTAINYA: area sentuh tiap item =
     tinggi nav, minimum 44px; isinya ikon 24 + jarak 3 + label ~13 = 40px.
     Lebih pendek dari 48px -> label sesak & area sentuh menyentuh ambang. */
  .bn-item{flex:1;min-width:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;
    height:var(--botnav-h);color:var(--text);font-size:11px;font-weight:600;text-decoration:none;
    -webkit-tap-highlight-color:transparent}
  .bn-item .ico{width:24px;height:24px;flex:none}
  /* label tidak boleh membungkus jadi dua baris: empat item (bukan lagi tiga)
     berarti tiap item ~25% lebar. Di 360px itu 90px — "Dashboard" pada 11px
     muat, tapi ellipsis lebih baik daripada nav yang tiba-tiba setinggi dua
     baris kalau kelak ada label lebih panjang. */
  .bn-item span{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  /* warna semua sama -> pembedanya ikon terisi + label tebal */
  .bn-item[aria-current="page"]{font-weight:700}
  /* TAMU: nav bawah TETAP TAMPIL, hanya Dashboard yang disembunyikan.
     Menggantikan aturan lama di page.css yang menyembunyikan SELURUH nav bawah
     untuk tamu di /@handle & /post/<id>. Dua alasan:
       1. nav bawah kini ada di SEMUA halaman termasuk feed; aturan lama membuat
          tamu berpindah antara halaman ber-nav dan tanpa-nav — persis
          ketidakkonsistenan yang sedang dicabut;
       2. tinggi nav menentukan tinggi .feed. Nav yang muncul-hilang menurut
          status login = dua tinggi scroller, dan yang untuk tamu persis
          seukuran viewport -> bug root scroller 23 Agt hidup lagi HANYA untuk
          tamu, dan senyap (pengujian selalu dilakukan sambil login).
     Create & Profile SENGAJA tetap terlihat untuk tamu dan melempar ke /login:
     itu perilaku sidebar desktop sejak lama (.nav-an juga hanya menyembunyikan
     Dashboard). Itu jalur masuk, bukan jalan buntu. */
  body:not(.signed-in) .bn-an{display:none}
  @media (min-width:1024px){.bottomnav{display:none}}

  /* ===== NAV AKTIF (sidebar & nav bawah) =====
     Ikon berpasangan: .ico-out saat non-aktif, .ico-in (terisi) saat aktif.
     Aturan di bawah HANYA menambah selector baru — tidak mengubah aturan yang
     sudah ada, jadi cascade lama tidak tergeser (feed aman). */
  .ico-in{display:none}
  .navitem[aria-current="page"] .ico-out,.bn-item[aria-current="page"] .ico-out{display:none}
  .navitem[aria-current="page"] .ico-in,.bn-item[aria-current="page"] .ico-in{display:block}
  /* Warna semua item nav sama (#0F1419). Pembedanya = ikon terisi + huruf tebal,
     bukan warna — pola Instagram/TikTok. (Aturan redup var(--muted) dicabut.) */

/* ===== SKELETON (dipindah dari page.css, 4 Agustus 2026) =====
   Feed hanya memuat styles.css, jadi kerangka muat tidak bisa tinggal di
   page.css. DIPINDAH ke sini supaya berlaku di semua halaman — bukan
   diduplikasi; page.css sekarang hanya menyimpan varian per halamannya. */
@keyframes sk-pulse{0%,100%{opacity:1}50%{opacity:.45}}
.sk{background:var(--bubble);border-radius:8px;animation:sk-pulse 1.2s ease-in-out infinite}
@media (prefers-reduced-motion:reduce){.sk{animation:none}}
.sk-line{display:block;height:13px;border-radius:7px}

/* ===== KEADAAN FEED (kosong / galat / kerangka) =====
   Dipakai ulang dari .gate: satu "slide" seukuran kartu, jadi scroll-snap dan
   tinggi feed tidak perlu aturan khusus. */
.feed-note{font-size:14px;line-height:1.5;color:var(--muted);margin-top:8px;max-width:280px}
.feed-state .gate{gap:6px}

/* ===== KERANGKA FEED (1 September 2026) =====
   .feed-state (kosong/galat) TETAP memakai .gate dan TETAP terpusat — untuk
   PESAN itu benar. Kerangka muat tidak: ia memakai .card/.stage/.media/.meta/
   .actions yang SAMA dengan kartu asli, jadi seluruh geometri (padding kartu,
   aspect-ratio 9/16 di desktop, --rail-w, pemendekan rel di lanskap) datang
   gratis dari aturan yang sudah teruji. Yang ditambahkan di bawah HANYA ukuran
   balok penggantinya.
   Dua kelas garis versi lama DIHAPUS bersama showSkeleton() lama; namanya
   sengaja tidak ditulis di sini karena uji menegakkan bahwa keduanya tidak
   muncul lagi di berkas mana pun.

   Kenapa .meta perlu override warna: .sk memakai var(--bubble) yang ikut tema
   (rgba(0,0,0,.05) di >=1024px), sedangkan .media SELALU #0a0a0a di semua
   lebar. Tanpa baris ini, isi .meta tidak terlihat sama sekali di desktop.
   Rel aksi sengaja TIDAK diikutkan: di HP ia di ATAS media dan --bubble sudah
   terang; di desktop ia di LUAR kartu di atas latar terang dan --bubble sudah
   gelap. Sama persis dengan perlakuan .act .bubble yang asli. */
.feed-skel .meta .sk{background:rgba(255,255,255,.16)}
.feed-skel .fs-pfp{width:32px;height:32px;border-radius:50%;flex:none}   /* = .meta .pfp */
.feed-skel .fs-name{width:118px;height:15px}                             /* = .meta .u  */
.feed-skel .fs-t1{width:88%;max-width:42ch;height:13px}                  /* = baris 1 judul */
.feed-skel .fs-t2{width:62%;max-width:42ch;height:13px;margin-top:7px}   /* = baris 2 judul */
.feed-skel .fs-btn{display:block;width:124px;height:34px;border-radius:999px;margin-top:11px}
.feed-skel .fs-act{display:block;width:44px;height:44px;border-radius:50%}
@media (min-width:1024px){.feed-skel .fs-act{width:48px;height:48px}}
/* HP lanskap: rel asli menyusut jadi 38px (lihat blok max-height:520px di
   bawah). Kerangka ikut, kalau tidak ia jadi satu-satunya hal di feed yang
   tidak menyusut saat layar dimiringkan. */
@media (max-height:520px) and (max-width:1023px){.feed-skel .fs-act{width:38px;height:38px}}

/* Sprite <defs> logo — dulu style="display:none" inline yang diblokir CSP. */
.sprite-hidden{display:none}

/* ===== SHEET REPORT (5 Agustus 2026) =====
   Di styles.css, bukan page.css, karena feed hanya memuat styles.css dan
   Report ada di feed. Satu tempat, dua permukaan. */
.rp-lead{font-size:15px;font-weight:600;margin-bottom:12px}
.rp-note{font-size:13.5px;line-height:1.55;color:var(--muted);margin-bottom:16px}
.rp-note a{color:inherit;text-decoration:underline;text-underline-offset:2px}
.rp-list{display:flex;flex-direction:column}
/* Baris penuh, tinggi 48px: daftar ini diketuk di ponsel, dan pilihan yang
   salah ketuk di sini berarti laporan dengan kategori keliru. */
.rp-opt{display:flex;align-items:center;width:100%;min-height:48px;padding:12px 2px;
  border:0;border-top:1px solid var(--line);background:transparent;color:inherit;
  font:inherit;font-size:15px;text-align:left;cursor:pointer}
.rp-opt:first-child{border-top:0}
.rp-opt:focus-visible{outline:2px solid var(--accent);outline-offset:-2px;border-radius:6px}
@media (hover:hover){.rp-opt:hover{opacity:.7}}
.rp-done{width:100%;margin-top:4px}
