/* ============================================================================
   Siesta TV — /tv and /tv/{id}
   ----------------------------------------------------------------------------
   Light surface, matching the rest of siesta.ai.

   COLOUR RULE
   ───────────
   Brand navy (#060649) is the accent, and it is spent on exactly one thing: a
   filter dropdown that is actually filtering. Headings, the icons beside them
   and card titles use the
   theme's heading black (--si-heading-color, #0b0f19) like every other page on
   the site. Everything else is the theme's grey ramp. No third colour anywhere.

   Not in bundleconfig.json on purpose: like talk.css, this is loaded only by
   the pages that need it, through @section styles.

   Scoped under .tv-page throughout so nothing here can leak into the shared
   header, the footer or the chat widget.
   ========================================================================== */

.tv-page {
    /* The accent. Used by the active filter dropdown and nothing else. */
    --tv-navy: #060649;

    /* Headings and titles: the theme's --si-heading-color, spelled out so the
       card titles (which are spans, not headings) can match them. */
    --tv-ink: #0b0f19;
    --tv-ink-rgb: 11, 15, 25;

    --tv-bg: #ffffff;
    --tv-bg-soft: #f6f7fd;
    --tv-surface: rgba(var(--tv-ink-rgb), 0.04);
    --tv-border: #e2e5f1;
    --tv-border-strong: #d4d7e5;

    --tv-text-dim: #6b6f80;
    --tv-text-faint: #b4b7c9;

    --tv-radius: 0.875rem;

    position: relative;
    overflow-x: clip;
    background: var(--tv-bg);
    color: var(--tv-text-dim);
}

/* Headings are deliberately not restyled here: they inherit the theme's
   --si-heading-color, so /tv reads the same as every other page. */

.tv-page :focus-visible {
    outline: 2px solid var(--tv-ink);
    outline-offset: 3px;
}

/* ------------------------------------------------------------------ hero --- */

/*  No overflow clipping here. The veil sits at inset:0 so there is nothing to
    crop, and a hidden overflow would cut off the filter dropdown's menu, which
    is absolutely positioned inside this section.

    z-index rather than isolation: it gives the veil at z-index:-1 the stacking
    context it needs, and lifts the whole hero above the rails below, whose
    .tv-rail-wrap is positioned and comes later in the document.            */
.tv-hero {
    position: relative;
    z-index: 3;
    /* Top padding clears the absolutely positioned site header. */
    padding: 10rem 0 2.5rem;
}

/* A quiet grey wash rather than a photo: on white, a blurred still reads as
   smudge. */
.tv-hero-veil {
    position: absolute;
    inset: 0;
    z-index: -1;
    display: block;
    background: linear-gradient(180deg, var(--tv-bg-soft) 0%, var(--tv-bg) 100%);
}

/*  Title and featured video on the left, search and level on the right.

    No max-width on .tv-hero-inner: it is the Bootstrap .container itself, so
    narrowing it would centre the block and push the heading inwards, off the
    left edge the rails below line up on.

    align-items: flex-end puts the search on the same baseline as the "Start
    watching" row rather than floating beside the headline.                  */
.tv-hero-inner {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1.75rem 2.5rem;
}

.tv-hero-lead {
    min-width: 0;
}

.tv-hero-aside {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.875rem;
    /* Shrinks before it wraps, but never below a usable search field. */
    flex: 0 1 26rem;
    min-width: 17rem;
}

.tv-hero-title {
    margin-bottom: 1.5rem;
    font-size: clamp(2.25rem, 5vw, 3.5rem);
    line-height: 1.1;
    letter-spacing: -0.02em;
}

.tv-hero-featured {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1.25rem;
}

.tv-hero-featured-meta {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 0;
}

