/* PartConverter -- design tokens: "manuskrip & tinta emas" (ink/paper/gold, sudah ada
   sebelumnya) disempurnakan jadi sistem semantik lengkap + varian gelap. Serif Playfair
   Display utk momen judul (echo dari 5 pilihan font judul output produk sendiri), Inter
   utk seluruh teks UI fungsional (form, tombol, saldo token). */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Playfair+Display:wght@600;700&family=Cinzel:wght@600&family=Cormorant+Garamond:wght@600&family=Bodoni+Moda:wght@700&family=Marcellus&family=Noto+Music&display=swap');

:root {
  /* izinkan transisi CSS ke/dari keyword ukuran intrinsik (mis. block-size:auto pada
     ::details-content, item 5). Inert utk properti lain yg tak menganimasi ke keyword. */
  interpolate-size: allow-keywords;

  --bg: #F1F0F6;
  --surface: #FFFFFF;
  --surface-hover: #FAF9FC;
  --border: #D9D5E6;
  --border-strong: #B9B3CE;

  --text: #1B2740;
  --text-soft: #545A76;
  --text-faint: #7A7F9C;
  --text-on-accent: #FFFFFF;
  --text-on-accent-soft: #4A3A12;

  --accent: #A67C00;
  --accent-strong: #7A5900;
  --accent-soft: #FBF0D9;
  --accent-line: #D4AF37;

  --success: #2F6846;
  --success-soft: #DFEEE4;
  --success-strong: #1E4A31;
  --danger: #A23B3B;
  --danger-soft: #F5E1DE;
  --danger-strong: #7A2A2A;
  --warning: #B5651D;
  --warning-soft: #F7E6D5;
  --warning-strong: #8A4A12;

  --focus: #1B2740;

  --shadow-sm: 0 1px 2px rgba(27, 39, 64, .06);
  --shadow-md: 0 6px 20px rgba(27, 39, 64, .07);

  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 16px;

  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;

  --text-xs: 12px;
  --text-sm: 13.5px;
  --text-base: 16px;
  --text-md: 18px;
  --text-lg: 22px;
  --text-xl: 30px;

  --font-display: 'Playfair Display', Georgia, 'Times New Roman', serif;
  --font-body: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;

  --dur-fast: 150ms;
  --dur-base: 220ms;
  --ease: cubic-bezier(.4, 0, .2, 1);

  --topbar-h: 66px;

  color-scheme: light;
}

/* Tema gelap HANYA lewat toggle manual (default terang, tak ikut prefers-color-scheme
   sistem -- keputusan eksplisit user) -- lihat app.js `syncThemeButton()`/inline script di
   index.html yg set atribut ini dari localStorage sebelum first paint. */
:root[data-theme="dark"] {
  --bg: #15141D;
  --surface: #1E1D2A;
  --surface-hover: #262533;
  --border: #34334A;
  --border-strong: #47465F;

  --text: #ECEAF3;
  --text-soft: #B7B9CE;
  --text-faint: #83869E;
  --text-on-accent: #1B1508;
  --text-on-accent-soft: #F1E6C9;

  --accent: #D8B65C;
  --accent-strong: #E8C878;
  --accent-soft: #362C15;
  --accent-line: #D4AF37;

  --success: #7CD3A0;
  --success-soft: #16301F;
  --success-strong: #B7E8C6;
  --danger: #E28080;
  --danger-soft: #3A1D1D;
  --danger-strong: #F3C0C0;
  --warning: #E0A15C;
  --warning-soft: #362409;
  --warning-strong: #F0C793;

  --focus: #ECEAF3;

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .35);
  --shadow-md: 0 6px 24px rgba(0, 0, 0, .45);

  color-scheme: dark;
}

* { box-sizing: border-box; }

/* [hidden] vs display eksplisit: lihat catatan lama -- author style selalu menang atas
   default UA utk atribut hidden, jadi elemen ber-display eksplisit (mis. .userbar flex)
   butuh override !important ini spy `el.hidden = true` benar2 menyembunyikan. */
[hidden] { display: none !important; }

html { scrollbar-gutter: stable; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

h1 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--text-xl);
  letter-spacing: -.01em;
  margin: 0 0 var(--space-2);
  color: var(--text);
}

code {
  font-family: ui-monospace, 'SF Mono', Consolas, monospace;
  font-size: .92em;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 1px 5px;
}

/* ---------- ikon ---------- */
.icon {
  width: 20px; height: 20px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.75;
  stroke-linecap: round; stroke-linejoin: round;
  vertical-align: -4px;
}
.icon-sm { width: 16px; height: 16px; }
.icon-lg { width: 32px; height: 32px; }
.icon-xl { width: 44px; height: 44px; stroke-width: 1.4; }
.icon-spin { animation: spin .8s linear infinite; transform-origin: 50% 50%; }
/* [poin 5, 2026-09-12] ikon token = glyph Noto Music asli (teks, bukan SVG gambar-tangan) --
   dikombinasikan dgn .icon utk box-size yg sama, override properti khusus-SVG yg tak relevan. */
.icon--glyph {
  display: inline-flex; align-items: center; justify-content: center;
  font-family: 'Noto Music', sans-serif; font-style: normal; line-height: 1;
  font-size: 20px;
}
.icon-sm.icon--glyph { font-size: 16px; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ---------- focus (aturan #1: harus terlihat jelas di SEMUA permukaan) ---------- */
:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: 2px;
}
.btn-primary:focus-visible {
  outline-color: var(--text-on-accent);
  outline-offset: -4px;
  box-shadow: 0 0 0 4px var(--accent-strong);
}
.option-card:focus-within {
  outline: 2px solid var(--focus);
  outline-offset: 1px;
}

/* ---------- topbar ---------- */
.topbar {
  position: sticky; top: 0; z-index: 30;
  display: flex; flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  min-height: var(--topbar-h);
  box-sizing: border-box;
  padding: 12px var(--space-5);
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}
.topbar__left { display: flex; align-items: center; gap: var(--space-3); min-width: 0; }
.topbar__right { display: flex; align-items: center; gap: var(--space-4); margin-left: auto; }

/* generic ikon-tombol bulat (hamburger, dll) -- theme-toggle punya kelas sendiri (identik) */
.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; flex: none;
  border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface); color: var(--text-soft); cursor: pointer;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease);
}
.icon-btn:hover { background: var(--bg); color: var(--text); border-color: var(--border-strong); }
.icon-btn .icon { width: 19px; height: 19px; }

/* Wordmark "ANGKAKAN" -- nama produk final (2026-09-07), dua-nada navy+emas mengikuti logo.
   .brand-mark = versi TEKS ringkas di header (lockup penuh terlalu detail utk slot sekecil
   ini). .hero__brand = logo ASLI (PNG, latar diproses transparan dr file user, lihat memory
   project-hero-copy-self-service-shift.md) di atas hero login -- lebih besar, ada ruang. */
/* [2026-09-16, poin 4 "disamakan dengan logo asli, sizenya dibesarkan"] Playfair Display (thin,
   elegan) TERNYATA jauh dr gaya logo PNG asli (huruf lebar/blocky/all-caps). Cinzel (sudah
   di-load, weight 600, lihat @import atas) jauh lbh dekat -- font itu jg font "template" DEFAULT
   utk judul PDF not-angka (lihat CLAUDE.md), jd kemungkinan besar font asli logo brand ini.
   Ukuran naik 18px -> 28px + letter-spacing lebih lebar (Cinzel didesain butuh nafas antar-huruf). */
.brand-mark {
  font-family: 'Cinzel', var(--font-display); font-weight: 600;
  font-size: 28px; line-height: 1.15; letter-spacing: .04em;
  margin: 0; white-space: nowrap;
}
.hero__brand {
  display: block;
  width: 100%;
  max-width: 420px;
  height: auto;
  margin: 0 auto var(--space-5);
}
/* dua file terpisah (bukan 1 file + filter CSS) krn navy di logo asli dirender ULANG per
   pixel jadi warna lain (bukan cuma invert/brightness) -- lihat memory utk cara olahnya.
   Tema situs ini HANYA lewat toggle manual (bukan prefers-color-scheme), jd cukup kunci ke
   atribut [data-theme] yg sama dgn seluruh token warna lain. */
.hero__brand--dark { display: none; }
:root[data-theme="dark"] .hero__brand--light { display: none; }
:root[data-theme="dark"] .hero__brand--dark { display: block; }
.brand-mark__navy { color: var(--text); }
.brand-mark__gold { color: var(--accent); }
/* [2026-09-16, poin 1 "huruf G pakai musical symbol G clef, sama dgn logo asli"] Cinzel (di atas)
   nol dukungan glyph musik -- G-clef (U+1D11E) dibungkus font Noto Music, pola SAMA persis dgn
   treble clef di hero__art (index.html). Ukuran+posisi disetel manual biar sejajar visual dgn
   huruf Cinzel di kiri-kanannya (glyph musik py proporsi/baseline beda dr huruf Latin). */
.brand-mark__gclef {
  /* [2026-09-16, "dikecilkan, samakan dgn size font-nya"] 1.3em kegedean -- balik ke 1em persis
     spy sama besar dgn huruf Cinzel di kiri-kanannya. [SUSULAN "masih kurang kecil, lebar sama
     tinggi lebih pendek"] font-size TAK diturunkan lg (lebar ikut mengecil) -- scaleY vertikal
     saja spy lebar glyph tetap, cuma tingginya yg dipangkas. */
  font-family: 'Noto Music', serif; font-size: 1em;
  display: inline-block; vertical-align: -0.08em;
  transform: scaleY(0.7); transform-origin: center;
}

/* ---------- nav utama (baris horizontal di topbar, freeze/sticky ikut .topbar) ---------- */
.topnav {
  display: flex; align-items: center; gap: var(--space-1);
  flex: 1 1 auto; justify-content: center;
  min-width: 0; flex-wrap: wrap;
}
.topnav__item {
  display: flex; align-items: center; gap: var(--space-2);
  box-sizing: border-box;
  /* [BARU 2026-09-20] position:relative -- ankor utk #navPesanBadge (.gift-icon__badge reused,
     position:absolute); nol efek visual di tombol lain yg nol badge di dalamnya. */
  position: relative;
  padding: 8px 14px; border: none; border-radius: 999px;
  background: none; color: var(--text-soft);
  font: inherit; font-size: var(--text-sm); font-weight: 600;
  white-space: nowrap; cursor: pointer;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.topnav__item:hover { background: var(--bg); color: var(--text); }
.topnav__item.is-active { background: var(--accent-soft); color: var(--text-on-accent-soft); }
.topnav__item .icon { flex: none; width: 18px; height: 18px; }

@media (max-width: 1300px) {
  /* [Fix 2026-09-25, sesi "fit bagus di HP/tablet/laptop/monitor"] Breakpoint lama 720px cuma
     nutup HP -- diukur langsung: logo+5 item nav teks-penuh+avatar BARU muat 1 baris mulai
     ~1280px, jadi tablet (768-1024px) & laptop kecil (1100-1200px) ikut kena `.topbar__right`
     (avatar) terlempar ke baris sendiri (greedy `flex:1 1 auto` #topnav mengklaim sisa baris).
     Ikon-saja (aria-label tetap ada, nol regresi aksesibilitas) jauh lebih ringkas -- naikkan
     ambang batas ke 1300px (margin aman dr 1280px yg terukur pas) menutup SEMUA tablet & laptop
     umum, monitor/laptop besar (>1300px) tetap dapat label teks penuh. */
  .topnav__item span { display: none; }
  .topnav__item { padding: 8px 10px; }
}

.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; flex: none;
  border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface); color: var(--text-soft);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.theme-toggle:hover { background: var(--bg); color: var(--text); border-color: var(--border-strong); }
