/* ==========================================================================
   Storefront Halo — sistem visual

   Batasan yang menentukan seluruh berkas ini: warna merek datang dari DATA
   (storefronts.primary_color), berbeda tiap Tenant dan tiap Mitra. Karena itu
   yang dirancang di sini bukan palet, melainkan SISTEM yang sanggup menampung
   warna merek apa pun tanpa hancur.

   Konsekuensinya: --brand hanya dipakai pada tombol utama, isi bar progres,
   keadaan aktif, dan aksen kecil. Seluruh sisanya netral. Netralnya sendiri
   dipilih hangat, bukan abu-abu murni -- abu netral membuat halaman terasa
   seperti dasbor, sedangkan ini halaman yang menerima uang atas nama ibadah.

   Tanpa framework CSS dan tanpa webfont: halaman ini dibuka dari WhatsApp di
   jaringan seluler, dan setiap kilobyte menunda tombol donasi.
   ========================================================================== */

:root {
  --brand: #0f766e;
  --brand-ink: #ffffff;

  /* --brand SEBAGAI TEKS, bukan sebagai latar. Keduanya tidak boleh dipakai
     bergantian: di mode gelap --brand mentah di atas --ground hanya 3,38:1,
     dan warnanya BERASAL DARI DATA (storefronts.primary_color) sehingga Tenant
     berwarna gelap jatuh lebih parah lagi -- tanpa satu pun gejala, karena
     halamannya tetap tampil utuh. Di mode terang ia sama persis dengan --brand;
     yang berubah hanya turunannya di blok gelap di bawah. */
  --brand-text: var(--brand);

  --ground: #fbfaf8;
  --surface: #ffffff;
  --surface-sunken: #f4f2ee;
  --ink: #1a1917;
  --ink-soft: #45413b;
  --muted: #6e6a63;
  --line: #e7e3dc;
  --line-strong: #d4cec4;

  /* Dipakai sebagai TEKS 11px pada lencana bertintanya sendiri (12% di atas
     --surface), jadi keduanya harus lolos 4,5:1 di sana -- bukan sekadar di
     atas putih. Nilai sebelumnya (#1f7a4c dan #b45309) hanya 3,92:1 dan
     4,16:1: "Verifikasi otomatis" dan "Verifikasi manual" adalah janji tentang
     uang, dan justru itu yang paling tidak boleh sulit dibaca. */
  --ok: #146c3a;
  --warn: #92400e;
  --danger: #b42318;
  --info: #1d4ed8;

  --radius: 12px;
  --radius-sm: 8px;
  --radius-lg: 18px;
  --shadow: 0 1px 2px rgba(26, 25, 23, 0.05), 0 8px 24px -12px rgba(26, 25, 23, 0.18);
  --shadow-lift: 0 2px 4px rgba(26, 25, 23, 0.06), 0 16px 32px -16px rgba(26, 25, 23, 0.24);

  --page: 40rem;
  --page-wide: 68rem;

  --logo-backdrop-light: #ffffff;
  --logo-backdrop-dark: #1c1c17;

}

@media (prefers-color-scheme: dark) {
  :root {
    --ground: #14140f;
    --surface: #1c1c17;
    --surface-sunken: #242420;
    --ink: #f2f0eb;
    --ink-soft: #d6d2c9;
    --muted: #9d988e;
    --line: #302f29;
    --line-strong: #423f38;
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 24px -12px rgba(0, 0, 0, 0.6);
    --shadow-lift: 0 2px 4px rgba(0, 0, 0, 0.45), 0 16px 32px -16px rgba(0, 0, 0, 0.7);

    /* Warna merek dicerahkan MENUJU PUTIH, bukan diganti: identitas Tenant
       harus tetap terbaca sebagai warnanya sendiri. Diturunkan dari --brand
       lewat color-mix supaya ia ikut berubah mengikuti data -- daftar warna
       tetap di sini akan benar untuk satu Tenant dan salah untuk semua yang
       lain. */
    --brand-text: color-mix(in srgb, var(--brand) 55%, #ffffff);

    /* Kebalikan dari mode terang: di atas permukaan gelap, hijau dan jingga
       pekat justru melebur ke latarnya. */
    --ok: #4ade80;
    --warn: #fbbf24;
    --danger: #f87171;
    --info: #7dd3fc;
  }
}

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

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

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  font-size: 16px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

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

a {
  color: inherit;
}

h1,
h2,
h3 {
  margin: 0;
  line-height: 1.25;
  letter-spacing: -0.015em;
  text-wrap: balance;
}

h1 {
  font-size: clamp(1.375rem, 1.1rem + 1.4vw, 1.875rem);
  font-weight: 700;
}
h2 {
  font-size: 1.0625rem;
  font-weight: 650;
}
h3 {
  font-size: 0.9375rem;
  font-weight: 650;
}

p {
  margin: 0 0 0.85em;
}
p:last-child {
  margin-bottom: 0;
}

/* Uang dan hitungan selalu tabular: kolom angka yang tidak sejajar terbaca
   sebagai kesalahan, dan halaman ini penuh angka. */
.money {
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

/* --- kerangka halaman ------------------------------------------------------ */

.shell {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

.wrap {
  width: 100%;
  max-width: var(--page);
  margin-inline: auto;
  /* Tumbuh bersama layar. Nilai tetap 1rem membuat isi menempel ke tepi pada
     tablet, tempat --page-wide belum menggigit sehingga tidak ada margin lain
     yang menolong. */
  padding-inline: clamp(1rem, 3.5vw, 2rem);
}

.wrap-wide {
  max-width: var(--page-wide);
}

/* Halaman akun: masuk, daftar, dan seluruh alur kata sandi.
 *
 * Kolomnya sempit dengan sengaja -- satu lajur kolom isian tidak bertambah baik
 * dengan bertambah lebar -- tetapi lebarnya BUKAN alasan ia tampak kecil di
 * desktop. Yang membuatnya begitu adalah kartunya menempel ke atas: pada layar
 * 1440x900 ia berdiri di 368px dengan hampir 400px ruang kosong di bawahnya,
 * sehingga yang terbaca bukan "kolom yang fokus" melainkan "halaman yang gagal
 * memuat sisanya".
 *
 * Nilainya dulu ditulis sebagai style inline yang sama persis di enam template.
 * Enam salinan satu angka berarti enam tempat yang dapat menyimpang, dan yang
 * menyimpang akan ditemukan satu per satu oleh orang yang membuka halamannya. */
.auth-page {
  max-width: 27rem;
}

/* Judul halaman akun lebih kecil daripada judul katalog: halaman ini kartu
 * fungsional, bukan kepala pemasaran. Dulu tiga template menyetelnya lewat
 * style inline 1.35rem sementara empat lainnya memakai clamp bawaan -- dua
 * ukuran h1 di dalam satu alur yang sama (daftar -> terkirim -> buat sandi). */
.auth-page h1 {
  font-size: 1.35rem;
}

/* Dipusatkan pada RUANG YANG TERSISA, bukan pada layar: header dan footer sudah
 * memakan bagiannya, dan memusatkan terhadap viewport akan mendorong kartu turun
 * melewati keduanya.
 *
 * :has() menyeleksi induknya, sehingga aturan ini hanya menyentuh halaman akun
 * dan tidak satu pun halaman lain -- .site-main dipakai bersama seluruh etalase.
 * Peramban yang belum mengenal :has() membuang seluruh blok ini dan kembali ke
 * perataan atas, yang persis keadaan sebelumnya: mundur, bukan rusak.
 *
 * "safe" menjaga isi yang lebih tinggi daripada ruangnya: tanpa itu, formulir
 * daftar pada jendela pendek akan terpusat sampai kepalanya terpotong ke atas,
 * tempat tidak ada gulir yang dapat menjangkaunya. */
.site-main:has(> .auth-page) {
  display: grid;
  align-content: safe center;
}

@media (min-width: 48rem) {
  .auth-page {
    max-width: 30rem;
  }

  /* Kartu akun bernapas lebih lega daripada kartu di dalam daftar. Padding
   * 1.125rem milik .card-pad lahir untuk kartu yang berjajar padat di ponsel;
   * pada kartu tunggal yang menjadi satu-satunya isi layar, ia membuat kolom
   * isian nyaris menyentuh tepinya.
   *
   * Diberi kekhususan (0,2,0) terhadap .card-pad yang (0,1,0), jadi ia menang
   * tanpa !important -- dan HANYA di dalam .auth-page, sehingga kartu di halaman
   * lain tidak ikut berubah. */
  .auth-page .card-pad {
    padding: 1.75rem;
  }
}

.site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  backdrop-filter: saturate(1.4) blur(10px);
  border-bottom: 1px solid var(--line);
}

.site-header > .wrap {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-height: 3.5rem;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  text-decoration: none;
  font-weight: 650;
  letter-spacing: -0.01em;
  min-width: 0;
}

.brand-logo {
  height: 1.75rem;
  width: auto;
  border-radius: 6px;
}

.brand-mark {
  display: grid;
  place-items: center;
  width: 1.875rem;
  height: 1.875rem;
  border-radius: 8px;
  background: var(--brand);
  color: var(--brand-ink);
  font-size: 0.875rem;
  font-weight: 700;
  flex: none;
}

.brand-text {
  display: grid;
  min-width: 0;
  line-height: 1.2;
}
.brand-name {
  font-size: 0.9375rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.brand-under {
  font-size: 0.6875rem;
  font-weight: 500;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.header-nav {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 0.25rem;
}

.header-nav a {
  padding: 0.4rem 0.6rem;
  border-radius: var(--radius-sm);
  font-size: 0.875rem;
  font-weight: 550;
  color: var(--ink-soft);
  text-decoration: none;
}
.header-nav a:hover {
  background: var(--surface-sunken);
  color: var(--ink);
}
/* Penandanya dipasang layout.html dengan membandingkan Path terhadap
   Scope.URL, bukan dengan merangkai path sendiri: di etalase Mitra setiap
   tautan berprefiks /m/{slug}, sehingga perbandingan terhadap "/campaign"
   telanjang tidak pernah cocok dan halaman aktif tidak pernah tertandai. */
.header-nav a[aria-current="page"] {
  color: var(--brand-text);
  background: color-mix(in srgb, var(--brand) 10%, transparent);
}

.header-nav a:focus-visible,
.footer-links a {
  position: relative;
  display: inline-block;
  padding-block: 0.4rem;
}
.footer-links a::after {
  content: "";
  position: absolute;
  inset: 50% -0.25rem auto -0.25rem;
  height: 2.75rem;
  transform: translateY(-50%);
}

.footer-links a:focus-visible,
.brand:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* Di bawah 60rem seluruh nav inline menyerah kepada menu lipat: satu ambang,
   sama dengan runtuhnya tata letak ruang akun, dan header ponsel tinggal
   merek + sesi tidaknya jelas dari isi menunya. Formulir keluar ikut karena
   panel menyediakan padanannya. */
@media (max-width: 59.999rem) {
  .header-nav {
    display: none;
  }
}

/* "Keluar" adalah tombol karena ia MENULIS, tetapi ia harus terbaca sebagai
   bagian dari nav -- sebuah tombol bergaya tombol di sebelah tautan teks
   membuat keluar tampak sebagai aksi utama halaman. Gayanya karena itu disamakan
   dengan .header-nav a, bukan dengan .btn. */
.header-session {
  display: flex;
}
.header-session button {
  padding: 0.4rem 0.6rem;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--ink-soft);
  font: inherit;
  font-size: 0.875rem;
  font-weight: 550;
  cursor: pointer;
}
.header-session button:hover {
  background: var(--surface-sunken);
  color: var(--ink);
}
.header-session button:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: 2px;
}

/* Menu lipat header -- SATU untuk semua halaman di bawah 60rem, ambang yang
   sama dengan runtuhnya nav inline dan sidebar ruang akun. Isinya ditentukan
   SESI, bukan halaman: ber-sesi berarti butir ruang akun + tautan publik,
   tanpa sesi berarti Masuk + tautan publik -- menu yang berubah bentuk
   mengikuti tempat donatur berdiri terbaca sebagai dua produk. Ikon menu di
   pojok jauh lebih terbaca sebagai "ini bisa dibuka" daripada baris ringkasan
   tertutup di atas isi, yang dulu menjadi menu akun versi ponsel. */
.head-menu {
  position: relative;
  display: none;
  /* Ke tepi kanan: pendorong auto biasanya milik .header-nav, tetapi nav itu
     tersembunyi persis ketika menu ini tampil -- tanpa pendorongnya sendiri,
     tombolnya menempel ke nama lembaga dan panelnya terpotong tepi kiri. */
  margin-left: auto;
}
.head-menu > summary {
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: var(--radius-sm);
  color: var(--ink-soft);
  cursor: pointer;
  list-style: none;
}
.head-menu > summary::-webkit-details-marker {
  display: none;
}
.head-menu > summary:hover,
.head-menu[open] > summary {
  background: var(--surface-sunken);
}
.head-menu > summary:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: 2px;
}
.head-menu-panel {
  position: absolute;
  right: 0;
  top: calc(100% + 0.4rem);
  z-index: 50;
  width: 16rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lift);
  overflow: hidden;
}
@media (max-width: 59.999rem) {
  .head-menu {
    display: block;
  }
}
/* Panel adalah DROPDOWN, bukan sidebar -- butirnya berbagi markup dengan
   sidebar akun tetapi tidak bahasa rupanya. Chevron per-baris padam: panel
   yang sudah terbuka tidak butuh afordansi "masuk lebih dalam". Garis
   per-baris padam juga; yang tersisa garis PEMISAH KELOMPOK -- di atas tautan
   publik dan di atas Keluar -- supaya butir akun, tautan publik, dan aksi
   pengakhir terbaca sebagai tiga kelompok, bukan satu daftar panjang. */
.head-menu-panel .chev {
  display: none;
}
.head-menu-panel .row {
  border-bottom: 0;
}
.head-menu-panel .head-menu-public,
.head-menu-panel .account-nav-out {
  border-top: 1px solid var(--line);
  margin-top: 0;
}

.site-main {
  flex: 1;
  padding-block: 1.5rem 4rem;
}

/* Bar aksi melekat di bawah. Di ponsel inilah satu-satunya tombol donasi yang
   selalu terjangkau ibu jari, dan itu pola yang disepakati seluruh rujukan. */
.action-bar {
  position: sticky;
  bottom: 0;
  z-index: 30;
  background: color-mix(in srgb, var(--surface) 94%, transparent);
  backdrop-filter: blur(10px);
  border-top: 1px solid var(--line);
  padding: 0.75rem 0 calc(0.75rem + env(safe-area-inset-bottom));
}

.action-bar > .wrap {
  display: flex;
  gap: 0.625rem;
}

