:root {
    --app-shell-bg: #080d14;
    --app-rail-bg: #0c131d;
    --app-header-bg: #0d1520;
    --app-content-bg: #0a111a;
    --app-widget-bg: #0d151f;
    --app-widget-header-bg: #101a26;
    --app-card-hover-bg: #142232;
    --app-widget-border: #1c2b3a;
    --app-rail-border: #1a2937;
    --app-on-surface: #edf5ff;
    --app-eyebrow-color: #8190a2;
    --app-selection-color: #31c8ff;
    --app-selection-on-color: #041017;
    --app-selection-surface: rgba(49, 200, 255, 0.08);
    --app-selection-surface-strong: rgba(49, 200, 255, 0.18);
    --app-selection-border: rgba(49, 200, 255, 0.72);
    --app-selection-glow: rgba(49, 200, 255, 0.24);
    /* Focus wash. Deliberately the blue the channel rows already used, not the
       cyan of --app-selection-*: focus and selection have to stay tellable
       apart when a row is both. */
    --app-focus-fill: rgba(32, 129, 218, 0.26);
    --purple: #8d73ff;
    --danger: #ff6584;
    /* Overscan margin, off by default. A TV set to a stretched picture mode
       crops each edge and never reports it; raise this to ~32-44px (3-4% of
       1080) if content gets clipped. The horizontal inset follows it across
       the 16:9 canvas so both edges lose the same share. */
    --nova-safe-inset: 0px;
    --nova-safe-inset-x: calc(var(--nova-safe-inset) * 16 / 9);
    /* Compositor motion. Duration is long on purpose: the Mi Box WebView
       vsyncs near 30 Hz, so 220 ms is six frames and reads as a jump.
       380 ms is eleven decelerating frames. These two shifts MUST match the
       last-wins .topbar / catalog rail heights (112 + 84). */
    --nova-motion: 0.38s cubic-bezier(0.16, 1, 0.3, 1);
    --nova-topbar-shift: 112px;
    --nova-lane-shift: 84px;
}

* {
    box-sizing: border-box;
}
html,
body {
    width: 1920px;
    height: 1080px;
    margin: 0;
    overflow: hidden;
    background: var(--app-shell-bg);
    color: var(--app-on-surface);
    font-family: 'SamsungOne', 'Segoe UI', Arial, sans-serif;
}
#app-shell {
    position: relative;
    z-index: 2;
    width: 1920px;
    height: 1080px;
}
button,
input {
    font: inherit;
}
button {
    border: 0;
    cursor: pointer;
}
.hidden {
    display: none !important;
}

/* Black first, then only the zoom — no static N beat before it.

   Three times the original 1.9s. The app spends that time logging in and
   pulling its categories, so a longer opening is not a longer wait: it is the
   wait made visible, and it buys the mark room to grow from 168px to 900px
   instead of stopping at 504. Visual size stays inside the 2048px bitmap, so
   nothing upscales on a 4K set. */
#boot-splash {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 200;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    width: 100%;
    height: 100%;
    background: #05070a;
    animation: boot-splash-fade 5.7s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}
#boot-splash.is-done {
    opacity: 0;
    pointer-events: none;
}
.boot-splash-mark {
    /* Layout stays at the FINAL size so the 4096px source is decoded once
       at that size. The zoom is a compositor scale() — width/height
       animation was ~15 fps on the box and read as a stutter. End size
       560 (was 900): same 5.7s, less "the N eats the screen". */
    display: block;
    width: 560px;
    height: 560px;
    flex: none;
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
    -webkit-transform-origin: 50% 50%;
    transform-origin: 50% 50%;
    will-change: transform;
    animation: boot-splash-zoom 5.7s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}
@keyframes boot-splash-zoom {
    from {
        -webkit-transform: translateZ(0) scale(0.3);
        transform: translateZ(0) scale(0.3);
    }
    to {
        -webkit-transform: translateZ(0) scale(1);
        transform: translateZ(0) scale(1);
    }
}
@keyframes boot-splash-fade {
    0%,
    78% {
        opacity: 1;
    }
    100% {
        opacity: 0;
    }
}

.topbar {
    height: 96px;
    display: flex;
    align-items: center;
    padding: 14px 26px;
    border-bottom: 1px solid var(--app-widget-border);
    background: var(--app-header-bg);
}
.brand {
    width: 340px;
    display: flex;
    align-items: center;
}
.brand span {
    display: block;
    margin-top: 3px;
    color: var(--app-eyebrow-color);
    font-size: 15px;
}
.main-nav {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 680px;
    margin: 0 auto;
    padding: 7px;
    border: 1px solid var(--app-widget-border);
    border-radius: 17px;
    background: #0a121c;
}
.nav-button {
    min-width: 190px;
    height: 54px;
    border-radius: 12px;
    color: #a9b7c8;
    background: transparent;
    font-size: 19px;
}
.nav-button.active {
    color: var(--app-on-surface);
    border: 1px solid var(--app-selection-border);
    background: linear-gradient(
        135deg,
        var(--app-selection-surface-strong),
        var(--app-selection-surface)
    );
    box-shadow: 0 0 24px var(--app-selection-glow);
}
/* The language control sits beside the account button. The pair is boxed to
   the width the account button used to hold on its own, so the centred nav
   does not shift sideways just because a second control appeared. */
.topbar-actions {
    width: 320px;
    display: flex;
    align-items: center;
}
.topbar-actions .source-button {
    width: auto;
    min-width: 0;
    flex: 1 1 auto;
}
.language-button {
    width: 86px;
    height: 62px;
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border-radius: 15px;
    color: var(--app-on-surface);
    background: #111c28;
}
.language-button strong {
    font-size: 17px;
    font-weight: 650;
    letter-spacing: 0.02em;
}
/* A globe: one ring, one equator, one meridian. Drawn rather than set as an
   emoji, which renders as a flat monochrome box on several TV font stacks. */
.language-globe {
    position: relative;
    width: 23px;
    height: 23px;
    flex: 0 0 auto;
    border: 2px solid currentColor;
    border-radius: 50%;
    opacity: 0.92;
}
.language-globe::before,
.language-globe::after {
    position: absolute;
    content: '';
    border: 2px solid currentColor;
}
.language-globe::before {
    top: 50%;
    right: -2px;
    left: -2px;
    height: 0;
    margin-top: -1px;
    border-width: 2px 0 0;
}
.language-globe::after {
    top: -2px;
    bottom: -2px;
    left: 50%;
    width: 0;
    margin-left: -5px;
    border-width: 0 2px;
    border-radius: 50%;
    padding: 0 3px;
}
.source-button {
    width: 320px;
    height: 62px;
    padding: 0 22px;
    border-radius: 15px;
    color: var(--app-on-surface);
    background: #111c28;
    text-align: left;
}
.source-button span {
    display: block;
    color: var(--app-eyebrow-color);
    font-size: 14px;
}
.source-button strong {
    display: block;
    max-width: 270px;
    margin-top: 4px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 17px;
}

[data-focusable]:focus {
    outline: none;
    color: white;
    box-shadow:
        0 0 0 4px var(--app-selection-color),
        0 12px 34px rgba(0, 0, 0, 0.45);
    transform: scale(1.025);
    transition:
        transform 0.1s ease,
        box-shadow 0.1s ease;
}
input[data-focusable]:focus {
    transform: scale(1.01);
}

.login-screen {
    width: 1920px;
    height: 1080px;
    display: grid;
    grid-template-columns: 1.12fr 0.88fr;
    align-items: center;
    gap: 100px;
    padding: 80px 145px;
    background:
        radial-gradient(
            circle at 12% 25%,
            rgba(111, 72, 232, 0.28),
            transparent 35%
        ),
        radial-gradient(
            circle at 82% 78%,
            rgba(49, 200, 255, 0.12),
            transparent 30%
        ),
        #080b12;
}
.brand-lockup {
    display: flex;
    align-items: center;
    color: #f7f9fc;
}
.brand-symbol {
    position: relative;
    display: block;
    flex: none;
    overflow: hidden;
    border: 1px solid rgba(255, 255, 255, 0.14);
    background: #090b10;
    box-shadow: 0 14px 42px rgba(0, 0, 0, 0.34);
}
.brand-symbol::before,
.brand-symbol::after {
    content: "";
    position: absolute;
    z-index: 1;
    top: 22%;
    width: 17%;
    height: 56%;
    border-radius: 999px;
    background: #f7f9fc;
}
.brand-symbol::before { left: 22%; }
.brand-symbol::after { right: 22%; }
.brand-n-diagonal {
    position: absolute;
    z-index: 1;
    left: 42%;
    top: 17%;
    width: 17%;
    height: 66%;
    border-radius: 999px;
    background: #f7f9fc;
    transform: rotate(-38deg);
}
.brand-play {
    position: absolute;
    z-index: 1;
    left: 42%;
    top: 36%;
    width: 0;
    height: 0;
    border-top: 15px solid transparent;
    border-bottom: 15px solid transparent;
    border-left: 23px solid #35c8f2;
    filter: drop-shadow(0 4px 10px rgba(18, 158, 214, 0.32));
}
.brand-word {
    display: block;
    font-weight: 700;
    letter-spacing: 0.16em;
    line-height: 1;
}
.brand-product {
    display: block;
    color: #8f9daf;
    font-weight: 650;
    letter-spacing: 0.22em;
}
.brand-lockup-compact .brand-symbol {
    width: 62px;
    height: 62px;
    border-radius: 19px;
}
.brand-lockup-compact .brand-play {
    border-top-width: 9px;
    border-bottom-width: 9px;
    border-left-width: 14px;
}
.brand-lockup-compact .brand-word { margin-left: 18px; font-size: 29px; }
.brand-lockup-hero {
    margin-bottom: 48px;
}
.brand-lockup-hero .brand-symbol {
    width: 112px;
    height: 112px;
    border-radius: 32px;
}
.brand-lockup-hero .brand-word {
    margin-left: 30px;
    font-size: 66px;
}
.brand-lockup-hero .brand-product {
    margin-left: 18px;
    padding-top: 16px;
    font-size: 20px;
}
.eyebrow {
    color: var(--app-selection-color);
    font-size: 14px;
    font-weight: 800;
    letter-spacing: 0.16em;
}
.welcome-copy h1 {
    margin: 18px 0 24px;
    font-size: 66px;
    line-height: 1.06;
}
.welcome-copy h1 em {
    color: var(--purple);
    font-style: normal;
}
.welcome-copy p {
    max-width: 720px;
    color: #9eabba;
    font-size: 23px;
    line-height: 1.5;
}
.login-card {
    width: 580px;
    padding: 44px;
    border: 1px solid #253447;
    border-radius: 26px;
    background: linear-gradient(145deg, #131c29, #0d141e);
    box-shadow: 0 35px 100px rgba(0, 0, 0, 0.42);
}
.login-card h2 {
    margin: 8px 0 28px;
    font-size: 36px;
}
.login-card label {
    display: block;
    margin: 17px 0;
    color: #aab6c4;
    font-size: 16px;
}
.login-card input,
.rail-search,
.catalog-search {
    display: block;
    width: 100%;
    height: 58px;
    margin-top: 8px;
    padding: 0 17px;
    border: 1px solid #27384b;
    border-radius: 12px;
    outline: none;
    color: white;
    background: #0a121c;
    font-size: 18px;
}
.remember-row {
    display: flex !important;
    align-items: center;
}
.remember-row input {
    width: 24px;
    height: 24px;
    margin: 0;
}
.primary-button {
    min-height: 58px;
    padding: 0 28px;
    border-radius: 13px;
    color: white;
    background: linear-gradient(120deg, #6551dc, #8d73ff 55%, #2cb9df);
    font-size: 19px;
    font-weight: 750;
}
.login-card .primary-button {
    width: 100%;
    margin-top: 14px;
}
.pair-phone-button {
    width: 100%;
    height: 58px;
    margin-top: 12px;
    border: 1px solid #35516d;
    border-radius: 13px;
    color: #dcecff;
    background: #122235;
    font-size: 20px;
    font-weight: 750;
}
.pair-phone-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    margin-right: 12px;
    border-radius: 8px;
    color: #08131e;
    background: var(--app-selection-color);
    font-size: 13px;
    font-weight: 900;
}
.form-error {
    min-height: 22px;
    color: var(--danger);
}

.app-screen {
    width: 1920px;
    height: 984px;
}
.live-screen,
.catalog-screen {
    display: grid;
    grid-template-columns: 300px 1fr;
}
.category-rail {
    height: 984px;
    padding: 18px 12px 14px;
    border-right: 1px solid var(--app-rail-border);
    background: var(--app-rail-bg);
}
.rail-heading {
    height: 42px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 10px;
    color: var(--app-eyebrow-color);
    font-size: 13px;
    letter-spacing: 0.08em;
}
.rail-heading strong {
    color: #6f8499;
    font-size: 14px;
}
.rail-search {
    height: 48px;
    margin: 0 0 12px;
    font-size: 15px;
}
.category-list {
    height: 850px;
    overflow-x: hidden;
    overflow-y: auto;
    padding: 2px 1px 24px;
}
.category-item {
    width: 100%;
    min-height: 52px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    /* Spacing lives on the children, not in flex `gap`: gap only reached flex
       containers in Chromium 84 and this app ships to a Tizen 5.0 WebView, so
       relying on it would leave the icon welded to the label there while
       looking correct on a desk browser. */
    padding: 8px 12px;
    border-radius: 10px;
    color: #d1dbe6;
    background: transparent;
    text-align: left;
}
.category-item span {
    /* flex:1 keeps the label hugging its pin icon while the count stays on
       the right edge; without it space-between would spread all three. */
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    text-align: left;
    font-size: 16px;
}
.category-item small {
    flex-shrink: 0;
    margin-left: 10px;
    color: #718297;
    font-size: 13px;
}
.category-item.selected {
    color: var(--app-selection-color);
    border: 1px solid var(--app-selection-border);
    background: linear-gradient(
        135deg,
        var(--app-selection-surface-strong),
        var(--app-selection-surface)
    );
}
/* Favoris / Récents / Toutes are app shelves, not provider categories. They
   used to wear a raised surface plus an accent bar, which fused into one
   blob; now a CSS-drawn glyph (see the finish pass) carries the identity and
   the rows stay as bare as the provider ones — the same language IPTV
   desktop players use for their pinned shortcuts. The last pinned row still
   carries a divider so the provider list visibly starts below it.
   Synthetics are always rendered first, so sibling order is reliable. */
.category-item.category-pinned span {
    font-weight: 600;
    letter-spacing: 0.02em;
}
.category-item.category-pinned + .category-item:not(.category-pinned) {
    margin-top: 14px;
    position: relative;
}
.category-item.category-pinned + .category-item:not(.category-pinned)::before {
    content: '';
    position: absolute;
    top: -8px;
    left: 4px;
    right: 4px;
    height: 1px;
    background: rgba(255, 255, 255, 0.12);
}

.live-main {
    height: 984px;
    padding: 14px;
    background: transparent;
}
.preview-row {
    height: 504px;
    display: grid;
    grid-template-columns: 880px 1fr;
    gap: 14px;
}
.video-stage {
    position: relative;
    width: 880px;
    height: 495px;
    overflow: hidden;
    border: 1px solid var(--app-widget-border);
    border-radius: 16px;
    background: transparent;
}
#av-player,
#html-player {
    position: absolute;
    left: 0;
    top: 0;
    width: 880px;
    height: 495px;
    margin: 0;
    padding: 0;
    border: 0;
}
#av-player {
    position: fixed;
    z-index: 1;
    left: 0;
    top: 0;
    width: 1px !important;
    height: 1px !important;
    background: transparent;
    pointer-events: none !important;
}
#html-player {
    z-index: 1;
    display: none;
    object-fit: contain;
}

/* Hebrew subtitles Nova fetched itself, painted by the page over the picture.
   z-index 30 sits above the video (1) and below the player overlay (40) and
   its panels, so the controls and the Sous-titres list stay readable on top of
   a cue rather than behind it.

   No flex `gap` and no `inset`: Tizen 5.0 is Chromium 63 and supports neither.
   The lines are stacked with a sibling margin, and the box is placed with the
   four long-hand offsets. `direction: rtl` belongs on the line, not here --
   the block itself is centred, and each line sets its own paragraph
   direction so a Latin name inside a Hebrew sentence still reads correctly. */
.nova-subtitle-layer {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 42px;
    z-index: 30;
    display: block;
    padding: 0 6%;
    text-align: center;
    pointer-events: none;
}
.nova-subtitle-layer.hidden {
    display: none;
}
/* One row per line of the cue. The row is full width and does the centring;
   the black band inside it is an inline-block, so it is only ever as wide as
   the words it carries -- a one-word cue no longer paints a bar across the
   whole picture (couch finding).

   `line-height: 0` on the row is what closes the seam between two stacked
   bands. An inline-block sits on the text baseline, so its line box reserves a
   descender slot underneath it; that slot, not a margin, was the white gap
   between the two black bars. Zeroing the row's line height removes it, and
   the band restores its own line height for the text inside. */
.nova-subtitle-row {
    display: block;
    line-height: 0;
    text-align: center;
}
.nova-subtitle-line {
    display: inline-block;
    max-width: 92%;
    font-size: 30px;
    line-height: 1.24;
    font-weight: 600;
    color: #ffffff;
    background: rgba(0, 0, 0, 0.62);
    padding: 3px 14px;
    border-radius: 4px;
    /* The shadow is the readability floor on a bright frame: a TV viewer is
       three metres away and the background box alone loses thin glyphs. */
    text-shadow:
        0 2px 4px rgba(0, 0, 0, 0.95),
        0 0 10px rgba(0, 0, 0, 0.85);
}
/* Fullscreen is where films are actually watched, and the frame is 1920 wide
   instead of 880 -- the windowed size would be unreadably small there. */
.video-stage.fullscreen .nova-subtitle-layer {
    bottom: 78px;
}
.video-stage.fullscreen .nova-subtitle-line {
    font-size: 44px;
    padding: 4px 20px;
}
.video-stage.fullscreen {
    position: fixed;
    z-index: 40;
    left: 0;
    top: 0;
    width: 1920px;
    height: 1080px;
    overflow: hidden;
    border: 0;
    border-radius: 0;
}
.video-stage.fullscreen #html-player,
.video-stage.fullscreen .video-empty {
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    max-width: 1920px;
    max-height: 1080px;
}
.video-stage.fullscreen .video-overlay {
    position: fixed;
}
.native-fullscreen .topbar,
.native-fullscreen .category-rail,
.native-fullscreen .program-panel,
.native-fullscreen .guide-panel {
    visibility: hidden;
}
.video-empty {
    position: absolute;
    z-index: 2;
    left: 0;
    top: 0;
    width: 880px;
    height: 495px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    background: radial-gradient(circle, #142232, #05080d 66%);
    color: #a7b4c3;
}
.video-empty img {
    width: 94px;
    height: 94px;
    margin-bottom: 18px;
    opacity: 0.8;
}
.video-empty strong {
    color: white;
    font-size: 25px;
}
.video-empty span {
    margin-top: 8px;
    font-size: 17px;
}
.video-overlay {
    position: absolute;
    z-index: 3;
    left: 0;
    right: 0;
    bottom: 0;
    height: 88px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 18px;
    color: white;
    background: linear-gradient(transparent, rgba(0, 0, 0, 0.82));
    pointer-events: none;
}
.player-status {
    min-width: 0;
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 16px;
}
.player-controls {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    pointer-events: auto;
}
.player-control {
    min-width: 76px;
    height: 44px;
    padding: 0 12px;
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 9px;
    color: white;
    background: rgba(10, 17, 26, 0.88);
    font-size: 14px;
}
.player-control-primary {
    color: var(--app-selection-on-color);
    border-color: var(--app-selection-color);
    background: var(--app-selection-color);
    font-weight: 750;
}
.video-stage.fullscreen .player-controls {
}
.video-stage.fullscreen .player-control {
    min-width: 110px;
    height: 54px;
    font-size: 17px;
}

.program-panel {
    height: 495px;
    padding: 30px;
    overflow: hidden;
    border: 1px solid var(--app-widget-border);
    border-radius: 16px;
    background: linear-gradient(145deg, #0e1823, #0a1119);
}
.program-channel {
    height: 82px;
    display: flex;
    align-items: center;
    margin-bottom: 30px;
}
.program-logo-shell {
    width: 66px;
    height: 66px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    border: 1px solid #26384a;
    border-radius: 14px;
    color: var(--purple);
    background: #080d13;
    font-size: 28px;
    font-weight: 800;
}
.program-logo-shell img {
    width: 100%;
    height: 100%;
    padding: 7px;
    object-fit: contain;
}
.program-channel h1 {
    max-width: 520px;
    margin: 0 0 6px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 25px;
}
.program-channel span {
    color: var(--app-eyebrow-color);
    font-size: 15px;
}
.program-panel h2 {
    margin: 16px 0 8px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 30px;
}
.program-panel > p {
    color: var(--app-eyebrow-color);
}
.program-description {
    height: 100px;
    overflow: hidden;
    font-size: 17px;
    line-height: 1.45;
}
.program-progress {
    height: 7px;
    margin-top: 22px;
    overflow: hidden;
    border-radius: 8px;
    background: #27313d;
    box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.6);
}
.program-progress span {
    display: block;
    width: 0;
    height: 100%;
    border-radius: inherit;
    background: var(--app-selection-color);
    box-shadow: 0 0 12px var(--app-selection-glow);
}

.guide-panel {
    position: relative;
    height: 452px;
    overflow: hidden;
    border: 1px solid var(--app-widget-border);
    border-radius: 15px;
    background: var(--app-widget-bg);
}
.guide-total {
    margin-left: auto;
    color: #c2d0dd;
    font-size: 20px;
    font-weight: 700;
}
.guide-header {
    height: 76px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 20px;
    border-bottom: 1px solid var(--app-widget-border);
    background: var(--app-widget-header-bg);
}
.guide-header h2 {
    margin: 4px 0 0;
    max-width: 750px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 21px;
}
.guide-actions {
    display: flex;
    align-items: center;
    color: var(--app-eyebrow-color);
}
.small-button {
    min-height: 46px;
    padding: 0 19px;
    border-radius: 10px;
    color: #dce7f2;
    background: #142130;
}
.time-ruler {
    height: 38px;
    display: grid;
    grid-template-columns: 360px repeat(3, 1fr);
    border-bottom: 1px solid var(--app-widget-border);
    color: #718297;
    font-size: 13px;
}
.time-ruler::before {
    content: 'CHAÎNES';
    padding: 12px 18px;
    border-right: 1px solid var(--app-widget-border);
}
.time-ruler span {
    padding: 12px 14px;
    border-right: 1px solid var(--app-widget-border);
}
.channel-guide {
    height: 336px;
    overflow: hidden;
}
.channel-row {
    width: 100%;
    height: 56px;
    display: grid;
    grid-template-columns: 360px 1fr;
    padding: 0;
    border-bottom: 1px solid #172331;
    color: var(--app-on-surface);
    background: transparent;
    text-align: left;
}
.channel-row.selected {
    border: 1px solid var(--app-selection-border);
    background: linear-gradient(
        135deg,
        var(--app-selection-surface-strong),
        var(--app-selection-surface)
    );
}
.channel-identity {
    min-width: 0;
    display: grid;
    grid-template-columns: 56px 1fr;
    align-items: center;
    gap: 12px;
    padding: 5px 14px;
    border-right: 1px solid var(--app-widget-border);
}
.channel-logo {
    width: 46px;
    height: 46px;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    border-radius: 10px;
    background: #070c12;
    color: var(--purple);
    font-weight: 800;
}
.channel-logo img {
    width: 100%;
    height: 100%;
    padding: 4px;
    object-fit: contain;
}
.channel-copy {
    min-width: 0;
}
.channel-copy strong,
.channel-copy span {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.channel-copy strong {
    font-size: 15px;
}
.channel-copy span {
    margin-top: 3px;
    color: var(--app-eyebrow-color);
    font-size: 12px;
}
.epg-track {
    min-width: 0;
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
}
.epg-block {
    min-width: 0;
    padding: 9px 14px;
    border-right: 1px solid var(--app-widget-border);
    color: #9facba;
    background: #0b121b;
}
.epg-block strong,
.epg-block span {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.epg-block strong {
    color: #dce5ee;
    font-size: 13px;
}
.epg-block span {
    margin-top: 4px;
    font-size: 11px;
}
.epg-block.current {
    background: var(--app-selection-surface);
}
.epg-placeholder {
    grid-column: 1 / 4;
    padding: 19px 14px;
    color: #617184;
    font-size: 13px;
}
.epg-block.no-data strong {
    color: #69798b;
    font-weight: 500;
}
.guide-message {
    height: 56px;
    display: flex;
    align-items: center;
    padding: 0 18px;
    color: #69798b;
    font-size: 14px;
}

.catalog-main {
    position: relative;
    height: 984px;
    padding: 28px 34px;
    overflow: hidden;
    background: var(--app-content-bg);
}
.catalog-header {
    height: 108px;
    display: flex;
    align-items: center;
    justify-content: space-between;
}
.catalog-header h1 {
    margin: 8px 0 0;
    max-width: 800px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 38px;
}

/* --------------------------------------------------------------------------
   Search progress ring.

   Replaces "RECHERCHE 42% · 18 TROUVÉS". A viewer read the old "4 / 136" as
   the size of the catalogue; a ring that fills states progress without any
   number that can be mistaken for a count of films.

   Tizen 5.0 (Chromium 63) constraints, all deliberate:
     - no flex `gap` and no `inset`: spacing is margin, the percent is
       positioned with top/left/width/height;
     - stroke-dashoffset is animated, never a transform on the <svg> — a
       transformed SVG in the header forced a full repaint of the heading and
       stuttered the shelves behind it;
     - the rotation that puts 0% at 12 o'clock is a STATIC attribute on the
       circle (index.html), applied once, not a CSS transform.
   -------------------------------------------------------------------------- */
.search-ring {
    position: relative;
    display: inline-block;
    width: 58px;
    height: 58px;
    margin-right: 18px;
    vertical-align: -12px;
}
.search-ring svg {
    display: block;
    width: 58px;
    height: 58px;
}
/* Grey, not blue, and deliberately NOT var(--app-selection-color): the ring
   states progress, it is not a thing the cursor is on. Wearing the selection
   colour made it the loudest object on a page whose language is greys, and it
   competed with the actual focus ring for the eye. */
.search-ring-track {
    fill: none;
    stroke: rgba(255, 255, 255, 0.14);
    stroke-width: 5;
}
.search-ring-value {
    fill: none;
    stroke: #d7dade;
    stroke-width: 5;
    stroke-linecap: round;
    stroke-dasharray: 157.08 157.08;
    stroke-dashoffset: 157.08;
    -webkit-transition: stroke-dashoffset 0.3s linear;
    transition: stroke-dashoffset 0.3s linear;
}
.search-ring b {
    position: absolute;
    top: 0;
    left: 0;
    width: 58px;
    height: 58px;
    line-height: 58px;
    text-align: center;
    font-size: 18px;
    font-weight: 600;
    color: #d7dade;
}
.catalog-search {
    width: 430px;
    margin: 0;
}
.content-grid {
    height: 746px;
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    grid-template-rows: repeat(2, 360px);
    gap: 18px;
}
.content-card {
    position: relative;
    min-width: 0;
    height: 360px;
    overflow: hidden;
    border: 1px solid var(--app-widget-border);
    border-radius: 15px;
    color: white;
    background: var(--app-widget-bg);
    text-align: left;
}
.content-card img,
.content-card .placeholder {
    width: 100%;
    height: 275px;
    object-fit: cover;
    background: #111b27;
}
.content-card .placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--purple);
    font-size: 58px;
}
.content-card strong {
    display: block;
    padding: 12px 14px 2px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 17px;
}
.content-card span {
    display: block;
    padding: 5px 14px;
    color: var(--app-eyebrow-color);
    font-size: 13px;
}
.pagination {
    height: 72px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--app-eyebrow-color);
}
.loading-state,
.empty-state {
    height: 746px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    color: var(--app-eyebrow-color);
}
.spinner {
    width: 62px;
    height: 62px;
    border: 6px solid #213041;
    border-top-color: var(--app-selection-color);
    border-radius: 50%;
    animation: spin 1s linear infinite;
}
@keyframes spin {
    to {
        transform: rotate(360deg);
    }
}