.theme-toggle .icon { width: 18px; height: 18px; }
/* [2026-09-16, poin 2] Klaim Hadiah sebaris dgn badge token (bukan blok terpisah di bawah) --
   wrap, spy tak pecah aneh di layar sempit saat tombol muncul. */
.balance-header-row { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; margin-bottom: var(--space-4); }
.token-badges { display: flex; gap: var(--space-2); }
.token-badge {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--text-xs); font-weight: 600; font-variant-numeric: tabular-nums;
  padding: 4px 9px; border-radius: 999px; white-space: nowrap;
  border: 1px solid var(--border);
  color: var(--text-soft);
}
.token-badge--premium { background: var(--accent-soft); color: var(--text-on-accent-soft); border-color: var(--accent-line); }
.token-badge--gratis { background: var(--bg); }
.token-badge .icon { width: 13px; height: 13px; flex: none; opacity: .85; }  /* logo token */

/* [poin 4, 2026-09-14 "lambang segno diberi lingkaran, jadi mirip token beneran"] glyph Segno
   polos (teks doang) dibungkus lingkaran ber-gradasi emas + bevel tipis, spy terbaca sbg KOIN
   fisik bukan cuma simbol musik lepas. Satu komponen dipakai di semua konteks "ini token
   sungguhan" (badge saldo, fitur klaim poin 5) -- ukuran default kecil (badge inline), `--lg`
   utk tampilan menonjol (dialog klaim). */
.token-coin {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 16px; height: 16px; border-radius: 50%;
  background: radial-gradient(circle at 32% 28%, #F6E4AC, var(--accent-line) 55%, var(--accent-strong) 100%);
  box-shadow: inset 0 -1.5px 2px rgba(0,0,0,.35), inset 0 1px 1px rgba(255,255,255,.55);
}
.token-coin .icon--glyph { font-size: 11px; color: #4A3A12; width: auto; height: auto; }
.token-coin--lg { width: 44px; height: 44px; }
.token-coin--lg .icon--glyph { font-size: 26px; }

/* [2026-09-16, poin 3 "token gratis jadi silver, gold khusus premium"] koin gratis pakai
   gradasi abu-perak drpd emas -- struktur bevel identik, cuma palet warna diganti, spy tetap
   kebaca sbg "koin" yg sama, bukan elemen baru. */
.token-coin--silver {
  background: radial-gradient(circle at 32% 28%, #F3F4F6, #B7BEC9 55%, #838B99 100%);
}
.token-coin--silver .icon--glyph { color: #33383F; }

/* ---------- dropdown user (poin 6, 2026-09-12) -- gantikan .userbar+logoutBtn+themeToggle
   terpisah: satu trigger avatar Google, panel berisi email/saldo/tema/keluar. ---------- */
.user-menu { position: relative; }
.user-menu__trigger {
  display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface); padding: 3px 10px 3px 3px; cursor: pointer;
  color: var(--text-soft); font: inherit;
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.user-menu__trigger:hover { background: var(--bg); border-color: var(--border-strong); }
.user-menu__trigger .icon { width: 16px; height: 16px; transition: transform var(--dur-fast) var(--ease); }
.user-menu.is-open .user-menu__trigger .icon { transform: rotate(180deg); }
.user-menu__avatar {
  width: 28px; height: 28px; border-radius: 999px; flex: none; object-fit: cover; display: block;
}
.user-menu__avatar--fallback {
  display: flex; align-items: center; justify-content: center;
  background: var(--accent-soft); color: var(--text-on-accent-soft);
  font-size: var(--text-xs); font-weight: 700;
}
.user-menu__panel {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 100;
  min-width: 230px; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-md); box-shadow: var(--shadow-sm), var(--shadow-md);
  padding: var(--space-3); display: none;
}
.user-menu.is-open .user-menu__panel { display: block; }
.user-menu__email {
  margin: 0 0 var(--space-2); font-size: var(--text-sm); font-weight: 600; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* [poin 5, 2026-09-12] Tag visual "Admin" di dropdown user -- bypass token-nya sendiri di
   server (purchase_download), ini cuma penanda supaya admin sadar akunnya dlm mode itu. */
.admin-tag {
  display: inline-block; margin: 0 0 var(--space-3); padding: 2px 8px;
  font-size: 10px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--accent-strong); background: var(--accent-soft); border-radius: var(--radius-sm);
}
.user-menu__tokens { display: flex; gap: var(--space-2); margin-bottom: var(--space-3); flex-wrap: wrap; }
.user-menu__item {
  display: flex; align-items: center; gap: var(--space-2); width: 100%;
  border: none; background: none; padding: 9px 6px; border-radius: var(--radius-sm);
  font: inherit; font-size: var(--text-sm); color: var(--text); cursor: pointer; text-align: left;
  transition: background var(--dur-fast) var(--ease);
}
.user-menu__item:hover { background: var(--bg); }
.user-menu__item--danger { color: var(--danger-strong); }
.user-menu__item + .user-menu__item { margin-top: 2px; border-top: 1px solid var(--border); padding-top: 9px; }

/* ---------- layout ----------
   [poin 4, 2026-09-13 "lebarkan semua UI jadi 3/4 halaman"] DULU cap tetap 1040px (~2/3 dr
   layar desktop lazim 1440-1536px) -- skr `max(1040px, 75vw)`: di layar SEMPIT/SEDANG (di mana
   75vw <= 1040px) perilaku byte-identik dgn sebelumnya (nol regresi laptop/tablet biasa, dan
   mobile ttp dibatasi viewport-nya sendiri spt catatan lama, bukan angka ini); di layar LEBAR
   (1920px+, di mana 75vw > 1040px) kontainer ikut melebar sampai 75% viewport, bukan lagi
   mentok di cap lama. */
.page {
  max-width: max(1040px, 75vw);
  margin: 0 auto;
  padding: var(--space-6) var(--space-4) 60px;
}
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-6) var(--space-6);
  box-shadow: var(--shadow-sm), var(--shadow-md);
  animation: card-in var(--dur-base) var(--ease);
}
@keyframes card-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .card { animation: none; }
}
@media (max-width: 480px) {
  .page { padding: var(--space-5) var(--space-3) 40px; }
  .card { padding: var(--space-5) var(--space-4); border-radius: var(--radius-md); }
}

.sub { color: var(--text-soft); margin: 0 0 var(--space-5); max-width: 60ch; }

/* ---------- hero / halaman masuk (loginView) ----------
   Komposisi RATA-TENGAH ala landing page (bukan formulir): ilustrasi "not balok -> not angka"
   di atas, judul besar serif + garis emas, deskripsi rata kiri-kanan, lalu 3 kartu manfaat.
   Judul pakai clamp() -- tak ada px tetap yang memaksa scroll horizontal di layar kecil. */
#loginView { text-align: center; }

.hero { display: block; margin-bottom: var(--space-5); }

.hero__art {
  display: flex; justify-content: center;
  padding: var(--space-5) var(--space-4);
  margin-bottom: var(--space-5);
  background: var(--accent-soft);
  border: 1px solid var(--accent-line);
  border-radius: var(--radius-md);
}
.hero__art svg { width: 100%; max-width: 280px; height: auto; display: block; }

.hero__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(2rem, 1.35rem + 3vw, 3rem);
  line-height: 1.12;
  letter-spacing: -.018em;
  color: var(--text);
  margin: 0 auto;
  max-inline-size: 22ch;
  text-wrap: balance;
}
.hero__title .accent {
  color: var(--accent-strong);
  text-decoration: underline;
  text-decoration-color: var(--accent-line);
  text-decoration-thickness: .07em;
  text-underline-offset: .14em;
}

.hero__rule {
  width: 60px; height: 3px; border-radius: 2px;
  background: var(--accent-line);
  margin: var(--space-4) auto;
}

.hero__lede {
  font-size: var(--text-md);
  line-height: 1.6;
  color: var(--text-soft);
  max-width: 58ch;
  margin: 0 auto;
  text-align: justify;
  text-align-last: center;
  hyphens: auto;
}
.hero__lede strong { color: var(--text); font-weight: 600; }

@media (max-width: 480px) {
  .hero__title { max-inline-size: none; }
  /* rata kiri-kanan di layar sempit bikin celah kata melebar ("sungai") -- rata kiri saja */
  .hero__lede { text-align: left; text-align-last: left; }
}

/* [BARU 2026-09-20] daftar pertanyaan problem-agitation di hero, sblm tagline "AngkaKan saja!".
   Pola sama dgn .benefit-list/.benefit-item di bawah (baris flex angka/ikon + teks), rata kiri
   dlm kontainer center spy tiap kalimat gampang dibaca (bukan ikut rata-tengah #loginView). */
.pain-list {
  list-style: none; margin: 0 auto var(--space-5); padding: 0;
  display: flex; flex-direction: column; gap: var(--space-3);
  max-width: 48ch; text-align: left;
}
/* [REVISI 2026-09-20, "pertanyaan masalah dibuat bold"] weight+warna dinaikkan (dulu
   text-soft) -- pola sama dgn `strong` di .hero__lede: font-weight 600 + var(--text) penuh,
   bukan angka bold sembarang, spy konsisten dgn konvensi "penekanan teks" situs ini. */
.pain-item { display: flex; align-items: flex-start; gap: var(--space-3); font-size: var(--text-md); font-weight: 600; color: var(--text); }
/* [REVISI 2026-09-20, "pakai bullet saja, ga usah numbering"] dulu lingkaran berisi angka --
   skr murni titik dekoratif, posisinya disejajarkan ke baris pertama teks (bukan ke tengah
   tinggi item) via margin-top. */
.pain-item__num {
  flex: none; width: 8px; height: 8px; margin-top: 10px;
  border-radius: 50%; background: var(--accent-line);
}

.benefit-list {
  list-style: none; margin: var(--space-4) 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--space-3);
}
.benefit-item { display: flex; align-items: flex-start; gap: var(--space-3); font-size: var(--text-sm); color: var(--text-soft); }
.benefit-item .icon { flex: none; margin-top: 1px; color: var(--accent); }
.benefit-item strong { color: var(--text); }

/* 3 kartu manfaat rata-tengah -- menggantikan tampilan "daftar label formulir" */
.benefit-list--grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-3);
  margin: var(--space-5) 0;
}
.benefit-list--grid .benefit-item {
  flex-direction: column; align-items: center; text-align: center;
  gap: var(--space-2);
  padding: var(--space-4) var(--space-3);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  font-size: var(--text-xs);
}
.benefit-item__icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; flex: none;
  border-radius: 10px;
  background: var(--accent-soft); color: var(--accent-strong);
}
.benefit-item__icon .icon { width: 20px; height: 20px; margin: 0; color: inherit; }
.benefit-list--grid .benefit-item strong { display: block; color: var(--text); font-size: var(--text-sm); margin-bottom: 2px; }

@media (max-width: 560px) {
  .benefit-list--grid { grid-template-columns: 1fr; }
}

.incentive-chip {
  display: flex; align-items: center; gap: var(--space-2);
  width: fit-content;
  font-size: var(--text-xs); font-weight: 600;
  padding: 9px 15px; border-radius: 999px;
  background: var(--accent-soft); color: var(--text-on-accent-soft);
  border: 1px solid var(--accent-line);
  margin: 5px auto var(--space-4); /* [2026-09-16, poin 6 "turunkan 5px"] top 0 -> 5px */
  animation: chip-stopgo 4.5s var(--ease) infinite;
}
.incentive-chip .icon { flex: none; }

/* [poin "Teks ini align center. Jadikan 1 template tulisan yang sama", 2026-09-13] `.msg-warn`
   dasar rata-kiri/lebar-penuh (pas utk konteks paragraf biasa di halaman lain) -- DI DALAM
   #loginView semua yg lain (headline, chip bonus, tombol) rata-tengah+sempit, jd badge ini
   (dan elemen warning APA PUN di hero -- lihat #devLoginWrap di bawah, SATU template yg sama)
   perlu override supaya konsisten dgn "template" hero, bukan nongol sebagai bar lebar-penuh yg
   beda sendiri. */
