/* =====================================================================
 * SIPADU PEMDI — SISTEM GERAK
 * =====================================================================
 * Berkas ini HANYA berisi gerak: transisi, animasi, dan mikro-interaksi.
 * Dipisahkan dari app.css supaya seluruh gerak aplikasi bisa dibaca,
 * disetel, atau dimatikan di satu tempat — bukan diburu satu per satu di
 * antara 2.000 baris gaya tata letak.
 *
 * ---------------------------------------------------------------------
 * TIGA ATURAN YANG TIDAK BOLEH DILANGGAR
 *
 * 1. HANYA `transform` dan `opacity` yang dianimasikan.
 *    Keduanya ditangani compositor peramban tanpa menghitung ulang tata
 *    letak. Menganimasikan `width`, `height`, `top`, atau `margin` memaksa
 *    reflow tiap frame — di komputer kantor yang umum dipakai OPD, itulah
 *    perbedaan antara "halus" dan "tersendat". Satu pengecualian disebut
 *    eksplisit di tempatnya (batang progres), dengan alasannya.
 *
 * 2. GERAK TIDAK PERNAH MENUNDA PEKERJAAN.
 *    Seluruh durasi di bawah 420ms, dan tidak ada elemen yang dimulai
 *    dari `opacity: 0` tanpa animasi yang pasti menyelesaikannya. Halaman
 *    yang isinya tak muncul karena animasi gagal adalah halaman rusak,
 *    bukan halaman lambat.
 *
 * 3. prefers-reduced-motion DIHORMATI SEPENUHNYA.
 *    Bukan "dikurangi" — dimatikan. Bagi sebagian orang gerak halus bukan
 *    perkara selera melainkan pemicu pusing dan mual (gangguan
 *    vestibular). Blok di dasar berkas ini menihilkan SEMUA animasi dan
 *    transisi di sini, termasuk yang ditambahkan nanti, dengan pemilih
 *    menyeluruh — sehingga gerak baru yang lupa dikecualikan tetap ikut
 *    mati. Ini juga yang membuat aplikasi tetap memenuhi WCAG 2.3.3.
 * ---------------------------------------------------------------------
 */

:root {
    /* Durasi. Angka-angka ini bukan selera: di bawah ~120ms gerak terbaca
       sebagai kedipan (tidak membantu), di atas ~450ms mulai terasa
       menahan. Rentang 160–420ms adalah wilayah "terasa hidup tapi tidak
       menunggu". */
    --gerak-kilat: 120ms;
    --gerak-cepat: 180ms;
    --gerak-sedang: 260ms;
    --gerak-lambat: 380ms;

    /* Easing.
       --gerak-keluar: cepat di awal, melambat di akhir (ease-out kuat).
         Dipakai untuk yang MASUK — objek datang dengan momentum lalu
         mendarat lembut, seperti benda nyata yang direm.
       --gerak-masuk: kebalikannya, untuk yang PERGI.
       --gerak-halus: simetris, untuk perubahan keadaan kecil (warna,
         bayangan) yang tidak punya arah. */
    --gerak-keluar: cubic-bezier(0.16, 1, 0.3, 1);
    --gerak-masuk: cubic-bezier(0.55, 0, 1, 0.45);
    --gerak-halus: cubic-bezier(0.4, 0, 0.2, 1);

    /* Jarak angkat baku untuk elemen yang muncul. Kecil dengan sengaja:
       gerak 8px terbaca sebagai "muncul", gerak 40px terbaca sebagai
       "melompat" dan mengganggu pembacaan. */
    --gerak-naik: 10px;
}

/* =====================================================================
 * 1. MASUK HALAMAN
 * ===================================================================== */

/* Transisi lintas dokumen. Peramban yang mendukung (Chrome/Edge modern)
   akan melarutkan halaman lama ke halaman baru saat berpindah tautan —
   tanpa satu baris JavaScript, tanpa mengubah cara navigasi bekerja.
   Peramban yang belum mendukung mengabaikan aturan ini sepenuhnya dan
   tetap mendapat animasi masuk di bawah. */
@view-transition {
    navigation: auto;
}

/* Kerangka aplikasi DIAM saat berpindah halaman.
 *
 * Dengan memberi nama tersendiri, topbar dan sidebar diperlakukan peramban
 * sebagai elemen yang SAMA di kedua halaman — ia tidak ikut dilarutkan,
 * melainkan tetap di tempatnya sementara isinya berganti. Inilah yang
 * membedakan rasa "aplikasi" dari "situs yang memuat ulang": menu yang ikut
 * berkedip tiap klik membuat setiap perpindahan terasa seperti mulai dari nol.
 *
 * Namanya harus UNIK per halaman — karena hanya ada satu topbar dan satu
 * sidebar, syarat itu terpenuhi dengan sendirinya. */
.sipadu-topbar { view-transition-name: sipadu-topbar; }
.sipadu-sidebar { view-transition-name: sipadu-sidebar; }
.sipadu-bottom-nav { view-transition-name: sipadu-bottom-nav; }

/* Larutan halaman dibuat SINGKAT (160ms). Sesudahnya, animasi masuk konten di
   bawah yang mengambil alih — kalau keduanya sama-sama panjang, perpindahan
   terasa dua kali: halaman melarut, lalu isinya melarut lagi. */
::view-transition-old(root),
::view-transition-new(root) {
    animation-duration: 160ms;
    animation-timing-function: var(--gerak-halus);
}

