/* ============================================================================
   JANGAN edit di produk — sumber kebenaran: wtb-design-system/css/patterns.css
   ============================================================================
   WTB CINEMATIC SIGNATURE — POLA REUSABLE
   PT Widigital Tri Buana · patterns teruji dari widigitaltribuana.co.id
   ============================================================================

   PRASYARAT: import wtb-cinematic.css SEBELUM file ini.
     @import './wtb-cinematic.css';
     @import './patterns.css';

   Semua nilai merujuk token dari wtb-cinematic.css — tidak ada hardcoded color.
   Framework-agnostic: Astro / Next.js / SvelteKit / Vite / plain HTML semua kompatibel.
   ============================================================================ */


/* ============================================================================
   BASE RESET — opsional, bisa diganti reset framework masing-masing.
   WAJIB di dalam @layer base: rule tanpa layer selalu menang atas rule
   ber-layer, jadi reset unlayered mematikan SEMUA utility margin/padding
   framework yang hidup di @layer utilities (Tailwind v4) — insiden klinova.id
   25 Agu 2026: mx-auto/px-6 mati se-situs, konten mepet tepi viewport.
   Di situs tanpa framework perilaku tak berubah (selector * memang paling
   lemah; @layer didukung semua browser sejak 2022).
   ============================================================================ */

@layer base {
  *, *::before, *::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
  }
}

html {
  scroll-behavior: smooth;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

body {
  font-family: var(--ff-body);
  background: var(--background);
  color: var(--foreground);
  font-size: 16px;
  font-weight: 400;
  line-height: 1.65;
  overflow-x: hidden;
}

/* Grain overlay subtle — noise film 2% menambah persepsi premium di monitor 8-bit */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 1000;
  pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='1'/%3E%3C/svg%3E");
  opacity: 0.02;
  background-size: 128px 128px;
}

a      { color: inherit; text-decoration: none; }
img, svg { display: block; max-width: 100%; }
button { cursor: pointer; border: none; background: none; font-family: inherit; }


/* ============================================================================
   FOCUS RING — AA accessible
   ============================================================================ */

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

:focus:not(:focus-visible) { outline: none; }


/* ============================================================================
   SKIP LINK
   ============================================================================ */

.skip-link {
  position: absolute;
  top: -100%;
  left: var(--space-4);
  background: var(--foreground);
  color: var(--background);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-md);
  font-family: var(--ff-body);
  font-size: var(--text-sm);
  font-weight: 500;
  z-index: 9999;
  transition: top var(--duration-normal) var(--ease-out);
}

.skip-link:focus { top: var(--space-4); }


/* ============================================================================
   CONTAINER / SHELL
   ============================================================================ */

.container,
.wtb-shell {
  width: 100%;
  max-width: 1200px;
  margin-inline: auto;
  padding-inline: var(--space-6);
}

@media (min-width: 768px) {
  .container, .wtb-shell { padding-inline: var(--space-8);  }
}
@media (min-width: 1024px) {
  .container, .wtb-shell { padding-inline: var(--space-12); }
}
@media (min-width: 1440px) {
  .container, .wtb-shell { padding-inline: var(--space-16); }
}


/* ============================================================================
   TIPOGRAFI HEADING — Schibsted Grotesk 700 + Newsreader italic aksen
   ============================================================================ */

h1, h2, h3, h4 {
  font-family: var(--ff-display);
  color: var(--foreground);
  line-height: 1.08;
  margin: 0;
  font-weight: 700;
  letter-spacing: -0.025em;
}

h1, .text-display {
  font-size: var(--text-display);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.04;
}

h2 {
  font-size: var(--text-h2);
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.1;
}

h3 {
  font-size: var(--text-h3);
  font-weight: 600;
  letter-spacing: -0.015em;
  line-height: 1.2;
}

/* Aksen italic dalam heading = Newsreader serif (pasangan Tome master)
   Contoh: <h2>Bespoke <em>Agentic AI</em> Systems</h2> */
h1 em, h2 em, h3 em, h4 em, .text-display em {
  font-family: var(--ff-serif);
  font-weight: 400;
  font-style: italic;
  letter-spacing: -0.01em;
}

p {
  font-family: var(--ff-body);
  font-weight: 400;
  line-height: 1.7;
  margin: 0;
  font-size: 15px;
  color: var(--muted-foreground);
  /* IKUT parent (inherit), BUKAN justify global. Justify global bikin spasi
     antar-kata melar tak rata ("sungai" putih) pada teks pendek/kolom sempit/
     list ber-· → berantakan. Dengan inherit: parent `text-center` → tengah,
     default → kiri, utility text-* tetap dihormati. Editorial panjang yang
     butuh justify: pakai opt-in .text-justify di bawah. */
  text-align: inherit;
}

/* Opt-in justify — HANYA untuk blok editorial panjang & lebar (bukan UI/kartu). */
.text-justify {
  text-align: justify;
  text-align-last: left;
  -webkit-hyphens: auto;
  hyphens: auto;
}


/* ============================================================================
   EYEBROW / BADGE / LABEL — JetBrains Mono, uppercase, spasi lebar
   ============================================================================ */

/* Kontrak label master: SEMUA eyebrow/label kecil uppercase = JetBrains Mono */
.eyebrow {
  font-family: var(--ff-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--brand);
}

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 3px var(--space-3);
  background: rgba(123, 63, 160, 0.08);
  color: var(--brand);
  border-radius: var(--radius-full);
  font-family: var(--ff-mono);
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* Eyebrow dengan garis aksen di kiri */
.eyebrow-line {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--ff-body);
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--brand);
}

.eyebrow-line::before {
  content: '';
  width: 24px;
  height: 1px;
  background: var(--brand);
  flex-shrink: 0;
}

/* Aplikasikan mono ke semua varian eyebrow dan label struktural */
[class*="eyebrow"], .badge {
  font-family: var(--ff-mono) !important;
  letter-spacing: 0.12em;
}


/* ============================================================================
   AURORA LINE — garis aksen signature 3px (DNA logo sebagai aksen presisi)
   Contoh: <div class="aurora-line"></div> sebagai topline seksi
   ============================================================================ */

.aurora-line {
  height: 3px;
  border-radius: 2px;
  background: var(--sig-gradient);
}


/* ============================================================================
   AURORA HERO — latar aurora TER-RENDER untuk hero TERANG
   ============================================================================
   Latar menggunakan plate webp ter-render (anti-banding di monitor 8-bit)
   sebagai ganti gradasi CSS radial yang banding.

   ASET OVERRIDABLE — default resolve ke /images/aurora/aurora-porcelain.webp
   (jalur ABSOLUT dari root situs) lewat token --aurora-image.

   KENAPA ABSOLUT, BUKAN RELATIF (perbaikan 1.3.0, insiden produksi Studio 31 Agu):
   jalur relatif dihitung dari LOKASI AKHIR berkas CSS, bukan dari repo. Begitu
   bundler (Vite/webpack/Next) memindahkan CSS ke /_app/immutable/assets/,
   '../assets/aurora/' menunjuk folder yang tidak pernah ada -> 404 SENYAP: tidak
   ada galat build, halaman tetap jalan, hero cuma kehilangan plate auroranya dan
   jatuh ke background datar. Jalur absolut tidak ikut berpindah, jadi tahan bundler.
   KONSEKUENSINYA: konsumen WAJIB menyalin assets/aurora/ ke folder publiknya
   sebagai /images/aurora/ (lihat ADOPTION.md), atau menimpa --aurora-image.
   Proyek konsumen TIDAK perlu edit CSS ini — cukup timpa token --aurora-image di scope
   root mereka sendiri (path beda per stack: Next.js/Vite public/ vs Astro public/ vs
   custom CDN) — contoh kode lengkap ada di ADOPTION.md § "Override aset Aurora", BUKAN
   di sini (dijaga sengaja supaya berkas ini nol contoh literal ":root { ... }" yang
   bisa kebaca keliru sebagai deklarasi asli oleh parser token berbasis regex).
   Tanpa override, fallback var(--aurora-image, url(...)) di bawah yang dipakai.

   Intensitas per-halaman: set --aurora-opacity (default 0.85).
   Foolproof: plate dipasang di ::before z-index:-1 + isolation:isolate,
   jadi KONTEN mengalir di atasnya otomatis TANPA perlu z-index manual.
   ============================================================================ */

.aurora-hero {
  position: relative;
  isolation: isolate;
  background-color: var(--background);
}

.aurora-hero::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    linear-gradient(90deg, var(--background) 0%, rgba(244,242,247,0) 34%),
    linear-gradient(0deg, var(--background) 0%, rgba(244,242,247,0) 22%),
    var(--aurora-image, url('/images/aurora/aurora-porcelain.webp')) right top / cover no-repeat var(--background);
  opacity: var(--aurora-opacity, 0.85);
}

@media (max-width: 768px) {
  .aurora-hero::before {
    opacity: 0.5;
    background-position: 82% 0;
  }
}






