/* ============================================================
   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}

  /* ============================================================
     [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{
    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}
  .m-acct{display:none}
  body.signed-in .m-acct{display:block}

  /* ===== 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 ===== */
  .shell{position:fixed;inset:0}
  /* POTONGAN NAV BAWAH — FEED SAJA, dan itu disengaja.
     Di feed nav MENYITA ruang: .shell berhenti di atas nav, jadi .meta &
     .actions (yang menempel di dasar kartu) ikut naik sendiri — tidak ada satu
     pun yang perlu digeser manual.
     Di halaman NON-feed nav MENIMPA: .shell tetap penuh dan yang mengalah
     adalah padding-bottom .page-main (page.css). Itu perilaku yang sudah
     berjalan sejak dulu dan TIDAK diubah.
     JANGAN naikkan aturan ini ke .shell umum. Kalau dipasang di semua halaman,
     ruang nav terhitung DUA KALI di 23 halaman non-feed (potongan .shell +
     padding .page-main), dan isinya mengambang ~100px di atas dasar layar.

     Potongan ini juga yang menggantikan --top-gap:6px sebagai penjaga agar
     .feed tidak persis seukuran viewport — riwayat lengkap di kepala berkas.
     Selectornya body[data-page="home"], dan index.html memang menulis atribut
     itu secara harfiah (bukan mengandalkan default 'home' di shell.js), jadi
     tidak ada celah antara apa yang dilihat CSS dan apa yang dilihat JS.

     safe-area ikut dipotong di sini, jadi .meta/.actions TIDAK boleh
     menambahkannya lagi — itu akan terhitung dua kali. */
  body[data-page="home"] .shell{inset:0 0 calc(var(--botnav-h) + env(safe-area-inset-bottom)) 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}
  .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}
    /* SELECTOR HARUS SESPESIFIK aturan potongan nav di atas
       (body[data-page="home"] .shell). `.shell{inset:0}` polos = spesifisitas
       (0,1,0) dan KALAH dari (0,2,0) meskipun letaknya lebih bawah — media
       query tidak menambah spesifisitas apa pun. Kalau ditulis polos, desktop
       feed menyisakan 50px kosong di bawah padahal nav bawah display:none di
       sini. Ini kasus "aturannya ADA tapi tidak MENANG". */
    .shell,body[data-page="home"] .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{width:100%;text-align:left;height:38px;padding:0 12px;margin-top:2px;
      border-top:1px solid var(--line);border-radius:0 0 9px 9px;color:var(--muted);
      font-size:13px;font-weight:600;background:none;cursor:pointer}
    .ds-all:hover{color:var(--text);background:var(--hover)}

    .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}
.feed-sk-1{width:60%;height:18px}
.feed-sk-2{width:40%;margin-top:10px}

/* 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}