/* Pembungkus tombol Bagikan. Ia ada hanya karena daerah live-nya harus
   bersaudara dengan tombolnya, bukan berada di dalamnya -- role="status" di
   dalam <button> ikut terbaca sebagai nama tombolnya. Dijadikan flex supaya
   tombol di dalamnya tetap setinggi barisnya di action-bar, dan tetap selebar
   kolomnya di panel kanan. */
.share {
  display: flex;
}
.share > .btn {
  flex: 1;
}

.site-footer {
  border-top: 1px solid var(--line);
  background: var(--surface);
  padding-block: 2rem 2.5rem;
  color: var(--muted);
  font-size: 0.8125rem;
}

.footer-grid {
  display: grid;
  gap: 1.25rem;
}
@media (min-width: 34rem) {
  .footer-grid {
    grid-template-columns: 1.4fr 1fr;
  }
}

/* Tautan footer setinggi 15px hanya cukup untuk mata, bukan untuk jempol.
   padding-block menaikkannya ke ~29px dan pita ::after menuntaskannya ke 44px,
   tanpa mengubah jarak antar-baris yang terlihat. */
.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem 1rem;
  padding: 0;
  margin: 0;
  list-style: none;
}
.footer-links a {
  text-decoration: none;
  color: var(--ink-soft);
}
.footer-links a:hover {
  text-decoration: underline;
}

/* Atribusi pemegang izin. Ditegakkan di penyaji, bukan lewat kolom -- tidak ada
   cara mematikannya lewat data. */
.attribution {
  margin-top: 1.25rem;
  padding-top: 1rem;
  border-top: 1px dashed var(--line-strong);
  font-size: 0.8125rem;
}
.attribution strong {
  color: var(--ink-soft);
}

/* --- tombol ---------------------------------------------------------------- */

.btn {
  --btn-bg: var(--surface);
  --btn-fg: var(--ink);
  --btn-line: var(--line-strong);
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  padding: 0.75rem 1.1rem;
  border: 1px solid var(--btn-line);
  border-radius: var(--radius);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font: inherit;
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.08s ease, filter 0.15s ease, background 0.15s ease;
}
.btn:hover {
  filter: brightness(0.97);
}
.btn:active {
  transform: translateY(1px);
}
.btn:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: 2px;
}

.btn-primary {
  --btn-bg: var(--brand);
  --btn-fg: var(--brand-ink);
  --btn-line: transparent;
  box-shadow: var(--shadow);
}

.btn-danger {
  --btn-fg: var(--danger);
  --btn-line: color-mix(in srgb, var(--danger) 45%, var(--line));
}

.btn-block {
  width: 100%;
}
.btn-grow {
  flex: 1;
}
/* Kecil hanya SECARA VISUAL. Tingginya tetap 2.75rem karena "Ubah" dan
   "Aamiin" duduk persis di jalur bayar: sebelumnya keduanya 32px, di bawah
   ambang 44px yang dapat dikenai ibu jari dengan andal. Yang dikecilkan tetap
   tipografi dan padding mendatarnya, sehingga tata letak desktop tidak
   melar -- tombol yang lebih tinggi 12px tidak memindahkan apa pun. */
.btn-sm {
  padding: 0.45rem 0.75rem;
  min-height: 2.75rem;
  font-size: 0.8125rem;
  border-radius: var(--radius-sm);
}

/* --- kartu & seksi --------------------------------------------------------- */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
}

.card-pad {
  padding: 1.125rem;
}

.stack {
  display: grid;
  gap: 1.25rem;
}
.stack-sm {
  display: grid;
  gap: 0.625rem;
}
.stack-lg {
  display: grid;
  gap: 2rem;
}

.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.75rem;
}
.section-head a:not(.btn) {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--brand-text);
  text-decoration: none;
  white-space: nowrap;
}

.eyebrow {
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.muted {
  color: var(--muted);
}
.small {
  font-size: 0.8125rem;
}
.tiny {
  font-size: 0.75rem;
}

/* Subjudul halaman: kalimat kecil meredup tepat di bawah h1.
   Lahir dari sembilan salinan `class="muted small" style="margin-top: 0.25rem"`
   yang tersebar di sembilan template -- sembilan tempat yang dapat menyimpang,
   dan subjudul adalah bentuk pertama yang disalin halaman baru. */
.page-sub {
  margin-top: 0.25rem;
  font-size: 0.8125rem;
  color: var(--muted);
}

/* Catatan kaki terpusat: baris kecil di ujung halaman -- "Ada kendala?",
   tautan pulang, tautan daftar/masuk. Sebelumnya tiap halaman menulis
   `class="tiny muted" style="text-align: center"`-nya sendiri. */
.footnote {
  text-align: center;
  font-size: 0.75rem;
  color: var(--muted);
}

/* Baris seksi: judul + hitungan + chevron, menuju rutenya sendiri.
   Satu partial dipakai berkali-kali; detail campaign tetap pendek dan daftar
   panjangnya dipaginasi di halaman masing-masing. */
.rows {
  display: grid;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.9rem 1.125rem;
  text-decoration: none;
  color: inherit;
  border-bottom: 1px solid var(--line);
  background: none;
  border-left: 0;
  border-right: 0;
  border-top: 0;
  font: inherit;
  text-align: left;
  cursor: pointer;
  width: 100%;
}
.row:last-child {
  border-bottom: 0;
}
.row:hover {
  background: var(--surface-sunken);
}
/* Cincinnya MASUK KE DALAM, bukan keluar. `.rows` memangkas isinya dengan
   overflow: hidden supaya latar hover tidak melimpah dari sudut membulatnya --
   dan pemangkasan yang sama memotong habis cincin fokus bawaan, sehingga
   pengguna papan ketik menelusuri daftar kanal bayar tanpa tahu ia sedang di
   baris mana. outline-offset negatif menaruh cincinnya di dalam baris,
   sehingga ia selamat tanpa membuka pemangkasnya. */
.row:focus-visible,
.channel:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: -3px;
  border-radius: var(--radius-sm);
}
.row-title {
  font-weight: 600;
  font-size: 0.9375rem;
}
.row-title-danger {
  color: var(--danger);
}
.row-thumb {
  width: 2.75rem;
  height: 2.75rem;
  flex-shrink: 0;
  object-fit: cover;
  border-radius: var(--radius-sm);
  background: var(--surface-sunken);
}
.row-meta {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--muted);
  font-size: 0.8125rem;
}
/* Ketebalan garis SVG inline memakai DUA nilai saja. Glyph penunjuk --
   chevron, panah, centang -- 2.5, supaya tetap tegas di 14-16px; ikon garis
   lainnya (outline) 1.75. Nilai tengah (2) sempat tersebar tanpa aturan dan
   membuat ikon yang berdampingan tampak acak ketebalannya. */
.chev {
  flex: none;
  opacity: 0.5;
}

/* --- blok dana ------------------------------------------------------------- */

.fund-amount {
  font-size: clamp(1.5rem, 1.2rem + 1.5vw, 1.875rem);
  font-weight: 700;
  color: var(--brand-text);
  line-height: 1.15;
}

.fund-label {
  font-size: 0.8125rem;
  color: var(--muted);
}
.fund-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
}

.fund-meta {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  font-size: 0.8125rem;
  color: var(--muted);
  margin-top: 0.5rem;
}
.fund-meta strong {
  color: var(--ink-soft);
  font-weight: 650;
}

.progress {
  height: 6px;
  border-radius: 999px;
  background: var(--surface-sunken);
  overflow: hidden;
  margin-top: 0.625rem;
}
.progress > i {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--brand);
}

/* --- kartu campaign -------------------------------------------------------- */

.campaign-grid {
  display: grid;
  gap: 0.875rem;
}
/* Jumlah kolom ditentukan lebar WADAH, bukan lebar layar.
   
   Versi sebelumnya memakai media query: dua kolom sejak 46rem, tiga sejak
   68rem, dan tiga dibatasi ke .wrap-wide supaya judul tidak terpotong. Batasan
   itu benar TETAPI tidak menjangkau kisi yang duduk di kolom sempit: pada
   halaman /donate/{slug} kisinya berada di dalam kolom formulir 576px, jadi
   aturan 46rem tetap memecahnya jadi dua -- kartu 281px, thumbnail tetap 120px,
   dan kolom teks tinggal 121px. Judulnya terender "Sumur Air Bersih unt…" di
   ketiga lebar desktop.
   
   Media query mengukur hal yang salah. Yang menentukan apakah sebuah kartu
   muat adalah lebar wadahnya, dan auto-fit menanyakannya langsung.
   
   Ambangnya 20rem: kartu 320px dikurangi thumbnail 120px, gap, dan padding
   menyisakan kolom teks yang cukup untuk dua baris judul penuh. Di bawah itu
   kisinya jatuh ke satu kolom -- lebih baik satu kartu terbaca daripada dua
   kartu yang judulnya terpotong.
   
   min(100%, 20rem) menjaga wadah yang lebih sempit dari 20rem tidak meluber. */
.campaign-grid {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
}

.ccard {
  display: grid;
  grid-template-columns: 7.5rem 1fr;
  gap: 0.875rem;
  padding: 0.75rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  text-decoration: none;
  color: inherit;
  transition: box-shadow 0.15s ease, transform 0.08s ease;
}
.ccard:hover {
  box-shadow: var(--shadow-lift);
}
.ccard:active {
  transform: translateY(1px);
}
.ccard:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: 2px;
}

.ccard-thumb {
  aspect-ratio: 16 / 10;
  border-radius: var(--radius-sm);
  background: linear-gradient(
    135deg,
    color-mix(in srgb, var(--brand) 22%, var(--surface-sunken)),
    var(--surface-sunken)
  );
  display: grid;
  place-items: center;
  color: color-mix(in srgb, var(--brand) 70%, var(--muted));
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-align: center;
  padding: 0.25rem;
}

.ccard-body {
  min-width: 0;
  display: grid;
  align-content: start;
  gap: 0.3rem;
}

.ccard-title {
  font-size: 0.9375rem;
  font-weight: 650;
  line-height: 1.35;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* DUA baris, bukan satu. Yang terpangkas pada satu baris justru ekornya --
   "Yayasan Cahaya Insani · dipromo…" -- padahal penanda "dipromosikan" itulah
   yang menerangkan bahwa program ini milik Mitra dan ditampilkan atas kurasi
   Tenant. Itu materi kepercayaan, bukan hiasan. */
.ccard-owner {
  font-size: 0.75rem;
  color: var(--muted);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.ccard-fund {
  margin-top: 0.15rem;
  font-size: 0.8125rem;
}
.ccard-fund b {
  font-weight: 700;
}

/* --- lencana --------------------------------------------------------------- */

.badge {
  /* Anak grid melar mengikuti kolomnya, dan inline-flex tidak menahannya:
     lencana "Zakat" pernah selebar 608px dengan teks 11px terkulai di pojok,
     persis seperti komponen yang gagal dimuat. */
  justify-self: start;
  width: fit-content;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  font-size: 0.6875rem;
  font-weight: 650;
  letter-spacing: 0.01em;
  background: var(--surface-sunken);
  color: var(--ink-soft);
  border: 1px solid var(--line);
}
.badge-brand {
  background: color-mix(in srgb, var(--brand) 12%, var(--surface));
  color: color-mix(in srgb, var(--brand-text) 78%, var(--ink));
  border-color: color-mix(in srgb, var(--brand) 24%, var(--line));
}
.badge-ok {
  background: color-mix(in srgb, var(--ok) 12%, var(--surface));
  color: var(--ok);
  border-color: color-mix(in srgb, var(--ok) 26%, var(--line));
}
.badge-warn {
  background: color-mix(in srgb, var(--warn) 12%, var(--surface));
  color: var(--warn);
  border-color: color-mix(in srgb, var(--warn) 26%, var(--line));
}

.chips {
  display: flex;
  gap: 0.4rem;
  overflow-x: auto;
  padding-bottom: 0.25rem;
  scrollbar-width: none;
}
.chips::-webkit-scrollbar {
  display: none;
}
/* Chip 36px kurang 8px dari sasaran sentuh yang nyaman. Tingginya dinaikkan
   lewat min-height, bukan padding, supaya teksnya tetap di tengah. */
.chip {
  flex: none;
  min-height: 2.75rem;
  display: inline-flex;
  align-items: center;
  padding: 0.4rem 0.75rem;
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--ink-soft);
  font-size: 0.8125rem;
  font-weight: 550;
  text-decoration: none;
  white-space: nowrap;
}
.chip[aria-current="true"] {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--brand-ink);
}
.chip:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: 2px;
}

/* --- pintasan jenis dana --------------------------------------------------- */

/* TIGA kolom dikunci sampai 34rem, bukan diserahkan ke auto-fit. Dengan
   minmax(5rem, ...) jumlah kolomnya berubah tepat di tengah rentang ponsel:
   360px dapat 3 dan rapi, 390px dapat 4 -- sehingga enam jenis dana jatuh
   sebagai 4+2 dengan lubang selebar dua tile, dan labelnya terpaksa membelah.
   Lebar tile yang mengikuti kolom jauh lebih baik daripada jumlah kolom yang
   mengikuti lebar layar. */
.quick {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.5rem;
}
/* Paling banyak ENAM petak per baris (ADR 0039): lebih dari itu tiap petak
   menyempit dan barisannya berhenti terbaca sebagai pilihan -- ia jadi
   deretan. Batasnya ditegakkan lewat lebar kolom minimum yang tidak pernah
   lebih kecil dari seperenam wadahnya (dikurangi lima gap 0.5rem), sehingga
   auto-fit tidak pernah memuat kolom ketujuh; petak yang lebih sedikit tetap
   melebar memenuhi barisnya. */
@media (min-width: 34rem) {
  .quick {
    grid-template-columns: repeat(auto-fit, minmax(max(7rem, calc((100% - 2.5rem) / 6)), 1fr));
  }
}

.quick a {
  display: grid;
  gap: 0.35rem;
  /* justify-items, BUKAN place-items. Dengan place-items: center isi tile ikut
     dipusatkan TEGAK, sehingga ikon pada tile berlabel satu baris duduk lebih
     rendah daripada ikon tetangganya yang berlabel dua baris -- barisan ikon
     yang seharusnya sejajar tampak berombak. Yang dipusatkan cukup mendatar;
     tegaknya dimulai dari atas, dan labelnya dijatah tinggi dua baris. */
  justify-items: center;
  align-content: start;
  padding: 0.85rem 0.4rem;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--surface);
  text-decoration: none;
  color: var(--ink-soft);
  font-size: 0.75rem;
  font-weight: 600;
  text-align: center;
}
.quick a:hover {
  border-color: color-mix(in srgb, var(--brand) 40%, var(--line));
  color: var(--ink);
}
/* Dua baris dijatahkan meski hanya terpakai satu: itu yang membuat seluruh
   tile setinggi sama tanpa bergantung pada panjang nama jenis dana, yang
   ditentukan tiap Tenant sendiri. */