.series-screen {
    padding: 28px 42px;
    overflow: hidden;
    background: var(--app-content-bg);
}
.series-hero {
    height: 300px;
    display: flex;
    margin-top: 20px;
}
.series-hero img {
    width: 190px;
    height: 280px;
    object-fit: cover;
    border-radius: 16px;
    background: var(--app-widget-bg);
}
.series-hero h1 {
    margin: 12px 0;
    font-size: 48px;
}
.series-hero p {
    max-width: 1200px;
    color: var(--app-eyebrow-color);
    font-size: 19px;
    line-height: 1.5;
}
.episode-grid {
    height: 570px;
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    align-content: start;
    gap: 14px;
    overflow-y: auto;
}
.episode-card {
    height: 90px;
    padding: 17px;
    border-radius: 12px;
    color: white;
    background: var(--app-widget-bg);
    text-align: left;
}
.episode-card small {
    display: block;
    margin-top: 7px;
    color: var(--app-eyebrow-color);
}

.toast {
    position: fixed;
    z-index: 50;
    left: 50%;
    bottom: 52px;
    transform: translateX(-50%);
    padding: 16px 28px;
    border: 1px solid #35495f;
    border-radius: 12px;
    background: #152232;
    box-shadow: 0 18px 55px #000;
    font-size: 18px;
}
/* The "we are updating, this is not a crash" notice.

   Shown for a moment before the page is replaced by a new bundle. Everything
   here is Tizen 5 safe: no flex `gap` (sibling margins instead) and no
   `inset` (the four sides are written out), because the 2019 panel supports
   neither and would leave the card unpositioned in the top-left corner.

   z-index sits above .dialog-backdrop (60) and the boot splash (200) both:
   the restart takes whatever is on screen away, so nothing may cover it. */
.update-notice {
    position: fixed;
    z-index: 300;
    left: 0;
    top: 0;
    width: 1920px;
    height: 1080px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(5, 7, 10, 0.92);
}
.update-notice-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 44px 62px;
    border: 1px solid var(--app-widget-border);
    border-radius: 22px;
    background: #111b28;
    box-shadow: 0 24px 70px #000;
    text-align: center;
}
.update-notice-card strong {
    display: block;
    margin-top: 26px;
    font-size: 34px;
}
.update-notice-card span {
    display: block;
    margin-top: 12px;
    font-size: 22px;
    color: var(--app-eyebrow-color);
}
.update-notice-spinner {
    display: block;
    width: 54px;
    height: 54px;
    border: 5px solid #35495f;
    border-top-color: #4da3ff;
    border-radius: 50%;
    -webkit-animation: update-notice-spin 1s linear infinite;
    animation: update-notice-spin 1s linear infinite;
}
@-webkit-keyframes update-notice-spin {
    to {
        -webkit-transform: rotate(360deg);
    }
}
@keyframes update-notice-spin {
    to {
        -webkit-transform: rotate(360deg);
        transform: rotate(360deg);
    }
}

/* Full APK install wall (Android only). Tizen 5 safe: no flex gap, no inset. */
.apk-update-block {
    position: fixed;
    z-index: 320;
    left: 0;
    top: 0;
    width: 1920px;
    height: 1080px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(5, 7, 10, 0.96);
}
.apk-update-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 760px;
    padding: 48px 62px;
    border: 1px solid var(--app-widget-border);
    border-radius: 22px;
    background: #111b28;
    box-shadow: 0 24px 70px #000;
    text-align: center;
}
.apk-update-card strong {
    display: block;
    font-size: 36px;
}
.apk-update-card p {
    display: block;
    margin-top: 18px;
    margin-bottom: 0;
    font-size: 22px;
    line-height: 1.45;
    color: var(--app-eyebrow-color);
    white-space: pre-wrap;
}
.apk-update-install {
    display: block;
    margin-top: 32px;
    padding: 18px 48px;
    border: 0;
    border-radius: 14px;
    background: #4da3ff;
    color: #061018;
    font-size: 26px;
    font-weight: 700;
}
.apk-update-install:focus,
.apk-update-install:focus-visible {
    outline: 3px solid #fff;
    outline-offset: 4px;
}
/* Retired: the web-bundle wall has one answer, Installer. A second button was
   a way to postpone an update the box has already downloaded, and on the couch
   it read as the choice being optional. Kept as a hard hide so an older
   bundle's markup cannot paint it either. */
.apk-update-later {
    display: none !important;
}

.web-update-banner {
    /* Retired: the offer is #web-update-block, a full-screen wall. Kept as a
       hard hide so an older bundle's markup cannot paint an unreachable strip
       over the picture. */
    display: none !important;
}


.dialog-backdrop {
    position: fixed;
    z-index: 60;
    left: 0;
    top: 0;
    width: 1920px;
    height: 1080px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.78);
}
.dialog {
    width: 520px;
    padding: 38px;
    border: 1px solid var(--app-widget-border);
    border-radius: 22px;
    background: #111b28;
    text-align: center;
}
.dialog h2 {
    margin: 0 0 28px;
    font-size: 31px;
}
/* Resume prompt: the title keeps its spacing when no detail line is present,
   so the margin lives here rather than on the heading. */
.dialog h2 + .dialog-detail {
    margin: -18px 0 28px;
}
.dialog-detail {
    color: var(--app-eyebrow-color);
    font-size: 23px;
    line-height: 1.35;
}
/* Detail pages: the metadata line above the synopsis, and the action row. */
.detail-meta {
    margin: 0 0 10px;
    color: var(--app-eyebrow-color);
    font-size: 22px;
}
/* Provider plots run to paragraphs; the hero has a fixed height, so an
   unclamped synopsis pushed the action row off the page (and into the episode
   grid on series). -webkit-line-clamp is fine on this engine. A series keeps
   3 lines because its episodes sit below; a film page has the room for 6. */
#series-plot,
#movie-plot {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
#series-plot { -webkit-line-clamp: 3; }
#movie-plot { -webkit-line-clamp: 6; }
.detail-actions {
    display: flex;
    margin-top: 18px;
}
/* Flex gap is dead on this engine; see the fallback block at the end. */
.detail-actions > * + * { margin-left: 14px; }
.detail-actions .small-button,
.detail-actions .primary-button { min-width: 200px; }
/* Lecture, on a detail page, is the app's own white — not the sign-in card's
   violet-to-cyan gradient it was borrowing. Report from the couch: "le bouton
   lecture je ne veux plus qu'il soit bleu". Netflix and Prime both make the
   primary action a plain light slab for the same reason: on a near-black
   catalogue it is already the loudest thing on screen without a colour.
   The login card keeps the gradient; this rule is scoped to the film and
   series action rows.

   Its :focus lives at the END of the file, not here: three
   `[data-focusable]:focus` rules carry !important and all paint a light ring,
   which is invisible on a white button. See the override block at the bottom
   and apps/android-tv/bench/shoot-detail-colours.mjs. */
.detail-actions .primary-button {
    color: #101319;
    background: #f2f3f5;
}
/* A favourited title used to fill this button with the selection blue, which
   would have left exactly one blue object in a row that just lost it. Same
   neutral language: a lit surface, not a colour. */
.detail-actions .is-favorite {
    color: #f5f5f7;
    background: rgba(255, 255, 255, 0.16);
}
.pairing-dialog {
    width: 880px;
    padding: 34px 38px 38px;
    border: 1px solid #31465e;
    border-radius: 24px;
    background: #101b28;
    box-shadow: 0 30px 100px #000b;
}
.pairing-dialog header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
}
.pairing-dialog h2 {
    margin: 7px 0 24px;
    font-size: 34px;
}
.pairing-content {
    display: grid;
    grid-template-columns: 340px 1fr;
    gap: 38px;
    align-items: center;
}
.pairing-qr {
    width: 340px;
    height: 340px;
    padding: 16px;
    border-radius: 20px;
    background: white;
}
.pairing-qr svg {
    display: block;
    width: 100%;
    height: 100%;
}
.pairing-copy ol {
    margin: 0;
    padding-left: 28px;
    color: #d2ddec;
    font-size: 20px;
    line-height: 1.65;
}
.pairing-code-block {
    margin-top: 22px;
    padding: 15px 20px;
    border: 1px solid #2c435a;
    border-radius: 14px;
    background: #091521;
}
.pairing-code-block span {
    display: block;
    color: #8295ab;
    font-size: 14px;
    text-transform: uppercase;
    letter-spacing: .12em;
}
.pairing-code-block strong {
    display: block;
    margin-top: 3px;
    color: var(--app-selection-color);
    font-size: 31px;
    letter-spacing: .18em;
}
#pairing-status {
    margin: 18px 0 4px;
    color: #cbd8e7;
    font-size: 18px;
}
.pairing-expiry {
    margin: 0;
    color: #8092a7;
    font-size: 16px;
}
.dialog div {
    display: flex;
    justify-content: center;
}

/* TV readability and focus geometry */
[data-focusable]:focus {
    outline: none;
    transform: none;
    box-shadow: inset 0 0 0 4px var(--app-selection-color), 0 0 24px var(--app-selection-glow);
    transition: box-shadow 0.08s ease, background 0.08s ease;
}
input[data-focusable]:focus {
    transform: none;
}
.topbar {
    height: 108px;
    padding: 16px 28px;
}
.brand strong { font-size: 28px; }
.brand span, .source-button span { font-size: 18px; }
.source-button strong { font-size: 20px; }
.nav-button { height: 62px; font-size: 23px; }
.app-screen { height: 972px; }
.live-screen, .catalog-screen { grid-template-columns: 340px 1fr; }
.category-rail { height: 972px; padding: 18px 14px; }
.rail-heading { height: 48px; font-size: 17px; }
.rail-heading strong { font-size: 18px; }
.rail-search { height: 58px; margin-bottom: 14px; font-size: 19px; }
.category-list { height: 830px; padding: 4px 3px 28px; }
.category-item { min-height: 64px; padding: 10px 16px; border-radius: 12px; }
.category-item span { font-size: 20px; }
.category-item small { font-size: 20px; }
.live-main { height: 972px; padding: 16px; }
.preview-row { height: 494px; grid-template-columns: 850px 1fr; gap: 16px; }
.video-stage, #av-player, #html-player, .video-empty { width: 850px; height: 478px; }
.program-panel { height: 478px; padding: 30px 34px; }
.eyebrow { font-size: 17px; }
.program-channel h1 { font-size: 29px; }
.program-channel span { font-size: 19px; }
.program-panel h2 { font-size: 34px; }
.program-panel > p, .program-description { font-size: 20px; }
.guide-panel { height: 446px; }
.guide-header { height: 82px; }
.guide-header h2 { font-size: 26px; }
.guide-actions { font-size: 18px; }
.small-button { min-height: 52px; font-size: 19px; }
.time-ruler { height: 40px; grid-template-columns: 410px repeat(3, 1fr); font-size: 16px; }
.channel-guide { height: 320px; }
.channel-row { height: 64px; grid-template-columns: 410px 1fr; }
.channel-identity { grid-template-columns: 58px 1fr; gap: 14px; padding: 6px 16px; }
.channel-logo { width: 52px; height: 52px; }
.channel-copy strong { font-size: 19px; }
.channel-copy span { margin-top: 4px; font-size: 15px; }
.epg-block { padding: 10px 16px; }
.epg-block strong { font-size: 17px; }
.epg-block span { font-size: 14px; }
.epg-placeholder { padding: 20px 16px; font-size: 16px; }
.guide-message { height: 64px; font-size: 18px; }
.player-status { font-size: 20px; }
.video-overlay { height: 104px; padding: 16px 20px; }
.player-controls {}
.player-control {
    min-width: 76px;
    height: 62px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0 14px;
    border-radius: 14px;
    font-size: 17px;
}
.player-control b { font-size: 25px; line-height: 1; }
.player-control span { white-space: nowrap; }
.player-control-primary { min-width: 118px; }
.video-stage.fullscreen .video-overlay { height: 136px; padding: 28px 56px; }
.video-stage.fullscreen .player-control { min-width: 128px; height: 70px; font-size: 20px; }
.catalog-main { height: 972px; padding: 24px 30px; }
.catalog-header h1 { font-size: 42px; }
.catalog-search { height: 62px; font-size: 20px; }
.content-grid { height: 748px; grid-template-columns: repeat(5, 1fr); grid-template-rows: repeat(2, 365px); gap: 18px; }
.content-card { height: 365px; }
.content-card img, .content-card .placeholder { height: 276px; object-fit: contain; }
.content-card strong { font-size: 20px; padding-top: 13px; }
.content-card span { font-size: 16px; }
.login-screen { grid-template-columns: 1fr 670px; gap: 70px; padding: 42px 115px; }
.login-card { width: 670px; padding: 30px 38px; }
.login-card h2 { margin: 6px 0 16px; font-size: 34px; }
.login-card label { margin: 11px 0; font-size: 19px; }
.login-card input { height: 54px; margin-top: 5px; font-size: 20px; }
.login-card .primary-button { margin-top: 10px; }
.secondary-button {
    width: 100%; min-height: 56px; margin-top: 10px; border-radius: 13px;
    color: #dce7f2; background: #1a2838; font-size: 19px;
}
.accounts-dialog {
    width: 1040px;
    max-height: 850px;
    overflow: hidden;
    border: 1px solid #2b3d50;
    border-radius: 24px;
    background: #0d1621;
    box-shadow: 0 40px 120px #000;
}
.accounts-dialog header {
    height: 118px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 24px 32px;
    border-bottom: 1px solid var(--app-widget-border);
}
.accounts-dialog h2 { margin: 7px 0 0; font-size: 36px; }
.icon-button { width: 62px; height: 62px; border-radius: 14px; color: white; background: #172536; font-size: 32px; }
.accounts-list { max-height: 590px; overflow-y: auto; padding: 18px 24px; }
.account-row {
    min-height: 112px;
    display: grid;
    grid-template-columns: 1fr auto auto auto;
    align-items: center;
    gap: 12px;
    margin: 8px 0;
    padding: 18px 20px;
    border: 1px solid var(--app-widget-border);
    border-radius: 16px;
    background: #101b28;
}
.account-row.active { border-color: var(--app-selection-border); background: var(--app-selection-surface); }
.account-copy { min-width: 0; }
.account-copy strong { display: block; font-size: 24px; }
.account-copy span { display: block; margin-top: 7px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #91a2b5; font-size: 17px; }
.account-action { min-width: 120px; height: 58px; padding: 0 18px; border-radius: 12px; color: white; background: #18283a; font-size: 18px; }
.account-action.danger { color: #ff9caf; }
.accounts-dialog footer { padding: 22px 28px 28px; border-top: 1px solid var(--app-widget-border); }
.accounts-dialog footer .primary-button { width: 100%; }

/* Large-screen legibility pass */
.brand strong { font-size: 31px; }
.brand span { font-size: 20px; }
.source-button span { font-size: 18px; }
.source-button strong { font-size: 23px; }
.nav-button { font-size: 25px; font-weight: 650; }
.rail-heading, .rail-heading strong { font-size: 19px; }
.rail-search { font-size: 21px; }
.category-item span { font-size: 23px; font-weight: 600; }
.category-item small { font-size: 23px; }
.eyebrow { font-size: 19px; }
.program-channel h1 { font-size: 33px; }
.program-channel span { font-size: 22px; }
.program-panel h2 { font-size: 39px; line-height: 1.15; }
.program-panel > p, .program-description { font-size: 23px; line-height: 1.4; }
.program-description { height: 106px; }
.guide-header h2 { font-size: 30px; }
.guide-actions { font-size: 21px; }
.time-ruler { font-size: 18px; }
.channel-row { height: 80px; }
.channel-identity { grid-template-columns: 68px 1fr; padding: 7px 16px; }
.channel-logo { width: 64px; height: 64px; }
.channel-copy strong { font-size: 23px; }
.channel-copy span { font-size: 19px; }
.epg-block { padding: 14px 16px; }
.epg-block strong { font-size: 21px; }
.epg-block span { font-size: 18px; }
.epg-placeholder { font-size: 18px; }
.player-status { font-size: 23px; font-weight: 650; }
.player-control { min-width: 82px; height: 64px; font-size: 19px; }
.player-control b { font-size: 28px; }
.video-stage.fullscreen .video-overlay {
    height: 154px;
    padding: 32px 62px;
    opacity: 1;
    transition: opacity 0.22s ease;
}
.video-stage.fullscreen .video-overlay.idle {
    opacity: 0;
    pointer-events: none;
}
.video-stage.fullscreen .player-status { font-size: 28px; }
.video-stage.fullscreen .player-controls {}
.video-stage.fullscreen .player-control {
    min-width: 146px;
    height: 82px;
    padding: 0 22px;
    border-radius: 18px;
    font-size: 24px;
}
.video-stage.fullscreen .player-control b { font-size: 34px; }
.catalog-header h1 { font-size: 46px; }
.catalog-search { font-size: 22px; }
.content-card strong { font-size: 23px; }
.content-card span { font-size: 18px; }
.pagination { font-size: 20px; }
.login-card label { font-size: 21px; }
.login-card input { font-size: 22px; }
.primary-button, .secondary-button { font-size: 21px; }
.account-copy strong { font-size: 27px; }
.account-copy span, .account-action { font-size: 20px; }
.toast { font-size: 22px; }

/* Keep the native Samsung surface inside the preview. Older Tizen versions
   render a black plugin surface even while AVPlay is idle. */
#av-player {
    position: fixed;
    left: 0;
    top: 0;
    width: 1px;
    height: 1px;
    pointer-events: none;
}
.demo-mode #av-player { display: none; }

.channel-row { overflow: hidden; }
.channel-row { display: flex; cursor: pointer; }
html.tizen-engine .category-item,
html.tizen-engine .channel-row {
    cursor: pointer;
}
.channel-identity { flex: 0 0 410px; }
.epg-track { flex: 1 1 auto; display: flex; height: 100%; align-self: stretch; }
.epg-block {
    flex: 1 1 0;
    width: 33.333%;
    height: 100%;
    display: flex;
    flex-direction: column;
    justify-content: center;
}
.epg-placeholder { flex: 1 1 auto; }
.channel-copy strong, .channel-copy span,
.epg-block strong, .epg-block span { line-height: 1.05; }
.epg-block { padding-top: 10px; padding-bottom: 10px; }
.channel-copy span { font-size: 20px; }
.epg-block span { font-size: 20px; }

.series-screen { padding: 28px 48px; }
.series-hero { height: 380px;margin-top: 18px; }
.series-hero img { width: 230px; height: 350px; object-fit: contain; }
.series-hero h1 { margin: 14px 0 18px; font-size: 58px; }
.series-hero p { max-width: 1320px; font-size: 25px; line-height: 1.5; }
.episode-grid {
    height: 480px;
    grid-template-columns: repeat(4, 1fr);
    gap: 16px;
    padding: 2px 4px 24px;
}
.season-heading {
    grid-column: 1 / -1;
    margin: 10px 0 0;
    color: var(--app-selection-color);
    font-size: 30px;
}
.episode-card { height: 112px; padding: 20px; font-size: 24px; }
.episode-card small { margin-top: 9px; font-size: 20px; }
.episode-grid > p { grid-column: 1 / -1; font-size: 24px; color: var(--app-eyebrow-color); }

/* Remote-first navigation: page transitions happen at list boundaries. */
.guide-actions,
.pagination { display: none !important; }

/* Compact TV playback dock */
.video-overlay {
    height: 82px;
    padding: 11px 16px;
    background: linear-gradient(transparent, rgba(3, 7, 12, 0.9));
}
.player-status { font-size: 18px; font-weight: 600; }
.player-controls {
    padding: 5px;
    border: 1px solid rgba(255, 255, 255, 0.13);
    border-radius: 34px;
    background: rgba(7, 13, 21, 0.82);
}
.player-control,
.player-control-primary {
    min-width: 0;
    width: 58px;
    height: 50px;
    padding: 0;
    border-color: transparent;
    border-radius: 25px;
    background: transparent;
    font-size: 17px;
}
.player-control b { font-size: 19px; font-weight: 750; }
.player-control span { display: none; }
.player-control-primary {
    color: #061018;
    background: var(--app-selection-color);
}
#player-fullscreen { width: 58px; }
.player-control:focus {
    box-shadow: inset 0 0 0 3px white, 0 0 18px var(--app-selection-glow);
}
.video-stage.fullscreen .video-overlay {
    height: 116px;
    padding: 22px 44px;
}
.video-stage.fullscreen .player-status { font-size: 23px; }
.video-stage.fullscreen .player-controls {padding: 7px; border-radius: 42px; }
.video-stage.fullscreen .player-control {
    min-width: 0;
    width: 70px;
    height: 64px;
    padding: 0;
    border-radius: 32px;
    font-size: 20px;
}
.video-stage.fullscreen .player-control b { font-size: 22px; }

/* Minimal TV controls inspired by modern media players. The icons are drawn
   in CSS so older Samsung fonts never replace them with emoji or empty boxes. */
.main-nav {
    /* Same flex-gap caveat as the category rows: the separation is carried by
       the buttons themselves so the three tabs never fuse on the TV. */
    padding: 5px;
    border-color: rgba(255, 255, 255, 0.08);
    border-radius: 18px;
    background: rgba(7, 14, 24, 0.76);
}
.main-nav .nav-button + .nav-button {
    margin-left: 4px;
}
.nav-button {
    position: relative;
    border: 0;
    border-radius: 14px;
    color: #aebccc;
}
.nav-button::before {
    content: '';
    display: inline-block;
    width: 7px;
    height: 7px;
    margin: 0 12px 3px 0;
    border: 2px solid currentColor;
    border-radius: 2px;
    opacity: 0.7;
}
.nav-button.active {
    border: 0;
    color: #f8fbff;
    background: rgba(54, 123, 226, 0.34);
    box-shadow: inset 0 0 0 1px rgba(119, 207, 255, 0.7);
}
.nav-button.active::before {
    border-color: var(--app-selection-color);
    background: var(--app-selection-color);
    box-shadow: 0 0 10px var(--app-selection-glow);
}

.program-panel h2 {
    display: -webkit-box;
    max-height: 90px;
    white-space: normal;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
}

.video-overlay {
    height: 76px;
    padding: 9px 14px;
    background: linear-gradient(transparent, rgba(2, 5, 10, 0.94));
}
.player-status {
    padding-left: 5px;
    color: rgba(255, 255, 255, 0.88);
    font-size: 17px;
    font-weight: 550;
}
.player-controls,
.video-stage.fullscreen .player-controls {
    padding: 4px;
    border: 1px solid rgba(255, 255, 255, 0.11);
    border-radius: 24px;
    background: rgba(8, 13, 21, 0.8);
    box-shadow: 0 8px 28px rgba(0, 0, 0, 0.32);
    -webkit-backdrop-filter: blur(12px);
}
.player-control,
.player-control-primary,
.video-stage.fullscreen .player-control {
    position: relative;
    min-width: 0;
    width: 46px;
    height: 42px;
    padding: 0;
    border: 0;
    border-radius: 20px;
    color: rgba(255, 255, 255, 0.82);
    background: transparent;
    box-shadow: none;
}
.player-control-primary {
    color: white;
    background: rgba(255, 255, 255, 0.09);
}
.player-control:focus,
.player-control-primary:focus {
    color: #051018;
    background: var(--app-selection-color);
    box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.92), 0 0 18px var(--app-selection-glow);
    transform: scale(1.06);
}
#player-fullscreen { width: 46px; }
.player-control b,
.video-stage.fullscreen .player-control b {
    position: relative;
    display: block;
    width: 22px;
    height: 22px;
    margin: auto;
    font-size: 13px;
    font-weight: 750;
    line-height: 22px;
}
.control-pause::before,
.control-pause::after {
    content: '';
    position: absolute;
    top: 3px;
    width: 5px;
    height: 16px;
    border-radius: 2px;
    background: currentColor;
}
.control-pause::before { left: 4px; }
.control-pause::after { right: 4px; }
.control-play::before {
    content: '';
    position: absolute;
    left: 6px;
    top: 3px;
    width: 0;
    height: 0;
    border-top: 8px solid transparent;
    border-bottom: 8px solid transparent;
    border-left: 13px solid currentColor;
}
.control-expand::before,
.control-expand::after {
    content: '';
    position: absolute;
    width: 7px;
    height: 7px;
    border-color: currentColor;
}
.control-expand::before {
    left: 2px;
    top: 2px;
    border-left: 3px solid;
    border-top: 3px solid;
    box-shadow: 11px 0 0 -1px transparent;
}
.control-expand::after {
    right: 2px;
    bottom: 2px;
    border-right: 3px solid;
    border-bottom: 3px solid;
}
.video-stage.fullscreen .video-overlay {
    height: 104px;
    padding: 20px 42px;
}
.video-stage.fullscreen .player-status { font-size: 21px; }
.video-stage.fullscreen .player-control,
.video-stage.fullscreen #player-fullscreen {
    width: 54px;
    height: 50px;
    border-radius: 24px;
}

/* Make long channel lists discoverable without adding focusable page buttons. */
.guide-scroll-cue {
    position: absolute;
    z-index: 8;
    top: 122px;
    right: 8px;
    bottom: 8px;
    width: 42px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: space-between;
    pointer-events: none;
}
.guide-scroll-cue i {
    position: relative;
    width: 38px;
    height: 38px;
    border: 2px solid rgba(49, 200, 255, 0.25);
    border-radius: 50%;
    background: rgba(6, 14, 24, 0.9);
    opacity: 0;
    transition: opacity 0.12s ease;
}
.guide-scroll-cue i::after {
    content: '';
    position: absolute;
    left: 11px;
    top: 11px;
    width: 11px;
    height: 11px;
    border-right: 3px solid var(--app-selection-color);
    border-bottom: 3px solid var(--app-selection-color);
}
.guide-scroll-cue .cue-up::after { transform: rotate(225deg); top: 14px; }
.guide-scroll-cue .cue-down::after { transform: rotate(45deg); top: 8px; }
.guide-scroll-cue.can-up .cue-up,
.guide-scroll-cue.can-down .cue-down {
    opacity: 1;
    filter: drop-shadow(0 0 5px var(--app-selection-glow));
}
.catalog-scroll-cue {
    top: 142px;
    right: 8px;
    bottom: 22px;
}

/* Keep the complete focus surface inside the panel and tint every EPG cell,
   including the text columns, instead of drawing only an edge around them. */
.channel-row {
    width: 100%;
    margin: 0;
    border-radius: 4px;
}
.channel-row:focus {
    background: rgba(32, 129, 218, 0.28);
    box-shadow: inset 0 0 0 4px var(--app-selection-color), 0 0 16px rgba(34, 211, 238, 0.22);
}
.channel-row:focus .channel-identity,
.channel-row:focus .epg-block,
.channel-row:focus .epg-placeholder {
    background-color: rgba(24, 103, 180, 0.16);
}

/* The programme panel already carries the full channel name. */
.player-status {
    max-width: 230px;
    padding-left: 6px;
}

/* Overscan safe area, inert while --nova-safe-inset is 0px: every value below
   collapses back to the authored 1920x1080 geometry. Raise the variable and
   the shell pads in while html/body keep painting the full canvas, so the
   frame stays app-coloured. The canvas has no slack, so each stacked region
   gives back exactly one inset and the sums still close. */
#app-shell {
    padding: var(--nova-safe-inset) var(--nova-safe-inset-x);
}
.app-screen { width: auto; height: calc(972px - 2 * var(--nova-safe-inset)); }
.login-screen { width: auto; height: calc(1080px - 2 * var(--nova-safe-inset)); }
/* Definite heights, not 100%: these are auto-row grid items, so a percentage
   would resolve against their own content and let a long list grow the row. */
