/* ==========================================================================
   Prava — Galeri Client
   Stylesheet berdiri sendiri. Tidak bergantung Bootstrap / Tailwind / jQuery.
   Token warna & font di-inject dari GallerySetting lewat layout.blade.php.
   ========================================================================== */

/* --------------------------------------------------------------- Tokens -- */
:root {
    --bg: #fbfbfa;
    --surface: #ffffff;
    --fg: #17171a;
    --muted: #74747e;
    --line: rgba(0, 0, 0, .09);
    --line-strong: rgba(0, 0, 0, .16);
    --overlay: rgba(250, 250, 249, .72);
    --shadow: 0 1px 2px rgba(0, 0, 0, .04), 0 8px 24px rgba(0, 0, 0, .06);
    --radius: 14px;   /* ditimpa dari GallerySetting */
    --gap: 12px;      /* ditimpa dari GallerySetting */
    --accent: #1a1a1a;    /* ditimpa dari GallerySetting */
    --accent-fg: #ffffff; /* teks di ATAS aksen — dihitung GallerySetting::readableOn() */
    --pad: clamp(18px, 5vw, 64px);
    --bar-h: 66px;
    /* Kurva gerak TUNGGAL untuk seluruh produk — galeri client DAN halaman
       publik studio (landing.css memakai token yang sama, tidak punya kurva
       sendiri). Dulu keduanya berbeda tipis dan akibatnya terasa: client yang
       membuka halaman depan lalu masuk ke galerinya melewati dua "kepribadian"
       gerak yang tidak sama. Nilainya melambat sangat panjang di ujung —
       berhenti mulus, tidak pernah memantul. Tercatat di docs/design-dna.json
       (design_system.motion.easing); ubah di sini, ubah di sana juga. */
    --ease: cubic-bezier(.16, 1, .3, 1);

    /* Jeda giliran antar-elemen yang muncul bersama (grid galeri, kartu karya,
       daftar langkah). Dibaca JS lewat getComputedStyle, bukan ditulis ulang
       sebagai angka di sana — dua sumber kebenaran untuk nilai yang sama pasti
       berbeda begitu salah satunya disetel. */
    --stagger: 70ms;

    /* Lebar kolom grid = ukuran thumbnail yang dipilih admin, apa adanya.
       JANGAN kembalikan batas 42vw ke sini: batas itu untuk ponsel, tapi
       dulu ditulis di :root sehingga ikut berlaku di desktop dan memangkas
       SEMUA ukuran besar ke angka yang sama — di jendela ~1300px, "sedang"
       sampai "raksasa" jadi tidak ada bedanya sama sekali (dan di pratinjau
       admin yang lebih sempit, bedanya hilang lebih awal lagi).
       Penanganan ponsel ada di blok @media "Kolom di layar kecil". */
    --col: var(--thumb-w, 300px);
}

[data-theme="dark"] {
    --bg: #08080a;
    --surface: #131316;
    --fg: #f2f2f4;
    --muted: #9b9ba6;
    --line: rgba(255, 255, 255, .11);
    --line-strong: rgba(255, 255, 255, .22);
    --overlay: rgba(8, 8, 10, .72);
    --shadow: 0 1px 2px rgba(0, 0, 0, .5), 0 12px 32px rgba(0, 0, 0, .45);
}

/* Tema "ikuti perangkat": token yang PERSIS SAMA dengan [data-theme="dark"]
   di atas, hanya dipasang saat perangkat pembacanya memang sedang gelap.
   Sengaja diduplikasi, bukan digabung ke daftar selektor yang sama: yang satu
   harus berada DI DALAM media query dan yang satu tidak, jadi tidak ada bentuk
   yang menyatukan keduanya. Ubah satu, ubah keduanya.

   Aksennya TIDAK ada di sini — ia bergantung warna pilihan admin, jadi
   dicetak layout dari GallerySetting::autoDarkAccent(). */
@media (prefers-color-scheme: dark) {
    [data-theme="auto"] {
        --bg: #08080a;
        --surface: #131316;
        --fg: #f2f2f4;
        --muted: #9b9ba6;
        --line: rgba(255, 255, 255, .11);
        --line-strong: rgba(255, 255, 255, .22);
        --overlay: rgba(8, 8, 10, .72);
        --shadow: 0 1px 2px rgba(0, 0, 0, .5), 0 12px 32px rgba(0, 0, 0, .45);
    }
}

/* Beri tahu peramban tema apa yang sedang dipakai, supaya bagian yang digambar
   peramban sendiri ikut menyesuaikan: bilah gulir, kotak isian, dan pemutar
   video bawaan. Tanpa ini galeri gelap masih punya bilah gulir putih terang. */
:root {
    color-scheme: light;
}

[data-theme="dark"],
[data-theme="contrast"] {
    color-scheme: dark;
}

[data-theme="auto"] {
    color-scheme: light dark;
}

/* Pembungkus <picture> untuk logo dua-versi (terang/gelap). `display: contents`
   membuat pembungkusnya hilang dari layout sehingga <img> di dalamnya tetap
   diperlakukan seperti anak langsung induknya — .foot__logo yang memakai
   `margin: 0 auto` tetap terpusat, dan .bar__logo tetap jadi item flex. */
.logo-swap {
    display: contents;
}


[data-theme="sepia"] {
    --bg: #f6f1e7;
    --surface: #fffdf8;
    --fg: #3a2f24;
    --muted: #8a7860;
    --line: rgba(90, 70, 45, .14);
    --line-strong: rgba(90, 70, 45, .28);
    --overlay: rgba(246, 241, 231, .76);
}

[data-theme="contrast"] {
    --bg: #000000;
    --surface: #0c0c0c;
    --fg: #ffffff;
    --muted: #b8b8bd;
    --line: rgba(255, 255, 255, .2);
    --line-strong: rgba(255, 255, 255, .45);
    --overlay: rgba(0, 0, 0, .82);
}

/* ----------------------------------------------------------------- Base -- */
*,
*::before,
*::after {
    box-sizing: border-box;
}

html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
}

.g-body {
    margin: 0;
    background: var(--bg);
    color: var(--fg);
    font-family: var(--font-body);
    font-size: 15px;
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    overflow-wrap: break-word;
}

.g-body.is-locked {
    overflow: hidden;
}

img {
    max-width: 100%;
    display: block;
}

h1,
h2 {
    font-family: var(--font-heading);
    font-weight: 400;
    letter-spacing: -.01em;
    margin: 0;
}

a {
    color: inherit;
    text-decoration: none;
}

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

.ico {
    width: 20px;
    height: 20px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.ico--fill {
    fill: currentColor;
    stroke: none;
}

/* -------------------------------------------------------------- Tombol -- */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    border: 1px solid transparent;
    border-radius: 999px;
    padding: 11px 22px;
    font: inherit;
    font-size: 13.5px;
    font-weight: 500;
    letter-spacing: .01em;
    cursor: pointer;
    background: none;
    color: inherit;
    transition: background-color .18s var(--ease), border-color .18s var(--ease),
        transform .18s var(--ease), opacity .18s var(--ease);
}

/* Aksen dipakai apa adanya di SEMUA tema.
   Sebelumnya tema gelap menimpa .btn--solid dengan --fg, sehingga mengubah
   warna aksen tidak mengubah apa pun yang terlihat di galeri. Keterbacaan
   dijaga --accent-fg (dihitung dari luminansi aksen), bukan dengan
   membuang warnanya.

   Blok ini WAJIB berada SESUDAH .btn. Sebelumnya ia ditulis di dekat daftar
   variabel tema di puncak berkas — spesifisitasnya sama persis dengan .btn,
   jadi `background: none` dan `color: inherit` milik .btn yang datang
   belakangan membatalkan keduanya dan tombol "solid" tidak pernah benar-benar
   berwarna di mana pun (terukur: background rgba(0,0,0,0)). Di atas foto
   sampul yang gelap hal itu tidak kentara — teks putihnya tetap terbaca —
   sehingga bugnya lolos sampai tombol yang sama dipakai di atas panel putih. */
.btn--solid {
    background: var(--accent);
    color: var(--accent-fg);
}

.btn:active {
    transform: scale(.975);
}

.btn:disabled {
    opacity: .4;
    cursor: not-allowed;
}

.btn--solid:not(:disabled):hover {
    filter: brightness(1.12);
}

.btn--ghost {
    border-color: var(--accent);
    color: var(--accent);
}

.btn--ghost:not(:disabled):hover {
    background: var(--accent);
    color: var(--accent-fg);
    border-color: var(--accent);
}

.iconbtn {
    display: inline-grid;
    place-items: center;
    width: 42px;
    height: 42px;
    border: 0;
    border-radius: 50%;
    background: rgba(255, 255, 255, .09);
    color: #fff;
    cursor: pointer;
    backdrop-filter: blur(8px);
    transition: background-color .18s var(--ease), transform .18s var(--ease);
}

.iconbtn:hover {
    background: rgba(255, 255, 255, .2);
}

.iconbtn:active {
    transform: scale(.92);
}

.iconbtn.is-on .ico {
    fill: #ff4d6d;
    stroke: #ff4d6d;
}

