/* .lpv-live-page-body is applied to <body> whenever the live page opens
   as its OWN page - the "Cipta Halaman WP" fullscreen page, the standalone
   HTML export, and the admin Preview - so it should feel like opening a
   native TikTok/Shopee Live app: the video fills the whole mobile screen
   edge to edge from the very first frame, no card/letterboxing, no
   "grows after a while" - it's simply always 100% of the viewport.
   .lpv-embed-wrap wraps the widget when embedded via the [live_pageviral]
   shortcode inside a normal theme page instead, so it keeps the smaller
   centered phone-card look and never touches the theme's own <body>. */
.lpv-live-page-body,
.lpv-embed-wrap {
  margin: 0;
  background: #17181c;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
}
.lpv-embed-wrap { padding: 20px 10px; }

/* 100vh first as a fallback for browsers without dvh support; 100dvh
   (dynamic viewport height) after it wins on everything else, and is
   the actual fix for the video appearing to "grow" after a moment on
   mobile - that was 100vh being recalculated when the browser's own
   address bar auto-hides on scroll, not the video itself changing. */
.lpv-live-page-body {
  min-height: 100vh;
  min-height: 100dvh;
  padding: 0;
}
/* render_html() always wraps its output in .lpv-embed-wrap (shared by the
   shortcode-embed path), so in a fullscreen context that div ends up
   nested INSIDE .lpv-live-page-body too - without this, its 20px/10px
   padding above would still show as a visible border around the "fullscreen"
   frame, undermining the whole point of this section. */
.lpv-live-page-body .lpv-embed-wrap {
  padding: 0;
  width: 100%;
  height: 100%;
}

.lpv-live-frame {
  position: relative;
  width: 100%;
  max-width: 420px;
  aspect-ratio: 9 / 16;
  background: var(--bg, #0b0b0f);
  border-radius: 18px;
  overflow: hidden;
  color: #fff;
  box-shadow: 0 20px 60px rgba(0,0,0,0.5);
  display: flex;
  flex-direction: column;
}

/* Fullscreen contexts: no card - the frame itself IS the screen. Covers two
   different cases: (1) .lpv-live-page-body, the standalone export's own
   permanent full-page mode (edge-to-edge on ALL sides, unchanged below), and
   (2) .lpv-live-frame.lpv-illusion, the CSS-driven "fullscreen illusion"
   toggled on tap (see enterFullscreenIllusion() in live-page.js) for the
   embedded [live_pageviral] shortcode case - which, per user request, is
   edge-to-edge at the TOP only: the bottom keeps its normal rounded-card
   look and stops short of the phone's own bottom nav/gesture area instead
   of stretching flush to it (see the height/border-radius/box-shadow diff
   from the standalone rule below, and .lpv-embed-wrap.lpv-illusion-active's
   align-items: flex-start further down, which is what lets the frame sit at
   the top of the screen with the gap left below it rather than centered).
   Deliberately NO :fullscreen selector here, even though v1.13.3 DOES also
   attempt the real Fullscreen API again (requestRealFullscreenBestEffort()
   in live-page.js, purely to additionally hide the browser's own address
   bar): v1.13.0 styled off :fullscreen directly and reverted in v1.13.1/
   v1.13.2 because a real Fullscreen API element only paints its own DOM
   subtree, and it behaves inconsistently enough across mobile browsers/
   in-app WebViews (WhatsApp/Facebook/TikTok's own in-app browser, which is
   how most visitors actually open a live link) that styling depended on it
   working correctly, so when it misbehaved every button on screen stopped
   responding. These class-based rules are applied by JS regardless of
   whether the real Fullscreen API call underneath them succeeds, half-
   works, or isn't supported at all - so every button, modal and drawer
   keeps working exactly like it does in the small card view, address bar
   hidden or not. */
.lpv-live-page-body .lpv-live-frame {
  max-width: none;
  aspect-ratio: auto;
  width: 100%;
  height: 100vh;
  height: 100dvh;
  /* Mobile browsers (Android Chrome & iOS Safari alike) don't always shrink
     100vh/100dvh for the on-screen keyboard - the frame can stay full
     device-height, pushing .lpv-bottom (the comment input + its 3 icon
     buttons) below the fold, reachable only by manually panning the visual
     viewport. --lpv-vvh mirrors window.visualViewport.height (which DOES
     update live as the keyboard opens/closes - see the listener in
     live-page.js) so the frame actually shrinks to match what's visibly on
     screen and the flex layout below (.lpv-spacer soaks up the slack,
     .lpv-bottom stays pinned to the real bottom) keeps the input + buttons
     in view without any swipe/pan. Falls back to 100dvh on the rare browser
     without VisualViewport support. */
  height: var(--lpv-vvh, 100dvh);
  border-radius: 0;
  box-shadow: none;
}
/* Same idea, but the frame is left a bit shorter than the full viewport
   (14px, plus the bottom safe-area inset on notched/gesture-bar phones)
   and only the bottom two corners are rounded again, so the bottom of the
   card looks exactly like it does outside illusion mode instead of
   stretching flush to the screen edge. */
.lpv-live-frame.lpv-illusion {
  max-width: none;
  aspect-ratio: auto;
  width: 100%;
  height: calc(100vh - 14px);
  height: calc(100dvh - 14px);
  height: calc(var(--lpv-vvh, 100dvh) - 14px - env(safe-area-inset-bottom, 0px));
  border-radius: 0 0 18px 18px;
  box-shadow: 0 20px 60px rgba(0,0,0,0.5);
}
/* Edge-to-edge means real notches/home-indicators are now in play (they
   weren't when the widget sat inset inside a card) - viewport-fit=cover
   (set on <meta name="viewport">) exposes safe-area-inset-*, keep the top
   UI clear of them. env() with 0px fallback is a no-op on phones without a
   notch. Only the TOP overlay needs this now - the bottom bar in illusion
   mode keeps its normal (non-edge-to-edge) padding since the frame no
   longer touches the screen's bottom edge there. */
.lpv-live-page-body .lpv-live-overlay-top,
.lpv-live-frame.lpv-illusion .lpv-live-overlay-top {
  padding-top: calc(12px + env(safe-area-inset-top, 0px));
}
.lpv-live-page-body .lpv-bottom {
  padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px));
}

