/*
 * cx-widgets.css — styles for cx widget components
 * Injected by cx-widgets.js when first widget is used.
 * B.2.9
 */

:root { --cx-widgets-version: 'B.2.9'; }

/* ── Timer widget containers — never steal flex space ─────────────────────── */

/*
 * Two selectors per widget throughout this file: `~=` matches the bare word,
 * `*=` with the trailing '=' matches the labelled param form
 * (progress=Audio_description). The '=' anchors it, so it cannot also match
 * a longer word that merely starts the same way.
 */
body [data-cx~="countdown"], body [data-cx*="countdown="],
body [data-cx~="progress"],  body [data-cx*="progress="],
body [data-cx~="hourglass"], body [data-cx*="hourglass="] {
    flex: none !important;
}

/* ── Reserved height and idle track (progress) ────────────────────────────── */
/*
 * The host must keep its height whether or not a widget is running, or the
 * page reflows every time a cue starts and ends.
 *
 * Both of the ways the widget goes away wipe the host's content:
 * the widget empties innerHTML when the clip finishes, and cx-render.js
 * sets innerHTML = EMPTY on a blank cue before it calls stopTimer. So the
 * idle track can't be markup — it's a ::before on the host, which survives
 * both, and it needs no JS at all.
 *
 * It doubles as the track the live bar fills over, which a progress bar
 * wants anyway: at 0% the bar is invisible and the track is what you see.
 *
 * min-height is derived from the same variables the live widget lays itself
 * out with, so the reserved space and the real content can't drift apart.
 */

:root {
    --cx-progress-pad:  0.5rem;
    --cx-progress-bar:  8px;
    --cx-progress-gap:  0.4rem;
    --cx-progress-time: 1.28rem;   /* 0.85rem text at body line-height 1.5 */
    --cx-widget-label:  1.05rem;   /* 0.7rem caps at line-height 1.5 */
}

/*
 * Layout is three bands: an info line (label left, time right), a gap, then
 * the bar. Label and time SHARE that line — stacking them cost a whole extra
 * row, which in a baseline-aligned header pushed the title down the page.
 */
body [data-cx~="progress"],
body [data-cx*="progress="] {
    position:   relative;
    box-sizing: border-box;
    /* Fills its container horizontally. The flex:none above stops the host
     * stealing space along a column body's main axis, but in a row-direction
     * container (a footer, a header) that same rule leaves it at content
     * width — which is nothing, since the widget markup is now out of flow.
     * width is direction-safe in a way that a flex-basis would not be. */
    width:      100%;
    min-height: calc(2 * var(--cx-progress-pad) + var(--cx-progress-bar)
                     + var(--cx-progress-gap) + var(--cx-progress-time));
}

body [data-cx~="progress"]::before,
body [data-cx*="progress="]::before {
    content:       '';
    position:      absolute;
    left:          var(--cx-progress-pad);
    right:         var(--cx-progress-pad);
    top:           calc(var(--cx-progress-pad) + var(--cx-progress-time)
                        + var(--cx-progress-gap));
    height:        var(--cx-progress-bar);
    background:    var(--cx-border);
    border-radius: 2px;
    opacity:       0.55;
}

/* ── Bars side by side ────────────────────────────────────────────────────── */
/*
 * Widget hosts are width:100% with flex-shrink:0, which is right when each
 * has a container to itself but wrong when two share one: in a no-wrap row
 * they overflow, and body[col]'s overflow:hidden clips the second — present,
 * but off-screen, which looks exactly like it failed to render.
 *
 * No class to remember. A plain wrapper is enough:
 *
 *     <div>
 *         <div data-cx="ad.audio progress=Audio_description"></div>
 *         <div data-cx="st.audio progress=Subtitles"></div>
 *     </div>
 *
 * Any element directly holding two adjacent hosts becomes a row. body is
 * excluded — it has its own layout from data-cx="col"/"row", and two bars
 * dropped straight into a column body should stack, not silently turn it
 * sideways. cx-base.css already uses :has() (body:has(> iframe.cx-backdrop),
 * body:has(.cx-vc-wrap)), so this needs nothing new of the browser.
 *
 * .cx-row is NOT that container, despite the name: it is height:100vh, a
 * full-screen primitive, and a strip of bars in one would eat the viewport.
 *
 * The [data-cx*="progress"] in the :has() test is deliberately loose — it
 * only has to spot "two bars next to each other" to decide the container is a
 * row, and the precise host selectors below are what actually get styled.
 */