.category-rail,
.live-main,
.catalog-main { height: calc(972px - 2 * var(--nova-safe-inset)); }
.category-list { height: calc(830px - 2 * var(--nova-safe-inset)); }
.preview-row {
    height: calc(494px - var(--nova-safe-inset));
    grid-template-columns: calc(850px - var(--nova-safe-inset-x)) 1fr;
}
.video-stage,
.program-panel { height: calc(478px - var(--nova-safe-inset)); }
.video-stage { width: 100%; }
.program-description { height: calc(106px - var(--nova-safe-inset)); }
.guide-panel { height: calc(446px - var(--nova-safe-inset)); }
.channel-guide { height: calc(320px - var(--nova-safe-inset)); }
.channel-row { height: calc(80px - var(--nova-safe-inset) / 4); }
.channel-identity { grid-template-columns: calc(68px - var(--nova-safe-inset) / 4) 1fr; }
.channel-logo {
    width: calc(64px - var(--nova-safe-inset) / 4);
    height: calc(64px - var(--nova-safe-inset) / 4);
}
.content-grid {
    height: calc(748px - var(--nova-safe-inset));
    grid-template-rows: repeat(2, calc(365px - var(--nova-safe-inset) / 2));
}
.content-card { height: calc(365px - var(--nova-safe-inset) / 2); }
.content-card img,
.content-card .placeholder { height: calc(276px - var(--nova-safe-inset) / 2); }
.loading-state,
.empty-state { height: calc(746px - var(--nova-safe-inset)); }
.series-hero { height: calc(380px - var(--nova-safe-inset)); }
.series-hero img { height: calc(350px - var(--nova-safe-inset)); }
.episode-grid { height: calc(480px - 2 * var(--nova-safe-inset)); }
.toast { bottom: calc(52px + var(--nova-safe-inset)); }

/* The picture itself keeps the whole panel: the fullscreen stage is
   position: fixed, so it never sees the shell padding. Only its control dock
   is pulled back inside the safe area. */
.video-stage.fullscreen .video-overlay {
    height: calc(104px + var(--nova-safe-inset));
    padding: 20px calc(42px + var(--nova-safe-inset-x)) calc(20px + var(--nova-safe-inset));
}

/* Picture modes. JS toggles exactly one .fit-* class on #video-stage, the same
   container that already carries .fullscreen, so the two stack. The media box
   itself never moves; only the fit rule inside it changes. */
#html-player,
.video-empty {
    width: calc(850px - var(--nova-safe-inset-x));
    height: calc(478px - var(--nova-safe-inset));
}
.video-stage.fit-contain #html-player { object-fit: contain; }
.video-stage.fit-cover #html-player { object-fit: cover; }
/* There is no oversizing rule here any more, and that is the fix.

   Bundle 80 magnified the picture by enlarging #html-player past the stage
   (width: Z*100% with a negative offset) whenever the page decided a film
   needed a punch-in. Two things went wrong with it on the Q70R:

   - Tizen 5's compositor resamples the part of an oversized layer that hangs
     outside its clip, which is the blurred vertical band down the side.
   - The rule was keyed on `.picture-zoomed` alone, so it outranked
     `.fit-fill` on left/top/width/height — the band appeared in Étiré too,
     a mode that must never be magnified at all. (Verified by evaluating the
     real cascade: fill kept object-fit: fill but inherited the zoomed box.)

   Every player already does cover in hardware: AVPlay CROPPED_FULL, ExoPlayer
   RESIZE_MODE_ZOOM, and object-fit: cover here. The picture element is now
   always exactly the stage, in every mode, and the decoder does the scaling —
   so nothing is ever resampled and no edge can smear. */
.video-stage.fit-fill #html-player,
.video-stage.fit-fill #av-player { object-fit: fill; }
/* Forced ratios stretch the picture into a box of the wanted shape. The stage
   is already 16:9, so 16:9 is the whole box and 4:3 is three quarters of its
   width, centred. */
.video-stage.fit-169 #html-player,
.video-stage.fit-169 #av-player { object-fit: fill; }
.video-stage.fit-43 #html-player { left: 12.5%; width: 75%; object-fit: fill; }

/* Mode badge: same pill as the playback dock so it reads as part of the
   overlay. Add .hidden to remove it, .idle to fade it out with the dock. */
.player-fit-badge {
    position: absolute;
    z-index: 4;
    top: calc(16px + var(--nova-safe-inset));
    right: calc(16px + var(--nova-safe-inset-x));
    padding: 8px 16px;
    border: 1px solid rgba(255, 255, 255, 0.11);
    border-radius: 20px;
    color: rgba(255, 255, 255, 0.88);
    background: rgba(8, 13, 21, 0.8);
    box-shadow: 0 8px 28px rgba(0, 0, 0, 0.32);
    -webkit-backdrop-filter: blur(12px);
    font-size: 17px;
    font-weight: 550;
    letter-spacing: 0.08em;
    opacity: 1;
    transition: opacity 0.22s ease;
    pointer-events: none;
}
.video-stage.fullscreen .player-fit-badge {
    top: calc(28px + var(--nova-safe-inset));
    right: calc(44px + var(--nova-safe-inset-x));
    padding: 10px 22px;
    font-size: 21px;
}
.player-fit-badge.idle {
    opacity: 0;
}

/* ==========================================================================
   10-foot typography pass.
   The app was authored at desk-reading sizes; from a sofa nothing under about
   28px is legible on a 1080p panel. Everything below is driven by the --type-*
   scale so sizes stay in step. The canvas is a fixed 1920x1080 with no
   scrolling, so each block that grows is paid for by a matching height change
   further down.
   ========================================================================== */
:root {
    --type-2xs: 18px; /* control glyphs only */
    --type-xs: 22px; /* counters, code blocks */
    --type-sm: 26px; /* metadata, times, secondary lines */
    --type-md: 28px; /* body minimum at 3m */
    --type-lg: 31px; /* emphasised body, nav tabs */
    --type-xl: 36px; /* section titles */
    --type-2xl: 40px; /* panel titles */
    --type-3xl: 50px; /* screen titles */
    --type-4xl: 64px; /* hero */
}

/* --- Top bar: 108 -> 124 so the enlarged brand and source blocks fit. ----- */
.topbar { height: 124px; }
.brand { width: 360px; }
.brand strong { font-size: 38px; line-height: 1.15; }
.brand span { font-size: var(--type-sm); }
.main-nav { width: 720px; padding: 6px; }
.nav-button { min-width: 226px; height: 68px; font-size: var(--type-lg); }
.source-button { width: 360px; height: 74px; }
.topbar-actions { width: 360px; }
.topbar-actions .source-button { width: auto; }
.language-button { width: 100px; height: 74px; }
.language-button strong { font-size: 20px; }
.language-globe { width: 27px; height: 27px; }
.source-button span { font-size: 24px; }
.source-button strong { max-width: 310px; font-size: var(--type-md); }

/* --- Screens lose the 16px the top bar gained. --------------------------- */
.app-screen { height: calc(956px - 2 * var(--nova-safe-inset)); }
.category-rail,
.live-main,
.catalog-main { height: calc(956px - 2 * var(--nova-safe-inset)); }
/* Grid items default to a min-content floor. Clamped -webkit-box text reports
   its whole unwrapped line as min-content, which would push the 1fr tracks
   past 1920 and drag the canvas with them. */
.live-main,
.catalog-main,
.program-panel { min-width: 0; }

/* --- Category rail ------------------------------------------------------- */
.rail-heading { height: 48px; }
.rail-heading span,
.rail-heading strong { font-size: 24px; }
.rail-search { height: 62px; margin-bottom: 14px; font-size: var(--type-md); }
.category-list { height: calc(796px - 2 * var(--nova-safe-inset)); }
/* Rows carried zero vertical separation, so the pinned shelves' surfaces,
   the focus fill and the selection gradient all touched their neighbours and
   read as one solid blob. 8px of margin (not flex gap: this WebView is
   Chromium 63, flex gap arrived in 84) gives every surface its own edge. */
.category-item { min-height: 72px; margin-bottom: 8px; }
/* 8px row margin + the divider's 14px section break = 22px; centre the rule. */
.category-item.category-pinned + .category-item:not(.category-pinned)::before {
    top: -11px;
}
/* 24px, not 29: measured against this provider's 826 real category names in
   the 260px label column, 29px left only 54% of them readable whole — the
   rest ellipsed mid-word, which is worse than smaller type at 3m. 24px takes
   that to 77%. The count needs room too, hence the smaller secondary size
   below rather than the 29px that made every row read as two titles. */
.category-item span { font-size: 24px; }
.category-item small {
    font-size: 20px;
    font-variant-numeric: tabular-nums;
}
.eyebrow { font-size: 24px; }

/* --- Live: preview row. The stage shrinks to 720x405 so the guide can hold
       full-size rows; the now-playing panel gains the width the video gives
       up (682 -> 812), which is where the larger text goes. ---------------- */
.preview-row {
    height: calc(421px - var(--nova-safe-inset));
    grid-template-columns: calc(720px - var(--nova-safe-inset-x)) 1fr;
}
.video-stage,
.program-panel { height: calc(405px - var(--nova-safe-inset)); }
/* Fill the stage's padding box exactly; the stage has a 1px border, so a
   border-box-sized media element would bleed 2px under it. */
#html-player,
.video-empty { width: 100%; height: 100%; }
.video-empty strong { font-size: var(--type-lg); }
.video-empty span { font-size: var(--type-sm); }
.program-panel { padding: 22px 34px; }
.program-channel { height: 90px; margin-bottom: 0; }
.program-logo-shell { width: 66px; height: 66px; font-size: 34px; }
.program-channel h1 { max-width: 640px; font-size: 38px; }
.program-channel span { font-size: var(--type-sm); }
.program-panel h2 {
    max-height: 90px;
    margin: 10px 0 6px;
    font-size: var(--type-2xl);
    line-height: 1.12;
}
.program-panel > p { margin: 6px 0; font-size: var(--type-sm); }
/* Two explicit clamped lines with an ellipsis rather than a hard cut: the
   synopsis is arbitrary length and the panel has no room to grow. */
.program-description {
    height: calc(68px - var(--nova-safe-inset));
    margin: 0;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    text-overflow: ellipsis;
    font-size: var(--type-sm);
    line-height: 1.3;
}
.program-progress { margin-top: 10px; }

/* --- Live: guide panel. The channel-search block is permanent markup now,
       so the panel is sized for header + ruler + search + rows instead of
       clipping the last rows. Header puts its eyebrow beside the title to buy
       back a line. Rows are 88px, which fits 3 at a time; app.js renders a
       page of 4, so the guide scrolls the last one into view on focus. ----- */
.guide-panel { height: calc(503px - var(--nova-safe-inset)); }
.guide-header { height: 72px; }
.guide-header > div:first-child {
    display: flex;
    align-items: baseline;
}
.guide-header h2 { margin: 0; font-size: var(--type-xl); }
.guide-total { font-size: var(--type-sm); }
.time-ruler { height: 54px; font-size: var(--type-sm); }
.time-ruler::before,
.time-ruler span { padding: 9px 16px; }
.guide-panel .rail-heading { height: 36px; }
.guide-panel .rail-search { margin-bottom: 8px; }
/* Four channels rather than three. The list grew by the 36px freed with the
   redundant "CHAÎNES" heading, and the row lost 13px — still taller than the
   64px rows the smaller breakpoint has always used, so nothing gets tighter
   than a layout already proven readable. app.js measures this rather than
   assuming it, so if a future edit no longer fits four whole rows the guide
   simply shows three and says so. */
.channel-guide {
    height: calc(300px - var(--nova-safe-inset));
    overflow-y: auto;
}
.channel-row { height: calc(75px - var(--nova-safe-inset) / 3); }
.channel-identity {
    grid-template-columns: calc(60px - var(--nova-safe-inset) / 3) 1fr;
    padding: 6px 16px;
}
.channel-logo {
    width: calc(56px - var(--nova-safe-inset) / 3);
    height: calc(56px - var(--nova-safe-inset) / 3);
    font-size: var(--type-xs);
}
/* Two lines of text in a 75px row. The padding, not the type, is what gives:
   the block reserved 20px of it and left 53px for 59.4px of text, so both
   lines were cut. Text size and line-height are unchanged — the type has to
   stay readable at 3m, and tightening line-height below 1.1 pulls the line box
   under the glyphs' own height, which clips ascenders instead of saving room. */
.channel-copy strong { font-size: 29px; line-height: 1.1; }
.channel-copy span { font-size: var(--type-sm); line-height: 1.1; }
.epg-block { padding: 4px 16px; }
.epg-block strong { font-size: var(--type-md); line-height: 1.1; }
.epg-block span { font-size: var(--type-sm); line-height: 1.1; }
.epg-placeholder { padding: 16px; font-size: var(--type-sm); }
.guide-message { height: 88px; font-size: var(--type-md); }

/* --- Playback dock ------------------------------------------------------- */
.video-overlay { height: 86px; padding: 12px 16px; }
.player-status { max-width: 320px; font-size: var(--type-sm); }
.player-control,
.player-control-primary { width: 58px; height: 50px; }
#player-fullscreen { width: 58px; }
.player-control b,
.video-stage.fullscreen .player-control b {
    width: 30px;
    height: 26px;
    font-size: var(--type-2xs);
    line-height: 26px;
}
.control-pause::before,
.control-pause::after { top: 4px; width: 6px; height: 18px; }
.control-pause::before { left: 8px; }
.control-pause::after { right: 8px; }
.control-play::before {
    left: 10px;
    top: 3px;
    border-top-width: 10px;
    border-bottom-width: 10px;
    border-left-width: 16px;
}
.control-expand::before,
.control-expand::after { width: 9px; height: 9px; }
.control-expand::before { left: 4px; top: 4px; }
.control-expand::after { right: 4px; bottom: 4px; }
.video-stage.fullscreen .video-overlay {
    height: calc(126px + var(--nova-safe-inset));
    padding: 22px calc(42px + var(--nova-safe-inset-x)) calc(22px + var(--nova-safe-inset));
}
.video-stage.fullscreen .player-status { font-size: var(--type-lg); }
.video-stage.fullscreen .player-control,
.video-stage.fullscreen #player-fullscreen { width: 68px; height: 58px; }
.player-fit-badge { font-size: var(--type-sm); }
.video-stage.fullscreen .player-fit-badge { font-size: var(--type-lg); }

/* --- Catalog ------------------------------------------------------------- */
.catalog-header h1 { font-size: var(--type-3xl); }
.catalog-search { width: 500px; height: 68px; font-size: var(--type-md); }
.content-grid {
    height: calc(800px - var(--nova-safe-inset));
    grid-template-rows: repeat(2, calc(391px - var(--nova-safe-inset) / 2));
}
.content-card { height: calc(391px - var(--nova-safe-inset) / 2); }
.content-card img,
.content-card .placeholder { height: calc(286px - var(--nova-safe-inset) / 2); }
.content-card .placeholder { font-size: var(--type-4xl); }
/* Explicit line-height, not `normal`: Arabic and Hebrew fonts reserve a much
   taller normal line box than Latin at the same font-size, so this provider's
   |AR| series titles pushed the card 8-11px past its fixed height and had
   their rating clipped. A stated line-height makes the row height identical
   whatever the script. */
.content-card strong {
    padding-top: 13px;
    font-size: 29px;
    line-height: 1.25;
    /* Two lines by default: this provider sends no release date, so the meta
       row below is absent and its space belongs to the title, which was being
       cut mid-word at one line. Cards that DO carry a date fall back to one
       line via .has-meta so the card height stays fixed either way. */
    height: calc(29px * 2.5 + 15px);
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    white-space: normal;
}
.content-card.has-meta strong {
    height: calc(29px * 1.25 + 15px);
    -webkit-line-clamp: 1;
}
.content-card span {
    font-size: var(--type-sm);
    line-height: 1.25;
}
.loading-state,
.empty-state { height: calc(800px - var(--nova-safe-inset)); }
.loading-state p { font-size: var(--type-md); }
.empty-state h2 { font-size: var(--type-xl); }