.tv-hero-featured-label {
    color: var(--tv-text-faint);
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.tv-hero-featured-title {
    color: var(--tv-ink);
    font-weight: 600;
    text-decoration: none;
}

.tv-hero-featured-title:hover {
    text-decoration: underline;
}

.tv-hero-featured-duration {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    color: var(--tv-text-dim);
    font-size: 0.875rem;
}

/* --------------------------------------------------------------- filters --- */

/* Stacked, not side by side: the search gets the full column width and the
   two dropdowns split the row under it. */
.tv-filter-form {
    display: flex;
    flex-direction: column;
    gap: 0.625rem;
    width: 100%;
}

.tv-search {
    position: relative;
    flex: 1 1 auto;
}

.tv-search > .ti {
    position: absolute;
    top: 50%;
    left: 1rem;
    transform: translateY(-50%);
    color: var(--tv-text-faint);
    pointer-events: none;
}

.tv-search-input {
    width: 100%;
    padding: 0.75rem 1rem 0.75rem 2.75rem;
    border: 1px solid var(--tv-border);
    border-radius: 2rem;
    background: var(--tv-bg);
    color: var(--tv-ink);
    font-size: 1rem;
}

.tv-search-input::placeholder {
    color: var(--tv-text-faint);
}

.tv-search-input:focus {
    border-color: var(--tv-border-strong);
    outline: none;
}

/* Only reachable without JavaScript: tv.js filters as you type and hides it. */
.tv-filter-submit {
    padding: 0.75rem 1.5rem;
    border: 1px solid var(--tv-border);
    border-radius: 2rem;
    background: var(--tv-bg);
    color: var(--tv-ink);
    font-weight: 600;
}

/* -- dropdowns -------------------------------------------------------------
   Two controls per filter, one visible at a time. Without tv.js the wrapper
   never gets .is-enhanced and the native <select> is the dropdown; with it,
   the native control hides and the styled trigger plus listbox take over.
   The native popup is drawn by the OS and cannot be styled, which is why the
   second one exists at all.                                                  */

.tv-selects {
    display: flex;
    gap: 0.5rem;
    width: 100%;
}

.tv-select {
    position: relative;
    flex: 1 1 0;
    min-width: 0;
}

/* The pill, shared by the native fallback and the enhanced trigger. */
.tv-select-native,
.tv-select-trigger {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    width: 100%;
    min-width: 0;
    padding: 0.5rem 0.875rem;
    border: 1px solid var(--tv-border);
    border-radius: 2rem;
    background: var(--tv-bg);
    color: var(--tv-text-dim);
    font: inherit;
    font-weight: 600;
    line-height: 1.2;
    text-align: left;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.tv-select-trigger {
    display: none;
}

.tv-select.is-enhanced .tv-select-trigger {
    display: flex;
}

.tv-select.is-enhanced .tv-select-native {
    display: none;
}

.tv-select-native:hover,
.tv-select-trigger:hover {
    border-color: var(--tv-border-strong);
}

.tv-select-trigger > .ti {
    flex: 0 0 auto;
    font-size: 1.0625rem;
}

.tv-select-label {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

.tv-select-caret {
    opacity: 0.6;
    transition: transform 0.15s ease;
}

.tv-select.is-open .tv-select-caret {
    transform: rotate(180deg);
}

/* Filled navy while the dropdown is actually narrowing something: the same
   signal the selected chip used to give, and the only accent on the page. */
.tv-select.is-active .tv-select-trigger,
.tv-select.is-active .tv-select-native {
    border-color: var(--tv-navy);
    background: var(--tv-navy);
    color: #fff;
}

.tv-select-trigger:focus-visible,
.tv-select-native:focus-visible {
    outline: 2px solid var(--tv-ink);
    outline-offset: 2px;
}

/* -- the menu -------------------------------------------------------------- */

.tv-select-menu {
    position: absolute;
    top: calc(100% + 0.375rem);
    left: 0;
    z-index: 30;
    /* At least as wide as the trigger, but free to grow for a long topic name
       and capped before it runs off a phone. */
    min-width: 100%;
    width: max-content;
    max-width: min(22rem, 78vw);
    max-height: 19rem;
    overflow-y: auto;
    padding: 0.375rem;
    border: 1px solid var(--tv-border);
    border-radius: 0.875rem;
    background: #fff;
    box-shadow: 0 1rem 2.5rem rgba(var(--tv-ink-rgb), 0.16);
}

.tv-select-menu[hidden] {
    display: none;
}

.tv-select-option {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    padding: 0.5rem 0.625rem;
    border: 0;
    border-radius: 0.5rem;
    background: transparent;
    color: var(--tv-text-dim);
    font-size: 0.9375rem;
    font-weight: 500;
    line-height: 1.3;
    text-align: left;
    cursor: pointer;
    transition: background 0.12s ease, color 0.12s ease;
}

.tv-select-option > .ti:first-child {
    flex: 0 0 auto;
    font-size: 1.0625rem;
    opacity: 0.75;
}

.tv-select-option:hover,
.tv-select-option:focus-visible {
    background: var(--tv-surface);
    color: var(--tv-ink);
    outline: none;
}

.tv-select-option[aria-selected="true"] {
    color: var(--tv-ink);
    font-weight: 600;
}

.tv-select-option-label {
    flex: 1 1 auto;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.tv-select-option-count {
    flex: 0 0 auto;
    color: var(--tv-text-faint);
    font-size: 0.75rem;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
}

.tv-select-tick {
    flex: 0 0 auto;
    color: var(--tv-navy);
    font-size: 1rem;
    opacity: 0;
}

.tv-select-option[aria-selected="true"] .tv-select-tick {
    opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
    .tv-select-caret,
    .tv-select-option,
    .tv-select-trigger {
        transition: none;
    }
}

.tv-results {
    display: inline-flex;
    align-items: center;
    gap: 1rem;
    margin: 0;
    color: var(--tv-text-dim);
    font-size: 0.9375rem;
    white-space: nowrap;
}

/* A real link, so it takes the site's link colour rather than a colour this
   page picks. Not the chip accent. */
.tv-clear {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    color: var(--si-link-color);
    font-weight: 600;
    text-decoration: none;
}

.tv-clear:hover {
    text-decoration: underline;
}

/* ------------------------------------------------------------------ rows --- */

/* The rails now end the page, so they carry the bottom margin the CTA used
   to provide before the footer. */
.tv-rows {
    padding-bottom: 3.5rem;
}

.tv-row {
    padding: 1.75rem 0;
}

.tv-row-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: 0.75rem 1.5rem;
    margin-bottom: 0.25rem;
}

.tv-row-headings {
    min-width: 0;
}

.tv-row-title {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-bottom: 0.25rem;
    font-size: 1.375rem;
    letter-spacing: -0.01em;
}

/* Same black as the heading it sits next to, not an accent. */
.tv-row-title > .ti {
    color: var(--tv-ink);
    font-size: 1.5rem;
}

.tv-row-desc {
    max-width: 46rem;
    margin: 0;
    color: var(--tv-text-dim);
    font-size: 0.9375rem;
    line-height: 1.55;
}

.tv-row-all {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    flex-shrink: 0;
    color: var(--tv-text-dim);
    font-size: 0.875rem;
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
}

.tv-row-all:hover {
    color: var(--tv-ink);
}

.tv-row-count {
    padding: 0.1rem 0.45rem;
    border-radius: 1rem;
    background: var(--tv-surface);
    font-size: 0.75rem;
}

/* ----------------------------------------------------------------- rails --- */

.tv-rail-wrap {
    position: relative;
}

/* A plain scroll container, not a carousel library: trackpad, touch, keyboard
   and the arrow buttons all drive the same scrollLeft. */
.tv-rail {
    display: flex;
    gap: 1rem;
    /* Vertical padding leaves room for the hover lift and its shadow, which
       would otherwise be clipped: overflow-x:auto forces overflow-y too. */
    padding: 0.75rem 0 1.5rem;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x proximity;
    scroll-behavior: smooth;
    scrollbar-width: none;
    -ms-overflow-style: none;
    overscroll-behavior-x: contain;
}

.tv-rail::-webkit-scrollbar {
    display: none;
}

@media (prefers-reduced-motion: reduce) {
    .tv-rail {
        scroll-behavior: auto;
    }
}

.tv-rail-nav {
    position: absolute;
    top: calc(50% - 1rem);
    z-index: 3;
    display: none;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    transform: translateY(-50%);
    border: 1px solid var(--tv-border);
    border-radius: 50%;
    background: var(--tv-bg);
    color: var(--tv-ink);
    font-size: 1.25rem;
    box-shadow: 0 0.5rem 1.25rem rgba(var(--tv-ink-rgb), 0.16);
    transition: background 0.15s ease, color 0.15s ease, opacity 0.15s ease;
}

.tv-rail-nav:hover {
    background: var(--tv-ink);
    color: #fff;
}

.tv-rail-prev {
    left: -0.5rem;
}

.tv-rail-next {
    right: -0.5rem;
}

/* Arrows are a pointer affordance; touch devices just swipe. tv.js also sets
   [hidden] on whichever end of the rail has nothing left to scroll to. */
@media (hover: hover) and (min-width: 768px) {
    .tv-rail-nav:not([hidden]) {
        display: flex;
        opacity: 0;
    }

    .tv-rail-wrap:hover .tv-rail-nav:not([hidden]),
    .tv-rail-nav:focus-visible {
        opacity: 1;
    }
}

/* ----------------------------------------------------------------- cards ---
   YouTube layout: a clean thumbnail with nothing painted over it except the
   duration chip in the bottom corner, and the title in ink underneath. No
   scrim, so the screenshot in the still stays readable all the way down, and
   the title gets two full lines instead of one clipped one.                 */

.tv-card {
    display: block;
    flex: 0 0 auto;
    width: 17rem;
    scroll-snap-align: start;
    color: inherit;
    text-decoration: none;
    transition: transform 0.18s ease;
}

.tv-card:hover,
.tv-card:focus-visible {
    color: inherit;
    transform: translateY(-4px);
}

.tv-card-thumb {
    position: relative;
    display: block;
    aspect-ratio: 16 / 9;
    border: 1px solid var(--tv-border);
    border-radius: var(--tv-radius);
    background: var(--tv-bg-soft);
    overflow: hidden;
    transition: box-shadow 0.18s ease, border-color 0.18s ease;
}

.tv-card:hover .tv-card-thumb {
    border-color: var(--tv-border-strong);
    box-shadow: 0 0.75rem 1.75rem rgba(var(--tv-ink-rgb), 0.18);
}

.tv-card-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: top center;
    transition: transform 0.4s ease;
}

.tv-card:hover .tv-card-thumb img {
    transform: scale(1.06);
}

/*  The caption is a sibling of the thumbnail, not an overlay on it: the title
    sits under the still in ink, the way a YouTube card reads.

    min-height reserves both lines whether or not the title needs them, so the
    bottom edges of a rail line up instead of stepping up and down with title
    length.                                                                   */
.tv-card-caption {
    display: block;
    padding: 0.625rem 0.125rem 0;
}

.tv-card-title {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    min-height: 2.7em;
    overflow: hidden;
    color: var(--tv-ink);
    font-weight: 600;
    font-size: 0.9375rem;
    line-height: 1.35;
}

.tv-card-play {
    position: absolute;
    top: 50%;
    left: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 3.25rem;
    height: 3.25rem;
    transform: translate(-50%, -50%) scale(0.85);
    border-radius: 50%;
    /* Sits on the thumbnail, so it needs its own contrast regardless of the
       image underneath. */
    background: rgba(var(--tv-ink-rgb), 0.82);
    color: #fff;
    font-size: 1.25rem;
    opacity: 0;
    backdrop-filter: blur(4px);
    transition: opacity 0.18s ease, transform 0.18s ease;
}

.tv-card:hover .tv-card-play,
.tv-card:focus-visible .tv-card-play {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1);
}

/* Bottom right of the still, the way every video player labels a thumbnail. */
.tv-card-duration {
    position: absolute;
    right: 0.5rem;
    bottom: 0.5rem;
    z-index: 2;
    padding: 0.15rem 0.45rem;
    border-radius: 0.35rem;
    background: rgba(var(--tv-ink-rgb), 0.82);
    color: #fff;
    font-size: 0.75rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

/* ----------------------------------------------------------------- empty --- */

.tv-empty {
    padding: 4rem 0 5rem;
}

.tv-empty-icon {
    display: block;
    margin-bottom: 1rem;
    color: var(--tv-text-faint);
    font-size: 3rem;
}

.tv-empty-title {
    margin-bottom: 0.5rem;
    font-size: 1.5rem;
}

.tv-empty-desc {
    max-width: 32rem;
    margin: 0 auto 1.5rem;
    color: var(--tv-text-dim);
}

/* ---------------------------------------------------------------- detail --- */

/*  Laid out like a video site, not a marketing page:

        ┌─────────────────────────┬──────────┐
        │ player                  │ watch    │
        ├─────────────────────────┤ next     │
        │ title / meta / info     │ list     │
        └─────────────────────────┴──────────┘

    Full page width rather than the Bootstrap .container the rest of the site
    uses, so the player gets the room it deserves. The cap is generous rather
    than absent: past ~2100px the line lengths in the panel stop being readable.  */
.tv-detail-wrap {
    width: 100%;
    max-width: 2100px;
    /* Top clears the absolutely positioned site header. */
    padding: 7rem clamp(1rem, 3vw, 3rem) 4rem;
    margin: 0 auto;
}

/*  The fold-fit cap lives here, not on the player.

    A 16:9 player would run past the bottom of a wide, short window, and the
    only way to shorten it while keeping the ratio is to narrow it. Capping the
    player alone left its grid column at full width, so the sidebar stayed
    pinned to the right and the difference showed up as a gap. Capping the grid
    instead pulls the sidebar in with it: player width + sidebar + gap.        */
.tv-detail-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 24rem;
    gap: 1.5rem 2rem;
    align-items: start;
    max-width: calc((100vh - 13rem) * 16 / 9 + 26rem);
}

.tv-detail-main {
    min-width: 0;
}

.tv-back {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    margin-bottom: 1rem;
    color: var(--tv-text-dim);
    font-size: 0.9375rem;
    font-weight: 600;
    text-decoration: none;
}

.tv-back:hover {
    color: var(--tv-ink);
}

.tv-player-frame {
    position: relative;
    isolation: isolate;
    border-radius: 1rem;
    /* The player itself stays black: that is the video, not the page. */
    background: #000;
    overflow: hidden;
}

.tv-player-frame:focus-visible {
    outline: 2px solid var(--tv-ink);
    outline-offset: 3px;
}

.tv-player {
    display: block;
    width: 100%;
    aspect-ratio: 16 / 9;
    background: #000;
    /* The frame is 16:9 and so is the video, so cover crops nothing during
       playback. It exists for the poster: catalogue thumbnails are stills in
       whatever ratio they were captured, and contain leaves them floating in
       a letterbox. */
    object-fit: cover;
}

.is-enhanced .tv-player {
    cursor: pointer;
}

/* -- centre button and click feedback --------------------------------------
   White glyph on a dark translucent disc, sitting on the picture. The button
   is there whenever the video is paused; the pulse is the momentary bloom on
   every toggle, carrying the icon of whatever just happened.                 */

.tv-player-start,
.tv-player-pulse {
    position: absolute;
    top: 50%;
    left: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 4.75rem;
    height: 4.75rem;
    padding: 0;
    transform: translate(-50%, -50%);
    border: none;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.6);
    color: #fff;
    font-size: 1.625rem;
    backdrop-filter: blur(2px);
}

