/* ==========================================================================
   Prava — Halaman publik studio (depan + detail karya)

   Dimuat SESUDAH gallery.css dan sengaja bergantung padanya: token warna
   (--bg/--fg/--accent/--line), reset, dan komponen .btn semuanya dipakai ulang,
   supaya halaman publik terlihat satu keluarga dengan galeri yang dikirim ke
   client. Yang ditambahkan di sini hanya yang khas halaman publik.

   Semua ukuran memakai clamp() terhadap lebar layar — tidak ada satu pun
   breakpoint yang mengganti ukuran huruf, jadi tampilannya menyesuaikan mulus
   dari 320px sampai layar lebar tanpa lompatan.

   Profil desainnya ditulis lengkap di docs/design-dna.json (tiga dimensi:
   design_system / design_style / visual_effects). Kalau menambah komponen di
   sini, ambil nilainya dari token di bawah — jangan menaruh angka baru langsung
   di aturannya, kalau tidak profil itu langsung basi.
   ========================================================================== */

/* ------------------------------------------------------------------ Token -- */
/* Ditaruh di .lp, BUKAN :root: gallery.css juga dipakai galeri client, dan
   token gerak di bawah ini khusus halaman publik. Warna & font sengaja TIDAK
   ada di sini — keduanya milik admin dan sudah dicetak layout dari
   AppSetting/GallerySetting. */
.lp {
    /* Gerak. Kurvanya (--ease) dan jeda gilirannya (--stagger) sengaja TIDAK
       didefinisikan di sini — keduanya milik gallery.css dan dipakai bersama
       galeri client, supaya client yang pindah dari halaman depan ke galerinya
       tidak melewati dua "kepribadian" gerak yang berbeda. Yang khas halaman
       publik cuma tangga durasinya: lebih panjang dari galeri karena di sini
       gerak memang bagian dari kesannya, bukan sekadar umpan balik. */
    --lp-dur-micro: 160ms;
    --lp-dur: 320ms;
    --lp-dur-macro: 760ms;

    /* Bentuk. --radius dari GallerySetting tetap dihormati sebagai nilainya.
       Tangga radius penuh & tangga bayangan ada di docs/design-dna.json tapi
       sengaja TIDAK dideklarasikan di sini selama belum ada yang memakainya:
       token yatim membuat pembaca berikutnya mengira ada aturan yang rusak.
       Kedalaman di halaman ini dibawa blur/scrim/parallax, bukan bayangan —
       kartu karya memang tidak berbayang. */
    --lp-r-md: var(--radius, 16px);

    /* Ritme vertikal: berdenyut dengan TINGGI viewport, bukan lebarnya, supaya
       jarak antar-section terasa sama di laptop pendek maupun monitor tinggi. */
    --lp-section-y: clamp(56px, 10vh, 120px);
    --lp-bar-h: 68px;

    background: var(--bg);
    color: var(--fg);
    font-family: var(--font-body);
    /* Bar-nya sticky; tanpa ini anchor #karya berhenti persis di balik bar. */
    scroll-behavior: smooth;
    scroll-padding-top: 84px;
}

@media (prefers-reduced-motion: reduce) {
    .lp {
        scroll-behavior: auto;
    }
}

/* ================================================ Transisi antar-halaman == */
/* View Transitions lintas dokumen: foto kartu yang diklik membawa nama yang
   sama dengan foto hero halaman karya (dipasang JS tepat sebelum navigasi),
   jadi fotonya terlihat MEMBESAR ke posisi barunya alih-alih halaman berkedip
   putih. Peramban yang belum mendukung tidak kehilangan apa pun — @view-transition
   diabaikan dan navigasinya biasa saja. */
@view-transition {
    navigation: auto;
}

::view-transition-old(root),
::view-transition-new(root) {
    animation-duration: .42s;
    animation-timing-function: var(--ease);
}

.lp-work__bg {
    view-transition-name: lp-work-hero;
}

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

    /* `animation: none` membuat pseudo-elemen transisi tersangkut di frame
       pertama; memperpendeknya jauh lebih aman daripada mematikannya. */
    ::view-transition-group(*),
    ::view-transition-old(*),
    ::view-transition-new(*) {
        animation-duration: 1ms !important;
    }

    .lp-work__bg {
        view-transition-name: none;
    }
}

/* ======================================================= Butiran film == */
/* Satu-satunya tekstur di seluruh halaman: feTurbulence SVG sebagai data URI,
   STATIS (tidak dianimasikan, jadi biayanya nol setelah dilukis sekali).
   Gunanya menghilangkan kesan "digital datar" pada bidang warna besar —
   sengaja setipis 3,5% supaya tidak pernah disadari sebagai efek.
   pointer-events: none WAJIB — lapisannya menutupi seluruh viewport. */