/* Animasi masuk konten — dasar yang SELALU jalan, termasuk di peramban
   tanpa View Transitions. Diterapkan pada wadah isi, bukan pada <body>,
   supaya topbar dan sidebar tetap diam: kerangka yang ikut berkedip tiap
   pindah halaman membuat aplikasi terasa memuat ulang seluruhnya. */
.sipadu-container > * {
    animation: gerak-muncul var(--gerak-sedang) var(--gerak-keluar) both;
}

@keyframes gerak-muncul {
    from {
        opacity: 0;
        transform: translateY(var(--gerak-naik));
    }
    to {
        opacity: 1;
        transform: none;
    }
}

/* Stagger: anak-anak pertama wadah isi masuk berurutan, bukan serentak.
   Selisihnya 40ms — cukup untuk terbaca sebagai urutan, terlalu singkat
   untuk terasa sebagai antrean.

   Dibatasi delapan elemen pertama, lalu berhenti. Halaman Beranda punya
   belasan panel; kalau seluruhnya di-stagger, panel terakhir baru muncul
   setengah detik kemudian — pengguna yang menggulir cepat akan melihat
   halaman yang masih "merakit dirinya". */
.sipadu-container > *:nth-child(1) { animation-delay: 0ms; }
.sipadu-container > *:nth-child(2) { animation-delay: 40ms; }
.sipadu-container > *:nth-child(3) { animation-delay: 80ms; }
.sipadu-container > *:nth-child(4) { animation-delay: 120ms; }
.sipadu-container > *:nth-child(5) { animation-delay: 160ms; }
.sipadu-container > *:nth-child(6) { animation-delay: 200ms; }
.sipadu-container > *:nth-child(7) { animation-delay: 240ms; }
.sipadu-container > *:nth-child(n + 8) { animation-delay: 280ms; }

/* Kartu angka & kartu berita di dalam grid ikut berurutan. Dipisahkan dari
   aturan di atas karena mereka bukan anak langsung .sipadu-container. */
.sipadu-beranda-angka > *,
.sipadu-beranda-berita > * {
    animation: gerak-muncul var(--gerak-sedang) var(--gerak-keluar) both;
}
.sipadu-beranda-angka > *:nth-child(2),
.sipadu-beranda-berita > *:nth-child(2) { animation-delay: 60ms; }
.sipadu-beranda-angka > *:nth-child(3),
.sipadu-beranda-berita > *:nth-child(3) { animation-delay: 120ms; }
.sipadu-beranda-angka > *:nth-child(4),
.sipadu-beranda-berita > *:nth-child(4) { animation-delay: 180ms; }
.sipadu-beranda-angka > *:nth-child(n + 5),
.sipadu-beranda-berita > *:nth-child(n + 5) { animation-delay: 220ms; }

/* Panel kiri halaman login: identitas instansi masuk lebih dulu, formulir
   menyusul. Urutannya disengaja — mata diarahkan ke identitas, tangan
   menyusul ke isian. */
.sipadu-auth__aside {
    animation: gerak-muncul var(--gerak-lambat) var(--gerak-keluar) both;
}
.sipadu-auth__card {
    animation: gerak-muncul var(--gerak-lambat) var(--gerak-keluar) 80ms both;
}

/* =====================================================================
 * 2. MIKRO-INTERAKSI
 * ===================================================================== */

/* Tombol: menekan memberi umpan balik seketika. `:active` menekan 1px —
   sekecil itu tidak terlihat sebagai animasi, tetapi terasa sebagai
   respons. Transisi TIDAK dipasang untuk `:active` agar umpan baliknya
   tidak tertunda sedikit pun. */
.ina-button {
    transition:
        transform var(--gerak-kilat) var(--gerak-halus),
        box-shadow var(--gerak-cepat) var(--gerak-halus),
        background-color var(--gerak-cepat) var(--gerak-halus),
        border-color var(--gerak-cepat) var(--gerak-halus),
        color var(--gerak-cepat) var(--gerak-halus);
}
.ina-button:hover { transform: translateY(-1px); }
.ina-button:active { transform: translateY(1px); transition-duration: 0ms; }
.ina-button:disabled,
.ina-button[disabled] { transform: none; }

/* Panel & kartu: terangkat sedikit saat disentuh kursor. Hanya yang
   memang bisa diklik — panel statis yang ikut bergerak akan menjanjikan
   interaksi yang tidak ada. */
.sipadu-stat,
.sipadu-panel {
    transition:
        transform var(--gerak-cepat) var(--gerak-keluar),
        box-shadow var(--gerak-cepat) var(--gerak-keluar);
}
.sipadu-stat:hover {
    transform: translateY(-2px);
    box-shadow: 0 10px 24px -16px rgb(0 0 0 / 40%);
}

/* Baris daftar & baris tabel: sorotan halus saat kursor lewat, supaya
   mata tidak kehilangan baris yang sedang dibaca pada daftar panjang. */
.sipadu-list__item,
.ina-table__row {
    transition: background-color var(--gerak-cepat) var(--gerak-halus);
}

/* Navigasi samping: penanda aktif tumbuh dari kiri, bukan muncul begitu
   saja. transform: scaleY dipakai (bukan height) agar tidak memicu
   reflow. */
.sipadu-nav__link {
    position: relative;
    transition:
        background-color var(--gerak-cepat) var(--gerak-halus),
        color var(--gerak-cepat) var(--gerak-halus),
        padding-left var(--gerak-cepat) var(--gerak-halus);
}
.sipadu-nav__link:hover { padding-left: calc(var(--ina-spacing-4, 1rem) + 3px); }
.sipadu-nav__link .sipadu-nav__icon {
    transition: transform var(--gerak-cepat) var(--gerak-keluar);
}
.sipadu-nav__link:hover .sipadu-nav__icon { transform: scale(1.12); }