.quick a span {
  min-height: 2.5rem;
}
.quick a:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: 2px;
}
.quick svg {
  color: var(--brand-text);
}

/* --- statistik ------------------------------------------------------------- */

/* Jumlah kolom MENGIKUTI jumlah petak, bukan ditetapkan empat.
   
   Versi sebelumnya selalu repeat(4, ...), sehingga lembaga yang baru
   mendaftar -- yang hanya punya satu petak "Berdiri" -- menampilkan satu
   kotak selebar seperempat dengan tiga perempat sisanya berupa bidang
   berwarna garis. Tampak seperti kisi yang gagal memuat isinya.

   auto-fit meruntuhkan track yang kosong, sehingga satu petak mengisi
   penuh, dua membelah dua, dan empat tetap berjajar empat. min(100%, ...)
   menjaga petak tunggal tidak melimpah di layar tersempit. */
.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 7.5rem), 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
/* Batas ATAS hanya untuk yang isinya sedikit.
   
   Tanpa itu, lembaga yang baru mendaftar -- yang hanya punya petak "Berdiri" --
   menampilkan satu bilah selebar 574px berisi dua kata di tengah: bacanya
   seperti spanduk, bukan angka.
   
   Batasnya TIDAK boleh dipasang tanpa syarat: empat petak justru sudah benar
   mengisi penuh, dan menyempitkannya adalah kemunduran. width: fit-content juga
   dicoba dan salah -- ia menyusut ke lebar minimum, sehingga empat petak pecah
   jadi empat baris di ponsel.
   
   :has() menanyakan jumlah petaknya langsung, dan itu satu-satunya cara CSS
   membedakan "sedikit" dari "banyak". */
/* Syaratnya dibungkus :where() supaya kekhususannya tetap (0,1,0):
   :has(.stat:nth-child(2)) telanjang menaikkan selector ini ke (0,5,0),
   dan penimpa mana pun -- termasuk `.account-main .stats`, yang melepas batas
   ini untuk petak berlabel kalimat -- kalah tanpa satu pun pesan. Peramban
   yang mengenal :has() mengenal :where() juga. */
.stats:where(:not(:has(.stat:nth-child(2)))) {
  max-width: 14rem;
}
.stats:where(:has(.stat:nth-child(2)):not(:has(.stat:nth-child(3)))) {
  max-width: 28rem;
}

.stat {
  background: var(--surface);
  padding: 0.9rem 0.75rem;
  text-align: center;
}
/* Petak yang MENAUT (ringkasan ruang akun): tampil sama dengan petak diam,
   afordansinya hover + cincin fokus. Cincin ke dalam -- .stats memangkas
   isinya dengan overflow hidden, sama seperti .rows. */
a.stat {
  text-decoration: none;
  color: inherit;
}
a.stat:hover {
  background: var(--surface-sunken);
}
a.stat:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: -3px;
}
.stat b {
  display: block;
  font-size: 1.0625rem;
  font-weight: 700;
}
.stat span {
  font-size: 0.6875rem;
  color: var(--muted);
}

/* --- cerita ---------------------------------------------------------------- */

.prose {
  color: var(--ink-soft);
}
.prose p {
  margin-bottom: 0.85em;
}
.prose blockquote {
  margin: 1.25em 0;
  padding: 0.9rem 1rem;
  border-left: 3px solid var(--brand);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  background: var(--surface-sunken);
  font-style: italic;
  color: var(--ink);
}
.prose blockquote cite {
  display: block;
  margin-top: 0.5rem;
  font-style: normal;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--muted);
}

/* --- donatur & doa --------------------------------------------------------- */

.donor {
  display: grid;
  grid-template-columns: 2.25rem 1fr;
  gap: 0.75rem;
  padding: 0.85rem 0;
  border-bottom: 1px solid var(--line);
}
.donor:last-child {
  border-bottom: 0;
}
.avatar {
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 999px;
  display: grid;
  place-items: center;
  background: var(--surface-sunken);
  color: var(--ink-soft);
  font-size: 0.75rem;
  font-weight: 700;
}
.donor-top {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem;
  font-size: 0.875rem;
}
.donor-name {
  font-weight: 650;
}
.prayer {
  margin-top: 0.35rem;
  padding: 0.6rem 0.75rem;
  border-radius: var(--radius-sm);
  background: var(--surface-sunken);
  font-size: 0.875rem;
  color: var(--ink-soft);
}
.amin {
  margin-top: 0.4rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.75rem;
  color: var(--muted);
}

/* --- formulir -------------------------------------------------------------- */

.field {
  display: grid;
  gap: 0.35rem;
}
.field > label {
  font-size: 0.8125rem;
  font-weight: 600;
}
.field .hint {
  font-size: 0.75rem;
  color: var(--muted);
}

.input,
.textarea {
  width: 100%;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: 1rem;
}
.input:focus,
.textarea:focus {
  outline: 2px solid color-mix(in srgb, var(--brand) 55%, transparent);
  outline-offset: 1px;
  border-color: var(--brand);
}
.textarea {
  min-height: 5.5rem;
  resize: vertical;
}

.input-money {
  display: flex;
  align-items: center;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
}
.input-money span {
  padding-inline: 0.85rem;
  color: var(--muted);
  font-weight: 650;
  border-right: 1px solid var(--line);
  align-self: stretch;
  display: grid;
  place-items: center;
}
.input-money input {
  flex: 1;
  min-width: 0;
  border: 0;
  padding: 0.75rem 0.85rem;
  background: none;
  color: inherit;
  font: inherit;
  font-size: 1.125rem;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
}
.input-money input:focus {
  outline: none;
}
.input-money:focus-within {
  outline: 2px solid color-mix(in srgb, var(--brand) 55%, transparent);
  border-color: var(--brand);
}

.presets {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.5rem;
}
.preset {
  display: grid;
  gap: 0.15rem;
  padding: 0.7rem 0.75rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.preset:hover {
  border-color: color-mix(in srgb, var(--brand) 45%, var(--line-strong));
}
.preset:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: 2px;
}
/* Keadaan terpilih ditandai secara kasatmata, bukan hanya bagi pembaca layar:
   nominal anjuran yang sudah aktif sejak halaman dibuka harus TERLIHAT aktif,
   atau donatur mengira ia belum memilih apa pun lalu mengetik ulang.
   Digantung pada aria-pressed, dan hanya pada itu: keadaan yang sama pernah
   ditulis dua kali (di sini dan lewat kelas .is-on) dengan dua mekanisme yang
   sama-sama hidup, sehingga satu yang diubah orang berikutnya menyisakan yang
   lain tetap berlaku. */
.preset[aria-pressed="true"] {
  border-color: var(--brand);
  background: color-mix(in srgb, var(--brand) 8%, var(--surface));
  box-shadow: inset 0 0 0 1px var(--brand);
}
.preset b {
  font-size: 0.9375rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.preset span {
  font-size: 0.6875rem;
  color: var(--muted);
  line-height: 1.35;
}

/* Baris centang adalah SASARAN SENTUH, bukan sekadar teks berpendamping kotak.
   Kotaknya sendiri 17px; yang dapat ditekan seharusnya seluruh barisnya, dan
   sebelumnya baris itu hanya setinggi 24px. Ditinggikan lewat padding supaya
   yang bertambah ikut menjadi jarak antar baris -- dua centang yang berdempetan
   sama berbahayanya dengan centang yang terlalu kecil. */
/* Tingginya mengikuti isinya, bukan dipaksa 2,75rem: empat baris centang
   berturut-turut yang masing-masing setinggi tombol membuat bagian ini lebih
   tinggi daripada seluruh isian di atasnya.

   Perlindungan salah-tekan yang sesungguhnya ada di `.submit-guard` -- jarak
   penyangga sebelum tombol yang mengirim uang. Itu yang menahan ibu jari
   mengenai "Donasi Sekarang", bukan tinggi tiap barisnya. */
/* Baris centang setinggi 27px pada formulir yang menerima uang -- termasuk
   centang titipan, yang menentukan atas nama siapa kwitansi terbit. Seluruh
   baris memang dapat ditekan karena <label> membungkusnya; yang kurang adalah
   TINGGINYA. */
.checkline {
  display: flex;
  gap: 0.6rem;
  align-items: center;
  min-height: 2.75rem;
  padding-block: 0.35rem;
  font-size: 0.875rem;
  color: var(--ink-soft);
  cursor: pointer;
}
.checkline input {
  margin-top: 0.25rem;
  width: 1.25rem;
  height: 1.25rem;
  accent-color: var(--brand);
  flex: none;
}
.checkline input:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: 2px;
}

/* Jarak SEBELUM tombol yang mengirim uang. Tanpa ini centang terakhir berhenti
   10px di atas "Donasi Sekarang": ibu jari yang meleset dari centang persetujuan
   tidak batal mencentang, melainkan MENGIRIM DONASI. Jaraknya sengaja tidak
   diserahkan pada gap .stack-sm -- yang di sini bukan ritme visual melainkan
   zona penyangga. */
/* Centang yang berurutan dirapatkan jadi satu kelompok: jarak antar blok
   formulir (10px) terlalu renggang untuk baris yang sebenarnya sebuah daftar. */
.checkline + .checkline {
  margin-top: -0.35rem;
}

/* Tombol kirim menempel di dasar layar -- HANYA di ponsel, mengikuti titik
   putus .hide-desktop (60rem). Di desktop kolomnya 576px dan menggulir jauh lebih
   murah; bilah di sana membuang ruang tanpa alasan sepadan.

   Yang membuat ini aman sudah ada lebih dulu: form-validate.js menggulirkan
   kolom pertama yang bermasalah dengan block: "nearest", dan scroll-margin-block
   6rem di bawah persis menyediakan jarak bebas untuk bilah setinggi ~69px --
   komentarnya bahkan menyebut "bar aksi melekat di bawah". Jadi ketukan yang
   terlalu dini tidak berakhir buntu, melainkan memandu ke kolom yang kurang.

   padding-bottom pada formulirnya menjaga isi terakhir tetap dapat digulir
   melewati bilah saat mengetik, bukan tertahan di baliknya. */
@media (max-width: 59.999rem) {
  .checkout-form {
    padding-bottom: 1rem;
  }
  .checkout-submit {
    position: sticky;
    bottom: 0;
    z-index: 30;
    margin-inline: -1rem;
    /* padding-atas MENGGANTIKAN margin .submit-guard, bukan menghapusnya.
       Jaraknya ada supaya ibu jari yang meleset dari centang terakhir tidak
       batal mencentang melainkan MENGIRIM DONASI -- dan dengan bilah menempel,
       apa pun dapat berhenti tepat di atas tombolnya. */
    padding: 1.25rem 1rem calc(0.75rem + env(safe-area-inset-bottom));
    background: color-mix(in srgb, var(--surface) 94%, transparent);
    backdrop-filter: blur(10px);
    border-top: 1px solid var(--line);
  }
  .checkout-submit .submit-guard {
    margin-top: 0;
  }
}

.submit-guard {
  margin-top: 1.25rem;
}

/* stepper jiwa/kuantitas */
.stepper {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface);
}
.stepper button {
  width: 2.75rem;
  height: 2.75rem;
  border: 0;
  background: none;
  color: var(--ink);
  font-size: 1.125rem;
  font-weight: 600;
  cursor: pointer;
}
.stepper button:hover {
  background: var(--surface-sunken);
}
.stepper button:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}
/* Sama seperti daftar kanal: pemangkas sudut stepper memotong cincin fokus,
   jadi cincinnya ditaruh di dalam. */
.stepper button:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: -3px;
}
.stepper output {
  min-width: 2.75rem;
  text-align: center;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  border-inline: 1px solid var(--line);
  align-self: stretch;
  display: grid;
  place-items: center;
}

/* --- kanal bayar ----------------------------------------------------------- */

.paygroup + .paygroup {
  margin-top: 1.25rem;
}
.paygroup-head {
  margin-bottom: 0.5rem;
}
/* h2, BUKAN h3. Halaman ini hanya punya satu h1 ("Pilih metode pembayaran"),
   sehingga judul grup ber-h3 melompati satu tingkat dan daftar heading yang
   dibacakan pembaca layar kehilangan susunannya. Ukurannya tetap seukuran h3
   karena yang salah tingkatannya, bukan penampilannya. */
.paygroup-head h2 {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  font-size: 0.9375rem;
}

.channel {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.85rem 1rem;
  border-bottom: 1px solid var(--line);
  text-decoration: none;
  color: inherit;
}
.channel:last-child {
  border-bottom: 0;
}
.channel:hover {
  background: var(--surface-sunken);
}
/* line-height dirapatkan dari 1,65 yang diwarisi body. Dua baris teks kanal
   ditakar agar setinggi logonya (2,25rem): tanpa ini blok teks 51px berdiri di
   sebelah logo 36px, dan barisnya tampak tidak sejajar. */
.channel-name {
  font-weight: 600;
  font-size: 0.9375rem;
  line-height: 1.3;
}
.channel-note {
  display: block;
  font-size: 0.75rem;
  line-height: 1.35;
  color: var(--muted);
}

/* --- salin ----------------------------------------------------------------- */

.copyline {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.85rem 1rem;
  background: var(--surface-sunken);
  border-radius: var(--radius);
}
.copyline div {
  min-width: 0;
}
.copyline b {
  display: block;
  font-size: 1.0625rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
  overflow-wrap: anywhere;
}

/* --- pemberitahuan --------------------------------------------------------- */

.notice {
  display: flex;
  gap: 0.65rem;
  padding: 0.85rem 1rem;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--surface-sunken);
  font-size: 0.875rem;
  color: var(--ink-soft);
}
.notice svg {
  flex: none;
  margin-top: 0.15rem;
}
.notice-warn {
  background: color-mix(in srgb, var(--warn) 8%, var(--surface));
  border-color: color-mix(in srgb, var(--warn) 28%, var(--line));
  color: color-mix(in srgb, var(--warn) 82%, var(--ink));
}

/* --- rincian dana ---------------------------------------------------------- */

.ledger {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.875rem;
}
.ledger td {
  padding: 0.6rem 0;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}
.ledger tr:last-child td {
  border-bottom: 0;
}
.ledger td:last-child {
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-weight: 650;
  white-space: nowrap;
  padding-left: 1rem;
}
/* Tooltip keterangan. TOMBOL, bukan span ber-title: title tidak terjangkau
   papan ketik dan tidak muncul di peranti sentuh sama sekali. */
.tip {
  position: relative;
  display: inline-flex;
  vertical-align: middle;
  margin-left: 0.25rem;
}

.tip-btn {
  display: inline-grid;
  place-items: center;
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--muted);
  cursor: pointer;
}
.tip-btn:hover {
  color: var(--ink-soft);
}

