/* ==========================================================================
   SUNDARA — komponen & section
   ========================================================================== */

/* ------------------------------------------------------------------ NAV */
.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: var(--z-nav);
  height: var(--nav-h);
  display: flex;
  align-items: center;
  /* Keadaan "stuck" adalah DEFAULT, bukan tambahan. Nav yang terbaca tanpa
     JS: latar shell, tinta gelap, garis bawah. JS melepasnya dengan menambah
     .is-top selama viewport masih di atas hero. Kalau JS mati, yang tersisa
     adalah nav yang tetap bisa dibaca — bukan teks putih di atas langit putih. */
  background: color-mix(in srgb, var(--c-shell) 88%, transparent);
  backdrop-filter: blur(14px);
  color: var(--c-ink);
  border-bottom: 1px solid var(--a-line);
  transition: color .45s var(--ease), background-color .45s var(--ease);
}
/* Frame pertama tidak boleh dianimasikan: initNav() melepas keadaan stuck
   segera setelah boot, dan transisi di sana terlihat sebagai kedipan. */
.nav:not(.is-ready) { transition: none; }
.nav.is-top {
  background: none;
  backdrop-filter: none;
  color: var(--c-shell);
  border-bottom-color: transparent;
}
/* Nav transparan hanya di atas section gelap. Di atas section terang ia kembali
   ke keadaan stuck (latar shell): tinta gelap di atas latar transparan jatuh
   di atas sisa foto hero dan kontrasnya hilang. */
.nav.is-top[data-on='light'] {
  background: color-mix(in srgb, var(--c-shell) 88%, transparent);
  backdrop-filter: blur(14px);
  color: var(--c-ink);
  border-bottom-color: var(--a-line);
}
.nav__in {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
}
.nav__brand { display: flex; align-items: center; gap: .7rem; }
.nav__brand svg { width: 1.9rem; height: 1.9rem; }
.nav__brand b {
  font-family: var(--font-brand);
  font-size: 1.25rem;
  font-weight: 400;
  letter-spacing: .22em;
  text-transform: uppercase;
}
.nav__links { display: flex; align-items: center; gap: 2rem; }
.nav__links a {
  font-size: var(--fs-small);
  position: relative;
  padding-block: .3rem;
}
.nav__links a::after {
  content: '';
  position: absolute;
  left: 0; bottom: 0;
  width: 100%; height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right;
  transition: transform .4s var(--ease);
}
.nav__links a:hover::after,
.nav__links a[aria-current='page']::after, .nav__links a.is-active::after { transform: scaleX(1); transform-origin: left; }
.nav__actions { display: flex; align-items: center; gap: .75rem; }
/* submenu Tipe Unit: hover + focus-within, tanpa JS. ::before menjembatani
   celah antara tautan dan panel supaya hover tidak putus di tengah jalan. */
.nav__drop { position: relative; }
.nav__sub {
  position: absolute; top: calc(100% + .9rem); left: -1.1rem;
  display: grid; min-width: 10rem; padding: .6rem 1.1rem;
  background: var(--c-shell); color: var(--c-ink);
  border-radius: 4px; box-shadow: 0 14px 34px rgb(0 0 0 / .14);
  opacity: 0; visibility: hidden; transform: translateY(.4rem);
  transition: opacity .25s var(--ease), transform .25s var(--ease), visibility .25s;
}
.nav__sub::before { content: ''; position: absolute; inset: -1rem 0 auto; height: 1rem; }
.nav__drop:hover .nav__sub,
.nav__drop:focus-within .nav__sub { opacity: 1; visibility: visible; transform: none; }
.nav__links .nav__sub a { justify-self: start; padding-block: .45rem; }


.nav__burger { display: none; width: 2.5rem; height: 2.5rem; position: relative; }
.nav__burger i {
  position: absolute; left: 50%; width: 1.35rem; height: 1px;
  background: currentColor; transform: translateX(-50%);
  transition: transform .4s var(--ease), opacity .25s;
}
.nav__burger i:nth-child(1) { top: 1rem; }
.nav__burger i:nth-child(2) { top: 1.5rem; }
.is-open .nav__burger i:nth-child(1) { transform: translateX(-50%) translateY(.25rem) rotate(45deg); }
.is-open .nav__burger i:nth-child(2) { transform: translateX(-50%) translateY(-.25rem) rotate(-45deg); }

/* menu layar penuh mobile */
.menu {
  position: fixed; inset: 0; z-index: var(--z-menu);
  background: var(--c-arang); color: var(--c-shell);
  display: grid; align-content: center; gap: .5rem;
  padding: var(--gutter);
  clip-path: inset(0 0 100% 0);
  transition: clip-path .7s var(--ease);
  pointer-events: none;
}
.is-open .menu { clip-path: inset(0 0 0 0); pointer-events: auto; }
/* Menu layar penuh berada di atas nav, jadi burger — satu-satunya tombol
   tutup — ikut tertimbun. Saat terbuka nav dinaikkan ke atas menu dan dibuat
   transparan; garis burger yang sudah beranimasi jadi X berfungsi sebagai
   tombol close, tanpa markup tambahan di lima halaman. */
.is-open .nav {
  z-index: calc(var(--z-menu) + 1);
  background: none;
  backdrop-filter: none;
  color: var(--c-shell);
  border-bottom-color: transparent;
}
.menu a {
  font-family: var(--font-display);
  font-size: clamp(2.2rem, 9vw, 3.6rem);
  line-height: 1.08;
  opacity: 0; transform: translateY(24px);
  transition: opacity .5s var(--ease), transform .5s var(--ease);
}
.menu .menu__sub { font-size: clamp(1.3rem, 5vw, 1.8rem); padding-left: 1.5rem; }
.is-open .menu a { opacity: 1; transform: none; transition-delay: calc(.12s + var(--i) * .06s); }

@media (max-width: 900px) {
  .nav__links { display: none; }
  .nav__burger { display: block; }
  .nav .btn { display: none; }
}

/* ----------------------------------------------------------------- HERO */
.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  align-items: flex-end;
  padding-bottom: clamp(3rem, 7vw, 6rem);
  color: var(--c-shell);
  overflow: hidden;
}
.hero__media { position: absolute; inset: 0; z-index: var(--z-below); }
.hero__media img {
  width: 100%; height: 100%;
  object-fit: cover;
  animation: kenburns 22s ease-out both;
}

/* Slider hero (partials/hero.blade.php). Slide ke-i = .hero__img ke-i + .hero__text ke-i.
   Default (dan tanpa JS): hanya .is-active yang terlihat. Efek dipilih lewat data-effect;
   prefers-reduced-motion mematikan transisinya lewat aturan global di base.css. */
.hero__img { position: absolute; inset: 0; opacity: 0; transition: opacity .9s var(--ease), transform .9s var(--ease); }
/* Slide lama (.is-prev) tetap opak di bawah; slide baru memudar masuk di atasnya,
   jadi latar halaman tidak pernah terlihat di tengah pergantian. */
.hero__img.is-prev { opacity: 1; z-index: 1; transition: none; }
.hero__img.is-active { opacity: 1; z-index: 2; }
.hero:not([data-effect="zoom"]) .hero__media img { animation: none; }
/* Geser: slide masuk menutup slide lama (.is-prev) dari kanan/kiri; JS menaruh posisi awalnya. */
.hero[data-effect="slide"] .hero__img { opacity: 1; visibility: hidden; transition: transform .9s var(--ease); }
.hero[data-effect="slide"] .hero__img.is-prev { visibility: visible; z-index: 1; }
.hero[data-effect="slide"] .hero__img.is-active { visibility: visible; z-index: 2; }

.hero__texts { display: grid; }
.hero__text { grid-area: 1 / 1; align-self: end; opacity: 0; visibility: hidden; transition: opacity .6s var(--ease), visibility .6s; }
.hero__text.is-active { opacity: 1; visibility: visible; transition-delay: .25s; }

.hero__nav { display: flex; align-items: center; gap: .25rem; margin-top: 1.75rem; margin-left: -.75rem; }
.hero__nav button {
  width: 2.75rem; height: 2.75rem; display: grid; place-items: center;
  color: inherit; background: none; border: 0; padding: 0; cursor: pointer;
}
.hero__dot::before { content: ''; width: 1.75rem; height: 2px; background: currentColor; opacity: .4; transition: opacity .3s; }
.hero__dot[aria-current="true"]::before { opacity: 1; }
.hero__toggle svg { width: .9rem; height: .9rem; }
.hero__toggle[aria-pressed="true"] .i-pause,
.hero__toggle[aria-pressed="false"] .i-play { display: none; }

