@import url("./tokens.css");

@font-face {
  font-family: "Plus Jakarta Sans";
  src: url("../fonts/plus-jakarta-sans-latin.woff2") format("woff2");
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
}

/* ============================================================================
   ALIAS LOKAL — satu-satunya blok yang boleh memuat nilai literal.
   Semua warna berakhir di token Design System (tokens.css di atas).
   --canvas DIHAPUS: Design System sudah menyediakannya dengan nilai identik.
   --yellow / --surface / --ink / --text DIPERTAHANKAN sebagai nama token lokal
   karena assets/js/app.js menulis ulang keempatnya saat runtime (Appearance).
   Font: ADR-G04 — hanya --font-display & --font-body yang di-override.
   ============================================================================ */
:root {
  --font-display: "Plus Jakarta Sans", Arial, sans-serif;
  --font-body: "Plus Jakarta Sans", Arial, sans-serif;

  --ink: var(--ink-900);
  --text: var(--text-body);
  --muted: var(--text-muted);
  --line: var(--border-default);
  --soft: var(--bg-subtle);
  --surface: var(--bg-card);
  --yellow: var(--brand);
  /* Tinggi baris bilah navigasi bawah. Dipakai DUA tempat — tinggi tombolnya
     sendiri dan ruang bawah `.site-shell` — jadi ia harus punya satu sumber. */
  --bottom-nav-row: 58px;
  --green: var(--accent-mint);
  --warning: var(--accent-orange);
  --red: var(--tag-danger-text);
  --shell: 560px;
  --pad: var(--space-6);
  --radius: var(--radius-lg);
  --shadow: var(--shadow-pop);
}

* { box-sizing: border-box; }
/* Tombol yang menggantikan anchor (tujuannya kini dari Settings lewat Action Engine)
   harus mewarisi tipografi dan tidak membawa gaya bawaan browser. */
