/* ============================================================
   PERUHAN — Design System
   Palette: Forest + Stone + Amber (satu accent)
   Font: Plus Jakarta Sans (body) / Outfit (display)
   ============================================================ */

/* ---------- 1. Tokens ---------- */
@font-face{font-family:'PJS';src:url('../fonts/pjs-latin.woff2') format('woff2');font-weight:200 800;font-display:swap;font-style:normal}
@font-face{font-family:'PJS';src:url('../fonts/pjs-ext.woff2') format('woff2');font-weight:200 800;font-display:swap;font-style:normal;unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'Outfit';src:url('../fonts/outfit-latin.woff2') format('woff2');font-weight:100 900;font-display:swap;font-style:normal}
@font-face{font-family:'Outfit';src:url('../fonts/outfit-ext.woff2') format('woff2');font-weight:100 900;font-display:swap;font-style:normal;unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}

:root{
  /* colour roles */
  --brand:#12513c;        /* deep forest - structure */
  --brand-ink:#0c3b2b;
  --brand-soft:#e4efe9;
  --accent:#c8791a;       /* amber - the single accent */
  /* warna teks/ikon DI ATAS latar --brand. Di mode terang --brand gelap
     (butuh teks putih), di mode gelap --brand terang (butuh teks gelap).
     Token ini yang membuat avatar "S" selalu terbaca di kedua mode. */
  --on-brand:#fff;
  /* teks/ikon DI ATAS latar --accent (amber). Amber itu terang di KEDUA
     mode, jadi teksnya harus selalu gelap. Versi lama `color:#fff` =
     kontras 1.05 (putih di atas amber) -> tombol "Lihat Agenda" nyaris
     tak terbaca. */
  --on-accent:#3a2405;
  /* teks di atas latar --brand-ink (footer). --brand-ink itu hijau GELAP di
     mode terang tapi hijau TERANG di mode gelap, jadi teksnya harus ikut. */
  --on-brand-ink:#fff;
  /* PITA GELAP (hero & footer). --brand-ink berubah jadi hijau TERANG di mode
     gelap, sehingga teks putih di atasnya jadi kontras 1.45. Pita ini memang
     harus tetap gelap di KEDUA mode, jadi pakai token sendiri. */
  --band:#0c3b2b;
  --on-band:#fff;
  --accent-soft:#fdf1de;
  --accent-ink:#8a4f08;

  --bg:#f6f6f4;
  --surface:#ffffff;
  --surface-2:#fbfbfa;
  --fg:#15201c;
  --fg-2:#4c5a55;
  --fg-3:#7b8781;
  --line:#e2e5e2;
  --line-2:#eef0ee;

  /* Zebra tabel: 2 warna SOFT yang tetap JELAS beda tapi tidak bikin keder.
     Baris 1 = putih (--surface), baris 2 = tint hijau sangat pucat.
     Jangan pakai --surface-2 (#fbfbfa) -> terlalu dekat putih, zebra tak
     terlihat sama sekali. */
  /* Rasio kontras baris 1 vs baris 2 (terukur, bukan tebasan):
       versi lama --surface-2 40%  -> 1.009  (PRAKTIS TAK TERLIHAT)
       nilai di bawah ini        -> 1.13   (jelas, tetap soft)
     Jangan naikkan >1.25: baris jadi bergaris-garis keras dan "berkeder".
     Teks utama tetap 14.8:1, teks redup --fg-3 3.3:1 (sudah redup sejak
     awal, zebra tidak mengubahnya). */
  --row-2:#eaf3ee;
  --row-hover:#dfece6;
  --row-aksi:#f4f8f5;   /* baris tombol: paling pucat */
  /* Latar tombol di dalam tabel. Harus berbeda dari KEDUA warna baris:
     1.23 vs baris putih, 1.09 vs baris zebra (#eaf3ee). Kalau tombolnya
     transparan, warnanya persis sama dengan zebra (rasio 1.000) -> samar. */
  /* ---------- TOKEN TOMBOL DI DALAM TABEL (sistem warna per makna) ----------
     Semua angka rasio di bawah DIUKUR, bukan tebasan.
     Syarat: (a) teks di atas tombol >= 4.5 (WCAG AA), (b) latar tombol
     >= 1.10 dari KEDUA warna baris (putih & zebra hijau) supaya tidak
     "samar", (c) tetap soft -- bukan blok warnasolid yang menyilaukan.

     PITFALL: memakai --ok-bg/--warn-bg/--err-bg yang sudah ada TIDAK bisa
     dipakai -- nilainya terlalu dekat dengan zebra (1.01-1.03), persis
     masalah yang sedang diperbaiki. Jadi butuh tint sendiri yang lebih
     tegas (~30-38% warna, bukan 15%). */
  --btn-netral-bg:#dae6e0; --btn-netral-fg:#15201c;  /* teks 13.05, baris 1.28/1.13 */
  --btn-ok-bg:#daeae3;    --btn-ok-fg:#0d3d27;     /* teks  9.84, baris 1.25/1.10 */
  --btn-warn-bg:#eee4d3;  --btn-warn-fg:#5c3a05;   /* teks  8.08, baris 1.26/1.11 */
  --btn-err-bg:#f4e1dd;   --btn-err-fg:#7a2416;    /* teks  7.97, baris 1.26/1.11 */
  --btn-info-bg:#d5dde6;   --btn-info-fg:#182b3d;    /* biru   hue 210, teks 10.55 */
  --btn-violet-bg:#ddd5e6; --btn-violet-fg:#2b183d; /* ungu   hue 270, teks 11.33 */
  --btn-kuning-bg:#e6e6d5; --btn-kuning-fg:#3d3d18; /* kuning hue  60, teks  8.86 */
  --btn-bg:var(--btn-netral-bg);

  /* ---------- TOKEN KOTAK/BADGE STATUS ----------
     Masalah SAMA seperti tombol: --ok-bg/--warn-bg/--err-bg/--info-bg dan
     --line-2 terlalu dekat dengan zebra (kontras 1.008-1.048) -> kotak
     "Lunas"/"Aktif"/"Warga" nyaris tak terlihat tepinya.
     Token di bawah = tint lebih tegas (~22%), semuanya DIUKUR:
     syarat teks >= 4.5 dan latar >= 1.10 dari KEDUA warna baris. */

  /* ---------- TOKEN BADGE/KOTAK STATUS ----------
     Masalah yang sama seperti tombol: --ok-bg/--warn-bg/--err-bg terlalu
     dekat dengan zebra (1.008-1.048) -> kotak status "Lunas"/"Aktif" nyaris
     tak terlihat tepinya. Token ini tint lebih tegas (~22%).
     Syarat: teks >= 4.5, latar >= 1.10 dari KEDUA warna baris. */
  --badge-ok-bg:#cde2d8;    --badge-ok-fg:#14603d;   /* teks 5.58, baris 1.36/1.20 */
  --badge-warn-bg:#ebdec9;  --badge-warn-fg:#8a5608; /* teks 4.63, baris 1.33/1.17 */
  --badge-err-bg:#eed2ce;   --badge-err-fg:#94291a;  /* teks 5.69, baris 1.42/1.26 */
  --badge-info-bg:#cddbe5;  --badge-info-fg:#1c5c8a; /* teks 5.04, baris 1.41/1.25 */
  --badge-muted-bg:#dbdedd; --badge-muted-fg:#3d4a45;/* teks 6.85, baris 1.35/1.20 */
  --badge-accent-bg:#f2dfc8; --badge-accent-fg:#8a4f08;/* teks 5.05, baris 1.30/1.15 */
  --badge-brand-bg:#d3e4dc;  --badge-brand-fg:#12513c; /* teks 7.04, baris 1.32/1.17 */
  --badge-menunggu-bg:#f5dc62;   --badge-menunggu-fg:#473e12;   /* YELLOW hue 50, teks 7.78, latar 1.37/1.21, + ikon jam */
  --badge-belumbayar-bg:#d4cbdf; --badge-belumbayar-fg:#281f33; /* UNGU hue 268, teks 10.05, latar 1.57/1.38 */
  /* 4) UNGU = "> 3 bulan tidak masuk" (status login, permintaan 16 Okt 2026:
     merah belum pernah · hijau pernah · kuning < 3 bulan · ungu > 3 bulan). */
  --badge-ungu-bg:#ddcbf0;  --badge-ungu-fg:#4a2483;  /* UNGU hue 272, teks 9.17 */
  /* Status login "> 3 bulan tidak masuk" (permintaan user 16 Okt 2026:
     red=belum pernah, green=pernah, yellow=<3 bulan, ungu=>3 bulan). */
  --badge-lama-bg:#d8cbe8;  --badge-lama-fg:#3a1f5e;  /* UNGU hue 275, teks 8.86 */

  /* Ikon jam (Tabler v3, MIT) sebagai mask -> warnanya ikut `currentColor`
     badge, jadi otomatis cocok di mode terang maupun gelap. Dipakai oleh
     .badge-menunggu supaya "Menunggu" beda BENTUK, bukan cuma warna. */
  --ikon-jam:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 7v5l3 2'/%3E%3C/svg%3E");

  --ok:#1a7a4f;  --ok-bg:#e3f4eb;
  --warn:#a2670a; --warn-bg:#fdf2e0;
  --err:#b3341f;  --err-bg:#fbeae7;
  /* Kabar Duka: kuning hangat, kontras cukup di mode terang & gelap. */
  --duka-bg:#fff8e1;  --duka-bg-hi:#fff1c2;
  --duka-line:#e8b04a;  --duka-ink:#7a5b00;
  /* Pita berkabung: cokelat-hitam, teks krem (kartu khusus Kabar Duka). */
  --duka-pita:#2e2418;  --duka-pita-fg:#f4e3bd;
  --info:#1c5c8a; --info-bg:#e6f0f7;

  /* space */
  --s1:.25rem; --s2:.5rem; --s3:.75rem; --s4:1rem; --s5:1.5rem;
  --s6:2rem;   --s7:3rem;   --s8:4rem;   --s9:6rem;
  /* radius: satu sistem, all-soft */
  --r-sm:8px; --r:14px; --r-lg:20px; --r-full:999px;
  /* motion */
  --f-fast:150ms; --f:240ms; --f-slow:420ms;
  --ease:cubic-bezier(.22,.61,.36,1);
  --shadow:0 1px 2px rgba(18,81,60,.05),0 4px 14px -6px rgba(18,81,60,.12);
  --shadow-lg:0 2px 6px rgba(18,81,60,.06),0 18px 40px -18px rgba(18,81,60,.28);
  --wrap:1180px;
}

html[data-mode="dark"]{
  --on-brand:#0e1512;
  --band:#0a2b20;
  --on-accent:#2a1a04;
  --on-brand-ink:#0e1512;
  /* Status colors HARUS dicerahkan di mode gelap. Versi lama hanya menimpa
     latarnya (--ok-bg/--warn-bg/--err-bg); teksnya tetap gelap
     (#1a7a4f/#a2670a/#b3341f) -> kontras ~3.3 di atas latar gelap. */
  /* Zebra mode gelap: --surface #151e1a, jadi baris 2 harus SEDIKIT lebih
     terang, bukan lebih gelap (kalau lebih gelap, hilang ke background). */
  --row-2:#1e2b26;
  --row-hover:#283830;
  --row-aksi:#19231f;
  --btn-netral-bg:#2a3831; --btn-netral-fg:#e8efeb;  /* teks 10.52, baris 1.39/1.20 */
  --btn-ok-bg:#204030;    --btn-ok-fg:#4ec98a;     /* teks  5.47, baris 1.49/1.29 */
  --btn-warn-bg:#3f3b24;  --btn-warn-fg:#e8b04a;   /* teks  5.77, baris 1.51/1.30 */
  --btn-err-bg:#41342c;   --btn-err-fg:#f08a75;    /* teks  4.91, baris 1.42/1.23 */
  --btn-info-bg:#29363b;   --btn-info-fg:#99a9ba;    /* teks 5.19, baris 1.37/1.18 */
  --btn-violet-bg:#302f3b; --btn-violet-fg:#a999ba; /* teks 4.98, baris 1.29/1.12 */
  --btn-kuning-bg:#373e2c; --btn-kuning-fg:#baba99; /* teks 5.60, baris 1.53/1.32 */
  --btn-bg:var(--btn-netral-bg);

  /* ---------- TOKEN KOTAK/BADGE STATUS ----------
     Masalah SAMA seperti tombol: --ok-bg/--warn-bg/--err-bg/--info-bg dan
     --line-2 terlalu dekat dengan zebra (kontras 1.008-1.048) -> kotak
     "Lunas"/"Aktif"/"Warga" nyaris tak terlihat tepinya.
     Token di bawah = tint lebih tegas (~22%), semuanya DIUKUR:
     syarat teks >= 4.5 dan latar >= 1.10 dari KEDUA warna baris. */
  --badge-ok-bg:#224433;    --badge-ok-fg:#4ec98a;   /* teks 5.17, baris 1.58/1.36 */
  --badge-warn-bg:#433e25;  --badge-warn-fg:#e8b04a; /* teks 5.50, baris 1.59/1.37 */
  --badge-err-bg:#45362e;   --badge-err-fg:#f08a75;  /* teks 4.72, baris 1.48/1.27 */
  --badge-info-bg:#2c4249;  --badge-info-fg:#7cc0f0; /* teks 5.37, baris 1.61/1.39 */
  --badge-muted-bg:#353f3b; --badge-muted-fg:#c3cfc9;/* teks 6.80, baris 1.56/1.35 */
  --badge-accent-bg:#483f26; --badge-accent-fg:#f0c680;/* teks 6.50, baris 1.63/1.41 */
  --badge-brand-bg:#1d3a2e;  --badge-brand-fg:#6fcf9f; /* teks 6.15, baris 1.37/1.18 */
  --badge-menunggu-bg:#4a3f18;   --badge-menunggu-fg:#f0d878;   /* teks 7.34, latar 1.64/1.41 */
  --badge-belumbayar-bg:#30303b; --badge-belumbayar-fg:#ac9ebc; /* teks 5.19, latar 1.31/1.13 */
  --badge-ungu-bg:#382b55;  --badge-ungu-fg:#c4a6f2;  /* UNGU terang, teks 7.03 */
  --badge-lama-bg:#33294a;  --badge-lama-fg:#c9aef0;  /* UNGU terang, teks 6.94 */
  --ok:#4ec98a; --warn:#e8b04a; --err:#f08a75;
  /* Kabar Duka mode gelap: lebih gelap, teks tetap terbaca. */
  --duka-bg:#2b2410;  --duka-bg-hi:#3a300f;
  --duka-line:#e8b04a;  --duka-ink:#f0d878;
  --duka-pita:#1a1510;  --duka-pita-fg:#f0d878;
  --brand:#6fcf9f; --brand-ink:#a5e3c2; --brand-soft:#12241d;
  --accent:#e8a94a; --accent-soft:#2c2113; --accent-ink:#f0c680;
  --bg:#0e1512; --surface:#151e1a; --surface-2:#1a2420;
  --fg:#e8efeb; --fg-2:#a7b5af; --fg-3:#7b8a83;
  --line:#25322c; --line-2:#1e2823;
  --ok-bg:#12291f; --warn-bg:#2a2114; --err-bg:#2c1917; --info-bg:#132229;
  --shadow:0 1px 2px rgba(0,0,0,.3),0 4px 14px -6px rgba(0,0,0,.5);
  --shadow-lg:0 2px 6px rgba(0,0,0,.35),0 18px 40px -18px rgba(0,0,0,.7);
}

@media (prefers-color-scheme:dark){
  html:not([data-mode="light"]){
    --on-brand:#0e1512;
    --band:#0a2b20;
    --on-accent:#2a1a04;
    --on-brand-ink:#0e1512;
    /* Zebra mode gelap: --surface #151e1a, jadi baris 2 harus SEDIKIT lebih
     terang, bukan lebih gelap (kalau lebih gelap, hilang ke background). */
  --row-2:#1e2b26;
  --row-hover:#283830;
  --row-aksi:#19231f;
  --btn-netral-bg:#2a3831; --btn-netral-fg:#e8efeb;  /* teks 10.52, baris 1.39/1.20 */
  --btn-ok-bg:#204030;    --btn-ok-fg:#4ec98a;     /* teks  5.47, baris 1.49/1.29 */
  --btn-warn-bg:#3f3b24;  --btn-warn-fg:#e8b04a;   /* teks  5.77, baris 1.51/1.30 */
  --btn-err-bg:#41342c;   --btn-err-fg:#f08a75;    /* teks  4.91, baris 1.42/1.23 */
  --btn-info-bg:#29363b;   --btn-info-fg:#99a9ba;    /* teks 5.19, baris 1.37/1.18 */
  --btn-violet-bg:#302f3b; --btn-violet-fg:#a999ba; /* teks 4.98, baris 1.29/1.12 */
  --btn-kuning-bg:#373e2c; --btn-kuning-fg:#baba99; /* teks 5.60, baris 1.53/1.32 */
  --btn-bg:var(--btn-netral-bg);

  /* ---------- TOKEN KOTAK/BADGE STATUS ----------
     Masalah SAMA seperti tombol: --ok-bg/--warn-bg/--err-bg/--info-bg dan
     --line-2 terlalu dekat dengan zebra (kontras 1.008-1.048) -> kotak
     "Lunas"/"Aktif"/"Warga" nyaris tak terlihat tepinya.
     Token di bawah = tint lebih tegas (~22%), semuanya DIUKUR:
     syarat teks >= 4.5 dan latar >= 1.10 dari KEDUA warna baris. */
  --badge-ok-bg:#224433;    --badge-ok-fg:#4ec98a;   /* teks 5.17, baris 1.58/1.36 */
  --badge-warn-bg:#433e25;  --badge-warn-fg:#e8b04a; /* teks 5.50, baris 1.59/1.37 */
  --badge-err-bg:#45362e;   --badge-err-fg:#f08a75;  /* teks 4.72, baris 1.48/1.27 */
  --badge-info-bg:#2c4249;  --badge-info-fg:#7cc0f0; /* teks 5.37, baris 1.61/1.39 */
  --badge-muted-bg:#353f3b; --badge-muted-fg:#c3cfc9;/* teks 6.80, baris 1.56/1.35 */
  --badge-accent-bg:#483f26; --badge-accent-fg:#f0c680;/* teks 6.50, baris 1.63/1.41 */
  --badge-brand-bg:#1d3a2e;  --badge-brand-fg:#6fcf9f; /* teks 6.15, baris 1.37/1.18 */
  --badge-menunggu-bg:#4a3f18;   --badge-menunggu-fg:#f0d878;   /* teks 7.34, latar 1.64/1.41 */
  --badge-belumbayar-bg:#30303b; --badge-belumbayar-fg:#ac9ebc; /* teks 5.19, latar 1.31/1.13 */
  --badge-ungu-bg:#382b55;  --badge-ungu-fg:#c4a6f2;  /* UNGU terang, teks 7.03 */
  --badge-lama-bg:#33294a;  --badge-lama-fg:#c9aef0;  /* UNGU terang, teks 6.94 */
  --ok:#4ec98a; --warn:#e8b04a; --err:#f08a75;
  /* Kabar Duka mode gelap: lebih gelap, teks tetap terbaca. */
  --duka-bg:#2b2410;  --duka-bg-hi:#3a300f;
  --duka-line:#e8b04a;  --duka-ink:#f0d878;
  --duka-pita:#1a1510;  --duka-pita-fg:#f0d878;
    --brand:#6fcf9f; --brand-ink:#a5e3c2; --brand-soft:#12241d;
    --accent:#e8a94a; --accent-soft:#2c2113; --accent-ink:#f0c680;
    --bg:#0e1512; --surface:#151e1a; --surface-2:#1a2420;
    --fg:#e8efeb; --fg-2:#a7b5af; --fg-3:#7b8a83;
    --line:#25322c; --line-2:#1e2823;
    --ok-bg:#12291f; --warn-bg:#2a2114; --err-bg:#2c1917; --info-bg:#132229;
  }
}

/* ---------- 2. Base ---------- */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
body{
  margin:0;font-family:'PJS',system-ui,sans-serif;background:var(--bg);color:var(--fg);
  font-size:16px;line-height:1.6;-webkit-font-smoothing:antialiased;
}
h1,h2,h3,h4{font-family:'Outfit','PJS',sans-serif;font-weight:700;line-height:1.15;margin:0 0 var(--s3);letter-spacing:-.02em}
h1{font-size:clamp(2rem,5.2vw,3.4rem)}
h2{font-size:clamp(1.5rem,3.4vw,2.2rem)}
h3{font-size:1.2rem}
p{margin:0 0 var(--s4)}
a{color:var(--brand);text-decoration:none}
a:hover{text-decoration:underline}
img{max-width:100%;display:block}
:focus-visible{outline:3px solid var(--accent);outline-offset:2px;border-radius:4px}
.wrap{max-width:var(--wrap);margin-inline:auto;padding-inline:var(--s4)}
@media(min-width:768px){.wrap{padding-inline:var(--s6)}}
.skip{position:absolute;left:-9999px}
.skip:focus{left:8px;top:8px;z-index:200;background:var(--surface);padding:10px 16px;border-radius:var(--r-sm)}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

/* ---------- 3. Nav ---------- */
.nav{position:sticky;top:0;z-index:60;background:color-mix(in srgb,var(--surface) 88%,transparent);
  backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border-bottom:1px solid var(--line)}
/* SAMAKAN dengan .wrap konten & footer. Kalau header lebih lebar dari
   body's, isiheaderbergeser ke kiri/kanan -> halaman terlihat tidak rapi.
   Konten di dalam .nav-in tetap boleh penuh (tanpa padding ganda). */
/* padding-inline HARUS sama dengan .wrap (var(--s4)) supaya isi header,
   konten, dan footer mulai dari satu garis vertikal yang sama. */
/* Navbar boleh LEBIH lebar dari .wrap konten. Isinya (logo + nama +
   8 menu + tombol akun) butuh ~1402px, sedangkan --wrap cuma 1180px.
   Kalau dipaksa sama --wrap, nama housing akan terus mengecil / meluber.
   Yang penting alignment: padding-nya sama dengan .wrap, jadi konten
   navbar & konten halaman mulai dari satu garis vertikal yang sama. */
.nav-in{max-width:1560px;margin-inline:auto;padding:0 var(--s4);min-height:68px;
  display:flex;align-items:center;gap:var(--s4);flex-wrap:nowrap}
@media(min-width:768px){.nav-in{min-height:72px;padding-inline:var(--s6)}}

