/* ==========================================================================
   Fansys — sistem desain Material (tema gelap + emas)
   --------------------------------------------------------------------------
   Nada visualnya hitam pekat dengan aksen emas mengikuti identitas logo:
   permukaan gelap bertingkat, garis tipis, dan kedalaman yang dibentuk oleh
   bayangan berlapis (elevation) alih-alih garis tebal. Aksen warna hanya
   muncul pada elemen yang bisa ditindaklanjuti.

   Tiga hal yang perlu diketahui sebelum mengubah berkas ini:

   1. Seluruh warna merek berasal dari variabel `--brand`, `--brand-dark`, dan
      `--accent` yang ditimpa saat proses berjalan oleh applyBranding() di
      app.js. Karena itu jangan pernah menulis nilai heksadesimal merek secara
      langsung — pakai variabelnya, atau perubahan warna dari Panel Branding
      tidak akan ikut terpakai.

   2. Nama kelas lama (.card, .stat, .badge, .btn, .alert, .shell, .sidebar,
      dan seterusnya) sengaja dipertahankan. 28 halaman HTML memakainya, dan
      mengganti nama kelas berarti menyunting semuanya tanpa manfaat nyata.
      Yang berubah adalah tampilannya, bukan kontraknya.

   3. Animasi apa pun di berkas ini harus mati saat pengguna meminta
      `prefers-reduced-motion`. Aturan itu ada satu blok di bagian paling
      bawah; tambahkan animasi baru ke dalamnya.

   4. Etalase publik (halaman dengan <body class="shop-body"> atau
      class="auth-page"> — beranda, katalog, produk, keranjang, toko mitra,
      halaman informasi, masuk/daftar) memakai lapisan gaya "boutique"
      tersendiri di bagian ETALASE, di bawah selektor `.shop-body`/`.auth-page`.
      Panel admin & dashboard mitra TIDAK disentuh lapisan ini — keduanya
      tetap memakai bentuk pil/shadow Material di atas. Jangan pindahkan gaya
      boutique ke selektor global (mis. `button, .btn` polos) atau dashboard
      ikut berubah tanpa diminta.
   ========================================================================== */

@import url('/assets/vendor/roboto/roboto.css');
@import url('/assets/vendor/playfair-display/playfair-display.css');
@import url('/assets/vendor/fontawesome/css/fontawesome.min.css');
@import url('/assets/vendor/fontawesome/css/solid.min.css');
@import url('/assets/vendor/fontawesome/css/regular.min.css');
@import url('/assets/vendor/fontawesome/css/brands.min.css');

:root {
  /* -- warna merek (ditimpa applyBranding) -------------------------------- */
  --brand: #cda86a;
  --brand-dark: #b3894a;
  --accent: #e8c07d;

  /* -- bentuk (shape scale Material) -------------------------------------- */
  --r-xs: 6px;
  --r-sm: 10px;
  --r: 14px;
  --r-lg: 20px;
  --r-xl: 28px;
  --r-full: 999px;
  --radius: var(--r); /* alias lama */

  --shadow: var(--e1); /* alias lama */

  /* -- motion (kurva easing standar Material) ----------------------------- */
  --ease: cubic-bezier(.2, 0, 0, 1);
  --ease-emph: cubic-bezier(.2, 0, 0, 1);
  --ease-out: cubic-bezier(0, 0, 0, 1);
  --t-fast: 120ms;
  --t: 220ms;
  --t-slow: 400ms;
  --ease-spring: cubic-bezier(.34, 1.56, .64, 1); /* sedikit melewati, lalu kembali */

  --topbar-h: 60px;
}

/* ==========================================================================
   TEMA — dua palet yang dapat dipilih superadmin
   --------------------------------------------------------------------------
   Atribut `data-theme` dipasang pada <html> oleh server (lihat
   `services/tema.js`), bukan oleh JavaScript. Alasannya menentukan: kalau
   dipasang JS, halaman sempat tergambar dengan palet bawaan lalu berkedip
   ke palet yang benar — pada tema terang, kedipnya berupa layar hitam penuh
   selama beberapa ratus milidetik di SETIAP perpindahan halaman.

   Tema "gelap" sengaja ditulis sebagai pasangan `:root, :root[data-theme=gelap]`
   supaya tetap berlaku bila atributnya gagal tersisipkan — kegagalan
   menghasilkan tampilan lama yang utuh, bukan halaman tanpa warna.

   Menambah tema baru = menambah satu blok di sini + satu entri di
   `services/tema.js`. Tidak ada tempat ketiga yang perlu disunting.
   ========================================================================== */

/* -------------------------------------------------- HITAM & EMAS (gelap) */
:root,
:root[data-theme="gelap"] {
  --brand-light: color-mix(in srgb, var(--brand) 17%, #050505);
  --brand-ink: #241a08;   /* teks di atas permukaan/tombol berwarna emas */
  --brand-on-bg: var(--brand); /* emas sebagai TEKS di atas latar halaman */

  /* permukaan: hitam dengan tingkat keabuan bertahap */
  --bg: #0a0a0a;
  --surface: #141414;
  --surface-1: #181818;
  --surface-2: #1e1e1e;
  --surface-3: #262626;
  --border: #2a2a2a;
  --border-strong: #3a3a3a;

  --text: #f2ece0;
  --text-2: #c9bfa8;
  --muted: #8f8672;

  --ok: #4ade80;      --ok-bg: #12291b;
  --warn: #fbbf24;    --warn-bg: #33230a;
  --danger: #f87171;  --danger-bg: #331414;
  --info: #60a5fa;    --info-bg: #101f33;

  --ink-on-solid: #0b1a10;
  --scrim: rgba(0, 0, 0, .6);
  --bar-bg: rgba(10, 10, 10, .92);      /* bilah toko yang menempel saat digulir */
  --bg-overlay: linear-gradient(160deg, rgba(10, 10, 10, .72), rgba(10, 10, 10, .86));

  --e1: 0 1px 2px rgba(0, 0, 0, .35), 0 1px 3px rgba(0, 0, 0, .32);
  --e2: 0 2px 4px rgba(0, 0, 0, .32), 0 4px 10px rgba(0, 0, 0, .34);
  --e3: 0 4px 8px rgba(0, 0, 0, .34), 0 10px 24px rgba(0, 0, 0, .38);
  --e4: 0 8px 16px rgba(0, 0, 0, .40), 0 20px 44px rgba(0, 0, 0, .44);
  --e5: 0 12px 28px rgba(0, 0, 0, .44), 0 32px 72px rgba(0, 0, 0, .50);
}

/* -------------------------------------------------- EMAS & HITAM (terang)
   Dasar gading hangat, emas sebagai warna utama, hitam sebagai pendamping
   untuk teks dan detail tegas.

   Dua kehalusan yang menentukan tema ini terbaca atau tidak:

   1. `--brand-on-bg` digelapkan dari `--brand`. Warna merek dari Panel
      Branding (#cda86a) adalah emas muda yang dirancang menyala di atas
      hitam; dipakai apa adanya sebagai teks di atas gading ia hanya
      mencapai rasio kontras ±1,9:1 — praktis tidak terbaca. `color-mix`
      dipakai agar penggelapannya mengikuti warna apa pun yang kelak dipilih
      superadmin, bukan satu nilai tetap yang langsung basi begitu warnanya
      diganti.

   2. Bayangannya bernada cokelat hangat, bukan hitam netral. Bayangan hitam
      di atas gading terlihat kotor dan "murah"; nada cokelat membuatnya
      terbaca sebagai kedalaman, bukan noda. */
:root[data-theme="emas"] {
  --brand-light: color-mix(in srgb, var(--brand) 16%, #ffffff);
  --brand-ink: #1a1405;
  /*
   * 55%, bukan angka yang "kelihatan emas" seperti 72%.
   *
   * Rasio kontras terhadap latar gading diukur, bukan dikira: pada 72% hasilnya
   * 3,43:1 — lolos untuk elemen antarmuka besar tetapi GAGAL untuk teks biasa
   * (WCAG AA menuntut 4,5:1), sehingga setiap tautan di dalam paragraf jadi
   * sulit dibaca. 55% menghasilkan 4,82:1. Angka ini jangan dinaikkan tanpa
   * mengukur ulang; emas yang lebih menyala di sini dibayar dengan tautan yang
   * tidak terbaca oleh sebagian pembaca.
   *
   * Emas yang terang tetap muncul di tempat yang aman: sebagai LATAR tombol
   * dan garis aksen (`--brand`), di mana kontrasnya dijamin oleh tinta gelap
   * di atasnya (`--brand-ink`, terukur 8,21:1).
   */
  --brand-on-bg: color-mix(in srgb, var(--brand) 55%, #2a1f05);

  --bg: #fbf8f2;
  --surface: #ffffff;
  --surface-1: #fdfbf6;
  --surface-2: #f6f1e6;
  --surface-3: #efe8d8;
  --border: #e8dfc8;
  --border-strong: #d6c89f;

  --text: #14110b;
  --text-2: #4a4335;
  --muted: #6f6754;   /* 5,29:1 — nada gading menuntutnya lebih gelap dari abu biasa */

  /* Warna semantik dipekatkan sampai lolos 4,5:1 di atas latar pucatnya
     masing-masing; nilai Tailwind yang lazim (#15803d, #a16207) berhenti di
     4,47 dan 4,50 — persis di ambang, dan gagal begitu latarnya sedikit
     bergeser. */
  --ok: #166534;      --ok-bg: #e8f5ec;
  --warn: #92500a;    --warn-bg: #fdf4e0;
  --danger: #b91c1c;  --danger-bg: #fdeaea;
  --info: #1d4ed8;    --info-bg: #eaf0fd;

  --ink-on-solid: #ffffff;
  --scrim: rgba(40, 31, 10, .45);
  --bar-bg: rgba(251, 248, 242, .88);
  --bg-overlay: linear-gradient(160deg, rgba(251, 248, 242, .78), rgba(244, 237, 222, .90));

  --e1: 0 1px 2px rgba(80, 62, 20, .07), 0 1px 3px rgba(80, 62, 20, .06);
  --e2: 0 2px 4px rgba(80, 62, 20, .07), 0 4px 10px rgba(80, 62, 20, .07);
  --e3: 0 4px 8px rgba(80, 62, 20, .08), 0 10px 24px rgba(80, 62, 20, .09);
  --e4: 0 8px 16px rgba(80, 62, 20, .10), 0 20px 44px rgba(80, 62, 20, .11);
  --e5: 0 12px 28px rgba(80, 62, 20, .12), 0 32px 72px rgba(80, 62, 20, .14);
}

* { box-sizing: border-box; }
::selection { background: color-mix(in srgb, var(--brand) 35%, transparent); color: var(--text); }

html { scroll-behavior: smooth; }

/* ------------------------------------------------------------ scrollbar
   Tipis dan bernada emas, bukan bawaan browser yang tebal & putih —
   berlaku di seluruh halaman (Firefox lewat scrollbar-color, Chrome/Edge/
   Safari lewat ::-webkit-scrollbar). */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
  background-color: var(--border-strong);
  border-radius: var(--r-full);
  border: 2px solid var(--bg);
  background-clip: padding-box;
}
*::-webkit-scrollbar-thumb:hover { background-color: var(--brand); }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: 'Roboto', system-ui, -apple-system, 'Segoe UI', Arial, sans-serif;
  font-size: 15px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: var(--brand-on-bg); text-decoration: none; transition: color var(--t-fast) var(--ease); }
a:hover { color: var(--brand-dark); text-decoration: none; }

h1, h2, h3, h4 { margin: 0 0 .5rem; line-height: 1.22; font-weight: 500; letter-spacing: -.01em; }
h1 { font-size: 1.65rem; font-weight: 500; }
h2 { font-size: 1.2rem; }
h3 { font-size: 1rem; }
p { margin: 0 0 .75rem; color: var(--text-2); }
small { color: var(--muted); }
strong, b { font-weight: 500; color: var(--text); }

/* Font Awesome: sedikit turun agar sejajar dengan garis dasar teks. */
.fa-solid, .fa-regular, .fa-brands { vertical-align: -.05em; }

/* ------------------------------------------------------------------ fokus
   Cincin fokus dipisah dari :focus biasa supaya tidak muncul saat elemen
   diklik dengan tetikus — hanya saat navigasi papan ketik. */
:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
  border-radius: var(--r-xs);
}
:focus:not(:focus-visible) { outline: none; }