.link-list button, .schedule-contact button, .consultation > button { width: 100%; border: 0; background: none; color: inherit; font: inherit; text-align: left; cursor: pointer; }
[hidden] { display: none !important; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  min-width: 320px;
  background: var(--canvas);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
body.drawer-is-open,
body:has(dialog[open]) { overflow: hidden; }
button, input { font: inherit; }
button, a { -webkit-tap-highlight-color: transparent; }
button { color: inherit; }
img { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
/* Ikon BIDANG (Material Symbols, ms-*) menyatakan dirinya lewat atribut
   fill="currentColor" stroke="none". Reset di atas mematikannya: CSS selalu
   mengalahkan atribut presentasi, jadi `fill: none` menang dan ikonnya
   digambar TANPA isi — tampak seperti tidak muncul sama sekali, bukan seperti
   galat. Aturan ini mengembalikannya, dicocokkan lewat `stroke="none"` yang
   HANYA dipasang toSvg() pada ikon berbendera filled. */
svg[stroke="none"] { fill: currentColor; stroke: none; }

.sr-only { position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
/* Lewati-ke-konten: menyalin pola yang sudah dipakai app admin (`admin/assets/admin.css`)
   supaya satu tautan berperilaku sama di dua aplikasi. Ia disembunyikan dengan
   `transform`, BUKAN `display:none`/`visibility`, karena keduanya membuat tautan
   hilang dari urutan fokus keyboard — persis fungsi yang seharusnya ia sediakan.
   `position: fixed` menahannya di luar `.site-shell` yang ber-`overflow: clip`. */
.skip-link { position: fixed; z-index: 999; top: 8px; left: 8px; padding: 10px 14px; background: var(--ink); color: var(--text-on-dark); transform: translateY(-150%); }
.skip-link:focus { transform: none; }

:focus-visible { outline: 3px solid var(--accent-blue); outline-offset: 3px; }
.site-shell { position: relative; width: 100%; max-width: var(--shell); min-height: 100vh; margin: 0 auto; background: var(--surface); border-inline: 1px solid var(--border-input); overflow: clip; }
.site-header { position: sticky; z-index: 30; top: 0; height: 68px; padding: 0 var(--pad); display: flex; align-items: center; justify-content: space-between; background: rgba(255, 255, 255, .94); border-bottom: 1px solid rgba(230, 232, 238, .9); backdrop-filter: blur(14px); }
.brand { width: 137px; overflow: hidden; }
.brand img { width: 137px; height: auto; }
.header-actions { display: flex; gap: 17px; align-items: center; }
.text-action { display: inline-flex; gap: 7px; align-items: center; padding: 8px 0; border: 0; background: none; font-size: var(--text-2xs); font-weight: 700; cursor: pointer; }
.text-action svg { width: 17px; height: 17px; }

.hero-pad { padding: 24px var(--pad) 22px; }
.hero-meta { display: flex; justify-content: space-between; align-items: center; margin-bottom: 17px; color: var(--text-subtle); font-size: var(--text-2xs); font-weight: 700; letter-spacing: .11em; text-transform: uppercase; }
.hero-meta strong { color: var(--text); font-size: var(--text-2xs); letter-spacing: .13em; }
.search-box { height: 50px; padding: 0 14px; display: flex; align-items: center; gap: 10px; background: var(--bg-subtle); border: 1px solid var(--border-default); border-radius: 13px; cursor: text; }
.search-box svg { width: 18px; height: 18px; color: var(--text-subtle); }
.search-box input { min-width: 0; flex: 1; border: 0; outline: 0; background: transparent; color: var(--text); font-size: var(--text-xs); }
.search-box input::placeholder { color: var(--text-subtle); }
.search-box kbd { min-width: 23px; padding: 2px 6px; border: 1px solid var(--border-input); border-radius: 6px; background: var(--vs-white); color: var(--text-subtle); font: 600 var(--text-2xs)/17px var(--font-body); text-align: center; }
.shortcut-grid { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 17px 8px; margin-top: 20px; }
.shortcut { min-width: 0; padding: 0; display: flex; flex-direction: column; align-items: center; border: 0; background: none; color: var(--text-muted); font-size: var(--text-2xs); font-style: normal; font-weight: 650; line-height: 1.3; text-align: center; cursor: pointer; }
.shortcut > span { position: relative; width: 51px; height: 51px; margin-bottom: 7px; display: grid; place-items: center; background: var(--bg-subtle); border: 1px solid var(--border-default); border-radius: 13px; transition: transform .2s, border-color .2s, background .2s; }
.shortcut:hover > span { transform: translateY(-2px); border-color: var(--ink-200); background: var(--vs-white); }
.shortcut svg { width: 18px; height: 18px; }

.section { padding: 34px var(--pad); border-top: 1px solid var(--border-faint); }
.section-heading { margin-bottom: 17px; display: flex; align-items: end; justify-content: space-between; }
.section-heading > div > span,
.schedule-intro > span { display: none; margin-bottom: 4px; color: var(--text-subtle); font-size: var(--text-2xs); font-weight: 750; letter-spacing: .18em; text-transform: uppercase; }
.section-heading h2 { margin: 0; font-size: 23px; line-height: 1.1; letter-spacing: -.045em; }
.section-heading > button { display: flex; align-items: center; gap: 5px; padding: 6px 0; border: 0; background: none; color: var(--text-muted); font-size: var(--text-2xs); font-weight: 700; cursor: pointer; }
.section-heading > button svg { width: 14px; height: 14px; }

.promo-section { padding-inline: 0; padding-top: 6px; }
.promo-slider { display: flex; gap: 10px; overflow-x: auto; padding: 0 var(--pad) 4px; scroll-padding-inline: var(--pad); scroll-snap-type: x mandatory; scrollbar-width: none; }
.promo-slider::-webkit-scrollbar, .featured-track::-webkit-scrollbar, .gallery-track::-webkit-scrollbar { display: none; }
.promo-hero { position: relative; flex: 0 0 calc(100% - 30px); aspect-ratio: 1.72 / 1; overflow: hidden; border-radius: var(--radius); background: var(--bg-ink); color: var(--text-on-dark); scroll-snap-align: start; cursor: pointer; }
.promo-hero > img { width: 100%; height: 100%; object-fit: cover; }
.promo-hero::after { content: ""; position: absolute; inset: 0; background: linear-gradient(90deg, rgba(0,0,0,.84), rgba(0,0,0,.28) 67%, rgba(0,0,0,.04)); }
.promo-overlay { position: absolute; z-index: 1; inset: 0; padding: 23px; display: flex; flex-direction: column; align-items: flex-start; }
.promo-overlay > span { color: var(--yellow); font-size: var(--text-2xs); font-weight: 800; letter-spacing: .18em; }
.promo-overlay h2 { margin: 8px 0 7px; font-size: clamp(23px, 7.2vw, 34px); line-height: .98; letter-spacing: -.055em; }
.promo-overlay p { margin: 0; color: var(--text-on-dark-muted); font-size: var(--text-2xs); }
.promo-overlay button { margin-top: auto; padding: 0; display: flex; align-items: center; gap: 7px; border: 0; background: none; color: var(--text-on-dark); font-size: var(--text-2xs); font-weight: 750; cursor: pointer; }
.promo-overlay button svg { width: 14px; height: 14px; }
.slider-dots { margin-top: 10px; display: flex; justify-content: center; gap: 5px; }
.slider-dots button { width: 5px; height: 5px; padding: 0; border: 0; border-radius: 50%; background: var(--ink-200); cursor: pointer; transition: width .2s, border-radius .2s, background .2s; }
.slider-dots button.is-active { width: 20px; border-radius: 10px; background: var(--ink); }

.compact-promos { padding-top: 20px; padding-bottom: 20px; display: grid; gap: 9px; }
.compact-promo { min-width: 0; padding: 7px; display: grid; grid-template-columns: 66px 1fr 20px; gap: 12px; align-items: center; border: 1px solid var(--border-default); border-radius: 13px; background: var(--vs-white); text-align: left; cursor: pointer; transition: border-color .2s, transform .2s; }
.compact-promo:hover { border-color: var(--ink-200); transform: translateY(-1px); }
.compact-promo img { width: 66px; height: 57px; background: var(--bg-inset); object-fit: cover; border-radius: 9px; }
.compact-promo > span { min-width: 0; display: grid; }
.compact-promo small { color: var(--accent-orange); font-size: var(--text-2xs); font-weight: 800; letter-spacing: .14em; }
.compact-promo strong { margin: 2px 0 1px; overflow: hidden; font-size: var(--text-2xs); text-overflow: ellipsis; white-space: nowrap; }
.compact-promo em { overflow: hidden; color: var(--text-subtle); font-size: var(--text-2xs); font-style: normal; text-overflow: ellipsis; white-space: nowrap; }
.compact-promo > svg { width: 15px; height: 15px; color: var(--text-subtle); }

.featured { padding-inline: 0; }
.featured .section-heading { padding-inline: var(--pad); }
.featured-track { display: flex; gap: 11px; overflow-x: auto; padding: 0 var(--pad) 5px; scroll-padding-inline: var(--pad); scroll-snap-type: x mandatory; scrollbar-width: none; }
.featured-card { min-width: 0; flex: 0 0 196px; padding: 0; overflow: hidden; border: 1px solid var(--border-default); border-radius: 15px; background: var(--vs-white); text-align: left; scroll-snap-align: start; cursor: pointer; }
.featured-card .image-wrap { position: relative; display: block; aspect-ratio: 1.44 / 1; overflow: hidden; background: var(--bg-inset); }
.featured-card img { width: 100%; height: 100%; object-fit: cover; transition: transform .35s ease; }
.featured-card:hover img { transform: scale(1.035); }
.featured-card .image-wrap b { position: absolute; left: 10px; top: 10px; padding: 5px 7px; border-radius: 6px; background: var(--ink); color: var(--text-on-dark); font-size: var(--text-2xs); font-weight: 750; }
.featured-card .card-copy { padding: 12px; display: grid; }
.featured-card strong { font-size: 12px; }
.featured-card em { margin: 4px 0 8px; font-size: var(--text-2xs); font-style: normal; font-weight: 750; }
.featured-card em small { color: var(--text-subtle); font-size: var(--text-2xs); font-weight: 500; }
.featured-card i { width: fit-content; padding: 3px 6px; border-radius: 5px; background: var(--tag-success-bg); color: var(--green); font-size: var(--text-2xs); font-style: normal; font-weight: 750; }
.featured-card i.limited { background: var(--tag-warning-bg); color: var(--tag-warning-text); }

/* Kartu grid (`.grid-card`) — anatomi kartu `card_grid` prototipe: gambar berasio 3:2
   di ATAS (badge opsional menumpang di atasnya), lalu judul, subjudul, harga, dan pil
   ketersediaan. Ia berbagi bahasa visual dengan `.featured-card` (border, radius,
   perbesaran gambar saat hover) karena di prototipe keduanya memang satu view-model.

   Kartu BARIS `.explore-card` di bawah tetap ada untuk daftar koleksi satu/dua kolom.
   Ia tidak dipakai grid lagi: pada 4 kolom kolom teksnya tersisa ~38px. */
.grid-card { min-width: 0; padding: 0; overflow: hidden; display: flex; flex-direction: column; border: 1px solid var(--border-default); border-radius: 15px; background: var(--vs-white); text-align: left; cursor: pointer; }
.grid-card .image-wrap { position: relative; display: block; aspect-ratio: 3 / 2; overflow: hidden; background: var(--bg-inset); }
.grid-card img { width: 100%; height: 100%; object-fit: cover; transition: transform .35s ease; }
.grid-card:hover img { transform: scale(1.035); }
.grid-card .image-wrap b { position: absolute; left: 10px; top: 10px; padding: 5px 7px; border-radius: 6px; background: var(--ink); color: var(--text-on-dark); font-size: var(--text-2xs); font-weight: 750; }
/* `align-content: start` menahan isi tetap di atas ketika grid meregangkan kartu
   pendek setinggi kartu tertinggi di barisnya. */
.grid-card .card-copy { padding: 12px; display: grid; align-content: start; }
.grid-card strong { font-size: var(--text-xs); line-height: 1.25; }
.grid-card .card-copy em { margin-top: 3px; color: var(--text-subtle); font-size: var(--text-2xs); font-style: normal; line-height: 1.35; }
.grid-card .card-copy b { margin-top: 7px; font-size: var(--text-2xs); }

.explore-grid { display: grid; gap: 10px; grid-template-columns: repeat(var(--cols-mobile, 1), minmax(0, 1fr)); }
.explore-card { min-width: 0; min-height: 100px; padding: 9px; display: grid; grid-template-columns: 102px 1fr 20px; gap: 13px; align-items: center; border: 1px solid var(--border-default); border-radius: 15px; background: var(--vs-white); text-align: left; cursor: pointer; }
.explore-card > img { width: 102px; height: 82px; border-radius: 10px; background: var(--bg-inset); object-fit: cover; }
.explore-card > span { min-width: 0; display: grid; }
.explore-card small { color: var(--text-subtle); font-size: var(--text-2xs); font-weight: 750; letter-spacing: .12em; }
.explore-card strong { margin: 3px 0 1px; font-size: var(--text-xs); }
.explore-card em { color: var(--text-subtle); font-size: var(--text-2xs); font-style: normal; }
.explore-card b { margin-top: 8px; font-size: var(--text-2xs); }
.explore-card b i { color: var(--text-subtle); font-size: var(--text-2xs); font-style: normal; font-weight: 500; }
.explore-card > span > i { width: fit-content; margin-top: 4px; padding: 2px 5px; border-radius: 4px; background: var(--tag-warning-bg); color: var(--tag-warning-text); font-size: var(--text-2xs); font-style: normal; font-weight: 750; }
.explore-card > svg { width: 15px; height: 15px; color: var(--text-subtle); transition: transform .2s; }
.explore-card:hover > svg { transform: translateX(3px); }

.feature-promo article { position: relative; aspect-ratio: 1.02 / 1; overflow: hidden; border-radius: 18px; background: var(--bg-ink); color: var(--text-on-dark); }
.feature-promo img { width: 100%; height: 100%; object-fit: cover; }
.feature-promo article::after { content: ""; position: absolute; inset: 0; background: linear-gradient(0deg, rgba(0,0,0,.88), rgba(0,0,0,.04) 70%); }
.feature-promo article > div { position: absolute; z-index: 1; inset: auto 22px 22px; }
.feature-promo small { color: var(--yellow); font-size: var(--text-2xs); font-weight: 800; letter-spacing: .16em; }
.feature-promo h2 { margin: 7px 0; font-size: 29px; line-height: .98; letter-spacing: -.055em; }
.feature-promo p { margin: 0 0 18px; color: var(--text-on-dark-muted); font-size: var(--text-2xs); }
.feature-promo button { padding: 10px 13px; display: flex; align-items: center; gap: 7px; border: 0; border-radius: 8px; background: var(--vs-white); color: var(--text-body); font-size: var(--text-2xs); font-weight: 750; cursor: pointer; }
.feature-promo button svg { width: 13px; height: 13px; }

.consultation { margin: 0 var(--pad); padding: 28px 23px; display: flex; align-items: end; justify-content: space-between; gap: 20px; border: 0; border-radius: 18px; background: var(--ink); color: var(--text-on-dark); }
.consultation > div > span { color: var(--yellow); font-size: var(--text-2xs); font-weight: 800; letter-spacing: .12em; }
.consultation h2 { margin: 8px 0; font-size: 25px; line-height: 1.02; letter-spacing: -.05em; }
.consultation p { max-width: 230px; margin: 0; color: var(--text-on-dark-muted); font-size: var(--text-2xs); }
.consultation > a, .consultation > button { flex: 0 0 102px; padding: 11px; display: grid; grid-template-columns: 1fr 13px; align-items: center; border: 1px solid rgba(255,255,255,.22); border-radius: 10px; font-size: var(--text-2xs); font-weight: 750; }
.consultation > a small, .consultation > button small { grid-column: 1 / -1; margin-bottom: 4px; color: var(--text-on-dark-muted); font-size: var(--text-2xs); letter-spacing: .12em; }
.consultation > a svg, .consultation > button svg { width: 13px; height: 13px; }

.highlights { padding-inline: 0; }
.highlights .section-heading { padding-inline: var(--pad); }
.gallery-track { display: flex; gap: 10px; overflow-x: auto; padding: 0 var(--pad) 5px; scroll-padding-inline: var(--pad); scroll-snap-type: x mandatory; scrollbar-width: none; }
.gallery-track button, .gallery-track > span { min-width: 0; flex: 0 0 82%; padding: 0; display: grid; border: 0; background: var(--vs-white); text-align: left; scroll-snap-align: start; cursor: zoom-in; }
.gallery-track img { width: 100%; aspect-ratio: 1.48 / 1; border-radius: 14px; object-fit: cover; }
.gallery-track span { margin-top: 9px; font-size: var(--text-2xs); font-weight: 750; }
.gallery-track small { color: var(--text-subtle); font-size: var(--text-2xs); }

.link-list { border-top: 1px solid var(--border-default); }
.link-list a, .link-list button { min-height: 67px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid var(--border-default); }
.link-list span { display: grid; gap: 3px; }
.link-list small { color: var(--text-subtle); font-size: var(--text-2xs); font-weight: 750; letter-spacing: .14em; }
.link-list strong { font-size: var(--text-2xs); }
.link-list svg { width: 16px; height: 16px; color: var(--text-subtle); transition: transform .2s; }
.link-list a:hover svg, .link-list button:hover svg { transform: translate(2px, -2px); }

.accordion { border-top: 1px solid var(--border-default); }
.accordion details { border-bottom: 1px solid var(--border-default); }
.accordion summary { min-height: 61px; display: flex; align-items: center; justify-content: space-between; gap: 15px; font-size: var(--text-2xs); font-weight: 700; cursor: pointer; list-style: none; }
.accordion summary::-webkit-details-marker { display: none; }
.accordion summary span { font-size: 17px; font-weight: 400; transition: transform .2s; }
.accordion details[open] summary span { transform: rotate(45deg); }
.accordion p { margin: -5px 32px 18px 0; color: var(--text-subtle); font-size: var(--text-2xs); }

.site-footer { padding: 47px var(--pad) 40px; border-top: 1px solid var(--border-faint); background: var(--bg-subtle); text-align: center; }
.site-footer > img { width: 202px; margin: 0 auto 24px; }
.site-footer nav { display: flex; justify-content: center; gap: 20px; font-size: var(--text-2xs); font-weight: 750; }
.site-footer p { margin: 22px 0 0; color: var(--text-subtle); font-size: var(--text-2xs); line-height: 1.8; }

.search-results { padding: 10px var(--pad) 28px; border-bottom: 1px solid var(--border-faint); }
.search-result-head { margin-bottom: 9px; display: flex; justify-content: space-between; color: var(--text-subtle); font-size: var(--text-2xs); font-weight: 700; letter-spacing: .1em; text-transform: uppercase; }
.search-result-list { display: grid; gap: 7px; }
.search-result { min-width: 0; padding: 8px; display: grid; grid-template-columns: 58px 1fr 18px; align-items: center; gap: 11px; border: 1px solid var(--border-default); border-radius: 11px; background: var(--vs-white); text-align: left; cursor: pointer; }
.search-result img { width: 58px; height: 48px; border-radius: 8px; object-fit: cover; }
.search-result > span { min-width: 0; display: grid; }
.search-result small { color: var(--text-subtle); font-size: var(--text-2xs); font-weight: 750; letter-spacing: .1em; }
.search-result strong { overflow: hidden; margin: 2px 0; font-size: var(--text-2xs); text-overflow: ellipsis; white-space: nowrap; }
.search-result em { overflow: hidden; color: var(--text-subtle); font-size: var(--text-2xs); font-style: normal; text-overflow: ellipsis; white-space: nowrap; }
.search-result > svg { width: 14px; height: 14px; }
.search-empty { padding: 28px 15px; border-radius: 12px; background: var(--bg-subtle); color: var(--text-subtle); font-size: var(--text-2xs); text-align: center; }
body.is-searching .gateway-stream > .section,
body.is-searching .gateway-stream > .site-footer { display: none; }

.drawer-backdrop { position: fixed; z-index: 80; inset: 0; background: rgba(5,5,5,.38); backdrop-filter: blur(4px); }
.menu-drawer { position: fixed; z-index: 90; top: 0; right: 0; width: min(88vw, 370px); height: 100dvh; padding: 24px 26px 27px; display: flex; flex-direction: column; background: var(--vs-white); box-shadow: -18px 0 60px rgba(0,0,0,.16); transform: translateX(105%); visibility: hidden; transition: transform .35s cubic-bezier(.22,.84,.36,1), visibility .35s; }
.menu-drawer.is-open { transform: translateX(0); visibility: visible; }
.drawer-close, .dialog-close { width: 38px; height: 38px; display: grid; place-items: center; border: 1px solid var(--border-default); border-radius: 50%; background: var(--vs-white); cursor: pointer; }
.drawer-close { margin-left: auto; }
.drawer-close svg, .dialog-close svg { width: 17px; height: 17px; }
.menu-drawer nav { margin: auto 0; display: grid; }
.menu-drawer nav button { padding: 8px 0; display: flex; justify-content: flex-end; align-items: baseline; gap: 13px; border: 0; background: none; text-align: right; cursor: pointer; }
.menu-drawer nav small { color: var(--text-disabled); font-size: var(--text-2xs); font-weight: 650; }
.menu-drawer nav span { font-size: 31px; font-weight: 750; line-height: 1.2; letter-spacing: -.05em; transition: transform .2s; }
.menu-drawer nav button:hover span { transform: translateX(-4px); }
.drawer-social { display: flex; justify-content: flex-end; gap: 8px; }
.drawer-social a { width: 35px; height: 35px; display: grid; place-items: center; border: 1px solid var(--border-default); border-radius: 50%; font-size: var(--text-2xs); font-weight: 750; }

dialog { max-width: none; padding: 0; border: 0; background: var(--vs-white); color: var(--text); }
dialog::backdrop { background: rgba(5,5,5,.52); backdrop-filter: blur(5px); }
.listing-dialog { width: min(100% - 24px, 520px); max-height: calc(100dvh - 24px); overflow: auto; border-radius: 20px; box-shadow: var(--shadow); }
.listing-dialog > .dialog-close { position: sticky; z-index: 2; top: 13px; float: right; margin: 13px 13px -51px 0; box-shadow: 0 4px 14px rgba(0,0,0,.1); }
.listing-hero { position: relative; aspect-ratio: 1.45 / 1; overflow: hidden; background: var(--bg-inset); }
.listing-hero img { width: 100%; height: 100%; object-fit: cover; }
.listing-body { clear: both; padding: 22px; }
.listing-kicker { color: var(--text-subtle); font-size: var(--text-2xs); font-weight: 750; letter-spacing: .14em; }
/* h1 DAN h2 disasar aturan yang sama: builder listing dipakai dua wadah dan hanya
   TINGKAT judulnya yang berbeda (h1 di halaman /listing/:slug, h2 di dalam dialog).
   Tanpa `.listing-body h1` halaman penuh akan jatuh ke ukuran bawaan browser dan
   tampilannya ikut berubah — padahal yang diperbaiki cuma semantik heading. */
.listing-body h1,
.listing-body h2 { margin: 7px 0 4px; font-size: 27px; line-height: 1; letter-spacing: -.05em; }
.listing-price { font-size: var(--text-2xs); font-weight: 750; }
.listing-description { margin: 14px 0 18px; color: var(--text-subtle); font-size: var(--text-2xs); }
.option-list { display: grid; gap: 7px; }
.listing-option { padding: 12px; display: grid; grid-template-columns: 18px 1fr auto; gap: 9px; align-items: center; border: 1px solid var(--border-default); border-radius: 10px; background: var(--vs-white); text-align: left; cursor: pointer; }
.listing-option i { width: 14px; height: 14px; border: 1px solid var(--ink-200); border-radius: 50%; }
.listing-option span { display: grid; }
.listing-option strong { font-size: var(--text-2xs); }
.listing-option small { color: var(--text-subtle); font-size: var(--text-2xs); }
.listing-option b { font-size: var(--text-2xs); }
.listing-option.is-selected { border-color: var(--ink); background: var(--bg-subtle); }
.listing-option.is-selected i { border: 4px solid var(--ink); }
.listing-actions { margin-top: 18px; display: grid; grid-template-columns: 1fr auto; gap: 8px; }
.listing-actions a, .listing-actions button { min-height: 45px; padding: 0 16px; display: flex; align-items: center; justify-content: center; gap: 8px; border: 1px solid var(--ink); border-radius: 9px; font-size: var(--text-2xs); font-weight: 750; cursor: pointer; }
.listing-actions a, .listing-actions [data-listing-book] { background: var(--ink); color: var(--text-on-dark); }
.listing-actions button { background: var(--vs-white); }
.listing-actions [data-listing-share] { background: var(--vs-white); }
.listing-actions svg { width: 14px; height: 14px; }

.share-dialog { width: min(100% - 32px, 410px); padding: 28px; border-radius: 19px; }
.share-dialog .dialog-close { position: absolute; top: 16px; right: 16px; }
.share-dialog > span { color: var(--text-subtle); font-size: var(--text-2xs); font-weight: 750; letter-spacing: .16em; }
.share-dialog h2 { margin: 8px 45px 6px 0; font-size: 23px; line-height: 1.05; letter-spacing: -.045em; }
.share-dialog p { margin: 0 0 20px; color: var(--text-subtle); font-size: var(--text-2xs); }
.share-dialog > div { display: grid; gap: 8px; }
.share-dialog a, .share-dialog > div button { min-height: 45px; padding: 0 13px; display: flex; justify-content: space-between; align-items: center; border: 1px solid var(--border-default); border-radius: 9px; background: var(--vs-white); font-size: var(--text-2xs); font-weight: 750; cursor: pointer; }
.share-dialog a:first-child { border-color: var(--ink); background: var(--ink); color: var(--text-on-dark); }
.share-dialog svg { width: 14px; height: 14px; }

.lightbox-dialog { width: min(100% - 24px, 880px); height: min(82dvh, 700px); overflow: hidden; border-radius: 18px; background: var(--ink); color: var(--text-on-dark); }
.lightbox-dialog .dialog-close { position: absolute; z-index: 2; right: 15px; top: 15px; border-color: rgba(255,255,255,.3); background: rgba(0,0,0,.4); color: var(--text-on-dark); }
.lightbox-dialog figure { width: 100%; height: 100%; margin: 0; display: grid; grid-template-rows: 1fr auto; }
.lightbox-dialog figure img { width: 100%; height: 100%; min-height: 0; object-fit: contain; }
.lightbox-dialog figcaption { padding: 15px 60px 18px; color: var(--text-on-dark-muted); font-size: var(--text-2xs); text-align: center; }
.lightbox-prev, .lightbox-next { position: absolute; z-index: 2; top: 50%; width: 38px; height: 38px; display: grid; place-items: center; border: 1px solid rgba(255,255,255,.28); border-radius: 50%; background: rgba(0,0,0,.38); color: var(--text-on-dark); cursor: pointer; }
.lightbox-prev { left: 13px; }
.lightbox-next { right: 13px; }
.lightbox-prev svg, .lightbox-next svg { width: 16px; height: 16px; }

/* ── Bilah navigasi bawah (mobile) ────────────────────────────────────────────
   Permintaan owner: layar publik terasa lebih "native" di ponsel.

   TIDAK ada padanannya di prototipe (ADR-G01) — satu-satunya elemen fixed-bottom di
   sana adalah toast. Jadi komponen ini TAMBAHAN atas prototipe, bukan reproduksi;
   ia sengaja meminjam bahasa yang sudah ada (ink untuk keadaan aktif, --text-subtle
   untuk istirahat, ikon dari registry kanonik) supaya tidak terbaca sebagai desain asing.

   Disembunyikan di >= 640px: di sana header + drawer sudah menampung navigasi, dan
   bilah bawah pada layar lebar justru memakan tinggi tanpa menambah jangkauan jempol.

   `env(safe-area-inset-bottom)` bukan hiasan — tanpa itu label tab tertimpa home
   indicator iPhone, dan itulah satu detail yang paling cepat membuat sebuah web
   terasa BUKAN aplikasi. */
.bottom-nav { position: fixed; z-index: 70; left: 50%; bottom: 0; width: 100%; max-width: var(--shell); transform: translateX(-50%); display: grid; grid-template-columns: repeat(4, 1fr); padding-bottom: env(safe-area-inset-bottom, 0px); border-top: 1px solid var(--border-default); background: rgba(255, 255, 255, .94); backdrop-filter: blur(14px); }
/* Target sentuh 56px: di atas minimum 44px, karena baris ini dipakai dengan jempol
   sambil berdiri, bukan dengan kursor. */
.bottom-nav button { min-height: var(--bottom-nav-row); padding: 8px 4px 9px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; border: 0; background: none; color: var(--text-subtle); font-size: var(--text-2xs); font-weight: 650; cursor: pointer; -webkit-tap-highlight-color: transparent; }
/* Ukuran ikon datang dari atribut `size` pada <vs-icon>, BUKAN dari CSS di sini:
   vs-icons.js merender ke Shadow DOM, jadi selector `.bottom-nav button svg` tidak
   pernah menjangkaunya. Aturan semacam itu bukan sekadar tidak berguna — ia
   menyiratkan kendali yang sebenarnya tidak ada. Yang MENEMBUS shadow boundary
   hanya properti terwarisi, dan `color` termasuk (HOST_CSS memakai color:inherit,
   ikonnya currentColor) — itulah sebabnya keadaan aktif di bawah memakai warna. */
/* Keadaan aktif dibawa WARNA + BOBOT, bukan warna saja: pembaca yang sulit
   membedakan abu dari hitam tetap melihat perbedaan tebal hurufnya. */
.bottom-nav button[aria-current="page"] { color: var(--text-strong); font-weight: 800; }

/* Shell diberi ruang bawah supaya isi terakhir halaman tidak tertutup bilah.
   Tingginya diturunkan dari SATU sumber (`--bottom-nav-row` + 1px border-top),
   bukan ditulis ulang sebagai angka lepas: versi pertama menyisakan 57px untuk
   bilah setinggi 59px, dan 2px terakhir halaman tertutup tanpa terlihat rusak. */
@media (max-width: 639px) {
  .site-shell { padding-bottom: calc(var(--bottom-nav-row) + 1px + env(safe-area-inset-bottom, 0px)); }
}
@media (min-width: 640px) {
  .bottom-nav { display: none; }
  .site-shell { padding-bottom: 0; }
}

.toast { position: fixed; z-index: 110; left: 50%; bottom: 22px; max-width: calc(100% - 40px); padding: 10px 15px; border-radius: 9px; background: var(--ink); color: var(--text-on-dark); box-shadow: var(--shadow); font-size: var(--text-2xs); font-weight: 650; opacity: 0; pointer-events: none; transform: translate(-50%, 14px); transition: opacity .2s, transform .2s; }
.toast.is-visible { opacity: 1; transform: translate(-50%, 0); }

/* Gaya dialog form konsultasi DICABUT bersama JS-nya (R5 · Integrasi): rute
   `/f/:slug` kini punya runtime sendiri (`.vsform-*`). `.consultation` — kartu
   ajakan pada blok form_card — TETAP dipakai dan tidak ikut dihapus. */

.schedule-view { min-height: 100vh; background: var(--surface); }
.subpage-header { position: sticky; z-index: 30; top: 0; min-height: 68px; padding: 10px var(--pad); display: grid; grid-template-columns: 76px 1fr 65px; align-items: center; border-bottom: 1px solid var(--border-default); background: rgba(255,255,255,.95); backdrop-filter: blur(14px); }
.subpage-header button { padding: 7px 0; display: flex; align-items: center; gap: 4px; border: 0; background: none; font-size: var(--text-2xs); font-weight: 700; cursor: pointer; }
.subpage-header button:last-child { justify-content: flex-end; }
.subpage-header button svg { width: 15px; height: 15px; }
.subpage-header > div { min-width: 0; display: grid; text-align: center; }
.subpage-header strong { font-size: var(--text-2xs); }
.subpage-header small { overflow: hidden; color: var(--text-subtle); font-size: var(--text-2xs); text-overflow: ellipsis; white-space: nowrap; }
.schedule-intro { padding: 18px var(--pad) 16px; }
.schedule-intro h1 { margin: 0 0 12px; font-size: 26px; line-height: 1; letter-spacing: -.055em; }

/* Kepala jadwal = BILAH STATUS OPERASIONAL, bukan sampul halaman.
   SPEC: "18.02 Dokumentasi Teknis/SPEC — Kepala Layar Jadwal (schedule-intro) — 2026-08-23.md"

   Versi sebelumnya menumpuk tiga fakta sebagai tiga kartu (187px) sehingga kartu ruang
   pertama baru mulai di y=589 dari viewport 812 — 72% layar habis sebelum jadwalnya
   terlihat. Sekarang jam + pil berbagi satu baris, dan tanggal turun jadi baris teks. */
.schedule-facts { display: flex; flex-wrap: wrap; align-items: stretch; gap: var(--space-3); }
.schedule-fact { min-width: 0; margin: 0; padding: 8px 12px; display: grid; align-content: center; gap: 1px; border: 1px solid var(--border-default); border-radius: 12px; background: var(--vs-white); }
.schedule-fact > strong { color: var(--text-subtle); font-family: var(--font-mono); font-size: var(--text-2xs); font-weight: 500; letter-spacing: var(--ls-label-sm); text-transform: uppercase; }

/* DIVERGENSI: prototipe menaruh jam operasional sebagai kicker mono 10px — justru yang
   PALING kecil. Owner meminta ia paling menonjol, TAPI tidak besar. Jadi penonjolannya
   dibawa KONTRAS (satu-satunya permukaan gelap di kepala), bukan ukuran: nilainya 14px.
   `tabular-nums` menjaga lebar "10:00-19:00" tetap saat jam berganti musim (10-19 / 10-22). */
.schedule-fact.is-hours { flex: 1 1 auto; border-color: var(--ink); background: var(--bg-ink); }
.schedule-fact.is-hours > strong { color: var(--text-on-dark-muted); }
.schedule-fact.is-hours > span { color: var(--text-on-dark); font-size: var(--text-sm); font-weight: var(--fw-bold); letter-spacing: -.01em; font-variant-numeric: tabular-nums; }

/* Pil jumlah slot. Teksnya sudah membawa labelnya sendiri ("29 SLOT TERBUKA", ditulis
   app.js), jadi ia tidak diberi <strong> label lagi. */
.schedule-facts > b { flex: none; align-self: stretch; display: flex; align-items: center; padding: 0 9px; border-radius: 12px; background: var(--yellow); color: var(--ink); font-size: var(--text-2xs); letter-spacing: .08em; }

/* Tanggal hidup DI DALAM kartu jam: keduanya menjawab satu pertanyaan yang sama
   ("hari apa, buka jam berapa"), jadi memisahkannya jadi dua blok memecah satu fakta
   menjadi dua. Tetap <h2> supaya outline dokumen (h1 -> h2) tidak putus — dan karena
   itulah pembungkus kartunya <div>, bukan <p>: <p> tidak boleh memuat <h2>, parser
   akan menutupnya lebih awal dan strukturnya pecah tanpa satu pun pesan galat. */
.schedule-fact.is-hours > h2 { margin: 2px 0 0; color: var(--text-on-dark-muted); font-size: var(--text-2xs); font-weight: var(--fw-semibold); }

.schedule-legend { padding: 9px var(--pad); display: flex; flex-wrap: wrap; gap: 10px; border-block: 1px solid var(--border-faint); color: var(--text-subtle); font-size: var(--text-2xs); font-weight: 650; }
.schedule-legend span { display: flex; align-items: center; gap: 5px; }
.schedule-legend i { width: 10px; height: 10px; border: 1px solid var(--border-input); border-radius: 3px; background: var(--vs-white); }
.schedule-legend i.hold { border-color: var(--brand-line); background: var(--brand-soft); }
.schedule-legend i.booked { border-color: var(--ink); background: var(--ink); }
.schedule-legend i.past { background: var(--bg-inset); }
.schedule-status { margin: 15px var(--pad) 0; padding: 10px 12px; border: 1px solid var(--border-default); border-radius: 9px; background: var(--bg-subtle); color: var(--text-subtle); font-size: var(--text-2xs); }
.schedule-status.is-live { border-color: var(--tag-success-border); background: var(--tag-success-bg); color: var(--tag-success-text); }
.schedule-status.is-fallback { border-color: var(--tag-warning-border); background: var(--tag-warning-bg); color: var(--tag-warning-text); }
.schedule-status button { margin-left: 7px; padding: 0; border: 0; background: none; color: inherit; font-size: inherit; font-weight: 800; text-decoration: underline; cursor: pointer; }
.schedule-rooms { padding: 17px var(--pad) 30px; display: grid; gap: 14px; }
.schedule-room { overflow: hidden; border: 1px solid var(--border-default); border-radius: 16px; background: var(--vs-white); }
.schedule-room > img { width: 100%; aspect-ratio: 1.88 / 1; object-fit: cover; }
.schedule-room-copy { padding: 14px 14px 10px; display: flex; justify-content: space-between; gap: 15px; }
.schedule-room-copy > span { min-width: 0; display: grid; }
.schedule-room-copy strong { font-size: 12px; }
.schedule-room-copy small { overflow: hidden; color: var(--text-subtle); font-size: var(--text-2xs); text-overflow: ellipsis; white-space: nowrap; }
.schedule-room-copy b { flex: none; color: var(--green); font-size: var(--text-2xs); }
/* Strip slot MEMBUNGKUS, tidak menggulir mendatar.
   Rail horizontal menyembunyikan jam-jam terakhir di balik tepi kartu: pengunjung
   yang mencari slot sore harus menebak bahwa strip itu bisa digeser, dan di kartu
   dua-kolom (≥640px) yang terlihat tinggal empat jam pertama. Dengan `auto-fill`
   seluruh jam tampil sekaligus, kolomnya SEJAJAR antar baris (itu sebabnya grid,
   bukan `flex-wrap` yang meninggalkan tepi kanan bergerigi), dan jumlah kolom
   mengikuti lebar kartu — sama untuk shell 560px maupun 980px tanpa media query. */
.slot-strip { display: grid; grid-template-columns: repeat(auto-fill, minmax(52px, 1fr)); gap: 6px; padding: 4px 14px 14px; }
.slot { height: 52px; padding: 0; display: grid; place-items: center; border: 1px solid var(--border-input); border-radius: 8px; background: var(--vs-white); font-size: var(--text-2xs); font-weight: 700; cursor: pointer; }
.slot:hover:not(:disabled) { border-color: var(--ink); }
.slot.hold { border-color: var(--brand-line); background: var(--brand-soft); color: var(--tag-identity-text); }
.slot.booked { border-color: var(--ink); background: var(--ink); color: var(--text-on-dark); }
.slot.past { background: var(--bg-inset); color: var(--text-disabled); }
.slot:disabled { cursor: not-allowed; }
/* R8 - baris tanggal (mencabut "hari ini saja" PRD 12.49), pemilih durasi, dan
   status `closed`. Tiga penambahan pada keluarga .schedule-* yang sudah ada;
   tidak ada keluarga kelas baru, dan tidak ada nilai warna literal. */
.schedule-dates { padding: 0 var(--pad) 4px; display: flex; gap: 6px; overflow-x: auto; scrollbar-width: none; }
.schedule-dates::-webkit-scrollbar { display: none; }
.schedule-date { flex: 0 0 auto; min-height: var(--field-h-sm); padding: 0 12px; border: 1px solid var(--border-input); border-radius: var(--radius-sm); background: var(--vs-white); color: var(--text-body); font-size: var(--text-2xs); font-weight: 700; cursor: pointer; }
.schedule-date:hover { border-color: var(--ink); }
.schedule-date.is-today { border-color: var(--brand-line); }
.schedule-date.is-active { border-color: var(--ink); background: var(--ink); color: var(--text-on-dark); }
.schedule-durations-host { padding: 0 var(--pad) 4px; }
.schedule-durations-host[hidden] { display: none; }
.schedule-durations { display: flex; gap: 6px; }
.schedule-duration { min-height: var(--field-h-sm); padding: 0 12px; border: 1px solid var(--border-input); border-radius: var(--radius-pill); background: var(--vs-white); color: var(--text-body); font-size: var(--text-2xs); font-weight: 700; cursor: pointer; }
.schedule-duration.is-active { border-color: var(--ink); background: var(--yellow); color: var(--ink); }
.schedule-legend i.closed { border-color: var(--border-strong); background: repeating-linear-gradient(135deg, var(--bg-inset), var(--bg-inset) 2px, var(--vs-white) 2px, var(--vs-white) 4px); }
.slot.closed { border-color: var(--border-strong); background: repeating-linear-gradient(135deg, var(--bg-inset), var(--bg-inset) 3px, var(--vs-white) 3px, var(--vs-white) 6px); color: var(--text-disabled); }
.slot.is-selected { border-color: var(--ink); background: var(--yellow); color: var(--ink); }
.schedule-room-copy b.is-closed { color: var(--text-subtle); }
/* Blok `studio_schedule` di dalam Page: pembungkusnya .section, jadi padding
   horizontal keluarga .schedule-* dinolkan agar tidak dobel dengan padding section. */
.block-studio-schedule .schedule-dates, .block-studio-schedule .schedule-durations-host, .block-studio-schedule .schedule-rooms, .block-studio-schedule .schedule-legend { padding-left: 0; padding-right: 0; }
.block-studio-schedule .schedule-status { margin-left: 0; margin-right: 0; }
.block-studio-schedule .schedule-rooms { padding-top: 14px; padding-bottom: 0; }
.schedule-empty { padding: 30px 20px; border: 1px dashed var(--border-input); border-radius: 13px; color: var(--text-subtle); font-size: var(--text-2xs); text-align: center; }
.schedule-contact { padding: 0 var(--pad) 40px; display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.schedule-contact a, .schedule-contact button { min-width: 0; padding: 12px; display: grid; grid-template-columns: 22px 1fr 14px; gap: 8px; align-items: center; border: 1px solid var(--border-default); border-radius: 11px; }
.schedule-contact > a > svg, .schedule-contact > button > svg { width: 17px; height: 17px; }
.schedule-contact > a > svg:last-child, .schedule-contact > button > svg:last-child { width: 12px; height: 12px; }
.schedule-contact span { min-width: 0; display: grid; }
.schedule-contact small { color: var(--text-subtle); font-size: var(--text-2xs); font-weight: 750; letter-spacing: .1em; }
.schedule-contact strong { overflow: hidden; font-size: var(--text-2xs); text-overflow: ellipsis; white-space: nowrap; }

@media (min-width: 640px) {
  :root { --shell: 640px; --pad: 28px; }
  .site-header { height: 72px; }
  .brand, .brand img { width: 155px; }
  .hero-pad { padding-top: 29px; }
  .shortcut-grid { column-gap: 16px; }
  .shortcut { font-size: var(--text-2xs); }
  .shortcut > span { width: 57px; height: 57px; }
  .promo-hero { flex-basis: calc(100% - 42px); }
  .compact-promos { grid-template-columns: 1fr 1fr; }
  .featured-card { flex-basis: 224px; }
  .explore-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .explore-card { grid-template-columns: 86px 1fr 17px; }
  .explore-card > img { width: 86px; height: 82px; }
  .feature-promo article { aspect-ratio: 1.5 / 1; }
  .gallery-track button, .gallery-track > span { flex-basis: 47%; }
  .schedule-rooms { grid-template-columns: 1fr 1fr; }
  .schedule-intro { padding-top: 26px; }
  .schedule-intro > span { display: block; }
  .schedule-intro h1 { margin-bottom: 16px; font-size: 32px; }
}

@media (min-width: 1024px) {
  :root { --shell: 980px; --pad: 42px; }
  .site-header { height: 76px; }
  .hero-pad { padding-top: 34px; padding-bottom: 30px; }
  .shortcut-grid { column-gap: 25px; }
  .shortcut > span { width: 61px; height: 61px; }
  .promo-hero { flex-basis: calc(100% - 65px); aspect-ratio: 2.3 / 1; }
  .promo-overlay { padding: 34px; }
  .promo-overlay h2 { font-size: 42px; }
  .section { padding-top: 45px; padding-bottom: 45px; }
  .compact-promos { grid-template-columns: 1fr 1fr; }
  .featured-track { display: grid; grid-template-columns: repeat(3, 1fr); overflow: visible; }
  .featured-card { width: auto; }
  .explore-grid { grid-template-columns: repeat(var(--cols-desktop, 2), minmax(0, 1fr)); }
  .explore-card { grid-template-columns: 112px 1fr 20px; }
  .explore-card > img { width: 112px; height: 90px; }
  .feature-promo article { aspect-ratio: 2.15 / 1; }
  .feature-promo h2 { font-size: 40px; }
  .consultation { margin-inline: var(--pad); padding: 38px 36px; }
  .consultation h2 { font-size: 34px; }
  .gallery-track { display: grid; grid-template-columns: repeat(3, 1fr); overflow: visible; }
  .gallery-track button, .gallery-track > span { width: auto; }
  .quick-links, .faq { display: grid; grid-template-columns: 220px 1fr; gap: 60px; }
  .quick-links .section-heading, .faq .section-heading { align-self: start; }
  .search-result-list { grid-template-columns: 1fr 1fr; }
  .schedule-rooms { grid-template-columns: 1fr 1fr; gap: 18px; }
  .schedule-intro h1 { font-size: 42px; }
}

@media (max-width: 540px) {
  .listing-dialog { width: 100%; max-height: 92dvh; margin: auto 0 0; border-radius: 20px 20px 0 0; }
  .listing-dialog[open] { animation: sheet-in .28s ease-out; }
  @keyframes sheet-in { from { transform: translateY(30px); opacity: 0; } }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { scroll-behavior: auto !important; transition-duration: .01ms !important; animation-duration: .01ms !important; animation-iteration-count: 1 !important; }
}


/* ── Blok berbasis registry (R2) ─────────────────────────────────────────────
   Seluruh isi beranda kini dirender dari Block Registry ke `.gateway-stream`;
   simpul warisan `data-legacy-block` sudah dicabut bersama markup statisnya.
   Aturan di bawah HANYA menambahkan yang belum punya anatomi — kelas lama
   (.site-header, .hero-pad, .promo-hero, .featured-card, …) dipakai ulang apa
   adanya supaya hasil render identik dengan desain yang sudah disetujui. */
.block-rich-text { margin: 0 var(--pad); }
.vs-rich { color: var(--text-body); font-size: var(--text-sm); line-height: var(--lh-relaxed); }
.vs-rich > :first-child { margin-top: 0; }
.vs-rich > :last-child { margin-bottom: 0; }
.vs-rich h2 { margin: var(--space-7) 0 var(--space-4); font-family: var(--font-display); font-size: var(--text-h3); font-weight: var(--fw-bold); line-height: var(--lh-snug); letter-spacing: var(--ls-tight); }
.vs-rich h3 { margin: var(--space-6) 0 var(--space-3); font-family: var(--font-display); font-size: var(--text-h4); font-weight: var(--fw-semibold); line-height: var(--lh-snug); }
.vs-rich p { margin: 0 0 var(--space-4); }
.vs-rich strong { font-weight: var(--fw-semibold); color: var(--text-strong); }
.vs-rich ul, .vs-rich ol { margin: 0 0 var(--space-4); padding-left: var(--space-6); display: grid; gap: var(--space-2); }
.vs-rich li { margin: 0; }
.vs-rich blockquote { margin: 0 0 var(--space-4); padding-left: var(--space-5); border-left: 2px solid var(--border-default); color: var(--text-muted); }
.vs-rich a { color: var(--text-strong); text-decoration: underline; text-underline-offset: 3px; }
.vs-rich[data-heading="editorial"] h2 { font-size: var(--text-h2); letter-spacing: var(--ls-display); }

.block-divider { margin: var(--space-7) var(--pad); }
.block-divider hr { margin: 0; border: 0; border-top: 1px solid var(--border-default); }

.block-spacer[data-size="s"] { height: var(--space-6); }
.block-spacer[data-size="m"] { height: var(--space-9); }
.block-spacer[data-size="l"] { height: var(--space-12); }

/* Header blok. `display: contents` menjaga <header class="site-header"> tetap
   menjadi anak KOTAK dari .gateway-stream, sehingga position:sticky-nya masih
   punya containing block setinggi seluruh stream. Pembungkus biasa akan membuat
   header berhenti menempel setelah beberapa piksel. */
.block-header { display: contents; }
.block-hero-meta { padding-bottom: 0; }
/* Padding hero dijahit lewat sibling: baris deskriptor → kotak cari → menu grid
   harus terbaca sebagai SATU blok hero seperti markup lamanya. */
.block-header + .block-search { padding-top: 0; }
/* `.search-results` (panel hasil, biasanya [hidden]) berdiri sebagai sibling DI ANTARA
   kotak cari dan menu grid, sehingga selector adjacent `+` yang dipakai sebelumnya
   tidak pernah cocok: jarak 22px yang tampil selama ini adalah padding-bottom kotak
   cari yang tidak pernah dinihilkan, bukan angka yang dirancang. Dipakai `~` (menu
   grid unique per gateway dan selalu sesudah kotak cari), lalu jarak antar-grup
   ditulis SEKALI di .block-menu-grid — bukan hasil penjumlahan dua padding tetangga. */
.block-search:has(~ .block-menu-grid) { padding-bottom: 0; }
/* Jarak grup hero harus jelas lebih besar dari ritme di dalam grid (row-gap 17px);
   kalau tidak, kotak cari dan menu grid terbaca sebagai satu tumpukan rapat. Fluid
   --space-8 → --space-9 mengikuti lebar layar, dan simetris atas-bawah supaya garis
   pemisah seksi berikutnya tidak menempel ke label baris terakhir. */
.block-menu-grid { padding-block: clamp(var(--space-8), 4.5vw, var(--space-9)); }
.block-menu-grid .shortcut-grid { margin-top: 0; }
/* Saat mencari, `.search-results` menyelinap DI ANTARA kotak cari dan menu grid.
   Padding bawah yang dinihilkan di atas harus dikembalikan khusus untuk keadaan
   ini — tanpa aturan ini hasil pertama menempel ke kotak carinya. */
body.is-searching .block-search:has(~ .block-menu-grid) { padding-bottom: var(--space-6); }


/* Meta kepala seksi — hitungan turunan data yang menggantikan kicker editorial
   literal (tidak ada schema blok yang menyimpan teks kicker). */
.section-meta { flex: none; color: var(--text-subtle); font-family: var(--font-mono); font-size: var(--text-2xs); font-weight: var(--fw-medium); letter-spacing: .12em; text-transform: uppercase; }

/* promo_slider = banner gambar saja: tanpa overlay teks, tanpa scrim. */
.promo-hero.is-plain::after { content: none; }
/* promo_card layout "banner" memakai anatomi .promo-hero di luar rail slider. */
.promo-banner { padding-inline: var(--pad); }
.promo-banner .promo-hero { flex: none; }
/* Satu blok promo_card = satu kartu compact. Kartu compact yang berurutan
   dirapatkan supaya terbaca sebagai satu kelompok, seperti markup lamanya. */
.compact-promos + .compact-promos { padding-top: 0; border-top: 0; }

/* Nada pil ketersediaan. Anatomi dasarnya sudah ada (.featured-card i /
   .explore-card > span > i); di sini hanya nada yang belum terpetakan. */
.featured-card i.limited, .explore-card > span > i.limited, .grid-card .card-copy > i.limited { background: var(--tag-warning-bg); color: var(--tag-warning-text); }
.featured-card i.soon, .explore-card > span > i.soon, .grid-card .card-copy > i.soon { background: var(--tag-info-bg); color: var(--tag-info-text); }
.featured-card i.full, .explore-card > span > i.full, .grid-card .card-copy > i.full { background: var(--tag-danger-bg); color: var(--tag-danger-text); }
.explore-card > span > i { background: var(--tag-success-bg); color: var(--green); }
.grid-card .card-copy > i { width: fit-content; margin-top: 7px; padding: 3px 6px; border-radius: 5px; background: var(--tag-success-bg); color: var(--green); font-size: var(--text-2xs); font-style: normal; font-weight: 750; }

/* Jawaban FAQ & Terms adalah rich text, bukan satu <p>. */
.accordion .accordion-answer { margin: -5px 32px 18px 0; color: var(--text-subtle); font-size: var(--text-2xs); }
.accordion .accordion-answer > :last-child { margin-bottom: 0; }
.accordion .accordion-answer p { margin: 0 0 var(--space-3); }

/* Social Links — gaya list berteks (keputusan owner 13 Agu 2026: registry ikon
   kanonik sengaja tidak memuat brand mark, jadi gaya ikon menunggu upstream). */
.social-list { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-5); font-size: var(--text-2xs); font-weight: 750; }
.social-list a { text-decoration: underline; text-underline-offset: 3px; }

/* Hasil pencarian tanpa gambar tetap mengisi kolom thumbnail grid-nya. */
.search-result .search-thumb { width: 58px; height: 48px; border-radius: 8px; background: var(--bg-inset); }

/* Kegagalan memuat konten tidak boleh terlihat seperti halaman yang memang kosong. */
.stream-error { display: grid; justify-items: start; gap: var(--space-3); }
.stream-error h2 { margin: 0; font-size: 23px; line-height: 1.1; letter-spacing: -.045em; }
.stream-error p { margin: 0; color: var(--text-subtle); font-size: var(--text-2xs); }
.stream-error button { min-height: 44px; margin-top: var(--space-2); padding: 0 16px; border: 1px solid var(--ink); border-radius: 9px; background: var(--ink); color: var(--text-on-dark); font-size: var(--text-2xs); font-weight: 750; cursor: pointer; }
.stream-error button:disabled { opacity: .5; cursor: progress; }

/* ── Permukaan rute (R3) ─────────────────────────────────────────────────────
   Enam rute bersih memakai kerangka yang SUDAH ADA: topbar `.subpage-header`
   (styles.css:292, sama dengan layar Jadwal) dan `.section` sebagai wadah.
   Yang baru hanya kepala rute, status screen, dan halaman promo.            */

.route-head { display: grid; gap: var(--space-2); }
.route-head > span { color: var(--text-subtle); font-size: var(--text-2xs); font-weight: 750; letter-spacing: .18em; text-transform: uppercase; }
.route-head h1 { margin: 0; font-size: 29px; line-height: 1.02; letter-spacing: -.05em; }
.route-head .vs-rich { margin-top: var(--space-2); }
.route-empty { margin: 0; color: var(--text-subtle); font-size: var(--text-2xs); }

/* Detail listing sebagai HALAMAN. Anatominya sama persis dengan di dalam dialog —
   satu builder, dua wadah — jadi yang perlu diatur hanya pembatalan padding
   `.section` supaya hero-nya menempel tepi seperti di dialog. */
.route-listing { padding-inline: 0; padding-top: 0; border-top: 0; }
.route-listing .listing-hero { border-radius: 0; }

/* ── Detail promo `/promo/:slug` ─────────────────────────────────────────────
   Kartu mandiri, bukan elemen yang mengambang langsung di `.section`. Hero
   menempel ke tepi kartu — radius dan `overflow: clip` dipegang WADAH, jadi
   gambar tidak butuh radius sendiri dan tidak akan meleset setengah piksel di
   sudut. Anatomi kartunya meminjam kartu status 404 (border + radius-lg +
   surface-0 + shadow-card) supaya permukaan publik memakai satu bahasa.

   Skalanya fluid, bukan berbreakpoint: judul memakai clamp() dan grid "promo
   lain" memakai auto-fill, sehingga shell 560px → 980px tidak perlu media query
   tersendiri dan lebar di antaranya tidak pernah jatuh ke tata letak yang salah. */
.route-promo { display: grid; gap: var(--space-9); }

.promo-detail { display: grid; overflow: clip; border: 1px solid var(--border-default); border-radius: var(--radius-lg); background: var(--surface-0); box-shadow: var(--shadow-card); }
.promo-detail-hero { position: relative; aspect-ratio: 1.72 / 1; background: var(--bg-inset); }
.promo-detail-hero img { width: 100%; height: 100%; object-fit: cover; }
/* Badge = angka penawaran; ditempel di hero sebagai chip merek, bukan teks biasa. */
.promo-detail-badge { position: absolute; top: var(--space-4); left: var(--space-4); padding: var(--space-2) var(--space-3); border-radius: var(--radius-pill); background: var(--brand); color: var(--brand-ink); font-size: var(--text-2xs); font-weight: var(--fw-bold); letter-spacing: var(--ls-label-sm); }
.promo-detail-badge.is-inline { position: static; justify-self: start; }
.promo-detail-body { padding: var(--space-6); display: grid; gap: var(--space-3); }
.promo-detail-label { color: var(--accent-orange); font-size: var(--text-2xs); font-weight: 800; letter-spacing: .16em; }
.promo-detail-body h1 { margin: 0; font-family: var(--font-display); font-size: clamp(26px, 6.4vw, var(--text-h1)); line-height: var(--lh-tight); letter-spacing: var(--ls-display); }
.promo-detail-sub { margin: 0; color: var(--text-muted); font-size: var(--text-sm); line-height: var(--lh-relaxed); }
.promo-detail-period { margin: 0; display: flex; align-items: center; gap: var(--space-2); color: var(--text-subtle); font-size: var(--text-2xs); font-weight: var(--fw-semibold); letter-spacing: .06em; }
.promo-detail-period svg { flex: none; width: 14px; height: 14px; }
.promo-detail-cta { width: 100%; margin-top: var(--space-2); }

/* Promo lain — satu kartu per promo. Kolomnya ditentukan lebar yang tersedia
   (`auto-fill` + `min()`), jadi kartu tidak pernah lebih sempit dari 232px dan
   tidak pernah melebar melewati satu kolom penuh di layar sempit. */
.promo-more { display: grid; gap: var(--space-5); }
.promo-more-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 232px), 1fr)); gap: var(--space-4); }
.promo-mini { min-width: 0; display: grid; grid-template-rows: auto 1fr; overflow: clip; border: 1px solid var(--border-default); border-radius: var(--radius-md); background: var(--surface-0); cursor: pointer; transition: transform .2s, border-color .2s, box-shadow .2s; }
.promo-mini:hover { transform: translateY(-2px); border-color: var(--ink-200); box-shadow: var(--shadow-card); }
.promo-mini:focus-visible { outline: 0; box-shadow: var(--shadow-focus); }
.promo-mini-thumb { position: relative; aspect-ratio: 16 / 10; background: var(--bg-inset); }
.promo-mini-thumb img { width: 100%; height: 100%; object-fit: cover; }
.promo-mini-badge { position: absolute; top: var(--space-3); left: var(--space-3); padding: var(--space-1) var(--space-2); border-radius: var(--radius-pill); background: var(--brand); color: var(--brand-ink); font-size: var(--text-2xs); font-weight: var(--fw-bold); }
.promo-mini-body { padding: var(--space-4); display: grid; align-content: start; gap: var(--space-1); }
.promo-mini-body small { color: var(--accent-orange); font-size: var(--text-2xs); font-weight: 800; letter-spacing: .14em; text-transform: uppercase; }
.promo-mini-body strong { color: var(--text-strong); font-size: var(--text-xs); line-height: var(--lh-snug); }
/* Subtitle dipotong dua baris supaya tinggi kartu tetap rata di satu baris grid. */
.promo-mini-body span { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; color: var(--text-subtle); font-size: var(--text-2xs); line-height: var(--lh-normal); }

