/* ANDRE JAYA ELECTRONIC Online Shop - Mobile-first, Lazada-style, Navy/White theme */

:root {
    --navy: #0d1b4c;
    --navy-dark: #081235;
    --blue-accent: #2f6bff;
    --blue-light: #eaf1ff;
    --bg: #ffffff;
    --bg-soft: #f4f6fb;
    --text: #1c1f2b;
    --text-muted: #6b7280;
    --border: #e3e7f1;
    --danger: #e74c3c;
    --success: #1aa260;
    --warning: #f5a623;
    --radius: 12px;
    --max-width: 480px;
    --topbar-height: 58px;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
/* .cart-summary (position: sticky; bottom: 0) stays pinned to the viewport's
   bottom ~62px for almost the entire scroll range of any page that has it
   (cart, checkout) — not just near the very end. A naive "scroll this element
   into view" (native scrollIntoView(), and what Playwright's actionability
   engine uses) only checks geometric intersection with the viewport, not
   whether an opaque sticky element is painted on top — so whatever happens to
   sit in that reserved band is reported as "already visible" and never gets
   scrolled clear, even though it's genuinely covered and unclickable.
   scroll-padding-bottom fixes this at the source: the browser's own
   scroll-into-view logic now always leaves room for the bar. */
html { scroll-padding-bottom: 70px; }
body {
    font-family: -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
    background: var(--bg-soft);
    color: var(--text);
    -webkit-font-smoothing: antialiased;
}
a { text-decoration: none; color: inherit; }
img { max-width: 100%; display: block; }
button { font-family: inherit; cursor: pointer; }
ul { list-style: none; margin: 0; padding: 0; }
/* Browser default margin-top pada heading (mis. h4 ~1em) tidak pernah
   disentuh sebelumnya — tempat yang sudah set margin lewat style/class
   inline tetap menang (override reset ini apa adanya), tapi di mana pun itu
   belum ditulis eksplisit, jaraknya jadi berlebihan & tidak proporsional
   dibanding card lain yang judulnya bukan tag heading (mis. <span>). */
h1, h2, h3, h4, h5, h6 { margin: 0; }

.app-shell {
    max-width: var(--max-width);
    margin: 0 auto;
    background: var(--bg);
    min-height: 100vh;
    position: relative;
    padding-bottom: 72px;
    box-shadow: 0 0 24px rgba(13, 27, 76, 0.08);
}

/* ===== Top Sticky Bar (gradien navy->biru + aksen blob abstrak, sama bahasa
   visual dengan .owner-hero di dashboard owner) ===== */
/* Putih -> abu (bukan navy->biru), permintaan user 2026-09-11 — lanjutan
   dari .admin-sidebar: logo mark (sjb-mark-128.png) punya lingkaran biru
   sendiri di latarnya, nyaris menyatu dengan header navy/biru lama. Karena
   header ini SELALU tampil (semua halaman customer) dan sebelumnya seluruh
   isinya (blob dekorasi, teks logo, kolom pencarian) dikalibrasi untuk latar
   gelap, elemen-elemen itu ikut disesuaikan di bawah — bukan cuma ganti
   warna background sendirian. */
.topbar {
    position: sticky;
    top: 0;
    z-index: 50;
    background: linear-gradient(180deg, #ffffff 0%, var(--border) 100%);
    color: var(--text);
    /* Kanan 8px & atas 8px (bukan 16px/14px seperti awalnya) — sisanya
       dipindah jadi padding-right/padding-top .topbar-form, lihat komentar
       di sana kenapa badge notifikasi butuh ruang napas di situ, bukan
       di sini. */
    padding: 8px 8px 12px 16px;
    border-bottom: 1px solid var(--border);
}
/* Blob abstrak DAN overflow:hidden-nya sengaja ditaruh di .topbar-form
   (bukan .topbar sendiri) — .notif-dropdown adalah SIBLING .topbar-form
   (lihat markup di layouts/app.blade.php: keduanya anak langsung .topbar),
   dan dropdown itu SENGAJA "keluar" dari kotak .topbar (top: calc(100% +
   8px), lihat komentar di .notif-dropdown) supaya tidak menimpa kolom
   pencarian. Kalau overflow:hidden ada di .topbar, dropdown yang keluar
   dari kotak itu ikut terpotong habis (tidak kelihatan sama sekali) —
   bug nyata yang ditemukan 2026-09-08 pas baru pindah posisi dropdown.
   Blob-nya sendiri tetap terlihat sama persis karena .topbar-form
   memenuhi hampir seluruh kotak .topbar (cuma dikurangi padding). */
.topbar-form {
    display: flex;
    flex-direction: column;
    gap: 8px;
    position: relative;
    overflow: hidden;
    /* Badge notifikasi (.cart-badge di #notifBtn) sengaja poke keluar
       -6px (kanan) dan -4px (atas) dari kotak tombolnya sendiri (gaya
       "badge pojok" biasa) — tanpa ruang napas di sini, overflow:hidden
       di atas ikut memotong ujungnya (ketahuan dari 2 screenshot user
       terpisah: kanan dulu, lalu atas, 2026-09-08 — sisi kanan sempat
       diperbaiki lebih dulu, ternyata sisi atas juga kena masalah yang
       sama persis). Kedua padding ini + pengurangan yang sepadan di
       .topbar (lihat komentar di sana) menjaga posisi visual ikon tetap
       sama seperti sebelumnya, cuma geser garis potong overflow-nya. */
    padding-right: 8px;
    padding-top: 6px;
}
/* Blob abstrak (radial-gradient murni, bukan gambar/SVG) — dua lingkaran
   lembut yang sebagian terpotong tepi header, meniru rasa ".owner-hero-spark"
   tanpa perlu data grafik sungguhan (header ini tampil di semua halaman
   customer, bukan cuma dashboard yang punya angka omset). */
.topbar-form::before,
.topbar-form::after {
    content: '';
    position: absolute;
    border-radius: 50%;
    pointer-events: none;
    z-index: 0;
}
/* Blob putih transparan di atas navy dulu terlihat sebagai kilau lembut —
   di atas latar putih-abu yang baru, putih-di-atas-putih akan tidak
   kelihatan sama sekali. Diganti tint biru sangat pudar (warna
   --blue-accent, bukan putih) supaya kesan dekoratifnya tetap ada di latar
   terang. */
.topbar-form::before {
    width: 220px;
    height: 220px;
    top: -150px;
    right: -50px;
    background: radial-gradient(circle, rgba(47, 107, 255, 0.10), rgba(47, 107, 255, 0) 70%);
}
.topbar-form::after {
    width: 170px;
    height: 170px;
    bottom: -120px;
    left: -50px;
    background: radial-gradient(circle, rgba(47, 107, 255, 0.07), rgba(47, 107, 255, 0) 70%);
}
/* Konten form (baris logo, kolom pencarian) tetap di atas blob — sama pola
   dengan ".owner-hero > *:not(.owner-hero-spark)". .notif-dropdown TIDAK
   perlu aturan ini — dia sibling .topbar-form, sudah punya z-index:80
   sendiri (lihat .notif-dropdown), jauh di atas blob mana pun. */
.topbar-form > * { position: relative; z-index: 1; }
.topbar-row {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    position: relative;
}
.topbar-actions { display: flex; align-items: center; gap: 10px; flex-shrink: 0; margin-top: 2px; }
.topbar-actions .icon-btn { color: var(--navy); }
.notif-dropdown {
    display: none;
    position: absolute;
    /* top:46px (lama) cuma menutupi tinggi baris logo — di header 2-baris
       (logo + kolom pencarian di bawahnya) itu bikin dropdown menimpa
       SEBAGIAN baris pencarian, bukan mulai persis di bawah header
       seluruhnya (ketahuan jelas begitu header dikasih latar gradien +
       kolom pencarian jadi pil putih solid, 2026-09-08 — sebelumnya
       sama-sama putih jadi tumpang-tindihnya tidak kelihatan). 100% =
       persis di tepi bawah .topbar (posisi relatifnya), berapa pun tinggi
       header-nya — tidak pernah menimpa apa pun di dalam header lagi. */
    top: calc(100% + 8px);
    right: 0;
    width: 220px;
    background: #fff;
    border-radius: 10px;
    box-shadow: 0 8px 24px rgba(8, 18, 53, 0.25);
    overflow: hidden;
    z-index: 80;
}
.notif-dropdown.open { display: block; }
.notif-dropdown-title {
    padding: 10px 14px;
    font-size: 12.5px;
    font-weight: 700;
    color: var(--navy);
    border-bottom: 1px solid var(--border);
}
.notif-empty {
    padding: 20px 14px;
    font-size: 12px;
    color: var(--text-muted);
    text-align: center;
}
/* Daftar notifikasi nyata di dalam dropdown lonceng. Lebarnya dinaikkan dari
   220px karena sekarang memuat judul + isi, bukan satu kalimat kosong. */
.notif-dropdown { width: 290px; max-height: 380px; overflow-y: auto; }
.notif-dropdown-title { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.notif-read-all {
    background: none; border: 0; padding: 0; cursor: pointer;
    font-size: 11px; font-weight: 600; color: var(--blue-accent);
}
.notif-item {
    display: block; padding: 10px 14px; border-bottom: 1px solid var(--border);
    color: var(--text);
}
.notif-item:last-of-type { border-bottom: none; }
/* Yang belum dibaca ditandai dua kali (latar + garis kiri), bukan warna saja —
   supaya tetap terbaca oleh pengguna yang sulit membedakan warna. */
.notif-item.notif-unread { background: var(--blue-light); border-left: 3px solid var(--blue-accent); }
.notif-item-title { display: flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 700; color: var(--navy); }
.notif-item-icon { display: inline-flex; flex-shrink: 0; color: var(--blue-accent); }
.notif-item-icon svg { display: block; width: 14px; height: 14px; }
.notif-item.notif-staff-activity .notif-item-icon { color: #92620a; }
.notif-item-body { display: block; font-size: 11.5px; color: var(--text-muted); line-height: 1.45; margin-top: 2px; }
.notif-item-time { display: block; font-size: 10.5px; color: var(--text-muted); margin-top: 3px; }
/* Lonceng admin/owner campur dua audience: aktivitas customer (checkout,
   registrasi) dan aktivitas staff lain (voucher/pengaturan diubah). Warna
   amber membedakannya sekilas dari biru "pesanan customer" bawaan, tanpa
   perlu baca judulnya dulu. */
.notif-item.notif-staff-activity { border-left: 3px solid var(--warning); }
.notif-item.notif-staff-activity.notif-unread { background: #fff8ec; border-left-color: var(--warning); }
.notif-item.notif-staff-activity .notif-item-title { color: #92620a; }
.notif-badge-tag {
    display: inline-block; font-size: 9.5px; font-weight: 700; letter-spacing: .02em;
    padding: 1px 6px; border-radius: 99px; margin-left: 6px; vertical-align: middle;
    background: var(--blue-light); color: var(--blue-accent);
}
.notif-item.notif-staff-activity .notif-badge-tag { background: #fdecc8; color: #92620a; }
.notif-footer {
    display: block; padding: 10px 14px; text-align: center;
    font-size: 12px; font-weight: 600; color: var(--blue-accent);
    border-top: 1px solid var(--border); background: var(--bg-soft);
}

/* Halaman notifikasi penuh (/notifications) */
.notif-row {
    display: flex; align-items: flex-start; gap: 10px;
    padding: 12px 14px; border-bottom: 1px solid var(--border); color: var(--text);
}
.notif-row.notif-unread { background: var(--blue-light); }
.notif-row.notif-staff-activity { border-left: 3px solid var(--warning); }
.notif-row.notif-staff-activity.notif-unread { background: #fff8ec; }
.notif-row-main { flex: 1; min-width: 0; }
.notif-dot {
    width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0;
    background: var(--blue-accent); margin-top: 5px;
}
.topbar-logo { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.3; }
/* Varian dengan lambang: lambang di kiri, teks menumpuk di kanannya. */
.topbar-logo.has-mark { flex-direction: row; align-items: center; gap: 9px; min-width: 0; }
.topbar-logo-mark { width: 42px; height: 42px; flex-shrink: 0; object-fit: contain; display: block; }
.topbar-logo-text { display: flex; flex-direction: column; align-items: flex-start; min-width: 0; }
/* Nama toko panjang ("SUKSES JAYA BERSAMA") harus muat di sebelah lambang pada
   layar 360px, jadi teksnya mengecil begitu ada lambang. */
.topbar-logo.has-mark .topbar-logo-line1 { font-size: 13.5px; letter-spacing: 0.2px; }
.topbar-logo.has-mark .topbar-logo-line2 { font-size: 11px; letter-spacing: 1.2px; }
.topbar-logo.has-mark .topbar-tagline { font-size: 9px; margin-top: 2px; }
/* Di 320px, nama panjang + lambang + 2 ikon aksi tidak muat pada ukuran di atas:
   teksnya (nowrap) menabrak ikon chat, bukan membungkus. Kecilkan keduanya. */
@media (max-width: 360px) {
    .topbar-logo.has-mark { gap: 7px; }
    .topbar-logo.has-mark .topbar-logo-mark { width: 36px; height: 36px; }
    .topbar-logo.has-mark .topbar-logo-line1 { font-size: 11.5px; }
    .topbar-logo.has-mark .topbar-logo-line2 { font-size: 9.5px; letter-spacing: 0.8px; }
    .topbar-logo.has-mark .topbar-tagline { font-size: 8px; }
    /* Layar sangat sempit: tombol "Lihat Toko" di topbar admin/owner
       menyempit jadi ikon saja (label disembunyikan) supaya tetap muat
       berdampingan dengan lonceng notifikasi & menu hamburger. */
    .lihat-toko-btn { padding: 6px 8px; }
    .lihat-toko-label { display: none; }
}
.topbar-logo-line1 {
    font-weight: 800;
    font-size: 17px;
    letter-spacing: 0.3px;
    white-space: nowrap;
    color: var(--navy);
}
.topbar-logo-line2 {
    font-weight: 800;
    font-size: 14px;
    letter-spacing: 1.5px;
    white-space: nowrap;
    color: var(--blue-accent);
}
.topbar-tagline {
    display: block;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.5px;
    color: var(--text-muted);
    margin-top: 3px;
    white-space: nowrap;
}
.search-wrap { position: relative; width: 100%; }
/* Dulu pil putih polos cukup kontras sendiri di atas navy — sekarang latar
   headernya sendiri putih-ke-abu, jadi ditambah border supaya kolom
   pencarian tidak menyatu jadi satu blok putih tak berbatas dengan
   headernya. */
.search-bar {
    display: flex;
    align-items: center;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 20px;
    padding: 7px 12px;
    gap: 6px;
    box-shadow: 0 2px 10px rgba(8, 18, 53, 0.1);
}
.search-bar input {
    border: none;
    outline: none;
    flex: 1;
    min-width: 0;
    width: 100%;
    font-size: 13px;
    color: var(--text);
}
/* position:fixed (bukan absolute) SENGAJA — elemen ini sekarang duduk di
   luar .topbar-form (overflow:hidden), jadi tidak bisa lagi mengandalkan
   ancestor relatif untuk top/left/right statis. main.js yang mengisi
   left/top/width lewat inline style tiap kali kotak dibuka, dihitung dari
   getBoundingClientRect() .search-wrap — lihat komentar di
   layouts/app.blade.php kenapa dipindah. */
.search-suggest {
    display: none;
    position: fixed;
    background: #fff;
    border-radius: 12px;
    box-shadow: 0 12px 30px rgba(8, 18, 53, 0.28);
    overflow: hidden;
    max-height: 340px;
    overflow-y: auto;
    z-index: 80;
}
.search-suggest.open { display: block; }
.search-suggest-note {
    padding: 9px 14px;
    font-size: 11px;
    color: var(--text-muted);
    background: var(--bg-soft);
    border-bottom: 1px solid var(--border);
}
.search-suggest-note a { color: var(--blue-accent); font-weight: 700; }
.search-suggest-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 14px;
    border-bottom: 1px solid var(--border);
    color: var(--text);
}
.search-suggest-item:last-child { border-bottom: none; }
.search-suggest-item img { width: 34px; height: 34px; border-radius: 6px; object-fit: cover; flex-shrink: 0; }
.search-suggest-item .name { font-size: 12.5px; flex: 1; }
/* .price-wrap membungkus .price-old (opsional, harga asli dicoret — pakai
   styling global .price-old yang sudah ada) + .price (harga sekarang),
   ditumpuk vertikal supaya muat di baris sempit kotak saran. */
.search-suggest-item .price-wrap { display: flex; flex-direction: column; align-items: flex-end; gap: 1px; flex-shrink: 0; }
.search-suggest-item .price { font-size: 12px; font-weight: 700; color: var(--navy); white-space: nowrap; }
.search-suggest-item .price-old { font-size: 10.5px; white-space: nowrap; }
.search-suggest-empty { padding: 16px 14px; font-size: 12px; color: var(--text-muted); text-align: center; }
.icon-btn {
    position: relative;
    color: #fff;
    padding: 4px;
    display: inline-flex;
    align-items: center;
    background: none;
    border: none;
    outline: none;
    -webkit-tap-highlight-color: transparent;
}
.ico { display: block; }
.ic-inline { display: flex; align-items: center; color: var(--text-muted); }
.cart-badge {
    position: absolute;
    top: -4px;
    right: -6px;
    background: var(--danger);
    color: #fff;
    font-size: 10px;
    font-weight: 700;
    border-radius: 10px;
    padding: 1px 5px;
    min-width: 16px;
    text-align: center;
}
/* ===== Hero Carousel ===== */
.hero-carousel-wrap { position: relative; }
.hero-dots {
    position: absolute;
    bottom: 12px;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    gap: 6px;
    z-index: 2;
}
.hero-dots .dot {
    width: 6px; height: 6px; border-radius: 50%;
    background: rgba(255,255,255,0.5);
    cursor: pointer;
    transition: width 0.2s, border-radius 0.2s, background 0.2s;
}
.hero-dots .dot.active { background: #fff; width: 16px; border-radius: 3px; }
.hero-carousel {
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    gap: 0;
}
.hero-carousel::-webkit-scrollbar { display: none; }
.hero-slide {
    flex: 0 0 100%;
    scroll-snap-align: start;
    position: relative;
}
.hero-slide img { width: 100%; height: 150px; object-fit: cover; }
.hero-slide .hero-caption {
    position: absolute;
    left: 16px;
    bottom: 14px;
    color: #fff;
}
.hero-caption h3 { margin: 0 0 4px; font-size: 16px; font-weight: 800; }
.hero-caption p { margin: 0; font-size: 12px; opacity: 0.9; }

/* ===== Popup promo melayang (beranda) =====
   width:70vw murni (bukan dibatasi lebar kolom app-shell 480px) — mengikuti
   permintaan "memenuhi 70% width layar" apa adanya. max-height dikombinasi
   object-fit:contain di <img> supaya tetap aman di layar lebar/pendek
   (mis. desktop landscape): 70vw x rasio potret gambar ini bisa jauh lebih
   tinggi dari viewport-nya kalau tidak dibatasi tingginya juga. */
.promo-popup-overlay {
    position: fixed; inset: 0; background: rgba(13, 27, 76, .45); z-index: 2000;
    display: flex; align-items: center; justify-content: center; padding: 20px;
}
.promo-popup-overlay[hidden] { display: none; }
.promo-popup-inner { position: relative; }
.promo-popup-inner img {
    display: block; max-width: 70vw; max-height: 85vh; width: auto; height: auto;
    object-fit: contain; margin: 0 auto;
}
.promo-popup-close {
    position: absolute; top: -14px; right: -14px; width: 32px; height: 32px;
    border-radius: 50%; background: #fff; color: var(--navy); border: none;
    font-size: 20px; line-height: 1; cursor: pointer; box-shadow: 0 2px 10px rgba(13, 27, 76, .35);
    display: flex; align-items: center; justify-content: center; padding: 0;
}

/* ===== Video Widget (beranda, di atas Flash Sale) ===== */
/* Padding kiri/kanan 14px sama seperti .section, supaya sejajar dengan
   konten lain di halaman alih-alih nempel ke tepi layar. aspect-ratio pada
   elemen dalam (bukan wrap-nya) menjaga videonya 16:9 di lebar layar
   berapa pun, tanpa JS. */
.video-widget-wrap {
    padding: 0 14px;
    margin-bottom: 16px;
}
.video-widget-frame {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 9;
    background: #000;
    border-radius: var(--radius);
    overflow: hidden;
}
.video-widget-frame iframe,
.video-widget-frame video {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: 0;
    /* object-fit hanya berlaku ke <video>, iframe tidak menerima properti ini
       (harmless di sana) — video upload jarang pas 16:9 PERSIS, cover
       mengisi kotak tanpa distorsi walau memotong sedikit sisi yang lebih
       panjang, konsisten dgn .product-card img & elemen media lain di sini. */
    object-fit: cover;
}

/* ===== Section Titles ===== */
.section {
    padding: 16px 14px;
}
.section-title {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 12px;
}
.section-title h2 { display: flex; align-items: center; gap: 7px; font-size: 15px; margin: 0; font-weight: 800; color: var(--navy); }
.section-title a { font-size: 12px; color: var(--blue-accent); font-weight: 600; }

/* ===== Quick Categories ===== */
.category-grid {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: 8px;
}
.category-item {
    display: block;
    text-align: center;
    font-size: 11px;
    color: var(--text);
    background: none;
    border: none;
    padding: 0;
    font-family: inherit;
    cursor: pointer;
    text-decoration: none;
}
/* Permintaan user 2026-09-13: ganti ikon jadi galeri preset berwarna
   (App\Support\CategoryIcons), lingkaran latar lama DIHAPUS — ikon barunya
   sudah "eye catching" sendiri (duotone navy+biru), bulatan seragam lama
   justru menyeragamkan semua kategori jadi terlihat sama dari jauh. */
/* Permintaan user 2026-09-13 (revisi kedua): lingkaran latar KEMBALI, tapi
   sekarang warnanya berbeda-beda per ikon (pastel, dari palet yang serasi
   satu sama lain — lihat App\Support\CategoryIcons::circleColor()), bukan
   satu warna biru rata untuk semua kategori seperti sebelumnya. Warna
   diberikan lewat inline style dari Blade (background-color per kategori),
   CSS di sini cuma bentuk lingkaran + ukurannya. */
.category-icon {
    width: 56px;
    height: 56px;
    margin: 0 auto 6px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
}
.category-icon svg { width: 34px; height: 34px; }
.category-icon img { width: 34px; height: 34px; object-fit: contain; }

/* Slot ke-10 grid kategori: tombol "Lainnya" buka/tutup baris tambahan
   (permintaan user 2026-09-28) — sisanya sama persis .category-item, cuma
   baris label ikut menampilkan panah kecil yang berputar saat terbuka. */
.category-toggle-chevron {
    display: inline-flex;
    vertical-align: -1px;
    margin-left: 1px;
    transition: transform 0.25s ease;
}
.category-toggle-chevron svg { width: 12px; height: 12px; }
.category-toggle[aria-expanded="true"] .category-toggle-chevron { transform: rotate(90deg); }

/* Baris tambahan: auto-height via grid-template-rows 0fr->1fr, digabung
   fade opacity — trik CSS murni (bukan max-height angka tebakan, bukan JS
   ukur scrollHeight tiap toggle). Wrapper jadi grid 1-baris; baris itu
   sendiri yang dianimasikan tingginya, isi di dalam clip lewat
   overflow:hidden pada .category-more-inner selama transisi berjalan. */
.category-more-wrap {
    display: grid;
    grid-template-rows: 0fr;
    opacity: 0;
    transition: grid-template-rows 0.35s ease, opacity 0.3s ease;
}
.category-more-wrap.is-open {
    grid-template-rows: 1fr;
    opacity: 1;
}
.category-more-inner { overflow: hidden; min-height: 0; }
.category-grid-more { margin-top: 12px; }

/* .story-close: dipertahankan (dipakai ulang oleh .filter-panel-header),
   sisa CSS story viewer lama sudah dihapus 2026-09-13 bersama fiturnya
   (diganti baris video TikTok — lihat .tiktok-strip di bawah). */
.story-close { background: none; border: none; color: #fff; font-size: 26px; line-height: 1; cursor: pointer; padding: 0 6px; }

/* ===== Flash Sale ===== */
.flash-sale-header {
    display: flex;
    align-items: center;
    gap: 8px;
    background: linear-gradient(90deg, var(--navy), var(--blue-accent));
    color: #fff;
    padding: 10px 14px;
    border-radius: var(--radius) var(--radius) 0 0;
}
.flash-sale-header h2 { font-size: 14px; margin: 0; font-weight: 800; }
.countdown {
    display: flex;
    gap: 4px;
    margin-left: auto;
    font-size: 11px;
    font-weight: 700;
}
.countdown span { background: rgba(255,255,255,0.2); padding: 2px 6px; border-radius: 4px; }
.flash-sale-strip {
    display: flex;
    gap: 10px;
    overflow-x: auto;
    padding: 12px;
    background: var(--blue-light);
    border-radius: 0 0 var(--radius) var(--radius);
}
.flash-sale-strip .product-card { flex: 0 0 130px; }

/* ===== Baris Video TikTok (permintaan user 2026-09-13, ganti story kategori) =====
   Thumbnail diam (bukan video sungguhan) — TikTok tidak mengizinkan embed
   autoplay tanpa interaksi pengguna, jadi "gerakan" di sini murni highlight
   yang bergantian (JS main.js, tiap 5 detik) menandai thumbnail mana yang
   "aktif", bukan video yang jalan. Video asli baru dimuat (lazy) saat modal
   dibuka lewat klik — ini justru pola paling hemat memori, bukan sebaliknya. */
.tiktok-strip {
    display: flex;
    gap: 10px;
    overflow-x: auto;
    padding: 4px 2px 8px;
}
.tiktok-thumb {
    flex: 0 0 84px;
    aspect-ratio: 9/16;
    border-radius: var(--radius);
    overflow: hidden;
    position: relative;
    border: 2.5px solid transparent;
    background: #000;
    padding: 0;
    cursor: pointer;
    transition: border-color .3s, transform .3s;
}
.tiktok-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; opacity: 0.85; transition: opacity .3s; }
.tiktok-thumb-icon { position: absolute; bottom: 6px; left: 6px; color: #fff; display: flex; filter: drop-shadow(0 1px 3px rgba(0,0,0,0.6)); }
/* Thumbnail "aktif" — bergantian tiap 5 detik lewat JS, murni penanda visual. */
.tiktok-thumb.is-active {
    border-color: var(--blue-accent);
    transform: scale(1.04);
}
.tiktok-thumb.is-active img { opacity: 1; }

.tiktok-modal-overlay {
    position: fixed;
    inset: 0;
    left: 50%;
    transform: translateX(-50%);
    max-width: var(--max-width);
    width: 100%;
    background: rgba(0, 0, 0, 0.92);
    z-index: 300;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
}
.tiktok-modal-overlay[hidden] { display: none; }
.tiktok-modal-inner { position: relative; width: 100%; max-width: 340px; }
.tiktok-modal-close {
    position: absolute;
    top: -40px;
    right: 0;
    background: none;
    border: none;
    color: #fff;
    font-size: 28px;
    line-height: 1;
    cursor: pointer;
    padding: 4px 8px;
}
#tiktokModalEmbed iframe { border-radius: var(--radius); }

/* ===== Banner Iklan Beranda 2 (permintaan user 2026-09-12) =====
   Kotak antara grid produk & footer beranda. Versi gambar (.ad-banner-2
   tanpa modifier) cuma bungkus <img> — animasinya (GIF) datang dari file
   yang diunggah Owner sendiri. Versi teks bawaan (.ad-banner-2-text)
   "bergerak" lewat CSS murni: gradasi latar berjalan pelan + 3 badge
   metode bayar berdenyut bergantian — dipakai selama Owner belum
   mengunggah gambar/GIF sendiri. */
.ad-banner-2 {
    display: block;
    border-radius: var(--radius);
    overflow: hidden;
    text-decoration: none;
}
.ad-banner-2 img { width: 100%; display: block; }

.ad-banner-2-text {
    position: relative;
    background: linear-gradient(135deg, var(--navy), var(--blue-accent), var(--navy));
    background-size: 200% 200%;
    animation: adBanner2Gradient 6s ease infinite;
    padding: 24px 18px;
    color: #fff;
    text-align: center;
}
@keyframes adBanner2Gradient {
    0%, 100% { background-position: 0% 50%; }
    50% { background-position: 100% 50%; }
}
/* Kilau lembut, sama pola dengan .topbar-form::before/::after — dekorasi
   murni, tidak ikut aksesibilitas (pointer-events:none). */
.ad-banner-2-glow {
    position: absolute; inset: 0;
    background: radial-gradient(circle at 25% 20%, rgba(255, 255, 255, 0.18), transparent 60%);
    pointer-events: none;
}
.ad-banner-2-content { position: relative; z-index: 1; }
.ad-banner-2-kicker {
    display: inline-block;
    font-size: 10.5px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
    background: rgba(255, 255, 255, 0.2);
    padding: 4px 10px; border-radius: 20px;
    margin-bottom: 8px;
}
.ad-banner-2-title { font-size: 17px; font-weight: 800; margin: 0 0 6px; line-height: 1.35; }
.ad-banner-2-subtitle {
    font-size: 12px; line-height: 1.5; opacity: 0.92;
    margin: 0 auto 14px; max-width: 320px;
}
.ad-banner-2-brands { display: flex; justify-content: center; gap: 8px; flex-wrap: wrap; }
.ad-banner-2-brand {
    background: #fff; color: var(--navy);
    font-weight: 800; font-size: 11.5px;
    padding: 6px 14px; border-radius: 20px;
    animation: adBanner2BrandPulse 2.4s ease-in-out infinite;
}
/* Ditunda bergiliran (0/.3/.6 detik) — 3 badge berdenyut satu-satu, bukan
   serentak, supaya kelihatan "hidup" bukan cuma berkedip bareng. */
.ad-banner-2-brand:nth-child(1) { animation-delay: 0s; }
.ad-banner-2-brand:nth-child(2) { animation-delay: 0.3s; }
.ad-banner-2-brand:nth-child(3) { animation-delay: 0.6s; }
@keyframes adBanner2BrandPulse {
    0%, 100% { transform: scale(1); }
    15% { transform: scale(1.12); }
    30% { transform: scale(1); }
}
/* Pembeli yang eksplisit minta gerakan minimal (aksesibilitas) — matikan
   animasi hias, konten tetap sepenuhnya terbaca tanpa gerakan apa pun. */
@media (prefers-reduced-motion: reduce) {
    .ad-banner-2-text, .ad-banner-2-brand { animation: none; }
}

/* ===== Product Grid / Card ===== */
.product-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 10px;
}
.product-card {
    background: #fff;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
    position: relative;
}
.product-card .thumb { position: relative; }
.product-card .thumb img { width: 100%; aspect-ratio: 1/1; object-fit: cover; }
.badge-discount {
    position: absolute;
    top: 6px;
    left: 6px;
    background: var(--danger);
    color: #fff;
    font-size: 10px;
    font-weight: 700;
    padding: 2px 6px;
    border-radius: 4px;
}

/* Permintaan user 2026-09-13: badge diskon/"Paling Laris" di GRID (listing)
   harus bergerak & mencolok, supaya beda jelas dari kartu produk biasa yang
   tidak diskon/tidak laris — sengaja dibalik dari keputusan lama ("bising
   kalau semua kartu berkedip") karena sekarang user MEMANG minta itu.
   Gerakannya tetap dijaga halus (skala kecil, glow lembut) supaya tidak jadi
   norak walau dipasang di banyak kartu sekaligus. */
.product-card .badge-discount {
    animation: badgeDiskonNyala 2s ease-in-out infinite;
    will-change: transform, box-shadow;
}

/* Badge diskon di HALAMAN DETAIL produk (.badge-discount-pulse) memakai
   animasi yang SAMA (badgeDiskonNyala) — cuma satu badge di halaman itu jadi
   sudah otomatis menonjol, tidak perlu keyframe terpisah lagi.

   Gerakannya sengaja kecil (skala 1->1.06, geser <1px) — cukup untuk tertangkap
   sudut mata tanpa membuat baris harga ikut bergoyang. */
.badge-discount-pulse {
    animation: badgeDiskonNyala 1.9s ease-in-out infinite;
    will-change: transform, box-shadow;
}
@keyframes badgeDiskonNyala {
    0%, 100% {
        transform: scale(1);
        box-shadow: 0 0 0 0 rgba(231, 76, 60, 0.55);
        filter: brightness(1);
    }
    45% {
        transform: scale(1.06) translateY(-0.5px);
        box-shadow: 0 0 10px 3px rgba(231, 76, 60, 0.45);
        filter: brightness(1.12);
    }
    70% {
        transform: scale(1.01);
        box-shadow: 0 0 4px 1px rgba(231, 76, 60, 0.25);
        filter: brightness(1.04);
    }
}
/* Pembeli yang menyalakan "kurangi animasi" di sistemnya tetap melihat badge
   yang sama, hanya tanpa gerakan — pola yang sudah dipakai .order-stepper-line. */
@media (prefers-reduced-motion: reduce) {
    .badge-discount-pulse,
    .product-card .badge-discount,
    .badge-bestseller,
    .product-card.is-bestseller { animation: none; }
}
.product-info { padding: 8px 10px 10px; }
.product-info .title {
    font-size: 12.5px;
    line-height: 1.3;
    height: 32px;
    overflow: hidden;
    margin-bottom: 4px;
}
.price-row { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; }
.price-now { color: var(--navy); font-weight: 800; font-size: 14px; }
.price-old { color: var(--text-muted); font-size: 11px; text-decoration: line-through; }
.meta-row { display: flex; justify-content: space-between; flex-wrap: wrap; row-gap: 4px; margin-top: 6px; font-size: 10.5px; color: var(--text-muted); }
.rating { color: var(--warning); }
.sold-count { display: inline-flex; align-items: center; gap: 4px; }
.badge-tag { display: inline-block; font-size: 9.5px; font-weight: 700; padding: 1.5px 6px; border-radius: 4px; white-space: nowrap; }
/* "Paling Laris" dibedakan dari "Produk Baru" lewat gerakan, bukan cuma warna
   (permintaan user 2026-09-13) — efek "shine" (garis cahaya menyapu dari kiri
   ke kanan), pola umum e-commerce untuk menonjolkan badge tanpa perlu
   berkedip/melompat (itu terasa murahan di grid berisi banyak kartu). */
.badge-bestseller {
    background: #fff4e0;
    color: #b9740a;
    position: relative;
    overflow: hidden;
}
.badge-bestseller::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(115deg, transparent 20%, rgba(255, 255, 255, 0.85) 50%, transparent 80%);
    transform: translateX(-100%);
    animation: badgeBestsellerShine 2.6s ease-in-out infinite;
}
@keyframes badgeBestsellerShine {
    0%, 40% { transform: translateX(-100%); }
    70%, 100% { transform: translateX(100%); }
}
.badge-new { background: #e8f8f0; color: var(--success); }
/* Glow di border kartu "Paling Laris" sekarang berdenyut halus (bukan statis)
   supaya kartu ini terlihat jelas berbeda dari kartu tanpa badge saat scroll
   cepat di grid — beda gerakan dari badge diskon (pulse) & badge laris
   (shine) supaya ketiganya tidak terasa seperti animasi yang sama diulang. */
.product-card.is-bestseller {
    border: 1px solid var(--border);
    animation: bestsellerGlow 2.6s ease-in-out infinite;
    will-change: box-shadow;
}
@keyframes bestsellerGlow {
    0%, 100% { box-shadow: 0 0 10px 1px rgba(245, 166, 35, 0.4); }
    50% { box-shadow: 0 0 20px 4px rgba(245, 166, 35, 0.7); }
}
.product-card.is-new { border-color: var(--success); }

/* ===== Filter / Sort Bar ===== */
.filter-bar-wrap {
    position: sticky;
    top: var(--topbar-height, 58px);
    z-index: 40;
    background: #fff;
    border-bottom: 1px solid var(--border);
}
.filter-row {
    display: flex;
    gap: 8px;
    padding: 9px 14px;
    overflow-x: auto;
}
.filter-row.sort-row {
    border-top: 1px dashed var(--border);
    overflow-x: hidden;
    align-items: center;
}
/* Sub-kategori muncul di bawah pill grup yang punya anak — fade-in ringan
   supaya kemunculannya terasa disengaja, bukan lompatan layout mendadak. */
.filter-row.subcategory-row {
    border-top: 1px dashed var(--border);
    background: var(--bg-soft);
    animation: subcategoryFadeIn 0.2s ease-out;
}
@keyframes subcategoryFadeIn {
    from { opacity: 0; transform: translateY(-4px); }
    to { opacity: 1; transform: none; }
}
.sort-chips { display: flex; gap: 8px; overflow-x: auto; flex: 1; }
.filter-fab-btn {
    flex-shrink: 0;
    width: 32px; height: 32px;
    border-radius: 50%;
    background: var(--blue-light);
    color: var(--navy);
    display: flex; align-items: center; justify-content: center;
    border: none; cursor: pointer; position: relative;
}
.filter-fab-btn.has-filter::after {
    content: '';
    position: absolute; top: 1px; right: 1px;
    width: 8px; height: 8px; border-radius: 50%;
    background: var(--danger); border: 1.5px solid #fff;
}

/* ===== Full Filter Side Panel ===== */
.filter-overlay {
    display: none;
    position: fixed; inset: 0;
    left: 50%; transform: translateX(-50%);
    max-width: var(--max-width); width: 100%;
    background: rgba(8, 18, 53, 0.45);
    z-index: 90;
}
.filter-overlay.open { display: block; }
.filter-panel {
    position: fixed;
    top: 0; bottom: 0; right: 0;
    transform: translateX(100%);
    width: 82%;
    max-width: 380px;
    background: #fff;
    z-index: 91;
    display: flex;
    flex-direction: column;
    transition: transform 0.28s ease;
    box-shadow: -6px 0 20px rgba(8, 18, 53, 0.2);
}
.filter-panel.open { transform: translateX(0); }
@media (min-width: 481px) {
    .filter-panel { right: calc(50% - var(--max-width) / 2); }
}

/* ── Chat in-app (2026-09-12, geometri direvisi 2026-09-13) ────────────────
   Jendela mengambang di pojok kanan bawah (gaya widget chat website pada
   umumnya — Intercom/Crisp/Tawk.to), BUKAN drawer setinggi layar penuh
   seperti .filter-panel.

   TIDAK ADA TOMBOL FAB TERPISAH (permintaan user 2026-09-13: "tidak perlu
   ada floating button... button chat tetap cukup dari head navbar saja") —
   pintu masuknya cuma ikon topbar & tombol di .sticky-action-bar halaman
   produk, keduanya sudah [data-chat-open]. Ruang yang dulu disisakan untuk
   FAB (footprint 60px + jarak 18px = 78px di atas bottom-nav) sekarang
   dipakai menambah TINGGI panel: bottom digeser turun 64px (jadi cuma
   berjarak 14px dari bottom-nav, sama seperti .filter-panel) sementara
   height ditambah 64px yang sama persis — sisi ATAS panel tetap di tempat
   yang sama, sisi BAWAH turun mengisi ruang yang dibebaskan.

   Lebar/tinggi dihitung dua cara sekaligus (vw/vh DIBATASI persentase dari
   var(--max-width)): di HP, viewport = lebar shell, jadi vw/vh saja sudah
   pas. Di layar lebar, shell tetap terkunci var(--max-width) di tengah —
   tanpa batas kedua, "70% lebar halaman" akan berarti 70% dari lebar
   BROWSER, jendela chat jadi raksasa dan tidak lagi terasa seperti bagian
   dari aplikasi.

   Peta z-index yang dipatuhi:
     .bottom-nav / .sticky-action-bar  60
     .filter-overlay / .filter-panel   90 / 91
     .chat-overlay / .chat-panel       92 / 93
     #toastContainer                   999  <- toast dari dalam panel tetap terbaca
     .form-modal-overlay               1000
     .wt-overlay/.wt-cutout/.wt-tooltip 1500 / 1501 / 1502  <- walkthrough spotlight
     #pageLoadingOverlay               2000
     .promo-popup-overlay              2000  <- lihat catatan di seksi walkthrough
*/

/* Bukan scrim gelap seperti .filter-overlay — jendela chat sekarang cuma
   mengisi sebagian layar, jadi menggelapkan SELURUH halaman di baliknya
   akan terasa lebih mengganggu daripada drawer penuh yang digantikannya.
   Tetap dipasang penuh layar (transparan) semata untuk menangkap "klik di
   luar untuk menutup", pola yang sama dengan .filter-overlay. */
.chat-overlay {
    display: none;
    position: fixed; inset: 0;
    background: transparent;
    z-index: 92;
}
.chat-overlay.open { display: block; }

.chat-panel {
    position: fixed;
    right: 14px;
    /* WAJIB variabel, bukan angka mati: tinggi bottom-nav dihitung main.js dan
       berbeda antar halaman (halaman tanpa nav mengembalikan 0px). Sama
       seperti .filter-panel/.filter-overlay — tanpa FAB, tidak ada lagi yang
       perlu diberi jarak ekstra di bawah panel. */
    bottom: calc(var(--bottom-nav-height, 0px) + 14px);
    width: 70vw;
    max-width: calc(var(--max-width) * 0.7);
    height: calc(50vh + 64px); /* +64px = ruang yang dulu disisakan untuk FAB */
    max-height: calc(var(--max-width) * 1.05 + 64px);
    min-height: 384px; /* thread + composer tetap terpakai di viewport pendek (mode landscape HP) */
    background: var(--bg-soft);
    border-radius: 16px;
    overflow: hidden; /* membulatkan sudut header/composer tanpa menyentuh rule masing-masing */
    z-index: 93;
    display: flex;
    flex-direction: column;
    transform: translateY(16px) scale(0.97);
    opacity: 0;
    pointer-events: none;
    transition: transform 0.22s ease, opacity 0.22s ease;
    box-shadow: 0 16px 40px rgba(8, 18, 53, 0.28);
}
.chat-panel.open {
    transform: translateY(0) scale(1);
    opacity: 1;
    pointer-events: auto;
}
@media (min-width: 481px) {
    /* Menempel 14px dari tepi KANAN SHELL yang terkunci di tengah, bukan
       tepi kanan browser. */
    .chat-panel { right: calc(50% - var(--max-width) / 2 + 14px); }
}
@media (prefers-reduced-motion: reduce) {
    .chat-panel { transition: none; }
}

.chat-panel-header {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: 8px; padding: 14px 16px;
    background: #fff; border-bottom: 1px solid var(--border);
}
.chat-panel-header h3 { margin: 0; font-size: 15px; color: var(--navy); }
.chat-status { font-size: 11.5px; color: var(--text-muted); }

/* Permintaan user 2026-09-13: "sematan pesanannya hanya nempel disitu saja"
   — kartu konteks TIDAK LAGI panel terpisah (.chat-card, full-bleed, selalu
   menempel di atas riwayat) — sekarang LAMPIRAN di dalam bubble pesan
   pembeli itu sendiri (.chat-attachment), persis forward WhatsApp: kartu
   bergaris tepi & sudut membulat, muncul SEKALI di atas teks pesan yang
   memicunya, lalu ikut tergulung ke riwayat seperti pesan biasa. */
.chat-attachment {
    background: rgba(255,255,255,.92);
    border: 1px solid rgba(13,27,76,.14);
    border-radius: 10px;
    padding: 8px 10px;
    margin-bottom: 6px;
    display: flex; gap: 10px; align-items: flex-start;
}
.chat-bubble-customer .chat-attachment { background: rgba(255,255,255,.14); border-color: rgba(255,255,255,.28); }
.chat-bubble-customer .chat-attachment .chat-card-kicker,
.chat-bubble-customer .chat-attachment .chat-card-line { color: rgba(255,255,255,.75); }
.chat-bubble-customer .chat-attachment .chat-card-title { color: #fff; }
.chat-attachment img {
    width: 42px; height: 42px; border-radius: 8px;
    object-fit: cover; flex-shrink: 0;
}
.chat-card-body { min-width: 0; flex: 1; }
.chat-card-kicker { font-size: 10.5px; color: var(--text-muted); }
.chat-card-title {
    font-size: 12.5px; font-weight: 700; color: var(--text);
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.chat-card-line { font-size: 11.5px; color: var(--text-muted); }
.chat-card-switch {
    margin-top: 6px;
    background: none; border: 0; padding: 0;
    font: inherit; font-size: 11px; font-weight: 600;
    color: var(--blue-accent); cursor: pointer;
}
.chat-card-options {
    display: flex; flex-direction: column; gap: 4px;
    margin-top: 6px;
}
.chat-card-options button {
    text-align: left;
    background: var(--bg-soft);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 6px 8px;
    font: inherit; font-size: 11px;
    color: var(--text); cursor: pointer;
}
.chat-card-pills { display: flex; flex-wrap: wrap; gap: 4px; margin: 3px 0; }
.chat-card-pills span {
    font-size: 10px; padding: 1px 6px; border-radius: 20px;
    background: var(--blue-light); color: var(--navy);
}

.chat-thread { flex: 1; overflow-y: auto; padding: 12px 16px; }
.chat-bubble {
    max-width: 82%;
    padding: 8px 11px;
    border-radius: 12px;
    font-size: 12.8px;
    line-height: 1.45;
    margin-bottom: 8px;
    word-break: break-word;
}
.chat-bubble-customer { margin-left: auto; background: var(--navy); color: #fff; border-bottom-right-radius: 4px; }
.chat-bubble-bot, .chat-bubble-admin { background: #fff; color: var(--text); border: 1px solid var(--border); border-bottom-left-radius: 4px; }
.chat-bubble-admin { border-left: 3px solid var(--blue-accent); }
.chat-bubble-meta { font-size: 10px; color: var(--text-muted); margin: -4px 0 8px; }
.chat-bubble-customer + .chat-bubble-meta { text-align: right; }

/* Chip saran SEKARANG BAGIAN ALUR PERCAKAPAN (revisi 2026-09-13) — dulu
   panel terpisah mengambang di atas composer dengan tombol tutup sendiri;
   keluhan user: panel itu menghalangi, dan tombol tutupnya berdempetan
   dengan chip pertama. Sekarang cuma <div> biasa yang ikut di-append ke
   #chatThread tepat setelah bubble bot-nya (lihat bubble() di main.js) —
   ikut tergulung ke riwayat begitu percakapan berlanjut, tanpa perlu
   affordance "tutup" sama sekali (persis quick-reply WhatsApp Business). */
.chat-inline-suggestions {
    display: flex; flex-wrap: wrap; gap: 6px;
    margin: -2px 0 10px;
}
.chat-chip {
    font-size: 11.5px;
    padding: 6px 10px;
    border-radius: 20px;
    border: 1px solid var(--blue-accent);
    background: #fff;
    color: var(--blue-accent);
    cursor: pointer;
    text-align: left;
}
.chat-chip:hover { background: var(--blue-light); }

.chat-composer {
    display: flex; gap: 8px; align-items: flex-end;
    padding: 10px 12px;
    background: #fff;
    border-top: 1px solid var(--border);
}
.chat-composer textarea {
    flex: 1;
    resize: none;
    max-height: 96px;
    padding: 9px 11px;
    border: 1.5px solid var(--border);
    border-radius: 10px;
    font: inherit;
    font-size: 12.8px;
}
.chat-composer .btn { flex: 0 0 44px; padding: 9px 0; display: flex; align-items: center; justify-content: center; }
.filter-panel-header {
    display: flex; align-items: center; justify-content: space-between;
    padding: 16px; border-bottom: 1px solid var(--border);
}
.filter-panel-header h3 { margin: 0; font-size: 15px; color: var(--navy); }
.filter-panel-header .story-close { color: var(--navy); }
.filter-panel-body { flex: 1; overflow-y: auto; padding: 4px 16px; }
.filter-panel-section { padding: 14px 0; border-bottom: 1px solid var(--border); }
.filter-panel-section h4 { font-size: 12.5px; color: var(--navy); margin: 0 0 10px; }
.filter-option-list { display: flex; flex-direction: column; gap: 10px; }
.filter-option { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text); }
.filter-option input { width: auto; margin: 0; accent-color: var(--navy); }
.price-range-row { display: flex; align-items: center; gap: 8px; color: var(--text-muted); }
.price-range-row input {
    flex: 1; min-width: 0; padding: 9px 10px; border: 1px solid var(--border); border-radius: 8px; font-size: 13px;
}
.filter-panel-footer { display: flex; gap: 10px; padding: 14px 0 24px; position: sticky; bottom: 0; background: #fff; }
.filter-panel-footer .btn { flex: 1; }
.filter-chip {
    white-space: nowrap;
    font-size: 12px;
    padding: 6px 12px;
    border-radius: 16px;
    border: 1px solid var(--border);
    color: var(--text);
    background: #fff;
}
.filter-chip.active, .filter-chip:hover { background: var(--navy); color: #fff; border-color: var(--navy); }
/* Pill "Harga" (katalog) — ikon panah naik/turun di sebelah label, lihat Modules/Catalog/resources/views/index.blade.php. */
.sort-chip-price { display: inline-flex; align-items: center; gap: 4px; }
.sort-chip-price .ico { flex-shrink: 0; }

/* Baris kedua terpisah untuk kasus yang butuh perhatian admin (pengiriman
   gagal/retur dari webhook kurir) — border oranye tipis membedakannya dari
   flow status utama tanpa terasa seperti error/alert yang menakutkan. */
.filter-row-alert { align-items: center; padding-top: 0 !important; flex-wrap: wrap; }
.filter-row-alert-label { font-size: 11.5px; color: var(--text-muted); white-space: nowrap; }
.filter-chip-alert { border-color: #f0b429; color: #92400e; }
.filter-chip-alert.active, .filter-chip-alert:hover { background: #f0b429; color: #1a1a1a; border-color: #f0b429; }

/* ===== Buttons ===== */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    text-align: center;
    padding: 11px 16px;
    border-radius: 8px;
    font-weight: 700;
    font-size: 13.5px;
    border: none;
}
.btn-primary { background: var(--navy); color: #fff; }
.btn-primary:hover { background: var(--navy-dark); }
.btn:disabled { opacity: .55; cursor: not-allowed; }
.btn-accent { background: var(--blue-accent); color: #fff; }
.btn-outline { background: #fff; color: var(--navy); border: 1.5px solid var(--navy); }
.btn-block { width: 100%; }
.btn-sm { padding: 6px 10px; font-size: 12px; }
.btn-danger { background: var(--danger); color: #fff; }
/* Tombol persegi kecil untuk aksi ikon-saja (edit/hapus per baris) — dipakai
   di tempat aksi bertekslah "Edit"/"Hapus" akan berulang di banyak baris
   sekaligus (mis. tabel/tree kategori), supaya baris tetap ringkas. */
.btn-icon {
    display: inline-flex; align-items: center; justify-content: center;
    width: 30px; height: 30px; padding: 0; border-radius: 8px; flex-shrink: 0;
}
.btn-icon:disabled { opacity: .35; cursor: not-allowed; }

/* ===== Product Detail Page ===== */
.gallery { position: relative; }
/* position+overflow: wadah animasi geser antar foto (lihat slideMainImageTo
   di product.blade.php) — foto "keluar" dianimasikan absolute di dalam sini
   sebelum dibuang, overflow:hidden mencegah ia terlihat meluber ke luar. */
.gallery-main { position: relative; overflow: hidden; }
.gallery-main img, .gallery-main video { width: 100%; aspect-ratio: 1/1; object-fit: cover; }
.gallery-thumbs { display: flex; gap: 6px; padding: 8px 14px; overflow-x: auto; }
.gallery-thumbs img, .gallery-thumb-video {
    width: 56px; height: 56px; object-fit: cover; border-radius: 8px;
    border: 2px solid transparent; opacity: 0.7; flex-shrink: 0; cursor: pointer;
}
.gallery-thumbs img.active, .gallery-thumb-video.active { border-color: var(--blue-accent); opacity: 1; }
/* Thumbnail video (permintaan user 2026-09-08): kotak sejajar foto lain,
   PERSIS di posisi kedua (sebelah thumbnail foto utama) — bukan area/badge
   terpisah. Latar navy + ikon play putih di tengah, supaya jelas beda dari
   thumbnail foto walau tanpa gambar preview videonya sendiri. */
.gallery-thumb-video {
    background: var(--navy); display: flex; align-items: center; justify-content: center;
}
.gallery-thumb-video .play-icon { color: #fff; font-size: 16px; line-height: 1; }

.price-panel { padding: 14px; border-bottom: 8px solid var(--bg-soft); }
.price-panel h1 { font-size: 16px; margin: 0 0 8px; line-height: 1.35; }
.price-panel .price-now { font-size: 22px; }
/* Permintaan user 2026-09-15: harga coret & badge diskon di halaman detail
   kalah ukuran dari .price-now (22px) di sebelahnya — dibesarkan supaya
   sepadan, bukan cuma sisa teks kecil yang gampang terlewat. */
.price-panel .price-old { font-size: 15px; }
.price-panel .badge-discount { font-size: 13px; padding: 4px 9px; border-radius: 5px; }
.warranty-tag {
    display: inline-block; margin-top: 8px; font-size: 11px; color: var(--success);
    background: #e8f8f0; padding: 3px 8px; border-radius: 4px; font-weight: 700;
}
.condition-tag {
    display: inline-flex; align-items: center; gap: 4px;
    font-size: 11px; padding: 3px 8px; border-radius: 4px; font-weight: 700;
}
.condition-tag .ico { width: 12px; height: 12px; }
.condition-new { color: var(--blue-accent); background: var(--blue-light); }
.condition-used { color: #b45309; background: #fff1e0; }

.variant-block { padding: 14px; border-bottom: 8px solid var(--bg-soft); }
.variant-block h4 { font-size: 13px; margin: 0 0 8px; color: var(--navy); }
.variant-options { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.variant-pill {
    padding: 7px 14px; border: 1.5px solid var(--border); border-radius: 8px;
    font-size: 12.5px; background: #fff;
}
.variant-pill.selected { border-color: var(--navy); background: var(--navy); color: #fff; }

.spec-table { padding: 14px; border-bottom: 8px solid var(--bg-soft); }
.spec-table h4 { display: flex; align-items: center; gap: 6px; font-size: 13px; margin: 0 0 10px; color: var(--navy); }
.spec-table table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.spec-table td { padding: 6px 0; border-bottom: 1px dashed var(--border); }
.spec-table td:first-child { color: var(--text-muted); width: 45%; }

.desc-block { padding: 14px 14px calc(90px + var(--bottom-nav-height, 0px)); font-size: 13px; line-height: 1.6; color: #333; }
.desc-block h4 { display: flex; align-items: center; gap: 6px; font-size: 13px; margin: 0 0 8px; color: var(--navy); }

/* Menumpuk TEPAT DI ATAS .bottom-nav (bukan di atasnya) lewat
   --bottom-nav-height (disetel main.js dari tinggi asli .bottom-nav) —
   halaman produk sekarang selalu menampilkan bottom-nav juga (FR lama
   sengaja menyembunyikannya di sini, sudah tidak lagi). Var-nya default
   0px lewat var() sendiri, jadi halaman yang memang tidak punya bottom-nav
   tidak berubah. */
.sticky-action-bar {
    position: fixed;
    left: 50%;
    transform: translateX(-50%);
    bottom: var(--bottom-nav-height, 0px);
    width: 100%;
    max-width: var(--max-width);
    background: #fff;
    border-top: 1px solid var(--border);
    display: flex;
    align-items: stretch;
    gap: 8px;
    padding: 10px 12px;
    z-index: 60;
}
.sticky-action-bar .chat-btn {
    flex: 0 0 46px;
    background: var(--blue-light);
    color: var(--blue-accent);
    border-radius: 8px;
    display: flex; align-items: center; justify-content: center;
    font-size: 20px;
    /* <button> tidak mewarisi default <a>: tanpa empat baris ini tombolnya
       muncul dengan border abu-abu bawaan peramban dan kursor teks. */
    border: 0; cursor: pointer; padding: 0; font: inherit;
    text-decoration: none;
}
.sticky-action-bar .btn { flex: 1; }

/* ===== Cart Page ===== */
.cart-item {
    display: flex; gap: 10px; padding: 12px 14px; border-bottom: 1px solid var(--border);
}
.cart-item img { width: 64px; height: 64px; object-fit: cover; border-radius: 8px; }
.cart-item .info { flex: 1; }
.cart-item .title { font-size: 13px; margin-bottom: 2px; }
.cart-item .variant { font-size: 11px; color: var(--text-muted); }
.cart-price-row { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-top: 4px; }
.cart-price-row .unit-price { font-size: 13px; font-weight: 800; color: var(--navy); }
.cart-price-row .subtotal-price { font-size: 11.5px; color: var(--text-muted); }
.qty-control { display: flex; align-items: center; gap: 8px; margin-top: 6px; flex-wrap: wrap; }
.qty-control button {
    width: 24px; height: 24px; border-radius: 6px; border: 1px solid var(--border); background: #fff;
}
/* Ganti varian langsung dari keranjang — sejajar dengan stepper jumlah. */
.qty-control .variant-select {
    font-size: 11.5px; color: var(--text); border: 1px solid var(--border); border-radius: 6px;
    padding: 3px 6px; background: #fff; max-width: 120px;
}
.cart-note-input {
    width: 100%;
    margin-top: 8px;
    padding: 7px 10px;
    border: 1px solid var(--border);
    border-radius: 8px;
    font-size: 11.5px;
    font-family: inherit;
    color: var(--text);
    resize: none;
    min-height: 34px;
}
.cart-note-input::placeholder { color: #b6bccb; }
/* position: fixed + --bottom-nav-height, BUKAN lagi "position: sticky; bottom: 0"
   (permintaan user 2026-09-15: keranjang belanja sekarang selalu menampilkan
   bottom-nav juga, sama seperti halaman produk sudah lebih dulu diperbaiki —
   lihat komentar .sticky-action-bar). Dengan sticky+bottom:0 bar ini akan
   menumpuk PERSIS di posisi bottom-nav; var ini (disetel main.js dari tinggi
   asli .bottom-nav, default 0px kalau bottom-nav tidak ada di halaman itu)
   yang menggeser bar ke tepat di atasnya. */
.cart-summary {
    position: fixed; left: 50%; transform: translateX(-50%);
    bottom: var(--bottom-nav-height, 0px);
    width: 100%; max-width: var(--max-width);
    background: #fff; border-top: 1px solid var(--border);
    padding: 12px 14px; display: flex; align-items: center; gap: 10px; justify-content: flex-end;
    z-index: 60;
}
/* Sisakan ruang setinggi .cart-summary tepat di atasnya. Tanpa ini, konten
   yang kebetulan jadi baris terakhir sebelum bar ini (mis. salah satu pilihan
   kurir/pembayaran paling bawah, tergantung tinggi form) permanen tertutup
   bar sticky-nya — scroll sejauh mana pun tidak akan pernah membebaskannya,
   karena bar ini memang selalu menempel di dasar viewport begitu sisa
   konten di bawahnya lebih pendek dari tingginya sendiri. */
.sticky-summary-spacer { height: 70px; }
.cart-summary .price-breakdown { flex: 1; }
.breakdown-row { display: flex; justify-content: space-between; font-size: 12px; margin-bottom: 6px; }
.breakdown-row span:first-child { color: var(--text-muted); }
.breakdown-row span:last-child { font-weight: 600; color: var(--navy); }
.cart-summary .total { flex: 1; }
.cart-summary .total .label { font-size: 11px; color: var(--text-muted); }
.cart-summary .total .value { font-size: 16px; font-weight: 800; color: var(--navy); }

/* ===== Checkout ===== */
.checkout-section { padding: 14px; border-bottom: 8px solid var(--bg-soft); }
.checkout-section h3 { font-size: 13px; color: var(--navy); margin: 0 0 10px; }
.form-group { margin-bottom: 10px; }
.form-group label { display: block; font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
.form-group input, .form-group select, .form-group textarea {
    width: 100%; padding: 10px; border: 1px solid var(--border); border-radius: 8px; font-size: 13px;
}
.form-row { display: flex; gap: 8px; }
.form-row .form-group { flex: 1; }
.option-card {
    display: flex; align-items: center; gap: 10px; padding: 10px; border: 1.5px solid var(--border);
    border-radius: 8px; margin-bottom: 8px; font-size: 13px;
}
.option-card.selected { border-color: var(--blue-accent); background: var(--blue-light); }
.option-card .name { flex: 1; }
.option-card .cost { font-weight: 700; color: var(--navy); }

/* Toggle "Kirim ke Alamat"/"Ambil di Toko" (checkout.blade.php) — permintaan
   user 2026-09-15: yang TIDAK dipilih teks & ikonnya abu-abu, supaya beda
   dari yang aktif kelihatan jelas tanpa harus melihat radio-nya. Ikon
   @icon() memakai stroke="currentColor", jadi cukup atur `color` di sini —
   svg-nya otomatis ikut. Discope ke 2 kartu ini saja (bukan .option-card
   umum) karena kartu kurir/metode-bayar tidak punya masalah kontras yang
   sama (tidak ada ikon berwarna netral seperti @icon di dalamnya). */
#fulfillmentChoiceDelivery,
#fulfillmentChoicePickup { color: var(--text-muted); }
#fulfillmentChoiceDelivery.selected,
#fulfillmentChoicePickup.selected { color: var(--navy); }

/* Ringkasan kurir/metode-bayar yang sudah terpilih (checkout) — sengaja
   dibuat mirip .option-card.selected supaya terasa satu bahasa visual,
   dipakai untuk MENYEMBUNYIKAN daftar radio penuh sampai pembeli benar-benar
   mau menggantinya lewat tombol "Ganti ...". */
.selected-summary {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    padding: 10px; border: 1.5px solid var(--blue-accent); background: var(--blue-light);
    border-radius: 8px; font-size: 13px; margin-bottom: 8px;
}
.selected-summary .info { flex: 1; min-width: 0; line-height: 1.5; }
.selected-summary .cost { font-weight: 700; color: var(--navy); }
.selected-summary .cost .old-cost { text-decoration: line-through; color: var(--text-muted); font-weight: 400; margin-right: 5px; }
.selected-summary .cost .free-badge { color: var(--success); }
/* Sama seperti .selected-summary di atas, tapi utk baris ongkir per kurir di
   DAFTAR (permintaan user 2026-09-15) — .option-card .cost tidak otomatis
   mewarisi rule di atas karena beda ancestor (.selected-summary vs
   .option-card), jadi perlu rule terpisah, bukan salah ketik duplikat. */
.option-card .cost .old-cost { text-decoration: line-through; color: var(--text-muted); font-weight: 400; margin-right: 5px; }
/* Ringkasan voucher (permintaan user 2026-09-13: "behaviournya harus sama
   kaya metode bayar") — potongan warnanya merah, sama seperti .voucher-pick
   .potongan di daftar penuhnya, BUKAN navy seperti .cost biasa (itu untuk
   ongkos/harga, bukan pengurang). */
.selected-summary .potongan { font-weight: 700; color: #ff5f6d; font-size: 12px; flex-shrink: 0; }
.change-link {
    background: none; border: none; padding: 0; margin: 0; flex-shrink: 0;
    color: var(--blue-accent); font-weight: 700; font-size: 12px; cursor: pointer; text-decoration: underline;
}

/* Lencana warna bermerek untuk metode pembayaran (checkout) — permintaan
   user 2026-09-15: "pakai logo dong, masa polosan gitu". Lihat
   App\Support\PaymentLogos kenapa ini kotak warna+singkatan, bukan artwork
   logo resmi. Lebar mengikuti panjang teks (bukan kotak kaku), supaya nama
   pendek ("BCA") dan panjang ("Indomaret") sama-sama terbaca wajar. */
.pay-logo {
    flex-shrink: 0;
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 30px; height: 20px; padding: 0 6px;
    border-radius: 6px;
    font-size: 9.5px; font-weight: 800; letter-spacing: 0.2px;
    white-space: nowrap;
    line-height: 1;
}

/* Logo file asli (dipangkas 2026-09-15 dari kanvas 80x40 ke bbox konten
   masing-masing — filenya semula punya margin kosong seragam di semua sisi,
   itu penyebab celah kiri-kanan yang terlihat sebelumnya, bukan soal
   object-fit) — bingkai tipis + latar putih supaya tetap terbaca bersih di
   atas .selected (biru muda), bukan cuma kotak gambar mengambang begitu
   saja. object-fit:contain
   menjaga rasio tiap logo (semua file berbagi kanvas 80x40 yang sama, jadi
   ukurannya konsisten satu sama lain tanpa perlu disetel per-file). */
.pay-logo-img {
    flex-shrink: 0;
    width: 44px; height: 22px;
    object-fit: contain;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 1px;
}

/* Bekas kartu VA bertingkat (checkout.blade.php & change_payment.blade.php
   sudah diratakan 2026-09-15, semua channel jadi .option-card datar biasa)
   — .va-back-btn TETAP dipakai (tombol "Kembali" generik di bawah seluruh
   daftar metode checkout), sisanya (.va-option-card/.va-main-row/
   .va-change-btn/.va-bank-list/.va-bank-options-scroll/.va-bank-option)
   sudah tidak punya konsumen di blade manapun, dihapus. */
.va-back-btn {
    align-self: flex-start; margin-top: 4px;
    display: inline-flex; align-items: center; gap: 4px;
    background: none; border: none; padding: 4px 0;
    color: var(--text-muted); font-size: 12px; font-weight: 700; cursor: pointer;
}

/* ===== Auth ===== */
/* Permintaan user 2026-09-15: latar navy gelap + dot-matrix diganti gradient
   terang (ivory di atas -> biru pucat di tengah -> biru lembut di bawah),
   dicontohkan lewat screenshot app lain yang pakai gradient ungu + abstract
   blob. Teks logo & tagline yang dulu putih/biru muda (dirancang utk latar
   gelap) ikut diubah jadi warna navy tua supaya tetap kontras di latar baru. */
/* Jaring pengaman rubber-band iOS: overscroll di ujung bawah/atas sebentar
   menampilkan background <body> asli (var(--bg-soft), abu-abu) di balik
   halaman auth sebelum bounce-back. :has() menyasar body ini TANPA perlu
   menambah class baru ke layout bersama (app.blade.php dipakai semua
   halaman, bukan cuma auth).
   Revisi 2026-09-15: strip putih masih terlihat di bawah walau body sudah
   dikasih warna — ternyata html (bukan cuma body) yang mengintip, karena
   konten .auth-wrap (logo+banner+kartu, sekarang lebih tinggi dari 1
   viewport) membuat tinggi dokumen sungguhan sedikit beda dari 100dvh yang
   dipakai .auth-wrap. html DAN body sekarang sama-sama diberi warna gradient
   (bukan cuma satu), jadi potongan mana pun yang mengintip tetap warna yang
   benar, bukan putih polos. */
html:has(.auth-wrap), body:has(.auth-wrap) { background: #a7bee2; }
.auth-wrap {
    position: relative;
    min-height: 100vh;
    /* dvh = tinggi viewport SUNGGUHAN (bukan yang terkecil saat address bar
       browser mobile masih terbuka) — permintaan user: ada strip abu-abu
       (background body) kelihatan di bawah gradient begitu address bar
       collapse saat scroll, karena 100vh lama berhenti di viewport terkecil.
       Fallback 100vh tetap ada untuk browser lama yang belum kenal dvh. */
    min-height: 100dvh;
    padding: 40px 24px;
    background: linear-gradient(180deg, #f1efe2 0%, #c6d3e1 52%, #a7bee2 100%);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    justify-content: center;
}
.auth-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
/* Doodle ikon kategori (outline putih tipis) tersebar di latar — permintaan
   user 2026-09-15. Di ATAS canvas blob (z-index 1, canvas default di belakang
   krn ia elemen pertama) tapi di BAWAH logo & kartu (z-index 2) supaya tidak
   pernah menabrak konten yang harus dibaca. Warna putih transparan tipis
   ("outlined aja biar tidak terlalu mencolok") — lihat App\Support\CategoryDoodles. */
.auth-doodle {
    position: absolute;
    z-index: 1;
    /* Permintaan user 2026-09-15: "tambahin transparency lagi dong agar
       lebih transparant" — opacity diturunkan lagi 0.55 -> 0.3. */
    color: rgba(255, 255, 255, 0.3);
    pointer-events: none;
    /* Base transform dipakai ulang di tiap keyframe step (translate -50%/-50%
       yang men-center titik left/top + rotate acak) supaya animasi drift
       (translate tambahan) tidak menimpa/menghapus positioning dasarnya. */
    transform: translate(-50%, -50%) rotate(var(--doodle-rotate));
    animation: authDoodleFloat var(--doodle-duration, 7s) ease-in-out infinite;
    animation-delay: var(--doodle-delay, 0s);
}
/* Gerakan lembut naik-turun-geser (permintaan user) — pakai transform (bukan
   margin/left/top) supaya di-render compositor, bukan memicu reflow tiap
   frame utk 16 elemen sekaligus. Durasi & delay acak PER DOODLE (inline
   style dari CategoryDoodles::scattered()) supaya tidak serempak. */
@keyframes authDoodleFloat {
    0%, 100% { transform: translate(-50%, -50%) rotate(var(--doodle-rotate)) translate(0, 0); }
    25% { transform: translate(-50%, -50%) rotate(var(--doodle-rotate)) translate(var(--doodle-drift-x, 10px), calc(var(--doodle-drift-y, 12px) * -1)); }
    50% { transform: translate(-50%, -50%) rotate(var(--doodle-rotate)) translate(0, var(--doodle-drift-y, 12px)); }
    75% { transform: translate(-50%, -50%) rotate(var(--doodle-rotate)) translate(calc(var(--doodle-drift-x, 10px) * -1), 0); }
}
@media (prefers-reduced-motion: reduce) {
    .auth-doodle { animation: none; }
}
/* margin-bottom dikecilkan 24px -> 10px (permintaan user: "naikkan lagi
   posisi logo") — .auth-wrap men-center SELURUH blok (logo+badge+kartu)
   lewat justify-content:center, jadi menambah banner trust-badge di bawah
   sendirinya sudah mendorong logo naik; margin ini dikurangi supaya jarak
   logo->banner tidak menganga terlalu lebar seperti sebelumnya. */
/* Permintaan user 2026-09-15: jarak logo->banner trust-badge masih terasa
   jauh walau margin sudah dikecilkan sekali (24px->10px) — ternyata sebagian
   besar jaraknya berasal dari padding transparan di PNG lockup sendiri
   (~46px kosong di bawah kontennya sebelum di-crop, lihat sjb-lockup.png),
   bukan cuma margin CSS. Gambar sudah di-crop rapat ke kontennya; margin
   CSS dikecilkan sekali lagi (10px -> 2px) sebagai pelengkap. */
.auth-logo { position: relative; z-index: 2; text-align: center; margin-bottom: 2px; }
.auth-logo .brand { font-size: 22px; font-weight: 800; color: var(--navy-dark); line-height: 1.25; }
/* Dua bagian nama ditumpuk, bukan disambung: nama panjang seperti
   "SUKSES JAYA BERSAMA" + "ELEKTRONIK" akan menyatu jadi satu kata kalau
   dibiarkan inline (dulu tidak terlihat karena nama lama pendek). */
.auth-logo .brand .l1 { display: block; font-size: 19px; letter-spacing: 0.3px; }
.auth-logo .brand .l2 { display: block; font-size: 14px; letter-spacing: 3px; color: var(--navy); margin-top: 1px; }
.auth-logo .tagline { font-size: 11.5px; color: #4a5a72; margin-top: 4px; }
/* Banner "Produk Original / Garansi Resmi / Pengiriman Cepat / Pelayanan
   Terbaik" + tagline — lihat partials/trust_badges.blade.php. Setelah
   beberapa iterasi ikon SVG buatan tangan tidak pernah persis sama dengan
   referensi (shield/truk selalu ada yang beda), user menyerah dan kasih
   gambar aslinya sendiri (trustbadge.png) — sekarang cuma <img> polos,
   width 100% dibatasi max-width supaya tidak melebar berlebihan di layar
   lebar padahal aslinya rasio wide-banner (500x100). */
.auth-trust-badges { position: relative; z-index: 2; text-align: center; margin-bottom: 22px; }
.auth-trust-img { width: 100%; max-width: 380px; height: auto; margin: 0 auto; }
/* Efek kilau logo — pola & animasi SAMA PERSIS dengan .badge-bestseller
   (garis cahaya menyapu kiri->kanan, lihat komentar di sana), dipasang di
   sini karena permintaan user (2026-09-15): logo login diberi "efek
   mengkilat kaya efek animasi badge paling laris di produk". Butuh wrapper
   (bukan langsung di <img>) karena elemen replaced (<img>) tidak merender
   ::after.
   Revisi 2026-09-15 (kedua) — mark diganti lockup penuh (ikon + wordmark
   "SUKSES JAYA BERSAMA ELEKTRONIK" + tagline, bukan cuma ikon bulat lagi),
   jadi wrap TIDAK lagi 96x96 bulat (itu akan memotong habis wordmark di
   bawah ikon) — lebar mengikuti lebar kartu, tinggi menyesuaikan rasio
   gambar (aspect-ratio persegi dari sumbernya), border-radius kecil saja. */
.auth-logo-mark-wrap {
    display: block;
    position: relative;
    width: min(240px, 70%);
    /* Rasio disamakan dengan sjb-lockup.png yang SUDAH di-crop rapat ke
       kontennya (475x413, dulu 1/1 waktu gambarnya masih 500x500 dgn
       padding transparan) — kalau dibiarkan 1/1, object-fit:contain akan
       menyisakan letterbox kosong atas-bawah persis seukuran padding yang
       baru saja dihapus dari file gambarnya, jadi tidak ada gunanya. */
    aspect-ratio: 475 / 413;
    margin: 0 auto;
    border-radius: 12px;
    overflow: hidden;
}
.auth-logo-mark-wrap::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(115deg, transparent 20%, rgba(255, 255, 255, 0.85) 50%, transparent 80%);
    transform: translateX(-100%);
    /* Permintaan user 2026-09-15 (revisi): "sekali saja ketika refresh
       pertama kali, jangan terus2an" — beda dari .badge-bestseller (yang
       memang dirancang infinite di grid produk), di sini "1" (sekali jalan)
       + forwards supaya berhenti diam di ujung sapuan, bukan mengulang. */
    animation: badgeBestsellerShine 1.8s ease-in-out 1 forwards;
}
.auth-logo-mark-wrap .auth-logo-mark {
    margin: 0;
    width: 100%;
    height: 100%;
    object-fit: contain;
}
@media (prefers-reduced-motion: reduce) {
    .auth-logo-mark-wrap::after { animation: none; }
}
.auth-card {
    position: relative;
    z-index: 2;
    background: #fff;
    border-radius: 16px;
    padding: 24px 22px;
    /* Permintaan user 2026-09-15: lebar panel disamakan dengan lebar banner
       trust-badge di atasnya (.auth-trust-img, max-width:380px) — sebelumnya
       kartu ini melebar penuh mengikuti .auth-wrap (padding 24px kiri-kanan
       saja), jauh lebih lebar dari banner di atasnya. */
    width: 100%;
    max-width: 380px;
    margin-left: auto;
    margin-right: auto;
    box-shadow: 0 20px 50px rgba(3, 9, 34, 0.45);
    /* Efek glow SEKALI di border, memudar perlahan — permintaan user:
       "muncul nya hanya sekali aja di awal refresh lalu perlahan
       menghilang cahaya disekeliling panel loginnya". Riwayat warna: emas
       (awal) -> putih ("jangan emas") -> ternyata putih nyaris tak
       kelihatan di atas gradient .auth-wrap yang SUDAH terang (dibuktikan
       lewat perbandingan render terpisah) -> biru navy gelap (13,27,76 =
       var(--navy), warna biru logo SJB sendiri) supaya kontras jelas
       tanpa perlu warna asing dari luar palet merek. forwards+sekali
       (bukan infinite) karena ini bukan kartu "paling laris", cuma efek
       satu kali di kunjungan halaman. */
    animation: authCardGlowOnce 2.4s ease-out 1;
}
/* Header "Login Page" di dalam panel — sempat disertai tagline "Website
   Official Online Store" di atasnya, dihapus lagi (user: "aneh bgt"). */
/* Permintaan user: warna solid var(--navy-dark) "jangan hitam lah" (memang
   nyaris hitam, #081235) — diganti gradient hitam->biru meniru huruf "S"
   di logo SJB (gradient serupa dari gelap ke var(--navy) #0d1b4c).
   background-clip:text + color:transparent adalah SATU-SATUNYA cara CSS
   mewarnai teks dengan gradient (tidak ada properti "text-gradient"). */
.auth-card-heading {
    text-align: center;
    font-size: 20px;
    font-weight: 800;
    margin: 0 0 18px;
    background: linear-gradient(135deg, #000000 0%, var(--navy-dark) 45%, var(--navy) 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}
@keyframes authCardGlowOnce {
    0% { box-shadow: 0 20px 50px rgba(3, 9, 34, 0.45), 0 0 0 3px rgba(13, 27, 76, 0.9), 0 0 40px 14px rgba(13, 27, 76, 0.55), 0 0 70px 24px rgba(13, 27, 76, 0.3); }
    100% { box-shadow: 0 20px 50px rgba(3, 9, 34, 0.45), 0 0 0 3px rgba(13, 27, 76, 0), 0 0 40px 14px rgba(13, 27, 76, 0), 0 0 70px 24px rgba(13, 27, 76, 0); }
}
@media (prefers-reduced-motion: reduce) {
    .auth-card { animation: none; }
}
.auth-switch { text-align: center; margin-top: 16px; font-size: 12.5px; }
.auth-switch a { color: var(--blue-accent); font-weight: 700; }
.alert { padding: 10px 12px; border-radius: 8px; font-size: 12.5px; margin-bottom: 14px; }
.alert-danger { background: #fdecea; color: var(--danger); }
.alert-success { background: #e8f8f0; color: var(--success); }
.auth-divider {
    display: flex; align-items: center; gap: 10px;
    margin: 16px 0; font-size: 11.5px; color: var(--text-muted);
}
.auth-divider::before, .auth-divider::after { content: ''; flex: 1; height: 1px; background: var(--border); }
/* Kontainer tombol Google: beri tinggi minimum sama dengan tombol pil standar
   Google (40px) supaya layout tidak "melompat" sesaat sebelum skrip Google
   selesai dimuat dan renderButton() mengisi kontainer ini. */
.google-signin-container { min-height: 40px; margin-bottom: 4px; }
.google-login-note {
    background: var(--bg-soft); border-radius: 8px; padding: 10px 12px;
    font-size: 11px; color: var(--text-muted); margin-bottom: 16px;
}
.google-login-note code { background: #fff; padding: 1px 5px; border-radius: 4px; color: var(--navy); }

/* ===== Bottom Tab Nav (customer) ===== */
.bottom-nav {
    position: fixed; left: 50%; transform: translateX(-50%); bottom: 0; width: 100%;
    max-width: var(--max-width); background: #fff; border-top: 1px solid var(--border);
    display: flex; z-index: 60;
}
.bottom-nav a {
    flex: 1; text-align: center; padding: 8px 2px 6px; font-size: 9.5px; color: var(--text-muted); line-height: 1.2;
}
.bottom-nav a .ic { display: flex; width: fit-content; margin: 0 auto 2px; }
.bottom-nav a.active { color: var(--navy); font-weight: 700; }

/* Tombol tengah yang menonjol. Dipakai tab Promo; .live-tab dipertahankan
   supaya modul Live Shopping langsung tampil benar saat dinyalakan lagi. */
.bottom-nav a.promo-tab,
.bottom-nav a.live-tab { color: var(--text-muted); font-weight: 600; }
.bottom-nav a.promo-tab .ic,
.bottom-nav a.live-tab .ic {
    width: 42px; height: 42px;
    margin: -20px auto 3px;
    border-radius: 50%;
    background: linear-gradient(135deg, #ff5f6d, #ffa751);
    color: #fff;
    display: flex; align-items: center; justify-content: center;
    border: 3px solid #fff;
    box-shadow: 0 4px 10px rgba(255, 95, 109, 0.45);
}
.bottom-nav a.promo-tab.active,
.bottom-nav a.live-tab.active { color: #ff5f6d; }
.bottom-nav a.promo-tab.active .ic,
.bottom-nav a.live-tab.active .ic { box-shadow: 0 4px 14px rgba(255, 95, 109, 0.65); }

/* ===== Panel Admin / Owner — tata letak web desktop (sidebar kiri) =====
   Dulu panel ini bergaya aplikasi HP (topbar + bottom tabs). Diubah jadi
   layout web biasa karena panel kerja lebih banyak dipakai di layar lebar,
   dan navigasi yang harus dibuka dulu terasa janggal untuk dipakai seharian.
   Di layar sempit sidebar-nya menciut jadi drawer, jadi panel tetap terpakai
   dari HP. Bottom-nav sisi PEMBELI (.bottom-nav) tidak terpengaruh. */
.admin-shell { background: #fff; min-height: 100vh; position: relative; }
/* min-height dikunci sama dengan .admin-sidebar-head supaya tepi bawah
   keduanya sejajar — kalau tidak, pita navy di atas terlihat berundak di
   perbatasan sidebar dan konten. */
.admin-topbar {
    position: sticky; top: 0; z-index: 50;
    /* Sama seperti .admin-sidebar — putih ke abu, bukan navy solid, biar
       satu kesatuan dan logo mark di kepala sidebar tidak "hilang" di atas
       navy. Border bawah ditambah karena .admin-shell di baliknya putih
       polos — tanpa ini tidak ada garis pemisah topbar/konten sama sekali. */
    background: linear-gradient(180deg, #ffffff 0%, var(--border) 100%);
    color: var(--text); padding: 12px 16px;
    border-bottom: 1px solid var(--border);
    min-height: 58px; box-sizing: border-box;
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
}
.admin-topbar .brand { font-weight: 800; font-size: 14px; white-space: nowrap; }
.admin-topbar-sub { font-size: 11px; color: var(--text-muted); margin-top: 2px; }
/* .icon-btn dasar (dipakai lintas situs) putih (#fff) — cocok utk topbar
   navy lama, tapi hilang di topbar terang ini. Tombol hamburger (#adminMenuBtn)
   & lonceng notifikasi (#notifBtn) keduanya polos .icon-btn tanpa kelas lain,
   jadi override di sini supaya kelihatan lagi. */
.admin-topbar .icon-btn { color: var(--navy); }
/* overflow-x TIDAK diletakkan di sini (dulu ada) — overflow apa pun selain
   'visible' pada ancestor mematahkan position:sticky pada elemen di
   dalamnya (mis. sidebar mini quick-jump halaman produk). Konten lebar
   (tabel) mengurus overflow-nya sendiri lewat .table-scroll. */
.admin-body { padding: 16px 16px 32px; }

@media (min-width: 1024px) {
    .admin-shell { margin-left: var(--admin-sidebar-w); }
    .admin-body { padding: 22px 24px 40px; }
}

/* Ikon topbar yang halamannya sedang dibuka. */
.admin-topbar .icon-btn.is-active { color: var(--navy); background: rgba(13, 27, 76, 0.1); border-radius: 8px; }

/* Mini-header sticky di halaman Detail Pesanan admin — nomor pesanan +
   pemesan tetap terlihat selagi scroll. position:fixed (bukan sticky, lihat
   komentar di order_detail.blade.php); JS mengatur `top` dan lebar supaya
   pas menempel di bawah .admin-topbar dan sejajar dengan .admin-shell. */
.order-detail-sticky-header {
    position: fixed;
    left: 0;
    right: 0;
    z-index: 45;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 10px 16px;
    background: var(--blue-light);
    border-bottom: 1px solid var(--border);
    box-shadow: 0 2px 8px rgba(13, 27, 76, 0.06);
}
/* Ikut bergeser sejauh sidebar supaya tetap sejajar dengan area konten,
   bukan menindih sidebar. */
@media (min-width: 1024px) {
    .order-detail-sticky-header { left: var(--admin-sidebar-w); padding: 10px 24px; }
}
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; margin-bottom: 20px; }
.stat-card { border: 1px solid var(--border); border-radius: var(--radius); padding: 14px; }
.stat-card .label { font-size: 12px; color: var(--text-muted); margin-bottom: 6px; }
.stat-card .value { font-size: 20px; font-weight: 800; color: var(--navy); }
.panel { border: 1px solid var(--border); border-radius: var(--radius); margin-bottom: 20px; overflow: hidden; }
/* Sepasang panel berdampingan (mis. "Nomor WA CS" + "Metode Pembayaran
   Default" di Pengaturan Integrasi) — susut jadi 1 kolom di layar sempit
   supaya form di dalamnya tidak terlalu terjepit. */
.panel-pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
@media (max-width: 720px) {
    .panel-pair { grid-template-columns: 1fr; }
}
/* Panel yang dibungkus <a> (mis. baris pesanan admin yang seluruh kartunya
   bisa diklik) — sedikit umpan balik visual saat disentuh/hover. */
a.panel:hover, a.panel:active { border-color: var(--blue-accent); background: var(--bg-soft); }
.panel-header { padding: 12px 16px; border-bottom: 1px solid var(--border); font-weight: 700; color: var(--navy); font-size: 13.5px; display: flex; justify-content: space-between; align-items: center; }
/* Modifier untuk header SECTION (judul halaman/tab besar, mis. "Selesai" di
   Kelola Pesanan) — bukan header tiap kartu item individual di daftarnya
   (yang tetap pakai .panel-header polos supaya tidak ikut menyala biru satu
   per satu). Gradasi biru menandai "ini judul section", warna solid di
   bawahnya (isi daftar) tetap netral supaya section vs item tetap kebeda. */
/* Putih -> abu (bukan navy->biru) — permintaan user 2026-09-12, lanjutan
   dari .admin-sidebar/.topbar: dipakai di 52 tempat lintas 23 file (semua
   judul section panel admin/owner), jadi satu perubahan di sini otomatis
   menyeragamkan semuanya sekaligus dengan warna sidebar/topbar yang baru.
   Ikonnya (@icon()) pakai currentColor (lihat app/Support/Icons.php), jadi
   cukup ganti `color` di sini — ikon ikut berubah otomatis, tidak perlu
   disentuh satu-satu. --navy GLOBAL tetap tidak disentuh (dipakai luas di
   tempat lain yang tidak diminta ikut berubah, mis. .owner-hero). */
.panel-header-section {
    padding: 12px 16px; border-bottom: 1px solid var(--border); font-weight: 700;
    color: var(--navy); font-size: 13.5px; display: flex; justify-content: space-between; align-items: center;
    background: linear-gradient(180deg, #ffffff 0%, var(--border) 100%); gap: 8px;
}
.panel-header-section .panel-header-note { color: var(--text-muted); }
/* Bungkus ikon+judul jadi satu flex-item (bukan ikon & teks jadi 2 anak
   flex terpisah) — .panel-header-section pakai justify-content:space-between,
   kalau ikon & teks jadi anak lepas-lepas malah kesorong ke ujung berlawanan. */
.panel-header-title { display: flex; align-items: center; gap: 8px; }
.panel-header-section .panel-header-link { color: var(--blue-accent); text-decoration: underline; }
/* Tab aktif/nonaktif di dalam panel. */
.tab-row { display: flex; gap: 2px; padding: 0 10px; border-bottom: 1px solid var(--border); }
.tab-item {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 10px 12px; font-size: 12.5px; font-weight: 600;
    color: var(--text-muted); text-decoration: none;
    border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.tab-item.active { color: var(--navy); font-weight: 800; border-bottom-color: var(--blue-accent); }
.tab-count {
    font-size: 10px; font-weight: 800; background: var(--bg-soft); color: var(--text-muted);
    padding: 1px 6px; border-radius: 20px;
}
.tab-item.active .tab-count { background: var(--blue-light); color: var(--blue-accent); }

.filter-bar {
    display: flex; gap: 8px; padding: 12px 16px; flex-wrap: wrap; align-items: center;
    border-bottom: 1px solid var(--border); background: var(--bg-soft);
}
/* Kolom pencarian yang melar duluan; select ikut menyusut kalau ruang sempit
   supaya baris ini tidak pernah memaksa halaman menggeser ke samping. */
.filter-input { flex: 1 1 160px; min-width: 0; }
.filter-select { flex: 0 1 auto; min-width: 0; }
.filter-bar input[type="search"], .filter-bar select {
    padding: 7px 10px; border: 1px solid var(--border); border-radius: 8px;
    font-size: 12.5px; background: #fff; color: var(--text);
}

/* Tombol aksi berdampingan, tidak pernah terpotong separuh. */
.action-cell { white-space: nowrap; }
.action-cell > a, .action-cell > form { display: inline-block; vertical-align: middle; }
.action-cell > * + * { margin-left: 4px; }

/* Tabel lebar harus bisa digeser SENDIRI, bukan menumpang .admin-body.
   .panel memakai overflow:hidden (untuk sudut membulat), jadi tabel yang lebih
   lebar dari layar terpotong di situ dan scroll di container luar tidak pernah
   sampai — di layar HP kolom paling kanan (Aksi) jadi tidak bisa diraih sama
   sekali. Pembungkus ini yang menggeser, bukan halamannya. */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
/* Lebar minimum memaksa kolom berhenti menyempit dan mulai menggeser; tanpa
   ini tabel malah memampatkan diri sampai teksnya pecah per huruf. */
.table-scroll > table.data-table { min-width: 720px; }
table.data-table { width: 100%; border-collapse: collapse; font-size: 13px; }
/* Metode Pembayaran & Biaya Layanan: tiap grup (VA/E-Wallet/QRIS/Kartu
   Kredit/Paylater) punya <table> SENDIRI-SENDIRI yang ditumpuk — tanpa
   table-layout:fixed, lebar tiap kolom mengikuti isi tabelnya masing-masing
   (mis. "Virtual Account BCA" vs "ShopeePay") jadi kolom antar-tabel tidak
   sejajar walau sama-sama 4 kolom. Lebar tetap di sini memaksa keempatnya
   selalu sejajar dari grup manapun. */
table.data-table.fee-table { table-layout: fixed; }
table.data-table.fee-table th:nth-child(1), table.data-table.fee-table td:nth-child(1) { width: 70px; }
table.data-table.fee-table th:nth-child(2), table.data-table.fee-table td:nth-child(2) { width: 34%; }
table.data-table.fee-table th:nth-child(3), table.data-table.fee-table td:nth-child(3) { width: 22%; }
table.data-table.fee-table th:nth-child(4), table.data-table.fee-table td:nth-child(4) { width: 160px; }
table.data-table th, table.data-table td { padding: 10px 16px; border-bottom: 1px solid var(--border); text-align: left; }
table.data-table th { background: var(--bg-soft); color: var(--text-muted); font-size: 11.5px; text-transform: uppercase; }
/* Header tetap kelihatan saat tabel panjang di-scroll — dipakai tabel
   dalam .table-scroll yang tingginya sendiri dibatasi lewat max-height
   (lihat pemakaiannya, mis. admin/customers.blade.php). */
table.data-table.sticky-head thead th { position: sticky; top: 0; z-index: 1; }
table.data-table tbody tr:hover { background: var(--bg-soft); }
table.data-table tr.row-link { cursor: pointer; }
.status-pill { padding: 3px 8px; border-radius: 10px; font-size: 11px; font-weight: 700; }
/* Stepper progres pesanan (partials/order_progress_stepper.blade.php) —
   dipakai customer & admin/owner sekaligus. overflow-x:auto sendiri (bukan
   ikut melebarkan body) karena 5 node + garis penghubung lebih lebar dari
   layar HP sempit. */
.order-stepper { display: flex; align-items: flex-start; overflow-x: auto; padding: 10px 2px 4px; }
.order-stepper-item { display: flex; flex-direction: column; align-items: center; flex-shrink: 0; width: 78px; text-align: center; }
.order-stepper-node {
    width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    border: 2px solid var(--border); color: var(--text-muted); background: #fff;
}
.order-stepper-done .order-stepper-node { border-color: var(--success); color: var(--success); }
/* Node langkah "active" SENGAJA TIDAK dibedakan dari "upcoming" (tetap abu
   polos, warisan gaya .order-stepper-node dasar) — permintaan user
   2026-09-08: langkah yang belum genuinely tercapai (belum ada tanggal/
   status update) tidak boleh terlihat hijau/selesai, supaya "sedang
   berjalan" tidak keliru terbaca sama seperti "sudah selesai". Indikasi
   "sedang berjalan"-nya cukup lewat garis menujunya yang animasi
   (.order-stepper-line-progress di bawah), bukan warna node. */
.order-stepper-terminal .order-stepper-node { border-color: var(--danger); color: var(--danger); }
.order-stepper-label { font-size: 11px; font-weight: 600; color: var(--text); margin-top: 6px; line-height: 1.3; }
.order-stepper-upcoming .order-stepper-label { color: var(--text-muted); font-weight: 400; }
.order-stepper-terminal .order-stepper-label { color: var(--danger); }
.order-stepper-date { font-size: 9.5px; color: var(--text-muted); margin-top: 3px; line-height: 1.3; }
.order-stepper-line { flex: 1; min-width: 18px; height: 2px; background: var(--border); margin-top: 15px; }
.order-stepper-line-done { background: var(--success); }
/* Garis menuju langkah yang sedang berjalan (belum genuinely tercapai) —
   garis-garis kecil hijau yang "berjalan" ke arah node berikutnya, beda
   dari .order-stepper-line-done (hijau solid, sudah benar-benar selesai). */
.order-stepper-line-progress {
    background-image: repeating-linear-gradient(90deg, var(--success) 0 8px, transparent 8px 16px);
    background-size: 32px 100%;
    animation: orderStepperLineProgress 0.9s linear infinite;
}
@keyframes orderStepperLineProgress {
    from { background-position: 0 0; }
    to { background-position: 32px 0; }
}
@media (prefers-reduced-motion: reduce) {
    .order-stepper-line-progress { animation: none; background-image: repeating-linear-gradient(90deg, var(--success) 0 8px, var(--border) 8px 16px); }
}
.order-item-row { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.order-item-thumb { width: 36px; height: 36px; border-radius: 6px; object-fit: cover; flex-shrink: 0; border: 1px solid var(--border); }
.order-item-text { font-size: 12.5px; flex: 1; }
/* Daftar Produk (Aktif/Nonaktif/Draft) — 2x lipat dari .order-item-thumb
   (feedback user: "kecil banget sih foto nya"), kelas sendiri supaya tidak
   ikut membesarkan thumbnail di halaman Pesanan yang berbagi ukuran 36px. */
.product-list-thumb { width: 72px; height: 72px; border-radius: 8px; object-fit: cover; flex-shrink: 0; border: 1px solid var(--border); }

/* Kartu baris pesanan: seluruh kartu "diklik ke detail" lewat .order-row-link
   yang di-stretch menutupi kartu (bukan <a> pembungkus semuanya) — dulu ini
   juga menyisakan ruang untuk tombol aksi cepat (Siap Kirim/WA) berdampingan
   dengan z-index lebih tinggi, tapi itu sudah dihapus: semua aksi pesanan
   sekarang HANYA lewat halaman detail, list ini murni untuk menyisir & buka. */
.order-row { position: relative; cursor: pointer; }
.order-row-link { position: absolute; inset: 0; z-index: 1; }
/* Bootstrap-5 pagination markup ships a redundant mobile-only Prev/Next
   block gated by Bootstrap's d-sm-none utility class — unused here since
   this codebase has no Bootstrap, so both blocks would render at once. */
.d-sm-none { display: none; }
.pagination { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; list-style: none; padding: 0; margin: 0; }
.pagination .page-item .page-link {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 30px; height: 30px; padding: 0 8px;
    border: 1px solid var(--border); border-radius: 6px;
    color: var(--navy); font-size: 12.5px; font-weight: 600;
    text-decoration: none; background: #fff;
}
.pagination .page-item.active .page-link { background: var(--navy); border-color: var(--navy); color: #fff; }
.pagination .page-item.disabled .page-link { color: var(--text-muted); background: var(--bg-soft); cursor: default; }
.status-new, .status-pending { background: #fff4e0; color: var(--warning); }
.status-processed, .status-paid { background: var(--blue-light); color: var(--blue-accent); }
.status-ready_to_ship { background: #f3e8ff; color: #7c3aed; }
/* Ambil di Toko — sengaja beda warna dari ready_to_ship (ungu) supaya kedua
   jalur pemenuhan tidak tertukar sekilas pandang di daftar pesanan. */
.status-ready_for_pickup { background: #e0f2fe; color: #0369a1; }
.status-shipped, .status-in_transit { background: #e6f0ff; color: var(--navy); }
.status-completed { background: #e8f8f0; color: var(--success); }
.status-cancelled, .status-rejected, .status-expired, .status-failed { background: #fdecea; color: var(--danger); }
.status-delivery_failed, .status-returned { background: #fff1e0; color: #b45309; }

/* Sticky header (order-detail-sticky-header) punya background var(--blue-light)
   sendiri — status-paid/status-processed juga pakai --blue-light sebagai
   background pill-nya, jadi tanpa modifier ini keduanya menyatu tak terlihat
   di situ. Background putih solid + border memaksa kontras apa pun warna teks
   pill-nya, dipakai HANYA di konteks ini (bukan mengubah .status-pill dasar
   yang sudah kontras baik di background putih biasa). */
.status-pill-contrast {
    background: #fff !important;
    border: 1px solid rgba(13, 27, 76, 0.12);
    font-size: 12px;
    padding: 4px 10px;
}

.bar-chart { display: flex; align-items: flex-end; gap: 10px; height: 160px; padding: 10px 4px; }
.bar-chart .bar { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; }
.bar-chart .bar .fill { width: 100%; background: linear-gradient(180deg, var(--blue-accent), var(--navy)); border-radius: 4px 4px 0 0; }
.bar-chart .bar .fill-label { font-size: 10px; color: var(--text-muted); margin-top: 4px; }
.bar-chart .bar .bar-value { font-size: 10.5px; color: var(--navy); font-weight: 700; margin-bottom: 4px; }

/* ===== Dashboard Owner =====
   Aksen warna di sini punya arti tetap dan tidak dipakai dekoratif: hijau =
   uang masuk/sehat, kuning = menunggu tindakan, merah = perlu tindakan
   sekarang, biru/ungu = netral informatif. Tiap kartu memakai satu warna saja
   supaya halaman tetap terbaca, bukan jadi pelangi. */
.owner-hero {
    position: relative; overflow: hidden;
    background: linear-gradient(135deg, var(--navy), var(--blue-accent));
    border-radius: var(--radius); padding: 16px 16px 18px;
    margin-bottom: 14px; color: #fff;
}
.owner-hero-top { display: flex; justify-content: space-between; align-items: flex-start; gap: 8px; }
.owner-hero-greet { font-size: 12px; font-weight: 600; opacity: 0.75; }
.owner-hero-label { font-size: 11.5px; opacity: 0.85; margin-top: 2px; }
.owner-hero-badge {
    display: inline-flex; align-items: center; gap: 4px;
    background: rgba(255, 255, 255, 0.18); border-radius: 20px;
    padding: 4px 9px; font-size: 10.5px; font-weight: 700;
    flex-shrink: 0;
}
.owner-hero-badge svg { width: 12px; height: 12px; }
.owner-hero-value { font-size: 27px; font-weight: 800; margin-top: 8px; letter-spacing: -0.5px; }
.owner-hero-foot { display: flex; align-items: center; gap: 7px; margin-top: 7px; }
.owner-hero-foot-note { font-size: 11px; opacity: 0.8; }
/* Kurva omset jadi latar, bukan data yang dibaca persis — angka pastinya ada
   di grafik batang di bawah. Ditaruh di belakang teks lewat z-index negatif. */
.owner-hero-spark {
    position: absolute; left: 0; right: 0; bottom: 0;
    width: 100%; height: 62px; z-index: 0; pointer-events: none;
}
.owner-hero > *:not(.owner-hero-spark) { position: relative; z-index: 1; }

.trend {
    display: inline-flex; align-items: center; gap: 3px;
    font-size: 11px; font-weight: 800; border-radius: 20px; padding: 2px 7px;
    white-space: nowrap;
}
.trend-up { background: #e8f8f0; color: var(--success); }
.trend-down { background: #fdecea; color: var(--danger); }
.trend-flat { background: var(--bg-soft); color: var(--text-muted); }
/* Di atas gradien navy, pil putih-transparan kontrasnya lebih stabil daripada
   pastel terang yang hampir hilang. */
.trend-on-dark, .trend-on-dark.trend-up, .trend-on-dark.trend-down, .trend-on-dark.trend-flat {
    background: rgba(255, 255, 255, 0.2); color: #fff;
}

.alert-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-bottom: 16px; }
/* Kartu "butuh tindakan" (dashboard owner) — putih->abu (bukan navy solid
   lagi), permintaan user 2026-09-12, menyusul .admin-sidebar/.panel-header-
   section. Border ditambah (dulu 'none') karena kartu ini duduk langsung di
   atas .admin-shell yang JUGA putih — tanpa border, tepi kartu akan hilang
   sama sekali (sama jebakan seperti .search-bar sebelumnya).
   Tiap tone (warning/info/danger/ok) TETAP seragam satu warna — itu
   keputusan desain 2026-09-10 yang berdiri sendiri (bukan cuma akal-akalan
   kontras di atas navy), jadi tidak diutak-atik lagi di sini. */
.alert-card {
    display: flex; flex-direction: column; align-items: center; gap: 2px;
    padding: 11px 6px; border-radius: var(--radius);
    background: linear-gradient(180deg, #ffffff 0%, var(--border) 100%);
    border: 1px solid var(--border); text-align: center;
    color: var(--navy); text-decoration: none;
}
.alert-card .alert-ic { display: flex; color: var(--navy); }
.alert-card .alert-ic svg { width: 17px; height: 17px; }
.alert-num { font-size: 19px; font-weight: 800; line-height: 1.15; color: var(--navy); }
.alert-label { font-size: 10px; color: var(--text-muted); font-weight: 600; line-height: 1.25; }

/* Kartu KPI (Pengunjung/Total Pesanan/dst) — putih->abu sama seperti di
   atas. Border sudah ikut dari .stat-card (base class-nya, lihat markup
   "stat-card stat-card-rich"), tidak perlu ditambah manual di sini.
   Warna ikon per-tone (green/blue/purple/amber) TETAP seragam — sama
   alasan seperti .alert-card di atas, keputusan desain berdiri sendiri. */
.stat-card-rich { background: linear-gradient(180deg, #ffffff 0%, var(--border) 100%); }
.stat-card-rich .stat-card-top {
    display: flex; justify-content: space-between; align-items: center;
    margin-bottom: 9px;
}
.stat-ic {
    display: inline-flex; align-items: center; justify-content: center;
    width: 30px; height: 30px; border-radius: 9px; flex-shrink: 0;
}
.stat-ic svg { width: 16px; height: 16px; }
.stat-ic-green, .stat-ic-blue, .stat-ic-purple, .stat-ic-amber {
    background: var(--blue-light); color: var(--blue-accent);
}
/* .trend-on-dark (dipakai juga di .owner-hero yang TETAP navy — TIDAK
   disentuh di atas) sengaja menyeragamkan warna pil jadi putih-transparan
   MURNI karena alasan kontras di atas navy (lihat komentar aslinya di
   .trend-on-dark) — bukan keputusan desain berdiri sendiri seperti 2 kasus
   di atas. Sekarang kartu KPI ini terang lagi, alasan kontras itu sudah
   tidak berlaku, jadi dikembalikan ke warna asli per-arah (.trend-up/
   .trend-down/.trend-flat) supaya panah naik/turun kembali informatif —
   selector 2-kelas di sini sengaja lebih spesifik drpd `.trend-on-dark`
   supaya menang tanpa perlu mengubah urutan rule generiknya (dipakai
   .owner-hero juga). */
.stat-card-rich .trend-up.trend-on-dark { background: #e8f8f0; color: var(--success); }
.stat-card-rich .trend-down.trend-on-dark { background: #fdecea; color: var(--danger); }
.stat-card-rich .trend-flat.trend-on-dark { background: var(--bg-soft); color: var(--text-muted); }
.stat-card-rich .label { margin-bottom: 3px; font-size: 11.5px; color: var(--text-muted); }
.stat-card-rich .value { font-size: 17.5px; letter-spacing: -0.3px; color: var(--navy); }

.visitor-row { display: grid; grid-template-columns: repeat(3, 1fr); }
.visitor-cell { padding: 14px 8px; text-align: center; }
.visitor-cell + .visitor-cell { border-left: 1px solid var(--border); }
.visitor-num { font-size: 19px; font-weight: 800; color: var(--navy); letter-spacing: -0.3px; }
/* Konversi diberi warna karena itu satu-satunya angka di baris ini yang
   menggabungkan trafik dengan penjualan. */
.visitor-num-accent { color: var(--success); }
.visitor-label { font-size: 10px; color: var(--text-muted); font-weight: 600; margin-top: 3px; line-height: 1.3; }
.visitor-foot {
    padding: 9px 16px; border-top: 1px solid var(--border);
    background: var(--bg-soft); font-size: 10.5px; color: var(--text-muted); line-height: 1.4;
}

.panel-header-note { font-size: 11px; color: var(--text-muted); font-weight: 600; }
.panel-header-link { font-size: 12px; color: var(--blue-accent); font-weight: 600; text-decoration: none; }

/* Batang bulan tertinggi diberi warna penuh, sisanya diredam — mata langsung
   menemukan puncaknya tanpa harus membandingkan enam tinggi yang mirip. */
.bar-chart-rich { height: 168px; padding: 16px 10px 10px; }
/* .bar tidak punya tinggi eksplisit, jadi height:<persen> pada .fill tidak
   punya acuan dan runtuh jadi nol — batangnya tidak pernah terlihat. Kolomnya
   diberi tinggi penuh, dan label dikunci agar tidak ikut menyusut supaya
   persentase .fill dihitung terhadap ruang yang benar-benar ada. */
.bar-chart-rich .bar { height: 100%; }
.bar-chart-rich .bar .bar-value,
.bar-chart-rich .bar .fill-label { flex-shrink: 0; }
.bar-chart-rich .bar .fill {
    background: linear-gradient(180deg, #b9ccf7, #93b0ef);
    border-radius: 5px 5px 0 0; max-width: 34px;
    min-height: 3px;
}
.bar-chart-rich .bar.is-peak .fill { background: linear-gradient(180deg, var(--blue-accent), var(--navy)); }
.bar-chart-rich .bar.is-peak .bar-value { color: var(--blue-accent); }
.bar-chart-rich .bar .bar-value { font-size: 9.5px; color: var(--text-muted); }
.bar-chart-rich .bar .fill-label { font-weight: 600; }

.rank-list { padding: 4px 0; }
.rank-row { display: flex; align-items: center; gap: 10px; padding: 9px 16px; }
.rank-row + .rank-row { border-top: 1px solid var(--border); }
.rank-num {
    display: flex; align-items: center; justify-content: center;
    width: 22px; height: 22px; border-radius: 7px; flex-shrink: 0;
    font-size: 11px; font-weight: 800;
    background: var(--bg-soft); color: var(--text-muted);
}
/* Tiga besar diberi warna podium; peringkat 4-5 tetap netral supaya kontras
   "terlaris" itu terlihat. */
.rank-num-1 { background: #fff4e0; color: #d99413; }
.rank-num-2 { background: #eef1f6; color: #7c8798; }
.rank-num-3 { background: #fbeee3; color: #b3703a; }
.rank-body { flex: 1; min-width: 0; }
.rank-name {
    font-size: 12.5px; font-weight: 600; color: var(--text);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.rank-meter { height: 4px; border-radius: 3px; background: var(--bg-soft); margin-top: 5px; overflow: hidden; }
.rank-meter span { display: block; height: 100%; border-radius: 3px; background: linear-gradient(90deg, var(--blue-accent), var(--navy)); }
.rank-stats { text-align: right; flex-shrink: 0; }
.rank-sold { font-size: 12px; font-weight: 800; color: var(--navy); }
.rank-price { font-size: 10.5px; color: var(--text-muted); margin-top: 1px; }
.rank-empty { padding: 18px 16px; text-align: center; font-size: 12px; color: var(--text-muted); }

/* Aliran aktivitas. Garis vertikal tipis di belakang ikon mengikat baris-baris
   jadi satu lini waktu, bukan sekadar daftar terpisah. */
.feed { padding: 4px 0; position: relative; }
.feed-row { display: flex; gap: 10px; padding: 10px 16px; position: relative; }
.feed-row + .feed-row { border-top: 1px solid var(--border); }
.feed-ic {
    display: inline-flex; align-items: center; justify-content: center;
    width: 28px; height: 28px; border-radius: 50%; flex-shrink: 0;
}
.feed-ic svg { width: 14px; height: 14px; }
.feed-ic-green { background: #e8f8f0; color: var(--success); }
.feed-ic-blue { background: var(--blue-light); color: var(--blue-accent); }
.feed-ic-purple { background: #f0ecfd; color: #6b4ede; }
.feed-ic-amber { background: #fff4e0; color: var(--warning); }
.feed-ic-slate { background: var(--bg-soft); color: var(--text-muted); }
.feed-body { flex: 1; min-width: 0; }
.feed-text { font-size: 12.5px; line-height: 1.45; color: var(--text); }
.feed-meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 3px; }
.feed-actor { font-size: 10.5px; font-weight: 700; color: var(--navy); }
.feed-role {
    font-size: 9.5px; font-weight: 700; padding: 1px 6px; border-radius: 20px;
    text-transform: uppercase; letter-spacing: 0.3px;
}
/* Peran dibedakan warnanya supaya "pembeli" tidak tertukar dengan "staf"
   sekilas — itu perbedaan paling penting di aliran ini. */
.feed-role-owner { background: #f0ecfd; color: #6b4ede; }
.feed-role-admin { background: var(--blue-light); color: var(--blue-accent); }
.feed-role-customer { background: #e8f8f0; color: var(--success); }
.feed-time { font-size: 10.5px; color: var(--text-muted); margin-left: auto; }

.tool-grid { padding: 6px 0; }
.tool-item { display: flex; align-items: center; gap: 10px; padding: 11px 16px; color: var(--text); text-decoration: none; }
.tool-item + .tool-item { border-top: 1px solid var(--border); }
.tool-ic {
    display: inline-flex; align-items: center; justify-content: center;
    width: 30px; height: 30px; border-radius: 9px; flex-shrink: 0;
}
.tool-ic svg { width: 16px; height: 16px; }
.tool-ic-blue { background: var(--blue-light); color: var(--blue-accent); }
.tool-ic-purple { background: #f0ecfd; color: #6b4ede; }
.tool-ic-amber { background: #fff4e0; color: var(--warning); }
.tool-text { font-size: 13px; font-weight: 600; flex: 1; }
/* Chevron dibuat dari border, bukan ikon tambahan — murni dekoratif. */
.tool-item::after {
    content: ''; width: 7px; height: 7px; flex-shrink: 0;
    border-right: 2px solid var(--text-muted); border-bottom: 2px solid var(--text-muted);
    transform: rotate(-45deg); opacity: 0.5;
}

.form-card { border: 1px solid var(--border); border-radius: var(--radius); padding: 18px; max-width: 560px; }
/* Form yang perlu mengisi lebar penuh kolom konten (mis. Kelola Status di
   detail pesanan admin) — card lain di halaman yang sama tidak dibatasi
   560px, jadi form ini terlihat sempit sebelah kalau ikut default. */
.form-card-full { max-width: none; }

/* ===== Live Shopping ===== */
.live-video-wrap { position: relative; background: #000; }
.live-video-bg { width: 100%; height: 260px; object-fit: cover; opacity: 0.85; }
.live-badge-live {
    position: absolute; top: 12px; left: 12px;
    background: #ff3b3b; color: #fff; font-size: 11px; font-weight: 800;
    padding: 4px 9px; border-radius: 5px; display: flex; align-items: center; gap: 5px;
}
.live-badge-live .dot { width: 6px; height: 6px; border-radius: 50%; background: #fff; animation: live-pulse 1.2s infinite; }
@keyframes live-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }
.live-badge-viewers {
    position: absolute; top: 12px; right: 12px;
    background: rgba(0,0,0,0.55); color: #fff; font-size: 11px; font-weight: 700;
    padding: 4px 9px; border-radius: 5px; display: flex; align-items: center; gap: 4px;
}
.live-host-bar {
    position: absolute; left: 12px; right: 12px; bottom: 12px;
    background: rgba(8,18,53,0.55); backdrop-filter: blur(2px);
    border-radius: 10px; padding: 8px 10px;
    display: flex; align-items: center; gap: 10px;
}
.live-host-avatar {
    width: 34px; height: 34px; border-radius: 50%; background: var(--navy); color: #fff;
    display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.live-host-info { flex: 1; min-width: 0; }
.live-host-info .name { color: #fff; font-size: 12.5px; font-weight: 700; }
.live-host-info .sub { color: #d6def5; font-size: 10.5px; }

.live-product-strip { display: flex; gap: 10px; overflow-x: auto; }
.live-product-card {
    flex: 0 0 120px; position: relative;
    border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; background: #fff;
}
.live-product-card img { width: 100%; aspect-ratio: 1/1; object-fit: cover; }
.live-product-info { padding: 6px 8px 8px; }
.live-product-info .title { font-size: 11px; height: 28px; overflow: hidden; margin-bottom: 3px; }
.live-product-info .price-now { font-size: 12.5px; }
.live-add-btn {
    position: absolute; top: 6px; right: 6px;
    width: 22px; height: 22px; border-radius: 50%; border: none;
    background: var(--navy); color: #fff; font-size: 15px; line-height: 1; font-weight: 700;
}

.live-chat-wrap { margin: 0 14px 16px; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.live-chat-feed { max-height: 160px; overflow-y: auto; padding: 10px 12px; display: flex; flex-direction: column; gap: 6px; background: var(--bg-soft); }
.live-chat-msg { font-size: 12px; color: var(--text); }
.live-chat-msg b { color: var(--navy); }
.live-chat-input { display: flex; border-top: 1px solid var(--border); background: #fff; }
.live-chat-input input { flex: 1; border: none; outline: none; padding: 10px 12px; font-size: 12.5px; min-width: 0; }
.live-chat-input button { background: none; border: none; color: var(--navy); padding: 0 14px; }

/* ===== Footer ===== */
/* padding-bottom 90px sengaja besar — reserve ruang dari .bottom-nav yang
   position:fixed. Footer sekarang jauh lebih tinggi (info toko + medsos +
   link kebijakan) — diverifikasi visual (Playwright) bahwa 90px tetap
   cukup karena ruang itu di BAWAH seluruh konten footer, bukan di antara
   section-section-nya. */
.site-footer { padding: 20px 14px 90px; text-align: center; font-size: 11px; color: var(--text-muted); }
.site-footer-tagline { font-size: 12px; font-weight: 700; color: var(--navy); margin-bottom: 6px; }
.site-footer-section { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border); }
/* Feedback user 2026-09-12: garis pemisah persis di bawah tagline (sebelum
   info alamat/jam) dihapus — tagline & info toko dianggap satu blok,
   pemisah antar-section LAIN (WA+medsos, link kebijakan, copyright) tetap
   dipertahankan. */
.site-footer-tagline + .site-footer-section { border-top: none; padding-top: 0; margin-top: 10px; }
/* .ico global-nya display:block (biar SVG rapi berdiri sendiri di tombol
   ikon) — dipakai di mana pun ikon+teks perlu sebaris (bukan pecah baris di
   atasnya): footer info toko, link WA/medsos di halaman Tentang Kami, dst. */
.inline-icon-text { display: inline-flex; align-items: center; gap: 5px; }
.site-footer-section-title { font-size: 10.5px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--navy); margin-bottom: 6px; }
.site-footer-links { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px 14px; }
.site-footer-links a { color: var(--text-muted); text-decoration: underline; }
/* Feedback user 2026-09-12: garis pemisah sebelum baris ikon WA/medsos juga
   dihapus — cuma spasi (margin-top bawaan .site-footer-section), tanpa
   garis. */
.site-footer-social { display: flex; justify-content: center; gap: 14px; border-top: none; padding-top: 0; }
.site-footer-social a { color: var(--navy); display: inline-flex; }
.site-footer-copyright { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border); }

/* ===== Kartu Langkah (Kebijakan Retur & Pembatalan) =====
   Sengaja BUKAN gaya gradasi dashboard (.stat-card-rich/.alert-card, dipakai
   panel admin/owner) — flat putih + aksen biru muda, senada elemen
   customer lain yang sudah ada (mis. .badge-new/.price-now). */
.retur-step-list { list-style: none; margin: 0 0 20px; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.retur-step-card { display: flex; gap: 12px; align-items: flex-start; background: #fff; border: 1px solid var(--border); border-radius: var(--radius); padding: 12px 14px; }
.retur-step-num { flex-shrink: 0; width: 28px; height: 28px; border-radius: 50%; background: var(--blue-light); color: var(--navy); font-weight: 800; font-size: 13px; display: flex; align-items: center; justify-content: center; }
.retur-step-body { flex: 1; min-width: 0; }
.retur-step-title { font-weight: 700; font-size: 13px; color: var(--navy); margin-bottom: 2px; display: flex; align-items: center; gap: 6px; }
.retur-step-title .ic { display: flex; color: var(--blue-accent); flex-shrink: 0; }
.retur-step-desc { font-size: 12px; color: var(--text-muted); line-height: 1.45; }

/* ===== Halaman Kebijakan (S&K/Privasi/Retur/FAQ/Tentang Kami/hub) ===== */
.policy-hub-grid { display: flex; flex-direction: column; gap: 10px; }
.policy-hub-card { display: flex; align-items: center; gap: 12px; background: #fff; border: 1px solid var(--border); border-radius: var(--radius); padding: 14px; text-decoration: none; }
.policy-hub-card .ic { flex-shrink: 0; width: 40px; height: 40px; border-radius: 10px; background: var(--blue-light); color: var(--navy); display: flex; align-items: center; justify-content: center; }
.policy-hub-card-title { font-weight: 700; font-size: 13.5px; color: var(--navy); }
.policy-hub-card-desc { font-size: 11.5px; color: var(--text-muted); margin-top: 2px; }
.policy-section { margin-bottom: 18px; }
.policy-section h2 { font-size: 14.5px; color: var(--navy); margin: 0 0 8px; }
.policy-section p, .policy-section li { font-size: 12.5px; color: var(--text); line-height: 1.6; }
.policy-section ul, .policy-section ol { padding-left: 20px; margin: 0 0 8px; }
.policy-draft-note { margin-top: 16px; padding: 12px 14px; background: var(--bg-soft); border-radius: var(--radius); font-size: 11.5px; color: var(--text-muted); font-style: italic; }
.policy-checklist { list-style: none; margin: 0 0 8px; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.policy-checklist li { display: flex; align-items: flex-start; gap: 8px; }
.policy-checklist .ico { flex-shrink: 0; color: var(--blue-accent); margin-top: 1px; }
.faq-item { border-bottom: 1px solid var(--border); padding: 12px 0; }
.faq-item:last-child { border-bottom: none; }
.faq-q { font-weight: 700; font-size: 13px; color: var(--navy); margin-bottom: 4px; }
.faq-a { font-size: 12.5px; color: var(--text-muted); line-height: 1.5; }

/* Empty state */
.empty-state { text-align: center; padding: 60px 20px; color: var(--text-muted); }
.empty-state .icon { display: flex; justify-content: center; margin-bottom: 10px; }

/* ===== Product Reviews ===== */
/* Carousel "Beri Ulasan" — dulu semua kartu ditumpuk vertikal (bisa panjang
   banget kalau produk yang menunggu diulas banyak), sekarang digeser
   horizontal senapas .hero-carousel (scroll-snap + auto-advance JS di
   main.js), supaya tetap ringkas satu baris tinggi berapa pun jumlah
   produknya. Lebar kartu sengaja 88% (bukan 100%) — sisa 12% jadi "intip"
   kartu berikutnya, isyarat visual bahwa ini bisa digeser tanpa perlu teks
   penjelas. */
.review-carousel-wrap { position: relative; }
.review-carousel {
    position: relative; /* jadi offsetParent .review-slide, supaya offsetLeft-nya = target scrollLeft yang tepat di main.js */
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    gap: 10px;
    padding: 0 14px 4px;
    margin: 0 -14px;
}
.review-carousel::-webkit-scrollbar { display: none; }
.review-slide { flex: 0 0 88%; scroll-snap-align: start; }
.review-dots { display: flex; justify-content: center; gap: 6px; padding-top: 6px; }
.review-dots .dot {
    width: 6px; height: 6px; border-radius: 50%;
    background: var(--border);
    cursor: pointer;
    transition: width 0.2s, border-radius 0.2s, background 0.2s;
}
.review-dots .dot.active { background: var(--navy); width: 16px; border-radius: 3px; }

.star-input { display: flex; gap: 2px; }
.star-input .star-btn {
    background: none; border: none; padding: 0 1px; cursor: pointer;
    font-size: 22px; line-height: 1; color: #d5d9e3;
}
.star-input .star-btn.on { color: var(--warning); }
.review-list { padding: 0 14px; }
.review-item { padding: 12px 0; border-bottom: 1px solid var(--border); }
.review-item:last-child { border-bottom: none; }
.review-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; }
.review-author { font-size: 12.5px; font-weight: 700; color: var(--navy); }
.review-stars { color: var(--warning); font-size: 13px; letter-spacing: 1px; }
.review-date { font-size: 10.5px; color: var(--text-muted); }
.review-comment { font-size: 12.5px; color: var(--text); line-height: 1.5; }
.review-reply {
    font-size: 12px; color: var(--text); line-height: 1.5; margin-top: 8px;
    background: var(--bg-soft); border-radius: 8px; padding: 8px 10px;
}
.review-summary {
    display: flex; align-items: center; gap: 10px; padding: 12px 14px;
    border-bottom: 8px solid var(--bg-soft);
}
.review-summary .avg { font-size: 26px; font-weight: 800; color: var(--navy); }
.review-summary .avg-stars { color: var(--warning); font-size: 14px; }
.review-summary .avg-count { font-size: 11.5px; color: var(--text-muted); }
.review-empty { padding: 16px 14px; font-size: 12.5px; color: var(--text-muted); border-bottom: 8px solid var(--bg-soft); }

/* Produk yang belum cukup diulas: tampil netral, bukan angka bintang yang
   belum layak dipercaya (lihat Product::MIN_REVIEWS_FOR_RATING). */
.review-summary .avg.avg-belum { color: var(--text-muted); font-weight: 700; }
.review-summary .avg-stars.avg-belum { color: var(--text-muted); font-size: 12.5px; font-weight: 600; }
.rating.rating-baru { color: var(--text-muted); font-weight: 500; }
.review-pagination { padding: 12px 14px; display: flex; justify-content: center; }
.review-pagination svg { width: 14px; height: 14px; display: inline-block; }

/* ===== Halaman Promo ===== */
.promo-hero { position: relative; overflow: hidden; min-height: 190px; display: flex; align-items: flex-end; }
.promo-hero-bg {
    position: absolute; inset: 0;
    background-size: cover; background-position: center;
    transform: scale(1.05);
}
.promo-hero::after {
    content: ''; position: absolute; inset: 0;
    background: linear-gradient(180deg, rgba(13,27,76,.25) 0%, rgba(13,27,76,.88) 100%);
}
/* Banner gambar tanpa overlay teks (lihat $hasBannerText di promo.blade.php)
   — gradient di atas cuma ada supaya teks putih kebaca, jadi harus mati
   kalau tidak ada teks sama sekali, biar gambarnya tampil bersih. */
.promo-hero--plain::after { display: none; }
.promo-hero-inner { position: relative; z-index: 1; padding: 20px 16px 18px; color: #fff; }
.promo-hero-kicker {
    display: inline-block; font-size: 11px; font-weight: 700; letter-spacing: .08em;
    text-transform: uppercase; padding: 4px 10px; border-radius: 99px;
    background: linear-gradient(135deg, #ff5f6d, #ffa751); margin-bottom: 8px;
}
.promo-hero-title { font-size: 24px; font-weight: 800; line-height: 1.2; margin: 0 0 6px; }
.promo-hero-sub { font-size: 12.5px; opacity: .9; margin: 0; line-height: 1.55; }

.promo-card { padding-bottom: 4px; }
.promo-countdown { font-size: 12px; color: var(--text-muted); }
.promo-countdown b {
    background: var(--navy); color: #fff; padding: 2px 5px;
    border-radius: 4px; font-size: 11.5px; margin: 0 1px;
}

/* Kartu voucher — bentuk tiket dengan takik bulat di sisi kiri, seperti tiket sungguhan */
.voucher-card {
    display: flex; align-items: stretch; gap: 0;
    background: linear-gradient(135deg, #ff8a65, #ff5f6d);
    border-radius: 14px; margin-bottom: 12px; position: relative; overflow: hidden;
    box-shadow: 0 4px 14px rgba(255, 95, 109, .25);
}
.voucher-card.voucher-type-free_shipping {
    background: linear-gradient(135deg, #34c9c1, #1e9bd7);
    box-shadow: 0 4px 14px rgba(30, 155, 215, .25);
}
.voucher-card::before,
.voucher-card::after {
    content: ''; position: absolute; width: 18px; height: 18px; border-radius: 50%;
    background: var(--bg, #f4f6fb); left: 96px; z-index: 2;
}
.voucher-card::before { top: -9px; }
.voucher-card::after { bottom: -9px; }
.voucher-left {
    width: 96px; flex-shrink: 0; display: flex; flex-direction: column;
    align-items: center; justify-content: center; text-align: center;
    padding: 16px 8px; position: relative; color: #fff;
}
.voucher-left::after {
    content: ''; position: absolute; right: 0; top: 14px; bottom: 14px;
    border-left: 1.5px dashed rgba(255,255,255,.55);
}
.voucher-icon { margin-bottom: 4px; opacity: .95; }
.voucher-value { font-size: 19px; font-weight: 800; color: #fff; line-height: 1.15; }
.voucher-code {
    font-size: 10px; color: rgba(255,255,255,.85); margin-top: 4px;
    font-family: ui-monospace, monospace; letter-spacing: .04em;
    background: rgba(255,255,255,.18); padding: 2px 6px; border-radius: 4px;
}
.voucher-right {
    flex: 1; padding: 12px 14px 12px 16px; display: flex; align-items: center;
    justify-content: space-between; gap: 10px;
    background: #fff; border-radius: 0 14px 14px 0;
}
.voucher-detail { font-size: 12.5px; color: var(--text-muted); line-height: 1.5; }
.voucher-claimed-tag {
    display: inline-flex; align-items: center; gap: 4px; flex-shrink: 0;
    font-size: 11.5px; font-weight: 600; color: var(--success, #17916b);
}
.voucher-card.is-claimed { opacity: .6; }
.voucher-card.is-claimed .voucher-right { background: #f7fbf9; }

/* Pilihan voucher di halaman checkout — mini-tiket 1 baris per voucher,
   takik kecil di kiri jadi penanda tipe (bukan kartu besar seperti di Promo). */
.voucher-pick {
    display: flex; align-items: center; gap: 8px; padding: 9px 12px 9px 10px;
    border: 1px solid var(--border); border-radius: 8px; margin-bottom: 6px;
    font-size: 12.5px; cursor: pointer; position: relative; overflow: hidden;
    white-space: nowrap;
}
.voucher-pick::before {
    content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 4px;
    background: var(--border);
}
.voucher-pick[data-type="percent"]::before,
.voucher-pick[data-type="fixed"]::before { background: linear-gradient(180deg, #ff8a65, #ff5f6d); }
.voucher-pick[data-type="free_shipping"]::before { background: linear-gradient(180deg, #34c9c1, #1e9bd7); }
.voucher-pick .voucher-pick-icon { flex-shrink: 0; display: flex; color: var(--text-muted); }
.voucher-pick[data-type] .voucher-pick-icon { color: var(--navy); }
.voucher-pick span:not(.potongan):not(.voucher-pick-icon) {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}
.voucher-pick.selected { border-color: var(--navy); background: #f5f8ff; }
.voucher-pick .potongan { margin-left: auto; flex-shrink: 0; font-weight: 700; color: #ff5f6d; font-size: 12px; }
/* Voucher gratis ongkir dikunci saat mode Ambil di Toko aktif (permintaan
   user 2026-09-13: tidak ada ongkir sama sekali di jalur itu) — lihat
   applyFulfillmentMode() di checkout.blade.php. */
.voucher-pick:has(input:disabled) { cursor: not-allowed; opacity: 0.55; }

/* ===== Halaman "Voucher Saya" ===== */
.voucher-group-title {
    font-size: 12.5px; color: var(--text-muted); font-weight: 700;
    margin: 16px 0 8px; text-transform: uppercase; letter-spacing: .04em;
}
/* Voucher terpakai / hangus tetap ditampilkan (pembeli berhak tahu riwayatnya)
   tapi diredupkan supaya tidak tertukar dengan yang masih bisa dipakai. */
.voucher-card.is-mati { opacity: .55; background: var(--bg-soft); }
.voucher-status { flex-shrink: 0; text-align: right; }
.voucher-expiry { font-size: 11px; color: var(--text-muted); white-space: nowrap; }
.voucher-expiry.segera { color: var(--danger); font-weight: 700; }

/* ===== Loading overlay — dipasang selama proses penting yang butuh jeda
   server (submit pesanan, ganti metode bayar, dll) supaya pembeli tidak
   bisa klik ulang / navigasi sebelum prosesnya selesai. Dipakai lewat
   window.showPageLoading()/hidePageLoading() di main.js. ===== */
#pageLoadingOverlay {
    position: fixed; inset: 0; background: rgba(255, 255, 255, .9); z-index: 2000;
    display: none; align-items: center; justify-content: center; flex-direction: column;
    gap: 14px;
}
#pageLoadingOverlay.show { display: flex; }
#pageLoadingOverlay .spinner {
    width: 40px; height: 40px; border-radius: 50%;
    border: 3.5px solid var(--border); border-top-color: var(--navy);
    animation: pageLoadingSpin .7s linear infinite;
}
#pageLoadingOverlay .msg { color: var(--navy); font-size: 13px; font-weight: 600; }
@keyframes pageLoadingSpin { to { transform: rotate(360deg); } }

/* ===== Toast — pengganti window.alert() bawaan browser =====
   Non-blocking, tampil di atas bottom-nav, hilang sendiri. Lebar mengikuti
   .app-shell di halaman customer; di panel admin (lebih lebar) jadi kartu
   kecil di pojok kanan-bawah supaya tidak menutupi tabel. */
#toastContainer {
    position: fixed; left: 50%; bottom: 84px; transform: translateX(-50%);
    z-index: 999; width: calc(100% - 32px); max-width: calc(var(--max-width) - 32px);
    display: flex; flex-direction: column; gap: 8px; align-items: center;
    pointer-events: none;
}
.admin-shell #toastContainer {
    left: auto; right: 24px; bottom: 84px; transform: none;
    width: auto; max-width: 320px; align-items: stretch;
}
.toast {
    pointer-events: auto; display: flex; align-items: center; gap: 8px;
    background: var(--navy); color: #fff; padding: 11px 16px; border-radius: 10px;
    font-size: 13px; font-weight: 500; line-height: 1.4;
    box-shadow: 0 8px 24px rgba(13, 27, 76, .25);
    opacity: 0; transform: translateY(10px); transition: opacity .2s ease, transform .2s ease;
    cursor: pointer;
}
.toast.show { opacity: 1; transform: translateY(0); }
/* Riwayat: hijau var(--success) solid -> putih+outline biru ("aneh juga")
   -> balik filled hijau lagi, TAPI disamakan persis dengan warna hijau yang
   sudah dipakai .alert-success (var(--success), #1aa260) — permintaan user:
   "hijaunya jangan hijau tua, samain kaya warna hijau flash message itu".
   var(--success) itu sendiri sudah bukan hijau tua (dipakai sebagai warna
   TEKS di .alert-success, medium green, bukan gelap) — jadi kembali ke nilai
   yang sama, bukan warna hijau baru. */
.toast-success { background: var(--success); color: #fff; }
.toast-error { background: var(--danger); }

/* ===== Modal konfirmasi — pengganti window.confirm() bawaan browser ===== */
.confirm-overlay {
    position: fixed; inset: 0; background: rgba(13, 27, 76, .45); z-index: 1000;
    display: flex; align-items: center; justify-content: center; padding: 24px;
    opacity: 0; pointer-events: none; transition: opacity .15s ease;
}
.confirm-overlay.show { opacity: 1; pointer-events: auto; }
.confirm-dialog {
    background: #fff; border-radius: var(--radius); padding: 20px;
    width: 100%; max-width: 300px; text-align: center;
    transform: scale(.96); transition: transform .15s ease;
    box-shadow: 0 20px 50px rgba(13, 27, 76, .3);
}
.confirm-overlay.show .confirm-dialog { transform: scale(1); }
.confirm-dialog p { font-size: 13.5px; color: var(--text); margin-bottom: 16px; line-height: 1.5; }
.confirm-dialog .confirm-actions { display: flex; gap: 8px; }
.confirm-dialog .confirm-actions button {
    flex: 1; padding: 10px; border-radius: 8px; font-size: 13px; font-weight: 700; border: none;
}
.confirm-dialog .confirm-cancel { background: var(--bg-soft); color: var(--text); }
.confirm-dialog .confirm-ok { background: var(--danger); color: #fff; }

/* ===== Form modal — popup untuk form admin (mis. "Buat Voucher") =====
   Sama pola dengan .confirm-overlay (backdrop + kartu tengah), tapi lebih
   lebar dan bodinya bisa scroll sendiri karena isinya form panjang, bukan
   cuma satu kalimat konfirmasi. display:none dulu di CSS (bukan cuma
   opacity), supaya form di dalamnya tidak ikut ke-tab/terbaca screen reader
   selagi popup tertutup. */
.form-modal-overlay {
    display: none; position: fixed; inset: 0; background: rgba(13, 27, 76, .45);
    z-index: 1000; align-items: center; justify-content: center; padding: 24px;
}
.form-modal-overlay.show { display: flex; }
.form-modal {
    background: #fff; border-radius: var(--radius); width: 100%; max-width: 560px;
    max-height: 90vh; display: flex; flex-direction: column;
    box-shadow: 0 20px 50px rgba(13, 27, 76, .3);
}
.form-modal-header {
    display: flex; align-items: center; justify-content: space-between;
    padding: 14px 18px; border-bottom: 1px solid var(--border);
    font-weight: 700; color: var(--navy); font-size: 14px; flex-shrink: 0;
}
.form-modal-close {
    background: none; border: none; font-size: 22px; line-height: 1; color: var(--text-muted);
    cursor: pointer; padding: 0; width: 28px; height: 28px;
}
.form-modal-body { padding: 18px; overflow-y: auto; }
.form-modal-body .form-card { padding: 0; border: none; max-width: none; }

/* ===== Popup "ulas sekarang" sesudah Pesanan Diterima =====
   Dibangun lewat window.openReviewPopup() di main.js, memakai wadah
   .form-modal-overlay/.form-modal yang sama dengan modal-modal lain (Tolak
   Pesanan dkk) — cuma isi body-nya beda, satu kartu ringkas per produk. */
.review-popup-subtitle { font-size: 12px; color: var(--text-muted); margin: 0 0 14px; line-height: 1.5; }
.review-popup-card { border: 1px solid var(--border); border-radius: 10px; padding: 12px; margin-bottom: 10px; }
.review-popup-card:last-of-type { margin-bottom: 0; }
.review-popup-card-head { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.review-popup-card-head img { width: 36px; height: 36px; border-radius: 6px; object-fit: cover; border: 1px solid var(--border); flex-shrink: 0; }
.review-popup-card-name { font-size: 12.5px; font-weight: 600; }
.review-popup-textarea {
    width: 100%; margin-top: 8px; padding: 8px 10px; border: 1px solid var(--border);
    border-radius: 8px; font-size: 12.5px; font-family: inherit; resize: none;
}
.review-popup-error { font-size: 11.5px; color: var(--danger); margin-top: 6px; }

/* ===== Category tree — grup teratas + kategori anak, sebagai kartu ringkas
   bukan form always-expanded. Baris anak diberi indentasi + karakter "└─"
   supaya hierarkinya kelihatan sekilas, bukan cuma dari urutan tampil. ===== */
.category-tree-card {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
}
.category-tree-row {
    display: flex; align-items: center; justify-content: space-between;
    gap: 10px; padding: 10px 14px; flex-wrap: wrap;
    border-top: 1px solid var(--border);
}
.category-tree-row-root {
    border-top: none;
    background: var(--bg-soft);
}
.category-tree-row-child { padding-left: 30px; }
.category-tree-label {
    display: flex; align-items: center; gap: 8px;
    font-size: 13.5px; color: var(--text); min-width: 0;
}
.category-tree-row-root .category-tree-label { font-weight: 700; color: var(--navy); }
.category-tree-branch { color: var(--text-muted); font-size: 13px; flex-shrink: 0; }
/* Ikon galeri terpilih di daftar Etalase (permintaan user 2026-09-13: "gak
   keliatan mana icon selectednya, harusnya muncul juga di tree itu"). */
.category-tree-label svg { flex-shrink: 0; }
.category-tree-actions { display: flex; gap: 6px; flex-shrink: 0; }
.category-tree-empty {
    color: var(--text-muted); font-size: 12.5px; font-style: italic;
    display: flex; align-items: center; gap: 8px;
}

/* ---- Aset merek (logo) ---- */
/* Halaman auth berlatar gradient terang (2026-09-15, dulu navy gelap).
   Dipakai monogram, BUKAN lockup penuh — tulisan "SUKSES" pada lockup
   berwarna navy tetap kontras di latar terang ini juga, tapi monogram
   dipertahankan supaya konsisten dgn ukuran/komposisi yang sudah ada.
   Nama toko sekarang teks navy tua (lihat .auth-logo .brand). */
.auth-logo-mark {
    display: block;
    width: 96px;
    height: 96px;
    object-fit: contain;
    margin: 0 auto 10px;
    filter: drop-shadow(0 6px 18px rgba(20, 40, 80, 0.2));
}
/* Panel admin: lambang kecil di samping nama toko. */
.admin-brand-row { display: flex; align-items: center; gap: 10px; min-width: 0; }
.admin-brand-mark { width: 34px; height: 34px; flex-shrink: 0; object-fit: contain; display: block; }

/* ── Sidebar panel admin ─────────────────────────────────────────────────────
   Navigasi tunggal panel owner/admin — tidak ada bottom-nav di sisi ini.
   Desktop (≥1024px): menempel permanen di kiri, selalu terlihat, seperti
   panel admin web pada umumnya. Layar sempit: menciut jadi drawer yang
   digeser dari kiri (pola & durasi transisi sama dengan .filter-panel sisi
   pembeli), supaya panel tetap bisa dipakai dari HP.

   Warnanya navy penuh — sewarna topbar — sehingga keduanya membentuk satu
   bidang "L" yang menyambung, bukan dua kotak berbeda yang dipisah garis.
   Karena itu TIDAK ada border terang di sisi kanan sidebar: garis terang di
   situ akan memotong pita navy di bagian atas dan terlihat seperti topbar
   yang terputus. */
:root { --admin-sidebar-w: 248px; }

/* Putih -> abu (bukan navy solid) — permintaan user 2026-09-11: logo mark
   (public/assets/img/brand/sjb-mark-128.png) punya lingkaran biru di
   latarnya sendiri, jadi di atas navy solid nyaris menyatu dengan sidebar
   dan "hilang". `--navy` GLOBAL sengaja TIDAK disentuh (dipakai luas di
   tempat lain — .stat-card-rich, .alert-card, dst — yang tidak diminta
   ikut berubah), override warna cuma di blok sidebar/topbar ini saja. */
.admin-sidebar {
    position: fixed;
    top: 0; bottom: 0; left: 0;
    width: 84%;
    max-width: var(--admin-sidebar-w);
    background: linear-gradient(180deg, #ffffff 0%, var(--border) 100%);
    color: var(--text);
    z-index: 91;
    display: flex;
    flex-direction: column;
    transform: translateX(-100%);
    transition: transform 0.28s ease;
    box-shadow: 6px 0 20px rgba(8, 18, 53, 0.12);
}
.admin-sidebar.open { transform: translateX(0); }

.admin-sidebar-overlay {
    position: fixed; inset: 0;
    background: rgba(8, 18, 53, 0.45);
    z-index: 90;
    opacity: 0;
    transition: opacity 0.28s ease;
}
.admin-sidebar-overlay.open { opacity: 1; }

/* Tinggi dikunci sama dengan .admin-topbar (padding 12px + dua baris teks)
   supaya garis bawahnya bersambung lurus dengan tepi bawah topbar. */
.admin-sidebar-head {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    min-height: 58px;
    padding: 12px 16px;
    box-sizing: border-box;
    border-bottom: 1px solid var(--border);
}
.admin-sidebar-identity { display: flex; align-items: center; gap: 10px; min-width: 0; }
.admin-sidebar-mark { flex-shrink: 0; object-fit: contain; border-radius: 6px; display: block; }
.admin-sidebar-name { font-size: 14px; font-weight: 800; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.admin-sidebar-role { font-size: 11px; color: var(--text-muted); margin-top: 2px; }
.admin-sidebar-head .icon-btn { color: var(--navy); }

.admin-sidebar-scroll { flex: 1; overflow-y: auto; padding-bottom: 8px; -webkit-overflow-scrolling: touch; }
.admin-sidebar-group {
    font-size: 10.5px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
    color: var(--text-muted);
    padding: 16px 16px 6px;
}
.admin-sidebar-link {
    display: flex; align-items: center; gap: 11px;
    padding: 10px 16px;
    font-size: 13.5px; color: var(--text-muted);
    border-left: 3px solid transparent;
}
.admin-sidebar-link .ic { display: flex; color: var(--text-muted); flex-shrink: 0; }
.admin-sidebar-label { flex: 1; min-width: 0; }
.admin-sidebar-link:hover { background: var(--bg-soft); color: var(--navy); }
.admin-sidebar-link:hover .ic { color: var(--navy); }
.admin-sidebar-link.is-active {
    background: var(--blue-light);
    border-left-color: var(--blue-accent);
    color: var(--navy);
    font-weight: 700;
}
.admin-sidebar-link.is-active .ic { color: var(--blue-accent); }
/* Angka pesanan baru — sejajar teks, bukan menempel di ikon seperti dulu di
   bottom-nav, karena di sini ruangnya memanjang ke samping. */
.admin-sidebar-badge {
    flex-shrink: 0;
    min-width: 20px; padding: 0 6px;
    background: var(--danger); color: #fff;
    border-radius: 10px;
    font-size: 11px; font-weight: 700; line-height: 19px; text-align: center;
}
.admin-sidebar-logout { padding: 14px 16px; border-top: 1px solid var(--border); }
/* Sidebar sekarang terang — --danger biasa (bukan versi muda utk kontras di
   atas navy) sudah cukup terbaca sendiri. */
.admin-sidebar-logout .btn {
    display: flex; align-items: center; justify-content: center; gap: 6px;
    color: var(--danger);
    border-color: rgba(231, 76, 60, 0.4);
    background: transparent;
}
.admin-sidebar-logout .btn:hover { background: rgba(231, 76, 60, 0.08); }

@media (min-width: 1024px) {
    /* Permanen: tanpa geser, tanpa bayangan, tanpa overlay — bagian dari
       tata letak halaman, bukan lapisan di atasnya. */
    .admin-sidebar { transform: none; width: var(--admin-sidebar-w); box-shadow: none; }
    .admin-sidebar-overlay { display: none; }
    .admin-sidebar-close { display: none; }
    #adminMenuBtn { display: none; }
}

/* Editor deskripsi produk — toolbar sederhana, lihat main.js. */
.rte { border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.rte-toolbar { display: flex; gap: 2px; padding: 6px; background: var(--bg-soft); border-bottom: 1px solid var(--border); }
.rte-toolbar button {
    width: 28px; height: 28px;
    display: flex; align-items: center; justify-content: center;
    border: 1px solid transparent; border-radius: 6px;
    background: transparent; color: var(--text);
    font-weight: 700; font-size: 13px;
    cursor: pointer;
}
.rte-toolbar button:hover { background: #fff; border-color: var(--border); }
.rte-editable {
    min-height: 90px; padding: 10px 12px;
    font-size: 13.5px; line-height: 1.5;
    outline: none;
}
.rte-editable h3 { font-size: 15px; margin: 4px 0; }
.rte-editable ul { margin: 4px 0; padding-left: 20px; }

/* ── Halaman produk gabungan (Info Dasar + Detail + Harga/Stok + Foto/Video
   + Variasi dalam satu dokumen) ──────────────────────────────────────────
   Dulunya 3 halaman terpisah (tab yang benar-benar pindah halaman); sekarang
   satu halaman panjang dengan sidebar mini kiri untuk LOMPAT ANTAR SECTION
   (anchor scroll, bukan navigasi), supaya admin tidak kehilangan konteks
   "produk yang mana" saat mengurus foto/varian. */
.product-form-layout { display: flex; align-items: flex-start; gap: 20px; }
.product-form-nav {
    display: none; /* tampil mulai desktop — lihat media query di bawah */
    flex-direction: column;
    gap: 2px;
    width: 200px; flex-shrink: 0;
    position: sticky; top: 76px;
    background: #fff; border: 1px solid var(--border); border-radius: var(--radius);
    padding: 12px;
}
.product-form-nav-title {
    font-size: 12.5px; font-weight: 700; color: var(--navy);
    padding: 4px 8px 10px; border-bottom: 1px solid var(--border); margin-bottom: 6px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.product-form-nav-link {
    display: flex; align-items: center; gap: 8px;
    padding: 8px; border-radius: 8px;
    font-size: 13px; color: var(--text);
}
.product-form-nav-link:hover { background: var(--bg-soft); }
.product-form-nav-link.is-active { background: var(--blue-light); color: var(--blue-accent); font-weight: 700; }
.product-form-nav-link.is-locked { color: var(--text-muted); opacity: .55; }
.product-form-preview-btn { margin-top: 8px; }

.product-form-content { flex: 1; min-width: 0; display: flex; flex-direction: column; }
/* <form> pembungkus Info Dasar/Detail/Harga tidak boleh nested dengan <form>
   Foto & Video (butuh enctype berbeda) — display:contents membuat elemen
   <form> ini "transparan" secara layout, sehingga section-section di
   dalamnya jadi flex item SEJAJAR dengan section Foto & Video/Variasi di
   luarnya. Urutan visual (Foto & Video tampil ke-2) diatur lewat `order` di
   tiap section, lepas dari urutan section di dalam HTML. */
.product-form-main { display: contents; }
#section-dasar { order: 1; }
#section-media { order: 2; }
#section-detail { order: 3; }
#section-harga-stok { order: 4; }
#section-variasi { order: 5; }
.product-form-submit-bar { order: 6; }
/* Jarak antar section jauh lebih lega dari form lama (dulu field-field
   langsung bertumpuk tanpa jeda) — tiap section adalah kelompok field yang
   punya tujuan sendiri, jadi diberi identitas visual sendiri. */
.product-form-section {
    scroll-margin-top: 76px; /* supaya anchor tidak nempel persis di tepi atas saat discroll */
    padding: 22px 20px;
    border: 1px solid var(--border); border-radius: var(--radius);
    background: #fff;
    margin-bottom: 20px;
}
.product-form-section-title {
    display: flex; align-items: center; gap: 8px;
    font-size: 14.5px; font-weight: 700; color: var(--navy);
    margin: 0 0 18px; padding-bottom: 12px; border-bottom: 1px solid var(--border);
}
/* Kartu foto/varian di dalam section Foto&Video/Variasi TIDAK dijepit
   560px seperti .form-card di halaman lain — section-nya sendiri sudah
   selebar konten, kartu di dalamnya sekadar pemisah visual antar-baris. */
#section-media .form-card, #section-variasi .form-card { max-width: none; }
.product-form-main .form-group { margin-bottom: 16px; }
.product-form-main .form-row { gap: 16px; margin-bottom: 4px; }
.product-form-locked-msg {
    color: var(--text-muted); font-size: 13px;
    padding: 20px; text-align: center;
    background: var(--bg-soft); border-radius: 8px; border: 1px dashed var(--border);
}
.product-form-submit-bar {
    position: sticky; bottom: 0;
    display: flex; gap: 10px;
    padding: 14px 0 4px;
    background: linear-gradient(to top, #fff 60%, transparent);
}

@media (min-width: 900px) {
    .product-form-nav { display: flex; }
}

/* ── Modal Pratinjau — iframe halaman produk sungguhan, dibatasi lebar
   HP (sama dengan --max-width sisi pembeli) tapi tinggi PENUH layar. ────── */
.preview-modal-overlay {
    display: none; position: fixed; inset: 0;
    background: rgba(8, 18, 53, 0.55); z-index: 1000;
}
.preview-modal-overlay.show { display: block; }
.preview-modal {
    display: none; position: fixed;
    top: 0; bottom: 0; left: 50%; transform: translateX(-50%);
    width: 100%; max-width: var(--max-width);
    background: #fff; z-index: 1001;
    flex-direction: column;
    box-shadow: 0 0 40px rgba(0,0,0,.3);
}
.preview-modal.show { display: flex; }
.preview-modal-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: 10px 14px; background: var(--navy); color: #fff;
    font-size: 13px; font-weight: 600; flex-shrink: 0;
}
.preview-modal-head .icon-btn { color: #fff; }
.preview-modal-frame { flex: 1; width: 100%; border: none; }

/* ── Dialog "Review Perubahan" sebelum simpan ────────────────────────────
   Beda dari .confirm-overlay/.confirm-dialog (pesan statis 1 baris, dipakai
   konfirmasi hapus): dialog ini menampilkan DAFTAR field yang berubah
   (lama → baru), jadi lebih lebar dan bisa scroll kalau daftarnya panjang. */
.change-review-overlay {
    position: fixed; inset: 0; background: rgba(13, 27, 76, .45); z-index: 1000;
    display: flex; align-items: center; justify-content: center; padding: 24px;
    opacity: 0; pointer-events: none; transition: opacity .15s ease;
}
.change-review-overlay.show { opacity: 1; pointer-events: auto; }
.change-review-dialog {
    background: #fff; border-radius: var(--radius); padding: 20px;
    width: 100%; max-width: 420px; max-height: 80vh;
    display: flex; flex-direction: column;
    transform: scale(.96); transition: transform .15s ease;
    box-shadow: 0 20px 50px rgba(13, 27, 76, .3);
}
.change-review-overlay.show .change-review-dialog { transform: scale(1); }
.change-review-dialog h4 {
    font-size: 14px; color: var(--navy); margin: 0 0 4px;
    display: flex; align-items: center; gap: 6px;
}
.change-review-dialog .change-review-subtitle { font-size: 12px; color: var(--text-muted); margin-bottom: 12px; }
.change-review-list { overflow-y: auto; margin-bottom: 16px; border-top: 1px solid var(--border); }
.change-review-row { padding: 10px 0; border-bottom: 1px solid var(--border); font-size: 13px; }
.change-review-row .field-label { font-weight: 600; color: var(--navy); margin-bottom: 3px; }
.change-review-row .field-diff { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.change-review-row .old-value { color: var(--text-muted); text-decoration: line-through; }
.change-review-row .new-value { color: var(--text); font-weight: 600; }
.change-review-empty { padding: 16px 0; text-align: center; color: var(--text-muted); font-size: 13px; }
.change-review-dialog .confirm-actions { display: flex; gap: 8px; flex-shrink: 0; }
.change-review-dialog .confirm-actions button {
    flex: 1; padding: 10px; border-radius: 8px; border: none; font-weight: 700; font-size: 13px; cursor: pointer;
}
.change-review-dialog .confirm-cancel { background: var(--bg-soft); color: var(--text); }
.change-review-dialog .confirm-ok { background: var(--navy); color: #fff; }

/* Tooltip instan — atribut `title` bawaan browser punya delay ~1 detik
   sebelum muncul; ini pakai data-tooltip + CSS supaya tampil begitu
   di-hover/focus, tanpa jeda. */
[data-tooltip] { position: relative; }
[data-tooltip]::after {
    content: attr(data-tooltip);
    position: absolute; bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%);
    background: #1f2937; color: #fff; font-size: 12px; font-weight: 400; line-height: 1.5;
    padding: 8px 10px; border-radius: 8px; white-space: pre-line; width: max-content; max-width: 260px;
    text-align: left; box-shadow: 0 4px 14px rgba(0,0,0,.18);
    opacity: 0; visibility: hidden; pointer-events: none; z-index: 50;
    transition: opacity .1s ease;
}
[data-tooltip]:hover::after, [data-tooltip]:focus-visible::after {
    opacity: 1; visibility: visible;
}

/* ── Utas chat di panel admin (2026-09-12) ───────────────────────────────────
   Terpisah dari .chat-* milik pembeli: di sisi admin utasnya dirender server
   sebagai halaman biasa (bukan panel geser), lebarnya lebar, dan gelembungnya
   perlu menampilkan siapa pengirim + jejak keputusan bot. Memakai ulang kelas
   pembeli akan memaksa keduanya berubah bersama padahal kebutuhannya beda. */
.chat-admin-thread {
    max-height: 520px;
    overflow-y: auto;
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 12px;
    background: var(--bg-soft);
}
.chat-admin-msg { margin-bottom: 12px; }
.chat-admin-msg:last-child { margin-bottom: 0; }
.chat-admin-msg-who {
    font-size: 11px; font-weight: 700; color: var(--text-muted);
    margin-bottom: 3px;
}
.chat-admin-msg-who span { font-weight: 400; margin-left: 6px; }
.chat-admin-msg-body {
    font-size: 13px; line-height: 1.5;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 8px 10px;
    word-break: break-word;
}
.chat-admin-msg-customer .chat-admin-msg-body { border-left: 3px solid var(--blue-accent); }
.chat-admin-msg-admin .chat-admin-msg-body { border-left: 3px solid var(--success); }
.chat-admin-msg-bot .chat-admin-msg-body { border-left: 3px solid var(--border); color: var(--text-muted); }
/* Lampiran produk/pesanan menempel di pesan pembeli (permintaan user
   2026-09-13) — versi admin dari .chat-attachment milik pembeli, dirender
   server (bukan JS) karena halaman ini sudah full server-render. */
.chat-admin-attachment {
    display: flex; gap: 8px; align-items: flex-start;
    background: var(--blue-light);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 7px 9px;
    margin-bottom: 6px;
}
.chat-admin-attachment img { width: 38px; height: 38px; border-radius: 6px; object-fit: cover; flex-shrink: 0; }
.chat-admin-attachment-kicker { font-size: 10px; color: var(--text-muted); }
.chat-admin-attachment-title { font-size: 12px; font-weight: 700; color: var(--navy); }
.chat-admin-attachment-line { font-size: 11px; color: var(--text-muted); }
.chat-admin-attachment-pills { display: flex; flex-wrap: wrap; gap: 4px; margin: 3px 0; }
.chat-admin-attachment-pills span {
    font-size: 9.5px; padding: 1px 6px; border-radius: 20px;
    background: #fff; color: var(--navy);
}
.chat-admin-trace {
    font-size: 10.5px; color: var(--text-muted);
    margin-top: 3px; font-family: ui-monospace, Menlo, monospace;
}
.chat-admin-jadikan-faq {
    display: inline-block; margin-top: 4px;
    font-size: 10.5px; font-weight: 600;
    color: var(--blue-accent); text-decoration: none;
}
.chat-presence {
    display: flex; align-items: center; gap: 5px;
    font-size: 11px; color: var(--text-muted);
    margin-top: 2px;
}
.chat-presence i {
    width: 7px; height: 7px; border-radius: 50%;
    background: var(--text-muted);
    flex: 0 0 7px;
}
.chat-presence.is-online i { background: var(--success); }
.chat-presence.is-online { color: var(--success); font-weight: 600; }

/* ══════════════════════════════════════════════════════════════════════
   Walkthrough spotlight (public/assets/js/walkthrough.js)

   Scrim berlubang dibuat dari SATU elemen: .wt-cutout diberi box-shadow
   dengan spread raksasa, sehingga "lubang" adalah kotak elemen itu sendiri
   dan bayangannya menggelapkan sisa layar. Bukan SVG mask (dukungan lama
   tidak merata) dan bukan 4 div pembingkai (empat elemen yang harus
   dianimasikan serempak). Efek sampingnya menguntungkan: transisi antar
   langkah cukup menganimasikan top/left/width/height satu elemen.

   .wt-overlay transparan dan TIDAK menggelapkan apa pun — tugasnya cuma
   menangkap interaksi supaya halaman di baliknya tidak bisa diklik saat tur
   berjalan (pola yang sama dengan .filter-overlay). Yang menggelapkan adalah
   box-shadow .wt-cutout.

   Warna scrim rgba(13,27,76,.45) sengaja sama persis dengan .filter-overlay
   dan .confirm-overlay — satu bahasa visual untuk "layar sedang dikunci".

   Popup promo beranda (z-index 2000) SENGAJA berada di ATAS tur: keduanya
   menembak di kunjungan pertama ke beranda, dan mesin tur menunda dirinya
   sampai popup itu ditutup (lihat walkthrough.js). Jangan menaikkan
   z-index tur melewati 2000 untuk "memperbaiki" tumpang tindih itu — yang
   benar adalah menunggu, bukan menimpa.
   ══════════════════════════════════════════════════════════════════════ */
.wt-overlay {
    position: fixed; inset: 0; z-index: 1500;
    touch-action: none;
}
.wt-cutout {
    position: fixed; z-index: 1501;
    pointer-events: none;
    border-radius: 12px;
    box-shadow: 0 0 0 9999px rgba(13, 27, 76, .45);
    transition: top .22s ease, left .22s ease, width .22s ease, height .22s ease;
}
/* Langkah tanpa anchor (pembuka/penutup): lubangnya diciutkan ke titik di
   tengah layar sehingga yang terlihat hanya scrim penuh. */
.wt-cutout.is-kosong { box-shadow: 0 0 0 9999px rgba(13, 27, 76, .55); }

.wt-tooltip {
    position: fixed; z-index: 1502;
    width: 268px; max-width: calc(100vw - 24px);
    background: #fff; border-radius: var(--radius);
    padding: 14px 15px 12px;
    box-shadow: 0 10px 30px rgba(13, 27, 76, .22);
}
.wt-progress {
    font-size: 11px; font-weight: 700; letter-spacing: .04em;
    text-transform: uppercase; color: var(--blue-accent);
    margin-bottom: 5px;
}
.wt-title { font-size: 14.5px; font-weight: 800; color: var(--navy); margin-bottom: 4px; }
.wt-body { font-size: 12.8px; line-height: 1.55; color: var(--text-muted); }
.wt-actions {
    display: flex; align-items: center; gap: 8px;
    margin-top: 12px;
}
.wt-actions .wt-skip {
    margin-right: auto;
    background: none; border: none; padding: 6px 2px;
    font-size: 12px; font-weight: 600; color: var(--text-muted);
    cursor: pointer; font-family: inherit;
}
.wt-actions .wt-skip:hover { color: var(--danger); }
.wt-actions button.wt-prev,
.wt-actions button.wt-next {
    border-radius: 8px; padding: 7px 14px;
    font-size: 12.5px; font-weight: 700;
    cursor: pointer; font-family: inherit;
    border: 1.5px solid var(--border); background: #fff; color: var(--navy);
}
.wt-actions button.wt-next {
    border-color: var(--navy); background: var(--navy); color: #fff;
}
.wt-actions button[hidden] { display: none; }

/* Panah kecil yang menunjuk anchor. Arahnya diset JS lewat class .ke-atas
   (tooltip di bawah anchor, panah menunjuk ke atas) / .ke-bawah. */
.wt-arrow {
    position: absolute; width: 12px; height: 12px;
    background: #fff; transform: rotate(45deg);
}
.wt-tooltip.ke-atas .wt-arrow { top: -6px; box-shadow: -3px -3px 6px rgba(13, 27, 76, .06); }
.wt-tooltip.ke-bawah .wt-arrow { bottom: -6px; box-shadow: 3px 3px 6px rgba(13, 27, 76, .06); }
.wt-tooltip.tanpa-panah .wt-arrow { display: none; }

@media (prefers-reduced-motion: reduce) {
    .wt-cutout { transition: none; }
}

/* Penunjuk arah ke Pusat Bantuan di dalam panel chat (partials/chat_panel).
   Baris tipis di bawah header, di ATAS #chatThread — sengaja di luar daftar
   pesan supaya tidak ikut tergulung hilang saat percakapan memanjang. */
.chat-guide-link {
    display: flex; align-items: center; gap: 7px;
    padding: 9px 14px;
    border-bottom: 1px solid var(--border);
    background: var(--bg-soft);
    font-size: 12px; color: var(--text-muted);
    text-decoration: none;
    flex-shrink: 0;
}
.chat-guide-link strong { color: var(--navy); }
.chat-guide-link span { flex: 1; }
.chat-guide-link:hover { background: var(--blue-light); }