@keyframes kenburns { from { transform: scale(1.06); } to { transform: scale(1.14); } }


/* Scrim hero = SATU lapis di atas foto, bukan kotak di belakang teks.
   Dua arah dalam satu elemen: lapis mendatar menggelapkan kolom kiri tempat
   headline duduk, lapis --a-scrim menahan dasar frame. Karena keduanya
   membentang inset:0, tidak ada tepi kotak yang bisa terlihat memotong foto.
   Angka: `node tools/eyebrow-contrast.mjs`. */
.hero__scrim {
  position: absolute; inset: 0; z-index: var(--z-behind);
  background:
    linear-gradient(90deg,
      rgba(var(--a-scrim-rgb), .58) 0%,
      rgba(var(--a-scrim-rgb), .44) 32%,
      rgba(var(--a-scrim-rgb), 0) 70%),
    var(--a-scrim);
  transition: background .7s var(--ease);
}
.hero__in { width: 100%; }
/* Pagu & ukuran headline hero diatur di satu tempat, lihat .hero .display di bawah. */
.hero__meta {
  display: flex; flex-wrap: wrap; gap: 1.5rem 2.5rem;
  margin-top: 2.5rem; padding-top: 1.5rem;
  border-top: 1px solid var(--a-line-dark);
}
.hero__meta div { min-width: 7rem; }
.hero__meta .num { font-size: 1.6rem; font-family: var(--font-display); display: block; }

/* anchor harga */
.hero__price { display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem 1.25rem; margin-top: 1.5rem; }
/* font-weight: var(--fw-display), BUKAN 400. 400 adalah bobot Zodiak di
   Klasik; di Senja (Bricolage) dan Hutan (Schibsted) display-nya 700-800,
   jadi angka mati di sini membuat harga hero tampil setipis body text
   sementara headline di atasnya tebal. */
.hero__price b {
  font-family: var(--font-display); font-size: clamp(1.6rem, 3.4vw, 2.4rem);
  font-weight: var(--fw-display); letter-spacing: -.02em;
}
.hero__price .eyebrow { width: 100%; }

.scrollhint {
  position: absolute; right: var(--gutter); bottom: 2.5rem;
  display: flex; flex-direction: column; align-items: center; gap: .75rem;
  /* Penanda afordansi, bukan label baca: kapitalnya dipertahankan karena teks
     vertikal satu kata lebih terbaca dalam kapital, tapi tracking-nya dipangkas
     separuh agar tidak bersaing dengan .eyebrow. */
  font-size: var(--fs-eyebrow); letter-spacing: .12em; text-transform: uppercase;
  writing-mode: vertical-rl;
}
.scrollhint::after {
  content: ''; width: 1px; height: 3.5rem; background: currentColor;
  transform-origin: top; animation: hint 2.2s var(--ease-io) infinite;
}
@keyframes hint { 0% { transform: scaleY(0); } 50% { transform: scaleY(1); } 100% { transform: scaleY(0); transform-origin: bottom; } }
@media (max-width: 900px) { .scrollhint { display: none; } }

/* -------------------------------------------------------------- MARQUEE */
/* Satu baris bergulir. Grup isi digandakan oleh initMarquee() sampai lebih
   lebar dari container, lalu disalin sekali lagi supaya translate -50%
   menyambung mulus. Dulu clone-nya ditempel ke .marquee (bukan ke track),
   jadi muncul sebagai baris kedua — itu "double text" yang terlihat. */