/* --- Series -------------------------------------------------------------- */
.series-hero { height: calc(330px - var(--nova-safe-inset)); }
.series-hero img { height: calc(320px - var(--nova-safe-inset)); }
.series-hero h1 { margin: 8px 0 10px; font-size: var(--type-4xl); }
.series-hero p { max-width: 1320px; font-size: var(--type-lg); line-height: 1.4; }
.detail-meta { font-size: var(--type-md); }
.detail-actions .small-button,
.detail-actions .primary-button { min-width: 240px; }
.season-heading { font-size: var(--type-xl); }
.episode-grid {
    height: calc(492px - 2 * var(--nova-safe-inset));
    grid-template-columns: repeat(4, 1fr);
    gap: 16px;
}
.episode-card { height: 124px; font-size: var(--type-lg); }
.episode-card small { font-size: var(--type-sm); }
.episode-grid > p { font-size: 29px; }

/* --- Login / onboarding -------------------------------------------------- */
.welcome-copy h1 { font-size: 70px; }
.welcome-copy p { max-width: 860px; font-size: 29px; }
.login-card { padding: 26px 38px; }
.login-card h2 { margin: 6px 0 14px; font-size: var(--type-2xl); }
.login-card label { margin: 6px 0; font-size: var(--type-md); }
.login-card input,
.rail-search,
.catalog-search { height: 62px; font-size: var(--type-md); }
.remember-row input { width: 30px; height: 30px; }
.primary-button { min-height: 66px; font-size: var(--type-md); }
.secondary-button { min-height: 64px; font-size: var(--type-md); }
.pair-phone-button { height: 66px; font-size: var(--type-md); }
.pair-phone-icon { width: 40px; height: 40px; font-size: 20px; }
.form-error { min-height: 34px; font-size: var(--type-sm); }
.small-button { min-height: 60px; font-size: var(--type-sm); }

/* --- Dialogs, toast ------------------------------------------------------ */
.icon-button { width: 68px; height: 68px; font-size: 38px; }
.dialog { width: 620px; }
.dialog h2 { font-size: 38px; }
.dialog-detail { font-size: var(--type-md); }
.pairing-dialog { width: 980px; }
.pairing-dialog h2 { font-size: var(--type-2xl); }
.pairing-content li { font-size: var(--type-md); }
.pairing-code-block span { font-size: var(--type-xs); }
#pairing-code { font-size: 38px; }
.pairing-expiry,
#pairing-status { font-size: var(--type-sm); }
.accounts-dialog { width: 1140px; }
.accounts-dialog header { height: 130px; }
.accounts-dialog h2 { font-size: 42px; }
.accounts-list { max-height: 560px; }
.account-row { min-height: 128px; }
.account-copy strong { font-size: 34px; }
.account-copy span { font-size: var(--type-sm); }
.account-action { min-width: 140px; height: 64px; font-size: var(--type-sm); }
.toast { font-size: var(--type-md); }

/* ==========================================================================
   Finish pass: control glyphs, focus, alignment.
   ========================================================================== */

/* --- Sound and picture-format glyphs. Drawn in CSS like the rest of the
       family so they inherit currentColor and stay monochrome against the
       dock; an emoji would render in its own colour font at its own weight.
       The speaker body is shared and ::after carries the state mark, so the
       on/off pair reads as one control rather than two icons. --------------- */
/* Longhand offsets, not the `inset` shorthand: this app targets Tizen 5.0,
   whose WebView predates it, and an unsupported shorthand is dropped whole —
   the ::before would keep position:absolute with auto offsets and collapse to
   nothing, silently erasing the glyph. Same reason everywhere below. */
.control-sound::before,
.control-muted::before {
    content: '';
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background: currentColor;
    clip-path: polygon(10% 35%, 27% 35%, 57% 6%, 57% 94%, 27% 65%, 10% 65%);
}
.control-sound::after {
    content: '';
    position: absolute;
    right: 2px;
    top: 5px;
    width: 16px;
    height: 16px;
    border: 4px solid transparent;
    border-right-color: currentColor;
    border-radius: 50%;
}
/* Muted swaps the wave for a cross. A slash across the body would have to be
   drawn in the same colour as the body and would vanish where they overlap. */
.control-muted::after,
.player-control.is-muted .control-sound::after {
    content: '';
    position: absolute;
    right: 1px;
    top: 8px;
    width: 11px;
    height: 11px;
    border: 0;
    border-radius: 0;
    background:
        linear-gradient(
            45deg,
            transparent calc(50% - 1.5px),
            currentColor calc(50% - 1.5px),
            currentColor calc(50% + 1.5px),
            transparent calc(50% + 1.5px)
        ),
        linear-gradient(
            -45deg,
            transparent calc(50% - 1.5px),
            currentColor calc(50% - 1.5px),
            currentColor calc(50% + 1.5px),
            transparent calc(50% + 1.5px)
        );
}
/* Three stacked rules: the channel list. Drawn in CSS like every other
   control mark rather than shipping an icon font. */
.control-list::before {
    content: '';
    position: absolute;
    top: 4px;
    right: 1px;
    bottom: 4px;
    left: 1px;
    background:
        linear-gradient(currentColor, currentColor) left 0 top 0 / 100% 3px no-repeat,
        linear-gradient(currentColor, currentColor) left 0 center / 100% 3px no-repeat,
        linear-gradient(currentColor, currentColor) left 0 bottom 0 / 100% 3px no-repeat;
    border-radius: 1px;
}
.control-list::after {
    display: none;
}

/* A 4:3 block inside a 16:9 frame — the ratio choice itself, and distinct from
   the open corner brackets that already mean fullscreen. */
.control-fit::before {
    content: '';
    position: absolute;
    top: 4px;
    right: 0;
    bottom: 4px;
    left: 0;
    border: 3px solid currentColor;
    border-radius: 4px;
}
.control-fit::after {
    content: '16:9';
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: currentColor;
    background: transparent;
    font-size: 9px;
    font-weight: 750;
    letter-spacing: -0.04em;
}
/* "−30"/"+30" are 33px of text in a 30px box; the glyph slot was sized for
   drawn marks, so the numeric ones get their own width. */
.player-control b.control-seek,
.video-stage.fullscreen .player-control b.control-seek { width: 40px; }

/* --- Section and pinned-row glyphs. Same CSS-drawn currentColor family as
       the dock controls: border boxes for outlines, clip-path for solid
       marks, box-shadow to repeat a bar. The whitespace the markup leaves
       between <i> and <span> collapses to one space, so the icon margins
       stay small. ---------------------------------------------------------- */
.nav-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.nav-icon {
    position: relative;
    width: 26px;
    height: 26px;
    margin-right: 10px;
    flex: none;
}
/* Direct: a screen on its stand. */
.nav-live::before {
    content: '';
    position: absolute;
    left: 1px;
    right: 1px;
    top: 2px;
    bottom: 7px;
    border: 2.5px solid currentColor;
    border-radius: 4px;
}
.nav-live::after {
    content: '';
    position: absolute;
    left: 7px;
    right: 7px;
    bottom: 2px;
    height: 2.5px;
    border-radius: 2px;
    background: currentColor;
}
/* Films: clapperboard — solid lid over an outlined body. */
.nav-vod::before {
    content: '';
    position: absolute;
    left: 1px;
    right: 1px;
    top: 9px;
    bottom: 2px;
    border: 2.5px solid currentColor;
    border-radius: 3px;
}
.nav-vod::after {
    content: '';
    position: absolute;
    left: 1px;
    right: 1px;
    top: 2px;
    height: 4.5px;
    border-radius: 2px;
    background: currentColor;
}
/* Séries: an episode stacked behind the screen. */
.nav-series::before {
    content: '';
    position: absolute;
    left: 1px;
    right: 1px;
    top: 8px;
    bottom: 2px;
    border: 2.5px solid currentColor;
    border-radius: 4px;
}
.nav-series::after {
    content: '';
    position: absolute;
    left: 5px;
    right: 5px;
    top: 2px;
    height: 2.5px;
    border-radius: 2px;
    background: currentColor;
}

.pin-icon {
    position: relative;
    width: 24px;
    height: 24px;
    margin-right: 12px;
    flex: none;
}
/* Favoris: star. */
.pin-fav::before {
    content: '';
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background: currentColor;
    clip-path: polygon(
        50% 2%, 63% 36%, 98% 38%, 71% 60%,
        81% 95%, 50% 74%, 19% 95%, 29% 60%,
        2% 38%, 37% 36%
    );
}
/* Récents: clock face with hands at ten past ten-ish. */
.pin-recent::before {
    content: '';
    position: absolute;
    top: 1px;
    right: 1px;
    bottom: 1px;
    left: 1px;
    border: 2.5px solid currentColor;
    border-radius: 50%;
}
.pin-recent::after {
    content: '';
    position: absolute;
    left: 10.5px;
    top: 6px;
    width: 7px;
    height: 7.5px;
    background: currentColor;
    clip-path: polygon(0 0, 34% 0, 34% 66%, 100% 66%, 100% 100%, 0 100%);
}
/* Toutes les chaînes: list bars. */
.pin-all::before {
    content: '';
    position: absolute;
    left: 2px;
    right: 2px;
    top: 4px;
    height: 3.5px;
    border-radius: 2px;
    background: currentColor;
    box-shadow:
        0 7px 0 currentColor,
        0 14px 0 currentColor;
}
/* Tous les films: play badge. */
.pin-movies::before {
    content: '';
    position: absolute;
    top: 1px;
    right: 1px;
    bottom: 1px;
    left: 1px;
    border: 2.5px solid currentColor;
    border-radius: 50%;
}
.pin-movies::after {
    content: '';
    position: absolute;
    left: 9.5px;
    top: 7.5px;
    width: 8px;
    height: 9px;
    background: currentColor;
    clip-path: polygon(0 0, 100% 50%, 0 100%);
}
/* Toutes les séries: the same stacked-episode mark as the nav. */
.pin-series::before {
    content: '';
    position: absolute;
    left: 1px;
    right: 1px;
    top: 7px;
    bottom: 1px;
    border: 2.5px solid currentColor;
    border-radius: 4px;
}
.pin-series::after {
    content: '';
    position: absolute;
    left: 4px;
    right: 4px;
    top: 1px;
    height: 2.5px;
    border-radius: 2px;
    background: currentColor;
}
/* The focus recipe whitens the label; the glyph follows it. */
.category-item:focus .pin-icon { color: #ffffff; }

/* --- Rating badge on catalogue posters. Sits on the artwork, top-right, the
       way desktop IPTV clients present it — the line under the title is then
       free to carry the year instead of the two fighting for one slot. The
       pill is opaque enough to stay readable over a bright poster. --------- */
.card-rating {
    position: absolute;
    top: 10px;
    right: 10px;
    z-index: 1;
    display: flex;
    align-items: center;
    height: 38px;
    padding: 0 12px 0 34px;
    border-radius: 19px;
    color: #ffffff;
    background: rgba(6, 11, 18, 0.82);
    font-size: var(--type-sm);
    font-weight: 650;
    font-style: normal;
    line-height: 1;
}
/* Same star as the Favoris shelf, in the provider-rating amber. */
.card-rating::before {
    content: '';
    position: absolute;
    left: 10px;
    top: 50%;
    width: 18px;
    height: 18px;
    margin-top: -9px;
    background: #f5c451;
    clip-path: polygon(
        50% 2%, 63% 36%, 98% 38%, 71% 60%,
        81% 95%, 50% 74%, 19% 95%, 29% 60%,
        2% 38%, 37% 36%
    );
}

/* --- Focus. One recipe everywhere instead of a ring here and a fill there.
       The dark band just inside the accent keeps the ring readable when the
       focused element is a bright poster. ---------------------------------- */
[data-focusable]:focus {
    box-shadow:
        inset 0 0 0 4px var(--app-selection-color),
        inset 0 0 0 6px rgba(4, 10, 18, 0.55),
        0 0 26px var(--app-selection-glow);
}
.category-item:focus,
.content-card:focus,
.episode-card:focus,
.nav-button:focus,
.small-button:focus,
.source-button:focus,
.account-row:focus,
.account-action:focus,
.pair-phone-button:focus,
.icon-button:focus {
    background-color: var(--app-focus-fill);
}
.channel-row:focus { background-color: var(--app-focus-fill); }
.category-item:focus span,
.content-card:focus strong,
.episode-card:focus,
.channel-row:focus .channel-copy strong {
    color: #ffffff;
}

/* --- Guide panel gutter. Header, ruler, search block and channel rows each
       started at a different x; they now share one 16px edge. ------------- */
.guide-header { padding: 12px 16px; }
.guide-panel .rail-heading { padding: 0 16px; }
/* width MUST be explicit here: an <input> with width:auto falls back to its
   intrinsic size-attribute width (~422px) instead of filling the gutter —
   in the 340px category rail that poked 112px into the video area. */
.guide-panel .rail-search {
    width: calc(100% - 32px);
    margin: 0 16px 8px;
}
/* Same idea in the rail: heading and item text share one column, and the
   search field's border sits on that column rather than out at the panel
   padding. The list's stray 3px side padding pushed items off it. */
.category-rail .rail-heading { padding-left: 16px; padding-right: 16px; }
.category-list { padding-left: 0; padding-right: 0; }
.category-rail .rail-search {
    width: calc(100% - 32px);
    margin: 0 16px 14px;
}

/* The chevron cue is a 42px strip pinned to the panel's right edge; without
   this reserve the last EPG column's text ran underneath it. */
.channel-guide { padding-right: 52px; }

/* --- Picture settings panel, opened by the dock's Image button. Anchored
       above the dock inside the stage so it works windowed and fullscreen. -- */
.image-settings {
    position: absolute;
    right: 14px;
    bottom: 96px;
    z-index: 9;
    min-width: 340px;
    padding: 18px;
    border: 1px solid rgba(255, 255, 255, 0.11);
    border-radius: 14px;
    background: rgba(8, 13, 21, 0.96);
}
.image-settings-title {
    margin-bottom: 12px;
    color: #8fa3b8;
    font-size: var(--type-xs, 20px);
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}
.image-settings-option {
    width: 100%;
    min-height: 56px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 8px 14px;
    border-radius: 10px;
    color: #d1dbe6;
    background: transparent;
    font-size: var(--type-md, 26px);
    text-align: left;
}
.image-settings-option small {
    visibility: hidden;
    color: var(--app-selection-color);
    font-size: var(--type-md, 26px);
}
.image-settings-option.selected {
    color: var(--app-selection-color);
    background: var(--app-selection-surface);
}
.image-settings-option.selected small { visibility: visible; }
.video-stage.fullscreen .image-settings {
    right: auto;
    left: 50%;
    bottom: 140px;
    transform: translateX(-50%);
}
/* The track list can run long (a stream may carry a dozen audio languages), so
   unlike the four fixed picture modes it scrolls rather than growing past the
   top of the screen. */
.track-settings .image-settings-list {
    max-height: 420px;
    overflow-y: auto;
}

/* Timeline for films, episodes and catch-up — the "truc normal" every player
   has. Absolutely positioned across the top of the overlay; the overlay grows
   via .has-timeline. Live playback never shows it (no seekable window), and
   non-live playback is always fullscreen, so the windowed 850px bar never has
   to fit it. */
.player-timeline {
    position: absolute;
    top: 16px;
    right: 56px;
    left: 56px;
    display: flex;
    align-items: center;
}
.player-timeline > span {
    flex: 0 0 auto;
    min-width: 96px;
    color: var(--app-on-surface);
    font-size: 24px;
    font-variant-numeric: tabular-nums;
}
#timeline-elapsed { text-align: right; padding-right: 18px; }
#timeline-total { text-align: left; padding-left: 18px; }
.timeline-bar {
    position: relative;
    flex: 1 1 auto;
    height: 12px;
    padding: 0;
    border: 0;
    border-radius: 6px;
    background: rgba(255, 255, 255, 0.22);
}
.timeline-fill {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 0;
    border-radius: 6px;
    background: var(--app-selection-color);
}
.timeline-thumb {
    position: absolute;
    top: 50%;
    left: 0;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: var(--app-on-surface);
    transform: translate(-13px, -13px);
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.55);
}
/* The focus recipe outlines the whole bar; the thumb grows too so the eye
   lands on the part the arrows move. */
.timeline-bar:focus .timeline-thumb {
    width: 34px;
    height: 34px;
    transform: translate(-17px, -17px);
    background: var(--app-selection-color);
}
.timeline-bubble {
    position: absolute;
    bottom: 26px;
    padding: 6px 14px;
    border-radius: 10px;
    background: var(--app-on-surface);
    color: #041017;
    font-size: 24px;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    transform: translateX(-50%);
}
/* Room for the timeline row above the status+controls row. */
.video-stage.fullscreen .video-overlay.has-timeline {
    height: 200px;
    padding-top: 78px;
}

/* The windowed dock carries up to seven controls beside the status text in an
   850px bar. The bar must never wrap or push a control outside itself: the
   status line is the flexible part and shrinks with an ellipsis instead. */
.video-overlay {
    flex-wrap: nowrap;
}
.player-status {
    flex-shrink: 1;
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}
.player-controls {
    flex-shrink: 0;
}

/* Catch-up: past programmes of one channel, grouped by day. Reuses the
   accounts-dialog frame; these are just its rows. */
.catchup-day {
    margin: 18px 4px 8px;
    color: var(--app-eyebrow-color);
    font-size: 20px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}