.tv-player-start {
    z-index: 2;
    display: none;
    cursor: pointer;
    transition: transform 0.18s ease, background 0.18s ease;
}

/* Paused means the button is up, the way it is on any video site. The end card
   has the frame to itself, so the button stands down for it. */
.is-enhanced:not(.is-playing):not(.is-ended) .tv-player-start {
    display: flex;
}

.tv-player-start:hover {
    background: rgba(0, 0, 0, 0.75);
    transform: translate(-50%, -50%) scale(1.06);
}

.tv-player-pulse {
    z-index: 3;
    opacity: 0;
    pointer-events: none;
}

/* The muted-autoplay nudge. Top left, out of the way of the duration badge and
   the controls, gone the moment there is sound. */
.tv-unmute {
    position: absolute;
    top: 0.875rem;
    left: 0.875rem;
    z-index: 4;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.5rem 0.875rem;
    border: 1px solid rgba(255, 255, 255, 0.3);
    border-radius: 2rem;
    background: rgba(0, 0, 0, 0.62);
    color: #fff;
    font-size: 0.875rem;
    font-weight: 600;
    cursor: pointer;
    backdrop-filter: blur(2px);
    transition: background 0.15s ease;
}

.tv-unmute[hidden] {
    display: none;
}

.tv-unmute:hover {
    background: rgba(0, 0, 0, 0.8);
}

