/* ==========================================================================
   Split_TV :: files/split_tv.css
   The wall is a media surface, so it is deliberately dark - the picture is
   the content and a bright frame around live video is glare. The admin panel
   (system/config.php) is the light one.
   ========================================================================== */

#stv_wrap {
    position: fixed;
    inset: 0;
    z-index: 99990;
    display: none;
    flex-direction: column;
    background: #0d1117;
    color: #e8eef5;
    font-size: 14px;
    direction: rtl;
}
#stv_wrap.stv_on { display: flex; }
/* The wrap itself as well, not only its children: the compact window is sized
   from JS and it carries a border, so a content-box wrap would draw 2px wider
   and taller than the height the client measured its picture against. */
#stv_wrap, #stv_wrap * { box-sizing: border-box; }

/* -- header ------------------------------------------------------------- */
#stv_wrap .stv_hd {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 10px;
    background: #151c25;
    border-bottom: 1px solid rgba(255, 255, 255, .09);
    flex-wrap: nowrap;
}
#stv_wrap .stv_ttl {
    font-weight: 800;
    font-size: 15px;
    letter-spacing: .2px;
    margin-inline-end: 4px;
    white-space: nowrap;
}
#stv_wrap .stv_ttl i { color: #01a6d4; margin-inline-end: 6px; }
#stv_wrap .stv_sp { flex: 1 1 auto; }
#stv_wrap .stv_hbtn {
    border: 1px solid rgba(255, 255, 255, .14);
    background: rgba(255, 255, 255, .05);
    color: #e8eef5;
    border-radius: 8px;
    padding: 6px 10px;
    cursor: pointer;
    font-size: 14px;
    line-height: 1;
    min-width: 34px;
    flex: 0 0 auto;
}
#stv_wrap .stv_hbtn:hover { background: rgba(1, 166, 212, .22); border-color: #01a6d4; }
#stv_wrap .stv_hbtn.stv_act { background: rgba(1, 166, 212, .28); border-color: #01a6d4; }
#stv_wrap .stv_hbtn.stv_x:hover { background: rgba(224, 38, 60, .3); border-color: #e0263c; }
/* The mute button silences everything at once, and it does it in one direction
   - so it must read as an ACTION, not as a state. Font Awesome 4 has no
   crossed-out speaker, so the bar is drawn over a normal one: a speaker on its
   own is what a volume control looks like, and viewers clicked it expecting
   sound. */
#stv_wrap .stv_b_mute { position: relative; }
#stv_wrap .stv_b_mute::after {
    content: "";
    position: absolute;
    left: 50%;
    top: 50%;
    width: 68%;
    height: 2px;
    border-radius: 2px;
    background: #ff5c6c;
    box-shadow: 0 0 0 1px rgba(0, 0, 0, .55);
    transform: translate(-50%, -50%) rotate(-45deg);
    pointer-events: none;
}
#stv_wrap .stv_b_mute:hover::after { background: #ff8a95; }
/* The header buttons that carry their name beside the icon. Below 700px the
   row is nowrap and clipped, so the words go before the buttons do. */
#stv_wrap .stv_b_pick,
#stv_wrap .stv_b_cmp { display: inline-flex; align-items: center; gap: 6px; }
#stv_wrap .stv_hlab { font-size: 13px; font-weight: 600; white-space: nowrap; }
@media (max-width: 700px) {
    #stv_wrap .stv_hlab { display: none; }
}
#stv_wrap .stv_lay {
    background: #0d1117;
    color: #e8eef5;
    border: 1px solid rgba(255, 255, 255, .14);
    border-radius: 8px;
    padding: 6px 8px;
    font-size: 13px;
    cursor: pointer;
    /* The chat's own stylesheet sets every select to 100% width. Left alone it
       fills the header, pushing the buttons onto two more rows. */
    width: auto;
    min-width: 58px;
    max-width: 90px;
    flex: 0 0 auto;
}

/* -- body / wall -------------------------------------------------------- */
#stv_wrap .stv_body { flex: 1 1 auto; display: flex; min-height: 0; position: relative; }
#stv_wrap .stv_wall {
    flex: 1 1 auto;
    position: relative;
    display: grid;
    grid-template-columns: repeat(var(--stv-cols, 3), 1fr);
    grid-template-rows: repeat(var(--stv-rows, 2), 1fr);
    /* No gap and no padding on purpose. A gap is bare wall between the
       windows, and bare wall is the only place a stray strip of video can be
       seen - nothing is painted over it. The windows touch instead, and each
       one draws its own share of the separation on top of its own picture. */
    gap: 0;
    padding: 0;
    min-width: 0;
    min-height: 0;
    background: #05080c;
    /* Windows are numbered left to right. Letting the page's RTL direction
       flip the grid would make "window 1" land on the right in Hebrew and on
       the left in English, and the admin's slot numbers have to mean one
       thing. The text inside the tiles inherits its own direction. */
    direction: ltr;
}

/* -- focus (spotlight) --------------------------------------------------
   One window fills the screen; the rest become a rail of small ones down the
   side. It is the same grid with two columns - no tile is moved in the DOM,
   so nothing re-buffers when the viewer switches into or out of this.

   The rail rows are sized by the pictures rather than sharing the height out
   equally: an equal share makes each thumbnail a tall slot holding a short
   16:9 picture, which reads as a huge gap between them. The trailing 1fr is
   what the big window spans into, so it still fills the wall while the rail
   stacks tight against the top. */
#stv_wrap.stv_mode_focus .stv_wall {
    grid-template-columns: minmax(96px, 15%) 1fr;
    grid-template-rows: repeat(var(--stv-rows, 1), auto) 1fr;
    align-content: start;
    overflow-y: auto;
    overflow-x: hidden;
}
#stv_wrap.stv_mode_focus .stv_tile:not(.stv_maxed) {
    aspect-ratio: 16 / 9;
    align-self: start;
    min-height: 0;
}
#stv_wrap.stv_mode_focus .stv_slot { display: none; }
#stv_wrap.stv_mode_focus .stv_tile:not(.stv_maxed) .stv_nm { font-size: 10px; }
#stv_wrap.stv_mode_focus .stv_tile:not(.stv_maxed) .stv_tb { width: 26px; height: 24px; font-size: 12px; }
#stv_wrap.stv_mode_focus .stv_tile:not(.stv_maxed) .stv_t_share { display: none; }
/* The separation ring each window draws for itself is the frame, so the
   spotlighted one is marked by colouring that rather than by an outline the
   ring would sit on top of. */
#stv_wrap.stv_mode_focus .stv_tile.stv_maxed::after { border-color: rgba(1, 166, 212, .7); }
/* A small window can be dragged onto the big one to trade places with it. */
#stv_wrap.stv_mode_focus .stv_tile:not(.stv_maxed) .stv_ov { cursor: pointer; }

/* -- compact ------------------------------------------------------------
   A small window that floats over the chat instead of covering it, showing
   one channel picked from the side list. */
#stv_wrap.stv_mode_compact {
    inset: auto;
    width: 640px;
    height: 430px;
    border-radius: 12px;
    overflow: hidden;
    box-shadow: 0 18px 48px rgba(0, 0, 0, .55);
    border: 1px solid rgba(255, 255, 255, .14);
}
#stv_wrap.stv_mode_compact .stv_hd {
    cursor: move;
    touch-action: none;
    padding: 5px 7px;
    gap: 5px;
    flex-wrap: nowrap;
    overflow: hidden;
}
#stv_wrap.stv_mode_compact .stv_ttl {
    font-size: clamp(10px, calc(var(--stv-cw, 380px) * .0316), 12px);
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Under this the buttons have shrunk as far as they still read, so the title
   goes rather than the buttons sliding out past the edge of the window. */
#stv_wrap.stv_cw_sm .stv_ttl { display: none; }
#stv_wrap.stv_mode_compact .stv_ttl i { margin-inline-end: 3px; }
/* Sized off the window's own width, which the client publishes as `--stv-cw`.
   A container query would have to contain the whole window, and the header and
   the strip sit outside the tile that already is one. */
#stv_wrap.stv_mode_compact .stv_hbtn {
    padding: 3px clamp(2px, calc(var(--stv-cw, 380px) * .0158), 6px);
    min-width: clamp(18px, calc(var(--stv-cw, 380px) * .0684), 26px);
    font-size: clamp(9px, calc(var(--stv-cw, 380px) * .0316), 12px);
}
#stv_wrap.stv_mode_compact .stv_hlab { display: none; }
/* Except these two, which were asked for by name. In the small window they are
   the only way back to the wall and the only way to another channel, and an
   icon on its own was being read as decoration. The WINDOW's width decides
   whether they fit, not the page's, so this has to beat the phone rule above
   as well - a compact window on a phone is still 600-odd pixels wide. */
#stv_wrap.stv_mode_compact .stv_b_cmp .stv_hlab,
#stv_wrap.stv_mode_compact .stv_b_pick .stv_hlab { display: inline; }
#stv_wrap.stv_cw_sm .stv_b_cmp .stv_hlab,
#stv_wrap.stv_cw_sm .stv_b_pick .stv_hlab { display: none; }
#stv_wrap.stv_mode_compact .stv_lay,
#stv_wrap.stv_mode_compact .stv_views,
#stv_wrap.stv_mode_compact .stv_b_wrst,
#stv_wrap.stv_mode_compact .stv_b_reload { display: none; }
/* Compact plays one window at a time, so there is nothing to hold big. */
#stv_wrap.stv_mode_compact .stv_t_pin { display: none; }

/* -- compact channel strip ----------------------------------------------
   Only the compact window shows it; in the grid every channel is already on
   screen, so a row of logos would say nothing. */
#stv_wrap .stv_strip { display: none; }
#stv_wrap.stv_mode_compact .stv_strip {
    display: flex;
    align-items: center;
    flex: 0 0 auto;
    gap: clamp(2px, calc(var(--stv-cw, 380px) * .0105), 4px);
    padding: 4px 5px;
    background: #12181f;
    border-bottom: 1px solid rgba(255, 255, 255, .1);
    overflow-x: auto;
    overflow-y: hidden;
    direction: ltr;
    scrollbar-width: thin;
}
#stv_wrap .stv_strip_b {
    position: relative;
    flex: 0 0 auto;
    width: clamp(28px, calc(var(--stv-cw, 380px) * .111), 42px);
    height: clamp(20px, calc(var(--stv-cw, 380px) * .079), 30px);
    padding: 0;
    border-radius: 6px;
    border: 1px solid rgba(255, 255, 255, .14);
    background: #0d1117 no-repeat center / contain;
    color: #cfd8e3;
    font-size: clamp(7px, calc(var(--stv-cw, 380px) * .0237), 9px);
    line-height: 1.05;
    cursor: pointer;
    overflow: hidden;
}
/* The row is the viewer's own shortlist, so each logo carries the way out of
   it. On a mouse it waits for the pointer; a finger has no hover to wait for. */