.catchup-row {
    display: flex;
    align-items: baseline;
    width: 100%;
    min-height: 64px;
    margin: 6px 0;
    padding: 12px 18px;
    border: 1px solid var(--app-widget-border);
    border-radius: 12px;
    color: var(--app-on-surface);
    background: transparent;
    text-align: left;
    font-size: 24px;
}
.catchup-row time {
    flex: 0 0 150px;
    color: var(--app-eyebrow-color);
    font-size: 22px;
}
.catchup-row span {
    flex: 1;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* Three-bar equalizer: the audio-track glyph. Distinct from the speaker
   mute mark, which is a different control. */
.control-audio::before {
    content: '';
    position: absolute;
    left: 50%;
    top: 3px;
    width: 4px;
    height: 20px;
    margin-left: -2px;
    background: currentColor;
    border-radius: 1px;
    box-shadow:
        -7px 5px 0 0 currentColor,
        7px 8px 0 0 currentColor;
}
.control-audio::after {
    display: none;
}
/* Captions badge for the subtitle button only. */
.control-tracks::before {
    content: 'CC';
    position: absolute;
    top: 5px;
    right: 0;
    bottom: 8px;
    left: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 3px solid currentColor;
    border-radius: 5px;
    font-size: 9px;
    font-weight: 800;
    letter-spacing: 0.02em;
}
.control-tracks::after {
    display: none;
}

/* --- Trailing cell borders drew a rule against the panel edge. ----------- */
.time-ruler span:last-child,
.epg-track .epg-block:last-child { border-right: 0; }

/* --- No scrollbars on a TV. Nothing here is pointer-scrolled, the remote
       moves focus and the list follows; the guide's chevron cue already
       signals that there is more. A visible bar also stole content width. -- */
.category-list,
.channel-guide,
.episode-grid,
.accounts-list,
.program-description { scrollbar-width: none; }
.category-list::-webkit-scrollbar,
.channel-guide::-webkit-scrollbar,
.episode-grid::-webkit-scrollbar,
.accounts-list::-webkit-scrollbar { width: 0; height: 0; }

/* --- The search block sits above the time ruler, so the ruler's column
       headings land directly on the rows they label instead of being
       separated from them by an unrelated search field. The ruler's first
       cell then repeated the "CHAÎNES" heading two rows above it; the time
       columns are the ones that need naming, the channel column does not. -- */
.guide-panel .time-ruler { border-top: 1px solid var(--app-widget-border); }
.time-ruler::before { content: ''; }

/* --- The two stacked panels sat at 15px and 16px radius. ----------------- */
.guide-panel { border-radius: 16px; }
.content-card { border-radius: 12px; }

/* A category that unfolds says so, with a caret on its right edge: pointing
   left while closed, rotated down once open. The second OK that unfolds a
   group was otherwise invisible — nothing distinguished a group of bouquets
   from a plain category, so nobody pressed it twice. Borders rather than a
   glyph, like every other mark in this app, and a rotation rather than a
   second shape so the two states are obviously the same control. */
.category-caret {
    flex: none;
    flex-shrink: 0;
    width: 0;
    height: 0;
    margin-left: 12px;
    border-top: 7px solid transparent;
    border-bottom: 7px solid transparent;
    border-right: 9px solid currentColor;
    opacity: 0.55;
    transition: transform 0.2s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.16s linear;
}
.category-item.selected .category-caret,
.category-item:focus .category-caret {
    opacity: 1;
}
/* Left points at (-1, 0); a quarter turn anticlockwise puts it at (0, 1),
   which is down on a screen. */
.category-item.category-open .category-caret {
    transform: rotate(-90deg);
}

/* ==========================================================================
   Fullscreen channel browser (the "zap" overlay).

   TiviMate's central idea, and the one thing the guide screen could never do:
   read what is on the other channels WITHOUT leaving the one you are
   watching. The picture keeps playing full size underneath and the browser
   takes one column on the left, so two thirds of the screen stays live video.

   A bottom strip repeating the highlighted programme was tried and removed:
   the row already carries it, and the strip only ate picture.

   Highlighting a row is not zapping. Moving through the list only changes
   which row is lit; OK is what actually switches. That separation is the whole
   feature.
   ========================================================================== */
.zap-overlay {
    position: absolute;
    z-index: 6;
    left: 0;
    top: 0;
    width: 1920px;
    height: 1080px;
    color: white;
}
.video-stage.fullscreen .zap-overlay {
    position: fixed;
}
.zap-panel {
    position: absolute;
    left: 0;
    top: 0;
    width: 620px;
    height: 1080px;
    padding: 44px 34px 34px;
    background: linear-gradient(
        to right,
        rgba(3, 6, 12, 0.95) 0,
        rgba(3, 6, 12, 0.92) 64%,
        rgba(3, 6, 12, 0) 100%
    );
}
.zap-head {
    height: 74px;
}
.zap-head strong {
    display: block;
    margin-top: 6px;
    font-size: 30px;
    line-height: 1.1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.zap-list {
    height: 880px;
    overflow: hidden;
}
.zap-row {
    width: 100%;
    height: 74px;
    display: flex;
    align-items: center;
    position: relative;
    padding: 0 14px;
    border: 0;
    border-radius: 12px;
    background: transparent;
    color: #c8d2de;
    text-align: left;
    font: inherit;
}
.zap-row > * + * {
    margin-left: 16px;
}
/* The highlight is drawn by the app, not by :focus. Focus stays on one
   element for the whole list so a re-render cannot drop the D-pad. */
.zap-row.current {
    background: rgba(255, 255, 255, 0.13);
    color: white;
    box-shadow: inset 0 0 0 3px var(--app-selection-color);
}
.zap-row.playing .zap-row-name strong::after {
    content: "";
    display: inline-block;
    width: 9px;
    height: 9px;
    margin-left: 10px;
    border-radius: 50%;
    background: #34d17a;
    vertical-align: middle;
}
.zap-row-number {
    width: 54px;
    flex: none;
    font-size: 21px;
    color: #7f8ea1;
    text-align: right;
}
.zap-row.current .zap-row-number {
    color: #c8d2de;
}
.zap-row-logo {
    width: 54px;
    height: 54px;
    flex: none;
    position: relative;
    border-radius: 10px;
    background: rgba(255, 255, 255, 0.07);
    overflow: hidden;
}
.zap-row-logo img {
    position: absolute;
    left: 0;
    top: 0;
    width: 54px;
    height: 54px;
    object-fit: contain;
}
.zap-row-name {
    min-width: 0;
    flex: 1;
    overflow: hidden;
}
.zap-row-name strong {
    display: block;
    font-size: 22px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.zap-row-name span {
    display: block;
    margin-top: 3px;
    font-size: 17px;
    color: #90a0b2;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.zap-row.current .zap-row-name span {
    color: #b9c6d4;
}
/* Thin "where we are in this programme" bar. Fullscreen channel list only —
   never on the mini-player rail, where it would fight the zap. */
.zap-row-progress {
    position: absolute;
    left: 14px;
    right: 14px;
    bottom: 5px;
    height: 2px;
    overflow: hidden;
    border-radius: 1px;
    background: rgba(255, 255, 255, 0.18);
    pointer-events: none;
}
.zap-row-progress i {
    display: block;
    height: 100%;
    background: #ffffff;
}
.zap-legend {
    height: 60px;
    display: flex;
    align-items: center;
    font-size: 18px;
    color: #8494a6;
}
.zap-legend > * + * {
    margin-left: 26px;
}
.zap-legend b {
    display: inline-block;
    min-width: 62px;
    margin-right: 8px;
    padding: 4px 10px;
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.14);
    color: white;
    font-size: 16px;
    text-align: center;
}

/* ==========================================================================
   Tizen 5.0 compatibility: flex `gap` fallback.
   Measured on the Q70R (UA "SMART-TV; LINUX; Tizen 5.0"): a flex container
   with gap:20px renders its children 0px apart, and `getComputedStyle` reports
   gap as undefined — row-gap/column-gap only reached flex layout in Chromium
   84. Every flex row below therefore had its spacing silently dropped on the
   television while looking correct in a desk browser, which is what made the
   dock, the top bar and the guide header read as welded-together blocks.
   Grid containers need their own fallback too — see the grid-gap block after
   this one: Chromium 57 only shipped the prefixed `grid-gap` alias, and the
   unprefixed `gap` these rules use reached grid in 66, after this engine.
   Each margin repeats that container's FINAL cascaded gap; keep them in step
   if a gap above changes. `> * + *` leaves the first child flush with the
   container edge, exactly as gap does. Harmless on newer engines: the gap
   declarations were removed from these rules, so the margin is the only
   source of separation on both.
   ========================================================================== */
.brand > * + * { margin-left: 16px; }
.dialog div > * + * { margin-left: 16px; }
.guide-actions > * + * { margin-left: 14px; }
.guide-header > div:first-child > * + * { margin-left: 14px; }
.image-settings-option > * + * { margin-left: 12px; }
.pagination > * + * { margin-left: 24px; }
.player-control > * + * { margin-left: 8px; }
.player-controls > * + * { margin-left: 2px; }
.program-channel > * + * { margin-left: 16px; }
.remember-row > * + * { margin-left: 12px; }
.series-hero > * + * { margin-left: 40px; }
.video-overlay > * + * { margin-left: 18px; }
.topbar-actions > * + * { margin-left: 14px; }
.language-button > * + * { margin-left: 9px; }

/* ==========================================================================
   Tizen 5.0 compatibility: grid `gap` -> `grid-gap` alias.
   Same engine gap as above, grid flavour: this stylesheet uses the unprefixed
   `gap`, which only reached grid layout in Chromium 66 — the Q70R's engine
   parses just the older `grid-gap` alias. Without these the poster grids, the
   episode grid, the login split, the preview row and the account rows all
   render welded together on the television while looking correct in a desk
   browser. On newer engines grid-gap and gap are the same property, and each
   value below repeats that container's FINAL cascaded gap, so this block is
   invisible there; keep the values in step if a gap above changes.
   ========================================================================== */
.login-screen { grid-gap: 70px; }
.preview-row { grid-gap: 16px; }
.channel-identity { grid-gap: 14px; }
.content-grid { grid-gap: 18px; }
.episode-grid { grid-gap: 16px; }
.pairing-content { grid-gap: 38px; }
.account-row { grid-gap: 12px; }

/* ===========================================================================
   NOVA TV 2026 — calm, content-first shell
   One restrained surface language, fewer simultaneous facts, and focus that
   reads from a couch without turning every component cyan.
   ========================================================================== */
:root {
    --app-shell-bg: #05070a;
    --app-rail-bg: #080a0e;
    --app-header-bg: #07090c;
    --app-content-bg: #05070a;
    --app-widget-bg: #0b0e13;
    --app-widget-header-bg: #0d1117;
    --app-card-hover-bg: #151a22;
    --app-widget-border: rgba(255, 255, 255, 0.09);
    --app-rail-border: rgba(255, 255, 255, 0.07);
    --app-on-surface: #f5f5f7;
    --app-eyebrow-color: #8e949e;
    --app-selection-color: #0a84ff;
    --app-selection-on-color: #ffffff;
    --app-selection-surface: rgba(255, 255, 255, 0.08);
    --app-selection-surface-strong: rgba(255, 255, 255, 0.14);
    --app-selection-border: rgba(255, 255, 255, 0.74);
    --app-selection-glow: rgba(0, 0, 0, 0.34);
    --app-focus-fill: rgba(255, 255, 255, 0.13);
}

body {
    color: #f5f5f7;
    background: #05070a;
    font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI", sans-serif;
}

.topbar {
    height: 112px;
    padding: 16px 32px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
    background: rgba(7, 9, 12, 0.96);
}
.brand { width: 380px; }
.brand-lockup-compact .brand-symbol {
    width: 56px;
    height: 56px;
    border-radius: 17px;
}
.brand-lockup-compact .brand-word {
    margin-left: 16px;
    font-size: 30px;
    letter-spacing: 0.18em;
}
.brand > div:last-child { margin-left: 18px; }
.brand #clock { color: #8e949e; font-size: 24px; }

.main-nav {
    width: 600px;
    padding: 5px;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 28px;
    background: rgba(255, 255, 255, 0.045);
}
.nav-button {
    min-width: 190px;
    height: 60px;
    border: 0;
    border-radius: 23px;
    color: #a9afb9;
    font-size: 27px;
    font-weight: 600;
}
.nav-button.active {
    color: #f3f3f5;
    border: 0;
    background: #60636a;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.12), 0 5px 15px rgba(0, 0, 0, 0.2);
}
.nav-button.active .nav-icon { color: #f3f3f5; }
.main-nav .nav-icon { display: none; }
.main-nav .nav-button::before { display: none; }
.main-nav .nav-button:focus {
    outline: 2px solid rgba(137, 140, 147, 0.78);
    outline-offset: 2px;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1), 0 5px 15px rgba(0, 0, 0, 0.2);
    transform: none;
}
.source-button {
    width: 220px;
    height: 60px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 22px;
    border: 1px solid rgba(255, 255, 255, 0.09);
    border-radius: 30px;
    background: rgba(255, 255, 255, 0.045);
    text-align: center;
}
.source-button span { display: none; }
.source-button strong {
    max-width: 176px;
    margin: 0;
    color: #f5f5f7;
    font-size: 25px;
    font-weight: 600;
    line-height: 1;
    text-align: center;
}
/* The language control borrows the account pill's shape, translucent fill and
   hairline rather than the base theme's opaque navy block -- the two sit side
   by side, so anything else reads as a foreign control. The group is boxed to
   the brand's width and pushed right: .main-nav centres itself with auto
   margins, so the two flanks have to measure the same or the tabs drift. */
.topbar-actions {
    width: 380px;
    justify-content: flex-end;
}
.topbar-actions .source-button { width: 220px; flex: 0 0 auto; }
.language-button {
    width: 108px;
    height: 60px;
    border: 1px solid rgba(255, 255, 255, 0.09);
    border-radius: 30px;
    background: rgba(255, 255, 255, 0.045);
}
.language-button strong {
    color: #f5f5f7;
    font-size: 23px;
    font-weight: 600;
    line-height: 1;
}
.language-globe {
    width: 24px;
    height: 24px;
    border-width: 2px;
    color: #a9afb9;
    opacity: 1;
}
.language-button:focus .language-globe { color: #f3f3f5; }
.catalog-search-button {
    width: 60px;
    padding: 0;
}
.catalog-search-button-label {
    display: none;
}
.catalog-search-glyph {
    position: relative;
    width: 22px;
    height: 22px;
    flex: none;
    border: 2px solid #a9afb9;
    border-radius: 50%;
    box-sizing: border-box;
}
.catalog-search-glyph::after {
    content: '';
    position: absolute;
    right: -5px;
    bottom: -3px;
    width: 8px;
    height: 2px;
    background: #a9afb9;
    transform: rotate(45deg);
    transform-origin: left center;
}
.catalog-search-button:focus .catalog-search-glyph {
    border-color: #f3f3f5;
}
.catalog-search-button:focus .catalog-search-glyph::after {
    background: #f3f3f5;
}
/* Sign-in has no topbar, so its own copy sits in the corner of the screen --
   otherwise the first thing a viewer meets is French with no way out of it. */
.login-screen { position: relative; }
.login-language-button {
    position: absolute;
    top: 42px;
    right: 52px;
    z-index: 3;
}

.app-screen { height: calc(968px - 2 * var(--nova-safe-inset)); }
.live-screen,
.catalog-screen { grid-template-columns: 420px 1fr; }
.category-rail {
    height: calc(968px - 2 * var(--nova-safe-inset));
    padding: 24px 14px;
    border-right: 1px solid rgba(255, 255, 255, 0.07);
    background: #080a0e;
}
.rail-heading {
    height: 42px;
    padding: 0 14px;
    color: #868d98;
    font-size: 20px;
    letter-spacing: 0.1em;
}
.rail-heading span,
.rail-heading strong { font-size: 20px; font-weight: 500; }
.category-rail .rail-search,
.guide-panel .rail-search,
.catalog-search {
    height: 58px;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 18px;
    color: #f5f5f7;
    background: rgba(255, 255, 255, 0.055);
    font-size: 23px;
}
.category-rail .rail-search {
    width: calc(100% - 16px);
    margin: 0 8px 16px;
    padding: 0 16px;
}
/* Category rails are browsed directly with the remote; the separate search
   stop cost space and made the left navigation feel like a form. */
.category-rail .rail-search { display: none !important; }
.rail-search,
.catalog-search {
    color: #f1f1f3 !important;
    -webkit-text-fill-color: #f1f1f3;
    caret-color: #c9cbd0;
    opacity: 1;
}
.rail-search::placeholder,
.catalog-search::placeholder {
    color: #777d87;
    -webkit-text-fill-color: #777d87;
    opacity: 1;
}
.category-list {
    height: calc(824px - 2 * var(--nova-safe-inset));
    padding: 0 4px 20px;
}
.category-item {
    min-height: 62px;
    margin-bottom: 5px;
    padding: 8px 14px;
    border: 0;
    border-radius: 18px;
    color: #aeb4be;
    background: transparent;
}
.category-item span { font-size: 23px; font-weight: 550; }
.category-item small { color: #707782; font-size: 19px; font-weight: 500; }
.category-item.selected {
    color: #f5f5f7;
    background: rgba(255, 255, 255, 0.10);
    box-shadow: none;
}
.category-item.selected::after { display: none; }
.category-item.category-pinned + .category-item:not(.category-pinned)::before {
    border-color: rgba(255, 255, 255, 0.07);
}

.live-main {
    height: calc(968px - 2 * var(--nova-safe-inset));
    padding: 18px 22px;
    background: #05070a;
}
.preview-row {
    height: calc(396px - var(--nova-safe-inset));
    grid-template-columns: 900px 1fr;
    gap: 16px;
    grid-gap: 16px;
}
.video-stage,
#av-player,
#html-player,
.video-empty,
.program-panel {
    height: calc(396px - var(--nova-safe-inset));
}
.video-stage,
.program-panel,
.guide-panel {
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 26px;
    background: #0a0d12;
    box-shadow: none;
}
.video-stage { width: 100%; overflow: hidden; }
.native-player-host .video-stage {
    /* ExoPlayer's SurfaceView is below the transparent WebView. An opaque fill
       here masks decoded frames while leaving their audio audible. */
    background: transparent;
}
.native-player-host .live-main {
    /* A transparent child cannot punch through an opaque ancestor. The Android
       window already paints the same shell colour outside videoClip, so this
       stays visually black while allowing frames through inside the clip. */
    background: transparent;
}
#av-player,
#html-player,
.video-empty { width: 100%; }
.video-empty {
    color: #848b96;
    background: radial-gradient(circle at 50% 35%, #151b25, #080a0e 66%);
}
.brand-lockup-stage { margin-bottom: 24px; }
.brand-lockup-stage .brand-word {
    color: #f5f5f7;
    font-size: 34px;
    letter-spacing: 0.28em;
}
.video-empty strong { color: #f5f5f7; font-size: 29px; font-weight: 600; }
.video-empty span:not(.brand-word) { font-size: 22px; }
.program-panel { padding: 24px 30px; }
.program-channel h1 { font-size: 28px; font-weight: 600; }
.program-panel h2 {
    margin: 12px 0 5px;
    max-height: 92px;
    overflow: hidden;
    font-size: 38px;
    line-height: 1.15;
    font-weight: 650;
}
.program-panel > p { margin: 4px 0; font-size: 21px; }
.program-description {
    height: 58px;
    overflow: hidden;
    color: #8f96a1;
    font-size: 20px;
    line-height: 1.4;
}

.guide-panel {
    height: calc(520px - var(--nova-safe-inset));
    margin-top: 16px;
    overflow: hidden;
}
.guide-header {
    height: 74px;
    padding: 16px 22px 8px;
    border-bottom: 0;
    background: transparent;
}
.guide-header h2 { font-size: 31px; font-weight: 650; }
.guide-actions { display: none; }
.guide-total { color: #858c97; font-size: 21px; }
.guide-panel .rail-heading { height: 0; padding: 0; overflow: hidden; }
.guide-panel .rail-search {
    width: calc(100% - 36px);
    margin: 0 18px 12px;
    padding: 0 18px;
}
.time-ruler { display: none; }
.channel-guide {
    height: calc(360px - var(--nova-safe-inset));
    padding: 0 18px;
}
.channel-row {
    display: block;
    height: calc(86px - var(--nova-safe-inset) / 4);
    overflow: hidden;
    border: 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.065);
    border-radius: 0;
    color: #d8dbe0;
    background: transparent;
}
.channel-identity {
    width: 100%;
    height: 100%;
    grid-template-columns: 58px 1fr;
    padding: 9px 14px;
}
.channel-logo { width: 54px; height: 54px; border-radius: 14px; }
.channel-logo,
.program-logo-shell {
    position: relative;
    border: 1px solid rgba(255, 255, 255, 0.12);
    background: linear-gradient(145deg, #1b2029, #0b0e13);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
}
.channel-logo img,
.program-logo-shell img {
    position: relative;
    z-index: 2;
    width: 100%;
    height: 100%;
    padding: 8px;
    object-fit: contain;
    background: #11151c;
}
.channel-logo-fallback {
    position: absolute;
    z-index: 1;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    display: flex !important;
    align-items: center;
    justify-content: center;
    margin: 0 !important;
    color: #aeb7c5 !important;
    font-size: 16px !important;
    font-weight: 750;
    letter-spacing: 0.06em;
}
.channel-copy strong { font-size: 24px; font-weight: 600; }
.channel-copy span { margin-top: 3px; color: #858c97; font-size: 19px; }
.epg-track { display: none; }
.channel-row.selected { background: rgba(10, 132, 255, 0.11); }
.guide-scroll-cue { display: none; }

.catalog-main {
    height: calc(968px - 2 * var(--nova-safe-inset));
    padding: 24px 30px;
    background: #05070a;
}
.catalog-header { height: 100px; }
.catalog-header h1 { font-size: 42px; font-weight: 650; letter-spacing: -0.02em; }
.content-grid {
    height: 760px;
    gap: 20px;
    grid-gap: 20px;
}
.content-card {
    overflow: hidden;
    border: 0;
    border-radius: 22px;
    background: #0b0e13;
    box-shadow: none;
}
.content-card img,
.content-card .placeholder { background: #10141b; }

.category-item:focus,
.source-button:focus,
.language-button:focus,
.small-button:focus,
.primary-button:focus,
.secondary-button:focus,
.pair-phone-button:focus,
.nav-button:focus {
    outline: 3px solid rgba(255, 255, 255, 0.92);
    outline-offset: 2px;
    box-shadow: 0 12px 34px rgba(0, 0, 0, 0.34);
    transform: scale(1.015);
}
.category-item:focus {
    outline: 0;
    outline-offset: 0;
    color: #f3f3f5;
    background: #60636a;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.12);
    transform: none;
}
.category-item:focus small,
.category-item:focus span { color: #f3f3f5; }
.channel-row:focus {
    outline: 3px solid rgba(255, 255, 255, 0.92);
    outline-offset: -3px;
    border-radius: 18px;
    background: rgba(255, 255, 255, 0.13);
    box-shadow: none;
    transform: none;
}
.content-card:focus {
    outline: 4px solid #f5f5f7;
    outline-offset: 3px;
    box-shadow: 0 18px 42px rgba(0, 0, 0, 0.46);
    transform: scale(1.035);
}

/* Film playback must read instantly from the couch. Every actionable control,
   including timeline and track rows, uses the same unmistakable blue focus. */
.video-stage.fullscreen .player-control:focus,
.video-stage.fullscreen .player-control-primary:focus,
.image-settings-option:focus {
    outline: 3px solid #ffffff;
    outline-offset: 2px;
    color: #ffffff;
    background: #0a84ff;
    box-shadow: 0 0 0 6px rgba(10, 132, 255, 0.25), 0 12px 30px rgba(0, 0, 0, 0.45);
    transform: scale(1.06);
}
.image-settings-option:focus small { color: #ffffff; }
.timeline-bar:focus {
    outline: 4px solid #0a84ff;
    outline-offset: 8px;
    box-shadow: 0 0 0 8px rgba(10, 132, 255, 0.2);
}

/* --- Player geometry and monochrome focus refinement -------------------- */
.preview-row {
    height: calc(450px - var(--nova-safe-inset));
    grid-template-columns: 800px 1fr;
}
.video-stage,
#av-player,
#html-player,
.video-empty,
.program-panel {
    height: calc(450px - var(--nova-safe-inset));
}
.video-stage {
    border-radius: 28px;
    clip-path: inset(0 round 28px);
}
.guide-panel {
    height: calc(466px - var(--nova-safe-inset));
}
.channel-guide {
    height: calc(306px - var(--nova-safe-inset));
}
.channel-row,
.channel-row.selected,
.channel-row:focus {
    margin-bottom: 4px;
    border-radius: 18px;
}
.channel-row.selected {
    border: 1px solid rgba(255, 255, 255, 0.18);
    background: rgba(255, 255, 255, 0.09);
}
.player-controls,
.video-stage.fullscreen .player-controls {
    border-radius: 999px;
}

/* Focus stays monochrome like the top navigation: white surface, dark mark. */
.player-control:focus,
.player-control-primary:focus,
.video-stage.fullscreen .player-control:focus,
.video-stage.fullscreen .player-control-primary:focus,
.image-settings-option:focus {
    outline: 2px solid rgba(151, 154, 161, 0.82);
    outline-offset: 2px;
    color: #f3f3f5;
    background-color: #60636a !important;
    background-image: none !important;
    box-shadow: inset 0 0 0 100px #60636a, 0 6px 18px rgba(0, 0, 0, 0.32) !important;
}
.image-settings-option:focus small { color: #f3f3f5; }
.timeline-bar:focus {
    outline: 0 !important;
    box-shadow: none !important;
    transform: none !important;
}
.timeline-bar:focus .timeline-thumb {
    width: 40px;
    height: 40px;
    transform: translate(-20px, -20px);
    background: #c0c2c7;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.58);
}

/* Android TV's IME owns a separate surface and may pan the fixed 1920px page,
   leaving the real input obscured. Mirror the active value in a stable banner
   above the keyboard so every typed character remains visible from the sofa. */
.typing-preview {
    position: fixed;
    z-index: 120;
    top: 128px;
    left: 50%;
    width: 920px;
    min-height: 94px;
    padding: 14px 24px;
    border: 1px solid rgba(255, 255, 255, 0.15);
    border-radius: 22px;
    color: #f1f1f3;
    background: rgba(24, 26, 31, 0.98);
    box-shadow: 0 18px 50px rgba(0, 0, 0, 0.48);
    transform: translateX(-50%);
}
.typing-preview span {
    display: block;
    margin-bottom: 5px;
    color: #8e929a;
    font-size: 17px;
    font-weight: 700;
    letter-spacing: 0.12em;
}
.typing-preview strong {
    display: block;
    overflow: hidden;
    font-size: 31px;
    font-weight: 600;
    line-height: 1.25;
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* Subtitles stay a CC badge. Audio has its own equalizer mark. */
.control-tracks::before {
    content: 'CC';
    top: 5px;
    right: 0;
    bottom: 8px;
    left: 0;
    border-width: 3px;
    border-radius: 5px;
    font-size: 9px;
    letter-spacing: 0.02em;
}
.control-tracks::after { display: none; }

/* Fullscreen is only the picture: no window border or dark lower wash. */
.video-stage.fullscreen {
    border: 0;
    border-radius: 0;
    clip-path: none;
}
.video-stage.fullscreen .video-overlay {
    background: transparent;
}

/* Keep the reduced live player's controls on the lower edge without tinting
   or darkening the picture. */
.video-stage:not(.fullscreen) .video-overlay {
    left: -2px;
    right: -2px;
    bottom: -2px;
    height: calc(100% + 4px);
    align-items: flex-end;
    padding: 0 18px 14px;
    background: transparent;
}

/* Live/program progress and the movie seek fill stay neutral; blue is reserved
   for informational eyebrows rather than playback state. */
.program-progress span,
.timeline-fill { background: #7d8087; box-shadow: none; }

/* Provider episode titles can be sentences (and occasionally include release
   tags). Keep both lines inside the fixed TV card instead of letting their
   intrinsic text height paint over the next row. */
.episode-card {
    min-width: 0;
    height: 124px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    overflow: hidden;
    padding: 15px 18px;
    box-sizing: border-box;
}
.episode-card strong {
    display: block;
    flex: none;
    max-width: 100%;
    overflow: hidden;
    font-size: 23px;
    line-height: 1.15;
    white-space: nowrap;
    text-overflow: ellipsis;
}
.episode-card small {
    max-width: 100%;
    max-height: 46px;
    display: -webkit-box;
    margin-top: 7px;
    overflow: hidden;
    font-size: 19px;
    line-height: 1.2;
    white-space: normal;
    overflow-wrap: break-word;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
}

/* Five live channels fit at once without shrinking typography. Reclaim the
   space from vertical chrome and row padding; the 54px logo and both existing
   text sizes remain unchanged. */
.guide-header {
    height: 58px;
    padding: 9px 22px 3px;
}
.guide-panel .rail-search {
    height: 52px;
    margin-bottom: 8px;
}
.channel-guide {
    height: calc(332px - var(--nova-safe-inset));
}
.channel-row,
.channel-row.selected,
.channel-row:focus {
    height: 60px;
    margin-bottom: 4px;
}
.channel-identity {
    height: 60px;
    padding: 3px 12px;
}

/* Expanded provider bouquets are a real second level below the clean merged
   group. Indentation and a quiet guide line communicate hierarchy without
   changing the established focus or text sizing. */
.category-item.category-subcategory {
    position: relative;
    padding-left: 46px;
    border-radius: 16px;
    color: #9da2ab;
}
.category-item.category-subcategory::before {
    content: '';
    position: absolute;
    left: 24px;
    top: 12px;
    bottom: 12px;
    width: 2px;
    border-radius: 2px;
    background: rgba(255, 255, 255, 0.13);
}
.category-item.category-subcategory:focus,
.category-item.category-subcategory.selected {
    color: #f3f3f5;
}

/* Minimal bottom-only overflow indicators. They are intentionally not
   focusable controls: one quiet chevron appears only while more content is
   available below, then disappears at the end. */
.category-rail { position: relative; }
.section-more-cue,
#guide-scroll-cue {
    position: absolute;
    z-index: 12;
    left: 50%;
    right: auto;
    top: auto;
    bottom: 5px;
    width: 54px;
    height: 26px;
    display: block;
    pointer-events: none;
    transform: translateX(-50%);
    background: linear-gradient(to bottom, rgba(8, 10, 14, 0), #080a0e 72%);
}
#guide-scroll-cue {
    bottom: 3px;
    background: linear-gradient(to bottom, rgba(10, 13, 18, 0), #0a0d12 72%);
}
.section-more-cue i,
#guide-scroll-cue .cue-down {
    position: absolute;
    left: 50%;
    bottom: 3px;
    width: 12px;
    height: 12px;
    display: block;
    border: 0;
    border-right: 2px solid #858991;
    border-bottom: 2px solid #858991;
    border-radius: 1px;
    opacity: 0;
    transform: translateX(-50%) rotate(45deg);
    background: transparent;
    filter: none;
}
.section-more-cue i { opacity: 1; }
#guide-scroll-cue .cue-up { display: none; }
#guide-scroll-cue.can-down .cue-down { opacity: 1; filter: none; }
#guide-scroll-cue .cue-down::after { display: none; }

/* --- Cinematic catalog: Netflix-like discovery, NOVA restraint ---------
   Films and series share one remote-first composition: categories become a
   quiet horizontal strip, the focused title owns the hero, and one poster
   shelf is the only scrolling content region. */
.catalog-screen {
    grid-template-columns: 1fr;
    grid-template-rows: 88px 1fr;
    background: #05070a;
}
.catalog-screen > .category-rail {
    width: auto;
    height: 92px;
    display: block;
    padding: 12px 30px;
    overflow: hidden;
    border-right: 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.07);
    background: #080a0e;
    box-sizing: border-box;
}
.catalog-lane-bar {
    display: flex;
    align-items: center;
    height: 68px;
}
.catalog-lane {
    display: flex;
    align-items: center;
    min-width: 210px;
    height: 60px;
    margin-right: 14px;
    padding: 0 22px;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 30px;
    color: #c5c8ce;
    background: rgba(255, 255, 255, 0.04);
    font-size: 22px;
    font-weight: 600;
}
.catalog-lane.selected {
    color: #f3f3f5;
    background: #34373d;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.12);
}
.catalog-lane:focus {
    outline: 2px solid #d0d2d6;
    outline-offset: 2px;
    color: #fff;
    background: #4a4d54;
}
.lane-flag,
.lane-search-icon {
    flex: none;
    width: 42px;
    height: 28px;
    margin-right: 12px;
    overflow: hidden;
    border-radius: 4px;
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.28);
    background: #111;
}
.lane-flag svg {
    display: block;
    width: 42px;
    height: 28px;
}
.lane-search-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    box-shadow: none;
    background: transparent;
}
.lane-search-icon svg {
    display: block;
    width: 26px;
    height: 26px;
}
html[dir="rtl"] .catalog-lane span {
    direction: rtl;
}
.catalog-screen .rail-heading {
    height: 52px;
    padding: 0;
    justify-content: flex-start;
    color: #8b9099;
}
.catalog-screen .rail-heading strong { display: none; }
.catalog-screen .category-list {
    width: 100%;
    height: 64px;
    display: flex;
    align-items: center;
    padding: 4px 8px;
    overflow-x: auto;
    overflow-y: hidden;
    box-sizing: border-box;
}
.catalog-screen .category-list::-webkit-scrollbar { display: none; }
.catalog-screen .category-item {
    flex: 0 0 auto;
    width: auto;
    min-width: 126px;
    min-height: 52px;
    margin: 0 7px 0 0;
    padding: 0 19px;
    border-radius: 26px;
    color: #a8adb6;
    background: rgba(255, 255, 255, 0.035);
}
.catalog-screen .category-item span {
    flex: 0 1 auto;
    max-width: 255px;
    overflow: hidden;
    font-size: 21px;
    font-weight: 550;
    white-space: nowrap;
    text-overflow: ellipsis;
}
.catalog-screen .category-item small {
    flex: 0 0 auto;
    margin-left: 12px;
    color: #747a84;
    font-size: 18px;
}
.catalog-screen .category-item.selected {
    color: #eeeeef;
    background: #34373d;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1);
}
.catalog-screen .category-item:focus {
    outline: 2px solid #858991;
    outline-offset: 2px;
    color: #f1f1f2;
    background: #50535a;
    box-shadow: none;
}
.catalog-screen .category-item.category-subcategory { padding-left: 19px; }
.catalog-screen .category-item.category-subcategory::before { display: none; }
.catalog-screen .pin-icon { margin-right: 10px; }

.catalog-screen .catalog-main {
    height: calc(880px - 2 * var(--nova-safe-inset));
    padding: 16px 30px 10px;
    overflow: hidden;
    background: #05070a;
    box-sizing: border-box;
}
.catalog-feature {
    position: relative;
    height: 430px;
    overflow: hidden;
    border: 0;
    border-radius: 8px;
    background: #0b0e13;
}
.catalog-feature-backdrop {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center 22%;
    opacity: 0.72;
    filter: saturate(1.12) contrast(1.04);
    transform: none;
}
.catalog-feature-shade {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background:
        linear-gradient(90deg, rgba(5, 7, 10, 0.92) 0%, rgba(5, 7, 10, 0.62) 46%, rgba(5, 7, 10, 0.18) 100%),
        linear-gradient(0deg, rgba(5, 7, 10, 0.88) 0%, transparent 48%);
}
.catalog-feature-copy {
    position: relative;
    z-index: 2;
    width: 1240px;
    height: 100%;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 30px 46px;
    box-sizing: border-box;
}
.catalog-feature-copy .eyebrow {
    color: #9aa0aa;
    font-size: 17px;
    font-weight: 600;
    letter-spacing: 0.16em;
}
/* Prime lets a long title take a second line rather than cutting it at one:
   the ellipsis was eating half of every provider-prefixed title. */
.catalog-feature-copy h2 {
    max-width: 1150px;
    max-height: 128px;
    display: -webkit-box;
    margin: 10px 0 8px;
    overflow: hidden;
    color: #f4f5f7;
    font-size: 55px;
    font-weight: 700;
    line-height: 1.08;
    letter-spacing: -0.025em;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
}
.catalog-feature-meta {
    min-height: 29px;
    margin: 0;
    color: #cdd2d9;
    font-size: 22px;
    font-weight: 500;
}
.catalog-feature-summary {
    max-width: 1060px;
    max-height: 94px;
    display: -webkit-box;
    margin: 10px 0 0;
    overflow: hidden;
    color: #b7bcc5;
    font-size: 22px;
    line-height: 1.38;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
}
/* The preview strip carries no buttons and no repeated poster: backdrop and
   copy only, Prime Video style. Actions live on the detail page. */
.catalog-screen .catalog-header .catalog-search {
    display: none;
}
.catalog-screen.search-lane .catalog-header .catalog-search,
.catalog-screen.catalog-searching .catalog-header .catalog-search {
    display: block;
}
.catalog-feature.empty .catalog-feature-backdrop { display: none; }

/* --------------------------------------------------------------------------
   Poster in a landscape band.

   Measured on the panel: a film's list entry only ever carries a PORTRAIT
   poster, and one series in five has no landscape backdrop at all. Cover-
   cropping a 2:3 poster into a 1920x540 band keeps ~18% of its height — the
   title card and every face are outside the crop — and blows the surviving
   strip up about threefold, which is the "mal cadré et peu qualitatif" of the
   report. So a poster is not cropped: it is drawn WHOLE, at its own ratio, on
   the side the copy does not use, and DOWNSCALED (a 600x900 file shown ~360px
   wide has more pixels than it needs, so it is crisp rather than soft).

   Costs nothing: same <img>, same request, two properties. No filter, no
   mask, no transform — the three things Tizen 5 turns into a black slab or a
   blurred band. ------------------------------------------------------------ */
.catalog-feature-backdrop.hero-art-poster {
    left: auto;
    right: 96px;
    width: auto;
    max-width: 34%;
    height: 84%;
    top: 8%;
    bottom: auto;
    object-fit: contain;
    object-position: center center;
}
html[dir="rtl"] .catalog-feature-backdrop.hero-art-poster {
    right: auto;
    left: 96px;
}
/* With a contained poster the picture no longer reaches the copy, so the
   heavy horizontal wash would only darken bare background. The vertical
   settle stays: it is what seats the band on the shelf below. */
.catalog-feature.hero-poster .catalog-feature-shade {
    background: linear-gradient(
        0deg,
        #05070a 0%,
        rgba(5, 7, 10, 0.72) 16%,
        rgba(5, 7, 10, 0.18) 38%,
        rgba(5, 7, 10, 0) 60%
    );
}

.catalog-screen .catalog-header {
    height: 78px;
    padding: 0 4px;
}
.catalog-screen .catalog-header .eyebrow { display: none; }
.catalog-screen .catalog-header h1 {
    margin: 0;
    max-width: 890px;
    color: #d7d9dd;
    font-size: 29px;
    font-weight: 600;
    letter-spacing: -0.015em;
}
.catalog-screen .catalog-search {
    width: 430px;
    height: 54px;
    border-radius: 27px;
    font-size: 20px;
}
.catalog-screen .content-grid {
    height: 335px;
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    grid-template-rows: 335px;
    gap: 18px;
    grid-gap: 18px;
    overflow: visible;
}
.catalog-screen .content-card {
    height: 322px;
    overflow: visible;
    border: 0;
    border-radius: 18px;
    color: #e3e4e7;
    background: transparent;
}
.catalog-screen .content-card img,
.catalog-screen .content-card .placeholder {
    width: 100%;
    height: 255px;
    overflow: hidden;
    border-radius: 18px;
    object-fit: cover;
    background: #11141a;
}
.catalog-screen .content-card strong,
.catalog-screen .content-card.has-meta strong {
    height: 31px;
    display: block;
    padding: 10px 4px 0;
    overflow: hidden;
    color: #d8dade;
    font-size: 20px;
    font-weight: 600;
    line-height: 1.05;
    white-space: nowrap;
    text-overflow: ellipsis;
}
.catalog-screen .content-card span {
    padding: 6px 4px 0;
    color: #808690;
    font-size: 17px;
}
.catalog-screen .card-rating { display: none; }
.catalog-screen .content-card:focus {
    z-index: 4;
    outline: 0;
    box-shadow: none;
    transform: scale(1.045) translateY(-4px);
}
.catalog-screen .content-card:focus strong { color: #f1f1f2; }
.catalog-screen .loading-state,
.catalog-screen .empty-state {
    position: absolute;
    z-index: 8;
    left: 30px;
    right: 30px;
    bottom: 10px;
    height: 413px;
    border-radius: 24px;
    background: #07090d;
}
.catalog-screen #catalog-scroll-cue { display: none; }

/* --- True streaming shelves ---------------------------------------------
   Vertical movement changes category; horizontal movement stays inside one
   category. The shelves container is the only scroll owner. */
.catalog-screen { display: block; }
.catalog-screen > .category-rail {
    display: block !important;
    width: auto;
    height: 84px;
    padding: 10px 30px;
}
.catalog-screen .catalog-feature {
    display: block !important;
    height: 400px;
    margin: 0;
    border-radius: 0;
}
.catalog-screen #catalog-grid,
.catalog-screen #catalog-pagination,
.catalog-screen #catalog-scroll-cue { display: none !important; }
.catalog-screen .catalog-main {
    width: 100%;
    height: calc(900px - 2 * var(--nova-safe-inset));
    padding: 0;
    box-sizing: border-box;
}
.catalog-screen .catalog-header {
    display: none;
    height: 64px;
    padding: 6px 30px;
}
.catalog-screen.search-lane .catalog-header,
.catalog-screen.catalog-searching .catalog-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
}
.catalog-screen .catalog-header h1 {
    color: #e1e2e5;
    font-size: 36px;
    font-weight: 650;
}
.catalog-screen .catalog-search {
    width: 460px;
    height: 54px;
}
.catalog-screen .catalog-search:focus {
    outline: 2px solid #b4b6bb;
    outline-offset: 2px;
    border-color: #c0c2c7;
    color: #f2f2f4;
    background: #303238;
    box-shadow:
        0 0 0 4px rgba(180, 182, 187, 0.22),
        0 8px 24px rgba(0, 0, 0, 0.34);
    transform: scale(1.012);
}
.catalog-shelves {
    height: calc(500px - 2 * var(--nova-safe-inset));
    overflow-x: hidden;
    overflow-y: auto;
    padding: 10px 30px 90px;
    scroll-behavior: auto;
    box-sizing: border-box;
}
.catalog-screen.search-lane .catalog-shelves,
.catalog-screen.catalog-searching .catalog-shelves {
    height: calc(836px - 2 * var(--nova-safe-inset));
}
.catalog-shelves::-webkit-scrollbar { display: none; }
/* The focused card grows upward, so the heading needs real clearance rather
   than the few pixels the h2 line box happens to leave. A 298px card at
   scale(1.08) rises 11.9px from the zoom, 5px more from the lift, and carries
   a 3px outline at 3px offset on top of that — about 23px above its resting
   edge. The card also paints opaque at z-index 5, so anything it reaches is
   covered, not just crowded. 20px here keeps the title clear at full zoom. */
.catalog-shelf {
    position: relative;
    min-height: 368px;
    margin-bottom: 18px;
}
.catalog-shelf h2 {
    height: 42px;
    margin: 0 0 20px;
    display: flex;
    align-items: center;
    color: #d7d9dd;
    font-size: 27px;
    font-weight: 620;
    letter-spacing: -0.015em;
}
.catalog-shelf-track {
    height: 306px;
    display: -webkit-box;
    display: flex;
    -webkit-box-orient: horizontal;
    -webkit-box-direction: normal;
    flex-direction: row;
    flex-wrap: nowrap;
    overflow: visible;
    -webkit-transform: translateZ(0);
    transform: translateZ(0);
}
.catalog-shelf-card {
    position: relative;
    -webkit-box-flex: 0;
    flex: 0 0 calc((100% - 112px) / 8);
    width: calc((100% - 112px) / 8);
    max-width: calc((100% - 112px) / 8);
    margin-right: 16px;
    min-width: 0;
    height: 298px;
    overflow: visible;
    border: 0;
    border-radius: 17px;
    color: #d9dbe0;
    background: transparent;
    text-align: left;
    transition: transform 0.22s cubic-bezier(0.16, 1, 0.3, 1);
}
.catalog-shelf-card img,
.catalog-shelf-card .placeholder {
    width: 100%;
    height: 246px;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    border-radius: 17px;
    object-fit: cover;
    color: #8b8e95;
    background: #11141a;
    font-size: 54px;
}
.catalog-shelf-card .card-rating {
    top: 9px;
    right: 9px;
    display: flex;
    height: 30px;
    padding: 0 10px 0 29px;
    border-radius: 15px;
    color: #e9e9eb;
    background: rgba(9, 12, 17, 0.84);
    font-size: 15px;
    font-weight: 650;
}
.catalog-shelf-card .card-rating::before {
    left: 9px;
    width: 15px;
    height: 15px;
    margin-top: -7.5px;
}
.catalog-shelf-card strong {
    width: 100%;
    display: block;
    padding: 9px 3px 0;
    overflow: hidden;
    color: #d9dbe0;
    font-size: 19px;
    font-weight: 600;
    line-height: 1.1;
    white-space: nowrap;
    text-overflow: ellipsis;
    box-sizing: border-box;
}
.catalog-shelf-card span {
    display: block;
    padding: 5px 3px 0;
    color: #777d87;
    font-size: 16px;
}
.catalog-shelf-card:focus {
    z-index: 5;
    outline: 0;
    box-shadow: none;
    transform: scale(1.05) translate3d(0, -3px, 0);
    transform-origin: 50% 100%;
}
.catalog-shelf-card:focus strong { color: #f0f0f2; }
.catalog-shelf-loading {
    grid-column: 1 / -1;
    height: 246px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 20px;
    color: #737984;
    background: rgba(255, 255, 255, 0.025);
    font-size: 20px;
}
.catalog-shelf-more {
    position: absolute;
    z-index: 7;
    right: 7px;
    top: 151px;
    width: 15px;
    height: 15px;
    display: none;
    border-top: 2px solid #898d95;
    border-right: 2px solid #898d95;
    transform: rotate(45deg);
    pointer-events: none;
}
.catalog-shelf.has-more .catalog-shelf-more { display: block; }
.catalog-shelf.catalog-search-hidden { display: none; }
html[dir="rtl"] .catalog-shelf-card {
    margin-right: 0;
    margin-left: 16px;
}
/* Hebrew UI (dir=rtl): poster captions are almost always Latin — Avatar,
   FR/EN, 4K. An RTL ellipsis clips them on the LEFT (`…irbender (2024)
   (US)`), so you have to open the card to read the start. Isolate the
   caption as LTR: start stays visible, cut on the right. */
html[dir="rtl"] .catalog-shelf-card strong,
html[dir="rtl"] .content-card strong,
html[dir="rtl"] .catalog-feature-copy h2 {
    direction: ltr;
    unicode-bidi: isolate;
}
.catalog-screen .loading-state {
    top: 90px;
    bottom: 0;
    height: auto;
}

/* ==========================================================================
   Focus, une seule fois et pour de bon.
   Mesuré dans l'app après la refonte : seul .nav-button gardait un focus
   visible. Partout ailleurs — rail des catégories, lignes de chaînes, boutons,
   champs de recherche, contrôles du lecteur — le focus se résumait à un fond
   blanc à 4-9 %, et la ligne de catégorie en direct était carrément
   transparente : à trois mètres, impossible de savoir où est le curseur.
   Trois recettes concurrentes s'étaient accumulées (anneau bleu, remplissage
   gris, rien), d'où un curseur différent selon l'écran.

   Ce bloc est le dernier du fichier, donc il gagne. `box-shadow` plutôt
   qu'`outline` : sur le moteur de la TV (Chromium ~M63) un outline est tracé
   RECTANGULAIRE et ignore border-radius — c'est précisément le curseur « pas
   rond ». Une ombre portée, elle, suit l'arrondi de chaque élément.
   ========================================================================== */
[data-focusable]:focus {
    outline: none !important;
    box-shadow:
        0 0 0 4px var(--app-selection-border),
        0 0 0 7px rgba(4, 10, 18, 0.75),
        0 10px 30px rgba(0, 0, 0, 0.5) !important;
}
/* Remplissage gris pour ce qui est du texte sur fond sombre. Les visuels
   (affiches, vignettes) gardent leur image : l'anneau seul les désigne. */
.category-item:focus,
.channel-row:focus,
.nav-button:focus,
.small-button:focus,
.source-button:focus,
.language-button:focus,
.icon-button:focus,
.account-row:focus,
.account-action:focus,
.pair-phone-button:focus,
.catchup-row:focus,
.image-settings-option:focus,
.episode-card:focus,
.player-control:focus,
.player-control-primary:focus {
    background-color: #60636a !important;
    background-image: none !important;
    color: #ffffff !important;
}
.category-item:focus span,
.category-item:focus small,
.channel-row:focus .channel-copy strong,
.channel-row:focus .channel-now,
.episode-card:focus small,
.image-settings-option:focus small,
.catchup-row:focus time,
.catchup-row:focus span {
    color: #ffffff !important;
}
/* Les champs de saisie gardent leur fond : le texte tapé doit rester lisible,
   l'anneau suffit à les désigner. */
input[data-focusable]:focus {
    background-color: rgba(255, 255, 255, 0.10) !important;
    color: #ffffff !important;
}
/* La barre de temps est fine : l'anneau la doublerait en hauteur. Elle se
   signale par sa poignée, qui grossit et passe en blanc. */
.timeline-bar:focus {
    box-shadow: none !important;
}
.timeline-bar:focus .timeline-thumb {
    width: 34px;
    height: 34px;
    transform: translate(-17px, -17px);
    background: #ffffff;
    box-shadow: 0 0 0 4px rgba(4, 10, 18, 0.75), 0 4px 14px rgba(0, 0, 0, 0.6);
}

/* ==========================================================================
   AVPlay: let the picture through.
   The Samsung player draws on a hardware plane BEHIND the browser surface.
   While the page paints an opaque background you get sound and no image --
   exactly the report that followed the redesign, which gave html/body a solid
   fill. app.js sets `avplay-active` on <html> during playback; everything
   stacked above the plane must then be transparent. Rails, panels and bars
   keep their own backgrounds: only the column down to the video frame is
   cleared. Inert on Android, where the class is never set.
   ========================================================================== */
.avplay-active,
.avplay-active body,
.avplay-active #app-shell,
.avplay-active main,
.avplay-active .app-screen,
.avplay-active .live-main,
.avplay-active .preview-row,
.avplay-active #video-stage {
    background: transparent !important;
    background-color: transparent !important;
    background-image: none !important;
}
/* Idle frame: no chrome on the plane, focused or not. The mini-player's
   hairline and cursor are drawn by .stage-frame on top of the picture --
   see "Windowed mini-player" at the end of this file. An inset shadow here
   would be painted under the stage's children and so would show only while
   AVPlay played, which is the empty/playing split that rule fixes. */
.avplay-active #video-stage,
.avplay-active #video-stage:focus {
    box-shadow: none !important;
}
/* The live column is cleared for AVPlay. Keep the rail opaque so the
   graphite category cursor does not sit on the hardware plane. */
html.avplay-active .category-rail,
html.tizen-engine.avplay-active .category-rail {
    background: #080a0e !important;
}
/* The AVPlay element stays under the controls but above the cleared page. */
.avplay-active #av-player {
    background: transparent !important;
}

/* ==========================================================================
   Tizen engine only: focus ring drawn INWARD.
   Measured on the TV. Two problems, one rule. The generic
   [data-focusable]:focus above only ever won on .nav-button, so the rows
   app.js builds -- categories, channels -- had no ring at all; `html body`
   raises specificity above every per-screen override. And an outward ring
   needs 8px of room that rail and guide rows do not have (0-6px of margin
   inside `overflow: hidden` containers), so it was clipped on one or two
   sides, or off-screen at the edge. An inset shadow is painted inside the
   element: it cannot be cut, it follows the radius, and it reads from three
   metres. Scoped to .tizen-engine so Android keeps its own focus design,
   which does not have either problem.
   ========================================================================== */
html.tizen-engine body [data-focusable]:focus {
    box-shadow:
        inset 0 0 0 4px #ffffff,
        inset 0 0 0 7px rgba(4, 10, 18, 0.9) !important;
}
html.tizen-engine body .timeline-bar:focus { box-shadow: none !important; }

/* Live guide logos: a wide well (most marks are 16:9 / wordmarks), not a
   square stamp. Samsung's pointer zooms hovered <img>, so the image never
   receives events. */
.channel-row,
.channel-row:focus,
.channel-row:hover,
.channel-row.selected {
    height: 72px !important;
    overflow: hidden !important;
    transform: none !important;
    -webkit-transform: none !important;
}
.channel-identity {
    overflow: hidden !important;
    min-width: 0;
    grid-template-columns: 96px 1fr !important;
    padding: 8px 14px 8px 12px !important;
}
.channel-logo,
.channel-row:focus .channel-logo,
.channel-row:hover .channel-logo {
    width: 88px !important;
    height: 48px !important;
    max-width: 88px !important;
    max-height: 48px !important;
    overflow: hidden !important;
    flex-shrink: 0 !important;
    position: relative;
    border: 0 !important;
    border-radius: 6px !important;
    background: transparent !important;
    box-shadow: none !important;
}
.channel-logo img,
.channel-row:focus .channel-logo img,
.channel-row:hover .channel-logo img {
    pointer-events: none !important;
    position: absolute !important;
    left: 0 !important;
    top: 0 !important;
    width: 88px !important;
    height: 48px !important;
    max-width: 88px !important;
    max-height: 48px !important;
    padding: 0 !important;
    object-fit: contain !important;
    object-position: left center !important;
    background: transparent !important;
    image-rendering: auto;
    transform: none !important;
    -webkit-transform: none !important;
    zoom: 1 !important;
}
.channel-logo-fallback {
    font-size: 13px !important;
}
html.tizen-engine img:hover,
html.tizen-engine img:focus {
    transform: none !important;
    -webkit-transform: none !important;
    zoom: 1 !important;
}

/* Mini-player: the stage is focusable so OK can go fullscreen, but Samsung's
   cursor must not scale or ring a transparent box over the AVPlay plane. */
html.tizen-engine #video-stage,
html.tizen-engine #video-stage:hover,
html.tizen-engine.avplay-active #video-stage,
html.tizen-engine.avplay-active #video-stage:hover {
    transform: none !important;
    -webkit-transform: none !important;
}
html.tizen-engine #video-stage:focus,
html.tizen-engine.avplay-active #video-stage:focus {
    transform: none !important;
    -webkit-transform: none !important;
    outline: none !important;
}
html.tizen-engine #av-player,
html.tizen-engine #html-player,
html.tizen-engine .video-empty {
    pointer-events: none !important;
}

/* Counts clutter the rails. The totals stay in the data layer. */
.rail-heading strong,
.category-item small,
.guide-total,
#guide-scroll-label,
#live-category-count,
#catalog-category-count,
#channel-search-count,
#live-page-label {
    display: none !important;
}

/* Hebrew: the document is dir=rtl. Mirror chrome, keep playback LTR. */
html[dir="rtl"] .brand-lockup {
    direction: ltr;
}
html[dir="rtl"] .login-language-button {
    right: auto;
    left: 52px;
}
html[dir="rtl"] .category-rail {
    border-right: 0;
    border-left: 1px solid rgba(255, 255, 255, 0.07);
}
html[dir="rtl"] .catalog-feature-shade {
    background:
        linear-gradient(270deg, rgba(5, 7, 10, 0.92) 0%, rgba(5, 7, 10, 0.62) 46%, rgba(5, 7, 10, 0.18) 100%),
        linear-gradient(0deg, rgba(5, 7, 10, 0.88) 0%, transparent 48%);
}
html[dir="rtl"] .lane-flag,
html[dir="rtl"] .lane-search-icon {
    margin-right: 0;
    margin-left: 12px;
}
html[dir="rtl"] .catalog-lane {
    margin-right: 0;
    margin-left: 14px;
}
html[dir="rtl"] #video-overlay,
html[dir="rtl"] .player-timeline,
html[dir="rtl"] .card-rating,
html[dir="rtl"] #clock {
    direction: ltr;
}
html[dir="rtl"] .timeline-fill,
html[dir="rtl"] .timeline-thumb,
html[dir="rtl"] .timeline-bubble {
    left: 0;
    right: auto;
}

/* Hebrew, part two: the pieces added after the RTL pass.

   Flex rows mirror themselves under dir=rtl, so only two things need saying —
   the margins that were written as `left` and anything positioned absolutely,
   which the direction property does not touch at all. The caret is both: it
   moves to the other end of the row on its own, but keeps pointing the way it
   was drawn, and a caret pointing away from its label reads as an arrow to
   nowhere. Mirrored, it points back at the text in both languages, and opens
   downward from there. */
html[dir="rtl"] .category-caret {
    margin-left: 0;
    margin-right: 12px;
    border-right: 0;
    border-left: 9px solid currentColor;
}
/* Right points at (1, 0); a quarter turn clockwise puts it at (0, 1). */
html[dir="rtl"] .category-item.category-open .category-caret {
    transform: rotate(90deg);
}
/* The channel browser is docked, so it is positioned rather than laid out:
   left/right and the gradient have to be restated by hand. */
html[dir="rtl"] .zap-panel {
    left: auto;
    right: 0;
    padding: 44px 16px 34px 48px;
    background: linear-gradient(
        to left,
        rgba(3, 6, 12, 0.96) 0,
        rgba(3, 6, 12, 0.94) 82%,
        rgba(3, 6, 12, 0) 100%
    );
}
html[dir="rtl"] .zap-row {
    text-align: right;
}
html[dir="rtl"] .zap-row-name,
html[dir="rtl"] .zap-row-name strong,
html[dir="rtl"] .zap-row-name span {
    text-align: right;
}
html[dir="rtl"] .zap-row > * + * {
    margin-left: 0;
    margin-right: 16px;
}
html[dir="rtl"] .zap-row-number {
    text-align: left;
}
html[dir="rtl"] .zap-legend > * + * {
    margin-left: 0;
    margin-right: 26px;
}
html[dir="rtl"] .zap-legend b {
    margin-right: 0;
    margin-left: 8px;
}
/* Replay chevron: a physical margin inside a row that mirrors itself. The
   glyph points into the panel in both languages, since that is the direction
   the forward arrow actually travels — Right in French, Left in Hebrew. */
html[dir="rtl"] .zap-row-replay i {
    margin-left: 0;
    margin-right: 4px;
    transform: scaleX(-1);
    display: inline-block;
}
/* Absolutely positioned, so `direction` does not move it. The pause card is
   deliberately absent: it carries dir="ltr" like the dock, so its own spacing
   stays physical in both languages. */
html[dir="rtl"] .live-delay-badge {
    left: auto;
    right: 54px;
}

/* Hebrew, part three: the rest of the Tizen flex-gap fallback.
   `> * + * { margin-left }` stands in for a gap this engine ignores, but
   margin-left is a PHYSICAL side: dir=rtl reverses the row and the spacing
   then piles up on the outer edge while the children weld together. Each rule
   below repeats its container's own fallback value, mirrored.
   Two families are deliberately absent: the ones already mirrored where they
   are declared (.zap-row, .zap-legend, .season-tabs, the fullscreen
   .player-control, and the guide header's first child, which switches to
   space-between instead), and everything inside #video-overlay -- that subtree
   is forced back to direction:ltr above so playback chrome keeps its
   left-to-right order, which leaves its margin-left already on the right
   side. */
html[dir="rtl"] .brand > * + * {
    margin-left: 0;
    margin-right: 16px;
}
/* The clock overrides that fallback with 18px at equal specificity, so it
   needs its own mirror or the rule above would silently shrink it. */
html[dir="rtl"] .brand > div:last-child {
    margin-left: 0;
    margin-right: 18px;
}
html[dir="rtl"] .topbar-actions > * + * {
    margin-left: 0;
    margin-right: 14px;
}
html[dir="rtl"] .language-button > * + * {
    margin-left: 0;
    margin-right: 9px;
}
html[dir="rtl"] .program-channel > * + * {
    margin-left: 0;
    margin-right: 16px;
}
html[dir="rtl"] .guide-actions > * + * {
    margin-left: 0;
    margin-right: 14px;
}
html[dir="rtl"] .pagination > * + * {
    margin-left: 0;
    margin-right: 24px;
}
html[dir="rtl"] .detail-actions > * + * {
    margin-left: 0;
    margin-right: 14px;
}
html[dir="rtl"] .remember-row > * + * {
    margin-left: 0;
    margin-right: 12px;
}
html[dir="rtl"] .dialog div > * + * {
    margin-left: 0;
    margin-right: 16px;
}

/* ==========================================================================
   Focus, second pass. Reported from the couch: the poster frame reads as two
   frames, the rail's top row has its ring clipped by the heading above it, and
   the globe looks struck through.
   ========================================================================== */

/* Poster cards: ONE frame, on the artwork itself, plus the lift. The unified
   cursor ring wrapped the whole card (poster + caption) while the catalogue
   screens drew a second grey rectangle around the image — that pair is the
   "double frame" reported from the couch, twice. The card cancels the unified
   ring; the image alone wears it, Prime Video style, and the shadow follows
   the artwork's rounded corners (an outline on this engine would not). */
.content-card:focus,
.catalog-shelf-card:focus {
    box-shadow: 0 20px 48px rgba(0, 0, 0, 0.62) !important;
    background-color: transparent !important;
    transform: scale(1.09);
    /* Grow up into the heading gap, not down into the next category —
       that overflow is the stray box under the focused row. */
    transform-origin: 50% 100%;
    transition: transform 0.12s ease, box-shadow 0.12s ease;
    z-index: 4;
}
/* Episode cards are text tiles: they keep the unified ring and grey fill,
   only the lift is shared with the poster cards. */
.episode-card:focus {
    transform: scale(1.09);
    transform-origin: 50% 50%;
    transition: transform 0.12s ease, box-shadow 0.12s ease;
    z-index: 4;
}
.content-card:focus img,
.content-card:focus .placeholder,
.catalog-shelf-card:focus img,
.catalog-shelf-card:focus .placeholder,
.catalog-screen .content-card:focus img,
.catalog-screen .content-card:focus .placeholder {
    outline: none;
    box-shadow:
        0 0 0 4px #f2f4f7,
        0 0 0 6px rgba(4, 10, 18, 0.6);
}
/* A grown card must not be cropped by the row it sits in. */
.content-grid,
.catalog-shelf-track {
    overflow: visible;
}

/* The ring is drawn inside the item, so it survives only while the item does:
   the first row of a scrolling list ends up flush against the clip edge and
   loses its top line to the heading above. A little scroll room keeps every
   row whole. */
.category-list {
    padding-top: 8px;
    scroll-padding: 10px 0 28px;
}
.category-list .category-item {
    scroll-margin: 10px 0;
}

/* Same clip, other list: the unified cursor ring sits 7px OUTSIDE a channel
   row, and the guide list starts flush under its search field, so the top
   row's ring lost its upper arc to the container edge. Breathing room above
   and below keeps the ring whole on the first and last rows. */
.channel-guide {
    padding-top: 9px;
    padding-bottom: 9px;
    scroll-padding: 9px 0;
}
.channel-row {
    scroll-margin: 9px 0;
}

/* The globe's meridians were positioned at -2px, so both ran past the sphere
   and the mark read as a circle with a line through it. They belong inside. */
.language-globe::before {
    right: 0;
    left: 0;
}
.language-globe::after {
    top: 0;
    bottom: 0;
}
.language-globe {
    opacity: 1;
}

/* A text caret is the one cursor in this app that is not ours to style away,
   so it is at least the app's colour rather than the engine's blue. */
input[data-focusable] {
    caret-color: var(--app-selection-color);
}

/* ==========================================================================
   Detail pages, Netflix-style: full-bleed backdrop behind left-aligned copy,
   and for series a season selector (episode count on each tab) above a
   scrolling list of episode rows. Flex rows space children with margins,
   never `gap` — the Tizen twin (Chromium ~M63) welds them together.
   ========================================================================== */
.detail-screen {
    position: relative;
    padding: 0;
    background: #05070a;
}
.detail-backdrop {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: 68% 18%;
    opacity: 0.88;
}
html[dir="rtl"] .detail-backdrop {
    object-position: 32% 18%;
}
/* Detail page, poster case. Same rule as the catalogue band: rather than
   crop a 2:3 poster into a full-screen landscape plate (which cuts the title
   card and every face and blows the strip up), the poster is drawn WHOLE on
   the side the copy does not use. Measured, not guessed — see
   nova-hero-art.js. */
.detail-backdrop.hero-art-poster {
    left: auto;
    right: 110px;
    width: auto;
    max-width: 30%;
    top: 9%;
    bottom: auto;
    height: 62%;
    object-fit: contain;
    object-position: center center;
}
html[dir="rtl"] .detail-backdrop.hero-art-poster {
    right: auto;
    left: 110px;
}
/* The wide horizontal wash exists to keep text legible ON the picture. With
   a contained poster the picture is not under the text any more, so the wash
   would only flatten the page to near-black. */
.detail-screen.hero-poster .detail-shade {
    background: linear-gradient(
        180deg,
        rgba(5, 7, 10, 0.28) 0%,
        rgba(5, 7, 10, 0.16) 26%,
        rgba(5, 7, 10, 0.48) 62%,
        rgba(5, 7, 10, 0.9) 84%,
        #05070a 100%
    );
}
.detail-shade {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background:
        linear-gradient(
            90deg,
            #05070a 0%,
            rgba(5, 7, 10, 0.94) 20%,
            rgba(5, 7, 10, 0.62) 42%,
            rgba(5, 7, 10, 0.28) 68%,
            rgba(5, 7, 10, 0.08) 100%
        ),
        linear-gradient(
            180deg,
            rgba(5, 7, 10, 0.18) 0%,
            rgba(5, 7, 10, 0.08) 22%,
            rgba(5, 7, 10, 0.42) 58%,
            rgba(5, 7, 10, 0.88) 82%,
            #05070a 100%
        );
}
html[dir="rtl"] .detail-shade {
    background:
        linear-gradient(
            270deg,
            #05070a 0%,
            rgba(5, 7, 10, 0.94) 20%,
            rgba(5, 7, 10, 0.62) 42%,
            rgba(5, 7, 10, 0.28) 68%,
            rgba(5, 7, 10, 0.08) 100%
        ),
        linear-gradient(
            180deg,
            rgba(5, 7, 10, 0.18) 0%,
            rgba(5, 7, 10, 0.08) 22%,
            rgba(5, 7, 10, 0.42) 58%,
            rgba(5, 7, 10, 0.88) 82%,
            #05070a 100%
        );
}
.detail-body {
    position: relative;
    z-index: 2;
    height: 100%;
    display: flex;
    flex-direction: column;
    padding: 24px 48px 18px;
    box-sizing: border-box;
}
.detail-back {
    align-self: flex-start;
    flex: none;
    margin-bottom: 4px;
}
.detail-copy {
    flex: none;
    max-width: 1180px;
    margin-top: 8px;
}
/* A movie page has nothing below the copy: let it breathe lower. */
.detail-copy-movie {
    margin-top: 96px;
}
.detail-copy .eyebrow {
    color: #9aa0aa;
    font-size: 17px;
    font-weight: 600;
    letter-spacing: 0.16em;
}
.detail-copy h1 {
    max-height: 132px;
    display: -webkit-box;
    margin: 10px 0;
    overflow: hidden;
    color: #f4f5f7;
    font-size: 57px;
    font-weight: 700;
    line-height: 1.08;
    letter-spacing: -0.025em;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
}
.detail-screen .detail-meta {
    margin: 0;
    color: #cdd2d9;
    font-size: 23px;
    font-weight: 500;
}
.detail-plot {
    max-width: 1060px;
    max-height: 90px;
    display: -webkit-box;
    margin: 12px 0 0;
    overflow: hidden;
    color: #b7bcc5;
    font-size: 21px;
    line-height: 1.4;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
}
.detail-tracks {
    margin: 10px 0 0;
    color: #99a0ab;
    font-size: 20px;
}
.detail-screen .detail-actions {
    margin-top: 18px;
}
.season-tabs {
    display: flex;
    flex: none;
    margin-top: 24px;
    overflow-x: auto;
    padding: 8px 4px;
    scroll-padding: 0 12px;
}
.season-tabs > * + * {
    margin-left: 12px;
}
html[dir="rtl"] .season-tabs > * + * {
    margin-left: 0;
    margin-right: 12px;
}
.season-tab {
    flex: none;
    min-width: 168px;
    padding: 9px 24px;
    border: 0;
    border-radius: 12px;
    color: #d5d8de;
    background: rgba(255, 255, 255, 0.07);
    text-align: center;
    scroll-margin: 0 12px;
}
.season-tab strong {
    display: block;
    font-size: 21px;
    font-weight: 650;
}
.season-tab small {
    display: block;
    margin-top: 2px;
    color: #8d939e;
    font-size: 16px;
}
.season-tab.selected {
    color: #ffffff;
    background: rgba(255, 255, 255, 0.16);
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.22);
}
.season-tab.selected small {
    color: #c3c8d0;
}
.episode-list {
    flex: 1;
    margin-top: 12px;
    overflow-y: auto;
    padding: 10px 4px 14px;
    scroll-padding: 10px 0 14px;
}
.episode-list > p {
    color: #9aa1ac;
    font-size: 24px;
}
.episode-list .episode-card.episode-row {
    width: 100%;
    height: auto;
    min-height: 108px;
    display: flex;
    /* The tile version of .episode-card stacks as a centered column; a
       Netflix row is explicitly horizontal and left-bound. */
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
    padding: 12px 20px;
    border-radius: 14px;
    background: rgba(255, 255, 255, 0.045);
    text-align: left;
    scroll-margin: 10px 0;
}
.episode-list .episode-card.episode-row + .episode-card.episode-row {
    margin-top: 10px;
}
/* Rows are wide: the poster-card zoom would push them off screen. The
   unified ring and fill already mark them. */
.episode-list .episode-card.episode-row:focus {
    z-index: 4;
    transform: none;
}
.season-tab:focus {
    z-index: 5;
    color: #ffffff;
    background: rgba(255, 255, 255, 0.08);
}
.episode-row-num {
    flex: none;
    width: 54px;
    color: #8d939e;
    font-size: 30px;
    font-weight: 700;
}
.episode-row-thumb {
    flex: none;
    width: 198px;
    height: 111px;
    border-radius: 10px;
    object-fit: cover;
    background: #11151c;
}
span.episode-row-thumb-empty {
    display: flex;
    align-items: center;
    justify-content: center;
    color: #5a6069;
    font-size: 30px;
}
.episode-row-copy {
    flex: 1;
    min-width: 0;
    display: block;
    margin-left: 24px;
}
html[dir="rtl"] .episode-row-copy {
    margin-left: 0;
    margin-right: 24px;
}
html[dir="rtl"] .episode-list .episode-card.episode-row {
    text-align: right;
}
.episode-row-copy strong {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    color: #e8eaee;
    font-size: 24px;
    font-weight: 600;
}
.episode-row-copy strong em {
    flex: none;
    margin-left: 18px;
    color: #9aa1ac;
    font-size: 19px;
    font-style: normal;
    font-weight: 500;
}
html[dir="rtl"] .episode-row-copy strong em {
    margin-left: 0;
    margin-right: 18px;
}
.episode-row-copy small {
    max-height: 52px;
    display: -webkit-box;
    margin-top: 6px;
    overflow: hidden;
    color: #9aa1ac;
    font-size: 19px;
    line-height: 1.35;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
}

/* ==========================================================================
   Sliding chrome. While the cursor browses posters the top nav band and the
   lane band leave the screen; one Up brings them back with the focus already
   on them. Motion is compositor-only (translate3d + opacity). Animating
   height/padding on this WebView reflows the whole catalog every frame and
   lands at ~15 fps — that is the "ça fait comme un bug" feeling. JS adds
   `nova-chrome-sliding` first (bars and content glide together), then
   `nova-chrome-hidden` snaps the rows shut with no transition. The
   `:not(.nova-chrome-hidden)` guard drops the content translate at the snap
   so the grown layout does not sit 196px too high. On the detail pages the
   top bar is simply absent: switching section from inside a title's page
   only ever produced focus traps.
   ========================================================================== */
.topbar,
.catalog-screen > .category-rail,
.app-screen.catalog-screen,
.catalog-screen .catalog-main {
    transition:
        transform var(--nova-motion),
        opacity 0.28s linear;
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
}
html.nova-chrome-sliding .topbar,
html.nova-chrome-sliding .catalog-screen > .category-rail {
    transform: translate3d(0, -110%, 0);
    opacity: 0;
    pointer-events: none;
    will-change: transform;
}
html.nova-chrome-sliding:not(.nova-chrome-hidden) .app-screen.catalog-screen {
    transform: translate3d(0, calc(-1 * var(--nova-topbar-shift)), 0);
    will-change: transform;
}
html.nova-chrome-sliding:not(.nova-chrome-hidden) .catalog-screen .catalog-main {
    transform: translate3d(0, calc(-1 * var(--nova-lane-shift)), 0);
    will-change: transform;
}
html.nova-chrome-no-motion .topbar,
html.nova-chrome-no-motion .catalog-screen > .category-rail,
html.nova-chrome-no-motion .app-screen.catalog-screen,
html.nova-chrome-no-motion .catalog-screen .catalog-main {
    transition: none !important;
}
html.nova-chrome-hidden .topbar,
html.nova-chrome-hidden .catalog-screen > .category-rail {
    height: 0;
    padding-top: 0;
    padding-bottom: 0;
    border-bottom: 0;
    overflow: hidden;
    opacity: 0;
}
html.nova-chrome-hidden .app-screen.catalog-screen,
html.nova-chrome-hidden .catalog-screen .catalog-main {
    transform: none;
    transition: none;
}
/* Zero-height bars still expose their buttons to the focus engine (they keep
   their own box size); with the content gone there is nothing to land on. */
html.nova-chrome-hidden .topbar > *,
html.nova-chrome-hidden .catalog-screen > .category-rail > * {
    display: none;
}
html.nova-chrome-hidden .catalog-screen {
    grid-template-rows: 0 1fr;
}
html.nova-chrome-hidden .app-screen.catalog-screen {
    height: calc(1052px - 2 * var(--nova-safe-inset));
}
html.nova-chrome-hidden .catalog-screen .catalog-main {
    height: calc(1064px - 2 * var(--nova-safe-inset));
}
html.nova-chrome-hidden .catalog-shelves {
    height: calc(524px - 2 * var(--nova-safe-inset));
}
html.nova-chrome-hidden .catalog-screen.search-lane .catalog-shelves,
html.nova-chrome-hidden .catalog-screen.catalog-searching .catalog-shelves {
    height: calc(1000px - 2 * var(--nova-safe-inset));
}
html.nova-detail-open .topbar {
    display: none;
}
html.nova-detail-open .app-screen.detail-screen {
    height: calc(1052px - 2 * var(--nova-safe-inset));
}

/* ==========================================================================
   Browse hero, Prime-style. The poster sits top-right (top-left in Hebrew)
   and the black field must dissolve into it — a hard 30% slab of #05070a
   read as a cut. A long multi-stop fade plus a taller image (the poster
   now reaches further down) is what a streaming home does. Copy sits
   higher than the shelf, not glued to it. The first rail is lower; only
   about a quarter of the next rail peeks, so the screen is hero + one
   category, not two-thirds of another.
   chrome visible:  520 + 380 = 900
   chrome hidden:   610 + 454 = 1064
   one shelf ~368px → peek 12px (chrome up) / 86px ≈ 1/4 (chrome down)
   ========================================================================== */
.catalog-screen .catalog-feature {
    height: 540px;
}
.catalog-feature-backdrop {
    left: auto;
    width: 82%;
    opacity: 1;
    object-position: center 28%;
    transition: opacity 0.28s ease;
    -webkit-mask-image: linear-gradient(
        90deg,
        transparent 0%,
        rgba(0, 0, 0, 0.4) 16%,
        #000 38%
    );
    mask-image: linear-gradient(
        90deg,
        transparent 0%,
        rgba(0, 0, 0, 0.4) 16%,
        #000 38%
    );
}
.catalog-feature-shade {
    background:
        linear-gradient(
            90deg,
            #05070a 0%,
            rgba(5, 7, 10, 0.9) 14%,
            rgba(5, 7, 10, 0.52) 32%,
            rgba(5, 7, 10, 0.18) 50%,
            rgba(5, 7, 10, 0) 72%
        ),
        linear-gradient(
            0deg,
            #05070a 0%,
            rgba(5, 7, 10, 0.78) 18%,
            rgba(5, 7, 10, 0.22) 40%,
            rgba(5, 7, 10, 0) 62%
        );
}
.catalog-feature-copy {
    width: 1020px;
    justify-content: flex-end;
    padding: 30px 46px 88px;
}
.catalog-feature-copy h2 {
    max-width: 960px;
    text-shadow: 0 2px 14px rgba(0, 0, 0, 0.55);
}
.catalog-feature-summary {
    max-width: 900px;
    -webkit-line-clamp: 2;
    max-height: 64px;
    text-shadow: 0 1px 10px rgba(0, 0, 0, 0.5);
}
.catalog-feature-meta {
    text-shadow: 0 1px 10px rgba(0, 0, 0, 0.5);
}
html[dir="rtl"] .catalog-feature-backdrop {
    left: 0;
    right: auto;
    -webkit-mask-image: linear-gradient(
        270deg,
        transparent 0%,
        rgba(0, 0, 0, 0.4) 16%,
        #000 38%
    );
    mask-image: linear-gradient(
        270deg,
        transparent 0%,
        rgba(0, 0, 0, 0.4) 16%,
        #000 38%
    );
}
html[dir="rtl"] .catalog-feature-shade {
    background:
        linear-gradient(
            270deg,
            #05070a 0%,
            rgba(5, 7, 10, 0.9) 14%,
            rgba(5, 7, 10, 0.52) 32%,
            rgba(5, 7, 10, 0.18) 50%,
            rgba(5, 7, 10, 0) 72%
        ),
        linear-gradient(
            0deg,
            #05070a 0%,
            rgba(5, 7, 10, 0.78) 18%,
            rgba(5, 7, 10, 0.22) 40%,
            rgba(5, 7, 10, 0) 62%
        );
}
html[dir="rtl"] .catalog-feature-copy {
    margin-left: auto;
}

.catalog-shelves {
    height: calc(360px - 2 * var(--nova-safe-inset));
}
.catalog-screen.catalog-searching .catalog-feature,
.catalog-screen.search-lane .catalog-feature {
    height: 0;
    overflow: hidden;
}
.catalog-screen.search-lane .catalog-shelves,
.catalog-screen.catalog-searching .catalog-shelves {
    height: calc(836px - 2 * var(--nova-safe-inset));
}
html.nova-chrome-hidden .catalog-shelves {
    height: calc(524px - 2 * var(--nova-safe-inset));
}
html.nova-chrome-hidden .catalog-screen.search-lane .catalog-shelves,
html.nova-chrome-hidden .catalog-screen.catalog-searching .catalog-shelves {
    height: calc(1000px - 2 * var(--nova-safe-inset));
}

/* ==========================================================================
   Series page, closer to Netflix. Seasons stop being grey pills and become
   text tabs — quiet by default, white with an underline when selected.
   Episode rows drop the card box: bigger thumbnail, thin separators, and
   the row only lights up under focus (the global grey-fill recipe).
   ========================================================================== */
.season-tabs {
    margin-top: 20px;
    padding: 12px 10px 24px;
    overflow-x: auto;
}
.season-tabs > * + * {
    margin-left: 34px;
}
html[dir="rtl"] .season-tabs > * + * {
    margin-left: 0;
    margin-right: 34px;
}
.season-tab {
    position: relative;
    z-index: 1;
    min-width: 0;
    padding: 10px 12px 14px;
    border-radius: 10px;
    color: #878d98;
    background: transparent;
    /* The underline is a box-shadow, not border-bottom: the focus recipe
       repaints backgrounds but leaves shadows of unfocused siblings alone,
       and a border would nudge the text baseline between states. */
    box-shadow: inset 0 -3px 0 transparent;
}
.season-tab strong {
    font-size: 24px;
    font-weight: 700;
}
.season-tab small {
    display: block;
    margin-top: 3px;
    color: #6d737e;
    font-size: 16px;
}
.season-tab.selected {
    color: #ffffff;
    background: transparent;
    box-shadow: inset 0 -3px 0 #f2f3f5;
}
.season-tab.selected small {
    color: #aab0ba;
}
.episode-list {
    margin-top: 0;
    padding: 18px 10px 22px;
}
.episode-list .episode-card.episode-row {
    position: relative;
    z-index: 1;
    min-height: 150px;
    padding: 16px 20px;
    border-radius: 14px;
    background: transparent;
    overflow: visible;
}
.episode-list .episode-card.episode-row + .episode-card.episode-row {
    position: relative;
    margin-top: 4px;
}
.episode-list .episode-card.episode-row + .episode-card.episode-row::before {
    content: '';
    position: absolute;
    top: -2px;
    right: 20px;
    left: 20px;
    height: 1px;
    background: rgba(255, 255, 255, 0.08);
}
.episode-row-num {
    width: 58px;
    color: #7e848f;
    font-size: 32px;
    font-weight: 700;
}
.episode-row-thumb {
    width: 236px;
    height: 133px;
    border-radius: 8px;
}
.episode-row-copy {
    margin-left: 26px;
}
html[dir="rtl"] .episode-row-copy {
    margin-left: 0;
    margin-right: 26px;
}
.episode-row-copy strong {
    font-size: 25px;
}
.episode-row-copy small {
    margin-top: 7px;
    max-height: 56px;
    font-size: 20px;
    color: #969da8;
}

/* ==========================================================================
   Playback bar, much thinner (review: "bcp plus fine et plus classe").
   The 12px slab with a permanent 26px knob becomes a 5px line whose knob
   only appears once the bar is focused — Netflix's scrub behaviour. These
   rules sit after the finish-pass overrides near line 3620, so they win.
   ========================================================================== */
.player-timeline > span {
    min-width: 88px;
    color: rgba(255, 255, 255, 0.88);
    font-size: 20px;
}
.timeline-bar {
    height: 5px;
    border-radius: 3px;
    background: rgba(255, 255, 255, 0.24);
}
.timeline-fill {
    border-radius: 3px;
    background: #f2f3f5;
}
.timeline-thumb {
    width: 14px;
    height: 14px;
    opacity: 0;
    background: #f2f3f5;
    transform: translate(-7px, -7px);
    transition: opacity 0.12s ease;
}
.timeline-bar:focus {
    background: rgba(255, 255, 255, 0.3);
}
.timeline-bar:focus .timeline-thumb {
    width: 22px;
    height: 22px;
    opacity: 1;
    transform: translate(-11px, -11px);
    background: #ffffff;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.6);
}
.timeline-bubble {
    bottom: 22px;
    padding: 5px 12px;
    font-size: 21px;
}

/* ==========================================================================
   Film/series fullscreen dock. Review: the CSS glyphs next to Format and
   Sous-titres were unreadable, and parking the bar on a side made it feel
   like leftover live-TV chrome. A single centered row of named buttons,
   a bit larger, no icons. Live keeps the compact glyph dock.
   ========================================================================== */
.video-stage.fullscreen .video-overlay.vod-dock,
.video-stage.fullscreen .video-overlay.vod-dock.has-timeline {
    display: flex;
    flex-direction: column;
    flex-wrap: nowrap;
    justify-content: flex-end;
    align-items: center;
    height: 220px;
    padding: 24px 48px 40px;
    box-sizing: border-box;
    background: linear-gradient(transparent 0%, rgba(5, 7, 10, 0.55) 55%, rgba(5, 7, 10, 0.88) 100%);
}
/* The default timeline is position:absolute; top/left of the overlay — that
   was built for the live dock (status + glyphs in one row). On the VOD dock
   it sat on the left and painted over Format / Sous-titres. Take it out of
   absolute and stack it above the buttons, full width. */
.video-stage.fullscreen .video-overlay.vod-dock .player-timeline {
    position: relative;
    top: auto;
    right: auto;
    left: auto;
    width: 100%;
    max-width: none;
    flex: none;
    margin: 0 0 18px;
    pointer-events: auto;
}
.video-stage.fullscreen .video-overlay.vod-dock > * + * {
    margin-left: 0;
}
.video-stage.fullscreen .video-overlay.vod-dock .player-status {
    display: none;
}
.video-stage.fullscreen .video-overlay.vod-dock .player-controls {
    position: relative;
    z-index: 2;
    justify-content: center;
    flex-wrap: nowrap;
    overflow: visible;
    margin-left: 0;
    padding: 6px 10px;
    border-radius: 32px;
}
.video-stage.fullscreen .video-overlay.vod-dock .player-control,
.video-stage.fullscreen .video-overlay.vod-dock .player-control-primary,
.video-stage.fullscreen .video-overlay.vod-dock #player-fullscreen {
    width: auto;
    min-width: 0;
    height: 49px;
    padding: 0 22px;
}
.video-stage.fullscreen .video-overlay.vod-dock .player-control b {
    display: none;
}
.video-stage.fullscreen .video-overlay.vod-dock .player-control span {
    display: inline-block;
    font-size: 22px;
    font-weight: 650;
    letter-spacing: 0.02em;
    white-space: nowrap;
}
.video-stage.fullscreen .player-control,
.video-stage.fullscreen .player-control-primary {
    width: auto;
    min-width: 0;
    height: 58px;
    padding: 0 20px;
}
.video-stage.fullscreen .player-control span {
    display: inline-block;
    font-size: 19px;
    font-weight: 600;
    white-space: nowrap;
    vertical-align: middle;
}
.video-stage.fullscreen .player-control b {
    display: inline-block;
    margin: 0;
    vertical-align: middle;
}
.video-stage.fullscreen #player-fullscreen {
    width: auto;
    padding: 0 20px;
}

/* ==========================================================================
   Hebrew category rows. dir=rtl already mirrors the flex order, but the
   label kept its hard-coded text-align:left, so the text sat at the LEFT
   while the pin icon sat at the RIGHT — the exact inverse of what a Hebrew
   reader expects (review). row-reverse in an RTL context runs the row
   left-to-right again: icon at the far left, label glued to the right, and
   the LTR margins land back on the correct sides for free.
   unicode-bidi:plaintext + text-align:start then aligns each label by its
   OWN script: Hebrew names hug the right, while untranslated Latin names
   (beIN Sport, provider categories) sit flush left, as requested.

   The unfold caret is DOM-last, so row-reverse parked it on the RIGHT —
   on top of the right-aligned Hebrew label. order:-1 sends it to the far
   LEFT, pointing back at the text, with a margin the 0×0 triangle can
   paint into without covering the word.
   ========================================================================== */
html[dir="rtl"] #live-category-list .category-item {
    flex-direction: row-reverse;
}
html[dir="rtl"] #live-category-list .category-item span {
    unicode-bidi: plaintext;
    text-align: start;
}
html[dir="rtl"] #live-category-list .category-item .category-caret {
    order: -1;
    margin-left: 0;
    margin-right: 14px;
}
html[dir="rtl"] #live-category-list .category-item.category-subcategory {
    padding-left: 12px;
    padding-right: 46px;
}
html[dir="rtl"] #live-category-list .category-item.category-subcategory::before {
    left: auto;
    right: 24px;
}