.tv-unmute > .ti {
    font-size: 1.0625rem;
}

.tv-player-pulse.is-on {
    animation: tv-player-pulse 0.5s ease-out;
}

@keyframes tv-player-pulse {
    0% {
        opacity: 0.95;
        transform: translate(-50%, -50%) scale(0.8);
    }
    100% {
        opacity: 0;
        transform: translate(-50%, -50%) scale(1.5);
    }
}

/* -- control bar ----------------------------------------------------------- */

/* Hidden until tv.js has taken the native controls away, so there is never a
   moment with two sets of controls or none. */
.tv-controls {
    position: absolute;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 3;
    display: none;
    flex-direction: column;
    gap: 0.25rem;
    padding: 2.5rem 0.875rem 0.625rem;
    /* Its own scrim, so white chrome stays legible over a bright frame. */
    background: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, 0.55) 55%, rgba(0, 0, 0, 0.8) 100%);
    opacity: 1;
    transition: opacity 0.2s ease, transform 0.2s ease;
}

.is-enhanced .tv-controls {
    display: flex;
}

/* Out of the way while playing and the pointer is still. */
.is-idle .tv-controls {
    opacity: 0;
    transform: translateY(0.5rem);
    pointer-events: none;
}

.is-idle .tv-player {
    cursor: none;
}

