/* ==========================================================================
   vxl components — the ONE shared component tier of default_v2.

   Ported from the marketing site's locked 2026 design system
   (vodlix-site-statamic docs/ds/vodlix-2026.css, decisions in
   docs/DESIGN-SYSTEM.md §11) onto the tenant token layer in layouts.css.
   Every layout (classic, cinematic, grid, showcase) consumes THESE
   components; a layout sheet may size and arrange them, never restyle them.

   The design language, in one breath: pill buttons in tiers, hairline
   borders and NO shadows on cards (shadow belongs to genuinely floating
   things only), hovers that change colour/border and never move anything,
   sharp ink for text that reads and muted ink only for metadata, a fixed
   type scale with −2% display tracking, tabular numerals for numbers, and
   one .15s ease for every colour transition.

   White-label invariant (same as layouts.css): no designed colour in here —
   only --vxl-* tokens, which derive from the tenant's --vod-* palette.

   Load position (global_header.html): after layouts.css (tokens first) and
   after the legacy tier (vodlix.css + tenant custom.css load earlier from
   global head.html), before every layout-*.css. Source order therefore
   beats any legacy rule that is not !important; where a legacy rule IS
   !important, the override here carries its own and is tagged
   `legacy-override: <file>:<line>` so Phase 10 can delete both ends.
   ========================================================================== */

/* ==========================================================================
   1. TYPE — the DS scale on the tenant's font.

   Element rules are wrapped in :where() with the Bootstrap sizing utilities
   excluded, so:
     - specificity stays (0,1,1) — beats Bootstrap's bare element rules,
       loses to any component rule with two classes (their headings keep
       their own sizes);
     - a heading that opts into a Bootstrap size (`h2.h5`, the rail-heading
       idiom all over the theme) is not matched at all and keeps meaning
       what it said.
   ========================================================================== */
/* .vxh__logo is excluded by name: the site logo ships as an <h1> in the
   header, and the display size has no business inside chrome. */
body[data-layout] :where(h1:not(.h1, .h2, .h3, .h4, .h5, .h6, .vxh__logo)) {
  font-size: var(--vxl-fs-h1);
  font-weight: 700;
  line-height: 1.12;
  letter-spacing: var(--vxl-tracking-tight);
}
body[data-layout] :where(h2:not(.h1, .h2, .h3, .h4, .h5, .h6)) {
  font-size: var(--vxl-fs-h2);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: var(--vxl-tracking-tight);
}
body[data-layout] :where(h3:not(.h1, .h2, .h3, .h4, .h5, .h6)) {
  font-size: var(--vxl-fs-h3);
  font-weight: 600;
  line-height: 1.3;
}
/* keep the scale monotonic below h3 — Bootstrap's h4 (1.5rem) would
   otherwise render LARGER than the DS h3 */
body[data-layout] :where(h4:not(.h1, .h2, .h3, .h4, .h5, .h6)) {
  font-size: var(--vxl-fs-body);
  font-weight: 600;
  line-height: 1.35;
}
body[data-layout] :where(h5:not(.h1, .h2, .h3, .h4, .h5, .h6)) {
  font-size: 14px;
  font-weight: 600;
  line-height: 1.4;
}
body[data-layout] :where(h6:not(.h1, .h2, .h3, .h4, .h5, .h6)) {
  font-size: var(--vxl-fs-small);
  font-weight: 600;
  line-height: 1.4;
}

/* Utilities — same measures, opt-in on any element. Placed after the
   element rules so an explicit .vxl-h* always wins the tie. */