.marquee {
  overflow: hidden;
  /* mask-image membuat stacking context, jadi strip butuh background
     eksplisit — tanpa itu ia tembus ke section di belakangnya. */
  background: var(--bg);
  border-block: 1px solid var(--line);
  padding-block: 1.15rem;
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
/* Durasi mengikuti panjang track, bukan angka bulat: 32s dulu untuk 9 tempat.
   Sekarang 12 tempat, jadi 43s menjaga kecepatan baca tetap ~97 px/detik.
   Tambah/kurangi entri -> setel ulang di sini, atau teks jadi terlalu cepat. */
.marquee__in { display: flex; width: max-content; background: var(--bg); animation: marq 43s linear infinite; will-change: transform; }
.marquee:hover .marquee__in, .marquee:focus-within .marquee__in { animation-play-state: paused; }
.marquee__in > span { display: flex; }
.marquee__in i {
  font-style: normal;
  padding-inline: 1.75rem;
  font-size: var(--fs-small);
  letter-spacing: .04em;
  display: flex; align-items: center; gap: .6rem;
  white-space: nowrap;
}
.marquee__in i::after { content: '·'; color: var(--accent-text); margin-inline-start: 1.15rem; }
.marquee__in b { font-weight: 700; }
/* Logo resmi punya rasio berbeda-beda; disamakan lewat tinggi, bukan lebar. */
/* SATU NADA. Di marquee yang membawa informasi adalah teksnya ("10 Menit",
   "Tol Padalarang"); logo hanya tekstur pendukung. Sepuluh logo berwarna
   penuh (Jasamarga, WHOOSH, KAI, IKEA, Borma, Dusun Bambu, Lembang Park Zoo,
   Wahoo, lambang Cimahi, lambang KBB) memasukkan belasan hue asing ke sistem
   beton + satu kuningan, dan One
   Metal Rule tidak selamat dari itu. Bandingkan dengan .banks--partners di
   bawah, yang justru dibiarkan berwarna. */
.marquee__logo { height: 1.75rem; width: auto; max-width: 5rem; object-fit: contain; display: block; flex: none; filter: grayscale(1) opacity(.55); }
/* Mark yang mendekati persegi (lambang daerah, lockup Lembang Park Zoo) kalah
   lebar dari wordmark memanjang saat tingginya disamakan — jadi bercak kecil.
   Diberi ruang lebih supaya bobot optisnya sebanding. */
.marquee__logo--compact { height: 2.25rem; }
@keyframes marq { to { transform: translate3d(-50%, 0, 0); } }
@media (prefers-reduced-motion: reduce) { .marquee__in { animation: none; } }

/* --------------------------------------------------------- TEXT STAGGER */
[data-stagger] .w {
  display: inline-block;
  opacity: 0;
  transform: translateY(.55em);
  transition: opacity .75s var(--ease), transform .75s var(--ease);
  transition-delay: calc(var(--i) * 42ms);
}
[data-stagger].is-in .w { opacity: 1; transform: none; }

/* ------------------------------------------------------------ UNIT CARD */
.units { display: grid; gap: clamp(2rem, 5vw, 4rem); grid-template-columns: repeat(2, 1fr); }
@media (max-width: 900px) { .units { grid-template-columns: 1fr; } }

.unit {
  display: flex; flex-direction: column;
  border-top: 1px solid var(--line);
  padding-top: 1.5rem;
}
.unit__media { margin-bottom: 1.5rem; }
.unit__media img { transition: transform 1.1s var(--ease); }
@media (hover: hover) {
  .unit:hover .unit__media img { transform: scale(1.05); }
}
.unit__head { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; }
/* letter-spacing: var(--ls-display), bukan .02em. Tracking POSITIF di ukuran
   display adalah sisa era Instrument Serif; ketiga font sekarang minta
   negatif, dan nilainya sudah ada per fork. */
.unit__name {
  font-family: var(--font-display); font-size: clamp(1.8rem, 3vw, 2.6rem);
  letter-spacing: var(--ls-display);
}
.unit__meaning { font-style: italic; color: var(--fg-muted); font-size: var(--fs-small); }
.unit__facts { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: 1rem; }
.unit__facts .tag { padding: .7rem; }
.unit__price { margin-top: 1rem; }
/* --font-num, BUKAN --font-display: di Klasik display-nya Zodiak, yang lebar
   digitnya berkisar 0.387-0.698 em dan tidak punya fitur OpenType `tnum`.
   Harga dua tipe berdiri berdampingan di #tipe, jadi digit yang lebarnya
   berbeda membuat kedua kolom angka tidak pernah sejajar. */
.unit__price .num {
  font-size: clamp(1.35rem, 2.4vw, 1.8rem);
  font-family: var(--font-num);
  font-weight: var(--fw-display);
  font-variant-numeric: tabular-nums lining-nums;
}
/* "Harga Cash Keras" menerangkan angka di bawahnya — label baca, bukan penanda
   section. Sentence case 14px, sejalan The Form Speaks Plainly Rule. */
.unit__price small { display: block; font-size: var(--fs-small); letter-spacing: 0; text-transform: none; color: var(--fg-muted); }

/* ------------------------------------------------------ HORIZONTAL SCROLL */
[data-hscroll] { overflow: hidden; padding-block: var(--section-y); }
[data-htrack] { display: flex; gap: clamp(1rem, 3vw, 3rem); will-change: transform; padding-inline: var(--gutter); }
[data-htrack] > * { flex: 0 0 min(38rem, 78vw); }
[data-hscroll].is-swipe { overflow-x: auto; scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch; }
[data-hscroll].is-swipe [data-htrack] { transform: none !important; }
[data-hscroll].is-swipe [data-htrack] > * { scroll-snap-align: center; }
/* Tablet sentuh ≥1024px tidak masuk @media (max-width: 900px) di bawah, dan
   bukan desktop untuk pin GSAP. Tanpa ini galeri terkunci overflow:hidden. */
@media (hover: none), (pointer: coarse) {
  [data-hscroll] { overflow-x: auto; scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain; }
  [data-hscroll] [data-htrack] { transform: none !important; }
}

.legend { display: flex; flex-wrap: wrap; gap: 1.5rem; margin-top: 1.5rem; font-size: var(--fs-small); }
.legend span { display: flex; align-items: center; gap: .5rem; }
/* Kunci harus membuka peta. Polygon dicat DI ATAS foto aerial dengan
   fill-opacity (.3 tersedia, .4 booked/sold), jadi swatch warna pekat tidak
   pernah cocok dengan apa pun yang terlihat di denah — "sold" bahkan memakai
   hex yang berbeda sama sekali. Swatch kini memakai opacity yang sama di atas
   dasar batu sebagai pengganti foto, sehingga nada di legenda = nada di peta. */
.legend i {
  width: .85rem; height: .85rem; border-radius: 2px;
  /* var(--c-tan), token yang PERSIS dipakai .siteplan .blok — bukan --accent.
     Keduanya kebetulan sama di ketiga fork sekarang; menyalin token petanya
     membuat legenda tetap benar kalau nanti tidak. */
  --swatch: color-mix(in srgb, var(--c-tan) 30%, transparent);
  background: linear-gradient(var(--swatch), var(--swatch)), var(--c-stone);
}

/* --------------------------------------------------------------- FILTER */
.chips { display: flex; flex-wrap: wrap; gap: .5rem; }
.chips button {
  padding: .5rem 1.05rem; border-radius: 100px;
  box-shadow: inset 0 0 0 1px var(--line);
  font-size: var(--fs-small);
  transition: background-color .3s var(--ease), color .3s var(--ease);
}
.chips button[aria-pressed='true'] { background: var(--c-ink); color: var(--c-shell); }

/* POI list lokasi */
.poi { border-top: 1px solid var(--line); }
.poi li {
  display: flex; align-items: center; justify-content: space-between; gap: 1.5rem;
  padding-block: 1.05rem; border-bottom: 1px solid var(--line);
  /* transform, bukan padding-left: properti layout memicu reflow tiap frame. */
  transition: opacity .35s, transform .35s var(--ease);
}
@media (hover: hover) {
  .poi li:hover { transform: translateX(.75rem); }
}
.poi li[hidden] { display: none; }

/* Logo kecil mengekor nama. Ditaruh sesudah teks, bukan sebelum: dengan
   begitu semua nama mulai di x yang sama, dan wordmark selebar WHOOSH
   (7,4:1) tidak perlu dipaksa masuk kotak seukuran lambang persegi. */
.poi__label { display: flex; align-items: center; gap: .85rem; min-width: 0; }
.poi__logo {
  height: 1.25rem; width: auto; max-width: 6rem;
  object-fit: contain; flex: none; display: block;
}
/* transform saat hover membuat stacking context; tanpa backdrop yang dicat,
   baris jadi tembus ke apa pun di belakangnya saat bergeser. */
.poi li { background: var(--bg); }
/* Sama seperti .marquee__logo--compact: mark mendekati persegi butuh tinggi
   lebih agar bobot optisnya menyamai wordmark memanjang. Baris setinggi 62px
   ditentukan teks, jadi 1.7rem masih jauh di bawah dan tidak menggeser layout. */
.poi__logo--compact { height: 1.7rem; }
@media (max-width: 600px) {
  .poi__label { gap: .65rem; }
  .poi__logo { height: 1.05rem; max-width: 4.5rem; }
  .poi__logo--compact { height: 1.43rem; }
}
.poi__min { white-space: nowrap; font-family: var(--font-num); font-variant-numeric: tabular-nums lining-nums; font-size: var(--fs-small); color: var(--accent-text); letter-spacing: -.02em; }

/* ----------------------------------------------------------------- FORM */
.form { display: grid; gap: 1.15rem; max-width: 34rem; }
/* date dan time ikut di sini: keduanya terlewat sebelumnya sehingga kotak
   pointernya hanya 32px — di bawah batas 44px, dan justru di form jadwal
   kunjungan yang merupakan jalur konversi. */
/* ::placeholder tidak pernah punya aturan di sini, jadi ia memakai warna
   bawaan peramban — sekitar 2,91:1 di atas section gelap, jauh di bawah 4,5:1.
   Bawaan itu diturunkan dari warna teks, dan teks di section gelap terang,
   sehingga hasilnya justru paling buruk tepat di form lead #kontak.
   opacity: 1 WAJIB: Firefox menerapkan opacity .54 pada placeholder, yang
   akan menghapus lagi apa pun kontras yang baru saja dibayar di sini.
   --fg-muted mengikuti .theme-dark, jadi satu aturan ini benar di kedua
   permukaan: 6,93:1 di section gelap, 6,55:1 di section terang. */
.form ::placeholder {
  color: var(--fg-muted);
  opacity: 1;
}

.form input[type='text'], .form input[type='tel'],
.form input[type='date'], .form input[type='time'], .form select {
  width: 100%; padding: .95rem 0; min-height: 44px;
  background: none; border: 0; border-bottom: 1px solid var(--line);
  transition: border-color .3s;
}
/* Garis bawah menandai fokus SEKALIGUS cincin :focus-visible dari base.css.
   Menghapus outline di form konversi = pengguna keyboard kehilangan posisi. */
.form input:focus, .form select:focus { border-color: var(--accent); }
.form input[aria-invalid='true'] { border-color: var(--c-clay); border-bottom-width: 2px; }
.form label.check { display: flex; gap: .7rem; align-items: flex-start; font-size: var(--fs-small); color: var(--fg-muted); }
.form label.check input { min-width: 1.35rem; min-height: 1.35rem; accent-color: var(--c-ink); }

/* Status form dibedakan lewat ikon + bobot, bukan warna saja. */
.form__note { font-size: var(--fs-small); color: var(--fg); min-height: 1.4em; }
.form__note[data-state='ok']::before    { content: '✓ '; font-weight: 700; }
.form__note[data-state='warn']::before  { content: '! '; font-weight: 700; }
.form__note[data-state='error']::before { content: '✕ '; font-weight: 700; }
.form__note[data-state='error'] { color: var(--c-clay); font-weight: 600; }
.form__note a { text-decoration: underline; text-underline-offset: 3px; }

.form__alt { display: block; margin-top: .4rem; font-size: var(--fs-small); color: var(--fg-muted); }
.form__alt a { color: var(--fg); text-decoration: underline; text-underline-offset: 3px; }

.privacy { font-size: var(--fs-small); color: var(--fg-muted); }
.privacy summary {
  cursor: pointer; color: var(--fg);
  /* Terukur 40px sebelumnya. Ditutup lewat padding, BUKAN lewat display:flex
     + min-height: `display` selain list-item menghapus segitiga disclosure
     bawaan Chrome, dan segitiga itu satu-satunya petunjuk bahwa blok ini bisa
     dibuka. Teks 14px x lh 1.62 = 22,7px, jadi 2 x .665rem menggenapkan 44px. */
  padding-block: .665rem;
  text-decoration: underline; text-underline-offset: 3px;
}
.privacy p { margin-top: .6rem; max-width: var(--maxw-text); }

/* -------------------------------------------------------- FLOATING WA */
.wa {
  --wa-bottom: calc(clamp(1.75rem, 3.5vw, 3rem) + env(safe-area-inset-bottom, 0px));
  position: fixed; right: clamp(1rem, 3vw, 2rem);
  bottom: var(--wa-bottom);
  z-index: var(--z-fab);
  display: inline-flex; align-items: center;
  padding: 1rem;
  border-radius: 100px;
  background: var(--c-wa); color: #FFF;
  /* Cincin putih: teal di atas section hijau tua nyaris menyatu tanpanya. */
  box-shadow: 0 0 0 2px #FFF, 0 12px 32px rgba(0,0,0,.28);
  /* Sembunyi harus melewati bottom + shadow, bukan cuma 120% tinggi sendiri —
     kalau tidak, tombol nongol separuh di tepi bawah layar. */
  transform: translateY(calc(100% + var(--wa-bottom) + 2rem));
  visibility: hidden; pointer-events: none;
  transition: transform .55s var(--ease), background-color .3s, visibility 0s .55s;
}
.wa.is-on {
  transform: none;
  visibility: visible; pointer-events: auto;
  transition: transform .55s var(--ease), background-color .3s, visibility 0s;
}
.wa:hover, .wa:focus-visible { background: var(--c-wa-2); color: #FFF; }
.wa svg { width: 1.5rem; height: 1.5rem; }

/* --------------------------------------------------------------- FOOTER */
/* CTA dan footer sama-sama --c-arang, jadi tanpa garis keduanya menyatu
   jadi satu blok gelap dengan lubang kosong di tengahnya. Hairline
   memberi batas; padding yang dipangkas menghapus lubangnya. */
.footer {
  background: var(--c-arang); color: var(--c-shell);
  padding-block: clamp(2.5rem, 5vw, 4rem) 2rem;
  border-top: 1px solid var(--a-line-dark);
}
/* Section terakhir sebelum footer: --section-y penuh (176px di 1920)
   ditambah padding footer menghasilkan 272px kosong tanpa apa pun. */
#kontak { padding-bottom: clamp(2.5rem, 5vw, 4rem); }
.footer a:hover { color: var(--accent-text); }
.footer__grid { display: grid; grid-template-columns: 1.4fr 1fr 1fr; gap: 2.5rem; }
@media (max-width: 900px) { .footer__grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 600px) { .footer__grid { grid-template-columns: 1fr; } }
.footer h2 { font-family: var(--font-body); font-size: var(--fs-eyebrow); letter-spacing: .2em; text-transform: uppercase; color: #A7A6A3; margin-bottom: 1rem; line-height: 1.12; }
.footer ul { list-style: none; padding: 0; display: grid; gap: .6rem; font-size: var(--fs-small); }
.footer__bottom {
  margin-top: clamp(2.5rem, 6vw, 4.5rem); padding-top: 1.5rem;
  border-top: 1px solid var(--a-line-dark);
  display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between;
  font-size: .75rem; color: #A7A6A3;
}
/* 64ch merender ~97 karakter; pakai token yang sudah dikalibrasi. */
.footer__disclaimer { max-width: var(--maxw-text); line-height: 1.6; }

/* Halaman panjang: hemat rendering. Jangan pada hero / section yang di-pin. */
.defer { content-visibility: auto; contain-intrinsic-size: auto 80svh; }

/* Transisi antar halaman — Chrome/Safari; Firefox jatuh ke navigasi biasa */
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: vtOut .3s cubic-bezier(.4,0,1,1) both; }
::view-transition-new(root) { animation: vtIn .45s cubic-bezier(0,0,.2,1) both; }
@keyframes vtOut { to { opacity: 0; } }
@keyframes vtIn { from { opacity: 0; } }

/* ------------------------------------------------------------------ FAQ */
.faq { border-top: 1px solid var(--line); }
.faq details { border-bottom: 1px solid var(--line); }
.faq summary {
  list-style: none;
  cursor: pointer;
  padding-block: 1.35rem;
  display: flex; align-items: baseline; justify-content: space-between; gap: 1.5rem;
  font-size: clamp(1rem, 1.6vw, 1.15rem);
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: '+';
  font-family: var(--font-num); font-variant-numeric: tabular-nums lining-nums;
  color: var(--accent-text);
  transition: transform .35s var(--ease);
  flex: none;
}
.faq details[open] summary::after { transform: rotate(45deg); }
.faq details > div {
  padding-bottom: 1.5rem;
  max-width: var(--maxw-text);
  color: var(--fg-muted);
  font-size: var(--fs-small);
  line-height: 1.65;
}

/* --------------------------------------------------- skema pembayaran */
.pay { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gutter); }
.pay--duo { grid-template-columns: repeat(2, 1fr); }
/* Aturan mobile HARUS sesudah .pay--duo: specificity sama, yang terakhir menang. */
@media (max-width: 900px) { .pay, .pay--duo { grid-template-columns: 1fr; } }
.pay article {
  padding: clamp(1.5rem, 3vw, 2.25rem);
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--bg);
}
.pay h3 { font-size: 1.25rem; }
.pay ul, .ticklist { list-style: none; padding: 0; margin-top: 1.25rem; display: grid; gap: .5rem; font-size: var(--fs-small); }
.pay li, .ticklist li { display: flex; gap: .6rem; align-items: flex-start; color: var(--fg-muted); }
.pay li::before { content: '•'; color: var(--accent-text); flex: none; }
.ticklist li::before { content: '✓'; color: var(--accent-text); flex: none; }

