/**
 * OZEN YouTube Player — front end.
 *
 * Scoped to the block. The frame uses aspect-ratio plus a min() width cap so
 * portrait and square videos stay inside the viewport and inside a 320px
 * column. The block never breaks out of the page container by itself: the
 * theme owns page width, which keeps every viewport free of horizontal
 * overflow.
 */

.ozen-yt {
  /* 16:9 fallback for markup without a ratio modifier. */
  --ozen-yt-w: 16;
  --ozen-yt-h: 9;
  --ozen-yt-max-h: 90vh;
  margin: 0 0 1.75rem;
  max-width: 100%;
  box-sizing: border-box;
}

.ozen-yt *,
.ozen-yt *::before,
.ozen-yt *::after {
  box-sizing: border-box;
}

/* Ratio modifiers only publish custom properties. */
.ozen-yt--16x9 {
  --ozen-yt-w: 16;
  --ozen-yt-h: 9;
  --ozen-yt-max-h: 90vh;
}

.ozen-yt--4x3 {
  --ozen-yt-w: 4;
  --ozen-yt-h: 3;
  --ozen-yt-max-h: 90vh;
}

.ozen-yt--9x16 {
  --ozen-yt-w: 9;
  --ozen-yt-h: 16;
  --ozen-yt-max-h: 80vh;
}

.ozen-yt--1x1 {
  --ozen-yt-w: 1;
  --ozen-yt-h: 1;
  --ozen-yt-max-h: 70vh;
}

.ozen-yt__frame {
  position: relative;
  aspect-ratio: var(--ozen-yt-w) / var(--ozen-yt-h);
  width: min(100%, calc(var(--ozen-yt-max-h) * var(--ozen-yt-w) / var(--ozen-yt-h)));
  margin-inline: auto;
  overflow: hidden;
  background: #101010;
}

/* Flyover entrance (1.1.0). Measured reference (LIVE /halls/): 42 px travel,
   750 ms, cubic-bezier(0.2, 0.7, 0.2, 1), opacity 0 -> 1, no zoom. The layer is
   emitted only for blocks that enable Flyover; it is inset to the frame, which
   keeps its #101010 background and overflow:hidden, so a 42 px translate shows
   the frame's own dark colour — never the page background. */
.ozen-yt__media {
  position: absolute;
  inset: 0;
}

/* Flyover entrance start state (1.2.0). Same defect and same fix as the
   Cinematic Slider: without a start state the layer renders at rest and the
   gate then snaps it visible -> hidden -> in. The start state is gated on
   .ozen-yt-flyover-armed, which the runtime adds at init only when Flyover is on
   and reduced motion is off, so a page with JS disabled or a failed view.js
   still shows the poster. The vars come from the block's direction (render.php
   emits them too) and the keyframe `from` resolves the same values, so
   assigning the animation never moves the layer. flyover=false blocks have no
   .ozen-yt__media layer, no armed class and no vars: markup unchanged. */
.ozen-yt[data-flyover="true"].ozen-yt-flyover-armed .ozen-yt__media {
  opacity: 0;
  transform: translate3d(var(--ozen-yt-fly-x, -42px), var(--ozen-yt-fly-y, 0px), 0);
}

/* --- Flyover pre-paint hidden state (1.3.0) ---
   Applied at stylesheet parse time — before the browser's first visible render
   — when scripting is enabled and reduced motion is off. This eliminates the
   36–130 ms first-paint flash where the media briefly renders at rest before
   the runtime adds .ozen-yt-flyover-armed. The rule is guarded by [data-flyover]
   so flyover=false blocks are untouched, and by :not([data-ozen-yt-init]) so
   the hiding lifts once the runtime has finished initializing the player:
   without that guard the media would stay pinned at opacity 0 after the
   entrance completes and the poster/play button would never show.
   Reduced-motion preference wins via the cascade (the @media
   (prefers-reduced-motion: reduce) block below forces opacity: 1 / transform:
   none with !important). No-JS is handled by the scripting media query: when
   scripting is disabled this rule does not apply. The --ozen-yt-fly-* vars are
   emitted by render.php in the style attribute, so the start offset is correct
   on the very first paint. The keyframe `from` resolves to the same values, so
   assigning the animation never moves the layer. */