body[data-layout] .vxl-h1 { font-size: var(--vxl-fs-h1); font-weight: 700; line-height: 1.12; letter-spacing: var(--vxl-tracking-tight); }
body[data-layout] .vxl-h2 { font-size: var(--vxl-fs-h2); font-weight: 600; line-height: 1.2; letter-spacing: var(--vxl-tracking-tight); }
body[data-layout] .vxl-h3 { font-size: var(--vxl-fs-h3); font-weight: 600; line-height: 1.3; }
body[data-layout] .vxl-lede { font-size: var(--vxl-fs-lede); line-height: 1.55; color: var(--vxl-ink-muted); }
body[data-layout] .vxl-small { font-size: var(--vxl-fs-small); line-height: 1.4; }
/* numbers that line up: prices, stats, durations, tables */
body[data-layout] .vxl-num { font-variant-numeric: var(--vxl-num); }

/* ==========================================================================
   2. FOCUS — keyboard focus is visible everywhere, pointer focus never is.
   The brand ring on controls; fields get their halo in the field kit (§6).
   :where() keeps this at (0,1,1) so components with a designed focus state
   (two-class rules) still win.
   ========================================================================== */
body[data-layout] :where(a, button, [role="button"], [tabindex]:not([tabindex="-1"]), select, summary):focus-visible {
  outline: 2px solid var(--vxl-focus-ring);
  outline-offset: 2px;
}

/* ==========================================================================
   3. MOTION — the DS contract: colour moves, geometry does not.
   Nothing here adds transitions to legacy elements (that would animate
   surfaces never designed for it); components in this sheet declare their
   own. Reduced motion kills everything, storefront-wide.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  body[data-layout] *,
  body[data-layout] *::before,
  body[data-layout] *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }
}

/* ==========================================================================
   4. BUTTONS — .vxl-btn, the canonical control.

   Tiers (the DS's, re-based for a white-label storefront):
     --primary    the tenant's brand. (The DS's ink-coloured primary is a
                  marketing-site choice; here the OWNER's standing decision
                  is that primary CTAs wear --vod-primary.)
     --secondary  soft neutral fill — where the DS ink tier landed.
     --ghost      hairline outline, fills soft on hover.
   Sizes: --sm / default / --lg. --lg is the hero-CTA measure that already
   shipped on cinematic (54px tall, 17px/600, 30px inline padding) — one
   control, one measure, every surface.
   Extras: --icon (circular ghost for icon-only actions) and
   .vxl-btn__progress (the bottom-edge watch-progress rule generalised from
   the cinematic resume button).

   The custom-property API (--vxl-btn-bg/-ink/-line/-bg-hover) is the DS's:
   a context (or the adapter block in §9) re-points the properties; the
   geometry never restates itself.
   ========================================================================== */
.vxl-btn {
  /* The curated customizer's button chains come FIRST: the Customize flow
     derives --vod-btn-primary-* from its five inputs (and the pruned
     Advanced panel may diverge them deliberately), so the canonical control
     must consume them or the customizer's button controls are dead. Every
     --vod-* is always defined (defaults are merged at save), so the first
     name in each chain is the operative one. */
  --vxl-btn-bg: var(--vod-btn-primary-bg, var(--vxl-accent));
  --vxl-btn-ink: var(--vod-btn-primary-text, var(--vxl-on-accent));
  --vxl-btn-line: transparent;
  --vxl-btn-bg-hover: var(--vod-btn-primary-hover-bg, var(--vxl-accent-hover));

  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 11px 22px;
  border: 1px solid var(--vxl-btn-line);
  border-radius: var(--vxl-btn-radius, var(--vxl-radius-pill));
  background: var(--vxl-btn-bg);
  color: var(--vxl-btn-ink);
  font-size: var(--vxl-fs-btn);
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  user-select: none;
  transition: background-color var(--vxl-dur-1) ease,
              border-color var(--vxl-dur-1) ease,
              color var(--vxl-dur-1) ease;
}
.vxl-btn:hover {
  background: var(--vxl-btn-bg-hover, var(--vxl-btn-bg));
  color: var(--vxl-btn-ink);
  text-decoration: none;
}
.vxl-btn:disabled,
.vxl-btn[aria-disabled="true"] {
  opacity: 0.5;
  pointer-events: none;
}
.vxl-btn .vxm-ico,
.vxl-btn svg {
  flex: none;
  width: 18px;
  height: 18px;
}