.lp-grain {
    position: fixed;
    inset: 0;
    z-index: 90;
    pointer-events: none;
    opacity: .035;
    mix-blend-mode: overlay;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

[data-theme="dark"] .lp-grain,
[data-theme="contrast"] .lp-grain {
    opacity: .05;
}

@media (prefers-color-scheme: dark) {
    [data-theme="auto"] .lp-grain {
        opacity: .05;
    }
}

/* --------------------------------------------------------------- Bar atas -- */
.lp__bar {
    position: sticky;
    top: 0;
    z-index: 60;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 14px var(--pad);
    background: transparent;
    border-bottom: 1px solid transparent;
    transition: background-color var(--lp-dur) var(--ease), border-color var(--lp-dur) var(--ease),
        padding var(--lp-dur) var(--ease), backdrop-filter var(--lp-dur) var(--ease);
}

/* Kelas dipasang JS setelah halaman digulir sedikit. Di puncak halaman bar
   dibiarkan transparan supaya foto hero terlihat penuh — begitu digulir ia
   berubah jadi bilah padat agar teksnya tetap terbaca di atas foto apa pun. */
.lp__bar.is-stuck {
    padding-block: 9px;
    background: var(--overlay);
    backdrop-filter: blur(16px);
    border-bottom-color: var(--line);
}

/* Bilah progres baca — satu-satunya pemakaian aksen yang bergerak tiap frame.
   Murah karena hanya `transform`, dan hidup di tepi bawah bar (bukan elemen
   fixed sendiri) supaya ia ikut hilang saat bar transparan di puncak halaman. */
.lp__progress {
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    height: 2px;
    background: var(--accent);
    transform: scaleX(var(--lp-progress, 0));
    transform-origin: 0 50%;
    opacity: 0;
    transition: opacity var(--lp-dur) var(--ease);
}

.lp__bar.is-stuck .lp__progress {
    opacity: 1;
}

/* Di puncak halaman, bar berdiri di atas foto hero yang gelap. */
.lp-has-hero .lp__bar:not(.is-stuck) {
    color: #fff;
}

.lp-has-hero .lp__bar:not(.is-stuck) .lp__nav a:not(.lp__nav-cta) {
    color: #fff;
}

.lp__brand {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    color: inherit;
    text-decoration: none;
    font-family: var(--font-heading);
    font-size: clamp(1.1rem, 2.4vw, 1.35rem);
    letter-spacing: .01em;
}

/* Tinggi dikunci, lebar mengikuti: logo studio bisa berupa lambang persegi
   ATAU wordmark yang sangat lebar. Mengunci keduanya ke 30×30 memeras wordmark
   jadi noda gelap kecil yang tidak terbaca sebagai apa pun. */
.lp__brand-logo {
    height: 30px;
    width: auto;
    max-width: min(190px, 44vw);
    object-fit: contain;
}

.lp__nav {
    display: flex;
    align-items: center;
    gap: 4px;
}

.lp__nav a {
    position: relative;
    padding: 9px 13px;
    color: inherit;
    text-decoration: none;
    font-size: .8rem;
    letter-spacing: .1em;
    text-transform: uppercase;
    opacity: .8;
    transition: opacity var(--lp-dur-micro) var(--ease);
}

.lp__nav a:hover {
    opacity: 1;
}

/* Garis rambut yang tumbuh dari tengah — penanda hover paling murah yang tidak
   menggeser tata letak sedikit pun. Dikecualikan untuk CTA: tombol berlatar
   penuh tidak butuh garis bawah. */
.lp__nav a:not(.lp__nav-cta)::after {
    content: "";
    position: absolute;
    left: 13px;
    right: 13px;
    bottom: 4px;
    height: 1px;
    background: currentColor;
    transform: scaleX(0);
    transform-origin: 50% 50%;
    transition: transform var(--lp-dur) var(--ease);
}

.lp__nav a:not(.lp__nav-cta):hover::after,
.lp__nav a:not(.lp__nav-cta):focus-visible::after {
    transform: scaleX(1);
}

/* CTA memakai .btn dari gallery.css; opacity/tracking di atas tidak boleh
   ikut mengaburkannya.

   `color` WAJIB ditulis ulang di sini. `.lp__nav a` di atas (satu kelas + satu
   tipe = spesifisitas 0,1,1) MENGALAHKAN `.btn--solid` (0,1,0), jadi tombol
   yang latarnya sudah berwarna aksen tetap memakai `color: inherit` — dan
   warna yang diwarisi bar adalah warna teks di atas LATAR BAR, bukan di atas
   aksen. Akibatnya di bar terang tulisan gelap tergambar di atas pil gelap:
   tombolnya terlihat kosong sama sekali. Gejalanya hanya muncul setelah
   halaman digulir (bar jadi padat) — di puncak halaman bar mewarisi #fff dari
   `.lp-has-hero`, kebetulan terbaca, jadi bugnya mudah lolos.
   Ini kerabat dekat jebakan 51: sekali lagi urutan/spesifisitas yang
   membatalkan .btn--solid, bukan warnanya yang salah. */
.lp__nav .lp__nav-cta {
    margin-left: 8px;
    opacity: 1;
    text-transform: none;
    letter-spacing: .01em;
    color: var(--accent-fg);
}

/* ------------------------------------------------------- Gerak mikro .btn -- */
/* .btn dari gallery.css sudah mentransisikan transform; di sini tinggal diberi
   nilainya. Naik 1px saat hover lalu tertekan penuh saat diklik — umpan balik
   yang terasa "fisik" tanpa satu pun properti layout ikut berubah. */
.lp .btn:not(:disabled):hover {
    transform: translateY(-1px);
}

.lp .btn:not(:disabled):active {
    transform: translateY(0) scale(.985);
}

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

    .lp .btn:not(:disabled):hover,
    .lp .btn:not(:disabled):active {
        transform: none;
    }
}