.is-ended .tv-controls,
.is-ended .tv-player-start {
    opacity: 0;
    pointer-events: none;
}

.tv-controls-row {
    display: flex;
    align-items: center;
    gap: 0.25rem;
}

.tv-controls-spacer {
    flex: 1 1 auto;
}

.tv-ctl {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: transparent;
    color: #fff;
    font-size: 1.125rem;
    cursor: pointer;
    transition: background 0.15s ease;
}

.tv-ctl:hover {
    background: rgba(255, 255, 255, 0.16);
}

.tv-timecode {
    margin-left: 0.5rem;
    color: rgba(255, 255, 255, 0.85);
    font-size: 0.8125rem;
    font-variant-numeric: tabular-nums;
}

.tv-timecode-sep {
    margin: 0 0.3rem;
    opacity: 0.5;
}

/* Volume slides open on hover, the way it does in every player. */
.tv-volume {
    display: flex;
    align-items: center;
}

.tv-volume-slider {
    width: 0;
    opacity: 0;
    transition: width 0.2s ease, opacity 0.2s ease;
}

.tv-volume:hover .tv-volume-slider,
.tv-volume-slider:focus-visible {
    width: 4.5rem;
    opacity: 1;
    margin-left: 0.25rem;
}

/* -- sliders ---------------------------------------------------------------
   Real input type=range, so dragging, clicking and keyboard stepping are the
   browser's own. Only the paint is ours: one gradient carrying played,
   buffered and remaining, driven by two custom properties set from tv.js.
   The -webkit- and -moz- pseudo-elements have to be written as separate rules;
   a browser that does not know one of them drops the whole selector.         */