@media (scripting: enabled) and (prefers-reduced-motion: no-preference) {
  .ozen-yt[data-flyover="true"]:not([data-ozen-yt-init]) .ozen-yt__media {
    opacity: 0;
    transform: translate3d(var(--ozen-yt-fly-x, -42px), var(--ozen-yt-fly-y, 0px), 0);
  }
}

/* --- Init-failure safety: force final state when pre-paint hid media but JS
   never initialized the slider (e.g., view.js failed to load or threw). The
   .ozen-yt-fallback class is added by the load-time safety net in view.js. It
   uses !important to override the pre-paint rule above and the armed start
   state, so the media is fully visible without animation. The gate observer
   checks for this class and skips the entrance when present. */
.ozen-yt[data-flyover="true"].ozen-yt-fallback .ozen-yt__media {
  opacity: 1 !important;
  transform: none !important;
}

@keyframes ozen-yt-flyover-in {
  from { transform: translate3d(var(--ozen-yt-fly-x, 0px), var(--ozen-yt-fly-y, 0px), 0); opacity: 0; }
  to { transform: translate3d(0, 0, 0); opacity: 1; }
}

.ozen-yt__poster {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
  border: 0;
  background: #101010;
  color: #fff;
  cursor: pointer;
}

.ozen-yt__poster-art {
  position: absolute;
  inset: 0;
  background: radial-gradient(120% 120% at 50% 40%, #2c3a30 0%, #141814 70%, #0b0d0b 100%);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
}

.ozen-yt__play {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  transition: transform 0.18s ease;
}

.ozen-yt__poster:hover .ozen-yt__play,
.ozen-yt__poster:focus-visible .ozen-yt__play {
  transform: scale(1.06);
}

.ozen-yt__poster:focus-visible {
  outline: 3px solid #e8ede4;
  outline-offset: 2px;
  /* Dark inner ring keeps the focus state visible on light poster art. */
  box-shadow: inset 0 0 0 3px #101010;
}

.ozen-yt__poster:focus-visible .ozen-yt__poster-art {
  box-shadow: inset 0 0 0 3px #101010;
}

.ozen-yt__iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

.ozen-yt__noscript {
  margin-block-start: 0.5rem;
  font-size: 0.875rem;
  text-align: start;
}

.ozen-yt.alignwide,
.ozen-yt.alignfull {
  width: 100%;
  max-width: 100%;
  margin-inline: 0;
}

/* --- Full Width mode (1.4.0) ---
   Full Width means the player reaches the viewport edges, not just the content
   column. The frame's 90vh height cap — which correctly keeps Contained and
   Wide inside the viewport — is what used to stop it: at 2000x1080 the cap
   resolved to 1728 px and at 2560x1440 to 2304 px, so an otherwise full-bleed
   block kept a 128 px / 120 px gutter on each side. In Full Width the frame
   simply takes the block's width. That width is already the viewport minus the
   scrollbar for a top-level alignfull block, and the Group/Column width when
   nested, so the rule needs no 100vw arithmetic and cannot create horizontal
   overflow. The ratio is untouched, so the frame grows taller as it grows wider
   and stays 16:9.
   Portrait and square ratios keep their viewport-height guard: a 9:16 player
   stretched to 2560 px would be 4551 px tall, which is not a usable
   presentation and would bury the rest of the page. Because those two ratios
   stay narrower than the block, they also keep the base rule's centring:
   without margin-inline:auto the capped frame inherits margin-inline:0 from
   the rule above and is pinned to the left edge, leaving the whole right side
   of a 2000/2560 px screen empty. */
.ozen-yt--full .ozen-yt__frame {
  width: 100%;
  max-width: 100%;
  margin-inline: 0;
}

.ozen-yt--full.ozen-yt--9x16 .ozen-yt__frame,
.ozen-yt--full.ozen-yt--1x1 .ozen-yt__frame {
  width: min(100%, calc(var(--ozen-yt-max-h) * var(--ozen-yt-w) / var(--ozen-yt-h)));
  margin-inline: auto;
}

@media (prefers-reduced-motion: reduce) {
  .ozen-yt__play {
    transition: none;
  }
  .ozen-yt__media {
    animation: none !important;
    transform: none !important;
    opacity: 1 !important;
  }
}