/* Tombol Putar Slideshow — dua ikon ditumpuk, ditoggle lewat [data-state]
   supaya tidak perlu ganti innerHTML lewat JS tiap kali status berubah. */
.iconbtn[data-lb-play] .ico--pause {
    display: none;
}

.iconbtn[data-lb-play][data-state="playing"] .ico--play {
    display: none;
}

.iconbtn[data-lb-play][data-state="playing"] .ico--pause {
    display: block;
}

/* ================================================================ COVER == */
.cover {
    position: relative;
    min-height: 100vh;
    min-height: 100svh;
    display: grid;
    isolation: isolate;
    overflow: hidden;
}

.cover__bg {
    position: absolute;
    inset: 0;
    z-index: -1;
}

.cover__img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    animation: drift 32s ease-out forwards;
}

@keyframes drift {
    from {
        transform: scale(1.06);
    }

    to {
        transform: scale(1.16);
    }
}

.cover__scrim {
    position: absolute;
    inset: 0;
    background:
        linear-gradient(180deg, rgba(0, 0, 0, .42) 0%, rgba(0, 0, 0, .26) 38%, rgba(0, 0, 0, .68) 100%);
}

/* Tanpa foto cover: latar polos bertema */
.cover__bg:not(:has(.cover__img)) .cover__scrim {
    background: var(--bg);
}

.cover__inner {
    position: relative;
    min-height: 100vh;
    min-height: 100svh;
    display: grid;
    grid-template-rows: 1fr auto;
    align-items: center;
    justify-items: center;
    gap: clamp(32px, 7vh, 72px);
    padding: clamp(40px, 9vh, 88px) var(--pad);
    text-align: center;
    color: #fff;
}

.cover__bg:not(:has(.cover__img))+.cover__inner {
    color: var(--fg);
}

.cover__head {
    align-self: end;
    animation: rise .9s var(--ease) both;
}

.cover__studio {
    margin: 0 0 clamp(14px, 3vh, 26px);
    font-size: 11px;
    font-weight: 500;
    letter-spacing: .28em;
    text-transform: uppercase;
    opacity: .8;
}

.cover__title {
    font-size: clamp(2.4rem, 8vw, 5.2rem);
    line-height: 1.04;
    max-width: 16ch;
}

.cover__date {
    margin: clamp(16px, 3vh, 26px) 0 0;
    font-size: 12px;
    letter-spacing: .22em;
    text-transform: uppercase;
    opacity: .8;
}

.cover__gate {
    align-self: start;
    width: 100%;
    max-width: 340px;
    animation: rise .9s .12s var(--ease) both;
}

.cover__meta {
    margin: 26px 0 0;
    font-size: 12px;
    letter-spacing: .14em;
    text-transform: uppercase;
    opacity: .65;
}

/* Varian posisi cover */
.cover--left .cover__inner {
    justify-items: start;
    text-align: left;
}

.cover--left .cover__title {
    max-width: 12ch;
}

.cover--split .cover__inner {
    grid-template-rows: none;
    grid-template-columns: 1fr 1fr;
    align-content: center;
    text-align: left;
    justify-items: start;
}

.cover--split .cover__head,
.cover--split .cover__gate {
    align-self: center;
}

@media (max-width: 780px) {
    .cover--split .cover__inner {
        grid-template-columns: 1fr;
        grid-template-rows: 1fr auto;
        justify-items: center;
        text-align: center;
    }
}

@keyframes rise {
    from {
        opacity: 0;
        transform: translateY(20px);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

/* ------------------------------------------------------------ Gate PIN -- */
.gate__label {
    margin: 0 0 18px;
    font-size: 12px;
    letter-spacing: .16em;
    text-transform: uppercase;
    opacity: .82;
}

.gate__inputs {
    display: flex;
    gap: 10px;
    justify-content: center;
}

.cover--left .gate__inputs,
.cover--split .gate__inputs {
    justify-content: flex-start;
}

.gate__box {
    width: 56px;
    height: 66px;
    border: 1px solid rgba(255, 255, 255, .34);
    border-radius: 12px;
    background: rgba(255, 255, 255, .1);
    backdrop-filter: blur(10px);
    color: #fff;
    font: inherit;
    font-size: 26px;
    font-weight: 500;
    text-align: center;
    caret-color: transparent;
    transition: border-color .18s var(--ease), background-color .18s var(--ease),
        transform .18s var(--ease);
}

.gate__box::placeholder {
    color: transparent;
}

.gate__box:focus {
    outline: none;
    border-color: #fff;
    background: rgba(255, 255, 255, .2);
    transform: translateY(-2px);
}

.gate__box.is-filled {
    border-color: rgba(255, 255, 255, .75);
}

.gate__inputs.is-error .gate__box {
    border-color: #ff6b81;
    animation: shake .42s var(--ease);
}

@keyframes shake {

    0%,
    100% {
        transform: translateX(0);
    }

    22% {
        transform: translateX(-8px);
    }

    45% {
        transform: translateX(7px);
    }

    68% {
        transform: translateX(-4px);
    }
}

.gate__error {
    min-height: 20px;
    margin: 12px 0 0;
    font-size: 13px;
    color: #ff8095;
}

.gate__submit {
    width: 100%;
    margin-top: 6px;
}

/* Gerbang PIN yang TIDAK berdiri di atas foto (cover tanpa foto, atau varian
   yang menaruhnya di panel bertema) — kotaknya ikut tema terang/gelap, kalau
   tidak teks putih di atas latar terang praktis tidak terbaca. */
.cover__bg:not(:has(.cover__img))+.cover__inner .gate__box,
.cover--panel .gate__box,
.cover--postcard .gate__box {
    border-color: var(--line-strong);
    background: var(--surface);
    color: var(--fg);
}

.cover--panel .gate__error,
.cover--postcard .gate__error {
    color: #d6455d;
}

/* ================================================================== BAR == */
/* Garis aksen tipis di puncak halaman: satu-satunya tempat warna aksen
   selalu terlihat, bahkan di galeri tanpa Set (tanpa pil aktif) dan tanpa
   tombol unduh. Tanpa ini, mengganti warna aksen sering tidak menghasilkan
   perubahan yang kasat mata di layar pertama. */
.bar {
    position: sticky;
    top: 0;
    z-index: 40;
    border-top: 3px solid var(--accent);
    background: var(--overlay);
    backdrop-filter: saturate(180%) blur(16px);
    -webkit-backdrop-filter: saturate(180%) blur(16px);
    border-bottom: 1px solid transparent;
    transition: border-color .25s var(--ease);
}

.bar.is-stuck {
    border-bottom-color: var(--line);
}

.bar__inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
    min-height: var(--bar-h);
    padding: 0 var(--pad);
}

/* Logo (opsional) di kiri, dua baris teks di kanannya. */
.bar__brand {
    display: flex;
    align-items: center;
    gap: 12px;
    min-width: 0;
    color: inherit;
    text-decoration: none;
}

.bar__text {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
}

/* Tinggi dikunci, lebar mengikuti: logo studio bisa berupa lambang persegi
   atau wordmark yang jauh lebih lebar dari tinggi. `flex-shrink: 0` menjaga
   logo tetap utuh — yang boleh menyusut saat bar sempit adalah teksnya, yang
   memang sudah punya elipsis. */
.bar__logo {
    flex: 0 0 auto;
    height: 30px;
    width: auto;
    max-width: min(150px, 34vw);
    object-fit: contain;
}

@media (max-width: 560px) {
    .bar__logo {
        height: 24px;
        max-width: 92px;
    }
}

.bar__title {
    font-family: var(--font-heading);
    font-size: 19px;
    line-height: 1.25;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Baris kedua bar: nama studio (+ tanggal acara bila ada). */
.bar__studio,
.bar__date {
    font-size: 10.5px;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--muted);
}

.bar__studio {
    display: block;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.bar__sep {
    margin: 0 4px;
    opacity: .55;
}

/* Label pendek hanya muncul di layar sempit (lihat @media di bawah). */
.btn__label--short {
    display: none;
}

.bar__actions {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-shrink: 0;
}

.bar__count {
    font-size: 12px;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--muted);
}

/* ---------------------------------------------------- Bar di layar kecil
   Bar bisa memuat sampai empat kendali (penghitung pilihan, Kirim ke admin,
   Bagikan, Unduh Semua). Di ponsel semuanya tidak muat berdampingan dengan
   judul, dan sebelumnya hanya jumlah foto yang disembunyikan sehingga
   tombol-tombolnya berdesakan lalu meluber. Penanganannya bertahap:
   ≤760px label tombol ikon disembunyikan (tombol jadi bundar),
   ≤430px "Kirim ke admin" ikut memendek. Judul TIDAK pernah dibuang —
   ia yang memberi tahu client sedang di galeri siapa. */
@media (max-width: 760px) {
    .bar__inner {
        gap: 12px;
    }

    .bar__count {
        display: none;
    }

    .bar__actions {
        gap: 8px;
    }

    /* Tombol berlabel + ikon → ikon saja, bentuk bundar sama seperti .iconbtn */
    .bar__actions .btn--ghost {
        gap: 0;
        width: 40px;
        height: 40px;
        padding: 0;
    }

    .bar__actions .btn--ghost .btn__label {
        display: none;
    }

    .bar__title {
        font-size: 16px;
    }
}

@media (max-width: 430px) {
    .bar__inner {
        gap: 10px;
        padding-inline: 14px;
    }

    .bar__actions .btn--solid {
        padding: 9px 13px;
        font-size: 12.5px;
    }

    .bar__actions .btn--solid .btn__label {
        display: none;
    }

    .bar__actions .btn--solid .btn__label--short {
        display: inline;
    }

    .pickbar {
        padding: 5px 9px;
        font-size: 12px;
    }

    /* Label "Penuh" memakan lebar yang tidak ada di layar sekecil ini;
       border merah + kartu yang meredup sudah cukup jadi penanda. */
    .pickbar__full {
        display: none !important;
    }
}

/* Nav Set */
.sets {
    display: flex;
    gap: 4px;
    padding: 0 var(--pad) 12px;
    overflow-x: auto;
    scrollbar-width: none;
}

.sets::-webkit-scrollbar {
    display: none;
}

/* Tab pilihan/favorit dipisahkan dari daftar Set dengan sedikit jarak +
   garis, supaya terbaca sebagai "milik saya", bukan salah satu Set. */
/* margin-left:auto SENGAJA tidak dipakai: di kontainer flex yang menggulir
   (.sets punya overflow-x:auto) margin otomatis bisa membuat item terakhir
   tidak bisa dijangkau saat daftarnya meluber. */
.sets__item--marks {
    margin-left: 6px;
    border-left: 1px solid var(--line);
    border-radius: 0 999px 999px 0;
    padding-left: 16px;
}

.sets__item--marks .sets__ico {
    color: var(--accent);
}

.sets__item {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 7px 15px;
    border-radius: 999px;
    font-size: 13px;
    color: var(--muted);
    white-space: nowrap;
    transition: background-color .18s var(--ease), color .18s var(--ease);
}

.sets__item:hover {
    background: var(--line);
    color: var(--fg);
}

.sets__item.is-active {
    background: var(--accent);
    color: var(--accent-fg);
}

.sets__n {
    font-size: 11px;
    opacity: .6;
}

/* ================================================================= HERO == */
.hero {
    position: relative;
    height: clamp(300px, 58vh, 620px);
    overflow: hidden;
}

.hero__img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.hero__scrim {
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, rgba(0, 0, 0, .16), rgba(0, 0, 0, .58));
}

