/* ============================================================
   GUNEX MOTION — Lapisan animasi & mikro-interaksi bersama
   ------------------------------------------------------------
   TUJUAN: memberi kesan modern, halus, dan "mahal" pada seluruh
   aplikasi TANPA MENGUBAH SATU PUN TATA LETAK (layout).

   ATURAN EMAS yang dipatuhi berkas ini:
   1. Hanya properti yang TIDAK memicu reflow yang dianimasikan:
      transform, opacity, box-shadow, filter, background-color,
      border-color, color. Tidak pernah width/height/margin/padding/
      top/left/display/gap/font-size.
   2. Semua aturan "keadaan" (hover/aktif) memakai :where(...) supaya
      bobot selektornya NOL. Artinya: bila sebuah halaman sudah punya
      gaya sendiri untuk elemen itu, gaya halaman SELALU menang.
      Berkas ini hanya mengisi yang belum diatur — tidak menimpa.
   3. Semua efek otomatis mati bila pengguna mengaktifkan
      "Kurangi Gerakan" (prefers-reduced-motion) di HP/laptopnya,
      dan saat mencetak (print) — supaya hasil cetak tetap bersih.
   4. Berlaku sama di desktop, iPad, Android, dan iOS. Efek "hover"
      hanya aktif pada perangkat bermouse; perangkat sentuh mendapat
      umpan balik "tekan" (press) yang lebih cocok untuk jari.

   Dipakai bersama /motion.js. CSS ini tetap aman bila JS gagal
   dimuat: yang hilang hanya efek riak & animasi bertahap, tidak ada
   konten yang menjadi tak terlihat.
   ============================================================ */