/* Brand: nama perumahan TIDAK PERNAH dipotong (tanpa ellipsis).
   Fontnya menyesuaikan lebar layar, dan kalau benar-benar sempit nama
   turun ke 2 baris -- tetap kebaca, tidak pernah jadi "Perumahan Taman S...". */
.brand{display:flex;align-items:center;gap:10px;font-family:'Outfit';font-weight:800;
  font-size:1.32rem;color:var(--fg);letter-spacing:-.02em;
  min-width:0;flex:0 1 auto;order:1;padding-block:6px}
/* Nama housing: flex:0 0 auto supaya TIDAK pernah gepengit (font-fit JS yang
   menentukan ukuran font, bukan flex yang memotong). .brand sendiri yang
   boleh menyusut supaya nav-links & kartu akun punya ruang. */
.brand-name{min-width:0;flex:0 0 auto;white-space:nowrap;line-height:1.15}
.brand:hover{text-decoration:none}
/* Logo asli (rumah taupe + dahan gradasi biru-hijau) diletakkan di kotak
   PUTIH supaya warnanya tidak berubah, dan tanpa gradasi brand supaya ikon
   tampil bersih seperti pada logo. */
.brand-mark{width:40px;height:40px;border-radius:11px;flex:none;
  display:grid;place-items:center;background:#fff;
  box-shadow:0 1px 2px rgba(0,0,0,.07);overflow:hidden}
.brand-mark img{width:100%;height:100%;object-fit:contain;display:block}

/* Menu utama: tampil mulai 980px. Di bawah itu pakai header HP (burger) yang
   bersih. Ambang 980 dipilih supaya HP dalam "Mode situs desktop" -- yang
   viewport-nya sekitar 980px -- dapat versi desktop yang sama dengan web. */
.nav-links{display:none;align-items:center;gap:10px;margin-left:auto;
  flex-wrap:nowrap;flex:0 0 auto;order:2}
@media(min-width:980px){.nav-links{display:flex}}
.nav-links a{color:var(--fg-2);font-size:.94rem;font-weight:500;padding:6px 0;
  border-bottom:2px solid transparent;white-space:nowrap}
.nav-links a:hover{color:var(--fg);text-decoration:none;border-bottom-color:var(--accent)}
.nav-cta{display:none;order:3}
/* gap wajib: tanpa ini tombol tema MENEMPEL ke kartu akun (borders saling
   bersentuhan dan terlihat seperti satu tombol aneh). */
@media(min-width:980px){.nav-cta{display:flex;align-items:center;gap:10px;
  margin-left:var(--s3)}}
.burger{background:none;border:1px solid var(--line);width:42px;height:42px;border-radius:11px;
  display:grid;place-items:center;cursor:pointer;color:var(--fg);font-size:1.3rem;order:3}
@media(min-width:980px){.burger{display:none}}
/* Ganti tema di layar kecil: tombol sendiri di header, supaya drawer tidak
   perlu memuatnya (menyamakan menu HP & desktop). */
.tema-hp{display:grid;place-items:center;width:40px;height:40px;flex:none;
  border:1px solid var(--line);border-radius:12px;background:var(--surface);
  color:var(--fg);font-family:'PJS';font-size:1.15rem;cursor:pointer;
  order:2;margin-left:auto}
.tema-hp:hover{background:var(--surface-2);border-color:var(--fg-3)}
.tema-hp:active{transform:scale(.96)}
@media(min-width:980px){.tema-hp{display:none}}

/* Zona rapat (980-1319px) -- ini yang dipakai HP saat "Mode situs desktop"
   (viewport ~980px). Semua tetap SATU BARIS dan bentuknya sama dengan web:
   nama perumahan dikecilkan, kartu akun jadi avatar saja, jarak dirapatkan. */
@media(min-width:980px) and (max-width:1319px){
  .nav-in{gap:8px;flex-wrap:nowrap}
  /* brand tetap boleh menyusut supaya font-fit punya ruang (flex:0 0 auto = LEBIAR,
     tidak bisa dikecilkan -> nav-links terdorong keluar & halaman melebar) */
  .brand{flex:0 1 auto;min-width:0;line-height:1.12;max-width:calc(100% - 220px)}
  .brand-mark{width:32px;height:32px;border-radius:9px;font-size:.95rem}
  /* gap 0 membuat menu terlihat menempel ("DashboardBeranda"). 12px
     masih muat karena nama perumahan sudah disembunyikan di bawah 1400px. */
  .nav-links{gap:12px;min-width:0;flex:0 1 auto}
  .nav-links a{font-size:.82rem;padding:5px 2px}
  .nav-cta{margin-left:var(--s3);min-width:0;flex:0 1 auto;gap:8px}
  .akun-wrap{flex:0 1 auto}
  .akun-btn{padding:3px}
  .akun-av{width:28px;height:28px;font-size:.82rem}
  .akun-id{display:none}
  .akun-ca{display:none}
}

/* Layar sangat sempit */
@media(max-width:480px){
  .nav-cta .btn{padding-inline:10px}
  .brand-mark{width:34px;height:34px;border-radius:10px;font-size:1rem}
  .nav-in{gap:10px}
  h1{font-size:1.55rem}
  h2{font-size:1.25rem}
  .wrap{padding-inline:var(--s3)}
  .nav-in{padding-inline:var(--s3)}
}
/* ---------- Kartu akun di header (web): avatar + nama + peran, klik = menu ---------- */
.akun-wrap{position:relative}
.akun-btn{display:flex;align-items:center;gap:10px;padding:5px 12px 5px 6px;
  border:1px solid var(--line);border-radius:var(--r-full);background:var(--surface);
  cursor:pointer;font:inherit;color:var(--fg);max-width:270px;
  transition:border-color var(--f-fast) var(--ease),background var(--f-fast) var(--ease)}
.akun-btn:hover{border-color:var(--fg-3);background:var(--surface-2)}
.akun-btn[aria-expanded="true"]{border-color:var(--brand);background:var(--surface-2)}
.akun-av{width:32px;height:32px;flex:none;border-radius:var(--r-full);background:var(--brand);
  color:var(--on-brand);display:grid;place-items:center;font-weight:800;font-size:.9rem}
/* Avatar: di dark mode --brand berubah jadi hijau TERANG, jadi teksnya harus
   tetap GELAP. Versi lama memakai #08130e yang hampir sama dengan hijau terang
   -> kontras 1.89 (huruf "S" nyaris tak terlihat). Pakai --surface yang
   selalu kontras dengan --brand di kedua mode. */

.akun-id{display:flex;flex-direction:column;align-items:flex-start;min-width:0}
.akun-nm{font-weight:700;font-size:.9rem;line-height:1.2;max-width:180px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.akun-ca{font-size:.7rem;color:var(--fg-3);line-height:1}

/* WEB = dropdown ringkas (klik avatar -> menu kecil).
   BEDA dari mobile yang pakai daftar kartu penuh. */
.akun-menu{position:absolute;top:calc(100% + 10px);right:0;z-index:70;min-width:250px;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r);
  box-shadow:var(--shadow-lg);padding:6px;display:flex;flex-direction:column;gap:2px;
  transform-origin:top right;opacity:0;transform:translateY(-6px) scale(.97);
  transition:opacity var(--f-fast) var(--ease),transform var(--f-fast) var(--ease)}
.akun-menu[hidden]{display:none}
.akun-menu:not([hidden]){opacity:1;transform:none}
.akun-menu::before{content:'';position:absolute;top:-5px;right:26px;width:10px;height:10px;
  background:var(--surface);border-left:1px solid var(--line);border-top:1px solid var(--line);
  transform:rotate(45deg)}
.akun-menu a{display:flex;align-items:center;gap:10px;min-height:42px;padding:0 12px;
  border-radius:var(--r-sm);color:var(--fg-2);font-weight:600;font-size:.92rem;
  text-decoration:none;transition:background var(--f-fast) var(--ease),color var(--f-fast) var(--ease)}
.akun-menu a::before{content:'';width:7px;height:7px;flex:none;border-radius:2px;
  background:var(--line);transition:background var(--f-fast) var(--ease)}
.akun-menu a:hover{background:var(--surface-2);color:var(--fg)}
.akun-menu a:hover::before{background:var(--accent)}
.akun-menu a.keluar{color:var(--err)}
.akun-menu a.keluar::before{background:var(--err-bg)}
.akun-menu a.keluar:hover{background:var(--err-bg);color:var(--err)}
.akun-sep{display:block;height:1px;background:var(--line);margin:4px 6px}
@media(max-width:979px){.akun-wrap{display:none}}

/* ---------- Menu HP: sheet full, konten dibatasi max-width supaya di layar lebar
   tetap di tengah dan tidak terlalu panjang/kosong. */
.drawer{position:fixed;inset:var(--nav-h,68px) 0 0;background:var(--surface);z-index:59;
  transform:translateY(-8px);opacity:0;pointer-events:none;
  transition:opacity var(--f) var(--ease),transform var(--f) var(--ease);
  overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain}
.drawer.open{opacity:1;transform:none;pointer-events:auto}
.drawer-in{max-width:520px;margin:0 auto;padding:var(--s5) var(--s4) calc(var(--s8) + env(safe-area-inset-bottom,0px));
  display:flex;flex-direction:column;gap:var(--s4)}

/* kartu identitas (hanya saat login) */
.drawer-me{display:flex;align-items:center;gap:12px;padding:12px 14px;
  border:1px solid var(--line);border-radius:var(--r);background:var(--surface-2)}
.drawer-av{width:42px;height:42px;flex:none;border-radius:var(--r-full);
  background:var(--brand);color:var(--on-brand);display:grid;place-items:center;
  font-weight:800;font-size:1.05rem}

