/* Shared video styles. Loads the vendored video.js skin, then reshapes it into
   something a theme can drive with CSS variables — same convention as
   carousel.css.

   The import is what pulls in the player chrome. It stays a plain @import
   rather than being concatenated because public/assets has no build step; one
   extra request, cached forever.

   Structure: .cms-video-frame is the FRAME (size, aspect, radius, background)
   and .cms-video inside it is the PLAYER. They are separate because video.js
   builds its own box around the <video>; frame styling applied to the player
   fights that box instead of containing it. The `class` option on cms_video()
   lands on the frame, which is what a theme wants to size and position.

   Custom properties (set on .cms-video-frame, or any ancestor):
     --cms-video-radius        corner radius. Accepts the full four-value
                               shorthand, so per-corner is just
                               `--cms-video-radius: 1.5rem 0 1.5rem 0`
                               (the client asked for exactly that).
     --cms-video-width         frame width (default 100%)
     --cms-video-max-width     ceiling for the frame (default none)
     --cms-video-aspect        aspect-ratio (default auto — the source's own
                               dimensions win)
     --cms-video-fit           object-fit for the video inside the frame
                               (default contain; headers want cover)
     --cms-video-bg            letterbox background (default #000)
     --cms-video-mobile-max    breakpoint below which the mobile still replaces
                               the video. NOT usable in the media query itself
                               (custom properties can't be) — override the
                               `@media` block below if 767px is wrong for you.
*/
@import url('vendor/videojs/video-js.min.css');

.cms-video-frame {
    --cms-video-radius: 0;
    --cms-video-width: 100%;
    --cms-video-max-width: none;
    /* Not `auto`: once video.js is up the frame has no in-flow child to be
       sized by (the <video> becomes an absolutely positioned .vjs-tech), so a
       frame without a ratio collapses to the control bar. cms_video() writes
       the clip's real ratio inline whenever the dimensions are known; this is
       only the fallback for a file we never measured. */
    --cms-video-aspect: 16 / 9;
    --cms-video-fit: contain;
    --cms-video-bg: #000;

    position: relative;
    display: block;
    width: var(--cms-video-width);
    max-width: var(--cms-video-max-width);
    aspect-ratio: var(--cms-video-aspect);
    background: var(--cms-video-bg);
    border-radius: var(--cms-video-radius);
    /* The radius has to clip the player, not just the frame — an unclipped
       child squares off every rounded corner. */
    overflow: hidden;
}

.cms-video-frame > .cms-video,
.cms-video-frame > .video-js {
    width: 100%;
    height: 100%;
}

/* Before video.js initialises (or when it never does) the bare <video> is the
   player and needs the fit applied directly. */
video.cms-video {
    display: block;
    object-fit: var(--cms-video-fit);
}

/* video.js moves the real <video> to .vjs-tech inside its own wrapper. */
.cms-video .vjs-tech {
    object-fit: var(--cms-video-fit);
}

/* ── Mobile still ─────────────────────────────────────────────────────────
   Both the image and the video are in the markup and CSS picks one, rather
   than swapping a src at runtime: an <img> the browser can see in the HTML
   starts downloading immediately, which is the entire point on a phone. The
   server sets preload="none" on the video whenever a still exists, and the
   module skips player setup while the video is hidden, so the video bytes are
   not spent on a viewport that will never show it. */
.cms-video-frame__mobile {
    display: none;
    width: 100%;
    height: 100%;
    object-fit: var(--cms-video-fit, cover);
}

@media (max-width: 767px) {
    .cms-video-frame[data-cms-video-has-mobile] > .cms-video-frame__mobile {
        display: block;
    }

    .cms-video-frame[data-cms-video-has-mobile] > .cms-video,
    .cms-video-frame[data-cms-video-has-mobile] > .video-js {
        display: none;
    }
}

/* ── Header background loop ───────────────────────────────────────────────
   Fills its container, crops rather than letterboxes, and takes no clicks.
   The class is applied SERVER-SIDE for autoplay videos, not by JS and not via
   :has() — the header is the first thing painted, and a cover style that
   arrives after the player initialises is a visible jump. Themes can add
   .cms-video-frame--cover themselves for the same shape without autoplay. */
.cms-video-frame--cover {
    --cms-video-fit: cover;

    height: 100%;
}

.cms-video-frame--cover .cms-video {
    pointer-events: none;
}

/* video.js adds .vjs-controls-disabled when it starts a player with no
   controls, which is every header loop. */
.vjs-controls-disabled.cms-video .vjs-big-play-button,
.vjs-controls-disabled.cms-video .vjs-control-bar,
.vjs-controls-disabled.cms-video .vjs-loading-spinner {
    display: none;
}