/* Dua kartu, bukan tiga: rincian 'sudah termasuk' pindah ke tipe-unit.html.
   Di sini angka berhenti jadi butir daftar dan naik jadi isi kartu. */
.pay--duo ul { margin-top: 1.5rem; gap: 1.25rem; }
.pay--duo li { display: grid; gap: .1rem; color: var(--fg); }
.pay--duo li::before { content: none; }
.pay--duo li > span { font-size: var(--fs-small); color: var(--fg-muted); }
.pay--duo li > b {
  font-family: var(--font-num);
  font-size: clamp(1.35rem, 2.4vw, 1.75rem);
  font-weight: var(--fw-display);
  line-height: 1.15; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums lining-nums;
  white-space: nowrap;
}
.pay--duo li > small { font-size: var(--fs-small); }

/* logo bank — mark buatan sendiri; ganti dengan aset resmi bila sudah ada */
.banks {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: .75rem 1rem; margin-top: 1.5rem;
}
.banks img { height: 1.75rem; width: auto; max-width: 8rem; object-fit: contain; display: block; flex: none; }
.banks span {
  padding: .6rem 1.1rem;
  border: 1px solid var(--line);
  border-radius: 4px;
  font-size: var(--fs-small);
  font-weight: 600;
  letter-spacing: .04em;
  color: var(--fg-muted);
}
/* Baris bank di bawah kalkulator: label kecil + deret logo */
.banks--partners {
  margin-top: clamp(2rem, 4vw, 3rem);
  padding-top: clamp(1.5rem, 3vw, 2rem);
  border-top: 1px solid var(--line);
  gap: .85rem 1.25rem;
}
.banks--partners .banks__label {
  padding: 0; border: 0; width: 100%;
  /* Kalimat pendek yang memperkenalkan deret logo, bukan penanda section.
     Kapital + tracking di sini salah satu dari sepuluh micro-label yang
     membuat penanda aslinya tenggelam. */
  font-size: var(--fs-small); font-weight: 600;
  letter-spacing: 0; text-transform: none;
  color: var(--fg-muted);
}
/* BERWARNA, sengaja — kebalikan dari .marquee__logo di atas. Ini klaim
   kredibilitas yang sudah dikonfirmasi developer dan pengenalan merek bank
   bergantung pada warnanya; mengabukannya menghapus nilai informasinya. Yang
   dijaga bukan hue-nya melainkan luasnya: satu strip berpagar hairline atas
   dengan tinggi terkunci, jadi warna asing tidak pernah melewati satu baris. */
.banks--partners img { height: 2rem; }
@media (max-width: 600px) {
  .banks img { height: 1.5rem; }
  /* Enam logo tidak muat satu baris di 390px. Flex-wrap membuat tepi kanan
     compang-camping karena lebar tiap logo berbeda; dua kolom mengunci
     perataannya dan terisi rata tiga baris. */
  .banks--partners {
    display: grid; grid-template-columns: 1fr 1fr;
    justify-items: start; align-items: center;
  }
  .banks--partners .banks__label { grid-column: 1 / -1; }
  .banks--partners img { height: 1.75rem; }
}

/* Catatan rute e-mail di #kontak. Disembunyikan by default lewat [hidden];
   layout (server) dan initWa() memasang data-wa-placeholder di <html> hanya
   kalau nomor resmi belum ada, jadi catatan ini hilang sendiri begitu nomornya dipasang. */
