/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */

:root {
  --font-default: "Funnel Sans", system-ui, -apple-system, sans-serif;
  --font-accent: "Funnel Display", "Funnel Sans", system-ui, sans-serif;

  --radius-s: 1rem;
  --radius-m: 1.6rem;
  --radius-l: 3.8rem;
  --radius-xl: 6rem;

  --speed-fast: .1s;
  --speed-med: .3s;
  --speed-slow: .6s;
  --bezier: cubic-bezier(.23, .65, .74, 1.09);

  /* light scheme — latar krem #FFFBF0, turunannya digeser ke arah hangat */
  --base: #FFFBF0;
  --base-tint: #FFFFFF;
  --base-shade: #F0EADA;
  --accent: #9F8BE7;
  --accent-rgb: 159, 139, 231;
  --additional: #DDF160;
  --t-bright: #161616;
  --t-medium: #303030;
  --t-muted: #585858;
  --t-faint: #B2AEAD;
  --t-on-accent: #161616;

  /* Tombol CTA punya warnanya SENDIRI, sengaja tidak menumpang --accent.
     Sempat dicoba mengganti --accent-nya langsung: seluruh situs ikut
     hijau, termasuk node SEO di diagram hero, dan diagramnya jadi kusam
     karena olive cuma 11% saturasi di antara lima warna yang pekat.
     Dengan token terpisah, yang pindah cuma tombolnya.

     Hijau ini GELAP (lightness 40%), kebalikan dari ungu --accent yang
     terang (71%), jadi tulisannya ivory, bukan gelap:
       #2D7645 + #FFFBF0 = 5,36:1  lulus AA
       #2D7645 + #161616 = 2,70:1  gagal
     Bonus: tombol ungu yang lama cuma 2,77:1 terhadap latar krem —
     di bawah 3:1, jadi BENTUK tombolnya nyaris tidak terbaca dan
     cuma kelihatan seperti teks melayang. Hijau ini 5,01:1. */
  --cta: #2D7645;
  --t-on-cta: #FFFBF0;

  --stroke: #E8E1D0;
  --shadow: 0 2rem 6rem rgba(22, 22, 22, .07);
}

/* --------------------------------------------------------------------------
   2. Base
   -------------------------------------------------------------------------- */

html {
  font-size: 62.5%;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  scroll-behavior: smooth;

  /* Header-nya position:fixed, jadi tanpa ini section yang dituju
     berhenti PAS di atas viewport dan judulnya ketutup header.
     Nilainya kira-kira tinggi header saat memadat (.is-scrolled:
     padding 1,2rem x2 + tombol ~4,6rem) plus sedikit ruang napas. */
  scroll-padding-top: 10rem;
}

body {
  margin: 0;
  background-color: var(--base);
  color: var(--t-medium);
  font-family: var(--font-default);
  font-size: 1.7rem;
  font-weight: 400;
  line-height: 1.6;
  overflow-x: hidden;
  transition: background-color var(--speed-med) var(--bezier),
              color var(--speed-med) var(--bezier);
}

h1, h2, h3, h4, h5, h6 {
  margin: 0;
  color: var(--t-bright);
  font-family: var(--font-accent);
  font-weight: 600;
  line-height: 1.06;
  letter-spacing: -.02em;
}

p { margin: 0 0 1.6rem; }
p:last-child { margin-bottom: 0; }

a {
  color: inherit;
  text-decoration: none;
  transition: color var(--speed-med) var(--bezier),
              opacity var(--speed-med) var(--bezier);
}

img, svg { max-width: 100%; }
ul { margin: 0; padding: 0; list-style: none; }

::selection { background: var(--accent); color: var(--t-on-accent); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}

.container { max-width: 138rem; padding-inline: 2.4rem; }

/* --------------------------------------------------------------------------
   3. Tombol pil
   -------------------------------------------------------------------------- */

.btn-pill {
  display: inline-flex;
  align-items: center;
  gap: 1rem;
  padding: 1.4rem 2.8rem;
  border: 1px solid var(--stroke);
  border-radius: 5rem;
  background: transparent;
  color: var(--t-bright);
  font-family: var(--font-default);
  font-size: 1.6rem;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  transition: all var(--speed-med) var(--bezier);
}

.btn-pill:hover {
  background: var(--t-bright);
  border-color: var(--t-bright);
  color: var(--base);
}

.btn-pill--accent {
  background: var(--cta);
  border-color: var(--cta);
  color: var(--t-on-cta);
}

.btn-pill--accent:hover {
  background: var(--t-bright);
  border-color: var(--t-bright);
  color: var(--base);
}

.btn-pill i { font-size: 1.8rem; }

/* --------------------------------------------------------------------------
   4. Header
   -------------------------------------------------------------------------- */

.header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 100;
  padding: 2.4rem 0;
  transition: padding var(--speed-med) var(--bezier),
              background-color var(--speed-med) var(--bezier),
              transform var(--speed-med) var(--bezier);
}

.header.is-scrolled {
  padding: 1.2rem 0;
  backdrop-filter: blur(14px);
  background-color: color-mix(in srgb, var(--base) 82%, transparent);
  border-bottom: 1px solid var(--stroke);
}

/* Layar pertama: header disingkirkan, ruangnya dikembalikan ke hero.
   Satu-satunya yang menyembunyikan header. Dulu ada N2 (menyelinap saat
   scroll turun) yang juga menulis transform lewat .is-tucked; keduanya
   sudah dibuang supaya perilakunya cuma punya satu sumber.

   Transisi transform-nya sudah ada di .header, jadi waktu .intro
   dilepas headernya meluncur turun, bukan muncul mendadak. */
.intro .header { transform: translateY(-100%); }

/* 12rem atas itu murni jatah header fixed. Tanpa header, kembalikan. */
.intro .hero { padding-top: 6rem; }

/* ...tapi HANYA di desktop. Di bawah 992px .burger adalah satu-satunya
   jalan ke navigasi, dan dia ikut terseret keluar layar waktu headernya
   ditarik — jadi di layar pertama beranda tidak ada cara membuka menu
   sama sekali; baru muncul setelah digulir 40px. Di lebar ini headernya
   dibiarkan di tempat.

   Latarnya tetap transparan sampai .is-scrolled, jadi masih terasa ringan
   menumpang di atas hero, bukan bar padat. Dan padding-top hero-nya
   dikembalikan ke 12rem — jatah header fixed — supaya judulnya tidak
   menyelinap di bawah logo.

   Ambangnya 991px, disamakan PERSIS dengan media query yang menyalakan
   .burger di bagian 4. Kalau salah satunya digeser, geser dua-duanya:
   beda satu piksel saja artinya ada lebar di mana headernya sembunyi
   tapi burgernya sudah aktif — persis bug yang ini. */
@media (max-width: 991px) {
  .intro .header { transform: none; }
  .intro .hero { padding-top: 12rem; }
}

.header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2.4rem;
}

.logo {
  display: inline-flex;
  align-items: center;
  gap: .8rem;
  color: var(--t-bright);
  font-family: var(--font-accent);
  font-size: 2.2rem;
  font-weight: 700;
  letter-spacing: -.03em;
}

/* Mark berdiri sendiri, tanpa lingkaran aksen — figur dengan kepala
   terpisah butuh ruang napas, dan di dalam disc 3.2rem malah jadi noda.
   SVG-nya satu path, mewarisi warna lewat currentColor. */
.logo__mark {
  flex: none;
  width: 3.2rem;
  height: 3.2rem;
  color: var(--t-bright);

  /* Dua kepala, dua warna — dua pihak yang ketemu, bukan kembar.
     SENGAJA bukan palet Google: mark berbentuk M + empat warna Google
     = Gmail, dan kita jualan jasa Google Ads, jadi jangan cari perkara.
     Versi satu warna tetap yang utama (lihat favicon). */
  --head-a: #6B54C6;
  --head-b: #C85A2C;
}

.logo__mark svg { display: block; width: 100%; height: 100%; }

.nav { display: flex; align-items: center; gap: 3.2rem; }

.nav a {
  color: var(--t-muted);
  font-size: 1.6rem;
  font-weight: 500;
}

.nav a:hover { color: var(--t-bright); }

.header__actions { display: flex; align-items: center; gap: 1.2rem; }

/* Tombol ganti tema DIHAPUS. Dulu dia satu-satunya isi header__actions
   yang tampil di HP, karena tombol "Ngobrol dulu" disembunyikan lewat
   .d-none .d-sm-inline-flex. Sekarang kebalikannya: CTA-nya yang tampil,
   dan tombol temanya hilang sama sekali — HTML, CSS, dan JS-nya. */

/* CTA header. Labelnya dua versi: "Ngobrol dulu" di layar lega, dan
   "Ngobrol" saja di HP — di 360px logo + tombol panjang tidak muat
   berdampingan. */
.header__cta-short { display: none; }

/* --- Burger + menu layar penuh -------------------------------------
   Dua-duanya cuma hidup di bawah 992px. Di atas itu .nav yang jalan,
   dan keduanya display:none supaya tidak bisa muncul karena salah
   klik atau resize. */
.burger {
  display: none;                 /* dinyalakan di media query bawah */
  width: 4.4rem;
  height: 4.4rem;
  position: relative;
  z-index: 2;                    /* di atas isi .header__inner lain */
  border: 1px solid var(--stroke);
  border-radius: 50%;
  background: transparent;
  cursor: pointer;
  padding: 0;
}

.burger span {
  position: absolute;
  left: 50%;
  width: 1.8rem;
  height: 2px;
  margin-left: -.9rem;
  border-radius: 2px;
  background: var(--t-bright);
  transition: transform var(--speed-med) var(--bezier),
              opacity var(--speed-med) var(--bezier);
}

.burger span:nth-child(1) { top: 1.7rem; }
.burger span:nth-child(2) { bottom: 1.7rem; }

/* Terbuka: dua garis jadi silang. Translate dulu ke tengah, baru rotate. */
.burger.is-open span:nth-child(1) { transform: translateY(.45rem) rotate(45deg); }
.burger.is-open span:nth-child(2) { transform: translateY(-.45rem) rotate(-45deg); }

.menu {
  position: fixed;
  inset: 0;
  /* 99 = tepat DI BAWAH .header (100). Panelnya menutupi seluruh isi
     halaman, tapi header-nya tetap mengambang di atas panel — jadi logo,
     tombol Ngobrol, dan tombol silangnya semua tetap kelihatan dan bisa
     diklik. Dulu nilainya 1 waktu .menu masih anak <header>; sekarang dia
     saudara <header>, jadi angkanya diadu langsung dengan z-index header. */
  z-index: 99;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 4rem;
  padding: 14rem 2.4rem 6rem;
  background: var(--base);
  /* visibility ikut ditransisikan supaya panel tidak bisa diklik waktu
     tersembunyi — opacity:0 sendirian masih menangkap tap. */
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--speed-med) var(--bezier),
              visibility var(--speed-med) var(--bezier);
}

.menu.is-open { opacity: 1; visibility: visible; }

.menu__nav { display: flex; flex-direction: column; gap: 2.4rem; }

.menu__nav a {
  font-family: var(--font-accent);
  font-size: 3.2rem;
  font-weight: 600;
  letter-spacing: -.02em;
  color: var(--t-bright);
  line-height: 1.1;
}

.menu__cta { align-self: flex-start; }

/* Badan dikunci waktu menu terbuka, biar halaman di baliknya tidak
   ikut ter-scroll. */
body.is-menu-open { overflow: hidden; }

@media (max-width: 991px) {
  .nav { display: none; }
  .burger { display: block; }
}

/* --------------------------------------------------------------------------
   5. Hero — SEMENTARA. Cuma cukup buat menampilkan gambar + teksnya;
   penataan sungguhannya menyusul.
   -------------------------------------------------------------------------- */

.hero {
  position: relative;
  /* Minimal SETINGGI LAYAR. Tanpa ini .hero bisa lebih pendek dari
     100dvh, dan .hero__bg yang setinggi layar akan terpotong
     overflow:hidden di bawah — sudut bawahnya hilang lagi.
     72rem dipertahankan sebagai lantai buat layar yang sangat pendek. */
  min-height: 72rem;
  min-height: max(72rem, 100dvh);
  display: flex;
  align-items: center;
  padding: 12rem 0 6rem;    /* 12rem atas = ruang buat header fixed */
  overflow: hidden;
}

/* Latar hero = EMPAT gumpalan yang dipatok ke empat sudut.

   Dulu ini satu background-hero.png dengan background-size:cover. Itu
   rusak di HP: 85% isi PNG-nya cuma #FFFBF0 polos dan gumpalannya
   menempel di tepi, jadi 57,9% bagian TENGAHnya kosong. `cover` di layar
   sempit memperbesar gambarnya sampai menutupi tinggi lalu memotong dari
   tengah — dan yang tersisa persis pita kosong itu. Di HP 375px yang
   kelihatan cuma 25%-75% gambar: nol gumpalan.

   Sekarang tiap gumpalan jadi PNG ber-alpha sendiri yang dipatok ke
   sudutnya. Lebar berapa pun, sudut tetap sudut — tidak ada lagi yang
   bisa terpotong habis. Ukurannya clamp(): batas atasnya disamakan
   dengan ukuran tampil lama di desktop (skala cover 1,406x), jadi
   desktop tidak berubah penampakannya.

   Bonus yang ikut beres: PNG lama alpha-nya 255 di semua piksel, jadi
   dia mengecat beige ke SELURUH kotak hero. Di mode gelap --base jadi
   #161616 tapi hero tetap beige, dan judulnya (--t-bright #FFFFFF)
   jadi putih-di-atas-beige. Sekarang yang dicat cuma gumpalannya. */
/* Lapisan hiasan sudut — MENETAP DI LAYAR sepanjang halaman.

   position:fixed, jadi keempat gumpalan selalu di sudut layar, bukan
   sudut section. Ukurannya mengecil sekali waktu mulai discroll (lihat
   S7) lalu tinggal segitu terus — hiasan, bukan pengalih perhatian.

   z-index 1 itu SENGAJA di atas latar section, bukan di bawahnya.
   Alasannya: .svc--tint (:1272) dan .footer punya latar #F0EADA yang
   PEKAT. Kalau lapisan ini di bawah mereka, gumpalannya berkedip
   hilang-muncul tiap kali melewati section bertint. Teksnya dinaikkan
   ke z-index 2 lewat .container, jadi urutannya:
     latar section (0) -> gumpalan (1) -> semua teks (2) -> header (100)

   --skala dianimasikan GSAP; semua gumpalan mengalikan tingginya
   dengan angka ini. */