/* Under the mini player the guide header is "GUIDE TV" (blue) + "Chaînes"
   (white) in one flex row. dir=rtl packs both on the right with the
   physical margin-left sitting on the outer edge, so they read as one
   glued blob. space-between sends the white title to the left of the
   screen, facing the blue label on the right. */
html[dir="rtl"] .guide-header > div:first-child {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    width: 100%;
}
html[dir="rtl"] .guide-header > div:first-child > * + * {
    margin-left: 0;
    margin-right: 0;
}
html[dir="rtl"] .program-panel .eyebrow {
    text-align: right;
}
html[dir="rtl"] .program-panel h2 {
    text-align: left;
}
/* Icon and label breathe: the shared `> * + *` margin is 8px, which reads
   cramped once real words sit next to the glyphs. */
.video-stage.fullscreen .player-control > * + * {
    margin-left: 12px;
}
html[dir="rtl"] .video-stage.fullscreen .player-control > * + * {
    margin-left: 0;
    margin-right: 12px;
}

/* The picture is a focus target of its own (Left off the category rail lands
   on it, OK opens it fullscreen), so it needs to say so. The ring itself is
   .stage-frame, an overlay above the picture -- see "Windowed mini-player"
   at the end of this file. Fullscreen never shows it: there the dock owns
   the cursor. */