/* ------------------------------------------------------------ Menu ponsel -- */
.lp__burger {
    display: none;
    flex-direction: column;
    justify-content: center;
    gap: 5px;
    width: 42px;
    height: 42px;
    padding: 0 10px;
    border: 0;
    border-radius: 12px;
    background: none;
    color: inherit;
    cursor: pointer;
}

.lp__burger span {
    display: block;
    height: 1.5px;
    background: currentColor;
    border-radius: 2px;
    transition: transform var(--lp-dur) var(--ease), opacity var(--lp-dur-micro) ease;
}

.lp__burger[aria-expanded="true"] span:nth-child(1) {
    transform: translateY(6.5px) rotate(45deg);
}

.lp__burger[aria-expanded="true"] span:nth-child(2) {
    opacity: 0;
}

.lp__burger[aria-expanded="true"] span:nth-child(3) {
    transform: translateY(-6.5px) rotate(-45deg);
}

.lp__menu {
    position: fixed;
    inset: 0;
    z-index: 55;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: var(--pad);
    background: var(--bg);
    animation: lp-menu-in .22s var(--ease);
}

.lp__menu[hidden] {
    display: none;
}

.lp__menu a {
    padding: 14px;
    color: var(--fg);
    text-decoration: none;
    font-family: var(--font-heading);
    font-size: 1.7rem;
    /* Tiap tautan menyusul sedikit demi sedikit; --i dipasang JS saat menu
       dibuka. Menu yang seluruh isinya muncul serentak terbaca seperti kedipan,
       bukan seperti sesuatu yang terbuka. */
    animation: lp-menu-item .42s var(--ease) both;
    animation-delay: calc(var(--i, 0) * 45ms);
}

/* Sama seperti CTA di bar: `.lp__menu a` (0,1,1) menang atas `.btn--solid`,
   jadi tombol di menu ponsel pun perlu warna teksnya ditulis ulang. */
.lp__menu .btn {
    margin-top: 14px;
    font-family: var(--font-body);
    font-size: 1rem;
}

.lp__menu .btn--solid {
    color: var(--accent-fg);
}

@keyframes lp-menu-in {
    from {
        opacity: 0;
    }
}

@keyframes lp-menu-item {
    from {
        opacity: 0;
        transform: translateY(14px);
    }
}

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

    .lp__menu,
    .lp__menu a {
        animation: none;
    }
}

@media (max-width: 720px) {
    .lp__nav {
        display: none;
    }

    .lp__burger {
        display: flex;
    }
}

@media (min-width: 721px) {

    /* Menu ponsel tidak boleh tertinggal terbuka saat layar dilebarkan */
    .lp__menu {
        display: none;
    }
}

/* ------------------------------------------------------------------- Hero -- */
.lp-hero {
    position: relative;
    display: grid;
    isolation: isolate;
    overflow: hidden;
    min-height: min(92svh, 860px);
    /* Bar-nya transparan dan menumpang di atas hero, jadi hero yang menyediakan
       ruang untuknya. */
    margin-top: calc(-1 * (var(--lp-bar-h) + 2px));
    padding-top: var(--lp-bar-h);
}

/* --lp-hp = progres gulir hero, 0 di puncak sampai 1 saat hero habis. Dipasang
   JS per frame (rAF) dan BERHENTI diperbarui begitu hero keluar viewport, jadi
   90% halaman tidak menjalankan handler ini sama sekali. */
.lp-hero__bg {
    position: absolute;
    /* Overscan 10% atas-bawah: tanpa ini, menggeser foto ke bawah untuk
       parallax akan membuka celah kosong di tepi atas hero. */
    inset: -10% 0;
    z-index: -1;
    transform: translate3d(0, calc(var(--lp-hp, 0) * 9%), 0);
    will-change: transform;
}

.lp-hero__bg img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* Drift dipasang pada <img>, parallax pada pembungkusnya — dua transform
       pada elemen yang sama akan saling menimpa (animasi selalu menang). */
    animation: lp-drift 26s ease-out forwards;
}

@keyframes lp-drift {
    from {
        transform: scale(1.08);
    }

    to {
        transform: scale(1);
    }
}