.drawer-id{min-width:0;display:flex;flex-direction:column;align-items:flex-start}
.drawer-nm{font-weight:700;font-size:1rem;line-height:1.25;color:var(--fg);
  max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* Semua isi menu (link, tombol, keluar) satu gaya: kartu full-width, rata kiri,
   tinggi sama, sudut sama. Bedanya cuma warna latar. */
.drawer-nav{display:flex;flex-direction:column;gap:8px}
.drawer-nav a,.drawer-item{display:flex;align-items:center;min-height:54px;padding:0 16px;
  border:1px solid var(--line);border-radius:var(--r);background:var(--surface);
  color:var(--fg);font-family:'PJS';font-weight:600;font-size:1rem;line-height:1.3;
  text-align:left;text-decoration:none;cursor:pointer;width:100%;
  transition:background var(--f-fast) var(--ease),border-color var(--f-fast) var(--ease)}
.drawer-nav a:hover,.drawer-item:hover{background:var(--surface-2);border-color:var(--fg-3)}
.drawer-nav a:active,.drawer-item:active{transform:scale(.99)}

/* pemisah tipis sebelum blok akun */
.drawer-sep{display:block;height:1px;background:var(--line);margin:var(--s2) 0}

/* Masuk: hijau solid + teks putih (kontras terang) */
.drawer-nav a.masuk{background:var(--brand);border-color:var(--brand);color:var(--on-brand);font-weight:700}
.drawer-nav a.masuk:hover{background:var(--brand-ink);border-color:var(--brand-ink)}
/* Keluar: merah muda + teks merah, tetap satu gaya */
.drawer-nav a.keluar{background:var(--err-bg);border-color:var(--err);color:var(--err);font-weight:700}
.drawer-nav a.keluar:hover{background:var(--err);color:#fff}

/* Honeypot anti bot form Daftar: hilang dari tampilan tapi tetap ada di DOM
   supaya bot yang mengisi semua input ikut mengisinya. */
.hp-trap{position:absolute;left:-9999px;top:-9999px;width:1px;height:1px;
  overflow:hidden;opacity:0;pointer-events:none}

/* ---------- 4. Buttons ---------- */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;font-family:'PJS';font-weight:600;
  font-size:.95rem;padding:12px 22px;border-radius:var(--r-full);border:1.5px solid transparent;cursor:pointer;
  transition:transform var(--f-fast) var(--ease),background var(--f-fast),box-shadow var(--f-fast);white-space:nowrap;min-height:46px}
.btn:active{transform:scale(.97)}
.btn-primary{background:var(--brand);color:var(--on-brand)}
html[data-mode="dark"] .btn-primary{color:#08130e}
@media(prefers-color-scheme:dark){html:not([data-mode="light"]) .btn-primary{color:#08130e}}
.btn-primary:hover{background:var(--brand-ink);text-decoration:none;box-shadow:var(--shadow)}
.btn-accent{background:var(--accent);color:var(--on-accent)}
.btn-accent:hover{background:var(--accent-ink);text-decoration:none}
.btn-ghost{background:transparent;border-color:var(--line);color:var(--fg)}
.btn-ghost:hover{background:var(--surface-2);text-decoration:none;border-color:var(--fg-3)}
.btn-danger{background:transparent;border-color:var(--err);color:var(--err)}
.btn-danger:hover{background:var(--err-bg);text-decoration:none}
.btn-sm{padding:8px 16px;font-size:.86rem;min-height:38px}
.btn-block{width:100%}
.btn:disabled{opacity:.5;cursor:not-allowed}

/* ---------- 23.9 LAPORAN KAS: warna per tipe (14 Okt 2026) ----------
   Permintaan user: "untuk laporan kas warga, kategori pengeluaran warna red,
   kalau pemasukan warna green".
   Warna dipakai di 3 tempat sekaligus supaya satu istilah = satu warna:
     /kas "Per kategori"  : badge Pemasukan (hijau) vs Pengeluaran (merah)
     /kas "Mutasi terakhir" & tabel 6 bulan : angka + / - (hijau / merah)
     /panel/kas KPI + tabel mutasi : sama
   classes: .kat-pemasukan / .kat-pengeluaran (baris) dan
            .kat-pemasukan-nil / .kat-pengeluaran-nil (angka nominal) */
.kat-pemasukan-nil{color:var(--ok)}
.kat-pengeluaran-nil{color:var(--err)}
.kat-pemasukan .badge{background:var(--badge-ok-bg);color:var(--badge-ok-fg);
  border-color:color-mix(in srgb,var(--badge-ok-fg) 26%,transparent)}
.kat-pengeluaran .badge{background:var(--badge-err-bg);color:var(--badge-err-fg);
  border-color:color-mix(in srgb,var(--badge-err-fg) 26%,transparent)}
/* Baris kategori dapat tint tipis supaya saat mata otosatis membaca
   "keluar" atau "masuk", tidak harus SHOULD baca badge-nya dulu. */
.baris.kat-pemasukan{box-shadow:inset 3px 0 0 var(--ok)}
.baris.kat-pengeluaran{box-shadow:inset 3px 0 0 var(--err)}

/* ---------- 5. Cards + surfaces ---------- */
.card{background:var(--surface);border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--shadow)}
.card-p{padding:var(--s5)}
/* ---------- Daftar pengumuman: 2 kolom SAMA BESAR ----------
   Permintaan user 16 Okt 2026: *"daftar pengumuman'y d bkin 2 kolom & tiap
   kolom ukuran'y sama semua"*. `repeat(2, minmax(0,1fr))` = dua kolom
   PERSIS sama lebar (bukan 1.3fr/.7fr seperti `.cols-2a`).
   `align-items:stretch` + `.peng-item` flex-column + `.peng-kaki{margin-top:auto}`
   bikin SEMUA kartu sama tinggi dan baris tombolnya rata di bawah, jadi
   kedua kolom terlihat rapi sejajar meski isi tiap pengumuman beda panjang.
   HP: 1 kolom (tidak dipaksa 2 supaya teks tidak sesak). */
.peng-grid{display:grid;gap:var(--s4);grid-template-columns:minmax(0,1fr);
  align-items:stretch}
@media(min-width:820px){
  .peng-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
}
.peng-item{display:flex;flex-direction:column;min-width:0}

/* ---------- Catatan: kelas `.peng-duka` / `.badge-duka` DIHAPUS ----------
   Digantikan template khusus `_kabar_duka.html` + keluarga `.duka*` di bawah
   (permintaan user 17 Okt 2026: "Bsa ga u bkinin template khusus kabar duka").
   Jangan dihidupkan lagi -- dua sistem warna kuning yang jalan bersamaan bikin
   tampilan tidak konsisten. */

/* =====================================================================
   KARTU KHUSUS KABAR DUKA (template `_kabar_duka.html`)
   Permintaan user 17 Okt 2026: *"Bsa ga u bkinin template khusus kabar duka"*.

   Bentuknya SENGAJA beda dari kartu pengumuman biasa:
     - pita berkabung gelap di atas (bukan badge kecil);
     - garis emas tipis sebagai pemisah, bukan border biasa;
     - kutipan doa di varian utama;
     - tanpa tombol Hapus (auto-hapus 7 hari).

   ⚠️ Animasi blink dipindah ke `.duka-badan`, BUKAN ke seluruh kartu. Pita
   gelap tetap diam supaya tidak terlihat seperti kedipan error; hanya badan
   kuning yang berdenyut pelan. `prefers-reduced-motion` mematikan animasi
   tapi warna kuning TETAP ada.
   ===================================================================== */
.duka{
  display:flex;flex-direction:column;overflow:hidden;
  border-radius:var(--r-lg);border:1px solid var(--duka-line);
  background:var(--surface);color:inherit;
  box-shadow:0 1px 2px rgba(0,0,0,.04),0 8px 24px -18px rgba(0,0,0,.35)}

/* Pita berkabung */
.duka-pita{
  display:flex;align-items:center;gap:8px;
  background:var(--duka-pita);color:var(--duka-pita-fg);
  padding:9px 16px;font-size:.76rem;font-weight:800;
  letter-spacing:.14em;text-transform:uppercase}
.duka-pita .ico{width:15px;height:15px;flex:none;opacity:.9}
.duka-tanggal{margin-left:auto;font-weight:600;letter-spacing:.02em;
  text-transform:none;opacity:.8;font-variant-numeric:tabular-nums}
.duka-sisa{margin-left:auto;font-weight:700;letter-spacing:.02em;
  text-transform:none;padding:2px 9px;border-radius:var(--r-full);
  background:rgba(244,227,189,.16);border:1px solid rgba(244,227,189,.3)}

/* Badan: kuning + denyut pelan */
.duka-badan{
  display:flex;flex-direction:column;gap:10px;flex:1;
  padding:var(--s5) var(--s5) var(--s4);
  background:var(--duka-bg);
  animation:duka-denyut 2.4s ease-in-out infinite}
@keyframes duka-denyut{
  0%,100%{background:var(--duka-bg)}
  50%{background:var(--duka-bg-hi)}}
.duka-judul{margin:0;font-size:1.16rem;line-height:1.35;color:var(--fg)}
.duka-isi{margin:0;font-size:.93rem;line-height:1.6;color:var(--fg-2);
  white-space:pre-line}
.duka-garis{height:1px;background:var(--duka-line);opacity:.5;margin:2px 0}
.duka-doa{margin:0;font-style:italic;font-size:.9rem;color:var(--duka-ink);
  font-weight:600;text-align:center}
.duka-kaki{display:flex;align-items:center;justify-content:space-between;
  gap:10px;margin-top:auto;padding-top:4px}
.duka-meta{font-size:.79rem;color:var(--fg-3);
  font-variant-numeric:tabular-nums}

/* Tombol di dalam kartu: cokelat senada pita, bukan hijau brand. */
.duka-btn{background:var(--duka-pita);color:var(--duka-pita-fg);
  border-color:var(--duka-pita)}
.duka-btn:hover{filter:brightness(1.18)}

/* Varian UTAMA (beranda): lebih besar, judul lebih tegas. */
.duka-utama .duka-judul{font-size:clamp(1.3rem,2.4vw,1.6rem)}
.duka-utama .duka-badan{padding:var(--s6)}
.duka-utama .duka-isi{font-size:1rem}

/* Varian KARTU (daftar publik): seluruh kartu bisa diklik. */
.duka-kartu{text-decoration:none;transition:transform .18s ease,box-shadow .18s ease}
.duka-kartu:hover{transform:translateY(-2px);
  box-shadow:0 2px 4px rgba(0,0,0,.05),0 14px 30px -20px rgba(0,0,0,.4)}
.duka-panah{display:grid;place-items:center;width:28px;height:28px;
  border-radius:var(--r-full);background:rgba(46,36,24,.08);
  color:var(--duka-pita)}
.duka-panah .ico{width:15px;height:15px}

/* Varian PANEL: rapat, tanpa animasi mencolok (pengurus sedang bekerja). */
.duka-panel .duka-badan{padding:var(--s4)}
.duka-panel .duka-judul{font-size:1.02rem}

@media(prefers-reduced-motion:reduce){
  .duka-badan{animation:none}
  .duka-kartu:hover{transform:none}}

/* Varian DETAIL (halaman /pengumuman/<id>): lebih lapang, judul besar,
   kutipan doa di atas judul, paragraf takziah di bawah isi. */
.duka-detail .duka-badan{padding:var(--s6);gap:var(--s4)}
.duka-judul-besar{font-size:clamp(1.45rem,3.2vw,2rem);line-height:1.25}
.duka-doa-atas{margin:0;font-size:.95rem}
.duka-isi-penuh{white-space:pre-line;font-size:1.03rem;line-height:1.75;
  color:var(--fg)}
.duka-takziah{margin:0;font-size:.93rem;line-height:1.7;color:var(--fg-2);
  font-style:italic;border-left:3px solid var(--duka-line);padding-left:14px}
.duka-detail .duka-kaki{border-top:1px solid var(--duka-line);padding-top:var(--s4);
  flex-wrap:wrap}

/* ---------- Foto pada kartu Kabar Duka ----------
   Permintaan user 17 Okt 2026: form Kabar Duka punya unggahan foto. Foto
   almarhum ditampilkan LAYAK (tidak dipotong aneh): `object-fit:contain` di
   bingkai bertalang gelap, bukan `cover` yang memotong wajah. */
.duka-foto{display:block;border-radius:var(--r);
  background:rgba(46,36,24,.06);border:1px solid var(--duka-line);
  object-fit:contain}
.duka-foto-utama{width:100%;max-height:340px;margin:0 auto 4px}
.duka-foto-kecil{width:64px;height:64px;flex:none;border-radius:var(--r-sm);
  object-fit:cover}
.duka-kepala{display:flex;align-items:center;gap:12px;min-width:0}
.duka-kepala .duka-judul{min-width:0}

/* Poster takziah: ditampilkan UTUH (bukan dipotong) karena sudah berupa
   gambar jadi berukuran 1080x1350. Klik untuk membuka versi penuh. */
.duka-poster{display:block;margin:0 auto;max-width:420px;width:100%;
  border-radius:var(--r);overflow:hidden;border:1px solid var(--duka-line);
  box-shadow:0 2px 6px rgba(0,0,0,.08),0 16px 40px -26px rgba(0,0,0,.45)}
.duka-poster img{display:block;width:100%;height:auto}
.duka-poster-aksi{display:flex;gap:8px;justify-content:center;margin-top:10px;
  flex-wrap:wrap}
.duka-poster-note{display:flex;align-items:flex-start;gap:9px;
  background:var(--duka-bg);border:1px dashed var(--duka-line);
  border-radius:var(--r);padding:10px 13px;font-size:.84rem;line-height:1.5;
  color:var(--duka-ink);margin-bottom:var(--s4)}
.duka-poster-note .ico{width:16px;height:16px;flex:none;margin-top:2px}

/* Catatan pembuka di form (bukan di kartu): pengingat aturan 7 hari. */
.duka-form-note{display:flex;align-items:flex-start;gap:9px;
  background:var(--duka-bg);border:1px solid var(--duka-line);
  border-radius:var(--r);padding:11px 13px;margin-bottom:var(--s4);
  font-size:.85rem;line-height:1.5;color:var(--duka-ink)}
.duka-form-note .ico{width:16px;height:16px;flex:none;margin-top:2px}
.field label .opsional{color:var(--fg-3);font-weight:500;font-size:.85em}

/* Pratinjau foto di form: dibatasi supaya tidak mendorong tombol jauh ke bawah. */
#pratinjauFoto{display:block;margin-top:10px;max-width:100%;max-height:180px;
  border-radius:var(--r);border:1px solid var(--line);object-fit:contain}
.peng-isi{flex:1;margin:6px 0}
.peng-kaki{margin-top:auto}

/* ---------- Dashboard: 2 panel sebelahan SAMA TINGGI ----------
   Permintaan user 16 Okt 2026: kotak "Iuran N bulan terakhir" & "Pesanan
   lapak" ukurannya disamakan biar enak dilihat.
   Masalah lama: grid pakai `align-items:start` -> tiap panel setinggi
   isinya sendiri (terukur 290px vs 319px, beda 29px, tepi bawah tak rata).
   Sekarang `stretch` + panel flex-column + `.panel-b{flex:1}`: kedua kotak
   SAMA TINGGI, isi yang lebih pendek meregang mengisi. Hanya dipakai
   dashboard (`.dash-pair`); halaman lain yang memakai `.g2` tidak berubah. */
/* ============ DUA PANEL SEBELAHAN: LEBAR SAMA + TINGGI SAMA ============
   Pola ini dipakai berulang (dashboard, /iuran, /kas). Pakai `.pair-sama`
   untuk kasus baru; `.dash-pair` & `.iuran-pair` dipertahankan sebagai alias
   supaya template lama tidak perlu diubah.
   Kenapa perlu: grid `.cols-2`/`.g2` default `align-items:start` -> tiap panel
   setinggi isinya sendiri, tepi bawah tidak rata (terukur 174px vs 191px).
   Dengan `stretch` + panel flex-column + `.panel-b{flex:1}`, kedua kotak
   sama tinggi dan isi yang lebih pendek meregang mengisi.
   ⚠️ JANGAN pakai `height` mati: isi bisa berubah (mutasi nambah) -> kepotong. */
.pair-sama,.dash-pair,.iuran-pair,.kas-pair{align-items:stretch}
.pair-sama>.panel,.dash-pair>.panel,
.iuran-pair>.panel,.kas-pair>.panel{display:flex;flex-direction:column}
.pair-sama>.panel>.panel-b,.dash-pair>.panel>.panel-b,
.iuran-pair>.panel>.panel-b,.kas-pair>.panel>.panel-b{
  flex:1;display:flex;flex-direction:column}
.pair-sama>.panel>.panel-b>.tbl-scroll,.dash-pair>.panel>.panel-b>.tbl-scroll,
.iuran-pair>.panel>.panel-b>.tbl-scroll,
.kas-pair>.panel>.panel-b>.tbl-scroll{flex:1}

/* ---------- `.list-sama`: pasangan panel berisi DAFTAR ----------
   Dipakai bersama `.pair-sama` kalau isi dua panel jauh beda panjang
   (Ringkasan pengurus: Agenda 1 baris/118px vs Pesanan 6 baris/471px =
   beda 353px). Kalau hanya diregangkan, kotak Agenda jadi KOSONG MELOMPONG
   353px -- jelek. Jadi tinggi isi dibatasi `--list-max` dan sisanya di-scroll:
   dua kotak tetap SAMA TINGGI, halaman lebih pendek, ruang kosong minimal.
   Ubah `--list-max` kalau mau lebih banyak baris kelihatan. */
/* Batas tinggi HANYA saat dua panel bersebelahan (>=820px). Di HP panel
   tumpuk vertikal, jadi tidak perlu sama tinggi -> daftar ditampilkan penuh
   tanpa scroll dalam kotak kecil. */
@media(min-width:820px){
  /* 330px = 4 baris (70.6px) + padding 48px -> baris TIDAK kepotong separuh.
     Cek ulang dengan /tmp/ukur_pad.py kalau tinggi baris berubah. */
  .list-sama{--list-max:330px}
  .list-sama>.panel>.panel-b{flex:1;min-height:0;overflow-y:auto;
    max-height:var(--list-max)}
}
/* Scrollbar tipis, jangan makan lebar panel. */
.list-sama>.panel>.panel-b::-webkit-scrollbar{width:8px}
.list-sama>.panel>.panel-b::-webkit-scrollbar-thumb{
  background:var(--line);border-radius:var(--r-full)}
.list-sama>.panel>.panel-b{scrollbar-width:thin}

/* `.panel-sama` = penanda saja (tanpa aturan sendiri); semua perilakunya
   diatur lewat induk `.dash-pair`/`.iuran-pair`. */
/* /iuran: 2 kolom SAMA LEBAR (bukan 1.3fr/.7fr seperti `.cols-2a` lama). */
@media(min-width:820px){.iuran-pair{grid-template-columns:repeat(2,minmax(0,1fr))}}
.panel{min-width:0;background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden}
.panel-h{padding:var(--s5);border-bottom:1px solid var(--line-2);display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap}
.panel-h h3{margin:0;flex:1;min-width:140px}
.panel-b{padding:var(--s5)}
.grid{display:grid;gap:var(--s4);min-width:0}
.grid>*{min-width:0}
.kpi{min-width:0;overflow-wrap:anywhere}
.g2{grid-template-columns:1fr}
.g3{grid-template-columns:1fr}
.g4{grid-template-columns:repeat(2,1fr)}
@media(min-width:640px){.g3{grid-template-columns:repeat(2,1fr)}.g4{grid-template-columns:repeat(4,1fr)}}
@media(min-width:900px){.g2{grid-template-columns:repeat(2,1fr)}.g3{grid-template-columns:repeat(3,1fr)}}

/* ---------- 6. Hero ---------- */
.hero{position:relative;background:var(--band);color:var(--on-band);overflow:hidden}
.hero-bg{position:absolute;inset:0}
.hero-bg img{width:100%;height:100%;object-fit:cover;opacity:.4}
.hero-bg::after{content:'';position:absolute;inset:0;
  background:linear-gradient(115deg,rgba(12,59,43,.94) 0%,rgba(12,59,43,.72) 52%,rgba(200,121,26,.28) 100%)}
/* Hero (beranda). Padding ATAS dirapatkan ke `--s5` (24px) supaya SAMA dengan
   `.sec` -- permintaan user 16 Okt 2026: "kok jarak header k tulisan jauh bgt".
   Dulu `--s7` (48px) / `--s8` (64px desktop), jadi tulisan pertama (eyebrow)
   turun 64px dari header padahal di halaman lain cuma 24px. Padding BAWAH juga
   diturunkan satu langkah (`--s8`/`--s9` -> `--s7`/`--s8`) supaya jeda hero ke
   section pertama tidak terlalu jauh. Jangan naikkan lagi tanpa alasan. */
.hero-in{position:relative;max-width:var(--wrap);margin-inline:auto;padding:var(--s5) var(--s4) var(--s7)}
@media(min-width:768px){.hero-in{padding:var(--s5) var(--s6) var(--s8)}}
.hero-grid{display:grid;gap:var(--s6);align-items:center}
@media(min-width:900px){.hero-grid{grid-template-columns:1.15fr .85fr}}
.hero h1{color:#fff;max-width:14ch;margin-bottom:var(--s4)}
.hero p.lead{font-size:1.06rem;color:rgba(255,255,255,.85);max-width:52ch;margin-bottom:var(--s5)}
.eyebrow{display:inline-flex;align-items:center;gap:8px;font-size:.74rem;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;color:var(--accent);background:rgba(200,121,26,.14);padding:7px 14px;border-radius:var(--r-full);margin-bottom:var(--s4)}
.hero-actions{display:flex;gap:var(--s3);flex-wrap:wrap}
.hero-actions .btn-ghost{border-color:rgba(255,255,255,.4);color:#fff}
.hero-actions .btn-ghost:hover{background:rgba(255,255,255,.12)}
.hero-stats{display:grid;grid-template-columns:repeat(2,1fr);gap:1px;background:rgba(255,255,255,.16);
  border:1px solid rgba(255,255,255,.16);border-radius:var(--r);overflow:hidden;backdrop-filter:blur(8px)}
.hero-stat{background:rgba(18,81,60,.5);padding:var(--s5) var(--s4)}
.hero-stat b{display:block;font-family:'Outfit';font-size:1.7rem;color:#fff;letter-spacing:-.02em;line-height:1.1}
.hero-stat span{font-size:.82rem;color:rgba(255,255,255,.72)}

/* ---------- 7. Sections ---------- */
/* Jarak section. Permintaan user 16 Okt 2026: "kok jarak header k tulisan jauh
   bgt ya bsa d rapihin gtu yar ga ad jeda jauh bgt sklian ma footer'y jga",
   lalu disusul: "yg footer msh jauh jrak'y bsa d dketin lg".

   SEBELUM (terukur di live): padding 48px (HP) / 64px (desktop) atas-bawah,
   DITAMBAH `.ft{margin-top:48px}` yang dobel dengan padding-bawah section.
   Hasilnya header->judul 64px & tulisan->tulisan footer 137px (desktop).

   TAHAP 1: padding atas dipotong jadi 24/32px, `.ft{margin-top}` DIHAPUS.
   TAHAP 2 (permintaan lanjutan): padding BAWAH juga dirapatkan, dan padding
   section dibuat SIMETRIS supaya jarak antar-section seragam.
   TAHAP 3 ("yg footer msh jauh jrak'y bsa d dketin lg"): 24px di kedua viewport
   (media override dihapus - desktop tidak perlu lebih lega dari HP).

   ⚠️ JANGAN naikkan lagi ke --s7/--s8 tanpa alasan: user sudah 3x minta
   dirapatkan. Kalau perlu diubah, ukur dulu di live (/tmp/ukur_jarak_hal.py
   untuk box, /tmp/ukur_footer.py untuk jarak TULISAN->TULISAN) dan bandingkan
   angkanya, jangan asal ganti token. */
.sec{padding:var(--s6) 0}
/* Section PERTAMA (yang nempel header) & TERAKHIR (yang nempel footer) dipangkas
   terpisah. Permintaan user 16 Okt 2026: "kok jarak header k tulisan jauh bgt ya
   bsa d rapihin gtu yar ga ad jeda jauh bgt sklian ma footer'y jga" lalu
   "yg footer msh jauh jrak'y bsa d dketin lg", lalu "Kok yg footer deket bgt ya
   bsa d samain kyk header jrak'y".

   KENAPA dipisah: `padding` section dipakai TIGA fungsi sekaligus --
   (a) jarak header ke konten, (b) jarak antar-section, (c) jarak konten ke
   footer. Kalau diturunkan global, jarak antar-section ikut rapat dan halaman
   ber-section-banyak (beranda) jadi berdesakan (terbukti: verifikasi visual
   bilang "cenderung terlalu mepet"). Jadi:
     - default `.sec` tetap LEGA (--s6 = 32px -> antar-section 64px);
     - section PERTAMA & TERAKHIR dipangkas ke --s5 (24px) -- SAMA di kedua ujung
       (permintaan terakhir: footer jangan lebih rapat dari header).
   JANGAN turunkan lagi ke --s3 (12px): pernah dicoba, user bilang "deket bgt".

   Specificity: `main > section.sec:first-of-type` / `:last-of-type` (0,2,2)
   menang atas `.sec` (0,1,0), jadi berlaku desktop & HP tanpa media query.
   Catatan: di BERANDA section pertama adalah `.hero` (bukan `.sec`), jadi
   `:first-of-type` TIDAK kena apa pun di sana -- itu memang benar, jarak hero ke
   header diatur terpisah lewat `.hero-in`.

   Terukur (desktop 1400px & HP 390px identik, semua 6 halaman):
     header -> tulisan      : 64px / 48px -> 24px
     konten -> kotak footer : 137px       -> 24px  (sama dgn header)
     antar-section beranda  : TIDAK berubah (64px) */
main > section.sec:first-of-type{padding-top:var(--s5)}
main > section.sec:last-of-type{padding-bottom:var(--s5)}
.sec-alt{background:var(--surface-2)}
.sec-head{max-width:64ch;margin-bottom:var(--s6)}
/* Judul nama perumahan (halaman Profil): selalu SATU BARIS, font menyesuaikan */
.sec-head-judul{max-width:none}
.judul-peruhan{white-space:nowrap;font-size:clamp(1.9rem,4vw,2.7rem);max-width:100%}
.sec-head h2{margin-bottom:var(--s2)}
.sec-head p{color:var(--fg-2);margin:0}
.sec-head-row{display:flex;align-items:flex-end;justify-content:space-between;gap:var(--s4);flex-wrap:wrap;margin-bottom:var(--s6)}

/* ---------- 8. Badges ---------- */
/* Badge/kotak status. Memakai token --badge-* yang lebih tegas dari zebra.
   Versi lama memakai --ok-bg/--warn-bg/--err-bg yang kontrasnya cuma
   1.008-1.048 terhadap baris zebra -> kotaknya "samar" (nyaris tak terlihat
   tepinya). Lihat blok token untuk angka terukurnya. */
.badge{display:inline-flex;align-items:center;gap:6px;font-size:.74rem;font-weight:700;letter-spacing:.02em;
  padding:5px 11px;border-radius:var(--r-full);background:var(--badge-brand-bg);color:var(--badge-brand-fg);white-space:nowrap;
  border:1px solid color-mix(in srgb,var(--badge-brand-fg) 26%,transparent)}
.badge-ok{background:var(--badge-ok-bg);color:var(--badge-ok-fg);
  border-color:color-mix(in srgb,var(--badge-ok-fg) 26%,transparent)}
.badge-warn{background:var(--badge-warn-bg);color:var(--badge-warn-fg);
  border-color:color-mix(in srgb,var(--badge-warn-fg) 26%,transparent)}
.badge-err{background:var(--badge-err-bg);color:var(--badge-err-fg);
  border-color:color-mix(in srgb,var(--badge-err-fg) 26%,transparent)}
/* UNGU: "> 3 bulan tidak masuk" di kolom "Pernah masuk?" */
.badge-ungu{background:var(--badge-ungu-bg);color:var(--badge-ungu-fg);
  border-color:color-mix(in srgb,var(--badge-ungu-fg) 26%,transparent)}
.badge-info{background:var(--badge-info-bg);color:var(--badge-info-fg);
  border-color:color-mix(in srgb,var(--badge-info-fg) 26%,transparent)}
.badge-muted{background:var(--badge-muted-bg);color:var(--badge-muted-fg);
  border-color:color-mix(in srgb,var(--badge-muted-fg) 26%,transparent)}
/* ---------- 3 status yang wajib kebedain satu sama lain ----------
   Permintaan user: "yang belum bayar, belum, sama menunggu dibedain ke warna
   yang lain yang belum ada, biar gak ada yang sama warnanya".
   Hijau/amber/merah/biru/abu sudah dipakai, jadi 3 warna BARU:
     Belum bayar (belum pernah bayar)  -> MAGENTA  (hue 325)
     Belum       (tagihan blm dibayar) -> UNGU     (hue 265)
     Menunggu    (nunggu verifikasi)   -> NAVY gelap (hue 220, value jauh
                                        Menunggu TAMBAH ikon jam: warna biru apa pun masih tetanggaan dengan
   "Dikirim" (info), jadi bentuk yang membedakan, bukan cuma warna. Ini juga
   menolong pengguna buta warna. */
/* "> 3 bulan tidak masuk" -> UNGU (4 warna status login, lihat _label_login) */
.badge-lama{background:var(--badge-lama-bg);color:var(--badge-lama-fg);
  border-color:color-mix(in srgb,var(--badge-lama-fg) 26%,transparent)}
.badge-belumbayar{background:var(--badge-belumbayar-bg);color:var(--badge-belumbayar-fg);
  border-color:color-mix(in srgb,var(--badge-belumbayar-fg) 30%,transparent)}
.badge-menunggu{background:var(--badge-menunggu-bg);color:var(--badge-menunggu-fg);
  border-color:color-mix(in srgb,var(--badge-menunggu-fg) 30%,transparent)}
.badge-menunggu::before{content:"";width:11px;height:11px;flex:0 0 11px;
  background:currentColor;
  -webkit-mask:var(--ikon-jam) center/contain no-repeat;
  mask:var(--ikon-jam) center/contain no-repeat}
@media(min-width:1000px){.bubble-ok{background:var(--ok-bg);color:var(--ok)}}

/* ---------- 9. Forms ---------- */
.field{margin-bottom:var(--s4)}
.field label{display:block;font-weight:600;font-size:.9rem;margin-bottom:6px;color:var(--fg)}
/* Penanda field WAJIB (mis. foto bukti transfer di /iuran). Merah supaya
   langsung kelihatan bahwa tanpa isian ini form tidak bisa dikirim. */
.field label .wajib{color:var(--err);font-weight:700}
.field .hint{font-size:.82rem;color:var(--fg-3);margin-top:6px;display:block}
.field .err{font-size:.84rem;color:var(--err);margin-top:6px;display:block;font-weight:500}
/* Tanda field WAJIB (mis. foto bukti transfer di /iuran). Merah supaya
   langsung kelihatan bahwa form tidak bisa dikirim tanpa isian ini. */
.field label .wajib{color:var(--err);font-weight:700;margin-left:2px}
/* Peringatan kecil di bawah field wajib (mis. "Belum ada agenda"). */
.field .wajib-kecil{color:var(--err);font-weight:600}
input[type=text],input[type=email],input[type=password],input[type=number],input[type=date],
input[type=time],input[type=tel],input[type=search],input[type=file],select,textarea{
  width:100%;font-family:'PJS';font-size:1rem;padding:13px 15px;border:1.5px solid var(--line);
  border-radius:var(--r-sm);background:var(--surface);color:var(--fg);transition:border-color var(--f-fast),box-shadow var(--f-fast);
  min-height:48px}
textarea{min-height:110px;resize:vertical;line-height:1.55}
input::placeholder,textarea::placeholder{color:var(--fg-3);opacity:1}
input:focus,select:focus,textarea:focus{outline:none;border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-soft)}
select{appearance:none;background-image:linear-gradient(45deg,transparent 50%,var(--fg-3) 50%),linear-gradient(135deg,var(--fg-3) 50%,transparent 50%);
  background-position:calc(100% - 19px) 21px,calc(100% - 14px) 21px;background-size:5px 5px;background-repeat:no-repeat;padding-right:40px}
input[type=file]{padding:10px;font-size:.9rem;min-height:0}
.row{display:grid;gap:var(--s4);min-width:0}
.row>*{min-width:0}
.field input,.field select,.field textarea{max-width:100%;width:100%}
.field{min-width:0}
/* baris tombol: boleh wrap di layar sempit, tombolnya flex:none */
.row-actions{display:flex;flex-wrap:wrap;align-items:center;gap:8px}

/* kotak pemberitahuan di dalam halaman */
.notice{display:flex;gap:10px;align-items:flex-start;padding:13px 15px;border-radius:var(--r-sm);
  font-size:.9rem;line-height:1.5;border:1px solid;margin-bottom:16px}
.notice b{display:block;margin-bottom:2px}
.notice-warn{background:var(--warn-bg);border-color:var(--warn);color:var(--fg)}
.notice-err{background:var(--err-bg);border-color:var(--err);color:var(--fg)}
.notice-ok{background:var(--ok-bg);border-color:var(--ok);color:var(--fg)}
/* penanda konten bisa digeser di HP (sidebar/tab/filter) */
.scroller{position:relative}
.scroller::after{content:'';position:absolute;top:0;right:0;bottom:0;width:26px;pointer-events:none;
  background:linear-gradient(to right,transparent,var(--surface));opacity:0;transition:opacity .2s}
@media(hover:none){.chipwrap::after{opacity:.9}}
.row-actions .btn{flex:none;min-width:0}
.flex-b{min-width:0}
@media(min-width:680px){.row-2{grid-template-columns:1fr 1fr}.row-3{grid-template-columns:repeat(3,1fr)}}
.check{display:flex;align-items:flex-start;gap:10px;font-size:.92rem;color:var(--fg-2);cursor:pointer}
.check input{width:20px;height:20px;min-height:0;margin-top:2px;flex:none;accent-color:var(--brand)}

/* ---------- 10. Table ---------- */
.tbl-scroll{overflow-x:auto;-webkit-overflow-scrolling:touch;max-width:100%;min-width:0;
  overscroll-behavior-x:contain;-webkit-overflow-scrolling:touch}
.tbl-scroll::-webkit-scrollbar{height:7px}
.tbl-scroll::-webkit-scrollbar-thumb{background:var(--line);border-radius:99px}
table.tbl{width:100%;border-collapse:collapse;font-size:.93rem;min-width:620px}
.tbl th{text-align:left;font-size:.74rem;text-transform:uppercase;letter-spacing:.07em;color:var(--fg-3);
  font-weight:700;padding:10px 12px;border-bottom:1px solid var(--line);white-space:nowrap}
.tbl td{padding:13px 12px;border-bottom:1px solid var(--line-2);vertical-align:middle}
.tbl tr:last-child td{border-bottom:0}
.tbl tbody tr:hover{background:var(--row-hover)}
/* ── Tombol sortir di judul kolom (permintaan user 16 Okt 2026) ──────────
   "d menu data warga d ksh tombol short d tiap judul'y".
   Seluruh area judul bisa diklik. Panah kecil menunjukkan kolom mana yang
   sedang aktif & arahnya.
   ⚠️ Panah diposisikan ABSOLUTE (bukan ikut aliran flex) supaya TIDAK
   memakan lebar teks judul -- kalau ikut aliran, judul jadi terpotong
   ("RUMAH" -> "RU...", "TELEGRAM" -> "TE..."). Padding kanan disisakan
   sedikit sebagai tempat panah. */
.tbl th.th-urut{padding:0;position:relative}
.tbl th.th-urut > a{display:block;
  width:100%;min-height:42px;padding:14px 17px 14px 12px;
  color:inherit;text-decoration:none;font:inherit;
  font-size:.74rem;font-weight:700;text-transform:uppercase;
  letter-spacing:.07em;white-space:nowrap;cursor:pointer;
  border-radius:var(--r-sm)}
.tbl th.th-urut.t-c > a{text-align:center;padding-left:17px}
.tbl th.th-urut > a:hover{background:var(--row-hover);color:var(--fg)}
.tbl th.th-urut > a:focus-visible{outline:2px solid var(--brand);outline-offset:-2px}
.tbl th.th-urut .th-arah{position:absolute;right:5px;top:50%;
  transform:translateY(-50%);font-size:.72rem;opacity:.4;line-height:1}
.tbl th.th-urut > a:hover .th-arah{opacity:.85}
.tbl th.th-urut.aktif > a{color:var(--brand)}
.tbl th.th-urut.aktif .th-arah{opacity:1;font-weight:700}
.num{font-variant-numeric:tabular-nums;white-space:nowrap}
.strong{font-weight:700}
.muted{color:var(--fg-3)}
.small{font-size:.86rem}

/* ---------- 11. KPI ---------- */
.kpi{background:var(--surface);border:1px solid var(--line);border-radius:var(--r);padding:var(--s5)}
.kpi .lab{font-size:.8rem;color:var(--fg-3);font-weight:600;margin-bottom:6px}
/* Angka rupiah HARUS utuh di 320px. Versi lama pakai nowrap + 1.15rem minimum,
   sehingga "Rp1.234.500" meluber dan dipotong ellipsis (scrollWidth 106 > 90).
   Perbaikan: izinkan wrapping antar digit (overflow-wrap:anywhere BUKAN
   text-overflow:ellipsis), dan turunkan clamp min ke 0.95rem + kurangi
   letter-spacing supaya "Rp1.234.500" muat satu baris di HP kecil. */
/* Angka rupiah HARUS utuh, bahkan di grid 4 kolom pada tablet (~114px/kartu).
   Solusi: container query unit (cqi) -- ukuran huruf mengikuti lebar KARTU,
   bukan lebar layar. Kalau browser tidak mendukung cqi, ada fallback vw.
   1cqi = 1% dari lebar kartu, jadi clamp(11px, 7.5cqi, 26.4px) selalu muat. */
.kpi{container-type:inline-size}
.kpi .val{font-family:'Outfit';font-size:clamp(.95rem,4.2vw,1.65rem);
  font-size:clamp(11px,7.5cqi,1.65rem);font-weight:700;
  letter-spacing:-.03em;line-height:1.15;
  overflow-wrap:anywhere;hyphens:none;
  font-variant-numeric:tabular-nums;white-space:nowrap}
.kpi .val.ok{color:var(--ok)}.kpi .val.warn{color:var(--warn)}.kpi .val.err{color:var(--err)}

/* ---------- 12. Toast + flash ---------- */
/* popup pesan di bawah layar: tidak menutupi konten, aman di HP */
#toast{position:fixed;left:50%;bottom:calc(16px + env(safe-area-inset-bottom,0px));
  transform:translateX(-50%);z-index:300;display:flex;flex-direction:column;
  gap:8px;align-items:center;pointer-events:none;
  width:min(560px,calc(100vw - 24px))}
#toast .t{display:flex;align-items:flex-start;gap:10px;width:100%;
  padding:13px 16px;border-radius:14px;background:var(--surface);color:var(--fg);
  border:1px solid var(--line);
  box-shadow:0 10px 30px rgba(8,14,11,.18),0 2px 6px rgba(8,14,11,.08);
  font-size:.92rem;line-height:1.45;font-weight:500;text-align:left;
  opacity:0;transform:translateY(14px) scale(.97);
  transition:opacity .22s ease,transform .22s cubic-bezier(.2,.9,.3,1.2);
  pointer-events:auto;overflow-wrap:anywhere}
#toast .t.in{opacity:1;transform:translateY(0) scale(1)}
#toast .t.err{background:var(--err-bg);border-color:var(--err)}
#toast .t.err::before{content:'\2715';color:var(--err);font-weight:800;flex:none;line-height:1.4}
#toast .t.ok{background:var(--ok-bg);border-color:var(--ok)}
#toast .t.ok::before{content:'\2713';color:var(--ok);font-weight:800;flex:none;line-height:1.4}
#toast .t.warn{background:var(--warn-bg);border-color:var(--warn)}
#toast .t.warn::before{content:'\26A0';color:var(--warn);flex:none;line-height:1.4}
@media(max-width:480px){
  #toast{bottom:0;left:0;transform:none;width:100%;
    padding:10px 10px calc(10px + env(safe-area-inset-bottom,0px))}
  #toast .t{border-radius:12px;font-size:.9rem}
}
@media(prefers-reduced-motion:reduce){
  #toast .t{transition:none;opacity:1;transform:none}
}

/* ---------- 13. Galeri ---------- */
.gal{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--s3)}
@media(min-width:700px){.gal{grid-template-columns:repeat(3,1fr)}}
@media(min-width:1000px){.gal{grid-template-columns:repeat(4,1fr)}}
/* ---------- Panel "Foto di galeri" (dalam .pair-sama) ----------
   Grid `.gal` biasa memakai media query LEBAR LAYAR (>=1000px -> 4 kolom),
   padahal panelnya cuma ~448px -> thumbnail jadi ~100px kecil dan menyisakan
   ruang kosong besar. `.gal-isi` memakai 2 kolom dan MELAR mengisi tinggi
   panel (yang tingginya mengikuti kotak "Unggah foto").
   `aspect-ratio:auto` + `grid-auto-rows:1fr` -> thumbnail mengisi sisa ruang,
   foto tetap rapi karena `object-fit:cover`.
   ⚠️ Hanya dipakai di panel_galeri.html. Galeri publik tetap `.gal` biasa. */