.tip-body {
  position: absolute;
  bottom: calc(100% + 0.4rem);
  /* Dijangkarkan ke KIRI, bukan dipusatkan: ikon ini duduk di dekat tepi kiri
     kartu, dan tooltip selebar 15rem yang dipusatkan padanya meluber keluar
     layar ponsel. */
  left: -0.25rem;
  z-index: 20;
  width: max-content;
  max-width: 15rem;
  padding: 0.5rem 0.65rem;
  border-radius: var(--radius-sm);
  background: var(--ink);
  color: var(--surface);
  font-size: 0.75rem;
  font-weight: 400;
  line-height: 1.4;
  text-align: left;
  box-shadow: var(--shadow);
}

/* Baris kedua pada kolom NILAI ikut rata kanan; .ledger .note bawaan dipakai
   kolom label yang rata kiri. */
.ledger td:last-child .note {
  text-align: right;
}

.ledger .note {
  display: block;
  font-size: 0.75rem;
  color: var(--muted);
  font-weight: 400;
}

/* Peruntukan adalah nilai ledger yang berupa KALIMAT, bukan angka.
   nowrap bawaan kolom nilai dipasang untuk nominal -- "Rp1.250.000" yang patah
   di tengah tidak terbaca lagi -- tetapi judul program panjangnya tak terbatas
   dan diisi lembaga. Dibiarkan nowrap, ia mendorong lebar tabelnya melewati
   layar ponsel, dan yang tergeser keluar adalah nominal di baris di bawahnya.

   Kelas yang sama dipakai baris EMAIL di halaman Data diri: alamat panjang plus
   lencana konfirmasinya pernah mendorong kartunya 46px keluar layar 390px --
   dan yang terpotong justru lencananya. overflow-wrap ikut dipasang karena
   email tak berspasi: white-space normal saja tidak memberinya tempat patah. */
.ledger td.purpose {
  white-space: normal;
  overflow-wrap: anywhere;
}

.ledger tr.receipt-item td {
  text-align: left;
  white-space: normal;
  font-weight: 400;
  padding-left: 0;
}
.receipt-item-line {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
}
.receipt-item-line .purpose {
  min-width: 0;
  overflow-wrap: anywhere;
}
.ledger:has(tr.receipt-item) tr:not(.receipt-item) td:first-child {
  white-space: nowrap;
}
.ledger tr.receipt-item .receipt-item-line .note {
  text-align: left;
}
.receipt-item-line .money {
  font-weight: 650;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* --- kosong ---------------------------------------------------------------- */

/* Empty state menjelaskan SIAPA yang bertanggung jawab dan menawarkan langkah
   berikutnya -- bukan sekadar "belum ada data". */
/* Empty state MENGISI ruang yang tersisa, bukan mengambang di puncaknya.
   
   main memakai flex: 1 supaya footer selalu menempel di dasar layar. Pada
   lembaga yang belum punya program, isinya hanya 185px sementara main 645px --
   sisanya 372px betul-betul hampa, dan halaman terbaca seperti belum jadi
   padahal keadaannya sudah dijelaskan dengan benar.
   
   min-height mengubah ruang mati itu menjadi ruang yang disengaja: kotaknya
   berdiri di tengah bidang yang memang miliknya. Dinyatakan dalam vh dengan
   batas rem supaya di layar pendek ia tidak justru mendorong isinya keluar. */
/* Panjang baris nyaman berhenti sekitar 75 karakter. Paragraf promo kalkulator
   mencapai 107 karakter dalam satu baris di 1440 karena ia mengisi kartu
   selebar 986px -- terbaca, tetapi mata kehilangan awal baris berikutnya. */
.promo-copy {
  max-width: 62ch;
}

/* "dari Rp300.000.000" tetap satu kesatuan. Tanpa ini pasangannya patah jadi
   "Rp9.000.000 dari" / "Rp300.000.000" -- kata "dari" tertinggal di ujung baris,
   terpisah dari angka yang ia terangkan. */
.ccard-fund .muted {
  white-space: nowrap;
}

.empty {
  display: grid;
  align-content: center;
  gap: 0.35rem;
  min-height: min(38vh, 18rem);
  padding: 1.5rem 1.125rem;
  text-align: center;
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-lg);
  background: var(--surface);
}
.empty b {
  font-size: 0.9375rem;
}
.empty p {
  font-size: 0.8125rem;
  color: var(--muted);
  margin: 0;
}
.empty > .btn {
  justify-self: center;
  margin-top: 0.5rem;
}
.empty-media {
  justify-self: center;
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  margin-bottom: 0.4rem;
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--ink) 6%, var(--surface));
  color: var(--ink);
}
.empty-warn {
  background: color-mix(in srgb, var(--warn) 8%, var(--surface));
  border-color: color-mix(in srgb, var(--warn) 28%, var(--line));
  color: color-mix(in srgb, var(--warn) 82%, var(--ink));
}
.empty-warn p {
  color: inherit;
}
.empty-warn .empty-media {
  background: color-mix(in srgb, var(--warn) 14%, var(--surface));
  color: var(--warn);
}

/* --- langkah --------------------------------------------------------------- */

.steps {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.6875rem;
  font-weight: 650;
  color: var(--muted);
  margin-bottom: 1rem;
}
.steps i {
  width: 1.375rem;
  height: 1.375rem;
  border-radius: 999px;
  display: grid;
  place-items: center;
  background: var(--surface-sunken);
  border: 1px solid var(--line-strong);
  font-style: normal;
}
/* "Selesai" dan "sekarang" dibedakan, bukan disamakan. Sebelumnya keduanya
   memakai satu kelas .on, sehingga di halaman terakhir KETIGA langkah tampil
   identik dan penanda ini berhenti menjawab pertanyaan yang menjadi alasan
   keberadaannya: saya sedang di mana. Penandanya juga dinyatakan kepada
   pembaca layar lewat aria-current="step" di templatnya -- warna saja tidak
   pernah cukup. */
.steps .done i {
  background: color-mix(in srgb, var(--brand) 16%, var(--surface));
  border-color: color-mix(in srgb, var(--brand) 55%, var(--line-strong));
  /* Digelapkan 18% dari --brand-text. Pada latar bercampur 16% brand, angkanya
     hanya mencapai 4,35:1 -- di bawah 4,5 yang dituntut teks berukuran 11px.
     Menggelapkan tulisannya lebih aman daripada memucatkan latarnya, karena
     latar itulah yang membedakan langkah "selesai" dari langkah yang belum
     dijalani. */
  color: color-mix(in srgb, var(--brand-text) 82%, #000);
}
.steps .done {
  color: var(--ink-soft);
}
.steps .now i {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--brand-ink);
}
.steps .now {
  color: var(--ink);
  font-weight: 700;
}
.steps span {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}
.steps hr {
  flex: 1;
  border: 0;
  border-top: 1px solid var(--line-strong);
  margin: 0;
}
/* Label langkah TIDAK disembunyikan di ponsel. Sebelumnya ia hilang di bawah
   26rem, sehingga di 390px stepper tinggal "1 - 2 - 3" tanpa keterangan -- dan
   justru di layar kecil itulah donatur paling perlu tahu tersisa berapa langkah.
   Ketiganya memang muat: ~278px terpakai dari 358px yang tersedia. */
@media (max-width: 22rem) {
  .steps .lbl {
    display: none;
  }
}

/* --- ringkasan melekat (desktop) -------------------------------------------
   Rujukan mengunci diri pada kolom ponsel karena punya aplikasi native.
   Storefront LAZ dibuka dari WhatsApp desktop, email kantor, dan presentasi
   CSR -- jadi kolomnya boleh melebar, dengan panel donasi yang mengikuti.  */

.detail {
  display: grid;
  gap: 1.5rem;
}
@media (min-width: 60rem) {
  .detail {
    grid-template-columns: minmax(0, 1fr) 20rem;
    align-items: start;
  }
  .detail-aside {
    position: sticky;
    top: 4.5rem;
  }
  .hide-desktop {
    display: none !important;
  }
}
@media (max-width: 59.999rem) {
  .only-desktop {
    display: none !important;
  }
}

.hero {
  aspect-ratio: 16 / 9;
  /* Kelas ini melayani DUA elemen: <img> berisi foto, dan <div> kotak cadangan
     bergradien. Tanpa width penuh, <img> yang lebih sempit daripada wadahnya
     menyisakan pilar gradien di kiri-kanan dengan sudut siku di dalam wadah
     membulat -- tampak seperti gambar gagal dimuat. */
  width: 100%;
  /* Tanpa ini <img class="hero"> memakai object-fit bawaan `fill` dan foto
     penerima manfaat DIREGANGKAN mengikuti kotak 16:9 -- wajah gepeng di
     halaman yang meminta uang atas nama ibadah. Kotak cadangan (div.hero)
     tidak terpengaruh. Kembarannya di kartu dan carousel sudah cover. */
  object-fit: cover;
  border-radius: var(--radius-lg);
  background: linear-gradient(
    135deg,
    color-mix(in srgb, var(--brand) 26%, var(--surface-sunken)),
    var(--surface-sunken)
  );
  display: grid;
  place-items: center;
  color: color-mix(in srgb, var(--brand) 70%, var(--muted));
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-bottom: 1rem;
}

.tabs {
  display: flex;
  gap: 0.25rem;
  padding: 0.25rem;
  background: var(--surface-sunken);
  border-radius: var(--radius);
  margin-bottom: 1rem;
}
.tabs button {
  flex: 1;
  padding: 0.55rem 0.5rem;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--muted);
  font: inherit;
  font-size: 0.875rem;
  font-weight: 600;
  cursor: pointer;
}
.tabs button[aria-selected="true"] {
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow);
}
.tabs button:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: -2px;
}

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* --- carousel beranda -------------------------------------------------------
   Digulir dengan scroll-snap, bukan JavaScript: ia bekerja sebelum Alpine
   termuat, mengikuti gerakan jari apa adanya, dan tetap dapat digulir dengan
   papan ketik. Alpine hanya menyalakan titik penanda.                       */

.carousel {
  position: relative;
}

.carousel-track {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 100%;
  gap: 0.75rem;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  border-radius: var(--radius-lg);
}
.carousel-track::-webkit-scrollbar {
  display: none;
}

.carousel-slide {
  scroll-snap-align: center;
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--surface-sunken);
}

.carousel-slide a {
  display: block;
  text-decoration: none;
  color: inherit;
}

.carousel-slide img {
  width: 100%;
  aspect-ratio: 12 / 5;
  object-fit: cover;
}

.carousel-caption {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  padding: 2.5rem 1rem 0.9rem;
  color: #fff;
  font-weight: 650;
  font-size: 0.9375rem;
  line-height: 1.35;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.72), transparent);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
}

.carousel-dots {
  display: flex;
  justify-content: center;
  gap: 0.75rem;
  margin-top: 0.65rem;
}

/* Titiknya tetap 8px SECARA VISUAL -- titik sebesar jempol berhenti terbaca
   sebagai penunjuk posisi dan mulai terbaca sebagai tombol.
   Sasaran sentuhnya diperluas ::after menjadi 44px tinggi, memakai pola yang
   sama dengan "Ubah" pada baris ringkasan. Jaraknya dinaikkan ke 0.75rem supaya
   pita antar-titik TIDAK saling tumpang tindih: pita yang beririsan membuat
   ketukan mendarat di titik sebelahnya, dan itu lebih menyesatkan daripada
   sasaran yang kecil. */
.carousel-dots button {
  position: relative;
  width: 0.5rem;
  height: 0.5rem;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: var(--line-strong);
  cursor: pointer;
  transition: width 0.18s ease, background 0.18s ease;
}
.carousel-dots button::after {
  content: "";
  position: absolute;
  inset: 50% -0.375rem auto -0.375rem;
  height: 2.75rem;
  transform: translateY(-50%);
}
.carousel-dots button[aria-selected="true"] {
  width: 1.375rem;
  background: var(--brand);
}
.carousel-dots button:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: 2px;
}

/* Gambar kartu campaign: sebagai <img>, bukan hanya kotak berlabel. */
img.ccard-thumb {
  object-fit: cover;
  width: 100%;
  height: auto;
}

/* --- isi panjang yang diringkas ---------------------------------------------
   Batas tingginya dipasang Alpine, bukan CSS: tanpa JavaScript isinya harus
   tetap terbaca penuh, termasuk oleh perayap dan pembaca layar.            */

.collapsible {
  position: relative;
  overflow: hidden;
}

/* Gradasi hanya untuk isi yang BENAR-BENAR terpangkas, bukan untuk setiap blok
   yang tertutup. Kalau ia menempel pada keadaan tertutup saja, paragraf pendek
   -- yang muat utuh dan karenanya tidak bertombol -- ikut memudar ke warna
   latar: pembaca melihat teks yang tampak terpotong tanpa cara membukanya,
   padahal tidak ada apa pun yang disembunyikan. */
.collapsible.is-clamped::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  height: 5rem;
  background: linear-gradient(to bottom, transparent, var(--surface) 88%);
  pointer-events: none;
}

.collapsible-more {
  margin-top: 0.6rem;
}

/* Fokus BERPINDAH ke sini saat isinya dibuka. Tombolnya menghapus dirinya
   sendiri begitu ditekan, sehingga tanpa pemindahan itu fokus jatuh ke <body>
   dan pengguna papan ketik terlempar ke awal dokumen -- tepat setelah ia
   meminta membaca lanjutannya. Cincinnya sengaja ditampilkan: ia satu-satunya
   petunjuk bahwa fokus memang berpindah ke sini. */
.collapsible:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: 4px;
  border-radius: var(--radius-sm);
}

[x-cloak] {
  display: none !important;
}

/* Gambar di dalam teks kaya. Kabar campaign berfoto, dan foto tanpa jarak
   menempel pada paragraf berikutnya. */
/* Baris teks panjang melelahkan dibaca. Paling parah justru di tablet tegak,
   tempat panel kanan sudah runtuh sehingga tidak ada yang memangkas lebarnya. */
.prose {
  max-width: 42rem;
}

.prose img {
  border-radius: var(--radius);
  margin-block: 1rem;
  width: 100%;
}

.prose figure {
  margin: 1.25rem 0;
}

.prose figcaption {
  margin-top: 0.5rem;
  font-size: 0.8125rem;
  color: var(--muted);
  text-align: center;
}

.prose h3,
.prose h4 {
  margin-top: 1.5rem;
  margin-bottom: 0.5rem;
}

.prose ul,
.prose ol {
  margin: 0 0 0.85em;
  padding-left: 1.25rem;
}

.prose li {
  margin-bottom: 0.3rem;
}