.hero__text {
    position: absolute;
    inset: auto 0 0;
    padding: var(--pad);
    color: #fff;
    text-align: center;
}

.hero__title {
    font-size: clamp(1.9rem, 5.2vw, 3.6rem);
    line-height: 1.1;
}

.hero__desc {
    max-width: 62ch;
    margin: 14px auto 0;
    font-size: 14.5px;
    opacity: .88;
}

/* Kepala halaman untuk tab Set & tab Favorit — satu-satunya halaman galeri
   yang TIDAK punya foto sampul, jadi seluruh pitanya cuma berisi teks.
   Ukurannya dulu disalin dari halaman bersampul (8vh atas + 4vh bawah) dan
   hasilnya sebuah lubang: terukur 256px hanya untuk dua baris teks, dengan
   ~110px kosong di atas judul dan ~81px lagi sebelum foto pertama — karena
   padding bawah .pagehead dan padding atas .wrap BERTUMPUK, dan di halaman
   tanpa foto tidak ada apa pun yang mengisi kekosongan itu. Sekarang pitanya
   dirapatkan dan diberi satu penanda garis aksen, motif yang sama dengan
   eyebrow di halaman publik studio — supaya kedua permukaan terbaca sekeluarga
   (lihat docs/design-dna.json, design_style.imagery.graphic_elements). */
.pagehead {
    padding: clamp(30px, 5.5vh, 60px) var(--pad) clamp(12px, 2vh, 22px);
    text-align: center;
}

/* Garis rambut aksen di atas judul. Digambar dengan ::before supaya tidak
   menambah satu pun elemen di markup, dan otomatis ada di tab Set maupun tab
   Favorit tanpa dua tempat yang harus diingat. */
.pagehead::before {
    content: "";
    display: block;
    width: 26px;
    height: 1px;
    margin: 0 auto clamp(14px, 2vh, 20px);
    background: var(--accent);
    opacity: .55;
}

.pagehead__title {
    font-size: clamp(1.7rem, 4.4vw, 2.9rem);
    line-height: 1.1;
    letter-spacing: -.01em;
}

/* Keterangan Set (Folder::description) — sebelumnya tidak punya aturan sama
   sekali, jadi ia tergambar selebar layar penuh dengan warna teks utama dan
   bersaing dengan judulnya sendiri. */
.pagehead__desc {
    max-width: 60ch;
    margin: 12px auto 0;
    line-height: 1.7;
    color: var(--muted);
}

.pagehead__meta {
    margin: 10px 0 0;
    font-size: 12px;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--muted);
}

/* Isinya masuk bergiliran, memakai keyframe `rise` yang sama dengan teks
   sampul. Tanpa ini kepala halaman muncul mentah sementara foto di bawahnya
   tersingkap satu per satu — dua ritme berbeda di layar yang sama. */
.pagehead::before,
.pagehead > * {
    animation: rise .9s var(--ease) both;
}

.pagehead__title {
    animation-delay: .06s;
}

.pagehead__desc {
    animation-delay: .12s;
}

.pagehead__meta {
    animation-delay: .18s;
}

/* Padding bawah .pagehead sudah memberi jarak ke foto pertama; padding atas
   .wrap di bawahnya cuma menumpuk di atasnya. Aturan ini yang menghapus
   tumpukan itu — dan sengaja TIDAK berlaku setelah .hero, karena di sana
   jaraknya memang dibutuhkan untuk memisahkan foto sampul dari grid. */
.pagehead + .wrap {
    padding-top: clamp(10px, 1.6vh, 20px);
}

/* ================================================================= GRID == */
.wrap {
    padding: clamp(26px, 5vh, 56px) var(--pad) clamp(48px, 9vh, 104px);
}

/* Masonry — tinggi alami tiap foto (CSS multi-column).
   Lebar kolom = --col (lihat :root). Pemangkasan untuk ponsel ada di blok
   @media "Kolom di layar kecil", bukan di sini. */
.grid {
    column-width: var(--col);
    column-gap: var(--gap);
}

.cell {
    margin: 0 0 var(--gap);
    break-inside: avoid;
    position: relative;
}

/* Reveal hanya aktif setelah JS menandai grid — tanpa JS foto tetap terlihat */
.grid.is-reveal .cell {
    opacity: 0;
    transform: translateY(14px);
}

.grid.is-reveal .cell.is-in {
    opacity: 1;
    transform: none;
    transition: opacity .6s var(--ease), transform .6s var(--ease);
}

.cell__btn {
    display: block;
    width: 100%;
    padding: 0;
    border: 0;
    border-radius: var(--radius);
    background: var(--line);
    cursor: zoom-in;
    overflow: hidden;
    aspect-ratio: var(--ar, 1.5);
}

/* Video-tautan yang sedang diputar di kartunya sendiri (lihat modul 3b di
   gallery.js). Mengambil alih kotak yang tadinya diisi .cell__btn, jadi
   rasionya harus ikut --ar supaya tinggi kartu tidak meloncat saat berganti. */
.cell__embed {
    display: block;
    width: 100%;
    aspect-ratio: var(--ar, 1.5);
    border: 0;
    border-radius: var(--radius);
    background: #000;
}

.cell__img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform .55s var(--ease), filter .3s var(--ease);
}

.cell__btn:hover .cell__img {
    transform: scale(1.035);
}

.cell__fav {
    position: absolute;
    top: 12px;
    right: 12px;
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background: rgba(0, 0, 0, .38);
    backdrop-filter: blur(6px);
    color: #ff4d6d;
    pointer-events: none;
}

.cell__fav .ico {
    width: 15px;
    height: 15px;
}

/* Grid seragam — potong ke rasio sama, paling rapi & modern */
[data-layout="grid"] .grid {
    column-width: auto;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(var(--col), 1fr));
    gap: var(--gap);
}

[data-layout="grid"] .cell {
    margin: 0;
}

[data-layout="grid"] .cell__btn {
    aspect-ratio: 1;
}

/* Vertikal / slideshow — satu kolom besar, fokus penuh ke foto */
[data-layout="vertical"] .grid {
    column-width: auto;
    display: flex;
    flex-direction: column;
    gap: max(var(--gap), 16px);
    max-width: 1180px;
    margin-inline: auto;
}

[data-layout="vertical"] .cell {
    margin: 0;
}


/* ------------------------------------------------- Varian sampul baru --- */
.cover--right .cover__inner {
    justify-items: end;
    text-align: right;
}

.cover--right .cover__title {
    max-width: 12ch;
}

/* Penuh: foto memenuhi layar, teks bertumpuk di bagian bawah */
.cover--full .cover__inner {
    grid-template-rows: 1fr auto;
    align-items: end;
    padding-bottom: clamp(48px, 10vh, 110px);
}