.tv-seek,
.tv-volume-slider {
    -webkit-appearance: none;
    appearance: none;
    height: 1rem;
    background: transparent;
    cursor: pointer;
}

.tv-seek {
    width: 100%;
    margin: 0;
}

.tv-seek::-webkit-slider-runnable-track {
    height: 0.25rem;
    border-radius: 1rem;
    background: linear-gradient(to right,
        #fff 0 var(--tv-seek-played, 0%),
        rgba(255, 255, 255, 0.45) var(--tv-seek-played, 0%) var(--tv-seek-buffered, 0%),
        rgba(255, 255, 255, 0.22) var(--tv-seek-buffered, 0%) 100%);
}

.tv-seek::-moz-range-track {
    height: 0.25rem;
    border-radius: 1rem;
    background: linear-gradient(to right,
        #fff 0 var(--tv-seek-played, 0%),
        rgba(255, 255, 255, 0.45) var(--tv-seek-played, 0%) var(--tv-seek-buffered, 0%),
        rgba(255, 255, 255, 0.22) var(--tv-seek-buffered, 0%) 100%);
}

.tv-seek::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 0.875rem;
    height: 0.875rem;
    margin-top: -0.3125rem;
    border: none;
    border-radius: 50%;
    background: #fff;
    /* Grows in on hover, so a still frame is not covered in furniture. */
    transform: scale(0);
    transition: transform 0.15s ease;
}

.tv-seek::-moz-range-thumb {
    width: 0.875rem;
    height: 0.875rem;
    border: none;
    border-radius: 50%;
    background: #fff;
    transform: scale(0);
    transition: transform 0.15s ease;
}

.tv-player-frame:hover .tv-seek::-webkit-slider-thumb {
    transform: scale(1);
}

.tv-player-frame:hover .tv-seek::-moz-range-thumb {
    transform: scale(1);
}

.tv-volume-slider::-webkit-slider-runnable-track {
    height: 0.25rem;
    border-radius: 1rem;
    background: linear-gradient(to right,
        #fff 0 var(--tv-volume-level, 100%),
        rgba(255, 255, 255, 0.25) var(--tv-volume-level, 100%) 100%);
}

.tv-volume-slider::-moz-range-track {
    height: 0.25rem;
    border-radius: 1rem;
    background: linear-gradient(to right,
        #fff 0 var(--tv-volume-level, 100%),
        rgba(255, 255, 255, 0.25) var(--tv-volume-level, 100%) 100%);
}

.tv-volume-slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 0.75rem;
    height: 0.75rem;
    margin-top: -0.25rem;
    border: none;
    border-radius: 50%;
    background: #fff;
}

.tv-volume-slider::-moz-range-thumb {
    width: 0.75rem;
    height: 0.75rem;
    border: none;
    border-radius: 50%;
    background: #fff;
}

/* -- end card --------------------------------------------------------------
   The navy splash that covers the finished video and counts into the next one. */

.tv-endcard {
    position: absolute;
    inset: 0;
    z-index: 4;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1.25rem;
    padding: clamp(1rem, 4vw, 2.5rem);
    text-align: center;
    background:
        radial-gradient(70% 90% at 50% 40%, rgba(16, 16, 96, 0.92), transparent 70%),
        rgba(6, 6, 73, 0.95);
    backdrop-filter: blur(6px);
    animation: tv-endcard-in 0.3s ease both;
}

.tv-endcard[hidden] {
    display: none;
}

@keyframes tv-endcard-in {
    from { opacity: 0; }
    to { opacity: 1; }
}

.tv-endcard-label {
    margin: 0;
    color: rgba(255, 255, 255, 0.6);
    font-size: 0.8125rem;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
}

