/* ============================================================================
   KOMPONEN — Guardian AI Dashboard
   Nilai warna/spasi/ikon ada di tokens.css. Berkas ini nol hex mentah.

   Rombak 2026-08-15: dashboard dipindah dari "melaporkan yang sudah lewat" ke
   "menjawab yang akan datang". Dua kartu paling atas sekarang RUNWAY (aman
   sampai kapan) dan LINTASAN (badan lagi ke arah mana) — dua pertanyaan yang
   dijanjikan spec-cfo-ahli-gizi.md tapi belum pernah dijawab permukaan ini.
   ============================================================================ */

*{margin:0;padding:0;box-sizing:border-box}
body{font-family:'Inter',-apple-system,'Segoe UI',sans-serif;background:var(--viz-page);color:var(--viz-ink);line-height:1.5;-webkit-font-smoothing:antialiased}
h1,h2,h3{font-family:'Poppins',sans-serif;line-height:1.2;letter-spacing:-.01em}
button{font-family:inherit;cursor:pointer}

/* ANGKA UANG — satu aturan, semua tempat.
   lining + tabular: kolom angka nggak goyang, nol nggak ketuker huruf O.
   Sans, bukan serif — serif bikin angka kecil kabur (lihat catatan font di
   index.html). Rules baku §9.2 mewajibkan tabular-nums di dashboard. */
body,table,input,select,button{font-variant-numeric:lining-nums tabular-nums;font-feature-settings:"tnum" 1,"lnum" 1}
.val,.figs strong,.num,.bullet-num,.fwd-num,.stat-num{font-family:'Inter',-apple-system,'Segoe UI',sans-serif;font-variant-numeric:lining-nums tabular-nums;letter-spacing:-.02em}
/* "Rp" dikecilin & ditipisin: yang harus kebaca duluan itu besarnya angka,
   bukan simbol mata uangnya. Dibungkus <span class="cur"> oleh rpHero(). */
.val .cur,.bullet-num .cur,.fwd-num .cur{font-size:.56em;font-weight:500;opacity:.72;margin-right:.12em;letter-spacing:0}

/* ---------- IKON SVG ----------
   Ganti emoji struktural 2026-08-15. Emoji itu font-dependent (beda rupa di
   Android/iOS/Windows), nggak bisa diwarnai lewat token, dan ukurannya ikut
   font-size jadi nggak pernah sejajar. Emoji TETAP dipakai di copy — itu suara
   Guardian (rules baku §5), bukan ikon. */
.ico{width:var(--icon-md);height:var(--icon-md);flex-shrink:0;display:block}
.ico-sm{width:var(--icon-sm);height:var(--icon-sm)}
.ico-lg{width:var(--icon-lg);height:var(--icon-lg)}
.ico path,.ico circle,.ico rect,.ico line,.ico polyline{
  fill:none;stroke:currentColor;stroke-width:var(--icon-stroke);
  stroke-linecap:round;stroke-linejoin:round}

/* ---------- KERANGKA ---------- */
/* kolom kiri dicat penuh sampai bawah — sidebar-nya sendiri sticky setinggi
   layar, tanpa ini area di bawahnya bolong saat konten lebih panjang dari
   viewport */
.shell{display:grid;grid-template-columns:var(--sb-w) 1fr;min-height:100vh;
  background:linear-gradient(90deg,var(--brand-navy-deep) 0 var(--sb-w),transparent var(--sb-w));
  transition:grid-template-columns .18s cubic-bezier(.4,0,.2,1)}
/* REL IKON (2026-08-16). Sidebar 236px itu 15% lebar layar 1440 yang isinya
   sebelas label yang sudah dihafal user harian. Dilipat jadi 64px, kontennya
   dapat 172px tambahan — cukup buat kolom keempat di grid ringkas. Nilai
   --sb-w di-override di .shell sendiri supaya gradasi latar ikut menyempit
   tanpa aturan kedua. */