/* R8 - hero berbranding form publik. Empat mode (foto / solid / gradient /
   pattern glyph) semuanya menulis gaya INLINE dari formHeaderStyle(); aturan di
   bawah hanya mengurus tata letak, scrim, dan warna teks. Tidak ada nilai warna
   literal: dua kelas tinta memakai token --text-on-dark dan --ink. */
.route-form-hero { position: relative; padding: var(--space-7) var(--pad); display: flex; align-items: flex-end; overflow: hidden; background-color: var(--bg-inset); }
.route-form-scrim { position: absolute; inset: 0; background: var(--ink); pointer-events: none; }
/* Scrim mengikuti arah tinta: latar gelap diredam dengan gelap, latar terang
   dengan terang. Scrim hitam di atas latar terang justru memperburuk kontras. */
.route-form-hero.is-ink-dark .route-form-scrim { background: var(--vs-white); }
.route-form-hero-copy { position: relative; display: grid; gap: var(--space-2); width: 100%; }
.route-form-hero-copy > span { font-size: var(--text-2xs); font-weight: 750; letter-spacing: .18em; text-transform: uppercase; opacity: .78; }
.route-form-hero-copy h1 { margin: 0; font-size: 29px; line-height: 1.02; letter-spacing: -.05em; }
.route-form-hero.is-ink-light { color: var(--text-on-dark); }
.route-form-hero.is-ink-dark { color: var(--ink); }
.route-form-mount { display: grid; gap: var(--space-3); }
.route-form-note { margin: 0; color: var(--text-subtle); font-size: var(--text-2xs); }