/* ── BLOK KEPERCAYAAN & KEPATUHAN (1.11.0) ──────────────────────────────────
   Bukti kepatuhan yang BISA DIPERIKSA, bukan lencana hiasan (ketok Bos 2 Sep).
   Setiap butir = PERNYATAAN + TAUTAN BUKTI. Aturan isi (ditegakkan gerbang
   cek_blok_kepercayaan.py di repo lini desain):
   - "Terdaftar PSE Komdigi" + nomor TD + tautan entri registri pse.komdigi.go.id.
     DILARANG kata "tersertifikasi/diaudit/diverifikasi pemerintah".
   - Nomor TD belum terbit -> butir PSE TIDAK dirender. Tanpa placeholder,
     tanpa "segera hadir" — yang tidak bisa dibuktikan tidak ditampilkan.
   - Blok ini BUKAN hero: porsi kecil dan tenang, tipografi 13px, tinta muted;
     kepercayaan dibangun oleh bukti yang bisa diklik, bukan ukuran huruf.

   VARIAN A — baris footer (semua situs):
     <div class="blok-percaya">
       <span class="blok-percaya__butir">Terdaftar PSE Komdigi
         <a href="https://pse.komdigi.go.id/tdpse">TDPSE 029496.01/DJAI.PSE/09/2026</a></span>
       <span class="blok-percaya__butir">Kebijakan Privasi sesuai UU PDP 27/2022
         <a href="/privacy">selengkapnya</a></span>
     </div>
   VARIAN B — bagian halaman (landing Klinova, harga/checkout Studio):
     <section class="blok-percaya blok-percaya--bagian">
       <p class="eyebrow">kepatuhan</p>
       (butir-butir yang sama, satu kolom, tiap butir baris sendiri)
     </section> */
.blok-percaya {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 24px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--muted-foreground);
}
.blok-percaya__butir { display: inline-flex; flex-wrap: wrap; gap: 6px; align-items: baseline; }
.blok-percaya__butir a {
  color: var(--brand);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
  /* sasaran sentuh: tautan kecil diberi padding tak terlihat */
  padding: 8px 2px;
  margin: -8px -2px;
}
.blok-percaya--bagian {
  display: block;
  max-width: 640px;
  padding: 24px 0;
}
.blok-percaya--bagian .blok-percaya__butir {
  display: flex;
  padding: 10px 0;
  border-top: 1px solid var(--border);
}
.blok-percaya--bagian .blok-percaya__butir:first-of-type { border-top: none; }

/* ── TOGGLE TEMA LEPAS — layar tanpa bilah kepala (1.10.0) ───────────────────
   Layar berkartu-terpusat (auth, wizard) tidak punya bilah kepala tempat
   .theme-toggle duduk — dan tanpa tombol, pengunjung belum-masuk tak punya jalan
   ke Deep Space. Penempatan kanonik: SUDUT KANAN ATAS VIEWPORT, fixed.
   Tidak menabrak kartu: .auth-util__kartu maks 400/480px terpusat, sedangkan
   tombol 44px menempel tepi — pada 390px pun sisa ruang kanan kartu >= 0 karena
   tombol duduk DI ATAS zona padding halaman (kartu mulai di bawah y tombol).
   Markup: pasang .theme-toggle--lepas BERSAMA .theme-toggle yang sudah ada. */
.theme-toggle--lepas {
  position: fixed;
  top: 16px;
  right: 16px;
  z-index: 60;
}
@media (max-width: 480px) {
  .theme-toggle--lepas { top: 12px; right: 12px; }
}

/* ── LOGO DUA REGISTER (1.10.0) ──────────────────────────────────────────────
   Aset lockup ber-plat (deepspace/porcelain) hanya sah di register yang sama
   dengan platnya — di register lain ia jadi bercak (terukur: lockup deepspace
   26px di bilah terang app.tangkasai). Dua bentuk sah di halaman dua-register:

   A. MARK + WORDMARK HTML (dianjurkan untuk bilah kepala / layar auth):
      <span class="logo-produk"><img src=".../mark-64.png" alt="">Nama</span>
      Mark 1:1 ber-plat kecil sah di dua register (preseden tangkasai.id 27 Agu);
      wordmark sebagai TEKS bertoken ikut register otomatis. Nol aset baru.

   B. SWAP DUA GAMBAR (bila lockup penuh memang dibutuhkan):
      <span class="logo-register">
        <img class="logo-register__terang" src="...-porcelain.png" alt="...">
        <img class="logo-register__gelap"  src="...-deepspace.png" alt="" aria-hidden="true">
      </span>
      Mekanisme persis .theme-toggle__icon. DILARANG merakit swap sendiri per repo.
      Catatan jujur: pack lockup WTB baru punya varian deepspace — sebelum varian
      porcelain diterbitkan di sumber, pakai bentuk A. */
.logo-produk {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--ff-display);
  font-weight: 700;
  color: var(--foreground);
}
.logo-produk img { display: block; border-radius: 8px; }
.logo-register { display: inline-block; }
.logo-register__gelap { display: none; }
html.dark .logo-register__terang, .dark .logo-register__terang { display: none; }
html.dark .logo-register__gelap,  .dark .logo-register__gelap  { display: inline; }

/* ── EMPTY-STATE — pola kanonik (1.8.0) ──────────────────────────────────────
   Keadaan kosong = AJAKAN PERTAMA, bukan pernyataan kekosongan. Ini layar pertama
   yang dilihat pelanggan baru begitu mendarat di dasbor — kesan "aplikasinya kosong"
   vs "aku tahu mulai dari mana" ditentukan di sini.

   MARKUP KANONIK:
     <div class="empty-state card card--solid">
       <span class="empty-state__ikon">(svg 24px kelas konten: dokumen/kalender/gambar)</span>
       <h2>Bikin konten pertamamu</h2>
       <p>Satu kalimat MANFAAT — apa yang terjadi setelah aksi, bukan apa yang tidak ada.</p>
       <a class="btn btn--primary">Buat sekarang</a>
       <a class="empty-state__sekunder">Lihat contoh dulu</a>
     </div>

   ATURAN KERAS:
   - JUDUL = ajakan/aksi ("Bikin konten pertamamu"), DILARANG kata keadaan:
     "kosong", "belum ada", "tidak ada data", "no data". Kekosongan sudah terlihat;
     menuliskannya menambah rasa gagal, bukan informasi.
   - SATU aksi utama .btn--primary. Sekunder = tautan teks. Dua tombol setara = ragu.
   - Ikon kelas KONTEN yang akan lahir (dokumen/kalender), BUKAN ikon kosong
     (kotak silang, folder kosong, awan sedih).
   - Wadah tabel/daftar TIDAK dirender saat kosong — jangan header kolom tanpa baris.
   - Dasbor pasca-onboarding Studio: bila wizard menghasilkan konten (momen L4),
     dasbor TIDAK PERNAH memakai empty-state — tampilkan kartu "konten pertamamu".
     Empty-state murni hanya untuk jalur lewati-semua. */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 12px;
  padding: 48px 32px;
  max-width: 480px;
  margin-inline: auto;
}
.empty-state__ikon {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--brand-100);
  color: var(--brand);
}
.empty-state__ikon svg { width: 24px; height: 24px; }
.empty-state h2 {
  font-family: var(--ff-display);
  font-size: 20px;
  font-weight: 700;
  color: var(--foreground);
  margin: 0;
}
.empty-state p {
  font-size: 15px;
  line-height: 1.6;
  color: var(--muted-foreground);
  margin: 0;
  max-width: 40ch;
}
.empty-state .btn { margin-top: 8px; }
.empty-state__sekunder {
  font-size: 14px;
  color: var(--brand);
  font-weight: 600;
}

/* ── LAYAR AUTH UTILITAS — pola kanonik (1.6.0) ───────────────────────────────
   Satu cetakan untuk verifikasi email, lupa/reset sandi, tautan kedaluwarsa, dan
   keadaan galat — di SEMUA produk. Patokan hidup: studiowtb.id/auth/verify-email.
   Lahir dari temuan mata Bos 1 Sep: sebaran lintas produk tanpa acuan menghasilkan
   H1 raksasa tiga baris memenuhi layar 1440, teks tanpa kartu, tombol kotak polos.

   MARKUP KANONIK:
     <main class="auth-util">
       <header class="auth-util__merek">(mark produk 48px + wordmark)</header>
       <section class="auth-util__kartu card card--solid">
         <span class="auth-util__ikon">(svg status 24px)</span>
         <h1>Cek email Anda</h1>
         <p>...penjelasan...</p>
         <button class="btn btn--ghost auth-util__aksi">Kirim ulang email</button>
         <p class="auth-util__sekunder">Salah alamat? <a>Daftar ulang</a></p>
       </section>
     </main>

   ATURAN KERAS:
   - Isi SELALU di dalam kartu; teks telanjang selebar viewport = cacat.
   - H1 TIDAK ikut membesar di layar lebar: clamp di bawah mengunci 24-30px.
     Skala display (var --text-display) HANYA untuk halaman marketing, bukan utilitas.
   - Satu aksi utama; selebihnya tautan teks --brand. Tombol wajib bentuk .btn
     (pill/radius token), bukan kotak border polos.
   - Ikon status di lingkaran tint --brand-100; JANGAN emotikon sebagai ikon status.
   - VARIAN VERIFIKASI-EMAIL (pola pintu masuk, 1.10.0): wajib menampilkan ALAMAT
     email tujuan; tombol kirim-ulang BER-JEDA (hitung mundur tampil); petunjuk folder
     Spam/Promosi; jalan keluar bila salah alamat ("Daftar ulang"). NOL tautan billing
     atau paket di layar ini. Baris HARGA pada form daftar: di BAWAH tombol daftar,
     teks kecil --muted-foreground — harga tidak pernah jadi kalimat pertama form.
     Rujukan hidup: Studio /auth/verify-email + /auth/signup.
   - JUDUL: kalimat biasa Bahasa Indonesia ("Cek email Anda", "Atur kata sandi baru",
     "Kata sandi berhasil diubah"). DILARANG Huruf Besar Tiap Kata dan kata Inggris
     ("Password" -> "kata sandi"). Tombol memakai bentuk .btn DS apa adanya
     (uppercase 13px tracking itu kontrak .btn, bukan cacat). */