/* Ikon di dalam tombol aksi ikut hidup sedikit. Dibatasi pada tombol
   yang memang punya ikon awalan supaya tidak menggoyang seluruh teks. */
.ina-button__prefix-icon {
    transition: transform var(--gerak-cepat) var(--gerak-keluar);
}
.ina-button:hover .ina-button__prefix-icon { transform: translateX(1px); }

/* Lencana dan chip ikon: membesar sangat sedikit saat kartunya disentuh. */
.sipadu-ikon-chip {
    transition: transform var(--gerak-cepat) var(--gerak-keluar);
}
.sipadu-panel:hover .sipadu-ikon-chip { transform: scale(1.06) rotate(-2deg); }

/* =====================================================================
 * 3. PERUBAHAN KEADAAN
 * ===================================================================== */

/* Panel level (detail indikator) ditukar dengan atribut `hidden` oleh
   app.js. Karena elemen yang kehilangan `hidden` dirender ulang, animasi
   masuknya berjalan sendiri tanpa JavaScript tambahan — cukup animasi ini
   terpasang pada elemennya. */
.sipadu-level-panel {
    animation: gerak-geser-masuk var(--gerak-sedang) var(--gerak-keluar) both;
}

@keyframes gerak-geser-masuk {
    from {
        opacity: 0;
        transform: translateX(12px);
    }
    to {
        opacity: 1;
        transform: none;
    }
}

/* Modal & drawer IDDS. Latar gelapnya melarut, dialognya naik sedikit
   sambil membesar dari 98% — bukan dari 80%, yang terbaca sebagai
   "meletus" dan menarik perhatian lebih besar daripada isinya. */
.ina-modal:not([hidden]) .ina-modal__backdrop {
    animation: gerak-larut var(--gerak-cepat) var(--gerak-halus) both;
}
.ina-modal:not([hidden]) .ina-modal__dialog {
    animation: gerak-dialog-masuk var(--gerak-sedang) var(--gerak-keluar) both;
}
.ina-drawer:not([hidden]) .ina-drawer__backdrop {
    animation: gerak-larut var(--gerak-cepat) var(--gerak-halus) both;
}
.ina-drawer:not([hidden]) .ina-drawer__panel {
    animation: gerak-drawer-masuk var(--gerak-sedang) var(--gerak-keluar) both;
}

@keyframes gerak-larut {
    from { opacity: 0; }
    to { opacity: 1; }
}

@keyframes gerak-dialog-masuk {
    from {
        opacity: 0;
        transform: translateY(12px) scale(0.98);
    }
    to {
        opacity: 1;
        transform: none;
    }
}

@keyframes gerak-drawer-masuk {
    from { transform: translateX(-100%); }
    to { transform: none; }
}

/* Akordeon bukti dukung: isinya melarut saat dibuka. Tingginya TIDAK
   dianimasikan — tinggi isi akordeon di sini tidak diketahui sebelum
   dibuka (jumlah tahapannya berbeda-beda), dan animasi tinggi yang
   ditebak akan tersentak di akhir. Melarut sambil naik sedikit memberi
   kesan yang sama tanpa risiko itu. */
.ina-accordion-card--open .ina-accordion-card__content-body {
    animation: gerak-muncul var(--gerak-cepat) var(--gerak-keluar) both;
}
.ina-accordion-card__chevron {
    transition: transform var(--gerak-cepat) var(--gerak-keluar);
}

/* Pesan (alert) yang baru muncul: turun lembut dari atas. */
.ina-alert {
    animation: gerak-turun var(--gerak-sedang) var(--gerak-keluar) both;
}

@keyframes gerak-turun {
    from {
        opacity: 0;
        transform: translateY(-8px);
    }
    to {
        opacity: 1;
        transform: none;
    }
}

/* Batang progres tumbuh dari nol saat halaman dibuka.
 *
 * INI SATU-SATUNYA tempat `width` dianimasikan, dan pengecualiannya
 * disengaja: lebar batang progres MEMANG angkanya — menganimasikan
 * transform: scaleX akan ikut merenggangkan ujung membulatnya dan
 * membuatnya tampak cacat di awal animasi. Jumlah batang per halaman
 * paling banyak belasan, jadi reflow-nya tidak terasa.
 *
 * `--isi` disetel sebaris oleh tampilan (style="width: N%"); animasi ini
 * hanya menunda pertumbuhannya, bukan mengubah nilai akhirnya. */
.ina-progress-bar__fill {
    /* transform-origin ditaruh pada ELEMEN, bukan di dalam keyframe: bila ia
       hanya disebut pada `from`, nilainya kembali ke tengah (50% 50%) pada
       `to`, sehingga batangnya tumbuh sambil titik tumpunya ikut bergeser —
       terlihat seperti tergelincir, bukan tumbuh dari kiri. */
    transform-origin: left center;
    animation: gerak-progres var(--gerak-lambat) var(--gerak-keluar) both;
    transition: width var(--gerak-lambat) var(--gerak-keluar);
}

@keyframes gerak-progres {
    from { transform: scaleX(0); }
    to { transform: none; }
}

/* Kartu berita & baris pengguna yang bisa diklik sudah punya transisinya
   sendiri di app.css; di sini hanya diselaraskan agar memakai token yang
   sama, bukan angka lepas. */