/* tiers */
.vxl-btn--secondary {
  --vxl-btn-bg: var(--vod-btn-secondary-bg, var(--vxl-btn-soft));
  --vxl-btn-ink: var(--vod-btn-secondary-text, var(--vxl-ink));
  --vxl-btn-line: var(--vod-btn-secondary-border, transparent);
  --vxl-btn-bg-hover: var(--vod-btn-secondary-hover-bg, var(--vxl-btn-soft-hover));
}
/* Ghost paint carries !important: the legacy tier fills the same anchors
   with its own forced chip (legacy-override: vodlix.css .playResumeBtn 8%
   fill; also out-ranks the trailer-excluded adapter below). */
body[data-layout] .vxl-btn--ghost {
  --vxl-btn-ink: var(--vxl-ink);
  --vxl-btn-bg-hover: var(--vxl-btn-soft);
  background: transparent !important;
  border: 1px solid var(--vxl-line-strong) !important;
  color: var(--vxl-ink) !important;
}
body[data-layout] .vxl-btn--ghost:hover {
  background: var(--vxl-btn-soft) !important;
  border-color: var(--vxl-line-strong) !important;
  color: var(--vxl-ink) !important;
}
body[data-layout] .vxl-btn--ghost[aria-expanded="true"] {
  background: var(--vxl-btn-soft) !important;
}

/* sizes */
.vxl-btn--sm {
  padding: 8px 16px;
  font-size: var(--vxl-fs-small);
}
body[data-layout] .vxl-btn--lg {
  /* legacy-override: vodlix.css btn paddings are forced; the canonical
     measure must hold everywhere the tier is asked for */
  /* Re-cut from 54px/17px/30px (owner call: the hero and detail CTAs read
     "swelled"). 46px on a 15.5px label is the measure the reference
     storefronts use — Netflix, HBO and Disney all sit in the 44–48 band —
     and it lands the tier just above the 44px icon disc beside it instead
     of towering 10px over it. Icon drops with the label so the glyph keeps
     its ratio to the type. */
  height: 46px;
  padding: 0 26px !important;
  gap: 9px;
  font-size: 15.5px;
}
.vxl-btn--lg .vxm-ico,
.vxl-btn--lg svg {
  width: 20px;
  height: 20px;
}

/* icon-only: a circular ghost. Same tier system applies on top.
   The paint carries !important: the legacy tier styles the same buttons as
   bordered discs at full force (legacy-override: modal.css ~184-204
   `.vxm .rundedBtn` border/background/hover !important), and an icon button
   must not grow its ring back on surfaces the modal namespace reaches. */
body[data-layout] .vxl-btn--icon {
  --vxl-btn-bg: transparent;
  --vxl-btn-ink: var(--vxl-ink);
  --vxl-btn-bg-hover: var(--vxl-btn-soft);
  width: 44px !important;
  height: 44px !important;
  min-width: 44px;
  min-height: 44px;
  max-width: 44px;
  max-height: 44px;
  /* the templates sprinkle mr-2/ml-2 on these; an asymmetric margin shifts
     the glyph off its caption's axis in labeled columns — the gap between
     icons belongs to the row, never to the button */
  margin: 0 !important;
  padding: 0 !important;
  border: 0 !important;
  border-radius: 50% !important;
  background: transparent !important;
}
body[data-layout] .vxl-btn--icon:hover {
  border: 0 !important;
  background: var(--vxl-btn-soft) !important;
}
body[data-layout] .vxl-btn--icon .vxm-ico,
body[data-layout] .vxl-btn--icon svg {
  width: 24px;
  height: 24px;
  margin: 0; /* templates sprinkle mr-* utilities on glyphs; the circle centres */
}
/* the ringed variant — ambient/player controls that want a visible edge */
body[data-layout] .vxl-btn--icon.vxl-btn--ring {
  border: 2px solid var(--vxl-ink-faint) !important;
}
body[data-layout] .vxl-btn--icon.vxl-btn--ring:hover {
  border: 2px solid var(--vxl-ink) !important;
  background: transparent !important;
}