#stv_wrap .stv_strip_x {
    position: absolute;
    top: 0;
    right: 0;
    display: none;
    width: 13px;
    height: 13px;
    border-radius: 0 5px 0 5px;
    background: rgba(224, 38, 60, .92);
    color: #fff;
    font-size: 11px;
    line-height: 13px;
    text-align: center;
}
#stv_wrap .stv_strip_b:hover .stv_strip_x { display: block; }
@media (hover: none) {
    #stv_wrap .stv_strip_x { display: block; }
}
/* The admin's own row, drawn as a group: no way out of it, and it is not
   picked up by a drag, so it should not read as one of the viewer's own. */
#stv_wrap .stv_strip_b.stv_strip_fix { border-color: rgba(255, 255, 255, .3); }
#stv_wrap .stv_strip_rst {
    position: sticky;
    left: 0;
    z-index: 2;
    flex: 0 0 auto;
    width: clamp(18px, calc(var(--stv-cw, 380px) * .058), 22px);
    height: clamp(20px, calc(var(--stv-cw, 380px) * .079), 30px);
    padding: 0;
    border: 0;
    border-radius: 6px;
    background: #12181f;
    color: #8fa2b6;
    font-size: clamp(9px, calc(var(--stv-cw, 380px) * .0263), 11px);
    cursor: pointer;
}
#stv_wrap .stv_strip_rst:hover { color: #fff; background: rgba(1, 166, 212, .35); }
/* While a logo is being carried the row must not scroll itself back to the one
   playing, and the one being carried says so. */
#stv_wrap.stv_strip_drag .stv_strip { cursor: grabbing; }
#stv_wrap .stv_strip_b.stv_strip_move { opacity: .4; }
#stv_wrap .stv_strip_b:hover { border-color: rgba(1, 166, 212, .7); }
#stv_wrap .stv_strip_b.stv_strip_on {
    border-color: #01a6d4;
    box-shadow: 0 0 0 1px #01a6d4 inset;
    background-color: rgba(1, 166, 212, .16);
}
#stv_wrap .stv_strip_t {
    display: block;
    padding: 2px;
    max-height: 100%;
    overflow: hidden;
}
#stv_wrap.stv_mode_compact .stv_wall { padding: 0; gap: 0; cursor: move; }
#stv_wrap.stv_mode_compact .stv_tile { border-radius: 0; border: 0; }
#stv_wrap.stv_mode_compact .stv_slot { display: none; }
#stv_wrap.stv_mode_compact .stv_grip_se { display: none; }
/* The window is small, so the channel list slides in OVER the picture instead
   of splitting the width with it - the video keeps the whole window. This is
   the fallback for when there is no room beside the window. */
#stv_wrap.stv_mode_compact .stv_picker {
    position: absolute;
    inset-block: 0;
    inset-inline-end: 0;
    z-index: 12;
    flex: 0 0 auto;
    width: 62%;
    min-width: 148px;
    max-width: 240px;
    box-shadow: 0 0 26px rgba(0, 0, 0, .6);
}
#stv_wrap.stv_mode_compact .stv_pick_hd { padding: 5px; }
#stv_wrap.stv_mode_compact .stv_cats { padding: 0 5px 5px; max-height: 46px; }

/* Compact's own way into the channel list: a small tab on the left edge. Every
   header button is a 26px square in that window, and the list is what a viewer
   reaches for most - so it gets a handle he cannot miss. Physical left, not
   inline-start: this is an edge of a floating box, not reading order.

   This is the fallback position, INSIDE the window. It is only used where the
   window has to clip its own children - on a phone, or squeezed so tightly that
   the list has to open over the picture - because a tab hanging outside an
   `overflow: hidden` box is simply cut off. Given room it moves outside; see
   the media query below. */
#stv_wrap .stv_tab { display: none; }
#stv_wrap.stv_mode_compact:not(.stv_fs) .stv_tab {
    display: flex;
    position: absolute;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    z-index: 11;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding: 5px 2px;
    border: 1px solid rgba(255, 255, 255, .16);
    border-left: 0;
    border-radius: 0 9px 9px 0;
    background: rgba(10, 14, 20, .82);
    color: #e8eef5;
    font-size: 12px;
    line-height: 1;
    cursor: pointer;
}
#stv_wrap.stv_mode_compact .stv_tab:hover {
    background: rgba(1, 166, 212, .55);
    border-color: #01a6d4;
}
#stv_wrap .stv_tab_i { display: block; font-size: 11px; transition: transform .15s; }
#stv_wrap .stv_tab_t {
    writing-mode: vertical-rl;
    text-orientation: mixed;
    font-size: 8px;
    letter-spacing: .3px;
    max-height: 44px;
    overflow: hidden;
}
/* The tab never moves: the list opens beside it and the tab is simply drawn on
   top, because it is also how the list is closed. */
#stv_wrap.stv_pick_on:not(.stv_pick_out) .stv_tab { z-index: 13; }
#stv_wrap.stv_pick_on:not(.stv_pick_out) .stv_tab_i { transform: rotate(180deg); }
/* The list is asked to be exactly the window's height, so its floor has to go:
   a 132px minimum under a 190px window pushes the bottom of the list out past
   the bottom of the picture it belongs to. */
#stv_wrap.stv_mode_compact .stv_pick_list { min-height: 0; }

/* Given room, the list sits BESIDE the window rather than on it. Covering a
   380px window with the list hides the very picture being chosen. Physical
   left/right here, not logical: this is about which side of the screen the
   window was dragged to, not about reading direction. `stv_pick_flip` is set
   by the client when the window is too near the left edge to fit the list. */
@media (min-width: 993px) {
    #stv_wrap.stv_mode_compact:not(.stv_fs):not(.stv_pick_over) {
        overflow: visible;
        --stv-pick-w: 240px;
    }
    #stv_wrap.stv_mode_compact:not(.stv_fs):not(.stv_pick_over) .stv_hd { border-radius: 12px 12px 0 0; }
    #stv_wrap.stv_mode_compact:not(.stv_fs):not(.stv_pick_over) .stv_wall {
        border-radius: 0 0 12px 12px;
        overflow: hidden;
    }
    #stv_wrap.stv_mode_compact:not(.stv_fs):not(.stv_pick_over) .stv_picker {
        right: 100%;
        left: auto;
        margin-right: 8px;
        margin-left: 0;
        width: var(--stv-pick-w);
        min-width: var(--stv-pick-w);
        max-width: none;
        /* No min-height: inset-block:0 already makes this exactly as tall as
           the picture beside it, and a floor only made it hang past the bottom
           of a short window. */
        border-radius: 12px;
        overflow: hidden;
        border: 1px solid rgba(255, 255, 255, .14);
    }
    #stv_wrap.stv_mode_compact:not(.stv_fs):not(.stv_pick_over).stv_pick_flip .stv_picker {
        right: auto;
        left: 100%;
        margin-right: 0;
        margin-left: 8px;
    }
    /* Room beside the window is also room to hang the tab off it, so it stops
       covering the picture. Same test as the list, because it is the window's
       own overflow that would otherwise cut the tab off. */
    #stv_wrap.stv_mode_compact:not(.stv_fs):not(.stv_pick_over) .stv_tab {
        left: auto;
        right: 100%;
        width: 18px;
        border-left: 1px solid rgba(255, 255, 255, .16);
        border-right: 0;
        border-radius: 9px 0 0 9px;
        box-shadow: -6px 4px 18px rgba(0, 0, 0, .45);
    }
    /* The window has been dragged too near the left edge for the list to open
       there - which means there is no room for the tab either. */
    #stv_wrap.stv_mode_compact:not(.stv_fs):not(.stv_pick_over).stv_pick_flip .stv_tab {
        right: auto;
        left: 100%;
        border-left: 0;
        border-right: 1px solid rgba(255, 255, 255, .16);
        border-radius: 0 9px 9px 0;
        box-shadow: 6px 4px 18px rgba(0, 0, 0, .45);
    }
    #stv_wrap.stv_mode_compact:not(.stv_fs):not(.stv_pick_over).stv_pick_flip .stv_tab_i {
        transform: rotate(180deg);
    }
    /* Flipped AND open: the list is to the right, so closing points left again. */
    #stv_wrap.stv_mode_compact:not(.stv_fs):not(.stv_pick_over).stv_pick_flip.stv_pick_on:not(.stv_pick_out) .stv_tab_i {
        transform: none;
    }
}

/* Full screen wins over every floating-window measurement. */
#stv_wrap.stv_fs {
    inset: 0 !important;
    width: auto !important;
    height: auto !important;
    border: 0;
    border-radius: 0;
    box-shadow: none;
}
#stv_wrap.stv_fs .stv_wgrip { display: none; }
#stv_wrap.stv_fs .stv_hd { cursor: default; }

/* One channel filling the screen. The tile is the full screen element, so it
   escapes the grid and has to size itself. */
#stv_wrap .stv_tile:fullscreen,
#stv_wrap .stv_tile:-webkit-full-screen {
    width: 100%;
    height: 100%;
    border: 0;
    border-radius: 0;
    background-color: #000;
    background-image: none !important;
}
#stv_wrap .stv_tile:fullscreen .stv_grip_se,
#stv_wrap .stv_tile:-webkit-full-screen .stv_grip_se { display: none; }
#stv_wrap .stv_tile:fullscreen .stv_bar,
#stv_wrap .stv_tile:-webkit-full-screen .stv_bar { opacity: 0; }
#stv_wrap .stv_tile:fullscreen:hover .stv_bar,
#stv_wrap .stv_tile:-webkit-full-screen:hover .stv_bar { opacity: 1; pointer-events: auto; }

/* The corner that resizes the compact window. Hatched so it reads as a grip. */
#stv_wrap .stv_wgrip { display: none; }
#stv_wrap.stv_mode_compact .stv_wgrip {
    display: block;
    position: absolute;
    right: 0;
    bottom: 0;
    width: 20px;
    height: 20px;
    cursor: nwse-resize;
    touch-action: none;
    z-index: 20;
    background:
        repeating-linear-gradient(135deg,
            rgba(255, 255, 255, .9) 0 1.5px,
            transparent 1.5px 4px),
        linear-gradient(135deg, transparent 50%, rgba(1, 166, 212, .75) 50%);
    -webkit-mask-image: linear-gradient(135deg, transparent 52%, #000 52%);
    mask-image: linear-gradient(135deg, transparent 52%, #000 52%);
}

/* -- one tile ----------------------------------------------------------- */
#stv_wrap .stv_tile {
    position: relative;
    overflow: hidden;
    /* Square, and deliberately. A rounded clip on a playing picture is handled
       by the video compositor rather than by the page, and it rounds the clip
       rectangle to whole pixels of its own - which is how edges of the picture
       got out of the window in the first place. A straight clip has nothing to
       round. */
    border-radius: 0;
    background: #000 center/cover no-repeat;
    min-width: 0;
    min-height: 0;
    contain: paint;
    /* Declared here, not on the dragging classes, or a window would snap back
       into place the moment the finger let go. */
    transition: transform .16s ease-out, box-shadow .16s ease-out;
}
/* The separation between windows, drawn inside each one and above its picture,
   so the strip between two windows is always something that is repainted every
   frame rather than wall that nothing owns. */