.auth-util {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 32px;
  padding: 48px 24px;
  background: var(--background);
}
.auth-util__merek {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  font-family: var(--ff-display);
  font-weight: 700;
  font-size: 22px;
  color: var(--foreground);
}
.auth-util__kartu {
  width: 100%;
  max-width: 400px;          /* kolom utilitas: 400px, titik. Bukan lebar viewport. */
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.auth-util__kartu h1 {
  font-family: var(--ff-display);
  font-size: clamp(24px, 2.6vw, 30px);   /* tidak meledak di 1440 */
  font-weight: 700;
  line-height: 1.2;
  color: var(--foreground);
  margin: 0;
}
.auth-util__kartu p {
  font-size: 15px;
  line-height: 1.65;
  color: var(--muted-foreground);
  margin: 0;
}
.auth-util__ikon {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--brand-100);
  color: var(--brand);
}
.auth-util__ikon svg { width: 24px; height: 24px; }
.auth-util__aksi { width: 100%; margin-top: 8px; }
.auth-util__sekunder {
  text-align: center;
  font-size: 14px;
}
.auth-util__sekunder a {
  color: var(--brand);
  font-weight: 600;
}

/* ── INPUT SANDI + TOMBOL MATA — pola kanonik (1.5.0) ─────────────────────────
   Lahir dari temuan mata Bos 1 Sep: sapuan pintu-masuk lintas produk menempatkan
   ikon mata MENGGANTUNG di luar/bawah field. Pola ini yang disalin, bukan dikarang.

   MARKUP KANONIK:
     <div class="input-sandi">
       <input type="password" id="sandi" autocomplete="current-password">
       <button type="button" class="input-sandi__mata"
               aria-label="Tampilkan sandi" aria-pressed="false">(svg mata)</button>
     </div>

   PERILAKU (JS milik konsumen, kontraknya di sini):
     klik  -> input.type bergantian password/text; aria-pressed & aria-label ikut;
              ikon berganti mata/mata-dicoret; fokus TETAP di tombol (jangan curi
              fokus input); JANGAN reset nilai input.
     type="button" WAJIB — tanpa itu tombol men-submit form.

   ATURAN POSISI: tombol DI DALAM kotak input, tepi kanan, terpusat vertikal.
   Sasaran sentuh minimal 44x44 px (WCAG 2.5.8 level tinggi; 24 minimum mutlak).
   Tinta ikon: var(--muted-foreground) -> var(--foreground) saat hover/fokus —
   dua register aman karena token. */
.input-sandi {
  position: relative;
  display: block;
}
.input-sandi > input {
  width: 100%;
  padding-right: 52px;   /* ruang tombol 44px + jarak 8px — jangan dikecilkan */
}
.input-sandi__mata {
  position: absolute;
  top: 50%;
  right: 4px;
  transform: translateY(-50%);
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  border-radius: var(--radius-md);
  color: var(--muted-foreground);
  cursor: pointer;
}
.input-sandi__mata:hover { color: var(--foreground); }
.input-sandi__mata:focus-visible {
  outline: 2px solid var(--ring, var(--brand));
  outline-offset: 2px;
  color: var(--foreground);
}
.input-sandi__mata svg { width: 20px; height: 20px; }

/* ── ON-AURORA — alas untuk teks kecil yang duduk DI ATAS plate .aurora-hero ──
   Plate aurora bukan warna, melainkan FOTO: nilainya berjalan dari periwinkle pucat
   sampai ungu pekat dalam satu bidang. Teks yang kebetulan mendarat di bagian pekat
   kehilangan kontras, dan tidak ada satu warna teks pun yang aman di seluruh bidang —
   karena itu warna teks saja TIDAK bisa menyelesaikannya, dan text-shadow hanya
   mengaburkan masalah tanpa menjamin rasio.

   Insiden yang melahirkan kelas ini (31 Agu 2026): begitu plate aurora dipulihkan dari
   404, dua teks kecil yang selama ini duduk di atas Porcelain polos mendarat di ungu
   pekat — tautan di bawah mockup hero .co.id jatuh ke 2,99:1 dan label kartu klinova.id
   ke 4,30:1. Keduanya LOLOS sebelum plate ada. Memperbaiki aset memunculkan cacat yang
   selama ini disembunyikan oleh aset yang hilang.

   Pakai pada teks kecil (di bawah ~16 px) yang harus berada di dalam bidang aurora.
   Untuk judul besar tidak perlu: ukurannya sendiri sudah memberi keterbacaan.
   Markup: <p class="on-aurora">...</p> atau <span class="on-aurora">...</span>
*/
.on-aurora {
  /* Tinta tautan DI DALAM alas (1.4.1). Alas mengangkat latar, tapi anak yang menimpa
     warna induk — tautan, tanda, em berwarna — tetap memakai tintanya sendiri. Tautan
     merek #7b3fa0 di atas alas terukur 5,74:1: LOLOS AA, tetapi dua penilai independen
     membacanya lemah bersebelahan dengan teks arang di paragraf yang sama. Ambang AA
     itu lantai, bukan sasaran; di dalam alas kita naikkan satu anak tangga ramp.
     Terang 8,32:1 (dari 6,17), gelap 9,76:1 (dari 7,73), identitas merek tetap ungu. */
  --on-aurora-link: var(--brand-600);
  background: color-mix(in srgb, var(--background) 90%, transparent);
  border-radius: var(--radius-md);
  padding: 0.15em 0.45em;
  /* inline-block supaya alas mengikuti baris teks, bukan melebar ke seluruh kolom */
  display: inline-block;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}

html.dark .on-aurora,
.dark .on-aurora { --on-aurora-link: var(--brand-300); }

.on-aurora a { color: var(--on-aurora-link); }

/* Di register gelap plate tidak dirender (lihat aturan di bawah), tapi kelas ini tetap
   sah dipakai: alasnya ikut token, jadi ia menjadi Deep Space, bukan porcelain terang. */

/* Register GELAP (html.dark): aurora-porcelain.webp = aset register TERANG —
   JANGAN dirender saat gelap; hero jatuh ke background-color token (ikut .dark).
   Dulu hanya guidance ADOPTION.md + preview.html (M145); dibakukan ke pattern
   ini pada JOB DEWAN kontras 25 Agu 2026 — insiden .co.id: plate terang tetap
   render di mode gelap sehingga teks hero (foreground terang) tenggelam. */
html.dark .aurora-hero::before { content: none; }

/* LEGIBILITAS teks di atas zona aurora — PENTING:
   Teks (terutama caption/label muted) yang duduk di area aurora-dominan
   (mis. sisi kanan hero) kontrasnya rendah jika pakai --muted-foreground.
   Gunakan .on-aurora: warna gelap + halo putih lembut mengangkat teks dari
   latar aurora yang ramai. Untuk body panjang di zona aurora, pakai
   --foreground (bukan --muted-foreground). */
.on-aurora,
.aurora-hero .caption,
.aurora-hero figcaption {
  color: var(--foreground);
  text-shadow: 0 0 10px rgba(244, 242, 247, 0.9), 0 1px 2px rgba(244, 242, 247, 0.75);
}


/* ============================================================================
   DEEP WASH — wash aurora soft-light di atas orb (elemen anak dalam .deep-field)
   Taruh sebagai div anak langsung dalam seksi gelap:
   <section class="deep-field">
     <div class="deep-wash"></div>
     <div class="container">…konten…</div>
   </section>
   ============================================================================ */

.deep-wash {
  position: absolute;
  inset: 0;
  background: var(--sig-gradient);
  mix-blend-mode: soft-light;
  opacity: 0.3;
  pointer-events: none;
}


/* ============================================================================
   DEEP FIELD — SEKSI DEEP SPACE PREMIUM reusable (resep 5-lapis)
   ============================================================================
   Cukup pasang kelas .deep-field ke elemen seksi gelap:
     base gradasi + vignette (background)
     + orb aurora (::before, z-index:-1)
     + wash soft-light (::after, z-index:-1)
   Semua self-contained — konten mengalir di atas tanpa perlu z-index manual.

   Tambah <div class="aurora-line"> di awal seksi untuk garis pemisah 3px.
   Varian .deep-field--alt = luminance lebih terang (tangga antar seksi gelap).

   WAJIB: elemen .deep-field harus punya position (sudah di-set),
   dan isolation:isolate agar ::before/::after tidak escape ke parent.
   ============================================================================ */

