/* =========================================================================
   Sigatra — Gerak
   =========================================================================
   Prinsip yang dipegang di seluruh file ini:

   1. Gerakan harus punya alasan. Setiap animasi menjawab satu dari tiga hal:
      "ini baru muncul", "tindakanmu diterima", atau "ini yang penting".
      Gerakan yang cuma bikin ramai tidak ditulis.

   2. Hanya menganimasikan transform dan opacity. Keduanya diproses kartu
      grafis, jadi tetap 60fps di ponsel kelas menengah — pasar utama situs
      ini. Menganimasikan width/height/top memaksa browser menghitung ulang
      tata letak dan langsung terasa tersendat.

   3. Menghormati prefers-reduced-motion. Sebagian orang mengalami pusing
      atau mual karena animasi layar. Di bagian akhir file, semua gerakan
      dimatikan untuk mereka — tanpa merusak satu pun tata letak.
   ========================================================================= */


/* -------------------------------------------------------------------------
   Muncul saat digulir

   Elemen ber-atribut data-reveal mulai dari keadaan tersembunyi, lalu
   IntersectionObserver di reveal.js menambahkan kelas .is-visible begitu
   elemen masuk layar.

   Jaring pengaman: kalau JavaScript mati atau gagal dimuat, aturan
   html:not(.js) di bawah membuat semuanya langsung terlihat. Tanpa itu,
   situs akan tampak kosong melompong — kegagalan yang jauh lebih buruk
   daripada sekadar kehilangan animasi.
   ------------------------------------------------------------------------- */

html:not(.js) [data-reveal] {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
}

[data-reveal] {
    opacity: 0;
    transform: translate3d(0, 1.35rem, 0);
    transition: opacity var(--dur-slow) var(--ease-out),
                transform var(--dur-slow) var(--ease-out),
                filter var(--dur-slow) var(--ease-out);
    transition-delay: calc(var(--i, 0) * 70ms);
}

[data-reveal].is-visible {
    opacity: 1;
    transform: translate3d(0, 0, 0);
    filter: none;
}

/* Varian arah */
[data-reveal="fade"]  { transform: none; }
[data-reveal="left"]  { transform: translate3d(-1.5rem, 0, 0); }
[data-reveal="right"] { transform: translate3d(1.5rem, 0, 0); }
[data-reveal="scale"] { transform: scale(0.955); }
[data-reveal="blur"]  { transform: translate3d(0, 1rem, 0); filter: blur(6px); }

/* Butir yang muncul berurutan. Jeda diambil dari --i yang dipasang di HTML
   supaya kisi kartu masuk satu per satu, bukan serempak. */
[data-reveal-group] > * {
    opacity: 0;
    transform: translate3d(0, 1.1rem, 0);
    transition: opacity var(--dur-slow) var(--ease-out),
                transform var(--dur-slow) var(--ease-out);
    transition-delay: calc(var(--i, 0) * 65ms);
}

[data-reveal-group].is-visible > * {
    opacity: 1;
    transform: translate3d(0, 0, 0);
}

html:not(.js) [data-reveal-group] > * {
    opacity: 1 !important;
    transform: none !important;
}


/* -------------------------------------------------------------------------
   Judul hero — muncul per kata

   Tiap kata dibungkus <span> oleh hero.js, lalu naik dari balik garis
   potong. Efeknya membuat kalimat pertama terbaca seolah sedang "ditulis"
   — memberi bobot pada janji utama halaman.
   ------------------------------------------------------------------------- */

.word-reveal {
    display: inline-block;
    overflow: hidden;
    vertical-align: bottom;
    /* Ruang tambahan supaya ekor huruf (g, y, j) tidak terpotong. */
    padding-bottom: 0.12em;
    margin-bottom: -0.12em;
}

.word-reveal > span {
    display: inline-block;
    transform: translate3d(0, 110%, 0);
    animation: word-up 0.85s var(--ease-out) forwards;
    animation-delay: calc(var(--i, 0) * 55ms);
}

html:not(.js) .word-reveal > span {
    transform: none;
    animation: none;
}

@keyframes word-up {
    to { transform: translate3d(0, 0, 0); }
}