.incentive-warn {
  justify-content: center; width: fit-content; margin-left: auto; margin-right: auto;
}
/* [2026-09-16, poin 5 "AngkaKan terus belajar! taruh center"] justify-content:center di atas
   TAK kelihatan efeknya di sini krn teksnya panjang (wrap 2-3 baris) -- box-nya (fit-content)
   otomatis melebar nyaris penuh krn teks panjang, jd nol sisa ruang utk "center"-kan si icon+teks
   sbg 1 unit; tiap BARIS teks jg tetap rata-kiri (flex-item biasa, bukan text-align). Scoped
   KHUSUS #loginView (3 pemakaian lain .incentive-warn di halaman lain sengaja TAK ikut berubah,
   lebar/konteksnya beda) -- icon ditumpuk DI ATAS teks (bukan disamping), teks di-text-align
   center spy tiap baris ikut center, bukan cuma boks luarnya. */
#loginView .incentive-warn {
  flex-direction: column; align-items: center; text-align: center; gap: 6px;
}

/* "stop and go": maju sedikit -> berhenti -> maju lagi -> berhenti lama, berulang.
   Menarik perhatian ke insentif akun baru tanpa gerak terus-menerus. */
@keyframes chip-stopgo {
  0%, 4%   { transform: translateX(0); }
  10%      { transform: translateX(5px); }
  20%      { transform: translateX(5px); }   /* berhenti */
  26%      { transform: translateX(10px); }
  36%      { transform: translateX(10px); }  /* berhenti */
  42%      { transform: translateX(0); }
  100%     { transform: translateX(0); }      /* diam panjang */
}
@media (prefers-reduced-motion: reduce) {
  .incentive-chip { animation: none; }
}

/* ---------- form fields ---------- */
.field { display: block; margin-bottom: var(--space-5); }
.field__label, fieldset.field legend {
  display: block; font-size: var(--text-sm); font-weight: 600; margin-bottom: var(--space-2);
}
.field select {
  width: 100%; padding: 10px 36px 10px 12px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); font: inherit; font-size: var(--text-sm);
  background: var(--surface) url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="%23545A76" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9l6 6 6-6"/></svg>') no-repeat right 10px center / 18px;
  appearance: none; color: var(--text);
  transition: border-color var(--dur-fast) var(--ease);
}
.field select:hover { border-color: var(--border-strong); }
fieldset.field { border: none; padding: 0; margin: 0 0 var(--space-5); }

/* ---------- sistem komponen: InputField (components.js) ---------- */
.field input[type="text"],
.field input[type="email"],
.field input[type="number"],
.field textarea {
  width: 100%; padding: 10px 12px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); font: inherit; font-size: var(--text-sm);
  background: var(--surface); color: var(--text);
  transition: border-color var(--dur-fast) var(--ease);
}
.field textarea { resize: vertical; min-height: 84px; }
.field input[type="text"]:hover, .field input[type="email"]:hover,
.field input[type="number"]:hover, .field textarea:hover { border-color: var(--border-strong); }
.field__required { color: var(--danger); margin-left: 2px; }
.field__error {
  display: flex; align-items: flex-start; gap: 6px;
  font-size: var(--text-xs); color: var(--danger-strong);
  margin: 6px 0 0;
}
.field.has-error input, .field.has-error textarea { border-color: var(--danger); }

/* ---------- [BARU 2026-09-20] editor rich-text Update/Info (bullet/numbering ala Gmail
   compose + paste markdown) ---------- */
.rte-toolbar {
  display: flex; align-items: center; gap: 2px; flex-wrap: wrap;
  padding: 6px; border: 1px solid var(--border); border-bottom: none;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0; background: var(--surface-2, var(--surface));
}
.rte-toolbar__btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border: none; border-radius: var(--radius-sm);
  background: none; color: var(--text-soft); cursor: pointer;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.rte-toolbar__btn:hover { background: var(--border); color: var(--text); }
.rte-toolbar__spacer { flex: 1 1 auto; }
.rte-toolbar__attach { position: relative; }
.rte-toolbar__attach.is-disabled { opacity: .4; cursor: not-allowed; pointer-events: none; }
.rte {
  min-height: 140px; max-height: 420px; overflow-y: auto;
  padding: 10px 12px; border: 1px solid var(--border); border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  background: var(--surface); color: var(--text); font: inherit; font-size: var(--text-sm);
  line-height: 1.55;
}
.rte:hover { border-color: var(--border-strong); }
.rte:focus { outline: none; border-color: var(--accent-line); }
.rte:empty:before { content: attr(data-placeholder); color: var(--text-faint); }
.rte ul, .rte ol { margin: 0 0 10px; padding-left: 22px; }
.rte p { margin: 0 0 10px; }
.rte img { max-width: 100%; border-radius: var(--radius-sm); }
.rte a { color: var(--accent-strong); }

.changelog-attach-list {
  display: flex; flex-wrap: wrap; gap: var(--space-2);
  margin: var(--space-2) 0 0;
}

/* Rendering ISI entri changelog (dipakai admin Pratinjau DAN halaman publik Update/Info,
   lihat js/changelog-render.js) -- token warna/tipografi SAMA dgn `.rte` di atas spy pratinjau
   & hasil publikasi konsisten. */
.changelog-entry__body { color: var(--text-soft); font-size: var(--text-sm); line-height: 1.55; }
.changelog-entry__body p { margin: 0 0 8px; }
.changelog-entry__body ul, .changelog-entry__body ol { margin: 0 0 8px; padding-left: 20px; }
.changelog-entry__body img { max-width: 100%; border-radius: var(--radius-sm); margin: 4px 0; }
.changelog-entry__body a { color: var(--accent-strong); }
.changelog-entry__attachments { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: 6px; }

.dev-panel {
  display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
  font-size: var(--text-xs); color: var(--warning-strong);
  background: var(--warning-soft);
  border: 1px dashed var(--warning);
  border-radius: var(--radius-sm);
  padding: 8px 12px;
  margin-bottom: var(--space-4);
}
/* ---------- radio-card (dipakai utk jenis sumber & jenis token) ---------- */
/* [UPDATE] .option-card kini wadah LUAR (border/bg/highlight), berisi 2 anak:
   __head (label radio, klik = pilih opsi) + __upload (area unggah, expand kalau opsi ini aktif). */
.option-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  margin-bottom: var(--space-2);
  background: var(--surface);
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease), padding-bottom var(--dur-base) var(--ease);
}
.option-card:last-child { margin-bottom: 0; }
.option-card:hover { border-color: var(--border-strong); }
.option-card:has(input:checked) {
  border-color: var(--accent-line);
  background: var(--accent-soft);
  padding-bottom: var(--space-3);
}
.option-card__head {
  position: relative;
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  cursor: pointer;
}
.option-card__head input {
  position: absolute; opacity: 0; width: 1px; height: 1px;
}
.option-card__icon {
  flex: none; width: 34px; height: 34px; border-radius: 9px;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg); color: var(--text-soft);
}
.option-card:has(input:checked) .option-card__icon {
  background: var(--accent-strong); color: var(--text-on-accent);
}
.option-card__body { flex: 1; min-width: 0; }
.option-card__title {
  display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
  font-size: var(--text-sm); font-weight: 600;
}
.option-card__desc { display: block; font-size: var(--text-xs); color: var(--text-soft); margin-top: 2px; }
.option-card__count { font-variant-numeric: tabular-nums; font-weight: 700; }

/* area unggah menyatu di dalam kartu opsi -- expand HANYA saat opsi ini terpilih (additional
   cue di luar highlight bg/border yg sudah ada di .option-card). max-height + overflow:hidden,
   pola SAMA dgn `.dl-confirm` di atas (bukan grid-template-rows: 0fr -- percobaan pertama
   ternyata TAK collapse ke 0 di browser ini, fr-track punya automatic-minimum-size sendiri yg
   tak mempan exception overflow:hidden spt track `auto`; max-height eksplisit tak py masalah
   itu). [2026-09-19] 180px->300px -- kartu PDF born-digital SEKARANG jg py `.msg-error` (catatan
   lapor-bug) di bawah `.dropzone`; angka lama cuma pas utk `.dropzone` sendirian. Cap lebih
   tinggi TAK mempengaruhi kartu lain (MusicXML/scan_foto) krn max-height cuma PLAFON, konten yg
   lebih pendek ttp resolve ke tinggi aslinya sendiri. */
.option-card__upload {
  max-height: 0;
  overflow: hidden;
  padding: 0 var(--space-4);
  transition: max-height var(--dur-base) var(--ease);
}
.option-card:has(input:checked) .option-card__upload {
  max-height: 300px;
}
@media (prefers-reduced-motion: reduce) {
  .option-card, .option-card__upload { transition: none; }
}

.tag-soon {
  font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  padding: 2px 7px; border-radius: 999px;
  background: var(--warning-soft); color: var(--warning-strong);
}
.tag-pro {
  font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  padding: 2px 7px; border-radius: 999px;
  background: var(--accent-soft); color: var(--text-on-accent-soft);
}
.tag-best {
  font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  padding: 2px 7px; border-radius: 999px;
  background: var(--success-soft); color: var(--success-strong);
}
/* [2026-09-19, "PDF born-digital diberi label BETA"] Netral (bukan hijau/emas/amber yg sudah
   dipakai tag lain -- BETA bukan klaim kualitas "terbaik" atau peringatan "tunggu", cuma status
   kematangan) -- outline abu-abu senada `.tier-badge`. */
.tag-beta {
  font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  padding: 1px 6px; border-radius: 999px;
  border: 1px solid var(--border-strong); color: var(--text-soft);
}

/* [2026-09-19, "OMR/scan greyed out tidak bisa diklik"] Kartu opsi yg radio-nya `disabled` --
   seluruh kartu (label+dropzone) jadi inert, bukan cuma radio-nya. [SUSULAN 2026-09-19, "masih
   terlalu visible... untuk versi dark coba disesuaikan juga"] Opacity SAJA kurang kena krn teks
   asli (`--text` navy tebal / putih terang) tetap kontras tinggi walau diredupkan -- override
   warna teks+ikon LANGSUNG ke `--text-faint` (token low-contrast yg SUDAH didefinisikan utk
   KEDUA tema, lihat :root) supaya efek "nonaktif" konsisten di light MAUPUN dark, bukan cuma
   ngandelin opacity generik. Badge "Segera hadir" (`.tag-soon`) SENGAJA TAK ikut diredupkan --
   py warna sendiri (warning) yg menang lewat spesifisitas, itu justru elemen yg PALING perlu
   tetap jelas kebaca (kenapa kartu ini nonaktif). */
.option-card:has(input:disabled) {
  opacity: .5;
  cursor: not-allowed;
}
.option-card:has(input:disabled) .option-card__title,
.option-card:has(input:disabled) .option-card__desc,
.option-card:has(input:disabled) .option-card__icon {
  color: var(--text-faint);
}
.option-card:has(input:disabled) .option-card__icon {
  background: transparent;
}
.option-card:has(input:disabled) .option-card__head,
.option-card:has(input:disabled) .option-card__upload {
  pointer-events: none;
}
.option-card:has(input:disabled):hover { border-color: var(--border); }

.tier-badge {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--text-xs); font-weight: 600; padding: 4px 9px; border-radius: 999px;
  border: 1px solid var(--border); color: var(--text-soft); white-space: nowrap;
}
.tier-badge--pro, .tier-badge--maestro { background: var(--accent-soft); color: var(--text-on-accent-soft); border-color: var(--accent-line); }

/* ---------- dropzone (kini bagian dalam kartu opsi, klik menyempit ke tombol ikon saja --
   poin 3: cegah misclick krn box ini skr menyatu dgn kartu opsi yg lebih besar. Drag&drop
   TETAP berlaku di seluruh area .dropzone -- itu gestur sengaja, bukan klik tak sengaja). */