.deep-field {
  position: relative;
  isolation: isolate;
  color: var(--deepspace-foreground);
  background:
    radial-gradient(120% 80% at 50% 0%, transparent 55%, rgba(0,0,0,0.45)),
    linear-gradient(180deg, #141130 0%, #0d0b1f 60%);
  /* glass tokens — re-deklarasi agar kartu/tombol di .deep-field pakai nilai gelap */
  --glass-bg:     rgba(20,16,35,0.78);
  --glass-border: rgba(255,255,255,0.16);
  --glass-blur:   14px;

  /* KONTRAK SHADCN — re-deklarasi lokal (JOB DEWAN-M146, 24 Agu 2026, dual-mode toggle).
     .deep-field WAJIB jadi pulau gelap mandiri (self-contained) TERLEPAS dari register
     halaman (:root Porcelain terang vs .dark Deep Space) — sebelum toggle dua-mode ada,
     ini otomatis benar karena seluruh halaman selalu .dark. Sejak halaman terang bisa jadi
     default (M145), komponen di dalam .deep-field yang pakai var(--foreground) dkk langsung
     (mis. aturan dasar h1-h4{color:var(--foreground)} di wtb-cinematic.css, atau utility
     text-foreground/text-muted-foreground/border-border/bg-card) ikut kebaca warna TERANG
     dari :root — teks jadi nyaris tak kelihatan di atas plat gelap. Nilai di bawah IDENTIK
     dengan blok .dark di wtb-cinematic.css (nol warna baru) — cuma discope ulang supaya
     berlaku lokal tanpa butuh <html class="dark">. JANGAN ubah nilainya di sini — kalau
     .dark berubah, sinkronkan manual (belum ada mekanisme @property share otomatis). */
  --background:             #0d0b1f;
  --foreground:             #edeaf5;
  --card:                   #221c34;
  --card-foreground:        #edeaf5;
  --popover:                #16121e;
  --popover-foreground:     #edeaf5;
  --primary:                #7b2fd6;
  --primary-foreground:     #ffffff;
  --secondary:              #1c1729;
  --secondary-foreground:   #d9d3e8;
  --muted:                  #17131f;
  --muted-foreground:       #9b92b5;
  --accent:                 #241a33;
  --accent-foreground:      #d6c4ea;
  --destructive:            #e5654f;
  --destructive-foreground: #1c0b08;
  --border:                 rgba(237, 234, 245, 0.13);
  --input:                  rgba(237, 234, 245, 0.18);
  --ring:                   #7b2fd6;

  /* EKSTENSI WTB — ikut re-deklarasi (nilai MIRROR blok .dark; keputusan lini desain 28 Agu 2026).
     Tanpa ini, .eyebrow/text-brand di pulau gelap memakai --brand register TERANG (#7b3fa0)
     = 1,9-2,8:1 di plat gelap (temuan Asisten di /pricing Studio, produksi).
     CATATAN: --brand-navy dan --cta BUKAN warna TEKS di latar gelap (3,6-4,2 / 2,5-3,0) —
     hanya aksen/garis/latar tombol; teks di atas --cta putih (±5,7:1). */
  --brand:           #bd92e3;
  --brand-secondary: #8ea5d3;
  --brand-navy:      #5a74b0;
  --cta:             #7b2fd6;
  --cta-hover:       #9a5cf0;

  /* STATUS — mirror blok .dark verbatim (1.1.3): tanpa ini centang fitur/status
     di pulau gelap memakai nilai register terang (terukur 2,6:1 di /pricing). */
  --success:            #34c776;
  --success-foreground: #061a0e;
  --warning:            #f5a623;
  --warning-foreground: #2a1500;
  --info:               #8ea5d3;
  --info-foreground:    #0d1a2e;
}

/* Lapisan orb aurora — 3 radial ellipse bertumpuk (z-index:-1 agar di bawah konten) */
.deep-field::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(ellipse 64% 52% at 12% 42%, rgba(107,91,196,0.30)  0%, transparent 62%),
    radial-gradient(ellipse 60% 56% at 88% 14%, rgba(142,165,211,0.22) 0%, transparent 58%),
    radial-gradient(ellipse 52% 46% at 60% 92%, rgba(123,63,160,0.18)  0%, transparent 56%);
}

/* Lapisan wash soft-light — sig-gradient di-blend soft-light */
.deep-field::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: var(--sig-gradient);
  mix-blend-mode: soft-light;
  opacity: 0.30;
}

/* Varian --alt: luminance lebih terang, untuk tangga antar seksi gelap */
.deep-field--alt {
  background:
    radial-gradient(120% 80% at 50% 0%, transparent 55%, rgba(0,0,0,0.45)),
    linear-gradient(180deg, #171331 0%, #12102a 55%);
}

.deep-field--alt::before {
  background:
    radial-gradient(ellipse 62% 58% at 84% 30%, rgba(123,63,160,0.30)  0%, transparent 60%),
    radial-gradient(ellipse 56% 48% at 8%  78%, rgba(142,165,211,0.22) 0%, transparent 56%),
    radial-gradient(ellipse 46% 42% at 32% 6%,  rgba(123,47,214,0.18)  0%, transparent 54%);
}


/* ============================================================================
   BUTTONS — pil CTA premium
   ============================================================================ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: 0.8rem 1.65rem;
  min-height: 44px;
  border-radius: var(--radius-pill);
  font-family: var(--ff-body);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  cursor: pointer;
  transition:
    background-color var(--duration-normal) var(--ease-hover),
    transform        var(--duration-fast)   var(--ease-hover),
    box-shadow       var(--duration-normal) var(--ease-hover),
    border-color     var(--duration-normal) var(--ease-hover);
  border: 1px solid transparent;
  text-decoration: none;
  white-space: nowrap;
}

/* ── Solid escape hatch — nilai lama .btn--primary sebelum glass.
   Pakai untuk form, email, UI datar yang backdrop-filter tidak cocok. */
.btn--solid {
  background: var(--cta);
  color: var(--cta-foreground);
  border-color: var(--cta);
  box-shadow: 0 4px 16px rgba(123,63,160,0.24);
}
.btn--solid:hover {
  background: var(--cta-hover);
  border-color: var(--cta-hover);
  transform: translateY(-2px);
  box-shadow: 0 8px 28px rgba(123,63,160,0.32);
}
.btn--solid:active { transform: translateY(0); box-shadow: none; }
/* ── Outline — tombol SEKUNDER bergaris brand (1.14.0) ───────────────────────
   Lahir dari potret resep Klinova 4 Sep: tombol sekunder berlatar --card + garis
   --border (ungu 14% = 1,24:1 di atas kartu) tidak bisa dibedakan dari medan input
   di atasnya — siluet sama, garis sama. Tombol harus terbaca sebagai tombol:
   garis 1,5px --brand (6,86:1 Porcelain) + teks --brand; latar transparan.
   Register gelap ikut token (--brand terang, AA di kartu kaca). Pakai untuk aksi
   kedua di samping .btn--solid/.btn--primary — bukan pengganti tautan teks. */
.btn--outline {
  background: transparent;
  color: var(--brand);
  border: 1.5px solid var(--brand);
  box-shadow: none;
}
.btn--outline:hover {
  background: var(--brand-100);
  transform: translateY(-1px);
}
.btn--outline:active { transform: translateY(0); background: var(--brand-100); }

/* Primary = glass violet tebal (DEFAULT baru).
   .btn--glass / .btn--glass-brand = alias identik — ketiganya menulis efek yang sama. */
.btn--primary,
.btn--glass,
.btn--glass-brand {
  background: linear-gradient(135deg, rgba(123,47,214,0.96), rgba(107,51,144,0.94));
  -webkit-backdrop-filter: blur(8px) saturate(185%);
  backdrop-filter: blur(8px) saturate(185%);
  border: 1px solid rgba(255,255,255,0.5);
  box-shadow:
    inset 0 1px 1px rgba(255,255,255,0.80),
    inset 0 -1px 1px rgba(255,255,255,0.28),
    inset 0 0 16px rgba(255,255,255,0.12),
    0 8px 28px -8px rgba(123,63,160,0.55),
    0 2px 6px rgba(31,22,50,0.14);
  text-shadow: 0 1px 6px rgba(31,22,50,0.35);
  color: #fff;
  position: relative;
  isolation: isolate;
}

/* specular top-sheen — cahaya jatuh di tepi atas pil, tidak menutup teks */
.btn--primary::after,
.btn--glass::after,
.btn--glass-brand::after {
  content: '';
  position: absolute;
  left: 7%; right: 7%; top: 1px;
  height: 46%;
  border-radius: inherit;
  background: linear-gradient(to bottom, rgba(255,255,255,0.55), rgba(255,255,255,0));
  opacity: 0.55;
  pointer-events: none;
}

.btn--primary:hover,
.btn--glass:hover,
.btn--glass-brand:hover {
  background: linear-gradient(135deg, rgba(123,47,214,1), rgba(107,51,144,0.98));
  transform: translateY(-2px);
  box-shadow:
    inset 0 1px 1px rgba(255,255,255,0.95),
    inset 0 0 20px rgba(255,255,255,0.20),
    0 14px 36px -8px rgba(123,63,160,0.65),
    0 2px 8px rgba(31,22,50,0.16);
}

.btn--primary:active,
.btn--glass:active,
.btn--glass-brand:active { transform: translateY(0); box-shadow: none; }

/* Tombol NONAKTIF datar (1.13.0, temuan review Klinova G5/G6): kilau specular dan
   bayangan angkat adalah bahasa "bisa ditekan" — pada :disabled keduanya dilepas,
   warna diredupkan, kursor menolak. Berlaku semua varian. */
.btn:disabled, .btn[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
  text-shadow: none;
}
.btn:disabled::after, .btn[aria-disabled="true"]::after { display: none; }

/* Di latar gelap: primary glass lebih translusen (violet dingin) */
.dark .btn--primary, .dark .btn--glass, .dark .btn--glass-brand,
.deep-field .btn--primary, .deep-field .btn--glass, .deep-field .btn--glass-brand {
  background: linear-gradient(135deg, rgba(123,47,214,0.42), rgba(123,63,160,0.34));
}
.dark .btn--primary:hover, .dark .btn--glass:hover, .dark .btn--glass-brand:hover,
.deep-field .btn--primary:hover, .deep-field .btn--glass:hover, .deep-field .btn--glass-brand:hover {
  background: linear-gradient(135deg, rgba(123,47,214,0.52), rgba(123,63,160,0.44));
}

/* Ghost = clear glass (DEFAULT baru) — terang */
.btn--ghost {
  background: rgba(255,255,255,0.40);
  -webkit-backdrop-filter: blur(8px) saturate(185%);
  backdrop-filter: blur(8px) saturate(185%);
  border: 1px solid rgba(123,63,160,0.22);
  color: var(--ink, var(--foreground));
  position: relative;
  isolation: isolate;
  box-shadow:
    inset 0 1px 1px rgba(255,255,255,0.80),
    inset 0 -1px 1px rgba(255,255,255,0.25),
    0 4px 12px -4px rgba(26,22,38,0.08);
}

.btn--ghost::after {
  content: '';
  position: absolute;
  left: 7%; right: 7%; top: 1px;
  height: 46%;
  border-radius: inherit;
  background: linear-gradient(to bottom, rgba(255,255,255,0.55), rgba(255,255,255,0));
  opacity: 0.55;
  pointer-events: none;
}

.btn--ghost:hover {
  background: rgba(255,255,255,0.58);
  border-color: rgba(123,63,160,0.38);
  transform: translateY(-1px);
  box-shadow:
    inset 0 1px 1px rgba(255,255,255,0.95),
    inset 0 0 16px rgba(255,255,255,0.18),
    0 8px 20px -6px rgba(26,22,38,0.12);
}

/* Ghost gelap — clear glass putih translusen */
.dark .btn--ghost,
.deep-field .btn--ghost {
  background: rgba(255,255,255,0.10);
  border: 1px solid rgba(255,255,255,0.38);
  color: var(--deepspace-foreground);
}
.dark .btn--ghost:hover,
.deep-field .btn--ghost:hover {
  background: rgba(255,255,255,0.18);
  border-color: rgba(255,255,255,0.55);
}

/* White — surface terang (untuk tombol di atas plat gelap) */
.btn--white {
  background: var(--card);
  color: var(--foreground);
  border-color: var(--card);
}

.btn--white:hover { background: var(--secondary); }

/* Active state semua tombol */
.btn:active { transform: scale(0.98); }

/* Size modifiers */
.btn--size-sm  { font-size: 13px; padding: 0.5rem 1rem;    min-height: 36px; }
.btn--size-md  { font-size: 13px; padding: 0.75rem 1.5rem; min-height: 44px; }
.btn--size-lg  { font-size: 13px; padding: 1rem 2rem;      min-height: 52px; }


/* ============================================================================
   CARDS — kartu mengambang (radius 18px + elevasi kontekstual)
   ============================================================================ */

/* ATURAN KANONIK GLASS CONTRAST (Fable art-director, 2026-07-30):
   Kartu glass di atas latar VARIATIF (aurora/foto): alpha material ≥0.78 gelap / ≥0.85 terang,
   ATAU wajib scrim gradient internal di belakang teks. Tint kartu SELALU ikut register;
   kartu lawan-register = objek produk opaque (≥0.95, tanpa backdrop-filter).
   Kesan glass di TEPI (rim+sheen≤0.2+hairline), bukan transparansi bodi.
   QC: teks terkecil vs titik paling terang latar ≥ 4.5:1 (AA). */

/* Card = GLASS by default. Escape hatch: .card--solid. Opt-in refraksi: .card--refract. */
.card {
  background: var(--glass-bg);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(160%);
  backdrop-filter: blur(var(--glass-blur)) saturate(160%);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-card);
  padding: var(--space-6);
  isolation: isolate;
  position: relative;
  box-shadow:
    inset 0 1px 1px rgba(255,255,255,0.90),
    inset 0 -1px 1px rgba(255,255,255,0.25),
    0 1px 3px rgba(26,22,38,0.08),
    0 12px 32px -16px rgba(26,22,38,0.16);
  transition:
    box-shadow    var(--duration-normal) var(--ease-hover),
    transform     var(--duration-normal) var(--ease-hover),
    border-color  var(--duration-normal) var(--ease-hover);
}