.sipadu-beranda-berita__kartu {
    transition:
        transform var(--gerak-cepat) var(--gerak-keluar),
        box-shadow var(--gerak-cepat) var(--gerak-keluar),
        border-color var(--gerak-cepat) var(--gerak-halus);
}

/* Gambar berita membesar perlahan saat kartunya disentuh — kartunya diam,
   hanya gambarnya yang bergerak di balik bingkai (overflow: hidden). */
.sipadu-beranda-berita__gambar img {
    transition: transform var(--gerak-lambat) var(--gerak-keluar);
}
.sipadu-beranda-berita__kartu:hover .sipadu-beranda-berita__gambar img {
    transform: scale(1.04);
}

/* Fokus papan ketik: cincin fokus muncul dengan transisi supaya
   perpindahan Tab terasa mengalir, bukan melompat. Ketebalannya TIDAK
   dikurangi — fokus yang samar adalah masalah aksesibilitas, bukan
   selera desain. */
a,
button,
[tabindex],
.ina-text-field__input {
    transition: outline-offset var(--gerak-kilat) var(--gerak-halus);
}
a:focus-visible,
button:focus-visible,
[tabindex]:focus-visible {
    outline-offset: 2px;
}

/* =====================================================================
 * 4. PENGHORMATAN prefers-reduced-motion — TIDAK BOLEH DIHAPUS
 * =====================================================================
 * Pemilih menyeluruh, bukan daftar kelas satu per satu. Alasannya: gerak
 * baru yang ditambahkan nanti akan otomatis ikut dimatikan di sini tanpa
 * ada yang perlu ingat memperbaruinya — dan yang paling mungkin terjadi
 * pada daftar manual adalah ia lupa diperbarui.
 *
 * `animation-duration: 1ms` (bukan `none`) dipakai dengan sengaja:
 * animasi yang dibatalkan total akan meninggalkan elemen ber-`opacity: 0`
 * pada keyframe `both` di sebagian peramban — halaman kosong. Dengan 1ms,
 * animasinya tetap BERJALAN sampai keadaan akhir, hanya tidak terlihat.
 */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 1ms !important;
        animation-delay: 0ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 1ms !important;
        transition-delay: 0ms !important;
        scroll-behavior: auto !important;
    }

    /* Pergeseran posisi pada :hover ikut ditiadakan — bukan perkara durasi,
       melainkan perpindahan itu sendiri. */
    .ina-button:hover,
    .ina-button:active,
    .sipadu-stat:hover,
    .sipadu-beranda-berita__kartu:hover {
        transform: none;
    }
    .sipadu-nav__link:hover { padding-left: var(--ina-spacing-4, 1rem); }
    .sipadu-nav__link:hover .sipadu-nav__icon,
    .sipadu-panel:hover .sipadu-ikon-chip,
    .ina-button:hover .ina-button__prefix-icon,
    .sipadu-beranda-berita__kartu:hover .sipadu-beranda-berita__gambar img {
        transform: none;
    }

    /* Transisi lintas halaman ikut dimatikan. */
    @view-transition {
        navigation: none;
    }
}

/* =====================================================================
 * 5. CETAK — jaring pengaman
 * =====================================================================
 * Animasi masuk memakai fill-mode `both`, yang berarti keadaan AWAL-nya
 * (opacity: 0) berlaku sebelum animasi berjalan. Sebagian mesin cetak
 * merender halaman tanpa menjalankan animasi sama sekali — dan hasilnya
 * lembar kosong berhantu: tata letaknya benar, isinya tak terlihat.
 *
 * Karena itu pada media cetak seluruh animasi ditiadakan DAN opacity
 * dipaksa penuh. Ini bukan penyempurnaan tampilan cetak, melainkan
 * pencegahan halaman kosong.
 */
@media print {
    *,
    *::before,
    *::after {
        animation: none !important;
        transition: none !important;
        opacity: 1 !important;
        transform: none !important;
    }
}

/* =====================================================================
 * 6. GERAK PER KOMPONEN
 * =====================================================================
 * Bagian 1–4 mengatur gerak HALAMAN. Bagian ini mengatur gerak tiap
 * KOMPONEN — dan setiap animasi di sini punya pekerjaan, bukan sekadar
 * menghias:
 *
 *   - menandai bahwa sesuatu BARU muncul (lencana, baris tabel),
 *   - membenarkan bahwa tindakan pengguna DITERIMA (centang, tersimpan),
 *   - menunjukkan ARAH sebuah perubahan (chevron, panel, dropdown),
 *   - menunjukkan bahwa sistem SEDANG BEKERJA (rangka muat).
 *
 * Yang tidak punya salah satu pekerjaan itu tidak masuk ke sini.
 *
 * TIDAK ADA animasi tak berujung (`infinite`) di bagian ini kecuali
 * rangka muat, yang memang menandakan "masih berjalan". Gerak abadi di
 * layar kerja menarik mata terus-menerus ke tempat yang tidak menuntut
 * perhatian, dan pada laptop ia menyalakan GPU sepanjang halaman terbuka.
 * ===================================================================== */

/* ---------- Kotak centang tahapan ---------- */
/* Ini TINDAKAN PALING SERING di seluruh aplikasi: petugas OPD menandai
   sebuah tahapan selesai. Karena penyimpanannya berjalan di latar (lihat
   autosimpan di app.js), tanpa umpan balik seketika pengguna tidak tahu
   klik-nya tertangkap — dan mengklik dua kali. Pop singkat menjawab itu
   sebelum jawaban server datang. */
