/* Dilarang keras menghapus credits
Devoleper : Rahmad Tenpoles
WhatsApp : wa.me/6285764735713
Instagram : instagram.com/rahmad_tenpoles
Telegram : t.me/rahmad_tenpoles
Dilarang keras menghapus credits */
/* =====================================================================================================
   VF UPGRADE LAYER (UI UX Pro Max audit, round 1). Loaded LAST on every customer page and in the admin.
   One file so the fixes are easy to find and review; every block says what it fixes.
   Motion tokens used here only: 100 (press) · 150 (exit) · 200 (enter) · 300 (slow / reveal).
   Every animation is transform / opacity only and is switched off under prefers-reduced-motion.
   ===================================================================================================== */

:root {
  --vx-in: 200ms;
  --vx-out: 150ms;
  --vx-slow: 300ms;
  --vx-ease-in: cubic-bezier(.2, 0, 0, 1);      /* decelerate: things arriving */
  --vx-ease-out: cubic-bezier(.4, 0, 1, 1);     /* accelerate: things leaving */
  --vx-spring: cubic-bezier(.16, 1, .3, 1);
  /* the 140 ms legacy token now sits on the agreed scale */
  --vf-dur: 150ms;
}

/* -----------------------------------------------------------------------------------------------------
   1. MOTION RESTORED. The @keyframes and animation rules had been stripped from the stylesheets: only the
      spinner and the error shake were left. Dialogs froze for 200 ms before disappearing, skeletons never
      shimmered, the "online" dot on the dashboards never pulsed, list rows never faded in, the landing band
      never ran, the admin chart bars never grew and the ad countdown ring never emptied.
   ----------------------------------------------------------------------------------------------------- */
@keyframes vx-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes vx-fade-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes vx-pop-in { from { opacity: 0; transform: translateY(8px) scale(.98); } to { opacity: 1; transform: none; } }
@keyframes vx-pop-out { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateY(6px) scale(.98); } }
@keyframes vx-sheet-in { from { transform: translateY(100%); } to { transform: none; } }
@keyframes vx-sheet-out { from { transform: none; } to { transform: translateY(100%); } }
@keyframes vx-menu-in { from { opacity: 0; transform: translateY(-4px) scale(.98); } to { opacity: 1; transform: none; } }
@keyframes vx-rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@keyframes vx-shimmer { from { transform: translateX(-100%); } to { transform: translateX(100%); } }
@keyframes vx-pulse { 0% { transform: scale(1); opacity: .55; } 80%, 100% { transform: scale(2.8); opacity: 0; } }
@keyframes vx-marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@keyframes vx-bar-grow { from { transform: scaleY(0); } to { transform: scaleY(1); } }
@keyframes vx-ring { from { stroke-dashoffset: 0; } to { stroke-dashoffset: 100; } }

@media (prefers-reduced-motion: no-preference) {
  /* Dialogs and sheets (VFPopup, quick view): enter 200 ms decelerating, exit 150 ms accelerating
     (vf-popup.js / quick-view.js add .is-closing and remove the dialog 200 ms later). */
  dialog.vf-pop[open], dialog.rs-qv[open] { animation: vx-pop-in var(--vx-in) var(--vx-ease-in) both; }
  dialog.vf-pop[open]::backdrop, dialog.rs-qv[open]::backdrop { animation: vx-fade-in var(--vx-in) var(--vx-ease-in) both; }
  dialog.vf-pop.is-closing, dialog.rs-qv.is-closing { animation: vx-pop-out var(--vx-out) var(--vx-ease-out) both; }
  dialog.vf-pop.is-closing::backdrop, dialog.rs-qv.is-closing::backdrop { animation: vx-fade-out var(--vx-out) var(--vx-ease-out) both; }
  @media (max-width: 1023.98px) {
    dialog.vf-pop--sheet[open] { animation: vx-sheet-in var(--vx-slow) var(--vx-spring) both; }
    dialog.vf-pop--sheet.is-closing { animation: vx-sheet-out var(--vx-out) var(--vx-ease-out) both; }
  }
  @media (max-width: 575.98px) {
    dialog.rs-qv[open] { animation: vx-sheet-in var(--vx-slow) var(--vx-spring) both; }
    dialog.rs-qv.is-closing { animation: vx-sheet-out var(--vx-out) var(--vx-ease-out) both; }
  }

  /* Dropdown menus and the site menu (vf-dd.js): a short drop-in; the phone sheet slides up. */
  .vf-dd__menu.is-open:not(.is-sheet) { animation: vx-menu-in var(--vx-out) var(--vx-ease-in) both; }
  .vf-dd__menu.is-open.is-sheet { animation: vx-sheet-in var(--vx-slow) var(--vx-spring) both; }
  .vf-dd__backdrop { animation: vx-fade-in var(--vx-in) var(--vx-ease-in) both; }

  /* Skeletons: one highlight sweep every 1.2 s (they only show while data is loading). */
  .vf-skel::after, .rs-skeleton::after { animation: vx-shimmer 1.2s linear infinite; }

  /* "Online" status dots (customer dashboard server card, admin dashboard): a ring that grows and fades, 2 s loop. */
  .vf-live-dot::after, .srv-row__state.is-online .srv-row__dot::after,
  .adm-live.is-live::before { animation: vx-pulse 2s var(--vx-ease-in) infinite; }

  /* Lists marked .vf-list-in: the first 8 rows fade up once, 40 ms apart. */
  .vf-list-in > * { animation: vx-rise var(--vx-slow) var(--vx-ease-in) both; }
  .vf-list-in > :nth-child(2) { animation-delay: 40ms; }  .vf-list-in > :nth-child(3) { animation-delay: 80ms; }
  .vf-list-in > :nth-child(4) { animation-delay: 120ms; } .vf-list-in > :nth-child(5) { animation-delay: 160ms; }
  .vf-list-in > :nth-child(6) { animation-delay: 200ms; } .vf-list-in > :nth-child(7) { animation-delay: 240ms; }
  .vf-list-in > :nth-child(n+8) { animation-delay: 280ms; }

  /* Landing "running band": runs only while it is on screen (landing.js sets .is-on); stops on hover AND on keyboard focus. */
  .vf-landing__band.is-on .vf-landing__track { animation: vx-marquee 40s linear infinite; }
  .vf-landing__band:is(:hover, :focus-within) .vf-landing__track { animation-play-state: paused; }

  /* Admin dashboard chart: bars grow from the baseline once, 20 ms apart. */
  .adm-bars7__bar { transform-origin: 50% 100%; animation: vx-bar-grow var(--vx-slow) var(--vx-ease-in) both; }
  .adm-bars7__col:nth-child(2) .adm-bars7__bar { animation-delay: 20ms; }  .adm-bars7__col:nth-child(3) .adm-bars7__bar { animation-delay: 40ms; }
  .adm-bars7__col:nth-child(4) .adm-bars7__bar { animation-delay: 60ms; }  .adm-bars7__col:nth-child(5) .adm-bars7__bar { animation-delay: 80ms; }
  .adm-bars7__col:nth-child(6) .adm-bars7__bar { animation-delay: 100ms; } .adm-bars7__col:nth-child(n+7) .adm-bars7__bar { animation-delay: 120ms; }

  /* Ad pop-up: the countdown ring empties over the 15 s the ad stays open; it pauses with the countdown. */
  .vf-ad__ring-fg { animation: vx-ring 15s linear both; }
  .vf-ad.is-paused .vf-ad__ring-fg, .vf-pop.is-paused .vf-ad__ring-fg { animation-play-state: paused; }
}