/* specular sheen di tepi atas kartu — tidak animasikan backdrop-filter */
.card::after {
  content: '';
  position: absolute;
  top: 1px;
  left: 0; right: 0;
  height: 38%;
  border-radius: inherit;
  background: linear-gradient(to bottom, rgba(255,255,255,0.50), rgba(255,255,255,0));
  opacity: 0.45;
  pointer-events: none;
}

/* Dark register — shadow lebih dalam, sheen lebih redup */
.dark .card,
.deep-field .card {
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.18),
    0 24px 60px -24px rgba(0,0,0,0.85);
}
.dark .card::after,
.deep-field .card::after { opacity: 0.25; }

/* Dense modifier — alpha lokal lebih tinggi untuk kartu di atas orb terang */
.card--dense { background: rgba(255,255,255,0.82); }
.dark .card--dense,
.deep-field .card--dense { background: rgba(20,16,35,0.85); }

/* Hover interaktif — angkat + border violet */
.card--interactive:hover {
  box-shadow: var(--shadow-lg);
  transform: translateY(-3px);
  border-color: var(--brand);
}

/* Opt-in refraksi — backdrop-filter url(#wtb-glass).
   Jangan jadikan default: berat GPU, hanya efektif di atas aurora/deep-space. */
.card--refract {
  -webkit-backdrop-filter: url("#wtb-glass") blur(var(--glass-blur)) saturate(160%);
  backdrop-filter: url("#wtb-glass") blur(var(--glass-blur)) saturate(160%);
}

/* ── Solid escape hatch (P3) — untuk form / tabel data / teks panjang
   dimana backdrop-filter tidak cocok atau latar tidak berwarna. */
.card--solid {
  background: var(--card);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
}

/* Mobile: kurangi blur agar lebih ringan di perangkat lambat */
@media (max-width: 768px) {
  .card { --glass-blur: 8px; }
}

/* Fallback: browser tanpa backdrop-filter — alpha tinggi supaya tetap terbaca */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .card { background: rgba(255,255,255,0.92); }
  .dark .card,
  .deep-field .card { background: rgba(34,28,52,0.92); }
}


/* ============================================================================
   SECTION RHYTHM — padding blok seragam per level
   ============================================================================ */

.section      { padding-block: var(--space-20); }
.section--lg  { padding-block: var(--space-32); }
.section--sm  { padding-block: var(--space-12); }
.section--subtle { background: var(--secondary); }

@media (min-width: 768px) {
  .section     { padding-block: var(--space-24); }
  .section--lg { padding-block: var(--space-32); }
}


/* ============================================================================
   NAV LINK — underline sweep dari kiri
   ============================================================================ */

.nav-link {
  position: relative;
  text-decoration: none;
  font-family: var(--ff-body);
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted-foreground);
  transition: color var(--duration-normal) var(--ease-out);
}

.nav-link::after {
  content: '';
  position: absolute;
  bottom: -3px;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--brand);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--duration-normal) var(--ease-snap);
}

.nav-link:hover { color: var(--foreground); }
.nav-link:hover::after { transform: scaleX(1); }

.nav-link.is-active { color: var(--foreground); }
.nav-link.is-active::after { transform: scaleX(1); }


/* ============================================================================
   KEYFRAMES — CSS-only auto-play
   ============================================================================ */

@keyframes fadeUp {
  from { opacity: 0; transform: translateY(18px); }
  to   { opacity: 1; transform: none; }
}