.dropzone {
  position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-2);
  box-sizing: border-box;
  text-align: center;
  padding: var(--space-4) var(--space-4) var(--space-5);
  border-top: 1px dashed var(--border);
  color: var(--text-soft);
  transition: background var(--dur-fast) var(--ease);
}
.dropzone.is-dragover {
  background: var(--accent-soft);
}
.dropzone__text { margin: 0; font-size: var(--text-xs); }
.dropzone__filename {
  margin: 0; font-size: var(--text-sm); font-weight: 600; color: var(--text);
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* tombol ikon = SATU-SATUNYA target klik-buka-dialog -- lingkaran 52px (>=44px a11y touch
   target), border+bg jelas spy "ini bisa diklik" kelihatan sendiri tanpa perlu teks. */
.dropzone__btn {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  width: 52px; height: 52px; flex: none;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--accent-strong);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
/* [fix] ikon var(--accent-strong) di atas bg hover var(--accent-soft) nyaris tak kelihatan
   (2 nuansa aksen yg mirip) -- WARNA IKON ganti khusus saat hover, warna diam tak disentuh. */
.dropzone__btn:hover { border-color: var(--accent-line); background: var(--accent-soft); color: var(--text-on-accent-soft); transform: scale(1.06); }
.dropzone__btn:active { transform: scale(0.97); }
.dropzone__btn:has(.dropzone__input:focus-visible) {
  outline: 2px solid var(--focus); outline-offset: 2px;
}
.dropzone__btn .icon-lg { width: 24px; height: 24px; }
.dropzone__input {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; cursor: pointer;
}
@media (prefers-reduced-motion: reduce) {
  .dropzone__btn { transition: none; }
}

/* ---------- tombol ---------- */
/* [2026-09-16, poin 1 "migrasi tombol lama ke sistem komponen baru"] .btn-primary dihapus
   (sudah 0 pemakaian nyata -- semua tombol lama lain sudah lama pakai .btn/.btn--*), .btn-google
   dimigrasikan jd varian .btn--google di bawah. .btn-link DIBIARKAN -- masih dipakai
   preview.html (harness dev-only, bukan bagian SPA yg dikirim ke user), migrasi ke sana nol
   manfaat user-facing. */
.btn-link {
  display: inline-flex; align-items: center; gap: 6px;
  background: none; border: none; color: var(--text-soft); font: inherit; font-size: var(--text-sm);
  text-decoration: underline; text-underline-offset: 2px; cursor: pointer; padding: 0;
}
.btn-link:hover { color: var(--text); }

/* ---------- sistem komponen: Button (components.js) ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  font: inherit; font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.btn:active:not(:disabled) { transform: translateY(1px); }
.btn:disabled { opacity: .55; cursor: default; }
.btn .icon { flex: none; }
.btn-block { width: 100%; }
/* [poin 5, 2026-09-13 "semua tombol interaksi taruh sebelah kanan, seperti transaksi, kirim
   pesan, dll"] `.btn` dasar = inline-flex -- lone standalone action button (Kirim Pesan, Kirim
   Laporan, Kirim Balasan, Terbitkan Entri, dst.) diam2 nempel kiri krn cuma ikut alur block
   normal. Ganti jd block-level flex SENDIRI + `width:fit-content` (tak melar penuh) +
   `margin-left:auto` (dorong ke kanan). Grup tombol (Setujui/Tolak dll.) pakai `.admin-actions`
   (`justify-content:flex-end`) drpd ini -- ini utk tombol TUNGGAL. */
.btn--right { display: flex; width: fit-content; margin-left: auto; }

.btn--sm { padding: 7px 14px; font-size: var(--text-xs); }
.btn--md { padding: 11px 20px; font-size: var(--text-sm); }
.btn--lg { padding: 13px 26px; font-size: var(--text-base); }

.btn--primary { background: var(--accent-strong); color: var(--text-on-accent); }
.btn--primary:hover:not(:disabled) { background: var(--accent); }
.btn--primary:focus-visible {
  outline-color: var(--text-on-accent); outline-offset: -4px;
  box-shadow: 0 0 0 4px var(--accent-strong);
}

.btn--secondary { background: var(--surface); border-color: var(--border-strong); color: var(--text); }
.btn--secondary:hover:not(:disabled) { background: var(--bg); border-color: var(--text-faint); }

.btn--ghost { background: none; border-color: transparent; color: var(--text-soft); }
.btn--ghost:hover:not(:disabled) { background: var(--bg); color: var(--text); }

.btn--danger { background: var(--danger); color: #fff; }
.btn--danger:hover:not(:disabled) { background: var(--danger-strong); }

/* [2026-09-16] varian Google login, dimigrasikan dr .btn-google lama -- warna literal (bukan
   token tema) krn brand Google sendiri yg mendikte putih/abu-abu, tak ikut dark/light kita. */
.btn--google { background: #FFFFFF; color: #3C4043; border-color: #DADCE0; padding-left: 14px; box-shadow: 0 1px 2px rgba(0, 0, 0, .08); }
.btn--google:hover:not(:disabled) { background: #F8F9FA; box-shadow: 0 1px 4px rgba(0, 0, 0, .15); }
.btn--google svg { width: 20px; height: 20px; flex: none; }

/* [Fix 2026-09-23, feedback "tombol Lapor Tidak Bisa Transkrip pindah ke sebelah kanan
   Transkripsi, jangan mepet"] Baris tombol utama + tombol lapor-gagal (kondisional, lihat
   index.html #convertFailReport). Formulir deskripsi di bawahnya (baru muncul stlh tombol
   lapor diklik) dikasih jarak wajar drpd baris tombol -- dulu nempel langsung (nol margin). */
.convert-actions { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
#convertFailReportForm { margin-top: var(--space-4); }

/* ---------- progress bar transkripsi (item 4) ---------- */
.progress { margin-top: var(--space-3); }
.progress__track {
  height: 8px; border-radius: 999px; background: var(--border); overflow: hidden;
}
.progress__bar {
  height: 100%; width: 0%; border-radius: 999px;
  background: linear-gradient(90deg, var(--accent-strong), var(--accent-line));
  transition: width var(--dur-base) var(--ease);
}
.progress__label {
  display: flex; align-items: center; gap: 6px;
  margin: var(--space-2) 0 0; font-size: var(--text-xs); color: var(--text-soft);
}
.progress__label .icon { flex: none; color: var(--accent-strong); }
@media (prefers-reduced-motion: reduce) {
  .progress__bar { transition: none; }
}

/* ---------- pesan status ---------- */
.hint { display: flex; align-items: flex-start; gap: 6px; font-size: var(--text-xs); color: var(--text-soft); margin: var(--space-2) 0 0; }
/* [poin 2, 2026-09-13 "Hubungi kami teksnya kacau"] `.hint` = flex -- BENAR utk ikon+teks
   singkat (spec aslinya), tp SALAH utk paragraf dgn `<a>`/teks campur panjang (tiap potongan
   jd flex-item terpisah, tata letak berantakan). Bug yg SAMA sudah kena & diperbaiki sbg
   `.dl-panel__hint` bespoke -- di sini digeneralisasi jd modifier supaya kasus serupa
   berikutnya tinggal pakai `hint hint--block`, bukan menambal lagi dr nol. */
.hint--block { display: block; }
.msg {
  display: flex; align-items: flex-start; gap: var(--space-2);
  font-size: var(--text-sm); border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-3); margin: 0 0 var(--space-4);
}
.msg .icon { margin-top: 1px; }
.msg-error { color: var(--danger-strong); background: var(--danger-soft); }
.msg-warn { color: var(--warning-strong); background: var(--warning-soft); }
.msg-success { color: var(--success-strong); background: var(--success-soft); }
.error { color: var(--danger-strong); background: var(--danger-soft); border-radius: var(--radius-sm); padding: var(--space-3); margin: var(--space-3) 0 0; font-size: var(--text-sm); }
.warn { color: var(--warning-strong); background: var(--warning-soft); border-radius: var(--radius-sm); padding: var(--space-3); margin: 0 0 var(--space-4); font-size: var(--text-sm); }

/* ---------- hasil ---------- */
.result-head { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-2); }
.result-head .icon-xl { color: var(--success); flex: none; }
.result-head h1 { margin: 0; }
/* item 2: nama berkas yg ditranskrip, di bawah judul */
.result-filename {
  display: flex; align-items: center; gap: 6px;
  margin: 0 0 var(--space-4); font-size: var(--text-sm); color: var(--text-soft);
}
.result-filename .icon { flex: none; color: var(--text-faint); }
.result-filename span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---------- pratinjau + panel unduh (alur token di titik unduh) ---------- */
.preview-box { position: relative; }
.preview-box__tag {
  position: absolute; top: 8px; left: 8px; z-index: 2;
  font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  padding: 3px 9px; border-radius: 999px;
  background: var(--danger-soft); color: var(--danger-strong);
}
.preview-box.is-blanked .preview-pages { filter: blur(26px); opacity: .2; }
.preview-box.is-blanked::after {
  content: 'Pratinjau disembunyikan'; position: absolute; inset: 0; z-index: 3;
  display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: var(--text-md); color: var(--danger-strong);
  background: var(--danger-soft); border-radius: var(--radius-md);
}
/* Ctrl+P / cetak: jangan biarkan pratinjau + panel unduh ikut tercetak */
@media print {
  .preview-pages, #dlPanel { display: none !important; }
  #previewBox::before {
    content: 'Pratinjau tidak tersedia untuk dicetak. Unduh PDF resmi dari layar hasil.';
    display: block; padding: 24px 0; font-weight: 700;
  }
}

.preview-pages {
  max-height: 74vh; overflow-y: auto; overflow-x: hidden;
  border: 1px solid var(--border); border-radius: var(--radius-md);
  background: var(--bg); padding: var(--space-3);
  display: flex; flex-direction: column; gap: var(--space-3);
  transition: filter var(--dur-base) var(--ease), opacity var(--dur-base) var(--ease);
}
.preview-pages img {
  width: 100%; height: auto; display: block; background: #fff;
  border: 1px solid var(--border); border-radius: 2px;
  user-select: none; -webkit-user-drag: none;
}

.dl-panel {
  margin-top: var(--space-4);
  padding: var(--space-4);
  border: 1px solid var(--border); border-radius: var(--radius-md);
  background: var(--bg);
}
.dl-panel__note { margin: 0 0 var(--space-3); font-size: var(--text-sm); color: var(--text-soft); }
.dl-panel__note b { color: var(--text); }
/* [2026-09-19, "tambahi sisa token di atas tombol Unduh Bersih"] Nempel tepi kanan (`justify-
   content:flex-end`) spy pas persis di atas `.dl-panel__buttons` yg jg rata-kanan -- bukan
   rata-kiri spt `.dl-panel__note` di atasnya. */
.dl-panel__balance {
  display: flex; align-items: center; justify-content: flex-end; gap: var(--space-2);
  margin: 0 0 var(--space-3); font-size: var(--text-xs); color: var(--text-soft);
}
/* [poin 5, 2026-09-13 "Unduh Bersih dan Unduh Gratis, jadikan bertumpuk bukan berjejer: yang
   atas yang bersih"] DULU row (berjejer) -- skr column, urutan sumber (Bersih ditulis duluan
   di HTML) otomatis taruh Bersih di ATAS. `align-items:flex-end` ikut arahan poin 5 "taruh
   sebelah kanan" -- keduanya nempel tepi kanan panel, bukan cuma bertumpuk rata-kiri. Tiap
   baris skr dibungkus `.dl-btn-group` (tombol + tooltip info, poin 2, 2026-09-17). */
.dl-panel__buttons { display: flex; flex-direction: column; align-items: flex-end; gap: var(--space-3); }
.dl-btn-group { display: inline-flex; align-items: center; gap: var(--space-2); }
.dl-cost { font-weight: 400; opacity: .9; }

/* [poin 2-5, 2026-09-17 "tooltip di sebelah kanan tombol, tanda tanya dilingkari"] Ganti
   `.dl-panel__hint`/`.dl-panel__reassure` (2 paragraf statis di bawah tombol, SELALU tampil)
   jd tooltip per-tombol -- definisi Bersih/Gratis cuma relevan sesaat mau pilih. Trigger =
   <button> asli (bukan span polos) spy focusable/tap-able tanpa JS tambahan; bubble muncul
   murni via :hover/:focus-within (tap di mobile jg jalan krn tap tombol otomatis memberi
   fokus). Warna bubble DIBALIK (bg=var(--text), teks=var(--bg)) spy otomatis kebaca di kedua
   tema tanpa override dark-mode terpisah. */
.tip { position: relative; display: inline-flex; }
.tip__trigger {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; padding: 0; border: none; background: none;
  color: var(--text-soft); cursor: pointer; border-radius: 50%;
}
.tip__trigger .icon { width: 16px; height: 16px; }
.tip__trigger:hover, .tip__trigger:focus-visible { color: var(--text); }
.tip__bubble {
  position: absolute; z-index: 20; bottom: calc(100% + 8px); right: 0;
  width: max-content; max-width: 210px;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm); background: var(--text); color: var(--bg);
  font-size: var(--text-xs); line-height: 1.5; text-align: left;
  opacity: 0; visibility: hidden; transform: translateY(4px);
  transition: opacity var(--dur-base) var(--ease), transform var(--dur-base) var(--ease);
  pointer-events: none;
}
.tip:hover .tip__bubble, .tip:focus-within .tip__bubble {
  opacity: 1; visibility: visible; transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) { .tip__bubble { transition: none; } }

/* item 5: konfirmasi sebelum token dipotong.
   item 3: animasi buka/tutup (max-height + opacity + margin) -- efek "meluncur turun" yg
   sama rasanya dgn <details> Lapor. max-height di elemen luar meng-clip padding `__inner`. */
.dl-confirm {
  max-height: 0; overflow: hidden; opacity: 0; margin-top: 0;
  transition: max-height var(--dur-base) var(--ease),
    opacity var(--dur-base) var(--ease), margin-top var(--dur-base) var(--ease);
}
.dl-confirm.is-open { max-height: 260px; opacity: 1; margin-top: var(--space-3); }
.dl-confirm__inner {
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--accent-line); border-radius: var(--radius-sm);
  background: var(--accent-soft);
}
.dl-confirm p { margin: 0 0 var(--space-3); font-size: var(--text-sm); color: var(--text); }
.dl-confirm__actions { display: flex; gap: var(--space-2); justify-content: flex-end; flex-wrap: wrap; }
@media (prefers-reduced-motion: reduce) { .dl-confirm { transition: none; } }
.dl-panel__voice {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-3);
  margin-top: var(--space-4); padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}