.video-stage[data-focusable] {
    outline: none;
}

/* Live has no dock: no timeline to scrub, OK pauses, Back leaves fullscreen,
   Left/Right opens the channel list. Only the status line survives, because a
   dead stream has to be able to say so. The controls are removed from the
   layout rather than merely faded, so nothing invisible can still take a
   press. */
#video-overlay.controls-hidden #player-timeline,
#video-overlay.controls-hidden .player-controls {
    display: none !important;
}

/* Tablet-only: live fullscreen on TV still has no dock (Back + D-pad). The
   same APK on a tablet has neither, so a corner shrink chip stays painted. */
.player-shrink-touch {
    display: none;
}
html.touch-shell .video-stage.fullscreen .player-shrink-touch {
    display: block;
    position: absolute;
    z-index: 5;
    top: 28px;
    right: 36px;
    min-width: 160px;
    height: 64px;
    padding: 0 28px;
    border: 1px solid rgba(255, 255, 255, 0.28);
    border-radius: 16px;
    color: #fff;
    background: rgba(10, 17, 26, 0.88);
    font-size: 24px;
    font-weight: 650;
    pointer-events: auto;
}

/* ==========================================================================
   Replay: the chevron in the channel list, the listing page, the pause card
   and the differed-time badge.
   ========================================================================== */