.tv-endcard-next {
    display: flex;
    align-items: center;
    gap: 1rem;
    max-width: 30rem;
    padding: 0.625rem;
    border-radius: 0.875rem;
    color: #fff;
    text-align: left;
    text-decoration: none;
    transition: background 0.15s ease;
}

.tv-endcard-next:hover {
    background: rgba(255, 255, 255, 0.1);
    color: #fff;
}

.tv-endcard-thumb {
    position: relative;
    flex: 0 0 auto;
    width: clamp(6.5rem, 16vw, 10rem);
    aspect-ratio: 16 / 9;
    border-radius: 0.5rem;
    background: rgba(0, 0, 0, 0.35);
    overflow: hidden;
}

.tv-endcard-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: top center;
    opacity: 0.85;
}

/* The countdown: a ring draining over the same span as the timer, with the
   seconds left in the middle. */
.tv-endcard-ring {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(6, 6, 73, 0.45);
}

.tv-endcard-ring svg {
    position: absolute;
    width: 3rem;
    height: 3rem;
    transform: rotate(-90deg);
}

.tv-ring-track,
.tv-ring-bar {
    fill: none;
    stroke-width: 3;
}

.tv-ring-track {
    stroke: rgba(255, 255, 255, 0.25);
}

.tv-ring-bar {
    stroke: #fff;
    stroke-linecap: round;
    /* 2 * pi * r, r = 17 */
    stroke-dasharray: 106.81;
    stroke-dashoffset: 0;
}

.is-counting .tv-ring-bar {
    animation: tv-ring-drain var(--tv-countdown, 8s) linear forwards;
}

@keyframes tv-ring-drain {
    to { stroke-dashoffset: 106.81; }
}

.tv-endcard-count {
    position: relative;
    color: #fff;
    font-size: 1rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.tv-endcard-meta {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    min-width: 0;
}

.tv-endcard-title {
    color: #fff;
    font-size: 1.0625rem;
    font-weight: 600;
    line-height: 1.3;
}

.tv-endcard-tagline {
    color: rgba(255, 255, 255, 0.7);
    font-size: 0.875rem;
    line-height: 1.4;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.tv-endcard-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    justify-content: center;
}

.tv-endcard-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.55rem 1.1rem;
    border: 1px solid rgba(255, 255, 255, 0.35);
    border-radius: 2rem;
    background: rgba(255, 255, 255, 0.12);
    color: #fff;
    font-size: 0.9375rem;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease;
}

.tv-endcard-btn:hover {
    border-color: rgba(255, 255, 255, 0.6);
    background: rgba(255, 255, 255, 0.22);
}

.tv-endcard-btn-ghost {
    border-color: transparent;
    background: transparent;
    color: rgba(255, 255, 255, 0.75);
}

.tv-endcard-btn-ghost:hover {
    background: rgba(255, 255, 255, 0.12);
    color: #fff;
}

/* The ring is a progress readout, not decoration, so it keeps running; only
   the splash fade is dropped. */
@media (prefers-reduced-motion: reduce) {
    .tv-endcard {
        animation: none;
    }

    .tv-player-start,
    .tv-controls {
        transition: none;
    }

    .tv-player-pulse.is-on {
        animation-duration: 0.01s;
    }
}

/* Short viewports: the end card has to shed something before it clips. */
@media (max-height: 30rem) {
    .tv-endcard {
        gap: 0.75rem;
    }

    .tv-endcard-tagline {
        display: none;
    }
}

.tv-detail-title {
    margin: 1.5rem 0 0.5rem;
    font-size: clamp(1.5rem, 2.6vw, 2.125rem);
    line-height: 1.2;
    letter-spacing: -0.02em;
}

.tv-detail-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 1.25rem;
    margin: 0 0 1.25rem;
    padding: 0;
    list-style: none;
    color: var(--tv-text-faint);
    font-size: 0.875rem;
}

.tv-detail-meta li {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
}

/* The write-up and its links in one block under the player. */
.tv-detail-panel {
    padding: 1.5rem;
    border: 1px solid var(--tv-border);
    border-radius: 1rem;
    background: var(--tv-bg-soft);
}

/* A lead paragraph, styled like the leads on every other page: grey, one
   step larger than body copy. Not an accent. */
.tv-detail-tagline {
    margin-bottom: 1rem;
    color: var(--tv-text-dim);
    font-size: 1.1875rem;
    line-height: 1.6;
}

.tv-detail-desc p {
    margin-bottom: 1rem;
    color: var(--tv-text-dim);
    font-size: 1.0625rem;
    line-height: 1.7;
}

.tv-detail-desc p:last-child {
    margin-bottom: 0;
}