.dl-panel__voice .field { margin-bottom: 0; flex: 1 1 220px; }
.dl-panel__voice .field select { width: 100%; }
#dlInfo { margin-top: var(--space-3); }

.result-actions {
  display: flex; align-items: center; gap: var(--space-3);
  margin-top: var(--space-4); flex-wrap: wrap;
}
.result-actions .btn { flex: 0 1 auto; }
.result-actions .btn .icon { width: 17px; height: 17px; }

/* ---------- dashboard ---------- */
/* kartu "Pesan dari Admin" -- placeholder sementara di atas Riwayat (user iterasi UX-nya nanti) */
.admin-msg {
  border: 1px solid var(--accent-line); border-radius: var(--radius-md);
  background: var(--accent-soft);
  padding: var(--space-4);
  margin-bottom: var(--space-6);
}
.admin-msg__head {
  display: flex; align-items: center; gap: var(--space-2);
  font-size: var(--text-xs); font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  color: var(--text-on-accent-soft);
  margin-bottom: var(--space-2);
}
.admin-msg__head .icon { flex: none; }
.admin-msg__body { margin: 0; font-size: var(--text-sm); color: var(--text); line-height: 1.6; white-space: pre-wrap; }
.admin-msg__body.is-empty { color: var(--text-faint); font-style: italic; }
.admin-msg__meta { margin: var(--space-2) 0 0; }

.dash-section { margin-bottom: var(--space-6); }
.dash-section:last-child { margin-bottom: 0; }
.dash-section h2 {
  font-family: var(--font-display); font-size: var(--text-md); font-weight: 700;
  margin: 0 0 var(--space-3);
}
.dash-section > .token-badges { margin-bottom: var(--space-3); }

/* Riwayat Transkripsi: kotak cari (submit-only) + paginasi */
.job-search { display: flex; gap: var(--space-2); margin-bottom: var(--space-3); }
.job-search__input {
  flex: 1; min-width: 0; padding: 9px 12px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  font: inherit; font-size: var(--text-sm); background: var(--surface); color: var(--text);
  transition: border-color var(--dur-fast) var(--ease);
}
.job-search__input:hover { border-color: var(--border-strong); }
.job-search__btn {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 42px; border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--text-soft); cursor: pointer;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease);
}
.job-search__btn:hover { background: var(--bg); color: var(--text); border-color: var(--text-faint); }
.job-search__btn .icon { width: 18px; height: 18px; }

.pager {
  display: flex; align-items: center; justify-content: center; gap: var(--space-3);
  margin-top: var(--space-4); flex-wrap: wrap;
}
.pager__info {
  font-size: var(--text-xs); color: var(--text-soft);
  font-variant-numeric: tabular-nums; text-align: center;
}

.job-list { display: flex; flex-direction: column; gap: var(--space-2); }

.job-card {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--border); border-radius: var(--radius-md);
  background: var(--surface);
}

/* kartu deskriptif tier gratis -- BUKAN paket yang bisa dibeli, sekadar penjelasan.
   Tinta hijau (--success) + aksen kiri sengaja beda dari .dropzone (netral/dashed) supaya
   tak terbaca sebagai kotak input lain -- ini banner info, bukan tempat berinteraksi. */
.plan-free {
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-4);
  border: 1px solid var(--success-soft);
  border-left: 3px solid var(--success);
  border-radius: var(--radius-md);
  background: var(--success-soft);
  margin: var(--space-4) 0 var(--space-5);
}
.plan-free .icon { flex: none; color: var(--success-strong); margin-top: 2px; }
.plan-free strong { display: block; font-size: var(--text-sm); color: var(--success-strong); margin-bottom: 2px; }
.plan-free p { margin: 0; font-size: var(--text-xs); color: var(--text-soft); line-height: 1.55; }
.plan-free b { color: var(--text); font-weight: 600; }

.pricing-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: var(--space-4);
  margin: var(--space-3) 0;
}
.pricing-card {
  position: relative;
  display: flex; flex-direction: column;
  padding: var(--space-5) var(--space-4) var(--space-4);
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  background: var(--surface);
}
.pricing-card--highlight { border-color: var(--accent-line); border-width: 2px; box-shadow: var(--shadow-md); }
.pricing-card__badge {
  position: absolute; top: -11px; left: var(--space-4);
  background: var(--accent-strong); color: var(--text-on-accent);
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  padding: 3px 10px; border-radius: 999px;
}

/* [2026-09-16, poin 5 "grid 3x3 desktop"] HANYA layout kolom -- warna TETAP ikut tema situs
   (var --surface/--border/--text dst di .pricing-card asli di atas, tak disentuh). Sempat
   dipaksa gelap literal 1 ronde ("gaya gelap dari referensi"), DIKOREKSI user sesi lanjutan
   ("salah prompt, kembalikan yg support mode terang & gelap") -- jgn diulang jd hardcode warna. */
.pricing-grid { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 860px) { .pricing-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .pricing-grid { grid-template-columns: 1fr; } }

/* [2026-09-16, poin 1+3 "hilangkan tulisan token" + "logo+angka, 2.5x lipat"; REVISI poin 2
   "di checkout jg koin+angka, ukuran sama"] modifier .pricing-card__name--token (ditempel
   otomatis di components.js kalau nameIconHtml diisi) -- dipakai grid harga DAN ringkasan
   checkout, jd style konsisten di ke-2 tempat (bukan discope per-ancestor spt sblmnya). Cuma
   angka polos (bukan lg "X token", lihat renderPackageList/showCheckout) + koin `.token-coin--lg`. */
.pricing-card__name--token { display: flex; align-items: center; gap: 10px; font-size: 45px; }
/* [2026-09-16, poin 2 "di checkout cukup koin+angka 30px"] REVISI dr "ukuran sama" ronde
   sblmnya -- dilihat langsung berdampingan, user putuskan checkout lebih kecil (30px) drpd grid
   (45px). Koin ikut diskalakan proporsional (44px->30px, glyph 26px->18px, rasio dijaga ~0.6). */
#checkoutSummary .pricing-card__name--token { font-size: 30px; gap: 8px; }
#checkoutSummary .token-coin--lg { width: 30px; height: 30px; }
#checkoutSummary .token-coin--lg .icon--glyph { font-size: 18px; }
.pricing-card__name { font-family: var(--font-display); font-size: var(--text-md); font-weight: 700; margin: 0 0 4px; }
.pricing-card__target { font-size: var(--text-xs); color: var(--text-soft); margin: 0 0 var(--space-3); min-height: 2.6em; }
.pricing-card__price { margin-bottom: var(--space-4); }
.pricing-card__amount { font-family: var(--font-display); font-size: 25px; font-weight: 700; color: var(--text); }
.pricing-card__billing { display: block; font-size: var(--text-xs); color: var(--text-soft); }
/* [2026-09-16, poin 4 "upsize juga harga & diskon"] */
.pricing-grid .pricing-card__amount { font-size: 34px; }
.pricing-grid .pricing-card__billing { font-size: 14px; }
.pricing-card__features { list-style: none; margin: 0 0 var(--space-4); padding: 0; flex: 1; display: flex; flex-direction: column; gap: 8px; }
.pricing-card__features li { display: flex; align-items: flex-start; gap: 8px; font-size: var(--text-xs); color: var(--text-soft); }
.pricing-card__features .icon { flex: none; color: var(--success); margin-top: 1px; width: 15px; height: 15px; }
.pricing-card__current {
  display: block; text-align: center; font-size: var(--text-xs); font-weight: 600;
  color: var(--text-faint); padding: 10px; border: 1px dashed var(--border); border-radius: var(--radius-sm);
}
/* ---------- checkout: back link di ATAS + pilih metode + instruksi bayar ---------- */
.checkout-back {
  display: inline-flex; align-items: center; gap: 6px;
  background: none; border: none; padding: 0; margin: 0 0 var(--space-4);
  font: inherit; font-size: var(--text-sm); font-weight: 600; color: var(--text-soft);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease);
}
.checkout-back:hover { color: var(--text); }
.checkout-back .icon { flex: none; }
#checkoutSummary { margin-bottom: var(--space-5); }
/* [2026-09-16, poin 3 "hilangkan fitur expand saat tombol dipilih"] `.option-card:has(input:
   checked)` global nambah padding-bottom (utk kasih ruang `.option-card__upload` mengembang di
   picker sumber-file) -- metode bayar di sini TAK py upload apa pun, jd efek "mengembang" itu
   nol guna & terlihat spt bug. Netralkan cuma utk fieldset ini, bukan global (picker sumber-file
   TETAP butuh perilaku aslinya). */
#checkoutMethodField .option-card:has(input:checked) { padding-bottom: 0; }