/* The only way a viewer learns this channel replays — and the chevron is the
   promise that the forward arrow opens it. */
.zap-row-replay {
    flex: none;
    font-size: 20px;
    color: #7f8ea1;
}
.zap-row-replay i {
    margin-left: 4px;
    font-style: normal;
    font-size: 22px;
}
.zap-row.current .zap-row-replay {
    color: var(--app-selection-color);
}

/* Favourite star — always visible in the row but dim when not starred. */
.zap-row-fav {
    flex: none;
    margin-left: 12px;
    font-size: 20px;
    color: rgba(255, 255, 255, 0.18);
}
.zap-row-fav.active {
    color: #f5c518;
}
.zap-row.current .zap-row-fav {
    color: rgba(255, 255, 255, 0.38);
}
.zap-row.current .zap-row-fav.active {
    color: #f5c518;
}

/* Restarting what is on air leads the listing, so it is the one row that
   reads as an action rather than as a programme. */
.zap-row-restart .zap-row-number {
    color: var(--app-selection-color);
    font-size: 24px;
}
.zap-row-restart .zap-row-name strong {
    color: white;
}
/* The day a programme aired, on the first row of each day. Inline inside the
   row's second line: a header row of its own would have overflowed the panel's
   fixed eleven-row window. */
.zap-row-day {
    color: #c8d2de;
    font-weight: 600;
}

.live-pause-card {
    position: absolute;
    left: 0;
    top: 0;
    width: 1920px;
    height: 1080px;
    z-index: 7;
    background: rgba(3, 6, 12, 0.55);
    text-align: center;
}
.video-stage.fullscreen .live-pause-card {
    position: fixed;
}
.live-pause-inner {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 760px;
    /* Translate rather than negative margins: the card's height depends on its
       content, and a hardcoded half-height drifts off centre the moment that
       changes. */
    transform: translate(-50%, -50%);
    padding: 40px 46px 46px;
    border-radius: 26px;
    background: rgba(6, 11, 20, 0.92);
    box-shadow: 0 26px 70px rgba(0, 0, 0, 0.55);
    color: white;
}
.live-pause-mark {
    display: block;
    font-size: 42px;
    color: #8494a6;
}
.live-pause-inner strong {
    display: block;
    margin-top: 10px;
    font-size: 34px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.live-pause-actions {
    display: flex;
    justify-content: center;
    margin-top: 34px;
}
/* No flex `gap`: Chromium 63 (Tizen 5.0) drops it and welds the two buttons
   together. See the compatibility block above. */
.live-pause-actions > * + * {
    margin-left: 18px;
}
/* Styled from scratch rather than on top of .player-control: the dock's
   fullscreen rules force width:auto/min-width:0 at a higher specificity, so a
   card button borrowing that class came out chip-sized and the pair came out
   unequal. Two equal slabs is the whole point at three metres. */
.live-pause-button {
    width: 268px;
    height: 82px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0 24px;
    border: 0;
    border-radius: 18px;
    background: rgba(255, 255, 255, 0.12);
    color: white;
    font: inherit;
    font-size: 24px;
}
.live-pause-button b {
    margin-right: 12px;
    font-size: 22px;
    font-weight: 400;
}

/* Persistent while a pause-derived timeshift plays: the dock fades, and being
   behind the live edge without knowing it is how a match gets spoiled. */
.live-delay-badge {
    position: absolute;
    left: 54px;
    top: 44px;
    z-index: 5;
    padding: 10px 20px;
    border-radius: 999px;
    background: rgba(6, 11, 20, 0.82);
    color: #ffd479;
    font-size: 20px;
    letter-spacing: 0.04em;
}
/* Deliberately absolute rather than fixed-in-fullscreen like the overlays
   above: those carry an explicit 1920x1080 box of their own, so `fixed` costs
   them nothing, while a bare fixed badge would offset itself against the real
   viewport instead of the 1920 canvas. The stage is a positioned 1920-wide
   box in fullscreen and its own box otherwise, which is exactly the reference
   this badge wants in both. */

/* ==========================================================================
   Windowed mini-player: the corners and the cursor, drawn ON TOP.

   Two failures came from the same place, which is why they kept coming back
   one at a time. The stage clips itself to a rounded rectangle
   (`overflow: hidden` + `clip-path: inset(0 round 28px)`), and everything
   that tried to round or ring it was painted INSIDE that clip:

   - The corner masks were children of the stage, so the clip removed them in
     exactly the corner they existed to cover. They painted nothing, and the
     AVPlay plane -- a hardware rectangle behind the page that no CSS radius
     can touch -- kept its square corners. The picture was never round.
   - The focus ring was an `inset` box-shadow on the stage. An inset shadow is
     painted with the element's background, UNDER its children, so the idle
     placeholder (opaque, fills the stage) swallowed it whole; while AVPlay
     played there was no child in the way and the ring appeared. Same rule,
     two looks -- the cursor "bugging differently" empty and full.

   So the stage now clips as a plain rectangle (radius 0), which still keeps
   the dock's deliberate -2px bleed inside, and the rounding plus the cursor
   are one overlay above every layer: four masks cut the corners of whatever
   is showing (placeholder, <video>, or hardware plane), and the frame draws
   the hairline and the focus ring on the same arc. One mechanism, one look,
   in all three states. Fullscreen keeps none of it: rounding a 75" panel
   would read as a floating window.
   ========================================================================== */
:root {
    /* The masks and the frame must agree to the pixel or the corner reads as
       ragged. The two arc stops straddle the radius by half a pixel so the
       edge is antialiased instead of stepped, and so no sliver of square
       video can leak past it. --mini-shell is the page behind the stage
       (.live-main), which is what a cut corner has to look like. */
    --mini-radius: 28px;
    --mini-arc-in: 27.5px;
    --mini-arc-out: 28.5px;
    --mini-shell: #05070a;
}
.video-stage:not(.fullscreen) {
    overflow: hidden;
    /* No border: absolutely positioned children start at the padding box, so
       a 1px border pushed the masks 1px off the frame's own arc. */
    border: 0;
    border-radius: 0;
    clip-path: none;
}
.stage-corners {
    display: none;
    pointer-events: none;
}
.video-stage:not(.fullscreen) .stage-corners {
    display: block;
}
.video-stage.fullscreen .stage-corners {
    display: none !important;
}
.stage-corner {
    position: absolute;
    z-index: 8;
    width: var(--mini-radius);
    height: var(--mini-radius);
    pointer-events: none;
}
/* Each mask is a tile the size of the radius, filled everywhere OUTSIDE a
   circle centred on its INNER corner -- the quarter-disc of shell colour that
   turns a square corner into a round one. */
.stage-corner-tl {
    top: 0;
    left: 0;
    background: radial-gradient(circle 34px at 100% 100%, transparent var(--mini-arc-in), var(--mini-shell) var(--mini-arc-out));
}
.stage-corner-tr {
    top: 0;
    right: 0;
    background: radial-gradient(circle 34px at 0 100%, transparent var(--mini-arc-in), var(--mini-shell) var(--mini-arc-out));
}
.stage-corner-bl {
    bottom: 0;
    left: 0;
    background: radial-gradient(circle 34px at 100% 0, transparent var(--mini-arc-in), var(--mini-shell) var(--mini-arc-out));
}
.stage-corner-br {
    bottom: 0;
    right: 0;
    background: radial-gradient(circle 34px at 0 0, transparent var(--mini-arc-in), var(--mini-shell) var(--mini-arc-out));
}
/* Above the masks so the ring's arc is drawn over their cut, never beside
   it. Transparent outside the radius, so the cut corner stays cut. */
.stage-frame {
    position: absolute;
    z-index: 9;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    border-radius: var(--mini-radius);
    pointer-events: none;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
}
.video-stage:not(.fullscreen):focus .stage-frame {
    box-shadow:
        inset 0 0 0 3px #ffffff,
        inset 0 0 0 6px rgba(5, 7, 10, 0.88);
}
/* The stage itself never rings again -- including the engine-wide
   [data-focusable] rule, which is why these carry the id. Anything it drew
   would land under the children and reintroduce the empty/playing split. */
html.tizen-engine body #video-stage,
html.tizen-engine body #video-stage:focus,
html.tizen-engine.avplay-active body #video-stage:focus,
.avplay-active #video-stage:focus,
.video-stage:focus,
.video-stage[data-focusable]:focus {
    background-color: transparent !important;
    transform: none !important;
    outline: none !important;
    box-shadow: none !important;
}
.video-stage.fullscreen,
.video-stage.fullscreen:focus,
.video-stage.fullscreen[data-focusable]:focus {
    border-radius: 0;
    clip-path: none;
    box-shadow: none !important;
}

/* Tizen 5: mask-image + filter on the hero JPEG paint a black slab, and
   scale(1.09) on every poster step is the shelf lag. Shade overlay already
   fades the picture; the card keeps the inset ring without lifting. */
html.tizen-engine .catalog-feature-backdrop {
    left: 0 !important;
    right: 0 !important;
    width: 100% !important;
    opacity: 1 !important;
    filter: none !important;
    -webkit-mask-image: none !important;
    mask-image: none !important;
    transition: none !important;
}
/* …except a contained poster, which is the one case that must NOT be
   stretched edge to edge. The rule above exists to kill the mask and the
   filter (Tizen 5 paints them as a black slab); the geometry it also forces
   would undo the poster panel, so the poster restates it. Still no mask, no
   filter, no transform. */
html.tizen-engine .catalog-feature-backdrop.hero-art-poster {
    left: auto !important;
    right: 96px !important;
    width: auto !important;
    max-width: 34% !important;
    top: 8% !important;
    bottom: auto !important;
    height: 84% !important;
    object-fit: contain !important;
}
html.tizen-engine[dir="rtl"] .catalog-feature-backdrop.hero-art-poster {
    right: auto !important;
    left: 96px !important;
}
html.tizen-engine .catalog-shelf-card,
html.tizen-engine .catalog-shelf-card:focus,
html.tizen-engine .content-card:focus {
    transform: none !important;
    transition: none !important;
}
/* Focus has to be spelled out at the end of the file, with !important, and
   NOT next to the button's own colour. Three separate `[data-focusable]:focus`
   rules already carry !important — including the late one that wins on every
   engine — and all three paint a WHITE or blue ring, which is invisible on a
   white button. Declaring the dark ring beside the background lost the
   cascade silently: the button rendered light (correct) with no visible
   cursor (wrong). Caught by rendering it, not by reading the diff; see
   apps/android-tv/bench/shoot-detail-colours.mjs. Kept at the bottom, where the override
   block lives, so it is not moved back next to its component. */
.detail-actions .primary-button:focus {
    color: #101319 !important;
    background: #ffffff !important;
    background-image: none !important;
    box-shadow:
        inset 0 0 0 4px rgba(9, 12, 18, 0.92),
        0 10px 30px rgba(0, 0, 0, 0.5) !important;
    transform: none !important;
}
/* The favourite button sits next to it and is a DARK surface, so it keeps
   the shared light ring; only its filled state is restated, because the
   generic grey fill would otherwise erase the "this is a favourite" signal. */
.detail-actions .is-favorite,
.detail-actions .is-favorite:focus {
    color: #f5f5f7 !important;
}

html.tizen-engine .catalog-shelf-card:focus img,
html.tizen-engine .catalog-shelf-card:focus .placeholder {
    box-shadow:
        inset 0 0 0 4px #ffffff,
        inset 0 0 0 6px rgba(4, 10, 18, 0.85) !important;
}