#stv_wrap .stv_tile::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 40;
    pointer-events: none;
    border: 2px solid #05080c;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .07);
}
#stv_wrap.stv_mode_compact .stv_tile::after { display: none; }

/* The wall is full and a channel is waiting for somewhere to go. Every window
   answers "which one?" for itself: a scrim that takes the click wherever it
   lands, and a marker saying what the click will do. The scrim covers the
   control bar (z-index 7) and the resize grip (9) on purpose - while the
   question is open a window is a target and nothing else. */
#stv_wrap .stv_mark {
    position: absolute;
    inset: 0;
    z-index: 20;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 8px;
    background: rgba(0, 0, 0, .55);
    cursor: copy;
}
#stv_wrap .stv_mark_c {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    max-width: 100%;
    padding: 9px 16px;
    border: 2px dashed rgba(255, 255, 255, .55);
    border-radius: 999px;
    background: rgba(0, 0, 0, .45);
    color: #fff;
    font-size: 14px;
    font-weight: 700;
    white-space: nowrap;
    overflow: hidden;
    animation: stv_markflash 1.7s ease-in-out infinite;
}
#stv_wrap .stv_mark_p { font-size: 20px; line-height: 1; }
/* The flash is in the glow, not in the opacity: a marker that fades out is a
   marker you can be looking straight at while it is invisible. */
@keyframes stv_markflash {
    0%, 100% {
        border-color: rgba(255, 255, 255, .5);
        box-shadow: 0 0 0 rgba(255, 255, 255, 0);
        text-shadow: 0 2px 4px rgba(0, 0, 0, .95);
    }
    50% {
        border-color: rgba(255, 255, 255, .95);
        box-shadow: 0 0 20px rgba(255, 255, 255, .4);
        text-shadow: 0 0 12px rgba(255, 255, 255, .8), 0 2px 4px rgba(0, 0, 0, .95);
    }
}
#stv_wrap .stv_tile:hover .stv_mark { background: rgba(1, 166, 212, .42); }
#stv_wrap .stv_tile:hover .stv_mark_c {
    animation: none;
    border-style: solid;
    border-color: #fff;
    background: rgba(1, 166, 212, .9);
    text-shadow: 0 2px 4px rgba(0, 0, 0, .95);
}

/* The chosen channel, floating over the wall until a window is picked. It
   never takes a click - the windows underneath are the answer - so only its
   own cancel button opts back in. */
#stv_wrap .stv_hold {
    position: absolute;
    z-index: 30;
    top: 14px;
    left: 50%;
    transform: translateX(-50%);
    direction: rtl;
    display: flex;
    align-items: center;
    gap: 10px;
    max-width: 92%;
    padding: 8px 10px;
    border-radius: 12px;
    background: rgba(13, 17, 23, .94);
    border: 1px solid rgba(1, 166, 212, .7);
    box-shadow: 0 14px 34px rgba(0, 0, 0, .6), 0 0 22px rgba(1, 166, 212, .35);
    pointer-events: none;
    /* Two rhythms on purpose: the drift is the card's own, the glow is in step
       with the markers on the windows, so the pair reads as one question. */
    animation: stv_holdfloat 2.6s ease-in-out infinite,
               stv_holdflash 1.7s ease-in-out infinite;
}
/* The centring translate is repeated in every frame - an animated transform
   replaces the whole property, it does not add to it. */
@keyframes stv_holdfloat {
    0%, 100% { transform: translate(-50%, 0); }
    50%      { transform: translate(-50%, -5px); }
}
/* Same trap as the transform above: box-shadow is ONE property, so the drop
   shadow has to be restated in both frames or it vanishes while the glow runs. */
@keyframes stv_holdflash {
    0%, 100% {
        border-color: rgba(1, 166, 212, .55);
        box-shadow: 0 14px 34px rgba(0, 0, 0, .6), 0 0 18px rgba(1, 166, 212, .28);
    }
    50% {
        border-color: rgba(1, 166, 212, 1);
        box-shadow: 0 14px 34px rgba(0, 0, 0, .6), 0 0 34px rgba(1, 166, 212, .8);
    }
}
/* The stage in the held card: the logo underneath, the live picture over it.
   The logo is what shows while the stream opens and what stays if it never
   does, so the video fades in rather than being there from the start. */
#stv_wrap .stv_hold_v {
    flex: 0 0 auto;
    position: relative;
    width: 128px;
    height: 72px;
    border-radius: 8px;
    overflow: hidden;
    background: #0d1117 center/contain no-repeat;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 11px;
    color: rgba(255, 255, 255, .4);
}
#stv_wrap .stv_hold_v.stv_haslogo { background-color: #fff; }
#stv_wrap .stv_hold_v video {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    background: #000;
    opacity: 0;
    transition: opacity .3s;
}
#stv_wrap .stv_hold_v video.stv_on { opacity: 1; }
#stv_wrap .stv_hold_tx { min-width: 0; }
#stv_wrap .stv_hold_nm {
    font-size: 14px;
    font-weight: 700;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
#stv_wrap .stv_hold_h { font-size: 11px; opacity: .75; }
#stv_wrap .stv_hold_x {
    flex: 0 0 auto;
    pointer-events: auto;
    border: 0;
    border-radius: 8px;
    width: 30px;
    height: 28px;
    color: #fff;
    background: rgba(255, 255, 255, .12);
    cursor: pointer;
}
#stv_wrap .stv_hold_x:hover { background: rgba(224, 38, 60, .85); }
@media (max-width: 600px) {
    #stv_wrap .stv_hold_v { width: 96px; height: 54px; }
    #stv_wrap .stv_hold_nm { font-size: 13px; }
}
#stv_wrap .stv_tile video,
#stv_wrap .stv_tile iframe {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: 0;
    background: #000;
    display: block;
}
#stv_wrap .stv_tile video { object-fit: contain; }
#stv_wrap .stv_tile.stv_fit_cover video { object-fit: cover; }
#stv_wrap .stv_tile.stv_fit_fill video { object-fit: fill; }

/* A station with sound and no picture. The video element is only there to
   carry the audio, so it is faded out rather than removed - hiding it outright
   makes some browsers drop the stream - and the logo plate below shows. */
#stv_wrap .stv_tile.stv_is_radio video {
    opacity: 0;
}
#stv_wrap .stv_tile .stv_radio_art {
    position: absolute;
    inset: 0;
    display: none;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    padding: 8% 10% 14%;
    box-sizing: border-box;
    background: radial-gradient(circle at 50% 40%, #1b2b33 0%, #000 70%);
}
#stv_wrap .stv_tile.stv_is_radio .stv_radio_art { display: flex; }
#stv_wrap .stv_tile .stv_radio_logo {
    flex: 1 1 auto;
    width: 100%;
    min-height: 0;
    background: center/contain no-repeat;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 34px;
    color: rgba(255, 255, 255, .25);
}
#stv_wrap .stv_tile .stv_radio_nm {
    flex: 0 0 auto;
    max-width: 100%;
    font-size: 12px;
    font-weight: 700;
    color: rgba(255, 255, 255, .62);
    text-align: center;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* The name is already on the bar above; in a small window it is the logo that
   has to be readable, so the caption stands down before the logo does. */
#stv_wrap.stv_mode_grid .stv_tile:not(.stv_maxed) .stv_radio_nm { display: none; }

/* The click catcher. It is what turns "click the picture" into "make this one
   fill the screen"; once the tile IS the screen it steps aside so the stream's
   own controls (and an embedded page) can be used normally. */
#stv_wrap .stv_tile .stv_ov {
    position: absolute;
    inset: 0;
    cursor: zoom-in;
    background: transparent;
}
#stv_wrap .stv_tile.stv_maxed .stv_ov { display: none; }

#stv_wrap .stv_tile .stv_bar {
    position: absolute;
    top: 0;
    inset-inline: 0;
    display: flex;
    align-items: center;
    gap: 2px;
    padding: 4px 6px;
    background: linear-gradient(to bottom, rgba(0, 0, 0, .75), rgba(0, 0, 0, 0));
    opacity: 0;
    transition: opacity .15s;
    /* Above .stv_card (4) and the resize corner (6). A channel that failed to
       start covers the tile with its retry card, and the close button has to
       stay reachable through it - otherwise a dead channel cannot be shut. */
    z-index: 7;
    pointer-events: none;
}
#stv_wrap .stv_tile:hover .stv_bar,
#stv_wrap .stv_tile.stv_touch .stv_bar,
#stv_wrap .stv_tile.stv_maxed .stv_bar { opacity: 1; pointer-events: auto; }
/* The name only. The strip itself stays, because the close button lives in it
   and a window that cannot be closed is worse than one that is labelled. */
#stv_wrap.stv_nobar .stv_tile .stv_nm { display: none; }
/* Nothing on top of the picture survives a resting wall. The selectors are
   spelled out one by one because each has to outweigh the rule it undoes. */
#stv_wrap.stv_rest .stv_tile .stv_bar,
#stv_wrap.stv_rest .stv_tile:hover .stv_bar,
#stv_wrap.stv_rest .stv_tile.stv_touch .stv_bar,
#stv_wrap.stv_rest .stv_tile.stv_maxed .stv_bar,
#stv_wrap.stv_rest .stv_tile .stv_foot,
#stv_wrap.stv_rest .stv_tile:hover .stv_foot,
#stv_wrap.stv_rest .stv_tile.stv_touch .stv_foot,
#stv_wrap.stv_rest .stv_tile.stv_maxed .stv_foot { opacity: 0; pointer-events: none; }
#stv_wrap.stv_rest .stv_tile:hover .stv_epg,
#stv_wrap.stv_rest .stv_tile.stv_touch .stv_epg,
#stv_wrap.stv_rest .stv_tile.stv_maxed .stv_epg { bottom: 0; }
#stv_wrap.stv_rest .stv_tile .stv_ov { cursor: none; }
#stv_wrap .stv_tile .stv_nm {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 12px;
    font-weight: 700;
    text-shadow: 0 1px 3px #000;
    padding-inline-start: 2px;
}
#stv_wrap .stv_tile .stv_tb {
    flex: 0 0 auto;
    border: 0;
    background: rgba(0, 0, 0, .45);
    color: #fff;
    border-radius: 6px;
    width: 34px;
    height: 30px;
    cursor: pointer;
    font-size: 15px;
    line-height: 1;
    padding: 0;
}
#stv_wrap .stv_tile .stv_tb:hover { background: rgba(1, 166, 212, .8); }
#stv_wrap .stv_tile .stv_tb.stv_x:hover { background: rgba(224, 38, 60, .85); }
#stv_wrap .stv_tile .stv_tb.stv_on { background: rgba(1, 166, 212, .9); }
#stv_wrap .stv_tile .stv_tb:disabled { opacity: .35; cursor: default; }
#stv_wrap .stv_tile .stv_tb:disabled:hover { background: rgba(0, 0, 0, .45); }
#stv_wrap .stv_tile .stv_t_rec.stv_rec_on { background: rgba(224, 38, 60, .85); }
#stv_wrap .stv_tile .stv_t_rec.stv_rec_on:hover { background: rgba(224, 38, 60, 1); }
/* The recording, said where the control bar cannot hide it: the bar takes
   itself away after a few still seconds, and a recording running unseen is the
   one thing here that has a cost the viewer does not want to discover later. */