.cover--full .cover__head {
    align-self: end;
}

.cover--full .cover__title {
    font-size: clamp(2.8rem, 10vw, 7rem);
    max-width: 20ch;
}

/* Minimal: abaikan foto, andalkan tipografi di atas latar bertema */
.cover--minimal .cover__bg {
    display: none;
}

.cover--minimal .cover__inner {
    color: var(--fg);
    background: var(--bg);
}

.cover--minimal .cover__title {
    font-size: clamp(2.2rem, 7vw, 4.6rem);
}

.cover--minimal .gate__box {
    border-color: var(--line-strong);
    background: var(--surface);
    color: var(--fg);
}

.cover--minimal .gate__error {
    color: #d6455d;
}

/* Atas: seluruh teks naik ke puncak layar, foto dibiarkan lega di bawahnya */
.cover--top .cover__inner {
    grid-template-rows: auto 1fr;
}

.cover--top .cover__head,
.cover--top .cover__gate {
    align-self: start;
}

/* Diagonal: judul memegang pojok kiri atas, gerbang PIN pojok kanan bawah —
   bagian tengah foto dibiarkan kosong sepenuhnya. */
.cover--corner .cover__inner {
    grid-template-rows: auto 1fr;
    text-align: left;
}

.cover--corner .cover__head {
    align-self: start;
    justify-self: start;
}

.cover--corner .cover__gate {
    align-self: end;
    justify-self: end;
    text-align: right;
}

.cover--corner .gate__inputs {
    justify-content: flex-end;
}

/* Kartu kaca: satu panel mengambang di tengah foto, bukan teks telanjang.
   .cover__inner berhenti setinggi layar dan berubah jadi kartu, jadi .cover
   sendiri yang memusatkannya. */
.cover--card {
    place-items: center;
    padding: clamp(18px, 4vw, 56px);
}

.cover--card .cover__inner {
    min-height: auto;
    width: min(560px, 100%);
    padding: clamp(28px, 4.4vw, 52px);
    gap: clamp(22px, 4vh, 40px);
    border: 1px solid rgba(255, 255, 255, .18);
    border-radius: var(--radius);
    background: rgba(10, 10, 12, .42);
    backdrop-filter: blur(18px);
}

.cover--card .cover__title {
    font-size: clamp(2rem, 5.4vw, 3.4rem);
    max-width: 14ch;
}

/* Panel samping: kolom polos bertema menempel di kiri, foto memenuhi sisanya.
   Panelnya digambar sebagai gradient dua-stop di .cover__inner supaya tidak
   perlu elemen tambahan di markup. */
.cover--panel .cover__inner {
    grid-template-columns: 46% 1fr;
    justify-items: start;
    padding-inline: 0;
    text-align: left;
    color: var(--fg);
    background: linear-gradient(90deg, var(--bg) 0 46%, transparent 46%);
}

.cover--panel .cover__head,
.cover--panel .cover__gate {
    grid-column: 1;
    width: 100%;
    padding-inline: clamp(24px, 3.4vw, 56px);
}

.cover--panel .cover__gate {
    max-width: 400px;
}

.cover--panel .cover__title {
    font-size: clamp(2rem, 4.4vw, 3.6rem);
    max-width: 13ch;
}

.cover--panel .gate__inputs {
    justify-content: flex-start;
}

@media (max-width: 780px) {

    /* Kolom kiri tidak masuk akal di layar sempit — panelnya pindah ke bawah */
    .cover--panel .cover__inner {
        grid-template-columns: 1fr;
        background: linear-gradient(180deg, transparent 0 46%, var(--bg) 46%);
    }
}

/* Kartu pos: foto memegang bagian atas layar, teks duduk di pita bertema di
   bawahnya — tidak ada teks yang menumpang di atas foto sama sekali. */
.cover--postcard {
    background: var(--bg);
    /* Drift foto tetap dipotong .cover__bg, jadi .cover boleh melar kalau
       teksnya lebih tinggi dari pita bawah. */
    overflow: visible;
}

.cover--postcard .cover__bg {
    inset: 0 0 50% 0;
    overflow: hidden;
}

.cover--postcard .cover__scrim {
    background: linear-gradient(180deg, rgba(0, 0, 0, .3) 0%, rgba(0, 0, 0, .04) 55%, rgba(0, 0, 0, .16) 100%);
}

.cover--postcard .cover__inner {
    min-height: auto;
    grid-template-rows: auto auto;
    align-content: start;
    gap: clamp(14px, 2.6vh, 26px);
    padding-top: calc(50svh + clamp(18px, 3vh, 36px));
    padding-bottom: clamp(24px, 4vh, 48px);
    color: var(--fg);
}

.cover--postcard .cover__head {
    align-self: start;
}

.cover--postcard .cover__title {
    font-size: clamp(1.9rem, 5.6vw, 3.6rem);
}

/* Majalah: judul besar berpalang garis di kiri, gerbang PIN sejajar di kanan */
.cover--magazine .cover__inner {
    grid-template-rows: 1fr auto;
    grid-template-columns: 1fr auto;
    justify-items: start;
    text-align: left;
    gap: clamp(24px, 4vh, 44px) clamp(24px, 5vw, 72px);
}

.cover--magazine .cover__head {
    grid-area: 2 / 1;
    align-self: end;
    padding-top: clamp(16px, 3vh, 30px);
    border-top: 1px solid rgba(255, 255, 255, .38);
}

.cover--magazine .cover__gate {
    grid-area: 2 / 2;
    align-self: end;
    width: auto;
    min-width: 280px;
}

.cover--magazine .cover__title {
    font-size: clamp(2.2rem, 7vw, 5.4rem);
    max-width: 15ch;
}

.cover--magazine .gate__inputs {
    justify-content: flex-start;
}

@media (max-width: 860px) {
    .cover--magazine .cover__inner {
        grid-template-columns: 1fr;
    }

    .cover--magazine .cover__head {
        grid-area: 1 / 1;
        align-self: end;
    }

    .cover--magazine .cover__gate {
        grid-area: 2 / 1;
        min-width: 0;
        width: 100%;
    }
}

/* Berbingkai: foto diberi margin sehingga terasa seperti cetakan */
.cover--framed {
    padding: clamp(12px, 2.4vw, 34px);
    background: var(--bg);
}

.cover--framed .cover__bg {
    inset: clamp(12px, 2.4vw, 34px);
    border-radius: var(--radius);
    overflow: hidden;
}

.cover--framed .cover__inner {
    min-height: calc(100svh - clamp(24px, 4.8vw, 68px));
}

/* ================================================== Varian grid baru ==== */

/* Grid dengan rasio tetap */
[data-layout="grid-portrait"] .grid,
[data-layout="grid-wide"] .grid {
    column-width: auto;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(var(--col), 1fr));
    gap: var(--gap);
}

[data-layout="grid-portrait"] .cell,
[data-layout="grid-wide"] .cell {
    margin: 0;
}

[data-layout="grid-portrait"] .cell__btn {
    aspect-ratio: 3 / 4;
}

[data-layout="grid-wide"] .cell__btn {
    aspect-ratio: 16 / 9;
}

/* Justified: tiap baris tinggi sama, lebar mengikuti rasio foto.
   Lebar dasar = rasio × tinggi baris; flex-grow meratakan sisa ruang.

   Tinggi barisnya diturunkan dari --col, BUKAN angka tetap seperti dulu
   (clamp(160px, 22vh, 260px)): dengan angka tetap, setelan "Ukuran thumbnail"
   sama sekali tidak berpengaruh di layout ini — semua ukuran menghasilkan
   baris yang identik. */
[data-layout="justified"] .grid {
    --row-h: clamp(90px, calc(var(--col) * .62), 620px);
    column-width: auto;
    display: flex;
    flex-wrap: wrap;
    gap: var(--gap);
}

[data-layout="justified"] .cell {
    margin: 0;
    flex: 1 1 calc(var(--ar, 1.5) * var(--row-h));
    max-width: 100%;
}

[data-layout="justified"] .cell__btn {
    height: 100%;
    aspect-ratio: var(--ar, 1.5);
}

/* Editorial: satu foto besar berselang dengan yang kecil */
[data-layout="editorial"] .grid {
    column-width: auto;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(var(--col), 1fr));
    grid-auto-flow: dense;
    gap: var(--gap);
}

[data-layout="editorial"] .cell {
    margin: 0;
}

[data-layout="editorial"] .cell__btn {
    aspect-ratio: 1;
}

/* Tiap foto ke-5 tampil dua kali lipat */
[data-layout="editorial"] .cell:nth-child(5n + 1) {
    grid-column: span 2;
    grid-row: span 2;
}

[data-layout="editorial"] .cell:nth-child(5n + 1) .cell__btn {
    aspect-ratio: 1;
}

@media (max-width: 640px) {
    [data-layout="editorial"] .cell:nth-child(5n + 1) {
        grid-column: span 2;
        grid-row: auto;
    }
}