@media (prefers-reduced-motion: reduce) {
    .lp-hero__bg img {
        animation: none;
    }

    .lp-hero__bg {
        transform: none;
    }
}

.lp-hero__scrim {
    position: absolute;
    inset: 0;
    background:
        linear-gradient(180deg, rgba(0, 0, 0, .58) 0%, rgba(0, 0, 0, .26) 42%, rgba(0, 0, 0, .72) 100%);
}

.lp-hero__inner {
    display: grid;
    place-content: center;
    justify-items: center;
    gap: clamp(12px, 2vh, 20px);
    padding: clamp(64px, 14vh, 140px) var(--pad);
    text-align: center;
    color: #fff;
    /* Isi hero tertinggal di belakang gulir lalu memudar — lapisan kedua dari
       parallax dua-lapis. Dijepit di 0 supaya teks tidak pernah "negatif". */
    transform: translate3d(0, calc(var(--lp-hp, 0) * 34px), 0);
    opacity: calc(1 - var(--lp-hp, 0) * .8);
}

@media (prefers-reduced-motion: reduce) {
    .lp-hero__inner {
        transform: none;
        opacity: 1;
    }
}

/* Tanpa foto sama sekali, hero jatuh ke latar bertema — teks putih akan hilang. */
.lp-hero--plain .lp-hero__inner {
    color: var(--fg);
}

.lp-hero__eyebrow {
    margin: 0;
    font-size: clamp(10px, 1.6vw, 11.5px);
    letter-spacing: .3em;
    text-transform: uppercase;
    opacity: .82;
}

.lp-hero__title {
    margin: 0;
    font-family: var(--font-heading);
    /* Sedikit lebih besar & lebih rapat dari sebelumnya: pada bobot 400, judul
       serif baru terbaca "mahal" kalau tracking-nya negatif dan barisnya
       nyaris bersentuhan. */
    font-size: clamp(2.8rem, 9vw, 6.4rem);
    line-height: .98;
    letter-spacing: -.02em;
    font-weight: 400;
}

.lp-hero__tagline {
    margin: 0;
    max-width: 42ch;
    font-size: clamp(1rem, 2.4vw, 1.25rem);
    line-height: 1.6;
    opacity: .9;
}

.lp-hero__actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 12px;
    margin-top: clamp(10px, 2vh, 22px);
}

/* Tombol ghost di atas foto: garisnya harus putih, bukan --accent yang gelap. */
.lp-hero:not(.lp-hero--plain) .btn--ghost {
    border-color: rgba(255, 255, 255, .55);
    color: #fff;
}

.lp-hero:not(.lp-hero--plain) .btn--ghost:hover {
    background: #fff;
    border-color: #fff;
    color: #16161a;
}

.btn--lg {
    padding: 14px 28px;
    font-size: 14.5px;
}

/* Petunjuk gulir — garis tipis yang berdenyut, bukan ikon panah yang ramai.
   Satu-satunya animasi tak berujung di seluruh halaman, dan ia ikut memudar
   habis begitu hero mulai tergulir. */
.lp-hero__cue {
    position: absolute;
    left: 50%;
    bottom: 26px;
    translate: -50% 0;
    display: grid;
    place-items: center;
    width: 26px;
    height: 44px;
    border-radius: 999px;
    opacity: clamp(0, calc(1 - var(--lp-hp, 0) * 3), 1);
    transition: opacity var(--lp-dur-micro) linear;
}

.lp-hero__cue span {
    display: block;
    width: 1px;
    height: 34px;
    background: linear-gradient(180deg, transparent, rgba(255, 255, 255, .9));
    animation: lp-cue 2s var(--ease) infinite;
}

@keyframes lp-cue {

    0%,
    100% {
        opacity: .25;
        transform: translateY(-6px);
    }

    50% {
        opacity: 1;
        transform: translateY(4px);
    }
}

@media (prefers-reduced-motion: reduce) {
    .lp-hero__cue {
        opacity: 1;
    }

    .lp-hero__cue span {
        animation: none;
    }
}

/* --------------------------------------------------------------- Statistik -- */
.lp-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: clamp(18px, 3vw, 34px);
    max-width: 1180px;
    margin-inline: auto;
    padding: clamp(32px, 6vh, 60px) var(--pad);
    border-bottom: 1px solid var(--line);
    text-align: center;
}

.lp-stats__num {
    display: block;
    font-family: var(--font-heading);
    font-size: clamp(1.9rem, 4.6vw, 2.9rem);
    line-height: 1.05;
    letter-spacing: -.015em;
    color: var(--accent);
    /* Angka dihitung naik oleh JS; tabular-nums menahan lebarnya supaya
       label di bawahnya tidak bergoyang tiap frame. */
    font-variant-numeric: tabular-nums;
}

.lp-stats__label {
    display: block;
    margin-top: 6px;
    font-size: 11px;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--muted);
}

/* ----------------------------------------------------------------- Section -- */
.lp-section {
    padding: var(--lp-section-y) var(--pad);
}