:root{
  /* Durasi — sengaja pendek. Animasi yang lambat terasa lambat,
     bukan mewah. 120–340ms adalah rentang yang terasa "instan tapi halus". */
  --gnx-t-fast: 130ms;
  --gnx-t:      220ms;
  --gnx-t-slow: 340ms;

  /* Kurva gerak. --gnx-ease-soft meniru gerak "decelerate" khas iOS:
     cepat di awal, mendarat perlahan — inilah yang membuat kesan premium. */
  --gnx-ease:      cubic-bezier(.22,.61,.36,1);
  --gnx-ease-soft: cubic-bezier(.16,1,.30,1);
  --gnx-ease-in:   cubic-bezier(.55,.06,.68,.19);

  /* Warna aksen untuk bilah progres & cincin fokus. Memakai variabel
     tema bila halaman menyediakannya, dengan cadangan biru Gunex. */
  --gnx-accent:      var(--blue, #2456b3);
  --gnx-accent-soft: var(--blue-bright, #3f7fd6);
}


/* ============================================================
   1. TRANSISI HALAMAN KE HALAMAN
   ============================================================ */

/* --- 1a. Cara modern (Chrome/Edge/Android & Safari 18.2+) ---
   View Transitions lintas-dokumen: browser memotret halaman lama,
   lalu memudarkannya ke halaman baru secara mulus — persis seperti
   aplikasi native. Tidak ada JavaScript, tidak ada jeda navigasi.
   Browser yang belum mendukung cukup mengabaikan blok ini. */
@view-transition{ navigation: auto; }

/* Sengaja HANYA opacity — tanpa skala.
   Versi sebelumnya menyertakan scale(.992) dan scale(1.006); pada layar
   besar pergeseran sub-piksel dari penskalaan itu membuat teks tampak
   bergetar sesaat, sehingga perpindahan terasa tidak mulus. */
::view-transition-old(root){ animation: gnxPageOut 140ms var(--gnx-ease-in) both; }
::view-transition-new(root){ animation: gnxPageIn 220ms var(--gnx-ease) both; }
@keyframes gnxPageOut{ from{ opacity:1; } to{ opacity:0; } }
@keyframes gnxPageIn { from{ opacity:0; } to{ opacity:1; } }

/* --- 1b. Cadangan untuk browser lama (Firefox, Safari lawas) ---
   motion.js menambahkan kelas .gnx-leaving ke <html> saat pengguna
   menekan tautan. Halaman meredup selama browser mengambil halaman
   berikutnya, jadi perpindahan tidak terasa "patah".
   Navigasi TIDAK ditahan sedikit pun — kelas ini murni visual. */
html.gnx-leaving body{
  /* SENGAJA TIDAK 0.
     Peredupan ini berjalan selama browser mengambil halaman berikutnya.
     Bila diredupkan sampai 0 dan jaringannya lambat, layar benar-benar
     KOSONG selama seluruh masa tunggu — persis keluhan "blank saat buka".
     Diredupkan sebagian saja: perpindahan tetap terasa, tapi halaman lama
     masih terbaca sampai halaman baru siap. */
  opacity: .55;
  transition: opacity 150ms var(--gnx-ease-in);
}


/* ============================================================
   2. BILAH PROGRES TIPIS DI ATAS LAYAR
   Muncul saat berpindah halaman atau saat data sedang diambil.
   position:fixed → tidak memengaruhi tata letak apa pun.
   ============================================================ */
.gnx-progress{
  position: fixed;
  top: 0; left: 0;
  height: 3px;
  width: 0;
  z-index: 2147483600;
  pointer-events: none;
  border-radius: 0 4px 4px 0;
  /* Gradien lebih panjang daripada barnya sendiri lalu digeser perlahan,
     sehingga warna terlihat mengalir ke depan — bukan blok warna mati. */
  background: linear-gradient(90deg,
    var(--gnx-accent) 0%,
    var(--gnx-accent-soft) 38%,
    #7fc4ff 58%,
    var(--gnx-accent-soft) 78%,
    var(--gnx-accent) 100%);
  background-size: 240% 100%;
  box-shadow: 0 0 12px rgba(63,127,214,.55), 0 1px 4px rgba(36,86,179,.35);
  opacity: 0;
  transition: width 240ms var(--gnx-ease), opacity 260ms var(--gnx-ease);
}
.gnx-progress.on{
  opacity: 1;
  animation: gnxBarAlir 1.8s linear infinite;
}
/* Kepala bercahaya di ujung depan — memberi kesan bar sedang "berlari",
   bukan sekadar melebar. Digambar sebagai pseudo-element, jadi tidak
   menambah satu pun simpul DOM. */
.gnx-progress::after{
  content: '';
  position: absolute;
  right: 0;
  top: -2px;
  width: 72px;
  height: 7px;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(255,255,255,.95), rgba(160,205,255,.35) 55%, transparent 100%);
  opacity: .85;
  animation: gnxBarDenyut 1.1s ease-in-out infinite;
}
@keyframes gnxBarAlir{
  from{ background-position: 120% 0; }
  to  { background-position: -120% 0; }
}
@keyframes gnxBarDenyut{
  0%, 100%{ opacity: .55; transform: scaleX(.85); }
  50%     { opacity: 1;   transform: scaleX(1.15); }
}
/* Mode hemat: warna solid, tanpa aliran dan tanpa kepala bercahaya. */
html[data-gnx-tier="hemat"] .gnx-progress{ animation: none; background: var(--gnx-accent); }
html[data-gnx-tier="hemat"] .gnx-progress::after{ display: none; }


/* ============================================================
   3. MIKRO-INTERAKSI TOMBOL & KARTU
   ============================================================ */

/* 3a. Pasang transisi pada semua elemen yang bisa ditekan.
   Selektor sengaja memakai pola [class*="-btn"] dsb. supaya ikut
   menjangkau penamaan khas tiap menu (gs-btn, iv-btn, pay-btn,
   iv2-btn, bk-btn, ...) tanpa perlu menyentuh berkas CSS menu itu. */
button,
[role="button"],
summary,
select,
.btn, .bk-btn, .qcard, .menu-card, .tile, .rk-card,
.arrange-btn, .arrange-reset, .year-btn, .cat-chip,
[class*="-btn"], [class*="-tab"], [class*="-chip"], [class*="-pill"],
[class*="-card"], [class*="-kpi"], [class*="-tile"], [class*="-opt"],
[class*="-seg-btn"], [class*="-toggle"], [class*="-switch"],
input[type="submit"], input[type="button"], input[type="reset"]{
  -webkit-tap-highlight-color: transparent;
  transition:
    transform        var(--gnx-t-fast) var(--gnx-ease),
    box-shadow       var(--gnx-t)      var(--gnx-ease),
    background       var(--gnx-t)      var(--gnx-ease),
    border-color     var(--gnx-t)      var(--gnx-ease),
    color            var(--gnx-t)      var(--gnx-ease),
    opacity          var(--gnx-t)      var(--gnx-ease),
    filter           var(--gnx-t)      var(--gnx-ease);
}
/* CATATAN: dipakai `background`, BUKAN `background-color`. Beberapa tombol
   dan kartu aplikasi memakai gradient (linear-gradient) yang berubah saat
   disorot; `background-color` saja akan membuat perubahan itu mematah. */

/* 3b. Umpan balik TEKAN — berlaku di semua perangkat, paling terasa
   di layar sentuh (menggantikan kotak abu-abu bawaan Android/iOS yang
   sudah dimatikan di atas).
   PENTING: TANPA `:not(:disabled)`. Penambahan itu menaikkan bobot
   selektor menjadi 0,2,0 sehingga akan MENIMPA aturan asli aplikasi
   seperti `.gs-btn:active`, `.iv-btn:active`, dan `.iv2-btn:active`.
   Dengan bobot 0,1,0, gaya halaman selalu menang — dan tombol nonaktif
   tetap aman karena sudah dikunci aturan 3d di bawah. */
/* WADAH DIKECUALIKAN — ini sumber masalah nyata.
   `[class*="-tab"]` juga cocok dengan "-table", sehingga `.pay-table-wrap`
   (wadah GULIR tabel Cek Bayar) ikut mengecil saat disentuh. Menyeret jari
   untuk menggulir jadi terasa seperti menekan tombol, dan barisnya sulit
   diklik. Hal sama terjadi pada `.pay-tabs` dan `.pay-field-btn` yang
   sebenarnya hanya pembungkus, bukan tombol.
   Semua pengecualian ditulis di dalam :where() sehingga bobot selektor
   tetap 0 — gaya asli aplikasi tidak akan tertimpa. */
:where(button, [role="button"], .btn, .bk-btn, .arrange-btn, .arrange-reset,
       input[type="submit"], input[type="button"],
       [class*="-btn"]:not([class*="-wrap"]):not([class*="-field"]):not([class*="-group"]):not([class*="-bar"]),
       [class*="-seg-btn"],
       [class*="-tab"]:not([class*="table"]):not([class*="-tabs"]):not([class*="-wrap"]),
       [class*="-chip"]:not([class*="-wrap"]):not([class*="-list"])):active{
  transform: scale(.965);
}
/* Efek tekan pada KARTU hanya untuk kartu yang benar-benar bisa diklik.
   Sebelumnya memakai [class*="-card"] dan [class*="-tile"] — selektor itu
   mengenai HAMPIR SEMUA kotak di aplikasi (.gs-card, .ts-card, .iv-card,
   dan seterusnya), padahal sebagian besar hanya wadah tampilan. Hasil
   pemeriksaan: di menu Gaji Supir, Tabungan, dan Laporan, TIDAK SATU PUN
   kotak yang kena efek ini bisa diklik. Efek tekan pada benda yang tidak
   bisa ditekan menyesatkan — seolah akan terjadi sesuatu, padahal tidak. */
:where(.qcard, .menu-card, .tile, .rk-card, [class*="-opt"]):active{
  transform: scale(.988);
}

/* 3c. Hover — HANYA untuk perangkat bermouse. Di HP, :hover bisa
   "nyangkut" setelah disentuh; media query ini mencegahnya. */
@media (hover: hover) and (pointer: fine){
  :where(button, .btn, .bk-btn, [class*="-btn"], input[type="submit"]):hover{
    transform: translateY(-1px);
  }
  :where(.qcard, .menu-card, .tile, .rk-card, [class*="-card"]):hover{
    box-shadow: 0 12px 30px rgba(15,23,42,.10);
  }
  :where([class*="-tab"], [class*="-chip"], [class*="-pill"]):hover{
    filter: brightness(1.04);
  }
}

/* 3d. Tombol nonaktif tidak boleh ikut bergerak. */
button:disabled, [class*="-btn"]:disabled, .btn:disabled{
  transform: none !important;
}

/* 3e. Efek riak (ripple) yang digambar motion.js.
   Wadahnya position:fixed & pointer-events:none, jadi benar-benar
   melayang di atas halaman — nol pengaruh ke tata letak. */
.gnx-ripple-box{
  position: fixed;
  overflow: hidden;
  pointer-events: none;
  z-index: 2147483500;
  contain: strict;
}
.gnx-ripple{
  position: absolute;
  border-radius: 50%;
  background: currentColor;
  opacity: .26;
  transform: translate(-50%, -50%) scale(0);
  animation: gnxRipple 540ms var(--gnx-ease) forwards;
}
@keyframes gnxRipple{
  to{ transform: translate(-50%,-50%) scale(1); opacity: 0; }
}


/* ============================================================
   4. MASUKNYA KONTEN SECARA BERTAHAP (STAGGER)
   Kartu, baris tabel, dan KPI muncul berurutan dengan jeda sangat
   pendek. Inilah efek yang paling membuat aplikasi terasa "hidup".
   transform+opacity saja → posisi elemen di halaman tidak bergeser.
   ============================================================ */
.gnx-reveal{
  opacity: 0;
  animation: gnxRise 320ms var(--gnx-ease-soft) forwards;
  animation-delay: var(--gnx-d, 0ms);
}
@keyframes gnxRise{
  from{ opacity:0; transform: translateY(9px); }
  to  { opacity:1; transform: none; }
}

/* Varian untuk baris tabel: geser sangat tipis supaya tabel panjang
   tidak terlihat "bergoyang". */
tr.gnx-reveal{
  animation-name: gnxRiseRow;
}
@keyframes gnxRiseRow{
  from{ opacity:0; transform: translateY(5px); }
  to  { opacity:1; transform: none; }
}


/* ============================================================
   5. DIALOG / OVERLAY / MODAL
   Semua menu memakai nama kelas berbeda (gs-overlay, iv-overlay,
   pay-overlay, iv2-overlay, match-overlay, gnx-overlay, ...), maka
   dicocokkan dengan pola [class*="overlay"].
   Animasi berjalan otomatis begitu elemen tampil — baik karena
   disisipkan ke halaman maupun karena display-nya berubah.
   ============================================================ */
[class*="overlay"]:not(.gnx-no-motion){
  animation: gnxBackdropIn 190ms var(--gnx-ease);
}
[class*="overlay"]:not(.gnx-no-motion) > *{
  animation: gnxDialogIn 300ms var(--gnx-ease-soft);
}
@keyframes gnxBackdropIn{
  from{ opacity: 0; }
  to  { opacity: 1; }
}
@keyframes gnxDialogIn{
  from{ opacity: 0; transform: translateY(12px) scale(.975); }
  to  { opacity: 1; transform: none; }
}
/* Catatan: fill-mode sengaja TIDAK dipakai, sehingga setelah animasi
   selesai tidak ada sisa transform pada dialog. Ini penting agar
   elemen position:fixed di dalam dialog (menu tarik-turun, tooltip)
   tetap berperilaku normal. */

/* Panel tab yang baru aktif. Bobot nol, jadi halaman yang sudah punya
   animasi sendiri (mis. .iv-panel.active) tetap memakai miliknya. */
:where([class*="-panel"], [class*="-pane"], [class*="-tabpanel"]).active{
  animation: gnxPanelIn 300ms var(--gnx-ease-soft);
}
@keyframes gnxPanelIn{
  from{ opacity:0; transform: translateY(7px); }
  to  { opacity:1; transform: none; }
}


/* ============================================================
   6. FORMULIR — kolom isian, pesan sukses & galat
   ============================================================ */
input, textarea, select{
  transition:
    border-color var(--gnx-t) var(--gnx-ease),
    box-shadow   var(--gnx-t) var(--gnx-ease),
    background-color var(--gnx-t) var(--gnx-ease);
}

/* Cincin fokus papan ketik (bukan saat diklik mouse) — membantu
   navigasi Tab tanpa mengubah ukuran elemen sedikit pun. */
:where(a, button, input, select, textarea, [tabindex]):focus-visible{
  outline: 2px solid var(--gnx-accent);
  outline-offset: 2px;
}

/* Pesan galat bergetar halus saat kelas .err dipasang — pengguna
   langsung tahu ada yang salah tanpa harus mencari teksnya. */
:where([class*="-msg"], [class*="-error"], [class*="-hint"], [class*="-status"]).err,
:where([class*="-error"], [class*="error-"]):not(:empty){
  animation: gnxShake 400ms var(--gnx-ease);
}
@keyframes gnxShake{
  0%,100%{ transform: translateX(0); }
  18%    { transform: translateX(-5px); }
  38%    { transform: translateX(4px); }
  58%    { transform: translateX(-3px); }
  78%    { transform: translateX(2px); }
}

/* Pesan berhasil muncul dengan "pop" lembut. */
:where([class*="-msg"], [class*="-status"]).ok{
  animation: gnxPopSoft 340ms var(--gnx-ease-soft);
}
@keyframes gnxPopSoft{
  from{ opacity:0; transform: translateY(-4px); }
  to  { opacity:1; transform: none; }
}


/* ============================================================
   7. ANGKA YANG BERUBAH
   motion.js memasang kelas ini ketika isi sebuah angka (KPI, total,
   saldo) berganti — mata langsung tertuju ke nilai yang baru.
   ============================================================ */
.gnx-flip{
  animation: gnxFlip 420ms var(--gnx-ease-soft);
}
@keyframes gnxFlip{
  0%  { opacity:.25; transform: translateY(6px); }
  60% { opacity:1;   transform: translateY(-1px); }
  100%{ opacity:1;   transform: none; }
}


/* ============================================================
   8. KEADAAN MEMUAT
   Teks "Memuat…" berdenyut lembut, dan wadah yang sedang menunggu
   data ikut meredup — bukan diam membeku.
   ============================================================ */
:where(.loading, [class*="loading-"], [class*="-loading"], [class*="-empty"]:empty){
  animation: gnxBreathe 1.5s ease-in-out infinite;
}
@keyframes gnxBreathe{
  0%,100%{ opacity: .55; }
  50%    { opacity: 1; }
}


/* ============================================================
   9. TOAST / NOTIFIKASI RINGAN
   Sebagian besar menu sudah punya transisi toast sendiri; aturan
   berbobot nol ini hanya melengkapi yang belum punya.
   ============================================================ */
:where([class*="toast"]).show{
  animation: gnxToastIn 340ms var(--gnx-ease-soft);
}
@keyframes gnxToastIn{
  from{ opacity:0; transform: translateY(14px); }
  to  { opacity:1; transform: none; }
}


/* ============================================================
   10. PENYESUAIAN PERANGKAT SENTUH (iOS & Android)
   ============================================================ */
@media (max-width: 1024px){
  /* SISA YANG DIHAPUS (gelombang pertama).
     Aturan ini dulu memasang -webkit-overflow-scrolling ke semua elemen
     bernama "table-wrap"/"-scroll"/"-tabs" tanpa memeriksa apakah mereka
     benar-benar menggulir. Itulah pola yang merebut sentuhan dari wadah
     gulir di dalamnya (bug Daftar Tujuan & Tabel Riwayat). Tugas ini kini
     bahwa elemennya memang menggulir. */
  /* (DIHAPUS) Penekanan ekstra dalam (scale .95) khusus layar sentuh.
     Menghasilkan tiga tingkat kedalaman yang berbeda-beda antar perangkat,
     dan terasa berlebihan. Satu tingkat (.965 dari aturan 3b) sudah cukup
     terasa dan konsisten di semua layar. */
}

/* Hilangkan kilatan biru bawaan Android pada seluruh area sadap. */
a, button, label, summary, [role="button"], [class*="-card"], [class*="-tab"]{
  -webkit-tap-highlight-color: transparent;
}


/* ============================================================
   11. MUNCUL SAAT DIGULIR (SCROLL REVEAL)
   Konten di bawah layar baru muncul ketika benar-benar terlihat.
   Dikendalikan IntersectionObserver di motion.js — TANPA event
   'scroll' sama sekali, jadi menggulir tetap ringan di HP lawas.
   Setiap elemen hanya diamati sekali lalu dilepas.
   ============================================================ */
.gnx-sr{
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 320ms var(--gnx-ease-soft),
              transform 320ms var(--gnx-ease-soft);
  transition-delay: var(--gnx-d, 0ms);
}
.gnx-sr.gnx-sr-on{ opacity: 1; transform: none; }


/* ============================================================
   12. GAMBAR & FOTO
   Thumbnail (bukti bayar, foto barang, galeri) tidak lagi
   "melompat" muncul, tetapi larut masuk begitu selesai dimuat.
   Hanya opacity — ruang gambar sudah dipesan sejak awal oleh
   tata letak asli, jadi tidak ada pergeseran sedikit pun.
   ============================================================ */
.gnx-img{ opacity: 0; transition: opacity 420ms var(--gnx-ease); }
.gnx-img.gnx-img-on{ opacity: 1; }


/* ============================================================
   13. BILAH ATAS SAAT HALAMAN DIGULIR
   Begitu konten mulai lewat di bawahnya, bilah atas mengangkat
   diri dengan bayangan tipis — memberi kesan berlapis seperti
   aplikasi native. Dideteksi lewat "sentinel" IntersectionObserver,
   bukan event scroll.
   ============================================================ */
:where([class*="topbar"], [class*="-head"], header){
  transition: box-shadow var(--gnx-t) var(--gnx-ease),
              background-color var(--gnx-t) var(--gnx-ease);
}
/* (DIHAPUS) Bayangan bilah atas berbasis kelas html.gnx-scrolled.
   Kelas itu dipasang-lepas di <html> tiap kali menggulir melewati puncak,
   dan setiap perubahan pada <html> membatalkan gaya seluruh dokumen —
   terlihat sebagai kedipan. Tugasnya diambil alih .gnx-stuck yang
   bekerja per-elemen. */


/* ============================================================
   14. BARIS DAFTAR & TABEL
   Sentuhan kecil yang membuat data terasa "bisa disentuh".
   ============================================================ */
tbody tr, [class*="-row"], [class*="-item"]{
  transition: background-color var(--gnx-t-fast) var(--gnx-ease),
              transform var(--gnx-t-fast) var(--gnx-ease);
}
@media (hover: hover) and (pointer: fine){
  :where(tbody tr:hover, [class*="-row"]:hover, [class*="-item"]:hover){
    background-color: rgba(36,86,179,.045);
  }
  /* Panah "buka" pada kartu ikut maju — isyarat arah yang jelas.
     CATATAN: `.go` pada Portal SENGAJA TIDAK disertakan. admin.css sudah
     menggeser `.menu-card:hover .go svg` sejauh 3px; bila wadah `.go`
     digeser juga, panah bergerak dua kali (6px) dan teks "Buka" ikut
     melenceng dari posisinya. */
  :where(.menu-card, .qcard, .rk-card):hover :where([class*="-arrow"], [class*="-go"]){
    transform: translateX(3px);
  }
}
:where([class*="-arrow"], [class*="-go"]){
  transition: transform var(--gnx-t) var(--gnx-ease-soft);
}


/* ============================================================
   15. FORMULIR — kolom aktif sedikit "terangkat"
   Hanya bayangan & warna garis: ukuran kolom tidak berubah,
   sehingga tidak ada satu pun elemen yang bergeser.
   ============================================================ */
:where(.field, [class*="-field"], label):focus-within{
  --gnx-focus: 1;
}
:where(input, textarea, select):focus{
  box-shadow: 0 0 0 3px rgba(36,86,179,.14);
}
/* Kotak centang & sakelar ikut merespons sentuhan. */
:where(input[type="checkbox"], input[type="radio"]){
  transition: transform var(--gnx-t-fast) var(--gnx-ease);
}
:where(input[type="checkbox"], input[type="radio"]):active{ transform: scale(.88); }
:where(input[type="checkbox"], input[type="radio"]):checked{
  animation: gnxCheck 300ms var(--gnx-ease-soft);
}
@keyframes gnxCheck{
  0%  { transform: scale(.8); }
  55% { transform: scale(1.12); }
  100%{ transform: scale(1); }
}


/* ============================================================
   16. TAB YANG BARU AKTIF
   Tab yang dipilih memberi "denyut" singkat supaya mata langsung
   tahu ke mana fokus berpindah.
   ============================================================ */
:where([class*="-tab"], [class*="-seg-btn"], [role="tab"]).active{
  animation: gnxTabOn 320ms var(--gnx-ease-soft);
}
@keyframes gnxTabOn{
  0%  { transform: translateY(2px); opacity: .6; }
  100%{ transform: none; opacity: 1; }
}

/* Lencana / badge yang nilainya berubah ikut berdenyut sekali. */
.gnx-pulse{ animation: gnxPulse 460ms var(--gnx-ease-soft); }
@keyframes gnxPulse{
  0%  { transform: scale(1); }
  40% { transform: scale(1.14); }
  100%{ transform: scale(1); }
}


/* ============================================================
   17. LACI / MENU GESER (halaman publik)
   ============================================================ */
:where([class*="mobile-nav"], [class*="-drawer"], [class*="-sheet"]){
  transition: transform var(--gnx-t-slow) var(--gnx-ease-soft),
              opacity var(--gnx-t) var(--gnx-ease);
}
:where([class*="nav-overlay"], [class*="-scrim"]){
  transition: opacity var(--gnx-t) var(--gnx-ease);
}


/* ============================================================
   18. MODE HEMAT — kunci "tidak memberatkan perangkat"
   motion.js memasang data-gnx-tier="hemat" pada <html> bila:
     • perangkat berRAM/inti kecil, atau mode hemat data aktif, ATAU
     • pengawas laju gambar mendeteksi halaman tersendat.
   Efek yang paling mahal dimatikan, efek murah tetap jalan —
   jadi aplikasi tetap terasa hidup, bukan mati total.
   ============================================================ */
html[data-gnx-tier="hemat"]{
  --gnx-t-fast: 90ms;
  --gnx-t:      150ms;
  --gnx-t-slow: 220ms;
}
html[data-gnx-tier="hemat"] .gnx-ripple-box{ display: none !important; }
html[data-gnx-tier="hemat"] .gnx-sr{ transform: none; }        /* cukup memudar */
html[data-gnx-tier="hemat"] [class*="overlay"]:not(.gnx-no-motion) > *{
  animation-duration: 180ms;
}
html[data-gnx-tier="hemat"] :where(.loading, [class*="-loading"]){
  animation: none; opacity: .7;
}
html[data-gnx-tier="hemat"] tr.gnx-reveal{ animation: none; opacity: 1; }
/* Bayangan besar adalah salah satu efek termahal untuk digambar ulang;
   di mode hemat cukup bayangan tipis. */
html[data-gnx-tier="hemat"] :where(.qcard,.menu-card,.tile,.rk-card,[class*="-card"]):hover{
  box-shadow: 0 4px 12px rgba(15,23,42,.08);
}

/* Gulir mulus hanya untuk perangkat yang sanggup. */
html:not([data-gnx-tier="hemat"]){ scroll-behavior: smooth; }


/* ============================================================
   19. FITUR LANJUTAN
   ============================================================ */

/* --- 19a. MENUTUP DIALOG ---
   Saat modal ditutup, motion.js menampilkan "bayangan" (salinan mati
   tanpa id dan tanpa pointer-events) lalu memudarkannya. Dialog aslinya
   sudah benar-benar dilepas oleh kode menu, jadi keadaan aplikasi tidak
   tersentuh sama sekali. */
.gnx-ghost{
  pointer-events: none !important;
  animation: gnxDialogOut 200ms var(--gnx-ease-in) forwards;
}
.gnx-ghost > *{
  animation: gnxDialogBoxOut 200ms var(--gnx-ease-in) forwards;
}
@keyframes gnxDialogOut{
  from{ opacity: 1; } to{ opacity: 0; }
}
@keyframes gnxDialogBoxOut{
  from{ opacity: 1; transform: none; }
  to  { opacity: 0; transform: translateY(8px) scale(.98); }
}
/* Overlay yang ditutup dengan melepas kelas .show perlu tetap tampil
   selama animasi keluar berjalan. */
[class*="overlay"].gnx-closing{
  display: flex !important;
  animation: gnxDialogOut 200ms var(--gnx-ease-in) forwards;
}
[class*="overlay"].gnx-closing > *{
  animation: gnxDialogBoxOut 200ms var(--gnx-ease-in) forwards;
}


/* --- 19b. (DIHAPUS) Animasi tombol -> centang ---
   Dihapus atas permintaan karena terlalu banyak kasus tepi. Konfirmasi
   keberhasilan dikembalikan ke toast bawaan tiap menu.
   gnxOkIn dipertahankan karena masih dipakai spinner tunggu. */
@keyframes gnxOkIn { from{ opacity:0; transform: scale(.9); } to{ opacity:1; transform:none; } }


/* --- 19c. (DIHAPUS) Kilau hijau pada baris/kartu yang berubah ---
   Dihapus atas permintaan: seringkali menyala di kartu yang tidak
   diubah, karena perubahan kecil apa pun di dalam kartu terbaca
   sebagai "isinya berganti". */


/* --- 19d. BARIS MELUNCUR SAAT DIURUT / DISARING (teknik FLIP) ---
   motion.js mengukur posisi lama, membiarkan menu menggambar ulang,
   lalu menggeser tiap baris kembali ke posisi lamanya dan melepasnya.
   Hanya transform: alur dokumen sudah final sebelum animasi dimulai. */
.gnx-move{
  transition: transform 340ms var(--gnx-ease-soft);
  will-change: transform;
}


/* --- 19e. KERANGKA MUAT (SKELETON) ---
   Digambar di lapisan absolut berkoordinat dokumen, sehingga ikut
   tergulir bersama halaman namun tetap di luar alur tata letak. */
@keyframes gnxFadeIn{ from{ opacity:0; } to{ opacity:1; } }


/* --- 19f. KARTU MENU MELEBUR JADI HALAMAN ---
   View Transitions dengan elemen bersama: kartu yang ditekan di Portal
   dan bilah atas halaman tujuan diberi nama yang sama, sehingga browser
   memuaikan yang satu menjadi yang lain. Hanya di browser yang mendukung;
   sisanya tetap mendapat pudar biasa. */


/* --- 19g. MODE HEMAT: matikan yang mahal --- */
html[data-gnx-tier="hemat"] .gnx-move{ transition: none; }


/* ============================================================
   20. GELOMBANG KEDUA FITUR
   ============================================================ */

/* --- 20a. GESER DARI TEPI UNTUK KEMBALI ---
   Halaman mengikuti jari. transform saja: alur dokumen tidak berubah,
   dan begitu gerakan selesai nilainya dibersihkan total. */
.gnx-swipe-anim{ transition: transform 280ms var(--gnx-ease-soft); }
.gnx-swipe-shadow{ box-shadow: -14px 0 34px rgba(15,23,42,.20); }


/* --- 20b. TITIK PIN ---
   Memantul saat diisi, bergetar saat PIN salah. */
.gnx-pin-pop{ animation: gnxPinPop 260ms var(--gnx-ease-soft); }
@keyframes gnxPinPop{
  0%  { transform: scale(1); }
  45% { transform: scale(1.18); }
  100%{ transform: scale(1); }
}
.gnx-pin-shake{ animation: gnxShake 420ms var(--gnx-ease); }


/* --- 20c. TOMBOL MENUNGGU (SPINNER) ---
   Lapisan melayang di atas tombol, sama seperti tanda centang: teks dan
   lebar tombol tidak disentuh sehingga tidak ada yang melompat. */
.gnx-spin-box{
  position: fixed;
  z-index: 2147483500;
  pointer-events: none;
  display: flex;
  align-items: center;
  justify-content: center;
  animation: gnxOkIn 110ms var(--gnx-ease-soft);
}
.gnx-spin-box b{
  display: block;
  width: 18px; height: 18px;
  border-radius: 50%;
  border: 2.5px solid currentColor;
  border-top-color: transparent;
  animation: gnxSpin 620ms linear infinite;
}
@keyframes gnxSpin{ to{ transform: rotate(360deg); } }


/* --- 20d. FOTO MEMBESAR DARI THUMBNAIL --- */
.gnx-zoom{
  transition: transform 360ms var(--gnx-ease-soft), opacity 200ms var(--gnx-ease);
  will-change: transform;
}


/* --- 20e. PANAH URUT BERBALIK ---
   Membalik pada sumbu Y lalu kembali ke identitas, jadi tidak pernah
   melenceng dari arah panah yang sebenarnya digambar aplikasi. */
.gnx-arrowflip{ animation: gnxArrowFlip 340ms var(--gnx-ease-soft); }
@keyframes gnxArrowFlip{
  0%  { transform: scaleY(1); }
  50% { transform: scaleY(-1); }
  100%{ transform: scaleY(1); }
}


/* --- 20f. ELEMEN LENGKET SAAT MENEMPEL ---
   Bilah atas / bilah filter mengangkat diri hanya ketika benar-benar
   sedang menempel, bukan asal halaman digulir. */
.gnx-stuck{ box-shadow: 0 6px 18px rgba(15,23,42,.14) !important; }


/* --- 20g. HITUNG MUNDUR TOAST ---
   Garis tipis melayang di tepi bawah toast, menunjukkan sisa waktu
   sebelum pesan hilang sendiri. */


/* --- 20h. (DIHAPUS) Napas hijau pada data yang baru tersimpan ---
   Dihapus bersama kilau hijau di atas: sumber warnanya sama dan
   sama-sama sering keliru sasaran. */


/* --- 20i. MODE HEMAT --- */
html[data-gnx-tier="hemat"] .gnx-zoom{ transition: none; }
html[data-gnx-tier="hemat"] .gnx-spin-box b{ animation-duration: 900ms; }


/* ============================================================
   21. PENCARIAN & KEMBALI KE ATAS
   ============================================================ */

/* --- 21a. SOROT HASIL PENCARIAN ---
   Memakai CSS Custom Highlight API: sorotan digambar browser di atas
   teks TANPA menyisipkan satu elemen pun ke dalam halaman. Ini penting
   di aplikasi billing — membungkus teks tabel dengan <mark> berisiko
   bentrok dengan kode menu yang membaca atau menggambar ulang isinya.
   Browser yang belum mendukung cukup tidak menampilkan sorotan; tidak
   ada yang rusak. */
::highlight(gnx-cari){
  background-color: rgba(250, 204, 21, .55);
  color: inherit;
}

/* --- 21b. TOMBOL KEMBALI KE ATAS ---
   position:fixed -> di luar alur dokumen, nol dampak tata letak.
   Menghormati area aman iPhone (notch bawah). */
.gnx-top{
  position: fixed;
  right: 18px;
  /* Jarak dari bawah dihitung motion.js agar tidak menutupi tombol
     melayang atau bilah tab yang sudah ada di halaman (mis. tombol
     tambah kendaraan di Gunex Fleet). */
  /* Nilai bawaan; motion.js menimpanya langsung pada elemen ini bila
     ada tombol melayang lain yang perlu dihindari. */
  bottom: calc(18px + env(safe-area-inset-bottom, 0px));
  width: 44px;
  height: 44px;
  z-index: 2147483400;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 50%;
  cursor: pointer;
  background: var(--gnx-accent);
  color: #fff;
  box-shadow: 0 6px 20px rgba(15,23,42,.28);
  opacity: 0;
  transform: translateY(16px) scale(.9);
  pointer-events: none;
  transition: opacity var(--gnx-t) var(--gnx-ease),
              transform var(--gnx-t) var(--gnx-ease-soft);
}
.gnx-top.on{
  opacity: .93;
  transform: none;
  pointer-events: auto;
}
.gnx-top:active{ transform: scale(.92); }
.gnx-top svg{ width: 20px; height: 20px; }
.gnx-top path{ fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }

html[data-gnx-tier="hemat"] .gnx-top{ transition-duration: 120ms; }


/* ============================================================
   22. GELOMBANG KEEMPAT
   ============================================================ */

/* --- 22a. INDIKATOR KONEKSI ---
   Bilah melayang di puncak layar. position:fixed -> nol dampak tata
   letak, dan tidak ada biaya sama sekali saat koneksi normal karena
   hanya bereaksi pada event 'online'/'offline' bawaan browser. */
.gnx-net{
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 2147483580;
  padding: 7px 14px;
  padding-top: calc(7px + env(safe-area-inset-top, 0px));
  text-align: center;
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .01em;
  color: #fff;
  background: #b91c1c;
  transform: translateY(-100%);
  transition: transform 300ms var(--gnx-ease-soft), background-color 300ms var(--gnx-ease);
  pointer-events: none;
}
.gnx-net.on{ transform: none; }
.gnx-net.pulih{ background: #15803d; }


/* --- 22b. SEDANG MENGUNGGAH ---
   CATATAN JUJUR: aplikasi mengunggah lewat fetch, dan fetch TIDAK bisa
   melaporkan kemajuan unggahan (hanya XMLHttpRequest yang bisa). Jadi
   yang ditampilkan adalah keadaan "sedang berjalan", bukan persentase
   palsu yang mengarang angka. */
.gnx-upload{
  position: fixed;
  z-index: 2147483500;
  pointer-events: none;
  overflow: hidden;
  border-radius: 10px;
  background: rgba(15,23,42,.55);
  display: flex;
  align-items: center;
  justify-content: center;
  animation: gnxFadeIn 200ms var(--gnx-ease);
}
.gnx-upload::after{
  content: '';
  position: absolute;
  left: 0; bottom: 0;
  height: 3px; width: 40%;
  background: linear-gradient(90deg, transparent, #fff, transparent);
  animation: gnxSlideBar 1.1s linear infinite;
}
.gnx-upload b{
  color: #fff;
  font-size: .74rem;
  font-weight: 600;
}
@keyframes gnxSlideBar{
  from{ transform: translateX(-100%); }
  to  { transform: translateX(350%); }
}


/* --- 22c. PERPINDAHAN TAB SESUAI ARAH ---
   Panel masuk dari kanan bila menuju tab berikutnya, dari kiri bila
   menuju tab sebelumnya. Mata jadi tahu arah perpindahan.
   Bobot 0,2,0 sehingga menang atas animasi panel netral di Bagian 5. */
[class*="-panel"].gnx-dari-kanan,
[class*="-pane"].gnx-dari-kanan{ animation: gnxDariKanan 300ms var(--gnx-ease-soft); }
[class*="-panel"].gnx-dari-kiri,
[class*="-pane"].gnx-dari-kiri{ animation: gnxDariKiri 300ms var(--gnx-ease-soft); }
@keyframes gnxDariKanan{
  from{ opacity: 0; transform: translateX(18px); }
  to  { opacity: 1; transform: none; }
}
@keyframes gnxDariKiri{
  from{ opacity: 0; transform: translateX(-18px); }
  to  { opacity: 1; transform: none; }
}


/* --- 22d. ADA PERUBAHAN BELUM DISIMPAN ---
   Cincin lembut berdenyut pada tombol simpan. Hanya box-shadow:
   ukuran dan posisi tombol tidak berubah sedikit pun. */
.gnx-nudge{ animation: gnxNudge 2.2s var(--gnx-ease) infinite; }
@keyframes gnxNudge{
  0%, 100%{ box-shadow: 0 0 0 0 rgba(36,86,179,.42); }
  45%     { box-shadow: 0 0 0 7px rgba(36,86,179,0); }
}
html[data-gnx-tier="hemat"] .gnx-nudge{ animation-duration: 3.2s; }


/* ============================================================
   23. GRAFIK MENGGAMBAR DIRINYA
   ------------------------------------------------------------
   Seluruh grafik aplikasi ini berupa div CSS (bukan canvas), dengan
   tinggi/lebar dipasang sebagai style inline oleh kode menu.

   ATURAN PENTING: nilai inline itu TIDAK BOLEH disentuh — itu data,
   bukan hiasan. Yang dianimasikan adalah `transform: scale`, yang
   berakhir tepat di scale(1). Jadi ukuran akhir batang selalu persis
   sama dengan yang dihitung aplikasi, dan tidak ada satu pun
   perhitungan tata letak yang dipicu selama animasi.
   ============================================================ */

/* Batang tegak (Statistik): tumbuh dari dasar. */
.gnx-bar-y{
  transform-origin: center bottom;
  animation: gnxBarY 620ms var(--gnx-ease-soft) both;
  animation-delay: var(--gnx-d, 0ms);
}
@keyframes gnxBarY{
  from{ transform: scaleY(0); opacity: .35; }
  to  { transform: scaleY(1); opacity: 1; }
}

/* Batang mendatar (Laporan, Gaji Supir, Tabungan Supir): tumbuh dari kiri. */
.gnx-bar-x{
  transform-origin: left center;
  animation: gnxBarX 620ms var(--gnx-ease-soft) both;
  animation-delay: var(--gnx-d, 0ms);
}
@keyframes gnxBarX{
  from{ transform: scaleX(0); opacity: .35; }
  to  { transform: scaleX(1); opacity: 1; }
}

html[data-gnx-tier="hemat"] .gnx-bar-y,
html[data-gnx-tier="hemat"] .gnx-bar-x{ animation: none; }


/* ============================================================
   24. PENGERASAN GULIR DI iOS
   ------------------------------------------------------------
   Disisir dari seluruh menu: ada ~20 wadah yang bisa digulir, dan
   sebagiannya belum punya penanganan sentuh khusus iOS. Ketiga
   properti di bawah tidak mengubah tata letak sedikit pun dan tidak
   berpengaruh apa-apa pada elemen yang memang tidak menggulir.

     -webkit-overflow-scrolling : gulir inersia di Safari lawas
     overscroll-behavior        : gulir tidak "menembus" ke halaman
                                  induk saat sudah mentok
     touch-action               : nyatakan tegas bahwa wadah ini
                                  memang untuk digulir, sehingga tidak
                                  ada gestur lain yang merebutnya
   ============================================================ */
/* PENANDA SCROLLER DIHAPUS SELURUHNYA.
   Riwayatnya: lapisan ini tiga kali merusak gulir sentuh karena mencoba
   "membantu" browser lewat -webkit-overflow-scrolling, overscroll-behavior,
   dan touch-action. Semuanya sudah dibuang. Lapisan animasi kini TIDAK
   menyentuh urusan gulir sama sekali — itu sepenuhnya ditangani browser
   dan CSS masing-masing menu. */

/* CATATAN PENTING — kenapa TIDAK ADA touch-action di sini.
   Itu keliru: Tabel Riwayat hanya menggulir MENDATAR, sehingga pembatas
   itu MEMBLOKIR gerakan tegak di seluruh area tabel — pengguna tidak bisa
   menggulir halaman saat jarinya berada di atas tabel.
   Browser sudah tahu sendiri elemen mana yang menggulir dan ke arah mana.
   Menyatakannya ulang lewat touch-action hanya menambah risiko tanpa
   manfaat, jadi sengaja tidak dipakai. */


/* ============================================================
   25b. WADAH GULIR: BEBAS EFEK
   ------------------------------------------------------------
   Di dalam area yang bisa digulir, kelancaran gulir jauh lebih penting
   daripada animasi. Lapisan berkomposisi (transform/animation) di dalam
   wadah gulir adalah penyebab umum gulir tersendat atau mati di iOS.
   Maka seluruh efek dimatikan di sana — termasuk kemunculan bertahap,
   sorot baris, dan transisi baris.
   ============================================================ */
/* DIPERSEMPIT: hanya efek MILIK LAPISAN INI yang dimatikan di dalam wadah
   gulir, bukan `*`.
   Versi sebelumnya memakai `*` sehingga ikut mematikan animasi milik
   aplikasi. Itu berbahaya: elemen yang mulai dari opacity:0 dan baru
   terlihat lewat animasinya akan hilang permanen — persis yang terjadi
   pada judul halaman depan. Efek lapisan ini juga sudah dicegah dari sisi
   JavaScript (SEL_AREA_GULIR), jadi aturan ini hanya jaring pengaman. */
:where([class*="table-wrap"], [class*="-scroll"], [class*="scroll-"]) [class*="gnx-"]{
  animation: none !important;
  transition: none !important;
  transform: none !important;
  will-change: auto !important;
  opacity: 1 !important;
}


/* ============================================================
   25c. HALAMAN LOGIN PIN: TANPA ELEMEN MELAYANG
   ------------------------------------------------------------
   Jaring pengaman untuk penanda di motion.js. Halaman login PIN tidak
   menampilkan riak sentuh maupun spinner tunggu: keduanya diposisikan
   dengan koordinat layar, sedangkan di halaman ini papan ketik iOS
   muncul-hilang dan menggeser tata letak — bayangannya jadi mendarat
   di tempat yang salah dan terasa telat.
   ============================================================ */
html.gnx-pin-page .gnx-ripple-box,
html.gnx-pin-page .gnx-spin-box,
html.gnx-pin-page .gnx-ghost{
  display: none !important;
}


/* ============================================================
   26. RASA APLIKASI (KHUSUS MODE PWA)
   ------------------------------------------------------------
   Semua aturan di sini HANYA aktif saat aplikasi dijalankan dari layar
   utama (standalone). Di browser biasa, halaman tetap berperilaku
   sebagai halaman web normal — zoom, pilih teks, dan tarik-untuk-muat-
   ulang tetap tersedia. Pemisahan ini disengaja: mematikan zoom di
   browser adalah kemunduran aksesibilitas tanpa manfaat.
   Kelas .gnx-pwa dipasang motion.js saat mendeteksi mode standalone.
   ============================================================ */

/* Hilangkan pantulan & tarik-untuk-muat-ulang: gerakan khas browser
   yang paling merusak ilusi "ini aplikasi". */
html.gnx-pwa, html.gnx-pwa body{
  overscroll-behavior-y: contain;
}

/* Ketukan langsung bereaksi (tanpa jeda 300 md menunggu ketukan ganda)
   dan tidak memicu zoom ketukan-ganda. */
html.gnx-pwa{ touch-action: manipulation; }

/* Elemen kendali tidak bisa diseleksi & tidak memunculkan menu tekan-lama.
   PENTING: teks isi, tabel, dan nominal SENGAJA tetap bisa diseleksi —
   di aplikasi penagihan, menyalin nomor invoice atau nominal itu
   pekerjaan sehari-hari, bukan gangguan. */
html.gnx-pwa :where(button, [role="button"], .btn, .bk-btn, .menu-card, .qcard,
  [class*="-btn"], [class*="-tab"], [class*="-chip"], [class*="-seg"],
  [class*="topbar"], [class*="-nav"], [class*="-fab"], label, summary){
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

/* Ikon dan gambar hiasan tidak bisa "diseret" keluar seperti di web. */
html.gnx-pwa :where(svg, [class*="-ic"] img, [class*="icon"] img){
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
}

/* Sorotan biru bawaan Android saat menyentuh: dimatikan menyeluruh. */
html.gnx-pwa *{ -webkit-tap-highlight-color: transparent; }


/* ============================================================
   27. SAKELAR PENGAMAN
   ============================================================ */

/* Saklar diagnostik ?nomotion=1 — mematikan seluruh lapisan animasi,
   termasuk aturan CSS yang tidak butuh JavaScript. */
/* PENTING: saklar ini HANYA menetralkan efek milik lapisan motion.
   Versi sebelumnya memakai selektor `*` sehingga mematikan animasi milik
   halaman itu sendiri. Akibatnya fatal di halaman profil perusahaan:
   judul hero memakai .word-reveal yang mulai dari opacity:0 dan baru
   terlihat lewat animasinya — begitu animasi dimatikan, judulnya hilang.
   Sekarang hanya kelas gnx-* yang disentuh. */
html[data-gnx-motion="off"] [class*="gnx-"]{
  animation: none !important;
  transition: none !important;
}
html[data-gnx-motion="off"] .gnx-reveal,
html[data-gnx-motion="off"] .gnx-sr,
html[data-gnx-motion="off"] .gnx-img{
  opacity: 1 !important;
  transform: none !important;
}


/* 11a. Hormati pengaturan aksesibilitas perangkat. Bila pengguna
   memilih "Kurangi Gerakan", semua efek dimatikan — dan yang
   terpenting, konten TETAP TERLIHAT (opacity dipaksa 1). */
@media (prefers-reduced-motion: reduce){
  *, *::before, *::after{
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  .gnx-reveal{ opacity: 1 !important; animation: none !important; }
  .gnx-sr, .gnx-img{ opacity: 1 !important; transform: none !important; }
  html.gnx-leaving body{ opacity: 1 !important; }
  .gnx-progress, .gnx-ghost,
  .gnx-spin-box{ display: none !important; }
  .gnx-top{ transition: none !important; }
  .gnx-nudge{ animation: none !important; }
  .gnx-upload::after{ animation: none !important; }
  .gnx-bar-y, .gnx-bar-x{ animation: none !important; transform: none !important; }
  [class*="overlay"].gnx-closing{ display: none !important; animation: none !important; }
  .gnx-zoom{ opacity: 1 !important; transform: none !important; animation: none !important; }
  body[style*="translate"]{ transform: none !important; }
  .gnx-move{ transition: none !important; transform: none !important; }
  ::view-transition-old(root), ::view-transition-new(root){ animation: none !important; }
  html{ scroll-behavior: auto !important; }
}

/* 11b. Saat mencetak / ekspor PDF: nol animasi, nol elemen melayang,
   semua konten terlihat penuh. */
@media print{
  *, *::before, *::after{
    animation: none !important;
    transition: none !important;
  }
  .gnx-reveal, .gnx-sr, .gnx-img{ opacity: 1 !important; transform: none !important; }
  .gnx-progress, .gnx-ripple-box, .gnx-ghost,
  .gnx-spin-box, .gnx-top, .gnx-net, .gnx-upload{ display: none !important; }
  .gnx-nudge{ animation: none !important; }
  /* Grafik harus tercetak pada ukuran penuhnya, bukan tertangkap
     di tengah animasi tumbuh. */
  .gnx-bar-y, .gnx-bar-x{ animation: none !important; transform: none !important; }
  body{ transform: none !important; }
  /* Menu Gaji & Tabungan menyembunyikan overlay saat mencetak dengan
     !important; jangan sampai sisa animasi tutup membuatnya ikut tercetak. */
  [class*="overlay"].gnx-closing{ display: none !important; }
  .gnx-move{ transition: none !important; transform: none !important; }
  html.gnx-leaving body{ opacity: 1 !important; }
}