.checkout-pay { margin-top: var(--space-4); }
.checkout-pay__amount {
  font-family: var(--font-display); font-size: var(--text-lg); font-weight: 700;
  color: var(--text); margin: 0 0 var(--space-4); text-align: center;
}
.checkout-pay__wait {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  margin: var(--space-4) 0 0; font-size: var(--text-sm); color: var(--text-soft);
}
.checkout-pay__wait .icon { flex: none; color: var(--accent-strong); }
#devPayBtn, #devPayFailBtn, #devPayExpireBtn { display: block; margin: var(--space-3) auto 0; }

/* [BARU 2026-09-21] Halaman sukses pembayaran -- ganti overlay bekas "tunggu" (.checkout-pay)
   begitu status 'paid' terdeteksi, WAJIB diklik OK sendiri (lihat balance.js). */
.checkout-success { text-align: center; padding: var(--space-2) 0; }
.checkout-success__icon {
  width: 56px; height: 56px; color: var(--success); margin: 0 auto var(--space-4);
}
.checkout-success__title {
  font-family: var(--font-display); font-size: var(--text-xl); font-weight: 700;
  color: var(--text); margin: 0 0 var(--space-2);
}
.checkout-success__desc {
  font-size: var(--text-sm); color: var(--text-soft); margin: 0 0 var(--space-5);
}
/* [FIX 2026-09-22] `.checkout-modal__card p { text-align: left }` (aturan global metode bayar)
   spesifisitasnya (class+elemen) LEBIH TINGGI drpd `.checkout-success` (class saja) -- menang
   walau `.checkout-success` diset center, bikin deskripsi ini kepentok rata kiri sendirian
   (judul & ikon di atasnya tetap center krn bukan <p>). Selector ber-ID di sini menang mutlak. */
#checkoutSuccess p { text-align: center; }
.checkout-success__desc strong { color: var(--text); font-weight: 700; }

.pay-qr {
  display: block; margin: 0 auto; max-width: 240px;
  border: 1px solid var(--border); border-radius: var(--radius-md); background: #fff;
  padding: var(--space-3);
}
.pay-qr--text code {
  display: block; font-size: 10px; line-height: 1.5; word-break: break-all;
  color: var(--text-soft); font-family: ui-monospace, Menlo, Consolas, monospace;
}
#payInstructions .hint { justify-content: center; text-align: center; }

.pay-va {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--bg);
  padding: var(--space-4);
}
.pay-va__bank { font-size: var(--text-xs); font-weight: 600; color: var(--text-soft); text-transform: uppercase; letter-spacing: .04em; }
.pay-va__num {
  font-family: var(--font-display); font-size: var(--text-lg); font-weight: 700;
  color: var(--text); font-variant-numeric: tabular-nums; letter-spacing: .04em;
}

.job-card__icon {
  flex: none; width: 34px; height: 34px; border-radius: 9px;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg); color: var(--text-soft);
}
.job-card__body { flex: 1; min-width: 0; }
.job-card__title {
  display: block; font-size: var(--text-sm); font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.job-card__meta { display: block; font-size: var(--text-xs); color: var(--text-soft); margin-top: 2px; }
.job-card__actions { display: flex; align-items: center; gap: var(--space-3); flex: none; flex-wrap: wrap; }

/* [fix poin 1, 2026-09-13] DULU `white-space: nowrap` -- label dinamis (mis. "+N token
   premium") bisa jauh lebih panjang drpd label statis lain, kepotong tanpa ellipsis kalau
   kontainernya sempit (bukan cuma di Riwayat Laporan -- generalisasi ke SEMUA pemakaian
   `.job-badge`, bukan cuma tambal 1 lokasi, spy pola sama tak muncul lg di label panjang
   manapun nanti). Boleh membungkus 2 baris sekarang, tetap terkandung rapi drpd terpotong. */
.job-badge {
  font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  padding: 2px 8px; border-radius: 999px;
}
.job-badge--paid { background: var(--success-soft); color: var(--success-strong); }
.job-badge--unpaid { background: var(--danger-soft); color: var(--danger-strong); }
/* diunduh pakai token gratis -- selesai, tp BUKAN pembelian premium -> netral, bukan hijau */
.job-badge--downloaded { background: var(--bg); color: var(--text-soft); border: 1px solid var(--border); }
/* [2026-09-11] thread Bugbase: "menunggu tindakan" bukan "salah" -- amber, bukan merah `--unpaid`. */
.job-badge--warning { background: var(--warning-soft); color: var(--warning-strong); }
/* [BARU 2026-09-25, polling real-time] Job msh 'processing' -- netral (sama nada dgn
   `--downloaded`, BUKAN warning/danger): bukan salah, cuma belum selesai. */
.job-badge--processing { background: var(--bg); color: var(--text-soft); border: 1px solid var(--border); }

/* [poin 6, 2026-09-14 "pindahkan ke bawah preview langsung, buat lebih visible, tema merah"]
   DULU garis-atas polos terselip di paling bawah halaman (di bawah tombol unduh+aksi) -- gampang
   terlewat krn posisinya & nol warna. Skr kartu ber-tema merah (`--danger*`, sama token warna
   dgn elemen "waspada/masalah" lain di situs ini) + reposisi tepat di bawah pratinjau (lihat
   index.html) spy jadi titik perhatian kedua stlh lihat hasil, bukan detail tersembunyi
   di ujung halaman. Mekanisme `<details>` (bisa dilipat) TETAP -- cuma kulitnya yg berubah. */
.bugreport {
  margin: var(--space-4) 0; padding: var(--space-4);
  border: 1.5px solid var(--danger); border-radius: var(--radius-md);
  background: var(--danger-soft);
}
.bugreport summary {
  cursor: pointer;
  font-weight: 700; font-size: calc(var(--text-sm) + 2px);
  display: flex; align-items: center; gap: var(--space-2);
  list-style: none; color: var(--danger-strong);
}
.bugreport summary .icon:first-child { color: var(--danger); }
.bugreport summary::-webkit-details-marker { display: none; }
.bugreport summary .icon-chev { transition: transform var(--dur-base) var(--ease); margin-left: auto; color: var(--danger); }
.bugreport[open] summary .icon-chev { transform: rotate(180deg); }
.bugreport[open] summary { margin-bottom: var(--space-2); }

/* item 5: animasi buka/tutup panel (Chromium 131+; browser lama fallback ke toggle instan) */
.bugreport::details-content {
  block-size: 0;
  overflow: hidden;
  opacity: 0;
  transition:
    block-size var(--dur-base) var(--ease),
    opacity var(--dur-base) var(--ease),
    content-visibility var(--dur-base) allow-discrete;
}
.bugreport[open]::details-content { block-size: auto; opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .bugreport::details-content { transition: none; }
  .bugreport summary .icon-chev { transition: none; }
}

.bugreport__reward {
  display: flex; align-items: flex-start; gap: var(--space-2);
  font-size: var(--text-xs); color: var(--text-soft); line-height: 1.55;
  background: var(--success-soft); border-radius: var(--radius-sm);
  padding: var(--space-3); margin: var(--space-3) 0 var(--space-2);
}
.bugreport__reward .icon { flex: none; color: var(--success); margin-top: 1px; }
.bugreport__reward b { color: var(--text); font-weight: 600; }
.bugreport__thanks {
  margin: 0 0 var(--space-4); font-size: var(--text-xs); font-style: italic;
  color: var(--text-soft); line-height: 1.55;
}
.bugreport .field { margin-bottom: var(--space-4); }
.bugreport .field textarea { margin-top: 0; min-height: 72px; }
/* [Fix 2026-09-23, feedback "click zone terlalu luas"] Input file asli skr `hidden` (lihat
   index.html) -- tombol pemicu adalah `<label class="btn ...">` terpisah, style di sini cuma
   jaga teksnya tak melipat 2 baris (flex item bisa wrap default kalau kolomnya sempit). */
.bugreport .bug-reply__toolbar label.btn { white-space: nowrap; margin-top: 0; }
.bugreport .btn { margin-top: var(--space-2); }

@media (max-width: 640px) {
  /* header sempit: izinkan bungkus supaya "Nama Produk" & badge tak tumpang tindih */
  .topbar { flex-wrap: wrap; row-gap: var(--space-2); }
}
@media (max-width: 480px) {
  .topbar { padding: 10px var(--space-3); }
  .result-actions { flex-direction: column; align-items: stretch; }
  /* [Fix 2026-09-25, laporan bug "logo+tombol Masuk layoutnya jelek" di HP nyata] Logo (~177px)
     + tombol "Masuk dengan Google" penuh (~210px) butuh ±400px -- HP umum (360-412px) cuma py
     ruang konten ~336-388px, jadi SELALU patah baris. ".btn--google__full" (teks " dengan
     Google") disembunyikan spy tombol jd "Masuk" saja (~104px) + ikon G yg sudah cukup dikenali
     -- muat sebaris dgn logo di hampir semua HP, nol lagi patah baris tak sengaja. */
  .btn--google__full { display: none; }
  /* [KOREKSI 2026-09-25, instruksi user langsung stlh lihat screenshot Chrome sungguhan] Sempat
     ditambah `margin-left: 0` di sini sbg "jaring pengaman" wrap -- TERNYATA lebih sering
     bikin tombol nempel PERSIS di sebelah logo dgn jarak kosong lebar di kanan (kasus UMUM,
     tombol pendek "Masuk" muat sebaris di hampir semua HP skr) drpd kasus wrap langka yg
     coba diantisipasi (~<=320px). Dicabut -- balik ke `margin-left: auto` bawaan
     `.topbar__right` (base rule) spy tombol SELALU rata-kanan tiap kali muat sebaris dgn logo,
     sesuai konvensi header umum (logo kiri, aksi kanan). */
}

/* ============================================================
   Halaman admin (admin.html) -- HALAMAN TERPISAH dari SPA index.html, tp [2026-09-12] SEKARANG
   SAMA cookie sesi Google (dulu header X-Admin-Token per-request terpisah) -- reuse token
   warna/spacing + .job-card (baris ikon+body+aksi) dr sini apa adanya utk daftar laporan/
   pembelian/user -- cuma kelas BARU yg genuinely baru ditambah di bawah.
   ============================================================ */
.admin-gate { max-width: 380px; margin: 12vh auto 0; text-align: center; }
.admin-gate h1 { margin: 0 0 var(--space-2); }
.admin-gate .sub { margin: 0 auto var(--space-5); }
.admin-gate form { text-align: left; }
.admin-gate .dev-panel { text-align: left; margin-top: var(--space-4); }
.admin-gate__icon {
  width: 40px; height: 40px; margin: 0 auto var(--space-3); display: block;
  color: var(--text-faint);
}
#googleLoginBtn { display: flex; align-items: center; justify-content: center; gap: var(--space-2); }

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: var(--space-3);
  margin-bottom: var(--space-6);
}
/* [2026-09-11] Skr <button> (dulu <div>) -- tiap kartu metrik py drill-down sendiri. */
.stat-card {
  position: relative;
  display: block; width: 100%; text-align: left;
  /* kanan dilebihkan tetap (bukan cuma saat is-active) spy nol overlap/loncatan layout dgn
     checkmark poin 5 saat nilai panjang (mis. "Rp 12.345.678" di Total Pendapatan). */
  padding: var(--space-4) 28px var(--space-4) var(--space-4);
  border: 1px solid var(--border); border-radius: var(--radius-md);
  background: var(--surface); cursor: pointer; font: inherit;
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.stat-card:hover { border-color: var(--accent-line); background: var(--accent-soft); }
.stat-card__value {
  display: block; font-family: var(--font-display); font-size: 26px; font-weight: 700;
  color: var(--text); font-variant-numeric: tabular-nums; line-height: 1.2;
}
.stat-card__label { display: block; font-size: var(--text-xs); color: var(--text-soft); margin-top: 2px; }
/* [2026-09-11, poin 5] "tanda" drill-down yg lg terbuka -- checkmark pojok, bukan cuma warna
   (warna aksen sendiri sudah dipakai `:hover`, butuh sinyal beda utk "TERPILIH" vs "diarah"). */
.stat-card__check { position: absolute; top: 10px; right: 10px; width: 16px; height: 16px; color: var(--accent-strong); display: none; }
.stat-card.is-active { border-color: var(--accent-line); background: var(--accent-soft); }
.stat-card.is-active .stat-card__check { display: block; }

/* [2026-09-11, poin 7] sub-metrik "Total Pendapatan" -- rincian per jenis paket, di ATAS
   daftar transaksi individual dlm drill-down yg sama. */
.submetric-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
  gap: var(--space-2); margin-bottom: var(--space-4);
}
.submetric {
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--bg); padding: var(--space-2) var(--space-3);
}
.submetric__value { display: block; font-weight: 700; font-size: var(--text-sm); color: var(--text); font-variant-numeric: tabular-nums; }
.submetric__label { display: block; font-size: var(--text-xs); color: var(--text-soft); margin-top: 2px; }