.route-cta { min-height: 45px; padding: 0 16px; display: inline-flex; align-items: center; justify-content: center; gap: 8px; border: 1px solid var(--ink); border-radius: 9px; background: var(--ink); color: var(--text-on-dark); font-size: var(--text-2xs); font-weight: 750; cursor: pointer; }
.route-cta svg { width: 16px; height: 16px; }

/* Status screen — 404 dan seluruh keadaan gating memakai SATU kartu, persis seperti
   `notFoundVals()` prototipe yang dipakai ulang enam kali.

   Anatomi direproduksi dari kartu 404 rancangan owner:
   12.04.02/Storefront & Public.dc.html:714. Tiga nilai inline master diterjemahkan
   ke token karena skala token yang menang (USAGE.md):
     font-weight 800   → --fw-bold (skala token berhenti di 700)
     letter-spacing -0.03em → --ls-display (-0.02em)
     padding 34px      → --space-8 (32px)
   Baris kode mono mengikuti pola `GUI/21/DENIED` ("ERROR 403 · FORBIDDEN"). */
.route-status { padding-top: var(--space-8); }
.status-card { padding: var(--space-8); display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-3); border: 1px solid var(--border-default); border-radius: var(--radius-md); background: var(--surface-0); box-shadow: var(--shadow-card); }
.status-code { font-family: var(--font-display); font-size: var(--text-display-lg); font-weight: var(--fw-bold); letter-spacing: var(--ls-display); line-height: 1; color: var(--text-strong); }
.status-kicker { font-family: var(--font-mono); font-size: var(--text-2xs); letter-spacing: var(--ls-label-sm); text-transform: uppercase; color: var(--text-subtle); }
.status-card h2 { margin: 0; font-family: var(--font-display); font-size: var(--text-h4); font-weight: var(--fw-bold); color: var(--text-strong); }
.status-card p { margin: 0; color: var(--text-muted); font-size: var(--text-sm); line-height: var(--lh-relaxed); }
.status-cta { min-height: var(--field-h-sm); margin-top: var(--space-1); padding: 0 var(--space-4); display: inline-flex; align-items: center; border: 0; border-radius: var(--radius-xs); background: var(--primary); color: var(--primary-contrast); font-size: var(--text-xs); font-weight: var(--fw-semibold); cursor: pointer; }
.status-cta:hover { background: var(--primary-hover); }
.status-cta:focus-visible { outline: 0; box-shadow: var(--shadow-focus); }