/* Watch progress as the button's own bottom edge — the treatment shipped on
   cinematic's Resume, now available to any button. Requires the button to
   clip (it does: border-radius + overflow). Track on the neutral rail,
   fill in currentColor so it is legible on any tier without a colour pair. */
.vxl-btn--progress {
  position: relative; /* host-agnostic: also used by non-.vxl-btn CTAs */
  overflow: hidden;
}
.vxl-btn__progress {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  height: 4px;
  background: color-mix(in srgb, currentColor 16%, transparent);
  pointer-events: none;
}
.vxl-btn__progress > i,
.vxl-btn__progress > span {
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  display: block;
  background: currentColor;
}

/* ==========================================================================
   5. BADGES + CHIPS
   ========================================================================== */
.vxl-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border: 1px solid transparent;
  border-radius: var(--vxl-radius-pill);
  font-size: var(--vxl-fs-small);
  font-weight: 600;
  line-height: 1.4;
  white-space: nowrap;
}
.vxl-badge--brand   { background: var(--vxl-accent-tint); color: var(--vxl-ink); }
.vxl-badge--solid   { background: var(--vxl-accent); color: var(--vxl-on-accent); }
.vxl-badge--ok      { background: var(--vxl-ok-tint); color: var(--vxl-ink); }
.vxl-badge--warn    { background: var(--vxl-warn-tint); color: var(--vxl-ink); }
.vxl-badge--danger  { background: var(--vxl-danger-tint); color: var(--vxl-ink); }
.vxl-badge--neutral { background: var(--vxl-surface-2); color: var(--vxl-ink); }
.vxl-badge--line    { border-color: var(--vxl-line-strong); color: var(--vxl-ink-muted); }

/* removable filter chip */
.vxl-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  border: 1px solid var(--vxl-line);
  border-radius: var(--vxl-radius-pill);
  background: var(--vxl-surface-1);
  color: var(--vxl-ink);
  font-size: var(--vxl-fs-small);
  transition: border-color var(--vxl-dur-1) ease;
}
.vxl-chip:hover { border-color: var(--vxl-line-strong); }

/* ==========================================================================
   6. FIELDS — input / select / textarea / label / error, the DS form kit.
   Adopted by auth + checkout in later phases; harmless standalone.
   ========================================================================== */
.vxl-field {
  display: block;
}
.vxl-field__label {
  display: block;
  margin-bottom: 6px;
  font-size: var(--vxl-fs-small);
  font-weight: 600;
  color: var(--vxl-ink);
}
.vxl-field__input,
.vxl-field select,
.vxl-field textarea {
  display: block;
  width: 100%;
  padding: 11px 14px;
  border: 1px solid var(--vxl-line-strong);
  border-radius: var(--vxl-radius-md);
  background: var(--vxl-surface-1);
  color: var(--vxl-ink);
  font-size: var(--vxl-fs-body);
  line-height: 1.4;
  transition: border-color var(--vxl-dur-1) ease, box-shadow var(--vxl-dur-1) ease;
}
.vxl-field__input::placeholder { color: var(--vxl-ink-faint); }
.vxl-field__input:focus,
.vxl-field select:focus,
.vxl-field textarea:focus {
  outline: none;
  border-color: var(--vxl-accent);
  box-shadow: 0 0 0 3px var(--vxl-focus-tint);
}
.vxl-field__hint  { margin-top: 6px; font-size: var(--vxl-fs-small); color: var(--vxl-ink-faint); }
.vxl-field__error { margin-top: 6px; font-size: var(--vxl-fs-small); color: var(--vxl-danger); }
.vxl-field.is-invalid .vxl-field__input { border-color: var(--vxl-danger); }