.prose a {
  color: var(--brand-text);
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* --- kepala halaman donasi ---------------------------------------------------
   Judul PROGRAM yang memimpin, bukan perintah "Pilih nominal": donatur sudah
   memilih programnya, dan yang perlu ia pastikan sebelum menyerahkan uang
   adalah bahwa ia berada di program yang benar.                            */

.donate-head {
  color: inherit;
  text-decoration: none;
  display: flex;
  gap: 0.75rem;
  align-items: center;
}
.donate-head:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: 3px;
  border-radius: var(--radius);
}

.donate-thumb {
  width: 3.25rem;
  height: 3.25rem;
  flex: none;
  border-radius: var(--radius);
  object-fit: cover;
}

.donate-title {
  margin-top: 0.3rem;
  font-size: 1.35rem;
  line-height: 1.25;
  text-wrap: balance;
}

.donate-prompt {
  margin-bottom: -0.35rem;
  font-size: 1rem;
  /* BUKAN --muted: "Pilih nominal donasi" adalah pertanyaan utama halaman ini,
     dan dengan muted ia tampil lebih redup daripada label cadangannya sendiri
     ("Atau masukkan nominal lain") yang nyaris hitam. */
  color: var(--ink-soft);
}

/* Preset tanpa label sama sekali: petak ringkas, tiga per baris. Label kosong
   BUKAN kasus istimewa yang ditambal spasi -- ia menentukan bentuk petaknya,
   karena baris kedua yang kosong terbaca sebagai sesuatu yang gagal dimuat. */
.presets-compact {
  /* minmax(0, ...) WAJIB. `1fr` sendirian berarti `minmax(auto, 1fr)`, sehingga
     kolom tidak dapat menyusut di bawah min-content-nya: tiga petak berisi
     "Rp1.000.000" memaksa dokumen jadi 414px di layar 390px, dan tombol
     bayarnya terpotong di tepi kanan. Saudaranya `.presets` sudah benar. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.presets-compact .preset {
  text-align: center;
  padding-block: 0.85rem;
}

/* Selektornya WAJIB sekhusus `.field .hint` (0,2,0). Sebagai `.hint-bad`
   telanjang (0,1,0) ia kalah dan warnanya tidak pernah berubah -- kelasnya
   terpasang, tetapi satu-satunya isyarat bahwa nominal ditolak jadi tombol yang
   meredup tanpa penjelasan. Kegagalan senyap di kolom yang menerima uang. */
.field .hint-bad {
  color: var(--danger);
}

/* --- galat formulir -------------------------------------------------------- */

/* Pesan galat yang disisipkan form-validate.js, menggantikan gelembung bawaan
   peramban yang berbahasa Inggris. Ia MENETAP di bawah kolomnya: gelembung
   bawaan hilang begitu tersentuh, sehingga donatur yang melirik ke kolom lain
   kehilangan satu-satunya keterangan tentang apa yang harus ia betulkan. */
.field-error {
  display: flex;
  gap: 0.35rem;
  margin-top: 0.35rem;
  font-size: 0.75rem;
  line-height: 1.45;
  color: var(--danger);
}

/* WAJIB, dan bukan basa-basi. `display: none` bawaan peramban untuk [hidden]
   berkekhususan (0,0,1); `.field-error` (0,1,0) mengalahkannya, sehingga node
   pesan yang disembunyikan justru TETAP TERBACA -- dan karena ia dibuat kosong
   sejak awal untuk tiap kolom, setiap formulir akan lahir dengan sederet baris
   kosong di bawah kolomnya tanpa satu pun galat terjadi. */
.field-error[hidden] {
  display: none;
}

/* Keadaan salah dinyatakan lewat aria-invalid, bukan lewat kelas tersendiri:
   satu atribut yang sekaligus dibaca pembaca layar dan menjadi kaitan gayanya,
   sehingga tampilan dan makna tidak dapat berpisah. */
.input[aria-invalid="true"],
.textarea[aria-invalid="true"] {
  border-color: var(--danger);
}
.input[aria-invalid="true"]:focus,
.textarea[aria-invalid="true"]:focus {
  outline-color: color-mix(in srgb, var(--danger) 55%, transparent);
}

/* Kolom nominal tidak menggambar bingkainya sendiri -- yang bergaris adalah
   pembungkus .input-money bersama awalan "Rp". Tanpa baris ini, satu-satunya
   kolom yang benar-benar menerima angka rupiah adalah pula satu-satunya yang
   tidak pernah tampak ditolak. */
.input-money:has(input[aria-invalid="true"]) {
  border-color: var(--danger);
}
.input-money:has(input[aria-invalid="true"]):focus-within {
  outline-color: color-mix(in srgb, var(--danger) 55%, transparent);
  border-color: var(--danger);
}

/* Header etalase MELEKAT di atas dan bar aksi melekat di bawah. Kolom yang
   di-fokus validator lalu digulir ke layar akan berhenti tepat DI BALIK
   keduanya: fokusnya benar, pesannya benar, hanya tidak ada yang terlihat --
   dan donatur menyimpulkan tombolnya rusak.

   Nilainya melebihi tinggi header dengan sengaja. Yang digulir ke layar adalah
   INPUT-nya, sedangkan LABEL-nya berdiri di atas input itu; jarak yang pas-pasan
   memunculkan kolomnya utuh tetapi memotong nama kolomnya. */
.input,
.textarea,
.input-money,
.field,
.form-error-slot {
  scroll-margin-block: 6.5rem 6rem;
}

.form-error-slot:not(:has(*)) {
  display: none;
}

.checkout-form > .form-error-slot {
  margin-bottom: 0.625rem;
}

/* Penolakan dari server, dibawa kembali sebagai kode galat lalu dipetakan jadi
   kalimat Indonesia di server. Warnanya mengikuti .notice-warn yang sudah ada. */
.notice-bad {
  background: color-mix(in srgb, var(--danger) 8%, var(--surface));
  border-color: color-mix(in srgb, var(--danger) 28%, var(--line));
  color: color-mix(in srgb, var(--danger) 82%, var(--ink));
}

/* Baris nominal pada halaman pembayaran: nilai di tengah, tombol ubah di ujung.
   Nominal hidup di URL, jadi halaman ini wajib punya jalan menyuntingnya. */
.amount-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}
.amount-value {
  margin-left: auto;
  font-size: 1.25rem;
}

/* "Ubah" pada baris ringkasan dikembalikan seukuran aslinya secara VISUAL --
   tombol setinggi 44px di sebelah angka nominal tampak seperti aksi utama,
   padahal aksi utamanya ada di bawah.
   Sasaran sentuhnya TIDAK ikut mengecil: ::after memperluas bidang yang dapat
   ditekan sampai 44px tanpa menambah tinggi yang terlihat. Membuang min-height
   begitu saja akan mengembalikan sasaran sentuh 32px yang baru saja diperbaiki. */
.amount-row .btn-sm,
.copyline .btn-sm {
  min-height: 0;
  padding-block: 0.35rem;
  position: relative;
}

.amount-row .btn-sm::after,
.copyline .btn-sm::after {
  content: "";
  position: absolute;
  inset: 50% 0 auto 0;
  height: 2.75rem;
  transform: translateY(-50%);
}

/* Nilai Metode sejajar dengan nilai Nominal di atasnya. Tanpa margin-left ia
   menempel pada labelnya, sehingga dua baris yang seharusnya membentuk satu
   kolom angka justru berdiri di tempat yang berbeda. */
/* Label "Metode" disembunyikan SECARA VISUAL saja.
   Nama bank penuh, badge instrumen, dan tombol ubah tidak muat sebaris bersama
   labelnya di lebar ponsel -- dan yang paling tidak boleh terpangkas adalah
   nama banknya. Labelnya tetap dibacakan pembaca layar: yang dibuang tata
   letaknya, bukan keterangannya. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.method-value {
  margin-left: auto;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.3rem;
}

/* Nama kanal boleh terpangkas, badge instrumennya TIDAK. Nama yang terpotong
   masih dapat dikenali dari awalannya; "Virtual Ac..." justru menghapus satu-
   satunya penanda yang membedakan kanal ini dari transfer manual bernama
   sama. */
.method-value strong {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Dirapatkan hanya di baris ini: di lebar ponsel 390px, nama bank penuh
   ditambah badge dan tombol ubah kurang belasan piksel dari muat sebaris.
   Yang dikorbankan padding badge, bukan nama banknya. */
.method-value .badge {
  flex: none;
  padding-inline: 0.4rem;
  letter-spacing: 0;
}

.method-value + .btn-sm {
  padding-inline: 0.6rem;
}

/* Kelas terpisah, bukan :has(): etalase ini dibuka dari WhatsApp, termasuk dari
   WebView Android lama yang belum mengenal selector itu. */
.amount-row-tight {
  gap: 0.5rem;
}

/* Logo kanal bayar. Tingginya dikunci supaya logo bank yang rasio aslinya
   berbeda-beda tidak membuat tiap baris setinggi berbeda. */
/* Penanda "ini dapat dipilih". Di ponsel barisnya sempit sehingga afordansinya
   tersirat; di desktop ia jadi 608px berisi nama di kiri dan ruang kosong,
   terbaca sebagai daftar keterangan alih-alih pilihan. */
.channel::after {
  content: "›";
  margin-left: auto;
  padding-left: 0.5rem;
  color: var(--muted);
  font-size: 1.25rem;
  line-height: 1;
}

.channel-logo {
  width: 2.25rem;
  height: 2.25rem;
  flex: none;
  object-fit: contain;
  border-radius: var(--radius-sm);
}

/* ── Panduan bayar: satu kartu per cara membayar.
   Yang pertama terbuka, sisanya terlipat -- donatur hanya membutuhkan salah
   satunya, dan tiga cara terbuka sekaligus membuatnya menggulir melewati
   langkah yang tidak ia pakai.

   Tanpa JavaScript tidak ada yang tersembunyi: .is-collapsed baru dipasang
   Alpine, jadi seluruh langkah terbaca meski berkasnya gagal dimuat. */
.guide-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  width: 100%;
  padding: 0.875rem 1rem;
  background: none;
  border: 0;
  font: inherit;
  font-weight: 650;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

.guide-head-static {
  cursor: default;
}

.guide-head .chev {
  flex: none;
  color: var(--muted);
  transition: transform 0.18s ease;
}

@media (prefers-reduced-motion: reduce) {
  .guide-head .chev { transition: none; }
}

.guide-head .chev.is-open {
  transform: rotate(180deg);
}

.guide-body {
  padding: 0 1rem 1rem;
}

.guide-body.is-collapsed {
  display: none;
}

/* Pemilih berkas: SELURUH barisnya adalah label, sehingga menekan di mana pun
   membuka pemilih berkas — bukan hanya pada tombolnya.

   Input bawaan disembunyikan SECARA VISUAL, bukan dimatikan. display:none akan
   mencabutnya dari alur fokus, sehingga pengguna papan ketik tidak punya cara
   apa pun membukanya — klik pada label tetap bekerja, jadi kegagalannya tak
   terlihat oleh yang mengetes dengan tetikus. */
.filepick {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.35rem 0.35rem 0.35rem 0.75rem;
  border: 1px solid var(--line, #d4d4d8);
  border-radius: 0.5rem;
  cursor: pointer;
  background: var(--surface, transparent);
}

.filepick input[type="file"] {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Nama berkas memakan sisa ruangnya dan dipotong dengan elipsis: nama dari
   kamera ponsel panjang, dan yang membungkus mendorong tombolnya keluar baris. */
.filepick-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--muted, #71717a);
  font-size: 0.9rem;
}

/* Cincin fokus dipasang pada SELURUH baris, karena yang terfokus adalah input
   yang tak terlihat. Tanpa ini, pengguna papan ketik men-Tab ke sesuatu yang
   tidak memberi satu pun tanda bahwa ia sedang terfokus. */
.filepick:focus-within {
  outline: 2px solid var(--brand, currentColor);
  outline-offset: 2px;
}

/* Tile kanal pada halaman retry adalah FORM + BUTTON, bukan tautan:
   menekannya MENULIS. Dua akibatnya harus ditambal di sini.

   display:contents pada formnya mengembalikan tombol menjadi anak langsung
   .rows. Tanpa itu, form yang menjadi anak grid membuat setiap tombol menjadi
   :last-child DI DALAM formnya sendiri -- sehingga `border-bottom: 0` berlaku
   pada SEMUANYA dan seluruh garis pemisah baris lenyap sekaligus.

   Dan tombol membawa gaya bawaan peramban -- latar, bingkai, font, perataan
   tengah -- yang .channel tidak pernah perlu menimpanya selama ia <a>. */
.channel-form {
  display: contents;
}

/* Digayakan lewat .channel-form, bukan `button.channel` saja.

   Aturan lama `.channel:last-child { border-bottom: 0 }` berkekhususan (0,2,0)
   dan MENGALAHKAN `button.channel` yang hanya (0,1,1) -- sementara setiap tombol
   memang anak terakhir formnya sendiri, sehingga seluruh garis pemisah lenyap.
   Lewat `.channel-form button.channel` kekhususannya (0,2,1) dan menang. */
.channel-form button.channel {
  width: 100%;
  font: inherit;
  text-align: left;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--line);
  cursor: pointer;
}

/* Yang terakhir ditentukan lewat FORM-nya, bukan lewat tombolnya.

   display:contents hanya mengubah pembentukan kotak tata letak -- ia TIDAK
   mengubah pohon DOM, sehingga :last-child tetap dinilai terhadap induk
   sesungguhnya. Setiap tombol adalah anak satu-satunya formnya sendiri, jadi
   `button.channel:last-child` berlaku pada SEMUANYA dan seluruh garis pemisah
   lenyap sekaligus. */
.channel-form:last-child button.channel {
  border-bottom: 0;
}

/* Pengungkap detail donasi pada halaman instruksi.

   <details> bawaan peramban: bekerja tanpa JavaScript, sudah dapat dijangkau
   papan ketik, dan keadaannya diumumkan pembaca layar. Yang digaya hanya
   penandanya, supaya ia terbaca sebagai baris yang dapat ditekan. */
/* Pengungkapnya SENDIRI yang menjadi kartu, bukan isinya.

   Sebagai teks telanjang di antara kartu-kartu lain, "Detail donasi" tidak
   terbaca sebagai sesuatu yang dapat ditekan -- ia tampak seperti keterangan
   yang tertinggal. Kartu memberinya tepi, dan tepi itulah yang menyatakan
   "ini satu benda, dan benda ini punya keadaan". */
.disclosure {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
}

/* Kartu di dalam kartu jadi dua bingkai bersarang yang tidak menyatakan apa
   pun. Yang di dalam melepas bingkainya dan hanya menyisakan jarak. */
.disclosure .card {
  background: none;
  border: 0;
  box-shadow: none;
  padding-top: 0;
}

.disclosure > summary {
  cursor: pointer;
  padding: 1.125rem;
  font-size: 0.9375rem;
  font-weight: 600;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

.disclosure > summary::-webkit-details-marker {
  display: none;
}

/* Panah dibuat sendiri karena penanda bawaan berbeda-beda di tiap peramban dan
   tidak dapat digaya. Ia berputar mengikuti keadaan, sehingga arahnya menyatakan
   "akan membuka" atau "akan menutup" tanpa satu kata pun. */
.disclosure > summary::before {
  content: "";
  width: 0.4rem;
  height: 0.4rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);
  transition: transform 0.15s ease;
}

.disclosure[open] > summary::before {
  transform: rotate(45deg);
}

.disclosure > summary:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

@media (prefers-reduced-motion: reduce) {
  .disclosure > summary::before {
    transition: none;
  }
}

/* ── Ruang akun donatur (rencana 016).

   Baris riwayat membawa BARIS KEDUA: asal etalase, tanggal, dan nomor donasi.
   Tanpa aturan ini, `.note` di dalam `.row-title` ikut mengalir di baris yang
   sama dengan judulnya -- dan yang terdorong keluar layar ponsel adalah nama
   etalase asalnya, satu-satunya penanda yang membedakan donasi bercakupan
   Tenant dari donasi yang lahir di etalase yang sedang dibuka. */
.row-title .note {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.75rem;
  font-weight: 400;
  color: var(--muted);
}

/* Baris riwayat berdiri DUA LAJUR SEJAJAR: judul di atas asal-tanggalnya di
   kiri, nominal tebal di atas keadaan pembayarannya di kanan. Bentuk lamanya
   -- nominal dan status berdampingan lalu membungkus seperlunya -- membuat
   keduanya kadang sebaris kadang bertumpuk, dan barisnya terbaca janggal.
   Dua lajur membuat pasangannya tetap: nominal sejajar judul, status sejajar
   tanggal. Chevron karena itu berdiri DI LUAR lajur ini, sebagai anak .row,
   supaya tidak ikut tertumpuk di bawah status.

   Namanya dipertahankan meski tidak lagi "wrap": ia tetap kelas khusus baris
   riwayat ruang akun, dan penjaga gayanya memegang nama ini.

   Berkelas SENDIRI, bukan menimpa `.row-meta`. Selector itu milik partial
   `section-row`, yang dipakai halaman kwitansi, halaman lembaga, dan halaman
   instruksi; menimpanya di sini membuat baris "Panduan bayar" ikut berubah
   bentuk -- di halaman yang dibuka donatur yang sedang bingung cara membayar,
   dan tanpa satu pun galat yang menyebutkan sebabnya. */
.row-meta-wrap {
  flex-direction: column;
  align-items: flex-end;
  gap: 0.15rem;
  text-align: right;
}
.row-meta-wrap .money {
  font-weight: 650;
  font-size: 0.875rem;
  color: var(--ink);
}

/* Petak keadaan pembayaran di ruang akun memakai KALIMAT sebagai label, bukan
   satu-dua kata. Batas lebar .stats untuk petak sedikit (14/28rem) lahir untuk
   angka pendek di /tentang -- pada kalimat ia menjepit: dua petak berdesakan
   sementara sisa barisnya kosong. Di ruang akun batasnya dilepas; auto-fit
   tetap membagi rata barisnya. */
.account-main .stats {
  max-width: none;
}

/* Kendali cakupan lintas-etalase: tautan kecil yang MENEMPEL pada kalimat
   yang menyatakan cakupannya (subjudul daftar, kartu ringkasan) -- pernyataan
   dan tombolnya satu tempat, bukan tombol yang menggantung di baris filter.
   Gayanya menyalin section-head a: satu bahasa untuk tautan-aksi kecil. */
.scope-link {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--brand-text);
  text-decoration: none;
  white-space: nowrap;
}
.scope-link:hover {
  text-decoration: underline;
}
.scope-link:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* Tata letak dua kolom: menu di kiri, isi di kanan.
 *
 * Aturannya ditulis SESUDAH .row-meta-wrap dengan sengaja. Penjaga gaya ruang
 * akun mengiris berkas ini dari judul bagian ini sampai bagian berikutnya lalu
 * menuntut .row-meta-wrap berada di dalam irisannya; bagian baru yang disisipkan
 * di atasnya memotong irisan itu lebih awal dan menggagalkan penjaganya dengan
 * pesan yang tidak pernah menyebut penyisipan sebagai sebabnya.
 *
 * Nav ditulis SETELAH isi di dalam DOM lalu dipindah ke kiri oleh `order`. Dengan
 * begitu ponsel memakai urutan alaminya tanpa satu baris JavaScript pun, dan
 * <h1> beserta angka tahun berjalan tetap yang pertama dibaca -- oleh mata di
 * layar 390px maupun oleh pembaca layar. Harganya: di desktop urutan fokus
 * papan ketik menjadi isi lalu nav. Itu diterima karena navigasi utama etalase
 * tetap berada di header, di atas keduanya. */