/* Illusion mode (embedded [live_pageviral] shortcode only - the standalone
   .lpv-live-page-body case is already permanently full-size and never gets
   this class) also needs .lpv-embed-wrap itself to break out of its normal
   centered/padded card layout and cover the WHOLE browser viewport, on top
   of the rest of the theme page underneath - otherwise the frame would just
   grow to fill its own padded box instead of the real screen. align-items:
   flex-start (overriding the centered default above) keeps the frame flush
   against the TOP of that viewport-covering box, so the intentional gap
   from the shorter height above lands entirely at the bottom, not split
   evenly top and bottom. */
.lpv-embed-wrap.lpv-illusion-active {
  position: fixed;
  inset: 0;
  z-index: 999999;
  padding: 0;
  background: #000;
  align-items: flex-start;
}
/* Prevents the theme page behind the illusion from scrolling underneath it
   (iOS in particular still allows background touch-scroll through a fixed
   overlay unless the page itself is also locked) - toggled alongside
   .lpv-embed-wrap.lpv-illusion-active by the same JS. */
html.lpv-illusion-lock,
html.lpv-illusion-lock body {
  overflow: hidden;
}

/* "Exit fullscreen" button - only meaningful (and only shown) once
   .lpv-illusion is active; there's no OS/browser-level Escape-key or
   swipe-down gesture to fall back on since this was never real Fullscreen
   API, so the widget needs its own visible way back to the normal card. */
.lpv-illusion-close {
  display: none;
  position: absolute;
  top: calc(10px + env(safe-area-inset-top, 0px));
  right: 10px;
  z-index: 8;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: none;
  cursor: pointer;
  background: rgba(0,0,0,0.45);
  color: #fff;
  font-size: 15px;
  align-items: center;
  justify-content: center;
}
.lpv-live-frame.lpv-illusion .lpv-illusion-close {
  display: flex;
}