[data-wa-placeholder='true'] [data-wa-note] { display: block; }
/* Klaim "nomor resmi" disembunyikan selama nomornya masih placeholder. */
[data-wa-placeholder='true'] [data-wa-official] { display: none; }

/* ----------------------------------------------- sticky bar mobile */
.mobar {
  position: fixed; inset: auto 0 0 0; z-index: var(--z-mobar);
  display: none;
  border-top: 1px solid var(--a-line);
  background: color-mix(in srgb, var(--c-shell) 94%, transparent);
  backdrop-filter: blur(14px);
  padding: .6rem var(--gutter) calc(.6rem + env(safe-area-inset-bottom));
  gap: .6rem;
  /* Selama CONTACT.whatsapp masih placeholder, initWa() menyembunyikan tombol
     WhatsApp dan bar tersisa satu tombol. :only-child tidak bisa dipakai —
     tombolnya di-display:none, jadi tetap terhitung anak. Pagu lebar pada anak
     + center di sini benar untuk satu maupun dua tombol, tanpa sinyal dari JS. */
  justify-content: center;
}
.mobar a { flex: 1 1 auto; max-width: 22rem; justify-content: center; padding-block: .85rem; }
@media (max-width: 900px) {
  .mobar { display: flex; }
  .wa { bottom: 5.25rem; }
}

/* ------------------------------------------------------- progres bar */
.progres { display: grid; gap: 1.25rem; }
.progres__row { display: grid; gap: .5rem; }
.progres__row header { display: flex; justify-content: space-between; font-size: var(--fs-small); }
.progres__bar { height: 3px; background: var(--line); border-radius: 3px; overflow: hidden; }
.progres__bar i { display: block; height: 100%; background: var(--accent); transform-origin: left; }

/* ------------------------------------------------------ HERO HALAMAN */
.pagehero {
  position: relative;
  min-height: 78svh;
  display: flex;
  align-items: flex-end;
  padding-bottom: clamp(2.5rem, 6vw, 5rem);
  color: var(--c-shell);
  overflow: hidden;
}
.pagehero--short { min-height: 62svh; }
.pagehero__media { position: absolute; inset: 0; z-index: var(--z-below); }
.pagehero__media img {
  width: 100%; height: 100%; object-fit: cover;
  animation: kenburns 24s ease-out both;
}
.pagehero .display {
  font-size: calc(clamp(2.4rem, 6.2vw, 5.2rem) * var(--fs-scale-display));
  max-width: var(--maxw-hero);
}

/* --------------------------------------------------- tabel banding */
.cmp { width: 100%; border-collapse: collapse; }
.cmp th, .cmp td { padding: 1.05rem 1rem; border-bottom: 1px solid var(--line); text-align: left; }
.cmp td.num { white-space: nowrap; }
.cmp thead th {
  font-family: var(--font-display);
  font-size: 1.35rem; font-weight: 400; letter-spacing: .08em;
  border-bottom: 1px solid var(--fg);
}
.cmp tbody th {
  /* Kepala baris tabel banding = nama spesifikasi yang dibaca berpasangan
     dengan nilainya. Sama seperti .dl__k: label baca, bukan penanda. */
  font-family: var(--font-body); font-weight: 500;
  font-size: var(--fs-small); letter-spacing: 0; text-transform: none;
  color: var(--fg-muted); width: 34%;
}
/* Ponsel: tiga kolom muat di 320px, jadi tak ada geser horizontal tersembunyi. */
@media (max-width: 600px) {
  .cmp th, .cmp td { padding: .7rem .35rem; }
  .cmp thead th { font-size: 1.05rem; letter-spacing: .02em; }
  .cmp tbody th { width: auto; }
}
/* --line-dark (.14) terlalu samar untuk batas pill & tombol ghost di section hijau. */
.theme-dark .tag, .theme-dark .btn--ghost { box-shadow: inset 0 0 0 1px rgba(242, 241, 239, .34); }
@media (hover: hover) {
  .cmp tbody tr:hover { background: var(--a-line-soft); }
}

/* --------------------------------------------------- virtual tour embed */
.tourwrap { background: var(--bg-alt); height: clamp(32rem, 82svh, 60rem); }
.tourwrap iframe { width: 100%; height: 100%; border: 0; display: block; }
@media (max-width: 900px) { .tourwrap { height: 78svh; } }

/* -------------------------------------------------------- peta embed */
.mapwrap { background: var(--bg-alt); }
.mapwrap iframe { filter: grayscale(1) contrast(.92); transition: filter .5s var(--ease); }
.mapwrap:hover iframe, .mapwrap:focus-within iframe { filter: none; }

/* Layar sentuh tidak punya hover: tanpa aturan ini peta selamanya abu-abu.
   Peta adalah alat baca lokasi, bukan elemen dekoratif. */
@media (hover: none) {
  .mapwrap iframe { filter: none; }
}

/* ==========================================================================
   Perbaikan pasca-review visual
   ========================================================================== */

/* Nav — scrim agar teks & tombol terbaca di atas render terang */
.nav::before {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(var(--a-scrim-rgb),.55), rgba(var(--a-scrim-rgb),0));
  pointer-events: none;
  transition: opacity .45s var(--ease);
}
/* Scrim hanya perlu saat nav transparan di atas render gelap/terang campur. */
.nav::before { opacity: 0; }
.nav.is-top:not([data-on='light'])::before { opacity: 1; }
.nav__in { position: relative; z-index: var(--z-base); }
/* Tombol WhatsApp di sticky bar mobile: hijau merek WhatsApp. */
.btn--wa { --btn-bg: var(--c-wa); --btn-fg: #FFF; }
.btn--wa::after { background: var(--c-wa-2); }
.btn--wa:hover { color: #FFF; }

/* Hero — beri ruang untuk nav fixed, jangan sampai headline menabrak logo */
.hero, .pagehero { padding-top: calc(var(--nav-h) + 2.5rem); }
/* --fs-scale-display WAJIB ikut di sini: aturan ini menimpa .display milik
   base.css, dan tanpa perkalian itu Zodiak (tinggi-x 6% lebih pendek) tidak
   pernah dapat kompensasinya di hero. Pagu memakai em, bukan ch: '0' Zodiak
   0.698 em vs Bricolage 0.609 vs Schibsted 0.632 — 15ch menghasilkan lebar
   yang berbeda 15% antarversi dan patahan baris yang tidak bisa diprediksi. */
.hero .display {
  font-size: calc(clamp(2.6rem, 6.2vw, 5.6rem) * var(--fs-scale-display));
  max-width: var(--maxw-hero);
}

.hero__in, .pagehero > .wrap, .pagehero__in { position: relative; }

/* .eyebrow adalah flex block, jadi kotaknya melar selebar .wrap (1293px di
   desktop) walau teksnya 230px — sisanya foto tanpa glyph yang tetap terhitung
   sebagai area gagal kontras. Penanda memang selebar katanya. */
.hero__in > .eyebrow,
.pagehero > .wrap > .eyebrow,
.pagehero__in > .eyebrow { width: max-content; max-width: 100%; }

/* Tombol hero seragam di mobile */
.hero__actions { display: flex; gap: .75rem; flex-wrap: wrap; margin-top: 1.75rem; }
@media (max-width: 600px) {
  .hero__actions { display: grid; gap: .65rem; }
  .hero__actions .btn { width: 100%; justify-content: center; min-height: 3.25rem; }
}

/* Render lanskap di hero mobile: jaga gerbang tetap dalam frame */
@media (max-width: 700px) {
  .hero__media img, .pagehero__media img { object-position: 62% center; }
  /* Gambar mobile 4:5 sudah dipotong sesuai bingkai; jangan digeser. */
  .hero__img:has(source) img { object-position: center; }
}

@media (max-width: 900px) {
  /* FAB WhatsApp melebur ke sticky bar — satu lapis saja */
  .wa { display: none; }
}
/* Konten terakhir tidak pernah tertutup lapisan melayang */
@media (max-width: 900px) { body { padding-bottom: 4.5rem; } }

/* Anchor jump tidak tersembunyi di balik nav */
html { scroll-padding-top: calc(var(--nav-h) + 1.5rem); }
section[id], h2 { scroll-margin-top: calc(var(--nav-h) + 1.5rem); }

/* Tombol: teks selalu di tengah, jangan melebar jadi bar */
.btn { justify-content: center; text-align: center; }
.form .btn { justify-self: start; min-width: 16rem; }

/* Angka waktu tempuh: sans + tabular, bukan monospace default browser */
.poi__min, .num {
  font-family: var(--font-body);
  font-variant-numeric: tabular-nums;
  letter-spacing: .01em;
}
.poi__min { font-size: .8125rem; letter-spacing: .06em; }

/* Checkbox: 44x44 adalah KOTAK POINTER-nya, kotak 20px yang terlihat digambar
   di dalamnya lewat ::before — pola yang persis sama dengan input[type='range']
   di atas (garis 2px adalah tampilannya, bukan kotaknya). Margin negatif 10px
   mengembalikan jejak visualnya ke 24px supaya ritme barisnya tidak berubah;
   yang melebar hanya area yang menerima jari. Centang jadi background ::before,
   bukan ::after terpisah, agar tidak ada dua item grid yang berebut tengah. */
input[type='checkbox'] {
  appearance: none;
  width: 44px; height: 44px; flex: none;
  margin: -10px;
  border: 0; background: none;
  display: grid; place-content: center;
  cursor: pointer;
}
input[type='checkbox']::before {
  content: '';
  width: 1.25rem; height: 1.25rem;
  border: 1px solid color-mix(in srgb, currentColor 38%, transparent);
  border-radius: 3px;
  transition: background-color .2s, border-color .2s;
}
input[type='checkbox']:checked::before {
  background: var(--c-tan) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M5 13l4 4L19 7' fill='none' stroke='%23282725' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/68% no-repeat;
  border-color: var(--c-tan);
}
.form label.check { min-height: 2.75rem; padding-block: .35rem; cursor: pointer; }

/* Select: panah kustom, bukan bawaan browser */
.form select {
  appearance: none;
  padding-right: 1.75rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M6 9l6 6 6-6' fill='none' stroke='%238C8579' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right .1rem center;
  background-size: 1.05rem;
}

/* Foto stok landmark: desaturasi menjaga palet earthy saat diam, tapi Dusun
   Bambu dan Waterboom dijual justru karena warnanya — jadi warna penuh
   dikembalikan saat kartunya di-hover/di-fokus. */
[data-htrack] .frame { position: relative; aspect-ratio: 4 / 3; }
[data-htrack] img {
  filter: grayscale(1) contrast(1.06) brightness(1.02);
  transition: filter .5s var(--ease), transform .6s var(--ease);
}
[data-htrack] .frame::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--accent) 55%, transparent),
    color-mix(in srgb, var(--c-arang) 62%, transparent));
  mix-blend-mode: multiply;
  transition: opacity .5s var(--ease);
}
[data-htrack] figure:hover img,
[data-htrack] figure:focus-within img { filter: none; transform: scale(1.02); }
[data-htrack] figure:hover .frame::after,
[data-htrack] figure:focus-within .frame::after { opacity: 0; }