#stv_wrap .stv_tile .stv_recchip {
    position: absolute;
    top: 7px;
    inset-inline-start: 8px;
    z-index: 11;
    display: none;
    padding: 3px 9px;
    border-radius: 20px;
    background: rgba(224, 38, 60, .92);
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .3px;
    pointer-events: none;
}
#stv_wrap .stv_tile.stv_recording .stv_recchip { display: block; animation: stv_recblink 1.8s ease-in-out infinite; }
#stv_wrap .stv_tile.stv_recording .stv_bar { padding-inline-start: 78px; }
@keyframes stv_recblink {
    0%, 100% { opacity: 1; }
    50%      { opacity: .5; }
}
/* A rewound window is a small one's worth of space; drop the timeline before
   the controls that cannot be done any other way. */
#stv_wrap.stv_mode_focus .stv_tile:not(.stv_maxed) .stv_scrubline,
#stv_wrap.stv_mode_focus .stv_tile:not(.stv_maxed) .stv_t_live { display: none !important; }
#stv_wrap .stv_tile .stv_bar button { cursor: pointer; }

/* -- the bottom strip ---------------------------------------------------
   The player controls, away from the name and the close button. */
#stv_wrap .stv_tile .stv_foot {
    position: absolute;
    bottom: 0;
    inset-inline-start: 0;
    inset-inline-end: 0;
    display: flex;
    /* Two lines: the timeline across the whole width, the buttons under it.
       Sharing one row put the timeline shoulder to shoulder with the volume
       slider, so ten controls read as a hedge of boxes with two identical
       sliders in the middle of it. */
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    padding: 4px 6px 6px;
    background: linear-gradient(to top, rgba(0, 0, 0, .78), rgba(0, 0, 0, 0));
    border-bottom-left-radius: 8px;
    border-bottom-right-radius: 8px;
    opacity: 0;
    transition: opacity .15s;
    z-index: 7;
    pointer-events: none;
}
#stv_wrap .stv_tile .stv_ctl {
    display: flex;
    align-items: center;
    /* One row, always. Wrapping was the old answer to eight touch-sized
       controls on a narrow tile, and it made the bar grow a second row over
       the picture the moment a window was touched or hovered - the height of
       the bar changed under the finger. The narrow windows drop the controls
       they have no room for instead (see the container query at the end of
       this file), which keeps the bar one row high at every size. */
    flex-wrap: nowrap;
    justify-content: flex-start;
    gap: 2px;
    min-width: 0;
}
/* Nothing in the bar may push the button at its end past the edge of the
   picture. The row is laid out LTR inside an RTL page, so an overflow is
   pushed out to the LEFT, where there is no scrollbar and no way back - the
   full-screen button simply stops existing. The three that carry a label or a
   slider give their width up first and the plain buttons never shrink, so the
   row is always narrower than the window it sits in. */
#stv_wrap .stv_tile .stv_ctl > * { flex-shrink: 0; }
#stv_wrap .stv_tile .stv_foot .stv_t_live,
#stv_wrap .stv_tile .stv_foot .stv_t_rec {
    flex-shrink: 1;
    min-width: 0;
    overflow: hidden;
}
#stv_wrap .stv_tile:hover .stv_foot,
#stv_wrap .stv_tile.stv_touch .stv_foot,
#stv_wrap .stv_tile.stv_maxed .stv_foot { opacity: 1; pointer-events: auto; }
#stv_wrap .stv_tile .stv_fsp { flex: 1 1 auto; }

/* A dark chip behind every one of ten icons is what made the bar look busy.
   The gradient already separates them from the picture, so the buttons are
   flat and only the one under the pointer is tinted. Every state that paints
   a background of its own is re-stated here, one class deeper, to outrank it. */
#stv_wrap .stv_tile .stv_foot .stv_tb { background: transparent; }
#stv_wrap .stv_tile .stv_foot .stv_tb:hover { background: rgba(255, 255, 255, .18); }
#stv_wrap .stv_tile .stv_foot .stv_tb:disabled,
#stv_wrap .stv_tile .stv_foot .stv_tb:disabled:hover { background: transparent; }
#stv_wrap .stv_tile .stv_foot .stv_t_pin.stv_on { background: rgba(1, 166, 212, .9); }
#stv_wrap .stv_tile .stv_foot .stv_t_rec.stv_rec_on { background: rgba(224, 38, 60, .9); }

/* Back to live. A pill carrying how far back the picture is, because a bare
   red circle beside the record button was two meanings drawn the same way.
   The countdown is forced LTR - in an RTL bar "-1:04" comes out as "1:04-". */
#stv_wrap .stv_tile .stv_foot .stv_t_live {
    width: auto;
    padding: 0 10px;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    border: 1px solid rgba(255, 255, 255, .3);
    border-radius: 20px;
    font-size: 11px;
    font-weight: 700;
}
#stv_wrap .stv_tile .stv_foot .stv_t_live:hover {
    background: rgba(224, 38, 60, .9);
    border-color: transparent;
}
#stv_wrap .stv_tile .stv_livedot {
    flex: 0 0 auto;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: #e0263c;
}
#stv_wrap .stv_tile .stv_livetx { direction: ltr; white-space: nowrap; }

/* Recording, labelled for the same reason: a bare circle among nine other
   icons gave no hint that this is the one that writes a file. */
#stv_wrap .stv_tile .stv_foot .stv_t_rec {
    width: auto;
    padding: 0 9px;
    display: inline-flex;
    align-items: center;
    gap: 5px;
}
#stv_wrap .stv_tile .stv_rectx {
    direction: ltr;
    white-space: nowrap;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .5px;
}

/* The volume slider only unrolls once there is room and a pointer on the
   tile - on a four-up grid it would be wider than the picture. The explicit
   LTR is not cosmetic: the wall is an RTL document, and a range input in RTL
   puts its loudest end on the left. */
#stv_wrap .stv_tile .stv_vol {
    flex: 0 1 auto;
    width: 0;
    min-width: 0;
    opacity: 0;
    margin: 0;
    height: 3px;
    direction: ltr;
    accent-color: #01a6d4;
    cursor: pointer;
    transition: width .15s, opacity .15s;
    vertical-align: middle;
}
#stv_wrap .stv_tile:hover .stv_vol,
#stv_wrap .stv_tile.stv_touch .stv_vol,
#stv_wrap .stv_tile.stv_maxed .stv_vol { width: 62px; opacity: 1; }
#stv_wrap .stv_tile .stv_tb.stv_act { color: #6ddcff; }

/* -- the timeline -------------------------------------------------------
   Drag to move through what the stream still holds. It has the whole width of
   the window on a line of its own, above the buttons - the way every player
   draws it, and the only way it cannot be mistaken for the volume slider. */
#stv_wrap .stv_tile .stv_scrubline {
    display: flex;
    align-items: center;
    padding: 0 2px 1px;
}
#stv_wrap .stv_tile .stv_scrub {
    flex: 1 1 auto;
    width: 100%;
    min-width: 0;
    margin: 0;
    height: 4px;
    direction: ltr;
    accent-color: #01a6d4;
    cursor: pointer;
    vertical-align: middle;
}

/* A paused live picture just stops moving, which looks like a dead stream, so
   it gets a play button instead - the picture resumes when it is clicked. */
#stv_wrap .stv_tile .stv_pausemark {
    position: absolute;
    top: 50%;
    /* Physical left, not inset-inline-start: transform is not direction-aware,
       so pairing the two puts the mark half a width off centre. */
    left: 50%;
    transform: translate(-50%, -50%);
    width: 72px;
    height: 72px;
    border-radius: 50%;
    display: none;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, .55);
    border: 2px solid rgba(255, 255, 255, .8);
    color: #fff;
    font-size: 28px;
    z-index: 6;
    pointer-events: none;
    transition: background .15s, transform .15s, border-color .15s;
}
/* A triangle in a circle reads as off-centre unless it is nudged along. */
#stv_wrap .stv_tile .stv_pausemark i { transform: translateX(3px); }
#stv_wrap .stv_tile.stv_paused .stv_pausemark {
    display: flex;
    /* Only clickable while it is showing, so it never steals the click that
       enlarges a playing window. */
    pointer-events: auto;
    cursor: pointer;
}
#stv_wrap .stv_tile.stv_paused .stv_pausemark:hover {
    background: rgba(1, 166, 212, .8);
    border-color: #fff;
    transform: translate(-50%, -50%) scale(1.06);
}
#stv_wrap .stv_tile.stv_paused .stv_ov { cursor: pointer; }

/* -- what is on now ------------------------------------------------------
   Sits just above the control bar and only appears once the guide actually
   has something for this channel. */
#stv_wrap .stv_tile .stv_epg {
    position: absolute;
    inset-inline: 0;
    bottom: 0;
    display: none;
    padding: 4px 8px 6px;
    background: linear-gradient(to top, rgba(0, 0, 0, .62), rgba(0, 0, 0, 0));
    z-index: 6;
    pointer-events: none;
    transition: bottom .15s;
}
/* The control bar only appears on hover, so the strip lives at the bottom
   edge until there is something under it to make room for. */
#stv_wrap .stv_tile:hover .stv_epg,
#stv_wrap .stv_tile.stv_touch .stv_epg,
#stv_wrap .stv_tile.stv_maxed .stv_epg { bottom: 46px; }
#stv_wrap .stv_tile .stv_epg.stv_epg_on { display: block; }
#stv_wrap .stv_tile .stv_epg_t {
    font-size: 11px;
    line-height: 1.35;
    color: #fff;
    text-shadow: 0 1px 2px rgba(0, 0, 0, .9);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
#stv_wrap .stv_tile .stv_epg_pr {
    height: 3px;
    margin-top: 4px;
    border-radius: 2px;
    background: rgba(255, 255, 255, .25);
    overflow: hidden;
}
#stv_wrap .stv_tile .stv_epg_fill { height: 100%; width: 0; background: #01a6d4; }

/* On a crowded grid the strip is more clutter than information. */
#stv_wrap.stv_mode_grid .stv_tile:not(.stv_maxed) .stv_epg_t { font-size: 10px; }