@keyframes fadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes drawLine {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

@keyframes pulseDot {
  0%, 100% { opacity: 0.8; }
  50%       { opacity: 1; box-shadow: 0 0 0 4px rgba(46,204,113,0.20); }
}


/* ============================================================================
   REVEAL — CSS-only, fail-open (konten visible by default tanpa JS)
   Animasi hanya bila user tidak prefer reduced-motion.
   ============================================================================ */

@media (prefers-reduced-motion: no-preference) {
  .reveal   { animation: fadeUp 0.7s var(--ease-silk) both; }
  .reveal-1 { animation: fadeUp 0.7s var(--ease-silk) 0.08s both; }
  .reveal-2 { animation: fadeUp 0.7s var(--ease-silk) 0.16s both; }
  .reveal-3 { animation: fadeUp 0.7s var(--ease-silk) 0.24s both; }
}


/* ============================================================================
   REVEAL ON-SCROLL — IntersectionObserver + fail-open
   ============================================================================
   Tanpa JS: elemen [data-reveal] tampil penuh (tidak ada state tersembunyi di CSS).
   JS meng-arm elemen yang masih di bawah viewport (.io-armed),
   lalu IntersectionObserver tambahkan .io-in saat masuk.
   Reduced-motion: JS tidak jalan (tidak perlu guard di sini).
   ============================================================================ */

@media (prefers-reduced-motion: no-preference) {
  .io-armed {
    opacity: 0;
    transform: translateY(16px);
  }

  .io-in {
    opacity: 1;
    transform: none;
    transition:
      opacity   0.65s var(--ease-enter),
      transform 0.65s var(--ease-enter);
  }
}


/* ============================================================================
   AKSESIBILITAS — reduced motion + keterbacaan mobile
   ============================================================================ */

/* Keterbacaan mobile: teks isi tidak boleh di bawah 16px di layar kecil */
@media (max-width: 480px) {
  p, li, dd, dt {
    font-size: max(16px, 1em);
  }
}

/* Hard stop semua animasi dan transisi */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* ============================================================================
   LIQUID GLASS — refraksi SVG (Chromium-only, progressive enhancement).
   Base kaca (blur + bevel + sheen) sudah di .btn--primary / .btn--ghost di atas —
   jalan di semua browser. Blok ini menambah displacement refraksi untuk Chromium.
   Konsumen WAJIB menaruh <svg> filter #wtb-glass sekali di layout:

   <svg width="0" height="0" style="position:absolute" aria-hidden="true">
     <filter id="wtb-glass" x="-20%" y="-20%" width="140%" height="140%"
             color-interpolation-filters="sRGB">
       <feTurbulence type="fractalNoise" baseFrequency="0.013 0.013"
                     numOctaves="2" seed="7" result="noise"/>
       <feGaussianBlur in="noise" stdDeviation="1.4" result="blurred"/>
       <feDisplacementMap in="SourceGraphic" in2="blurred" scale="24"
                          xChannelSelector="R" yChannelSelector="G"/>
     </filter>
   </svg>

   Card TIDAK dapat refraksi by default (terlalu berat GPU).
   Opt-in per-card: tambahkan kelas .card--refract.
   ============================================================================ */

/* Refraksi tombol — Chromium only, degradasi mulus di browser lain */
@supports (backdrop-filter: url("#wtb-glass")) or (-webkit-backdrop-filter: url("#wtb-glass")) {
  .btn--primary,
  .btn--glass,
  .btn--glass-brand,
  .btn--ghost {
    -webkit-backdrop-filter: url("#wtb-glass") blur(6px) saturate(185%);
    backdrop-filter: url("#wtb-glass") blur(6px) saturate(185%);
  }
}

@media (prefers-reduced-motion: reduce) {
  .btn--primary,
  .btn--glass,
  .btn--glass-brand,
  .btn--ghost { transition: none; }
  .btn--primary:hover,
  .btn--glass:hover,
  .btn--glass-brand:hover,
  .btn--ghost:hover { transform: none; }
}


/* ============================================================================
   GLASS BLOOM — div dekoratif absolut untuk memberi warna sub-latar
   pada seksi kartu glass (backdrop punya sesuatu untuk di-blur di porcelain).
   Pasang sebagai anak langsung dalam seksi — parent butuh position:relative.
   Opsional per-section.
   ============================================================================ */

.glass-bloom {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(60% 50% at 20% 20%, rgba(142,165,211,0.09), transparent 60%),
    radial-gradient(55% 45% at 85% 75%, rgba(107,91,196,0.08), transparent 60%);
}


/* ============================================================================
   THEME TOGGLE — tombol switch mode terang/gelap (JOB DEWAN-M145)
   Pasangan: bootstrap inline <head> (no-FOUC, cegah kedipan) + wtb-theme-toggle.js
   (klik + sinkron localStorage). Lihat ADOPTION.md § "Mode gelap/terang".

   Markup kanonik:
     <button type="button" class="theme-toggle" data-wtb-theme-toggle
             aria-pressed="false" aria-label="Ganti ke mode gelap">
       <svg class="theme-toggle__icon theme-toggle__icon--sun" ...>...</svg>
       <svg class="theme-toggle__icon theme-toggle__icon--moon" ...>...</svg>
     </button>

   Ikon sun/moon di-swap murni via ancestor selector `html.dark` — SATU sumber
   kebenaran (class .dark yang sama menggerakkan semua token warna di
   wtb-cinematic.css). JS hanya update aria-pressed/aria-label (a11y), bukan
   ikon — jadi ikon tidak pernah "telat"/tidak sinkron dengan token warna.
   Hanya pakai token semantik yang sudah ada — nol hex baru.
   ============================================================================ */

.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  flex: none;
  border-radius: var(--radius-full);
  border: 1px solid var(--border);
  background: var(--card);
  color: var(--foreground);
  cursor: pointer;
  transition:
    background-color var(--duration-normal) var(--ease-hover),
    border-color      var(--duration-normal) var(--ease-hover),
    transform         var(--duration-fast)   var(--ease-hover);
}

.theme-toggle:hover  { background: var(--muted); border-color: var(--ring); }
.theme-toggle:active { transform: scale(0.94); }

.theme-toggle__icon {
  width: 18px;
  height: 18px;
  stroke: currentColor;
  fill: none;
  pointer-events: none;
}

/* default: mode terang → tampilkan matahari, sembunyikan bulan */
.theme-toggle__icon--moon { display: none; }

/* mode gelap aktif (html.dark) → tampilkan bulan, sembunyikan matahari */
html.dark .theme-toggle__icon--sun  { display: none; }
html.dark .theme-toggle__icon--moon { display: block; }

@media (prefers-reduced-motion: reduce) {
  .theme-toggle { transition: none; }
}


/* ============================================================================
   v1.1.0 — 10 KOMPONEN BARU (form + editorial + data)
   Semua flat/UI-datar (form/data — bukan glass, lihat ADOPTION.md "escape hatch")
   kecuali cta-ribbon/aurora-panel/blockquote yang tetap register-invariant lewat
   token shadcn (--background/--foreground/--border/--input/--ring/--muted*)
   yang SUDAH di-override otomatis di dalam .dark dan .deep-field — nol CSS
   tambahan dibutuhkan supaya komponen ini benar di kedua register.
   ============================================================================ */

/* ── INPUT — bidang teks formulir. Markup: <input class="input">; state error:
   tambah class .input--error (pesan error: elemen teks biasa, warna var(--destructive)). ── */
.input {
  display: block;
  width: 100%;
  border: 1px solid var(--input);
  border-radius: var(--radius);
  padding: var(--space-3) var(--space-4);
  font-family: var(--ff-body);
  font-size: var(--text-sm);
  background: var(--card);
  color: var(--foreground);
  outline: none;
  transition:
    border-color var(--duration-normal) var(--ease-hover),
    box-shadow   var(--duration-normal) var(--ease-hover);
}
.input::placeholder { color: var(--muted-foreground); }
.input:focus {
  border-color: var(--ring);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ring) 32%, transparent);
}
.input--error { border-color: var(--destructive); }
.input--error:focus {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--destructive) 28%, transparent);
}
.input:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  background: var(--muted);
}

/* ── SELECT — dropdown pilihan. Chevron SVG pakai stroke='currentColor' (nol hex,
   ikut var(--foreground) elemen) — bukan warna literal. Markup: <select class="select">. ── */
.select {
  display: block;
  width: 100%;
  appearance: none;
  -webkit-appearance: none;
  border: 1px solid var(--input);
  border-radius: var(--radius);
  padding: var(--space-3) var(--space-4);
  padding-right: 2.5rem;
  font-family: var(--ff-body);
  font-size: var(--text-sm);
  background-color: var(--card);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--space-3) center;
  color: var(--foreground);
  cursor: pointer;
  outline: none;
  transition:
    border-color var(--duration-normal) var(--ease-hover),
    box-shadow   var(--duration-normal) var(--ease-hover);
}
.select:focus {
  border-color: var(--ring);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ring) 32%, transparent);
}
.select--error { border-color: var(--destructive); }
.select:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  background-color: var(--muted);
}

/* ── CHECKBOX — accent-color nativa (nol SVG kustom, nol hex). Markup:
   <input type="checkbox" class="checkbox" id="x"><label class="checkbox-label" for="x">. ── */
.checkbox {
  width: 18px;
  height: 18px;
  accent-color: var(--brand);
  cursor: pointer;
  flex-shrink: 0;
}
.checkbox:disabled { cursor: not-allowed; opacity: 0.45; }

.checkbox-label {
  font-family: var(--ff-body);
  font-size: var(--text-sm);
  color: var(--foreground);
  cursor: pointer;
}
.checkbox-label--disabled { color: var(--muted-foreground); cursor: not-allowed; }

/* ── TEXTAREA — area teks multi-baris. Markup: <textarea class="textarea">. ── */
.textarea {
  display: block;
  width: 100%;
  min-height: 120px;
  resize: vertical;
  border: 1px solid var(--input);
  border-radius: var(--radius);
  padding: var(--space-3) var(--space-4);
  font-family: var(--ff-body);
  font-size: var(--text-sm);
  line-height: 1.65;
  background: var(--card);
  color: var(--foreground);
  outline: none;
  transition:
    border-color var(--duration-normal) var(--ease-hover),
    box-shadow   var(--duration-normal) var(--ease-hover);
}
.textarea::placeholder { color: var(--muted-foreground); }
.textarea:focus {
  border-color: var(--ring);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ring) 32%, transparent);
}
.textarea--error { border-color: var(--destructive); }
.textarea:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  resize: none;
  background: var(--muted);
}

/* ── STAT — metrik angka besar + label + indikator perubahan. Markup:
   <div class="stat"><p class="stat-number">12.847</p><p class="stat-label">...</p>
   <span class="stat-change stat-change--up">↑ 18.4%</span></div>
   Komponen TANPA chrome kartu sendiri — gabungkan dengan .card/.card--solid
   untuk tile berbingkai (lihat contoh preview.html). ── */
.stat {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.stat-number {
  font-family: var(--ff-display);
  font-size: clamp(2rem, 1.5rem + 2vw, 2.75rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1;
  color: var(--foreground);
  font-variant-numeric: tabular-nums;
  margin: 0;
}
.stat-label {
  font-family: var(--ff-body);
  font-size: var(--text-sm);
  color: var(--muted-foreground);
  margin: 0;
}
.stat-change {
  display: inline-flex;
  align-self: flex-start;
  align-items: center;
  gap: var(--space-1);
  padding: 2px var(--space-3);
  border-radius: var(--radius-full);
  font-family: var(--ff-mono);
  font-size: var(--text-xs);
  font-weight: 500;
}
.stat-change--up {
  background: color-mix(in srgb, var(--success) 12%, transparent);
  color: var(--success);
}
.stat-change--down {
  background: color-mix(in srgb, var(--destructive) 12%, transparent);
  color: var(--destructive);
}

/* ── FEATURE ROW — baris horizontal ikon + judul + body. Markup:
   <div class="feature-row"><div class="feature-row-icon"><svg>...</svg></div>
   <div><h3 class="feature-row-title">Judul</h3><p class="feature-row-body">Body</p></div></div>
   Judul/body pakai class khusus (BUKAN selektor tag polos) supaya aman kalau
   konsumen menyisipkan <p class="eyebrow"> sebagai kicker di dalam baris. ── */
.feature-row {
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
}
.feature-row-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--brand) 10%, transparent);
  color: var(--brand);
  flex-shrink: 0;
}
.feature-row-title {
  font-family: var(--ff-display);
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--foreground);
  margin: 0 0 4px;
}
.feature-row-body {
  font-family: var(--ff-body);
  font-size: var(--text-sm);
  color: var(--muted-foreground);
  margin: 0;
}