/* Layar sentuh tidak punya hover: kartu tampil berwarna sejak awal. */
@media (hover: none) {
  [data-htrack] img { filter: none; }
  [data-htrack] .frame::after { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  [data-htrack] figure:hover img,
  [data-htrack] figure:focus-within img { transform: none; }
}


/* Galeri horizontal: peek disengaja + petunjuk geser */
[data-hscroll] { position: relative; }
[data-hscroll]::after {
  content: '⟶';
  position: absolute; right: var(--gutter); bottom: calc(var(--section-y) - 2rem);
  font-size: var(--fs-small); letter-spacing: .2em; color: var(--fg-muted);
  pointer-events: none;
}

/* Ritme vertikal: anak terakhir tidak menambah margin di atas padding section */
.section > .wrap > :last-child { margin-bottom: 0; }

/* Prosa tanpa kelas ikut pagu baca yang sama. Dibatasi ke .section supaya
   paragraf struktural (hero__price, form__note) tidak ikut terjepit. */
.section p:not([class]) { max-width: var(--maxw-text); }

/* Kontras teks sekunder di atas arang hangat */
/* --fg-muted gelap ada di tokens.css tiap versi. */

/* --------------------------------------------------------- MASTER PLAN */
/* Gambar rencana ditampilkan utuh (bukan object-fit) supaya nomor kavling
   tidak terpotong; lebar dibatasi agar lembar potret tidak menelan layar. */
.planfig > [data-reveal] { overflow: hidden; }
.planfig img { width: 100%; height: auto; display: block; background: var(--c-shell-2); }
.planfig--main { max-width: 46rem; margin-inline: auto; }
.planpair { display: grid; gap: clamp(1.5rem, 4vw, 2.5rem); }
@media (min-width: 900px) { .planpair { grid-template-columns: 1fr 1fr; align-items: start; } }
.planstack { display: grid; gap: clamp(1.5rem, 4vw, 2.5rem); }

/* ------------------------------------------------------ FASILITAS */
/* Band gerbang: 12/5. Rasio brosur lama (2339×827 = 2,83) menyisakan 44%
   langit polos di atas gapura dan membuat gerbang cuma mengisi sepertiga
   lebar frame. 2,4 memberi band lebih tinggi tanpa menelan layar lebar. */
.fac-band .frame { aspect-ratio: 12 / 5; }
.fac-band figcaption {
  margin-top: 1.25rem;
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem 2rem;
}
.fac-band h3 { font-size: clamp(1.2rem, 1.6vw, 1.4rem); }
.fac-band p { font-size: var(--fs-small); max-width: var(--maxw-text); }

/* Satu kolom, menggulir di sisi kanan judul yang sticky. Tanpa overflow
   internal: scrollbar di dalam kolom menyembunyikan isi dan rusak di layar
   sentuh — kolom kiri yang mengunci sudah memberi efek yang sama. */
.facility-list {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(2rem, 4vw, 3.25rem);
}
.facility-list figcaption { margin-top: 1rem; }
.facility-list h3 { font-size: 1.15rem; }
.facility-list p { font-size: var(--fs-small); margin-top: .35rem; max-width: var(--maxw-text); }

@media (max-width: 900px) {
  .fac-band .frame { aspect-ratio: 16 / 10; }
}

/* Render properti sendiri tetap berwarna penuh; hanya foto pihak ketiga yang duotone */
.fac-band img { filter: none; }
.fac-band .frame::after { content: none; }

/* ==========================================================================
   ADAPT — target sentuh & tempo hero di layar kecil
   Audiens utama membaca dari HP. Angka di bawah diukur di 375×812.
   ========================================================================== */

/* Semua kontrol yang menerima jari minimal 44×44px. Padding visual boleh
   lebih kecil; yang tidak boleh kecil adalah kotak yang menerima pointer. */
.chips button,
.nav__burger { min-height: 44px; }

.nav__burger { width: 2.75rem; height: 2.75rem; }
.chips button { display: inline-flex; align-items: center; }


/* Baris consent diberi ruang; ukuran kotak pointer checkbox diatur sekali di
   aturan input[type='checkbox'] di atas. */
.form label.check { min-height: 44px; align-items: center; }

/* Tautan footer setinggi 18px praktis tidak bisa diketuk. */
.footer a { display: inline-flex; align-items: center; min-height: 44px; }
/* Tautan di dalam kalimat footer bawah (mis. kredit foto). Aturan
   .footer a di atas menjadikannya kotak inline-flex setinggi 44px, yang di
   tengah paragraf 12px tidak bisa patah baris dan menyisakan kurung yatim di
   ujung baris. Kredit lisensi juga wajib terbaca SEBAGAI tautan: tanpa garis
   bawah ia tidak terbedakan dari kalimat di sekitarnya, dan --accent-text
   (#825D3B) di atas latar footer hanya 2,28:1 - lebih buruk, bukan lebih baik. */
.footer__bottom a {
  min-height: 0; display: inline;
  color: var(--c-shell);
  text-decoration: underline; text-underline-offset: .15em;
}
.footer__bottom a:hover, .footer__bottom a:focus-visible { color: #FFF; }
/* Tautan e-mail 194x18 itu ternyata bukan di footer melainkan di .form__alt
   (blok "hubungi kami langsung"), jadi aturan .footer a tidak pernah
   menyentuhnya. Nomor telepon di .dl__v punya lubang yang sama. */
.form__alt a, .dl__v a {
  display: inline-flex; align-items: center; min-height: 44px;
  vertical-align: middle;
}

@media (max-width: 900px) {
  /* Sticky bar sudah menyediakan WhatsApp; hero tidak perlu mengulangnya.
     Empat CTA dalam satu layar membuat tidak ada yang terbaca sebagai utama. */
  .hero__actions [data-wa] { display: none; }

  /* Scrim bawaan berat di bawah, sedangkan teks hero ada di atas.
     Di layar kecil headline jatuh tepat di langit paling terang. */
  .hero__scrim {
    background:
      linear-gradient(180deg, rgba(var(--a-scrim-rgb),.62) 0%, rgba(var(--a-scrim-rgb),.28) 38%, rgba(var(--a-scrim-rgb),0) 55%),
      linear-gradient(90deg, rgba(var(--a-scrim-rgb),.55) 0%, rgba(var(--a-scrim-rgb),.45) 55%, rgba(var(--a-scrim-rgb),.18) 100%),
      var(--a-scrim);
  }

  /* Sticky bar menutupi dasar hero; hero setinggi 100svh harus memberi
     ruang untuknya, kalau tidak CTA terpotong. */
  .hero { padding-bottom: calc(clamp(3rem, 7vw, 6rem) + 4.5rem); }

  /* Hero mobile: rapatkan jarak antar blok supaya harga dan CTA tetap
     terlihat tanpa menggulir, setelah blok metrik dihapus. */
  
  .hero__price { margin-top: 1.25rem; }
}

@media (max-width: 600px) {
  /* Tiga jarak ditumpuk vertikal lebih terbaca daripada dibungkus setengah baris. */
  
}

/* Logo dan tautan menu layar penuh juga menerima jari. */
.nav__brand { min-height: 44px; }
.menu a { min-height: 44px; align-items: center; display: flex; }


/* ==========================================================================
   MOBILE — hirarki section, tabel, dan gambar di layar kecil
   Aturan di bawah menutup lubang yang muncul saat .grid 12 kolom mengecil
   jadi 6 kolom sementara markup masih memakai span 7/8 secara inline.
   ========================================================================== */

@media (max-width: 900px) {
  /* span 7/8 melebihi 6 track -> grid membuat kolom implisit dan meluber.
     Di lebar ini satu kolom penuh memang bacaan yang benar.
     DUA selektor, sengaja: markup sedang dipindahkan dari grid-column inline
     ke kelas .col-N, dan selama pemindahan itu belum selesai keduanya hidup
     berdampingan. Selektor [style*=...] berhenti cocok begitu sebuah elemen
     kehilangan atribut style-nya — tanpa pasangan [class*="col-"] elemen itu
     akan meluber di HP tanpa suara. */
  .grid > [style*="grid-column"],
  .grid > [class*="col-"] { grid-column: 1 / -1 !important; }

  /* Sticky bar + safe area iPhone lebih tinggi dari 4.5rem. */
  body { padding-bottom: calc(4.5rem + env(safe-area-inset-bottom, 0px)); }

  /* Galeri horizontal harus tetap bisa digeser walau GSAP gagal dimuat.
     JS hanya menambah snap; CSS yang menjamin kontennya terjangkau. */
  [data-hscroll] {
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: var(--gutter);
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
  }
  [data-hscroll] [data-htrack] { transform: none !important; }
  /* Snap di tengah membuat kartu pertama tidak pernah bisa berhenti di awal. */
  [data-hscroll] [data-htrack] > * { scroll-snap-align: start; }

  /* Tabel perbandingan: geser horizontal sampai tepi layar, bukan terpotong
     rata gutter. */
  .table-scroll {
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter);
  }
}

/* Contain HANYA di sumbu-x: swipe horizontal tidak boleh memicu navigasi
   back, tapi roda vertikal harus tetap bisa chaining ke dokumen. Container
   ini tidak punya sisa scroll vertikal sendiri (overflow-x:auto membuat
   computed overflow-y ikut auto), jadi `overscroll-behavior: contain` dua
   sumbu -- yang lenis.css tempelkan ke setiap [data-lenis-prevent] -- akan
   mengunci scroll halaman di atas tabel. Lihat tools/lenis-prevent.mjs. */
.table-scroll {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
}

@media (max-width: 600px) {
  /* Grid dua/tiga kolom yang di-set inline tidak pernah runtuh sendiri.
     Denah 705x1166 di lebar 157px tidak terbaca. */
  .grid[style*="repeat(2"],
  .grid[style*="repeat(3"],
  .grid[style*="1fr 1fr"] { grid-template-columns: 1fr !important; }
  /* Denah: 2 kolom kecil, detailnya dibaca lewat lightbox. */
  .grid.plans { grid-template-columns: repeat(2, 1fr) !important; gap: .75rem; }

  /* Baris definisi: kunci + nilai rupiah tidak muat berdampingan. */
  .dl__row { gap: .75rem; }

  /* Panorama 4.56:1: 3:2 memotong dua pertiga lebarnya. */
  .frame--pano { aspect-ratio: 2 / 1; }

  /* Panah petunjuk gulir jatuh tepat di atas caption terakhir. */
  [data-hscroll]::after { display: none; }
}

/* ==========================================================================
   UTILITAS — pengganti style inline yang berulang di lima halaman.
   Bukan awal dari framework utility: hanya nilai yang benar-benar dipakai
   markup hari ini, dan skalanya mengikuti ritme yang sudah ada (0.75 / 1 /
   1.25 / 1.5 / 2rem), bukan skala baru.
   ========================================================================== */
.col-5 { grid-column: span 5; }
.col-6 { grid-column: span 6; }
.col-7 { grid-column: span 7; }

.mt-1 { margin-top: .75rem; }
.mt-2 { margin-top: 1rem; }
.mt-3 { margin-top: 1.25rem; }
.mt-4 { margin-top: 1.5rem; }
.mt-5 { margin-top: 2rem; }
.mt-6 { margin-top: clamp(2.5rem, 6vw, 4rem); }

/* Tidak ada .m-0 di sini dengan sengaja. `* { margin: 0 }` di base.css sudah
   menanganinya, dan tidak ada aturan yang memasang margin pada .dl__v / dd / ul.
   42 pemakaiannya di markup sudah dibuang: mengganti style="margin:0" dengan
   class="m-0" hanya memindahkan derau, bukan menghapusnya. */

.small { font-size: var(--fs-small); }

/* 12px bukan anak tangga dalam skala ini (17 body / 14 small / 11 eyebrow).
   Tiga catatan kaki di index, lokasi, dan kontak memasangnya lewat style
   inline. Justru catatan legal yang paling perlu terbaca di HP pada cahaya
   rendah, jadi dinaikkan ke --fs-small dan TIDAK dibuatkan token --fs-xsmall:
   anak tangga keempat di bawah 14px membuat skalanya berhenti berarti.
   !important dipakai karena style inline tidak bisa dikalahkan cara lain;
   aturan ini boleh dihapus begitu markup melepas font-size inline-nya. */
p.muted[style*="font-size:.75rem"] { font-size: var(--fs-small) !important; }

/* ==========================================================================
   RITME HALAMAN
   Lebar kontainer mengikuti jenis konten. Sebelumnya kesepuluh section
   memakai satu kotak 88rem dan satu-satunya variasi adalah selang-seling
   terang/gelap — itu yang terbaca sebagai template.
   ========================================================================== */

/* Fold dokumen: harga, simulasi KPR, FAQ. Dibaca, bukan dilihat. */
.section--doc { background: var(--bg-alt); }
.section--doc > .wrap { max-width: 64rem; }

/* Fold galeri: tipe unit, siteplan. Dilihat, bukan dibaca. */
.section--wide > .wrap { max-width: 88rem; }

/* Set piece penuh layar. */
.section--full { --gutter: 0rem; }
.section--full > .wrap { max-width: none; }

/* Fold pendukung tidak berteriak sekeras fold utama. Tanpa ini semua section
   terbaca sama penting dan halaman kehilangan alur baca. */
.section--doc h2 { font-size: calc(var(--fs-h2-minor) * var(--fs-scale-display)); }

/* Bekas section FAQ. Enam pertanyaan pindah ke kontak.html#faq; yang tersisa
   di beranda satu baris setinggi ~5rem, bukan fold penuh. Beranda jadi
   FAQ-link -> CTA tanpa jeda. */
.faqlink {
  background: var(--bg-alt);
  border-top: 1px solid var(--line);
  padding-block: clamp(1.75rem, 4vw, 2.75rem);
  font-size: var(--fs-small);
}
.faqlink p { display: flex; flex-wrap: wrap; gap: .35rem .5rem; }

/* Tiga fold ini terbaca terlalu longgar di layar besar: jarak vertikal
   section dan jarak judul-ke-isi dipotong separuh dari --section-y. */
#pembayaran, #legalitas { padding-block: clamp(2.25rem, 5.5vw, 5.5rem); }

/* ------------------------------------------------------------ GALERI + 360 */
/* Foto potongan pano. Rasio campur (4:5, 3:2, 4:3) -> kolom ala masonry,
   bukan grid yang memaksa satu rasio. --grid: semua 16:9, grid biasa cukup.
   Kelas sendiri, bukan .grid[style*=repeat], agar tidak ikut aturan
   1-kolom mobile milik grid inline. */
.gallery { columns: 3; column-gap: var(--gutter); }
.gallery > figure { break-inside: avoid; margin-bottom: var(--gutter); }
.gallery--grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gutter); }
.gallery--grid > figure { margin-bottom: 0; }
@media (max-width: 900px) {
  .gallery { columns: 2; }
  .gallery--grid { grid-template-columns: repeat(2, 1fr); }
}