/* Slideshow: gulir horizontal dengan snap, satu foto per layar */
[data-layout="slideshow"] .grid {
    column-width: auto;
    display: flex;
    flex-direction: row;
    gap: var(--gap);
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scrollbar-width: thin;
    padding-bottom: 12px;
    max-width: none;
    margin: 0;
}

[data-layout="slideshow"] .cell {
    margin: 0;
    flex: 0 0 auto;
    width: min(88vw, 1100px);
    scroll-snap-align: center;
}

[data-layout="slideshow"] .cell__btn {
    aspect-ratio: var(--ar, 1.5);
    max-height: 74vh;
}

[data-layout="slideshow"] .sentinel {
    display: none;
}

/* Set/tab berisi TEPAT SATU item — umum untuk Set kind=video (preset
   "Wedding Film" dkk. dibuat dengan max_files:1), tapi berlaku untuk foto
   tunggal juga. Kolom grid sekecil --thumb-w terasa sia-sia untuk satu
   kartu doang, jadi ditampilkan besar & berdiri sendiri alih-alih ikut
   lebar kolom layout yang sedang aktif. 1180px meniru batas lebar yang
   sudah dipakai [data-layout="vertical"] supaya konsisten dengan pola
   "satu kolom besar" yang sudah ada.
   Spesifisitas dinaikkan ([data-grid] + dua kelas) supaya menang atas
   SEMUA varian [data-layout="..."] .grid di atas, apa pun layout yang
   admin pilih di pengaturan tampilan. */
.grid[data-grid].grid--single {
    display: block;
    column-width: auto;
    max-width: 1180px;
    margin-inline: auto;
}

.grid--single .cell {
    margin: 0;
}

.grid--single .cell__btn,
.grid--single .cell__embed {
    width: 100%;
    max-height: 82vh;
}

/* ============================================ Kolom di layar kecil ====== */
/* `mobile_columns` memisahkan tampilan ponsel dari desktop. Dibiarkan
   `auto`, lebar kolom tetap dihitung dari --thumb-w (dibatasi 42vw, jadi
   praktis selalu 2 kolom). Nilai eksplisit menimpa --col dengan pecahan
   lebar grid, sehingga SEMUA layout berbasis grid ikut menyesuaikan tanpa
   aturan tambahan per-layout. */
@media (max-width: 640px) {

    /* `auto`: ukuran thumbnail desktop dipakai sebagai batas ATAS saja, lalu
       dipangkas 42vw supaya ponsel tetap dapat dua kolom. `- var(--gap)` WAJIB
       ada — browser menghitung jumlah kolom sebagai floor((lebar + gap) /
       (kolom + gap)), jadi kolom setepat 42vw membuat dua kolom + jaraknya
       melebihi layar dan justru jatuh ke SATU kolom raksasa (gejala
       "thumbnail di ponsel kegedean"). */
    .grid {
        --col: min(var(--thumb-w, 300px), calc(42vw - var(--gap)));
    }

    .g-body[data-mcols="1"] .grid {
        --col: 100%;
    }

    .g-body[data-mcols="2"] .grid {
        --col: calc((100% - var(--gap)) / 2);
    }

    .g-body[data-mcols="3"] .grid {
        --col: calc((100% - 2 * var(--gap)) / 3);
    }

    .g-body[data-mcols="4"] .grid {
        --col: calc((100% - 3 * var(--gap)) / 4);
    }

    /* Masonry memakai multi-column, dan `column-width` menolak satuan persen —
       jadi khusus layout itu jumlah kolom disetel langsung. Layout lain
       (flex/grid) mengabaikan `column-count`, jadi aman ditulis umum.
       `.grid--single` dikecualikan: kartu tunggalnya memang sengaja lebar
       penuh, membaginya jadi beberapa kolom justru mengecilkannya. */
    .g-body[data-mcols="1"] .grid:not(.grid--single) {
        column-count: 1;
        column-width: auto;
    }

    .g-body[data-mcols="2"] .grid:not(.grid--single) {
        column-count: 2;
        column-width: auto;
    }

    .g-body[data-mcols="3"] .grid:not(.grid--single) {
        column-count: 3;
        column-width: auto;
    }

    .g-body[data-mcols="4"] .grid:not(.grid--single) {
        column-count: 4;
        column-width: auto;
    }

    /* Justified menentukan lebar lewat flex-basis, bukan kolom */
    .g-body[data-layout="justified"][data-mcols="1"] .cell,
    .g-body[data-layout="justified"][data-mcols="2"] .cell,
    .g-body[data-layout="justified"][data-mcols="3"] .cell,
    .g-body[data-layout="justified"][data-mcols="4"] .cell {
        flex: 0 0 var(--col);
    }

    /* Editorial: foto "besar" tidak boleh minta 2 kolom kalau totalnya 1 */
    .g-body[data-layout="editorial"][data-mcols="1"] .cell:nth-child(5n + 1) {
        grid-column: auto;
    }
}

/* ------------------------------------------------------------- Video ---- */
.cell--video .cell__btn {
    cursor: pointer;
    background: #17171a;
}

/* Video tanpa poster: blok gelap bernama berkas */
.cell__blank {
    display: grid;
    place-items: center;
    width: 100%;
    height: 100%;
    padding: 16px;
    color: rgba(255, 255, 255, .5);
    font-size: 12px;
    letter-spacing: .08em;
    text-align: center;
    word-break: break-word;
}

.cell__play {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    pointer-events: none;
}

.cell__play .ico {
    width: 58px;
    height: 58px;
    padding: 16px;
    border-radius: 50%;
    background: rgba(0, 0, 0, .45);
    color: #fff;
    backdrop-filter: blur(6px);
    transition: transform .25s var(--ease), background-color .25s var(--ease);
}

.cell__btn:hover .cell__play .ico {
    transform: scale(1.08);
    background: rgba(0, 0, 0, .65);
}

.sets__ico {
    width: 13px;
    height: 13px;
    fill: currentColor;
    stroke: none;
}

.pagehead__desc {
    max-width: 62ch;
    margin: 12px auto 0;
    color: var(--muted);
    font-size: 14.5px;
}

/* Pemutar video di lightbox */
.lb__video {
    max-width: 100%;
    max-height: calc(100vh - var(--lb-chrome));
    max-height: calc(100dvh - var(--lb-chrome));
    width: auto;
    border-radius: 4px;
    background: #000;
    outline: none;
}

.lb__video[hidden],
.lb__img[hidden] {
    display: none;
}

/* Video via tautan (YouTube/Vimeo) — di-embed lewat iframe, bukan <video>
   `width: 100%` = lebar isi .lb__stage, jadi pemutar otomatis pas di antara
   tombol navigasi dan ikut ter-center oleh `place-items: center` milik stage;
   tidak perlu menebak lewat `vw` yang tidak tahu-menahu soal lebar navigasi.
   Tingginya diturunkan dari aspect-ratio, dan max-width yang menjaga agar
   tinggi hasilnya tidak melewati jatah vertikal.

   JANGAN diganti jadi `max-height: 100%` supaya "lebih besar": sudah
   diuji-ukur di Chrome — dengan lebar yang definit, browser TIDAK memperkecil
   lebarnya kembali saat max-height menggigit, jadi pemutar meluber 40px dan
   menabrak caption di 1920x1080 maupun 1366x768. */
.lb__embed {
    width: 100%;
    max-width: calc((100vh - var(--lb-chrome)) * 16 / 9);
    max-width: calc((100dvh - var(--lb-chrome)) * 16 / 9);
    aspect-ratio: 16 / 9;
    border: 0;
    border-radius: 4px;
    background: #000;
}

.lb__embed[hidden] {
    display: none;
}

@media (max-width: 720px) {
    /* Tombol navigasi disembunyikan di layar kecil, jadi stage memakai lebar
       penuh dan `width: 100%` pada .lb__embed sudah menangani sisi lebar —
       cukup jatah vertikalnya yang disesuaikan di sini. */
    .lb {
        --lb-chrome: 145px;
    }

    .cell__play .ico {
        width: 46px;
        height: 46px;
        padding: 12px;
    }
}

/* ------------------------------------------------- Sentinel & spinner --- */
.sentinel {
    display: grid;
    place-items: center;
    min-height: 60px;
    margin-top: 28px;
}

.spinner {
    width: 26px;
    height: 26px;
    border: 2px solid var(--line-strong);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: spin .7s linear infinite;
}

.spinner--lb {
    position: absolute;
    inset: 0;
    margin: auto;
    border-color: rgba(255, 255, 255, .25);
    border-top-color: #fff;
}

@keyframes spin {
    to {
        transform: rotate(360deg);
    }
}

/* ---------------------------------------------------- Kosong / footer --- */
.empty {
    padding: clamp(60px, 16vh, 150px) 0;
    text-align: center;
}

.empty__title {
    font-family: var(--font-heading);
    font-size: 1.55rem;
    margin: 0 0 8px;
}

.empty__text {
    margin: 0;
    color: var(--muted);
    font-size: 14px;
}

.foot {
    padding: clamp(28px, 5vh, 52px) var(--pad);
    border-top: 1px solid var(--line);
    text-align: center;
    font-size: 11px;
    letter-spacing: .24em;
    text-transform: uppercase;
    color: var(--muted);
}