/* Reduced motion: everything above stays off; also cancels the old rule in themes.css that FORCED 140 ms
   transitions with !important on every element for people who asked for less motion. */
@media (prefers-reduced-motion: reduce) {
  html.light-mode *, html.light-mode *::before, html.light-mode *::after,
  html.dark-mode *, html.dark-mode *::before, html.dark-mode *::after,
  html *, html *::before, html *::after {
    transition-duration: .01ms !important;
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }
  .vf-skel::after, .rs-skeleton::after { opacity: 0; }
  /* a loading spinner is feedback, not decoration: it keeps turning (slower) */
  .rs-spinner { animation-duration: 1.2s !important; animation-iteration-count: infinite !important; }
}

/* -----------------------------------------------------------------------------------------------------
   2. TOAST ON DESKTOP PAGES WITHOUT A BOTTOM NAV (admin, checkout): a later rule
      `body:not(.rs-has-bottomnav):not(.vf-has-actionbar) .vf-toast { bottom: 16px }` out-ranked the desktop
      `bottom: auto`, so the toast was pinned top AND bottom and grew into a dark panel over the whole page.
   ----------------------------------------------------------------------------------------------------- */
@media (min-width: 1024px) {
  html body .vf-toast.vf-toast.vf-toast { bottom: auto; height: auto; }
}

/* -----------------------------------------------------------------------------------------------------
   3. BOTTOM NAV — floating "liquid glass" pill (owner request). The ONE place glass is allowed (the design rule
      "no glass" in themes.css now names this exception). Readability first:
        - the glass is 72-78 % opaque surface + blur, so labels keep >= 4.5:1 over any content below;
        - solid fallback when backdrop-filter is missing, with prefers-reduced-transparency and prefers-contrast: more;
        - every item keeps icon + label and a 48 px+ target; floats 12 px above the safe area (home indicator).
   ----------------------------------------------------------------------------------------------------- */