body :not(body):has(> [data-cx*="progress"] + [data-cx*="progress"]):not(.cx-header):not(.cx-footer) {
    display:     flex;
    gap:         1rem;
    align-items: stretch;
}

/*
 * Header and footer are row flex already, so hosts there share the line
 * whether there are two or one — a single bar in a header sits beside the
 * title rather than taking a row of its own.
 *
 * This has to be scoped to containers known to run horizontally: there is no
 * direction-agnostic way to say "don't grow along the main axis but do fill
 * the cross axis", which is why it cannot live on the host itself.
 */

.cx-header > [data-cx~="progress"],
.cx-header > [data-cx*="progress="],
.cx-footer > [data-cx~="progress"],
.cx-footer > [data-cx*="progress="],
:has(> [data-cx*="progress"] + [data-cx*="progress"]) > [data-cx~="progress"],
:has(> [data-cx*="progress"] + [data-cx*="progress"]) > [data-cx*="progress="] {
    flex:      1 1 0 !important;
    width:     auto;
    min-width: 0;
}

/* ── Widget label ─────────────────────────────────────────────────────────── */
/*
 * Drawn from the host's data-cx-label attribute, stamped by cx-widgets.js at
 * module load. A pseudo-element rather than markup for the same reason the
 * track is one: the host's content is wiped when a clip ends and again on a
 * blank cue, so a label built as content would show only while a cue ran.
 */

body [data-cx-label]::after {
    content:        attr(data-cx-label);
    position:       absolute;
    left:           var(--cx-progress-pad);
    /* Stops short of the right edge so a long label ellipsises rather than
     * running under the time readout sitting there. */
    right:          calc(var(--cx-progress-pad) + 4rem);
    top:            var(--cx-progress-pad);
    font-family:    var(--cx-font-mono);
    font-size:      0.7rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color:          var(--cx-text-dim);
    line-height:    1.5;
    white-space:    nowrap;
    overflow:       hidden;
    text-overflow:  ellipsis;
    pointer-events: none;
}

/* The bar sits below the label. The widget fills the host, so its own padding
 * is what moves the bar down — the label is out of flow and cannot push it. */
body [data-cx~="progress"] .cx-widget-progress,
body [data-cx*="progress="] .cx-widget-progress {
    padding-top: calc(var(--cx-progress-pad) + var(--cx-progress-time)
                      + var(--cx-progress-gap));
}

/* Time sits at the right-hand end of the info line, opposite the label. */
.cx-widget-progress-time {
    position: absolute;
    top:      var(--cx-progress-pad);
    right:    var(--cx-progress-pad);
}

/* A tall widget in a baseline-aligned header drags the title down to its
 * bottom edge — the host has no text baseline of its own (its content is out
 * of flow), so the baseline falls back to the bottom margin edge. Centring
 * the hosts instead leaves neighbouring text where it would otherwise sit. */
.cx-header > [data-cx~="progress"],
.cx-header > [data-cx*="progress="],
.cx-footer > [data-cx~="progress"],
.cx-footer > [data-cx*="progress="] {
    align-self: center;
}

/* countdown and hourglass write in normal flow, so they need host padding
 * instead. Neither reserves height when idle — unchanged, and still worth
 * doing one day. */
body [data-cx*="countdown="],
body [data-cx*="hourglass="] {
    position:    relative;
    padding-top: calc(var(--cx-progress-pad) + var(--cx-widget-label));
    box-sizing:  border-box;
}

/* ── Countdown ────────────────────────────────────────────────────────────── */

.cx-widget-countdown {
    font-family:  var(--cx-font-mono);
    font-size:    2rem;
    color:        var(--cx-text);
    text-align:   center;
    line-height:  1;
    padding:      0.5rem;
}