.hero__bg {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  --skala: 1;
}

/* Semua isi halaman duduk DI ATAS gumpalan. Tanpa ini teksnya
   ketiban hiasan. */
.container { position: relative; z-index: 2; }

.hero__blob {
  position: absolute;
  background: no-repeat center / 100% 100%;
  will-change: transform;
  /* top ikut beranimasi waktu header muncul/pergi — lihat blok
     "GUMPALAN ATAS MENGHINDAR DARI HEADER" di bawah. Durasinya
     disamakan dengan transisi header supaya bergerak berbarengan. */
  transition: top var(--speed-med) var(--bezier);
}

/* UKURANNYA DIANGGARKAN DARI TINGGI, DAN MEMAKAI vmin.

   Dulu semuanya `vw` (lebar saja). Itu buta terhadap layar yang PENDEK:
   HP dimiringkan jadi ~1356x610, lebarnya besar sehingga gumpalannya
   ikut besar, padahal tingginya cuma 610px. Yang kiri-atas paling parah
   — rasionya 170:256, jadi tingginya 1,5x lebarnya: di batas 23,9rem dia
   jadi ~360px, sekitar 59% tinggi layar, dan menabrak judul.

   --tinggi = jatah TINGGI tiap gumpalan; lebarnya dihitung mundur dari
   situ lewat rasionya sendiri. Yang dijaga tingginya, karena itu sisi
   yang langka di layar pendek.

   Anggarannya min(25vh, 30vw) — dua pagar sekaligus:
     25vh  menjaga janji "maksimum 25% layar", ini yang menyelamatkan
           lanskap (dulu yang kiri-atas sampai 59% tinggi layar)
     30vw  menjaga potret. Di sana tingginya longgar tapi lebarnya
           sempit; tanpa pagar ini gumpalannya melebar menutupi
           separuh layar HP.

   vmin sempat dicoba dan salah: di potret vmin = LEBAR, jadi jatahnya
   ikut mengecil sampai cuma 13% tinggi — jauh lebih kecil dari yang
   sudah ada. min(vh, vw) memisahkan kedua pagar itu dengan benar. */
.hero__blob {
  /* lebar = tinggi x (lebar asli / tinggi asli), keduanya ikut --skala */
  width: calc(var(--tinggi) * var(--rasio) * var(--skala));
  height: calc(var(--tinggi) * var(--skala));
}

.hero__blob--tl {
  top: 0; left: 0;
  /* 30% lebih kecil dari yang bawah: yang atas berbagi ruang dengan
     header dan judul, jadi porsinya memang harus lebih kecil.
     Angka aslinya 27vh/48vw/34rem, dikali 0,7. */
  --tinggi: clamp(9rem, min(19vh, 34vw), 24rem);
  --rasio: calc(170 / 256);
  background-image: url("blob-kiri-atas.png");
}

.hero__blob--tr {
  top: 0; right: 0;
  /* sama, x0,7 dari 27vh/48vw/32rem */
  --tinggi: clamp(9rem, min(19vh, 34vw), 22rem);
  --rasio: calc(212 / 213);
  background-image: url("blob-kanan-atas.png");
}

.hero__blob--bl {
  bottom: 0; left: 0;
  --tinggi: clamp(12rem, min(26vh, 46vw), 30rem);
  --rasio: calc(201 / 203);
  background-image: url("blob-kiri-bawah.png");
}

.hero__blob--br {
  bottom: 0; right: 0;
  --tinggi: clamp(14rem, min(28vh, 50vw), 34rem);
  --rasio: calc(227 / 244);
  background-image: url("blob-kanan-bawah.png");
}

/* Di HP gumpalannya masih kelewat berat — yang kanan-atas sempat menabrak
   baris pertama judul. Digeser KELUAR 4rem ke arah sudutnya masing-masing,
   bukan dikecilkan: yang mau dilihat memang cuma pojoknya yang mengintip,
   sama seperti kesan di desktop. .hero sudah overflow:hidden, jadi
   kelebihannya terpotong rapi.

   Digeser lewat top/left/right/bottom, BUKAN transform — properti
   transform dipakai GSAP untuk melemparnya keluar waktu discroll (S7),
   dan kalau dipakai berdua di sini yang satu akan menimpa yang lain. */
@media (max-width: 575.98px) {
  .hero__blob--tl { top: -4rem; left: -4rem; }
  .hero__blob--tr { top: -4rem; right: -4rem; }
  .hero__blob--bl { bottom: -4rem; left: -4rem; }
  .hero__blob--br { bottom: -4rem; right: -4rem; }
}

/* LAYAR BERBENTUK LANSKAP — HP dimiringkan, tablet tidur, jendela
   desktop yang dipendekkan.

   Pagarnya RASIO layar (4/3), bukan lebar dan bukan tinggi tebakan.
   Ini yang dulu salah dua kali: mula-mula max-width 575.98px, sehingga
   HP lanskap (lebar ~1356px) dianggap desktop dan lolos; lalu
   max-height 560px, yang benar tapi angkanya karangan.

   Rasio menjelaskan keadaan yang sebenarnya penting — layarnya LEBAR
   DAN PENDEK — dan berlaku sama di resolusi berapa pun. */
@media (min-aspect-ratio: 4/3) {
  .hero__blob--tl { --tinggi: 19vh; top: -3vh; left: -4vh; }
  .hero__blob--tr { --tinggi: 19vh; top: -3vh; right: -4vh; }
  .hero__blob--bl { --tinggi: 26vh; bottom: -3vh; left: -4vh; }
  .hero__blob--br { --tinggi: 28vh; bottom: -3vh; right: -4vh; }
}

/* ---------- GUMPALAN ATAS MENGHINDAR DARI HEADER ----------

   Header z-index 100, lapisan gumpalan z-index 1 — jadi begitu header
   muncul, dua gumpalan atas ketiban bilahnya, lengkap dengan latar
   82% dan blur 14px yang memucatkan sisanya.

   Header TIDAK dibikin transparan: latar + blur itu yang menjaga
   tautan menu (--t-muted #585858) tetap terbaca waktu kartu dan judul
   lewat di bawahnya. Menghapusnya memindahkan masalah, bukan
   menyelesaikannya.

   Jadi gumpalannya yang turun ke bawah bilah header. Tidak ada celah
   krem menganga di atasnya, karena ruang itu memang diisi headernya.

   Tingginya diukur, bukan ditebak:
     header longgar (belum 100px)  = 96px
     header padat  (.is-scrolled)  = 73px
   Keduanya dilayani supaya tidak pernah ada sela.

   Cuma gumpalan ATAS. Yang bawah dibiarkan di sudut aslinya — tidak
   ada apa pun yang dipatok di dasar layar untuk ditabrak.

   Spesifisitasnya (0-2-0) menang atas aturan di media query (0-1-0),
   jadi dia berlaku di semua ukuran layar tanpa perlu ditulis ulang. */
html:not(.intro) .hero__blob--tl,
html:not(.intro) .hero__blob--tr { top: 9.6rem; }

html.is-padat .hero__blob--tl,
html.is-padat .hero__blob--tr { top: 7.3rem; }

/* kontainernya sama dengan section lain — cuma perlu naik di atas .hero__bg */
.hero .container { position: relative; z-index: 1; }

/* Diagram layanan — hero.png cuma jadi CONTOH, tidak dipakai di markup.
   Dibangun ulang dari div biasa supaya teksnya bisa dipilih, responsif,
   dan ikut tema.

   Kotaknya PERSEGI (aspect-ratio 1), jadi diagramnya lingkaran betulan
   seperti versi pertama. Bentuknya wajib tetap: viewBox panahnya juga
   persegi (600x600) — kalau rasio kotaknya berubah, busurnya meleset. */
.orbit {
  position: relative;
  width: min(100%, 52rem);
  aspect-ratio: 1;
  margin-inline: auto;

  /* ---------------------------------------------------------------
     SATU KNOB KECEPATAN untuk seluruh diagram.

     Semua animasi orbit — pulsa keliling, titik yang meluncur ke
     pusat, denyut hub, dan pembesaran titik tujuan — memakai
     --orbit-cycle sebagai durasi dan --orbit-step sebagai jarak
     antar-titik. Persentase di dalam @keyframes-nya relatif, jadi
     mengubah SATU angka di bawah ini memperlambat/mempercepat
     semuanya sekaligus tanpa merusak sinkronisasinya.

     Dulu 18s (jeda 3s). Sekarang 36s = setengah kecepatan.
     Mau lebih pelan lagi? Besarkan --orbit-cycle saja.
     Jeda WAJIB = cycle / 6, karena titiknya ada enam.

     --orbit-inflow mengatur ROMBONGAN yang meluncur ke pusat: enam
     titik berangkat BARENGAN tiap 6 detik, bukan satu-satu. Karena
     6s = 1 x jeda, rombongannya jatuh tepat di TIAP langkah pulsa
     keliling — dua irama itu terkunci, tidak saling menggeser.

     AWAS kalau mengubah angka ini: persentase di orbitInward0-5
     itu relatif terhadap --orbit-inflow, jadi memendekkan periodenya
     juga MEMPERCEPAT titiknya. Mau ubah periode tanpa mengubah
     kecepatan? Skalakan persentasenya dengan faktor yang berlawanan
     (periode dibagi 2 -> persentase dikali 2). Dulu 12s, sekarang 6s,
     dan persentasenya sudah dikali 2 supaya waktu tempuhnya tetap. */
  --orbit-cycle: 36s;
  --orbit-step: calc(var(--orbit-cycle) / 6);
  --orbit-inflow: calc(var(--orbit-cycle) / 6);
}

/* Penempatan pakai koordinat: tiap item menyimpan --x (cos) dan --y (sin)
   sudutnya, dikali 36% — persen, jadi ikut ukuran kotaknya. Karena
   kotaknya persegi, 36% mendatar = 36% tegak = lingkaran. */
.orbit__item {
  position: absolute;
  left: calc(50% + (var(--x) * 36%));
  top: calc(50% + (var(--y) * 36%));
  transform: translate(-50%, -50%);
  width: 12rem;
  display: grid;
  justify-items: center;
  gap: .8rem;
  text-align: center;
}

/* Panah — satu busur per celah, digambar di viewBox 600x600 (persegi,
   sama seperti .orbit). Ditaruh paling belakang, tidak bisa diklik. */