/* ── CTA RIBBON — pita ajakan bertindak lebar penuh. Gabungkan dengan
   .deep-field (gelap) atau .aurora-hero (terang) untuk latar. Markup:
   <div class="deep-field cta-ribbon"><p class="eyebrow">...</p>
   <h2>...</h2><p class="cta-ribbon-body">...</p><div class="cta-ribbon-actions">
   <a class="btn btn--primary">...</a></div></div>
   Body pakai class khusus (BUKAN selektor tag polos) supaya tidak menimpa
   .eyebrow kalau eyebrow dipasang sebagai <p> anak langsung juga. ── */
.cta-ribbon {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-16) var(--space-8);
  border-radius: var(--radius-card);
  text-align: center;
}
.cta-ribbon h2 {
  font-family: var(--ff-display);
  font-size: var(--text-h2);
  color: var(--foreground);
  max-width: 42rem;
  margin: 0;
}
.cta-ribbon-body {
  max-width: 34rem;
  font-size: var(--text-lg);
  color: var(--muted-foreground);
  margin: 0;
}
.cta-ribbon-actions {
  display: flex;
  gap: var(--space-4);
  flex-wrap: wrap;
  justify-content: center;
}

/* Kompak — baris horizontal, judul kiri + CTA kanan */
.cta-ribbon--compact {
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  text-align: left;
  padding: var(--space-8) var(--space-10);
  gap: var(--space-6);
  flex-wrap: wrap;
}

/* ── PRODUCT CARD — kartu produk WTB. Gabungkan dengan .card + .card--solid.
   Markup: <div class="card card--solid product-card"><div class="product-card-accent"
   style="background:var(--accent-studio)"></div><div class="product-card-header">
   <p class="eyebrow">Studio WTB</p><span class="badge">Unggulan</span></div>
   <h3>...</h3><p>...</p><ul class="product-card-features"><li>...</li></ul>
   <a class="btn btn--primary btn--size-sm">...</a></div> ── */
.product-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.product-card-accent {
  width: 36px;
  height: 4px;
  border-radius: var(--radius-sm);
}
.product-card-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
}
.product-card h3 {
  font-family: var(--ff-display);
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--foreground);
  margin: 0;
}
.product-card > p {
  font-size: var(--text-sm);
  color: var(--muted-foreground);
  flex: 1;
  margin: 0;
}
.product-card-features {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  list-style: none;
  margin: 0;
  padding: 0;
}
.product-card-features li {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  color: var(--muted-foreground);
}
.product-card-features li::before {
  content: '';
  width: 5px;
  height: 5px;
  border-radius: var(--radius-full);
  background: var(--brand);
  flex-shrink: 0;
}

/* ── BLOCKQUOTE — kutipan editorial, garis kiri brand violet. Markup:
   <blockquote class="blockquote"><p>Kutipan...</p><footer>Atribusi</footer></blockquote> ── */
.blockquote {
  border-left: 3px solid var(--brand);
  padding: var(--space-4) var(--space-6);
  margin: 0;
  max-width: 640px;
}
.blockquote p {
  font-family: var(--ff-serif);
  font-size: 1.125rem;
  font-style: italic;
  color: var(--foreground);
  line-height: 1.7;
  margin: 0;
}
.blockquote footer {
  margin-top: var(--space-3);
  font-family: var(--ff-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}
.blockquote footer::before { content: "— "; }

/* ── AURORA PANEL — plat kaca gelap dengan latar aurora sinematik. Selalu
   gabungkan dengan .deep-field (pemasok orb aurora + token gelap); kelas ini
   HANYA mengurus bentuk (radius + clip). Markup:
   <div class="deep-field aurora-panel" style="padding:40px"><p class="eyebrow">...</p>
   <h3>...</h3><p>...</p></div> ── */
.aurora-panel {
  border-radius: var(--radius-card);
  overflow: hidden;
}

/* ── PERMUKAAN PROMPTING — pola kanonik (1.12.0, ketok Bos 3 Sep 2026) ────────
   Halaman tempat pengguna BERTANYA ke AI dan MEMBACA jawabannya (Pengacara-ku
   Tanya Hukum, TangkasAI, Studio). Prinsip: jawaban AI = DOKUMEN yang dibaca,
   bukan obrolan yang lewat. Pengalaman menyenangkan lahir dari empat hal:
   komposer yang mengundang, keadaan berpikir yang jujur, jawaban yang rapi
   dibaca, dan sumber yang bisa diperiksa.

   ANATOMI (urutan DOM kanonik):
     <form class="prompt-composer">
       <textarea class="prompt-composer__field" rows="3" aria-label="Tulis pertanyaan"
                 placeholder="(contoh nyata domain produk, bukan 'Ketik di sini')"></textarea>
       <div class="prompt-composer__baris">
         <span class="prompt-composer__hint">Enter kirim &middot; Shift+Enter baris baru</span>
         <button type="submit" class="btn btn--primary btn--size-md">Tanya</button>
       </div>
     </form>
     <div class="prompt-chips">
       <button type="button" class="prompt-chip">(contoh pertanyaan — klik mengisi composer)</button>
     </div>
     <div class="prompt-thinking" hidden><span class="prompt-thinking__denyut"></span>
       Menyusun jawaban&hellip;</div>
     <article class="prompt-answer">
       <p class="prompt-answer__meta eyebrow">jawaban</p>
       <p>&hellip;isi&hellip; <a class="prompt-cite" href="#sumber-1">1</a></p>
       <footer>
         <ol class="prompt-sources">
           <li id="sumber-1"><a href="...">Judul sumber</a>
             <span class="prompt-sources__meta">Pasal 27 ayat (3) &middot; peraturan.go.id</span></li>
         </ol>
         <div class="prompt-actions">
           <button class="btn btn--ghost btn--size-sm">Salin</button>
           <button class="btn btn--ghost btn--size-sm">Tanya ulang</button>
         </div>
         <p class="prompt-disclaimer">(kalimat batas tanggung jawab milik produk)</p>
       </footer>
     </article>

   ATURAN KERAS:
   - Jawaban = dokumen SATU KOLOM, ukur baca maks 72ch, serif rata kiri.
     DILARANG bubble kiri-kanan, avatar/persona robot, dan justify.
   - Nol lompatan layout: .prompt-thinking memesan tempatnya (min-height) dan
     jawaban hanya BERTAMBAH tinggi saat streaming; komposer tidak berpindah.
   - Tombol kirim dinonaktifkan (disabled) selama .prompt-thinking tampil;
     SATU aksi primer, aksi lain .btn--ghost.
   - Kutipan bernomor: tiap .prompt-cite = elemen <a> fokusable yang menunjuk
     id butir .prompt-sources; nomor di teks dan di daftar WAJIB identik.
   - .prompt-disclaimer WAJIB tampak di bawah SETIAP jawaban (bukan hanya
     footer situs). Nada tenang gaya blok-percaya; ikut aturan kata terlarang
     blok kepercayaan (tanpa "tersertifikasi/diaudit/diverifikasi pemerintah").
   - Keadaan kosong (belum ada jawaban): pakai .empty-state + .prompt-chips —
     judul AJAKAN, contoh prompt yang KLIK-LANGSUNG mengisi komposer.
   - Placeholder komposer = contoh pertanyaan nyata dari domain produk.

   LAMPIRAN DOKUMEN (1.12.1) — komposer yang menerima berkas:
     <form class="prompt-composer" data-wtb-lampiran-batas="PDF/DOCX maks 10 MB">
       <textarea class="prompt-composer__field" ...></textarea>
       <div class="prompt-files">
         <span class="prompt-file">kontrak-kerja.pdf
           <span class="prompt-file__meta">1,2 MB</span>
           <button type="button" class="prompt-file__lepas" aria-label="Lepas lampiran">&times;</button></span>
         <span class="prompt-file prompt-file--unggah">putusan-123.pdf
           <span class="prompt-file__meta">mengunggah&hellip;</span></span>
         <span class="prompt-file prompt-file--galat">foto.heic
           <span class="prompt-file__meta">jenis tidak didukung</span>
           <button type="button" class="prompt-file__lepas" aria-label="Lepas lampiran">&times;</button></span>
       </div>
       <div class="prompt-composer__baris">
         <div class="prompt-composer__alat">
           <button type="button" class="prompt-attach" aria-label="Lampirkan dokumen">
             (svg klip 16px) Lampirkan</button>
           <span class="prompt-composer__hint">PDF/DOCX maks 10 MB &middot; Enter kirim</span>
         </div>
         <button type="submit" class="btn btn--primary btn--size-md">Tanya</button>
       </div>
     </form>
   ATURAN KERAS lampiran:
   - Tombol lampir WAJIB type="button" — bukan submit; salah type membuat klik
     lampir malah mengirim pertanyaan.
   - Batas jenis+ukuran WAJIB dideklarasikan di data-wtb-lampiran-batas DAN
     tampil ke pengguna (hint) SEBELUM ia memilih berkas — bukan baru muncul
     sebagai galat sesudahnya.
   - Berkas terlampir tampil sebagai chip .prompt-file (nama + meta + tombol
     lepas); chip TIDAK menimpa placeholder/teks yang sudah diketik.
   - Galat unggah tampil DI CHIP-nya (.prompt-file--galat + sebab di __meta),
     teks komposer tidak dihapus; chip galat tetap bisa dilepas.
   - Keadaan mengunggah (.prompt-file--unggah) jujur: tanpa tombol lepas boleh,
     tapi jangan diam — meta menyatakan sedang mengunggah.
   Gerbang mesin: wtb-design/scripts/cek_permukaan_prompting.py <URL>. */
.prompt-composer {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  background: var(--card);
  border: 1px solid var(--input);
  border-radius: var(--radius-card);
  padding: var(--space-3) var(--space-4);
  transition: border-color var(--duration-normal) var(--ease-hover),
              box-shadow   var(--duration-normal) var(--ease-hover);
}
.prompt-composer:focus-within {
  border-color: var(--ring);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ring) 18%, transparent);
}
.prompt-composer__field {
  border: none;
  background: transparent;
  resize: none;
  width: 100%;
  min-height: 3.2em;
  font-family: var(--ff-body);
  font-size: var(--text-base, 1rem);
  line-height: 1.6;
  color: var(--foreground);
  outline: none;                 /* ring dipindah ke :focus-within wadah */
}
.prompt-composer__field::placeholder { color: var(--muted-foreground); opacity: 0.75; }
.prompt-composer__baris {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}
.prompt-composer__hint {
  font-family: var(--ff-body);
  font-size: 12px;
  color: var(--muted-foreground);
}
.prompt-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-3);
}
.prompt-chip {
  font-family: var(--ff-body);
  font-size: 13.5px;
  line-height: 1.4;
  text-align: left;
  color: var(--foreground);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-full);
  padding: var(--space-2) var(--space-4);
  cursor: pointer;
  transition: border-color var(--duration-normal) var(--ease-hover),
              background-color var(--duration-normal) var(--ease-hover);
}
.prompt-chip:hover {
  border-color: var(--brand);
  background: var(--brand-100);
}
.dark .prompt-chip:hover,
.deep-field .prompt-chip:hover {
  background: color-mix(in srgb, var(--brand) 22%, transparent);
}
.prompt-thinking {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 44px;              /* memesan tempat — nol lompatan layout */
  margin-top: var(--space-4);
  font-family: var(--ff-body);
  font-size: var(--text-sm);
  color: var(--muted-foreground);
}
.prompt-thinking__denyut {
  width: 10px;
  height: 10px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--brand);
}
@media (prefers-reduced-motion: no-preference) {
  .prompt-thinking__denyut { animation: prompt-denyut 1.2s ease-in-out infinite; }
  @keyframes prompt-denyut {
    0%, 100% { opacity: 0.35; transform: scale(0.85); }
    50%      { opacity: 1;    transform: scale(1); }
  }
}
.prompt-answer {
  max-width: 72ch;
  margin-top: var(--space-6);
}
.prompt-answer__meta { margin-bottom: var(--space-3); }
/* Gaya baca HANYA untuk isi jawaban — meta (eyebrow) dan disclaimer dikecualikan
   eksplisit; tanpa :not() aturan ini menang spesifisitas dan merusak keduanya
   (pelajaran ds-tak-boleh-perang-spesifisitas). */