.account-layout {
  display: grid;
  gap: 2rem;
}

/* min-width: 0 supaya tabel rincian yang lebar menggulir DI DALAM kolomnya
 * alih-alih melebarkan kolomnya melewati jalur gridnya. Item grid bawaannya
 * min-width: auto, dan tanpa penimpaan ini kolom isi berhenti berukuran 576px
 * persis ketika isinya kebetulan lebih lebar. */
.account-main {
  min-width: 0;
}

@media (min-width: 60rem) {
  /* Titik pindahnya menyamai panel donasi melekat di halaman campaign, bukan
   * angka baru: satu kosakata tata letak, bukan dua. */

  /* Bingkainya SAMA PERSIS dengan kepala layout, yang memakai .wrap-wide.
   * Itulah yang membuat tepi kiri menu sejajar dengan logo dan tepi kanannya
   * sejajar dengan menu utama -- kolom yang berhenti beberapa piksel di dalam
   * garis kepala terbaca sebagai salah setel, bukan sebagai pilihan.
   *
   * Memakai var(--page-wide), BUKAN angka yang disalin: nilai yang disalin
   * berhenti sejajar begitu bingkai kepala diubah, dan tidak ada apa pun yang
   * memberi tahu. */
  .wrap-account {
    max-width: var(--page-wide);
  }

  /* Judul, menu, dan isi ditempatkan EKSPLISIT, bukan mengalir.
   *
   * Urutan DOM-nya judul -> menu -> isi, karena itulah urutan yang benar di
   * ponsel: judul menyebut halamannya, menu ringkas berdiri di bawahnya, lalu
   * isinya. Urutan itu sekaligus urutan tab, sehingga yang tampak dan yang
   * terjangkau papan ketik tidak pernah berselisih.
   *
   * Di layar lebar penempatan eksplisit memindahkan menu ke kolom kiri
   * merentang dua baris, sementara judul dan isi menumpuk di kolom kanan --
   * tanpa mengubah satu baris pun di DOM. */
  .account-layout {
    grid-template-columns: 15rem minmax(0, 1fr);
    align-items: start;
  }
  /* Judul merentang KEDUA kolom, dan menu mulai sejajar dengan kartu isi --
   * bukan menjulur ke atas melewatinya.
   *
   * Judulnya milik halaman, bukan milik kolom kanan: "Donasi Anda via LAZ
   * Peduli Umat — 2026" menerangkan seluruh yang di bawahnya, termasuk menu yang
   * menyaringnya. Menaruhnya di satu kolom membuat menu berdiri sejajar dengan
   * kalimat yang menerangkannya, dan itu yang terbaca sebagai menggantung.
   *
   * Kekhawatiran waktu perencanaan -- bahwa spanduk di atas akan menurunkan
   * derajat kalimat pembawa cakupan menjadi judul kedua -- tidak terjadi:
   * kalimat itu ADALAH judulnya, sehingga merentangkannya justru menaikkannya. */
  .account-head {
    grid-column: 1 / -1;
    grid-row: 1;
  }
  .account-nav {
    grid-column: 1;
    grid-row: 2;
  }
  .account-main {
    grid-column: 2;
    grid-row: 2;
  }

  /* Tidak ada `order` di sini. Menu berdiri lebih dulu di DOM, sehingga urutan
   * tampak dan urutan tab SAMA di kedua lebar layar.
   *
   * Dulu ia dibalik lewat order supaya menu yang panjang tidak mendahului isi
   * di ponsel. Sejak menu di ponsel menjadi satu baris ringkas yang berdiri di
   * atas isi, pembalikan itu justru merusak: menu terlihat paling atas tetapi
   * baru terjangkau papan ketik setelah SELURUH isi halaman dilewati. */
  .account-nav {
    position: sticky;
    /* 4.5rem membersihkan header melekat, yang tingginya min-height 3.5rem. */
    top: 4.5rem;
  }
}

/* Di layar sempit sidebar ini DISEMBUNYIKAN: butir-butirnya tampil lewat
 * panel menu lipat header (head-menu-account), yang mengambil alih persis di
 * ambang yang sama dengan runtuhnya tata letak dua kolom. Dulu versi ponselnya
 * berupa disclosure tertutup di atas isi -- baris "Donasi saya ⌄" yang tidak
 * terbaca sebagai menu; ikon menu di pojok header afordansinya jelas. */
@media (max-width: 59.999rem) {
  .account-nav {
    display: none;
  }
}

/* Penandanya menyalin kepala layout, bukan menemukan gaya baru: dua halaman
 * yang menandai "di sini Anda sedang berada" dengan dua cara berbeda membuat
 * pembaca menyimpulkan keduanya berarti hal yang berbeda. Panel menu lipat
 * memakai butir yang sama, jadi penandanya ikut. */
.account-nav a[aria-current="page"],
.head-menu-panel a[aria-current="page"] {
  color: var(--brand-text);
  background: color-mix(in srgb, var(--brand) 10%, transparent);
}
.account-nav a[aria-current="page"] .row-title,
.head-menu-panel a[aria-current="page"] .row-title {
  font-weight: 700;
}

/* Keluar berdiri di bawah PEMISAH. Ia bukan tujuan navigasi melainkan tindakan
 * yang mengakhiri sesi, dan yang berdiri rapat di bawah "Data diri" akan
 * ditekan oleh yang bermaksud menekan "Data diri".
 *
 * Garisnya adalah border-bottom milik baris di atasnya; yang ditambahkan di sini
 * hanya jaraknya. Garis kedua di atas formulir hanya menghasilkan dua garis
 * sejajar berjarak beberapa piksel. */
.account-nav-out {
  margin-top: 0.4rem;
}

/* Ikon meredup, kecuali pada butir yang sedang dibuka.
 *
 * Yang dibaca adalah labelnya; ikon di sini menolong mata menemukan baris yang
 * dicari, bukan menyampaikan makna. Ikon sepekat teks membuat kelimanya
 * bersaing dengan label yang justru menerangkannya. */
/* Ringkasan disclosure: baris yang menyebut halaman yang sedang dibuka.
 *
 * Disembunyikan saat TERBUKA, bukan menurut lebar layar. Aturan yang
 * menyembunyikannya di layar lebar akan menghapus satu-satunya jalan masuk
 * tepat ketika Alpine gagal dimuat dan tidak ada yang membukanya -- menu yang
 * hilang, bukan menu yang perlu satu ketukan. */
/* display: block WAJIB, dan ia menimpa `display: grid` milik .rows.
 *
 * <details> yang diberi display grid atau flex KEHILANGAN pengurutan bawaannya:
 * seluruh anaknya tetap dirender meski tertutup. Yang membuatnya tampak benar
 * hanya `overflow: hidden` milik .rows yang memotongnya -- sementara pembaca
 * layar tetap membacakan menunya dan tab papan ketik mendarat di tautan yang
 * tidak terlihat siapa pun. Terukur: pada menu tertutup setinggi 57px, dua
 * butirnya melaporkan tinggi 55px dengan offsetParent yang ada.
 *
 * Barisnya tetap menumpuk benar tanpa grid: tiap .row sudah display flex, dan
 * dalam aliran blok ia bertumpuk persis sama.
 *
 * Kekhususannya SAMA dengan .rows (0,1,0), jadi yang menang urutan berkas --
 * aturan ini WAJIB tetap berada sesudah .rows. */
/* display: block menimpa `display: grid` milik .rows, dan alasannya KEHATI-
 * HATIAN LINTAS-MESIN, bukan kerusakan yang terukur.
 *
 * Chromium diperiksa langsung dan menanganinya benar: dengan grid pun, menu
 * yang tertutup tidak memunculkan satu butir pun ke checkVisibility maupun ke
 * urutan fokus. Tetapi <details> ber-display selain block adalah wilayah yang
 * pernah berbeda antar-mesin, dan yang membuka etalase ini sebagian besar dari
 * Safari iOS -- tempat yang tidak dapat diperiksa dari sini. Bila di sana
 * pengurutannya luruh, yang bocor adalah seluruh menu beserta tombol keluar,
 * terpotong overflow: hidden sehingga tak seorang pun melihatnya.
 *
 * Barisnya menumpuk sama persis tanpa grid: tiap .row sudah display flex.
 * Harganya nol, jadi ia dipasang.
 *
 * Kekhususannya SAMA dengan .rows (0,1,0) sehingga yang menang urutan berkas --
 * aturan ini WAJIB tetap berada sesudah .rows. */
/* Judul ruang akun lebih kecil di ponsel daripada judul halaman lain.
 *
 * Judul di sini panjang dan bekerja, bukan judul pemasaran: "Donasi Anda via
 * LAZ Peduli Umat — 2026" pada 390px terbaca ~23px dan memakan dua baris penuh
 * sebelum satu angka pun terlihat.
 *
 * Yang digeser DASAR clamp-nya, bukan media query kedua: pada 390px ia jatuh ke
 * ~19px, sementara pada 1440px sukunya tetap melewati batas atas sehingga
 * desktop berhenti di 1.875rem yang sama persis seperti sebelumnya. Satu aturan,
 * dan tidak ada nilai yang disalin ke dua tempat untuk kemudian menyimpang. */
.account-head h1 {
  font-size: clamp(1.125rem, 0.9rem + 1.3vw, 1.875rem);
}

/* Tautan MUNDUR, dan bentuknya harus mengatakan itu.
 *
 * Ia sempat dirender lewat partial section-row, yang selalu memasang chevron ke
 * KANAN -- bahasa visual untuk "maju ke sesuatu" pada tindakan yang justru
 * kembali. Dan ia berdiri di kaki halaman, sehingga yang mencarinya baru
 * menemukannya setelah menggulir melewati seluruh isi: tepat kebalikan dari
 * tujuannya, yang lahir dari keluhan "bingung mau kembali ke mana".
 *
 * Berdiri di atas judul, dengan panah ke kiri, dan sengaja TIDAK menyerupai
 * tombol: ia navigasi, bukan tindakan yang halaman ini minta. */
.backlink {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.8125rem;
  color: var(--muted);
  text-decoration: none;
}
.backlink:hover {
  color: var(--ink);
  text-decoration: underline;
}
.backlink svg {
  flex: none;
}