.orbit__arrows {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* kotak ikon — masih placeholder, nanti diisi ikon sungguhan.
   --c datang dari .orbit__item: tiap kanal punya warnanya sendiri,
   sama dengan warna panah yang berangkat dari titik itu. */
.orbit__mark {
  width: 5.6rem;
  height: 5.6rem;
  display: grid;
  place-items: center;
  /* Garis penuh. Yang putus-putus sekarang tinggal hub-nya sendiri,
     jadi pusatnya yang menonjol, bukan lingkaran-lingkaran di tepi. */
  border: 2px solid var(--c, var(--stroke));
  border-radius: 50%;
  background: var(--base-tint);
  /* Ikonnya pakai currentColor, jadi warnanya ikut --c milik node —
     tiap kanal punya warnanya sendiri, sama dengan busur & titiknya. */
  color: var(--c, var(--t-faint));
}

/* Ukuran ikon dipatok PERSEN, bukan rem, supaya ikut mengecil sendiri
   waktu .orbit__mark dikecilkan di HP (5,6rem -> 4,8rem). */
.orbit__mark svg { width: 46%; height: 46%; display: block; }


/* --- Pulsa berjalan mengelilingi diagram ------------------------------
   Enam busur, tiap busur kebagian 1/6 dari putaran (--orbit-cycle). Yang
   bergerak sebenarnya cuma pola garis putus-putus (stroke-dashoffset),
   jadi tidak ada elemen yang di-reflow — murni kerjaan compositor.
   Panjang busurnya 106 unit (jari-jari 138 x sudut 44 derajat), makanya
   dash-nya 26 dan geserannya dari 106 ke -26. */

.orbit__pulse path {
  stroke-width: 19;          /* titik gemuk, bukan garis */
  stroke-linecap: round;
  stroke-dasharray: .1 400;  /* panjang nyaris nol + tutup bulat = BULATAN */
  opacity: 0;
  animation: orbitWalk var(--orbit-cycle) linear infinite;
  animation-delay: calc(var(--i) * var(--orbit-step));
}

/* Lapisan bawah: bulatan PUTIH yang lebih besar. Fungsinya memotong
   busur di bawahnya, jadi titiknya duduk di atas bidang bersih dan tidak
   menyatu dengan garis sewarna. Ini yang bikin dia langsung kelihatan. */
.orbit__pulse--ring path {
  stroke-width: 30;
  stroke: #FFFFFF !important;
  animation-name: orbitWalkRing;
}

/* Sama persis dengan orbitWalk, tanpa ganti-ganti warna. */
@keyframes orbitWalkRing {
  0%     { stroke-dashoffset: 0; opacity: 0; }
  0.6%   { opacity: 1; }
  16.10% { opacity: 1; }
  16.66% { stroke-dashoffset: -106; opacity: 0; }
  100%   { stroke-dashoffset: -106; opacity: 0; }
}

/* Busurnya sepanjang 106 unit. Titik muncul di posisi -dashoffset, jadi
   untuk jalan dari pangkal ke ujung, offset harus 0 -> -106. (Versi
   sebelumnya 106 -> -26, makanya titiknya berhenti di seperempat jalan.)
   Warnanya lompat-lompat lewat seluruh palet selama perjalanan. */
/* Tiap titik punya deret warnanya sendiri, dan deret itu SENGAJA tidak
   memuat warna busur yang sedang dilewatinya. Jadi titiknya tidak pernah
   menyatu dengan garisnya — selalu warna yang bertabrakan. */
.orbit__pulse--dot path:nth-child(1) { animation-name: orbitWalk0; }
.orbit__pulse--dot path:nth-child(2) { animation-name: orbitWalk1; }
.orbit__pulse--dot path:nth-child(3) { animation-name: orbitWalk2; }
.orbit__pulse--dot path:nth-child(4) { animation-name: orbitWalk3; }
.orbit__pulse--dot path:nth-child(5) { animation-name: orbitWalk4; }
.orbit__pulse--dot path:nth-child(6) { animation-name: orbitWalk5; }

@keyframes orbitWalk0 {
  0%     { stroke-dashoffset: 0; opacity: 0; stroke: #4C7DE0; }
  0.6%   { opacity: 1; }
  3.32%  { stroke: #4C7DE0; }
  3.33%  { stroke: #9F8BE7; }
  6.66%  { stroke: #9F8BE7; }
  6.67%  { stroke: #D98324; }
  9.99%  { stroke: #D98324; }
  10.00%  { stroke: #F0B429; }
  13.32%  { stroke: #F0B429; }
  13.33%  { stroke: #3E9B6B; }
  16.10% { opacity: 1; }
  16.66% { stroke-dashoffset: -106; opacity: 0; stroke: #3E9B6B; }
  100%   { stroke-dashoffset: -106; opacity: 0; }
}

@keyframes orbitWalk1 {
  0%     { stroke-dashoffset: 0; opacity: 0; stroke: #4C7DE0; }
  0.6%   { opacity: 1; }
  3.32%  { stroke: #4C7DE0; }
  3.33%  { stroke: #9F8BE7; }
  6.66%  { stroke: #9F8BE7; }
  6.67%  { stroke: #D98324; }
  9.99%  { stroke: #D98324; }
  10.00%  { stroke: #DE4B3C; }
  13.32%  { stroke: #DE4B3C; }
  13.33%  { stroke: #3E9B6B; }
  16.10% { opacity: 1; }
  16.66% { stroke-dashoffset: -106; opacity: 0; stroke: #3E9B6B; }
  100%   { stroke-dashoffset: -106; opacity: 0; }
}

@keyframes orbitWalk2 {
  0%     { stroke-dashoffset: 0; opacity: 0; stroke: #4C7DE0; }
  0.6%   { opacity: 1; }
  3.32%  { stroke: #4C7DE0; }
  3.33%  { stroke: #9F8BE7; }
  6.66%  { stroke: #9F8BE7; }
  6.67%  { stroke: #D98324; }
  9.99%  { stroke: #D98324; }
  10.00%  { stroke: #DE4B3C; }
  13.32%  { stroke: #DE4B3C; }
  13.33%  { stroke: #F0B429; }
  16.10% { opacity: 1; }
  16.66% { stroke-dashoffset: -106; opacity: 0; stroke: #F0B429; }
  100%   { stroke-dashoffset: -106; opacity: 0; }
}

@keyframes orbitWalk3 {
  0%     { stroke-dashoffset: 0; opacity: 0; stroke: #3E9B6B; }
  0.6%   { opacity: 1; }
  3.32%  { stroke: #3E9B6B; }
  3.33%  { stroke: #9F8BE7; }
  6.66%  { stroke: #9F8BE7; }
  6.67%  { stroke: #D98324; }
  9.99%  { stroke: #D98324; }
  10.00%  { stroke: #DE4B3C; }
  13.32%  { stroke: #DE4B3C; }
  13.33%  { stroke: #F0B429; }
  16.10% { opacity: 1; }
  16.66% { stroke-dashoffset: -106; opacity: 0; stroke: #F0B429; }
  100%   { stroke-dashoffset: -106; opacity: 0; }
}

@keyframes orbitWalk4 {
  0%     { stroke-dashoffset: 0; opacity: 0; stroke: #3E9B6B; }
  0.6%   { opacity: 1; }
  3.32%  { stroke: #3E9B6B; }
  3.33%  { stroke: #4C7DE0; }
  6.66%  { stroke: #4C7DE0; }
  6.67%  { stroke: #D98324; }
  9.99%  { stroke: #D98324; }
  10.00%  { stroke: #DE4B3C; }
  13.32%  { stroke: #DE4B3C; }
  13.33%  { stroke: #F0B429; }
  16.10% { opacity: 1; }
  16.66% { stroke-dashoffset: -106; opacity: 0; stroke: #F0B429; }
  100%   { stroke-dashoffset: -106; opacity: 0; }
}

@keyframes orbitWalk5 {
  0%     { stroke-dashoffset: 0; opacity: 0; stroke: #3E9B6B; }
  0.6%   { opacity: 1; }
  3.32%  { stroke: #3E9B6B; }
  3.33%  { stroke: #4C7DE0; }
  6.66%  { stroke: #4C7DE0; }
  6.67%  { stroke: #9F8BE7; }
  9.99%  { stroke: #9F8BE7; }
  10.00%  { stroke: #DE4B3C; }
  13.32%  { stroke: #DE4B3C; }
  13.33%  { stroke: #F0B429; }
  16.10% { opacity: 1; }
  16.66% { stroke-dashoffset: -106; opacity: 0; stroke: #F0B429; }
  100%   { stroke-dashoffset: -106; opacity: 0; }
}

/* --- Aliran masuk ke pusat --------------------------------------------
   Setelah pulsa keliling menyentuh sebuah titik, satu titik meluncur dari
   titik itu ke hub "Website". Ini bikin argumen halaman kelihatan:
   semua kanal bermuara ke satu tempat. Jari-jarinya 74 unit. */

/* Tiap jari-jari memakai warna titik pangkalnya, dipudarkan supaya tidak
   ikut ramai. Kepala panahnya diwarnai lewat <marker> masing-masing. */
.orbit__spokes path {
  stroke-width: 2;
  stroke-dasharray: 4 5;
  opacity: .45;
}

.orbit__in path {
  stroke-linecap: round;
  stroke-dasharray: .1 200;
  opacity: 0;
  /* Tanpa animation-delay: keenamnya BERANGKAT BARENGAN. Yang bikin
     mereka sampai di waktu berbeda adalah persentase di keyframes-nya,
     bukan durasinya — lihat catatan panjang di bawah. */
  animation: orbitInward var(--orbit-inflow) linear infinite;
}

.orbit__in--dot path { stroke-width: 15; }
.orbit__in--ring path { stroke-width: 24; stroke: #FFFFFF !important; }

/* --- Enam kecepatan ---------------------------------------------------
   KENAPA kecepatannya ditaruh di keyframes, bukan di animation-duration:
   durasi yang beda-beda juga mengubah PERIODE ULANGnya, jadi keenamnya
   cuma barengan sekali di awal lalu makin lama makin berpencar. Dengan
   durasi yang SAMA (--orbit-inflow) dan persentase yang beda, mereka
   kembali sejajar di tiap ulangan, selamanya.

   Urutan cepat-lambatnya sengaja mengikuti sifat kanalnya: Paid Media
   paling cepat, SEO paling lambat. Kebetulan itu juga bikin cepat dan
   lambat berselang-seling di lingkaran, jadi tidak terbaca seperti
   kipas yang menyapu.

   nth-child memilih dot DAN alas putihnya sekaligus, karena kedua
   grup .orbit__in punya urutan path yang sama. */
.orbit__in path:nth-child(1) { animation-name: orbitInward0; }   /* Paid Media  — 1,6s */
.orbit__in path:nth-child(2) { animation-name: orbitInward1; }   /* Social      — 2,0s */
.orbit__in path:nth-child(3) { animation-name: orbitInward2; }   /* Content     — 2,9s */
.orbit__in path:nth-child(4) { animation-name: orbitInward3; }   /* Analytics   — 2,3s */
.orbit__in path:nth-child(5) { animation-name: orbitInward4; }   /* SEO         — 3,4s */
.orbit__in path:nth-child(6) { animation-name: orbitInward5; }   /* Email & CRM — 2,6s */

/* --- Berangkat BERGILIRAN, bukan berombongan -------------------------
   Dulu keenamnya berangkat barengan tiap 6 detik. Yang paling lambat
   (SEO) mendarat di 56,6% = 3,4 detik, lalu TIDAK ADA APA-APA sampai
   rombongan berikutnya — 2,6 detik kosong, 43% dari waktunya. Diagram
   yang seharusnya bercerita "semuanya bermuara ke websitemu" jadi diam
   hampir separuh waktu.

   Sekarang start-nya digeser merata, satu tiap 1/6 periode. Karena
   tiap perjalanan makan 1,6-3,4 detik sementara jaraknya cuma 1 detik,
   selalu ada 2-3 titik yang sedang meluncur ke pusat kapan pun dilihat.

   Delay NEGATIF: animasinya dianggap sudah berjalan sejak sebelum
   halaman dibuka, jadi tidak ada jeda kosong di awal — begitu dimuat,
   titiknya sudah tersebar di tengah perjalanan.

   Dinyatakan sebagai pecahan dari --orbit-inflow, bukan detik mati,
   supaya tetap benar kalau --orbit-cycle diubah. */
.orbit__in path:nth-child(1) { animation-delay: calc(var(--orbit-inflow) * -0); }
.orbit__in path:nth-child(2) { animation-delay: calc(var(--orbit-inflow) * -0.1667); }
.orbit__in path:nth-child(3) { animation-delay: calc(var(--orbit-inflow) * -0.3333); }
.orbit__in path:nth-child(4) { animation-delay: calc(var(--orbit-inflow) * -0.5); }
.orbit__in path:nth-child(5) { animation-delay: calc(var(--orbit-inflow) * -0.6667); }
.orbit__in path:nth-child(6) { animation-delay: calc(var(--orbit-inflow) * -0.8333); }

/* Keenamnya sebangun: muncul di ~12% perjalanan, mulai pudar di ~85%,
   lalu diam sampai 100%. Cuma angka ujungnya yang beda. */
@keyframes orbitInward0 {
  0%     { stroke-dashoffset: 0; opacity: 0; }
  3.2%   { opacity: 1; }
  22.8%  { opacity: 1; }
  26.6%  { stroke-dashoffset: -74; opacity: 0; }
  100%   { stroke-dashoffset: -74; opacity: 0; }
}

@keyframes orbitInward1 {
  0%     { stroke-dashoffset: 0; opacity: 0; }
  4.0%   { opacity: 1; }
  27.8%  { opacity: 1; }
  32.6%  { stroke-dashoffset: -74; opacity: 0; }
  100%   { stroke-dashoffset: -74; opacity: 0; }
}

@keyframes orbitInward2 {
  0%     { stroke-dashoffset: 0; opacity: 0; }
  5.8%   { opacity: 1; }
  41.4%  { opacity: 1; }
  48.4%  { stroke-dashoffset: -74; opacity: 0; }
  100%   { stroke-dashoffset: -74; opacity: 0; }
}

@keyframes orbitInward3 {
  0%     { stroke-dashoffset: 0; opacity: 0; }
  4.6%   { opacity: 1; }
  32.8%  { opacity: 1; }
  38.4%  { stroke-dashoffset: -74; opacity: 0; }
  100%   { stroke-dashoffset: -74; opacity: 0; }
}

@keyframes orbitInward4 {
  0%     { stroke-dashoffset: 0; opacity: 0; }
  6.8%   { opacity: 1; }
  48.4%  { opacity: 1; }
  56.6%  { stroke-dashoffset: -74; opacity: 0; }
  100%   { stroke-dashoffset: -74; opacity: 0; }
}

@keyframes orbitInward5 {
  0%     { stroke-dashoffset: 0; opacity: 0; }
  5.2%   { opacity: 1; }
  37.2%  { opacity: 1; }
  43.4%  { stroke-dashoffset: -74; opacity: 0; }
  100%   { stroke-dashoffset: -74; opacity: 0; }
}

/* Hub berdenyut sekali tiap ROMBONGAN, memuncak ~2,4 detik setelah
   rombongannya berangkat — pas titik-titik tengah mendarat. */
.orbit__hub {
  animation: hubPulse var(--orbit-inflow) ease-out infinite;
  animation-delay: calc(var(--orbit-inflow) * 0.32);
}

/* Ikut aturan yang sama dengan orbitInward: periode dibagi 2, jadi
   persentasenya dikali 2 (4%/20% -> 8%/40%) supaya panjang denyutnya
   tetap ~0,5s naik dan ~2,4s reda. */
@keyframes hubPulse {
  0%   { transform: translate(-50%, -50%) scale(1);    background: var(--base-tint); }
  8%   { transform: translate(-50%, -50%) scale(1.05); background: color-mix(in srgb, var(--t-bright) 10%, var(--base-tint)); }
  40%  { transform: translate(-50%, -50%) scale(1);    background: var(--base-tint); }
  100% { transform: translate(-50%, -50%) scale(1);    background: var(--base-tint); }
}

/* Titik tujuan membesar sebentar tepat waktu pulsanya sampai.
   Node ke-j disentuh di jeda ke-j — sama dengan jadwal busurnya. */
.orbit__mark {
  animation: orbitPop var(--orbit-cycle) ease-in-out infinite;
  animation-delay: calc(var(--i) * var(--orbit-step));
}

/* Waktu disentuh, lingkarannya membesar DAN terisi warnanya sendiri. */
@keyframes orbitPop {
  0%   { transform: scale(1);    background: var(--base-tint); }
  1.3% { transform: scale(1.22); background: color-mix(in srgb, var(--c) 22%, var(--base-tint)); }
  5.3% { transform: scale(1);    background: var(--base-tint); }
  100% { transform: scale(1);    background: var(--base-tint); }
}

/* Diagram hero JALAN secara bawaan, sama seperti effect/index.html.
   Sebelumnya animasinya mati otomatis begitu OS minta reduced motion,
   jadi di mesin yang setelan "kurangi gerak"-nya menyala diagramnya
   diam total dan harus dibuka pakai ?motion=on dulu.

   Sekarang kebalikannya: yang mau jalur tenang buka ?motion=off.

   KONSEKUENSI YANG DISENGAJA: halaman ini tidak lagi menuruti
   prefers-reduced-motion secara otomatis. Kalau mau dikembalikan,
   ganti `[data-motion="off"]` di bawah jadi `:not([data-motion="on"])`
   dan balikkan juga cek `motion` di JS bagian bawah berkas. */
@media (prefers-reduced-motion: reduce) {
  :root[data-motion="off"] .orbit__pulse { display: none; }
  :root[data-motion="off"] .orbit__mark { animation: none; }
  :root[data-motion="off"] .orbit__hub { animation: none; }
  :root[data-motion="off"] .orbit__in { display: none; }
}

/* Jalur tenang juga harus berlaku waktu OS TIDAK minta reduced motion —
   kalau tidak, ?motion=off cuma bekerja di mesin yang setelannya nyala. */
:root[data-motion="off"] .orbit__pulse { display: none; }
:root[data-motion="off"] .orbit__mark { animation: none; }
:root[data-motion="off"] .orbit__hub { animation: none; }
:root[data-motion="off"] .orbit__in { display: none; }

/* pusat lingkaran — dipatok di 50%/50%, tidak ikut --x/--y */
.orbit__hub {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 30%;          /* ikut mengecil bareng diagramnya */
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  /* Ikon ditumpuk di ATAS label, bukan sebaris — makanya
     align-content dipakai, bukan sekadar place-items. */
  align-content: center;
  gap: .7rem;
  border: 2px dashed var(--t-bright);
  border-radius: 50%;
  background: var(--base-tint);
  /* Dulu --t-muted (#585858) — teks paling pucat di seluruh diagram,
     padahal ini justru tujuan semua panahnya. Dipekatkan ke --t-bright
     supaya jadi jangkar, bukan yang paling samar.

     SENGAJA bukan --accent: nilainya #9F8BE7, persis warna node SEO —
     hub-nya jadi kelihatan milik kanal SEO, bukan tujuan semuanya. */
  color: var(--t-bright);
  font-size: 1.6rem;
  font-weight: 700;
  text-align: center;
  /* Kapital lewat CSS, bukan diketik kapital di markup — biar teks
     aslinya tetap "Website" buat pembaca layar dan salin-tempel.
     Kapital butuh jarak huruf ekstra supaya tidak rapat. */
  text-transform: uppercase;
  letter-spacing: .08em;
}

/* Ikon ikut mengecil bareng hub-nya (hub = 30% lebar diagram). */
.orbit__hub svg { width: 3rem; height: 3rem; display: block; }

.orbit__label {
  color: var(--t-medium);
  font-size: 1.4rem;
  font-weight: 600;
  line-height: 1.3;
}

@media (max-width: 575.98px) {
  /* max(...) WAJIB ikut di sini. Aturan ini menang atas
     min-height: max(72rem, 100dvh) di :565 karena datang belakangan,
     jadi kalau ditulis 56rem polos, penjaganya hilang di HP.

     Akibatnya persis kelihatan di HP layar TINGGI (iPhone 14 Pro Max
     430x932, Pixel 412x915): tinggi hero ditentukan isinya (~800px),
     sementara .hero__bg setinggi 100dvh (~930px) — selisihnya dipotong
     overflow:hidden milik .hero, dan DUA GUMPALAN BAWAH hilang total.
     Di HP pendek (390x660) tidak kelihatan karena heronya kebetulan
     lebih tinggi dari layar. */
  .hero { min-height: max(56rem, 100dvh); }

  /* Di HP semua lapisan tetap hidup — busur, panah, pulsa keliling,
     dan aliran ke pusat. Yang dulu disembunyikan .orbit__arrows,
     padahal itu SELURUH <svg>, jadi diagramnya diam total.

     Busurnya ditipiskan 3 -> 1.5 supaya lengkungnya tidak jadi coretan
     di layar sempit. Kepala panahnya ikut mengecil sendiri, karena
     markerUnits bawaannya strokeWidth.

     SEMUA bulatan disamakan ukurannya di HP: pulsa keliling 30/19 dan
     aliran ke pusat 24/15 sama-sama jadi 19,5/12,5. Alasannya dua:
     (1) ukuran aslinya bikin bulatan jadi 20x lebar busurnya — dua kali
     ketimpangan desktop yang cuma 10x — jadi busurnya ketutup waktu
     dilewati; (2) di desktop pulsa itu bulatan yang LEBIH BESAR, di HP
     malah jadi yang lebih kecil, bobotnya kebalik.

     Angkanya 1,3x dari 15/9,5 yang sempat dipakai — masih di bawah
     ukuran asli, tapi tidak lagi terlalu kecil di layar HP.

     Ukuran desktop SENGAJA tidak disentuh — angka unit yang sama
     tampil lebih besar di sana (orbitnya 520px lawan 327px di HP). */
  .orbit__ring { stroke-width: 1.5; }
  .orbit__pulse--ring path,
  .orbit__in--ring path { stroke-width: 19.5; }
  .orbit__pulse--dot path,
  .orbit__in--dot path { stroke-width: 12.5; }
  .orbit__item { width: 9rem; }
  .orbit__mark { width: 4.8rem; height: 4.8rem; }
  .orbit__label { font-size: 1.2rem; }
  .orbit__hub { width: 32%; font-size: 1.2rem; }

  /* CTA header di HP: label pendek, padding rapat, panah dilepas.
     Di 360px ruang isinya cuma ~312px dan logo sudah makan ~160px. */
  .header__cta-long { display: none; }
  .header__cta-short { display: inline; }
  .header__cta { padding: 1.1rem 1.8rem; font-size: 1.4rem; }
  .header__cta .ph-bold { display: none; }
}


.hero__title {
  font-size: clamp(3.6rem, 4.2vw, 5.6rem);
  margin-bottom: 2.4rem;
}

.hero__lead {
  font-size: 1.8rem;
  color: var(--t-muted);
  max-width: 46ch;
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: .8rem;
  margin-bottom: 2.4rem;
  color: var(--t-muted);
  font-size: 1.3rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
}

.eyebrow::before {
  content: "";
  width: .8rem;
  height: .8rem;
  border-radius: 50%;
  background: var(--accent);
}

/* --------------------------------------------------------------------------
   6. CTA hero
   -------------------------------------------------------------------------- */

/* Dulu ini .contact, satu section sendiri di bawah hero. Sekarang
   tombolnya hidup DI DALAM hero, jadi yang tersisa cuma jaraknya. */
.hero__aksi {
  display: flex;
  justify-content: center;
  margin-top: 4.8rem;
}

.hero__cta { margin-top: 0; }

/* Selebar kolom di HP supaya jadi sasaran jempol yang besar,
   seukuran isinya mulai 576px. */
.hero__cta { width: 100%; justify-content: center; }

@media (min-width: 576px) {
  .hero__cta { width: auto; }
}


/* --------------------------------------------------------------------------
   7. Layanan — 7 section, satu per titik di diagram
   -------------------------------------------------------------------------- */

.svc { padding: 8rem 0; }
.svc--tint { background: var(--base-shade); }

.svc__head { max-width: 60ch; margin-bottom: 4rem; }

.svc__title {
  font-size: clamp(2.6rem, 3.4vw, 4rem);
  margin-bottom: 1.6rem;
}

.svc__lead { color: var(--t-muted); }

/* auto-fit: 1 kolom di HP, 2-3 kolom kalau lebarnya cukup — tanpa
   breakpoint tebak-tebakan. */
.svc__cards {
  display: grid;
  gap: 2.4rem;
  grid-template-columns: repeat(auto-fit, minmax(26rem, 1fr));
  margin-bottom: 4rem;
}

.card {
  padding: 3.2rem;
  border: 1px solid var(--stroke);
  border-radius: 2rem;
  background: var(--base-tint);
}

.svc--tint .card { background: var(--base); }

.card__title {
  font-size: 2rem;
  margin-bottom: 1.2rem;
}

.card__text {
  color: var(--t-muted);
  font-size: 1.6rem;
  margin: 0;
}

/* tombol selebar layar di HP, seukuran isinya mulai 576px */
.svc__cta { width: 100%; justify-content: center; }

@media (min-width: 576px) {
  .svc__cta { width: auto; }
}


/* --------------------------------------------------------------------------
   8. Penutup — buat yang belum yakin butuh yang mana
   -------------------------------------------------------------------------- */

.closing {
  padding: 10rem 0 12rem;
  text-align: center;
}

.closing__title {
  font-size: clamp(2.8rem, 3.6vw, 4.4rem);
  margin-bottom: 1.6rem;
}

.closing__lead {
  color: var(--t-muted);
  max-width: 52ch;
  margin: 0 auto 3.2rem;
}

.closing__btn { width: 100%; justify-content: center; }

@media (min-width: 576px) {
  .closing__btn { width: auto; }
}


/* --------------------------------------------------------------------------
   9. Proses — "nanti dighosting nggak?"
   Termasuk mini-timeline: representasi dasbor proyek yang dipegang klien.
   Murni HTML/CSS, bukan tangkapan layar, biar ikut tema dan bisa dibaca
   screen reader.
   -------------------------------------------------------------------------- */

.proses { padding: 8rem 0; }

.steps {
  display: grid;
  gap: 2.4rem;
  grid-template-columns: repeat(auto-fit, minmax(24rem, 1fr));
  margin-bottom: 4.8rem;
}

.step__num {
  width: 4.4rem;
  height: 4.4rem;
  display: grid;
  place-items: center;
  margin-bottom: 1.6rem;
  border-radius: 50%;
  background: var(--accent);
  color: var(--t-on-accent);
  font-family: var(--font-accent);
  font-weight: 700;
}

/* --- mini-timeline --- */

.timeline {
  padding: 2.4rem;
  border: 1px solid var(--stroke);
  border-radius: 2rem;
  background: var(--base-tint);
}

.timeline__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 1.2rem;
  margin-bottom: 1.6rem;
}

.timeline__name {
  font-family: var(--font-accent);
  font-size: 2rem;
  font-weight: 700;
  color: var(--t-bright);
}

.timeline__meta { color: var(--t-muted); font-size: 1.3rem; }

.timeline__bar-outer {
  height: .8rem;
  border-radius: 99px;
  background: var(--base-shade);
  overflow: hidden;
}

.timeline__bar-inner {
  display: block;
  height: 100%;
  border-radius: 99px;
  background: var(--accent);
}

.timeline__pct {
  margin: .8rem 0 2.4rem;
  color: var(--t-muted);
  font-size: 1.3rem;
}

/* garis "hari ini" — satu untuk semua baris */
.timeline__grid { position: relative; }

.timeline__today {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 38%;
  width: 0;
  border-left: 1px dashed var(--t-faint);
  pointer-events: none;
}

.timeline__row {
  display: grid;
  gap: .6rem;
  padding: 1.1rem 0;
  border-top: 1px solid var(--stroke);
}

.timeline__label { font-size: 1.4rem; font-weight: 600; color: var(--t-medium); }

.timeline__track {
  position: relative;
  height: 1.4rem;
  border-radius: 99px;
  background: var(--base-shade);
}

/* --s = mulai, --w = panjang, keduanya persen dari lebar track */
.timeline__span {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--s);
  width: var(--w);
  border-radius: 99px;
  background: var(--c, var(--accent));
}

/* di layar sempit label naik ke atas barnya; mulai 768px jadi dua kolom */
@media (min-width: 768px) {
  .timeline__row {
    grid-template-columns: 18rem 1fr;
    align-items: center;
    gap: 2rem;
  }
  .timeline__today { left: calc(18rem + 2rem + ((100% - 18rem - 2rem) * .38)); }
}

/* --------------------------------------------------------------------------
   10. Harga indikatif — "kira-kira berapa?"
   -------------------------------------------------------------------------- */

.harga { padding: 8rem 0; background: var(--base-shade); }

.prices {
  display: grid;
  gap: 2.4rem;
  grid-template-columns: repeat(auto-fit, minmax(26rem, 1fr));
}

.price {
  display: grid;
  align-content: start;
  gap: 1.6rem;
  padding: 3.2rem;
  border: 1px solid var(--stroke);
  border-radius: 2rem;
  background: var(--base);
}

.price--pick { border-color: var(--accent); border-width: 2px; }

.price__name {
  color: var(--t-muted);
  font-size: 1.3rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
}

.price__range {
  font-family: var(--font-accent);
  font-size: 2.8rem;
  font-weight: 700;
  line-height: 1.1;
  color: var(--t-bright);
}

.price__range small {
  display: block;
  margin-top: .4rem;
  color: var(--t-muted);
  font-family: var(--font-default);
  font-size: 1.3rem;
  font-weight: 400;
}

.price__list { display: grid; gap: 1rem; }

.price__list li {
  display: flex;
  gap: 1rem;
  color: var(--t-muted);
  font-size: 1.5rem;
}

.price__list i { color: var(--accent); font-size: 1.8rem; flex: none; }

.price .btn-pill { justify-content: center; margin-top: .8rem; }

.harga__note {
  margin-top: 3.2rem;
  color: var(--t-muted);
  font-size: 1.4rem;
  text-align: center;
}

/* --------------------------------------------------------------------------
   11. FAQ — sisa keberatan. Pakai <details>, jadi tanpa JS sama sekali.
   -------------------------------------------------------------------------- */

.faq { padding: 8rem 0; }

.faq__list { max-width: 82rem; }

.faq__item { border-bottom: 1px solid var(--stroke); }

.faq__item summary {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 2rem;
  padding: 2.4rem 0;
  cursor: pointer;
  color: var(--t-bright);
  font-family: var(--font-accent);
  font-size: 1.8rem;
  font-weight: 600;
  list-style: none;
}

.faq__item summary::-webkit-details-marker { display: none; }

.faq__item summary::after {
  content: "+";
  flex: none;
  color: var(--accent);
  font-size: 2.4rem;
  line-height: 1;
}

.faq__item[open] summary::after { content: "\2013"; }

.faq__answer {
  max-width: 68ch;
  margin: 0;
  padding-bottom: 2.4rem;
  color: var(--t-muted);
}

/* --------------------------------------------------------------------------
   12. Testimoni — "orang lain puas?"
   -------------------------------------------------------------------------- */

.testi { padding: 8rem 0; background: var(--base-shade); }

.testi__grid {
  display: grid;
  gap: 2.4rem;
  grid-template-columns: repeat(auto-fit, minmax(28rem, 1fr));
}

.testi .card { background: var(--base); }

.quote__text {
  font-size: 1.7rem;
  color: var(--t-medium);
  margin-bottom: 2.4rem;
}

.quote__who { display: flex; align-items: center; gap: 1.2rem; }

.quote__avatar {
  flex: none;
  width: 4.8rem;
  height: 4.8rem;
  display: grid;
  place-items: center;
  border: 2px dashed var(--stroke);
  border-radius: 50%;
  color: var(--t-faint);
  font-size: 1.2rem;
  font-weight: 600;
}

.quote__name { color: var(--t-bright); font-size: 1.5rem; font-weight: 600; }
.quote__role { color: var(--t-muted); font-size: 1.3rem; }

/* --------------------------------------------------------------------------
   13. Footer — nav, NAP, sosial
   -------------------------------------------------------------------------- */

.footer {
  padding: 8rem 0 3.2rem;
  background: var(--base-shade);
  border-top: 1px solid var(--stroke);
}

.footer__grid {
  display: grid;
  gap: 4rem;
  grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
  margin-bottom: 5.6rem;
}

.footer__about {
  color: var(--t-muted);
  font-size: 1.5rem;
  max-width: 34ch;
  margin: 1.6rem 0 0;
}

.footer__col h3 {
  margin-bottom: 1.6rem;
  color: var(--t-muted);
  font-size: 1.3rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
}

.footer__col ul { display: grid; gap: 1.2rem; }

.footer__col a,
.footer__col address {
  color: var(--t-medium);
  font-size: 1.5rem;
  font-style: normal;
  line-height: 1.6;
}

.footer__col a:hover { color: var(--t-bright); }

.footer__social { display: flex; flex-wrap: wrap; gap: 1.2rem; margin-top: 2.4rem; }

.footer__social a {
  width: 4.4rem;
  height: 4.4rem;
  display: grid;
  place-items: center;
  border: 1px solid var(--stroke);
  border-radius: 50%;
  color: var(--t-bright);
  font-size: 2rem;
  transition: all var(--speed-med) var(--bezier);
}

.footer__social a:hover { background: var(--t-bright); color: var(--base); }

.footer__bottom {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 1.6rem;
  padding-top: 3.2rem;
  border-top: 1px solid var(--stroke);
  color: var(--t-muted);
  font-size: 1.4rem;
}

/* --------------------------------------------------------------------------
   14. Efek scroll — dipasang dari lab ../inspiration/effect/

   Yang di bawah ini cuma menyiapkan TEMPAT. Yang menganimasikan ada di
   blok <script> paling bawah berkas; daftar efek mana menempel di mana
   ada di EFEK.txt.

   Aturan yang dipegang seluruh blok ini: keadaan AWAL tiap efek dibikin
   sama dengan keadaan normal halaman. Jadi kalau JS-nya tidak jalan —
   CDN diblokir, ?motion=off, koneksi putus — tidak ada satu elemen pun
   yang nyangkut tersembunyi. Dua overlay di bawah pengecualiannya, dan
   keduanya memang mulai dari nol (tak terlihat), bukan dari isi halaman.
   -------------------------------------------------------------------------- */

/* P1 — Lenis. Kelas .lenis dipasang sendiri oleh library-nya waktu dia
   hidup, jadi kalau JS-nya tidak jalan aturan di bawah tidak pernah kena
   dan halaman ini kembali ke scroll bawaan apa adanya.

   scroll-behavior:smooth (:312) TIDAK perlu dimatikan di sini — Lenis
   1.1.20 menulisnya sendiri jadi `auto` langsung di atribut style <html>,
   dan inline selalu menang. Sudah dicek di halaman ini. Yang tidak dia
   urus cuma tinggi dokumen. */
html.lenis,
html.lenis body { height: auto; }

/* buat elemen yang harus discroll sendiri, kalau nanti ada */
html.lenis [data-lenis-prevent] { overscroll-behavior: contain; }

/* G2 — bar progres scroll. z-index 120 = di atas .header (100), supaya
   tetap kelihatan waktu header memadat. */
.progress {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 120;
  width: 100%;
  height: 3px;
  /* titik tumpu di kiri — tanpa ini scaleX memuai dari tengah dan
     barnya tumbuh ke dua arah sekaligus */
  transform-origin: 0 50%;
  transform: scaleX(0);
  background: linear-gradient(90deg, var(--accent), var(--additional));
  pointer-events: none;
}

/* G1 — butiran film. inset -50% supaya waktu digeser 6% bolak-balik
   tepinya tidak pernah masuk layar. Gambar noise-nya dibangkitkan di JS
   sebagai data URI; opacity-nya juga dinyalakan dari sana, jadi tanpa JS
   lapisan ini benar-benar tidak ada. */
.grain {
  position: fixed;
  inset: -50%;
  z-index: 110;
  opacity: 0;
  pointer-events: none;
  will-change: transform;
}

/* S6 — garis proses. MENDATAR, bukan vertikal seperti di lab: .steps di
   sini grid auto-fit yang menyejajarkan 4 kartunya (:1207). Posisi dan
   lebar relnya diukur JS dari titik tengah .step__num pertama sampai
   terakhir, dan relnya menyembunyikan diri sendiri kalau nomornya tidak
   sebaris lagi (HP/tablet) — garis mendatar di antara kartu yang tersusun
   ke bawah tidak berarti apa-apa. */
.steps { position: relative; }

.steps__rail {
  position: absolute;
  height: 2px;
  border-radius: 2px;
  background: var(--stroke);
  opacity: 0;
  pointer-events: none;
}

.steps__fill {
  position: absolute;
  inset: 0;
  /* titik tumpu di kiri, alasannya sama dengan .progress */
  transform-origin: 0 50%;
  transform: scaleX(0);
  border-radius: inherit;
  background: linear-gradient(90deg, var(--accent), var(--additional));
}

/* Urutan menumpuknya dikunci di sini, jangan diandalkan ke kebetulan.
   Kartunya sengaja DI ATAS relnya, jadi garisnya cuma terlihat di sela
   antar-kartu — menyambung seperti rantai, bukan tercoret melintasi
   badan kartu yang putih polos. Nomornya naik lagi di dalam kartunya.

   Kalau ini dibiarkan tanpa z-index, hasilnya kebetulan sama — tapi cuma
   karena GSAP meninggalkan transform di .card sesudah reveal L3, dan
   elemen ber-transform ikut naik lapisan. Hilangkan L3 dan tumpukannya
   terbalik sendiri. Makanya ditulis eksplisit. */
.steps .card { position: relative; z-index: 1; }
.step__num { position: relative; z-index: 2; }


/* --------------------------------------------------------------------------
   15. Halaman layanan — hero
   Dipakai bersama oleh keenam halaman jasa-*.html.

   NAMANYA .pg-hero, BUKAN .hero, dan itu disengaja. Dua alasan:
     1. .intro .header menarik header keluar layar. Di beranda itu benar,
        heronya setinggi layar. Di sini tidak ada yang menutupi, jadi
        headernya harus kelihatan sejak awal — app.js sudah dijaga supaya
        .intro cuma dipasang kalau ada .hero.
     2. efek.js S1 (hero-scrollout) memakai querySelector('.hero'). Kalau
        section ini bernama .hero, judulnya ikut memudar waktu discroll.
   Keduanya aman selama kelasnya tetap .pg-hero.

   TIDAK diberi kelas reveal L3 apa pun: ini layar pertama, dan elemen yang
   sudah kelihatan waktu halaman dibuka tidak boleh digantung ke
   ScrollTrigger — trigger-nya tidak pernah menyala dan elemennya nyangkut
   opacity 0 selamanya. Catatannya di EFEK.txt bagian D.
   -------------------------------------------------------------------------- */
.pg-hero {
  position: relative;
  /* 12rem = jatah header fixed (padding 2.4rem + tinggi isinya), sisanya
     ruang napas. Beranda tidak butuh ini karena headernya memang disembunyikan
     di layar pertama. */
  padding: 18rem 0 10rem;
}

.pg-hero__title {
  font-family: var(--font-accent);
  font-weight: 700;
  font-size: clamp(3.8rem, 5vw, 6.4rem);
  line-height: 1.06;
  letter-spacing: -.02em;
  color: var(--t-bright);
  margin: 2rem 0 0;
  max-width: 20ch;
}

.pg-hero__lead {
  margin: 2.8rem 0 0;
  font-size: 1.8rem;
  line-height: 1.6;
  color: var(--t-muted);
  max-width: 50ch;
}

.pg-hero__aksi { margin-top: 4rem; }

/* Penanda halaman aktif di nav. Cuma warna + titik kecil, tidak menggeser
   apa pun, jadi tata letak headernya persis sama dengan beranda. */
.nav a[aria-current="page"] {
  color: var(--t-bright);
  font-weight: 600;
}
.nav a[aria-current="page"]::after {
  content: "";
  display: block;
  height: .3rem;
  border-radius: 999px;
  background: var(--accent);
  margin-top: .4rem;
}
.menu__nav a[aria-current="page"] { color: var(--accent); }

/* --------------------------------------------------------------------------
   Diagram "cara Google menemukanmu" — dibangun dari div + satu SVG garis,
   bukan gambar, supaya ikut hidup waktu temanya berubah dan tetap tajam
   di layar berapa pun.
   -------------------------------------------------------------------------- */
.crawl { position: relative; }

/* Judul kecil di atas diagram. Diagramnya punya satu inti — sinyal
   kepercayaan — dan tanpa judul orang harus menebaknya dari garis. */
.crawl__title {
  margin: 0 0 1.6rem;
  font-family: var(--font-accent);
  font-weight: 700;
  font-size: 1.6rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--t-bright);
}

/* Panggungnya: kotak berperbandingan tetap, tempat SVG dan node ditumpuk.
   Perbandingannya HARUS sama dengan viewBox SVG (400x320), karena posisi
   node dihitung dalam persen dari koordinat viewBox itu. */
.crawl__stage {
  position: relative;
  width: 100%;
  max-width: 54rem;
  aspect-ratio: 400 / 320;
}

.crawl__lines { position: absolute; inset: 0; width: 100%; height: 100%; }

.crawl__node {
  position: absolute;
  transform: translate(-50%, -50%);
  display: inline-flex;
  align-items: center;
  gap: .8rem;
  padding: 1.2rem 1.8rem;
  border-radius: var(--radius-m);
  background: var(--base-tint);
  border: 1px solid var(--stroke);
  box-shadow: var(--shadow);
  font-weight: 600;
  font-size: 1.5rem;
  color: var(--t-bright);
  white-space: nowrap;
}

.crawl__node i { font-size: 1.8rem; }

/* Mengikuti koordinat SVG: Google (56,52) kiri atas, Websitemu (322,262)
   kanan bawah, pada viewBox 400x320. */
.crawl__node--google { left: 14%; top: 16%; }
.crawl__node--you    { left: 80%; top: 82%; }

/* Yang dituju — dibedakan supaya jelas ini ujung ceritanya. */
.crawl__node--you {
  background: var(--t-bright);
  border-color: var(--t-bright);
  color: var(--base);
}

/* Legenda tiga baris. Tiga jenis garis, artinya beda-beda; tanpa legenda
   semuanya cuma terlihat seperti "garis". */
.crawl__legend {
  margin: 2rem 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: .8rem 2.4rem;
  font-size: 1.3rem;
  color: var(--t-muted);
}

.crawl__legend li { display: inline-flex; align-items: center; gap: .8rem; }

.crawl__key {
  width: 2.4rem;
  height: 0;
  border-top-width: 2px;
  border-top-style: solid;
  flex: none;
}

.crawl__key--crawl  { border-top-style: dashed; border-top-color: var(--t-faint); }
.crawl__key--direct { border-top-style: dashed; border-top-color: var(--t-medium); }
.crawl__key--trust  { border-top-color: var(--accent); border-top-width: 2.5px; }

/* --------------------------------------------------------------------------
   Titik berjalan di sepanjang garis — tekniknya sama persis dengan .orbit__in
   di beranda: satu strip nyaris nol panjang (stroke-dasharray .1) dengan
   linecap bulat, jadi yang terlihat cuma sebuah TITIK, lalu dashoffset-nya
   digeser supaya titik itu meluncur mengikuti path.

   Bedanya dari beranda: di sini tiap path diberi atribut pathLength="100".
   Dengan begitu dasharray dan dashoffset dihitung dalam satuan 1/100 panjang
   path, bukan piksel — jadi SATU keyframe saja cukup untuk semua garis,
   sepanjang apa pun, dan tidak perlu mengukur tiap path satu-satu.
   -------------------------------------------------------------------------- */
.crawl__walk path {
  stroke-linecap: round;
  stroke-width: 7;
  stroke-dasharray: .1 100;
  animation: crawlWalk 4s linear infinite;
}

@keyframes crawlWalk {
  0%   { stroke-dashoffset: 0;    opacity: 0; }
  8%   { opacity: 1; }
  78%  { opacity: 1; }
  100% { stroke-dashoffset: -100; opacity: 0; }
}

/* Saklar geraknya sama dengan beranda: ?motion=off mematikan semuanya.
   Titik-titik ini hiasan murni, jadi disembunyikan total — garis dan
   simpulnya tetap utuh, diagramnya masih terbaca penuh tanpa animasi. */
:root[data-motion="off"] .crawl__walk { display: none; }

@media (max-width: 575.98px) {
  .pg-hero { padding: 14rem 0 7rem; }
  .crawl__node { font-size: 1.3rem; padding: 1rem 1.4rem; }
  .crawl__tag  { font-size: 1.1rem; }
}


/* --------------------------------------------------------------------------
   16. Hero lebar + alur iklan berbayar (.pg-hero--solo, .flowx)

   Bentuknya PELACAK STATUS: satu rel dengan lima penanda, tiap penanda punya
   kartunya sendiri. Dipilih begitu supaya terbaca sebagai satu proses yang
   berjalan, bukan lima kotak yang kebetulan berjejer.

   Dua arah, satu markup, dan keduanya SIMETRIS - berangkat di satu sisi,
   pulang di sisi seberangnya:
     >= 992px  rel MENDATAR di ATAS,  garis balik mendatar di BAWAH
     <  992px  rel TEGAK di KIRI,     garis balik tegak di KANAN

   Kartunya sendiri TIDAK pernah berubah bentuk: slot media selalu di atas,
   tulisan di bawahnya. Yang berputar cuma relnya.
   -------------------------------------------------------------------------- */
.pg-hero--solo { padding-bottom: 12rem; }

.pg-hero__intro {
  max-width: 62rem;
  margin: 0 auto 7rem;
  text-align: center;
}

.pg-hero__intro .pg-hero__title,
.pg-hero__intro .pg-hero__lead { max-width: none; }
.pg-hero__intro .pg-hero__lead { margin-inline: auto; }

.flowx {
  position: relative;
  /* Ruang buat kaki kiri dan kanan garis balik, dan ruang di bawah buat
     kaki bawahnya. Kalau dikurangi, garisnya mulai memotong kartu tepi. */
  padding: 0 4rem 6.4rem;
}

.flowx__row {
  position: relative;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 2.4rem;
  align-items: start;
}

/* Rel: satu garis di belakang semua penanda, SATU ARAH - kepala panahnya
   cuma di ujung akhir. Bersama garis balik di bawah yang mendarat di ujung
   awal, keduanya menandai lingkarannya: maju di sini, pulang di sana. Tidak
   ada panah di antara kartu; dua ujung saja sudah cukup terbaca.

   Ujungnya ditarik ke 6%/94%, sedikit di luar penanda pertama dan terakhir
   yang duduk di 10% dan 90% - kalau pas di 10%/90%, kepala panahnya menimpa
   penandanya sendiri. */
.flowx__rail {
  position: absolute;
  left: 6%;
  right: 6%;
  top: .8rem;
  height: 2px;
  background: var(--stroke);
}

.flowx__rail::after {
  content: "";
  position: absolute;
  top: 50%;
  right: -1px;
  width: 0;
  height: 0;
  border-top: .5rem solid transparent;
  border-bottom: .5rem solid transparent;
  border-left: .75rem solid var(--stroke);
  transform: translateY(-50%);
}

.flowx__item { position: relative; display: flex; flex-direction: column; gap: 2rem; }

/* Kepala panah di TIAP SAMBUNGAN antar kartu, bukan cuma di ujung. Satu panah
   di ujung saja gampang terlewat - apalagi waktu relnya panjang atau kartunya
   ramai - jadi arah alurnya diulang di tiap celah. Ditaruh persis di tengah
   celah grid: tepi kanan kartu + separuh gap. */
.flowx__item:not(:last-child)::after {
  content: "";
  position: absolute;
  z-index: 1;
  top: .8rem;
  right: -1.2rem;
  width: 0;
  height: 0;
  border-top: .45rem solid transparent;
  border-bottom: .45rem solid transparent;
  border-left: .7rem solid var(--t-faint);
  transform: translate(50%, -50%);
}

/* Penandanya KOTAK bersudut tumpul, bukan bulat: yang digantung di rel ini
   kartu, jadi penandanya ikut berbentuk kartu kecil. */
.flowx__mark {
  position: relative;
  z-index: 1;
  align-self: center;
  width: 1.7rem;
  height: 1.7rem;
  border-radius: .45rem;
  background: var(--base);
  border: 3px solid var(--c);
}

.flowx__card {
  border-radius: var(--radius-m);
  background: var(--base-tint);
  border: 1px solid var(--stroke);
  box-shadow: var(--shadow);
  overflow: hidden;
}

/* SLOT MEDIA di atas kartu. Sekarang isinya SVG, tapi sengaja dibikin sebagai
   kotak berperbandingan tetap supaya nanti bisa diganti <img>, <video>, atau
   latar bergerak TANPA mengubah tata letak apa pun - tinggal taruh elemennya
   di dalam sini. Perbandingannya dikunci, jadi kelima kartu tetap sejajar
   berapa pun isinya. */
.flowx__art {
  position: relative;
  aspect-ratio: 16 / 9;
  display: grid;
  place-items: center;
  overflow: hidden;
  background: color-mix(in srgb, var(--c) 12%, var(--base-tint));
  border-bottom: 1px solid var(--stroke);
  color: var(--c);
}

.flowx__art svg { width: 58%; height: auto; display: block; }

/* Begitu slotnya diisi gambar atau video, dia mengisi penuh dan memotong
   kelebihannya sendiri. Tidak perlu CSS tambahan waktu itu terjadi. */
.flowx__art img,
.flowx__art video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.flowx__body { padding: 1.6rem 1.6rem 1.8rem; }

.flowx__step {
  display: block;
  font-size: 1.1rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--c);
  margin-bottom: .5rem;
}

.flowx__label { font-family: var(--font-accent); font-weight: 700; font-size: 1.7rem; line-height: 1.25; color: var(--t-bright); margin: 0; }
.flowx__sub   { font-size: 1.35rem; line-height: 1.55; color: var(--t-muted); margin: .7rem 0 0; }

/* --- garis balik: MENGELILINGI kartu, dari ujung rel kembali ke pangkalnya.

   Sebelumnya dia cuma baki dangkal di bawah kartu, jadi panahnya berhenti di
   bawah dan lingkarannya tidak pernah kelihatan menutup. Sekarang jalurnya
   penuh: turun di kanan LUAR kartu, menyeberang di bawah, lalu naik lagi di
   kiri luar sampai SEJAJAR REL, dan kepala panahnya mendarat di pangkal rel.

   Karena kedua kakinya berada di luar kartu, .flowx diberi padding kiri-kanan
   supaya ada ruangnya - tanpa itu garisnya akan memotong kartu tepi. --- */
.flowx__return {
  position: absolute;
  left: 0;
  right: 0;
  top: .8rem;
  bottom: 2.6rem;
  pointer-events: none;
}

/* Kaki bawah, menyeberang dari kanan ke kiri. */
.flowx__return::before {
  content: "";
  position: absolute;
  left: 1.3rem;
  right: 1.3rem;
  bottom: 0;
  height: 2px;
  background: var(--accent);
}

/* Kaki kanan: turun dari sejajar rel sampai ke kaki bawah. */
.flowx__return::after {
  content: "";
  position: absolute;
  right: 1.3rem;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--accent);
}

/* Kaki kiri: naik lagi sampai sejajar rel, berkepala panah di ujung atasnya. */
.flowx__return-up {
  position: absolute;
  left: 1.3rem;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--accent);
}

.flowx__return-head {
  position: absolute;
  left: 50%;
  top: -.7rem;
  width: 0; height: 0;
  border-left: .6rem solid transparent;
  border-right: .6rem solid transparent;
  border-bottom: .9rem solid var(--accent);
  transform: translateX(-50%);
}

.flowx__return-label {
  position: absolute;
  left: 50%;
  bottom: -.8rem;
  transform: translateX(-50%);
  display: inline-flex;
  align-items: center;
  gap: .7rem;
  background: var(--base);
  padding: 0 1.2rem;
  font-size: 1.3rem;
  font-weight: 600;
  color: var(--t-muted);
  white-space: nowrap;
}

.flowx__return-label i { font-size: 1.5rem; color: var(--accent); }

/* Labelnya SATU-SATUNYA bagian garis balik yang bisa diklik. Induknya
   pointer-events:none supaya kotak besarnya tidak menutupi kartu di
   belakangnya; di sini dinyalakan lagi khusus untuk label. Kliknya ditangani
   efek.js yang menyulih semua a[href^="#"] ke lenis.scrollTo, jadi
   gulungannya beranimasi, bukan meloncat. */
.flowx__return-label {
  pointer-events: auto;
  text-decoration: none;
  border: 1px solid transparent;
  border-radius: 999px;
  transition: color var(--speed-med) var(--bezier),
              border-color var(--speed-med) var(--bezier);
}

.flowx__return-label:hover,
.flowx__return-label:focus-visible {
  color: var(--t-bright);
  border-color: var(--stroke);
}

.flowx__return-label:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* ---------- di bawah 992px: rel berdiri, jadi TEGAK ---------- */
@media (max-width: 991.98px) {
  /* Kiri buat rel + penanda, kanan buat garis balik. Turun di kiri, naik di
     kanan - cermin dari versi mendatar, cuma diputar 90 derajat. */
  .flowx { padding-left: 4.2rem; padding-right: 4.2rem; }

  .flowx__row { grid-template-columns: 1fr; gap: 2rem; }

  .flowx__rail {
    left: -2.6rem;
    right: auto;
    top: 6%;
    bottom: 6%;
    width: 2px;
    height: auto;
  }

  /* Kepala panahnya ikut berputar: yang tadi menunjuk ke kanan sekarang
     menunjuk ke bawah. Border yang tidak dipakai dinolkan dulu, kalau tidak,
     sisa border dari versi mendatar bikin bentuknya miring. */
  .flowx__rail::after {
    top: auto;
    bottom: -1px;
    left: 50%;
    right: auto;
    border-top: .75rem solid var(--stroke);
    border-bottom: 0;
    border-left: .5rem solid transparent;
    border-right: .5rem solid transparent;
    transform: translateX(-50%);
  }

  /* Kartu TETAP utuh dengan slot media di atasnya, supaya medianya dapat
     lebar penuh - itu ruang yang nanti diisi gambar atau video. */
  .flowx__item { flex-direction: column; gap: 0; }

  .flowx__mark { position: absolute; left: -3.25rem; top: 1.6rem; align-self: auto; }

  /* Panah sambungan ikut berputar: menunjuk ke bawah, duduk di rel tegak. */
  .flowx__item:not(:last-child)::after {
    top: auto;
    bottom: -1rem;
    left: calc(-2.6rem + 1px);
    right: auto;
    border-top: .7rem solid var(--t-faint);
    border-bottom: 0;
    border-left: .45rem solid transparent;
    border-right: .45rem solid transparent;
    transform: translate(-50%, 50%);
  }

  /* Garis balik pindah ke SISI KANAN dan naik ke atas. */
  .flowx__return {
    position: absolute;
    right: 0;
    left: auto;
    top: 1.6rem;
    bottom: 1.6rem;
    width: 2.6rem;
    height: auto;
  }

  .flowx__return::before {
    left: auto;
    right: 1.2rem;
    top: 0;
    bottom: 0;
    width: 2px;
    height: auto;
  }

  .flowx__return::after  { display: none; }
  .flowx__return-up      { display: none; }

  .flowx__return-head { left: auto; right: 1.2rem; top: -.4rem; transform: translateX(50%); }

  .flowx__return-label {
    left: auto;
    right: 1.2rem;
    bottom: 50%;
    transform: translate(50%, 50%);
    writing-mode: vertical-rl;
    padding: 1.2rem 0;
  }

  .flowx__return-label i { transform: rotate(90deg); }
}

@media (max-width: 575.98px) {
  .pg-hero--solo { padding-bottom: 7rem; }
  .pg-hero__intro { margin-bottom: 4.8rem; }
  .flowx { padding-left: 3.4rem; padding-right: 3.4rem; }
  .flowx__rail { left: -2.2rem; }
  .flowx__mark { left: -2.85rem; }
  .flowx__item:not(:last-child)::after { left: calc(-2.2rem + 1px); }
  .flowx__label { font-size: 1.55rem; }
  .flowx__sub { font-size: 1.25rem; }
}

/* --------------------------------------------------------------------------
   17. Dinding potret (.wall) — hero jasa-sosmed.html

   Delapan ubin potret RAPAT, tanpa jarak sama sekali. Kepadatan itu sendiri
   yang jadi pesannya: "banyak yang lihat tiap hari" harus terlihat seperti
   kerumunan, bukan seperti beberapa kartu yang berjejer.

   Kolomnya digeser BERSELANG-SELING setengah ubin, seperti pasangan bata.
   Kalau lurus semua, sambungannya sejajar dan hasilnya terbaca seperti tabel.

   MASALAHNYA: menggeser kolom di dalam bingkai tetap akan menyisakan ruang
   kosong di atas dan di bawah. Jalan keluarnya, kisi di dalam dibuat LEBIH
   TINGGI dari jendelanya lalu ditarik ke atas, dan .wall memotong sisanya.
   Karena tiap kolom lebih tinggi dari jendela, digeser +-(--off) pun tidak
   pernah membuka celah. Kelebihan tinggi yang sama dipakai animasi hanyutnya,
   jadi geseran diam dan geseran bergerak memakai satu mekanisme yang sama.

   Ubinnya masih warna polos. Taruh <img> atau <video> di dalam .wall__t dan
   dia langsung mengisi penuh serta memotong sendiri - tidak ada CSS yang
   perlu diubah waktu itu terjadi.
   -------------------------------------------------------------------------- */
.wall {
  --off: 6rem;
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-m);
  aspect-ratio: 16 / 9;
  background: var(--base-shade);
}

.wall__grid {
  position: absolute;
  inset: calc(var(--off) * -1) 0;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  /* Nol mutlak. Seluruh efeknya bergantung pada tidak adanya celah. */
  gap: 0;
}

.wall__col {
  display: grid;
  grid-template-rows: repeat(2, 1fr);
  gap: 0;
  animation: wallDrift 18s ease-in-out infinite;
}

/* Berselang-seling: ganjil naik, genap turun. Fase animasinya juga dibalik,
   jadi keduanya tidak pernah bergerak searah. */
.wall__col:nth-child(odd)  { transform: translateY(calc(var(--off) * -1)); animation-delay: -9s; }
.wall__col:nth-child(even) { transform: translateY(var(--off)); }

/* Amplitudonya SETENGAH --off. Lebih dari itu dan ujung kolomnya mulai
   terlihat di tepi jendela. */
@keyframes wallDrift {
  0%, 100% { translate: 0 calc(var(--off) * -.5); }
  50%      { translate: 0 calc(var(--off) * .5); }
}

/* Ubin. Warna dasarnya dari --c, dipakai selama fotonya belum ada. */
.wall__t {
  position: relative;
  overflow: hidden;
  background: color-mix(in srgb, var(--c) 26%, var(--base-shade));
}

/* SOROTAN BERJALAN. Satu bingkai terang berpindah dari ubin ke ubin, satu per
   satu, terus berputar. Maksudnya persis pesan halaman ini: di tengah keramaian,
   yang diingat adalah yang menonjol - dan giliran itu bisa jadi punyamu.

   Dikerjakan tanpa JS: kedelapan ubin memakai animasi yang SAMA, cuma
   animation-delay-nya digeser lewat --i. Tiap ubin menyala sebentar lalu padam,
   dan karena jedanya berurutan, sorotannya terlihat berjalan.

   Ini murni hiasan di atas ubin. Kalau animasinya tidak jalan, yang hilang cuma
   bingkainya - potretnya tetap utuh, jadi aman dipakai di layar pertama. */
.wall__t::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;

  /* SATU bingkai saja. Warnanya gradien kerucut yang BERPUTAR, jadi bagian
     terang dan gelapnya berjalan mengelilingi ubin - terang ke gelap, gelap
     ke terang, terus-menerus.

     Kenapa gradien dan bukan satu warna: isi ubin nanti berupa foto atau
     video yang warnanya tidak bisa ditebak. Bingkai satu warna pasti hilang
     di suatu latar. Karena gradien ini selalu memuat ujung terang DAN ujung
     gelap sekaligus, di latar apa pun tetap ada bagian yang kontras.

     Caranya: kotak ini diisi gradien penuh, lalu bagian tengahnya DILUBANGI
     pakai mask - yang tersisa persis cincin selebar padding. Semua di dalam
     kotak ubin, tidak ada yang keluar, jadi tidak bisa terpotong di tepi. */
  padding: 3px;
  background: conic-gradient(from var(--ang, 0deg),
              #DDF160, #161616, #FFFBF0, #161616, #DDF160);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;

  opacity: 0;
  animation: wallPop 12s linear infinite,
             wallSpin 2.4s linear infinite;
  animation-delay: calc(var(--i, 0) * 1.5s), 0s;
}

/* Sudut gradien harus didaftarkan supaya bisa dianimasikan mulus - tanpa ini
   browser memperlakukannya sebagai nilai diskret dan putarannya meloncat. */
@property --ang { syntax: '<angle>'; initial-value: 0deg; inherits: false; }

@keyframes wallSpin {
  to { --ang: 360deg; }
}

@keyframes wallPop {
  0%   { opacity: 0; }
  3%   { opacity: 1; }
  9%   { opacity: 1; }
  13%  { opacity: 0; }
  100% { opacity: 0; }
}

/* Ubin yang disorot MEMBESAR dan menimpa tetangganya. Karena dindingnya rapat
   tanpa jarak, itulah yang bikin dia benar-benar terlihat keluar.

   MASALAHNYA dulu: dengan transform-origin di tengah, ubin di kolom tepi
   tumbuh KELUAR dari .wall dan sisi luarnya terpotong overflow:hidden - itu
   sebabnya bingkai kanannya hilang. Perbaikannya bukan mengecilkan skalanya,
   tapi mengubah TITIK TUMPUnya: kolom paling kiri tumbuh ke kanan, kolom
   paling kanan tumbuh ke kiri. Jadi pertumbuhannya selalu mengarah ke dalam
   dan tidak pernah menyentuh tepi potongan.

   Arah tegaknya sudah aman sejak awal - kisinya memang lebih tinggi dari
   jendelanya, jadi ada ruang lebih di atas dan di bawah. */
.wall__t {
  transform-origin: center;
  animation: wallLift 12s ease-in-out infinite;
  animation-delay: calc(var(--i, 0) * 1.5s);
}

.wall__col:first-child .wall__t { transform-origin: left center; }
.wall__col:last-child  .wall__t { transform-origin: right center; }

@keyframes wallLift {
  0%   { transform: scale(1);    z-index: 1; }
  3%   { transform: scale(1.2); z-index: 2; }
  9%   { transform: scale(1.2); z-index: 2; }
  13%  { transform: scale(1);    z-index: 1; }
  100% { transform: scale(1);    z-index: 1; }
}

:root[data-motion="off"] .wall__t { animation: none; transform: none; }

:root[data-motion="off"] .wall__t::before { animation: none; opacity: 0; }

/* Garis rambut antar ubin. Bukan jarak - cuma pemisah setipis mungkin supaya
   tiap potret masih terbaca sebagai potret terpisah waktu warnanya mirip. */
.wall__t::after {
  content: "";
  position: absolute;
  inset: 0;
  box-shadow: inset 0 0 0 1px rgba(255, 251, 240, .22);
}

.wall__t img,
.wall__t video,
.wall__t svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;

  /* Isi ubin ikut bergerak sendiri: membesar dan menggeser pelan, bergantian
     arah lewat --d. Jadi ada DUA gerakan yang bertumpuk - kolomnya hanyut,
     isinya mengambang - dan dindingnya tidak pernah terlihat diam.

     Berlaku juga buat <img> dan <video> asli nanti, jadi foto diam pun tetap
     terasa hidup tanpa perlu bikin video. */
  /* Dua animasi sekaligus di elemen yang sama: satu menggerakkan transform
     (mengambang), satu menggerakkan filter (menyala waktu kena sorotan).
     Boleh berbeda durasi dan jeda karena keduanya menyentuh properti yang
     berbeda - jadi tidak saling menimpa. */
  animation: wallZoom 14s ease-in-out infinite,
             wallDim  12s linear      infinite;
  animation-delay: calc(var(--i, 0) * -1.6s),
                   calc(var(--i, 0) * 1.5s);
  transform-origin: center;
}

/* Yang tidak sedang disorot ditahan agak redup. Karena kedelapan ubin memakai
   jadwal yang sama dengan jeda berurutan, di tiap saat PERSIS SATU ubin yang
   terang dan sisanya mundur - itulah yang bikin sorotannya terasa sebagai
   sorotan, bukan cuma garis yang berpindah. */
@keyframes wallDim {
  0%   { filter: brightness(.68) saturate(.8); }
  3%   { filter: brightness(1.08) saturate(1.1); }
  9%   { filter: brightness(1.08) saturate(1.1); }
  13%  { filter: brightness(.68) saturate(.8); }
  100% { filter: brightness(.68) saturate(.8); }
}

@keyframes wallZoom {
  0%, 100% { transform: scale(1.06) translate(calc(var(--d, 1) * -1.2%), 1%); }
  50%      { transform: scale(1.14) translate(calc(var(--d, 1) * 1.2%), -1%); }
}

:root[data-motion="off"] .wall__t img,
:root[data-motion="off"] .wall__t video,
:root[data-motion="off"] .wall__t svg { animation: none; transform: none; }

/* Tepi atas dan bawah dilembutkan supaya dindingnya menyatu ke halaman,
   bukan terpotong mendadak. */
.wall::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(to bottom,
              var(--base) 0%, transparent 14%,
              transparent 86%, var(--base) 100%);
}

:root[data-motion="off"] .wall__col { animation: none; }

/* JANGAN pernah jadi satu kolom. Satu kolom memanjang terbaca sebagai daftar,
   dan itu menghapus seluruh maksud gambarnya. */
@media (max-width: 767.98px) {
  .wall { --off: 4rem; aspect-ratio: 3 / 4; }

  /* Keempat blok kolom membungkus jadi 2 x 2. Tiap blok tetap berisi 2 ubin,
     jadi hasilnya 2 kolom x 4 baris ubin - persis yang diminta, tanpa markup
     kedua. grid-template-rows di .wall__col TIDAK diubah: isinya memang masih
     dua ubin per blok, dan mengubahnya jadi 4 akan menyisakan dua baris kosong.

     Geserannya ikut benar dengan sendirinya: blok ganjil (1 dan 3) semuanya
     jatuh di kolom kiri, genap (2 dan 4) di kanan - jadi kiri naik, kanan
     turun, pasangan batanya tetap. */
  .wall__grid { grid-template-columns: repeat(2, 1fr); }

  /* Blok 1 dan 3 jatuh di kolom kiri, 2 dan 4 di kanan - jadi titik tumpuhnya
     ikut aturan itu, bukan aturan first/last-child versi mendatar. */
  .wall__col:first-child .wall__t,
  .wall__col:last-child  .wall__t { transform-origin: center; }
  .wall__col:nth-child(odd)  .wall__t { transform-origin: left center; }
  .wall__col:nth-child(even) .wall__t { transform-origin: right center; }
}

/* --------------------------------------------------------------------------
   18. Piramida FYP (.fyp) — hero jasa-content-marketing.html

   Tumpukan MENURUN: tiap tingkat lebih lebar dan lebih ramai dari yang di
   atasnya, jadi siluetnya piramida terbalik. Dibaca dari atas ke bawah,
   dan sengaja tidak dimulai dari FYP - yang paling atas adalah pangkalnya,
   konten yang tepat sampai ke orang yang memang cocok. FYP di dasar, sebagai
   AKIBAT.

   Pemicunya ditulis DI SELA tiap tingkat. Itu yang bikin gambar ini
   menjelaskan sebab, bukan sekadar menggambar pertumbuhan.

   Isinya ikon ORANG, bukan titik: sekali lihat langsung terbaca "ini banyak
   orang" tanpa perlu keterangan. Warnanya dicampur biru dan merah supaya
   terbaca sebagai orang yang berbeda-beda, bukan satu pola yang diulang.
   -------------------------------------------------------------------------- */
.fyp { display: grid; gap: 0; }

.fyp__tier {
  width: var(--w);
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
  padding: 1.5rem 1.4rem;
  border-radius: var(--radius-m);
  background: color-mix(in srgb, var(--c) 10%, var(--base-tint));
  border: 1px solid var(--stroke);
}

.fyp__crowd {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: .3rem;
}

/* Ikon orang: kepala bulat di atas bahu membulat. */
.fyp__p { width: 1.7rem; height: 1.7rem; display: block; flex: none; }
.fyp__p use { fill: currentColor; }
/* Enam warna yang sama dengan diagram orbit di beranda. Tingkat atas cuma
   memakai dua yang pertama; tingkat terakhir memakai keenam-enamnya - itu
   yang menandai "menyebar ke mana-mana" tanpa perlu tulisan tambahan. */
.fyp__p--a { color: #4C7DE0; }
.fyp__p--b { color: #DE4B3C; }
.fyp__p--c { color: #3E9B6B; }
.fyp__p--d { color: #F0B429; }
.fyp__p--e { color: #9F8BE7; }
.fyp__p--f { color: #D98324; }

/* Animasi yang sama persis dengan versi titik dulu: menyala bergiliran,
   sedikit membesar, lalu redup lagi. Yang bergerak cuma ikon hiasan. */
.fyp__crowd .fyp__p {
  opacity: .42;
  animation: fypGlow 6s ease-in-out infinite;
  animation-delay: calc(var(--t) * 1.1s + var(--n) * .03s);
}

@keyframes fypGlow {
  0%, 100% { opacity: .35; transform: scale(1); }
  9%       { opacity: 1;   transform: scale(1.2); }
  28%      { opacity: .35; transform: scale(1); }
}

.fyp__label { font-weight: 600; font-size: 1.3rem; line-height: 1.35; color: var(--t-bright); text-align: center; margin: 0; }
.fyp__count { display: block; font-weight: 400; font-size: 1.1rem; color: var(--t-muted); margin-top: .25rem; }

/* Pemicu antar tingkat - inilah "caranya". */
.fyp__gate {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .7rem;
  padding: .9rem 0;
  margin: 0;
  font-size: 1.2rem;
  font-weight: 600;
  color: var(--t-muted);
  text-align: center;
}

.fyp__gate i { font-size: 1.5rem; color: var(--accent); flex: none; }

/* Dasar piramida. SENGAJA TIDAK dibikin gelap - blok hitam di tengah halaman
   krem yang lapang terlihat seperti tempelan dari desain lain. Yang
   membedakannya justru WARNANYA: kalau tiga tingkat di atas cuma biru dan
   merah, di sini keenam warna muncul sekaligus. Kerumunannya jadi beragam,
   dan "menyebar ke mana-mana" terbaca dari gambarnya sendiri.

   Latarnya lime tipis dengan garis penuh, jadi tetap menonjol sebagai ujung
   cerita tanpa keluar dari palet halaman. */
.fyp__tier--fyp {
  background: color-mix(in srgb, var(--additional) 26%, var(--base-tint));
  border-color: color-mix(in srgb, var(--additional) 70%, var(--t-bright));
}

:root[data-motion="off"] .fyp__crowd .fyp__p { animation: none; opacity: .6; transform: none; }

@media (max-width: 991.98px) {
  /* Kolomnya sudah tidak dibagi dua, jadi piramidanya dapat lebar penuh. */
  .fyp { max-width: 52rem; margin-inline: auto; }
}

@media (max-width: 575.98px) {
  /* Tingkat teratas terlalu sempit buat dibaca kalau --w dipertahankan.
     Semua dibikin penuh, dan yang menunjukkan urutan tinggal JUMLAH ikonnya. */
  .fyp__tier { width: 100%; padding: 1.2rem 1rem; }
  .fyp__p { width: 1.45rem; height: 1.45rem; }
  .fyp__label { font-size: 1.2rem; }
  .fyp__gate { font-size: 1.1rem; }
}


/* --------------------------------------------------------------------------
   19. Ilustrasi hero + chip UI yang menyembul (.shot)
   Dipakai di hero jasa-pembuatan-website-jakarta.html.

   DUA hal yang perlu diketahui sebelum mengubah bagian ini:

   1. hero-website.png TIDAK punya kanal alpha - latarnya putih pekat, bukan
      transparan. Di halaman krem (--base) itu tampil sebagai kotak putih yang
      kelihatan seperti salah pasang. mix-blend-mode:multiply mengalikan putih
      dengan latarnya, jadi putihnya lenyap dan ilustrasinya seperti dicetak
      langsung di halaman. Persis masalah yang dulu terjadi pada
      background-hero.png, catatannya ada di EFEK.txt.

      BATASNYA: multiply cuma bekerja di atas latar TERANG. Kalau suatu saat
      ada latar gelap di belakangnya, ilustrasinya akan hilang. Kalau nanti
      dapat versi SVG atau PNG ber-alpha, hapus satu baris blend-nya.

   2. Chip-nya sengaja MENYEMBUL keluar dari tepi gambar. Karena itu .shot
      harus overflow:visible - jangan diberi overflow:hidden, karena justru
      bagian yang melewati batas itulah yang bikin kesan menyembulnya jadi.
   -------------------------------------------------------------------------- */
.shot {
  position: relative;
  overflow: visible;
  max-width: 54rem;
  margin-inline: auto;
}

/* Bingkai gambar. overflow:hidden ADA DI SINI, bukan di .shot - kalau di
   .shot, chip yang sengaja menyembul keluar ikut terpotong. Jadi dua-duanya
   dapat yang dibutuhkan: gambar terpotong rapi waktu di-zoom, chip tetap
   boleh melewati batas. */
.shot__frame {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-m);
}

.shot__img {
  display: block;
  width: 100%;
  height: auto;
  mix-blend-mode: multiply;

  /* TUR TIGA TITIK. Satu gambar, tiga bagian, didekati bergiliran - masing
     masing sepadan dengan satu chip: Muat cepat, UI/UX responsif, lalu
     Sekali lihat langsung paham.

     CARANYA, dan ini bagian yang penting: titik tumpu (transform-origin)
     dipindah HANYA pada saat skalanya kembali ke 1. Di skala 1 titik tumpu
     tidak punya pengaruh visual apa pun, jadi perpindahannya tidak terlihat.
     Kalau dipindah sambil ter-zoom, gambarnya akan menggeser sendiri dan
     terlihat seperti tergelincir.

     Urutannya per sepertiga siklus: mundur ke utuh, pindah titik, masuk lagi
     ke bagian berikutnya. */
  transform-origin: 48% 45%;
  animation: heroTour 20s ease-in-out infinite;
}

@keyframes heroTour {
  /* Urutannya disengaja dan berakhir di pemahaman: halamannya terbuka dulu,
     tampil benar di layar apa pun, ditemukan orang, lalu - yang paling
     menentukan - langsung dimengerti. Tiap perhentian sepadan dengan satu
     chip di atas gambar.

     Titik tumpu HANYA berpindah saat skalanya 1. Di skala 1 dia tidak punya
     pengaruh visual, jadi perpindahannya tidak terlihat; kalau dipindah
     sambil ter-zoom, gambarnya akan terlihat tergelincir. */

  /* 1 - layar HP di tengah  (Dibuka cepat) */
  0%     { transform: scale(1);    transform-origin: 48% 45%; }
  5%     { transform: scale(1.55); transform-origin: 48% 45%; }
  20%    { transform: scale(1.55); transform-origin: 48% 45%; }
  25%    { transform: scale(1);    transform-origin: 48% 45%; }

  25.5%  { transform: scale(1);    transform-origin: 78% 58%; }

  /* 2 - HP + laptop di kanan  (UI/UX responsif) */
  30%    { transform: scale(1.55); transform-origin: 78% 58%; }
  45%    { transform: scale(1.55); transform-origin: 78% 58%; }
  50%    { transform: scale(1);    transform-origin: 78% 58%; }

  50.5%  { transform: scale(1);    transform-origin: 46% 20%; }

  /* 3 - kolom pencarian di atas  (SEO, AEO & GEO) */
  55%    { transform: scale(1.6);  transform-origin: 46% 20%; }
  70%    { transform: scale(1.6);  transform-origin: 46% 20%; }
  75%    { transform: scale(1);    transform-origin: 46% 20%; }

  75.5%  { transform: scale(1);    transform-origin: 24% 42%; }

  /* 4 - perancangnya di kiri  (Sekali lihat, langsung paham) */
  80%    { transform: scale(1.55); transform-origin: 24% 42%; }
  95%    { transform: scale(1.55); transform-origin: 24% 42%; }
  100%   { transform: scale(1);    transform-origin: 24% 42%; }
}

/* Chip: potongan antarmuka kecil yang mengambang di atas ilustrasi. Ini
   markup sungguhan, bukan bagian gambar - jadi tetap tajam di layar berapa
   pun, ikut palet situs, dan tulisannya bisa diganti tanpa menyentuh PNG. */
.shot__pop {
  position: absolute;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: .8rem;
  padding: 1rem 1.5rem;
  border-radius: 999px;
  background: var(--base-tint);
  border: 1px solid var(--stroke);
  /* Bayangannya lebih dalam dari kartu biasa: itu yang bikin chip terbaca
     MELAYANG di atas ilustrasi, bukan menempel di permukaannya. */
  box-shadow: 0 1.6rem 3.6rem rgba(22, 22, 22, .13);
  font-weight: 600;
  font-size: 1.4rem;
  color: var(--t-bright);
  white-space: nowrap;

  /* Dua animasi: mengambang terus-menerus, DAN menyala waktu bagiannya yang
     sedang didekati gambarnya. Siklus menyalanya 15 detik, sama persis dengan
     heroTour, dan --i menggesernya 5 detik - jadi tiap chip menyala tepat
     saat kamera berhenti di bagiannya. */
  animation: shotFloat 7s ease-in-out infinite,
             shotCue 20s ease-in-out infinite;
  /* JEDA MENYALANYA: calc(--i * 5s - 15s), bukan --i * -5s.

     animation-delay negatif memajukan garis waktu, jadi -5s membuat chip
     kedua menyala di sepertiga TERAKHIR, bukan di tengah - urutannya jadi
     1, 3, 2. Dengan dikurangi satu siklus penuh dulu, jedanya jadi -15s,
     -10s, -5s: sama-sama negatif (tidak ada penantian di awal), tapi
     urutannya kembali 1, 2, 3. */
  animation-delay: calc(var(--i, 0) * -2.1s),
                   calc(var(--i, 0) * 5s - 20s);
}

/* Yang tidak sedang gilirannya mundur sedikit: lebih pucat dan sedikit
   mengecil. Bedanya halus - ini penanda, bukan lampu sorot. */
@keyframes shotCue {
  0%   { opacity: .55; scale: .94; }
  5%   { opacity: 1;   scale: 1.06; }
  20%  { opacity: 1;   scale: 1.06; }
  25%  { opacity: .55; scale: .94; }
  100% { opacity: .55; scale: .94; }
}

.shot__pop i { font-size: 1.8rem; color: var(--c); }

@keyframes shotFloat {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-.9rem); }
}

/* Ditaruh di TEPI, bukan di tengah: dua wajah di ilustrasi tidak boleh
   tertutup. Nilai negatif itu yang membuatnya melewati batas gambar. */
.shot__pop--a { top: 14%;    left: -3.5rem; }
.shot__pop--b { top: 46%;    right: -3rem; }
.shot__pop--c { bottom: -1.6rem; left: 12%; }
.shot__pop--d { top: -1.8rem; right: 6%; }

:root[data-motion="off"] .shot__pop { animation: none; transform: none; opacity: 1; scale: 1; }
:root[data-motion="off"] .shot__img { animation: none; transform: none; }

@media (max-width: 1199.98px) {
  /* Kolomnya menyempit - chip ditarik masuk supaya tidak keluar layar. */
  .shot__pop { font-size: 1.25rem; padding: .8rem 1.2rem; }
  .shot__pop--a { left: -1.5rem; }
  .shot__pop--b { right: -1rem; }
  .shot__pop--d { right: 2%; }
}

@media (max-width: 575.98px) {
  .shot__pop { font-size: 1.15rem; padding: .7rem 1.1rem; gap: .6rem; }
  .shot__pop i { font-size: 1.5rem; }
  /* Di layar sempit chip tetap menyembul, tapi hanya sedikit - kalau ditarik
     terlalu jauh dia memicu gulungan mendatar. */
  .shot__pop--a { top: 6%;  left: -.8rem; }
  .shot__pop--b { top: 44%; right: -.8rem; }
  .shot__pop--c { bottom: -1.2rem; left: 8%; }
  .shot__pop--d { top: -1.2rem; right: 0; }
}


/* --------------------------------------------------------------------------
   20. Tiga alur usaha (.pipe) — hero jasa-crm-system.html

   Tiga alur berdampingan, satu per jenis usaha, masing-masing dengan tahapan
   yang BERBEDA. Itu seluruh maksud gambarnya: alur toko online memang bukan
   alur klinik. Satu papan kanban saja cuma bilang "CRM"; tiga papan yang
   berbeda yang membuktikan "CRM yang disesuaikan".

   Tiap alur duduk di dalam bingkai berkepala jendela dengan nama usahanya -
   jadi terbaca sebagai TIGA DASBOR yang berbeda, bukan tiga tampilan dari
   satu produk yang sama.

   Menyala bergiliran memakai pola --i yang sama dengan diagram lain di situs
   ini, jadi terasa satu keluarga walau bentuknya baru.
   -------------------------------------------------------------------------- */
.pipe {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 2rem;
  max-width: 118rem;
  margin-inline: auto;
  align-items: start;
}

.pipe__board {
  border-radius: var(--radius-m);
  background: var(--base-tint);
  border: 1px solid var(--stroke);
  box-shadow: var(--shadow);
  overflow: hidden;

  /* Yang tidak sedang gilirannya mundur sedikit. Bedanya halus - ini penanda
     giliran, bukan lampu sorot. Yang dianimasikan cuma bingkainya; tulisan di
     dalamnya tidak pernah ikut transparan. */
  /* Empat papan, siklus 20 detik, giliran 5 detik masing-masing. Jedanya
     dikurangi satu siklus penuh supaya urutannya 1-2-3-4: animation-delay
     negatif memajukan garis waktu, jadi kalau ditulis --i * -5s urutannya
     jadi terbalik-balik. */
  animation: pipeCue 20s ease-in-out infinite;
  animation-delay: calc(var(--i, 0) * 5s - 20s);
}

@keyframes pipeCue {
  0%   { border-color: var(--c); box-shadow: 0 2rem 5rem rgba(22,22,22,.14); transform: translateY(-.5rem); }
  20%  { border-color: var(--c); box-shadow: 0 2rem 5rem rgba(22,22,22,.14); transform: translateY(-.5rem); }
  25%  { border-color: var(--stroke); box-shadow: var(--shadow); transform: translateY(0); }
  100% { border-color: var(--stroke); box-shadow: var(--shadow); transform: translateY(0); }
}

/* Kepala jendela: titik-titik kecil plus nama usahanya. Ini yang bikin tiap
   alur terbaca sebagai dasbor tersendiri. */
.pipe__bar {
  display: flex;
  align-items: center;
  gap: .9rem;
  padding: 1.5rem 1.8rem;
  background: color-mix(in srgb, var(--c) 12%, var(--base-tint));
  border-bottom: 1px solid var(--stroke);
}

.pipe__dots { display: flex; gap: .4rem; flex: none; }
.pipe__dots span { width: .7rem; height: .7rem; border-radius: 50%; background: var(--c); opacity: .45; }

.pipe__name {
  font-size: 1.45rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--c);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Catatan kecil di bawah diagram. Ada karena diagramnya menampilkan empat
   kartu, dan empat kartu gampang terbaca sebagai klaim "CRM itu ada empat
   jenis" - klaim yang bisa dibantah orang, dan membelanya tidak menjual apa
   pun. Baris ini menyatakan sendiri bahwa keempatnya pola yang sering ditemui,
   bukan daftar baku. */
.pipe__note {
  max-width: 62rem;
  margin: 3.2rem auto 0;
  text-align: center;
  font-size: 1.35rem;
  line-height: 1.6;
  color: var(--t-muted);
}

/* Satu baris fokus di bawah nama arketipenya. Namanya saja - "Transaction
   CRM" - belum menjelaskan apa-apa buat orang yang belum kenal istilahnya;
   baris inilah yang membedakan keempatnya sejak pandangan pertama. */
.pipe__focus {
  padding: 1.2rem 1.6rem 0;
  margin: 0;
  font-size: 1.25rem;
  line-height: 1.45;
  color: var(--t-medium);
}

/* Dari mana orangnya masuk. Sengaja BERBEDA-BEDA jumlah dan isinya per usaha:
   toko online biasanya punya banyak corong sekaligus (WhatsApp, marketplace,
   Instagram), sementara klinik cuma dua. Perbedaan inilah buktinya bahwa
   alurnya memang tidak bisa disamakan - bukan cuma namanya yang diganti. */
.pipe__src {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem .6rem;
  padding: 1.2rem 1.6rem 0;
  font-size: 1.2rem;
  color: var(--t-muted);
}

.pipe__src span {
  padding: .35rem .8rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--c) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--c) 22%, transparent);
  white-space: nowrap;
}

.pipe__stages { list-style: none; margin: 0; padding: 1.6rem; display: grid; gap: .9rem; }

.pipe__stages li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.2rem 1.4rem;
  border-radius: 1.1rem;
  background: color-mix(in srgb, var(--c) 7%, var(--base-tint));
  border: 1px solid color-mix(in srgb, var(--c) 18%, transparent);
  font-size: 1.5rem;
  font-weight: 600;
  color: var(--t-bright);
}

/* Angka kecil di kanan tiap tahap: ini yang menyampaikan "semuanya tercatat".
   Angkanya CONTOH, bukan hasil klien - jangan pernah ditulis seolah data asli. */
.pipe__n {
  flex: none;
  min-width: 2.8rem;
  text-align: center;
  padding: .3rem .7rem;
  border-radius: 999px;
  background: var(--base);
  border: 1px solid var(--stroke);
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--t-muted);
}

:root[data-motion="off"] .pipe__board { animation: none; transform: none; }

/* EMPAT kolom, atau SATU. Tidak ada tahap dua kolom di antaranya.

   Dua-duanya sempat dicoba dan dua-duanya gagal karena alasan yang sama:
   tiap papan memuat nama, empat sumber masuk, dan empat tahap berangka -
   dijejalkan berdua sejajar, isinya terlalu padat untuk dibaca sekilas, dan
   diagram yang tidak terbaca tidak membandingkan apa pun.

   Jadi: berjejer empat kalau memang ada ruangnya, kalau tidak ditumpuk penuh
   satu per satu dengan ukuran yang nyaman. Perbandingannya didapat dengan
   menggulir, bukan dengan mengecilkan tulisan. */
@media (max-width: 1199.98px) {
  .pipe { grid-template-columns: 1fr; gap: 1.4rem; max-width: 48rem; }
  .pipe__bar { padding: 1.3rem 1.5rem; }
  .pipe__focus { padding: 1rem 1.4rem 0; font-size: 1.2rem; }
  .pipe__src { padding: .9rem 1.4rem 0; }
  .pipe__stages { padding: 1.4rem; }
}