.theme-dark .chips button[aria-pressed='true'] { background: var(--c-shell); color: var(--c-ink); }

/* Status kavling (siteplan#kavling). Warna status: tersedia = aksen, dipesan = garis tebal, terjual = redup + coret. */
.lots-legend { display: flex; flex-wrap: wrap; gap: 1.25rem; list-style: none; padding: 0; font-size: var(--fs-small); }
.lots-legend li { display: flex; align-items: center; gap: .5rem; }
.lots-legend .lot { width: 1.1rem; height: 1.1rem; }
.lots__grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(2.75rem, 1fr)); gap: .4rem; list-style: none; padding: 0; margin: 0 0 1.25rem; }
.lot { display: grid; place-items: center; aspect-ratio: 1; border-radius: 4px; font-size: var(--fs-small); font-variant-numeric: tabular-nums; border: 1px solid var(--line); }
.lot--tersedia { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
.lot--dipesan { border: 2px solid var(--fg); color: var(--fg); }
.lot--terjual { color: var(--fg-muted); text-decoration: line-through; opacity: .55; }

/* Breadcrumb */
.crumbs { padding-block: 1rem; font-size: var(--fs-small); }
.crumbs ol { display: flex; flex-wrap: wrap; gap: .5rem; margin: 0; padding: 0; list-style: none; }
.crumbs li + li::before { content: '›'; margin-right: .5rem; color: var(--fg-muted); }
.crumbs a { color: var(--fg-muted); }

/* Artikel */
.posts { display: grid; gap: clamp(1.5rem, 4vw, 3rem); grid-template-columns: repeat(3, 1fr); }
.post h3 a { text-decoration: none; }
.cats { display: flex; flex-wrap: wrap; gap: .5rem; }
.cats a { padding: .4rem .9rem; border: 1px solid var(--line); border-radius: 999px; font-size: var(--fs-small); text-decoration: none; }
.cats a[aria-current='page'] { background: var(--c-ink); color: var(--c-shell); }
.pager { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.preview-note { padding-block: .75rem; font-size: var(--fs-small); color: var(--fg-muted); }
.article-body { max-width: var(--maxw-text); }
.article-body > * + * { margin-top: 1rem; }
.article-body h2, .article-body h3 { margin-top: 2rem; }
.article-body h2 { font-size: calc(var(--fs-h2-minor) * var(--fs-scale-display)); }
.article-body h3 { font-size: clamp(1.2rem, 1.6vw, 1.5rem); }
.article-body img { max-width: 100%; height: auto; border-radius: 4px; }
.article-body figure { margin: 1.5rem 0; }
.article-body figcaption { margin-top: .5rem; font-size: var(--fs-small); color: var(--fg-muted); }
/* Trix menulis nama dan ukuran berkas sebagai caption bawaan; hanya caption yang diketik admin yang tampil. */
.article-body .attachment__name, .article-body .attachment__size { display: none; }
.article-body blockquote { padding-left: 1rem; border-left: 2px solid var(--line); color: var(--fg-muted); }
.article-body ul, .article-body ol { padding-left: 1.25rem; }
.article-body a { text-decoration: underline; }
@media (max-width: 900px) { .posts { grid-template-columns: 1fr; } }

/* ------------------------------------------------------------- lightbox */
.gallery a.frame, .plans a.frame { display: block; cursor: zoom-in; }
/* Rasio denah LT1/LT2 bisa beda beberapa px: caption didorong ke dasar sel agar sejajar. */
.plans figure { display: flex; flex-direction: column; }
.plans figcaption { margin-top: auto; padding-top: .75rem; }
.lightbox {
  width: 100%; height: 100%; max-width: none; max-height: none;
  margin: 0; padding: clamp(3.5rem, 6vw, 4.5rem) clamp(1rem, 6vw, 5rem);
  border: 0; background: rgb(0 0 0 / .92); color: var(--c-shell);
}
.lightbox[open] { display: grid; place-items: center; align-content: center; gap: .9rem; }
.lightbox::backdrop { background: none; }
.lightbox img {
  width: auto; height: auto; max-width: 100%; max-height: calc(100svh - 10rem);
  object-fit: contain; background: none;
}
.lightbox__cap { font-size: var(--fs-small); text-align: center; max-width: 40em; }
.lightbox__cap:empty { display: none; }
.lightbox__btn {
  position: absolute; width: 3rem; height: 3rem;
  font-size: 2.2rem; line-height: 1; color: inherit;
  /* Di ponsel panah menimpa gambar (denah putih): piringan gelap menjaga terbaca. */
  border-radius: 50%; background: rgb(0 0 0 / .55);
}
.lightbox__close { top: .75rem; right: .75rem; }
.lightbox__prev, .lightbox__next { top: 50%; transform: translateY(-50%); }
.lightbox__prev { left: .5rem; }
.lightbox__next { right: .5rem; }
.lightbox.is-single .lightbox__prev,
.lightbox.is-single .lightbox__next { display: none; }
html:has(.lightbox[open]) { overflow: hidden; }

/* ===================================================== Landing page promo (/promo) */
/* Hero berisi form: tinggi mengikuti isi, bukan 100svh, supaya form tidak jatuh di bawah lipatan mobile. */
.lp-hero { min-height: 0; align-items: center; }
.lp-hero__in { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: clamp(2rem, 5vw, 4.5rem); align-items: end; }
.lp-hero .display { font-size: calc(clamp(2.3rem, 5vw, 4.4rem) * var(--fs-scale-display)); }
.lp-ticks { list-style: none; padding: 0; margin-top: 1.5rem; display: grid; gap: .55rem; max-width: 34rem; }
.lp-ticks li { display: flex; gap: .6rem; }
.lp-ticks li::before { content: '✓'; color: var(--accent-text-dark); flex: none; }
.lp-card { background: var(--c-shell); color: var(--c-ink); padding: clamp(1.5rem, 3vw, 2.25rem); border-radius: 4px; scroll-margin-top: calc(var(--nav-h) + 1rem); }
.lp-card .muted { color: var(--fg-muted); }
.lp-card__title { font-size: 1.5rem; }
.lp-card .form .btn { width: 100%; min-width: 0; }
@media (max-width: 900px) { .lp-hero__in { grid-template-columns: 1fr; } }

.lp-perks { background: var(--bg-alt); padding-block: clamp(2rem, 4vw, 3rem); }
.lp-perks__list { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--gutter); }
.lp-perks__list > div { border-left: 1px solid var(--line); padding-left: 1rem; }
.lp-perks__list dt { font-weight: 600; }
.lp-perks__list dd { margin: .35rem 0 0; }
@media (max-width: 900px) { .lp-perks__list { grid-template-columns: repeat(2, 1fr); } }

.lp-why { display: grid; gap: clamp(2.5rem, 6vw, 5rem); }
.lp-why__row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--gutter); align-items: center; }
.lp-why__row:nth-child(even) > .frame { order: 2; }
@media (max-width: 900px) {
  .lp-why__row { grid-template-columns: 1fr; }
  .lp-why__row:nth-child(even) > .frame { order: 0; }
}

.lp-fac { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--gutter); }
@media (max-width: 600px) { .lp-fac { grid-template-columns: repeat(2, 1fr); } }

.lp-end > * { max-width: 48rem; }
.lp-end__actions { display: flex; gap: .75rem; flex-wrap: wrap; margin-top: 1.75rem; }
@media (max-width: 600px) { .lp-end__actions { display: grid; } .lp-end__actions .btn { width: 100%; min-height: 3.25rem; } }