.lp-section--alt {
    background: var(--surface);
    border-block: 1px solid var(--line);
}

.lp-section--narrow {
    max-width: 760px;
    margin-inline: auto;
}

.lp-section--tight {
    padding-top: clamp(28px, 5vh, 56px);
}

.lp-section__head {
    max-width: 640px;
    margin: 0 auto clamp(30px, 5vh, 56px);
    text-align: center;
}

.lp-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    margin: 0 0 12px;
    font-size: 11px;
    letter-spacing: .3em;
    text-transform: uppercase;
    color: var(--accent);
}

/* Garis rambut pendek sebelum eyebrow — satu-satunya ornamen grafis di seluruh
   halaman, dan ia yang memberi kesan editorial cetak. Digambar dengan ::before
   supaya tidak menambah satu pun elemen di markup. */
.lp-eyebrow::before {
    content: "";
    width: 26px;
    height: 1px;
    background: currentColor;
    opacity: .55;
}

.lp-section__title {
    margin: 0;
    font-family: var(--font-heading);
    font-size: clamp(2rem, 5.4vw, 3.4rem);
    line-height: 1.08;
    letter-spacing: -.01em;
    font-weight: 400;
}

.lp-section__lead {
    margin: 14px 0 0;
    font-size: clamp(.95rem, 2vw, 1.05rem);
    line-height: 1.7;
    color: var(--muted);
}

.lp-prose {
    margin: 0;
    font-size: 1rem;
    line-height: 1.8;
    color: var(--muted);
}

.lp-prose--lead {
    font-size: clamp(1.02rem, 2.2vw, 1.2rem);
    text-align: center;
}

/* ---------------------------------------------------------------- Penyaring -- */
.lp-filter {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 8px;
    margin-bottom: clamp(26px, 4vh, 42px);
}

.lp-filter__chip {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 9px 16px;
    border: 1px solid var(--line-strong);
    border-radius: 999px;
    background: none;
    color: inherit;
    font: inherit;
    font-size: .84rem;
    cursor: pointer;
    transition: background-color var(--lp-dur-micro) var(--ease),
        border-color var(--lp-dur-micro) var(--ease),
        color var(--lp-dur-micro) var(--ease),
        transform var(--lp-dur-micro) var(--ease);
}

.lp-filter__chip span {
    font-size: .72rem;
    opacity: .55;
}

.lp-filter__chip:hover {
    border-color: var(--accent);
}

.lp-filter__chip:active {
    transform: scale(.96);
}

.lp-filter__chip.is-on {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--accent-fg);
}

.lp-filter__chip.is-on span {
    opacity: .75;
}

/* -------------------------------------------------------------- Kartu karya -- */
.lp-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(310px, 100%), 1fr));
    gap: clamp(16px, 2.4vw, 30px);
    max-width: 1440px;
    margin-inline: auto;
}

.lp-grid__empty {
    margin: 28px 0 0;
    text-align: center;
    color: var(--muted);
}

.lp-card {
    display: block;
    color: inherit;
    text-decoration: none;
}

.lp-card[hidden] {
    display: none;
}

/* Kartu pertama yang TERLIHAT melebar jadi dua kolom — komposisi mosaik yang
   memberi grid satu titik berat, bukan deretan seragam. Kelasnya dipindah JS
   ke kartu pertama yang lolos penyaring; kalau JS mati, kartu pertama di DOM
   yang memakainya dan itu sudah benar untuk keadaan "tanpa penyaring". */
@media (min-width: 1024px) {
    .lp-card--feature {
        grid-column: span 2;
    }

    .lp-card--feature .lp-card__media {
        aspect-ratio: 16 / 10;
    }

    .lp-card--feature .lp-card__title {
        font-size: clamp(1.5rem, 2.6vw, 2rem);
    }
}

.lp-card__media {
    position: relative;
    display: block;
    overflow: hidden;
    border-radius: var(--lp-r-md);
    background: var(--line);
    aspect-ratio: 4 / 5;
    /* Condong mengikuti kursor. --lp-rx/--lp-ry dipasang JS hanya pada kartu
       yang sedang disentuh penunjuk halus; nilai bawaannya 0, jadi tanpa JS
       (dan di layar sentuh) aturan ini tidak berpengaruh sama sekali. */
    transform: perspective(900px) rotateX(var(--lp-rx, 0deg)) rotateY(var(--lp-ry, 0deg));
    transition: transform var(--lp-dur-macro) var(--ease);
}

.lp-card:hover .lp-card__media {
    transition-duration: var(--lp-dur-micro);
}

.lp-card__media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform .7s var(--ease);
}

.lp-card:hover .lp-card__media img,
.lp-card:focus-visible .lp-card__media img {
    transform: scale(1.05);
}

/* Lencana kategori & jumlah foto: keduanya menjawab pertanyaan pertama calon
   client ("ini pemotretan apa" / "hasilnya sebanyak apa") tanpa perlu diklik. */