/* ==========================================================================
   7. NOTES / PROGRESS / SKELETON / EMPTY — small shared furniture
   ========================================================================== */
.vxl-note {
  display: flex;
  gap: 10px;
  padding: 12px 14px;
  border: 1px solid var(--vxl-line);
  border-radius: var(--vxl-radius-md);
  background: var(--vxl-surface-1);
  color: var(--vxl-ink);
  font-size: var(--vxl-fs-small);
  line-height: 1.5;
}
.vxl-note--info { background: var(--vxl-accent-tint); border-color: transparent; }
.vxl-note--ok   { background: var(--vxl-ok-tint); border-color: transparent; }
.vxl-note--warn { background: var(--vxl-warn-tint); border-color: transparent; }

/* --- bootstrap .alert → .vxl-note shape ----------------------------------
   The open set of legacy `alert alert-*` blocks (message.html, coins,
   wallet_topup, passwordProtected) reads as the DS note without markup
   churn — the same bridge strategy as .btn-primary above. Scoped to
   body[data-layout], which only default_v2 stamps, so no other template
   is touched. Bootstrap's own colored variants lose to these tints. */
body[data-layout] .alert {
  display: flex;
  gap: 10px;
  padding: 12px 14px;
  border: 1px solid var(--vxl-line);
  border-radius: var(--vxl-radius-md);
  background: var(--vxl-surface-1);
  color: var(--vxl-ink);
  font-size: var(--vxl-fs-small);
  line-height: 1.5;
}
body[data-layout] .alert-info    { background: var(--vxl-accent-tint); border-color: transparent; }
body[data-layout] .alert-success { background: var(--vxl-ok-tint); border-color: transparent; }
body[data-layout] .alert-warning { background: var(--vxl-warn-tint); border-color: transparent; }
body[data-layout] .alert-danger  { background: var(--vxl-danger-tint); border-color: transparent; }
/* the dismiss × inherits ink instead of bootstrap's black text-shadow */
body[data-layout] .alert .close {
  color: var(--vxl-ink);
  text-shadow: none;
  opacity: 0.6;
  margin-left: auto;
}
body[data-layout] .alert .close:hover { opacity: 1; }

/* --- generic bootstrap modals → DS surface --------------------------------
   legacy-override: vodlix.css:917 pins .modal-content to --vod-menu-bg with
   !important — on tenants whose derived menu is near-black, the panel
   disappears into the page (verified: the Add Profile modal rendered as
   floating strips on black). The DS surface + hairline make the panel a
   panel. The info modal (.vxm) owns its own surface and is excluded. */
/* --- actions_style="labeled" on non-cinematic layouts ---------------------
   Cinematic carries its own labeled treatment (two-row grid, Trailer as a
   column) in layout-cinematic.css. Every other layout shares the same
   .vxm-actCol / .vxm-actLabel markup in the info modal and on the watch
   page; this turns those into icon-over-caption columns when the operator
   picks "Labeled icons". Desktop only — phones already run the labeled row
   from mobile-app.css. "Bare" is the absence of these rules. */
@media (min-width: 992px) {
  body:not([data-layout="cinematic"])[data-lo-actions-style="labeled"] .detailsIcon .vxm-actCol {
    display: inline-flex !important;
    flex-direction: column;
    align-items: center;
    gap: 6px;
  }
  body:not([data-layout="cinematic"])[data-lo-actions-style="labeled"] .detailsIcon .vxm-actCol > * {
    order: 0;
    margin: 0 !important;
  }
  body:not([data-layout="cinematic"])[data-lo-actions-style="labeled"] .detailsIcon .vxm-actLabel {
    display: block !important;
    font-size: 12px;
    font-weight: 600;
    line-height: 1;
    color: var(--vxl-ink-muted);
  }
  /* the row's own items (Play, Trailer, the ambient discs) align to the
     icon tops so captions hang below the icon line, not the button line */
  body:not([data-layout="cinematic"])[data-lo-actions-style="labeled"] .detailsIcon {
    align-items: flex-start;
  }
}