.gal.gal-isi{grid-template-columns:repeat(2,minmax(0,1fr));
  align-content:start}

/* ---------- WADAH FOTO & VIDEO di halaman Galeri panel ----------
   Permintaan user 16 Okt 2026: *"bkinin d galeri ad wadah tuk foto ma video
   jd klo upload foto dy msuk k wadah foto bgtu pn jga video smua msuk
   b'dasarkn kaitkan ke agenda"*.
   Tiap wadah berisi beberapa KELOMPOK (satu per agenda). */
.wadah-grup{margin-bottom:var(--s5)}
.wadah-grup:last-child{margin-bottom:0}
.wadah-grup-h{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  padding:8px 0 10px;border-bottom:1px dashed var(--line);margin-bottom:12px}
.wadah-agenda{font-weight:700;font-size:.95rem;color:var(--fg)}
/* "Tanpa agenda" dibedakan supaya jelas bukan bagian agenda tertentu. */
.wadah-agenda.wadah-tanpa{color:var(--fg-3);font-weight:600;font-style:italic}
/* Grid di dalam wadah: 3 kolom tetap (bukan 4) supaya thumbnail tidak kekecilan. */
.gal.gal-wadah{grid-template-columns:repeat(2,minmax(0,1fr))}
@media(min-width:700px){.gal.gal-wadah{grid-template-columns:repeat(3,minmax(0,1fr))}}
.gal-sub{font-size:.72rem;font-weight:500;opacity:.8;margin-top:2px}

/* Thumbnail VIDEO: pakai <video preload="metadata"> sebagai poster alami.
   `object-fit:cover` + aspect-ratio 1 (dari .gal-item) -> rapi sejajar foto. */
.gal-item video{width:100%;height:100%;object-fit:cover;display:block}

/* ---------- Tombol pilih jenis media (Foto / Video) ----------
   Permintaan user 16 Okt 2026: *"tmbhin buton buat milih foto/video d bwah
   tulisan Unggah Foto/Video"*. SATU grup tombol, yang aktif ditandai `.on`
   (pola yang disukai user: bukan dua tombol aksi terpisah). */
.pilih-jenis{display:flex;gap:8px;margin-bottom:var(--s4)}
.pj-btn{flex:1 1 0;display:inline-flex;align-items:center;justify-content:center;
  gap:6px;padding:11px 14px;border-radius:var(--r-sm);cursor:pointer;
  background:var(--bg-2);border:1px solid var(--line);color:var(--fg-2);
  font:inherit;font-weight:600;font-size:.92rem;
  transition:background var(--f) var(--ease),border-color var(--f) var(--ease),
             color var(--f) var(--ease)}