.lp-card__tag,
.lp-card__count {
    position: absolute;
    z-index: 2;
    padding: 6px 11px;
    border-radius: 999px;
    background: rgba(16, 16, 20, .58);
    backdrop-filter: blur(8px);
    color: #fff;
    font-size: 10.5px;
    letter-spacing: .14em;
    text-transform: uppercase;
}

.lp-card__tag {
    top: 12px;
    left: 12px;
}

.lp-card__count {
    bottom: 12px;
    right: 12px;
    letter-spacing: .08em;
}

.lp-card__hover {
    position: absolute;
    inset: 0;
    z-index: 1;
    display: grid;
    place-items: center;
    background: rgba(16, 16, 20, .4);
    color: #fff;
    font-size: .82rem;
    letter-spacing: .2em;
    text-transform: uppercase;
    opacity: 0;
    transition: opacity .28s var(--ease);
}

.lp-card:hover .lp-card__hover,
.lp-card:focus-visible .lp-card__hover {
    opacity: 1;
}

/* Layar sentuh tidak punya hover — overlay yang hanya muncul saat hover di
   sana berarti tidak pernah muncul sama sekali, jadi lencananya yang bekerja. */
@media (hover: none) {
    .lp-card__hover {
        display: none;
    }
}

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

    .lp-card__media,
    .lp-card__media img {
        transform: none !important;
        transition: none;
    }
}

/* Keluar/masuk saat penyaring diganti. `hidden` (display:none) tidak bisa
   ditransisikan, jadi JS memudarkan dulu lewat .is-out lalu baru menyembunyikan
   — dan urutan terbalik saat kartunya kembali. */
.lp-card {
    transition: opacity var(--lp-dur) var(--ease), transform var(--lp-dur) var(--ease);
}

.lp-card.is-out {
    opacity: 0;
    transform: scale(.96);
    pointer-events: none;
}

.lp-card__body {
    display: block;
    padding: 15px 2px 0;
}

.lp-card__title {
    display: block;
    font-family: var(--font-heading);
    font-size: clamp(1.2rem, 2.4vw, 1.45rem);
    line-height: 1.22;
    letter-spacing: -.005em;
}

.lp-card__meta {
    display: block;
    margin-top: 5px;
    font-size: 11px;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--muted);
}

/* -------------------------------------------------------------- Cara pesan -- */
.lp-steps {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr));
    gap: clamp(22px, 3vw, 40px);
    max-width: 1180px;
    margin: 0 auto;
    padding: 0;
    list-style: none;
    counter-reset: none;
}

.lp-step {
    padding-top: 22px;
    /* Garis atas digambar sendiri (bukan border) supaya bisa "ditarik" dari
       kiri ke kanan saat langkahnya masuk viewport. */
    position: relative;
}

.lp-step::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 2px;
    background: var(--accent);
    transform: scaleX(0);
    transform-origin: 0 50%;
    transition: transform var(--lp-dur-macro) var(--ease);
    /* `inherit` di sini SALAH: ia mewarisi transition-delay induknya, bukan
       nilai --lp-d yang dipasang JS. Custom property-nya yang harus dibaca
       langsung — ia sendiri sudah diwariskan dari elemen langkahnya. */
    transition-delay: var(--lp-d, 0ms);
}

.lp-reveal .lp-step.is-in::before,
html:not(.lp-reveal) .lp-step::before {
    transform: scaleX(1);
}

@media (prefers-reduced-motion: reduce) {
    .lp-step::before {
        transform: scaleX(1);
        transition: none;
    }
}

.lp-step__num {
    display: block;
    font-family: var(--font-heading);
    font-size: 1rem;
    letter-spacing: .2em;
    color: var(--accent);
}

.lp-step__title {
    margin: 10px 0 8px;
    font-family: var(--font-heading);
    font-size: clamp(1.3rem, 2.8vw, 1.7rem);
    line-height: 1.2;
    letter-spacing: -.005em;
    font-weight: 400;
}

.lp-step__text {
    margin: 0;
    font-size: .95rem;
    line-height: 1.75;
    color: var(--muted);
}

/* ----------------------------------------------------------------- Tentang -- */
.lp-about__inner {
    display: grid;
    grid-template-columns: 1fr;
    gap: clamp(28px, 5vw, 64px);
    align-items: center;
    max-width: 1180px;
    margin-inline: auto;
}

@media (min-width: 860px) {
    .lp-about__inner {
        grid-template-columns: 1fr 1fr;
    }

    .lp-about .lp-section__title {
        text-align: left;
    }
}

.lp-about__figure {
    margin: 0;
    overflow: hidden;
    border-radius: var(--lp-r-md);
    aspect-ratio: 4 / 3;
}

.lp-about__figure img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.lp-about__ig {
    margin: 22px 0 0;
}

.lp-about__ig a {
    display: inline-flex;
    gap: 6px;
    color: var(--accent);
    text-decoration: none;
    font-size: .9rem;
    letter-spacing: .04em;
}