.ina-checkbox__box {
    transition:
        background-color var(--gerak-cepat) var(--gerak-halus),
        border-color var(--gerak-cepat) var(--gerak-halus),
        transform var(--gerak-cepat) var(--gerak-keluar);
}
.ina-checkbox:hover .ina-checkbox__box { transform: scale(1.08); }
.ina-checkbox__input:active + .ina-checkbox__box { transform: scale(0.92); }

/* Tanda centangnya sendiri melompat masuk saat kotak berubah tercentang. */
.ina-checkbox__box--checked .ina-checkbox__icon {
    animation: gerak-centang var(--gerak-sedang) var(--gerak-keluar) both;
}

@keyframes gerak-centang {
    0%   { opacity: 0; transform: scale(0.2) rotate(-18deg); }
    60%  { opacity: 1; transform: scale(1.18) rotate(4deg); }
    100% { opacity: 1; transform: none; }
}

/* Kotak yang dinonaktifkan (tahapan milik OPD lain, atau level terkunci)
   tidak ikut bergerak — gerak di sana menjanjikan interaksi yang ditolak. */
.ina-checkbox--disabled .ina-checkbox__box,
.ina-checkbox__input:disabled + .ina-checkbox__box { transform: none !important; }

/* ---------- Lencana status ---------- */
/* Lencana adalah cara aplikasi ini mengabarkan PERUBAHAN status: "Menunggu
   Verifikasi" menjadi "Terverifikasi", "Belum Diajukan" menjadi "Diajukan".
   Pop kecil saat ia dirender membuat perubahan itu tertangkap mata pada
   halaman yang penuh lencana serupa. */
.ina-badge {
    animation: gerak-pop var(--gerak-cepat) var(--gerak-keluar) both;
    transition: transform var(--gerak-cepat) var(--gerak-keluar);
}

@keyframes gerak-pop {
    0%   { opacity: 0; transform: scale(0.86); }
    70%  { opacity: 1; transform: scale(1.04); }
    100% { opacity: 1; transform: none; }
}

/* ---------- Stepper level ---------- */
/* Langkah yang sedang dibuka diberi denyut SEKALI saat halaman dimuat —
   bukan berulang. Gunanya menunjukkan "Anda ada di sini" pada deretan
   lingkaran yang mirip satu sama lain; sesudah terlihat, ia tidak punya
   alasan untuk terus berdenyut. */
.ina-stepper__item {
    transition: transform var(--gerak-cepat) var(--gerak-keluar);
}
.ina-stepper__item--clickable:hover { transform: translateY(-2px); }
.ina-stepper__item--active .ina-stepper__icon-wrapper {
    animation: gerak-denyut 1100ms var(--gerak-halus) 1 both;
}
/* Langkah yang sudah tercapai: centangnya melompat masuk seperti kotak
   centang tahapan — bahasa gerak yang sama untuk arti yang sama. */
.ina-stepper__item--completed .ina-stepper__check-icon {
    animation: gerak-centang var(--gerak-sedang) var(--gerak-keluar) both;
}

@keyframes gerak-denyut {
    0%   { box-shadow: 0 0 0 0 rgb(25 107 205 / 45%); }
    70%  { box-shadow: 0 0 0 10px rgb(25 107 205 / 0%); }
    100% { box-shadow: 0 0 0 0 rgb(25 107 205 / 0%); }
}

/* ---------- Menu dropdown ---------- */
/* Tumbuh dari TEPI ATAS (transform-origin), bukan dari tengah: menu yang
   membesar dari tengahnya terlihat melayang entah dari mana, sedangkan
   yang tumbuh dari tombol pemicunya terbaca sebagai "keluar dari sini". */
.sipadu-dropdown__menu {
    transform-origin: top right;
    animation: gerak-dropdown var(--gerak-cepat) var(--gerak-keluar) both;
}

@keyframes gerak-dropdown {
    from { opacity: 0; transform: scale(0.96) translateY(-6px); }
    to   { opacity: 1; transform: none; }
}

.sipadu-dropdown__item {
    transition:
        background-color var(--gerak-kilat) var(--gerak-halus),
        padding-left var(--gerak-kilat) var(--gerak-halus);
}
.sipadu-dropdown__item:hover { padding-left: calc(var(--ina-spacing-4, 1rem) + 3px); }

/* ---------- Keadaan kosong ---------- */
/* Ikonnya mengambang dua kali lalu berhenti. Layar kosong adalah layar
   yang paling mudah disangka rusak; gerak singkat mengabarkan "halaman ini
   hidup, memang tidak ada isinya" tanpa menjadi hiasan abadi. */
.sipadu-empty i {
    animation: gerak-ambang 2200ms var(--gerak-halus) 2 both;
}

@keyframes gerak-ambang {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(-6px); }
}

/* ---------- Baris tabel ---------- */
/* Masuk berurutan seperti kartu, dibatasi sepuluh baris pertama. Tabel
   indikator berisi 20 baris; kalau semuanya di-stagger, baris terakhir
   muncul hampir sedetik kemudian dan tabel terlihat "mengetik dirinya". */