.foot p {
    margin: 0;
}

/* Logo studio di footer — hanya digambar bila studio mengunggahnya sendiri
   (lihat gallery/partials/footer.blade.php). Tingginya dikunci, lebarnya
   mengikuti: logo studio bentuknya bisa apa saja, dari lambang persegi sampai
   wordmark yang sangat lebar. */
.foot__logo {
    display: block;
    height: clamp(28px, 5vh, 44px);
    width: auto;
    max-width: min(240px, 70vw);
    margin: 0 auto 14px;
    object-fit: contain;
}

.foot__name {
    font-size: 12px;
    color: var(--fg);
}

/* Ditulis `.foot .foot__links` (bukan `!important`) supaya menang atas
   `.foot p { margin: 0 }` yang spesifisitasnya lebih tinggi dari kelas polos. */
.foot .foot__links {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 6px 18px;
    margin-top: 14px;
}

.foot__links a {
    color: inherit;
    text-decoration: none;
    border-bottom: 1px solid transparent;
    transition: color .18s ease, border-color .18s ease;
}

.foot__links a:hover {
    color: var(--fg);
    border-bottom-color: var(--line-strong);
}

/* Baris hak cipta: sengaja TIDAK uppercase & tidak ber-tracking lebar seperti
   sisa footer — ini kalimat yang memang untuk dibaca, bukan label. */
.foot .foot__rights {
    max-width: 62ch;
    margin: 18px auto 0;
    font-size: 11.5px;
    line-height: 1.6;
    letter-spacing: normal;
    text-transform: none;
}

.foot .foot__rights--muted {
    margin-top: 6px;
    opacity: .72;
}

/* ============================================================ LIGHTBOX == */
.lb {
    /* Jatah tinggi untuk chrome lightbox (bar atas + caption bawah) yang tidak
       boleh dipakai media. Terukur: bar 74px + caption 63px = 137px, sisanya
       napas. Sebelumnya dikira-kira 190px di tiap aturan — selisih ~50px itu
       membuat pemutar video mengecil sendiri tanpa alasan. Satu tempat saja
       supaya tidak lagi tersebar sebagai angka ajaib. */
    --lb-chrome: 150px;

    position: fixed;
    inset: 0;
    z-index: 90;
    display: grid;
    grid-template-columns: auto 1fr auto;
    grid-template-rows: auto 1fr auto;
    align-items: center;
    opacity: 0;
    transition: opacity .28s var(--ease);
}

.lb.is-open {
    opacity: 1;
}

.lb[hidden] {
    display: none;
}

.lb__backdrop {
    position: absolute;
    inset: 0;
    background: rgba(6, 6, 8, .95);
    backdrop-filter: blur(14px);
}

.lb__bar,
.lb__stage,
.lb__nav,
.lb__caption {
    position: relative;
}

.lb__bar {
    grid-column: 1 / -1;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 16px clamp(14px, 3vw, 26px);
    color: #fff;
}

.lb__count {
    font-size: 12px;
    letter-spacing: .16em;
    color: rgba(255, 255, 255, .68);
    font-variant-numeric: tabular-nums;
}

.lb__tools {
    display: flex;
    gap: 8px;
}

.lb__stage {
    grid-column: 2;
    display: grid;
    place-items: center;
    height: 100%;
    min-height: 0;
    padding: 0 clamp(6px, 1.5vw, 18px);
    margin: 0;
}

.lb__img {
    max-width: 100%;
    max-height: calc(100vh - 190px);
    max-height: calc(100dvh - 190px);
    width: auto;
    height: auto;
    object-fit: contain;
    border-radius: 4px;
    opacity: 0;
    transform: scale(.985);
    transition: opacity .32s var(--ease), transform .32s var(--ease);
}

.lb__img.is-ready {
    opacity: 1;
    transform: none;
}

.lb__nav {
    display: grid;
    place-items: center;
    width: clamp(46px, 6vw, 68px);
    height: 100%;
    border: 0;
    background: none;
    color: rgba(255, 255, 255, .55);
    cursor: pointer;
    transition: color .18s var(--ease);
}

.lb__nav:hover {
    color: #fff;
}

.lb__nav:disabled {
    opacity: .18;
    cursor: default;
}

.lb__nav .ico {
    width: 30px;
    height: 30px;
    stroke-width: 1.4;
}

.lb__nav--prev {
    grid-column: 1;
}

.lb__nav--next {
    grid-column: 3;
}

.lb__caption {
    grid-column: 1 / -1;
    padding: 16px clamp(14px, 3vw, 26px) clamp(20px, 4vh, 32px);
    text-align: center;
    font-size: 12.5px;
    color: rgba(255, 255, 255, .58);
}

@media (max-width: 720px) {
    .lb {
        grid-template-columns: 1fr;
        grid-template-rows: auto 1fr auto;
    }

    .lb__stage {
        grid-column: 1;
    }

    /* Di mobile navigasi lewat geser, panah disembunyikan */
    .lb__nav {
        display: none;
    }

    .lb__img {
        max-height: calc(100vh - 165px);
        max-height: calc(100dvh - 165px);
    }
}

/* ================================================================ NOTICE = */
.notice {
    min-height: 100vh;
    min-height: 100svh;
    display: grid;
    place-items: center;
    padding: var(--pad);
    text-align: center;
}

.notice__inner {
    max-width: 44ch;
}

.notice__title {
    font-size: clamp(1.6rem, 4.6vw, 2.6rem);
    margin-bottom: 14px;
}

.notice__text {
    color: var(--muted);
    margin: 0 0 26px;
}

/* ================================================== Aksesibilitas gerak = */
@media (prefers-reduced-motion: reduce) {

    *,
    *::before,
    *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
        scroll-behavior: auto !important;
    }

    .grid.is-reveal .cell {
        opacity: 1;
        transform: none;
    }
}

/* --------------------------------------------------- Panel progres unduhan
   Menggantikan toast singkat yang dulu dipakai. Unduhan di sini punya durasi
   nyata (ZIP dibangun di queue, berkas asli di-stream dari Google Drive),
   jadi statusnya perlu tempat tetap: tahapan, jumlah berkas, persentase, dan
   tombol unduh manual bila browser tidak memulainya sendiri. Selalu gelap
   seperti chrome lightbox supaya terbaca di semua tema dan di atas foto. */
.dlp {
    position: fixed;
    right: 20px;
    bottom: 20px;
    z-index: 96;
    width: 340px;
    max-width: calc(100vw - 32px);
    padding: 16px 18px 18px;
    border-radius: 16px;
    background: rgba(20, 20, 22, .96);
    color: #fff;
    box-shadow: 0 10px 40px rgba(0, 0, 0, .35);
    opacity: 0;
    transform: translateY(14px);
    pointer-events: none;
    transition: opacity .22s var(--ease), transform .22s var(--ease);
}

.dlp.is-visible {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
}

.dlp__x {
    position: absolute;
    top: 10px;
    right: 10px;
    width: 28px;
    height: 28px;
    display: grid;
    place-items: center;
    padding: 0;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: rgba(255, 255, 255, .55);
    cursor: pointer;
}

.dlp__x:hover {
    background: rgba(255, 255, 255, .1);
    color: #fff;
}

.dlp__x .ico {
    width: 15px;
    height: 15px;
}

.dlp__head {
    display: flex;
    align-items: center;
    gap: 9px;
    padding-right: 28px;
}

.dlp__title {
    font-size: 14px;
    font-weight: 600;
    letter-spacing: .01em;
}

/* Cincin berputar saat masih bekerja; berubah jadi titik statis saat selesai
   atau gagal — indikator gerak yang tidak pernah berhenti terbaca seperti
   proses yang menggantung. */
.dlp__spin {
    flex: none;
    width: 15px;
    height: 15px;
    border-radius: 50%;
    border: 2px solid rgba(255, 255, 255, .22);
    border-top-color: #fff;
    animation: dlp-spin .8s linear infinite;
}

.dlp.is-ready .dlp__spin,
.dlp.is-done .dlp__spin,
.dlp.is-error .dlp__spin {
    animation: none;
    border-color: transparent;
    background: var(--accent, #6c8cff);
}

.dlp.is-done .dlp__spin {
    background: #3ecf8e;
}

.dlp.is-error .dlp__spin {
    background: #ff6b6b;
}

@keyframes dlp-spin {
    to { transform: rotate(360deg); }
}

/* -- Tahapan -- */
.dlp__steps {
    display: flex;
    gap: 14px;
    margin: 12px 0 10px;
    padding: 0;
    list-style: none;
    font-size: 11.5px;
    letter-spacing: .02em;
    color: rgba(255, 255, 255, .45);
}

.dlp__step {
    display: flex;
    align-items: center;
    gap: 6px;
}

.dlp__dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: rgba(255, 255, 255, .25);
    transition: background .2s ease, box-shadow .2s ease;
}

.dlp__step.is-done {
    color: rgba(255, 255, 255, .7);
}

.dlp__step.is-done .dlp__dot {
    background: rgba(255, 255, 255, .7);
}

.dlp__step.is-now {
    color: #fff;
    font-weight: 600;
}