.lp-about__ig a:hover {
    text-decoration: underline;
}

/* --------------------------------------------------------------------- CTA -- */
.lp-cta {
    padding: clamp(60px, 12vh, 130px) var(--pad);
    background: var(--surface);
    border-top: 1px solid var(--line);
    text-align: center;
}

.lp-cta__inner {
    max-width: 720px;
    margin-inline: auto;
}

.lp-cta__title {
    margin: 0;
    font-family: var(--font-heading);
    font-size: clamp(1.9rem, 5.4vw, 3.2rem);
    line-height: 1.1;
    letter-spacing: -.015em;
    font-weight: 400;
}

.lp-cta__text {
    margin: 16px 0 0;
    font-size: clamp(.95rem, 2vw, 1.08rem);
    line-height: 1.75;
    color: var(--muted);
}

.lp-cta__actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 12px;
    margin-top: clamp(24px, 4vh, 38px);
}

/* ================================================== Halaman detail karya == */
.lp-work__hero {
    position: relative;
    display: grid;
    align-items: end;
    isolation: isolate;
    overflow: hidden;
    min-height: min(76svh, 720px);
    margin-top: calc(-1 * (var(--lp-bar-h) + 2px));
    padding: var(--lp-bar-h) var(--pad) clamp(40px, 8vh, 84px);
    color: #fff;
}

.lp-work__bg {
    position: absolute;
    inset: -10% 0;
    z-index: -1;
    transform: translate3d(0, calc(var(--lp-hp, 0) * 9%), 0);
    will-change: transform;
}

.lp-work__bg img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

@media (prefers-reduced-motion: reduce) {
    .lp-work__bg {
        transform: none;
    }
}

.lp-work__head {
    max-width: 1440px;
    width: 100%;
    margin-inline: auto;
}

.lp-work__back {
    display: inline-block;
    margin-bottom: 18px;
    color: inherit;
    text-decoration: none;
    font-size: .8rem;
    letter-spacing: .12em;
    text-transform: uppercase;
    opacity: .82;
    transition: opacity var(--lp-dur-micro) var(--ease), transform var(--lp-dur-micro) var(--ease);
}

.lp-work__back:hover {
    opacity: 1;
    transform: translateX(-3px);
}

.lp-work__tag {
    margin: 0 0 10px;
    font-size: 11px;
    letter-spacing: .3em;
    text-transform: uppercase;
    opacity: .9;
}

.lp-work__title {
    margin: 0;
    font-family: var(--font-heading);
    font-size: clamp(2.2rem, 7vw, 4.6rem);
    line-height: 1.04;
    letter-spacing: -.015em;
    font-weight: 400;
}

.lp-work__facts {
    margin: 14px 0 0;
    font-size: clamp(.85rem, 1.8vw, .98rem);
    letter-spacing: .06em;
    opacity: .88;
}

/* Foto karya: kolom masonry, tinggi mengikuti foto aslinya. Berbeda dari
   grid kartu yang sengaja dipotong 4:5 — di halaman detail, proporsi asli
   justru bagian dari yang ingin dinilai calon client. */
.lp-shots {
    max-width: 1440px;
    margin-inline: auto;
    column-width: 380px;
    column-gap: clamp(12px, 1.8vw, 22px);
}

.lp-shots__item {
    margin: 0 0 clamp(12px, 1.8vw, 22px);
    break-inside: avoid;
    overflow: hidden;
    border-radius: var(--lp-r-md);
    background: var(--line);
}

.lp-shots__item img {
    display: block;
    width: 100%;
    height: auto;
}

.lp-shots__note {
    max-width: 620px;
    margin: clamp(30px, 5vh, 52px) auto 0;
    text-align: center;
    font-size: .95rem;
    line-height: 1.75;
    color: var(--muted);
}

.lp-shots__note strong {
    color: var(--fg);
    font-weight: 600;
}

/* ------------------------------------------------------ Muncul saat digulir -- */
/* Progressive enhancement: kelas is-reveal dipasang JS pada <html>. Tanpa JS
   tidak ada satu pun elemen yang tersembunyi — pola yang sama dengan grid
   galeri client (lihat jebakan 8).

   --lp-d = penundaan giliran, dipasang JS per elemen berdasarkan urutannya di
   dalam KELOMPOK (grid/daftar) yang sama. Menaruhnya di :nth-child CSS tidak
   bisa: jumlah kartu berubah-ubah dan penyaring mengubah urutan tampilnya. */
.lp-reveal [data-reveal] {
    opacity: 0;
    transform: translateY(22px);
}

.lp-reveal [data-reveal].is-in {
    opacity: 1;
    transform: none;
    transition: opacity var(--lp-dur-macro) var(--ease), transform var(--lp-dur-macro) var(--ease);
    transition-delay: var(--lp-d, 0ms);
}