.metrik-drilldown { margin-top: var(--space-5); padding-top: var(--space-5); border-top: 1px solid var(--border); }
.admin-back {
  display: inline-flex; align-items: center; gap: 6px;
  background: none; border: none; padding: 0; margin: 0 0 var(--space-3);
  font: inherit; font-size: var(--text-sm); font-weight: 600; color: var(--text-soft);
  cursor: pointer; transition: color var(--dur-fast) var(--ease);
}
.admin-back:hover { color: var(--text); }
#metrikDrilldown h2 { margin: 0 0 var(--space-3); }

/* [poin 5, 2026-09-13 "semua tombol interaksi taruh sebelah kanan"] */
.admin-actions { display: flex; gap: var(--space-2); flex-wrap: wrap; justify-content: flex-end; margin-top: var(--space-3); }

/* ---------- Bugbase: master-detail spt email ---------- */
/* [2026-09-11, poin 6] hitungan "aktif" pindah dr kartu Metrik ke sini -- pil kecil di sebelah
   judul panel, reuse warna `.job-badge--warning` (semantik "perlu perhatian" sudah konsisten). */
.bugbase__head { display: flex; align-items: center; gap: var(--space-3); margin: 0 0 var(--space-2); }
.bugbase__head h1 { margin: 0; }
.bugbase {
  display: grid; grid-template-columns: minmax(220px, 300px) 1fr; gap: var(--space-4);
  align-items: start;
}
.bugbase__list {
  display: flex; flex-direction: column; gap: var(--space-2);
  max-height: 70vh; overflow-y: auto; padding-right: 2px;
}
.bugbase-item {
  display: block; width: 100%; text-align: left;
  padding: var(--space-3); border: 1px solid var(--border); border-radius: var(--radius-md);
  background: var(--surface); cursor: pointer; font: inherit;
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.bugbase-item:hover { border-color: var(--border-strong); }
.bugbase-item.is-selected { border-color: var(--accent-line); background: var(--accent-soft); }
.bugbase-item__top { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); margin-bottom: 4px; }
.bugbase-item__email { font-size: var(--text-sm); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* [poin 3, 2026-09-13] "Riwayat Laporan" (sisi user): judul/job-id/status ditumpuk atas-bawah,
   BEDA dr baris .bugbase-item__top (email+badge sejajar) yg dipakai admin's Reaktif -- di sini
   sengaja 1 field/baris spy job ID (penanda unik saat 1 job py >1 laporan, lihat poin 2 fix)
   selalu kebaca, bukan disembunyikan dlm baris sempit. */
.bugbase-item__title { display: block; font-size: var(--text-sm); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bugbase-item__jobid { display: block; font-size: 10.5px; color: var(--text-faint); font-family: monospace; margin-top: 1px; }
.bugbase-item .job-badge { display: inline-block; margin: 4px 0; }
.bugbase-item__snippet {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  font-size: var(--text-xs); color: var(--text-soft); line-height: 1.5;
}
.bugbase-item__date { display: block; font-size: 10.5px; color: var(--text-faint); margin-top: 4px; }

.bugbase__detail {
  padding: var(--space-4); border: 1px solid var(--border); border-radius: var(--radius-md);
  background: var(--surface); min-height: 200px;
}
.bugbase-detail__head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); margin-bottom: 4px; }
.bugbase-detail__head h2 { margin: 0; font-size: var(--text-md); }
.bugbase-detail__meta { font-size: var(--text-xs); color: var(--text-soft); margin: 0 0 var(--space-2); }
.bugbase-detail__screenshot { margin: var(--space-3) 0; }
.bugbase-detail__screenshot img {
  display: block; max-width: 100%; max-height: 360px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); object-fit: contain; background: var(--bg);
}
.bugbase-detail__desc { font-size: var(--text-sm); line-height: 1.6; margin: var(--space-3) 0; }

/* [FIX 2026-09-23, feedback testing nyata "percakapan admin cukup panjang, harus scroll page
   terlalu jauh"] `.bug-thread` = SATU komponen dipakai SEMUA jenis "Pesan" bolak-balik di
   situs ini (Bugbase admin, Riwayat Laporan user, widget Lapor di Hasil Transkripsi, Hubungi
   Kami user, Pesan Masuk admin -- cek transcribe.js/reports.js/bugbase.js/contact.js/pesan.js,
   semua render ke class ini) -- jadi SATU perubahan di sini otomatis berlaku ke semuanya.
   max-height+overflow-y: auto (pola SAMA dgn .bugbase__list di atas) bikin thread panjang
   discroll DI DALAM panelnya sendiri -- form balas & tombol aksi di bawahnya (di LUAR div ini)
   tetap selalu kelihatan tanpa perlu scroll halaman penuh. */
.bug-thread {
  display: flex; flex-direction: column; gap: var(--space-2); margin: var(--space-3) 0;
  max-height: 360px; overflow-y: auto; padding: var(--space-2); padding-right: calc(var(--space-2) + 4px);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.bug-msg {
  padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm);
  background: var(--bg); max-width: 85%;
}
.bug-msg--admin { align-self: flex-end; background: var(--accent-soft); }
.bug-msg__sender { display: block; font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--text-faint); }
.bug-msg--admin .bug-msg__sender { color: var(--text-on-accent-soft); }
.bug-msg__body { margin: 2px 0 0; font-size: var(--text-sm); white-space: pre-wrap; line-height: 1.5; }
.bug-msg__time { display: block; font-size: 10px; color: var(--text-faint); margin-top: 4px; }

/* [poin 7, 2026-09-13] tag "Tinjau Ulang" pd pesan yg dikirim dgn toggle menyala -- reuse warna
   `--warning` (semantik "perlu perhatian" sudah konsisten dgn job-badge--warning). */
.tag-review {
  display: inline-block; margin-left: 6px; padding: 1px 7px; border-radius: 999px;
  background: var(--warning-soft); color: var(--warning-strong); font-size: 9.5px;
  font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
}

/* [poin 6, 2026-09-13] lampiran opsional 1 pesan (paste gambar / file lain) */
.bug-msg__attach-link { display: block; margin-top: 6px; }
.bug-msg__attach-img {
  display: block; max-width: 220px; max-height: 220px; border-radius: var(--radius-sm);
  border: 1px solid var(--border); object-fit: contain; background: var(--bg);
}
.bug-msg__attach-file {
  display: inline-flex; align-items: center; gap: 6px; margin-top: 6px; padding: 4px 10px;
  border-radius: var(--radius-sm); border: 1px solid var(--border); background: var(--bg);
  font-size: var(--text-xs); color: var(--text); text-decoration: none;
}
.bug-msg__attach-file:hover { border-color: var(--border-strong); }
.bug-msg__attach-file .icon { width: 14px; height: 14px; }

.bug-reply { display: flex; flex-direction: column; gap: var(--space-2); margin-top: var(--space-3); }
.bug-reply textarea {
  width: 100%; padding: 10px 12px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  font: inherit; font-size: var(--text-sm); background: var(--surface); color: var(--text); resize: vertical;
}
/* [poin 6/7, 2026-09-13] baris alat di bawah textarea: toggle Tinjau Ulang + lampirkan file +
   nama file terlampir, tombol kirim didorong ke kanan (`margin-left:auto`, satu-satunya elemen
   "aksi" di baris ini, sisanya kontrol input -- BUKAN `.btn--right`, yg utk tombol TUNGGAL
   sendirian di kontainernya, bukan salah satu dr beberapa kontrol sebaris). */