.ina-table__body .ina-table__row {
    animation: gerak-baris var(--gerak-cepat) var(--gerak-keluar) both;
}
.ina-table__body .ina-table__row:nth-child(1) { animation-delay: 0ms; }
.ina-table__body .ina-table__row:nth-child(2) { animation-delay: 25ms; }
.ina-table__body .ina-table__row:nth-child(3) { animation-delay: 50ms; }
.ina-table__body .ina-table__row:nth-child(4) { animation-delay: 75ms; }
.ina-table__body .ina-table__row:nth-child(5) { animation-delay: 100ms; }
.ina-table__body .ina-table__row:nth-child(6) { animation-delay: 125ms; }
.ina-table__body .ina-table__row:nth-child(7) { animation-delay: 150ms; }
.ina-table__body .ina-table__row:nth-child(8) { animation-delay: 175ms; }
.ina-table__body .ina-table__row:nth-child(9) { animation-delay: 200ms; }
.ina-table__body .ina-table__row:nth-child(n + 10) { animation-delay: 220ms; }

/* translateY 4px saja — baris tabel berdempetan, gerak besar membuat
   seluruh tabel tampak bergelombang. */
@keyframes gerak-baris {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: none; }
}

/* ---------- Akordeon bukti dukung ---------- */
.ina-accordion-card {
    transition:
        border-color var(--gerak-cepat) var(--gerak-halus),
        box-shadow var(--gerak-cepat) var(--gerak-keluar);
}
.ina-accordion-card__icon {
    transition: transform var(--gerak-sedang) var(--gerak-keluar);
}
.ina-accordion-card--open .ina-accordion-card__icon { transform: scale(1.1); }
/* Chevron berputar menunjuk arah yang akan terjadi bila ditekan lagi. */
.ina-accordion-card__chevron--open { transform: rotate(180deg); }

/* ---------- Toast ---------- */
.sipadu-toast__jumlah {
    animation: gerak-pop var(--gerak-cepat) var(--gerak-keluar) both;
}

/* ---------- Umpan balik simpan-otomatis ---------- */
/* Teks "Tersimpan." muncul dan hilang sendiri. Pop singkat membuatnya
   tertangkap mata yang sedang menatap isian, bukan menatap keterangan di
   bawahnya. */
.js-autosimpan-status {
    transition: color var(--gerak-cepat) var(--gerak-halus);
}
.js-autosimpan-status:not(:empty) {
    animation: gerak-pop var(--gerak-cepat) var(--gerak-keluar) both;
}

/* ---------- Isian & unggah berkas ---------- */
.ina-text-field__wrapper {
    transition:
        border-color var(--gerak-cepat) var(--gerak-halus),
        box-shadow var(--gerak-cepat) var(--gerak-halus);
}
.sipadu-file-input {
    transition:
        border-color var(--gerak-cepat) var(--gerak-halus),
        background-color var(--gerak-cepat) var(--gerak-halus);
}
.sipadu-file-input::file-selector-button {
    transition:
        background-color var(--gerak-cepat) var(--gerak-halus),
        transform var(--gerak-kilat) var(--gerak-halus);
}
.sipadu-file-input:hover::file-selector-button { transform: translateY(-1px); }

/* ---------- Rangka muat (skeleton) ---------- */
/* Lihat bagian 8 di dasar berkas: pustaka bentuk rangka lengkap beserta
   catatan KAPAN ia pantas dipakai dan kapan justru merugikan. */

/* ---------- Kunci level ---------- */
/* Gembok pada langkah yang belum terbuka bergoyang sekali saat halaman
   dimuat — cukup untuk ditemukan mata, lalu diam. */
.sipadu-stepper__gembok {
    animation: gerak-goyang 600ms var(--gerak-halus) 1 both;
}

@keyframes gerak-goyang {
    0%, 100% { transform: rotate(0); }
    25%      { transform: rotate(-9deg); }
    75%      { transform: rotate(9deg); }
}

/* =====================================================================
 * 7. UNGKAP SAAT MASUK LAYAR — bekerja sama dengan assets/js/motion.js
 * =====================================================================
 * Kelas .gerak-ungkap DIPASANG OLEH JAVASCRIPT, tidak pernah ditulis di
 * HTML. Karena itu, bila motion.js gagal dimuat, tidak ada satu pun aturan
 * di bawah yang pernah cocok — dan halaman tampil utuh tanpa animasi.
 *
 * Urutan ini penting dan disengaja: menyembunyikan lewat CSS lalu
 * mengandalkan JS untuk memunculkan akan membuat kegagalan skrip berarti
 * halaman kosong. Kegagalan skrip tidak boleh pernah berarti kehilangan isi.
 */
.gerak-js .gerak-ungkap {
    opacity: 0;
    transform: translateY(18px);
    /* will-change hanya pada elemen yang MEMANG akan bergerak, dan hanya
       selama ia belum muncul — memasangnya permanen pada banyak elemen
       membuat peramban menahan lapisan compositor yang tidak terpakai. */
    will-change: opacity, transform;
    transition:
        opacity var(--gerak-lambat) var(--gerak-keluar),
        transform var(--gerak-lambat) var(--gerak-keluar);
}
.gerak-js .gerak-ungkap.is-tampak {
    opacity: 1;
    transform: none;
    will-change: auto;
}

@media (prefers-reduced-motion: reduce) {
    /* Sabuk pengaman kedua: seandainya motion.js sempat memasang kelasnya
       sebelum setelan terbaca, elemennya tetap terlihat. */
    .gerak-js .gerak-ungkap {
        opacity: 1 !important;
        transform: none !important;
    }
}