/* ── Runtime form publik `/f/:slug` (R5 · Integrasi) ─────────────────────────
   Satu permukaan untuk 21 tipe field, multi-step, dan layar kunci PIN. Anatominya
   mewarisi anatomi form konsultasi yang digantikannya — label di atas kontrol,
   kontrol selebar penuh, tinggi sentuh 44px — supaya perpindahannya tidak terasa
   seperti berganti produk. Stepper direproduksi dari GUI/09/STEPPER. */
.vsform { display: grid; gap: var(--space-4); }
.vsform-panel { display: grid; gap: var(--space-4); }
.vsform-field { display: grid; gap: var(--space-2); }
.vsform-field > span { color: var(--text-body); font-size: var(--text-2xs); font-weight: var(--fw-semibold); }
.vsform-req { margin-left: 3px; color: var(--accent-red); font-style: normal; }
.vsform-field input, .vsform-field select, .vsform-field textarea {
  width: 100%; min-height: var(--field-h); padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border-input); border-radius: var(--radius-md);
  background: var(--vs-white); color: var(--text-strong); font: inherit; font-size: var(--text-2xs); outline: 0;
}
.vsform-field textarea { min-height: calc(var(--field-h) * 2); resize: vertical; }
.vsform-field input:focus-visible, .vsform-field select:focus-visible, .vsform-field textarea:focus-visible {
  border-color: var(--border-strong); box-shadow: var(--shadow-focus);
}
.vsform-hint { color: var(--text-subtle); font-size: var(--text-2xs); line-height: var(--lh-normal); }
.vsform-warn { color: var(--tag-danger-text); font-size: var(--text-2xs); }