.lpv-live-media {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: #111;
}

.lpv-live-overlay-top {
  position: relative;
  z-index: 3;
  padding: 12px 12px 0;
  background: linear-gradient(to bottom, rgba(0,0,0,0.55), rgba(0,0,0,0));
}

.lpv-live-hostrow { display: flex; align-items: center; gap: 8px; }
.lpv-live-avatar { width: 36px; height: 36px; border-radius: 50%; object-fit: cover; border: 1.5px solid rgba(255,255,255,0.7); }
.lpv-live-host-info { flex: 1; min-width: 0; }
.lpv-live-host-name { font-size: 13px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 110px; }
.lpv-live-likes { font-size: 11px; color: #e5e5e5; display: flex; align-items: center; gap: 3px; }
.lpv-join-btn {
  background: var(--accent, #ff3b5c);
  color: #fff;
  border: none;
  font-size: 12px;
  font-weight: 700;
  padding: 6px 12px;
  border-radius: 999px;
  cursor: pointer;
  white-space: nowrap;
}
.lpv-join-btn.is-joined {
  background: rgba(255,255,255,0.16);
  max-width: 84px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.lpv-join-form {
  display: none;
  align-items: center;
  gap: 4px;
  background: rgba(0,0,0,0.4);
  border-radius: 999px;
  padding: 3px 4px 3px 10px;
}
.lpv-join-form.open { display: inline-flex; }
.lpv-join-form .lpv-join-input {
  width: 74px;
  min-width: 0;
  background: transparent;
  border: none;
  color: #fff;
  font-size: 12px;
  outline: none;
}
.lpv-join-form .lpv-join-input::placeholder { color: rgba(255,255,255,0.6); }
.lpv-join-form .lpv-join-ok {
  background: var(--accent, #ff3b5c);
  color: #fff;
  border: none;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
}
.lpv-viewer-avatars { display: flex; align-items: center; }
.lpv-viewer-avatars .lpv-dot {
  width: 20px; height: 20px; border-radius: 50%; background: #999; border: 1.5px solid #17181c;
  margin-left: -8px; background-size: cover; background-position: center;
}
.lpv-viewer-avatars .lpv-dot:first-child { margin-left: 0; }
.lpv-viewer-count { font-size: 12px; font-weight: 600; margin-left: 6px; }
.lpv-icon-btn { border: none; background: none; cursor: pointer; color: #888; }
.lpv-icon-btn:hover { color: #ff3b5c; }
.lpv-close-btn { background: rgba(0,0,0,0.35); border: none; color: #fff; width: 26px; height: 26px; border-radius: 50%; font-size: 14px; cursor: pointer; }

.lpv-rank-bar {
  margin-top: 10px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: rgba(255, 200, 60, 0.15);
  border: 1px solid rgba(255, 200, 60, 0.35);
  color: #ffd66b;
  font-size: 11px;
  font-weight: 700;
  padding: 6px 10px;
  border-radius: 8px;
}
.lpv-rank-bar .lpv-gift { color: #fff; opacity: 0.9; }

.lpv-live-badge-row { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
.lpv-live-badge { background: var(--accent, #ff3b5c); font-size: 11px; font-weight: 800; padding: 3px 8px; border-radius: 4px; letter-spacing: 0.03em; }
.lpv-mute-pill { background: rgba(0,0,0,0.4); font-size: 11px; padding: 3px 10px; border-radius: 999px; display: flex; align-items: center; gap: 5px; cursor: pointer; border: none; color: #fff; }
.lpv-mute-pill.on-right { margin-left: auto; }

.lpv-spacer { flex: 1; position: relative; z-index: 2; cursor: pointer; }

/* Tap-to-play gate: shown over the poster until the visitor taps once,
   which starts the video WITH sound in the same gesture (browsers only
   allow audio playback to start from a direct user gesture, so this one
   deliberate tap replaces the old two-step "autoplay muted, then tap a
   tiny pill to unmute" flow, which was easy to miss/mistap). */
.lpv-play-gate {
  position: absolute; inset: 0; z-index: 6;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,0.25);
  border: none; padding: 0; margin: 0; cursor: pointer;
}
.lpv-play-gate-btn {
  display: flex; align-items: center; gap: 10px;
  background: rgba(0,0,0,0.7);
  color: #fff; font-size: 14px; font-weight: 700;
  padding: 14px 22px; border-radius: 999px;
  white-space: nowrap;
  animation: lpvUnmutePulse 1.6s ease-in-out infinite;
}
.lpv-play-gate-icon { font-size: 20px; }
@keyframes lpvUnmutePulse {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.06); opacity: 0.9; }
}

.lpv-floating-hearts { position: absolute; right: 10px; bottom: 170px; width: 40px; height: 220px; z-index: 4; pointer-events: none; }
.lpv-floating-hearts span { position: absolute; bottom: 0; font-size: 18px; animation: lpvFloatUp 3s ease-in forwards; }
@keyframes lpvFloatUp {
  0% { transform: translateY(0) translateX(0) scale(0.6); opacity: 0; }
  15% { opacity: 1; }
  100% { transform: translateY(-200px) translateX(var(--drift, 10px)) scale(1); opacity: 0; }
}

.lpv-bottom {
  position: relative;
  z-index: 3;
  background: linear-gradient(to top, rgba(0,0,0,0.75), rgba(0,0,0,0.15) 70%, transparent);
  padding: 8px 12px 12px;
}

.lpv-pinned-comment {
  display: flex; gap: 8px; align-items: flex-start;
  background: rgba(20,20,24,0.75);
  border-left: 3px solid var(--accent, #ff3b5c);
  padding: 8px 10px;
  border-radius: 8px;
  font-size: 11.5px;
  margin-bottom: 8px;
}
.lpv-pinned-comment b { display: block; font-size: 10px; color: var(--accent, #ff3b5c); letter-spacing: 0.04em; margin-bottom: 2px; }

.lpv-chat-feed {
  max-height: 150px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin-bottom: 8px;
  mask-image: linear-gradient(to bottom, transparent, black 25%);
}
.lpv-chat-msg { font-size: 12.5px; line-height: 1.35; display: flex; gap: 6px; align-items: flex-start; animation: lpvMsgIn 0.25s ease; }
.lpv-chat-msg .lpv-name { font-weight: 700; color: #ffd6de; }
.lpv-chat-msg.is-reply .lpv-name { color: #7ee787; }
.lpv-chat-msg-body { min-width: 0; }
.lpv-reply-avatar {
  width: 18px; height: 18px; border-radius: 50%; object-fit: cover;
  flex-shrink: 0; margin-top: 1px;
  border: 1px solid rgba(255,255,255,0.4);
}
.lpv-reply-to {
  display: block;
  font-size: 10px;
  font-weight: 600;
  opacity: 0.75;
  margin-bottom: 2px;
}
.lpv-chat-msg .lpv-bubble-reply {
  background: rgba(22, 163, 74, 0.18);
  border: 1px solid rgba(126,231,135,0.3);
  padding: 4px 8px;
  border-radius: 8px;
  display: inline-block;
}
@keyframes lpvMsgIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }

.lpv-live-controls { display: flex; align-items: center; gap: 8px; }
.lpv-chat-input {
  flex: 1;
  background: rgba(255,255,255,0.12);
  border: 1px solid rgba(255,255,255,0.25);
  border-radius: 999px;
  padding: 9px 14px;
  color: #fff;
  font-size: 13px;
  outline: none;
}
.lpv-chat-input::placeholder { color: rgba(255,255,255,0.6); }
.lpv-icon-round {
  width: 38px; height: 38px; border-radius: 50%; border: none; cursor: pointer;
  display: flex; align-items: center; justify-content: center; font-size: 17px; flex-shrink: 0;
  background: rgba(255,255,255,0.14); color: #fff; position: relative;
}
.lpv-icon-round.cart {
  width: 46px; height: 46px; font-size: 23px;
  background: #ffcc33; color: #1a1a1a;
  box-shadow: 0 2px 8px rgba(255,204,51,0.5);
}
/* Deliberately a different colour/position from .cart (the yellow "beg"
   button) - visitors were mistaking the cart button for a chat-send
   button since there was no dedicated one before. This one submits
   #lpv-chat-form (type="submit"), the cart button stays type="button"
   and only opens the product drawer. */
.lpv-icon-round.send {
  background: var(--accent, #ff3b5c);
  color: #fff;
  font-size: 15px;
}
.lpv-icon-round .lpv-badge-count {
  position: absolute; top: -4px; right: -4px; background: var(--accent, #ff3b5c);
  color: #fff; font-size: 10px; font-weight: 800; min-width: 16px; height: 16px; border-radius: 999px;
  display: flex; align-items: center; justify-content: center; padding: 0 3px;
}

/* Cart drawer */
.lpv-cart-backdrop {
  position: fixed; inset: 0; background: rgba(0,0,0,0.55); z-index: 10;
  display: none; align-items: flex-end; justify-content: center;
}
.lpv-cart-backdrop.open { display: flex; }
.lpv-cart-drawer {
  width: 100%; max-width: 420px; background: #fff; color: #16171b;
  border-radius: 18px 18px 0 0; max-height: 82%; overflow-y: auto; padding: 18px;
}
.lpv-cart-drawer h3 { margin: 0 0 4px; font-size: 16px; }
.lpv-cart-drawer .lpv-sub { color: #6b7280; font-size: 12px; margin-bottom: 14px; }
.lpv-cart-product {
  display: flex; gap: 10px; border: 1px solid #eee; border-radius: 12px; padding: 10px; margin-bottom: 10px; align-items: center;
}
.lpv-cart-product-img {
  width: 56px; height: 56px; border-radius: 10px; flex-shrink: 0;
  background: #fff; border: 1px solid #eee; padding: 4px; box-sizing: border-box;
  display: flex; align-items: center; justify-content: center; overflow: hidden;
}
.lpv-cart-product-img img { width: 100%; height: 100%; object-fit: contain; }
.lpv-cart-product .lpv-info { flex: 1; min-width: 0; }
.lpv-cart-product .lpv-info .lpv-name { font-weight: 700; font-size: 13.5px; }
.lpv-cart-product .lpv-info .lpv-price { color: var(--accent, #ff3b5c); font-weight: 700; font-size: 13px; margin-top: 2px; }
/* WooCommerce's own <del>/<ins> markup for a sale price - force the
   struck-through / plain distinction explicitly (rather than relying on
   the browser's default <del> styling) since the active theme's own CSS
   loads alongside ours on the fullscreen page and can reset it. */
.lpv-price-html del { text-decoration: line-through !important; opacity: 0.55; font-weight: 400 !important; margin-right: 5px; font-size: 0.9em; }
.lpv-price-html ins { text-decoration: none !important; }
.lpv-cart-product.pinned { border-color: var(--accent, #ff3b5c); background: #fff7f8; }
.lpv-pin-tag { display:inline-block; font-size: 10px; font-weight: 800; color: var(--accent, #ff3b5c); background: #ffe4e9; padding: 2px 6px; border-radius: 4px; margin-bottom: 4px; }
.lpv-buy-btn {
  background: var(--accent, #ff3b5c); color: #fff; border: none; padding: 8px 14px;
  border-radius: 999px; font-size: 12.5px; font-weight: 700; cursor: pointer; flex-shrink: 0;
}

/* Order modal */
.lpv-order-backdrop {
  position: fixed; inset: 0; background: rgba(0,0,0,0.6); z-index: 20;
  display: none; align-items: center; justify-content: center; padding: 16px;
}
.lpv-order-backdrop.open { display: flex; }
.lpv-order-modal { width: 100%; max-width: 360px; background: #fff; color: #16171b; border-radius: 14px; padding: 20px; }
.lpv-order-modal h3 { margin: 0 0 4px; font-size: 16px; }
.lpv-order-modal .lpv-sub { color: #6b7280; font-size: 12.5px; margin-bottom: 14px; }
.lpv-order-modal label { display: block; font-size: 12.5px; font-weight: 600; margin: 10px 0 5px; }
.lpv-order-modal input, .lpv-order-modal select {
  width: 100%; padding: 9px 11px; border: 1px solid #e5e7eb; border-radius: 8px; font-size: 13.5px; box-sizing: border-box;
}
.lpv-order-modal .lpv-actions { display: flex; gap: 8px; margin-top: 16px; }
.lpv-order-modal .lpv-actions button { flex: 1; padding: 10px; border-radius: 8px; border: 1px solid #e5e7eb; background: #fff; font-weight: 700; cursor: pointer; }
.lpv-order-modal .lpv-actions .lpv-confirm { background: var(--accent, #ff3b5c); color: #fff; border-color: var(--accent, #ff3b5c); }

/* WooCommerce prints an accessibility-only label inside price_html for
   variable/range prices (e.g. "Price range: RM70 through RM150"). It's
   meant to be visually hidden - normally a theme's stylesheet does that,
   but the exported standalone HTML and the admin preview iframe don't
   load any theme CSS, so hide it explicitly here too. Covers both the
   cart drawer price and the "Sahkan pesanan" confirm modal price, since
   both render the same raw price_html via .lpv-price-html. */
.lpv-price-html .screen-reader-text {
  position: absolute !important;
  width: 1px; height: 1px;
  overflow: hidden;
  clip: rect(1px, 1px, 1px, 1px);
  white-space: nowrap;
}

.lpv-preview-note {
  position: absolute; top: 8px; left: 50%; transform: translateX(-50%);
  background: #111; color: #fff; font-size: 10px; padding: 3px 10px; border-radius: 999px; z-index: 5; opacity: 0.85;
}

/* Floating "promoted product" badge - sits between the chat feed and the
   chat-input row (see live-page-render.php's .lpv-bottom order), never
   above the pinned comment/video area, so it stays put regardless of how
   many chat messages have come in. Both the image/name area AND the
   dedicated "Beli Sekarang" button open the full product drawer (handles
   the "more than one package" case) rather than jumping straight to a
   single pre-selected package. */
.lpv-promo-badge {
  display: flex; align-items: center; gap: 8px;
  background: rgba(20,20,24,0.82);
  border: 1px solid rgba(255,255,255,0.14);
  border-radius: 12px;
  padding: 7px 8px;
  margin-bottom: 8px;
  backdrop-filter: blur(4px);
}
.lpv-promo-badge-main {
  display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0;
  background: none; border: none; padding: 0; margin: 0; cursor: pointer; text-align: left; color: #fff;
}
.lpv-promo-badge-img {
  width: 40px; height: 40px; border-radius: 8px; object-fit: contain; background: #fff;
  border: 1px solid rgba(255,255,255,0.25); flex-shrink: 0; padding: 2px; box-sizing: border-box;
}
.lpv-promo-badge-info { display: flex; flex-direction: column; min-width: 0; }
.lpv-promo-badge-tag {
  font-size: 9px; font-weight: 800; color: var(--accent, #ff3b5c); letter-spacing: 0.03em;
}
.lpv-promo-badge-name {
  font-size: 12px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 130px;
}
.lpv-promo-badge-price { font-size: 12px; font-weight: 800; color: #ffd66b; }
.lpv-promo-badge-more { font-size: 9.5px; color: rgba(255,255,255,0.65); white-space: nowrap; flex-shrink: 0; padding-right: 4px; }
.lpv-promo-badge-buy {
  flex-shrink: 0; background: var(--accent, #ff3b5c); color: #fff; border: none;
  padding: 8px 13px; border-radius: 999px; font-size: 11.5px; font-weight: 800; cursor: pointer;
}

/* Popup/overlay checkout: sits ABOVE the live frame's own video/audio, so
   tapping "Beli Sekarang" -> "Teruskan ke Checkout" (builtin checkout mode
   only) never navigates away and the live video/audio keeps running
   underneath - see assets/js/live-page.js + assets/js/checkout.js. */
.lpv-checkout-overlay {
  position: fixed; inset: 0; z-index: 30; background: #f4f4f6;
  display: none; overflow-y: auto; -webkit-overflow-scrolling: touch;
}
.lpv-checkout-overlay.open { display: block; }
.lpv-checkout-overlay-close {
  position: fixed; top: calc(10px + env(safe-area-inset-top, 0px)); right: 12px; z-index: 31;
  width: 32px; height: 32px; border-radius: 50%; border: none; cursor: pointer;
  background: rgba(0,0,0,0.55); color: #fff; font-size: 15px; display: flex; align-items: center; justify-content: center;
}
.lpv-checkout-overlay-inner { min-height: 100%; }

/* Testimonials (Shopee-style reviews) - the ⭐ icon button + drawer reuse
   .lpv-cart-backdrop/.lpv-cart-drawer's open/backdrop/sheet mechanics
   (see live-page-render.php), only the content inside is new here. */
.lpv-icon-round.testimonials {
  background: #fff3d6; color: #a5670a;
}
.lpv-testimonials-stats {
  display: flex; align-items: baseline; gap: 6px; margin-bottom: 14px;
  padding-bottom: 12px; border-bottom: 1px solid #eee;
}
.lpv-testimonials-avg { font-size: 22px; font-weight: 800; color: #16171b; }
.lpv-testimonials-stars, .lpv-testimonial-stars { color: #ffb400; font-size: 13px; letter-spacing: 1px; }
.lpv-testimonial-list { display: flex; flex-direction: column; gap: 14px; }
.lpv-testimonial-item { border-bottom: 1px solid #f0f0f0; padding-bottom: 12px; }
.lpv-testimonial-item:last-child { border-bottom: none; padding-bottom: 0; }

/* Same testimonial list, reused a second time inside the product/cart
   drawer itself (see live-page-render.php) so a visitor scrolling past the
   package list runs straight into social proof without needing to close
   this drawer and open the separate ⭐ one. */
.lpv-cart-testimonials-section {
  margin-top: 18px; padding-top: 16px; border-top: 1px solid #eee;
}
.lpv-cart-testimonials-heading h3 { margin: 0 0 4px; font-size: 15px; }
.lpv-testimonial-item-head { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.lpv-testimonial-avatar {
  width: 26px; height: 26px; border-radius: 50%; flex-shrink: 0;
  background: var(--accent, #ff3b5c); color: #fff; font-size: 12px; font-weight: 800;
  display: flex; align-items: center; justify-content: center;
}
.lpv-testimonial-name { font-weight: 700; font-size: 13px; color: #16171b; flex: 1; min-width: 0; }
.lpv-testimonial-comment { margin: 4px 0 0; font-size: 12.5px; line-height: 1.45; color: #333; }
.lpv-testimonial-photos { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; }
.lpv-testimonial-photo {
  width: 56px; height: 56px; border-radius: 8px; overflow: hidden; display: block;
  border: 1px solid #eee; flex-shrink: 0;
  /* Now a <button> (was an <a target="_blank">) so tapping it opens the
     in-page lightbox below instead of a new browser tab - reset the
     default button chrome back to how the plain image tile looked. */
  padding: 0; background: none; cursor: pointer;
}
.lpv-testimonial-photo img { width: 100%; height: 100%; object-fit: cover; }

/* Fast-checkout loading overlay - see #lpv-checkout-loading-backdrop in
   live-page-render.php / the skip-order-confirm submit branch in
   live-page.js. */
.lpv-checkout-loading-backdrop {
  position: fixed; inset: 0; background: rgba(0,0,0,0.72); z-index: 25;
  display: none; flex-direction: column; align-items: center; justify-content: center; gap: 14px;
  color: #fff;
}
.lpv-checkout-loading-backdrop.open { display: flex; }
.lpv-checkout-loading-spinner {
  width: 38px; height: 38px; border-radius: 50%;
  border: 3px solid rgba(255,255,255,0.25); border-top-color: #fff;
  animation: lpv-checkout-loading-spin 0.8s linear infinite;
}
.lpv-checkout-loading-text { font-size: 13px; font-weight: 600; }
@keyframes lpv-checkout-loading-spin { to { transform: rotate(360deg); } }

/* Testimonial photo lightbox - see the [data-lpv-lightbox] delegated click
   handler in live-page.js. Sits above every other overlay/drawer. */
.lpv-lightbox-backdrop {
  position: fixed; inset: 0; background: rgba(0,0,0,0.88); z-index: 30;
  display: none; align-items: center; justify-content: center; padding: 24px;
}
.lpv-lightbox-backdrop.open { display: flex; }
.lpv-lightbox-img { max-width: 100%; max-height: 100%; border-radius: 8px; object-fit: contain; }
.lpv-lightbox-close {
  position: absolute; top: 16px; right: 16px; width: 36px; height: 36px; border-radius: 50%;
  background: rgba(255,255,255,0.15); color: #fff; border: none; font-size: 16px; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}

/* Sale notification toast ("X beli Y · Z minit lalu") - a single
   reusable compact pill, rotated/populated client-side (see
   showNextSaleToast() in live-page.js). Sits just above the chat form,
   never full-screen, never blocks the video or input. */
.lpv-sale-toast {
  display: inline-flex; align-items: center; max-width: 90%;
  /* Green/high-contrast so a sale reads as "jualan/success" at a glance
     against the video behind it, instead of blending in as just another
     dark translucent chip like the rest of the overlay UI. */
  background: #17c964; color: #fff; font-size: 11.5px; font-weight: 700;
  box-shadow: 0 2px 10px rgba(23,201,100,0.45);
  padding: 7px 12px; border-radius: 999px; margin-bottom: 8px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  opacity: 0; transform: translateY(6px);
  transition: opacity 0.25s ease, transform 0.25s ease;
  pointer-events: none;
}
.lpv-sale-toast.show { opacity: 1; transform: translateY(0); }

/* Segment scroll/swipe carousel (TikTok/Reels-style "next host's live
   scrolls up from below") - only rendered when a live page has MORE THAN
   ONE playable host segment (see live-page-render.php); a single-segment
   page keeps the plain #lpv-video element it always had, untouched. Exactly
   two .lpv-segment-layer slots exist at any time (current + one
   preloaded neighbour) regardless of how many segments the page has - see
   the long comment in live-page.js for why (bandwidth: never preload every
   segment's video at once). */
.lpv-segment-stack {
  position: absolute; inset: 0; overflow: hidden; touch-action: pan-y;
}
.lpv-segment-layer {
  position: absolute; inset: 0;
  transform: translateY(100%);
  transition: transform 380ms ease;
}
.lpv-segment-layer.is-active { transform: translateY(0); }
.lpv-segment-layer.is-prev { transform: translateY(-100%); }
/* While a transition is actively running, JS adds this to both layers so
   the "incoming" one (still sitting at its off-screen starting position on
   the very first paint) doesn't itself get an unwanted transition from
   nothing to translateY(100%)/-100%) - only the SUBSEQUENT class change
   that starts the real slide should animate. */
.lpv-segment-layer.lpv-no-transition { transition: none; }

.lpv-segment-scroll-hint {
  position: absolute; right: 10px; bottom: 190px; z-index: 4;
  color: rgba(255,255,255,0.55); font-size: 20px;
  animation: lpvScrollHintBounce 1.8s ease-in-out infinite;
  pointer-events: none;
}
@keyframes lpvScrollHintBounce {
  0%, 100% { transform: translateY(0); opacity: 0.55; }
  50% { transform: translateY(5px); opacity: 0.9; }
}

/* Real YouTube live embed (Media tab "Sumber video utama" toggle) - a
   poster image sits here until the visitor taps the play gate, at which
   point live-page.js appends the actual <iframe> to this same wrapper. No
   carousel/segment scrolling applies in this mode - the whole video area is
   just this one live stream. */
.lpv-youtube-wrap {
  position: absolute; inset: 0; overflow: hidden; background: #111;
}
.lpv-youtube-iframe {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  border: 0;
}