/* -- picking a window up ------------------------------------------------
   The whole picture is the handle. It may not take the browser's vertical
   panning away, though: on a phone the wall is a tall scrolling column and
   the tiles cover all of it, so opting out of pan-y left no way to reach the
   windows further down. Sideways movement is ours, up and down is the
   browser's, and once a hold has actually begun a drag the move handler
   cancels the scroll outright. */
#stv_wrap .stv_tile { touch-action: pan-y; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
#stv_wrap .stv_tile video { -webkit-touch-callout: none; }
#stv_wrap.stv_dragging .stv_tile { cursor: grabbing; }
/* The hold, while it is being counted. Without this a finger resting on a
   picture looks exactly like a finger doing nothing, and the gesture that
   swaps two windows is undiscoverable. An outline, not an inset shadow: the
   video covers the tile's own background and would hide the shadow. */
#stv_wrap .stv_tile.stv_holding {
    outline: 2px solid rgba(1, 166, 212, .9);
    outline-offset: -3px;
    animation: stv_hold .42s ease-out forwards;
}
@keyframes stv_hold {
    from { transform: scale(1); outline-color: rgba(1, 166, 212, 0); }
    to   { transform: scale(.965); outline-color: rgba(1, 166, 212, .95); }
}
/* Picked up. Tilted and raised rather than faded: the window has to read as
   held ABOVE the wall, and a transparent one reads as switched off. The tilt
   is what makes the difference visible on a picture that is still moving. */
#stv_wrap .stv_tile.stv_lifted {
    outline: 2px solid rgba(1, 166, 212, .9);
    outline-offset: -3px;
    transform: scale(1.035) rotate(-2.2deg);
    box-shadow: 0 18px 38px rgba(0, 0, 0, .55), 0 0 0 1px rgba(1, 166, 212, .5);
    /* Above every other window, and above the bar that sits on the wall. */
    z-index: 30;
    /* The window is under the cursor for the whole drag, so elementFromPoint
       would only ever find the window being carried. */
    pointer-events: none;
    /* It has to sit exactly where the finger is, not ease towards it. */
    transition: none;
    backface-visibility: hidden;
}
/* No highlight on the window being displaced: it moves. The wall re-flows
   under the pointer during the drag, so the gap closing IS the feedback, and
   the outline it used to get was both redundant and - drawn with a very large
   inset spread - smeared across the wall as the target changed. */

/* -- unmute over the picture --------------------------------------------
   The speaker in the foot bar is 22px and hides itself with the rest of the
   bar, so the only reliable way to hear a window was to find that button.
   Hovering a silent window offers the switch at the size of the window
   instead. Hover only: on a touch screen there is no hover, and tapping the
   picture already enlarges it, which unmutes it. */
#stv_wrap .stv_tile .stv_unmute {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 62px;
    height: 62px;
    max-width: 38%;
    max-height: 38%;
    border-radius: 50%;
    display: none;
    align-items: center;
    justify-content: center;
    background: rgba(9, 14, 20, .62);
    border: 2px solid rgba(255, 255, 255, .5);
    color: #fff;
    font-size: 24px;
    cursor: pointer;
    z-index: 7;
    pointer-events: none;
    transition: background .15s ease, border-color .15s ease, transform .15s ease;
}
@media (hover: hover) and (pointer: fine) {
    /* Not while the window is being carried, and not over a paused picture -
       that one already has a mark of its own in the same place. */
    #stv_wrap:not(.stv_dragging) .stv_tile.stv_ismuted:not(.stv_paused):hover .stv_unmute {
        display: flex;
        pointer-events: auto;
    }
}
/* A finger has no hover to offer it with, so on touch it simply stands there
   for as long as the window is silent. Tapping the picture does enlarge and
   unmute, but that is one window's sound at the cost of the wall - which is
   the opposite of what somebody watching a split screen is asking for. */
@media (hover: none) {
    #stv_wrap:not(.stv_dragging):not(.stv_rest) .stv_tile.stv_ismuted:not(.stv_paused) .stv_unmute {
        display: flex;
        pointer-events: auto;
    }
}
#stv_wrap .stv_tile .stv_unmute:hover {
    background: rgba(1, 166, 212, .9);
    border-color: #fff;
    transform: translate(-50%, -50%) scale(1.08);
}

/* -- the resize corner --------------------------------------------------
   Dragging this grows the window over its neighbours. It is only meaningful
   on the grid, so the other two modes hide it. The hatching is what tells
   people it can be pulled at all. */
#stv_wrap .stv_tile .stv_grip_se {
    position: absolute;
    right: 0;
    bottom: 0;
    width: 22px;
    height: 22px;
    /* Above the control bar (7). Both only appear on hover and the bar covers
       the whole bottom strip, so a lower grip can never be grabbed at all. */
    z-index: 9;
    cursor: nwse-resize;
    touch-action: none;
    opacity: 0;
    transition: opacity .15s;
    background:
        repeating-linear-gradient(135deg,
            rgba(255, 255, 255, .85) 0 1.5px,
            transparent 1.5px 4px),
        linear-gradient(135deg, transparent 45%, rgba(1, 166, 212, .85) 45%);
    border-bottom-right-radius: 8px;
    -webkit-mask-image: linear-gradient(135deg, transparent 48%, #000 48%);
    mask-image: linear-gradient(135deg, transparent 48%, #000 48%);
}
#stv_wrap .stv_tile:hover .stv_grip_se,
#stv_wrap .stv_tile.stv_touch .stv_grip_se { opacity: 1; }
#stv_wrap.stv_mode_focus .stv_grip_se { display: none; }

/* FontAwesome 4 has no slashed speaker, so the off state draws its own line -
   a bare speaker on its own reads as "sound", not as "sound is off". */
#stv_wrap .stv_muted { position: relative; }
#stv_wrap .stv_muted::after {
    content: '';
    position: absolute;
    inset-block-start: 50%;
    inset-inline-start: 50%;
    width: 74%;
    height: 2px;
    background: #ff6b7a;
    border-radius: 2px;
    transform: translate(-50%, -50%) rotate(-45deg);
    box-shadow: 0 0 0 1px rgba(0, 0, 0, .7);
    pointer-events: none;
}
/* Keep the last control clear of the corner the grip sits in. Only the grid
   has a grip, so only the grid gives up the space. */
#stv_wrap.stv_mode_grid .stv_tile .stv_foot { padding-inline-end: 30px; }
/* While a window is being resized the pointer must not be captured by the
   video or an embedded page, or the drag stops the moment it crosses one. */
#stv_wrap.stv_resizing .stv_tile iframe,
#stv_wrap.stv_resizing .stv_tile video,
#stv_wrap.stv_resizing .stv_tile .stv_ov { pointer-events: none; }
#stv_wrap.stv_resizing { user-select: none; }

/* -- spotlighted tile --------------------------------------------------- */
#stv_wrap .stv_tile.stv_maxed .stv_ov { cursor: default; }

/* -- empty slot --------------------------------------------------------- */
#stv_wrap .stv_slot {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    /* The wall has no gaps of its own any more - see .stv_wall. */
    margin: 2px;
    border: 1px dashed rgba(255, 255, 255, .16);
    border-radius: 8px;
    color: rgba(255, 255, 255, .42);
    font-size: 13px;
    cursor: pointer;
    background: rgba(255, 255, 255, .015);
    min-width: 0;
    min-height: 0;
    text-align: center;
    padding: 6px;
    transition: border-color .15s, color .15s, background .15s;
}
/* Drawn, not typed: a plus in the page font is a small dash next to the
   label, and this has to read as the thing you press. */
#stv_wrap .stv_slot_plus {
    display: block;
    width: 34px;
    height: 34px;
    position: relative;
    /* The glyph itself is replaced by two bars, so it must not be drawn. */
    font-size: 0;
    line-height: 0;
    flex: 0 0 auto;
}
#stv_wrap .stv_slot_plus::before,
#stv_wrap .stv_slot_plus::after {
    content: '';
    position: absolute;
    inset-block-start: 50%;
    inset-inline-start: 50%;
    background: currentColor;
    border-radius: 2px;
    transform: translate(-50%, -50%);
}
#stv_wrap .stv_slot_plus::before { width: 100%; height: 2px; }
#stv_wrap .stv_slot_plus::after  { width: 2px; height: 100%; }
/* The slot is often only a few dozen pixels tall on a phone; below that the
   plus and the word together look like a smudge, so the word goes. */
@media (max-width: 560px) {
    #stv_wrap .stv_slot { gap: 6px; }
    #stv_wrap .stv_slot_plus { width: 24px; height: 24px; }
    #stv_wrap .stv_slot_txt { display: none; }
}
#stv_wrap .stv_slot:hover { border-color: #01a6d4; color: #01a6d4; background: rgba(1, 166, 212, .07); }

/* Not one channel reaches this viewer. It spans whatever the grid is. */
#stv_wrap .stv_none {
    grid-column: 1 / -1;
    grid-row: 1 / -1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    text-align: center;
    color: #8c9aa8;
    font-size: 14px;
}

/* The catalogue is fetched on the first open, so there is a moment with an
   empty wall and nothing to explain it. */
#stv_wrap.stv_loading .stv_wall::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 30px;
    height: 30px;
    margin: -15px 0 0 -15px;
    border-radius: 50%;
    border: 3px solid rgba(255, 255, 255, .16);
    border-top-color: #01a6d4;
    animation: stv_sp 0.9s linear infinite;
    z-index: 3;
}
#stv_wrap.stv_loading .stv_wall { position: relative; }

/* -- play card + failure banner ----------------------------------------- */
#stv_wrap .stv_card {
    position: absolute;
    inset: 0;
    z-index: 4;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 10px;
    text-align: center;
    background: rgba(5, 8, 12, .78);
    cursor: pointer;
    /* The failure card must not swallow the presses meant for the title bar
       above it, so it starts below the bar rather than at the very top. */
    padding-top: 34px;
}
#stv_wrap .stv_card .stv_cti { font-size: 13px; font-weight: 700; }
#stv_wrap .stv_card .stv_cnote { font-size: 12px; opacity: .75; max-width: 90%; }
/* Sits under the spinner while the chain is being walked. Wraps, because on a
   thumbnail in the spotlight rail the sentence is wider than the window. */
#stv_wrap .stv_card .stv_cwait {
    font-size: 12px;
    line-height: 1.45;
    max-width: 92%;
    color: #cfe6f2;
    opacity: .85;
}
#stv_wrap .stv_card .stv_cgo {
    border: 1px solid #01a6d4;
    background: rgba(1, 166, 212, .18);
    color: #cfefff;
    border-radius: 20px;
    padding: 7px 18px;
    font-size: 13px;
    cursor: pointer;
}
#stv_wrap .stv_card.stv_bad .stv_cti { color: #ff8a97; }
#stv_wrap .stv_spin {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    border: 3px solid rgba(255, 255, 255, .18);
    border-top-color: #01a6d4;
    animation: stv_sp 0.9s linear infinite;
}
@keyframes stv_sp { to { transform: rotate(360deg); } }