/* Foto tidak memudar masuk — ia TERSINGKAP dari bawah, seperti cetakan yang
   ditarik keluar dari amplop. Perbedaan kecil ini yang membuat grid karya
   terasa "difoto", bukan "di-render".

   JEBAKAN yang sudah menggigit sekali dan menghabiskan seluruh efeknya:
   clip-path IKUT DIHITUNG IntersectionObserver. Elemen yang menyembunyikan
   dirinya dengan `clip-path: inset(0 0 100%)` punya luas persinggungan NOL,
   jadi observer yang seharusnya membukanya tidak pernah melapor — kartunya
   tetap tak terlihat sampai halaman habis digulir. (Terukur: enam kartu,
   scrollY 0 → 3600, `.is-in` tetap 0.) Karena itu clip-path SELALU dipasang
   pada ANAK elemen yang diamati, tidak pernah pada elemennya sendiri.
   Kalau menambah ragam baru di sini, ikuti pola yang sama.

   Dua ragam, dan bedanya penting:
     "card"  — clip pada .lp-card__media saja. <img>-nya SUDAH punya transform
               sendiri untuk hover; menambah skala masuk di sana berarti dua
               aturan memperebutkan properti yang sama dan durasi hover-nya
               ikut berubah setelah animasi masuk selesai.
     "media" — clip PLUS skala turun dari 1.06, keduanya pada <img>. Dipakai
               foto yang tidak punya keadaan hover sama sekali (halaman karya,
               foto tentang). */
.lp-reveal [data-reveal="card"],
.lp-reveal [data-reveal="media"] {
    opacity: 1;
    transform: none;
}

.lp-reveal [data-reveal="card"] .lp-card__media,
.lp-reveal [data-reveal="media"] > img {
    clip-path: inset(0 0 100% 0);
}

.lp-reveal [data-reveal="card"].is-in .lp-card__media,
.lp-reveal [data-reveal="media"].is-in > img {
    clip-path: inset(0 0 0 0);
    transition: clip-path var(--lp-dur-macro) var(--ease);
    transition-delay: var(--lp-d, 0ms);
}

/* Kartu: tilt kursor dan clip hidup di elemen yang sama, jadi transisi
   transform-nya harus ikut ditulis di sini — aturan .is-in di atas mengganti
   seluruh properti `transition`. */
.lp-reveal [data-reveal="card"].is-in .lp-card__media {
    transition: clip-path var(--lp-dur-macro) var(--ease),
        transform var(--lp-dur-macro) var(--ease);
    transition-delay: var(--lp-d, 0ms), 0ms;
}

.lp-reveal [data-reveal="media"] > img {
    transform: scale(1.06);
}

.lp-reveal [data-reveal="media"].is-in > img {
    transform: none;
    transition: clip-path var(--lp-dur-macro) var(--ease), transform 1.1s var(--ease);
    transition-delay: var(--lp-d, 0ms);
}

/* Judul yang dipecah per kata TIDAK ikut memudar-naik seperti elemen lain:
   dua gerakan sekaligus pada elemen yang sama (blok naik + tiap kata naik)
   terbaca goyah, bukan berlapis. Yang bergerak hanya katanya. */
.lp-reveal [data-reveal="split"] {
    opacity: 1;
    transform: none;
}

/* Judul yang dipecah per kata. Pembungkus luar meng-crop, pembungkus dalam
   yang bergerak. Padding+margin negatif WAJIB: tanpa itu ekor huruf g/y/j
   terpotong rapi oleh overflow:hidden dan judulnya terlihat cacat. */
.lp-w {
    display: inline-block;
    overflow: hidden;
    vertical-align: bottom;
    padding: .12em .06em .16em;
    margin: -.12em -.06em -.16em;
}

.lp-w__i {
    display: inline-block;
    transition: transform var(--lp-dur-macro) var(--ease);
    transition-delay: var(--lp-d, 0ms);
}

/* Topengnya dipagari .lp-reveal, persis seperti [data-reveal] di atas: kelas
   itu dan pemecah katanya sama-sama dipasang JS, jadi keduanya ada atau
   tidak ada sekali-sekalian. Menaruh translateY di .lp-w__i tanpa pagar ini
   berarti judul hilang total di keadaan yang JS-nya setengah jalan. */
.lp-reveal .lp-w__i {
    transform: translateY(105%);
}

.lp-reveal .is-in .lp-w__i {
    transform: none;
}

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

    .lp-reveal [data-reveal],
    .lp-reveal [data-reveal].is-in,
    .lp-reveal [data-reveal="split"],
    .lp-reveal [data-reveal="card"],
    .lp-reveal [data-reveal="media"] {
        opacity: 1;
        transform: none;
        transition: none;
    }

    .lp-reveal [data-reveal="card"] .lp-card__media,
    .lp-reveal [data-reveal="card"].is-in .lp-card__media,
    .lp-reveal [data-reveal="media"] > img,
    .lp-reveal [data-reveal="media"].is-in > img,
    .lp-reveal .lp-w__i {
        clip-path: none;
        transform: none;
        transition: none;
    }
}