body[data-layout] .modal:not(.vxm) .modal-content {
  background-color: var(--vod-card-bg2, var(--vxl-surface-2, #181818)) !important;
  border: 1px solid var(--vxl-line, rgba(255, 255, 255, 0.08));
  border-radius: var(--vxl-radius-md, 8px);
}

.vxl-progress {
  height: 4px;
  border-radius: var(--vxl-radius-pill);
  background: var(--vxl-track);
  overflow: hidden;
}
.vxl-progress > i,
.vxl-progress > span {
  display: block;
  height: 100%;
  background: var(--vxl-accent);
}

.vxl-skel {
  position: relative;
  overflow: hidden;
  background: var(--vxl-track);
  border-radius: var(--vxl-radius-sm);
}
.vxl-skel::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg, transparent,
              color-mix(in srgb, var(--vod-white-color, #fff) 6%, transparent),
              transparent);
  animation: vxl-shimmer 1.6s infinite;
}
@keyframes vxl-shimmer {
  to { transform: translateX(100%); }
}

.vxl-empty {
  padding: var(--vxl-space-7) var(--vxl-space-5);
  border: 1px dashed var(--vxl-line-strong);
  border-radius: var(--vxl-radius-lg);
  text-align: center;
  color: var(--vxl-ink-muted);
}

/* ==========================================================================
   8. CARD — the shared surface. Hairline, NO shadow, hover is a hint of
   brand on the border and nothing moves. (Media tiles — .videoBlock — keep
   their own artwork-first treatment; this is for content panels.)
   ========================================================================== */
.vxl-card {
  border: 1px solid var(--vxl-line);
  border-radius: var(--vxl-card-radius, 6px);
  background: var(--vxl-surface-1);
  transition: border-color var(--vxl-dur-1) ease;
}
a.vxl-card:hover,
.vxl-card--link:hover {
  border-color: var(--vxl-hover-line);
}

/* ==========================================================================
   9. BOOTSTRAP BRIDGE — permanent.
   `.btn-primary` / `.btn-secondary` / `.btn-outline-white` are an OPEN SET:
   plugins, legacy templates and tenant-authored content emit them freely and
   always will. This bridge is how bootstrap-classed markup wears the DS —
   pill geometry, tenant-chain colours — without each emitter being taught
   the .vxl-btn classes. It is not a temporary shim and is not slated for
   deletion. (The bounded storefront CTAs — .playResumeBtn,
   .btn-editors-pick — were retired from here once their few templates were
   co-classed; the hero's chain paint lives in editorpick.css, its surface.)
   ========================================================================== */

/* --- .btn-primary → .vxl-btn--primary shape ------------------------------ */
/* legacy-override: vodlix.css:725 (+dup :2817) pins bg/border with !important */
/* Chain order preserved from the deleted legacy rule: a tenant's explicit
   --vod-btn-primary-bg (its own Appearance control) outranks the accent. */
body[data-layout] .btn.btn-primary {
  background: var(--vod-btn-primary-bg, var(--vxl-accent)) !important;
  border: 1px solid transparent !important;
  border-radius: var(--vxl-btn-radius, var(--vxl-radius-pill)) !important;
  color: var(--vod-btn-primary-text, #fff) !important;
  font-weight: 600;
  transition: background-color var(--vxl-dur-1) ease,
              border-color var(--vxl-dur-1) ease,
              color var(--vxl-dur-1) ease;
}
body[data-layout] .btn.btn-primary:hover,
body[data-layout] .btn.btn-primary:active,
body[data-layout] .btn.btn-primary:focus {
  background: var(--vod-btn-primary-hover-bg, var(--vxl-accent-hover)) !important;
  color: var(--vod-btn-primary-text, #fff) !important;
}

/* --- secondary bootstrap tiers → soft neutral ---------------------------- */
/* legacy-override: vodlix.css:735-770 (outline tiers, secondary) */
body[data-layout] .btn.btn-secondary {
  background: var(--vod-btn-secondary-bg, var(--vxl-btn-soft)) !important;
  border: 1px solid var(--vod-btn-secondary-border, transparent) !important;
  border-radius: var(--vxl-btn-radius, var(--vxl-radius-pill)) !important;
  color: var(--vod-btn-secondary-text, var(--vxl-ink)) !important;
  font-weight: 600;
}
body[data-layout] .btn.btn-secondary:hover {
  background: var(--vod-btn-secondary-hover-bg, var(--vxl-btn-soft-hover)) !important;
  color: var(--vod-btn-secondary-text, var(--vxl-ink)) !important;
}
body[data-layout] .btn.btn-outline-white,
body[data-layout] .btn.btn-white {
  border-radius: var(--vxl-btn-radius, var(--vxl-radius-pill)) !important;
}
/* the colored bootstrap tiers keep their semantic paint but follow the
   layout's button shape like every other labeled button (found on the
   account page: Delete Account sat at bootstrap's 4px beside square
   buttons). btn-warning is deliberately NOT here — cards wear it as the
   Premium badge, whose shape should not swing with the button era. */
body[data-layout] .btn.btn-danger,
body[data-layout] .btn.btn-outline-danger,
body[data-layout] .btn.btn-success,
body[data-layout] .btn.btn-info {
  border-radius: var(--vxl-btn-radius, var(--vxl-radius-pill)) !important;
}

/* --- the Premium badge: a quiet corner tag --------------------------------
   Owner call (critique round, 2026-08-24): the amber .btn-warning pill
   shouted — six per row overpowered the artwork. Now a small dark chip
   with amber TEXT: the meaning stays, the volume drops. The chip ground
   mixes from the tenant body colour (scrim rule), the amber is the
   semantic premium marker. It is a tag, not a button. */
body[data-layout] .videoBlock .btn.btn-warning.btn-sm {
  background: color-mix(in srgb, var(--vod-body-back-color, #101014) 78%, transparent) !important;
  color: var(--vxl-warn, #f2b544) !important;
  border: 0 !important;
  border-radius: 4px !important;
  padding: 3px 7px !important;
  font-size: 10.5px !important;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1.3 !important;
  pointer-events: none;
  box-shadow: none !important;
}

/* --- watch-page access gate: a primary/secondary PAIR ---------------------
   The player's login gate (shared player/ template — markup can't change
   without touching every style) stacks Login and Create Account as two
   identical primaries. The signup link takes the secondary tier here, so
   the pair reads with hierarchy. Scoped to body[data-layout]: default_v2
   only; styles/default keeps the shared markup's own look. */
body[data-layout] #playerApp a.btn-block[href*="signup"] {
  background: var(--vod-btn-secondary-bg, var(--vxl-btn-soft)) !important;
  border: 1px solid var(--vod-btn-secondary-border, transparent) !important;
  color: var(--vod-btn-secondary-text, var(--vxl-ink)) !important;
}
body[data-layout] #playerApp a.btn-block[href*="signup"]:hover {
  background: var(--vod-btn-secondary-hover-bg, var(--vxl-btn-soft-hover)) !important;
  color: var(--vod-btn-secondary-text, var(--vxl-ink)) !important;
}
/* The gate card sits in a short column beside the tall poster/related
   stack — pinned only to the top it leaves a page of dead black under it.
   Sticky keeps it in view while the viewer scrolls the content column. */
@media (min-width: 992px) {
  body[data-layout] #playerApp.not-player .card {
    position: sticky;
    top: calc(var(--vxl-header-h, 64px) + 24px);
  }
}

/* --- static pages (view_page.html): title + readable prose ----------------
   Operator-authored pages (About, Terms, Help) rendered as a raw content
   dump with no heading and no measure. The DS page header and a prose
   column make them read like pages instead of pasted text. */
.vxpage {
  padding-top: 24px;
}
.vxpage__title {
  margin: 0 0 20px;
  font-size: var(--vxl-fs-h2, 28px);
  font-weight: 700;
  letter-spacing: var(--vxl-tracking-tight);
  color: var(--vxl-ink);
}
.vxpage__body {
  max-width: 72ch;
  font-size: var(--vxl-fs-body, 15.5px);
  line-height: 1.7;
  color: var(--vxl-ink-muted);
}
.vxpage__body h1, .vxpage__body h2, .vxpage__body h3 {
  margin: 28px 0 10px;
  color: var(--vxl-ink);
  letter-spacing: var(--vxl-tracking-tight);
}
.vxpage__body p { margin: 0 0 14px; }
.vxpage__body a { color: var(--vxl-accent); }
.vxpage__body ul, .vxpage__body ol { margin: 0 0 14px; padding-left: 22px; }

/* ==========================================================================
   Dropdown motion — every storefront dropdown drops in the same way
   (transitions.dev menu-dropdown recipe, drop variant). Fade + short
   downward slide on open; close stays instant (fast exit). One global rule
   so header menus, browse filters, legacy category menus and the signup /
   login country pickers all move identically. The keyframe animates the
   independent `translate` property, NEVER `transform`: Popper positions
   these menus with an inline `transform: translate3d(…)`, and a keyframe
   transform replaces it at frame 0 — the menu then flies in from the
   pre-Popper origin (the verified "swoops in from the side" bug on the
   right-aligned header menus). `translate` composes on top instead. No
   dropups exist in this theme; add an exception here if one ever appears.
   ========================================================================== */
.dropdown-menu.show {
  animation: vx-menu-in var(--vxl-dur-2) var(--vxl-ease) backwards;
}
@keyframes vx-menu-in {
  from {
    opacity: 0;
    translate: 0 -8px;
  }
}

/* ==========================================================================
   Icon swap (transitions.dev recipe 09) — two icons stacked in one grid
   cell cross-fade through blur + scale. State lives on the wrapper:
   data-state="a" shows [data-icon="a"], "b" shows [data-icon="b"].
   Symmetric motion, so it rides --vxl-ease-io.
   ========================================================================== */
.vx-swap {
  position: relative;
  display: inline-grid;
  place-items: center;
}
.vx-swap > .vx-swap__icon {
  grid-area: 1 / 1;
  display: inline-flex;
  transition:
    opacity   var(--vxl-dur-2) var(--vxl-ease-io),
    filter    var(--vxl-dur-2) var(--vxl-ease-io),
    transform var(--vxl-dur-2) var(--vxl-ease-io);
  will-change: opacity, filter, transform;
}
.vx-swap[data-state="a"] > [data-icon="a"],
.vx-swap[data-state="b"] > [data-icon="b"] {
  opacity: 1;
  filter: blur(0);
  transform: scale(1);
}
.vx-swap[data-state="a"] > [data-icon="b"],
.vx-swap[data-state="b"] > [data-icon="a"] {
  opacity: 0;
  filter: blur(2px);
  transform: scale(0.25);
}

/* ==========================================================================
   Error shake (transitions.dev recipe 12) — four-leg cubic shake for
   "this is wrong" feedback (failed login, invalid field). Add .vx-shake,
   force a reflow to replay: el.classList.remove → void el.offsetWidth →
   el.classList.add. %-stops are the cumulative 80/60/80/60ms legs of the
   280ms total; recompute if a leg changes.
   ========================================================================== */
.vx-shake {
  animation: vx-shake 280ms linear;
}
@keyframes vx-shake {
  0%     { transform: translateX(0);    animation-timing-function: var(--vxl-ease); }
  28.57% { transform: translateX(6px);  animation-timing-function: var(--vxl-ease); }
  57.14% { transform: translateX(-6px); animation-timing-function: var(--vxl-ease); }
  78.57% { transform: translateX(4px);  animation-timing-function: var(--vxl-ease); }
  100%   { transform: translateX(0); }
}