/* Pilihan berbasis kotak centang / radio. Seluruh baris dapat diklik. */
.vsform-choices { display: grid; gap: var(--space-2); }
.vsform-check {
  display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: var(--space-3);
  min-height: var(--field-h); padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border-faint); border-radius: var(--radius-md);
  font-size: var(--text-2xs); cursor: pointer;
}
.vsform-check:hover { border-color: var(--border-default); background: var(--surface-1); }
.vsform-check input { width: 17px; height: 17px; margin: 0; accent-color: var(--ink-900); }

/* Pilihan visual: gallery pick, visual grid, card select. Kartu mengisi lebar
   yang ada — bukan jumlah kolom tetap, karena rute ini dibuka di layar mana pun. */
.vsform-visual { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: var(--space-3); }
.vsform-visual.is-card { grid-template-columns: minmax(0, 1fr); }
.vsform-visual-item {
  display: grid; gap: var(--space-2); padding: var(--space-3); text-align: left;
  border: 1px solid var(--border-faint); border-radius: var(--radius-md);
  background: var(--bg-card); font: inherit; cursor: pointer;
}
.vsform-visual.is-card .vsform-visual-item { grid-template-columns: minmax(0, 1fr) auto; align-items: center; }
.vsform-visual-item:hover { border-color: var(--border-default); }
.vsform-visual-item.is-on { border-color: var(--ink-900); box-shadow: var(--shadow-card); }
.vsform-visual-item:disabled { opacity: .45; cursor: not-allowed; }
.vsform-visual-item img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--radius-sm); }
.vsform-visual-copy { min-width: 0; display: grid; gap: 2px; }
.vsform-visual-copy strong { font-size: var(--text-2xs); font-weight: var(--fw-semibold); }
.vsform-visual-copy b { color: var(--text-body); font-family: var(--font-mono); font-size: var(--text-2xs); }
.vsform-visual-copy small { color: var(--text-subtle); font-size: var(--text-2xs); line-height: var(--lh-normal); }
.vsform-visual-copy i {
  justify-self: start; padding: 1px var(--space-2); border-radius: var(--radius-pill);
  background: var(--brand); color: var(--brand-ink); font-size: var(--text-2xs); font-style: normal; font-weight: var(--fw-semibold);
}