.dlp__step.is-now .dlp__dot {
    background: var(--accent, #6c8cff);
    box-shadow: 0 0 0 3px rgba(255, 255, 255, .14);
}

/* -- Bar -- */
.dlp__bar {
    height: 5px;
    border-radius: 999px;
    background: rgba(255, 255, 255, .14);
    overflow: hidden;
}

.dlp__fill {
    display: block;
    height: 100%;
    width: 0;
    border-radius: inherit;
    background: var(--accent, #6c8cff);
    transition: width .3s var(--ease);
}

.dlp.is-done .dlp__fill {
    background: #3ecf8e;
}

.dlp.is-error .dlp__fill {
    background: #ff6b6b;
}

/* Belum terukur (masih mengantre / menunggu server): pita berjalan, bukan bar
   diam di 0% yang terlihat seperti macet. */
.dlp.is-indeterminate .dlp__fill {
    width: 35% !important;
    /* `linear`, BUKAN var(--ease) — dan ini pengecualian yang disengaja.
       --ease adalah kurva MASUK: ia melambat panjang di ujungnya, bagus untuk
       sesuatu yang datang lalu berhenti. Di sini keyframe-nya cuma dua titik
       (-110% → 320%) dan berulang tanpa henti, jadi kurva itu membuat batangnya
       menyeberang cepat lalu merayap lama di luar layar — pengguna melihat
       batang yang berkedip sebentar lalu diam, persis seperti unduhan yang
       macet. Bar tak-terukur harus berkecepatan tetap. */
    animation: dlp-slide 1.3s linear infinite;
}

@keyframes dlp-slide {
    0%   { transform: translateX(-110%); }
    100% { transform: translateX(320%); }
}

.dlp__meta {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    margin-top: 9px;
    font-size: 12.5px;
    color: rgba(255, 255, 255, .78);
}

.dlp__pct {
    flex: none;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: #fff;
}

.dlp__note {
    margin: 7px 0 0;
    font-size: 11.5px;
    line-height: 1.45;
    color: rgba(255, 255, 255, .5);
}

.dlp__btn {
    display: block;
    margin-top: 12px;
    padding: 9px 14px;
    border-radius: 10px;
    background: #fff;
    color: #17171a;
    font-size: 13px;
    font-weight: 600;
    text-align: center;
    text-decoration: none;
}

/* `display: block` di atas mengalahkan default browser untuk atribut
   `hidden` (author stylesheet selalu menang atas UA stylesheet walau
   spesifisitasnya sama) — tanpa baris ini tombol selalu tampil aktif
   walau JS sudah men-set `els.btn.hidden = true` (lihat gallery.js). */
.dlp__btn[hidden] {
    display: none;
}

.dlp__btn:hover {
    background: rgba(255, 255, 255, .88);
}

/* Terkunci selama satu unduhan sedang berjalan. `pointer-events: none` cuma
   pelengkap — penahan sebenarnya ada di handler klik (lihat gallery.js),
   karena CSS tidak menahan aktivasi lewat keyboard. */
.dlp__btn.is-busy,
.btn.is-busy,
.iconbtn.is-busy {
    pointer-events: none;
    opacity: .5;
    cursor: default;
}

/* Iframe pemicu unduhan — harus tetap di dalam dokumen (bukan display:none
   di sebagian browser lama) tapi tidak boleh terlihat atau bisa difokus. */
.dl-frame {
    position: absolute;
    width: 0;
    height: 0;
    border: 0;
    opacity: 0;
    pointer-events: none;
}

@media (max-width: 560px) {
    .dlp {
        right: 12px;
        left: 12px;
        bottom: 12px;
        width: auto;
    }
}

@media (prefers-reduced-motion: reduce) {
    .dlp__spin,
    .dlp.is-indeterminate .dlp__fill {
        animation: none;
    }
}

/* ==========================================================================
   Bagikan — tombol di kartu, panel, dan halaman tautan satu berkas
   ========================================================================== */

/* -- Tombol kecil di kartu grid --
   Di perangkat sentuh tidak ada hover, jadi tombolnya selalu tampak (dengan
   opasitas lebih rendah) — kalau tidak, fitur ini tidak akan pernah ketemu
   di ponsel, yang justru paling banyak dipakai membagikan foto. */
.cell__share {
    position: absolute;
    top: 12px;
    left: 12px;
    display: grid;
    place-items: center;
    width: 32px;
    height: 32px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: rgba(0, 0, 0, .42);
    backdrop-filter: blur(6px);
    color: #fff;
    cursor: pointer;
    opacity: .85;
    transition: opacity .18s ease, background .18s ease, transform .18s ease;
}

.cell__share .ico {
    width: 15px;
    height: 15px;
}

.cell__share:hover {
    background: rgba(0, 0, 0, .62);
    transform: scale(1.06);
}

@media (hover: hover) {
    .cell__share {
        opacity: 0;
    }

    .cell:hover .cell__share,
    .cell__share:focus-visible {
        opacity: 1;
    }
}

/* Kartu yang sedang memutar video-tautan tidak boleh tertutup tombol ini */
.cell.is-playing .cell__share {
    display: none;
}

/* -- Panel bagikan --
   z-index di atas lightbox (90) dan panel unduhan (96): panel ini bisa
   dibuka dari dalam lightbox dan harus menutupinya. */
.sheet {
    position: fixed;
    inset: 0;
    z-index: 120;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
}

.sheet[hidden] {
    display: none;
}

.g-body.has-sheet {
    overflow: hidden;
}

.sheet__backdrop {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, .6);
    backdrop-filter: blur(3px);
}

.sheet__panel {
    position: relative;
    width: min(420px, 100%);
    max-height: calc(100dvh - 40px);
    overflow-y: auto;
    padding: 20px;
    border-radius: 18px;
    background: var(--surface);
    color: var(--fg);
    box-shadow: 0 24px 60px rgba(0, 0, 0, .35);
    animation: sheet-in .22s ease;
}

@keyframes sheet-in {
    from {
        opacity: 0;
        transform: translateY(14px);
    }
}

.sheet__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 4px;
}

.sheet__title {
    margin: 0;
    font-family: var(--font-heading);
    font-size: 1.25rem;
    font-weight: 500;
}

.sheet__head .iconbtn {
    color: var(--muted);
}

.sheet__sub {
    margin: 0 0 14px;
    color: var(--muted);
    font-size: .82rem;
    line-height: 1.5;
}

.sheet__link {
    display: flex;
    gap: 8px;
    margin: 10px 0 12px;
}

.sheet__url {
    flex: 1;
    min-width: 0;
    padding: 10px 12px;
    border: 1px solid var(--line);
    border-radius: 10px;
    background: var(--bg);
    color: var(--fg);
    font-family: var(--font-body);
    font-size: .82rem;
}

.sheet__copy {
    flex: none;
}

.sheet__pin {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    margin-bottom: 14px;
    color: var(--muted);
    font-size: .8rem;
    line-height: 1.45;
    cursor: pointer;
}

.sheet__pin input {
    margin-top: 2px;
    accent-color: var(--accent);
}

.sheet__pin-note {
    font-style: normal;
    opacity: .8;
}

.sheet__ways {
    display: grid;
    /* 62px: lima saluran masih muat satu baris di layar 390px — pada 74px
       "Aplikasi lain" terlempar sendirian ke baris kedua. */
    grid-template-columns: repeat(auto-fit, minmax(62px, 1fr));
    gap: 8px;
}

.way {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 7px;
    padding: 12px 4px;
    border: 1px solid var(--line);
    border-radius: 12px;
    background: transparent;
    color: var(--fg);
    font-family: var(--font-body);
    font-size: .7rem;
    text-align: center;
    text-decoration: none;
    cursor: pointer;
    transition: border-color .18s ease, background .18s ease;
}

.way:hover {
    border-color: var(--line-strong);
    background: var(--bg);
}

.way.is-disabled {
    opacity: .45;
    pointer-events: none;
}

.way__ico {
    display: grid;
    place-items: center;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: var(--line);
    color: var(--fg);
}

.way__ico .ico {
    width: 18px;
    height: 18px;
}

.way__ico--wa {
    background: #25d366;
    color: #fff;
}

.way__ico--tg {
    background: #229ed9;
    color: #fff;
}

.way__ico--fb {
    background: #1877f2;
    color: #fff;
}

.sheet__status {
    min-height: 1.1em;
    margin: 12px 0 0;
    color: var(--muted);
    font-size: .76rem;
}

/* Di ponsel panel menempel ke bawah layar — pola bottom sheet yang sudah
   dikenal, dan jempol tidak perlu menjangkau tengah layar. */
@media (max-width: 560px) {
    .sheet {
        align-items: flex-end;
        padding: 0;
    }

    .sheet__panel {
        width: 100%;
        max-height: 88dvh;
        border-radius: 20px 20px 0 0;
        padding-bottom: calc(20px + env(safe-area-inset-bottom));
    }

    @keyframes sheet-in {
        from {
            opacity: 0;
            transform: translateY(60px);
        }
    }
}