/* -------------------------------------------------------------------------
   Latar jaringan simpul

   Motif visual situs ini: titik-titik yang saling terhubung, mengacu ke
   layanan jaringan L2/L3 yang ditawarkan. Bergerak sangat pelan supaya
   terasa hidup tanpa menarik perhatian dari teks di atasnya.
   ------------------------------------------------------------------------- */

.mesh {
    position: absolute;
    inset: 0;
    z-index: var(--z-behind);
    overflow: hidden;
    pointer-events: none;
    /* Memudar ke bawah supaya menyatu dengan bagian berikutnya. */
    mask-image: radial-gradient(120% 100% at 50% 0%, #000 35%, transparent 78%);
    -webkit-mask-image: radial-gradient(120% 100% at 50% 0%, #000 35%, transparent 78%);
}

.mesh__canvas {
    width: 100%;
    height: 100%;
    opacity: 0;
    transition: opacity 1.2s var(--ease-out);
}

.mesh__canvas.is-ready { opacity: 1; }

/* Kisi titik statis sebagai lapisan dasar — tetap ada meski canvas gagal. */
.mesh__grid {
    position: absolute;
    inset: 0;
    background-image: radial-gradient(circle at center, var(--mesh-dot) 1px, transparent 1px);
    background-size: 2.25rem 2.25rem;
    opacity: 0.5;
}

/* Cahaya besar yang bergerak sangat pelan di belakang hero. */
.aurora {
    position: absolute;
    z-index: var(--z-behind);
    border-radius: 50%;
    filter: blur(72px);
    opacity: 0.5;
    pointer-events: none;
    will-change: transform;
}

.aurora--a {
    width: 34rem;
    height: 34rem;
    top: -12rem;
    inset-inline-start: -8rem;
    background: radial-gradient(circle, color-mix(in oklab, var(--accent) 42%, transparent), transparent 68%);
    animation: drift-a 26s var(--ease-in-out) infinite alternate;
}

.aurora--b {
    width: 28rem;
    height: 28rem;
    top: -6rem;
    inset-inline-end: -6rem;
    background: radial-gradient(circle, color-mix(in oklab, var(--accent-2) 34%, transparent), transparent 68%);
    animation: drift-b 32s var(--ease-in-out) infinite alternate;
}

@keyframes drift-a {
    from { transform: translate3d(0, 0, 0) scale(1); }
    to   { transform: translate3d(4rem, 2.5rem, 0) scale(1.14); }
}

@keyframes drift-b {
    from { transform: translate3d(0, 0, 0) scale(1.08); }
    to   { transform: translate3d(-3.5rem, 3rem, 0) scale(0.94); }
}


/* -------------------------------------------------------------------------
   Umpan balik tindakan
   ------------------------------------------------------------------------- */

@keyframes ripple {
    to {
        transform: scale(2.6);
        opacity: 0;
    }
}

/* Getaran singkat untuk isian yang ditolak. Amplitudonya kecil dengan
   sengaja — cukup untuk menarik mata, tidak sampai terasa menghakimi. */
@keyframes shake {
    0%, 100% { transform: translateX(0); }
    20%      { transform: translateX(-5px); }
    40%      { transform: translateX(4px); }
    60%      { transform: translateX(-3px); }
    80%      { transform: translateX(2px); }
}

@keyframes drawer-in {
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Centang yang tergambar sendiri saat form berhasil dikirim. */
.check-draw {
    stroke-dasharray: 34;
    stroke-dashoffset: 34;
    animation: draw 0.65s var(--ease-out) 0.15s forwards;
}

@keyframes draw {
    to { stroke-dashoffset: 0; }
}

/* Lingkaran pemuat */
@keyframes spin {
    to { transform: rotate(360deg); }
}

.spinner {
    width: 1.15em;
    height: 1.15em;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    animation: spin 0.7s linear infinite;
}


/* -------------------------------------------------------------------------
   Deretan berjalan
   ------------------------------------------------------------------------- */

@keyframes marquee {
    from { transform: translate3d(0, 0, 0); }
    /* -50% karena isi track digandakan dua kali — begitu salinan pertama
       habis, salinan kedua sudah tepat di posisinya, jadi tidak ada lompatan. */
    to   { transform: translate3d(-50%, 0, 0); }
}


/* -------------------------------------------------------------------------
   Perpindahan halaman

   View Transitions bawaan browser: satu deklarasi, nol JavaScript.
   Browser yang belum mendukung akan berpindah halaman seperti biasa —
   tidak ada yang rusak, hanya tidak ada transisinya.
   ------------------------------------------------------------------------- */

@view-transition {
    navigation: auto;
}

::view-transition-old(root) {
    animation: fade-out 180ms var(--ease-in-out) both;
}

::view-transition-new(root) {
    animation: fade-in 320ms var(--ease-out) 60ms both;
}

@keyframes fade-out {
    to { opacity: 0; transform: translateY(-0.5rem); }
}

@keyframes fade-in {
    from { opacity: 0; transform: translateY(0.75rem); }
}

/* Header dan tombol mengambang tidak boleh ikut memudar — keduanya tetap
   di tempat sepanjang perpindahan, jadi diberi nama transisi sendiri. */
.site-header  { view-transition-name: site-header; }
.float-actions { view-transition-name: float-actions; }

::view-transition-group(site-header),
::view-transition-group(float-actions) {
    animation-duration: 0s;
}

/*
   Pergantian tema — sapuan melingkar dari tombolnya.

   Animasi sesungguhnya dijalankan theme.js lewat Web Animations API supaya
   titik pusat lingkaran bisa mengikuti posisi tombol yang diklik. Yang
   diperlukan di CSS hanya mematikan silang-pudar bawaan, karena kalau
   keduanya berjalan bersamaan hasilnya terlihat keruh.
*/
html.is-theme-switching::view-transition-old(root),
html.is-theme-switching::view-transition-new(root) {
    animation: none;
    mix-blend-mode: normal;
}

html.is-theme-switching::view-transition-old(root) { z-index: 1; }
html.is-theme-switching::view-transition-new(root) { z-index: 2; }


/* -------------------------------------------------------------------------
   Kemajuan baca artikel

   Digerakkan langsung oleh posisi gulir halaman lewat animation-timeline.
   Tanpa event listener, tanpa perhitungan di JavaScript — browser yang
   mengurus, jadi tetap mulus meskipun tab sedang sibuk.
   ------------------------------------------------------------------------- */

.read-progress {
    position: fixed;
    inset-inline: 0;
    top: 0;
    height: 3px;
    z-index: var(--z-toast);
    background: var(--grad-brand);
    transform-origin: left center;
    transform: scaleX(0);
    pointer-events: none;
}

@supports (animation-timeline: scroll()) {
    .read-progress {
        animation: grow-x linear both;
        animation-timeline: scroll(root block);
    }
}

@keyframes grow-x {
    from { transform: scaleX(0); }
    to   { transform: scaleX(1); }
}


/* -------------------------------------------------------------------------
   Penyaringan kartu

   Saat filter diubah, kartu yang tersisa berpindah posisi. Tanpa transisi,
   perpindahannya berkedip dan pengunjung kehilangan jejak kartu yang
   sedang dilihat. @starting-style menangani kartu yang baru muncul.
   ------------------------------------------------------------------------- */

.catalog-grid > * {
    transition: opacity var(--dur) var(--ease-out),
                transform var(--dur) var(--ease-out);
}

.catalog-grid > [data-filtered-out="true"] {
    display: none;
}

@supports (transition-behavior: allow-discrete) {
    .catalog-grid > * {
        transition: opacity var(--dur) var(--ease-out),
                    transform var(--dur) var(--ease-out),
                    display var(--dur) allow-discrete;
    }

    @starting-style {
        .catalog-grid > * {
            opacity: 0;
            transform: translateY(0.75rem) scale(0.98);
        }
    }
}


/* -------------------------------------------------------------------------
   Sorotan mengikuti kursor

   Nilai --mx/--my diperbarui JavaScript. Diberi transisi supaya sorotan
   mengejar kursor dengan sedikit tertinggal — terasa seperti cahaya
   sungguhan, bukan kotak yang menempel di kursor.
   ------------------------------------------------------------------------- */

@property --mx {
    syntax: "<percentage>";
    initial-value: 50%;
    inherits: false;
}

@property --my {
    syntax: "<percentage>";
    initial-value: 0%;
    inherits: false;
}


/* -------------------------------------------------------------------------
   Interaksi: sorot dan klik

   Dua prinsip tambahan di atas tiga prinsip di awal berkas ini:

   * SOROT (hover) hanya dipasang di perangkat yang punya kursor sungguhan,
     lewat @media (hover: hover) and (pointer: fine). Di layar sentuh, hover
     "menempel" setelah disentuh: kartu tetap terangkat dan tombol tetap
     berkilau sampai pengunjung menyentuh tempat lain. Hasilnya terlihat
     seperti bug, bukan efek.

   * KLIK (:active) dipasang di SEMUA perangkat. Itu satu-satunya umpan balik
     yang dirasakan jari. Layar sentuh tidak punya hover, jadi tanpa ini
     ketukan terasa tidak "masuk".

   Durasi pendek (90-260ms; sorotan menyapu 720ms): situs ini bermerek
   "sigap", jadi gerakannya pun cepat selesai. Kurva spring hanya dipakai
   saat kembali dari tekanan, supaya terasa lentur dan bukan memantul-mantul.

   Warna WhatsApp (--wa) tidak dipakai di sini kecuali pada tombol WhatsApp
   itu sendiri, dan teal tidak pernah jadi bidang besar.
   ------------------------------------------------------------------------- */

/* Elemen kecil bulat yang ikut mendapat riak klik (ui.js). Perlu memotong
   riaknya sendiri, dan jadi tempat bertumpu bagi pseudo-element-nya. */
.icon-btn,
.chip {
    position: relative;
    overflow: hidden;
    isolation: isolate;
}


/* --- Tombol --------------------------------------------------------------- */

/* Kilau: pita cahaya miring yang menyapu dari kiri ke kanan. Posisi awalnya di
   luar tombol (dipotong overflow: hidden), jadi tak terlihat sampai disorot. */
.btn--wa::before,
.btn--primary::before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 55%;
    background: linear-gradient(105deg, transparent 0%, var(--shine) 50%, transparent 100%);
    transform: translateX(-130%) skewX(-18deg);
    opacity: 0;
    pointer-events: none;
}

/* Tombol WhatsApp utama: kilau menyapu dua kali saat pertama terlihat (kelas
   .is-pulsing dipasang ui.js). Jeda di antara dua sapuan sudah termasuk dalam
   keyframes, supaya tidak terasa seperti satu gerakan yang berulang cepat. */
.btn--pulse.is-pulsing::before {
    animation: sheen-pass 2.4s var(--ease-out) 0.4s 2;
}

@keyframes sheen-pass {
    0%        { opacity: 1; transform: translateX(-130%) skewX(-18deg); }
    45%       { opacity: 1; transform: translateX(260%) skewX(-18deg); }
    46%, 100% { opacity: 0; transform: translateX(260%) skewX(-18deg); }
}

/* Ikon WhatsApp "berdering": bergoyang meredam seperti lonceng. */
@keyframes bell-ring {
    0%   { transform: rotate(0); }
    14%  { transform: rotate(16deg) scale(1.1); }
    28%  { transform: rotate(-13deg) scale(1.1); }
    42%  { transform: rotate(10deg) scale(1.06); }
    56%  { transform: rotate(-7deg); }
    70%  { transform: rotate(4deg); }
    100% { transform: rotate(0); }
}

/* Saat ditekan: ikon mengecil sedikit, panah justru melesat maju. Dua arah
   berbeda untuk dua makna: "diterima" dan "berangkat". */
.btn:active .icon {
    transform: scale(0.88);
    transition-duration: var(--dur-instant);
}

.btn:active .icon--shift {
    transform: translateX(0.4rem);
}

@media (hover: hover) and (pointer: fine) {
    .btn--wa:hover::before,
    .btn--primary:hover::before {
        opacity: 1;
        transform: translateX(260%) skewX(-18deg);
        transition: transform 720ms var(--ease-out), opacity 100ms linear;
    }

    .btn--wa:hover > .icon:first-child {
        animation: bell-ring 700ms var(--ease-out) 60ms both;
    }
}

/* Tombol bulat kecil: menyusut saat ditekan. */
.icon-btn:active,
.lang-switch__item:active {
    transform: scale(0.9);
    transition-duration: var(--dur-instant);
}


/* --- Tombol mengambang ---------------------------------------------------- */

.float-btn--wa {
    position: relative;
}

/* Label yang meluncur keluar ke kiri: mengundang, bukan sekadar ikon polos.
   Hanya untuk tampilan; nama tombolnya sudah ada di aria-label. */
.float-btn__label {
    position: absolute;
    inset-inline-end: calc(100% + 0.7rem);
    top: 50%;
    translate: 0 -50%;
    padding: 0.45rem 0.85rem;
    border: 1px solid var(--border);
    border-radius: var(--r-full);
    background-color: var(--surface);
    color: var(--text);
    box-shadow: var(--shadow);
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
    white-space: nowrap;
    opacity: 0;
    transform: translateX(0.6rem);
    pointer-events: none;
    transition: opacity var(--dur-fast) var(--ease-out),
                transform var(--dur) var(--ease-out);
}

.float-btn--wa:focus-visible .float-btn__label {
    opacity: 1;
    transform: none;
}

/* Goyangan perhatian: tiga kali saja, jauh setelah tombol pertama muncul
   (ui.js memasang data-attention sekali). Selebihnya diam; goyangan tanpa
   henti berubah dari undangan jadi gangguan. */
.float-btn--wa[data-attention="true"] .icon {
    animation: float-attention 7s var(--ease-out) 3;
}

@keyframes float-attention {
    0%, 18%, 100% { transform: rotate(0) scale(1); }
    3%            { transform: rotate(15deg) scale(1.12); }
    6%            { transform: rotate(-13deg) scale(1.12); }
    9%            { transform: rotate(9deg) scale(1.06); }
    12%           { transform: rotate(-5deg) scale(1.02); }
    15%           { transform: rotate(2deg); }
}

.float-btn--top .icon {
    transition: transform var(--dur) var(--ease-spring);
}

/* Ditekan: tombol menyusut. Selektor lebih spesifik daripada keadaan
   data-visible di components.css, yang mengunci transform ke ukuran penuh. */
.float-btn[data-visible="true"]:active {
    transform: scale(0.9);
    transition-duration: var(--dur-instant);
}

@media (hover: hover) and (pointer: fine) {
    .float-btn--wa:hover .float-btn__label {
        opacity: 1;
        transform: none;
    }

    .float-btn--wa:hover .icon {
        animation: bell-ring 700ms var(--ease-out) both;
    }

    .float-btn--top:hover .icon {
        transform: translateY(-3px);
    }
}


/* --- Kartu ---------------------------------------------------------------- */

/* Garis aksen di tepi bawah yang tumbuh dari kiri. Kartu sudah punya
   overflow: hidden, jadi ujungnya mengikuti lengkung sudut. */
.card--interactive::after {
    content: "";
    position: absolute;
    inset: auto 0 0 0;
    height: 3px;
    background: var(--grad-brand);
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform var(--dur-slow) var(--ease-out);
    pointer-events: none;
}

.card--interactive:focus-within::after {
    transform: scaleX(1);
}

/* Ditekan: kartu turun setengah jalan, ikon menciut. Letaknya SETELAH aturan
   hover di components.css, jadi menang walau kursor masih di atas kartu. */
.card--interactive:active {
    transform: translateY(-1px) scale(0.992);
    transition-duration: var(--dur-instant);
}

.card--interactive:active .card__icon {
    transform: scale(0.94);
}

@media (hover: hover) and (pointer: fine) {
    .card--interactive:hover::after {
        transform: scaleX(1);
    }

    /* Nomor langkah di beranda bergeser sedikit ke kanan. */
    .step::before {
        transition: transform var(--dur) var(--ease-out);
    }

    .step:hover::before {
        transform: translateX(0.35rem);
    }
}


/* --- Tautan, remah roti, footer ------------------------------------------- */

/* Garis bawah yang digambar dari kiri. Elemen aslinya tidak diberi garis bawah
   (text-decoration: none), jadi tidak ada yang berganti, hanya muncul. */
.footer__list a,
.footer__legal a,
.breadcrumb a {
    position: relative;
}

.footer__list a::after,
.footer__legal a::after,
.breadcrumb a::after {
    content: "";
    position: absolute;
    inset-inline: 0;
    bottom: -0.15em;
    height: 1px;
    background-color: currentColor;
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform var(--dur) var(--ease-out);
}

.footer__list a:focus-visible::after,
.footer__legal a:focus-visible::after,
.breadcrumb a:focus-visible::after {
    transform: scaleX(1);
}

/* Dulu bergeser lewat padding-inline-start, yang memaksa perhitungan ulang
   tata letak di setiap frame (melanggar aturan "hanya transform dan opacity").
   Sekarang memakai transform, hasil gerak yang sama. */
.footer__list a {
    display: inline-block;
    transition: color var(--dur-fast) var(--ease-out),
                transform var(--dur) var(--ease-out);
}

.footer__list a:hover {
    padding-inline-start: 0;
}

.footer__contact-item .icon {
    transition: transform var(--dur) var(--ease-spring);
}

.legal-toc a {
    transition: color var(--dur-fast) var(--ease-out),
                transform var(--dur) var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
    .footer__list a:hover {
        transform: translateX(0.25rem);
    }

    .footer__list a:hover::after,
    .footer__legal a:hover::after,
    .breadcrumb a:hover::after {
        transform: scaleX(1);
    }

    .footer__contact-item:hover .icon {
        transform: scale(1.18) rotate(-8deg);
    }

    .legal-toc a:hover {
        transform: translateX(0.2rem);
    }
}


/* --- FAQ ------------------------------------------------------------------ */

.accordion__item {
    position: relative;
}

/* Garis gradasi menimpa garis pemisah di bawah butir yang sedang terbuka. */
.accordion__item::after {
    content: "";
    position: absolute;
    inset-inline: 0;
    bottom: -1px;
    height: 2px;
    background: var(--grad-brand);
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform var(--dur-slow) var(--ease-out);
    pointer-events: none;
}

.accordion__item[data-open="true"]::after {
    transform: scaleX(1);
}

/* Isi jawaban memudar naik sedikit setelah ruangnya terbuka. */
.accordion__content {
    opacity: 0;
    transform: translateY(-0.4rem);
    transition: opacity var(--dur) var(--ease-out),
                transform var(--dur) var(--ease-out);
}

.accordion__item[data-open="true"] .accordion__content {
    opacity: 1;
    transform: none;
    transition-delay: 90ms;
}

/* Putaran ikon pakai kurva spring: berayun sedikit melewati 180 derajat lalu
   mengendap, terasa lentur. */
.accordion__trigger .icon {
    transition: transform var(--dur) var(--ease-spring),
                color var(--dur-fast) var(--ease-out);
}

.accordion__trigger:active {
    transform: translateX(0.15rem);
    transition-duration: var(--dur-instant);
}


/* --- Saringan, pencarian, isian ------------------------------------------- */

.chip {
    transition: background-color var(--dur-fast) var(--ease-out),
                border-color var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out),
                transform var(--dur-fast) var(--ease-spring);
}

.chip:active {
    transform: scale(0.94);
    transition-duration: var(--dur-instant);
}

/* Memantul saat dipilih. Kelas dipasang filter.js lewat klik, bukan lewat
   aria-pressed, supaya chip yang sudah terpilih dari alamat URL tidak
   memantul sendiri saat halaman dimuat. */
.chip.is-popping {
    animation: chip-pop 380ms var(--ease-spring);
}

@keyframes chip-pop {
    0%   { transform: scale(0.9); }
    55%  { transform: scale(1.08); }
    100% { transform: scale(1); }
}

/* Kotak pencarian: ikon kaca pembesar menyala dan membesar saat kotaknya
   difokus. `scale` dan `translate` adalah properti terpisah, jadi tidak
   saling menimpa dengan translate bawaan penempatan ikon. */
.search-box > .icon:first-child {
    transition: color var(--dur-fast) var(--ease-out),
                scale var(--dur) var(--ease-spring);
}

.search-box:focus-within > .icon:first-child {
    color: var(--accent);
    scale: 1.12;
}

/* Tombol hapus berada di dalam .search-box, jadi ikon X-nya ikut terkena aturan
   `.search-box .icon` (absolut, 1rem dari kiri) yang dibuat untuk ikon kaca
   pembesar. Akibatnya X bergeser sekitar 7px ke kanan dari tengah tombol. Di
   sini ia dikembalikan ke alur biasa supaya terpusat oleh grid tombolnya. */
.search-box__clear .icon {
    position: static;
    translate: none;
    width: 1rem;
    height: 1rem;
    transition: rotate var(--dur) var(--ease-spring);
}

.search-box__clear:active .icon {
    rotate: 90deg;
}

/* Label ikut menyala saat isiannya difokus. */
.field__label {
    transition: color var(--dur-fast) var(--ease-out);
}

.field:focus-within .field__label {
    color: var(--accent-ink);
}

@media (hover: hover) and (pointer: fine) {
    .search-box__clear:hover .icon {
        rotate: 90deg;
    }
}


/* --- Tombol dalam proses (masuk, kirim) ----------------------------------- */

/* Isi tombol memudar, lingkaran pemuat muncul di tengah. Ukuran tombol tidak
   berubah, jadi tidak ada tata letak yang bergeser. ui.js memasang
   data-loading saat formulir lolos pemeriksaan dan terkirim. */
.btn > * {
    transition: opacity var(--dur-fast) var(--ease-out);
}

.btn[data-loading="true"] > * {
    opacity: 0;
}

.btn[data-loading="true"]::after {
    content: "";
    position: absolute;
    inset: 0;
    margin: auto;
    width: 1.2em;
    height: 1.2em;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    animation: spin 0.7s linear infinite;
}


/* --- Salin ke papan klip -------------------------------------------------- */

/* Ikon salin berganti centang (ui.js) dan memantul sekali. */
@keyframes copy-pop {
    0%   { transform: scale(0.5) rotate(-20deg); }
    60%  { transform: scale(1.25) rotate(4deg); }
    100% { transform: scale(1) rotate(0); }
}

[data-copied="true"] .icon {
    animation: copy-pop 420ms var(--ease-spring);
    color: var(--success);
}


/* -------------------------------------------------------------------------
   HORMATI PREFERENSI PENGGUNA

   Sistem operasi punya setelan "kurangi gerakan". Sebagian orang
   mengaktifkannya karena animasi memicu pusing atau mual. Blok ini
   mematikan seluruh gerakan untuk mereka.

   Yang TIDAK dimatikan: opacity akhir dan posisi akhir. Semua elemen tetap
   terlihat di tempat yang benar — hanya perjalanannya yang dihapus.
   ------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {

    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        transition-delay: 0ms !important;
        scroll-behavior: auto !important;
    }

    html {
        scroll-behavior: auto;
    }

    [data-reveal],
    [data-reveal-group] > * {
        opacity: 1 !important;
        transform: none !important;
        filter: none !important;
    }

    .word-reveal > span {
        transform: none !important;
        animation: none !important;
    }

    .aurora,
    .mesh__canvas {
        animation: none !important;
    }

    /* Efek yang artinya terletak pada geraknya (kilau, goyangan, pantulan)
       dimatikan, bukan sekadar dipercepat. */
    .btn--wa::before,
    .btn--primary::before {
        opacity: 0 !important;
        animation: none !important;
    }

    .float-btn--wa .icon,
    .btn--wa > .icon:first-child,
    .chip.is-popping,
    [data-copied="true"] .icon {
        animation: none !important;
    }

    /* Kanvas jaringan simpul dimatikan sepenuhnya — kisi titik statis di
       belakangnya sudah cukup memberi tekstur. */
    .mesh__canvas { display: none; }

    .marquee__track {
        animation: none !important;
        /* Tanpa animasi, deretan harus bisa digulir manual supaya isinya
           tetap bisa diakses. */
        overflow-x: auto;
    }

    .float-btn {
        opacity: 1;
        transform: none;
    }

    @view-transition {
        navigation: none;
    }
}