/* Konten struktural — judul, teks, pemisah. */
.vsform-heading { margin: var(--space-2) 0 0; font-size: var(--text-md); letter-spacing: var(--ls-tight); }
.vsform-desc { margin: 0; color: var(--text-muted); font-size: var(--text-2xs); line-height: var(--lh-relaxed); }
.vsform-divider { width: 100%; height: 1px; margin: var(--space-2) 0; border: 0; background: var(--border-faint); }
.vsform-rich { font-size: var(--text-2xs); line-height: var(--lh-relaxed); }

/* Persetujuan: dokumennya dapat dibaca DI TEMPAT, bukan tautan keluar. */
.vsform-agree { display: grid; gap: var(--space-2); }
.vsform-terms { border: 1px solid var(--border-faint); border-radius: var(--radius-md); background: var(--bg-subtle); }
.vsform-terms summary {
  padding: var(--space-2) var(--space-3); color: var(--text-body);
  font-size: var(--text-2xs); font-weight: var(--fw-semibold); cursor: pointer;
}
.vsform-terms .vs-rich { padding: 0 var(--space-3) var(--space-3); font-size: var(--text-2xs); line-height: var(--lh-relaxed); }

/* Stepper — GUI/09/STEPPER. Nomor, judul, dan penanda langkah yang sudah lewat. */
.vsform-steps { margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--space-3); list-style: none; }
.vsform-step { display: flex; align-items: center; gap: var(--space-2); color: var(--text-subtle); font-size: var(--text-2xs); }
.vsform-step b {
  width: 22px; height: 22px; display: grid; place-items: center; border-radius: var(--radius-pill);
  background: var(--ink-100); color: var(--text-muted); font-family: var(--font-mono); font-size: var(--text-2xs);
}
.vsform-step.is-active { color: var(--text-strong); font-weight: var(--fw-semibold); }
.vsform-step.is-active b { background: var(--ink-900); color: var(--text-on-dark); }
.vsform-step.is-done b { background: var(--tag-success-bg); color: var(--tag-success-text); }