/* =====================================================================
 * 8. RANGKA MUAT (SKELETON)
 * =====================================================================
 * KAPAN RANGKA MUAT PANTAS DIPAKAI — dan kapan ia justru merugikan.
 *
 * Aplikasi ini dirender di server (BFF CodeIgniter). Ketika sebuah halaman
 * sampai di peramban, ISINYA SUDAH ADA. Memasang rangka muat di atas isi
 * yang sudah lengkap berarti sengaja menyembunyikannya sesaat lalu
 * menampilkannya lagi — pengguna menunggu sesuatu yang sebenarnya sudah
 * siap, dan aplikasi terasa LEBIH LAMBAT daripada sebelum dihias.
 *
 * Karena itu rangka muat di sini dipakai HANYA pada tempat yang benar-benar
 * punya jeda, yaitu yang isinya datang SESUDAH halaman tampil:
 *
 *   1. Modal verifikasi   — detail tahapan diambil lewat fetch (app.js).
 *   2. Diagram aspek      — Chart.js diunduh dari CDN lalu menggambar.
 *   3. Gambar berita      — berkasnya diunduh setelah HTML-nya tiba.
 *
 * Bentuk-bentuk di bawah tersedia untuk SEMUA komponen, sehingga pemuatan
 * berikutnya — apa pun layarnya — tidak perlu menemukan gayanya sendiri.
 * Yang tidak disediakan adalah alasan untuk memakainya di tempat yang tidak
 * menunggu apa pun.
 *
 * AKSESIBILITAS: pembungkusnya WAJIB memakai aria-busy="true" dan berisi
 * teks tersembunyi seperti "Memuat…". Rangka muat adalah isyarat VISUAL;
 * bagi pembaca layar, kotak abu-abu berdenyut sama saja dengan halaman
 * kosong tanpa penjelasan.
 * ===================================================================== */

/* Dasar semua bentuk. Kilaunya SATU-SATUNYA animasi tak berujung di berkas
   ini, dan justru itulah pekerjaannya: selama ia bergerak, sistem masih
   menunggu. Begitu isinya datang, elemennya diganti dan animasinya ikut
   hilang bersamanya. */
.sipadu-rangka,
.sipadu-rangka-baris,
.sipadu-rangka-judul,
.sipadu-rangka-lencana,
.sipadu-rangka-bulat,
.sipadu-rangka-gambar,
.sipadu-rangka-kartu,
.sipadu-rangka-diagram {
    position: relative;
    overflow: hidden;
    background: var(--ina-neutral-100);
    border-radius: 6px;
}

.sipadu-rangka::after,
.sipadu-rangka-baris::after,
.sipadu-rangka-judul::after,
.sipadu-rangka-lencana::after,
.sipadu-rangka-bulat::after,
.sipadu-rangka-gambar::after,
.sipadu-rangka-kartu::after,
.sipadu-rangka-diagram::after {
    content: '';
    position: absolute;
    inset: 0;
    transform: translateX(-100%);
    background: linear-gradient(90deg,
        rgb(255 255 255 / 0%) 0%,
        rgb(255 255 255 / 70%) 50%,
        rgb(255 255 255 / 0%) 100%);
    animation: gerak-rangka 1400ms linear infinite;
}

@keyframes gerak-rangka {
    to { transform: translateX(100%); }
}

/* Bentuk-bentuk. Ukurannya meniru elemen yang akan menggantikannya, supaya
   isi yang datang tidak menggeser tata letak (layout shift) — rangka muat
   yang ukurannya meleset justru MENAMBAH kegaduhan yang ingin dicegahnya. */
/* display:block ditetapkan di SINI, bukan diserahkan ke kelas utilitas
   .d-block dari bootstrap-utilities. Utilitas itu datang dari CDN, dan
   proyek ini sudah pernah menyiapkan diri untuk CDN yang diblokir jaringan
   OPD (lihat Chart.js). Bentuk rangka yang bergantung pada berkas luar akan
   berbaris menyamping dan terlihat rusak justru pada jaringan yang paling
   butuh penanda "sedang memuat". */
.sipadu-rangka-baris   { display: block; height: 0.85rem; margin-bottom: 0.5rem; }
.sipadu-rangka-baris:last-child { margin-bottom: 0; }
/* Baris terakhir sebuah paragraf selalu lebih pendek — meniru itu membuat
   blok rangka terbaca sebagai teks, bukan sebagai tumpukan balok. */
.sipadu-rangka-baris--pendek { width: 62%; }
.sipadu-rangka-baris--sedang { width: 84%; }

.sipadu-rangka-judul   { display: block; height: 1.35rem; width: 55%; margin-bottom: 0.75rem; }
.sipadu-rangka-lencana { height: 1.25rem; width: 5.5rem; border-radius: 999px; display: inline-block; }
.sipadu-rangka-bulat   { height: 2rem; width: 2rem; border-radius: 50%; flex: 0 0 2rem; }
.sipadu-rangka-gambar  { display: block; height: 140px; width: 100%; }
.sipadu-rangka-diagram { display: block; height: 260px; width: 100%; border-radius: 10px; }

.sipadu-rangka-kartu {
    display: block;
    height: 88px;
    margin-bottom: var(--ina-spacing-3, 0.75rem);
    background: var(--ina-neutral-100);
}

/* Susunan siap pakai: satu baris daftar (avatar + dua baris teks). */
.sipadu-rangka-grup {
    display: flex;
    align-items: flex-start;
    gap: var(--ina-spacing-3, 0.75rem);
    padding: var(--ina-spacing-3, 0.75rem) 0;
}
.sipadu-rangka-grup__isi { flex: 1 1 auto; min-width: 0; }