/* -- channel picker ----------------------------------------------------- */
#stv_wrap .stv_picker {
    flex: 0 0 274px;
    display: none;
    flex-direction: column;
    background: #151c25;
    border-inline-start: 1px solid rgba(255, 255, 255, .09);
    min-height: 0;
}
#stv_wrap.stv_pick_on .stv_picker { display: flex; animation: stv_pick_in .2s ease; }
/* The list slides in and out from the edge it is docked to. `--stv-slide` is
   flipped for right-to-left pages, where that edge is the other one. */
#stv_wrap .stv_picker { --stv-slide: 22px; }
[dir="rtl"] #stv_wrap .stv_picker { --stv-slide: -22px; }
#stv_wrap.stv_pick_on.stv_pick_out .stv_picker { animation: stv_pick_out .2s ease forwards; }
@keyframes stv_pick_in {
    from { opacity: 0; transform: translateX(var(--stv-slide, 22px)); }
    to { opacity: 1; transform: none; }
}
@keyframes stv_pick_out {
    from { opacity: 1; transform: none; }
    to { opacity: 0; transform: translateX(var(--stv-slide, 22px)); }
}
#stv_wrap .stv_pick_hd { flex: 0 0 auto; padding: 8px; display: flex; gap: 6px; align-items: center; }
#stv_wrap .stv_pick_hd input {
    width: 100%;
    min-width: 0;
    padding: 8px 10px;
    border-radius: 8px;
    border: 1px solid rgba(255, 255, 255, .14);
    background: #0d1117;
    color: #e8eef5;
    font-size: 13px;
}
/* In compact the list deliberately ignores clicks elsewhere on the page - it
   is the channel changer sitting beside a small window and it decides for
   itself when to move - so it needs a way out of its own. */
#stv_wrap .stv_pick_x {
    flex: 0 0 auto;
    width: 30px;
    height: 32px;
    border: 0;
    background: transparent;
    color: rgba(255, 255, 255, .6);
    font-size: 15px;
    border-radius: 8px;
    cursor: pointer;
}
#stv_wrap .stv_pick_x:hover { background: rgba(255, 255, 255, .09); color: #fff; }
#stv_wrap .stv_pick_list {
    flex: 1 1 auto;
    /* A floor the filter rows above cannot push through. */
    min-height: 132px;
    overflow: auto;
    padding: 0 8px 10px;
}

/* -- category chips ------------------------------------------------------
   Built from the categories the channels actually carry, so this row is the
   country / genre filter for the side list. */
#stv_wrap .stv_cats {
    /* Shrinks before the list does, and scrolls once it runs out of room.
       There are TWO of these rows - countries and genres - so a share of the
       panel each meant they could take better than half of it between them and
       leave a catalogue of hundreds showing three channels. Two rows of chips
       is the cap; the rest is one scroll away. */
    flex: 0 1 auto;
    min-height: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
    padding: 0 8px 8px;
    max-height: 66px;
    overflow: auto;
}
#stv_wrap .stv_cat {
    font-size: 11px;
    line-height: 1;
    padding: 6px 10px;
    border-radius: 14px;
    border: 1px solid rgba(255, 255, 255, .14);
    background: rgba(255, 255, 255, .04);
    color: rgba(255, 255, 255, .78);
    cursor: pointer;
    white-space: nowrap;
}
#stv_wrap .stv_cat:hover { border-color: rgba(1, 166, 212, .6); color: #e8eef5; }
#stv_wrap .stv_cat.stv_cat_on {
    background: rgba(1, 166, 212, .2);
    border-color: #01a6d4;
    color: #cdeffb;
    font-weight: 700;
}
/* The manager can take the filter rows away and leave the plain search box. */
#stv_wrap.stv_nochips .stv_cats { display: none; }

/* Television / radio. A tab strip rather than another row of chips, because it
   divides the catalogue in two rather than narrowing it: everything below,
   including the country and genre chips, is showing one side or the other. */
#stv_wrap .stv_kinds {
    flex: 0 0 auto;
    display: none;
    gap: 2px;
    padding: 0 8px;
    margin-bottom: 8px;
    border-bottom: 1px solid rgba(255, 255, 255, .1);
}
#stv_wrap .stv_kind {
    flex: 1 1 0;
    font-size: 12px;
    line-height: 1;
    padding: 9px 6px;
    border: 0;
    border-bottom: 2px solid transparent;
    background: none;
    color: rgba(255, 255, 255, .6);
    cursor: pointer;
    white-space: nowrap;
    font-family: inherit;
}
#stv_wrap .stv_kind:hover { color: #e8eef5; }
#stv_wrap .stv_kind i { margin-inline-end: 5px; font-size: 11px; opacity: .8; }
#stv_wrap .stv_kind.stv_kind_on {
    color: #cdeffb;
    font-weight: 700;
    border-bottom-color: #01a6d4;
}
#stv_wrap .stv_kind.stv_kind_on i { opacity: 1; }
#stv_wrap.stv_mode_compact .stv_kinds { padding: 0 5px; margin-bottom: 5px; }
#stv_wrap .stv_grp {
    font-size: 11px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: .5px;
    opacity: .55;
    padding: 10px 4px 4px;
}
#stv_wrap .stv_grp_t {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 9px 8px;
    margin-top: 4px;
    border-radius: 8px;
    cursor: pointer;
    opacity: .8;
    background: rgba(255, 255, 255, .04);
    border: 1px solid rgba(255, 255, 255, .07);
    user-select: none;
}
#stv_wrap .stv_grp_t:hover { opacity: 1; background: rgba(255, 255, 255, .08); }
#stv_wrap .stv_grp_open { opacity: 1; border-color: rgba(1, 166, 212, .45); }
#stv_wrap .stv_grp_a { font-size: 10px; width: 10px; flex: 0 0 auto; }
#stv_wrap .stv_grp_n { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#stv_wrap .stv_grp_c {
    flex: 0 0 auto;
    font-size: 10px;
    padding: 1px 7px;
    border-radius: 10px;
    background: rgba(255, 255, 255, .1);
}
/* A genre inside a category. Quieter than the category above it and indented,
   so the nesting is legible without a second collapsing control. */
#stv_wrap .stv_sub_t {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 8px 0 2px;
    padding: 3px 8px 3px 14px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .4px;
    text-transform: uppercase;
    color: #9fd8ec;
    opacity: .75;
    border-inline-start: 2px solid rgba(1, 166, 212, .45);
}
#stv_wrap .stv_sub_n { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#stv_wrap .stv_sub_c { flex: 0 0 auto; opacity: .7; }
#stv_wrap .stv_pi {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 6px;
    border-radius: 8px;
    cursor: pointer;
    border: 1px solid transparent;
}
#stv_wrap .stv_pi:hover { background: rgba(255, 255, 255, .06); border-color: rgba(1, 166, 212, .5); }
#stv_wrap .stv_pi.stv_pi_on { background: rgba(1, 166, 212, .13); border-color: rgba(1, 166, 212, .55); }
/* The channel that is waiting for a window, and the way out of waiting. */
#stv_wrap .stv_pi.stv_pi_wait { background: rgba(1, 166, 212, .22); border-color: #01a6d4; }
#stv_wrap .stv_placebar {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 6px;
    padding: 8px 9px;
    border-radius: 8px;
    background: rgba(1, 166, 212, .16);
    border: 1px solid rgba(1, 166, 212, .5);
    font-size: 12px;
    line-height: 1.35;
}
#stv_wrap .stv_place_t { flex: 1 1 auto; min-width: 0; }
#stv_wrap .stv_place_x {
    flex: 0 0 auto;
    border: 0;
    border-radius: 6px;
    padding: 5px 10px;
    font-size: 12px;
    color: #fff;
    background: rgba(255, 255, 255, .16);
    cursor: pointer;
}
#stv_wrap .stv_place_x:hover { background: rgba(224, 38, 60, .8); }
#stv_wrap .stv_pi .stv_pth {
    flex: 0 0 auto;
    width: 46px;
    height: 30px;
    border-radius: 5px;
    background: #0d1117 center/cover no-repeat;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 10px;
    color: rgba(255, 255, 255, .4);
    overflow: hidden;
}

/* Station logos are transparent files drawn in the station's own colours, and
   a good half of them are drawn in black - on the dark wall those came out as
   an empty plate. White is the background they are all designed against.
   Scoped to stv_haslogo so the no-logo fallbacks (the "TV" / music-note plates
   and the name-only strip button) keep their dark plate and stay readable. */
#stv_wrap .stv_pi .stv_pth.stv_haslogo {
    background-color: #fff;
    background-size: contain;
}
#stv_wrap .stv_strip_b.stv_haslogo {
    background-color: #fff;
    border-color: rgba(255, 255, 255, .3);
}
/* Selection still reads: it is carried by the border and the inset ring, not
   by the plate colour. */
#stv_wrap .stv_strip_b.stv_haslogo.stv_strip_on { border-color: #01a6d4; }
#stv_wrap .stv_tile .stv_radio_logo.stv_haslogo {
    background-color: #fff;
    border-radius: 10px;
    padding: 10px;
    box-sizing: border-box;
}

#stv_wrap .stv_pi .stv_pnm {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 13px;
}
#stv_wrap .stv_pi .stv_pon { flex: 0 0 auto; font-size: 10px; color: #01a6d4; }

/* The i on a channel that has a paragraph behind it, and the paragraph. */
#stv_wrap .stv_pinfo {
    flex: 0 0 auto;
    width: 22px;
    height: 22px;
    border: 1px solid rgba(255, 255, 255, .22);
    border-radius: 50%;
    background: transparent;
    color: #cfe6f2;
    font-size: 11px;
    line-height: 1;
    padding: 0;
    cursor: pointer;
}
#stv_wrap .stv_pinfo:hover { background: rgba(1, 166, 212, .3); border-color: #01a6d4; }
#stv_wrap .stv_pab {
    display: none;
    margin: -2px 6px 6px;
    padding: 8px 10px;
    border-radius: 8px;
    border-inline-start: 3px solid #01a6d4;
    background: rgba(255, 255, 255, .06);
    font-size: 12px;
    line-height: 1.5;
    color: rgba(255, 255, 255, .82);
    text-align: start;
}
#stv_wrap .stv_pab.stv_pab_on { display: block; }

/* The same paragraph over a window. Only the channel's own window is ever
   asked for it, so it may cover the whole plate. */
#stv_wrap .stv_tile .stv_about {
    display: none;
    position: absolute;
    inset: 0;
    z-index: 6;
    padding: 14px 16px;
    background: rgba(6, 12, 18, .93);
    overflow: auto;
    cursor: pointer;
}
#stv_wrap .stv_tile.stv_about_on .stv_about { display: block; }
#stv_wrap .stv_tile .stv_about_h {
    font-size: 14px;
    font-weight: 800;
    color: #01a6d4;
    margin-bottom: 7px;
    text-align: start;
}
#stv_wrap .stv_tile .stv_about_b {
    font-size: 13px;
    line-height: 1.6;
    color: rgba(255, 255, 255, .88);
    text-align: start;
}
/* Who gets the i. A radio window is a logo and a name, so it always needs one.
   A grid of television does not - the picture is the description, and eight
   little i buttons are eight pieces of clutter. The compact window is the
   exception: one small picture, no guide beside it and no other window to
   compare against, so whatever it is playing may be asked about. */