/* ==========================================================================
   Tata letak aplikasi (topbar + sidebar + main)
   ========================================================================== */
.topbar {
  background: var(--surface);
  color: var(--text);
  padding: 0 1.1rem;
  height: var(--topbar-h);
  display: flex;
  align-items: center;
  gap: .9rem;
  position: sticky;
  top: 0;
  z-index: 50;
  border-bottom: 1px solid var(--border);
  box-shadow: var(--e1);
}
.topbar .brand {
  font-weight: 500; font-size: 1.08rem; letter-spacing: -.01em; color: var(--text);
  display: inline-flex; align-items: center; gap: .55rem;
}
.topbar .brand span { color: var(--text); }
.topbar .spacer { flex: 1; }
.topbar a { color: var(--text); }
.who-wrap {
  display: inline-flex; align-items: center; gap: .55rem; text-decoration: none;
  border-radius: var(--r-md); padding: .2rem .35rem; margin: -.2rem -.35rem;
  transition: background .15s var(--ease);
}
.who-wrap:hover { background: var(--surface-2); }
.topbar .who {
  font-size: .82rem; text-align: right; line-height: 1.35; color: var(--muted);
}
.topbar .who strong { color: var(--text); font-weight: 500; }

/*
 * Judul halaman & tombol kembali — bagian bilah atas versi ponsel. Ditulis
 * di markup yang sama dengan bilah desktop dan disembunyikan di sini, supaya
 * tidak ada dua kerangka yang harus dijaga tetap sinkron. Ditampilkan kembali
 * oleh blok "APLIKASI DI PONSEL" di akhir berkas.
 */
.appbar-title, .appbar-back { display: none; }

/* Foto profil — dipakai topbar (avatar-sm) dan halaman profil (avatar-lg). */
.avatar {
  border-radius: 50%; object-fit: cover; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--brand); color: #1a1206; font-weight: 700;
}
.avatar-sm { width: 34px; height: 34px; font-size: .95rem; }
/* Ukuran bawaan avatarHtml() — dipakai kartu akun pada lembar "Lainnya". */
.avatar-md { width: 44px; height: 44px; font-size: 1.15rem; }
.avatar-lg { width: 96px; height: 96px; font-size: 2.2rem; }
.avatar-xl { width: clamp(160px, 22vw, 240px); height: clamp(160px, 22vw, 240px); font-size: clamp(3.2rem, 6vw, 5.2rem); }
.avatar-inisial { text-transform: uppercase; }

.shell { display: flex; min-height: calc(100vh - var(--topbar-h)); align-items: flex-start; }

.sidebar {
  width: 244px; flex: 0 0 244px;
  background: var(--surface);
  border-right: 1px solid var(--border);
  padding: .9rem .6rem 2rem;
  position: sticky; top: var(--topbar-h);
  max-height: calc(100vh - var(--topbar-h));
  overflow-y: auto;
  scrollbar-width: thin;
}
/* Item nav memakai bentuk pil penuh — ciri khas navigation rail Material 3. */
.sidebar a {
  display: flex; align-items: center; gap: .7rem;
  padding: .58rem .9rem;
  border-radius: var(--r-full);
  color: var(--text-2);
  font-size: .9rem; font-weight: 400;
  margin-bottom: 2px;
  position: relative; overflow: hidden;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.sidebar a i { width: 1.15rem; text-align: center; font-size: .95rem; color: var(--muted); transition: color var(--t-fast) var(--ease); }
.sidebar a:hover { background: var(--surface-2); color: var(--text); }
.sidebar a:hover i { color: var(--text-2); }
.sidebar a.active { background: var(--brand-light); color: var(--brand-on-bg); font-weight: 500; }
.sidebar a.active i { color: var(--brand-on-bg); }
.sidebar .group {
  font-size: .7rem; text-transform: uppercase; letter-spacing: .09em;
  color: var(--muted); margin: 1.1rem .95rem .35rem; font-weight: 500;
}

main { flex: 1; padding: 1.5rem 1.6rem 4rem; min-width: 0; }

.page-head { display: flex; align-items: flex-start; gap: 1rem; margin-bottom: 1.25rem; flex-wrap: wrap; }
.page-head .spacer { flex: 1; }
.page-head h1 { display: flex; align-items: center; gap: .6rem; }
.page-head h1 i { color: var(--brand-on-bg); font-size: .9em; }

/* ==========================================================================
   Kartu & petak
   ========================================================================== */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 1.15rem 1.25rem;
  box-shadow: var(--e1);
  margin-bottom: 1.1rem;
  transition: box-shadow var(--t) var(--ease), transform var(--t) var(--ease);
}
.card h2, .card h3 { margin-top: 0; }
.card.flat { box-shadow: none; }
.card.raised { box-shadow: var(--e3); border-color: transparent; }
/* Dipakai pada daftar yang bisa diklik; jangan pasang ke kartu statis. */
.card.hoverable:hover { box-shadow: var(--e3); transform: translateY(-2px); }

.grid { display: grid; gap: 1.1rem; }
.grid.c2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.grid.c3 { grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); }
.grid.c4 { grid-template-columns: repeat(auto-fit, minmax(185px, 1fr)); }

.stat {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r);
  padding: 1rem 1.1rem; box-shadow: var(--e1); min-width: 0;
  transition: box-shadow var(--t) var(--ease), transform var(--t) var(--ease);
}
.stat:hover { box-shadow: var(--e2); transform: translateY(-1px); }
.stat .label {
  font-size: .74rem; color: var(--muted); text-transform: uppercase;
  letter-spacing: .07em; font-weight: 500;
  display: flex; align-items: center; gap: .4rem;
}
.stat .value {
  font-size: 1.6rem; font-weight: 500; margin-top: .2rem; letter-spacing: -.025em;
  color: var(--text); font-variant-numeric: tabular-nums; overflow-wrap: break-word;
}
.stat .sub { font-size: .8rem; color: var(--muted); }
.stat.brand {
  background: linear-gradient(135deg, var(--brand), var(--brand-dark));
  color: var(--brand-ink); border-color: transparent; box-shadow: var(--e2);
}
.stat.brand .label, .stat.brand .sub { color: color-mix(in srgb, var(--brand-ink) 72%, transparent); }
.stat.brand .value { color: var(--brand-ink); }

/* ==========================================================================
   Tabel
   ========================================================================== */
table { width: 100%; border-collapse: collapse; font-size: .89rem; }
th, td { padding: .68rem .6rem; text-align: left; border-bottom: 1px solid var(--border); vertical-align: top; }
th {
  font-size: .72rem; text-transform: uppercase; letter-spacing: .06em; color: var(--muted);
  font-weight: 500; background: transparent; white-space: nowrap;
  border-bottom: 1px solid var(--border-strong);
}
tbody tr { transition: background var(--t-fast) var(--ease); }
tbody tr:hover { background: var(--surface-2); }
tbody tr:last-child td { border-bottom: 0; }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.table-wrap { overflow-x: auto; margin: 0 -.25rem; padding: 0 .25rem; }

/* ==========================================================================
   Badge / chip
   ========================================================================== */
.badge {
  display: inline-flex; align-items: center; gap: .3rem;
  padding: .2rem .6rem; border-radius: var(--r-full);
  font-size: .74rem; font-weight: 500; line-height: 1.5;
  background: var(--surface-3); color: var(--text-2); white-space: nowrap;
}
.badge.ok, .badge.active, .badge.delivered, .badge.paid, .badge.released, .badge.verified { background: var(--ok-bg); color: var(--ok); }
.badge.warn, .badge.pending, .badge.pending_payment, .badge.submitted, .badge.requested,
.badge.frozen, .badge.waiting_verification { background: var(--warn-bg); color: var(--warn); }
.badge.danger, .badge.cancelled, .badge.rejected, .badge.terminated, .badge.reversed,
.badge.returned { background: var(--danger-bg); color: var(--danger); }
.badge.info, .badge.shipped, .badge.processing, .badge.packed, .badge.open { background: var(--info-bg); color: var(--info); }
.badge.master { background: #332708; color: #f3ce85; }
.badge.brand { background: var(--brand-light); color: var(--brand-on-bg); }

/* ==========================================================================
   Formulir
   ========================================================================== */
label { display: block; font-size: .82rem; font-weight: 500; margin-bottom: .3rem; color: var(--text-2); }

input, select, textarea {
  width: 100%; padding: .62rem .8rem;
  border: 1px solid var(--border-strong); border-radius: var(--r-sm);
  font: inherit; font-size: .92rem;
  background: var(--surface); color: var(--text);
  transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
input::placeholder, textarea::placeholder { color: var(--muted); }
input:hover, select:hover, textarea:hover { border-color: var(--muted); }
/* Garis fokus dibuat dari box-shadow, bukan outline, supaya sudut membulatnya
   ikut dan tidak ada lompatan tata letak. */
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 16%, transparent);
}
select {
  appearance: none;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%238f8672'%3E%3Cpath d='M4.4 6.2 8 9.8l3.6-3.6 1.1 1.1L8 12 3.3 7.3z'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right .55rem center; background-size: 1.1rem;
  padding-right: 2.1rem;
}
input[type="checkbox"], input[type="radio"] { width: auto; accent-color: var(--brand); }
textarea { resize: vertical; min-height: 4.5rem; }

.field { margin-bottom: .9rem; }
.row { display: grid; gap: .8rem; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }

/* -- toggle tampil/sembunyi kata sandi ------------------------------------ */
.pw-wrap { position: relative; }
.pw-wrap input { padding-right: 2.6rem; }
.pw-toggle {
  position: absolute; top: 0; right: 0; bottom: 0; width: 2.6rem;
  display: grid; place-items: center; font-size: .95rem;
  background: transparent; color: var(--muted); box-shadow: none; border: 0; padding: 0;
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
}
.pw-toggle:hover { background: transparent; color: var(--text-2); box-shadow: none; }

/* ==========================================================================
   Tombol — filled / tonal / outlined / text, plus efek riak
   ========================================================================== */