/* -- Tombol bagikan di halaman sampul -- */
.cover__share {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    margin-top: 14px;
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font-family: var(--font-body);
    font-size: .78rem;
    letter-spacing: .04em;
    text-transform: uppercase;
    opacity: .72;
    cursor: pointer;
    transition: opacity .18s ease;
}

.cover__share:hover {
    opacity: 1;
}

.cover__share .ico {
    width: 15px;
    height: 15px;
}

/* -- Halaman /s/{token} — satu berkas, tanpa PIN -- */
.share {
    display: flex;
    min-height: 100dvh;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 20px;
    padding: 28px 20px 40px;
}

.share__media {
    width: min(1000px, 100%);
    display: flex;
    justify-content: center;
}

.share__img,
.share__video {
    max-width: 100%;
    max-height: 66dvh;
    border-radius: var(--radius);
    object-fit: contain;
}

.share__embed {
    width: min(900px, 100%);
    aspect-ratio: 16 / 9;
    border: 0;
    border-radius: var(--radius);
}

.share__info {
    max-width: 460px;
    text-align: center;
}

.share__studio {
    margin: 0 0 6px;
    color: var(--muted);
    font-size: .72rem;
    letter-spacing: .16em;
    text-transform: uppercase;
}

.share__title {
    margin: 0;
    font-family: var(--font-heading);
    font-size: clamp(1.6rem, 4vw, 2.3rem);
    font-weight: 400;
}

.share__date {
    margin: 6px 0 0;
    color: var(--muted);
    font-size: .84rem;
}

.share__actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 10px;
    margin-top: 20px;
}

.share__note {
    margin: 16px 0 0;
    color: var(--muted);
    font-size: .8rem;
    line-height: 1.5;
}

@media (prefers-reduced-motion: reduce) {
    .sheet__panel {
        animation: none;
    }
}

/* ==========================================================================
   Pilihan album — bintang di kartu, penghitung di bar, daftar kiriman
   ========================================================================== */

/* -- Tombol pilih di kartu --
   Selalu terlihat (tidak seperti .cell__share yang hanya muncul saat hover):
   client harus bisa memindai sekilas mana yang sudah masuk album tanpa
   menyapukan kursor ke tiap kartu. */
.cell__pick {
    position: absolute;
    top: 12px;
    right: 12px;
    display: grid;
    place-items: center;
    width: 32px;
    height: 32px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: rgba(0, 0, 0, .42);
    backdrop-filter: blur(6px);
    color: rgba(255, 255, 255, .85);
    cursor: pointer;
    transition: background .18s ease, color .18s ease, transform .18s ease;
}

/* Bintang KOSONG saat belum dipilih, PENUH saat sudah — aksen gelap default
   (#1a1a1a) nyaris sewarna latar tombol yang belum aktif, jadi membedakannya
   lewat warna latar saja tidak cukup. Lebih spesifik dari .ico--fill di markup,
   sehingga pengisiannya benar-benar dikendalikan status tombol. */
.cell__pick .ico {
    width: 17px;
    height: 17px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.6;
    stroke-linejoin: round;
}

.cell__pick.is-on .ico {
    fill: currentColor;
    stroke: currentColor;
}

.cell__pick:hover {
    background: rgba(0, 0, 0, .62);
    transform: scale(1.08);
}

.cell__pick.is-on {
    background: var(--accent);
    color: var(--accent-fg);
    /* Cincin putih tipis: pemisah terakhir kalau aksennya kebetulan
       sewarna dengan fotonya. */
    box-shadow: 0 0 0 2px rgba(255, 255, 255, .92);
}

.cell.is-playing .cell__pick {
    display: none;
}

/* Bintang di lightbox: emas saat terpilih, bukan merah hati — .iconbtn.is-on
   mewarnai #ff4d6d yang benar untuk favorit tapi salah untuk pilihan album. */
.iconbtn--pick.is-on .ico {
    fill: #f5b301;
    stroke: #f5b301;
}

/* -- Penghitung di bar -- */
.pickbar {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    border: 1px solid var(--line);
    border-radius: 999px;
    color: var(--fg);
    font-size: 13px;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    transition: border-color .18s ease, background .18s ease;
}

.pickbar .ico {
    width: 14px;
    height: 14px;
    color: var(--accent);
}

.pickbar__limit {
    color: var(--muted);
}

.pickbar.is-full {
    border-color: var(--accent);
}

/* Label "Penuh" hanya muncul saat kuota habis — status permanen yang bisa
   dibaca kapan saja, pelengkap snack yang cuma lewat sebentar. */
.pickbar__full {
    display: none;
    padding: 1px 7px;
    border-radius: 999px;
    background: var(--accent);
    color: var(--accent-fg);
    font-size: 11px;
    letter-spacing: .02em;
}

.pickbar.is-full .pickbar__full {
    display: inline-block;
}

/* Kartu yang belum dipilih diredupkan saat kuota habis: penuhnya terlihat
   sebelum client menekan foto berikutnya. Yang SUDAH dipilih tetap terang
   supaya masih jelas mana yang bisa dilepas. */
.grid.is-pick-full .cell__pick:not(.is-on) {
    opacity: .35;
}

/* Batas terlampaui — dipulihkan sendiri setelah beberapa detik oleh JS. */
.pickbar.is-warn {
    border-color: #d9534f;
    background: rgba(217, 83, 79, .12);
}

/* -- Isian form di dalam .sheet -- */
.field {
    display: block;
    margin-bottom: 14px;
}

.field__label {
    display: block;
    margin-bottom: 6px;
    color: var(--muted);
    font-size: .74rem;
    letter-spacing: .06em;
    text-transform: uppercase;
}

.field__label em {
    font-style: normal;
    text-transform: none;
    letter-spacing: 0;
}

.field__input {
    width: 100%;
    padding: 10px 12px;
    border: 1px solid var(--line);
    border-radius: 10px;
    background: var(--bg);
    color: var(--fg);
    font-family: var(--font-body);
    font-size: .88rem;
}

.field__input:focus {
    border-color: var(--accent);
    outline: none;
}

.field__input--area {
    resize: vertical;
    min-height: 72px;
}

.sheet__foot {
    display: flex;
    justify-content: flex-end;
}

.sheet__sub--tight {
    margin: 14px 0 0;
}

/* -- Halaman /pilihan/{token} --
   Daftar bernomor, bukan grid rapat: admin membacanya sambil menyusun album,
   jadi nomor urut dan nama berkas justru yang paling dibutuhkan. */
.picknote {
    max-width: 640px;
    margin: 16px auto 0;
    padding: 14px 16px;
    border-left: 3px solid var(--accent);
    border-radius: 0 10px 10px 0;
    background: var(--surface);
    text-align: left;
}

.picknote__label {
    margin: 0 0 4px;
    color: var(--muted);
    font-size: .7rem;
    letter-spacing: .12em;
    text-transform: uppercase;
}

.picknote__text {
    margin: 0;
    white-space: pre-line;
}

.picklist {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 16px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.picklist__item {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.picklist__no {
    position: absolute;
    top: 8px;
    left: 8px;
    z-index: 1;
    display: grid;
    place-items: center;
    min-width: 24px;
    height: 24px;
    padding: 0 7px;
    border-radius: 999px;
    background: var(--accent);
    color: var(--accent-fg);
    font-size: 12px;
    font-variant-numeric: tabular-nums;
}

.picklist__img {
    display: block;
    width: 100%;
    aspect-ratio: 1;
    border-radius: var(--radius);
    object-fit: cover;
    background: var(--line);
}

.picklist__name {
    color: var(--muted);
    font-size: .72rem;
    overflow-wrap: anywhere;
}

/* ==========================================================================
   Snack — pesan singkat di bawah layar

   Menggantikan atribut `title` yang dipakai versi pertama pemberitahuan batas
   pilihan: tooltip harus ditunggu dengan kursor diam dan TIDAK PERNAH muncul
   di layar sentuh, padahal justru di ponsel client menandai fotonya. Posisi
   fixed di bawah supaya tidak tertutup bar galeri yang sticky di atas.
   ========================================================================== */
.snack {
    position: fixed;
    left: 50%;
    bottom: 24px;
    z-index: 130;
    max-width: min(440px, calc(100vw - 32px));
    padding: 12px 18px;
    border-radius: 12px;
    background: var(--fg);
    color: var(--bg);
    font-family: var(--font-body);
    font-size: .82rem;
    line-height: 1.45;
    text-align: center;
    box-shadow: 0 10px 30px rgba(0, 0, 0, .28);
    /* Tidak menghalangi klik: pesan ini murni informasi, dan client sering
       ingin langsung menekan foto lain tepat setelah membacanya. */
    pointer-events: none;
    opacity: 0;
    transform: translate(-50%, 12px);
    transition: opacity .2s var(--ease), transform .2s var(--ease);
}

.snack.is-in {
    opacity: 1;
    transform: translate(-50%, 0);
}

@media (max-width: 560px) {
    .snack {
        bottom: calc(16px + env(safe-area-inset-bottom));
    }
}

@media (prefers-reduced-motion: reduce) {
    .snack {
        transition: opacity .2s linear;
        transform: translate(-50%, 0);
    }
}