#stv_wrap .stv_tile:not(.stv_is_radio) .stv_t_info { display: none; }
#stv_wrap.stv_mode_compact .stv_tile .stv_t_info { display: inline-block; }
#stv_wrap.stv_mode_compact .stv_tile .stv_about { padding: 10px 12px; }
#stv_wrap.stv_mode_compact .stv_tile .stv_about_h { font-size: 12px; margin-bottom: 5px; }
#stv_wrap.stv_mode_compact .stv_tile .stv_about_b { font-size: 11.5px; line-height: 1.5; }

/* -- toast -------------------------------------------------------------- */
#stv_wrap .stv_toast {
    position: absolute;
    bottom: 14px;
    inset-inline-start: 50%;
    transform: translateX(50%);
    background: rgba(0, 0, 0, .86);
    border: 1px solid rgba(255, 255, 255, .16);
    border-radius: 20px;
    padding: 8px 18px;
    font-size: 13px;
    z-index: 20;
    pointer-events: none;
    opacity: 0;
    transition: opacity .2s;
}
#stv_wrap .stv_toast.stv_show { opacity: 1; }

/* -- view mode switch ---------------------------------------------------
   One button that opens a menu. The menu is the only place the mode names are
   spelled out, so it has to sit above the wall and the picker both. */
#stv_wrap .stv_views { position: relative; flex: 0 0 auto; }
#stv_wrap .stv_vbtn { display: inline-flex; align-items: center; gap: 4px; }
#stv_wrap .stv_vbtn .stv_caret { font-size: 10px; opacity: .8; }
#stv_wrap .stv_vmenu {
    display: none;
    position: absolute;
    top: calc(100% + 4px);
    inset-inline-start: 0;
    min-width: 172px;
    padding: 4px;
    background: #12181f;
    border: 1px solid rgba(255, 255, 255, .14);
    border-radius: 8px;
    box-shadow: 0 14px 34px rgba(0, 0, 0, .55);
    z-index: 30;
}
#stv_wrap.stv_vmenu_on .stv_vmenu { display: block; }
/* The compact window clips its own contents, or its rounded corners would not
   hold. The menu has to be let out of it while it is open. */
#stv_wrap.stv_mode_compact.stv_vmenu_on { overflow: visible; }
#stv_wrap .stv_vmenu .stv_vb {
    display: flex;
    align-items: center;
    gap: 9px;
    width: 100%;
    padding: 8px 10px;
    background: none;
    border: 0;
    border-radius: 6px;
    color: #cfd8e3;
    font: inherit;
    font-size: 13px;
    text-align: start;
    cursor: pointer;
}
#stv_wrap .stv_vmenu .stv_vb i { width: 16px; text-align: center; flex: 0 0 auto; }
#stv_wrap .stv_vmenu .stv_vb:hover { background: rgba(255, 255, 255, .08); color: #fff; }
#stv_wrap .stv_vmenu .stv_vb.stv_vb_on { background: rgba(1, 166, 212, .3); color: #fff; }
#stv_wrap .stv_hbtn.stv_hbtn_on { background: rgba(1, 166, 212, .3); color: #fff; }
/* -- launcher buttons ---------------------------------------------------
   Minimising hands the wall back to the addon's icon in the chat's tool bar,
   like every other addon. .stv_live is the "still playing behind the chat"
   marker the client puts on whichever launchers exist on this page. */
#split_tv_btn, #stv_plus_btn, #stv_user_btn, #stv_foot_btn { cursor: pointer; }
#split_tv_btn, #split_tv_locked_btn, #stv_plus_btn, #stv_user_btn,
#stv_foot_btn { position: relative; }
/* The addon's own artwork rather than fa-tv: a set with an aerial and a split
   screen reads as this addon, where the flat glyph reads as a monitor. The
   footer's own rule sizes it with the rest of the row; this is the fallback
   where :has() is missing. */
#stv_foot_btn img { width: auto; height: 20px; vertical-align: middle; }
#stv_foot_btn p { font-size: 9px; font-weight: 500; }
#split_tv_btn.stv_live,
#split_tv_locked_btn.stv_live,
#stv_plus_btn.stv_live,
#stv_user_btn.stv_live,
#stv_foot_btn.stv_live { color: #01a6d4; }
#split_tv_btn.stv_live::after,
#split_tv_locked_btn.stv_live::after,
#stv_plus_btn.stv_live::after,
#stv_user_btn.stv_live::after,
#stv_foot_btn.stv_live::after {
    content: '';
    position: absolute;
    top: 2px;
    inset-inline-end: 2px;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: #01a6d4;
    box-shadow: 0 0 0 2px rgba(1, 166, 212, .25);
    animation: stv_pulse 1.8s ease-in-out infinite;
}
@keyframes stv_pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

/* The wall while it is minimised: its own item in the chat's footer bar, sat
   next to the one the core puts there for a minimised conversation. It exists
   only while something is still playing, so it always carries the live mark. */
#stv_min_icon {
    position: relative;
    cursor: pointer;
    color: #01a6d4;
    text-align: center;
}
#stv_min_icon i { font-size: 20px; line-height: 1; }
#stv_min_icon::after {
    content: '';
    position: absolute;
    top: 2px;
    inset-inline-end: 2px;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: #01a6d4;
    box-shadow: 0 0 0 2px rgba(1, 166, 212, .25);
    animation: stv_pulse 1.8s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
    #stv_min_icon::after { animation: none; }
}
@media (prefers-reduced-motion: reduce) {
    #split_tv_btn.stv_live::after,
    #split_tv_locked_btn.stv_live::after,
    #stv_plus_btn.stv_live::after,
    #stv_user_btn.stv_live::after,
    #stv_foot_btn.stv_live::after { animation: none; }
}

/* The same launcher inside the "you cannot write here" bar that replaces the
   input row for guests and locked rooms - watching is not writing. */
.stv_locked_btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin: 0 6px;
    padding: 5px 12px;
    border-radius: 18px;
    border: 1px solid rgba(1, 166, 212, .5);
    background: rgba(1, 166, 212, .12);
    color: #0176a8;
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
    vertical-align: middle;
}
.stv_locked_btn:hover { background: rgba(1, 166, 212, .22); }

/* The chat behind the wall must not scroll under it while it is open. The
   compact window deliberately does NOT set this - it floats over the chat and
   the chat stays usable underneath, which is the whole point of it. */
html.stv_locked, html.stv_locked body { overflow: hidden; }

/* -- phones --------------------------------------------------------------
   A phone is one column wide however many windows are open, so on the grid
   the wall scrolls instead of squeezing sixteen unreadable thumbnails onto
   one screen. Focus keeps its spotlight but moves the rail underneath, which
   is the only orientation that leaves the big picture watchable. */
@media (max-width: 992px) {
    #stv_wrap .stv_wall {
        grid-template-columns: repeat(min(var(--stv-cols, 3), 2), 1fr);
        grid-template-rows: none;
        grid-auto-rows: minmax(28vw, 1fr);
        overflow-x: hidden;
        overflow-y: auto;
    }
    /* Not a grid here, and that is the whole point.

       A window is 16:9 or it is a picture with black down its sides - but on a
       grid the height of a window is never the window's to decide. The row
       decides; the row is stretched to share the wall out between however many
       rows there are; and a window the viewer once resized still carries an
       inline `grid-row: span 2`, which takes its height out of all the others.
       That is why they came out different heights and cut off mid-picture.

       Wrapping boxes have none of those: the ratio is the only thing setting
       the height, so every window is the same shape, whole, and the wall
       scrolls when they do not all fit. It also drops the inline spans on the
       floor, which is what the grid needed a !important to undo. */
    #stv_wrap.stv_mode_grid .stv_wall {
        display: flex;
        flex-flow: row wrap;
        align-content: flex-start;
    }
    #stv_wrap.stv_mode_grid .stv_tile,
    #stv_wrap.stv_mode_grid .stv_slot {
        flex: 0 0 calc(100% / min(var(--stv-cols, 3), 2));
        aspect-ratio: 16 / 9;
        height: auto;
    }
    /* Its side margins would push it past the row it was measured for. */
    #stv_wrap.stv_mode_grid .stv_slot { margin-inline: 0; }
    #stv_wrap .stv_picker {
        position: absolute;
        inset-block: 0;
        inset-inline-end: 0;
        z-index: 12;
        flex-basis: 82%;
        max-width: 320px;
    }
    /* Touch has no hover, so the controls a viewer needs are always visible. */
    #stv_wrap .stv_tile .stv_bar { opacity: 1; pointer-events: auto; }
    #stv_wrap .stv_tile .stv_foot { opacity: 1; pointer-events: auto; }
    #stv_wrap .stv_tile .stv_epg { bottom: 46px; }
    #stv_wrap .stv_tile .stv_tb { width: 40px; height: 36px; font-size: 17px; }
    #stv_wrap .stv_tile .stv_grip_se { opacity: 1; width: 28px; height: 28px; }
    #stv_wrap.stv_mode_grid .stv_tile .stv_foot { padding-inline-end: 34px; }
}
@media (max-width: 600px) {
    #stv_wrap .stv_wall {
        grid-template-columns: 1fr;
        grid-auto-rows: minmax(46vw, 1fr);
        /* Every pixel of a phone's width belongs to the picture. The vertical
           padding stays - it separates the wall from the header. */
        padding-inline: 0;
    }
    /* One to a row whatever the viewer picked for the desktop: two windows
       side by side on a phone is two pictures too small to watch. */
    #stv_wrap.stv_mode_grid .stv_tile,
    #stv_wrap.stv_mode_grid .stv_slot { flex-basis: 100%; }
    #stv_wrap .stv_ttl { font-size: 13px; }
    #stv_wrap .stv_hbtn { padding: 7px 9px; min-width: 36px; }
    #stv_wrap .stv_hd { gap: 5px; padding: 6px; flex-wrap: wrap; }

    /* Spotlight on a phone: the chosen channel across the top, the rest two to
       a row underneath. One per row would push all but the first off the
       screen; three would be too small to tell apart. */
    #stv_wrap.stv_mode_focus .stv_wall {
        display: flex;
        flex-direction: row;
        flex-wrap: wrap;
        align-content: flex-start;
        overflow-x: hidden;
        overflow-y: auto;
        gap: 4px;
    }
    #stv_wrap.stv_mode_focus .stv_tile.stv_maxed {
        flex: 0 0 100%;
        order: -1 !important;
        height: 42vh;
        min-height: 190px;
    }
    #stv_wrap.stv_mode_focus .stv_tile:not(.stv_maxed) {
        flex: 0 0 calc(50% - 2px);
        height: auto;
        aspect-ratio: 16 / 9;
        min-height: 0;
    }

    /* Compact becomes a sheet across the bottom rather than a tiny window on
       a small screen; the client clamps its box to fit either way. The width is
       the stylesheet's here, so the client takes the height from it (chrome
       plus a 16:9 picture) - anything else is a black band above and below the
       broadcast. Nothing is left to resize, so the grip goes with it.
       Edge to edge: a phone has no width to give away to a margin, and every
       pixel the frame keeps is a pixel off the picture in BOTH directions. The
       side border goes too - it would sit half off the screen anyway. */
    #stv_wrap.stv_mode_compact {
        inset-inline: 0 !important;
        width: auto !important;
        max-width: none;
        border-inline: 0;
        border-radius: 10px;
    }
    #stv_wrap.stv_mode_compact .stv_wgrip { display: none; }

    /* The window is as wide as the phone, so its header can afford real
       buttons: sized off the window's width they came out as a row of 26px
       dots, which is under the size a thumb can aim at. Paid for in height,
       which the client's own fit then gives back to the picture. */
    #stv_wrap.stv_mode_compact .stv_hd { padding: 7px 8px; gap: 6px; }
    #stv_wrap.stv_mode_compact .stv_hbtn {
        min-width: 34px;
        padding: 8px 9px;
        font-size: 14px;
    }
    #stv_wrap.stv_mode_compact .stv_b_cmp .stv_hlab,
    #stv_wrap.stv_mode_compact .stv_b_pick .stv_hlab { font-size: 12px; }

    /* A 16:9 picture on a phone is a window some 270px tall, and a list inside
       it showed two and a half channels out of hundreds. So on a phone the
       list stops being a panel in the window and becomes a sheet over the
       page: `fixed`, so the window's own `overflow: hidden` cannot cut it, and
       measured against the screen rather than against the window.
       Nothing above it is transformed, which is what would otherwise pull a
       fixed box back inside its parent. */
    #stv_wrap.stv_mode_compact:not(.stv_fs) .stv_picker {
        position: fixed;
        /* Sat on the bottom edge rather than spanning the screen: a sheet the
           full height of the phone reads as a page of its own, and it buries
           the window it is picking for. Height, not a second inset, because
           `top: auto` is what keeps it short. */
        inset-block: auto max(4vh, env(safe-area-inset-bottom));
        height: min(58vh, 460px);
        inset-inline: auto 0;
        width: 80%;
        min-width: 200px;
        max-width: 340px;
        border: 1px solid rgba(255, 255, 255, .14);
        border-radius: 14px;
        box-shadow: 0 22px 60px rgba(0, 0, 0, .7);
        overflow: hidden;
    }
    /* Room enough to show the filters and still leave a long list under them. */
    #stv_wrap.stv_mode_compact .stv_cats { max-height: 84px; }
    #stv_wrap.stv_mode_compact .stv_pick_list { min-height: 132px; }
}