/* Wadah GAMBAR yang belum selesai diunduh.
 *
 * Kelas ini dipasang pada pembungkus <img>, bukan pada gambarnya: begitu
 * berkasnya tiba, gambar menutupi rangka di bawahnya tanpa perlu satu baris
 * JavaScript pun. Kilau yang tersisa di belakang gambar buram tidak terlihat
 * karena gambarnya legap. */
.sipadu-beranda-berita__gambar:not(.sipadu-beranda-berita__gambar--kosong),
.sipadu-berita-baris__gambar,
.sipadu-berita-thumb,
.sipadu-beranda-modal__gambar {
    position: relative;
    overflow: hidden;
}
.sipadu-beranda-berita__gambar:not(.sipadu-beranda-berita__gambar--kosong)::before,
.sipadu-berita-baris__gambar::before,
.sipadu-berita-thumb::before,
.sipadu-beranda-modal__gambar::before {
    content: '';
    position: absolute;
    inset: 0;
    background:
        linear-gradient(90deg,
            rgb(255 255 255 / 0%) 0%,
            rgb(255 255 255 / 70%) 50%,
            rgb(255 255 255 / 0%) 100%),
        var(--ina-neutral-100);
    background-size: 200% 100%, auto;
    animation: gerak-rangka-latar 1400ms linear infinite;
}
/* Gambar berada DI ATAS rangka (z-index), jadi saat ia selesai dimuat
   rangkanya tertutup dengan sendirinya. */
.sipadu-beranda-berita__gambar img,
.sipadu-berita-baris__gambar img,
.sipadu-berita-thumb img,
.sipadu-beranda-modal__gambar img {
    position: relative;
    z-index: 1;
}

/* Versi latar dari kilau: yang bergerak adalah POSISI latar, bukan elemen —
   karena di sini rangka berupa ::before yang menutup penuh wadahnya dan
   tidak bisa digeser keluar tanpa ikut menghilang. */
@keyframes gerak-rangka-latar {
    from { background-position: -100% 0, 0 0; }
    to   { background-position: 200% 0, 0 0; }
}

/* Diagram yang sedang menunggu Chart.js. Dipasang & dilepas radar-aspek.js. */
.sipadu-radar.is-memuat { position: relative; min-height: 260px; }
.sipadu-radar.is-memuat canvas { opacity: 0; }
.sipadu-radar.is-memuat::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: 10px;
    background:
        linear-gradient(90deg,
            rgb(255 255 255 / 0%) 0%,
            rgb(255 255 255 / 70%) 50%,
            rgb(255 255 255 / 0%) 100%),
        var(--ina-neutral-100);
    background-size: 200% 100%, auto;
    animation: gerak-rangka-latar 1400ms linear infinite;
}

/* prefers-reduced-motion: kilaunya berhenti, kotaknya TETAP ADA.
   Rangka muat menyampaikan "sedang menunggu" — menghapusnya sama saja
   menghapus keterangan itu, bukan mengurangi gerak. Yang dihilangkan hanya
   geraknya. */
@media (prefers-reduced-motion: reduce) {
    .sipadu-rangka::after,
    .sipadu-rangka-baris::after,
    .sipadu-rangka-judul::after,
    .sipadu-rangka-lencana::after,
    .sipadu-rangka-bulat::after,
    .sipadu-rangka-gambar::after,
    .sipadu-rangka-kartu::after,
    .sipadu-rangka-diagram::after,
    .sipadu-beranda-berita__gambar::before,
    .sipadu-berita-baris__gambar::before,
    .sipadu-berita-thumb::before,
    .sipadu-beranda-modal__gambar::before,
    .sipadu-radar.is-memuat::after {
        animation: none;
        background: var(--ina-neutral-100);
    }
}

/* ---------- Bilah muat perpindahan halaman ---------- */
/* Dipasang & dilepas assets/js/motion.js. Lihat catatan panjang di sana:
   perpindahan halaman tidak bisa memakai rangka muat karena selama menunggu,
   layar masih menampilkan halaman LAMA.

   position: fixed + z-index tinggi supaya ia tetap terlihat di atas apa pun,
   termasuk topbar lengket dan modal yang sedang terbuka. */
.sipadu-bilah-muat {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 9999;
    height: 3px;
    width: 0;
    background: linear-gradient(90deg, var(--ina-primary-500), var(--ina-primary-700));
    /* 90% dicapai dalam 8 detik dengan easing yang melambat — ia terus maju
       selama menunggu, tetapi tidak pernah sampai ke ujung. Bilah yang penuh
       lalu diam berbohong: ia mengatakan "selesai" pada sesuatu yang belum. */
    transition: width 8000ms cubic-bezier(0.1, 0.8, 0.2, 1);
    pointer-events: none;
}
.sipadu-bilah-muat.is-jalan { width: 90%; }

@media (prefers-reduced-motion: reduce) {
    /* Bilahnya tidak dipasang sama sekali pada mode ini (lihat motion.js);
       aturan ini hanya jaring pengaman bila ia terlanjur ada. */
    .sipadu-bilah-muat { transition: none; width: 90%; }
}

/* Kilau rangka gambar DIHENTIKAN begitu gambarnya tiba (ditandai motion.js).
   Tanpa ini ia berputar selamanya di balik gambar yang sudah tampil: tidak
   terlihat, tetapi tetap menyalakan compositor sepanjang halaman terbuka. */
.sipadu-beranda-berita__gambar.is-siap::before,
.sipadu-berita-baris__gambar.is-siap::before,
.sipadu-berita-thumb.is-siap::before,
.sipadu-beranda-modal__gambar.is-siap::before {
    content: none;
}