button, .btn {
  position: relative; overflow: hidden;
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  padding: .58rem 1.15rem;
  border: 1px solid transparent;
  background: var(--brand); color: var(--brand-ink);
  border-radius: var(--r-full);
  font: inherit; font-size: .89rem; font-weight: 500; letter-spacing: .01em;
  cursor: pointer; text-align: center; white-space: nowrap;
  box-shadow: var(--e1);
  transition: box-shadow var(--t) var(--ease), background var(--t-fast) var(--ease),
              transform var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
button:hover, .btn:hover { background: var(--brand-dark); color: var(--brand-ink); box-shadow: var(--e2); }
button:active, .btn:active { transform: scale(.97); box-shadow: var(--e1); }
button:disabled, .btn.disabled { opacity: .42; cursor: not-allowed; box-shadow: none; transform: none; }
button:disabled:hover { background: var(--brand); }

/* Tonal — latar merek yang sangat terang, untuk aksi sekunder yang sering dipakai. */
button.ghost, .btn.ghost {
  background: var(--brand-light); color: var(--brand-on-bg); box-shadow: none;
}
button.ghost:hover, .btn.ghost:hover {
  background: color-mix(in srgb, var(--brand) 30%, var(--bg)); color: var(--brand-on-bg); box-shadow: var(--e1);
}

/* Outlined — aksi netral yang tetap terlihat sebagai tombol. */
button.neutral, .btn.neutral {
  background: var(--surface); color: var(--text-2); border-color: var(--border-strong); box-shadow: none;
}
button.neutral:hover, .btn.neutral:hover { background: var(--surface-2); color: var(--text); border-color: var(--muted); }

/* Text — aksi tersier, tanpa latar sampai disentuh. */
button.text, .btn.text { background: transparent; color: var(--brand-on-bg); box-shadow: none; }
button.text:hover, .btn.text:hover { background: var(--brand-light); color: var(--brand-on-bg); box-shadow: none; }

/*
 * Warna semantik `--danger` berperan sebagai latar penuh di sini, jadi warna
 * tulisannya harus berlawanan dengan latar itu — dan arah "berlawanan" berbeda
 * per tema (lihat catatan yang sama di bagian Toast). `--ink-on-solid`
 * menjawab keduanya: putih di tema terang, gelap di tema gelap.
 */
button.danger, .btn.danger { background: var(--danger); color: var(--ink-on-solid); }
button.danger:hover, .btn.danger:hover { background: color-mix(in srgb, var(--danger) 82%, #000); color: var(--ink-on-solid); }

button.accent, .btn.accent { background: var(--accent); color: var(--brand-ink); }
button.accent:hover, .btn.accent:hover { background: color-mix(in srgb, var(--accent) 85%, #000); color: var(--brand-ink); }

button.sm, .btn.sm { padding: .34rem .8rem; font-size: .8rem; gap: .35rem; }
button.lg, .btn.lg { padding: .78rem 1.7rem; font-size: .98rem; }
button.block, .btn.block { width: 100%; }

/* Tombol ikon bulat — dipakai di topbar dan kartu produk. */
button.icon, .btn.icon {
  width: 40px; height: 40px; padding: 0; border-radius: 50%;
  background: transparent; color: var(--text-2); box-shadow: none; flex: 0 0 auto;
}
button.icon:hover, .btn.icon:hover { background: var(--surface-2); color: var(--text); box-shadow: none; }
button.icon.sm, .btn.icon.sm { width: 32px; height: 32px; font-size: .82rem; }

/* Tombol aksi mengambang (FAB) — hanya satu per layar. */
.fab {
  position: fixed; right: 1.5rem; bottom: 1.5rem; z-index: 90;
  width: 58px; height: 58px; padding: 0; border-radius: var(--r-lg);
  font-size: 1.15rem; box-shadow: var(--e4);
}
.fab:hover { box-shadow: var(--e5); }

/* Riak. Elemen <span class="ripple"> disuntikkan oleh app.js saat tombol
   ditekan, lalu dibuang sendiri setelah animasinya selesai. */
.ripple {
  position: absolute; border-radius: 50%; transform: scale(0);
  background: currentColor; opacity: .28; pointer-events: none;
  animation: ripple-out 560ms var(--ease-out) forwards;
}
@keyframes ripple-out { to { transform: scale(2.6); opacity: 0; } }

/* ==========================================================================
   Alert
   ========================================================================== */
.alert {
  display: flex; gap: .7rem; align-items: flex-start;
  padding: .8rem 1rem; border-radius: var(--r-sm); margin-bottom: .9rem;
  font-size: .89rem; border: 1px solid transparent; line-height: 1.5;
}
.alert > i:first-child { font-size: 1rem; margin-top: .12rem; flex: 0 0 auto; }
.alert.info { background: var(--info-bg); border-color: color-mix(in srgb, var(--info) 35%, transparent); color: var(--info); }
.alert.ok { background: var(--ok-bg); border-color: color-mix(in srgb, var(--ok) 35%, transparent); color: var(--ok); }
.alert.warn { background: var(--warn-bg); border-color: color-mix(in srgb, var(--warn) 35%, transparent); color: var(--warn); }
.alert.danger { background: var(--danger-bg); border-color: color-mix(in srgb, var(--danger) 35%, transparent); color: var(--danger); }

/* ==========================================================================
   Bar kemajuan
   ========================================================================== */
.bar { height: 8px; border-radius: var(--r-full); background: var(--surface-3); overflow: hidden; margin: .4rem 0; }
.bar > i {
  display: block; height: 100%; background: var(--brand); border-radius: var(--r-full);
  transition: width var(--t-slow) var(--ease);
}
.bar > i.warn { background: var(--warn); }
.bar > i.ok { background: var(--ok); }

/* Indikator memuat tak tentu — dipakai di kepala kartu saat menunggu data. */
.progress-line { height: 3px; background: var(--surface-3); overflow: hidden; border-radius: var(--r-full); }
.progress-line::after {
  content: ''; display: block; height: 100%; width: 40%;
  background: var(--brand); border-radius: var(--r-full);
  animation: indeterminate 1.4s var(--ease) infinite;
}
@keyframes indeterminate {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(300%); }
}

/*
 * Area unggah seret-lepas.
 *
 * Berupa <label> yang membungkus <input type=file>, bukan div berpendengar
 * klik: label memberi aktivasi keyboard dan pembacaan pembaca layar secara
 * cuma-cuma. Input-nya tetap ada di DOM (tidak display:none) supaya tetap
 * dapat difokus dengan Tab.
 */
.drop {
  border: 2px dashed var(--border); border-radius: var(--r-md);
  padding: .9rem 1rem; text-align: center; cursor: pointer;
  background: var(--surface-2); transition: border-color .15s var(--ease), background .15s var(--ease);
}
.drop:hover, .drop:focus-within { border-color: var(--brand); }
.drop.over { border-color: var(--brand); background: var(--brand-soft, var(--surface-3)); }
.drop input[type=file] { display: block; width: 100%; margin-bottom: .5rem; font-size: .85rem; }
.drop input[type=file]:disabled { opacity: .5; cursor: not-allowed; }
.drop:has(input:disabled) { opacity: .6; cursor: not-allowed; }

/* Kerangka (skeleton) untuk katalog yang belum termuat. */
.skeleton {
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 37%, var(--surface-2) 63%);
  background-size: 400% 100%;
  animation: shimmer 1.4s ease-in-out infinite;
  border-radius: var(--r-sm);
}
@keyframes shimmer { 0% { background-position: 100% 0; } 100% { background-position: -100% 0; } }

/* ==========================================================================
   Utilitas
   ========================================================================== */
.muted { color: var(--muted); }
.right { text-align: right; }
.center { text-align: center; }
.nowrap { white-space: nowrap; }
.mono { font-variant-numeric: tabular-nums; }
.flex { display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; }
.hidden { display: none !important; }
.mt { margin-top: 1rem; } .mb { margin-bottom: 1rem; }
.divider { height: 1px; background: var(--border); border: 0; margin: 1.1rem 0; }

/* ==========================================================================
   Badge ajakan instal PWA
   ========================================================================== */
.pwa-banner {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 95;
  background: var(--surface); border-top: 1px solid var(--border); box-shadow: var(--e4);
  padding: .85rem 1rem calc(.85rem + env(safe-area-inset-bottom, 0px));
  animation: pwa-banner-in 320ms var(--ease-out);
}
.pwa-banner-in { max-width: 640px; margin: 0 auto; display: flex; align-items: center; gap: .8rem; }
.pwa-banner-ic {
  width: 40px; height: 40px; border-radius: var(--r-sm); object-fit: cover; flex: 0 0 auto;
  color: var(--brand-on-bg); font-size: 1.6rem; display: grid; place-items: center;
}
.pwa-banner-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: .1rem; }
.pwa-banner-txt strong { font-size: .88rem; }
.pwa-banner-txt span { font-size: .78rem; color: var(--text-2); line-height: 1.35; }
@keyframes pwa-banner-in { from { transform: translateY(100%); } }
@media (max-width: 560px) {
  .pwa-banner-txt span { display: none; }
}

/* ==========================================================================
   Toast (snackbar Material)
   ========================================================================== */
.toast-wrap {
  position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 999;
  display: flex; flex-direction: column; gap: .55rem; max-width: 400px;
}
/*
 * Toast memakai warna semantik sebagai LATAR penuh, jadi warna tulisannya
 * harus berlawanan dengan latar itu — dan arah "berlawanan" berbeda per tema.
 * Pada tema gelap warna semantiknya cerah (hijau/merah terang), sehingga
 * tulisannya gelap; pada tema terang warna semantiknya justru pekat, sehingga
 * tulisannya putih. Karena itu dipakai `--ink-on-solid`, bukan nilai tetap:
 * `color: #fff` yang dulu tertulis di sini menjadi putih di atas krem muda
 * begitu tema terang dipasang — tidak terbaca sama sekali.
 */
.toast {
  display: flex; align-items: center; gap: .6rem;
  padding: .8rem 1.1rem; border-radius: var(--r-sm); color: var(--text);
  box-shadow: var(--e4); font-size: .88rem; background: var(--surface-3);
  animation: snackbar-in 260ms var(--ease-out);
}
.toast.ok { background: var(--ok); color: var(--ink-on-solid); }
.toast.danger { background: var(--danger); color: var(--ink-on-solid); }
.toast.warn { background: var(--warn); color: var(--ink-on-solid); }
@keyframes snackbar-in {
  from { transform: translateY(14px) scale(.96); opacity: 0; }
  to { transform: none; opacity: 1; }
}

/* ==========================================================================
   Modal / dialog
   ========================================================================== */
.modal-wrap { animation: fade-in var(--t) var(--ease); }
/* Berbasis kelas, bukan `#modal > .card`: selektor ber-ID mengalahkan aturan
   media query di blok ponsel, sehingga animasi lembar-naik tidak pernah
   berlaku dan lembarnya muncul dengan gerak dialog desktop. */
.modal-card { animation: dialog-in 260ms var(--ease-out); }
@keyframes fade-in { from { opacity: 0; } }
@keyframes dialog-in {
  from { transform: translateY(18px) scale(.97); opacity: 0; }
  to { transform: none; opacity: 1; }
}

/* ==========================================================================
   Halaman masuk / daftar — gaya boutique, lihat catatan (4) di atas
   ========================================================================== */
.auth-page {
  display: flex; align-items: center; justify-content: center; min-height: 100vh;
  padding: 1.5rem; position: relative; overflow-x: hidden;
  background: var(--bg);
}
/* Garis emas tipis di keempat sisi jendela — pengganti gradasi mencolok versi
   lama; tanda tangan "bingkai" ala butik alih-alih warna penuh satu layar. */
.auth-page::before {
  content: ''; position: fixed; inset: 18px; border: 1px solid var(--border-strong);
  pointer-events: none; z-index: 0;
}
.auth-page::after { content: none; }
/*
 * `overflow-x`, bukan `overflow` penuh, dengan sengaja — .auth-page adalah body
 * (lihat public/daftar.html, masuk.html). Body dengan overflow:hidden pada
 * kedua sumbu ikut MENGUNCI SCROLL SELURUH DOKUMEN: karena <html> tidak
 * menyatakan overflow sendiri, peramban meneruskan (propagate) overflow body
 * ke viewport. Pada formulir pendaftaran yang lebih tinggi dari layar, itu
 * berarti bagian bawah formulir — termasuk tombol "Daftar sekarang" — tidak
 * pernah bisa digulir sampai terlihat.
 */
.auth-card {
  width: 100%; max-width: 440px; background: var(--surface); border: 1px solid var(--border);
  padding: 2.6rem 2.4rem; position: relative; z-index: 1;
  animation: dialog-in 420ms var(--ease-out);
}
.auth-card.wide { max-width: 760px; }
.auth-card .logo {
  font-family: 'Playfair Display', serif; font-size: 1.7rem; font-weight: 600;
  color: var(--text); margin-bottom: 1.3rem; letter-spacing: .01em;
  display: flex; align-items: center; gap: .6rem; justify-content: center;
}
.auth-card .logo span { color: var(--text); }
.auth-card > p.muted { text-align: center; margin-bottom: 1.6rem; }
.auth-card h3 {
  font-family: 'Playfair Display', serif; font-weight: 600; font-size: 1.15rem;
  padding-bottom: .5rem; border-bottom: 1px solid var(--border); margin-bottom: 1.1rem;
}

/* ==========================================================================
   Sisa halaman lama
   ========================================================================== */
.tree-row td:first-child { padding-left: calc(var(--d, 0) * 20px + .6rem); }
.legal-foot {
  font-size: .78rem; color: var(--muted); padding: 1.2rem 0 0;
  border-top: 1px solid var(--border); margin-top: 2rem; line-height: 1.7;
}

/* ==========================================================================
   ETALASE — komponen khusus halaman publik & katalog belanja
   --------------------------------------------------------------------------
   Bahasa desainnya sengaja BEDA dari panel admin di atas: judul serif
   (Playfair Display), sudut tajam (tanpa border-radius) ganti pil membulat,
   garis tipis satu piksel ganti bayangan berlapis untuk kedalaman, dan huruf
   kapital kecil berspasi lebar untuk label/navigasi — kosakata visual toko
   butik/editorial, bukan aplikasi Material. Token warna (--brand, --surface,
   --text, dst.) tetap dipakai sepenuhnya; yang berubah hanya bentuk.
   ========================================================================== */

/* -- kerangka halaman toko ------------------------------------------------ */
.shop-body { background: var(--bg); }
.wrap { max-width: 1180px; margin: 0 auto; padding: 0 1.5rem; }
.section { padding: 4rem 0; }
.section-head { text-align: center; max-width: 640px; margin: 0 auto 2.5rem; }
.section-head h2 { font-size: 2rem; }
.section-head p { color: var(--muted); font-size: 1.02rem; }

/* Judul serif di seluruh etalase — satu-satunya tempat Playfair Display
   dipakai. Panel admin tetap Roboto di semua bobot (lihat catatan berkas). */
.shop-body h1, .shop-body h2, .shop-body h3,
.auth-page h1, .auth-page h2, .auth-page h3 {
  font-family: 'Playfair Display', serif; font-weight: 600; letter-spacing: 0;
}

.eyebrow {
  display: inline-flex; align-items: center; gap: .5rem;
  font-size: .72rem; font-weight: 600; text-transform: uppercase; letter-spacing: .18em;
  color: var(--brand-on-bg); background: none; border: 0;
  padding: 0 0 .6rem; margin-bottom: 1rem; border-bottom: 1px solid var(--border);
}

/* Garis tipis emas — satu elemen dekoratif yang dipakai berulang sebagai
   pemisah antara judul dan isi (pengganti bayangan/pil versi lama). */
.rule-gold { width: 56px; height: 2px; background: var(--brand); border: 0; margin: 1.1rem 0; }
.section-head .rule-gold { margin: 1.1rem auto; }

/* -- tombol etalase — sudut tajam, huruf kapital berspasi, TANPA menyentuh
   selektor `button, .btn` global yang dipakai dashboard di atas ------------ */
.shop-body button, .shop-body .btn,
.auth-page button, .auth-page .btn {
  border-radius: 0; box-shadow: none;
  font-size: .78rem; font-weight: 600; letter-spacing: .1em; text-transform: uppercase;
  padding: .75rem 1.5rem;
}
.shop-body button:hover, .shop-body .btn:hover,
.auth-page button:hover, .auth-page .btn:hover { box-shadow: none; }
.shop-body button.sm, .shop-body .btn.sm, .auth-page button.sm, .auth-page .btn.sm { padding: .5rem 1rem; font-size: .7rem; }
.shop-body button.lg, .shop-body .btn.lg, .auth-page button.lg, .auth-page .btn.lg { padding: 1rem 2rem; font-size: .82rem; }
.shop-body button.icon, .shop-body .btn.icon,
.auth-page button.icon, .auth-page .btn.icon { border-radius: 0; padding: 0; }
.shop-body button.neutral, .shop-body .btn.neutral,
.auth-page button.neutral, .auth-page .btn.neutral { border: 1px solid var(--border-strong); }
.shop-body button.ghost, .shop-body .btn.ghost,
.auth-page button.ghost, .auth-page .btn.ghost { border: 1px solid var(--brand); background: none; }
.shop-body button.ghost:hover, .shop-body .btn.ghost:hover,
.auth-page button.ghost:hover, .auth-page .btn.ghost:hover { background: var(--brand-light); }
.shop-body .badge, .auth-page .badge { border-radius: 0; }
.shop-body .card, .auth-page .card { border-radius: 0; box-shadow: none; }
.shop-body .card.raised, .auth-page .card.raised { box-shadow: none; border: 1px solid var(--border); }
.shop-body input, .shop-body select, .shop-body textarea,
.auth-page input, .auth-page select, .auth-page textarea { border-radius: 0; }
.shop-body .alert, .auth-page .alert { border-radius: 0; }
.shop-body .pwa-banner { box-shadow: none; border-top: 1px solid var(--border-strong); }

/* -- bilah navigasi toko -------------------------------------------------- */
.shopbar {
  position: sticky; top: 0; z-index: 60;
  background: var(--bar-bg);
  backdrop-filter: saturate(140%) blur(10px);
  -webkit-backdrop-filter: saturate(140%) blur(10px);
  border-bottom: 1px solid var(--border);
}
.shopbar.stuck { box-shadow: 0 1px 0 var(--border); }
.shopbar-in { display: flex; align-items: center; gap: 2.2rem; height: 76px; }
.shopbar .brand { display: inline-flex; align-items: center; }
.shopbar .brand span {
  font-family: 'Playfair Display', serif; font-weight: 600; font-size: 1.2rem; letter-spacing: .01em;
}
.shopbar .links { display: flex; gap: 1.7rem; margin-left: .4rem; }
.shopbar .links a {
  position: relative; padding: .3rem 0; font-size: .78rem; color: var(--text-2);
  text-transform: uppercase; letter-spacing: .12em; font-weight: 500;
  transition: color var(--t-fast) var(--ease);
}
.shopbar .links a::after {
  content: ''; position: absolute; left: 0; right: 100%; bottom: 0; height: 1px;
  background: var(--brand); transition: right var(--t) var(--ease);
}
.shopbar .links a:hover, .shopbar .links a.active { background: none; color: var(--text); }
.shopbar .links a:hover::after, .shopbar .links a.active::after { right: 0; }

/*
 * -- lencana jumlah item keranjang / notifikasi ---------------------------
 * `.cart-btn` adalah <span> pembungkus di sekitar tombol ikon, BUKAN kelas
 * pada tombolnya sendiri. Itu disengaja: `button, .btn` (efek riak, di atas)
 * menyetel `overflow: hidden` pada tombolnya untuk membatasi riaknya, dan
 * lencana ini diposisikan `top:-2px; right:-2px` — sedikit menonjol ke luar
 * kotak tombol. Bila lencananya jadi ANAK tombol itu sendiri, bagian yang
 * menonjol tadi ikut terpotong overflow:hidden si tombol; angkanya terlihat
 * terpangkas di tepi atas. Membungkusnya di elemen terpisah yang tidak
 * memiliki overflow:hidden menghindarinya sepenuhnya.
 */
.cart-btn { position: relative; display: inline-flex; }
.cart-count {
  position: absolute; top: -2px; right: -2px;
  min-width: 17px; height: 17px; padding: 0 4px;
  border-radius: var(--r-full); background: var(--accent); color: var(--brand-ink);
  font-size: .64rem; font-weight: 700; text-align: center;
  display: flex; align-items: center; justify-content: center; line-height: 1;
  box-shadow: 0 0 0 2px var(--bg);
  pointer-events: none;
}
.cart-count:empty, .cart-count.zero { display: none; }
.cart-count.bump { animation: bump 420ms var(--ease-out); }
@keyframes bump {
  0% { transform: scale(1); } 35% { transform: scale(1.45); } 100% { transform: scale(1); }
}

/* -- hero — susunan asimetris, satu foto besar berbingkai (bukan tumpukan
   kartu melayang) ---------------------------------------------------------- */
.hero { position: relative; overflow: hidden; padding: 4.5rem 0 5rem; background: var(--bg); }
.hero-in { position: relative; z-index: 1; display: grid; gap: 4rem; align-items: center; grid-template-columns: 1fr .85fr; }
.hero h1 { font-size: clamp(2.3rem, 4.8vw, 3.6rem); line-height: 1.08; color: var(--text); font-weight: 600; }
.hero p.lead { font-size: 1.08rem; color: var(--text-2); max-width: 50ch; }
.hero-cta { display: flex; gap: 1rem; flex-wrap: wrap; margin-top: 2rem; }
/* Tiga angka bukti dalam SATU baris.
   Versi flex-wrap sebelumnya memakai padding tetap, sehingga di kolom hero yang
   hanya selebar setengah layar butir ketiga selalu terlempar ke baris kedua —
   2+1 yang terbaca seperti tata letak yang gagal, bukan yang disengaja. Grid
   dengan `minmax(0,1fr)` membuat ketiganya menyusut bersama alih-alih membungkus. */
.hero-proof {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0; margin-top: 2.8rem; border-top: 1px solid var(--border);
}
.hero-proof > div { padding: 1.1rem 1.1rem 0 0; margin-right: 1.1rem; border-right: 1px solid var(--border); min-width: 0; }
.hero-proof > div:last-child { border-right: 0; margin-right: 0; padding-right: 0; }
.hero-proof .n { overflow-wrap: anywhere; }
@media (max-width: 520px) {
  .hero-proof { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .hero-proof > div:nth-child(2) { border-right: 0; margin-right: 0; }
}

/* -- bilah promo di atas hero -------------------------------------------- */
.promo-bar {
  background: var(--brand); color: #1a1205;
  border-bottom: 1px solid rgba(0, 0, 0, .12);
}
.promo-bar-in {
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  padding-top: .6rem; padding-bottom: .6rem;
}
.promo-bar-teks { flex: 1 1 320px; font-size: .92rem; line-height: 1.45; }
.promo-bar-teks i { margin-right: .35rem; }
/* Sisa slot dibuat menonjol tersendiri: angka inilah yang menggerakkan orang,
   bukan kalimat di sekelilingnya. */
.promo-bar-sisa {
  display: inline-block; margin-left: .5rem; padding: .1rem .55rem;
  background: rgba(0, 0, 0, .82); color: #fff; border-radius: 99px;
  font-size: .8rem; font-weight: 700; letter-spacing: .02em; white-space: nowrap;
}
.promo-bar .btn { flex: 0 0 auto; background: #1a1205; color: var(--brand); border-color: #1a1205; }
.promo-bar .btn:hover { background: #000; color: var(--brand); }

/* -- poster promo di kolom kanan hero ------------------------------------ */
.promo-poster {
  display: block; position: relative; border: 1px solid var(--border-strong);
  padding: 10px; background: var(--surface); box-shadow: var(--e2);
  transition: transform var(--t) var(--ease), box-shadow var(--t) var(--ease);
}
.promo-poster img {
  display: block; width: 100%; height: auto;
  /* Poster promo lazimnya potret memanjang. Tingginya dipatok agar poster utuh
     dari judul sampai ajakan bergabung tetap masuk satu layar bersama hero —
     bukan `78vh` yang membuat bagian bawahnya terpotong garis lipat di layar
     laptop. `object-fit: contain` menjaga isinya tidak pernah terpangkas: pada
     gambar yang seluruh isinya teks, memotong sama saja dengan merusak. */
  max-height: min(70vh, 620px); object-fit: contain; object-position: center;
}
.promo-poster:hover { transform: translateY(-3px); box-shadow: var(--e3); }
.promo-poster-tag {
  position: absolute; left: 10px; right: 10px; bottom: 10px;
  background: rgba(0, 0, 0, .82); color: #fff; text-align: center;
  padding: .5rem .6rem; font-size: .84rem; letter-spacing: .02em;
}
.promo-poster-tag b { color: var(--brand); font-size: 1.1em; }
@media (prefers-reduced-motion: reduce) { .promo-poster:hover { transform: none; } }
.hero-proof .n { font-size: 1.4rem; font-weight: 600; color: var(--text); font-family: 'Playfair Display', serif; }
.hero-proof .l { font-size: .74rem; color: var(--muted); text-transform: uppercase; letter-spacing: .08em; }

/* Satu foto produk besar dalam bingkai bergaris tipis — lihat index.html,
   fungsi renderHeroArt(); bukan lagi tiga kartu melayang bertumpuk. */
.hero-art { position: relative; }
.hero-art .frame {
  position: relative; aspect-ratio: 3 / 4; border: 1px solid var(--border-strong);
  padding: 14px; background: var(--surface);
}
.hero-art .frame-in {
  position: relative; width: 100%; height: 100%; overflow: hidden;
  background: linear-gradient(135deg, var(--surface-2), var(--surface-3));
  display: flex; align-items: center; justify-content: center;
}
.hero-art .frame-in img { width: 100%; height: 100%; object-fit: cover; }
.hero-art .frame-in .ph { font-size: 3.4rem; color: color-mix(in srgb, var(--brand-on-bg) 55%, var(--surface-3)); }
.hero-art .tag {
  position: absolute; left: -1px; bottom: 22px; background: var(--brand); color: var(--brand-ink);
  padding: .6rem 1.1rem; font-size: .82rem; font-weight: 600;
}

/* -- kartu produk etalase — media persegi, sudut tajam, tanpa bayangan ---- */
.product-grid { display: grid; gap: 0; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
.product-grid .product-card { border-width: 1px 0 1px 1px; }
.product-grid .product-card:last-child,
.product-grid .product-card:nth-last-child(-n+12) { border-right-width: 1px; }

.product-card {
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--border);
  transition: border-color var(--t) var(--ease);
}
.product-card:hover { border-color: var(--brand); }

/*
 * Pembungkus tipis di sekeliling bingkai foto (`.product-media`) DAN
 * `.product-quick` — sengaja terpisah dari `.product-media` sendiri.
 *
 * `.product-media` mengunci rasio 1:1 dan `overflow:hidden`: itu yang membuat
 * fotonya selalu persegi rapi. `.product-quick` pada layar sentuh (`hover:none`,
 * yaitu SEMUA ponsel) berubah dari overlay melayang (`position:absolute`)
 * menjadi baris tombol yang menetap di bawah foto (`position:static`) —
 * sengaja begitu, karena ponsel tidak punya hover untuk memunculkan overlay.
 *
 * Bila `.product-quick` masih anak `.product-media`, "menetap" berarti ia ikut
 * masuk ke kotak yang tingginya dikunci rasio 1:1 dan flex row (arah bawaan
 * `.product-media`) — foto dan baris tombol saling berebut ruang yang sama,
 * terjepit berdampingan, lalu terpotong `overflow:hidden`. Itulah sebabnya foto
 * tampak terpotong dan tombol "Detail" tergencet di ponsel.
 *
 * Solusinya bukan menghapus kuncian 1:1 (foto memang harus tetap persegi),
 * melainkan memindahkan `.product-quick` keluar dari kotak itu: sebagai anak
 * `.product-media-wrap`, saat statis ia mengalir sebagai baris normal DI BAWAH
 * `.product-media`, dengan tinggi wrapper mengikuti keduanya — tanpa
 * memaksakan satu kotak persegi menampung dua hal sekaligus.
 */
.product-media-wrap { position: relative; }

.product-media {
  position: relative; aspect-ratio: 1 / 1; overflow: hidden;
  background: linear-gradient(135deg, var(--surface-2), var(--surface-3));
  display: flex; align-items: center; justify-content: center;
}
.product-media img { width: 100%; height: 100%; object-fit: cover; transition: transform var(--t-slow) var(--ease); }
.product-card:hover .product-media img { transform: scale(1.04); }
.product-media .ph { font-size: 2.6rem; color: color-mix(in srgb, var(--brand-on-bg) 55%, var(--surface-3)); }
.product-media .tags { position: absolute; top: 0; left: 0; display: flex; gap: 0; flex-wrap: wrap; }
.product-media .tags .badge { border-radius: 0; box-shadow: none; background: var(--bg); }

.product-quick {
  position: absolute; left: 0; right: 0; bottom: 0; padding: .6rem;
  display: flex; gap: .5rem; background: rgba(10, 10, 10, .82);
  opacity: 0; transform: translateY(8px);
  transition: opacity var(--t) var(--ease), transform var(--t) var(--ease);
}
.product-card:hover .product-quick, .product-card:focus-within .product-quick { opacity: 1; transform: none; }
@media (hover: none) {
  .product-quick { opacity: 1; transform: none; position: static; margin-top: .7rem; background: none; padding: 0; }
}

.product-body { padding: 1.2rem 1.2rem 1.3rem; display: flex; flex-direction: column; flex: 1; gap: .3rem; }
.product-body .cat { font-size: .68rem; text-transform: uppercase; letter-spacing: .1em; color: var(--muted); font-weight: 600; }
.product-body h3 { font-size: 1.05rem; font-weight: 600; margin: .15rem 0 0; line-height: 1.35; }
.product-body h3 a { color: var(--text); }
.product-body h3 a:hover { color: var(--brand-on-bg); }
.product-body .desc {
  font-size: .84rem; color: var(--muted); margin: .3rem 0 .4rem;
  display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.product-price {
  margin-top: auto; padding-top: .7rem; border-top: 1px solid var(--border);
  display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap;
}
.product-price .now {
  font-size: .92rem; font-weight: 600; color: var(--text); letter-spacing: .04em;
  font-variant-caps: all-small-caps;
}
.product-price .was { font-size: .8rem; color: var(--muted); text-decoration: line-through; }
.product-price .save { font-size: .72rem; font-weight: 500; color: var(--ok); background: var(--ok-bg); padding: .1rem .4rem; }
.product-foot { display: flex; gap: .5rem; margin-top: .9rem; }
.product-foot .btn, .product-foot button { flex: 1; }

/* -- panel penyaring ------------------------------------------------------ */
.filter-bar {
  display: flex; gap: .7rem; align-items: center; flex-wrap: wrap;
  padding: 1rem 1.2rem; background: var(--surface); border: 1px solid var(--border);
  margin-bottom: 0;
}
.filter-bar .search { position: relative; flex: 1; min-width: 220px; }
.filter-bar .search i { position: absolute; left: .85rem; top: 50%; transform: translateY(-50%); color: var(--muted); font-size: .9rem; }
.filter-bar .search input { padding-left: 2.4rem; }

/* Kategori sebagai tab bergaris bawah — bukan lagi pil terisi ala Material. */
.chips {
  display: flex; gap: 0; flex-wrap: wrap; border: 1px solid var(--border); border-top: 0;
  background: var(--surface); margin-bottom: 1.8rem;
}
.chip {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .8rem 1.1rem; border: 0; border-right: 1px solid var(--border);
  background: none; border-radius: 0;
  font-size: .76rem; text-transform: uppercase; letter-spacing: .08em; font-weight: 500;
  color: var(--text-2); cursor: pointer; white-space: nowrap;
  transition: color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.chip:hover { color: var(--text); background: var(--surface-2); }
.chip.active { background: var(--brand-light); border-color: var(--border); color: var(--brand-on-bg); font-weight: 600; }
.chip.active::before { content: ''; }

/* -- laci keranjang ------------------------------------------------------- */
.drawer-scrim {
  position: fixed; inset: 0; z-index: 200;
  background: var(--scrim);
  animation: fade-in var(--t) var(--ease);
}
.drawer {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 201;
  width: min(430px, 100%); background: var(--surface); border-left: 1px solid var(--border);
  display: flex; flex-direction: column;
  animation: drawer-in 300ms var(--ease-out);
}
@keyframes drawer-in { from { transform: translateX(100%); } }
.drawer-head {
  display: flex; align-items: center; gap: .7rem;
  padding: 1.3rem 1.4rem; border-bottom: 1px solid var(--border); flex: 0 0 auto;
}
.drawer-head h2 { margin: 0; flex: 1; font-size: 1.15rem; }
.drawer-body { flex: 1; overflow-y: auto; padding: 0 1.4rem; }
.drawer-foot { flex: 0 0 auto; padding: 1.2rem 1.4rem; border-top: 1px solid var(--border); background: var(--surface-1); }

.cart-line { display: flex; gap: .9rem; padding: 1rem 0; border-bottom: 1px solid var(--border); }
.cart-line:last-child { border-bottom: 0; }
.cart-line .thumb {
  width: 64px; height: 64px; flex: 0 0 auto; border: 1px solid var(--border); overflow: hidden;
  background: var(--surface-2); display: flex; align-items: center; justify-content: center;
  color: color-mix(in srgb, var(--brand-on-bg) 58%, var(--surface-3));
}
.cart-line .thumb img { width: 100%; height: 100%; object-fit: cover; }
.cart-line .info { flex: 1; min-width: 0; }
.cart-line .info .n { font-size: .9rem; font-weight: 500; line-height: 1.35; }
.cart-line .info .p { font-size: .82rem; color: var(--muted); }

/* Penambah/pengurang jumlah. */
.stepper {
  display: inline-flex; align-items: center; border: 1px solid var(--border-strong);
  overflow: hidden; background: var(--surface);
}
.stepper button {
  width: 32px; height: 32px; padding: 0; border-radius: 0; background: transparent;
  color: var(--text-2); box-shadow: none; font-size: .75rem;
}
.stepper button:hover { background: var(--surface-2); color: var(--text); box-shadow: none; }
.stepper button:disabled { opacity: .3; }
.stepper .q { min-width: 32px; text-align: center; font-size: .88rem; font-variant-numeric: tabular-nums; font-weight: 500; }

.total-row { display: flex; justify-content: space-between; font-size: .9rem; padding: .35rem 0; color: var(--text-2); }
.total-row.grand {
  font-size: 1.05rem; font-weight: 600; color: var(--text); font-family: 'Playfair Display', serif;
  padding-top: .8rem; margin-top: .5rem; border-top: 1px solid var(--border);
}

/* -- keadaan kosong ------------------------------------------------------- */
.empty { text-align: center; padding: 3rem 1rem; color: var(--muted); }
.empty i { font-size: 2.6rem; color: var(--border-strong); display: block; margin-bottom: .9rem; }
.empty h3 { color: var(--text-2); font-weight: 500; }

/* -- langkah checkout — nomor dalam kotak bergaris tipis, bukan lingkaran
   terisi ---------------------------------------------------------------- */
.steps { display: flex; align-items: center; gap: .5rem; margin-bottom: 1.8rem; flex-wrap: wrap; }
.steps .step {
  display: flex; align-items: center; gap: .5rem; font-size: .78rem; color: var(--muted);
  text-transform: uppercase; letter-spacing: .08em; font-weight: 500;
}
.steps .step .n {
  width: 24px; height: 24px; border: 1px solid var(--border-strong); flex: 0 0 auto;
  display: grid; place-items: center; font-size: .74rem; font-weight: 600;
  background: none; color: var(--muted);
  transition: background var(--t) var(--ease), color var(--t) var(--ease), border-color var(--t) var(--ease);
}
.steps .step.active { color: var(--text); }
.steps .step.active .n { background: var(--brand); color: var(--brand-ink); border-color: var(--brand); }
.steps .step.done .n { background: none; color: var(--ok); border-color: var(--ok); }
.steps .sep { flex: 1; min-width: 18px; height: 1px; background: var(--border-strong); }

/* Tata letak checkout: daftar produk di kiri, ringkasan lengket di kanan.
   Ambang 900px sengaja lebih tinggi dari titik henti umum — di bawah itu
   ringkasan yang menyempit membuat angka rupiah terpotong. */
.checkout-grid { grid-template-columns: 1.55fr 1fr; align-items: start; gap: 0; }
.checkout-grid > div:first-child .card { border-right: 0; }
@media (max-width: 900px) {
  .checkout-grid { grid-template-columns: 1fr; }
  .checkout-grid #ringkasan { position: static !important; }
  .checkout-grid > div:first-child .card { border-right: 1px solid var(--border); }
}

/* -- kartu fitur pada halaman depan — ikon dalam kotak bergaris, tanpa
   latar terisi ------------------------------------------------------------ */
.feature { display: flex; gap: 1.1rem; align-items: flex-start; }
.feature .ic {
  width: 48px; height: 48px; flex: 0 0 auto; border: 1px solid var(--border-strong);
  display: grid; place-items: center; font-size: 1.1rem;
  background: none; color: var(--brand-on-bg);
}
.feature h3 { margin-bottom: .3rem; font-size: 1.02rem; }
.feature p { margin: 0; font-size: .9rem; }

/* ==========================================================================
   HALAMAN DOKUMEN — kebijakan, syarat & ketentuan, bantuan
   --------------------------------------------------------------------------
   Aturan di bawah `.doc-body` menata HTML yang datang dari editor WYSIWYG.
   Karena markup itu tidak membawa kelas apa pun, penataannya harus lewat
   selektor elemen — dan karena itu semuanya WAJIB dibatasi di dalam .doc-body
   agar tidak bocor ke tabel dan daftar di halaman lain.
   ========================================================================== */
.doc-layout { display: grid; grid-template-columns: 268px 1fr; gap: 3rem; align-items: start; }

.doc-nav { position: sticky; top: 88px; }
.doc-nav .group {
  font-size: .7rem; text-transform: uppercase; letter-spacing: .09em;
  color: var(--muted); margin: 1.2rem .9rem .4rem; font-weight: 500;
}
.doc-nav .group:first-child { margin-top: 0; }
.doc-nav a {
  display: flex; align-items: center; gap: .65rem;
  padding: .5rem .9rem; border-left: 2px solid transparent;
  color: var(--text-2); font-size: .85rem; margin-bottom: 2px;
  text-transform: uppercase; letter-spacing: .05em;
  transition: border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.doc-nav a i { width: 1.05rem; text-align: center; font-size: .88rem; color: var(--muted); }
.doc-nav a:hover { border-left-color: var(--border-strong); color: var(--text); }
.doc-nav a.active { border-left-color: var(--brand); color: var(--brand-on-bg); font-weight: 600; }
.doc-nav a.active i { color: var(--brand-on-bg); }

/* Lebar baca dibatasi: teks hukum yang membentang penuh layar lebar sangat
   melelahkan dibaca, dan halaman ini justru yang paling perlu terbaca. */
.doc-head { max-width: 74ch; margin-bottom: 2rem; }
.doc-ic {
  width: 56px; height: 56px; border: 1px solid var(--border-strong); margin-bottom: 1.2rem;
  display: grid; place-items: center; font-size: 1.4rem;
  background: none; color: var(--brand-on-bg);
}
.doc-head h1 { font-size: clamp(1.8rem, 3.4vw, 2.4rem); }
.doc-head .lead { font-size: 1.06rem; color: var(--text-2); max-width: 68ch; }

.doc-body { max-width: 74ch; font-size: .97rem; line-height: 1.75; color: var(--text-2); }
.doc-body > *:first-child { margin-top: 0; }
.doc-body h2 {
  font-size: 1.22rem; margin: 2.4rem 0 .7rem; color: var(--text);
  padding-top: 1.4rem; border-top: 1px solid var(--border);
}
.doc-body h2:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.doc-body h3 { font-size: 1.02rem; margin: 1.6rem 0 .5rem; color: var(--text); }
.doc-body h4 { font-size: .94rem; margin: 1.2rem 0 .4rem; color: var(--text); }
.doc-body p { margin: 0 0 1rem; }
.doc-body ul, .doc-body ol { margin: 0 0 1.15rem; padding-left: 1.35rem; }
.doc-body li { margin-bottom: .5rem; }
.doc-body li::marker { color: var(--brand-on-bg); }
.doc-body a { text-decoration: underline; text-underline-offset: 2px; }
.doc-body blockquote {
  margin: 1.2rem 0; padding: .3rem 0 .3rem 1.15rem;
  border-left: 3px solid var(--brand); color: var(--muted); font-style: italic;
}
.doc-body hr { border: 0; border-top: 1px solid var(--border); margin: 2rem 0; }
.doc-body table { margin: 1.2rem 0; font-size: .9rem; }
.doc-body img { max-width: 100%; height: auto; border-radius: var(--r-sm); margin: 1rem 0; }
.doc-body figure { margin: 1.4rem 0; }
.doc-body figcaption { font-size: .84rem; color: var(--muted); margin-top: .5rem; }
.doc-body .alert { margin: 1.3rem 0; }

.doc-foot { max-width: 74ch; margin-top: 3rem; padding-top: 1.4rem; border-top: 1px solid var(--border); }
.doc-foot p { margin: 0; font-size: .89rem; }

@media (max-width: 900px) {
  .doc-layout { grid-template-columns: 1fr; gap: 1.6rem; }
  /* Di layar sempit daftar isi menjadi baris chip yang dapat digulir mendatar,
     bukan kolom setinggi layar yang mendorong isi halaman ke bawah lipatan. */
  .doc-nav {
    position: static; display: flex; gap: .4rem; overflow-x: auto;
    padding-bottom: .5rem; scrollbar-width: thin;
  }
  .doc-nav .group { display: none; }
  .doc-nav a {
    flex: 0 0 auto; border: 1px solid var(--border-strong); border-left: 1px solid var(--border-strong);
    background: var(--surface); white-space: nowrap;
  }
  .doc-nav a.active { border-color: var(--brand); }
}

/* -- footer toko -----------------------------------------------------------
   Tanda merek besar berhuruf serif di tengah, garis emas tipis, lalu kolom
   tautan seperti biasa dan baris legal di bawahnya — pola "majalah" alih-alih
   footer gelap kebiruan versi lama. */
.shop-foot { background: var(--bg); color: var(--muted); padding: 4rem 0 2.5rem; font-size: .88rem; margin-top: 4rem; border-top: 1px solid var(--border); }
.shop-foot-brand { text-align: center; max-width: 46ch; margin: 0 auto; }
.shop-foot-mark { font-family: 'Playfair Display', serif; font-size: 1.6rem; font-weight: 600; color: var(--text); }
.shop-foot-tagline { color: var(--muted); line-height: 1.75; margin: .8rem 0 0; }
.shop-foot .rule-gold { margin: 2rem auto; }
.shop-foot a { color: var(--text-2); }
.shop-foot a:hover { color: var(--brand-on-bg); }
.shop-foot h4 { color: var(--text); font-size: .74rem; text-transform: uppercase; letter-spacing: .12em; margin-bottom: 1rem; font-weight: 600; }
.shop-foot .cols { display: grid; gap: 2.2rem; grid-template-columns: 1fr 1fr 1fr; text-align: center; }
.shop-foot .bottom { border-top: 1px solid var(--border); margin-top: 2.4rem; padding-top: 1.5rem; font-size: .8rem; line-height: 1.9; text-align: center; }
.shop-foot .social { display: flex; gap: .5rem; margin-top: 1.2rem; justify-content: center; }
.shop-foot .social a {
  width: 36px; height: 36px; border: 1px solid var(--border-strong); display: grid; place-items: center;
  background: none; color: var(--text-2);
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.shop-foot .social a:hover { background: var(--brand); color: var(--brand-ink); border-color: var(--brand); }

/* ==========================================================================
   EDITOR WYSIWYG (panel admin)
   ========================================================================== */
.editor {
  border: 1px solid var(--border-strong); border-radius: var(--r);
  background: var(--surface); overflow: hidden;
  transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.editor:focus-within { border-color: var(--brand); box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 14%, transparent); }

.editor-bar {
  display: flex; align-items: center; gap: .15rem; flex-wrap: wrap;
  padding: .45rem .55rem; background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  position: sticky; top: var(--topbar-h); z-index: 10;
}
.editor-sep { width: 1px; height: 20px; background: var(--border-strong); margin: 0 .35rem; }
.editor-spacer { flex: 1; }

.editor-btn {
  width: 32px; height: 32px; padding: 0; border-radius: var(--r-xs);
  background: transparent; color: var(--text-2); box-shadow: none;
  font-size: .82rem; flex: 0 0 auto;
}
.editor-btn:hover { background: var(--surface-3); color: var(--text); box-shadow: none; }
.editor-btn.aktif { background: var(--brand-light); color: var(--brand-on-bg); }
.editor-btn:disabled { opacity: .3; }

.editor-blok {
  width: auto; min-width: 150px; padding: .3rem 1.9rem .3rem .55rem;
  font-size: .84rem; border-radius: var(--r-xs); background-position: right .35rem center;
}

/* Isi editor memakai kelas .doc-body juga, sehingga apa yang terlihat saat
   menyunting sama persis dengan tampilannya setelah terbit. */
.editor-isi {
  padding: 1.6rem 1.8rem; min-height: 420px; max-height: 62vh; overflow-y: auto;
  outline: none; max-width: none;
}
.editor-isi:empty::before {
  content: 'Mulai menulis di sini…'; color: var(--muted);
}
/* Batas bantu lebar baca — memperlihatkan di mana teks akan terpotong saat
   terbit, tanpa benar-benar membatasi area sunting. */
.editor-isi > * { max-width: 74ch; }

.editor-sumber {
  width: 100%; min-height: 420px; max-height: 62vh; border: 0; border-radius: 0;
  padding: 1.2rem 1.4rem; font-family: ui-monospace, 'Cascadia Mono', Consolas, monospace;
  font-size: .84rem; line-height: 1.65; resize: vertical; background: var(--surface-2);
}
.editor-sumber:focus { box-shadow: none; }

.editor-kaki {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  padding: .5rem .9rem; border-top: 1px solid var(--border);
  background: var(--surface-1); font-size: .78rem; color: var(--muted);
}

/* Pratinjau di dalam dialog */
.doc-preview { max-height: 62vh; overflow-y: auto; padding: .3rem; }

@media (max-width: 860px) {
  .editor-bar { position: static; }
  .editor-isi, .editor-sumber { padding: 1rem; min-height: 320px; }
}

/* ==========================================================================
   DOKUMEN PENGIRIMAN — label alamat & surat jalan
   --------------------------------------------------------------------------
   Ditata dalam milimeter, bukan piksel: keluarannya kertas, dan satu-satunya
   satuan yang berarti di kertas adalah satuan fisik. Ukuran font pun memakai
   `pt` supaya hasil cetak sama di layar mana pun.

   Blok ini berlaku di layar (sebagai pratinjau) maupun di printer. Perbedaan
   khusus cetak ada di @media print pada akhir berkas.
   ========================================================================== */
.cetak-body { background: var(--surface-3); }

.cetak-bar {
  position: sticky; top: 0; z-index: 50;
  background: var(--surface); border-bottom: 1px solid var(--border); box-shadow: var(--e1);
}
.divider-v { width: 1px; height: 22px; background: var(--border-strong); }

#lembar { padding: 1.5rem 0 3rem; }

/* -- label alamat --------------------------------------------------------- */
.label-wrap { display: flex; flex-direction: column; align-items: center; gap: 1.2rem; }
.label-wrap.a4 {
  display: grid; grid-template-columns: repeat(2, 100mm); justify-content: center;
}

/*
 * `.ship-label`, bukan `.label` — nama generik itu sebelumnya bentrok dengan
 * `<div class="label">` yang dipakai `.stat` di puluhan halaman (caption kecil
 * di atas angka statistik, lihat `.stat .label`). Spesifisitas CSS membuat
 * warna/ukuran fontnya menang dari aturan `.stat .label`, tapi width:100mm;
 * height:150mm; background:#fff di sini ikut menempel karena properti itu
 * tidak disentuh `.stat .label` sama sekali — hasilnya kotak putih raksasa di
 * balik setiap label statistik. Di tema terang lama itu tak kelihatan (putih
 * di atas putih); di tema gelap ini jadi kotak putih mencolok. */
.ship-label {
  width: 100mm; height: 150mm;
  background: #fff; color: #000;
  padding: 5mm; box-sizing: border-box;
  display: flex; flex-direction: column;
  font-size: 9pt; line-height: 1.35;
  border: 1px solid var(--border-strong);
  box-shadow: var(--e2);
  /* Label tidak boleh terpotong di tengah halaman, apa pun modenya. */
  break-inside: avoid; page-break-inside: avoid;
}

.label-atas {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 3mm;
  padding-bottom: 2mm; border-bottom: 0.6mm solid #000;
}
.label-merek strong { font-size: 13pt; letter-spacing: -.02em; display: block; }
.label-kurir {
  font-size: 8.5pt; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
  margin-top: .8mm;
}
.label-bc { text-align: right; }
.label-bc svg { display: block; }

.label-blok { padding: 2.5mm 0; border-bottom: 0.3mm dashed #999; }
.label-judul {
  font-size: 7pt; text-transform: uppercase; letter-spacing: .12em;
  color: #444; font-weight: 700; margin-bottom: 1mm;
}
.label-pengirim { font-size: 8pt; line-height: 1.3; }

/* Blok penerima mengambil sisa ruang dan dicetak paling besar — ini satu-satunya
   bagian yang benar-benar dibaca kurir di jalan. */
.label-penerima-blok { flex: 1; border-bottom: 0; }
.label-penerima-nama { font-size: 14pt; font-weight: 700; line-height: 1.2; }
.label-penerima-hp { font-size: 11pt; font-weight: 700; margin: .6mm 0 1.2mm; }
.label-penerima-alamat { font-size: 10pt; line-height: 1.4; }

.label-bawah { border-top: 0.6mm solid #000; padding-top: 2mm; }
.label-kv { display: inline-block; margin-right: 4mm; font-size: 9pt; }
.label-kv b { font-size: 11pt; }
.label-awb { margin-top: 2mm; text-align: center; }
.label-awb svg { display: inline-block; }
.label-awb-kosong {
  font-size: 8pt; color: #666; border: 0.3mm dashed #999;
  padding: 3mm; border-radius: 1mm;
}

/* -- surat jalan ---------------------------------------------------------- */
.surat {
  width: 194mm; min-height: 270mm;
  margin: 0 auto 1.5rem; padding: 10mm;
  background: #fff; color: #000; box-sizing: border-box;
  font-size: 9.5pt; line-height: 1.45;
  border: 1px solid var(--border-strong); box-shadow: var(--e2);
  break-after: page; page-break-after: always;
}
.surat:last-child { break-after: auto; page-break-after: auto; }

.surat-kop {
  display: flex; justify-content: space-between; gap: 10mm;
  padding-bottom: 4mm; border-bottom: 0.6mm solid #000; margin-bottom: 5mm;
}
.surat-merek { font-size: 16pt; font-weight: 700; letter-spacing: -.02em; }
.surat-alamat { font-size: 8.5pt; line-height: 1.4; color: #333; margin-top: 1mm; }
.surat-judul { font-size: 13pt; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; }
.surat-no { font-size: 11pt; font-weight: 700; }
.surat-tgl { font-size: 8.5pt; color: #333; }

.surat-pihak {
  display: grid; grid-template-columns: 1.4fr 1fr; gap: 8mm; margin-bottom: 5mm;
}

.surat-tabel { width: 100%; border-collapse: collapse; font-size: 9pt; }
.surat-tabel th, .surat-tabel td {
  border: 0.25mm solid #999; padding: 1.8mm 2mm; text-align: left; vertical-align: top;
}
.surat-tabel thead th {
  background: #eee; font-size: 8pt; text-transform: uppercase; letter-spacing: .05em;
  border-bottom-width: 0.5mm; color: #000;
}
.surat-tabel tfoot th { background: #f6f6f6; font-weight: 700; }
.surat-tabel .num { text-align: right; }

.surat-catatan {
  margin-top: 4mm; padding: 2.5mm 3mm; border-left: 0.8mm solid #000;
  background: #f7f7f7; font-size: 9pt;
}

.surat-ttd {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 10mm;
  margin-top: 14mm; text-align: center; font-size: 8.5pt;
}
.surat-ttd-garis { border-bottom: 0.3mm solid #000; height: 16mm; margin-bottom: 1.5mm; }

.surat-kaki {
  margin-top: 8mm; padding-top: 3mm; border-top: 0.25mm solid #ccc;
  font-size: 7.5pt; color: #444; line-height: 1.5;
}

@media (max-width: 640px) {
  .label-wrap.a4 { grid-template-columns: 100mm; }
}

/* ==========================================================================
   Animasi masuk saat digulir
   --------------------------------------------------------------------------
   Elemen ber-[data-reveal] mulai transparan dan sedikit turun; app.js memasang
   IntersectionObserver yang menambahkan .in begitu elemen terlihat.

   Perhatikan selektor `.js` di depan: kelas itu dipasang app.js pada <html>
   saat berkasnya dieksekusi. Arah ini disengaja — bila app.js gagal dimuat,
   aturan di bawah tidak pernah berlaku dan seluruh konten tetap terlihat.
   Menulisnya terbalik (sembunyikan dulu, tampilkan lewat JS) berarti satu
   kegagalan memuat skrip menghasilkan halaman kosong.
   ========================================================================== */
.js [data-reveal] {
  opacity: 0; transform: translateY(22px);
  transition: opacity var(--t-slow) var(--ease), transform var(--t-slow) var(--ease);
}
.js [data-reveal].in { opacity: 1; transform: none; }

/* Penundaan bertingkat untuk daftar kartu, supaya masuknya berurutan. */
.js [data-reveal][data-delay="1"] { transition-delay: 70ms; }
.js [data-reveal][data-delay="2"] { transition-delay: 140ms; }
.js [data-reveal][data-delay="3"] { transition-delay: 210ms; }
.js [data-reveal][data-delay="4"] { transition-delay: 280ms; }
.js [data-reveal][data-delay="5"] { transition-delay: 350ms; }

/* ==========================================================================
   Gerak & kehalusan tambahan
   --------------------------------------------------------------------------
   Aturan di bawah bekerja tanpa menyunting satu pun berkas HTML: seluruhnya
   menempel pada kelas yang sudah dipakai 28 halaman. Itu disengaja — animasi
   yang menuntut markup baru akan berhenti di halaman yang lupa diperbarui,
   dan tampilannya jadi timpang antar-halaman.

   Seluruh blok ini ikut mati pada `prefers-reduced-motion` lewat aturan di
   bagian paling bawah berkas.
   ========================================================================== */

/* -- masuknya halaman ----------------------------------------------------- */
.js main > .page-head,
.js main > .card,
.js main > .grid,
.js main > .stat-grid { animation: naik-halus 460ms var(--ease-out) both; }
.js main > .card { animation-delay: 40ms; }
.js main > .grid, .js main > .stat-grid { animation-delay: 80ms; }
@keyframes naik-halus {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: none; }
}

/* -- kartu: terangkat sedikit, bukan melompat ----------------------------- */
.card, .stat {
  transition: transform var(--t) var(--ease), box-shadow var(--t) var(--ease),
              border-color var(--t) var(--ease);
}
.card:hover, .stat:hover { transform: translateY(-2px); box-shadow: var(--e3); }

/*
 * Kilau emas yang menyapu kartu statistik saat disentuh.
 *
 * Dibuat dengan ::after ber-transform, bukan dengan menganimasikan
 * `background-position`: properti kedua memaksa peramban menggambar ulang
 * seluruh latar kartu tiap frame, dan pada daftar 12 kartu di Ringkasan Admin
 * hasilnya terasa tersendat di HP kelas menengah. Transform hanya menggeser
 * lapisan yang sudah dikomposisi GPU.
 */
.stat { position: relative; overflow: hidden; }
.stat::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(105deg, transparent 38%,
              color-mix(in srgb, var(--brand) 22%, transparent) 50%, transparent 62%);
  transform: translateX(-120%);
  transition: transform 820ms var(--ease-out);
}
.stat:hover::after { transform: translateX(120%); }

/* -- tombol: tekanan yang terasa ------------------------------------------ */
button:active:not(:disabled), .btn:active:not(.disabled) { transform: translateY(1px) scale(.985); }

/* -- baris tabel ---------------------------------------------------------- */
tbody tr { transition: background-color var(--t-fast) var(--ease); }

/* -- tautan sidebar: penanda emas yang memanjang -------------------------- */
.sidebar a { position: relative; }
.sidebar a::before {
  content: ''; position: absolute; left: 0; top: 50%; width: 3px; height: 0;
  background: var(--brand); border-radius: var(--r-full);
  transform: translateY(-50%);
  transition: height var(--t) var(--ease-spring);
}
.sidebar a.active::before, .sidebar a:hover::before { height: 62%; }

/* -- angka statistik: naik saat pertama tampil ---------------------------- */
.js .stat .value { animation: angka-masuk 520ms var(--ease-out) both; }
@keyframes angka-masuk {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

/* -- badge & chip --------------------------------------------------------- */
.badge, .chip { transition: background-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease); }

/* -- kartu produk: gambar sedikit membesar -------------------------------- */
.product-media { overflow: hidden; }
.product-media img { transition: transform 620ms var(--ease-out); }
.product-card:hover .product-media img { transform: scale(1.045); }

/* -- garis bawah tautan etalase ------------------------------------------- */
.shop-foot a, .doc-body a {
  background-image: linear-gradient(var(--brand-on-bg), var(--brand-on-bg));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0 1px;
  transition: background-size var(--t) var(--ease);
}
.shop-foot a:hover, .doc-body a:hover { background-size: 100% 1px; }

/* -- pemuatan: denyut lembut untuk kerangka konten ------------------------ */
@keyframes denyut { 50% { opacity: .55; } }

/* ==========================================================================
   Responsif
   ========================================================================== */
@media (max-width: 1000px) {
  .hero-in { grid-template-columns: 1fr; }
  .hero-art { display: none; }
  .shop-foot .cols { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 860px) {
  .shell { flex-direction: column; }
  .sidebar {
    width: 100%; flex: none; position: static; max-height: none;
    display: flex; flex-wrap: wrap; gap: .3rem;
    border-right: 0; border-bottom: 1px solid var(--border); padding: .7rem;
  }
  .sidebar a { flex: 0 0 auto; }
  .sidebar .group { display: none; }
  main { padding: 1.1rem 1rem 3rem; }
  .topbar { padding: 0 .8rem; gap: .55rem; }
  .topbar .who { display: none; }
  .shopbar .links { display: none; }
  .section { padding: 2.8rem 0; }
  .section-head h2 { font-size: 1.5rem; }
  .shop-foot .cols { grid-template-columns: 1fr; }
  .product-grid { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: .9rem; }
  .fab { right: 1rem; bottom: 1rem; }
}

@media (max-width: 480px) {
  .wrap { padding: 0 .8rem; }
  .shopbar-in { gap: .5rem; height: 60px; }
  .shopbar .brand span { display: none; }
  .shop-body .btn.sm, .auth-page .btn.sm {
    padding: .35rem .55rem; font-size: .66rem; letter-spacing: .04em;
  }
}

/* ==========================================================================
   APLIKASI DI PONSEL — kerangka bergaya aplikasi asli
   --------------------------------------------------------------------------
   Seluruh blok ini berlaku HANYA di bawah `body.shell-mitra`, kelas yang
   dipasang `pasangTabbar()` untuk mitra dan afiliator. Panel admin sengaja
   tidak ikut: pekerjaannya (jurnal, buku besar, cetak label per gelombang,
   rekonsiliasi) menuntut layar lebar, dan tata letak lamanya dipertahankan
   apa adanya.

   Kelasnya dipasang JavaScript, bukan disimpulkan dari lebar layar saja,
   karena yang menentukan bukan ukuran layar melainkan SIAPA yang membuka —
   admin di ponsel tetap harus melihat sidebar dan tabel yang biasa.

   Berlaku baik saat aplikasi sudah terpasang (PWA) maupun di tab peramban
   ponsel. Pengunjung yang belum memasang justru yang paling perlu diyakinkan
   bahwa aplikasi ini layak dipasang.
   ========================================================================== */

:root { --tabbar-h: 58px; }

.tabbar { display: none; }

@media (max-width: 860px) {

  /* -- tab bar bawah ------------------------------------------------------ */
  /*
   * Menempel di dasar layar karena itulah satu-satunya area yang terjangkau
   * jempol pada ponsel 6,5 inci ke atas. `env(safe-area-inset-bottom)`
   * menahan tab agar tidak tertindih home indicator iPhone; nilainya 0 pada
   * perangkat tanpa takik, jadi tidak perlu cabang terpisah.
   */
  body.shell-mitra .tabbar {
    display: flex;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
    background: var(--surface-1);
    border-top: 1px solid var(--border);
    padding-bottom: env(safe-area-inset-bottom, 0px);
    box-shadow: 0 -2px 14px rgb(0 0 0 / .16);
  }
  body.shell-mitra .tabbar .tab {
    flex: 1 1 0; min-width: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: .18rem;
    height: var(--tabbar-h);
    padding: 0 .15rem;
    background: none; border: 0; border-radius: 0; box-shadow: none;
    color: var(--muted); font-family: inherit; font-size: .62rem;
    letter-spacing: .02em; text-transform: none; font-weight: 500;
    text-decoration: none; cursor: pointer;
    transition: color var(--t-fast) var(--ease);
  }
  body.shell-mitra .tabbar .tab i { font-size: 1.12rem; }
  body.shell-mitra .tabbar .tab span {
    max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  body.shell-mitra .tabbar .tab.active { color: var(--brand-on-bg); }
  body.shell-mitra .tabbar .tab:active { background: var(--surface-2); }
  /* Riak Material tidak dipakai di tab bar — lihat catatan .tabbar .ripple di bawah. */
  body.shell-mitra .tabbar .ripple { display: none; }

  /*
   * Ruang bawah untuk konten. Tanpa ini, baris terakhir setiap daftar
   * tertutup tab bar — dan justru baris terakhir yang paling sering dicari
   * (transaksi terbaru ada di atas, tetapi tombol aksi ada di bawah).
   */
  body.shell-mitra main,
  body.shell-mitra .shop-body,
  body.shell-mitra .shop-foot {
    padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px) + 1.2rem);
  }

  /* -- sidebar diganti tab bar ------------------------------------------- */
  /*
   * Dinding chip 15 tautan (19 untuk admin) yang membungkus 4–5 baris SEBELUM
   * konten adalah bentuk lama yang digantikan blok ini. Menu selebihnya
   * pindah ke lembar "Lainnya", lengkap dengan judul kelompoknya.
   */
  body.shell-mitra .sidebar { display: none; }
  /*
   * `align-items: stretch` WAJIB ditimpa di sini.
   *
   * `.shell` memakai `align-items: flex-start` agar sidebar tidak ikut
   * memanjang setinggi konten pada tata letak desktop. Begitu arahnya menjadi
   * kolom, sumbu silangnya berputar 90° dan aturan yang sama membuat <main>
   * menyusut ke lebar ISINYA — konten hanya memakai sekitar dua pertiga layar,
   * dengan sisanya kosong. Tidak ada galat, tidak ada luapan; hanya terlihat
   * kalau benar-benar dilihat.
   */
  body.shell-mitra .shell { flex-direction: column; align-items: stretch; }

  /* -- bilah atas ringkas ------------------------------------------------- */
  body.shell-mitra .topbar { padding: 0 .5rem 0 .35rem; gap: .3rem; }
  body.shell-mitra .topbar .brand span { display: none; }
  body.shell-mitra .topbar .badge { display: none; }
  body.shell-mitra .topbar #btn-logout { display: none; }
  body.shell-mitra .appbar-back { display: inline-flex; }
  body.shell-mitra .appbar-title {
    display: block; flex: 1; min-width: 0;
    font-size: 1rem; font-weight: 500; color: var(--text);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  /* Logo disembunyikan saat ada tombol kembali — keduanya bersaing memperebutkan
     sudut kiri, dan pada halaman dalam yang dibutuhkan adalah jalan keluar. */
  body.shell-mitra .topbar:has(.appbar-back) .brand { display: none; }

  /* -- tabel menjadi kartu ------------------------------------------------ */
  /*
   * Penanda paling jelas bahwa sesuatu "situs web, bukan aplikasi" adalah
   * tabel yang harus digulir ke samping. Judul kolomnya dipindahkan ke tiap
   * sel lewat `data-l` (lihat labeliSel() di app.js), sehingga <thead> yang
   * tidak lagi berarti dapat disembunyikan.
   *
   * `thead` disembunyikan dengan teknik sr-only, BUKAN `display:none`:
   * pembaca layar tetap membacakan keterkaitan sel dengan judul kolomnya.
   */
  body.shell-mitra .table-wrap { overflow-x: visible; margin: 0; padding: 0; }
  body.shell-mitra .table-wrap table { border-collapse: separate; }
  body.shell-mitra .table-wrap thead {
    position: absolute; width: 1px; height: 1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
  body.shell-mitra .table-wrap table,
  body.shell-mitra .table-wrap tbody,
  body.shell-mitra .table-wrap tr,
  body.shell-mitra .table-wrap td { display: block; width: 100%; }
  body.shell-mitra .table-wrap tr {
    background: var(--surface-1);
    border: 1px solid var(--border); border-radius: 14px;
    padding: .55rem .85rem; margin-bottom: .65rem;
  }
  body.shell-mitra .table-wrap tr:last-child { margin-bottom: 0; }
  body.shell-mitra .table-wrap td {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: 1rem; padding: .3rem 0; border: 0; text-align: right;
  }
  body.shell-mitra .table-wrap td::before {
    content: attr(data-l);
    flex: 0 0 auto; text-align: left;
    color: var(--muted); font-size: .74rem; font-weight: 400;
    letter-spacing: .02em; text-transform: uppercase;
  }
  /* Sel tanpa label (colspan, atau kolom tanpa judul) memakai lebar penuh. */
  body.shell-mitra .table-wrap td:not([data-l]) { justify-content: flex-start; text-align: left; }
  body.shell-mitra .table-wrap td:not([data-l])::before { content: none; }
  body.shell-mitra .table-wrap td.num { text-align: right; }

  /* -- modal menjadi lembar bawah ---------------------------------------- */
  /*
   * Kartu melayang di tengah layar adalah pola desktop. Pada ponsel, lembar
   * yang naik dari bawah berada di jangkauan jempol dan menutup dengan gerak
   * yang sama seperti aplikasi asli.
   */
  body.shell-mitra .modal-wrap { align-items: flex-end; padding: 0; }
  body.shell-mitra .modal-card {
    max-width: none; width: 100%;
    max-height: 88dvh;
    border-radius: 22px 22px 0 0;
    padding-bottom: calc(1.2rem + env(safe-area-inset-bottom, 0px));
    animation: sheet-naik var(--t) var(--ease-out);
  }
  /* Pegangan seret — penanda visual bahwa ini lembar, bukan halaman baru. */
  body.shell-mitra .modal-card::before {
    content: ''; display: block;
    width: 38px; height: 4px; margin: -.2rem auto .9rem;
    background: var(--border-strong); border-radius: 99px;
  }
}

@keyframes sheet-naik {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}

/* -- modal: gaya dipindahkan dari style sebaris di openModal() ------------- */
/*
 * Nilainya sama persis dengan yang dulu ditulis `wrap.style.cssText`, jadi
 * tampilan desktop tidak berubah sedikit pun. Yang berubah adalah ia kini
 * DAPAT ditimpa media query — gaya sebaris tidak bisa.
 */
.modal-wrap {
  position: fixed; inset: 0; z-index: 200;
  background: rgb(19 28 38 / .45);
  display: flex; align-items: center; justify-content: center;
  padding: 1rem;
}
.modal-card {
  max-width: 660px; width: 100%; max-height: 85vh;
  overflow: auto; margin: 0;
  box-shadow: var(--e5); border-color: transparent;
}

/* -- isi lembar "Lainnya" -------------------------------------------------- */
.sheet-akun {
  display: flex; align-items: center; gap: .8rem;
  padding: .7rem .8rem; margin-bottom: .9rem;
  background: var(--surface-2); border-radius: 14px;
  color: var(--text); text-decoration: none;
}
.sheet-akun strong { display: block; font-weight: 500; }
.sheet-chev { margin-left: auto; color: var(--muted); font-size: .8rem; }
.sheet-nav { display: flex; flex-direction: column; }
.sheet-group {
  padding: 1rem .3rem .35rem;
  font-size: .68rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted); font-weight: 600;
}
.sheet-link {
  display: flex; align-items: center; gap: .85rem;
  /* 48px: batas bawah target sentuh yang nyaman. Daftar menu adalah tempat
     paling sering salah ketuk kalau barisnya dirapatkan. */
  min-height: 48px; padding: .1rem .3rem;
  color: var(--text); text-decoration: none;
  border-bottom: 1px solid var(--border);
}
.sheet-link:last-child { border-bottom: 0; }
.sheet-link > i:first-child { width: 1.3rem; text-align: center; color: var(--muted); }
.sheet-link.active { color: var(--brand-on-bg); font-weight: 500; }
.sheet-link.active > i:first-child { color: var(--brand-on-bg); }
.sheet-link:active { background: var(--surface-2); }
.sheet-keluar { width: 100%; margin-top: 1.1rem; justify-content: center; }

/* ==========================================================================
   Poles sentuh — berlaku untuk SEMUA peran, termasuk admin
   ========================================================================== */

/*
 * `100dvh` menggantikan `100vh` pada tinggi kerangka. Di peramban ponsel,
 * `100vh` dihitung terhadap viewport TANPA bilah alamat, sehingga elemen
 * setinggi layar selalu sedikit terpotong sampai penggunanya menggulir.
 */
.shell { min-height: calc(100dvh - var(--topbar-h)); }

@media (max-width: 860px) {
  /* Menahan pull-to-refresh yang tak disengaja saat menggulir daftar panjang,
     dan mencegah gulir di dalam lembar merambat ke halaman di belakangnya. */
  body { overscroll-behavior-y: contain; }
  .drawer-body, .modal-card { overscroll-behavior: contain; }

  /*
   * Tekan-lama pada elemen antarmuka memunculkan menu "Salin/Bagikan" bawaan
   * peramban — gerak yang di aplikasi asli tidak pernah terjadi. Dibatasi pada
   * kendali saja; teks konten tetap bisa disalin, karena nomor pesanan dan
   * nomor rekening memang perlu disalin.
   */
  .tabbar, .topbar, .sidebar, .shopbar, .btn, button, .badge, .tab {
    -webkit-touch-callout: none;
    -webkit-user-select: none; user-select: none;
  }

  /* Target sentuh minimum. Tombol ikon di bilah atas paling sering meleset. */
  .topbar .btn.icon, .topbar button.icon, .appbar-back { min-width: 42px; min-height: 42px; }
}

/*
 * Peramban ponsel memperbesar halaman secara otomatis bila ukuran huruf input
 * di bawah 16px saat difokuskan — dan tidak pernah mengembalikannya. Satu
 * ketukan pada kolom pencarian cukup untuk membuat seluruh tata letak tergeser
 * sampai halaman dimuat ulang.
 */
@media (max-width: 860px) {
  input, select, textarea { font-size: 16px; }
}

/* ==========================================================================
   Gerak yang dikurangi & cetak
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  .js [data-reveal] { opacity: 1; transform: none; }
  .product-card:hover { transform: none; }
  .product-quick { opacity: 1; transform: none; }

  /*
   * Animasi bernama harus dimatikan lewat `animation: none`, bukan cukup
   * dengan `animation-duration: .001ms` di atas. Durasi nol tetap menjalankan
   * keyframe-nya, dan keyframe yang berawal dari `opacity: 0` sempat
   * menyembunyikan elemennya satu frame — pada halaman dengan banyak kartu,
   * hasilnya kedipan yang justru mengganggu pengguna yang meminta lebih
   * sedikit gerak.
   */
  .js main > .page-head, .js main > .card, .js main > .grid,
  .js main > .stat-grid, .js .stat .value { animation: none; opacity: 1; transform: none; }
  .card:hover, .stat:hover { transform: none; }
  .stat::after { display: none; }
  .product-card:hover .product-media img { transform: none; }
  .sidebar a::before { transition: none; }
  .shop-foot a:hover, .doc-body a:hover { background-size: 100% 1px; }
}

@media print {
  .topbar, .sidebar, .shopbar, .drawer, .drawer-scrim, .fab, .toast-wrap, .pwa-banner,
  .tabbar, #push-ask-banner,
  button, .btn, .no-print { display: none !important; }
  body { background: #fff; }
  /* Ruang bawah untuk tab bar tidak berlaku di kertas — tanpa penimpaan ini,
     setiap halaman cetak berakhir dengan satu blok kosong setinggi tab bar. */
  main, .shop-body, .shop-foot { padding: 0; }
  .card, .stat { box-shadow: none; border-color: #ccc; break-inside: avoid; }

  /* -------------------------------------------------- dokumen pengiriman
     Latar dan garis pada label/surat jalan bukan hiasan: kepala tabel abu-abu
     dan garis tebal pemisah adalah penanda yang dibaca petugas sekilas.
     Tanpa print-color-adjust, peramban membuang seluruh latar demi menghemat
     tinta dan dokumennya kehilangan strukturnya. */
  .cetak-body { background: #fff; }
  .cetak-body * { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  #lembar { padding: 0; }

  .ship-label, .surat {
    border: 0 !important;
    box-shadow: none !important;
    margin: 0 auto;
  }

  /* Mode termal: satu label memenuhi satu halaman 100×150 mm tanpa margin. */
  .cetak-body[data-ukuran="termal"] .label-wrap { display: block; }
  .cetak-body[data-ukuran="termal"] .ship-label {
    width: 100mm; height: 150mm;
    break-after: page; page-break-after: always;
  }
  .cetak-body[data-ukuran="termal"] .ship-label:last-child {
    break-after: auto; page-break-after: auto;
  }

  /* Mode A4: dua label per halaman, tetap 100×150 mm supaya bisa digunting
     dan ditempel tanpa penyesuaian. */
  .cetak-body[data-ukuran="a4"] .label-wrap {
    display: grid; grid-template-columns: repeat(2, 100mm);
    justify-content: center; gap: 4mm;
  }

  .surat { width: auto; min-height: 0; padding: 0; }
}