.tv-detail-facts {
    margin-top: 1.5rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--tv-border-strong);
    display: flex;
    flex-direction: column;
    gap: 0.875rem;
}

/* Label in a fixed column so the three rows line up with each other. */
.tv-fact {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem 1rem;
}

.tv-fact-label {
    flex: 0 0 6rem;
    color: var(--tv-text-faint);
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.tv-fact-values {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    min-width: 0;
}

.tv-fact-links {
    gap: 0.5rem 1.25rem;
}

.tv-fact-links a {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    color: var(--tv-text-dim);
    font-weight: 600;
    text-decoration: none;
}

.tv-fact-links a:hover {
    color: var(--tv-ink);
    text-decoration: underline;
}

.tv-tag {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.4rem 0.8rem;
    border: 1px solid var(--tv-border);
    border-radius: 2rem;
    background: var(--tv-bg);
    color: var(--tv-text-dim);
    font-size: 0.875rem;
    font-weight: 600;
    text-decoration: none;
}

.tv-tag:hover {
    border-color: var(--tv-border-strong);
    background: var(--tv-surface);
    color: var(--tv-ink);
}

.tv-tag-muted {
    font-weight: 500;
    text-transform: lowercase;
}

/* ------------------------------------------------------- detail sidebar --- */

.tv-detail-side {
    min-width: 0;
}

.tv-side-title {
    margin-bottom: 1rem;
    font-size: 1.125rem;
}

.tv-side-list {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

/* Thumbnail left, text right, the way a playlist reads. */
.tv-side-item {
    display: grid;
    grid-template-columns: 10.5rem minmax(0, 1fr);
    gap: 0.75rem;
    padding: 0.5rem;
    margin: -0.5rem;
    border-radius: 0.75rem;
    color: inherit;
    text-decoration: none;
    transition: background 0.15s ease;
}

.tv-side-item:hover {
    background: var(--tv-surface);
    color: inherit;
}

.tv-side-thumb {
    position: relative;
    display: block;
    aspect-ratio: 16 / 9;
    border: 1px solid var(--tv-border);
    border-radius: 0.5rem;
    background: var(--tv-bg-soft);
    overflow: hidden;
}

.tv-side-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: top center;
}

.tv-side-body {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    min-width: 0;
}

.tv-side-item-title {
    color: var(--tv-ink);
    font-weight: 600;
    font-size: 0.9375rem;
    line-height: 1.3;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.tv-side-item-tagline {
    color: var(--tv-text-dim);
    font-size: 0.8125rem;
    line-height: 1.4;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.tv-side-item-meta {
    color: var(--tv-text-faint);
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
}

.tv-side-all {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    margin-top: 1.25rem;
    color: var(--tv-text-dim);
    font-size: 0.875rem;
    font-weight: 600;
    text-decoration: none;
}

.tv-side-all:hover {
    color: var(--tv-ink);
}

/* ------------------------------------------------------------ responsive --- */

/* The sidebar stops earning its width before the usual lg breakpoint: at
   1199px a 24rem column leaves the player too small to be the point of the
   page. Stack instead, sidebar last. */
@media (max-width: 1199.98px) {
    .tv-detail-grid {
        grid-template-columns: minmax(0, 1fr);
    }

    .tv-detail-grid {
        /* One column, so nothing is competing for width any more. */
        max-width: none;
    }

    .tv-detail-side {
        margin-top: 1rem;
        padding-top: 1.5rem;
        border-top: 1px solid var(--tv-border);
    }
}

@media (max-width: 991.98px) {
    .tv-hero {
        padding-top: 8.5rem;
    }

    .tv-detail-wrap {
        padding-top: 6rem;
    }
}

/* Below this the two hero columns are both too narrow to be worth having, so
   the search bar takes the full width under the title instead. */
@media (max-width: 767.98px) {
    .tv-hero-aside {
        align-items: stretch;
        flex-basis: 100%;
    }

}

@media (max-width: 575.98px) {
    .tv-hero {
        padding: 7.5rem 0 2rem;
    }

    .tv-card {
        width: 14rem;
    }

    .tv-row {
        padding: 1.25rem 0;
    }

    .tv-row-title {
        font-size: 1.1875rem;
    }

    .tv-detail-panel {
        padding: 1.25rem 1rem;
    }

    /* The fixed label column costs too much width on a phone. */
    .tv-fact {
        flex-direction: column;
        gap: 0.5rem;
    }

    .tv-fact-label {
        flex: none;
    }

    .tv-side-item {
        grid-template-columns: 8.5rem minmax(0, 1fr);
    }
}