:root {
  --vx-glass: color-mix(in srgb, var(--vf-surface-2) 74%, transparent);
  --vx-glass-edge: color-mix(in srgb, #fff 70%, transparent);
  --vx-glass-shine: linear-gradient(180deg, color-mix(in srgb, #fff 55%, transparent) 0%, color-mix(in srgb, #fff 0%, transparent) 55%);
  --vx-glass-shadow: 0 10px 30px -6px rgba(24, 20, 70, .28), 0 2px 6px rgba(24, 20, 70, .10);
  --vx-bnav-h: 64px;
  --vx-bnav-gap: 12px;
}
html.dark-mode {
  --vx-glass: color-mix(in srgb, var(--vf-surface-2) 70%, transparent);
  --vx-glass-edge: color-mix(in srgb, #fff 14%, transparent);
  --vx-glass-shine: linear-gradient(180deg, color-mix(in srgb, #fff 12%, transparent) 0%, color-mix(in srgb, #fff 0%, transparent) 60%);
  --vx-glass-shadow: 0 12px 32px -6px rgba(0, 0, 0, .6), 0 2px 6px rgba(0, 0, 0, .35);
}

html body .rs-bottomnav.vf-bnav.vx-bnav {
  position: fixed; top: auto; z-index: var(--rs-z-nav);
  left: max(var(--vx-bnav-gap), env(safe-area-inset-left, 0px)); right: max(var(--vx-bnav-gap), env(safe-area-inset-right, 0px));
  bottom: calc(var(--vx-bnav-gap) + env(safe-area-inset-bottom, 0px));
  width: auto; max-width: 480px; margin: 0 auto; height: var(--vx-bnav-h); padding: 0; box-sizing: border-box;
  border-radius: 999px; border: 1px solid var(--vx-glass-edge);
  background: var(--vx-glass);
  -webkit-backdrop-filter: blur(22px) saturate(180%); backdrop-filter: blur(22px) saturate(180%);
  box-shadow: var(--vx-glass-shadow), inset 0 1px 0 color-mix(in srgb, #fff 45%, transparent), inset 0 -1px 0 color-mix(in srgb, #000 6%, transparent);
  isolation: isolate; overflow: visible;
}
/* glossy top highlight (the "liquid" sheen), clipped to the pill */
html body .vx-bnav::before {
  content: ''; position: absolute; inset: 1px; border-radius: inherit; pointer-events: none; z-index: 0;
  background: var(--vx-glass-shine); opacity: .9;
}
html.dark-mode body .vx-bnav { box-shadow: var(--vx-glass-shadow), inset 0 1px 0 color-mix(in srgb, #fff 10%, transparent); }

html body .vx-bnav .vx-bnav__inner {
  position: relative; z-index: 1; height: 100%; max-width: none; margin: 0; padding: 0 8px;
  display: grid; grid-template-columns: repeat(var(--vx-n, 5), minmax(0, 1fr)); align-items: center;
}
/* soft accent glow that follows the active tab under the glass */
.vx-bnav__glow { position: absolute; top: 50%; left: 6px; width: calc((100% - 12px) / var(--vx-n, 5)); height: 44px; margin-top: -22px;
  transform: translateX(calc(var(--vx-i, 0) * 100%)); border-radius: 999px; pointer-events: none;
  background: radial-gradient(closest-side, color-mix(in srgb, var(--vf-accent) 22%, transparent), transparent); filter: blur(6px); opacity: .9; }
/* the active "liquid" pill: a lighter glass drop behind the active item */
.vx-bnav__pill { position: absolute; top: 8px; bottom: 8px; left: 6px; width: calc((100% - 12px) / var(--vx-n, 5)); pointer-events: none;
  transform: translateX(calc(var(--vx-i, 0) * 100%)); }
.vx-bnav__pill::before { content: ''; position: absolute; inset: 0 6px; border-radius: 999px;
  background: color-mix(in srgb, var(--vf-accent) 14%, var(--vf-surface-2));
  box-shadow: inset 0 1px 0 color-mix(in srgb, #fff 60%, transparent), inset 0 0 0 1px color-mix(in srgb, var(--vf-accent) 22%, transparent), 0 4px 12px -4px color-mix(in srgb, var(--vf-accent) 40%, transparent); }
html.dark-mode .vx-bnav__pill::before { background: color-mix(in srgb, var(--vf-accent) 26%, var(--vf-surface-2)); box-shadow: inset 0 1px 0 color-mix(in srgb, #fff 14%, transparent), inset 0 0 0 1px color-mix(in srgb, var(--vf-accent) 40%, transparent); }
.vx-bnav__pill.is-off, .vx-bnav__pill.is-off ~ .vx-bnav__glow { opacity: 0; }
.vx-bnav:has(.vx-bnav__pill.is-off) .vx-bnav__glow { opacity: 0; }

html body .vx-bnav .vx-bnav__item {
  position: relative; z-index: 1; height: 100%; min-height: 48px; min-width: 0; padding: 0; margin: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  border: 0; background: none; text-decoration: none; cursor: pointer; -webkit-tap-highlight-color: transparent; touch-action: manipulation;
  color: var(--vf-text-2); font: 500 12px/16px var(--vf-font);
}
html body .vx-bnav .vx-bnav__item[aria-current="page"] { color: var(--vf-accent-text); font-weight: 600; }
.vx-bnav__ico { width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; }
.vx-bnav__ico .rs-icon, .vx-bnav__fab .rs-icon { width: 22px; height: 22px; }
.vx-bnav__label { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* primary action (Deposit / Masuk): an accent glass bead inside the pill, not a button sticking out of it */
.vx-bnav__fab { width: 44px; height: 30px; border-radius: 999px; display: flex; align-items: center; justify-content: center;
  background: linear-gradient(180deg, color-mix(in srgb, var(--vf-accent) 82%, #fff), var(--vf-accent));
  color: var(--vf-on-accent);
  box-shadow: inset 0 1px 0 color-mix(in srgb, #fff 45%, transparent), 0 6px 14px -4px color-mix(in srgb, var(--vf-accent) 60%, transparent); }
html body .vx-bnav .vx-bnav__item--primary { color: var(--vf-text-1); font-weight: 600; }
html body .vx-bnav .vx-bnav__item--primary[aria-current="page"] { color: var(--vf-accent-text); }

/* focus + press */
html body .vx-bnav .vx-bnav__item:focus-visible { outline: none; }
html body .vx-bnav .vx-bnav__item:focus-visible::after { content: ''; position: absolute; inset: 6px 4px; border-radius: 999px; box-shadow: 0 0 0 2px var(--vf-focus); }
@media (hover: hover) and (pointer: fine) { html body .vx-bnav .vx-bnav__item:hover { color: var(--vf-text-1); } }

@media (prefers-reduced-motion: no-preference) {
  .vx-bnav__pill, .vx-bnav__glow { transition: transform var(--vx-slow) var(--vx-spring), opacity var(--vx-in) var(--vx-ease-in); }
  .vx-bnav__ico, .vx-bnav__fab { transition: transform 100ms var(--vx-ease-in); }
  html body .vx-bnav .vx-bnav__item:active .vx-bnav__ico, html body .vx-bnav .vx-bnav__item:active .vx-bnav__fab { transform: scale(.92); }
  html body .vx-bnav .vx-bnav__item[aria-current="page"] .vx-bnav__ico { transform: translateY(-1px); }
}

/* fallbacks: no backdrop blur, reduced transparency, more contrast, forced colours → solid surface */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  html body .rs-bottomnav.vf-bnav.vx-bnav { background: var(--vf-surface-2); }
}
@media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
  html body .rs-bottomnav.vf-bnav.vx-bnav { background: var(--vf-surface-2); -webkit-backdrop-filter: none; backdrop-filter: none; border-color: var(--vf-border-strong); }
  html body .vx-bnav::before, .vx-bnav__glow { display: none; }
}
@media (forced-colors: active) {
  html body .rs-bottomnav.vf-bnav.vx-bnav { background: Canvas; border: 1px solid CanvasText; }
  .vx-bnav__pill::before { background: Highlight; }
}

/* space under the page content: bar 64 + 12 float + 16 air (+ safe area); toasts sit above the bar */
body.rs-has-bottomnav, body:has(.vx-bnav:not([hidden])) { padding-bottom: calc(var(--vx-bnav-h) + var(--vx-bnav-gap) + 16px + env(safe-area-inset-bottom, 0px)); }
html:has(.vx-bnav:not([hidden])) { scroll-padding-bottom: calc(var(--vx-bnav-h) + var(--vx-bnav-gap) + 16px + env(safe-area-inset-bottom, 0px)); }
body:has(.vx-bnav:not([hidden])) .vf-toast { bottom: calc(var(--vx-bnav-h) + var(--vx-bnav-gap) + 12px + env(safe-area-inset-bottom, 0px)); }
@media (min-width: 1024px) {
  html body .rs-bottomnav.vf-bnav.vx-bnav { display: none; }
  body.rs-has-bottomnav, body:has(.vx-bnav:not([hidden])) { padding-bottom: 0; }
  html:has(.vx-bnav:not([hidden])) { scroll-padding-bottom: 0; }
}
html.vf-kb-open body .rs-bottomnav.vf-bnav.vx-bnav { display: none; }
@media print { html body .rs-bottomnav.vf-bnav.vx-bnav { display: none; } }

/* Ad confetti: the specks were added but never moved (their keyframes were missing). */
@keyframes vx-confetti { 0% { opacity: 1; transform: translate(0, 0) rotate(0); } 100% { opacity: 0; transform: translate(var(--x, 0), var(--y, 80px)) rotate(var(--r, 180deg)); } }
@media (prefers-reduced-motion: no-preference) { .vf-ad__confetti i { animation: vx-confetti 1100ms var(--vx-ease-in) var(--d, 0ms) both; } }

/* -----------------------------------------------------------------------------------------------------
   4. CONTRAST (measured; WCAG 1.4.3 text 4.5:1, 1.4.11 control boundaries 3:1).
   ----------------------------------------------------------------------------------------------------- */
/* 4a. Input / secondary-button borders were 1.4-1.9:1 in every global theme (the token promises >= 3:1). */

/* 4b. Toast / tooltip in DARK kertas, sakura and samudra: the light theme block set a dark --vf-inverse and the dark
       block never reset it, so dark text sat on a dark toast (1.1-1.3:1). Light "inverse" surfaces, as in default dark. */

/* 4c. White text on the hovered primary button was 3.7:1 (default dark) and 4.3:1 (samudra dark). */
html.dark-mode:not([data-vf-theme]), html.dark-mode[data-vf-theme="default"] { --vf-accent-hover: #5B4CEB; }

/* 4d. Meta text (--vf-text-3) on chips / inset areas (surface-3) was 4.38:1 in default light. */
:root { --vf-text-3: #69687E; }
html.dark-mode { --vf-text-3: #8D8AA6; }

/* 4e. Focus rings that were nearly invisible (22 % tint = 1.4:1): one visible 2 px ring. */
[data-rs-mode-toggle]:focus-visible, .rs-mode-toggle:focus-visible { outline: 2px solid var(--vf-focus) !important; outline-offset: 2px; }
.vf-pd2 .qty-stepper:focus-within, .am-input:focus, .am-input:focus-visible { outline: 2px solid var(--vf-focus); outline-offset: 2px; }

/* 5. Skip link: hidden until it receives keyboard focus, then shown top-left above everything. */
.vx-skip { position: fixed; top: 8px; left: 8px; z-index: calc(var(--rs-z-toast, 1100) + 10); padding: 12px 16px; border-radius: var(--vf-r-ctl, 12px);
  background: var(--vf-accent); color: var(--vf-on-accent); font: 600 14px/20px var(--vf-font); text-decoration: none; box-shadow: var(--vf-e2);
  transform: translateY(-160%); }
.vx-skip:focus, .vx-skip:focus-visible { transform: none; outline: 2px solid var(--vf-focus); outline-offset: 2px; }

/* Typed confirmation dialog (VFPopup.typedConfirm) */
.vf-typed { display: flex; flex-direction: column; gap: 8px; }
.vf-typed p { margin: 0; }
.vf-typed__label { margin-top: 4px; font: 600 14px/20px var(--vf-font); color: var(--vf-text-1); }
.vf-typed__input { width: 100%; min-height: 48px; padding: 0 16px; border: 1px solid var(--vf-border-strong); border-radius: var(--vf-r-ctl, 12px); background: var(--vf-surface-2); color: var(--vf-text-1); font: 400 16px/24px var(--vf-font-mono); }
.vf-typed__input:focus { outline: none; border-color: var(--vf-accent); box-shadow: 0 0 0 1px var(--vf-accent), 0 0 0 4px color-mix(in srgb, var(--vf-focus) 28%, transparent); }
.vf-pop .vf-btn:disabled { opacity: .45; cursor: not-allowed; }

/* Connection / session banner (VXNetBanner, payment pages) */
.vx-netbar { display: flex; align-items: center; gap: 12px; margin: 0 0 16px; padding: 12px 16px; border-radius: var(--vf-r-ctl, 12px); border: 1px solid var(--vf-border); background: var(--vf-warning-subtle); color: var(--vf-text-1); font: 400 14px/20px var(--vf-font); }
.vx-netbar p { margin: 0; flex: 1; }
.vx-netbar > .rs-icon { flex: none; color: var(--vf-warning); }
.vx-netbar--auth { background: var(--vf-danger-subtle); }
.vx-netbar--auth > .rs-icon { color: var(--vf-danger); }
.vx-netbar .vf-btn { flex: none; min-height: 40px; }
@media (max-width: 575.98px) { .vx-netbar { flex-wrap: wrap; } .vx-netbar .vf-btn { width: 100%; min-height: 48px; } }

/* Everything that sits above the bottom nav (deposit CTA, toasts, old toast stack) uses --rs-bottomnav-h:
   the floating pill takes 64 px + 12 px float. */
:root { --rs-bottomnav-h: calc(var(--vx-bnav-h, 64px) + var(--vx-bnav-gap, 12px)); }
@media (min-width: 1024px) { :root { --rs-bottomnav-h: 0px; } }   /* no bottom nav on desktop (as in themes.css) */

/* Admin user row menu: the destructive "Hapus" item is set apart from the everyday actions. */
.adm-menu__sep { border: 0; border-top: 1px solid var(--vf-border); margin: 8px 12px; }

/* -----------------------------------------------------------------------------------------------------
   6. TYPE SCALE (round 2, replaces the round-1 weight-only fix). The design rule is one scale —
      12/16 · 14/20 · 16/24 · 18/24 · 20/28 · 24/32 · 32/40 · 48/56 — and weights 400 / 500 / 600 only. The type tokens
      were at 13, 15, 17, 22, 26, 28, 30 and 36 px with line heights that were not multiples of 4, and at 700-800.
      Every token now sits on the scale (phone, then >= 1024 px).
   ----------------------------------------------------------------------------------------------------- */
:root {
  --vf-t-caption: 400 12px/16px var(--vf-font);       --vf-t-caption-md: 500 12px/16px var(--vf-font);   --vf-t-caption-strong: 600 12px/16px var(--vf-font);
  --vf-t-badge: 600 12px/16px var(--vf-font-num);         --vf-t-eyebrow: 600 12px/16px var(--vf-font-num);  --vf-t-mono-sm: 500 12px/16px var(--vf-font-mono);
  --vf-t-small: 400 14px/20px var(--vf-font);         --vf-t-small-loose: 400 14px/20px var(--vf-font);  --vf-t-small-md: 500 14px/20px var(--vf-font);
  --vf-t-small-strong: 600 14px/20px var(--vf-font);  --vf-t-code-sm: 500 14px/20px var(--vf-font-mono); --vf-t-code: 500 14px/20px var(--vf-font-mono);
  --vf-t-label: 500 14px/20px var(--vf-font);         --vf-t-body-sm: 400 14px/20px var(--vf-font);      --vf-t-ui: 500 14px/20px var(--vf-font);
  --vf-t-ui-strong: 600 14px/20px var(--vf-font);     --vf-t-num-sm: 600 14px/20px var(--vf-font-num);   --vf-t-price-sm: 600 14px/20px var(--vf-font-num);
  --vf-t-body: 400 16px/24px var(--vf-font);          --vf-t-body-lg: 400 18px/28px var(--vf-font);      --vf-t-btn: 600 16px/24px var(--vf-font);
  --vf-t-card-title: 600 16px/24px var(--vf-font);    --vf-t-cardtitle: 600 16px/24px var(--vf-font);    --vf-t-num: 600 16px/24px var(--vf-font-num);
  --vf-t-h3: 600 16px/24px var(--vf-font);            --vf-t-h2: 600 18px/24px var(--vf-font);           --vf-t-sec: 600 18px/24px var(--vf-font);
  --vf-t-section: 600 18px/24px var(--vf-font);       --vf-t-stat: 600 18px/24px var(--vf-font-num);     --vf-t-prodtitle: 600 20px/28px var(--vf-font);
  --vf-t-h1: 600 24px/32px var(--vf-font);            --vf-t-pagetitle: 600 24px/32px var(--vf-font);    --vf-t-price-lg: 600 24px/32px var(--vf-font-num);
  --vf-t-saldo: 600 24px/32px var(--vf-font-num);     --vf-t-display: 600 32px/40px var(--vf-font);
}
@media (min-width: 1024px) {
  :root {
    --vf-t-price-sm: 600 16px/24px var(--vf-font-num); --vf-t-h3: 600 18px/24px var(--vf-font); --vf-t-cardtitle: 600 18px/24px var(--vf-font);
    --vf-t-sec: 600 20px/28px var(--vf-font); --vf-t-section: 600 20px/28px var(--vf-font); --vf-t-h2: 600 24px/32px var(--vf-font);
    --vf-t-stat: 600 24px/32px var(--vf-font-num); --vf-t-prodtitle: 600 24px/32px var(--vf-font); --vf-t-h1: 600 32px/40px var(--vf-font);
    --vf-t-pagetitle: 600 32px/40px var(--vf-font); --vf-t-price-lg: 600 32px/40px var(--vf-font-num); --vf-t-saldo: 600 32px/40px var(--vf-font-num);
    --vf-t-display: 600 48px/56px var(--vf-font);
  }
}
/* the base h1 / h2 and literal 700 / 800 weights in the shared and admin stylesheets */
h1, h2 { font-weight: 600; }   /* the base h1 / h2 were 700 (ui.css) */
:is(.vf-landing, .adm-main, body) :is([style*="font-weight:700"], [style*="font-weight: 700"], [style*="font-weight:800"], [style*="font-weight: 800"]) { font-weight: 600 !important; }

/* 7. "No uppercase labels" (design rule): the 8 remaining uppercase eyebrow / group labels become sentence case. */
:is(.vf-eyebrow, .vf-menu .rs-menu__group, .vf-stats__lbl, .vf-auth .auth-divider span, h2.pf-group, .pay-qris__label, .pnl-page .pnl-pkg__specs dt, .vf-landing__eyebrow,
    .adm-kpi-group__title, #adminSidebar.adm-sidebar .adm-sb-label, .adm-palette__group) { text-transform: none !important; letter-spacing: 0 !important; font-weight: 600; }

/* 8. The desktop top bar still used frosted glass (blur 12 px) against the "solid surfaces" rule; solid like on phones. */
html body .rs-topbar, html body .rs-topbar.vf-top { -webkit-backdrop-filter: none; backdrop-filter: none; background: var(--vf-surface-2); }
.vf-landing :is(h1, h2, h3, .vf-landing__svcname, .vf-landing__num, .vf-landing__stats dd, .vf-landing__prodname, .vf-landing__prodprice, .vf-landing__eyebrow),
.vf-landing__hero h1 * { font-weight: 600; }

/* Sortable admin table headers (vf-upgrade.js §5) */
th[aria-sort] > .vx-sort { all: unset; box-sizing: border-box; display: inline-flex; align-items: center; gap: 4px; min-height: 32px; cursor: pointer; font: inherit; color: inherit; border-radius: 6px; }
th[aria-sort] > .vx-sort:hover { color: var(--vf-text-1); }
th[aria-sort] > .vx-sort:focus-visible { outline: 2px solid var(--vf-focus); outline-offset: 2px; }
.vx-sort__ico { width: 0; height: 0; border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 5px solid currentColor; opacity: .3; }
th[aria-sort="ascending"] .vx-sort__ico { border-top: 0; border-bottom: 5px solid currentColor; opacity: 1; }
th[aria-sort="descending"] .vx-sort__ico { opacity: 1; }
th.is-num[aria-sort] > .vx-sort { flex-direction: row-reverse; }

/* Round 2 — one button family (vf-btn). Old names (rs-btn, btn-save, set-btn, btn btn-primary/ghost, btn-system-*) were
   renamed in the markup; these keep their layout in the places that sized the old classes. */
.pd-dlg .vf-btn { min-height: var(--vf-hit); }
.pd-dlg .modal-foot > .vf-btn { flex: 1 1 0; }
.adm-settings .vf-btn.u-w-auto { width: auto; }
.vx-btn-warn.vf-btn { color: var(--vf-warning); }
.vf-btn--danger-outline:hover { background: var(--vf-danger-subtle); color: var(--vf-danger); }

/* Round 2 — one motion and one look for every hand-built pop-up (login "lupa password", profile confirm / password,
   deposit detail, coming-soon card, Alight Motion sheet + modal, panel renew, APK order sheet, live chat window).
   They are all role="dialog" boxes that the shared popup layer (global-theme.js) tags with data-rs-surface /
   data-rs-scrim while open; the tags drive the same enter / exit motion as VFPopup. */
@media (prefers-reduced-motion: no-preference) {
  [data-rs-scrim][data-rs-open] { animation: vx-fade-in var(--vx-in) var(--vx-ease-in) both; }
  [data-rs-surface="dialog"][data-rs-open]:not(dialog):not(.vf-dd__menu) { animation: vx-pop-in var(--vx-in) var(--vx-ease-in) both; }
  [data-rs-surface="sheet"][data-rs-open]:not(dialog) { animation: vx-sheet-in var(--vx-slow) var(--vx-spring) both; }
  [data-rs-surface="side"][data-rs-open]:not(dialog) { animation: vx-side-in var(--vx-slow) var(--vx-spring) both; }
  [data-rs-scrim][data-rs-leaving] { animation: vx-fade-out var(--vx-out) var(--vx-ease-out) both; }
  [data-rs-surface][data-rs-leaving], [data-rs-scrim][data-rs-leaving] [data-rs-surface] { animation: vx-pop-out var(--vx-out) var(--vx-ease-out) both; }
}
@keyframes vx-side-in { from { transform: translateX(100%); } to { transform: none; } }
/* the same type for their titles and body text */
[data-rs-surface] :is(h2, .fpw-title, .pf-modal__title, .dp-sheet__title, .am-sheet-title, .renew-modal-title, .rs-lc-title):first-of-type { font: 600 18px/24px var(--vf-font); color: var(--vf-text-1); }

/* -----------------------------------------------------------------------------------------------------
   Round 2 — ONE payment-method picker (partials/pay-methods.ejs): product page, APK Premium, Nokos (OTP),
   panel (styled from its segmented buttons) and the Alight Motion sheet all look and behave the same.
   ----------------------------------------------------------------------------------------------------- */
.vx-methods { border: 0; padding: 0; min-width: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.vx-method { position: relative; display: flex; flex-direction: column; align-items: flex-start; gap: 8px; min-height: 96px; padding: 12px; border-radius: var(--vf-r-ctl, 12px); border: 1px solid var(--vf-border-strong); background: var(--vf-surface-2); cursor: pointer; -webkit-tap-highlight-color: transparent; }
.vx-method input { position: absolute; opacity: 0; inset: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.vx-method__ico { width: 36px; height: 36px; border-radius: 10px; display: flex; align-items: center; justify-content: center; background: var(--vf-surface-3); color: var(--vf-accent-text); }
.vx-method__txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.vx-method__name { font: 600 16px/24px var(--vf-font); color: var(--vf-text-1); }
.vx-method__sub { font: 400 12px/16px var(--vf-font); color: var(--vf-text-2); font-variant-numeric: tabular-nums; }
.vx-method.is-short .vx-method__sub { color: var(--vf-danger); }
.vx-method__check { position: absolute; top: 10px; right: 10px; width: 22px; height: 22px; border-radius: 999px; display: flex; align-items: center; justify-content: center; border: 1.5px solid var(--vf-border-strong); color: transparent; background: var(--vf-surface-2); }
.vx-method:has(input:checked) { border: 2px solid var(--vf-accent); padding: 11px; background: color-mix(in srgb, var(--vf-accent-subtle) 70%, var(--vf-surface-2)); }
.vx-method:has(input:checked) .vx-method__ico { background: var(--vf-accent); color: var(--vf-on-accent); }
.vx-method:has(input:checked) .vx-method__check { border-color: var(--vf-accent); background: var(--vf-accent); color: var(--vf-on-accent); top: 9px; right: 9px; }
.vx-method:has(input:focus-visible) { outline: 2px solid var(--vf-focus); outline-offset: 2px; }
@media (hover: hover) and (pointer: fine) { .vx-method:not(:has(input:checked)):hover { background: var(--vf-surface-3); } }
.vx-methods--one { grid-template-columns: minmax(0, 1fr); }
.vx-methods--one .vx-method { min-height: 72px; flex-direction: row; align-items: center; }
.vx-method.selected:not(:has(input)) { border: 2px solid var(--vf-accent); padding: 11px; background: color-mix(in srgb, var(--vf-accent-subtle) 70%, var(--vf-surface-2)); }
@media (prefers-reduced-motion: no-preference) { .vx-method, .vx-method__ico, .vx-method__check { transition: background-color 150ms var(--vx-ease-in), border-color 150ms var(--vx-ease-in), color 150ms; } }

/* panel checkout: the Saldo / QRIS segmented buttons become the same tiles */
.vf-seg.pnl-paygroup { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; padding: 0; background: none; border: 0; }
.vf-seg.pnl-paygroup .pnl-pay { position: relative; min-height: 64px; justify-content: flex-start; gap: 10px; padding: 12px; border-radius: var(--vf-r-ctl, 12px); border: 1px solid var(--vf-border-strong); background: var(--vf-surface-2); color: var(--vf-text-1); font: 600 15px/20px var(--vf-font); box-shadow: none; }
.vf-seg.pnl-paygroup .pnl-pay .rs-icon { width: 36px; height: 36px; padding: 8px; border-radius: 10px; background: var(--vf-surface-3); color: var(--vf-accent-text); box-sizing: border-box; }
.vf-seg.pnl-paygroup .pnl-pay[aria-pressed="true"] { border: 2px solid var(--vf-accent); padding: 11px; background: color-mix(in srgb, var(--vf-accent-subtle) 70%, var(--vf-surface-2)); color: var(--vf-text-1); }
.vf-seg.pnl-paygroup .pnl-pay[aria-pressed="true"] .rs-icon { background: var(--vf-accent); color: var(--vf-on-accent); }
.vf-seg.pnl-paygroup .pnl-pay[aria-pressed="true"]::after { content: ''; position: absolute; top: 9px; right: 9px; width: 22px; height: 22px; border-radius: 999px; background: var(--vf-accent) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='white'%3E%3Cpath d='M12.736 3.97a.733.733 0 0 1 1.047 0c.286.289.29.756.01 1.05L7.88 12.01a.733.733 0 0 1-1.065.02L3.217 8.384a.757.757 0 0 1 0-1.06.733.733 0 0 1 1.047 0l3.052 3.093 5.4-6.425z'/%3E%3C/svg%3E") center / 14px no-repeat; }
.vf-seg.pnl-paygroup .pnl-pay:focus-visible { outline: 2px solid var(--vf-focus); outline-offset: 2px; }

/* Alight Motion "pilih metode" sheet: same tile look */
.am-pay-options { display: grid; gap: 8px; }
.am-pay-opt { min-height: 64px; padding: 12px; border-radius: var(--vf-r-ctl, 12px) !important; border: 1px solid var(--vf-border-strong) !important; background: var(--vf-surface-2) !important; }
.am-pay-opt > .rs-icon:first-child { width: 36px; height: 36px; padding: 8px; border-radius: 10px; background: var(--vf-surface-3); color: var(--vf-accent-text); box-sizing: border-box; flex: none; }
.am-pay-opt:hover { background: var(--vf-surface-3) !important; }
.am-pay-opt.am-pay-opt-muted .am-pay-opt-sub { color: var(--vf-danger); }
/* admin: buttons keep the compact admin text size (14/20) and padding the old .adm-btn had */
.adm-main .vf-btn:not(.vf-btn--block):not(.vf-btn--sm):not(.pn-prow__acts > .vf-btn), .adm-dlg .vf-btn, #adminSidebar .vf-btn { font: var(--vf-t-ui-strong); padding: 0 16px; }
.adm-main .pn-prow__acts > .vf-btn { font: var(--vf-t-ui-strong); }
.adm-main .vf-btn .rs-icon { width: 16px; height: 16px; }
/* admin Transaksi (round 3): server pager arrows that are off, and the "newest N" note */
.adm-pg__arrow.is-off { opacity: .35; pointer-events: none; }
a.adm-pg__arrow { display: inline-flex; align-items: center; justify-content: center; color: var(--vf-text-1); }
.adm-capnote { margin: 0 0 12px; }
[data-tab-filter][hidden] { display: none !important; }

/* -----------------------------------------------------------------------------------------------------
   9. ROUND 4 (UI/UX recovery): Popular catalog, icons, motion, footer, recent-purchase notices.
   ----------------------------------------------------------------------------------------------------- */

/* 9a. Keyframes that views referenced but no stylesheet defined (the live-chat sheet, the dashboard
       "segera hadir" dialog, the Nokos payment dialog): they opened and closed with a hard cut. */
@keyframes rs-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes rs-fade-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes rs-sheet-in { from { opacity: 0; transform: translateY(24px); } to { opacity: 1; transform: none; } }
@keyframes rs-sheet-out { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateY(16px); } }
@keyframes rs-drop-in { from { opacity: 0; transform: translateY(-8px) scale(.98); } to { opacity: 1; transform: none; } }
@keyframes rs-qv-out { from { opacity: 1; transform: none; } to { opacity: 0; transform: scale(.98); } }

/* 9b. Produk populer / pilihan carousel: each card fills its track. The card was a flex child with
       flex: 0 1 auto, so it shrank to its content (≈195 px in a 245 px column) and the row showed
       ragged gaps between narrow cards on desktop. */
.rs-hscroll__item > .vf-pcard { flex: 1 1 auto; width: 100%; }

/* Placeholder art (a product without an image): the glyph was 26 px on a 1:1 / 16:10 tile, which read
   as a broken image on wide cards. Scale it with the tile. */
.vf-pcard__ph .rs-icon { width: 32px; height: 32px; }
@media (min-width: 768px) { .vf-pcard__ph .rs-icon { width: 40px; height: 40px; } }

/* 9c. Less motion on product cards: a 2 px lift and a 2% image zoom instead of 4 px and 4% (the card
       grid has many cards; the larger move made the page feel jumpy while the pointer crossed it). */
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .vf-pcard { transition: transform var(--vx-out) var(--vx-ease-in), box-shadow var(--vx-out) var(--vx-ease-in), scale var(--vf-dur, 140ms) var(--vf-ease, ease-out); }
  .vf-pcard:hover { transform: translateY(-2px); }
  .vf-pcard:hover .vf-pcard__img { transform: scale(1.02); }
}

/* 9d. Section reveal ([data-reveal], vf-upgrade.js): only sections below the first screen, only when the
       script ran, once, 300 ms, 12 px. */
@media (prefers-reduced-motion: no-preference) {
  .vx-pre { opacity: 0; transform: translateY(12px); }
  .vx-pre.vx-shown { opacity: 1; transform: none; transition: opacity var(--vx-slow) var(--vx-ease-in), transform var(--vx-slow) var(--vx-ease-in); }
}
@media print { .vx-pre { opacity: 1 !important; transform: none !important; } }

/* 9e. Store footer (partials/footer.ejs): brand + description + trust line, Belanja / Bantuan / Kontak
       columns, copyright bar. One column of links per row pair on phones, four columns from 768 px. */
.vf-foot.vf-foot--full {
  display: block; flex: none; width: 100%; box-sizing: border-box; margin-top: 64px;
  padding: 40px var(--vf-page-pad) 24px; border-top: 1px solid var(--vf-border);
  background: var(--vf-surface-2); color: var(--vf-text-2); font: var(--vf-t-body-sm); text-align: left;
}
.vf-foot--full .vf-foot__grid { max-width: var(--vf-container, 1184px); margin: 0 auto; display: grid; grid-template-columns: 1fr 1fr; gap: 32px 24px; }
.vf-foot--full .vf-foot__brand { grid-column: 1 / -1; display: flex; flex-direction: column; align-items: flex-start; gap: 12px; }
.vf-foot--full .vf-foot__logo { display: inline-flex; min-height: 44px; align-items: center; text-decoration: none; font-size: 20px; }
.vf-foot--full .vf-foot__desc { margin: 0; max-width: 42ch; color: var(--vf-text-2); }
.vf-foot--full .vf-foot__trust { list-style: none; margin: 4px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-start; }
.vf-foot--full .vf-foot__trust li { display: inline-flex; align-items: center; gap: 8px; min-height: 32px; padding: 0 12px; border-radius: 999px; background: var(--vf-surface-3); color: var(--vf-text-1); font: var(--vf-t-caption-md); white-space: nowrap; }
.vf-foot--full .vf-foot__trust .rs-icon { width: 16px; height: 16px; color: var(--vf-accent-text); }
.vf-foot--full .vf-foot__h { margin: 0 0 8px; font: var(--vf-t-ui-strong); color: var(--vf-text-1); }
.vf-foot--full .vf-foot__col ul { list-style: none; margin: 0; padding: 0; }
.vf-foot--full .vf-foot__col a, .vf-foot--full .vf-foot__col button {
  display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 0; border: 0; background: none;
  color: var(--vf-text-2); font: var(--vf-t-body-sm); text-align: left; text-decoration: none; cursor: pointer;
  transition: color var(--vx-out) var(--vx-ease-in);
}
.vf-foot--full .vf-foot__col a:hover, .vf-foot--full .vf-foot__col button:hover { color: var(--vf-accent-text); }
.vf-foot--full .vf-foot__col .rs-icon { width: 16px; height: 16px; flex: none; }
.vf-foot--full .vf-foot__bar { max-width: var(--vf-container, 1184px); margin: 32px auto 0; padding-top: 16px; border-top: 1px solid var(--vf-border); font: var(--vf-t-caption); color: var(--vf-text-3); }
.vf-foot--full .vf-foot__copy { padding: 0; flex-basis: auto; text-align: left; }
@media (min-width: 768px) {
  .vf-foot--full .vf-foot__grid { grid-template-columns: minmax(0, 2fr) repeat(3, minmax(0, 1fr)); gap: 32px; }
  .vf-foot--full .vf-foot__brand { grid-column: auto; }
  .vf-foot--full .vf-foot__col a, .vf-foot--full .vf-foot__col button { min-height: 36px; }
}
@media (min-width: 768px) and (pointer: coarse) { .vf-foot--full .vf-foot__col a, .vf-foot--full .vf-foot__col button { min-height: 44px; } }
.vf-landing .vf-foot.vf-foot--full { margin-top: 48px; }
@media print { .vf-foot.vf-foot--full { display: none; } }

/* 9f. Recent-purchase notice (vf-recent.js; class .vf-buynote — .vf-recent is the member dashboard section): a small card bottom-left, above the phone bottom nav, out of the way of the
       toast (bottom-centre) and the help entry. Opacity + 8 px rise, 200 ms in / 150 ms out; no motion when reduced. */
.vf-buynote {
  position: fixed; z-index: calc(var(--rs-z-nav, 200) + 1); left: max(12px, env(safe-area-inset-left, 0px));
  bottom: calc(16px + env(safe-area-inset-bottom, 0px));
  width: min(340px, calc(100vw - 24px)); box-sizing: border-box;
  display: flex; align-items: center; gap: 12px; padding: 12px 8px 12px 12px;
  background: var(--vf-surface-2); color: var(--vf-text-1); border: 1px solid var(--vf-border); border-radius: var(--vf-r-card, 16px);
  box-shadow: var(--vf-e2, 0 8px 24px rgba(0, 0, 0, .12));
  opacity: 0; transform: translateY(8px); pointer-events: none;
  transition: opacity var(--vx-out) var(--vx-ease-out), transform var(--vx-out) var(--vx-ease-out);
}
.vf-buynote.is-in { opacity: 1; transform: none; pointer-events: auto; transition-duration: var(--vx-in); transition-timing-function: var(--vx-ease-in); }
.vf-buynote[hidden] { display: none; }
body:has(.vx-bnav:not([hidden])) .vf-buynote { bottom: calc(var(--vx-bnav-h) + var(--vx-bnav-gap) + 12px + env(safe-area-inset-bottom, 0px)); }
@media (min-width: 1024px) { body:has(.vx-bnav:not([hidden])) .vf-buynote { bottom: calc(24px + env(safe-area-inset-bottom, 0px)); } .vf-buynote { left: 24px; bottom: 24px; } }
.vf-buynote__ic { flex: none; width: 40px; height: 40px; border-radius: 12px; display: inline-flex; align-items: center; justify-content: center; background: var(--vf-success-subtle); color: var(--vf-success); }
.vf-buynote__txt { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.vf-buynote__eyebrow { font: var(--vf-t-caption); color: var(--vf-text-3); }
.vf-buynote__name { font: var(--vf-t-ui-strong); color: var(--vf-text-1); text-decoration: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
a.vf-buynote__name[href]:hover { color: var(--vf-accent-text); text-decoration: underline; }
.vf-buynote__ago { font: var(--vf-t-caption); color: var(--vf-text-2); }
.vf-buynote__x { flex: none; width: 44px; height: 44px; margin: -8px 0; display: inline-flex; align-items: center; justify-content: center; border: 0; border-radius: 999px; background: none; color: var(--vf-text-3); cursor: pointer; }
.vf-buynote__x:hover { background: var(--vf-surface-3); color: var(--vf-text-1); }
@media (prefers-reduced-motion: reduce) { .vf-buynote, .vf-buynote.is-in { transform: none; transition: none; } }
@media print { .vf-buynote { display: none !important; } }

/* 9g. Card rows line up: the phone name slot was 36 px for a 2-line clamp of 20 px lines (40 px), so a one-line name
       pulled its price 4 px higher than its neighbours. Desktop keeps the documented one line + ellipsis (the clamp was
       still 2, so a long name pushed its price down a line). The full name stays in the link's accessible name. */
@media (max-width: 1023.98px) { .vf-pcard__name { min-height: 40px; } }
@media (min-width: 1024px) { .vf-pcard__name { -webkit-line-clamp: 1; min-height: 20px; } }

/* "Semua produk" + sort on one line on phones: the visible "Urutkan" label made the heading wrap to two lines below
   480 px. The label stays for screen readers (it is the select's accessible name). */
@media (max-width: 479.98px) {
  .vf-sort__l { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
}

/* -----------------------------------------------------------------------------------------------------
   10. ADMIN, MOBILE FIRST (round 4).
   ----------------------------------------------------------------------------------------------------- */

/* 10a. Save bar (Pengaturan): the long/short label swap was written for .btn-save, a class renamed to .vf-btn in round 2,
        so both labels showed ("Simpan Tema Simpan Tema"). On phones the bar stacked the status line and two full-width
        buttons (≈150 px of a 844 px screen); now: status line, then Buang + Simpan side by side (Simpan wider). */
.adm-savebar .save-label--short { display: none; }
@media (max-width: 767.98px) {
  .adm-savebar .save-label--long { display: none; }
  .adm-savebar .save-label--short { display: inline; }
  .adm-savebar { column-gap: 8px; row-gap: 4px; }
  .adm-savebar > .vf-btn[data-savebar-discard] { flex: 1 1 0; min-width: 0; padding-inline: 12px; }
  .adm-savebar > #btnSaveAll { flex: 1.6 1 0; min-width: 0; padding-inline: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
}

/* 10b. Page header actions on phones: one even grid of full-width buttons instead of a ragged wrap (Panel aktif had four
        buttons in three rows of different widths; Katalog produk two rows of mixed widths). */
@media (max-width: 599.98px) {
  html body .adm-content-wrap .adm-head__actions, .adm-c-head-acts { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); width: 100%; gap: 8px; }
  html body .adm-content-wrap .adm-head__actions > .vf-btn, .adm-c-head-acts > .vf-btn { width: 100%; justify-content: center; }
}

/* 10c. Card headers with a long description and an action (Produk › Kategori): on phones the text was squeezed into a
        ~130 px column beside the button. The action moves under the text. */
@media (max-width: 599.98px) {
  .adm-card__head:has(.adm-card__meta) { flex-wrap: wrap; align-items: flex-start; }
  .adm-card__head:has(.adm-card__meta) > .adm-card__titles { flex: 1 1 100%; min-width: 0; }
  .adm-card__head:has(.adm-card__meta) > .pd-headbtn { flex: 0 0 auto; }
}

/* 10d. Voucher › Cek voucher on phones: Kode and the day/quantity field share a row, the product picker takes the next
        row, then the button (the 2-column grid left Kode and Durasi each alone in a half-width cell). */
@media (max-width: 767.98px) {
  .adm-vprev__form > .adm-field:first-child { order: 0; }
  .adm-vprev__form > .adm-vprev__n { order: 1; }
  .adm-vprev__form > .adm-vprev__prod { order: 2; }
  .adm-vprev__form > .vf-btn { order: 3; }
  .adm-vprev__form .vf-input { width: 100%; }
}

/* 10e. Header-grid buttons may wrap to two lines instead of clipping ("Import dari Pterodactyl"). */
@media (max-width: 599.98px) {
  html body .adm-content-wrap .adm-head__actions > .vf-btn, .adm-c-head-acts > .vf-btn { white-space: normal; height: auto; min-height: 44px; line-height: 18px; padding-block: 8px; text-align: center; }
}

/* 10f. Pengaturan › Tema on phones: the sticky "Tema aktif / Pratinjau" card squeezed its two lines of state beside the
        reset button; the button now sits under the text, full width. */
@media (max-width: 599.98px) {
  html body .th-current { position: static; flex-wrap: wrap; }   /* not sticky: the save bar now carries "Ada perubahan belum disimpan" */
  .th-current__text { flex: 1 1 0; min-width: 0; }
  .th-current__reset { flex: 1 1 100%; justify-content: center; }
}

/* 10g. A button hidden with the [hidden] attribute stays hidden. .vf-btn sets display: inline-flex, which beat the
        browser's [hidden] rule, so script-hidden buttons stayed on screen (Live chat "Notifikasi desktop" after the
        choice was made, the duplicate "Ekspor filter ini" on Transaksi). */
.vf-btn[hidden] { display: none !important; }

/* 10h. Admin menu (phone drawer, short desktop windows): the item list scrolls, but nothing showed that more items
        were below — on a 844 px phone "Reseller", "Status sistem", "Cadangan data" and "Pengaturan" sat under the fold
        with the last visible item cut exactly at the divider. The list now fades out at the bottom edge while there is
        more to scroll (vf-upgrade.js sets .is-more), and stops fading at the end. */
.adm-sb-nav.is-more { -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 48px), transparent); mask-image: linear-gradient(to bottom, #000 calc(100% - 48px), transparent); }

/* 10i. Read-only (replica) banner on customer pages: was an inline style with literal colours; now on the warning tokens. */
.vf-replica-banner { position: sticky; top: 0; z-index: 90; padding: 8px 16px; background: var(--vf-warning-subtle); color: var(--vf-text-1); border-bottom: 1px solid var(--vf-warning); font: var(--vf-t-ui-strong); text-align: center; }


/* -----------------------------------------------------------------------------------------------------
   11. ROUND 4e — public catalogue, product detail + checkout (shared by produk-detail and produk-stok-detail).
   ----------------------------------------------------------------------------------------------------- */
/* 3. "What happens after paying" — three short numbered steps (buyers: compact row above the secure note; guests: the
      full list in the buy card). Tokens only. */
.vx-flow { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.vx-flow li { display: flex; align-items: flex-start; gap: 12px; font: var(--vf-t-body-sm); color: var(--vf-text-2); }
.vx-flow li b { font-weight: 600; color: var(--vf-text-1); }
.vx-flow__n { flex: none; width: 24px; height: 24px; border-radius: 999px; display: inline-flex; align-items: center; justify-content: center; background: var(--vf-accent-subtle); color: var(--vf-accent-text); font: 600 12px/1 var(--vf-font-num); }
.vx-flow--compact { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; padding: 12px; border-radius: var(--vf-r-ctl, 12px); background: var(--vf-surface-3); }
.vx-flow--compact li { flex-direction: column; align-items: center; gap: 4px; text-align: center; font: var(--vf-t-caption); }

/* 4. Guest buy card: takes the payment card's place (after the product info), on phones and in the desktop side column */
.vx-pdp .vx-guestbuy { order: 5; }
.vx-guestbuy { gap: 16px; }
.vx-guestbuy__alt { margin: 0; text-align: center; font: var(--vf-t-body-sm); color: var(--vf-text-2); }
.vx-buybar--guest .vx-buybar__btn.vf-btn { text-decoration: none; }
@media (max-width: 1023.98px) { .vx-guestbuy__alt { margin-bottom: 8px; } }
.vx-link { color: var(--vf-accent-text); font-weight: 600; text-decoration: none; }
.vx-link:hover { text-decoration: underline; }
.vf-pd2__buy.vx-guestbuy { display: flex; flex-direction: column; }