/* The default skin's centred play button reads as a browser artefact rather
   than part of the site. Square it up and let themes recolour it. */
.cms-video .vjs-big-play-button {
    top: 50%;
    left: 50%;
    width: 4rem;
    height: 4rem;
    margin: 0;
    line-height: 4rem;
    border: 0;
    border-radius: 50%;
    transform: translate(-50%, -50%);
    background-color: var(--cms-video-play-bg, rgba(16, 16, 16, 0.55));
    /* Same treatment as the control panel, so the two read as one piece of
       site UI rather than two different players. */
    backdrop-filter: blur(0.8em) saturate(1.4);
    box-shadow: 0 0.3em 1.4em rgba(0, 0, 0, 0.35);
    transition: background-color 0.15s ease, transform 0.15s ease;
}

.cms-video-frame:hover .vjs-big-play-button {
    background-color: var(--cms-video-play-bg-hover, rgba(0, 0, 0, 0.8));
    transform: translate(-50%, -50%) scale(1.06);
}

.cms-video .vjs-big-play-button:focus {
    outline: none;
    box-shadow: 0 0 0 0.1875rem var(--cms-video-accent, #fff);
}

/* ── Control bar ──────────────────────────────────────────────────────────
   The vendored skin is a flat grey strip welded to the bottom edge, which is
   exactly what a browser's own player looks like. This makes it a floating
   panel instead: inset from the frame on all three sides, rounded, dark and
   blurred so it belongs to the site rather than to the viewport.

   Themes recolour with one property:
     --cms-video-accent  played portion, volume, spinner, focus ring
                         (default white)

   Units are `em` on purpose. video.js sets font-size: 10px on the player, so
   1em here is 10px and the whole bar scales with a single font-size override
   on .cms-video — which is how a theme makes the controls bigger. */
.cms-video .vjs-control-bar {
    right: 0.6em;
    bottom: 0.45em;
    left: 0.6em;
    width: auto;
    height: 3.8em;
    padding: 0 0.4em;
    /* The skin leaves this at flex-start and gives individual controls their
       own fixed heights (the volume slider's box is 3em regardless of the
       bar). Centring here is what keeps every control on one line when the bar
       is not the 3em the skin assumes. */
    align-items: center;
    border-radius: var(--cms-video-controls-radius, 0.6em);
    color: #fff;
    background: var(--cms-video-controls-bg, rgba(16, 16, 16, 0.62));
    /* Blur is what stops the panel reading as a solid box pasted over the
       picture. Unsupported browsers just get the flat background. */
    backdrop-filter: blur(0.8em) saturate(1.4);
    box-shadow: 0 0.3em 1.4em rgba(0, 0, 0, 0.35);
}

/* The glyph is an absolutely positioned ::before that already fills the button;
   the skin then centres it vertically with a line-height tuned to a 3em bar.
   Centre it for real instead — no arithmetic against the bar height, so a theme
   can resize the bar without the icons drifting out of it. (Doing this with
   line-height is the trap: `em` there resolves against the ::before's own
   1.8em font-size, not the button's, so the obvious value is off by 1.9em.) */
.cms-video .vjs-button > .vjs-icon-placeholder::before {
    display: flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
}

.cms-video .vjs-control-bar .vjs-button {
    opacity: 0.8;
    transition: opacity 0.12s ease;
}

.cms-video .vjs-control-bar .vjs-button:hover,
.cms-video .vjs-control-bar .vjs-button:focus-visible {
    opacity: 1;
}

/* The one time readout the skin leaves visible — time remaining. Tabular
   figures so the bar does not twitch every time the digits change. */
.cms-video .vjs-remaining-time {
    padding: 0 0.8em;
    font-variant-numeric: tabular-nums;
    opacity: 0.7;
}

/* ── Timeline ─────────────────────────────────────────────────────────────
   Height stays at the vendor's 0.3em, always. The scrubber knob is a ::before
   glyph the skin positions against exactly that number, and the bar is laid
   out by flexbox — so growing the track on hover both misaligns the knob and
   shoves every control sideways. Emphasis on hover comes from colour and the
   knob appearing, which cost no layout. */
.cms-video .vjs-progress-control {
    align-items: center;
    min-width: 4em;
}

.cms-video .vjs-progress-holder {
    height: 0.3em;
    margin: 0 0.8em;
    border-radius: 0.3em;
    background: rgba(255, 255, 255, 0.22);
    transition: background-color 0.12s ease;
}

.cms-video .vjs-progress-control:hover .vjs-progress-holder {
    background: rgba(255, 255, 255, 0.34);
}

.cms-video .vjs-play-progress,
.cms-video .vjs-load-progress,
.cms-video .vjs-load-progress div {
    border-radius: inherit;
}

.cms-video .vjs-play-progress {
    background: var(--cms-video-accent, #fff);
}

.cms-video .vjs-load-progress,
.cms-video .vjs-load-progress div {
    background: rgba(255, 255, 255, 0.28);
}

/* Knob: recoloured only. Its `top`/`right`/`font-size` are the vendor's
   arithmetic against a 0.3em track — override any of them and it sits off
   the line. */
.cms-video .vjs-play-progress::before {
    color: var(--cms-video-accent, #fff);
}

.cms-video .vjs-time-tooltip,
.cms-video .vjs-mouse-display .vjs-time-tooltip {
    padding: 0.4em 0.6em;
    border-radius: 0.3em;
    color: #fff;
    font-variant-numeric: tabular-nums;
    background: rgba(0, 0, 0, 0.8);
}

/* There is deliberately nothing here for the volume slider: the bar carries a
   mute toggle and no slider at all (see video.js's controlBar.children). The
   viewer's device owns the volume; the bar owns one honest on/off. */

/* Themed to match the play button instead of the skin's spinning dashes. */
.cms-video .vjs-loading-spinner {
    border-color: rgba(255, 255, 255, 0.25);
    border-top-color: var(--cms-video-accent, #fff);
}

/* Poster should crop the same way the video does, or the frame visibly jumps
   at the moment playback starts. */
.cms-video .vjs-poster img {
    object-fit: var(--cms-video-fit);
}

/* ── Enlarge, but not fullscreen ──────────────────────────────────────────
   The visitor can make an inline video bigger; clicking beside it puts it
   back. Deliberately not the browser's fullscreen, which hides the site and on
   iOS hands the clip to the system player.

   The button is hidden until the module marks the page as scripted, because a
   control that cannot do anything should not be reachable — and it is real
   markup (in the tab order) rather than something JS injects late. */
.cms-video-frame__expand {
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    z-index: 1;
    display: none;
    padding: 0.375rem;
    border: 0;
    border-radius: 0.25rem;
    color: #fff;
    background: rgba(0, 0, 0, 0.55);
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.15s ease;
}

.cms-video-frame__expand svg {
    display: block;
    width: 1rem;
    height: 1rem;
}

.cms-video-frame[data-cms-video-expand-ready] .cms-video-frame__expand {
    display: block;
}

.cms-video-frame:hover .cms-video-frame__expand,
.cms-video-frame__expand:focus-visible,
.cms-video-frame[data-cms-video-expanded] .cms-video-frame__expand {
    opacity: 1;
}

.cms-video-frame[data-cms-video-expanded] {
    position: fixed;
    inset: 0;
    z-index: 60;
    margin: auto;
    /* Sized from the video's own proportions (the module sets the custom
       property) so a portrait clip is not letterboxed into a 16:9 box. */
    width: min(92vw, calc(88vh * var(--cms-video-expanded-aspect, 1.7778)));
    height: auto;
    max-width: 92vw;
    max-height: 88vh;
    aspect-ratio: var(--cms-video-expanded-aspect, 16 / 9);
    box-shadow: 0 1.5rem 4rem rgba(0, 0, 0, 0.5);
}

.cms-video-backdrop {
    position: fixed;
    inset: 0;
    z-index: 59;
    background: rgba(0, 0, 0, 0.75);
}

/* ── Inside a gallery / carousel slide ────────────────────────────────────
   A gallery may mix video with images, which is what makes
   cms_gallery(name, 'carousel') a video slider. Gallery items are sized by the
   grid (or by --cms-carousel-aspect in a carousel) and images crop to fill, so
   a video frame has to do the same or a slider ends up with rows of different
   heights. */
.field-gallery__item > .cms-video-frame {
    --cms-video-fit: cover;

    width: 100%;
    height: 100%;
    aspect-ratio: auto;
}

/* ── Corner radius ────────────────────────────────────────────────────────
   --cms-video-radius takes the whole border-radius shorthand, so per-corner
   combinations are just four values. The `video` field builds that string
   from these named steps, which keeps the scale in the theme's hands rather
   than baking pixel values into stored content. */
.cms-video-frame {
    --cms-video-radius-none: 0;
    --cms-video-radius-small: 0.375rem;
    --cms-video-radius-large: 1.5rem;
}

/* Reduced motion is handled in video.js, not here: a background loop is left
   paused on its poster rather than hidden, so the frame still occupies its
   space and the page does not reflow. See video.js `prefersReducedMotion`. */