.bug-reply__toolbar { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.bug-reply__toolbar button[type="submit"] { margin-left: auto; }
.bug-reply__attach-name {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--text-xs); color: var(--text-soft); max-width: 180px;
}
.bug-reply__attach-filename {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* [BARU 2026-09-25, "harus bisa kirim multiple file"] Wadah BANYAK chip `.bug-reply__attach-name`
   (createAttachmentController, bug-thread.js) -- container flex-wrap, tiap chip tetap punya
   batas lebar sendiri (max-width di atas) spy nama panjang tak mendorong wadah keluar baris. */
.attach-list {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
}
/* [BARU 2026-09-25, 4 laporan bug lampiran] Tombol "x" generik -- dipakai kotak balas
   (bug-thread.js) DAN chip lampiran Update/Info (admin/pesan.js), 1 gaya visual utk SEMUA
   "hapus lampiran" di aplikasi ini. */
.attach-remove-btn {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px; border-radius: 50%; border: none; background: var(--border);
  color: var(--text-soft); font-size: 13px; line-height: 1; padding: 0; cursor: pointer;
}
.attach-remove-btn:hover { background: var(--border-strong); color: var(--text); }
.changelog-attach-chip {
  display: inline-flex; align-items: center; gap: 6px; margin: 0 6px 6px 0;
}
.changelog-attach-chip .bug-msg__attach-file { margin-top: 0; }
.btn-attach {
  display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px;
  flex: none; border-radius: 999px; border: 1px solid var(--border); background: var(--surface);
  color: var(--text-soft); cursor: pointer; transition: border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.btn-attach:hover { border-color: var(--border-strong); color: var(--text); }
/* [fix poin 7, 2026-09-14] ikon 16px terlalu kecil utk bentuk paperclip (garis-silang butuh
   resolusi lebih tinggi drpd bentuk sederhana) -- dinaikkan ke 18px, sama dgn `.theme-toggle
   .icon`, ikon bulat-kecil lain yg sudah terbukti cukup legible. Path glyph sendiri jg diganti
   ke bentuk paperclip yg lebih standar/teruji (dulu hasil racik tangan, sekarang path Feather
   Icons yg memang didesain utk render stroke tajam di ukuran kecil). */
.btn-attach .icon { width: 18px; height: 18px; }
/* Toggle "Tinjau Ulang" -- ikon disembunyikan via opacity (bukan `hidden`) saat OFF spy nol
   layout shift saat dinyalakan; state ON/OFF disampaikan lewat warna DAN ikon (color-not-only). */
.btn-toggle {
  display: inline-flex; align-items: center; gap: 6px; padding: 5px 12px; border-radius: 999px;
  border: 1px solid var(--border); background: var(--surface); color: var(--text-soft);
  font: inherit; font-size: var(--text-xs); font-weight: 600; cursor: pointer;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.btn-toggle:hover { border-color: var(--border-strong); }
.btn-toggle .icon { width: 14px; height: 14px; opacity: 0; transition: opacity var(--dur-fast) var(--ease); }
.btn-toggle[aria-pressed="true"] { background: var(--warning-soft); color: var(--warning-strong); border-color: transparent; }
.btn-toggle[aria-pressed="true"] .icon { opacity: 1; }

@media (max-width: 720px) {
  .bugbase { grid-template-columns: 1fr; }
  .bugbase__list { max-height: 280px; }
}

/* ---------- thread bug sisi USER (index.html, dalam .bugreport) --
   sama komponen .bug-thread/.bug-msg dgn admin.html, TP cermin: pesan user sendiri di kanan
   (konvensi chat "pesanku di kanan"), kebalikan dr sudut pandang admin. */
.bug-thread--user-view .bug-msg--user { align-self: flex-end; background: var(--accent-soft); }
.bug-thread--user-view .bug-msg--admin { align-self: flex-start; background: var(--bg); }
.bug-thread--user-view .bug-msg--user .bug-msg__sender { color: var(--text-on-accent-soft); }
.bugreport__status { margin: 0 0 var(--space-3); }

/* baris admin butuh deskripsi yg BUNGKUS (bug report), beda dr .job-card__title yg 1-baris
   ellipsis -- kelas tambahan, bukan ganti .job-card__title. */
.admin-item { align-items: flex-start; }
.admin-item__desc { display: block; font-size: var(--text-xs); color: var(--text); margin-top: 4px; line-height: 1.55; }
.admin-item__links { display: flex; gap: var(--space-3); margin-top: 6px; }
.admin-item__links a { font-size: var(--text-xs); font-weight: 600; color: var(--accent-strong); }

.admin-token-input {
  width: 56px; padding: 6px 8px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); font: inherit; font-size: var(--text-xs);
  background: var(--surface); color: var(--text); text-align: center;
}
.admin-token-input:hover { border-color: var(--border-strong); }
.admin-grant-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.admin-grant-row label { display: flex; align-items: center; gap: 4px; font-size: var(--text-xs); color: var(--text-soft); }

.admin-empty { font-size: var(--text-sm); color: var(--text-soft); padding: var(--space-3) 0; }

/* ---------- modal ringkas (poin 3, 2026-09-12: konfirmasi Logout di topbar user & admin) --
   OVERLAY tengah layar, beda dr `.dl-confirm` yg expand INLINE (topbar terlalu sempit/pendek
   utk itu). Dipakai KEDUA halaman (index.html + admin.html) -- pola SAMA persis, genuinely 1
   komponen bersama (bukan duplikasi kecil spt file/gaya lain di js/admin/*.js / js/*.js). ---------- */
.confirm-modal {
  position: fixed; inset: 0; z-index: 1000;
  display: none; align-items: center; justify-content: center;
  /* [2026-09-16, poin 1 "dim kurang gelap, tabrakan dgn layar belakang"] .4 -> .75, dipakai
     ketiga modal (checkout/gift/logout) yg pakai kelas sama -- konsisten, bukan cuma checkout. */
  background: rgba(0, 0, 0, .75); padding: var(--space-4);
}
.confirm-modal.is-open { display: flex; }
.confirm-modal__card {
  background: var(--surface); border-radius: var(--radius-md); border: 1px solid var(--border);
  padding: var(--space-5); max-width: 340px; width: 100%; text-align: center;
}
.confirm-modal__card p { margin: 0 0 var(--space-4); font-size: var(--text-sm); color: var(--text); }
.confirm-modal__actions { display: flex; gap: var(--space-2); justify-content: center; }

/* ---------- "Klaim Hadiah" (poin 5, 2026-09-14) ---------- */
/* Ikon kado + badge notifikasi -- dipakai di dropdown profil DAN halaman Saldo (2 instance,
   logika dibagi lewat JS, lihat wireGiftButton di app.js). Idle (nol pending) = statis; ada
   pending = goyang pelan (menarik perhatian) + badge angka merah. */
.gift-icon { position: relative; display: inline-flex; align-items: center; justify-content: center; }
.gift-icon__badge {
  position: absolute; top: -5px; right: -6px; min-width: 15px; height: 15px; padding: 0 3px;
  border-radius: 999px; background: var(--danger); color: #fff; font-size: 9.5px; font-weight: 700;
  display: flex; align-items: center; justify-content: center; line-height: 1;
  border: 1.5px solid var(--surface); font-variant-numeric: tabular-nums;
}
@keyframes gift-wiggle {
  0%, 55%, 100% { transform: rotate(0deg); }
  60% { transform: rotate(-10deg); } 65% { transform: rotate(9deg); }
  70% { transform: rotate(-7deg); } 75% { transform: rotate(6deg); }
  80% { transform: rotate(-3deg); } 85% { transform: rotate(2deg); }
}
.gift-icon--pending .icon { animation: gift-wiggle 2.6s ease-in-out infinite; transform-origin: 50% 85%; }
/* [2026-09-16] margin vertikal dulu di sini (waktu tombol ini blok terpisah di bawah badge) --
   dihapus, spacing skr ditangani `.balance-header-row` (wadah bersama dgn .token-badges). */
.gift-claim-btn { display: inline-flex; align-items: center; gap: var(--space-2); }
@media (prefers-reduced-motion: reduce) {
  .gift-icon--pending .icon { animation: none; }
}

.gift-modal__card { max-width: 380px; }
.gift-modal__icon {
  display: flex; align-items: center; justify-content: center; margin: 0 auto var(--space-3);
  width: 64px; height: 64px; border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, var(--accent-soft), transparent 70%);
  color: var(--accent-strong);
}
@keyframes gift-pop {
  0% { transform: scale(1) rotate(0deg); }
  35% { transform: scale(1.35) rotate(-12deg); }
  65% { transform: scale(.92) rotate(9deg); }
  100% { transform: scale(1) rotate(0deg); }
}
.gift-modal__icon.is-opening .icon { animation: gift-pop .7s cubic-bezier(.34, 1.56, .64, 1); }
@media (prefers-reduced-motion: reduce) {
  .gift-modal__icon.is-opening .icon { animation: none; }
}
.gift-modal__card h2 { margin: 0 0 var(--space-3); font-size: var(--text-md); font-family: var(--font-display); }
.gift-modal__list { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-3); max-height: 240px; overflow-y: auto; }
.gift-modal__item {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 4px var(--space-2);
  padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm);
  background: var(--bg); text-align: left;
}
.gift-modal__item-note { font-size: var(--text-xs); color: var(--text-soft); flex: 1 1 auto; min-width: 0; }
.gift-modal__item-amount { font-size: var(--text-sm); font-weight: 700; color: var(--accent-strong); flex: none; }
.gift-modal__total { font-weight: 700; font-size: var(--text-sm); }
.gift-modal__empty { font-size: var(--text-sm); color: var(--text-soft); margin: 0 0 var(--space-4); }

/* [BARU 2026-09-20] Pop-up "Selamat Datang" -- reuse .confirm-modal (nol handler tutup selain
   tombol OK-nya sendiri, lihat komentar di index.html), h2 dibutuhkan krn base .confirm-modal
   nol styling h2 (beda dr .gift-modal__card h2 di atas yg scoped ke dirinya sendiri). */
.welcome-modal__card { max-width: 380px; }
.welcome-modal__card h2 {
  margin: 0 0 var(--space-3); font-size: var(--text-md); font-family: var(--font-display); color: var(--text);
}
.welcome-modal__card strong { color: var(--accent-strong); }

/* [poin 4, 2026-09-12] Checkout jadi overlay -- konten (form/instruksi bayar) jauh lebih
   panjang & rata-kiri drpd dialog konfirmasi kecil biasa, jd override lebar+alignment+scroll
   `.confirm-modal__card` default (340px, text-align:center) di sini saja. */
.checkout-modal__card {
  max-width: 460px; text-align: left; max-height: 90vh; overflow-y: auto;
}
.checkout-modal__card p { text-align: left; }
/* [Fix 2026-09-25, laporan QA "Tutup button terlalu mepet dengan preview panel"] Jarak-bawah
   `.confirm-modal__card p { margin-bottom: var(--space-4) }` (base rule) cuma kena elemen <p> --
   `#changelogPreviewContent` (pratinjau Update/Info) itu <div class="job-list">, bukan <p>, jd
   nol jarak ke `.confirm-modal__actions` (baris tombol Tutup) di bawahnya. */
#changelogPreviewContent { margin-bottom: var(--space-4); }
.admin-empty--sm { padding: var(--space-2) 0; font-size: var(--text-xs); }

/* ---------- sub-tab (Bugbase: Reaktif/Riwayat Bug, poin 7 2026-09-12) ---------- */
.subtabs {
  display: flex; gap: var(--space-2); margin: 0 0 var(--space-4);
  border-bottom: 1px solid var(--border);
}
.subtabs__item {
  background: none; border: none; border-bottom: 2px solid transparent;
  padding: var(--space-2) var(--space-1) var(--space-3); margin-bottom: -1px;
  font: inherit; font-size: var(--text-sm); font-weight: 600; color: var(--text-soft);
  cursor: pointer; transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.subtabs__item:hover { color: var(--text); }
.subtabs__item.is-active { color: var(--accent-strong); border-bottom-color: var(--accent-strong); }

.bugbase-toolbar {
  display: flex; align-items: flex-end; justify-content: space-between; gap: var(--space-3);
  flex-wrap: wrap; margin-bottom: var(--space-3);
}
.bugbase-toolbar .hint { margin: 0; flex: 1; min-width: 200px; }
.field--inline { display: flex; align-items: center; gap: var(--space-2); margin: 0; }
.field--inline .field__label { margin: 0; white-space: nowrap; }
.field--inline select {
  padding: 6px 10px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  font: inherit; font-size: var(--text-xs); background: var(--surface); color: var(--text);
}

/* ---------- papan "Riwayat Bug" ala JIRA (poin 7, 2026-09-12) -- 1 kolom/status, scroll
   horizontal di layar sempit (aturan proyek: konten lebar scroll di kontainernya sendiri,
   bukan bikin body scroll horizontal). ---------- */
.kanban {
  display: flex; gap: var(--space-3); overflow-x: auto; padding-bottom: var(--space-2);
}
.kanban-col {
  flex: 0 0 240px; display: flex; flex-direction: column; gap: var(--space-2);
  background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-md);
  padding: var(--space-3);
}
.kanban-col__head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }
.kanban-col__count { font-size: var(--text-xs); font-weight: 700; color: var(--text-soft); font-variant-numeric: tabular-nums; }
.kanban-col__list { display: flex; flex-direction: column; gap: var(--space-2); min-height: 40px; }
.kanban-card {
  display: block; width: 100%; text-align: left;
  padding: var(--space-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface); cursor: pointer; font: inherit;
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.kanban-card:hover { border-color: var(--border-strong); }
.kanban-card.is-selected { border-color: var(--accent-line); background: var(--accent-soft); }
.kanban-card__email {
  display: block; font-size: var(--text-xs); font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.kanban-card__snippet {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  font-size: 10.5px; color: var(--text-soft); line-height: 1.4; margin-top: 2px;
}
.kanban-card__date { display: block; font-size: 10px; color: var(--text-faint); margin-top: 4px; }
.pager--compact { margin-top: 0; }
.pager--compact .pager__info { font-size: 10.5px; }

/* ---------- Panel Admin (dulu "Bagi Token") -- 3 sub-section berturut (Bagi Token/Angkat
   Admin/Riwayat), poin 3-5 2026-09-12. ---------- */
.admin-subhead {
  margin: var(--space-6) 0 var(--space-3); padding-top: var(--space-5);
  border-top: 1px solid var(--border); font-size: var(--text-md);
}
.admin-subhead:first-of-type { margin-top: var(--space-3); padding-top: 0; border-top: none; }

/* ---------- Panel Transkripsi (poin 4, 2026-09-12) -- iframe situs utama, admin unduh gratis.
   [poin 6, ronde lanjutan] `.admin-panel--flush` buang padding `.card` supaya iframe (yg SUDAH
   berisi UI user lengkap dgn topbar-nya sendiri) ngisi panel penuh, bukan spt situs-dlm-kotak
   dgn judul admin dobel di atasnya. `overflow:hidden` biar sudut iframe ikut kebulat radius card. */
.admin-panel--flush { padding: 0; overflow: hidden; }
.admin-iframe {
  display: block; width: 100%; height: calc(100vh - 120px); min-height: 480px; border: 0;
  background: var(--surface);
}