.prompt-answer p:not(.prompt-answer__meta):not(.prompt-disclaimer),
.prompt-answer li {
  font-family: var(--ff-serif);
  font-size: 1.0625rem;          /* 17px — ukuran baca teks panjang */
  line-height: 1.75;
  color: var(--foreground);
  text-align: left;
}
.prompt-answer p + p { margin-top: var(--space-4); }
.prompt-answer h2,
.prompt-answer h3 {
  font-family: var(--ff-display);
  color: var(--foreground);
  margin-top: var(--space-6);
  margin-bottom: var(--space-2);
}
.prompt-cite {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  vertical-align: super;
  min-width: 18px;
  padding: 1px 5px;
  font-family: var(--ff-mono);
  font-size: 11px;
  line-height: 1.4;
  text-decoration: none;
  color: var(--brand);
  background: var(--brand-100);
  border-radius: var(--radius-full);
}
.dark .prompt-cite,
.deep-field .prompt-cite {
  /* warna TIDAK ditimpa: --brand di register gelap sudah violet terang AA
     (#bd92e3, lift 1.1.3); menimpa dengan --primary = ungu CTA 1,9:1 (temuan
     audit 3 Sep). Hanya latarnya yang butuh cabang gelap. */
  background: color-mix(in srgb, var(--brand) 26%, transparent);
}
.prompt-cite:hover { background: var(--brand-200); }
.dark .prompt-cite:hover,
.deep-field .prompt-cite:hover {
  background: color-mix(in srgb, var(--brand) 40%, transparent);
}
.prompt-sources {
  margin-top: var(--space-6);
  padding-top: var(--space-4);
  padding-left: var(--space-5);
  border-top: 1px solid var(--border);
}
.prompt-sources li {
  font-family: var(--ff-body);
  font-size: var(--text-sm);
  line-height: 1.6;
}
.prompt-sources li + li { margin-top: var(--space-2); }
.prompt-sources__meta {
  display: block;
  font-family: var(--ff-mono);
  font-size: 11.5px;
  color: var(--muted-foreground);
}
.prompt-actions {
  display: flex;
  gap: var(--space-2);
  margin-top: var(--space-4);
}
.prompt-disclaimer {
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
  font-family: var(--ff-body);
  font-size: 13px;
  line-height: 1.6;
  color: var(--muted-foreground);
}

/* — Lampiran dokumen (1.12.1) — */
.prompt-composer__alat {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-width: 0;                  /* hint boleh truncate, submit tidak terdorong */
}
.prompt-attach {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  padding: var(--space-1) var(--space-2);
  border: none;
  background: transparent;
  border-radius: var(--radius);
  font-family: var(--ff-body);
  font-size: 12.5px;
  color: var(--muted-foreground);
  cursor: pointer;
  transition: color var(--duration-normal) var(--ease-hover),
              background-color var(--duration-normal) var(--ease-hover);
}
.prompt-attach:hover {
  color: var(--brand);
  background: var(--brand-100);
}
.dark .prompt-attach:hover,
.deep-field .prompt-attach:hover {
  background: color-mix(in srgb, var(--brand) 22%, transparent);
}
.prompt-attach svg { width: 16px; height: 16px; flex-shrink: 0; }
.prompt-files {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.prompt-file {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  max-width: 100%;
  padding: 4px var(--space-3);
  background: var(--muted);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-family: var(--ff-body);
  font-size: 13px;
  line-height: 1.5;
  color: var(--foreground);
  overflow-wrap: anywhere;       /* nama berkas panjang patah, bukan luber */
}
.prompt-file__meta {
  font-family: var(--ff-mono);
  font-size: 11px;
  color: var(--muted-foreground);
  white-space: nowrap;
}
.prompt-file__lepas {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  min-height: 22px;
  margin-right: -4px;
  border: none;
  background: transparent;
  border-radius: var(--radius-full);
  font-family: var(--ff-mono);
  font-size: 13px;
  line-height: 1;
  color: var(--muted-foreground);
  cursor: pointer;
}
.prompt-file__lepas:hover {
  color: var(--destructive);
  background: var(--destructive-100);
}
.dark .prompt-file__lepas:hover,
.deep-field .prompt-file__lepas:hover {
  color: var(--destructive-300);
  background: color-mix(in srgb, var(--destructive-500) 30%, transparent);
}
.prompt-file--galat {
  border-color: var(--destructive-300);
  background: var(--destructive-100);
  color: var(--destructive);
}
.prompt-file--galat .prompt-file__meta { color: var(--destructive); }
.dark .prompt-file--galat,
.deep-field .prompt-file--galat {
  background: color-mix(in srgb, var(--destructive-500) 22%, transparent);
  border-color: color-mix(in srgb, var(--destructive-400) 55%, transparent);
  color: var(--destructive-200);
}
.dark .prompt-file--galat .prompt-file__meta,
.deep-field .prompt-file--galat .prompt-file__meta { color: var(--destructive-200); }
@media (prefers-reduced-motion: no-preference) {
  .prompt-file--unggah { animation: prompt-unggah 1.4s ease-in-out infinite; }
  @keyframes prompt-unggah {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.55; }
  }
}
/* Teks kecil muted di atas kartu register gelap: kartu lebih terang dari latar
   deep-space, muted-foreground jatuh ke 4,15:1 (temuan audit 3 Sep) — diangkat
   ke arah foreground lewat color-mix, tetap turunan token. */
.dark .prompt-composer__hint,
.deep-field .prompt-composer__hint,
.dark .prompt-file__meta,
.deep-field .prompt-file__meta,
.dark .prompt-sources__meta,
.deep-field .prompt-sources__meta,
.dark .prompt-file__lepas,
.deep-field .prompt-file__lepas {
  color: color-mix(in srgb, var(--muted-foreground) 55%, var(--foreground));
}
.dark .prompt-composer__field::placeholder,
.deep-field .prompt-composer__field::placeholder {
  color: color-mix(in srgb, var(--muted-foreground) 55%, var(--foreground));
  opacity: 1;
}