/* ── Progress bar ─────────────────────────────────────────────────────────── */

.cx-widget-progress {
    display:        flex;
    flex-direction: column;
    align-items:    stretch;
    gap:            var(--cx-progress-gap);
    padding:        var(--cx-progress-pad);
    box-sizing:     border-box;
    /* Out of flow, filling the host. The host's min-height is then the only
     * thing setting its height, so starting and stopping a cue cannot move
     * the page — reserved and active are the same box by construction,
     * rather than by the min-height calc happening to match the content.
     * Being positioned also puts it over the host's ::before track; both
     * are z-index auto, so tree order decides and ::before is first. */
    position:       absolute;
    inset:          0;
}

.cx-widget-progress-bar {
    height:        var(--cx-progress-bar);
    background:    var(--cx-brand);
    width:         0%;
    /* No width transition. The bar is redrawn every animation frame now, and
     * a transition kept the painted bar trailing the true position — by 0.1s
     * at the old tick rate — which is the drift this widget exists to avoid.
     * It also stretched the full-bar end hold into a slide. */
    border-radius: 2px;
}

.cx-widget-progress-time {
    font-family: var(--cx-font-mono);
    font-size:   0.85rem;
    color:       var(--cx-text-dim);
    text-align:  right;
}

/* ── Video controller ─────────────────────────────────────────────────────── */

body:has(.cx-vc-wrap) {
    height:     auto;
    min-height: 100vh;
    overflow-y: auto;
    padding:    1rem;
}

.cx-vc-wrap {
    max-width:      860px;
    margin:         0 auto;
    display:        flex;
    flex-direction: column;
    gap:            1rem;
}

.cx-vc-header {
    display:        flex;
    align-items:    baseline;
    gap:            1rem;
    padding-bottom: 0.5rem;
    border-bottom:  1px solid var(--cx-border);
}

.cx-vc-title {
    font-family: var(--cx-font-serif-title);
    font-size:   1.2rem;
    color:       var(--cx-text-strong);
}

.cx-vc-meta {
    font-size:   0.8rem;
    color:       var(--cx-text-dim);
    font-family: var(--cx-font-mono);
}

.cx-vc-sock {
    font-size:   0.75rem;
    color:       var(--cx-text-dim);
    display:     flex;
    align-items: center;
    gap:         0.4rem;
    margin-left: auto;
}

.cx-vc-sock-dot {
    width:         7px;
    height:        7px;
    border-radius: 50%;
    background:    var(--cx-err);
    display:       inline-block;
    flex:          none;
}

/* Video container */
[data-cx="meta.video"] {
    width:        100%;
    aspect-ratio: 16/9;
    /* Literal black is deliberate: this is the matte a letterboxed video sits
     * on, which should be black whatever the theme. */
    background:   #000; /* cx-colour-ok */
}

/* Status bar — built by cx-render.js for data-cx="meta.video status" */
.cx-vc-status-bar {
    display:       flex;
    gap:           1.5rem;
    font-family:   var(--cx-font-mono);
    font-size:     0.8rem;
    color:         var(--cx-text-dim);
    padding:       0.4rem 0;
    border-bottom: 1px solid var(--cx-border);
}

.cx-vc-status-bar b { color: var(--cx-text); }

.cx-vc-log-head {
    font-size:      0.7rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color:          var(--cx-brand);
    margin:         0.5rem 0 0.25rem;
}

.cx-vc-log {
    font-family: var(--cx-font-mono);
    font-size:   0.78rem;
    max-height:  260px;
    overflow-y:  auto;
    border:      1px solid var(--cx-border);
    padding:     0.5rem;
    background:  var(--cx-surface);
}

.cx-vc-log-entry {
    padding:       0.15rem 0;
    border-bottom: 1px solid var(--cx-border-dim);
}

.cx-vc-log-entry.fired { color: var(--cx-ok); }
.cx-vc-log-entry.blank { color: var(--cx-text-dim); font-style: italic; }