/* Navigasi & keadaan akhir. */
.vsform-nav { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.vsform-submit, .vsform-back {
  min-height: 45px; padding: 0 var(--space-5); display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius-md); font: inherit; font-size: var(--text-2xs); font-weight: var(--fw-semibold); cursor: pointer;
}
.vsform-submit { flex: 1; border: 1px solid var(--ink-900); background: var(--ink-900); color: var(--text-on-dark); }
.vsform-submit:disabled { opacity: .6; cursor: progress; }
.vsform-back { border: 1px solid var(--border-default); background: var(--bg-card); color: var(--text-body); }
.vsform-status { color: var(--text-muted); font-size: var(--text-2xs); line-height: var(--lh-normal); }
.vsform-lock, .vsform-done {
  display: grid; gap: var(--space-3); padding: var(--space-5);
  border: 1px solid var(--border-faint); border-radius: var(--radius-lg); background: var(--bg-card);
}
.vsform-lock h3, .vsform-done h3 { margin: 0; font-size: var(--text-md); letter-spacing: var(--ls-tight); }
.vsform-lock p, .vsform-done p { margin: 0; color: var(--text-muted); font-size: var(--text-2xs); line-height: var(--lh-relaxed); }
.vsform-link {
  justify-self: start; padding: 0; border: 0; background: none; color: var(--text-body);
  font: inherit; font-size: var(--text-2xs); text-decoration: underline; cursor: pointer;
}
/* Pesan sukses yang dapat diatur admin (R6): rich text, jadi ia butuh ritme
   paragraf sendiri — `.vsform-done p` di atas hanya mengurus kalimat bawaan. */
.vsform-done-msg { color: var(--text-muted); font-size: var(--text-2xs); line-height: var(--lh-relaxed); }
.vsform-done-msg > :first-child { margin-top: 0; }
.vsform-done-msg > :last-child { margin-bottom: 0; }

/* ── Ergonomi form publik di layar sentuh ────────────────────────────────────
   Kontrol form mewarisi `--text-2xs` (11px). Token itu MICRO-LABEL — dimaksudkan
   untuk kapsul kategori dan keterangan, bukan untuk teks yang diketik pengunjung.
   Di ponsel akibatnya dua, dan keduanya tidak terlihat sama sekali di desktop:

   1. iOS Safari MEMBESARKAN seluruh halaman begitu field ber-font < 16px disentuh,
      dan tidak mengembalikannya setelah field ditinggalkan. Formnya terasa
      "melompat" sejak field pertama. 16px adalah ambang pasti milik iOS, bukan
      angka selera: 15.9px masih memicu zoom, jadi nilainya ditulis literal dan
      TIDAK boleh diganti token — semua token teks yang ada bernilai di bawahnya.
   2. 11px dengan padding vertikal 4px menyisakan pita teks ~13px di tengah kotak
      44px. Tingginya lolos ambang sentuh sementara isinya tidak terbaca, sehingga
      pemeriksaan aksesibilitas berbasis ukuran target pun lolos tanpa keluhan.

   Ditulis sebagai override di breakpoint ponsel supaya tipografi desktop yang
   sudah disetujui tidak ikut bergeser. `--field-h` di-override pada `.vsform`,
   bukan pada `:root`, karena token yang sama dipakai kontrol Schedule dan
   Listing — menaikkannya global akan menggeser komponen yang tidak diminta. */
@media (max-width: 639px) {
  .vsform { --field-h: 48px; }
  .vsform-field input, .vsform-field select, .vsform-field textarea {
    padding: var(--space-3) var(--space-4);
    font-size: 16px;
  }
  .vsform-field > span { font-size: var(--text-xs); }
  .vsform-hint, .vsform-warn { font-size: var(--text-xs); }
  .vsform-check { min-height: 48px; font-size: var(--text-xs); }
}

/* ── Promo slider berisi SATU promo ──────────────────────────────────────────
   `.promo-hero` sengaja lebih sempit dari relnya (`calc(100% - 30px)`, naik ke
   65px di desktop) supaya tepi slide berikutnya mengintip — isyarat "ini bisa
   digeser" tanpa perlu panah. Isyarat itu benar selama slide-nya lebih dari satu.

   Dengan satu promo tidak ada yang mengintip: sisa ruangnya jadi celah kosong di
   kanan dan kartunya terbaca sebagai terpotong, bukan sebagai rel yang bisa
   digeser. Titik indikator pun tidak digambar (renderers.js hanya memasangnya
   saat promos.length > 1), jadi tidak ada satu pun petunjuk yang menjelaskan
   celah itu.

   `:only-child` dipilih supaya aturannya MEMBATALKAN DIRI begitu promo kedua
   terbit — tidak ada kelas yang harus dilepas dan tidak ada keadaan yang bisa
   tertinggal salah. Ditulis dengan `.promo-slider >` agar spesifisitasnya (0,3,0)
   mengalahkan override flex-basis di kedua breakpoint tanpa bergantung urutan. */
.promo-slider > .promo-hero:only-child { flex-basis: 100%; }