.pj-btn:hover{border-color:var(--line-3);color:var(--fg)}
.pj-btn.on{background:var(--brand);border-color:var(--brand);color:#fff}
.pj-btn.on:hover{color:#fff}

/* Kartu untuk video LINK yang tidak punya thumbnail (Instagram/TikTok/FB/X).
   Isinya: ikon play di tengah + nama platform kecil di bawahnya.
   ⚠️ `position:absolute` + `top/left/width/height` eksplisit (bukan `inset`)
   karena aturan global `.gal-item>*` di bagian kartu menimpanya. */
.gal-item>.gal-linkkartu{position:absolute;top:0;left:0;width:100%;height:100%;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:8px;z-index:2;
  background:linear-gradient(135deg,var(--brand-2),var(--brand-d))}
.gal-item>.gal-linkkartu>.gal-linkplay{font-size:1.6rem;color:#fff;line-height:1;
  text-shadow:0 2px 10px rgba(0,0,0,.6)}
.gal-item>.gal-linkkartu>.gal-linkplat{font-size:.74rem;font-weight:700;
  letter-spacing:.1em;text-transform:uppercase;color:#fff;opacity:.9}
/* Versi publik/detail: kartu tautan dipakai sebagai <span> penuh. */
.gal-item>span.gal-linkkartu{position:absolute;top:0;left:0;width:100%;height:100%;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:8px;z-index:2;
  background:linear-gradient(135deg,var(--brand-2),var(--brand-d))}
.gal-item>span.gal-linkkartu>.gal-linkplay{font-size:1.6rem;color:#fff;line-height:1;
  text-shadow:0 2px 10px rgba(0,0,0,.6)}
.gal-item>span.gal-linkkartu>.gal-linkplat{font-size:.74rem;font-weight:700;
  letter-spacing:.1em;text-transform:uppercase;color:#fff;opacity:.9}

/* Lightbox: iframe embed video (YouTube/Vimeo). */
.lightbox iframe{width:min(1100px,94vw);height:min(620px,70vh);border:0;
  border-radius:var(--r);background:#000}
/* Penanda video (segitiga) di tengah supaya beda dari foto.
   ⚠️ WAJIB `top:0;left:0;width:100%;height:100%` -- kalau hanya `inset:0`
   dan induknya BUKAN flex/grid, elemen ini jadi bar setinggi isi (38px) yang
   menempel di TENGAH caption, bukan overlay penuh. Juga `z-index:2` supaya di
   atas gambar/caption. */
/* ⚠️ WAJIB pakai `.gal-item>.gal-play` (spesifisitas 0,2,0). Aturan global
   `.gal-item>*{position:relative;z-index:1}` di bagian kartu (baris ~1877)
   spesifisitasnya SAMA dengan `.gal-play` dan letaknya lebih akhir, sehingga
   menimpa `position:absolute` -> ikon play jatuh di BAWAH gambar, bukan
   overlay. Dengan selector ini, overlay-nya menang. */
.gal-item>.gal-play{position:absolute;top:0;left:0;width:100%;height:100%;
  display:grid;place-items:center;font-size:1.5rem;color:#fff;
  pointer-events:none;z-index:2;
  text-shadow:0 2px 10px rgba(0,0,0,.7)}
/* Di panel, video boleh dikontrol langsung (bukan cuma lewat lightbox). */
.gal-video video{object-fit:cover}
/* Lightbox: elemen video disembunyikan default, ditampilkan saat perlu. */
.lightbox video{max-width:min(1100px,94vw);max-height:84vh;border-radius:var(--r)}
/* Thumbnail tetap PERSEGI (aspect-ratio:1 dari `.gal-item`). Jangan dibuat
   `grid-auto-rows:1fr` -- pernah dicoba, thumbnail jadi 193x531 (potret
   memanjang) dan foto ter-crop berlebihan. */
.gal.gal-isi .gal-item{min-height:0}
.gal-item{position:relative;aspect-ratio:1;border-radius:var(--r);overflow:hidden;background:var(--line-2);cursor:pointer;border:0;padding:0}
.gal-item img{width:100%;height:100%;object-fit:cover;transition:transform var(--f-slow) var(--ease)}
.gal-item:hover img{transform:scale(1.06)}
.gal-cap{position:absolute;inset:auto 0 0 0;padding:26px 12px 11px;font-size:.82rem;font-weight:600;color:#fff;
  background:linear-gradient(transparent,rgba(0,0,0,.8));text-align:left}

/* ---------- Kartu galeri yang punya TOMBOL AKSI (panel) ----------
   Dilaporkan user 17 Okt 2026: "kok ga ad tombol hapus". Penyebabnya caption
   `position:absolute` setinggi 122px menempel di tepi bawah kartu 282px, dan
   `.gal-item` punya `overflow:hidden` -> tombol Hapus terpotong 111px.

   Solusi: kartu `.ada-aksi` jadi kolom flex; area gambarnya tetap persegi
   (`.gal-media`), caption turun ke BAWAH gambar sebagai blok normal sehingga
   tombol selalu terlihat utuh berapa pun panjang judulnya. */
.gal-item.ada-aksi{aspect-ratio:auto;display:flex;flex-direction:column;
  overflow:visible;background:var(--surface);border:1px solid var(--line)}
.gal-item.ada-aksi .gal-media{position:relative;width:100%;aspect-ratio:1;
  overflow:hidden;border-radius:calc(var(--r) - 1px) calc(var(--r) - 1px) 0 0;
  background:var(--line-2)}
.gal-item.ada-aksi .gal-media>img,
.gal-item.ada-aksi .gal-media>video{width:100%;height:100%;object-fit:cover;
  display:block}
/* Penanda play / kartu tautan menempel di AREA GAMBAR, bukan seluruh kartu. */
.gal-item.ada-aksi>.gal-media>.gal-play,
.gal-item.ada-aksi>.gal-media>.gal-linkkartu{position:absolute;
  top:0;left:0;width:100%;height:100%;z-index:2}
.gal-item.ada-aksi .gal-cap{position:static;inset:auto;padding:10px 12px 12px;
  background:var(--surface);color:var(--fg);font-weight:600;
  border-top:1px solid var(--line);display:flex;flex-direction:column;
  gap:4px;flex:1 1 auto}
.gal-item.ada-aksi .gal-sub{font-size:.76rem;font-weight:500;color:var(--fg-3)}
/* Tombol Hapus: selalu terlihat & tegas (user minta "kok ga ad tombol hapus").
   ⚠️ JANGAN set background/color di sini. Aturan global
   `.panel-b .btn:not([class*=btn-danger])...` (baris ~2275) punya specificity
   LEBIH TINGGI dan akan menimpanya -> tombol balik jadi abu samar.
   Solusinya: pakai kelas `.btn-danger` di HTML (makna "bahaya" sudah ada di
   sistem warna tombol §23.8), di sini cuma atur ukuran & penempatan. */
.gal-item.ada-aksi [data-delgal]{margin-top:auto;width:100%;
  font-weight:700;letter-spacing:.01em}
.gal-item.ada-aksi [data-delgal]:focus-visible{outline:2px solid var(--err);
  outline-offset:2px}
.lightbox{position:fixed;inset:0;background:rgba(8,14,11,.94);z-index:130;display:none;place-items:center;padding:var(--s4)}
.lightbox.open{display:grid}
.lightbox img{max-width:min(1100px,94vw);max-height:84vh;border-radius:var(--r);object-fit:contain}
.lightbox .cap{color:#fff;text-align:center;margin-top:var(--s4);max-width:60ch}
.lightbox button.x{position:absolute;top:16px;right:16px;width:46px;height:46px;border-radius:50%;
  background:rgba(255,255,255,.14);border:0;color:#fff;font-size:1.4rem;cursor:pointer}
.lightbox button.x:hover{background:rgba(255,255,255,.26)}

/* ---------- 14. Toko ---------- */
.prod{background:var(--surface);border:1px solid var(--line);border-radius:var(--r);overflow:hidden;
  display:flex;flex-direction:column;transition:transform var(--f) var(--ease),box-shadow var(--f) var(--ease)}
.prod:hover{transform:translateY(-3px);box-shadow:var(--shadow-lg)}
.prod-img{aspect-ratio:1;background:var(--line-2);overflow:hidden}
.prod-img img{width:100%;height:100%;object-fit:cover}
.prod-b{padding:var(--s4);display:flex;flex-direction:column;gap:6px;flex:1}
.prod-b h3{font-size:1rem;margin:0;font-family:'PJS';font-weight:700}
.prod-price{font-family:'Outfit';font-weight:700;font-size:1.12rem;color:var(--brand);letter-spacing:-.01em;font-variant-numeric:tabular-nums}
.chips{display:flex;gap:8px;overflow-x:auto;padding-bottom:6px;-webkit-overflow-scrolling:touch;scrollbar-width:none}
.chips::-webkit-scrollbar{display:none}
.chip{padding:8px 16px;border-radius:var(--r-full);border:1.5px solid var(--line);background:var(--surface);
  color:var(--fg-2);font-size:.88rem;font-weight:600;white-space:nowrap;cursor:pointer;flex:none;text-decoration:none}
.chip:hover{border-color:var(--fg-3);text-decoration:none}
.chip.on{background:var(--brand);border-color:var(--brand);color:var(--on-brand)}
html[data-mode="dark"] .chip.on{color:#08130e}
.qty{display:inline-flex;align-items:center;border:1.5px solid var(--line);border-radius:var(--r-sm);overflow:hidden}
.qty button{width:44px;height:48px;border:0;background:var(--surface);cursor:pointer;font-size:1.2rem;color:var(--fg)}
.qty button:hover{background:var(--surface-2)}
.qty input{width:60px;text-align:center;border:0;border-inline:1.5px solid var(--line);border-radius:0;min-height:48px;padding:0;-moz-appearance:textfield}
.qty input::-webkit-outer-spin-button,.qty input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}

/* ---------- 15. Timeline (kegiatan) ---------- */
.tl{position:relative;padding-left:34px}
.tl::before{content:'';position:absolute;left:11px;top:8px;bottom:8px;width:2px;background:var(--line)}
.tl-item{position:relative;padding-bottom:var(--s6)}
.tl-item:last-child{padding-bottom:0}
.tl-item::before{content:'';position:absolute;left:-28px;top:6px;width:14px;height:14px;border-radius:50%;
  background:var(--surface);border:3px solid var(--brand)}
.tl-item.past::before{border-color:var(--fg-3)}
.tl-item.cancel::before{border-color:var(--err)}
.tl-date{font-size:.8rem;font-weight:700;color:var(--accent);letter-spacing:.04em;text-transform:uppercase;margin-bottom:4px}

/* ---------- 16. Tabs / pills nav ---------- */
.tabs{display:flex;gap:var(--s2);border-bottom:1px solid var(--line);overflow-x:auto;margin-bottom:var(--s5);scrollbar-width:none}
.tabs::-webkit-scrollbar{display:none}
.tabs button,.tabs a{padding:11px 16px;border:0;background:none;font-family:'PJS';font-size:.94rem;font-weight:600;
  color:var(--fg-3);cursor:pointer;border-bottom:2.5px solid transparent;white-space:nowrap;text-decoration:none;flex:none}
.tabs button:hover,.tabs a:hover{color:var(--fg);text-decoration:none}
.tabs button.on,.tabs a.on{color:var(--brand);border-bottom-color:var(--brand)}

/* ---------- 17. Panel sidebar ---------- */
.shell{display:grid;gap:var(--s5);padding:var(--s5) 0 var(--s8);min-width:0;
  grid-template-columns:minmax(0,1fr)}
@media(min-width:1000px){.shell{grid-template-columns:236px minmax(0,1fr);align-items:start;padding-top:var(--s6)}}
/* grid item harus boleh menyusut, kalau tidak isi lebar (tabel) akan melebar grid */
.shell>*{min-width:0;max-width:100%}
.shell .side{min-width:0}
.shell .side>*{min-width:0}
.side{background:var(--surface);border:1px solid var(--line);border-radius:var(--r);padding:var(--s3);position:sticky;top:88px}
.side-h{font-size:.72rem;font-weight:700;text-transform:uppercase;letter-spacing:.1em;color:var(--fg-3);padding:10px 12px 6px}
.side a{display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:var(--r-sm);color:var(--fg-2);
  font-size:.92rem;font-weight:500;text-decoration:none}
.side a:hover{background:var(--surface-2);color:var(--fg)}
.side a.on{background:var(--brand-soft);color:var(--brand);font-weight:700}
.side a .n{margin-left:auto;background:var(--surface);border:1px solid var(--line);border-radius:var(--r-full);
  min-width:22px;height:22px;display:grid;place-items:center;font-size:.74rem;font-weight:700;padding-inline:5px}
/* Mobile: sidebar jadi tab strip. Tiap item = pill, bisa digeser,
   dan item aktif otomatis di-scroll ke tengah (lihat app.js). */
@media(max-width:999px){
  .sidewrap{position:relative}
  .side{position:static;display:flex;gap:6px;overflow-x:auto;scrollbar-width:none;
    padding:6px;-webkit-overflow-scrolling:touch;
    scroll-snap-type:x proximity;scroll-padding-inline:6px;
    background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-full)}
  .side::-webkit-scrollbar{display:none}
  .side-h{display:none}
  .side a{white-space:nowrap;flex:none;scroll-snap-align:center;
    border:1px solid var(--line);border-radius:var(--r-full);
    padding:8px 14px;font-size:.88rem;background:var(--surface)}
  .side a:hover{background:var(--surface-2)}
  .side a.on{background:var(--brand);color:var(--on-brand);border-color:var(--brand);font-weight:700}
  .side a.on .n{background:rgba(255,255,255,.22);border-color:transparent;color:#fff}
  /* petunjuk ada item lagi di kanan */
  .sidewrap::after{content:'';position:absolute;top:0;right:0;bottom:0;width:34px;
    pointer-events:none;border-radius:0 var(--r-full) var(--r-full) 0;
    background:linear-gradient(to right,transparent,var(--surface-2));
    opacity:0;transition:opacity .2s}
  .sidewrap.bisa-geser::after{opacity:1}
  .sidewrap.di-ujung::after{opacity:0}
}

/* ---------- 18. Empty / loading ---------- */
.empty{text-align:center;padding:var(--s8) var(--s4);color:var(--fg-3)}
.empty .ico{font-size:2.6rem;margin-bottom:var(--s3);opacity:.55}
.empty h3{color:var(--fg-2);font-size:1.1rem}
.empty p{max-width:44ch;margin-inline:auto;font-size:.94rem}
.skel{background:linear-gradient(90deg,var(--line-2) 25%,var(--line) 37%,var(--line-2) 63%);
  background-size:400% 100%;animation:sh 1.4s ease infinite;border-radius:var(--r-sm)}
@keyframes sh{0%{background-position:100% 0}100%{background-position:0 0}}
.spin{width:17px;height:17px;border:2.5px solid currentColor;border-right-color:transparent;border-radius:50%;
  animation:sp .65s linear infinite;display:inline-block;vertical-align:-3px}
@keyframes sp{to{transform:rotate(360deg)}}

/* ---------- 19. Footer ---------- */
/* Footer. `margin-top` DIHAPUS dan padding ATAS dirapatkan ke --s4 (16px).
   Permintaan user 16 Okt 2026: "kok jarak header k tulisan jauh bgt ya bsa d
   rapihin gtu yar ga ad jeda jauh bgt sklian ma footer'y jga" lalu
   "yg footer msh jauh jrak'y bsa d dketin lg".

   Dulu `margin-top:var(--s7)` (48px) DOBEL dengan padding-bawah section, dan
   padding atas 48px menambah lagi -> jarak tulisan terakhir ke tulisan pertama
   footer = 121px (desktop) / 105px (HP).

   Sekarang: tanpa margin-top, padding atas 16px (cuma ruang napas, bukan jeda
   kosong). Jarak tulisan->tulisan jadi 48px (desktop) / 40px (HP) -- isinya
   murni padding-bawah section + 16px. Jangan pasang margin-top lagi. */
.ft{background:var(--band);color:color-mix(in srgb,var(--on-band) 76%,transparent);padding:var(--s4) 0 var(--s5)}
.ft h4{color:var(--on-band);font-size:1rem;font-family:'PJS';font-weight:700;margin-bottom:var(--s3)}
.ft a{color:color-mix(in srgb,var(--on-band) 76%,transparent);font-size:.92rem}
.ft a:hover{color:var(--on-band)}
.ft-grid{display:grid;gap:var(--s5)}
/* Nama perumahan di FOOTER: lambang + teks SEJAJAR di satu baris (flex, bukan hack
   vertical-align). Boleh turun 2 baris kalau sempit, tapi baris pertamanya rapi. */
.ft-brand{display:flex;align-items:center;gap:10px;margin-bottom:var(--s3);
  font-family:'Outfit',sans-serif;font-weight:800;color:var(--on-band)}
.ft-brand .brand-mark{width:36px;height:36px;border-radius:10px;
  box-shadow:0 1px 2px rgba(0,0,0,.06)}
.ft-brand .brand-name{min-width:0;white-space:normal;overflow-wrap:break-word;
  font-size:clamp(1rem,1.4vw,1.14rem);line-height:1.25;letter-spacing:-.015em;max-width:100%}
@media(min-width:768px){.ft-grid{grid-template-columns:1.6fr 1fr 1fr}}
.ft-bot{border-top:1px solid color-mix(in srgb,var(--on-band) 20%,transparent);margin-top:var(--s6);padding-top:var(--s4);
  display:flex;justify-content:space-between;gap:var(--s3);flex-wrap:wrap;font-size:.85rem}

/* ---------- 20. Auth ---------- */
.auth-wrap{max-width:440px;margin-inline:auto;padding:var(--s7) var(--s4) var(--s8)}
.auth-card{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);padding:var(--s6);box-shadow:var(--shadow-lg)}
.auth-card h1{font-size:1.7rem;margin-bottom:var(--s2)}
.demo-box{background:var(--warn-bg);border:1px solid color-mix(in srgb,var(--warn) 25%,transparent);border-radius:var(--r-sm);padding:var(--s4);margin-bottom:var(--s5)}
.demo-box b{color:var(--warn);display:block;margin-bottom:6px;font-size:.88rem}
.demo-box code{font-family:ui-monospace,monospace;font-size:.84rem;color:var(--fg);display:block;line-height:1.7}

/* ---------- 20b. Halaman masuk: gaya & logo (DIATUR ADMIN) ----------
   Permintaan user 16 Okt 2026: "bsa ga tampilan login'y d rubah sesuai ma yg
   d pilih admin" -> dipilih "Ketiganya (tulisan + desain + warna/logo)".

   Warna dipasang sebagai variabel LOKAL di `.auth-wrap` (inline style dari
   `login_warna_css()` di app.py), bukan mengubah `:root` — supaya halaman
   lain tidak ikut berubah warna. Semua nilai `--brand` pilihan sengaja GELAP
   supaya teks putih di atasnya (--on-brand) tetap terbaca di mode terang
   maupun gelap. */
/* Wadah logo: BULAT (bawaan situs). Dikembalikan 16 Okt 2026 setelah user
   minta "Balikin k sblum'y deh" (sebelumnya sempat dicoba jadi kotak lebar).
   Posisi: DI TENGAH (permintaan user 16 Okt 2026: "Logo taro d center").
   - display:flex (bukan inline-flex) + margin:0 auto -> kotak jadi
     block-level dan otomatis ke tengah induknya.
   - Cuma LOGO yang di tengah; judul & teks tetap rata kiri.
   Ukuran DIBESARKAN (permintaan user 16 Okt 2026: "Bsa d gedein logo'y"):
   wadah 72 -> 112px, logo di dalam 44 -> 84px (rasio tetap ~0,75). */
.auth-logo{display:flex;align-items:center;justify-content:center;
  width:112px;height:112px;border-radius:var(--r-full);background:var(--brand-soft);
  margin:0 auto var(--s4)}
.auth-logo img{width:84px;height:84px;object-fit:contain;border-radius:var(--r-full)}

/* gaya "pita": blok warna di samping (desktop) / di atas (HP) */
.auth-wrap.gaya-pita{max-width:880px;display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:var(--s6);align-items:stretch}
.auth-wrap.gaya-pita .auth-card{margin:0}
.auth-pita{background:var(--brand);color:var(--on-brand);border-radius:var(--r-lg);
  padding:var(--s7) var(--s6);display:flex;align-items:center;
  box-shadow:var(--shadow-lg)}
.auth-pita-in h1{color:var(--on-brand);font-size:1.9rem;margin:0 0 var(--s3)}
.auth-pita-in p{margin:0;opacity:.92;line-height:1.6}
.auth-pita .auth-logo{background:color-mix(in srgb,var(--on-brand) 18%,transparent)}

/* gaya "polos": tanpa kotak & bayangan */
.auth-wrap.gaya-polos .auth-card{background:transparent;border:0;box-shadow:none;
  padding:0}

/* Blok "Logo halaman masuk" di form Pengaturan.
   ⚠️ SENGAJA pakai nama class SENDIRI (`.logo-edit`), BUKAN
   `.pengurus-edit-foto`. Test `t_simpel` menghitung jumlah
   `class="pengurus-edit"` dan `class="pengurus-edit-foto"` — harus 6, satu
   per pengurus. Kalau class itu dipakai ulang di sini, hitungannya jadi 7 dan
   test gagal (kejadian 16 Okt 2026). Tampilannya dibuat sama: avatar kiri,
   tombol kanan. */
.logo-edit{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.logo-edit .ava-foto,.logo-edit .ava-inisial{width:56px;height:56px;font-size:1.2rem}
.logo-edit-aksi{display:flex;flex-direction:column;align-items:flex-start;gap:4px}

@media(max-width:767px){
  .auth-wrap.gaya-pita{grid-template-columns:minmax(0,1fr);gap:var(--s5)}
  .auth-pita{padding:var(--s6) var(--s5)}
  .auth-pita-in h1{font-size:1.6rem}
}

/* ---------- 21. Utilities ---------- */
.flex{display:flex;gap:var(--s3);align-items:center;flex-wrap:wrap}
.flex-b{display:flex;gap:var(--s3);align-items:center;justify-content:space-between;flex-wrap:wrap}
.mt0{margin-top:0}.mb0{margin-bottom:0}.mb4{margin-bottom:var(--s4)}.mb5{margin-bottom:var(--s5)}
.mt4{margin-top:var(--s4)}.mt5{margin-top:var(--s5)}.mt6{margin-top:var(--s6)}
.tc{text-align:center}.tr{text-align:right}
/* Rata kanan-kiri (justify). Dipakai pertama kali di halaman Profil: paragraf
   "Tentang lingkungan kami" (permintaan user 15 Okt 2026: "isi tentang
   lingkungan kami bisa d bkin justify").

   `hyphens:auto` DISERTAKAN tapi DIKETAHUI TIDAK BERPENGARUH di Chromium
   (diuji 15 Okt 2026: kata "mempertanggungjawabkan" dalam kotak 60px tidak
   terpenggal). Penyebabnya Chromium tidak punya kamus pemenggalan Bahasa
   Indonesia. Dibiarkan sebagai persiapan kalau browser/kamusnya sudah ada;
   JANGAN diandalkan untuk merapatkan celah antar kata.
   Celah terlebar terukur di HP (390px): 15px (baris normal ~4px). */
.tj{text-align:justify;hyphens:auto}
.hide{display:none!important}
.cols-2{display:grid;gap:var(--s5)}
@media(min-width:820px){.cols-2{grid-template-columns:1fr 1fr}.cols-2a{grid-template-columns:1.3fr .7fr}}
.sticky-side{position:sticky;top:88px}

/* ---------- 22. Reveal + reduced motion ---------- */
.rv{opacity:0;transform:translateY(14px)}
.rv.in{opacity:1;transform:none;transition:opacity .55s var(--ease),transform .55s var(--ease)}
@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}
  .rv{opacity:1;transform:none}
}

/* ---------- Percakapan keluhan (chat) ---------- */
.chatbox{border:1px solid var(--line);border-radius:var(--r);padding:14px;margin-bottom:14px;background:var(--surface-2)}
.chat{display:flex;flex-direction:column;gap:8px;max-height:340px;overflow-y:auto;
  padding:2px 2px 8px;scroll-behavior:smooth}
.chat-empty{padding:14px 0;text-align:center}
.bubble{max-width:82%;padding:10px 13px;border-radius:14px;position:relative;
  font-size:.9rem;line-height:1.5;overflow-wrap:anywhere}
/* ── Baris percakapan: nama & peran DI DALAM bubble ── */
.chat-row{display:flex;flex-direction:column;max-width:82%}
.chat-row.me{align-self:flex-end;align-items:flex-end}
.chat-row.them{align-self:flex-start;align-items:flex-start}
@media(max-width:420px){.chat-row{max-width:90%}}

/* meta di dalam bubble: nama + waktu */
.bubble .meta{display:flex;align-items:baseline;gap:6px;flex-wrap:wrap;
  font-size:.68rem;margin-top:5px}
.bubble .meta .who{font-weight:700}
.bubble .meta .jam{opacity:.75}
.bubble.peran-pengurus .meta{color:var(--ok)}
.bubble.peran-admin .meta{color:var(--accent)}
.bubble.peran-warga .meta{color:var(--fg-3)}
.bubble.peran-warga{background:var(--surface);color:var(--fg);border:1px solid var(--line);border-bottom-right-radius:5px}
.bubble.peran-pengurus{background:var(--ok-bg);color:var(--fg);border:1px solid var(--ok);border-bottom-left-radius:5px}
.bubble.peran-admin{background:var(--accent-soft);color:var(--fg);border:1px solid var(--accent);border-bottom-left-radius:5px}
.chat-form{display:flex;gap:8px;margin-top:10px}
.chat-form input{flex:1;min-width:0;padding:10px 13px;border-radius:var(--r-full);
  border:1px solid var(--line);background:var(--surface);color:var(--fg);font:inherit;font-size:.9rem}
.chat-form input:focus{outline:2px solid var(--brand);outline-offset:1px}
.chat-form .btn{flex:none}
@media(max-width:420px){
  .bubble{max-width:90%}
  .chat{max-height:280px}
  .chatbox{padding:12px}
}

/* ---------- 23.10 Laporan keluhan SELESAI: 2 kolom (14 Okt 2026) ----------
   Permintaan user: "laporan yang udah selesai dibikin per 2 kolom berdasarkan
   laporannya". Jadi tiap KARTU laporan = 1 sel grid (bukan 2 kolom di dalam
   satu kartu). Yang masih diproses tetap 1 kolom penuh -- itu yang masih
   butuh perhatian, jadi jangan dipecah.

   Default 1 kolom; 2 kolom mulai 820px (sama dengan ambang `.cols-2` di app ini).
   `align-items:stretch` (default grid, ditulis eksplisit) = semua kartu satu
   baris sama tinggi, ujungnya rata. Dulu `start` -> kartu pendek "melayang"
   di atas dan ujungnya tidak rata dengan kartu sebelah (permintaan user
   14 Okt 2026: "kotak laporan yang sudah selesai dibikin sama rata"). */
.keluhan-grid{display:grid;gap:14px;align-items:stretch}
@media(min-width:820px){.keluhan-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}
/* Di dalam grid, margin bawah kartu bikin jarak antar-baris tidak rata
   (kartu terakhir tiap baris tetap punya margin). Nol-kan; jarak diatur `gap`. */
.keluhan-grid>.chatbox{margin-bottom:0;display:flex;flex-direction:column}
/* .chat jadi flex:1 -> kena tinggi sisa kartu, jadi beberapa kartu dengan
   jumlah pesan berbeda tetap punya kotak chat setinggi sama. */
.keluhan-grid>.chatbox>.chat{flex:1 1 auto;min-height:0}
/* Kartu di grid jadi lebih sempit -> batasi tinggi chat supaya 2 kartu
   sebelah tidak saling mendorong terlalu jauh. */
@media(min-width:820px){.keluhan-grid .chat{max-height:260px}}
/* Penanda "ada isi di atas" kalau percakapan lebih panjang dari kotaknya.
   Tanpa ini, pesan yang kepotong kelihatan seperti bug (permintaan awal user
   ke-2 "biar enak dilihat"). Gradient pakai warna permukaan kartu supaya
   menyatu di mode terang maupun gelap (pakai color-mix, bukan warna tetap). */
.keluhan-grid .chat{background:
  linear-gradient(var(--surface-2) 30%,transparent) top/100% 14px no-repeat local,
  linear-gradient(transparent,var(--surface-2) 70%) bottom/100% 14px no-repeat local,
  radial-gradient(farthest-side at 50% 0,color-mix(in srgb,var(--fg) 22%,transparent),transparent) top/100% 7px no-repeat scroll,
  radial-gradient(farthest-side at 50% 100%,color-mix(in srgb,var(--fg) 22%,transparent),transparent) bottom/100% 7px no-repeat scroll}

/* ---------- 23.11 Halaman keluhan: 2 kolom SAMA LEBAR (14 Okt 2026) ----------
   Permintaan user: "yang buat laporan sama laporan saya ukurannya disamain
   biar enak dilihat".
   `.cols-2a` (1.3fr .7fr) dipakai 14 halaman lain, jadi TIDAK diubah global.
   Di halaman keluhan perbandingannya jadi 710px vs 382px -- terlalu timpang
   karena form "Buat laporan" tidak butuh selebar itu. Di sini dibuat 50/50.
   `align-items:start` tetap (dari .cols-2 / inline) supaya panel pendek tidak
   ditarik tinggi panel panjang -- itu justru bikin ruang kosong besar. */
@media(min-width:820px){.keluhan-cols{grid-template-columns:repeat(2,minmax(0,1fr))}}

/* ---------- 23.12 Sama TINGGI: "Buat laporan" vs "Laporan saya" (14 Okt 2026) ----------
   Setelah lebar disamakan (§23.11), tingginya masih beda jauh: form 850px vs
   panel daftar 250px -> ada ~600px ruang kosong di kanan bawah kolom.
   Permintaan user: "...ukurannya disamain biar enak dilihat".
   Fix: `align-items:stretch` (hapus override inline `align-items:start`) supaya
   kedua panel sama tinggi, dan `.panel-b` jadi kolom flex supaya isinya nempel
   di ATAS -- bukan tergantung di tengah ruang kosong.

   PENTING: `align-items:start` yang ada di template dihapus. Override inline
   menang atas CSS, jadi kelas baru tidak akan pernah kena. */
@media(min-width:820px){
  .keluhan-cols{align-items:stretch}
  .keluhan-cols>.panel{display:flex;flex-direction:column}
  .keluhan-cols>.panel>.panel-b{flex:1 1 auto;display:flex;flex-direction:column}
}

/* ---------- 23.14 Agenda: 2 kolom per STATUS (14 Okt 2026) ----------
   Permintaan user: "daftar agenda sebelah kiri khusus hanya untuk agenda yang
   belum selesai, begitu pun sebaliknya".
   Beda dengan grid kartu biasa (yang kartunya mengalir baris-per-baris):
   di sini tiap KOLOM adalah daftar tersendiri, jadi kolom kiri bisa berisi 3
   kartu dan kolom kanan 3 kartu tanpa saling mempengaruhi tinggi.

   `align-items:stretch` (default grid, ditulis eksplisit) -- kedua kolom sama
   tinggi. Isi tiap kolom tetap di ATAS (`.agenda-kol` flex kolom tanpa
   `justify-content`), jadi kolom yang isinya sedikit tidak punya kartu yang
   melayang di tengah. Kartu di kiri & kanan tetap sama lebar (423px). */
.agenda-2kolom{display:grid;gap:var(--s5);align-items:stretch}
@media(min-width:820px){.agenda-2kolom{grid-template-columns:repeat(2,minmax(0,1fr))}}
.agenda-kol{display:flex;flex-direction:column;gap:var(--s4);min-width:0}
/* Judul kolom: kecil, huruf kapital, ada garis bawah tipis sebagai pemisah
   supaya jelas ini dua daftar berbeda, bukan satu daftar panjang. */
.agenda-kol-h{display:flex;align-items:center;gap:var(--s2);
  font-size:.78rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase;
  color:var(--fg-3);padding-bottom:10px;border-bottom:1px solid var(--line-2)}
/* Kartu di dalam kolom: jarak sudah dari `gap` kolom, jadi margin bawah
   bawaan (mb4) harus nol supaya jarak antar-kartu rata. */
.agenda-kol>.card{margin-bottom:0}
/* Isi kartu jadi kolom flex supaya baris tombol nempel DASAR kartu -- judul
   1 baris vs 2 baris tetap bikin tombol sejajar. */
.agenda-kol>.card{display:flex;flex-direction:column}
.agenda-kol>.card>.flex.mt4{margin-top:auto;padding-top:var(--s4)}

/* ---------- Legenda peran akun ---------- */
.role-legend{display:flex;flex-wrap:wrap;gap:8px 18px;margin-top:12px;
  padding:12px 14px;border:1px solid var(--line);border-radius:var(--r-sm);
  background:var(--surface-2)}
.role-legend-i{display:inline-flex;align-items:center;gap:8px;font-size:.83rem;color:var(--fg-2)}
/* Keterangan warna kolom "Pernah masuk?" (permintaan user 16 Okt 2026:
   "sama kasih keterangan berapa bulannya"). Warna badge-nya SAMA dengan
   yang di tabel, jadi admin tinggal mencocokkan. Judulnya satu baris penuh
   di atas daftarnya supaya jelas ini keterangan kolom yang mana. */
.legend-masuk{margin-top:10px;row-gap:10px}
.legend-judul{flex:1 1 100%;font-size:.8rem;font-weight:600;color:var(--fg-2)}
/* Badge contoh dipadatkan (bukan seukuran badge di tabel) supaya legendanya
   tidak makan tempat, tapi warnanya tetap sama -> tinggal dicocokkan. */
.legend-masuk .badge{font-size:.68rem;padding:3px 7px}
@media(max-width:560px){ .role-legend{flex-direction:column;gap:8px}
  .legend-judul{margin-bottom:4px} }

/* ---------- Peran akun (badge): warga polos, pengurus hijau, admin oranye ---------- */
.badge-role-warga{background:var(--badge-muted-bg);color:var(--badge-muted-fg);
  border:1px solid color-mix(in srgb,var(--badge-muted-fg) 26%,transparent)}
.badge-role-pengurus{background:var(--badge-ok-bg);color:var(--badge-ok-fg);
  border:1px solid color-mix(in srgb,var(--badge-ok-fg) 26%,transparent)}
.badge-role-admin{background:var(--badge-accent-bg);color:var(--badge-accent-fg);
  border:1px solid color-mix(in srgb,var(--badge-accent-fg) 26%,transparent)}

/* ---------- Iuran: daftar bulan tertunggak ---------- */
.tunggak-box{border:1px solid var(--line);border-radius:var(--r-sm);
  background:var(--surface-2);padding:12px 14px}
.tunggak-head{display:flex;align-items:center;justify-content:space-between;
  gap:10px;flex-wrap:wrap}
.tunggak-total{font-weight:800;font-size:.95rem;color:var(--err)}
.tunggak-list{display:grid;gap:8px}
.tunggak-item{display:grid;grid-template-columns:minmax(0,1fr) auto auto;
  align-items:center;gap:10px;width:100%;text-align:left;cursor:pointer;
  padding:11px 13px;border:1px solid var(--line);border-radius:var(--r-sm);
  background:var(--surface);font:inherit;color:inherit;
  transition:border-color .15s,background .15s}
.tunggak-item:hover{border-color:var(--ok);background:var(--surface-2)}
.tunggak-item.on{border-color:var(--ok);box-shadow:0 0 0 2px var(--ok-bg,rgba(47,125,85,.15))}
.tunggak-nama{font-weight:700;min-width:0;overflow-wrap:anywhere}
.tunggak-nom{font-variant-numeric:tabular-nums;white-space:nowrap;color:var(--ink-2)}
@media(max-width:420px){
  .tunggak-item{grid-template-columns:minmax(0,1fr) auto;gap:6px 10px}
  .tunggak-item .badge{grid-column:2;justify-self:end}
  .tunggak-nama{font-size:.92rem}
}

/* ---------- Foto pengurus (avatar) ----------
   Dipakai di 2 tempat:
     /profil            -> .pengurus-baris .pengurus-id  (yang dilihat warga)
     /panel/pengaturan  -> .pengurus-edit                (form admin)
   Foto kosong SELALU diganti lingkaran inisial (helper inisial_nama()) --
   jangan biarkan baris tanpa foto jadi kosong tanpa penanda. */
.ava-foto,.ava-inisial{width:44px;height:44px;border-radius:var(--r-full);
  flex:none;object-fit:cover;display:inline-flex;align-items:center;
  justify-content:center;font-family:'Outfit','PJS',sans-serif;font-weight:700;
  font-size:.95rem;letter-spacing:.02em;user-select:none}
.ava-foto{border:1px solid var(--line)}
.ava-inisial{background:var(--brand-soft);color:var(--brand-ink,var(--brand));
  border:1px dashed var(--line-2)}
.pengurus-id{display:inline-flex;align-items:center;gap:10px;min-width:0}
.pengurus-id .strong{min-width:0;overflow-wrap:anywhere}
/* Nama + jabatan bertumpuk: nama di atas, jabatan di bawahnya.
   Permintaan user 16 Okt 2026: "nama jabatan'y d taro d bawah nama pengurus
   tulisan'y d pasin aj ga blh lebh gede dri ukuran font nama pengurus".
   UKURAN JABATAN = 1rem = SAMA dengan nama (16px), JANGAN lebih besar.
   Nama tetap di kiri (tidak digeser) supaya posisinya seperti sebelumnya;
   warna & bobotnya lebih ringan supaya nama tetap jadi fokus utama. */
.pengurus-teks{display:inline-flex;flex-direction:column;gap:1px;min-width:0}
.pengurus-jabatan{font-size:1rem;line-height:1.25;font-weight:500;
  color:var(--fg-2);overflow-wrap:anywhere}
/* Badge email pengurus (permintaan user 16 Okt 2026: "bsa ga d ksh almat email
   yar enak d liat"). Badge biasa `white-space:nowrap`, jadi alamat email yang
   panjang (mis. bambang.suryanto@gmail.com) akan MELUBER keluar kartu di HP.
   Di sini nowrap dilepas + `overflow-wrap:anywhere` supaya alamatnya membungkus
   rapi, dan `max-width:100%` supaya tidak melebihi kartu. Ikon surat tetap di
   kiri (flex), teks rata kiri saat membungkus. */
.badge-email{white-space:normal;overflow-wrap:anywhere;max-width:100%;
  text-align:start;line-height:1.35}
.pengurus-edit{display:grid;grid-template-columns:auto minmax(0,1fr);gap:14px;
  align-items:start;padding:12px;border:1px solid var(--line);
  border-radius:var(--r-sm);background:var(--surface-2)}
.pengurus-edit-foto{display:flex;flex-direction:column;align-items:center;gap:8px;width:132px}
.pengurus-edit-foto .ava-foto,.pengurus-edit-foto .ava-inisial{width:76px;height:76px;font-size:1.5rem}
.pengurus-edit-fotoaksi{display:flex;flex-direction:column;align-items:center;gap:4px;
  width:100%;text-align:center}
.pengurus-edit-fotoaksi .btn{width:100%;justify-content:center}
@media(max-width:560px){
  .pengurus-edit{grid-template-columns:minmax(0,1fr)}
  .pengurus-edit-foto{flex-direction:row;width:auto;align-items:center;gap:12px;flex-wrap:wrap}
  .pengurus-edit-fotoaksi{width:auto;align-items:flex-start;text-align:left}
  .pengurus-edit-fotoaksi .btn{width:auto}
}

/* ---------- Ambil foto (kamera / galeri): dinamis ---------- */
.foto-pilih{display:grid;gap:8px}
.foto-btn-row{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}
.foto-btn-row .btn{display:inline-flex;align-items:center;justify-content:center;
  gap:6px;width:100%;min-width:0;padding:11px 10px;line-height:1.2;text-align:center;
  overflow-wrap:anywhere;hyphens:auto}
/* nama file: potong rapi, jangan bikin baris melebar */
.foto-nama{display:block;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* preview: grid supaya rapi di lebar mana pun */
.foto-preview{display:grid;grid-template-columns:auto minmax(0,1fr) auto;
  align-items:center;gap:12px;margin-top:10px;padding:10px;
  border:1px solid var(--line);border-radius:var(--r-sm);background:var(--surface-2)}
.foto-preview img{width:clamp(56px,14vw,92px);height:clamp(56px,14vw,92px);
  object-fit:cover;border-radius:var(--r-sm);border:1px solid var(--line);flex:none}
.foto-info{display:flex;flex-direction:column;gap:2px;min-width:0}
.foto-ok{font-size:.82rem;font-weight:700;color:var(--ok)}
.foto-nama2{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.foto-hapus{flex:none;white-space:nowrap}
@media(max-width:420px){
  .foto-btn-row{grid-template-columns:repeat(2,minmax(0,1fr))}
  .foto-btn-row .btn{padding:12px 6px;font-size:.82rem}
  .foto-preview{grid-template-columns:auto minmax(0,1fr);grid-template-areas:'img info' 'img hapus';
    gap:8px 10px}
  .foto-preview img{grid-area:img;align-self:start}
  .foto-info{grid-area:info}
  .foto-hapus{grid-area:hapus;justify-self:start}
}
@media(max-width:340px){
  .foto-btn-row{grid-template-columns:minmax(0,1fr)}
}

/* ---------- Laporan ditutup (selesai): chat read-only ---------- */
.chat-tutup{display:flex;flex-direction:column;gap:2px;margin-top:10px;
  padding:11px 14px;border:1px dashed var(--line-2);border-radius:var(--r-sm);
  background:var(--surface-2);font-size:.85rem;color:var(--fg-2);text-align:center}
.chat-tutup b{color:var(--fg)}

/* ---------- Mention (@nama) ---------- */
.mention-box{position:absolute;bottom:calc(100% + 6px);left:0;z-index:120;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-sm);
  box-shadow:0 10px 28px rgba(8,14,11,.16);overflow:hidden;max-height:230px;overflow-y:auto;
  width:min(320px,calc(100% - 60px))}
.mention-item{display:flex;align-items:center;gap:8px;width:100%;text-align:left;
  padding:9px 12px;border:0;background:none;font:inherit;font-size:.86rem;color:var(--fg);cursor:pointer}
.mention-item:hover,.mention-item:focus-visible{background:var(--brand-soft);outline:none}
.mention-item .peran{margin-left:auto;font-size:.66rem;font-weight:700;padding:1px 7px;
  border-radius:99px;background:var(--line-2);color:var(--fg-2);text-transform:uppercase;letter-spacing:.02em}
.mention-item .peran.peran-pengurus{background:var(--ok-bg);color:var(--ok)}
.mention-item .peran.peran-admin{background:var(--accent-soft);color:var(--accent)}
.chat-form{position:relative}

/* tag @nama di dalam teks bubble */
.mention{font-weight:700;color:var(--brand);background:var(--brand-soft);
  padding:0 4px;border-radius:5px}
.bubble.peran-pengurus .mention{color:var(--ok);background:rgba(255,255,255,.55)}
.bubble.peran-admin .mention{color:var(--accent);background:rgba(255,255,255,.55)}
@media(max-width:420px){.mention-box{width:calc(100% - 56px)}}

/* ---------- Info rekening iuran (icon + nilai) ---------- */
.rek-box{display:flex;flex-direction:column;gap:8px;
  background:var(--brand-soft);border-radius:var(--r);padding:12px 14px}
.rek-item{display:flex;align-items:center;gap:10px;min-width:0}
.rek-ico{width:19px;height:19px;flex:none;color:var(--brand);opacity:.9}
.rek-val{font-size:.92rem;font-weight:700;color:var(--fg);min-width:0;
  overflow-wrap:anywhere}
.rek-val.num{letter-spacing:.01em}
.rek-salin{margin-left:auto;flex:none;display:inline-flex;align-items:center;
  justify-content:center;width:32px;height:32px;padding:0;cursor:pointer;
  border:1px solid var(--line);border-radius:var(--r-full);
  background:var(--surface);color:var(--fg-2);
  transition:color .15s,border-color .15s,background .15s}
.rek-salin svg{width:15px;height:15px}
.rek-salin:hover{color:var(--brand);border-color:var(--brand);
  background:var(--surface-2)}
.rek-salin:active{transform:scale(.94)}
@media(max-width:400px){
  .rek-box{padding:11px 12px;gap:7px}
  .rek-ico{width:18px;height:18px}
  .rek-val{font-size:.88rem}
}

/* ---------- Pratinjau info rekening (panel Pengaturan) ---------- */
.rek-val.rek-kosong{font-weight:600;color:var(--fg-2);opacity:.75}
.rek-salin:disabled{opacity:.45;cursor:default}
.rek-salin:disabled:hover{color:var(--fg-2);border-color:var(--line);background:var(--surface)}

/* ---------- Data warga: aksi di bawah nama ---------- */
/* min-width tabel 620px + kolom Nama 330px bikin kolom kanan terpotong.
   perksip kolom lain supaya "Telegram" & "Status" tetap kelihatan. */
/* table-layout:AUTO (permintaan 16 Okt 2026: "bkinin dinamis ikutin nama yg
   trpanjang"). Sebelumnya `fixed` + lebar px per kolom -> lebar HARUS dihitung
   manual tiap kali ada data baru yang lebih panjang, dan salah hitung bikin
   teks pecah/kepotong. Dengan `auto`, browser mengukur sendiri: kolom otomatis
   selebar ISI TERPANJANG di kolom itu. Tidak ada lagi angka px yang perlu
   dijaga — tabel menyesuaikan sendiri saat ada nama/email/NIK lebih panjang.
   Syaratnya: sel yang tidak boleh pecah diberi `white-space:nowrap`
   (lihat aturan kolom 1-3 di bawah) supaya min-content = lebar teks penuh. */
table.tbl.warga{min-width:0;table-layout:auto;width:100%}
/* Padding dirapatkan & isi sel RATA TENGAH vertikal (permintaan 16 Okt 2026:
   "d rapihin tabel'y yar realitis"). Sebelumnya `vertical-align:top` +
   padding 9px membuat sel yang isinya cuma 1 badge menyisakan ~70px kosong
   di bawah -> baris terlihat bolong & tidak seimbang. Dengan middle, tiap
   isi sel duduk sejajar di tengah barisnya. */
/* Padding horizontal 8px (bukan 9px): tabel cuma 918px & 8 kolom, jadi
   tiap px berarti. Dengan 8px, kebutuhan lebar tiap kolom turun 2px. */
/* Padding 7px (bukan 8px): tabel hanya 918px, dengan auto-layout tiap px
   membantu semua kolom kebagian. */
table.tbl.warga th,table.tbl.warga td{padding:7px 6px}
/* SEMUA isi sel & judul RATA TENGAH (permintaan 16 Okt 2026: "smua isi tabel
   d bkin center"). Sebelumnya cuma kolom angka/badge yang `t-c`; sekarang
   seragam -> kolom kelihatan lurus & seimbang. */
table.tbl.warga th,table.tbl.warga td{text-align:center}
table.tbl.warga td{vertical-align:middle}
table.tbl.warga td.small{font-size:.85rem}
/* Lebar kolom (total 918px) — dirapikan 16 Okt 2026 ("d rapihin tabel'y yar
   realitis"). Sebelumnya: 180+150+76+140+96+96+84+96.
   Yang diperbaiki: kolom 3 "Rumah" (76px) TERLALU SEMPIT -> "Blok A / 02"
   & "RT 04 RW 07" masing-masing pecah 2 baris -> sel jadi 97px & baris
   tabel melar 106px. Kolom 2 dilebarkan dikit supaya email utuh.
   Lebar diambil dari kolom yang masih longgar (Nama, Peran, Status).
   Sekarang: 148+182+98+100+100+108+84+98 = 918.
   ⚠️ Kalau menambah kolom lagi, SEMUA nth-child di bawah WAJIB digeser &
   totalnya tetap 918 — kalau tidak, lebarnya bergeser (kolom terakhir
   tidak punya lebar -> browser menekan kolom Nama sampai separuh).
   ⚠️ Ukuran tiap kolom harus muat JUDUL + panah sortir (8px kiri + teks +
   16px kanan). "TELEGRAM" butuh ~93px, makanya kolom 5 = 96px.
   ⚠️ Kolom 3 minimal 96px: "RT 04 RW 07" (~77px) harus muat SATU baris.
   ⚠️ Ukur dulu, JANGAN kira-kira. Cara ukur yang benar = `canvas`
   measureText dengan font hasil `getComputedStyle(el).font`, lalu +18px
   padding. Hasil ukur 16 Okt 2026 (font saat itu):
     nama 127px · NIK 155px · hp 118px · email 169px · blok 68 · RT 81
     badge: kol4 98 · kol5 62 · kol6 105 · kol7 67 · kol8 43
   Dari situ kebutuhan tiap kolom = max(teks, badge, judul+29) + 18.
   ⚠️ Kolom 1: NIK 16 digit adalah penentu (155px @ 0.85rem) — LEBIH lebar
   dari nama terpanjang (127px). Karena NIK cuma info tambahan, fontnya
   dikecilkan ke 0.72rem (≈131px) supaya nama+NIK muat SATU baris tanpa
   kolom jadi gemuk. Kalau dinaikkan lagi, NIK akan pecah 2 baris.
   ⚠️ Kolom 2 minimal 186px: email terpanjang "bayu.pamungkas@warga.id"
   butuh 169px @0.8rem. Karena email = info sekunder, fontnya diturunkan
   ke 0.78rem (≈165px) supaya ada sisa aman. Kalau kolomnya sempit, email
   pecah dan menyisakan 1-2 huruf sendirian ("...warga.i" + "d").
   ⚠️ KEBUTUHAN MINIMUM tiap kolom (hasil ukur canvas 16 Okt 2026, dengan
   padding sel 8px). JANGAN diturunkan di bawah ini — teks/badge akan
   pecah baris atau terpotong:
     1 = 147 (NIK 16 digit @.72rem)    5 =  99 (badge "Tersambung")
     2 = 181 (email terpanjang @.78rem) 6 = 107 (badge "Belum pernah")
     3 =  97 ("RT 04 RW 07")           7 =  82 (badge "Pengurus")
     4 =  99 (badge "Belum bayar")     8 =  97 (badge "Menunggu")
   ⚠️ Badge di kolom 4-8 DIPADATKAN ke .68rem/3px 7px (lihat aturan di
   bawah) — dengan gaya itu "Belum pernah" 91px, "Belum bayar" 83px,
   "Tersambung" 83px, "Pengurus" 66px, "Menunggu" 81px.
   ⚠️ Kalau font badge dinaikkan lagi (mis. balik ke .74rem/5px 11px),
   "Belum bayar" jadi 98px & "Pengurus" terpotong jadi "Pengur..." —
   lebar kolom 4/5/6/7/8 WAJIB dihitung ulang kalau begitu.
   ⚠️ Total WAJIB tetap 918px (lebar tabel). Kalau perlu lebih, ambil dari
   kolom yang punya sisa — jangan lupa perbarui angka di komentar ini.
   ⚠️ Kolom 1 minimal 152px: NAMA & NIK (16 digit ≈ 119px, dengan spasi
   ≈ 127px) harus muat SATU baris. Kalau sempit, NIK pecah 2 baris dan
   baris tabel melar 106px (cuma 3 warga yang kena, jadi kelihatan pincang).
   Cara ukur kebutuhan kolom: klon sel ke wadah lebar-tak-terbatas lalu baca
   `getBoundingClientRect().width` (= lebar alami tanpa pembungkusan). */
/* ══════════ LEBAR KOLOM: DINAMIS (tidak ada px hard-code) ══════════
   Permintaan 16 Okt 2026: *"bkinin dinamis ikutin nama yg trpanjang"*.
   Dulu lebar dikunci px (`width:182px` dst) dan HARUS dihitung ulang tiap
   ada data lebih panjang — salah hitung = teks pecah/kepotong (kejadian:
   email "bayu.pamungkas@warga.id", badge "Pengurus" kepotong).
   Sekarang: `table-layout:auto` + `white-space:nowrap` pada kolom teks,
   jadi browser otomatis memakai lebar ISI TERPANJANG. Nama/email/NIK
   sepanjang apa pun tetap utuh tanpa perlu menyentuh CSS lagi.
   ⚠️ JANGAN kembalikan `table-layout:fixed` atau `width:<px>` per kolom —
   itu mematikan sifat dinamis ini.
   ⚠️ Kolom 1-3 WAJIB nowrap: kalau boleh pindah baris, min-content-nya
   mengecil dan browser akan memecah email/NIK jadi 2 baris lagi.
   Estimasi min-width saat ini (nowrap + padding 14px):
     1=145 (NIK 131) · 2=179 (email 165) · 3=95 ("RT 04 RW 07" 81)
     4=97 · 5=97 · 6=105 · 7=80 · 8=95  ->  total 893px (muat 918) ✅ */
table.tbl.warga .warga-nama,
table.tbl.warga td:nth-child(2),
table.tbl.warga td:nth-child(3){white-space:nowrap}
/* Nama panjang: jangan sampai kolom Nama mendominasi & menekan kolom lain.
   Dipecah hanya kalau memang tidak muat (nama Indonesia jarang >20 huruf). */
/* ⚠️ `normal` DIHAPUS: bikin nama pecah 2 baris & mematikan min-content
   kolom. Sekarang ikut nowrap dari grup di atas.
   ⚠️ Selector harus `>.strong` (SPAN), BUKAN `>strong` (elemen) — HTML-nya
   `<span class="strong">`, jadi `>strong` tidak pernah cocok. */
table.tbl.warga .warga-nama>.strong{white-space:nowrap}
/* Badge di kolom 4-8 DIPADATKAN (0.68rem / 3px 7px) supaya semua nilai
   muat 1 baris & tidak terpotong: "Belum pernah" 91px, "Belum bayar" 84px,
   "Tersambung" 83px. Sebelumnya .74rem/5px 11px -> "Pengurus" kepotong
   jadi "Pengur..." dan badge turun baris (baris tabel melar 106px). */
table.tbl.warga td:nth-child(4) .badge,
table.tbl.warga td:nth-child(5) .badge,
table.tbl.warga td:nth-child(6) .badge,
table.tbl.warga td:nth-child(7) .badge,
table.tbl.warga td:nth-child(8) .badge{font-size:.68rem;padding:3px 7px}
/* Kolom 4 & 5 punya 2 badge bertumpuk -> rapatkan jaraknya supaya tidak
   menambah tinggi baris. */
table.tbl.warga td:nth-child(4) .badge + .badge{margin-top:2px}
/* NIK: dikecilkan sedikit dari nama (info tambahan) TAPI tidak sekecil dulu
   (.72rem) — sejak lebar kolom dinamis, NIK tidak lagi perlu dipaksa muat;
   kolomnya ikut melebar sendiri. 16 digit @.75rem ≈ 115px. */
table.tbl.warga .warga-nama>.muted{font-size:.75rem;letter-spacing:0;
  white-space:nowrap}
/* Judul kolom BOLEH turun baris: "PERNAH MASUK?" tidak muat 1 baris di
   kolomnya, dan karena tabel `table-layout:auto`, judul yang nowrap akan
   MEMAKSA kolom jadi lebar & menekan kolom lain. Dengan normal, min-content
   kolom = kata terpanjang saja ("PERNAH" / "MASUK?"), bukan seluruh judul. */
table.tbl.warga th.th-urut > a{padding:9px 16px 9px 8px;line-height:1.25;
  white-space:normal}
/* Semua judul kolom center (permintaan "smua isi tabel d bkin center"),
   bukan cuma yang `t-c`. Padding kiri=kanan supaya teks benar-benar di
   tengah; ruang panah tetap disisakan di kanan. */
table.tbl.warga th.th-urut > a{text-align:center;justify-content:center;
  padding-left:10px;padding-right:10px}
/* ⚠️ PITFALL: selector harus `.strong` (KELAS di <span class="strong">),
   bukan `strong` (elemen). Sebelum 16 Okt 2026 semua aturan di sini pakai
   `>strong` sehingga TIDAK PERNAH COCOK -> display:block & ukuran font di
   media query tidak pernah jalan. Sudah diperbaiki semua. */
table.tbl.warga .warga-nama>.strong{display:block}
/* Peran & Status jadi 2 kolom terpisah: badge-nya dibungkus biar
   tidak meluber, kolom tetap ramping tapi angkanya pas satu baris. */
/* ⚠️ Ellipsis DIHAPUS (dulu `overflow:hidden;text-overflow:ellipsis`).
   Penyebab badge "Pengurus" terpotong jadi "Pengur..." saat kolomnya
   sempit. Sejak lebar kolom dinamis, badge tidak perlu dipotong — kolomnya
   yang menyesuaikan. Jangan pasang ellipsis lagi di sini. */
table.tbl.warga .warga-peran,table.tbl.warga .warga-status{
  white-space:nowrap}
table.tbl.warga .warga-peran>.badge,
table.tbl.warga .warga-status>.badge{font-size:.68rem;padding:3px 7px;
  display:inline-block;vertical-align:middle}
/* Padding kolom 6 & 7 dulu dirapatkan (9px 6px) karena kolomnya sempit.
   Sekarang dihapus: kolom 6 "Pernah masuk?" butuh lebar untuk judulnya, dan
   padding yang beda sendiri bikin kolom terlihat tidak sejajar. Semua sel
   pakai padding yang sama (7px 9px) -> ritme baris seragam. */
/* Email & NIK: pindah baris di @ atau /, bukan di tengah kata.
   ⚠️ `overflow-wrap:anywhere` dari aturan umum MEMOTONG di tengah kata
   ("pengurus@peruhan.i" + "d" sendirian di baris bawah) — kelihatan tidak
   rapi. Di kolom ini pakai `break-word` saja: hanya memotong kalau memang
   tidak ada pilihan lain, jadi kata utuh sedapat mungkin dipertahankan. */
/* Email: font kembali ke .8rem (dulu dipaksa .78rem demi muat kolom 182px).
   Dengan lebar dinamis, kolomnya melebar sendiri mengikuti email terpanjang
   -> email tetap SATU baris tanpa dikorbankan ukurannya. */
table.tbl.warga td:nth-child(2){font-size:.8rem}
table.tbl.warga td:nth-child(2) .num,
table.tbl.warga td:nth-child(2) .muted{white-space:nowrap;
  overflow-wrap:normal;word-break:normal;hyphens:none}
table.tbl.warga .warga-nama>.strong{display:block}

/* ---------- Aksi: satu baris PENUH dari Nama s/d Peran/Status ----------
   Tombol ditaruh di <tr> sendiri dengan <td colspan=6>, jadi lebarnya
   selebar tabel (bukan sempit di kolom Nama) -> tombol lega & pasti
   satu baris, sementara baris data tetap pendek. */
table.tbl.warga tbody tr:not(.warga-aksi)>td{border-bottom:0}
/* Baris AKSI (tombol) diberi warna sendiri yang JAUH lebih pucat dari
   zebra, supaya jelas "baris tambahan" tanpa bersaing dengan baris data. */
tr.warga-aksi>td{padding:5px 9px 8px;background:var(--row-aksi);
  border-bottom:1px solid var(--line-2);vertical-align:middle}
/* Tombol aksi: RATA PENUH + jarak antar tombol seragam (permintaan user
   16 Okt 2026: *"tombol aksi tuk jarak & jarak antar tombol d sama ratakan"*).
   Sebelumnya cuma `gap:6px` + rata kiri -> 4 tombol menumpuk di kiri dan
   sisa ~480px kosong di kanan (terukur). Sekarang tiap tombol MELEBAR sama
   rata mengisi penuh lebar baris, jadi tidak ada jeda kosong & jaraknya
   selalu sama (gap 8px). `flex-basis:150px` bikin tombol otomatis turun
   baris kalau memang tidak muat (layar sempit), bukan gepeng. */
tr.warga-aksi>td>div{display:flex;flex-wrap:wrap;gap:8px;align-items:stretch}
tr.warga-aksi>td>div>.btn{flex:1 1 150px;min-width:0;justify-content:center;
  text-align:center}
tr.warga-aksi .btn,tr.warga-aksi .btn-sm{padding:6px 12px;font-size:.79rem;
  line-height:1.2;border-radius:8px;white-space:nowrap}
tr.warga-aksi>td>div:empty{display:none}
tr.warga-aksi>td:empty{display:none}

/* ---------- Data warga: RESPONSIF ----------
   Di bawah 720px tabel tidak lagi tabel: tiap baris data jadi KARTU
   (nama di atas, detail di bawahnya) dan baris aksi jadi deretan tombol
   penuh. Alasannya: 7 kolom tidak mungkin muat di 360px -> kolom nama
   terjepit 0px & baris jadi 300px. Tablets 720-979px tetap tabel tapi
   kolom dirapatkan & font diperkecil. */
@media(max-width:979px){
  /* Tetap `auto` (bukan `fixed`) supaya sifat dinamis bertahan di tablet;
     kalau konten lebih lebar dari layar, `.tbl-scroll` yang menggeser. */
  table.tbl.warga{table-layout:auto;min-width:0}
  table.tbl.warga .warga-nama{width:auto;min-width:0}
  table.tbl.warga th,table.tbl.warga td{padding:8px 6px}
  table.tbl.warga .warga-nama>.strong{font-size:.95rem}
  table.tbl.warga td.small,table.tbl.warga td.num{font-size:.78rem}
  table.tbl.warga .warga-peran>.badge,
  table.tbl.warga .warga-status>.badge{font-size:.64rem;padding:3px 5px}
  tr.warga-aksi>td{padding:7px 7px 10px}
  tr.warga-aksi .btn,tr.warga-aksi .btn-sm{padding:8px 12px;font-size:.76rem}
}

@media(max-width:719px){
  /* 1. Tabel -> blok (kartu) */
  table.tbl.warga,
  table.tbl.warga tbody,
  table.tbl.warga tbody tr,
  table.tbl.warga tbody tr:not(.warga-aksi),
  table.tbl.warga td{display:block}
  table.tbl.warga thead{position:absolute;width:1px;height:1px;
    overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
  table.tbl.warga{table-layout:auto;min-width:0}
  /* Batalkan lebar kolom tetap (specificity 0,3,2). `:nth-child(n)`
     memberi specificity sama sehingga aturan ini (di akhir file) menang. */
  table.tbl.warga th:nth-child(n),
  table.tbl.warga td:nth-child(n),
  table.tbl.warga .warga-nama{width:auto;min-width:0}

  /* 2. Baris data = KARTU (grid 2 kolom: padat, tidak 7 baris) */
  table.tbl.warga tbody tr:not(.warga-aksi){
    display:grid;grid-template-columns:1fr 1fr;gap:4px 10px;
    background:var(--surface);border:1px solid var(--line);
    border-radius:14px;margin:0 0 6px;padding:10px 10px 11px;
    box-sizing:border-box;box-shadow:0 1px 2px rgba(0,0,0,.04)}
  table.tbl.warga tbody tr:not(.warga-aksi)>td{
    padding:0;border-bottom:0;display:block;
    min-width:0;max-width:100%;
    overflow-wrap:break-word;word-break:normal}
  table.tbl.warga tbody tr:not(.warga-aksi)>td *{
    min-width:0;max-width:100%;overflow-wrap:break-word;word-break:normal}

  /* 3. Posisi tiap sel (kolom ke-1..7 = Nama, HP/Email, Rumah, Iuran,
     Telegram, Peran, Status) supaya padat 4 baris tanpa sel orphan. */
  table.tbl.warga tbody tr:not(.warga-aksi)>td:nth-child(1){grid-area:1/1}
  table.tbl.warga tbody tr:not(.warga-aksi)>td:nth-child(7){grid-area:1/2}
  table.tbl.warga tbody tr:not(.warga-aksi)>td:nth-child(2){grid-area:2/1/2/3}
  table.tbl.warga tbody tr:not(.warga-aksi)>td:nth-child(3){grid-area:3/1}
  table.tbl.warga tbody tr:not(.warga-aksi)>td:nth-child(4){grid-area:3/2}
  table.tbl.warga tbody tr:not(.warga-aksi)>td:nth-child(5){grid-area:4/1}
  table.tbl.warga tbody tr:not(.warga-aksi)>td:nth-child(6){grid-area:4/2}

  table.tbl.warga tbody tr:not(.warga-aksi)>td.warga-nama{padding-bottom:0}
  table.tbl.warga td.warga-nama::before{display:none}
  table.tbl.warga td.warga-nama>.strong{font-size:1.03rem;line-height:1.3}
  table.tbl.warga td.warga-nama .small.muted.num{font-size:.75rem;margin-top:1px}
  /* Status di kanan atas, sejajar nama */
  table.tbl.warga tbody tr:not(.warga-aksi)>td.warga-status{
    text-align:right;padding-top:2px}

  /* 4. Field pendek: label DI ATAS nilai (bukan di samping) supaya
     tidak berebut ruang di lebar setengah kartu. */
  table.tbl.warga tbody tr:not(.warga-aksi)>td[data-label]{
    font-size:.85rem;line-height:1.4}
  table.tbl.warga tbody tr:not(.warga-aksi)>td[data-label]::before{
    content:attr(data-label);display:block;
    font-size:.66rem;font-weight:600;letter-spacing:.05em;
    text-transform:uppercase;color:var(--fg-3);margin-bottom:3px}
  table.tbl.warga td.warga-peran,table.tbl.warga td.warga-status{
    white-space:normal;overflow:visible;text-overflow:clip}

  /* 5. Baris aksi: tombol besar penuh, membungkus rapi */
  tr.warga-aksi>td{
    display:block;width:auto;background:transparent;
    border-bottom:0;padding:0 0 14px}
  tr.warga-aksi>td>div{gap:7px}
  tr.warga-aksi .btn,tr.warga-aksi .btn-sm{
    flex:1 1 auto;min-width:calc(50% - 4px);
    padding:11px 10px;font-size:.8rem;text-align:center}
  /* Tombol bahaya/peng deserved full baris */
  tr.warga-aksi .btn-primary{flex:1 1 100%}
}

/* HP sangat kecil: 1 kolom (narrow) supaya tidak sesak & tidak geser */
@media(max-width:379px){
  table.tbl.warga tbody tr:not(.warga-aksi){grid-template-columns:1fr}
  table.tbl.warga tbody tr:not(.warga-aksi)>td:nth-child(3),
  table.tbl.warga tbody tr:not(.warga-aksi)>td:nth-child(4),
  table.tbl.warga tbody tr:not(.warga-aksi)>td:nth-child(5),
  table.tbl.warga tbody tr:not(.warga-aksi)>td:nth-child(6){grid-area:auto}
  table.tbl.warga tbody tr:not(.warga-aksi)>td:nth-child(1){grid-area:auto}
  table.tbl.warga tbody tr:not(.warga-aksi)>td:nth-child(2){grid-area:auto}
  table.tbl.warga tbody tr:not(.warga-aksi)>td.warga-status{text-align:left}
  tr.warga-aksi .btn,tr.warga-aksi .btn-sm{min-width:100%}
}

/* Tablet: perkecil sedikit supaya 7 kolom tetap muat */
@media(min-width:720px) and (max-width:979px){
  table.tbl.warga th,table.tbl.warga td{padding:7px 4px}
  table.tbl.warga th{font-size:.66rem;letter-spacing:.04em}
  table.tbl.warga .warga-nama>.strong{font-size:.9rem}
  table.tbl.warga td.small,table.tbl.warga td.num{font-size:.74rem}
  table.tbl.warga .badge{font-size:.6rem;padding:2px 5px}
  tr.warga-aksi .btn,tr.warga-aksi .btn-sm{padding:7px 9px;font-size:.7rem}
}

/* ============================================================
   23. POLISH — ikon, hover-cahaya, dan gerak halus
   ------------------------------------------------------------
   Lapisan tambahan di atas Design System yang sudah ada
   (Forest + Stone + Amber, radius all-soft, font PJS/Outfit).
   Aturan main:
     - HANYA animasi `transform` + `opacity` (ringan, 60fps)
     - tidak ada warna baru: semua dari token yang sudah ada
     - semua gerak mati total kalau OS minta "reduced motion"
     - ikon dari sprite Tabler (MIT), bukan emoji
   ============================================================ */

/* ---------- 23.1 Wadah sprite ikon ---------- */
/* Sprite harus ada di DOM supaya <use href="#i-..."> bisa menemukannya,
   tapi tidak boleh kelihatan dan tidak boleh memakan ruang.
   Catatan: JANGAN pakai display:none — di sebagian browser itu mematikan
   rujukan <use> dan semua ikon jadi hilang. */
.sprite{position:absolute;width:0;height:0;overflow:hidden;pointer-events:none}

/* ---------- 23.0 Navbar: mode kompak vs penuh ----------
   Pengukuran live (admin, 8 menu):
     logo 40 + nama 352 + menu 720 + tombol akun 256 = 1402px
   Di bawah ~1400px semua tidak muat. Daripada memampatkan nama jadi
   10px ( difficult dibaca) atau membiarkan teks meluber keluar kotak,
   navbar secretly memakai mode KOMPAK: logo tetap, nama disembunyikan
   (nama situs sudah tertulis di hero & footer), menu jadi lebih rapat.
   Di >=1400px: mode penuh, nama muncul lagi.
   PENTING: yang disembunyikan hanya .brand-name. .brand tetap flex:0 0 auto
   supaya kontaknya tidak melebar ke tempat teks yang disembunyikan.
   CATATAN: JANGAN menulis `.nav-in{gap:...}` di sini. Aturan itu MENIMPA
   `@media(max-width:480px){.nav-in{gap:10px}}` (cascade, sama specificity,
   yang belakangan menang) dan bikin navbar meluber 3px di 320px. */
.brand{flex:0 0 auto;max-width:none}
/* Zona HP (<980px): menu disembunyikan (pakai drawer), jadi NAMA SITUS
   harus tetap tampil -- ada ruang plenty. Versi lama menyembunyikannya
   sampai 1400px, sehingga di HP header cuma logo tanpa tulisan sama sekali.
   Di sini `.brand{gap:0}` TIDAK dipakai: nama butuh jaraknya dari logo.
   Font tetapurate oleh font-fit JS (pasangSatuBaris). */
@media(min-width:980px) and (max-width:1399px){
  .nav-in .brand-name{display:none}
  .brand{gap:0}
  .nav-links{gap:14px;margin-left:auto}
  .nav-links a{font-size:.9rem}
  .nav-cta{gap:10px}
}
@media(min-width:1400px){
  .nav-in{gap:var(--s5)}
  .nav-links{gap:10px}
  .nav-links a{font-size:.94rem}
}

/* ---------- 23.1b Sidebar panel: ikon + jarak aman ---------- */
/* Ikon di sidebar hilang sampai layar cukup lebar. Di tablet (1000-1200px)
   kolom sidebar cuma 236px -- kalau ikon ikut, label seperti
   "Kas & buku besar" jadi dua baris dan ruang di sidebar habis. */
.side a .ico{color:var(--fg-3);flex:none;
  transition:color var(--f) var(--ease),transform var(--f) var(--ease)}
.side a:hover .ico{color:var(--fg);transform:scale(1.1)}
.side a.on .ico{color:var(--brand)}
@media(max-width:1179px){.side a .ico{display:none}}
@media(min-width:1180px){.side a .ico{display:inline-block}}

/* ---------- 23.2 Ukuran ikon ---------- */
.ico{width:1.15em;height:1.15em;flex:none;display:inline-block;
  vertical-align:-.18em;pointer-events:none}
.ico-sm{width:1em;height:1em;vertical-align:-.15em}
/* Ikon di menu desktop baru muncul kalau layar BENAR-BENAR lebar.
   Alasan: 7 menu + ikon = baris navbar ~230px lebih lebar, dan pada
   980-1399px navbar jadi meluber sehingga nama perumahan tertindih menu.
   Di layar sempit menu tetap jelas dari teksnya. */
.nav-links a .ico{display:none}
@media(min-width:1400px){.nav-links a .ico{display:inline-block}}
.ico-lg{width:1.4em;height:1.4em;vertical-align:-.24em}
.ico-xl{width:1.9em;height:1.9em;vertical-align:-.28em}
/* ikon di dalam tautan menu: beri jarak dari teksnya */
.nav-links a .ico,.drawer-nav a .ico,.drawer-item .ico,
.akun-menu a .ico,.ft a .ico{margin-inline-end:7px}
/* ikon di dalam .btn tidak perlu margin: .btn sudah pakai gap:8px */
.btn .ico{margin:0}

/* Lingkaran ikon (kartu fitur / statistik) */
.ico-bul{width:44px;height:44px;border-radius:13px;display:grid;place-items:center;
  background:var(--brand-soft);color:var(--brand);flex:none;
  transition:transform var(--f) var(--ease),background var(--f) var(--ease),
             color var(--f) var(--ease)}
.ico-bul .ico{width:22px;height:22px}
.ico-bul.accent{background:var(--accent-soft);color:var(--accent-ink)}

/* ---------- 23.3 Tombol tema: matahari <-> bulan ---------- */
/* Dua ikon ada di DOM, tapi hanya SATU yang ditampilkan (pakai display).
   JANGAN menumpuk keduanya lalu pakai opacity, dan JANGAN tulis
   `#themeToggleMobile{display:...}` di sini: ID selector mengalahkan
   `@media(min-width:980px){.tema-hp{display:none}}`, sehingga di desktop
   muncul DUA tombol tema. */
.ico-sun,.ico-moon{grid-area:1/1;transition:opacity var(--f) var(--ease),
  transform var(--f-slow) var(--ease)}
/* terang (default) -> tampilkan BULAN, karena klik = pindah ke gelap */
.ico-sun{opacity:0;transform:rotate(-70deg) scale(.4)}
.ico-moon{opacity:1;transform:none}
/* gelap -> tampilkan MATAHARI */
html[data-mode="dark"] .ico-sun{opacity:1;transform:none}
html[data-mode="dark"] .ico-moon{opacity:0;transform:rotate(70deg) scale(.4)}
/* ikut OS selama user belum pernah memilih tema sendiri */
@media(prefers-color-scheme:dark){
  html:not([data-mode="light"]) .ico-sun{opacity:1;transform:none}
  html:not([data-mode="light"]) .ico-moon{opacity:0;transform:rotate(70deg) scale(.4)}
}
/* saat tombolnya disorot, ikon berputar sedikit */
#themeToggle:hover .ico-sun,#themeToggle:hover .ico-moon,
#themeToggleMobile:hover .ico-sun,#themeToggleMobile:hover .ico-moon{
  transform:rotate(24deg) scale(1.05)}

/* ---------- 23.4 Hover-cahaya: blok warna ikut kursor ----------
   Permintaan: "kalau mouse dideketin dia ngeblok berwarna".
   Titik cahaya mengikuti posisi kursor di dalam kartu. Posisi dikirim JS
   lewat variabel --mx / --my (satu listener untuk semua kartu, hemat). */
.glow,.card,.panel,.kpi,.auth-card,.hero-stat,.gal-card,.prod,.gal-item{
  position:relative;isolation:isolate}
.glow::before,.card::before,.panel::before,.kpi::before,.auth-card::before,
.gal-card::before,.prod::before,.gal-item::before{
  content:'';position:absolute;inset:0;border-radius:inherit;
  pointer-events:none;z-index:0;opacity:0;
  transition:opacity var(--f) var(--ease);
  background:radial-gradient(240px circle at var(--mx,50%) var(--my,50%),
    color-mix(in srgb,var(--accent) 15%,transparent),transparent 72%)}
.glow:hover::before,.card:hover::before,.panel:hover::before,.kpi:hover::before,
.auth-card:hover::before,.gal-card:hover::before,.prod:hover::before,.gal-item:hover::before{opacity:1}
/* garis tepi menyala tepat di titik kursor */
.glow::after,.card::after,.panel::after,.kpi::after,.auth-card::after,
.gal-card::after,.prod::after,.gal-item::after{
  content:'';position:absolute;inset:0;border-radius:inherit;
  pointer-events:none;z-index:0;opacity:0;padding:1px;
  transition:opacity var(--f) var(--ease);
  background:radial-gradient(200px circle at var(--mx,50%) var(--my,50%),
    color-mix(in srgb,var(--accent) 62%,transparent),transparent 70%);
  -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;mask-composite:exclude}
.glow:hover::after,.card:hover::after,.panel:hover::after,.kpi:hover::after,
.auth-card:hover::after,.gal-card:hover::after,.prod:hover::after,.gal-item:hover::after{opacity:1}
/* isi kartu harus di atas lapisan cahaya */
.glow>*,.card>*,.panel>*,.kpi>*,.auth-card>*,.gal-card>*,.prod>*,.gal-item>*{
  position:relative;z-index:1}

/* kartu terangkat halus saat disorot */
.card,.panel,.kpi,.auth-card,.hero-stat,.gal-card,.prod,.gal-item{transition:
  transform var(--f) var(--ease),box-shadow var(--f) var(--ease),
  border-color var(--f) var(--ease)}
.card:hover,.panel:hover,.auth-card:hover{
  transform:translateY(-3px);box-shadow:var(--shadow-lg);
  border-color:color-mix(in srgb,var(--accent) 34%,var(--line))}
.kpi:hover{transform:translateY(-2px)}
/* .prod & .gal-item sudah punya hover lift sendiri (bagian 13/14) */

/* ---------- 23.5 Tombol: kilau melintas + dorongan ---------- */
.btn{position:relative;overflow:hidden}
.btn::after{content:'';position:absolute;inset:0;pointer-events:none;z-index:2;
  background:linear-gradient(115deg,transparent 30%,
    rgba(255,255,255,.30) 50%,transparent 70%);
  transform:translateX(-140%);transition:transform var(--f-slow) var(--ease)}
.btn:hover::after{transform:translateX(140%)}
.btn-ghost::after{background:linear-gradient(115deg,transparent 30%,
  color-mix(in srgb,var(--accent) 22%,transparent) 50%,transparent 70%)}
.btn:hover{transform:translateY(-1px)}
.btn:active{transform:translateY(0) scale(.98)}
.btn .ico{transition:transform var(--f) var(--ease)}
.btn:hover .ico{transform:scale(1.12)}

/* ---------- 23.6 Menu: garis bawah meluncur ---------- */
.nav-links a{position:relative}
.nav-links a::after{content:'';position:absolute;left:0;right:0;bottom:-2px;height:2px;
  background:var(--accent);border-radius:2px;transform:scaleX(0);transform-origin:left;
  transition:transform var(--f) var(--ease)}
.nav-links a:hover::after,.nav-links a[aria-current="page"]::after{transform:scaleX(1)}
/* menu yang sedang dibuka: warnai teksnya juga */
.nav-links a[aria-current="page"]{color:var(--fg);font-weight:600}

/* Drawer HP: item menyorot dari kiri */
.drawer-nav a,.drawer-item{position:relative;
  transition:background var(--f) var(--ease),color var(--f) var(--ease),
             transform var(--f) var(--ease)}
.drawer-nav a:hover,.drawer-item:hover{background:var(--brand-soft);
  transform:translateX(5px)}
.drawer-nav a .ico,.drawer-item .ico{color:var(--fg-3);
  transition:color var(--f) var(--ease),transform var(--f) var(--ease)}
.drawer-nav a:hover .ico,.drawer-item:hover .ico{color:var(--brand);
  transform:scale(1.1)}
/* tombol Masuk/Keluar di drawer sudah berwarna sendiri */
.drawer-nav a.masuk .ico,.drawer-nav a.keluar .ico,.drawer-item.masuk .ico,
.drawer-item.keluar .ico{color:currentColor}

/* ---------- 23.7 Ikon bergerak halus ---------- */
/* burger berputar saat menu terbuka */
.burger .ico{transition:transform var(--f) var(--ease)}
.burger[aria-expanded="true"] .ico{transform:rotate(90deg)}
/* ikon dalam lingkaran membesar sedikit saat kartunya disorot */
.card:hover .ico-bul,.panel:hover .ico-bul,.kpi:hover .ico-bul,
.glow:hover .ico-bul,.auth-card:hover .ico-bul{
  transform:scale(1.08) rotate(-3deg);background:var(--accent-soft);
  color:var(--accent-ink)}
/* lonceng/notifikasi bergoyang saat disorot */
.ring:hover .ico{animation:ring .7s var(--ease)}
@keyframes ring{
  0%,100%{transform:rotate(0)}
  20%{transform:rotate(13deg)}
  45%{transform:rotate(-11deg)}
  70%{transform:rotate(7deg)}
}

/* ---------- 23.8 Kartu masuk berurutan (bukan serentak) ---------- */
.grid.stagger>*:nth-child(1){--rv-d:0ms}
.grid.stagger>*:nth-child(2){--rv-d:70ms}
.grid.stagger>*:nth-child(3){--rv-d:140ms}
.grid.stagger>*:nth-child(4){--rv-d:210ms}
.grid.stagger>*:nth-child(5){--rv-d:280ms}
.grid.stagger>*:nth-child(6){--rv-d:350ms}
.grid.stagger>*:nth-child(7){--rv-d:420ms}
.grid.stagger>*:nth-child(8){--rv-d:490ms}
.grid.stagger .rv.in{transition-delay:var(--rv-d,0ms)}

/* ---------- 23.9 Judul section: garis aksen tumbuh ---------- */
.sec-head h2::after{content:'';display:block;height:3px;width:46px;
  margin-top:var(--s2);background:var(--accent);border-radius:3px;
  transform:scaleX(0);transform-origin:left;
  transition:transform .7s var(--ease)}
.sec-head.in h2::after,.rv.in .sec-head h2::after{transform:scaleX(1)}

/* ---------- 23.10 Angka statistik ---------- */
.hero-stat b{transition:transform var(--f) var(--ease)}
.hero-stat:hover b{transform:translateY(-2px) scale(1.05)}
.hero-stat:hover{background:rgba(18,81,60,.72)}

/* ---------- 23.11 Baris tabel: sorot lembut ---------- */
table.tbl tbody tr{transition:background var(--f-fast) var(--ease)}
table.tbl tbody tr:hover{background:var(--row-hover)}

/* ---------- 23.12 Fokus keyboard tetap kelihatan ---------- */
.card:focus-within,.panel:focus-within,.kpi:focus-within{
  border-color:var(--accent)}

/* ---------- 23.13 Semua gerak mati kalau OS minta ---------- */
@media(prefers-reduced-motion:reduce){
  .glow::before,.glow::after,.card::before,.card::after,.panel::before,
  .panel::after,.kpi::before,.kpi::after,.auth-card::before,.auth-card::after,
  .gal-card::before,.gal-card::after,.prod::before,.prod::after,
  .gal-item::before,.gal-item::after,.
  .btn::after{display:none}
  .btn:hover,.card:hover,.panel:hover,.auth-card:hover,.kpi:hover,
  .hero-stat:hover b,.drawer-nav a:hover,.drawer-item:hover,
  .burger[aria-expanded="true"] .ico,.card:hover .ico-bul,.panel:hover .ico-bul,
  .kpi:hover .ico-bul,.glow:hover .ico-bul,.auth-card:hover .ico-bul{
    transform:none}
  .ico,.ico-bul,.btn .ico,.ico-sun,.ico-moon{transition:none;animation:none}
  .nav-links a::after,.sec-head h2::after{transition:none;transform:scaleX(1)}
  .grid.stagger .rv.in{transition-delay:0ms}
}


/* ---------- 23.4 TABEL: justify per jenis isi ---------- */
/* Setiap kolom diberi ARAH sendiri supaya angka sejajar rapi dan kolom
   teks/keterangan tetap enak dibaca. Arah ditentukan oleh KELAS pada sel,
   bukan text-align inline (yang mudah bentrok dengan cascade).

   .t-l  -> rata kiri   : nama, keterangan, kategori, bulan
   .t-r  -> rata kanan  : nominal, tanggal, jumlah, stok
   .t-c  -> rata tengah : badge status, checkbox, tombol aksi

   PENTING: `th` SELALU harus ikut arah `td` di bawahnya. Header dan isi
   yang beda arah adalah penyebab paling umum tabel terlihat berantakan. */

/* 1) Default aman: teks kiri */
.tbl th,.tbl td{text-align:left}

/* 1b) JUDUL KOLOM (thead th) -> SELALU TENGAH.
   Permintaan user 13 Okt: judul kolom ditulis rata tengah, biar seragam
   semua tabel. Isi selnya tetap ikut kelas arahnya (.num kanan, .t-c tengah,
   teks kiri) -- jadi angka tetap sejajar rapi, judulnya rapi di tengah.
   Specificity: `.tbl thead th.num` (0,2,2) > `.tbl th.num` (0,2,1), jadi
   aturan ini menang tanpa perlu !important. */
.tbl thead th,
.tbl thead th.num,
.tbl thead th.t-r,
.tbl thead th.t-c,
.tbl thead th.t-a,
.tbl thead th.pilih,
.tbl thead th.t-w{text-align:center}

/* 1c) JUDUL SORTIR: <a> di dalam <th> mengambil alih perataan.
   `text-align:center` di atas tidak berpengaruh ke flex, jadi <a> pakai
   justify-content. Default (tanpa kelas) = KIRI, `.t-c` = TENGAH.
   Tanpa aturan ini, judul kolom kiri ikut ke tengah dan tidak sejajar
   dengan isi selnya. */
.tbl th.th-urut > a{justify-content:flex-start}
.tbl th.th-urut.t-c > a{justify-content:center}

/* 2) Angka & tanggal rata kanan. Ini yang bikin kolom rupiah "bergeser"
      tiap baris kalau rata kiri -- panjang tiap angka berbeda. */
.tbl th.num,.tbl td.num,
.tbl th.t-r,.tbl td.t-r{text-align:right}

/* 3) Badge / status / aksi: tengah */
.tbl th.t-c,.tbl td.t-c,
.tbl th.t-a,.tbl td.t-a{text-align:center}

/* 4) Kolom checkbox paling kiri: tengah + sesempit mungkin */
.tbl td.pilih,.tbl th.pilih{text-align:center;width:1%}

/* 5) Isi panjang boleh wrap, jangan jadi satu baris meluber */
.tbl td.t-w,.tbl th.t-w{white-space:normal;word-break:break-word}

/* 6) Baris Total (tfoot): tegas, angka tetap rata kanan */
.tbl tfoot td{background:var(--surface-2);font-weight:700;
  border-top:2px solid var(--line);border-bottom:0}
.tbl tfoot td.t-c{text-align:center}

/* Angka "Belum terbayar" di baris Total HARUS merah.
   ⚠️ Class `.err` selama ini hanya didefinisikan untuk `.kpi .val.err` dan
   `.field .err` -- jadi `class="num strong err"` di dalam tabel TIDAK berwarna
   apa pun (terukur rgb(21,32,28) = hitam). Permintaan user 17 Okt 2026:
   *"total Belum terbayar nominal'y u ksh warna red"*. Selector di bawah
   sengaja spesifik (0,3,1) supaya menang atas `.tbl tfoot td` (0,1,2). */
.tbl tfoot td.err,
.tbl tfoot td.num.err{color:var(--err);font-weight:800}

/* Baris "Total N bulan" + nominalnya hijau (uang masuk).
   ⚠️ Sama seperti `.err`: `.ok` hanya didefinisikan untuk `.kpi .val.ok`,
   jadi di dalam tabel class itu TIDAK berwarna. Permintaan user 17 Okt 2026:
   *"tuk total 1 bulan+nominal'y bkin warna green"*. */
.tbl tfoot td.ok,
.tbl tfoot td.num.ok{color:var(--ok);font-weight:800}

/* 7) Padding seragam + garis antar kolom tipis (mudah diikuti mata) */
.tbl th,.tbl td{padding:13px 14px;vertical-align:middle}
.tbl th{padding:11px 14px}
.tbl th+th,.tbl td+td{border-left:1px solid color-mix(in srgb,var(--line) 45%,transparent)}
.tbl thead th{background:var(--surface-2);position:sticky;top:0;z-index:2}

/* 8) Zebra halus + hover: baris panjang jadi mudah dibaca */
/* 8) ZEBRA: 2 warna SOFT yang jelas beda tapi tidak bikin keder.
   Versi lama: --surface-2 (#fbfbfa) pada 40% -> nyaris putih, zebra tidak
   terlihat sama sekali. Sekarang pakai tint hijau pucat (--row-2). */
.tbl tbody tr:nth-child(even){background:var(--row-2)}
.tbl tbody tr:hover{background:var(--row-hover)}

/* 8b) DATA WARGA: tiap baris data diikuti 1 baris AKSI, sehingga
   nth-child(even) jatuh ke baris AKSI -- zebra tidak pernah terlihat di
   baris data. Baris data selalu di posisi GANJIL (1,3,5,...), jadi zebra
   = 4n+1. Bergantung pada "1 baris aksi per baris data" (dijaga test
   t_urut.py: 'baris aksi = banyak baris data'). */
table.tbl.warga tbody tr:not(.warga-aksi):nth-child(4n+1){background:var(--row-2)}
/* Baris aksi: tint lebih pucat dari zebra supaya tidak bentrok saat
   baris data di atasnya kebetulan juga bertint. */
table.tbl.warga tbody tr.warga-aksi>td{background:var(--row-aksi)}

/* 9) Sel aksi: tombol rapat & sejajar, bukan menara tinggi */
.tbl td.t-a>div{display:flex;gap:6px;flex-wrap:wrap;justify-content:center;align-items:center}
.tbl td.t-a .btn-sm{padding:6px 11px;font-size:.78rem;min-height:34px}
.tbl td.t-a .btn{white-space:nowrap}

/* 10) JANGAN pakai `:has(>span.badge)` untuk memaksa tengah.
   Dua kolom (Iuran lunas, Riwayat ikut terhapus) isinya CAMPURAN: badge
   UNTUK sebagian baris, teks biasa untuk baris lain. Aturan :has bikin
   header (kelas eksplisit) tidak cocok dengan isi -> kolom terlihat
   "goyang" saat baris berganti. Arah kolom harus decided di TEMPLATE. */
.tbl td.t-c,.tbl th.t-c{text-align:center}


/* 11) Kolom "Iuran lunas" (Data warga) isinya 2 baris: jumlah bulan +
       badge "Bulan ini". `.num` brings white-space:nowrap, jadi sel meluber
       13-56px. Override khusus kolom ini: boleh wrap. */
table.tbl.warga td[data-label="Iuran lunas"],
table.tbl.warga td.num.t-c{white-space:normal;overflow-wrap:anywhere}
/* 12) Kolom Telegram juga memuat badge -> normal flow, bukan nowrap */
table.tbl.warga td.t-c{white-space:normal}


/* 13) Kolom Nama (Data warga) — DIUBAH 16 Okt 2026.
       Aturan lama memaksa NIK/nama PECAH BARIS (`white-space:normal`) karena
       lebar kolom dulu dikunci px & NIK 16 digit meluber. Sejak lebar kolom
       DINAMIS (table-layout:auto), kolom melebar sendiri mengikuti NIK/nama
       terpanjang -> tidak perlu dipaksa pecah lagi. Justru `normal` di sini
       MERUSAK sifat dinamis: min-content kolom jadi selebar kata terpanjang,
       bukan selebar teks utuh, sehingga nama ikut terpecah 2 baris
       ("Sukaya Doang" jadi 2 baris & baris tabel melar 100px).
       ⚠️ JANGAN kembalikan ke `normal` selama tabel pakai auto-layout. */
table.tbl.warga td.warga-nama .num,
table.tbl.warga td.warga-nama .small{white-space:nowrap;
  overflow-wrap:normal}


/* ---------- 23.5 KPI: angka lebih besar, keterangan lebih kecil ---------- */
/* Permintaan user 13 Okt: "Iuran Rp10,000 x 20 warga yang sudah lunas"
   dikecilkan, tapi ANGKA TOTAL IURAN dibesarkan biar jelas.
   Angka pakai satuan container (cqi) = 1% lebar KARTU, jadi di kartu yang
   lebih lebar angkanya otomatis ikut besar. */
/* Angka KPI: min 16px supaya di kartu sempit (HP, grid 2 kolom) TETAP jelas
   lebih besar dari keterangannya. Sebelumnya min 14px vs ket 11.8px -> beda
   cuma 2px, hierarkinya tidak terbaca. Maks 2.15rem di kartu lebar. */
.kpi .val{font-size:clamp(.95rem,4.2vw,1.65rem);
  font-size:clamp(16px,9.2cqi,2.15rem);}
.kpi .lab{font-size:.78rem;line-height:1.35}
/* Keterangan ("Iuran Rp10,000 x 20 warga yang sudah lunas"): dikecilkan
   supaya tidak bersaing dengan angka. 0.7rem ~ 11.2px, tetap terbaca. */
.kpi .ket{font-size:.7rem;line-height:1.45;color:var(--fg-3);margin-top:7px;
  display:block}
.kpi .ket .rp{font-weight:600;color:var(--fg-2)}


/* ---------- 23.6 KPI: semua kotak sama tinggi & rapi ---------- */
/* Permintaan user: kotak "Total Pemasukan Tertunggak" harus sama dengan
   yang lain. Penyebabnya: kartu itu punya 2 baris keterangan (ada
   breakdown "nunggak · tanpa tagihan") sedangkan kartu lain 1 baris, jadi
   isinya mendadak lebih tinggi. Perbaikannya BUKAN memotong baris kedua,
   tapi:
     a) grid stretch -> semua kartu sama tinggi (default, dijamin)
     b) .lab dikunci max 2 baris supaya kartu dengan label 1 baris tetap
        menyisakan ruang yang sama untuk .ket
     c) .ket dikunci max 2 baris + overflow, jadi tidak ada kartu yang
        menjulur ke bawah dan membuat baris tidak rata. */
.kpi{display:flex;flex-direction:column;align-items:stretch}
/* label: boleh 1-2 baris, tinggi minimal sama supaya .val selalu mulai
   di baris yang sama di semua kartu */
.kpi .lab{min-height:2.6em;display:flex;align-items:flex-start}
/* Keterangan: ruangnya dipatok supaya kartu BERketerangan punya tinggi
   isi yang sama. */
.kpi .ket{min-height:2.9em}
.kpi .ket+.ket{min-height:0;margin-top:-2px}

/* PENTING: JANGAN pakai `.val{margin-top:auto}`. Itu mendorong angka ke
   BAWAH pada kartu yang tidak punya keterangan (mis. "Total pengeluaran",
   "Saldo kas"), sehingga angkanya jatuh beberapa baris dan tidak sejajar
   dengan angka di kartu sebelahnya yang berketerangan.
   Yang benar: angka selalu menempel tepat DI BAWAH label, dan ruang kosong
   dibiarkan di bawah. */
.kpi .val{margin-top:0}


/* ---------- 23.7 Zebra untuk daftar DIV (pseudo-tabel) ---------- */
/* "Per kategori", "Mutasi terakhir", agenda, daftar pengurus, dsb bukan
   <table> tapi <div class="baris"> di dalam .panel-b, jadi selector
   `.tbl tbody tr:nth-child(even)` tidak pernah kena. Damai, gunakan kelas
   yang sama (.baris) supaya zebra mengikat di satu tempat.

   PENTING: .baris dibungkus <span>/<div> anak, jadi nth-child harus
   menghitung anak LANGSUNG dari .panel-b -- kalau pakai .baris:nth-child
   akan salah hitung karena ada elemen lain (mis. .empty) di tengah. */
.panel-b>.baris:nth-child(even){background:var(--row-2)}
.panel-b>.baris:nth-child(odd){background:transparent}
.panel-b>.baris{border-radius:8px;transition:background var(--f-fast);
  justify-content:space-between;gap:var(--s3)}
.panel-b>.baris:hover{background:var(--row-hover)}
/* Di HP panel sempit, padding horizontal 12px bikin baris "Mutasi terakhir"
   meluber (kategori + tanggal + keterangan + nominal). Padding mengecil,
   isi panjang boleh wrap. */
@media(max-width:560px){
  .panel-b>.baris{padding:10px 8px !important}
  /* Isi kiri (kategori + tanggal + keterangan) WAJIB boleh wrap.
     `.num` bring white-space:nowrap; kalau dipakai pada <div> pembungkus,
     seluruh baris ikut jadi nowrap dan meluber. */
  .panel-b>.baris>div:first-child{min-width:0;overflow-wrap:anywhere}
  .panel-b>.baris>div:first-child .num{white-space:normal}
}
/* Baris terakhir: tidak ada garis bawah menggantung */
.panel-b>.baris:last-child{border-bottom:0 !important}


/* ---------- 23.8 TOMBOL di dalam tabel: sistem warna per makna ---------- */
/* Latar tombol WAJIB opaque. Versi lama (.btn-ghost/.btn-danger
   `background:transparent`) membuat warna baris zebra TEMBUS sehingga tombol
   menyatu dengan baris -- rasio 1.000, persis "samar". Terukur 458 tombol,
   222 di antaranya transparan.

   Empat makna:
     netral  abu    -> navigasi/utilitas: Set Telegram, Reset password,
                       Lihat bukti, Pilih galeri/kamera, Centang semua,
                       Kosongkan, Yang belum pernah masuk
     sukses  hijau  -> positif/menerima: Jadikan Warga, Jadikan Pengurus
     peringatan amber -> membalik/menghentikan: Belum, Batalkan, Batal,
                       Matikan, Mulai iuran
     bahaya  merah  -> destruktif: Tolak, Hapus, Hapus warga terpilih,
                       Reset iuran & kas, Reset chat keluhan

   Kenapa tidak memakai --ok-bg/--warn-bg/--err-bg yang sudah ada? Nilainya
   terlalu dekat dengan zebra (1.01-1.03) -- persis masalah yang diperbaiki.
   Butuh tint sendiri yang lebih tegas.

   PITFALL CSS: selector panjang TIDAK BOLEH dipotong baris sebelum `:not()`.
   Spasi/newline di sana dianggap DESCENDANT COMBINATOR, sehingga selectornya
   berubah arti dan tidak pernah cocok (tombol tetap transparan). Semua
   selector di bawah sengaja ditulis SATU BARIS.

   PITFALL specificity: rantai :not() menaikkan specificity, jadi setiap
   varian WAJIB dikecualikan di aturan catch-all -- kalau tidak, tombolnya
   ikut warna netral. */
.tbl .btn,.panel-b .btn,tr.warga-aksi .btn,.tbl button,.panel-b button{
  border-width:1px;border-style:solid;
  transition:background var(--f-fast),color var(--f-fast),border-color var(--f-fast)}
.tbl .btn:not([class*="btn-primary"]):not([class*="btn-accent"]):not([class*="btn-ok"]):not([class*="btn-warn"]):not([class*="btn-danger"]):not([class*="btn-info"]):not([class*="btn-violet"]):not([class*="btn-kuning"]),.panel-b .btn:not([class*="btn-primary"]):not([class*="btn-accent"]):not([class*="btn-ok"]):not([class*="btn-warn"]):not([class*="btn-danger"]):not([class*="btn-info"]):not([class*="btn-violet"]):not([class*="btn-kuning"]),tr.warga-aksi .btn:not([class*="btn-primary"]):not([class*="btn-accent"]):not([class*="btn-ok"]):not([class*="btn-warn"]):not([class*="btn-danger"]):not([class*="btn-info"]):not([class*="btn-violet"]):not([class*="btn-kuning"]){background:var(--btn-netral-bg);color:var(--btn-netral-fg);border-color:color-mix(in srgb,var(--btn-netral-fg) 22%,transparent)}
.tbl .btn-ok,.panel-b .btn-ok,tr.warga-aksi .btn-ok{background:var(--btn-ok-bg);color:var(--btn-ok-fg);border-color:color-mix(in srgb,var(--btn-ok-fg) 32%,transparent)}
.tbl .btn-warn,.panel-b .btn-warn,tr.warga-aksi .btn-warn{background:var(--btn-warn-bg);color:var(--btn-warn-fg);border-color:color-mix(in srgb,var(--btn-warn-fg) 32%,transparent)}
.tbl .btn-danger,.panel-b .btn-danger,tr.warga-aksi .btn-danger{background:var(--btn-err-bg);color:var(--btn-err-fg);border-color:color-mix(in srgb,var(--btn-err-fg) 32%,transparent)}
/* Hover: latar sedikit digelapkan -> tombol "hidup" tanpa melompat */
.tbl .btn:not([class*="btn-primary"]):not([class*="btn-accent"]):not([class*="btn-ok"]):not([class*="btn-warn"]):not([class*="btn-danger"]):not([class*="btn-info"]):not([class*="btn-violet"]):not([class*="btn-kuning"]):hover,.panel-b .btn:not([class*="btn-primary"]):not([class*="btn-accent"]):not([class*="btn-ok"]):not([class*="btn-warn"]):not([class*="btn-danger"]):not([class*="btn-info"]):not([class*="btn-violet"]):not([class*="btn-kuning"]):hover,tr.warga-aksi .btn:not([class*="btn-primary"]):not([class*="btn-accent"]):not([class*="btn-ok"]):not([class*="btn-warn"]):not([class*="btn-danger"]):not([class*="btn-info"]):not([class*="btn-violet"]):not([class*="btn-kuning"]):hover{background:color-mix(in srgb,var(--btn-netral-bg) 82%,var(--fg))}
.tbl .btn-ok:hover,.panel-b .btn-ok:hover,tr.warga-aksi .btn-ok:hover{background:color-mix(in srgb,var(--btn-ok-bg) 84%,var(--btn-ok-fg));border-color:var(--btn-ok-fg)}
.tbl .btn-warn:hover,.panel-b .btn-warn:hover,tr.warga-aksi .btn-warn:hover{background:color-mix(in srgb,var(--btn-warn-bg) 84%,var(--btn-warn-fg));border-color:var(--btn-warn-fg)}
.tbl .btn-danger:hover,.panel-b .btn-danger:hover,tr.warga-aksi .btn-danger:hover{background:color-mix(in srgb,var(--btn-err-bg) 84%,var(--btn-err-fg));border-color:var(--btn-err-fg)}

.tbl .btn-info,.panel-b .btn-info,tr.warga-aksi .btn-info{background:var(--btn-info-bg);color:var(--btn-info-fg);border-color:color-mix(in srgb,var(--btn-info-fg) 32%,transparent)}
.tbl .btn-violet,.panel-b .btn-violet,tr.warga-aksi .btn-violet{background:var(--btn-violet-bg);color:var(--btn-violet-fg);border-color:color-mix(in srgb,var(--btn-violet-fg) 32%,transparent)}
.tbl .btn-kuning,.panel-b .btn-kuning,tr.warga-aksi .btn-kuning{background:var(--btn-kuning-bg);color:var(--btn-kuning-fg);border-color:color-mix(in srgb,var(--btn-kuning-fg) 32%,transparent)}
.tbl .btn-info:hover,.panel-b .btn-info:hover,tr.warga-aksi .btn-info:hover{background:color-mix(in srgb,var(--btn-info-bg) 84%,var(--btn-info-fg));border-color:var(--btn-info-fg)}
.tbl .btn-violet:hover,.panel-b .btn-violet:hover,tr.warga-aksi .btn-violet:hover{background:color-mix(in srgb,var(--btn-violet-bg) 84%,var(--btn-violet-fg));border-color:var(--btn-violet-fg)}
.tbl .btn-kuning:hover,.panel-b .btn-kuning:hover,tr.warga-aksi .btn-kuning:hover{background:color-mix(in srgb,var(--btn-kuning-bg) 84%,var(--btn-kuning-fg));border-color:var(--btn-kuning-fg)}

/* .btn-primary & .btn-accent TIDAK diubah: sudah punya latar opaque sendiri
   (--brand / --accent) dengan kontras 8-9:1. */