.account-disclosure {
  display: block;
}
.account-disclosure[open] > .account-summary {
  display: none;
}
.account-summary {
  /* Segitiga bawaan dibuang; chevron sendiri yang menyatakan arahnya, sejalan
   * dengan baris lain di repo ini. */
  list-style: none;
}
.account-summary::-webkit-details-marker {
  display: none;
}
.account-summary .row-title {
  font-weight: 700;
}

/* Blok identitas di kepala menu.
 *
 * Ia mengisi ruang yang dulu kosong di kiri atas. Tanpa isi, kolom kiri adalah
 * kartu setinggi 226px di sebelah kolom setinggi 459px -- dan kartu berbingkai
 * yang separuh tinggi tetangganya terbaca sebagai objek kecil, bukan sebagai
 * navigasi.
 *
 * Berlatar sunken supaya ia terbaca sebagai kepala daftar, bukan sebagai butir
 * pertama yang dapat ditekan. */
.account-me {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.9rem 1.125rem;
  border-bottom: 1px solid var(--line);
  background: var(--surface-sunken);
}
.account-me-mark {
  flex: none;
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  background: var(--brand);
  color: #fff;
  font-size: 0.8125rem;
  font-weight: 700;
}
/* min-width: 0 WAJIB. Tanpa itu anak flex menolak menyusut di bawah lebar
   isinya, sehingga ellipsis tidak pernah aktif dan alamat panjang justru
   melebarkan seluruh kolom menu. */
.account-me-text {
  min-width: 0;
  display: grid;
}
.account-me-name,
.account-me-mail {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.account-me-name {
  font-size: 0.875rem;
  font-weight: 600;
}
.account-me-mail {
  font-size: 0.75rem;
  color: var(--muted);
}

.account-nav .row-ico,
.head-menu-panel .row-ico {
  flex: none;
  color: var(--muted);
}
.account-nav a[aria-current="page"] .row-ico,
.head-menu-panel a[aria-current="page"] .row-ico {
  color: inherit;
}

/* Tombol keluar di kepala layout DISEMBUNYIKAN selama menu ruang akun ada.
 *
 * Syaratnya keberadaan menunya, BUKAN daftar path. Daftar path adalah tempat
 * ketiga yang harus ikut diperbarui setiap rute akun bertambah, dan yang lupa
 * diperbarui gagal diam-diam. Yang lebih menentukan: /account/complete berbagi
 * prefiks yang sama tetapi TIDAK memuat menu ini -- menyembunyikan tombolnya di
 * sana mencabut satu-satunya jalan keluar dari halaman itu. Dengan :has(),
 * tombolnya hilang tepat ketika penggantinya ada, dan tidak pernah sebelum itu.
 *
 * Peramban yang belum mengenal :has() membuang aturan ini dan menampilkan
 * keduanya -- persis keadaan sebelumnya: mundur, bukan rusak. */
body:has(.account-nav) .header-session {
  display: none;
}

.nav-group{margin:.9rem 0 .25rem;padding:0 .85rem;font-size:.68rem;letter-spacing:.09em;
text-transform:uppercase;color:var(--muted,#78837d);font-weight:600}
.brand-plain{cursor:default;text-decoration:none;color:inherit}

.search-bar {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.5rem;
  align-items: center;
}
.search-bar .input {
  min-width: 0;
}

/* ── Ruang kerja Mitra: nada status, lipatan donasi, penyaring, pagination.
 *
 * Bagian sendiri, dan itu WAJIB: penjaga gaya ruang akun mengiris app.css dari
 * judul bagiannya sampai pemisah "/* ──" berikutnya, dan sebelum bagian ini
 * lahir tidak ada satu pun pemisah sesudahnya -- sehingga apa pun yang
 * ditambahkan di ujung berkas ini terhitung sebagai gaya ruang akun. */

/* --- nada status pembayaran ------------------------------------------------ */

.badge-info {
  background: color-mix(in srgb, var(--info) 12%, var(--surface));
  color: var(--info);
  border-color: color-mix(in srgb, var(--info) 26%, var(--line));
}
.badge-danger {
  background: color-mix(in srgb, var(--danger) 12%, var(--surface));
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 26%, var(--line));
}
.badge-muted {
  background: color-mix(in srgb, var(--muted) 10%, var(--surface));
  color: var(--muted);
  border-color: color-mix(in srgb, var(--muted) 24%, var(--line));
}
.badge-ended {
  background: var(--ink-soft);
  color: var(--surface);
  border-color: var(--ink-soft);
}

/* --- lipatan donasi Mitra --------------------------------------------------- */

.fold {
  display: block;
  border-bottom: 1px solid var(--line);
}
.fold:last-child {
  border-bottom: 0;
}
.fold > .fold-summary {
  list-style: none;
  cursor: pointer;
  border-bottom: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.35rem 0.75rem;
  align-items: start;
}
.fold-summary .row-title {
  grid-column: 1;
  min-width: 0;
}
.fold-summary .row-meta {
  grid-column: 1;
  margin-left: 0;
  align-items: flex-start;
  text-align: left;
}
.fold-summary .fold-chev {
  grid-column: 2;
  grid-row: 1 / -1;
  align-self: center;
}
.fold-summary .nowrap {
  white-space: nowrap;
}
.fold > .fold-summary::-webkit-details-marker {
  display: none;
}
.fold > .fold-summary:hover {
  background: color-mix(in srgb, var(--ink) 3%, var(--surface));
}
.fold[open] > .fold-summary {
  background: color-mix(in srgb, var(--brand) 7%, var(--surface));
  border-bottom: 1px solid color-mix(in srgb, var(--brand) 18%, var(--line));
}
.fold-chev {
  transition: transform 0.15s ease;
}
.fold[open] > .fold-summary .fold-chev {
  transform: rotate(180deg);
}
.fold-body {
  display: grid;
  gap: 0.75rem;
  padding: 0.875rem 1.125rem 1rem;
  background: color-mix(in srgb, var(--ink) 2%, var(--surface));
}

.fold-table {
  display: grid;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  font-size: 0.875rem;
  line-height: 1.5;
}
.fold-head,
.fold-line,
.fold-total {
  display: grid;
  gap: 0.25rem 0.75rem;
  padding: 0.625rem 0.875rem;
  align-items: baseline;
}
.fold-head {
  display: none;
}
.fold-line {
  grid-template-columns: 1fr auto;
  border-top: 1px solid var(--line);
}
.fold-table > .fold-line:first-child {
  border-top: 0;
}
.fold-for {
  grid-column: 1 / -1;
  display: grid;
  gap: 0.1rem;
  min-width: 0;
  font-weight: 500;
}
.fold-for .note {
  font-weight: 400;
}
.fold-line .num {
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: 0.5rem;
  grid-column: 1 / -1;
  font-variant-numeric: tabular-nums;
}
.fold-line .num::before {
  content: attr(data-kolom);
  margin-right: auto;
  color: var(--muted);
  font-size: 0.8125rem;
}
.fold-line .num .note {
  color: var(--muted);
  font-size: 0.8125rem;
}
.fold-total {
  grid-template-columns: 1fr auto;
  border-top: 1px solid var(--line);
  background: color-mix(in srgb, var(--ink) 3%, var(--surface));
  font-size: 0.9375rem;
}
.fold-foot {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
}

@media (min-width: 48rem) {
  .fold > .fold-summary {
    grid-template-columns: minmax(0, 1fr) auto auto;
  }
  .fold-summary .row-meta {
    grid-column: 2;
    grid-row: 1;
    align-items: flex-end;
    text-align: right;
  }
  .fold-summary .badge {
    white-space: nowrap;
  }
  .fold-summary .fold-chev {
    grid-column: 3;
    grid-row: 1;
  }
  .fold-head {
    display: grid;
    grid-template-columns: 2fr 1fr 1fr 1fr;
    background: color-mix(in srgb, var(--ink) 3%, var(--surface));
    color: var(--muted);
    font-size: 0.8125rem;
  }
  .fold-line {
    grid-template-columns: 2fr 1fr 1fr 1fr;
  }
  .fold-for {
    grid-column: auto;
  }
  .fold-line .num {
    grid-column: auto;
    display: grid;
    justify-items: end;
    gap: 0.1rem;
  }
  .fold-line .num::before {
    content: none;
  }
  .fold-head .num {
    text-align: right;
  }
}

.fundraiser-name {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 0.5rem;
}
.fundraiser-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.35rem 0.75rem;
  align-items: start;
}
.fundraiser-row .row-title {
  grid-column: 1;
  min-width: 0;
}
.fundraiser-row .row-meta {
  grid-column: 1;
  margin-left: 0;
}
.fundraiser-row .chev {
  grid-column: 2;
  grid-row: 1 / span 2;
  align-self: center;
}
@media (min-width: 48rem) {
  .fundraiser-row {
    grid-template-columns: minmax(0, 1fr) auto auto;
  }
  .fundraiser-row .row-meta {
    grid-column: 2;
    grid-row: 1;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.1rem;
    text-align: right;
  }
  .fundraiser-row .chev {
    grid-column: 3;
    grid-row: 1;
  }
}

.fold-summary.campaign-summary {
  display: flex;
  align-items: center;
}
.campaign-summary .row-title {
  grid-column: unset;
  min-width: 0;
  flex: 1 1 auto;
}
.campaign-summary .row-meta {
  grid-column: unset;
  grid-row: unset;
  margin-left: 0;
  flex: none;
}

.campaign-updates .fund-label {
  margin-bottom: 0.35rem;
}
.update-list {
  width: 100%;
  border-collapse: collapse;
}
.update-item td {
  padding: 0.3rem 0;
  vertical-align: middle;
}
.update-item td:first-child {
  width: 2rem;
  padding-right: 0.6rem;
}
.update-thumb,
.update-thumb-empty {
  display: block;
  width: 2rem;
  height: 2rem;
  border-radius: var(--radius-sm);
  object-fit: cover;
  background: var(--surface-sunken);
}
.update-title {
  font-size: 0.8125rem;
  color: var(--ink-soft);
}
.update-date {
  text-align: right;
  padding-left: 0.75rem;
  font-size: 0.75rem;
  color: var(--muted);
  white-space: nowrap;
}

/* --- pagination ------------------------------------------------------------ */

.pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  flex-wrap: wrap;
}
.pager-at {
  font-size: 0.9375rem;
  color: var(--muted);
}
.pager-edge {
  font-size: 0.9375rem;
  color: var(--muted);
  opacity: 0.5;
}
@media (max-width: 34rem) {
  .pager-at {
    order: 1;
    flex-basis: 100%;
    text-align: center;
  }
}

/* --- penyaring daftar donasi Mitra ------------------------------------------ */

.filters {
  display: grid;
  gap: 0.75rem;
  overflow: hidden;
}
.filters .chips {
  margin-inline: -1.125rem;
  padding-inline: 1.125rem;
}
.filters .search-bar {
  grid-template-columns: 1fr 1fr;
}
.filters .search-bar .input {
  grid-column: 1 / -1;
}
@media (min-width: 34rem) {
  .filters .search-bar {
    grid-template-columns: minmax(0, 1fr) auto auto;
  }
  .filters .search-bar .input {
    grid-column: auto;
  }
}
.range-bar {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 0.5rem;
  font-size: 0.875rem;
  color: var(--muted);
}
.range-bar .input {
  width: 100%;
  min-width: 0;
  font-size: 0.875rem;
}

/* --- dialog/drawer filter donasi Mitra -------------------------------------- */

/* Satu komponen, dua presentasi lewat media query -- bukan dua implementasi.
   Bawaannya (mobile, <60rem) drawer melekat ke bawah; >=60rem menjadi dialog
   di tengah. html:has(.scroll-locked) tidak dipakai -- kuncinya kelas pada
   <html> sendiri, ditoggle Alpine (x-effect), lebih murah daripada memantau
   turunan lewat :has(). */
html.scroll-locked {
  overflow: hidden;
}

.filter-trigger {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}
.filter-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.125rem;
  height: 1.125rem;
  padding: 0 0.3rem;
  border-radius: 999px;
  background: var(--brand);
  color: var(--brand-ink);
  font-size: 0.6875rem;
  font-weight: 700;
}

.active-chips {
  align-items: center;
}
.chip-removable {
  gap: 0.4rem;
  background: color-mix(in srgb, var(--brand) 10%, var(--surface));
  border-color: color-mix(in srgb, var(--brand) 28%, var(--line));
  color: color-mix(in srgb, var(--brand-text) 82%, var(--ink));
}

.filter-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(26, 25, 23, 0.45);
  z-index: 60;
}

/* Drawer, bawaan (<60rem): melekat ke bawah layar, tinggi dibatasi 80dvh
   (bukan vh -- bilah alamat peramban mobile memakan vh tanpa memakan dvh),
   gulir di dalam. Kepala dan kaki tetap terlihat; isi tengah yang bergulir. */
.filter-panel {
  position: fixed;
  z-index: 61;
  left: 0;
  right: 0;
  bottom: 0;
  max-height: 80dvh;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  box-shadow: var(--shadow-lift);
}
.filter-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 1.125rem;
  border-bottom: 1px solid var(--line);
  flex: none;
}
.filter-panel-close {
  border: none;
  background: none;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--muted);
  padding: 0.25rem 0.5rem;
  cursor: pointer;
}
.filter-panel-close:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: 2px;
}
.filter-form {
  display: contents;
}
.filter-panel-body {
  overflow-y: auto;
  padding: 1.125rem;
  display: grid;
  gap: 1.125rem;
}
.filter-group {
  display: grid;
  gap: 0.5rem;
}
.filter-group .chips {
  margin-inline: 0;
  padding-inline: 0;
  padding-bottom: 0;
  overflow-x: visible;
  flex-wrap: wrap;
}
.filter-group-label {
  font-size: 0.8125rem;
  color: var(--muted);
}
.filter-panel-foot {
  padding: 1rem 1.125rem;
  border-top: 1px solid var(--line);
  flex: none;
}
.filter-panel-foot .btn {
  width: 100%;
}

/* Dialog, >=60rem: lepas dari tepi bawah, dipusatkan sebagai kartu -- lebar
   layout (--page-wide) TIDAK disentuh, dialognya berdiri sendiri di atas
   backdrop. */
@media (min-width: 60rem) {
  .filter-panel {
    left: 50%;
    right: auto;
    bottom: auto;
    top: 4rem;
    transform: translateX(-50%);
    width: 26rem;
    max-height: calc(100dvh - 8rem);
    border-radius: var(--radius-lg);
  }
  .filter-panel-wide {
    width: min(48rem, calc(100vw - 4rem));
  }
}

/* --- identitas & banner etalase Mitra --------------------------------- */

.partner-logo-pick {
  position: relative;
  display: grid;
  place-items: center;
  min-height: 4.5rem;
  padding: 0.625rem;
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  background: var(--surface-sunken);
  cursor: pointer;
}