/* -- controls sized to the window ---------------------------------------
   The same 34px button is right on a nine-up grid and cramped on a window
   filling half the screen. A container query measures the WINDOW, which is the
   only thing that decides this - the grid count, the mode and the page width
   all reach it only through the window's width. Last in the file so it also
   outweighs the touch sizes set in the narrow-screen block above. */
#stv_wrap .stv_tile { container-type: inline-size; }
/* A window too narrow to hold the whole bar in one row. What goes is chosen,
   not left to the browser: the timeline, the volume slider and the catch-up
   controls all belong to a window being watched, and a thumbnail in the rail
   is not one. Play, sound, pin and full screen stay, which is the whole of
   what a small window is for. `!important` because paintFoot writes `display`
   inline as it turns the catch-up controls on and off. */
@container (max-width: 340px) {
    #stv_wrap .stv_tile .stv_scrubline,
    #stv_wrap .stv_tile .stv_vol,
    #stv_wrap .stv_tile .stv_t_live,
    #stv_wrap .stv_tile .stv_t_rec,
    #stv_wrap .stv_tile .stv_t_cast { display: none !important; }
    #stv_wrap .stv_tile .stv_ctl { gap: 1px; }
    #stv_wrap .stv_tile .stv_foot { padding: 5px 6px; }
    #stv_wrap .stv_tile .stv_tb { width: 32px; height: 30px; font-size: 15px; }
    #stv_wrap .stv_mark_c { padding: 6px 10px; gap: 5px; font-size: 12px; }
    #stv_wrap .stv_mark_p { font-size: 16px; }
}
@container (max-width: 270px) {
    #stv_wrap .stv_tile .stv_tb { width: 26px; height: 24px; font-size: 13px; }
    #stv_wrap .stv_tile .stv_nm { font-size: 11px; }
}
/* The buttons grow at 430px and the labelled pills come back with them, which
   between there and here adds up to more than the window is wide. Recording
   and casting are the two that wait: both are errands rather than watching,
   and neither is reached for on a window this size. */
@container (max-width: 560px) {
    #stv_wrap .stv_tile .stv_t_rec,
    #stv_wrap .stv_tile .stv_t_cast { display: none !important; }
}
@container (min-width: 430px) {
    #stv_wrap .stv_tile .stv_tb { width: 40px; height: 36px; font-size: 18px; border-radius: 7px; }
    #stv_wrap .stv_tile .stv_ctl { gap: 4px; }
    #stv_wrap .stv_tile .stv_foot { padding: 6px 10px 9px; }
    #stv_wrap .stv_tile .stv_bar { padding: 6px 9px; gap: 4px; }
    #stv_wrap .stv_tile .stv_nm { font-size: 14px; }
    #stv_wrap .stv_tile .stv_scrub { height: 6px; }
    #stv_wrap .stv_tile .stv_foot .stv_t_live { font-size: 12px; padding: 0 12px; }
    #stv_wrap .stv_tile .stv_foot .stv_t_rec { padding: 0 11px; }
    #stv_wrap .stv_tile .stv_rectx { font-size: 12px; }
    #stv_wrap .stv_tile:hover .stv_vol,
    #stv_wrap .stv_tile.stv_touch .stv_vol,
    #stv_wrap .stv_tile.stv_maxed .stv_vol { width: 92px; height: 5px; }
}
@container (min-width: 760px) {
    #stv_wrap .stv_tile .stv_tb { width: 46px; height: 42px; font-size: 21px; }
    #stv_wrap .stv_tile .stv_nm { font-size: 15px; }
    #stv_wrap .stv_tile .stv_scrub { height: 7px; }
    #stv_wrap .stv_tile:hover .stv_vol,
    #stv_wrap .stv_tile.stv_touch .stv_vol,
    #stv_wrap .stv_tile.stv_maxed .stv_vol { width: 120px; height: 6px; }
}

/* -- the way out of full screen -----------------------------------------
   Moved into whatever element is filling the screen, so it is not written
   under `#stv_wrap` - a full-screen TILE paints nothing outside its own
   subtree. `left`, not `inset-inline-start`: the corner asked for is the
   physical left one whichever way the page reads. The colours and the drop
   are the manager's and arrive inline. */
.stv_fsx {
    position: absolute;
    top: 56px;
    top: var(--stv-fsx-top, 56px);
    left: 0;
    /* A notch or a rounded corner would otherwise sit on top of it. Written
       again rather than replacing what is above: a browser that does not know
       var() or max()/env() drops the line it cannot read and keeps the last
       one it could. */
    top: max(var(--stv-fsx-top, 56px), env(safe-area-inset-top));
    left: env(safe-area-inset-left);
    /* Over every layer the addon draws. A screen with no browser chrome on it
       may have no other way back, so nothing is allowed to cover this. */
    z-index: 2147483000;
    pointer-events: auto;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 28px;
    padding: 0;
    border: 0;
    /* Held against the edge of the screen, so only the two corners that show
       are rounded. */
    border-radius: 0 9px 9px 0;
    background: #fff;
    color: #111;
    font-size: 14px;
    line-height: 1;
    cursor: pointer;
    box-shadow: 0 2px 10px rgba(0, 0, 0, .45);
    /* The picture underneath is a click target of its own. */
    touch-action: manipulation;
    transition: transform .15s, box-shadow .15s;
}
.stv_fsx:hover,
.stv_fsx:focus { box-shadow: 0 3px 14px rgba(0, 0, 0, .6); }
.stv_fsx:active { transform: scale(.94); }

/* -- held upright --------------------------------------------------------
   When the orientation lock is refused, real full screen on an upright phone
   is only a 16:9 strip across the middle - so the WINDOW is turned instead,
   the way the game addons do it. The box keeps its portrait SHAPE - its width
   is the screen's height - and a rotation about its own centre lands it
   exactly over the screen. `inset` has to go, or the four edges keep sizing
   the box. Physical left/top, because `transform` is not direction-aware and
   the wrap is RTL.

   LAST in the file, and matched on two classes, on purpose: the compact rules
   inside the phone media query carry `width: auto !important` and
   `inset-inline: 0 !important`, and at equal specificity the LATER important
   declaration wins - which squashed the turned window back to compact size. */
#stv_wrap.stv_fs.stv_turned {
    position: fixed !important;
    inset: auto !important;
    left: 50% !important;
    top: 50% !important;
    width: 100vh !important;
    width: 100svh !important;
    height: 100vw !important;
    max-width: none !important;
    max-height: none !important;
    border: 0 !important;
    border-radius: 0 !important;
    transform: translate(-50%, -50%) rotate(var(--stv-turn, -90deg)) !important;
}

/* Turned, the window IS the screen, so its chrome is clutter: full screen
   means the picture, not the picture wearing a header. The tile keeps its own
   controls, and the cross in the corner is the way out. */
#stv_wrap.stv_fs.stv_turned .stv_hd,
#stv_wrap.stv_fs.stv_turned .stv_strip,
#stv_wrap.stv_fs.stv_turned .stv_wgrip,
#stv_wrap.stv_fs.stv_turned .stv_tab,
#stv_wrap.stv_fs.stv_turned .stv_picker { display: none !important; }

/* One window was asked for, so one window is shown: turned with the whole
   grid still on, the wanted picture got half the screen. Flex rather than the
   wall's grid, so the tiles' inline grid coordinates simply stop applying. */
#stv_wrap.stv_fs.stv_turned.stv_turn1 .stv_wall { display: flex !important; }
#stv_wrap.stv_fs.stv_turned.stv_turn1 .stv_tile:not(.stv_fs_tile),
#stv_wrap.stv_fs.stv_turned.stv_turn1 .stv_slot,
#stv_wrap.stv_fs.stv_turned.stv_turn1 .stv_none { display: none !important; }
#stv_wrap.stv_fs.stv_turned.stv_turn1 .stv_tile.stv_fs_tile {
    flex: 1 1 auto;
    width: 100%;
    height: 100%;
    max-width: none;
    max-height: none;
}