.shell[data-sidebar="rail"]{--sb-w:var(--sb-w-rail)}
.sidebar{background:var(--brand-navy-deep);color:#fff;padding:var(--space-6) var(--space-4);position:sticky;top:0;height:100vh;overflow-y:auto;overflow-x:hidden;border-right:1px solid rgba(255,255,255,.07)}
/* motif Thread Device sebagai tekstur samar — penanda identitas, bukan hiasan ramai */
.sidebar::before{content:"";position:absolute;inset:0;background-image:var(--thread);background-size:72px 72px;opacity:.14;pointer-events:none}
.sidebar>*{position:relative;z-index:1}
.sb-logo{display:flex;align-items:center;gap:var(--space-2);font-weight:800;font-size:15.5px;letter-spacing:-.01em;margin-bottom:var(--space-7);padding:0 var(--space-2)}
.sb-logo svg{width:28px;height:28px}
.sb-section{font-size:10.5px;text-transform:uppercase;letter-spacing:.12em;color:#6d81a8;font-weight:700;margin:var(--space-5) var(--space-3) var(--space-2)}
/* min-height:44px — target sentuh minimum (ui-ux-pro-max §2/§5 touch-target-size) */
.sb-link{display:flex;align-items:center;gap:var(--space-3);padding:10px 11px;min-height:44px;border-radius:var(--radius-sm);color:#c2d0e6;text-decoration:none;font-size:13.5px;font-weight:500;margin-bottom:2px;position:relative;transition:background .15s,color .15s}
.sb-link:hover{background:rgba(255,255,255,.07);color:#fff}
/* status aktif: rel putih di tepi + latar biru — dua penanda, bukan warna saja */
.sb-link.active{background:rgba(46,123,224,.22);color:#fff;font-weight:600}
.sb-link.active::before{content:"";position:absolute;left:calc(var(--space-4) * -1);top:50%;transform:translateY(-50%);width:3px;height:20px;border-radius:0 3px 3px 0;background:#fff}
.main{min-width:0}

/* ---------- SIDEBAR TERLIPAT (rel ikon) ----------
   Label disembunyikan pakai lebar 0 + overflow, BUKAN display:none — teksnya
   tetap ada di pohon aksesibilitas, jadi pengguna screen reader tetap dengar
   "Transaksi", bukan tombol tanpa nama. Judul juga tetap ditulis ke title/
   aria-label oleh app.js sebagai jaring kedua. */
.shell[data-sidebar="rail"] .sidebar{padding-left:10px;padding-right:10px}
.shell[data-sidebar="rail"] .sb-label{width:0;opacity:0;overflow:hidden;white-space:nowrap;display:inline-block}
.shell[data-sidebar="rail"] .sb-link{justify-content:center;padding:10px 0;gap:0}
.shell[data-sidebar="rail"] .sb-logo{justify-content:center;padding:0;gap:0}
.shell[data-sidebar="rail"] .sb-logo .sb-label{width:0;opacity:0}
/* Judul kelompok jadi garis pemisah. Dihapus total bikin sebelas ikon jadi
   satu tumpukan tanpa batas sama sekali — pengelompokan Duit/Badan/Kebiasaan
   itu taksonomi produk, bukan hiasan. */
.shell[data-sidebar="rail"] .sb-section{font-size:0;height:1px;margin:var(--space-3) var(--space-2);background:rgba(255,255,255,.12);letter-spacing:0}
/* Tooltip rel. Muncul dari data-tip yang diisi app.js. */
.shell[data-sidebar="rail"] .sb-link::after{
  content:attr(data-tip);position:absolute;left:calc(100% + 10px);top:50%;transform:translateY(-50%);
  background:var(--brand-navy);color:#fff;font-size:12.5px;font-weight:600;white-space:nowrap;
  padding:6px 10px;border-radius:var(--radius-sm);border:1px solid rgba(255,255,255,.14);
  box-shadow:var(--shadow-md);opacity:0;pointer-events:none;transition:opacity .12s;z-index:40}
.shell[data-sidebar="rail"] .sb-link:hover::after,
.shell[data-sidebar="rail"] .sb-link:focus-visible::after{opacity:1}
/* Rel putih penanda aktif digeser ikut padding baru — tanpa ini dia melayang
   10px dari tepi dan kelihatan seperti garis nyasar. */
.shell[data-sidebar="rail"] .sb-link.active::before{left:-10px}
/* Tombol lipat: ikon berputar biar arahnya jelas, bukan dua ikon berbeda. */
#sidebarToggle .ico{transition:transform .18s cubic-bezier(.4,0,.2,1)}
.shell[data-sidebar="rail"] #sidebarToggle .ico{transform:rotate(180deg)}
/* Melipat sidebar itu perkara tata letak, bukan tontonan. Yang minta gerak
   minimum tetap dapat perubahannya seketika. */
@media(prefers-reduced-motion:reduce){
  .shell,#sidebarToggle .ico,.shell[data-sidebar="rail"] .sb-link::after{transition:none}
}
/* Di bawah 980px sidebar sudah jadi strip horizontal — melipatnya nggak punya
   arti dan malah bikin sebelas ikon tanpa label. Mode rel dipaksa mati. */
@media(max-width:980px){
  .shell[data-sidebar="rail"]{--sb-w:auto}
  .shell[data-sidebar="rail"] .sb-label{width:auto;opacity:1}
  .shell[data-sidebar="rail"] .sb-link{justify-content:flex-start;padding:10px 11px;gap:var(--space-3)}
  .shell[data-sidebar="rail"] .sb-link::after{display:none}
  #sidebarToggle{display:none}
}

.topbar{position:sticky;top:0;z-index:20;background:color-mix(in srgb,var(--viz-surface) 88%,transparent);backdrop-filter:blur(12px);border-bottom:1px solid var(--viz-border);padding:var(--space-3) var(--space-7);display:flex;align-items:center;justify-content:space-between;gap:var(--space-4);flex-wrap:wrap}
.status-badge{background:rgba(12,163,12,.12);color:var(--viz-good-text);font-size:11px;font-weight:700;letter-spacing:.04em;padding:var(--space-1) 11px;border-radius:999px;border:1px solid rgba(12,163,12,.28)}
.topbar-right{display:flex;align-items:center;gap:10px}
/* 44x44 — standar Apple HIG/Material */
.icon-btn{width:44px;height:44px;border-radius:var(--radius-sm);border:1px solid var(--viz-border);background:var(--viz-surface);color:var(--viz-ink-2);display:flex;align-items:center;justify-content:center;transition:border-color .15s,color .15s}
.icon-btn:hover{background:var(--viz-page);border-color:var(--brand-blue);color:var(--brand-blue)}
/* tombol ini <a>, tidak tersentuh aturan global input/select/button */
.tg-btn{background:var(--brand-blue);color:#fff;border:none;padding:var(--space-2) var(--space-4);min-height:44px;display:inline-flex;align-items:center;justify-content:center;border-radius:var(--radius-sm);font-weight:600;font-size:13.5px;text-decoration:none}

/* max-width + margin-inline — cegah kartu/chart melebar berlebihan di layar sangat lebar */
.content{padding:var(--space-6) var(--space-7) 64px;max-width:1600px;margin-inline:auto}
.page-title{font-size:23px;font-weight:800;margin-bottom:var(--space-1);letter-spacing:-.02em}
.page-sub{color:var(--viz-ink-2);font-size:13.5px;margin-bottom:var(--space-5)}

/* ---------- SAPAAN + PEMILIH PERIODE ---------- */
.greet-row{margin-bottom:var(--space-4);display:flex;align-items:flex-end;justify-content:space-between;gap:var(--space-5);flex-wrap:wrap}
.greet-row h1{font-size:22px;font-weight:800;letter-spacing:-.02em;margin-bottom:3px}
.greet-row .greet-date{color:var(--viz-ink-2);font-size:13px}
.greet-row .periode-bar{margin:0}
.periode-bar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:var(--space-3) 0 var(--space-5)}
.periode-bar label{font-size:12px;color:var(--viz-ink-2);font-weight:600;letter-spacing:.02em;text-transform:uppercase}
.periode-status{font-size:12.5px;color:var(--viz-ink-2)}
.periode-bar.loading{opacity:.6;pointer-events:none}

/* ============================================================================
   DUA KARTU PAHLAWAN — pertanyaan yang menghadap ke depan
   Kiri  : "Aman sampai kapan"  (runway, sisi CFO)
   Kanan : "Badan ke arah mana" (lintasan berat, sisi ahli gizi)

   Dua-duanya sengaja dibikin setara: sebelum rombak ini, sisi badan ada di
   blok ke-11 dari 12 sementara semua menu sidebar isinya uang. Produk sudah
   jual dua hal sejak 2026-08-14; permukaannya baru nyusul sekarang.

   Riset yang mengunci jumlahnya: dashboard eksekutif 3-7 KPI di atas lipatan,
   batas satu layar 5-9 (Miller 7±2). Dua pahlawan + empat ringkas = enam.
   ============================================================================ */
.pillar-row{display:grid;grid-template-columns:1fr;gap:var(--space-4);margin-bottom:var(--space-4)}
.fwd{background:var(--viz-surface);border:1px solid var(--viz-border);border-radius:var(--radius);padding:var(--space-5) var(--space-6);box-shadow:var(--shadow-sm);display:flex;flex-direction:column;min-height:200px}
/* Kartu uang pakai gradasi navy (jangkar merek). Kartu badan tetap permukaan
   polos — dua gradasi bersebelahan bikin dua-duanya berhenti kebaca sebagai
   penekanan, dan yang hilang justru maksudnya. */
.fwd.anchor{background:linear-gradient(150deg,var(--brand-blue) 0%,var(--brand-navy) 55%,var(--brand-navy-deep) 100%);color:#fff;border:none;position:relative;overflow:hidden}
.fwd.anchor::before{content:"";position:absolute;inset:0;background-image:var(--thread);background-size:72px 72px;opacity:.18;pointer-events:none}
.fwd.anchor>*{position:relative;z-index:1}
.fwd-head{display:flex;align-items:center;gap:var(--space-2);font-family:'Poppins',sans-serif;font-size:11.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--viz-ink-2);margin-bottom:var(--space-3)}
.fwd.anchor .fwd-head{color:#b9d3f2}
.fwd-num{font-size:clamp(28px,3.6vw,42px);font-weight:800;line-height:1.05;margin-bottom:var(--space-2)}
.fwd.anchor .fwd-num{color:#fff}
.fwd-sub{font-size:14px;line-height:1.5;color:var(--viz-ink-2);margin-bottom:var(--space-3)}
.fwd.anchor .fwd-sub{color:#cfe0f6}
.fwd-spark{width:100%;height:52px;display:block;margin:auto 0 var(--space-3)}
/* LANGKAH — rules baku §9.2: tiap kartu yang nyebut angka wajib punya satu
   langkah di bawahnya. Ini garisnya, bukan saran. */
.fwd-step{margin-top:auto;padding-top:var(--space-3);border-top:1px solid var(--viz-grid);font-size:13.5px;font-weight:600;line-height:1.5}
.fwd.anchor .fwd-step{border-top-color:rgba(255,255,255,.18)}
.fwd-step a{color:var(--brand-blue);text-decoration:none;font-weight:700}
.fwd.anchor .fwd-step a{color:var(--brand-amber-soft)}
.fwd-step a:hover{text-decoration:underline}
/* Keadaan kosong. Riset retensi: kurva berhenti paling curam di 2 minggu
   pertama — persis waktu datanya paling tipis. Kartu kosong yang cuma nampilin
   sumbu hampa itu kegagalan di titik paling rawan. */
.fwd-empty{font-size:13.5px;line-height:1.6;color:var(--viz-ink-2)}
.fwd.anchor .fwd-empty{color:#cfe0f6}
.fwd-progress{display:flex;align-items:center;gap:var(--space-2);margin:var(--space-3) 0;font-size:12.5px;color:var(--viz-ink-2)}
.fwd-dots{display:flex;gap:5px}
.fwd-dots i{width:9px;height:9px;border-radius:50%;background:var(--viz-grid);display:block}
.fwd-dots i.on{background:var(--brand-blue)}

/* ---------- EMPAT KOTAK RINGKAS ---------- */
.kpi-quad{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--space-3);margin-bottom:var(--space-4)}
/* Kartu ringkas. Rombak 2026-08-16: hover DULU ngangkat kartu 2px
   (translateY). Diganti jadi perubahan warna tepi karena kartu ini bukan
   tombol — nol yang terjadi kalau diklik, jadi umpan balik "terangkat"
   itu janji palsu. Tepi yang menegas = "ini satu blok utuh", dan itu memang
   satu-satunya hal benar yang mau dibilang hover di sini. */
.tile{background:var(--viz-surface);border:1px solid var(--viz-border);border-radius:var(--radius);padding:var(--space-4) var(--space-5);box-shadow:var(--shadow-sm);transition:border-color .15s,box-shadow .15s}
.tile:hover{border-color:color-mix(in srgb,var(--brand-blue) 34%,transparent);box-shadow:var(--shadow-md)}
.tile .lbl{font-family:'Poppins',sans-serif;font-size:11px;color:var(--viz-ink-2);margin-bottom:var(--space-2);font-weight:600;letter-spacing:.06em;text-transform:uppercase;display:flex;align-items:center;gap:6px}
.tile .lbl .ico{width:var(--icon-sm);height:var(--icon-sm)}
.tile .val{font-size:clamp(20px,2.1vw,25px);font-weight:700;margin-bottom:var(--space-1);line-height:1.1}
.tile .delta{font-size:12.5px;font-weight:600;line-height:1.45}
.delta.good{color:var(--viz-good-text)}
.delta.bad{color:var(--status-critical)}
/* --viz-ink-2, bukan --viz-ink-muted: baris ini ada di empat kartu paling atas
   halaman dan isinya keterangan yang beneran dibaca ("buat 17 hari sisa bulan
   ini"), bukan hiasan. Tinta paling redup tempatnya di label sumbu, bukan di
   sini. */
.delta.neutral{color:var(--viz-ink-2)}

/* KPI angka mentah — turun ke lapis "detail", di bawah dua kartu pahlawan.
   Bukan dihapus: "saldoku berapa" tetap pertanyaan sah, dia cuma bukan
   pertanyaan PERTAMA lagi sejak produk ini jadi penasihat. */
.kpi-row{display:grid;grid-template-columns:1.5fr 1fr 1fr;gap:var(--space-3);margin-bottom:var(--space-4)}
.kpi-row.wide{grid-template-columns:1.6fr 1fr 1fr 1fr}
.kpi-row.solo{grid-template-columns:minmax(0,280px)}
.kpi-row.secondary{grid-template-columns:repeat(3,1fr);margin:var(--space-4) 0 var(--space-5)}
.tile.mini{padding:var(--space-3) var(--space-4)}
.tile.hero{background:linear-gradient(150deg,var(--brand-blue) 0%,var(--brand-navy) 55%,var(--brand-navy-deep) 100%);color:#fff;border:none;position:relative;overflow:hidden}
.tile.hero::before{content:"";position:absolute;inset:0;background-image:var(--thread);background-size:72px 72px;opacity:.18;pointer-events:none}
.tile.hero>*{position:relative;z-index:1}
.tile.hero .lbl{color:#b9d3f2}
.tile.hero .val{font-size:clamp(32px,4vw,46px);font-weight:800;color:#fff}
.tile.hero .val .cur{opacity:.62}

/* ============================================================================
   KARTU "PERLU DIURUS" — pengganti gauge "Skor Uang 3/100"
   Skor tanpa sebab bikin cemas, bukan bikin gerak: angka 3 dari 100 nggak
   nunjuk satu pun hal yang bisa dikerjain hari ini, dan itu melanggar aturan
   nomor satu rules baku (§0 tes 3: tiap masalah ditutup satu jalan keluar).

   Bukti yang milih bentuk penggantinya: alert saat kejadian + satu ketukan
   MINDAHIN DUIT nurunin biaya 17-19% (FCA/Grubb 2025) — dan mekanismenya
   mindahin duit, bukan ngerem belanja. Makanya tiap baris di sini punya satu
   tombol, dan tombolnya nyuruh melakukan sesuatu, bukan berhati-hati.
   ============================================================================ */
.urus-list{display:flex;flex-direction:column;gap:var(--space-2)}
.urus-item{display:flex;align-items:flex-start;gap:var(--space-3);padding:var(--space-3);border-radius:var(--radius-sm);background:var(--viz-page);border:1px solid var(--viz-border)}
.urus-item .urus-ico{flex-shrink:0;margin-top:1px}
.urus-item.crit .urus-ico{color:var(--status-critical)}
.urus-item.warn .urus-ico{color:var(--status-warn)}
.urus-item.info .urus-ico{color:var(--brand-blue)}
.urus-body{flex:1;min-width:0;font-size:13.5px;line-height:1.5}
.urus-body strong{display:block;margin-bottom:2px}
.urus-act{flex-shrink:0;align-self:center;background:var(--brand-blue);color:#fff;border:none;border-radius:999px;padding:var(--space-2) var(--space-4);font-size:12.5px;font-weight:700;text-decoration:none;display:inline-flex;align-items:center;min-height:44px;white-space:nowrap}
.urus-act:hover{background:var(--brand-navy)}

/* ---------- KARTU UMUM ---------- */
.card{background:var(--viz-surface);border:1px solid var(--viz-border);border-radius:var(--radius);padding:var(--space-5) var(--space-6);box-shadow:var(--shadow-sm)}
.card h3{font-size:15.5px;margin-bottom:3px;font-weight:700}
.card .card-sub{font-size:12.5px;color:var(--viz-ink-2);margin-bottom:var(--space-4);line-height:1.55}
.grid-2{display:grid;grid-template-columns:1.3fr 1fr;gap:var(--space-4);margin-bottom:var(--space-4)}
.grid-even{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-4)}
.section-gap{margin-top:var(--space-7)}
.hidden{display:none !important}

/* ---------- METER JATAH ---------- */
.meter-row{margin-bottom:var(--space-3)}
.meter-row:last-child{margin-bottom:0}
.meter-top{display:flex;justify-content:space-between;font-size:13px;margin-bottom:6px;gap:var(--space-3)}
.meter-top .name{font-weight:600}
.meter-top .figs{color:var(--viz-ink-2);white-space:nowrap}
.meter-top .figs strong{color:var(--viz-ink)}
.meter-track{height:9px;border-radius:999px;background:var(--viz-grid);overflow:hidden}
/* ujung data membulat 4px & menempel baseline — spesifikasi mark skill dataviz */
.meter-fill{height:100%;border-radius:999px;transition:width .4s cubic-bezier(.4,0,.2,1)}
.meter-fill.good{background:var(--status-good)}
.meter-fill.warn{background:var(--status-warn)}
.meter-fill.critical{background:var(--status-critical)}
.meter-status{display:flex;align-items:center;gap:5px;font-size:11.5px;font-weight:600;margin-top:5px}
.meter-status .ico{width:14px;height:14px}
.meter-status.good{color:var(--status-good)}
.meter-status.warn{color:#a06600}
.meter-status.critical{color:var(--status-critical)}
:root[data-theme="dark"] .meter-status.warn{color:var(--status-warn)}
@media (prefers-color-scheme:dark){:root:where(:not([data-theme="light"])) .meter-status.warn{color:var(--status-warn)}}

/* ---------- BAR POS ---------- */
.catbar-row{display:flex;align-items:center;gap:10px;margin-bottom:10px;font-size:13px}
.catbar-row:last-child{margin-bottom:0}
.catbar-label{width:170px;flex-shrink:0;display:flex;align-items:center;gap:7px}
.catbar-dot{width:10px;height:10px;border-radius:3px;flex-shrink:0}
.catbar-track{flex:1;background:var(--viz-page);border-radius:6px;height:20px;position:relative}
/* ujung data 4px membulat, pangkal rata (menempel baseline) — spesifikasi mark */
.catbar-fill{height:100%;border-radius:3px 5px 5px 3px;display:flex;align-items:center;position:relative;transition:width .4s cubic-bezier(.4,0,.2,1)}
/* angka pakai tinta teks, bukan warna seri — identitas dibawa titik warnanya */
.catbar-fill .amt{position:absolute;left:calc(100% + 9px);white-space:nowrap;font-weight:600;color:var(--viz-ink);font-size:12.5px;font-variant-numeric:tabular-nums}
.catbar-fill.tiny .amt{left:7px;color:#fff}
.catlegend{display:flex;flex-wrap:wrap;gap:var(--space-3);margin-top:var(--space-4);padding-top:var(--space-3);border-top:1px solid var(--viz-grid)}
.catlegend span{display:flex;align-items:center;gap:6px;font-size:11.5px;color:var(--viz-ink-2)}
.catlegend .catbar-dot{width:8px;height:8px}
.catwrap{display:flex;gap:var(--space-6);align-items:flex-start;flex-wrap:wrap}
.catwrap>.catbars-col{flex:1;min-width:220px}
.cat-donut-holder{flex-shrink:0;width:128px;height:128px;position:relative}
.cat-donut-holder svg{width:100%;height:100%;transform:rotate(-90deg)}
.cat-donut-holder .donut-total{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center}
.cat-donut-holder .donut-total strong{font-size:14.5px;font-weight:700;font-variant-numeric:lining-nums tabular-nums;line-height:1.2}
.cat-donut-holder .donut-total span{font-size:9.5px;color:var(--viz-ink-muted);font-weight:600}

/* ---------- CHART CASHFLOW ---------- */
.cf-wrap{position:relative}
#cfChart{width:100%;height:180px;display:block}
.cf-tooltip{position:absolute;pointer-events:none;background:var(--brand-navy-deep);color:#eaf0fa;font-size:12px;padding:9px 12px;border-radius:var(--radius-sm);opacity:0;transition:opacity .12s;white-space:nowrap;z-index:5;transform:translate(-50%,-110%);box-shadow:var(--shadow-md);font-variant-numeric:tabular-nums;border:1px solid rgba(255,255,255,.1)}
.cf-legend{display:flex;gap:var(--space-4);margin-top:var(--space-2);font-size:11.5px;color:var(--viz-ink-2)}
.cf-legend span{display:flex;align-items:center;gap:6px}
.cf-legend .sw{width:10px;height:10px;border-radius:3px}

/* ---------- KARTU "AMAN NGGAK BULAN INI?" (bullet graph, Few 2005) ---------- */
.bullet-card{margin-bottom:var(--space-4)}
.bullet-answer{font-size:17px;font-weight:700;line-height:1.35;margin:2px 0 6px;font-family:'Poppins',sans-serif;letter-spacing:-.01em}
.bullet-detail{font-size:14px;color:var(--viz-ink-2);line-height:1.55;margin-bottom:var(--space-3)}
.bullet-figs{display:flex;align-items:baseline;gap:9px;margin-bottom:10px;flex-wrap:wrap}
.bullet-num{font-size:clamp(26px,3vw,34px);font-weight:700;line-height:1.05}
.bullet-of{font-size:13.5px;color:var(--viz-ink-2);font-weight:500}
#bulletChart{width:100%;height:46px;display:block}
.bullet-legend{display:flex;gap:var(--space-4);margin-top:9px;font-size:11.5px;color:var(--viz-ink-2);flex-wrap:wrap}
.bullet-legend span{display:flex;align-items:center;gap:6px}
.bullet-legend i{display:inline-block;flex-shrink:0}
.bullet-legend .mk-bar{width:14px;height:9px;border-radius:3px;background:var(--viz-ink-muted)}
.bullet-legend .mk-pace{width:3px;height:14px;background:var(--viz-ink)}
.bullet-legend .mk-cap{width:14px;height:0;border-top:2px dashed var(--viz-baseline)}
.bullet-next{margin-top:var(--space-3);padding-top:13px;border-top:1px solid var(--viz-grid);font-size:13.5px;font-weight:600;color:var(--viz-ink);line-height:1.5}
.bullet-sub{margin:10px 0 0;font-size:12px}
.bullet-card h3{margin-bottom:var(--space-2)}

/* ---------- KURVA BELANJA ---------- */
.pace-card{margin-bottom:var(--space-4)}
#paceChart{width:100%;height:190px;display:block}
.pace-legend{display:flex;gap:var(--space-4);margin-top:var(--space-2);font-size:11.5px;color:var(--viz-ink-2);flex-wrap:wrap}
.pace-legend span{display:flex;align-items:center;gap:6px}
.pace-legend .ln{width:16px;height:0;border-top:3px solid var(--div-neg);border-radius:2px}
.pace-legend .ln.dash{border-top:2px dashed var(--viz-baseline)}

/* ---------- LAPORAN RADAR ---------- */
.radar-card{background:linear-gradient(150deg,var(--brand-blue) 0%,var(--brand-navy) 60%,var(--brand-navy-deep) 100%);border-radius:var(--radius);padding:var(--space-5) var(--space-6);color:#fff;position:relative;overflow:hidden;box-shadow:var(--shadow-sm);display:flex;gap:var(--space-3);align-items:flex-start}
.radar-card::before{content:"";position:absolute;inset:0;background-image:var(--thread);background-size:72px 72px;opacity:.16;pointer-events:none}
.radar-card>*{position:relative;z-index:1}
.radar-card .radar-icon{width:38px;height:38px;border-radius:50%;background:rgba(255,255,255,.14);border:1px solid rgba(255,255,255,.22);display:flex;align-items:center;justify-content:center;flex-shrink:0}
.radar-card .radar-body{flex:1;min-width:0}
.radar-card .radar-label{font-size:11px;font-weight:700;letter-spacing:.04em;color:#bcd4f5;margin-bottom:6px;display:flex;align-items:center;gap:6px}
.radar-card .radar-text{font-size:14.5px;line-height:1.5;margin-bottom:var(--space-3)}
.radar-card .radar-link{color:var(--brand-amber-soft);font-weight:700;font-size:13px;text-decoration:none}
.radar-card .radar-link:hover{text-decoration:underline}

/* ---------- LONCENG "AKU NYEBUT" ----------
   Kata "notifikasi" dilarang di permukaan user (rules baku §4.4 padanan wajib:
   notifikasi → aku nyebut). Nama class boleh tetap teknis; yang diganti
   label & aria-label-nya. */
.notif-btn{position:relative}
.notif-badge{position:absolute;top:var(--space-1);right:var(--space-1);min-width:15px;height:15px;padding:0 3px;border-radius:999px;background:var(--status-critical);color:#fff;font-size:9.5px;font-weight:800;display:flex;align-items:center;justify-content:center;line-height:1;border:1.5px solid var(--viz-surface)}
.notif-panel{position:absolute;top:calc(100% + var(--space-2));right:0;width:288px;background:var(--viz-surface);border:1px solid var(--viz-border);border-radius:var(--radius-sm);box-shadow:var(--shadow-md);padding:var(--space-2);z-index:30}
.notif-panel h4{font-size:12px;text-transform:uppercase;letter-spacing:.06em;color:var(--viz-ink-muted);padding:var(--space-2) var(--space-2) var(--space-1)}
.notif-panel .notif-item{display:flex;align-items:flex-start;gap:var(--space-2);width:100%;text-align:left;border:none;background:none;color:inherit;font:inherit;padding:9px var(--space-2);border-radius:var(--space-2);font-size:13px;cursor:pointer}
.notif-panel .notif-item:hover{background:var(--viz-page)}
.notif-panel .notif-item .ico{width:var(--icon-sm);height:var(--icon-sm);margin-top:2px}
.notif-panel .notif-item.crit .ico{color:var(--status-critical)}
.notif-panel .notif-item.warn .ico{color:var(--status-warn)}
.notif-panel .notif-empty{padding:var(--space-3) var(--space-2);font-size:13px;color:var(--viz-ink-2);line-height:1.55}

/* ---------- CATAT CEPAT ---------- */
.quickadd-card{margin-bottom:var(--space-5)}
.quickadd-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(84px,1fr));gap:10px;margin-top:var(--space-3)}
.quickadd-btn{display:flex;flex-direction:column;align-items:center;gap:7px;background:var(--viz-page);border:1px solid var(--viz-border);border-radius:var(--radius-sm);padding:var(--space-3) var(--space-2);min-height:76px;color:var(--viz-ink);font-size:12px;font-weight:600;transition:border-color .15s,background .15s}
.quickadd-btn:hover{border-color:var(--brand-blue);background:var(--brand-light)}
.quickadd-btn .qa-ico{width:34px;height:34px;border-radius:50%;background:var(--brand-blue);color:#fff;display:flex;align-items:center;justify-content:center}
.quickadd-btn .qa-ico .ico{width:var(--icon-sm);height:var(--icon-sm)}
.quickadd-btn.copied .qa-ico{background:var(--status-good)}
.quickadd-hint{font-size:11.5px;color:var(--viz-ink-muted);margin-top:-2px}

/* ============================================================================
   PILAR BADAN
   Aturan yang kepasang di sini, bukan cuma di niat (rules baku §9.9 + §7):
   · agregat kalori digambar sebagai RENTANG (batang min-max), bukan garis.
     Garis tunggal ngarang presisi yang datanya nggak punya. Angka PER MAKANAN
     boleh tunggal (ralat owner 2026-08-13).
   · berat & IMT ditampilin dan digrafikin, NOL komentar. Nol panah, nol
     persentase, nol kalimat sesudah angkanya.
   · hari tanpa catatan ditulis "belum ada catatan", nggak pernah "0 kkal" —
     angka nol tentang tubuh itu karangan.
   ============================================================================ */
.badan-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-4)}
.badan-kosong{font-size:13px;color:var(--viz-ink-2);line-height:1.6}
.badan-chip{display:inline-flex;align-items:baseline;gap:var(--space-2);padding:var(--space-2) var(--space-3);border-radius:10px;background:var(--viz-page);margin-top:10px}
.badan-chip b{font-size:20px}
.badan-chip span{font-size:13px;color:var(--viz-ink-2)}
.badan-stat{display:flex;justify-content:space-between;gap:var(--space-3);padding:9px 0;border-bottom:1px solid var(--viz-grid);font-size:14px}
.badan-stat:last-child{border-bottom:0}
.badan-stat span{color:var(--viz-ink-2)}
.badan-sumber{font-size:12px;color:var(--viz-ink-2);margin-top:10px;line-height:1.6}
#kalChart,#paceChart{width:100%;height:190px;display:block}
#beratChart,#jamChart{width:100%;height:150px;display:block}
#lintasanChart{width:100%;height:210px;display:block}
#makroChart{width:100%;height:170px;display:block}

/* Makro harian. Protein disorot dengan target personal — itu metrik yang riset
   bilang paling dilihat harian sesudah kalori, dan satu-satunya makro yang
   punya target per orang di produk ini. */
.makro-row{display:flex;align-items:center;gap:var(--space-3);margin-bottom:var(--space-3);font-size:13px}
.makro-row:last-child{margin-bottom:0}
.makro-name{width:82px;flex-shrink:0;color:var(--viz-ink-2)}
.makro-row.lead .makro-name{color:var(--viz-ink);font-weight:700}
.makro-track{flex:1;height:14px;border-radius:999px;background:var(--viz-page);overflow:hidden}
/* display:block WAJIB. .makro-track bukan flex container, jadi <span> di
   dalamnya tetap inline — dan height:100% nggak berlaku sama sekali di elemen
   inline. Tanpa baris ini semua bar makro tak terlihat, dan yang kebaca cuma
   trek kosong yang panjangnya seragam. */
.makro-fill{display:block;height:100%;border-radius:999px;background:var(--cat-1)}
.makro-row.lead .makro-fill{background:var(--cat-5)}
.makro-val{width:96px;flex-shrink:0;text-align:right;font-weight:600;font-variant-numeric:tabular-nums}

/* Riwayat makan. Skor Kenyang/Asli pakai band 1-3 / 4-6 / 7-10 dari
   copy-kalori-v4.md. Komentarnya cuma boleh dari tabel band itu — nol kata
   perintah gizi (rules baku §10). */
.makan-list{display:flex;flex-direction:column;gap:var(--space-2)}
.makan-item{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-3);border-radius:var(--radius-sm);background:var(--viz-page);border:1px solid var(--viz-border);flex-wrap:wrap}
.makan-jam{font-size:12px;color:var(--viz-ink-muted);width:44px;flex-shrink:0;font-variant-numeric:tabular-nums}
.makan-nama{flex:1;min-width:120px;font-size:13.5px;font-weight:600}
.makan-kal{font-size:13px;font-variant-numeric:tabular-nums;color:var(--viz-ink-2);white-space:nowrap}
.skor-chip{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:700;padding:3px 9px;border-radius:999px;white-space:nowrap}
.skor-chip.hi{background:rgba(12,163,12,.14);color:var(--viz-good-text)}
.skor-chip.mid{background:rgba(250,178,25,.18);color:#8a5b00}
.skor-chip.lo{background:rgba(208,59,59,.12);color:var(--status-critical)}
:root[data-theme="dark"] .skor-chip.mid{color:var(--status-warn)}
@media (prefers-color-scheme:dark){:root:where(:not([data-theme="light"])) .skor-chip.mid{color:var(--status-warn)}}

/* Pagar keselamatan ditampilkan, bukan disembunyikan. Kalau user kena blok
   keras (umur <18, IMT <18,5, hamil/menyusui), yang tampil penjelasan — bukan
   angka target yang nggak boleh dia kejar. */
.pagar-box{margin-top:var(--space-4);padding:var(--space-3) var(--space-4);border-radius:var(--radius-sm);background:var(--viz-page);border-left:3px solid var(--brand-blue);font-size:12.5px;color:var(--viz-ink-2);line-height:1.65}
.pagar-box strong{color:var(--viz-ink)}

/* ---------- KEBIASAAN: kalender streak ---------- */
.streak-cal{display:grid;grid-template-columns:repeat(auto-fill,minmax(16px,1fr));gap:4px;margin-top:var(--space-3)}
.streak-cal i{aspect-ratio:1;border-radius:3px;background:var(--viz-grid);display:block}
.streak-cal i.on{background:var(--status-good)}
.streak-cal i.today{outline:2px solid var(--brand-blue);outline-offset:1px}
.streak-legend{display:flex;gap:var(--space-3);margin-top:var(--space-3);font-size:11.5px;color:var(--viz-ink-2);flex-wrap:wrap}
.streak-legend span{display:flex;align-items:center;gap:6px}
.streak-legend i{width:10px;height:10px;border-radius:3px;display:block}

/* ---------- TABEL ---------- */
table{width:100%;border-collapse:collapse;font-size:13px}
th{text-align:left;color:var(--viz-ink-muted);font-weight:700;font-size:10.5px;text-transform:uppercase;letter-spacing:.07em;padding:9px 10px;border-bottom:1px solid var(--viz-grid);position:sticky;top:0;background:var(--viz-surface);z-index:1}
td{padding:10px;border-bottom:1px solid var(--viz-grid);font-variant-numeric:tabular-nums}
tbody tr{transition:background .12s}
tbody tr:hover{background:var(--viz-page)}
tr.current-month{background:var(--viz-page)}
tr.current-month td{font-weight:700}
.pill{display:inline-block;padding:2px 9px;border-radius:999px;font-size:11px;font-weight:600}
.pill.masuk{background:rgba(10,163,12,.12);color:var(--viz-good-text)}
.pill.keluar{background:rgba(208,59,59,.12);color:var(--status-critical)}
/* tabel 7 kolom butuh scroll-x terkontrol di layar sempit, bukan diperas
   sampai teksnya tak terbaca */
.table-scroll{max-height:420px;overflow:auto}

/* ---------- FORM & FILTER ---------- */
.filter-row{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:var(--space-4)}
.filter-row select, .filter-row input{border:1px solid var(--viz-border);background:var(--viz-surface);color:var(--viz-ink);padding:var(--space-2) var(--space-3);border-radius:var(--radius-sm);font-size:13px}
/* Aturan ini lebih spesifik dari aturan `select` global di bawah, jadi
   padding-nya menang — dan tanpa baris ini teks pilihan nabrak panah yang
   digambar aturan global. Sisi kanan disamain persis: 32px. */
.filter-row select{padding-right:32px}

/* ---------- KENDALI FORM: satu rupa buat semua ----------
   Ditambah 2026-08-16. Sebelumnya tiap <select> pakai rupa bawaan sistem —
   di Windows abu-abu kotak, di macOS pil biru, di Android segitiga hitam.
   Tiga rupa berbeda buat satu produk, dan nol di antaranya nyambung sama
   kartu di sekelilingnya. Panah digambar sendiri sebagai latar SVG supaya
   sama di mana-mana; padding kanan disisain buat dia. */
select{
  -webkit-appearance:none;appearance:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%235d6b80' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 10px center;
  padding-right:32px;border:1px solid var(--viz-border);border-radius:var(--radius-sm);
  background-color:var(--viz-surface);color:var(--viz-ink);
  padding-left:var(--space-3);font-size:13px;font-weight:500;
  transition:border-color .15s,box-shadow .15s}
select:hover{border-color:color-mix(in srgb,var(--brand-blue) 45%,transparent)}
/* Panah versi terang nggak kelihatan di atas permukaan gelap. Dua berkas SVG
   inline, bukan filter:invert — invert bikin panahnya putih pekat sementara
   teks di sebelahnya --viz-ink-2 yang jauh lebih redup. */
:root[data-theme="dark"] select{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%237286a4' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")}
@media(prefers-color-scheme:dark){
  :root:where(:not([data-theme="light"])) select{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%237286a4' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")}
}
/* Cincin fokus berjarak — kebaca di atas permukaan terang, gelap, MAUPUN
   tombol biru penuh. outline 2px lama ketelen di tombol primer. */
input:focus-visible, select:focus-visible, textarea:focus-visible{
  outline:none;box-shadow:var(--ring);border-color:var(--brand-blue)}
.filter-row input[type="search"]{flex:1;min-width:180px}
.goal-input-row{display:flex;gap:var(--space-2);margin-bottom:var(--space-4)}
.goal-input-row input{flex:1;border:1px solid var(--viz-border);background:var(--viz-page);color:var(--viz-ink);padding:9px var(--space-3);border-radius:var(--radius-sm);font-size:13.5px}
.goal-input-row button{background:var(--brand-blue);color:#fff;border:none;padding:9px var(--space-4);border-radius:var(--radius-sm);font-weight:600;font-size:13px}
.goal-meter-track{height:18px;border-radius:999px;background:var(--viz-grid);overflow:hidden}
/* target tabungan = momen "pencapaian" → aksen amber muted (Brand Bible §10 v1.5) */
.goal-meter-fill{height:100%;background:linear-gradient(90deg,var(--brand-amber),var(--brand-amber-soft));border-radius:999px;display:flex;align-items:center;justify-content:flex-end;padding-right:9px;transition:width .5s cubic-bezier(.4,0,.2,1)}
.goal-meter-fill span{color:#2B1D08;font-size:11px;font-weight:800;font-variant-numeric:tabular-nums}
.goal-note{font-size:11.5px;color:var(--viz-ink-muted);margin-top:var(--space-2);line-height:1.6}
.preset-btns{display:flex;gap:var(--space-2);margin-bottom:var(--space-3)}
.preset-btn{flex:1;border:1.5px solid var(--viz-border);background:var(--viz-surface);color:var(--viz-ink);padding:10px;border-radius:var(--radius-sm);font-size:12.5px;font-weight:600;text-align:center}
.preset-btn.active{border-color:var(--brand-blue);background:rgba(37,99,235,.08);color:var(--brand-blue)}
.preset-list{font-size:12.5px;margin-bottom:var(--space-3)}
.preset-list div{display:flex;justify-content:space-between;gap:var(--space-3);padding:5px 0;border-bottom:1px dashed var(--viz-grid)}
.copy-btn{width:100%;background:var(--brand-navy);color:#fff;border:none;padding:10px;border-radius:var(--radius-sm);font-weight:600;font-size:13px}
.copy-btn.copied{background:var(--status-good)}
.tool-form label{display:block;font-size:12.5px;color:var(--viz-ink-2);margin:10px 0 var(--space-1)}
.tool-form input{width:100%;border:1px solid var(--viz-border);background:var(--viz-page);color:var(--viz-ink);padding:9px var(--space-3);border-radius:var(--radius-sm);font-size:13.5px}
.tool-result{margin-top:var(--space-4);padding:var(--space-3);border-radius:10px;font-size:13.5px;display:none;line-height:1.6}
.tool-result.show{display:block}
.tool-result.good{background:rgba(10,163,12,.1);color:var(--viz-good-text)}
.tool-result.warn{background:rgba(250,178,25,.15);color:#8a5b00}
.tool-result.critical{background:rgba(208,59,59,.1);color:var(--status-critical)}
:root[data-theme="dark"] .tool-result.warn{color:var(--status-warn)}
@media (prefers-color-scheme:dark){:root:where(:not([data-theme="light"])) .tool-result.warn{color:var(--status-warn)}}
.tool-btn{margin-top:var(--space-3);width:100%;background:var(--brand-blue);color:#fff;border:none;padding:10px;border-radius:var(--radius-sm);font-weight:600;font-size:13px}
.filter-note{font-size:11.5px;color:var(--viz-ink-muted);margin:10px 2px 0;line-height:1.5}
.btn-export-csv{border-color:var(--brand-blue);color:var(--brand-blue);font-weight:600;display:inline-flex;align-items:center;gap:6px}
.btn-export-csv:hover{background:var(--brand-blue);color:#fff;border-color:var(--brand-blue)}

/* ---------- OVERLAY: LOGIN & PAYWALL ---------- */
/* layar masuk: navy pekat + tekstur thread — kesan "brankas", bukan halaman kosong */
.overlay{position:fixed;inset:0;background:var(--brand-navy-deep);z-index:100;display:flex;align-items:center;justify-content:center;padding:var(--space-6)}
.overlay::before{content:"";position:absolute;inset:0;background-image:var(--thread);background-size:72px 72px;opacity:.12;pointer-events:none}
.overlay::after{content:"";position:absolute;inset:0;background:radial-gradient(80% 60% at 50% 0%,rgba(46,123,224,.22),transparent 70%);pointer-events:none}
.overlay-card{background:var(--viz-surface);border:1px solid var(--viz-border);border-radius:22px;padding:42px 38px;max-width:410px;text-align:center;box-shadow:0 30px 70px -20px rgba(0,0,0,.55);position:relative;z-index:1}
.overlay-card .logo-big{width:58px;height:58px;margin-bottom:18px}
.overlay-card h2{font-size:21px;margin-bottom:9px;font-weight:800;letter-spacing:-.02em}
.overlay-card p{font-size:13.5px;color:var(--viz-ink-2);margin-bottom:var(--space-5);line-height:1.6}
.tg-login-holder{display:flex;justify-content:center;margin-top:var(--space-2)}
.overlay-card .upgrade-btn{display:inline-block;background:var(--brand-blue);color:#fff;text-decoration:none;padding:var(--space-3) var(--space-6);border-radius:999px;font-weight:600;font-size:14px}
.overlay-error{font-size:12.5px;color:var(--status-critical);margin-top:var(--space-3);line-height:1.6}
.overlay-shield{width:44px;height:44px;margin:0 auto var(--space-3);color:var(--brand-blue)}

/* ============================================================================
   GERAK — masuk bertahap, grafik "digambar", umpan balik tekan
   Ditambah 2026-08-16. Sebelumnya nol animasi masuk sama sekali: kartu dan
   grafik muncul serentak begitu data nyampe, dan itu kebaca datar buat
   dashboard yang isinya justru "di mana pun kamu lihat, ada gerak yang berarti".
   Semua durasi 150-400ms (ui-ux-pro-max §7), transform/opacity SAJA — nol
   width/height/top/left dianimasikan, jadi nol reflow/CLS.
   ============================================================================ */
@keyframes gai-rise{
  from{opacity:0;transform:translateY(10px) scale(.98)}
  to{opacity:1;transform:none}
}
.gai-enter{animation:gai-rise .42s cubic-bezier(.16,1,.3,1) both}

/* Grafik "digambar": svg-nya sendiri yang fade+tumbuh dari baseline, bukan
   isinya satu-satu (SVG diganti total tiap render lewat innerHTML, jadi
   menganimasikan tiap <rect>/<circle> baru berarti nulis ulang tiap fungsi
   di charts.js). Transform-origin bottom = kesan "naik dari dasar", cocok
   buat delapan grafik ini yang semuanya berbaseline di bawah. */
@keyframes gai-chart-in{
  from{opacity:0;transform:scaleY(.88) translateY(6px)}
  to{opacity:1;transform:none}
}
.chart-in{animation:gai-chart-in .5s cubic-bezier(.16,1,.3,1) both;transform-origin:bottom}

/* UMPAN BALIK TEKAN — semua tombol aksi yang punya tujuan jelas (submit,
   ambil, catat, salin) mengecil tipis saat ditekan. ui-ux-pro-max §7
   scale-feedback: 0.95-1.05, transform saja, pulih waktu dilepas. */
.tg-btn, .urus-act, .quickadd-btn, .tool-btn, .copy-btn, .goal-input-row button,
.pwa-primary, .pwa-secondary, .preset-btn, .btn-export-csv, .icon-btn, .upgrade-btn{
  transition:transform .12s cubic-bezier(.4,0,.2,1),background .15s,border-color .15s,color .15s,box-shadow .15s}
.tg-btn:active, .urus-act:active, .quickadd-btn:active, .tool-btn:active, .copy-btn:active,
.goal-input-row button:active, .pwa-primary:active, .pwa-secondary:active, .preset-btn:active,
.btn-export-csv:active, .icon-btn:active, .upgrade-btn:active{transform:scale(.96)}

@media(prefers-reduced-motion:reduce){
  .gai-enter,.chart-in{animation:none;opacity:1;transform:none}
  .tg-btn, .urus-act, .quickadd-btn, .tool-btn, .copy-btn, .goal-input-row button,
  .pwa-primary, .pwa-secondary, .preset-btn, .btn-export-csv, .icon-btn, .upgrade-btn{transition:none}
  .tg-btn:active, .urus-act:active, .quickadd-btn:active, .tool-btn:active, .copy-btn:active,
  .goal-input-row button:active, .pwa-primary:active, .pwa-secondary:active, .preset-btn:active,
  .btn-export-csv:active, .icon-btn:active, .upgrade-btn:active{transform:none}
}

/* ---------- KARTU HERO: DENYUT AMBIEN ----------
   .fwd.anchor, .tile.hero, dan .radar-card sudah gradasi navy — sekarang
   gradasinya bergeser pelan (12s, satu siklus nyaris tak terlihat kalau
   dipelototin, tapi kelihatan sebagai "kartu ini hidup" di ekor mata).
   background-position dianimasikan (bukan background sendiri) supaya GPU-
   compositable dan nggak memicu repaint per-pixel tiap frame. */
.fwd.anchor, .tile.hero, .radar-card{
  background-size:180% 180% !important;
  animation:gai-gradient-drift 14s ease-in-out infinite}
@keyframes gai-gradient-drift{
  0%,100%{background-position:0% 30%}
  50%{background-position:100% 70%}
}
@media(prefers-reduced-motion:reduce){
  .fwd.anchor, .tile.hero, .radar-card{animation:none}
}

/* ---------- RANGKA MUAT (skeleton) ----------
   Ditambah 2026-08-16. Sebelum ini, jeda antara "overlay Memuat… hilang" dan
   "angka masuk" nampilin kartu kosong berisi strip "—". Itu kebaca sebagai
   NOL, bukan sebagai BELUM — dan nol yang salah di kartu runway artinya user
   ngira dia bangkrut selama sedetik. Rangka abu-abu nggak pernah kebaca
   sebagai angka.
   aria-hidden dipasang pemanggilnya; screen reader dapat kabarnya lewat
   aria-live yang sudah ada, bukan lewat kotak kosong. */
.skel{display:block;border-radius:6px;background:var(--viz-grid);
  background-image:linear-gradient(90deg,transparent 0%,color-mix(in srgb,var(--viz-surface) 60%,transparent) 50%,transparent 100%);
  background-size:200% 100%;background-repeat:no-repeat;
  animation:skel-sweep 1.4s ease-in-out infinite}
.skel.line{height:12px;margin-bottom:8px}
.skel.line:last-child{margin-bottom:0}
.skel.num{height:30px;width:62%;margin-bottom:10px}
.skel.chart{height:150px;border-radius:var(--radius-sm)}
/* Varian "angka basi": dipakai waktu periode diganti dan angka lama masih
   nempel di layar. Teksnya dibikin transparan, BUKAN dihapus — jadi kotaknya
   persis selebar angkanya sendiri dan nol pergeseran tata letak waktu angka
   baru masuk. Nilai lamanya tetap utuh di DOM, jadi kalau permintaannya gagal
   angka yang benar tinggal muncul lagi. */
.skel-angka{color:transparent !important;border-radius:6px;
  background:var(--viz-grid);
  background-image:linear-gradient(90deg,transparent 0%,color-mix(in srgb,var(--viz-surface) 60%,transparent) 50%,transparent 100%);
  background-size:200% 100%;background-repeat:no-repeat;
  animation:skel-sweep 1.4s ease-in-out infinite}
.skel-angka .cur{color:transparent !important}
@media(prefers-reduced-motion:reduce){.skel-angka{animation:none;background-image:none}}
@keyframes skel-sweep{from{background-position:200% 0}to{background-position:-100% 0}}
/* Kilau yang jalan terus itu persis jenis gerak yang bikin sebagian orang
   pusing. Rangkanya tetap tampil — yang mati cuma sapuannya. */
@media(prefers-reduced-motion:reduce){.skel{animation:none;background-image:none}}

/* ---------- AKSESIBILITAS LINTAS-KOMPONEN ---------- */
:focus-visible{outline:2px solid var(--brand-blue-bright);outline-offset:2px;border-radius:4px}
/* Semua <input>/<select>/<button> dijamin tinggi minimum 44px sekali jalan.
   <a> (tg-btn, sb-link, urus-act) ditangani terpisah karena selector ini
   tidak menjangkau tag <a>. */
input, select, button{min-height:44px}
.skip-link{position:absolute;top:-48px;left:var(--space-2);background:var(--brand-blue);color:#fff;padding:10px var(--space-4);border-radius:var(--space-2);z-index:200;font-size:13px;font-weight:600;text-decoration:none;transition:top .15s}
.skip-link:focus{top:var(--space-2)}
@media(prefers-color-scheme:dark){:root:where(:not([data-theme="light"])) .skip-link{background:var(--brand-blue-bright)}}
:root[data-theme="dark"] .skip-link{background:var(--brand-blue-bright)}

/* ---------- TOAST BERSAMA ---------- */
/* z-index 95: tetap DI BAWAH .overlay (100) — jadi tidak akan pernah menutupi
   kartu login atau paywall walau dipanggil di saat yang salah — tapi DI ATAS
   kartu install (90). Posisi vertikalnya digeser lewat --toast-bottom waktu
   kartu install terbuka. */
.gai-toast{position:fixed;left:50%;bottom:var(--toast-bottom,18px);transform:translate(-50%,140%);z-index:95;
  display:flex;align-items:center;gap:var(--space-3);max-width:min(92vw,520px);
  background:var(--viz-ink);color:var(--viz-page);border-radius:var(--radius);
  padding:13px var(--space-4);box-shadow:var(--shadow-md);font-size:13.5px;line-height:1.45;
  opacity:0;pointer-events:none;transition:transform .22s ease,opacity .22s ease}
.gai-toast.show{transform:translate(-50%,0);opacity:1;pointer-events:auto}
.gai-toast .toast-text{flex:1}
.gai-toast .toast-action{background:transparent;border:1px solid currentColor;
  color:inherit;border-radius:999px;padding:6px var(--space-3);font-size:12.5px;font-weight:700;
  cursor:pointer;white-space:nowrap}
.gai-toast .toast-action:hover{background:rgba(255,255,255,.14)}

/* ---------- AJAKAN PASANG (PWA) ---------- */
/* z-index 90 dipilih sadar: ketiga overlay ber-z-index 100, jadi kartu ini
   SECARA FISIK tidak bisa menutupi login atau paywall walaupun mesin state-nya
   nanti salah. Pertahanan berlapis, bukan cuma mengandalkan logika JS. */
.pwa-prompt{position:fixed;left:var(--space-3);right:var(--space-3);bottom:var(--space-3);z-index:90;
  background:var(--viz-surface);border:1px solid var(--viz-border);
  border-radius:var(--radius);box-shadow:var(--shadow-md);padding:var(--space-4) var(--space-4) var(--space-3);
  display:flex;gap:var(--space-3);align-items:flex-start;
  transform:translateY(110%);opacity:0;pointer-events:none;
  transition:transform .22s ease,opacity .22s ease}
.pwa-prompt.show{transform:translateY(0);opacity:1;pointer-events:auto}
.pwa-prompt .pwa-icon{width:40px;height:40px;flex:none}
.pwa-prompt .pwa-body{flex:1;min-width:0}
.pwa-prompt h3{font-size:15px;font-weight:800;letter-spacing:-.01em;margin-bottom:var(--space-1);color:var(--viz-ink)}
.pwa-prompt p{font-size:12.5px;color:var(--viz-ink-2);line-height:1.55;margin-bottom:var(--space-3)}
.pwa-prompt .pwa-actions{display:flex;gap:var(--space-2);flex-wrap:wrap}
.pwa-prompt .pwa-primary{background:var(--brand-blue);color:#fff;border:none;border-radius:999px;
  padding:10px 22px;font-weight:600;font-size:13.5px;cursor:pointer}
.pwa-prompt .pwa-secondary{background:transparent;color:var(--viz-ink-2);border:1px solid var(--viz-border);
  border-radius:999px;padding:10px 18px;font-weight:600;font-size:13.5px;cursor:pointer}
.pwa-prompt .pwa-close{position:absolute;top:2px;right:var(--space-1);background:none;border:none;
  width:44px;padding:0;line-height:1;color:var(--viz-ink-muted);cursor:pointer;display:flex;align-items:center;justify-content:center}
/* [hidden] harus menang atas display:flex di .pwa-prompt — tanpa baris ini
   atribut hidden-nya tidak berpengaruh sama sekali dan kartunya ikut terbaca
   screen reader sejak halaman dimuat, jauh sebelum boleh muncul. */
.pwa-prompt[hidden]{display:none}
.pwa-share-glyph{width:15px;height:15px;vertical-align:-2px;margin:0 2px}
@media(min-width:760px){
  .pwa-prompt{left:auto;right:var(--space-5);bottom:var(--space-5);max-width:380px}
}

/* ============================================================================
   RESPONSIF
   ============================================================================ */
@media(max-width:1180px){
  .kpi-quad{grid-template-columns:repeat(2,1fr)}
  .kpi-row{grid-template-columns:1.4fr 1fr 1fr}
  .kpi-row.wide{grid-template-columns:1fr 1fr}
  .kpi-row.wide .tile.hero{grid-column:1 / -1}
}
@media(max-width:980px){
  .shell{grid-template-columns:1fr;background:none}
  .sidebar{position:relative;height:auto;display:flex;flex-wrap:wrap;gap:var(--space-1);align-items:center}
  .sb-link.active::before{display:none}
  .sb-logo{width:100%}
  .sb-section{display:none}
  .pillar-row{grid-template-columns:1fr}
  .badan-grid,.grid-even{grid-template-columns:1fr}
  .grid-2{grid-template-columns:1fr}
  .greet-row{display:block}
  .greet-row .periode-bar{margin:var(--space-3) 0 0}
  .kpi-row{grid-template-columns:repeat(2,1fr)}
  .kpi-row .tile.hero{grid-column:1 / -1}
  #bulletChart{height:52px}
}
@media(max-width:560px){
  .content{padding:18px var(--space-4) 48px}
  /* Menu 11 item bakal membungkus jadi 5 baris di HP dan ngedorong dua kartu
     pahlawan jauh ke bawah lipatan. Sekarang satu baris yang digeser ke
     samping — sama seperti penanganan 7 item sebelumnya. */
  .sidebar{flex-wrap:nowrap;overflow-x:auto;padding:var(--space-3) var(--space-3);gap:6px;
    scrollbar-width:none;-webkit-overflow-scrolling:touch}
  .sidebar::-webkit-scrollbar{display:none}
  .sb-logo{width:auto;margin:0 var(--space-2) 0 0;flex-shrink:0}
  .sb-link{flex-shrink:0;white-space:nowrap;margin-bottom:0}
  /* ---- ANGGARAN TINGGI LAYAR PERTAMA DI HP ----
     Diukur di 375×780. Sebelum blok ini, chrome atas makan 274px (sidebar 68 +
     topbar 123 + sapaan 151) dan dua kartu pahlawan masing-masing 340px — jadi
     kartu BADAN mulai di 732px dan nggak kelihatan sama sekali di layar
     pertama. Itu ngebubarin seluruh premis rombakan ini: dua pilar setara,
     dua-duanya kejawab dalam lima detik.

     Yang dipangkas dan alasannya:
     · lastUpdated  — jam pembaruan. Lencana LIVE di sebelahnya sudah bilang
                      hal yang sama, dan dia yang bikin topbar jadi dua baris.
     · periodeLabel — teksnya diulang persis di pStatus di baris yang sama.
     · tinggi spark — arah garis tetap kebaca di 34px; 52px itu ruang, bukan
                      informasi.
     Nol angka yang dihapus. Yang hilang cuma pengulangan dan udara. */
  .topbar{padding:var(--space-2) var(--space-4);gap:var(--space-2);flex-wrap:nowrap}
  #lastUpdated{display:none}
  /* "Buka di Telegram" membungkus jadi dua baris di 375px dan bikin seluruh
     topbar ikut tinggi. Kata "Buka di" itu basa-basi — tombolnya sudah jelas
     mau ngapain dari labelnya sendiri. */
  .tg-btn{padding:var(--space-2) var(--space-3);font-size:12.5px;white-space:nowrap}
  .tg-full{display:none}
  .topbar-right{gap:6px}
  #periodeLabel{display:none}
  .greet-row{margin-bottom:var(--space-3)}
  .greet-row .periode-bar{margin:var(--space-2) 0 0;gap:var(--space-2)}
  .periode-status{font-size:11.5px}

  /* Empat kotak ringkas jadi dua kolom. Ditumpuk satu kolom bikin kartu
     pahlawan kedua kedorong keluar layar pertama. */
  .kpi-quad{grid-template-columns:1fr 1fr;gap:10px}
  .pillar-row{gap:10px}
  .fwd{padding:var(--space-4);min-height:0}
  .fwd-num{font-size:30px;margin-bottom:var(--space-1)}
  .fwd-head{margin-bottom:var(--space-2)}
  .fwd-sub{font-size:13px;line-height:1.45;margin-bottom:var(--space-2)}
  .fwd-spark{height:34px;margin-bottom:var(--space-2)}
  .fwd-step{font-size:12.5px;padding-top:var(--space-2)}
  .fwd-empty{font-size:13px;line-height:1.5}
  .tile{padding:var(--space-3) var(--space-4)}
  .tile .val{font-size:21px}
  .kpi-row{grid-template-columns:1fr 1fr;gap:10px}
  .kpi-row .tile.hero{grid-column:1 / -1}
  .tile.hero .val{font-size:36px}
  /* Nominal pos dulu ditaruh di luar ujung bar dan nembus tepi layar
     (halaman jadi bisa digeser ke samping). Di HP dipindah ke dalam trek. */
  .catbar-row{flex-wrap:wrap;gap:6px}
  .catbar-label{width:100%}
  .catbar-track{width:100%;height:24px}
  .catbar-fill{position:static}
  .catbar-fill .amt{left:auto;right:9px;top:50%;transform:translateY(-50%)}
  .catbar-fill.tiny .amt{color:var(--viz-ink)}
  .kpi-row.secondary{grid-template-columns:1fr;gap:var(--space-2)}
  .kpi-row.secondary .tile{display:flex;align-items:center;justify-content:space-between;gap:var(--space-3);padding:var(--space-3) var(--space-4)}
  .kpi-row.secondary .tile .lbl,
  .kpi-row.secondary .tile .val{margin-bottom:0}
  .kpi-row.secondary .tile .delta{display:none}
  #bulletChart{height:58px}
  .bullet-legend{flex-direction:column;gap:5px}
  .bullet-answer{font-size:16px}
  /* Angka besarnya udah disebut di kalimat detail — di HP dia cuma nambah
     tinggi dan ngedorong grafik ke bawah lipatan. */
  .bullet-figs{display:none}
  .greet-row h1{font-size:20px}
  .makan-item{gap:var(--space-2)}
  .makro-name{width:64px}
  .makro-val{width:80px;font-size:12px}
  /* tabel 7 kolom dikasih lebar minimum + scroll-x terkontrol di .table-scroll,
     dibanding diperas sampai "Toko"/"Item" tak terbaca */
  table{min-width:640px}
}

/* Gerakan dimatikan buat yang minta gerak seminimal mungkin. Data tetap
   kebaca instan — animasi di sini nol perannya buat memahami angka. */
@media(prefers-reduced-motion:reduce){
  .gai-toast,.pwa-prompt,.meter-fill,.catbar-fill,.goal-meter-fill,.tile{transition:none}
  .tile:hover{transform:none}
}