.partner-logo-pick:hover {
  border-color: var(--brand);
}

.partner-logo-pick:focus-within {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

.partner-logo-pick input[type="file"] {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.partner-logo-preview {
  max-width: 100%;
  max-height: 5rem;
  object-fit: contain;
}

.logo-backdrops {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.75rem;
  width: 100%;
}

.logo-backdrop {
  display: grid;
  place-items: center;
  min-height: 7.5rem;
  padding: 1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}

.logo-backdrop .partner-logo-preview {
  max-height: 5rem;
}

.logo-backdrop-light {
  background: var(--logo-backdrop-light);
}

.logo-backdrop-dark {
  background: var(--logo-backdrop-dark);
}

.partner-logo-empty {
  color: var(--muted);
  font-size: 0.8125rem;
  text-align: center;
}

.partner-logo-edit {
  position: absolute;
  right: -0.5rem;
  bottom: -0.5rem;
  display: grid;
  place-items: center;
  width: 1.9rem;
  height: 1.9rem;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--muted);
  box-shadow: var(--shadow);
}

.partner-logo-pick:hover .partner-logo-edit,
.partner-logo-pick:focus-within .partner-logo-edit {
  border-color: var(--brand);
  color: var(--brand-text);
}

.partner-identity .ledger td:first-child {
  white-space: nowrap;
  width: 1%;
  padding-right: 1rem;
}

.partner-logo-static {
  cursor: default;
  border-style: solid;
}

.partner-logo-static:hover {
  border-color: var(--line);
}

.partner-banner-static + .partner-banner-static {
  border-top: 1px solid var(--line);
}

.partner-logo-hint {
  margin: 0;
  color: var(--muted);
  font-size: 0.75rem;
  line-height: 1.45;
}

.partner-banner-list {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  margin-inline: -1.125rem;
  border-top: 1px solid var(--line);
}

.partner-banner-add {
  margin-inline: -1.125rem;
  border-top: 1px solid var(--line);
}

.partner-banner-list .fold > .fold-summary,
.partner-banner-list .partner-banner-static,
.partner-banner-add > .fold-summary {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  padding: 0.8rem 1.125rem;
}

.partner-banner-list .fold-summary .row-title,
.partner-banner-list .partner-banner-static .row-title,
.partner-banner-add > .fold-summary .row-title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.partner-banner-list .fold-summary .row-meta,
.partner-banner-list .partner-banner-static .row-meta {
  flex: none;
  margin-left: auto;
  align-items: center;
  gap: 0.75rem;
}

.partner-banner-list .fold-body,
.partner-banner-add > .fold-body {
  padding: 0.875rem 1.125rem 1.125rem;
}

.partner-banner-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.partner-banner-delete {
  margin-left: auto;
}

.partner-banner-delete .btn {
  color: var(--danger);
}

.partner-banner-actions button:disabled {
  opacity: 0.45;
  cursor: default;
}

.partner-banner-thumb {
  flex: none;
  width: 5.25rem;
  height: 2.2rem;
  object-fit: cover;
  border-radius: calc(var(--radius) * 0.6);
  border: 1px solid var(--line);
}

.bulk-preview-scroll {
  max-height: min(60vh, 34rem);
  overflow: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
}

.bulk-preview-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.875rem;
}

.bulk-preview-table th {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: 0.6rem 0.75rem;
  background: var(--surface-sunken);
  border-bottom: 1px solid var(--line);
  color: var(--muted);
  font-size: 0.75rem;
  font-weight: 600;
  text-align: left;
}

.bulk-preview-table td {
  padding: 0.6rem 0.75rem;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}

.bulk-preview-table tr:last-child td {
  border-bottom: 0;
}

.bulk-preview-table .num {
  width: 1%;
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}

.bulk-preview-table .note {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.75rem;
  color: var(--muted);
  overflow-wrap: anywhere;
}

.bulk-preview-table .badge {
  white-space: normal;
}

.bulk-preview-table .checkline {
  min-height: 0;
  padding-block: 0;
  white-space: nowrap;
}

.bulk-preview-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.75rem 0;
  background: var(--surface);
}

@media (max-width: 59.999rem) {
  .bulk-preview-foot {
    position: sticky;
    bottom: 0;
    z-index: 2;
    border-top: 1px solid var(--line);
  }
}

.bulk-preview .chips {
  flex-wrap: wrap;
}

.partner-logo-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.partner-banner-preview {
  display: block;
  width: 100%;
  aspect-ratio: 12 / 5;
  object-fit: cover;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-sunken);
}

.partner-confirm-foot {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
}

.partner-banner-list .fold-body,
.partner-banner-list .fold-body .stack-sm,
.partner-banner-list .fold-body .field {
  grid-template-columns: minmax(0, 1fr);
}

.partner-banner-list .fold-body .input {
  min-width: 0;
}

/* --- ringkasan ruang kerja Mitra ------------------------------------------- */

.summary-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 1rem;
}
.summary-toolbar .eyebrow {
  margin: 0;
}
.summary-toolbar .chips {
  flex-wrap: wrap;
  min-width: 0;
  overflow: visible;
  padding-bottom: 0;
}
.chip-range {
  gap: 0.4rem;
}
button.chip {
  font-family: inherit;
  cursor: pointer;
}
.chip[aria-pressed="true"] {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--brand-ink);
}
.date-range {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.75rem;
}
.date-field {
  display: grid;
  gap: 0.3rem;
  min-width: 0;
  font-size: 0.75rem;
  color: var(--muted);
}
.date-field .input {
  width: 100%;
  min-width: 0;
  font-size: 0.875rem;
}

.summary-grid,
.summary-pair {
  display: grid;
  gap: 0.75rem;
}
.summary-pair > .card {
  align-content: start;
}
.summary-pair > .card:only-child {
  grid-column: 1 / -1;
}
@media (min-width: 40rem) {
  .summary-grid,
  .summary-pair {
    grid-template-columns: 1fr 1fr;
  }
}
.summary-card,
.trend,
.trend-head,
.summary-pair {
  --split-strong: var(--brand-text);
  --split-mid: color-mix(in srgb, var(--brand-text) 58%, var(--surface));
  --split-soft: color-mix(in srgb, var(--brand-text) 28%, var(--surface));
}
.summary-card {
  display: grid;
  align-content: start;
  gap: 0.75rem;
  padding: 1.125rem 1.25rem 1.25rem;
}
.summary-label {
  margin: 0;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--ink-soft);
}
.summary-amount {
  margin: -0.35rem 0 0;
  font-size: clamp(1.5rem, 1.25rem + 1vw, 1.875rem);
  font-weight: 700;
  line-height: 1.15;
  color: var(--brand-text);
}
.summary-amount-plain {
  color: var(--ink);
}
.summary-change {
  margin: -0.35rem 0 0;
  font-size: 0.8125rem;
  color: var(--muted);
}
.summary-change-figure {
  font-weight: 650;
  color: var(--ink-soft);
}
.summary-change[data-arah="up"] .summary-change-figure {
  color: var(--ok);
}
.summary-change[data-arah="down"] .summary-change-figure {
  color: var(--danger);
}
.split-bar {
  display: flex;
  gap: 2px;
  height: 0.5rem;
  border-radius: 999px;
  overflow: hidden;
  background: var(--surface-sunken);
}
.split-bar i {
  display: block;
  flex-basis: 0;
  min-width: 2px;
}
.split-list {
  margin: 0;
  display: grid;
  gap: 0.45rem;
  font-size: 0.8125rem;
}
.split-list div {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.75rem;
}
.split-list dt {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--ink-soft);
}
.split-list dd {
  margin: 0;
  font-weight: 600;
  color: var(--ink);
  white-space: nowrap;
}
.split-list dd span {
  display: inline-block;
  min-width: 2.75rem;
  text-align: right;
  font-weight: 400;
  color: var(--muted);
}
.split-dot {
  flex: none;
  display: inline-block;
  width: 0.625rem;
  height: 0.625rem;
  border-radius: 3px;
}
.split-strong {
  background: var(--split-strong);
}
.split-mid {
  background: var(--split-mid);
}
.split-soft {
  background: var(--split-soft);
}
.split-total {
  box-shadow: inset 0 0 0 1.5px var(--ink-soft);
}

.action-rows .row {
  padding-block: 0.75rem;
}
.action-count {
  flex: none;
  min-width: 2rem;
  height: 2rem;
  padding: 0 0.5rem;
  display: grid;
  place-items: center;
  border-radius: 999px;
  background: color-mix(in srgb, var(--brand) 12%, var(--surface));
  color: var(--brand-text);
  font-size: 0.875rem;
  font-weight: 700;
}
.action-label {
  font-size: 0.875rem;
  font-weight: 550;
  color: var(--ink);
}

.card-title {
  display: grid;
  gap: 0.15rem;
}
.card-title .eyebrow {
  margin: 0;
}

.trend-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem 1rem;
}
.trend-head .eyebrow {
  margin: 0;
}
.trend-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  font-size: 0.75rem;
  color: var(--ink-soft);
}
.trend-legend span {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}
.trend {
  display: grid;
  gap: 0.6rem;
  padding-top: 0.75rem;
}
.trend-plot {
  position: relative;
  height: 11rem;
  margin: 3.75rem 0 0 2.75rem;
}
.trend-line {
  position: absolute;
  left: 0;
  right: 0;
  border-top: 1px solid var(--line);
}
.trend-line span {
  position: absolute;
  right: calc(100% + 0.5rem);
  transform: translateY(-50%);
  font-size: 0.6875rem;
  color: var(--muted);
  white-space: nowrap;
}
.trend-bars {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
}
.trend-col {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.trend-col:focus {
  outline: none;
}
.trend-col:hover,
.trend-bars:not(:hover) .trend-col:focus,
.trend-bars:not(:hover):not(:has(.trend-col:focus)) .trend-col-default {
  background: color-mix(in srgb, var(--surface-sunken) 75%, transparent);
}
.trend-bars:hover .trend-col:not(:hover) .trend-bar,
.trend-bars:not(:hover):has(.trend-col:focus) .trend-col:not(:focus) .trend-bar,
.trend-bars:not(:hover):not(:has(.trend-col:focus)) .trend-col:not(.trend-col-default) .trend-bar {
  opacity: 0.5;
}
.trend-bar {
  width: min(2.5rem, 62%);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  border-radius: 4px 4px 0 0;
  background: var(--split-soft);
  overflow: hidden;
}
.trend-bar i {
  display: block;
  background: var(--split-strong);
  border-top: 2px solid var(--surface);
}
.trend-col-current .trend-bar {
  background: repeating-linear-gradient(
    135deg,
    var(--split-soft) 0 4px,
    color-mix(in srgb, var(--split-soft) 55%, var(--surface)) 4px 7px
  );
}
.trend-tip {
  position: absolute;
  top: -3.75rem;
  left: -2.75rem;
  right: 0;
  display: none;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 1.25rem;
  min-height: 2.75rem;
  padding: 0.5rem 0.75rem;
  border-radius: var(--radius-sm);
  background: var(--surface-sunken);
  font-size: 0.8125rem;
  color: var(--ink-soft);
  pointer-events: none;
}
.trend-col:hover .trend-tip,
.trend-bars:not(:hover) .trend-col:focus .trend-tip,
.trend-bars:not(:hover):not(:has(.trend-col:focus)) .trend-col-default .trend-tip {
  display: flex;
}
.trend-tip b {
  margin-right: auto;
  color: var(--ink);
}
.trend-tip span {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}
.trend-tip em {
  font-style: normal;
  font-weight: 650;
  color: var(--ink);
}
@media (max-width: 34rem) {
  .trend-plot {
    margin-top: 6.5rem;
  }
  .trend-tip {
    top: -6.5rem;
    min-height: 5.25rem;
    grid-template-columns: 1fr 1fr;
    align-content: center;
    gap: 0.35rem 1rem;
  }
  .trend-col:hover .trend-tip,
  .trend-bars:not(:hover) .trend-col:focus .trend-tip,
  .trend-bars:not(:hover):not(:has(.trend-col:focus)) .trend-col-default .trend-tip {
    display: grid;
  }
  .trend-tip b {
    grid-column: 1 / -1;
  }
  .trend-tip span {
    display: grid;
    grid-template-columns: auto 1fr;
    column-gap: 0.4rem;
    font-size: 0.75rem;
  }
  .trend-tip em {
    grid-column: 1 / -1;
    font-size: 0.875rem;
  }
}
.trend-axis {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  margin-left: 2.75rem;
  font-size: 0.75rem;
  color: var(--muted);
  text-align: center;
}

.meter-list,
.rank-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
}
.meter-list {
  gap: 1rem;
}
.meter-list .progress {
  margin-top: 0.4rem;
}
.meter-list .tiny {
  margin-top: 0.3rem;
}
.meter-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.75rem;
  font-size: 0.875rem;
}
.meter-title {
  font-weight: 600;
  min-width: 0;
}

.rank-list li {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.7rem 0;
  border-top: 1px solid var(--line);
}
.rank-list li:first-child {
  border-top: 0;
  padding-top: 0.25rem;
}
.rank {
  flex: none;
  width: 1.75rem;
  height: 1.75rem;
  display: grid;
  place-items: center;
  border-radius: 999px;
  background: var(--surface-sunken);
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--ink-soft);
}
.rank-list li:first-child .rank {
  background: color-mix(in srgb, var(--brand) 14%, var(--surface));
  color: var(--brand-text);
}
.rank-name {
  display: grid;
  min-width: 0;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--ink);
}
.rank-name .tiny {
  font-weight: 400;
}
.rank-amount {
  margin-left: auto;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--ink);
  white-space: nowrap;
}

/* --- baris Daftar Donatur Mitra --------------------------------------------- */

/* Kolom kanan membawa dua frasa tanggal lengkap ("Donasi pertama: 1 Januari
   2026"), lebih panjang dari pasangan nominal+status milik baris lain yang
   berbagi .row-meta-wrap. Di bawah .row-meta-wrap tetap bertumpuk tanpa
   masalah; yang sempit adalah LEBAR-nya saat berbagi satu baris dengan nama
   donatur, dan itu membuat tiap frasa tanggal terpotong di tengah.

   Selectornya DUA KELAS BARU (.donor-row, .donor-row-dates), bukan
   `.donor-row .row-meta` -- TestDashboardStylesDoNotReachSharedRows menolak
   selector .row-meta TELANJANG di luar blok ruang akun, dan kelas gabungan
   lewat spasi tetap cocok dengan pola itu. */
@media (max-width: 59.999rem) {
  .donor-row {
    flex-wrap: wrap;
  }
  .donor-row-dates {
    flex-basis: 100%;
    margin-left: 0;
    align-items: flex-start;
    text-align: left;
  }
}
