/* Regles propres au hub TV.
 *
 * `preview.css` est chargee avant celle-ci et fournit tout : le theme sombre,
 * les tokens de couleur, la typographie, `.container`, `.button`, `.eyebrow`,
 * `.sr-only` et le lien d'evitement. Rien de ce qui existe deja n'est redefini
 * ici.
 *
 * La page est concue telephone d'abord. Sur un petit ecran le lecteur passe en
 * haut, la liste en dessous sur une colonne, et la barre d'outils reste
 * atteignable au pouce. La grille passe a deux puis trois colonnes quand la
 * largeur le permet, jamais l'inverse.
 */

.tv-page {
    --tv-player-radius: var(--radius-md);
    padding-bottom: 0;

    /* La zone reservee par l'encoche d'un iPhone, en application installee.
     *
     * Elle vaut zero dans un navigateur, ou la page ne passe jamais dessous :
     * les regles qui l'additionnent restent donc justes dans les deux cas.
     * Elle ne se trouve nulle part ailleurs dans la feuille, alors que la
     * marge du bas est traitee en quatre endroits. */
    --tv-safe-top: env(safe-area-inset-top, 0px);

    /* La hauteur de la barre du haut, encoche exclue.
     *
     * Le lecteur se colle juste en dessous d'elle. La meme valeur est lue par
     * `tv-page.js`, qui mesure la barre plutot que de recopier un nombre :
     * les deux finissaient par diverger des que la hauteur changeait. */
    --tv-topbar-h: 3.5rem;
}

/* L'attribut `hidden` ne suffit pas.
 *
 * Le navigateur applique `display: none` a `[hidden]` depuis sa feuille de
 * style, donc une regle de classe comme `.tv-player-bar { display: flex }` est
 * plus specifique et l'annule : l'element reste affiche alors que le script
 * vient de le cacher. C'est exactement ce qui faisait apparaitre la barre de
 * transport a 0:00 et le message d'attente en meme temps que la video.
 *
 * La regle est ici plutot que sur chaque element, parce que le piege se
 * reproduira au prochain composant ajoute.
 */
.tv-page [hidden] {
    display: none !important;
}

/* Barre du haut ------------------------------------------------------------ */

.tv-topbar {
    position: sticky;
    z-index: 40;
    top: 0;
    border-bottom: 1px solid var(--line);
    background: color-mix(in srgb, var(--bg) 88%, transparent);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);

    /* Son contenu passe sous l'encoche sans cela. Le fond, lui, remonte
       jusqu'au bord : c'est le fond qui doit couvrir l'encoche, pas le vide. */
    padding-top: var(--tv-safe-top);

    /* La barre se retire quand on descend, et revient quand on remonte. Sur un
       telephone, ses 3.5rem font pres d'un dixieme de l'ecran : autant de
       place rendue a la liste qu on parcourt.
     *
     * Elle est `sticky` et non `fixed`, donc la retrait se fait par
     * `transform` : la barre garde sa place dans le flux, et le contenu en
     * dessous ne bouge pas d'un coup. */
    transition: transform 200ms ease;
}

/* La barre est sortie du cadre. Le lecteur suit : il recupere sa place, sinon
   il resterait sous une zone vide. */
.tv-page[data-barre-retiree] .tv-topbar {
    transform: translateY(-100%);
}

/* Le clavier doit toujours atteindre la barre : un lien qui garde le focus
   pendant que la barre est sortie serait visible nulle part. */
.tv-page[data-barre-retiree] .tv-topbar:focus-within {
    transform: none;
}

.tv-topbar-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    min-height: 3.5rem;
}

.tv-brand {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    color: var(--text);
    text-decoration: none;
}

.tv-brand img {
    border-radius: 0.5rem;
}

.tv-brand-copy {
    display: grid;
    line-height: 1.15;
}

.tv-brand-name {
    font-size: 0.95rem;
    font-weight: 700;
    letter-spacing: 0.01em;
}

.tv-brand-name em {
    color: var(--blue);
    font-style: normal;
}

.tv-brand-sub {
    color: var(--subtle);
    font-size: 0.68rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
}

.tv-topbar-actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.tv-topbar-link {
    padding: 0.4rem 0.6rem;
    color: var(--muted);
    font-size: 0.85rem;
    text-decoration: none;
}

.tv-topbar-link:hover {
    color: var(--text);
}

.tv-remote-button {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.45rem 0.75rem;
    border: 1px solid var(--line-strong);
    border-radius: 999px;
    background: var(--bg-panel);
    color: var(--text);
    font: inherit;
    font-size: 0.82rem;
    cursor: pointer;
}

.tv-remote-button:hover {
    border-color: var(--blue);
    color: var(--blue);
}

.tv-topbar-player-link {
    padding: 0.35rem 0.7rem;
    font-size: 0.78rem;
    min-height: 2.2rem;
    gap: 0.35rem;
}

/* Sur un telephone, le libelle prend toute la place du bouton. */
@media (max-width: 560px) {
    .tv-remote-button-label,
    .tv-topbar-link {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip: rect(0, 0, 0, 0);
        white-space: nowrap;
    }

    .tv-remote-button {
        padding: 0.45rem 0.6rem;
        font-size: 1rem;
    }
}

/* Titre -------------------------------------------------------------------- */

.tv-hero {
    padding: 2.5rem 0 1.5rem;
    background:
        radial-gradient(80rem 24rem at 50% -8rem, rgba(79, 112, 255, 0.16), transparent 70%),
        var(--bg);
}

.tv-hero-inner {
    max-width: var(--container);
    margin: 0 auto;
    padding: 0 1.25rem;
}

.tv-hero-title {
    margin: 0 0 0.8rem;
    font-family: var(--font-identity);
    font-size: clamp(2rem, 8vw, 3.4rem);
    font-weight: 700;
    line-height: 1.05;
    letter-spacing: -0.01em;
}

.tv-hero-lead {
    max-width: 46rem;
    margin: 0;
    color: var(--muted);
    font-size: 1rem;
    line-height: 1.65;
}

.tv-hero-lead code,
.tv-explain code,
.tv-modal code {
    padding: 0.1rem 0.35rem;
    border-radius: 0.3rem;
    background: var(--bg-panel);
    color: var(--cyan);
    font-size: 0.85em;
}

.tv-install {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem;
    margin: 1.4rem 0 0;
}

.tv-install-note {
    color: var(--subtle);
    font-size: 0.82rem;
}

/* La marche a suivre d'installation.
   Elle est visible par defaut, replie : tous les navigateurs ne declenchent
   pas l evenement d'installation, et sans elle un appareil eligible ne verrait
   rien du tout. */
.tv-install-howto {
    margin-top: 1rem;
    border-top: 1px solid var(--line);
    padding-top: 0.8rem;
}

.tv-install-howto > summary {
    color: var(--muted);
    font-size: 0.85rem;
    cursor: pointer;
}

.tv-install-howto > summary:hover {
    color: var(--text);
}

.tv-install-howto ul {
    margin: 0.8rem 0 0;
    padding-left: 1.1rem;
    color: var(--muted);
    font-size: 0.84rem;
    line-height: 1.6;
}

.tv-install-howto li + li {
    margin-top: 0.5rem;
}

.tv-install-howto code {
    font-size: 0.9em;
}

/* L'avertissement de certificat.
   Il doit se voir sans etre une alarme : la page fonctionne, seule
   l'installation est hors d'atteinte. */
.tv-install-warning {
    display: block;
    margin-top: 0.8rem;
    padding: 0.7rem 0.9rem;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-md);
    background: var(--bg-panel);
    line-height: 1.5;
}

.tv-install-warning[hidden] {
    display: none;
}

.tv-install-warning strong {
    color: var(--text);
}

/* Sur telephone, le héros doit tenir en quatre lignes. La liste est la raison
   d etre de la page : un titre d'une page et demie et un paragraphe avant elle
   feraient perdre l'écran à l utilisateur, alors qu il est venu voir des
   chaînes. Le détail est en pied de page et dans la section d'explication. */
@media (max-width: 760px) {
    .tv-hero {
        padding: 1.15rem 0 0.9rem;
    }

    .tv-hero .eyebrow {
        margin-bottom: 0.55rem;
        font-size: 0.62rem;
        letter-spacing: 0.12em;
    }

    .tv-hero-title {
        margin-bottom: 0.5rem;
        font-size: clamp(1.7rem, 8.5vw, 2.3rem);
        line-height: 1.1;
    }

    .tv-hero-lead {
        font-size: 0.88rem;
        line-height: 1.5;
    }

    .tv-install {
        margin-top: 0.9rem;
    }
}

/* Espace de travail : lecteur puis liste ---------------------------------- */

.tv-workspace {
    max-width: var(--container);
    margin: 0 auto;
    padding: 0 1.25rem 3rem;
}

/* Sur telephone, le lecteur n'existe pas avant le premier choix.
 *
 * Un cadre 16/9 vide fait 220 pixels de haut et repousse la liste sous la ligne
 * de flottaison : sur un ecran de 6 pouces, l'utilisateur voit un rectangle noir
 * et rien d'autre. Comme la liste est la raison d etre de la page, le lecteur
 * apparait au moment ou il sert, et le script le ramene a l'ecran apres la
 * premiere lecture.
 *
 * L'ordre du document n'est pas change : le lecteur reste avant la liste dans le
 * HTML, donc la navigation au clavier et les lecteurs d'ecran suivent le meme
 * ordre que sur un ordinateur. */
@media (max-width: 760px) {
    .tv-player:not(.is-active) {
        display: none;
    }
}

.tv-player.is-active {
    animation: tv-player-in 200ms ease-out;
}

@keyframes tv-player-in {
    from {
        opacity: 0;
        transform: translateY(-0.5rem);
    }
}

@media (prefers-reduced-motion: reduce) {
    .tv-player.is-active {
        animation: none;
    }

    /* La barre disparait et apparait sans glisser. Elle change toujours
       d'etat : c'est la position qu'on cherche, pas le mouvement. */
    .tv-topbar,
    .tv-player {
        transition: none;
    }
}

.tv-player {
    margin-bottom: 1.25rem;
    border: 1px solid var(--line);
    border-radius: var(--tv-player-radius);
    background: var(--bg-elevated);

    /* Le lecteur se colle en haut de la fenetre quand on descend chercher une
       chaine, et reprend sa place quand on remonte.
     *
     * Sans cela, l'utilisateur doit faire defiler toute la liste pour revenir
     * a l'image — et c'est precisement ce qu'il fait en cherchant parmi
     * 9 000 chaines. Le lecteur passe donc en mode compact :
     * l'image et le nom de la chaine restent la, en petit, et l'on sait
     * toujours ce qu'on regarde.
     *
     * `position: sticky` plutot que `fixed` : le lecteur revient
     * naturellement a sa place dans la page quand on remonte, sans qu'un
     * script ait a le reafficher. Et `top` laisse la place de la barre du
     * site, qui est elle aussi fixe. */
    position: sticky;
    top: calc(var(--tv-topbar-h) + var(--tv-safe-top));
    z-index: 20;

    /* Quand la barre se retire, le lecteur monte — mais il ne colle pas au
       bord. Collee a zero, la carte avait l'air d'etre coupee : bordure et
       coins arrondis contre l'ecran. Ces huit pixels la rendent entiere.
     *
     * La transition evite que les deux se deplacent d'un coup. */
    transition: top 200ms ease;
}

.tv-page[data-barre-retiree] .tv-player {
    top: calc(0.5rem + var(--tv-safe-top));
}

/* Le mode compact.
 *
 * On ne garde que l'essentiel quand on descend chercher une chaine : un
 * bandeau de l'image, le nom de ce qui joue, et le bouton pour revenir.
 *
 * L'image doit occuper plus de place que la barre. C'est la seule regle de
 * proportion ici, et elle a ete corrigee parce qu'elle n'etait pas tenue : a
 * 3.5rem de haut, l'image faisait cinquante-six pixels et la barre soixante.
 * On passait donc plus de temps a lire « Revenir a la video » qu'a voir ce
 * qu'on regardait — l'inverse de ce que le bandeau est cense faire.
 *
 * Le pays, la categorie et la qualite disparaissent aussi : ils sont deja sur
 * la carte qu'on a cliqee, et la carte est juste en dessous. Les outils —
 * retenir, partager, plein ecran — disparaissent de meme. Ils ne sont pas
 * interdits : ils reviennent des qu'on remonte, et sur une bande de quarante
 * pixels de haut ils seraient de toute facon inutilisables au doigt. La place
 * qu'ils occupent vaut mieux que celle de la liste qu'on parcourt.
 *
 * La pleine taille revient des qu'on remonte : rien n'est masque
 * definitivement, et rien ne depend du JavaScript pour cela. */
.tv-player[data-stuck] .tv-player-stage {
    /* Le cadre garde son ratio pour ne jamais rogner l'image en pleine
       taille, mais ici on le ramene a un bandeau. Il faut donc neutraliser le
       ratio : le `height` d'un bloc demande plus fort que `aspect-ratio`.
     *
     * Le `vh` de secours est ce qui evite de manger l'ecran en paysage, ou la
     * fenetre fait trois cent quatre-vingt-dix pixels de haut. */
    aspect-ratio: auto;
    height: min(6rem, 22vh);
    max-height: none;
}

/* En bandeau, l'image remplit la largeur : elle est donc rognee en hauteur,
 * et non pas en largeur comme d'habitude. On n'en voit que la moitie — ce
 * qu'il faut pour reconnaitre la chaine, pas pour regarder la sequence. La
 * pleine taille, elle, reste entierement visible. */
.tv-player[data-stuck] .tv-video {
    object-fit: cover;
}

.tv-player[data-stuck] .tv-player-bar {
    padding: 0.25rem 0.75rem;
}

.tv-player[data-stuck] .tv-player-tools {
    display: none;
}

/* Le sous-titre disparait en mode compact : pays, categorie et resolution
 * sont redits par la carte juste en dessous, et ils doublent la hauteur de
 * la barre — c'est lui qui la faisait depasser l'image. */
.tv-player[data-stuck] .tv-player-sub {
    display: none;
}

/* Le bouton « retour a la video » : c'est lui qui rend la main a
   l'utilisateur quand il a fini de chercher.
 *
 * Trente-six pixels de haut, sous les quarante-quatre recommandes au doigt.
 * C'est un compromis assume : c'est la hauteur du bouton qui fixait celle de
 * la barre, et donc la place que l'image perdait. */
.tv-player-unstick {
    display: none;
    align-items: center;
    gap: 0.35rem;
    min-height: 2.25rem;
    padding: 0.35rem 0.85rem;
    border: 1px solid var(--line-strong);
    border-radius: 999px;
    background: transparent;
    color: var(--blue);
    font: inherit;
    font-size: 0.78rem;
    white-space: nowrap;
    cursor: pointer;
}

.tv-player[data-stuck] .tv-player-unstick {
    display: inline-flex;
}

/* Sur telephone, le cadre 16/9 tient tout seul et la video remplit la largeur
   de l'ecran : le lecteur est la premiere chose qu'on voit.
   Sur un grand ecran, 16/9 sur toute la largeur ferait 620 pixels de vide avant
   d'atteindre la liste. La hauteur est donc plafonnee, et la video s'adapte au
   cadre sans jamais deformation. */
.tv-player-stage {
    position: relative;
    /* Plein largeur, sans centrage : la video se cadre dans le noir, et les
       bandes laterales d'un cadre 16/9 plus haut que large restent noires
       au lieu de laisser voir le fond du panneau. */
    width: 100%;
    aspect-ratio: 16 / 9;
    max-height: min(52vh, 30rem);
    background: #000;
}

.tv-video {
    display: block;
    width: 100%;
    height: 100%;
    background: #000;
    object-fit: contain;
}

.tv-video[hidden] {
    display: none;
}

.tv-player-idle {
    position: absolute;
    inset: 0;
    display: grid;
    place-content: center;
    gap: 0.4rem;
    padding: 1.5rem;
    color: var(--muted);
    text-align: center;
}

.tv-player-idle p {
    max-width: 34rem;
    margin: 0 auto;
    font-size: 0.9rem;
}

.tv-player-idle-mark {
    color: var(--blue);
    font-size: 2.4rem;
    line-height: 1;
}

.tv-player-idle-note {
    color: var(--subtle);
    font-size: 0.78rem;
}

.tv-player-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.85rem 1rem;
    border-top: 1px solid var(--line);
}

.tv-player-meta {
    min-width: 0;
}

.tv-player-name {
    margin: 0;
    font-size: 0.98rem;
    font-weight: 700;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tv-player-sub {
    margin: 0.15rem 0 0;
    color: var(--subtle);
    font-size: 0.78rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tv-player-tools {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
}

/* Fond transparent pose explicitement : ces boutons heritaient sinon le fond
   gris que la feuille du site donne a ses boutons, et trois dalles grises
   couchaient le nom de la chaine. */
.tv-tool-button {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.35rem 0.7rem;
    border: 1px solid var(--line-strong);
    border-radius: 999px;
    background: transparent;
    color: var(--muted);
    font: inherit;
    font-size: 0.8rem;
    white-space: nowrap;
    cursor: pointer;
}

.tv-tool-button:hover {
    border-color: var(--blue);
    color: var(--text);
}

.tv-tool-button:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 2px;
}

.tv-tool-button[aria-pressed="true"] {
    border-color: var(--orange);
    color: var(--orange);
}

.tv-player-error {
    margin: 0;
    padding: 0.85rem 1rem;
    border-top: 1px solid var(--line);
    background: rgba(255, 92, 122, 0.09);
    color: #ffb0c0;
    font-size: 0.86rem;
}

/* Un flux coupe annonce une mauvaise nouvelle, pas une catastrophe : en mode
   compact, le message tient sur une ligne. Le reste du bandeau appartient a la
   liste, et le texte entier revient des qu on remonte. */
.tv-player[data-stuck] .tv-player-error {
    padding: 0.35rem 0.75rem;
    font-size: 0.78rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Panneau de recherche ----------------------------------------------------- */

.tv-panel {
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    background: var(--bg-panel);
    padding: 1rem;
}

.tv-search {
    display: grid;
    gap: 0.6rem;
    grid-template-columns: 1fr;
}

.tv-search-field {
    position: relative;
    display: flex;
    align-items: center;
}

.tv-search-icon {
    position: absolute;
    left: 0.85rem;
    color: var(--subtle);
    font-size: 1.1rem;
    pointer-events: none;
}

.tv-search-input {
    width: 100%;
    padding: 0.8rem 2.5rem;
    border: 1px solid var(--line-strong);
    border-radius: 0.75rem;
    background: var(--bg);
    color: var(--text);
    font: inherit;
    font-size: 1rem;
}

.tv-search-input:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 1px;
}

.tv-search-clear {
    position: absolute;
    right: 0.5rem;
    padding: 0.3rem 0.5rem;
    border: 0;
    border-radius: 0.5rem;
    background: transparent;
    color: var(--muted);
    font-size: 1.2rem;
    line-height: 1;
    cursor: pointer;
}

.tv-search-clear:hover {
    color: var(--text);
}

.tv-select {
    width: 100%;
    padding: 0.7rem 2rem 0.7rem 0.8rem;
    border: 1px solid var(--line-strong);
    border-radius: 0.75rem;
    background: var(--bg) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='%23aeb6cd' d='M1 1l5 5 5-5'/%3E%3C/svg%3E") no-repeat right 0.8rem center / 0.7rem;
    color: var(--text);
    font: inherit;
    font-size: 0.9rem;
    appearance: none;
}

.tv-search-submit,
.tv-search-reset {
    width: 100%;
}

@media (min-width: 760px) {
    .tv-search {
        grid-template-columns: minmax(14rem, 1fr) 12rem 12rem auto;
        align-items: center;
    }

    .tv-search-submit {
        width: auto;
    }
}

/* Sur telephone, les deux listes partagent une rangee.
 *
 * Empilees, elles prenaient la moitie de l'ecran avant la premiere chaine, sur
 * un ecran ou chaque rangee compte. Le champ de recherche et le bouton
 * restent pleine largeur : ce sont les deux gestes les plus frequents. */
@media (max-width: 759px) {
    .tv-search {
        grid-template-columns: 1fr 1fr;
        gap: 0.5rem;
    }

    .tv-search-field {
        grid-column: 1 / -1;
    }

    /* « Filtrer » et « Tout effacer » partagent une rangee. Empiles, le second
       prenait une bande grise de pleine largeur sous le premier, pour une
       action qu'on utilise une fois. */
    .tv-search-submit,
    .tv-search-reset {
        grid-column: auto;
        width: 100%;
    }

    .tv-select {
        padding-left: 0.65rem;
        font-size: 0.85rem;
    }
}

.tv-quickfilters {
    display: flex;
    gap: 0.4rem;
    margin-top: 0.9rem;
    padding-bottom: 0.2rem;
    overflow-x: auto;
    scrollbar-width: none;
}

.tv-quickfilters::-webkit-scrollbar {
    display: none;
}

.tv-chip {
    flex: 0 0 auto;
    padding: 0.35rem 0.75rem;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: var(--bg);
    color: var(--muted);
    font: inherit;
    font-size: 0.8rem;
    white-space: nowrap;
    cursor: pointer;
}

.tv-chip:hover {
    border-color: var(--line-strong);
    color: var(--text);
}

.tv-chip.is-active {
    border-color: var(--blue);
    background: rgba(126, 154, 255, 0.14);
    color: var(--blue);
}

.tv-status {
    margin: 0.9rem 0 0;
    color: var(--subtle);
    font-size: 0.8rem;
}

/* Grille de chaines -------------------------------------------------------- */

.tv-grid {
    display: grid;
    gap: 0.6rem;
    margin: 0.75rem 0 0;
    padding: 0;
    list-style: none;
    grid-template-columns: 1fr;
}

@media (min-width: 560px) {
    .tv-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (min-width: 980px) {
    .tv-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

.tv-card {
    min-width: 0;
}

.tv-card-button {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    width: 100%;
    padding: 0.55rem;
    border: 1px solid var(--line);
    border-radius: 0.85rem;
    background: var(--bg-elevated);
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
    transition: border-color 160ms ease, background-color 160ms ease, transform 160ms ease;
}

.tv-card-button:hover {
    border-color: var(--line-strong);
    background: var(--bg-panel-light);
}

.tv-card-button:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 2px;
}

.tv-card.is-playing .tv-card-button {
    border-color: var(--blue);
    background: rgba(126, 154, 255, 0.1);
}

.tv-card.is-favorite .tv-card-play::after {
    content: "★";
    position: absolute;
    top: -0.15rem;
    right: -0.15rem;
    font-size: 0.7rem;
    color: var(--orange);
}

.tv-card-logo {
    position: relative;
    flex: 0 0 auto;
    width: 3rem;
    height: 3rem;
    border-radius: 0.6rem;
    background: var(--bg);
    overflow: hidden;
}

.tv-card-logo img {
    display: block;
    width: 100%;
    height: 100%;
    /* Les logos sont carres et de tailles tres variables : `object-fit` les
       centre sans les deformer, et le fond blanc de certains logos disparait
       sur un fond sombre. */
    object-fit: contain;
    padding: 0.2rem;
    background: #fff;
}

/* Le bouton de diffusion, sur la carte.
   Il est masque tant que le navigateur ne sait pas caster : sur un iPhone,
   ou sans Chromecast sur le reseau, un bouton qui ne fait rien est pire que
   pas de bouton du tout. */
.tv-card-cast {
    position: absolute;
    right: 0.5rem;
    bottom: 0.5rem;
    display: grid;
    place-content: center;
    width: 2.2rem;
    height: 2.2rem;
    padding: 0;
    border: 1px solid var(--line-strong);
    border-radius: 999px;
    background: rgba(8, 11, 22, 0.86);
    color: #fff;
    font-size: 0.85rem;
    cursor: pointer;
}

.tv-card-cast[hidden] {
    display: none;
}

.tv-card-cast:hover,
.tv-card-cast[aria-pressed='true'] {
    border-color: var(--blue);
    color: var(--blue);
}

.tv-card-cast:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 2px;
}

/* Bouton de diffusion dans le lecteur.
   Il est masque tant que le navigateur ne sait pas caster. */
[data-tv-cast-button] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.35rem;
    padding: 0.35rem 0.7rem;
    font-size: 0.78rem;
    min-height: 2.2rem;
    border: 1px solid var(--line-strong);
    border-radius: 999px;
    background: rgba(8, 11, 22, 0.86);
    color: #fff;
    cursor: pointer;
}

[data-tv-cast-button][hidden] {
    display: none;
}

[data-tv-cast-button]:hover,
[data-tv-cast-button][aria-pressed='true'] {
    border-color: var(--blue);
    color: var(--blue);
}

.tv-card-play {
    position: absolute;
    inset: 0;
    display: grid;
    place-content: center;
    background: rgba(8, 11, 22, 0.72);
    color: #fff;
    font-size: 1.1rem;
    opacity: 0;
    transition: opacity 160ms ease;
}

.tv-card-button:hover .tv-card-play,
.tv-card-button:focus-visible .tv-card-play,
.tv-card.is-playing .tv-card-play {
    opacity: 1;
}

.tv-card-body {
    display: grid;
    gap: 0.2rem;
    min-width: 0;
}

.tv-card-name {
    font-size: 0.9rem;
    font-weight: 600;
    line-height: 1.25;
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
}

.tv-card-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    color: var(--subtle);
    font-size: 0.74rem;
}

.tv-card-quality {
    color: var(--green);
}

.tv-card-labels {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    margin-top: 0.15rem;
}

.tv-tag {
    padding: 0.05rem 0.4rem;
    border-radius: 999px;
    background: var(--bg-panel-light);
    color: var(--muted);
    font-size: 0.65rem;
    letter-spacing: 0.02em;
}

.tv-tag--geo-blocked {
    color: var(--orange);
}

.tv-tag--not-24\/7 {
    color: var(--violet);
}

/* Les logos distants manquent parfois : on garde la place plutot que de
   laisser l'image s'effondrer. */
.tv-card-logo img[data-broken="true"] {
    opacity: 0;
}

/* Etat vide et pagination -------------------------------------------------- */

.tv-empty {
    display: grid;
    gap: 0.5rem;
    justify-items: start;
    margin-top: 1.25rem;
    padding: 1.5rem;
    border: 1px dashed var(--line-strong);
    border-radius: var(--radius-md);
    color: var(--muted);
    font-size: 0.9rem;
}

.tv-empty-title {
    margin: 0;
    color: var(--text);
    font-size: 1rem;
    font-weight: 700;
}

.tv-empty .button {
    margin-top: 0.4rem;
    text-decoration: none;
}

.tv-pagination {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    margin-top: 1.25rem;
    padding-top: 1rem;
    border-top: 1px solid var(--line);
}

.tv-page-link {
    padding: 0.5rem 0.9rem;
    border: 1px solid var(--line-strong);
    border-radius: 999px;
    color: var(--text);
    font-size: 0.85rem;
    text-decoration: none;
}

.tv-page-link:hover {
    border-color: var(--blue);
    color: var(--blue);
}

.tv-page-status {
    color: var(--subtle);
    font-size: 0.8rem;
}

/* Explications ------------------------------------------------------------- */

.tv-explain {
    padding: 3rem 0 4rem;
    border-top: 1px solid var(--line);
    background: var(--bg-elevated);
}

.tv-explain h2 {
    margin: 0 0 1.75rem;
    font-family: var(--font-identity);
    font-size: clamp(1.4rem, 4vw, 2rem);
    line-height: 1.15;
}

.tv-explain-grid {
    display: grid;
    gap: 1.75rem;
    grid-template-columns: 1fr;
}

@media (min-width: 860px) {
    .tv-explain-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

.tv-explain h3 {
    margin: 0 0 0.5rem;
    font-size: 1rem;
}

.tv-explain p {
    margin: 0;
    color: var(--muted);
    font-size: 0.9rem;
    line-height: 1.65;
}

/* Pied --------------------------------------------------------------------- */

.tv-footer {
    border-top: 1px solid var(--line);
    background: var(--bg);
}

.tv-footer-inner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding-top: 1.5rem;
    padding-bottom: 2rem;
    color: var(--subtle);
    font-size: 0.82rem;
}

.tv-footer a {
    color: var(--text);
    text-decoration: none;
}

.tv-footer a:hover {
    color: var(--blue);
}

.tv-footer p {
    margin: 0;
}

/* Boite de la telecommande ------------------------------------------------- */

.tv-modal[hidden] {
    display: none;
}

.tv-modal {
    position: fixed;
    z-index: 90;
    inset: 0;
    display: grid;
    place-items: end center;
}

.tv-modal-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(4, 6, 12, 0.7);
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
}

.tv-modal-panel {
    position: relative;
    width: min(30rem, 100%);
    margin: 0 0 0;
    padding: 1.25rem;
    border: 1px solid var(--line-strong);
    border-bottom: 0;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    background: var(--bg-panel);
    box-shadow: 0 -1.5rem 3rem rgba(0, 0, 0, 0.45);
}

@media (min-width: 640px) {
    .tv-modal {
        place-items: center;
    }

    .tv-modal-panel {
        margin: 1.5rem;
        border-bottom: 1px solid var(--line-strong);
        border-radius: var(--radius-lg);
    }
}

.tv-modal-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 0.9rem;
}

.tv-modal-head h2 {
    margin: 0;
    font-size: 1.1rem;
}

.tv-modal-close {
    padding: 0.25rem 0.6rem;
    border: 1px solid var(--line);
    border-radius: 0.6rem;
    background: transparent;
    color: var(--muted);
    font-size: 1.3rem;
    line-height: 1;
    cursor: pointer;
}

.tv-modal-lead {
    margin: 0 0 1rem;
    color: var(--muted);
    font-size: 0.9rem;
    line-height: 1.6;
}

.tv-code-box {
    display: grid;
    gap: 0.4rem;
    justify-items: center;
    padding: 1rem;
    border: 1px dashed var(--line-strong);
    border-radius: var(--radius-md);
    background: var(--bg);
}

.tv-code-label {
    color: var(--subtle);
    font-size: 0.7rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
}

.tv-code {
    font-family: var(--font-sans);
    font-size: clamp(2rem, 9vw, 2.8rem);
    font-weight: 700;
    letter-spacing: 0.35em;
    text-indent: 0.35em;
}

.tv-modal-note {
    margin: 0.9rem 0 0;
    color: var(--subtle);
    font-size: 0.8rem;
}

.tv-modal-note.is-error {
    color: #ffb0c0;
}

/* Respect des preferences systeme ----------------------------------------- */

@media (prefers-reduced-motion: reduce) {
    .tv-card-button,
    .tv-card-play {
        transition: none;
    }
}

/* ==========================================================================
   La page lecteur, affichee sur une television.
   ==========================================================================

   Cette section n'existait pas : la page etait rendue sans une seule regle,
   et sortait donc en HTML nu. Elle est ecrite pour un ecran de salon, donc
   pour etre lue a trois metres, et pour etre pilotable a la telecommande.

   Deux ecrans distincts et jamais melanges : l attente d appairage, puis la
   video. La video est masquee tant que l appairage n a pas eu lieu, parce
   qu'un cadre noir de 1920 pixels sur un ecran de salon fait croire a une
   television cassee. */

.tv-player-page {
    min-height: 100vh;
    min-height: 100dvh;
    padding: 0;
    overflow: hidden;
}

.tv-screen {
    position: fixed;
    inset: 0;
    background: #000;
}

/* La video occupe tout, et sa boite de cadre est respectee. */
.tv-screen-video {
    display: block;
    width: 100%;
    height: 100%;
    background: #000;
    object-fit: contain;
}

.tv-screen-video[hidden] {
    display: none;
}

/* L attente d appairage, en surimpression sur la video masquee. */
.tv-screen-overlay[hidden] {
    /* `display: grid` ci-dessous l'emporterait sur le `display: none` que
       l'agent utilisateur applique a [hidden] : l'ecran d attente resterait
       peint par-dessus la video, et l'utilisateur verrait le code de la
       television alors qu'il regarde une chaine. Sans cette regle, le lecteur
       fonctionne et parait casse. */
    display: none;
}

.tv-screen-overlay {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    padding: 2rem 1.25rem;
    overflow-y: auto;
    background:
        radial-gradient(60rem 30rem at 50% 0%, rgba(79, 112, 255, 0.16), transparent 70%),
        var(--bg);
}

.tv-screen-panel {
    display: grid;
    justify-items: center;
    gap: 1rem;
    width: min(46rem, 100%);
    text-align: center;
}

.tv-screen-mark {
    width: 4rem;
    height: 4rem;
    border-radius: 1rem;
}

.tv-screen-kicker {
    margin: 0;
    color: var(--blue);
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
}

.tv-screen-title {
    margin: 0;
    font-family: var(--font-identity);
    font-size: clamp(1.5rem, 4.5vw, 2.75rem);
    font-weight: 700;
    line-height: 1.15;
}

.tv-screen-lead {
    margin: 0;
    max-width: 34rem;
    color: var(--muted);
    font-size: clamp(0.95rem, 2vw, 1.15rem);
    line-height: 1.6;
}

.tv-screen-lead strong {
    color: var(--text);
}

/* Le code est l'information a recopier depuis un canape : il est le plus gros
   element de l'ecran, avec un espace fine entre les lettres pour le lire
   sans se tromper, et sans zero ni O. */
.tv-screen-code {
    display: block;
    padding: 1.1rem 1.6rem;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-md);
    background: var(--bg-panel);
    color: var(--text);
    font-family: var(--font-sans);
    /* Borne haute resserree : la page du lecteur est cadree, donc `vw` sur une
       television 4K produisait un code de quinze caracteres de haut, hors du
       panneau et difficile a recopier. `rem` suit la taille reelle de la
       page, qui est la meme partout. */
    font-size: clamp(2.2rem, 5vw, 3.6rem);
    font-weight: 700;
    letter-spacing: 0.22em;
    text-indent: 0.22em;
    line-height: 1;
    font-variant-numeric: tabular-nums;

    /* Six lettres espacees ne doivent jamais se casser en deux lignes.
       La coupure rendait le code illisible — « V 4 F Z » puis « 4 J » —
       alors que c'est precisement le chemin de secours quand le QR ne
       fonctionne pas. Le `clamp` a ete baisse en consequence : a 7rem les
       six lettres ne tenaient plus dans la largeur du panneau. */
    white-space: nowrap;
}

/* Le QR, sur la television.
   Le fond blanc est obligatoire : un QR inverse — modules clairs sur fond
   sombre — n'est pas lu par la plupart des lecteurs de telephone, qui
   cherchent la zone la plus claire pour la decoder, et l echappent. Deux
   telephones sur dix refusent un QR sombre. Le cadre blanc est donc pose par
   la feuille de style, autour d'une image qui elle aussi est sur fond blanc :
   c'est leger, et l'utilisateur ne voit aucune difference a l'ecran. */
.tv-screen-qr {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.8rem;
}

.tv-screen-qr[hidden] {
    display: none;
}

.tv-screen-qr-image {
    display: block;
    width: min(240px, 52vw);
    height: auto;
    padding: 0.85rem;
    border-radius: var(--radius-md);
    background: #fff;
}

/* Meme a 240 pixels de large, le QR se lit a trois metres : c'est la taille
   minimale recommandee, et le telephone est en general plus loin de l'ecran
   que la main du premier essai ne le fait croire. */
@media (min-width: 900px) and (max-height: 820px) {
    .tv-screen-qr-image {
        width: 190px;
    }
}

.tv-screen-qr-hint {
    max-width: 24rem;
    color: var(--muted);
    font-size: 0.95rem;
    line-height: 1.5;
    text-align: center;
}


/* Un ecran large et bas — une television en 720p, le cas le plus courant —
   n'a pas la hauteur pour le QR, le titre, les deux textes et le code les uns
   apres les autres : le code sortait du cadre, alors qu'il est justement la
   voie de secours quand le QR ne fonctionne pas.

   On resserre les marges et les tailles plutot que de supprimer quoi que ce
   soit — sauf le message sous le QR, qui repete ce que dit le titre. Le
   passage se fait sur la hauteur autant que sur la largeur, parce qu'une
   fenetre large et basse est precisement le cas a traiter. */
@media (min-width: 900px) and (max-height: 900px) {
    .tv-screen-panel {
        gap: 0.5rem;
    }

    .tv-screen-mark {
        width: 2.6rem;
        height: 2.6rem;
        border-radius: 0.7rem;
    }

    .tv-screen-title {
        font-size: clamp(1.3rem, 3vw, 1.9rem);
    }

    .tv-screen-qr-hint {
        display: none;
    }

    .tv-screen-code-details {
        margin-top: 0.3rem;
        padding-top: 0.7rem;
    }
}

/* Le code, toujours visible sous le QR.
   Ce n'est plus un <details> : un element repliable que rien ne replie
   n'annonce plus une action, et la fleche faisait croire qu'il y avait
   autre chose a ouvrir. */
.tv-screen-code-details {
    width: 100%;
    margin-top: 0.6rem;
    padding-top: 1.1rem;
    border-top: 1px solid var(--line);
}

.tv-screen-code-label {
    margin: 0 0 0.5rem;
    color: var(--muted);
    font-size: 0.78rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.tv-screen-status {
    margin: 0;
    min-height: 1.4em;
    color: var(--subtle);
    font-size: 0.9rem;
}

.tv-screen-status.is-error {
    color: #ffb0c0;
}

/* Le repli, discret : une television qui affiche le code n'en a pas besoin, et
   un element deplie au milieu de l ecran volerait la lecture au code. */
.tv-screen-fallback {
    margin-top: 0.5rem;
    color: var(--subtle);
    font-size: 0.85rem;
    text-align: left;
    width: min(34rem, 100%);
}

.tv-screen-fallback summary {
    cursor: pointer;
    text-align: center;
}

.tv-screen-fallback summary:hover {
    color: var(--muted);
}

.tv-screen-form {
    display: flex;
    gap: 0.5rem;
    margin-top: 0.75rem;
}

.tv-screen-input {
    flex: 1 1 auto;
    min-width: 0;
    padding: 0.85rem 1rem;
    border: 1px solid var(--line-strong);
    border-radius: 0.75rem;
    background: var(--bg);
    color: var(--text);
    font: inherit;
    font-size: 1.25rem;
    letter-spacing: 0.2em;
    text-align: center;
    text-transform: uppercase;
}

.tv-screen-input:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 1px;
}

.tv-screen-submit {
    flex: 0 0 auto;
    padding: 0.85rem 1.4rem;
    border: 1px solid var(--blue);
    border-radius: 0.75rem;
    background: var(--blue-strong);
    color: #fff;
    font: inherit;
    font-size: 0.95rem;
    font-weight: 600;
    cursor: pointer;
}

.tv-screen-submit:hover {
    border-color: var(--blue);
}

/* Le mode cinema : en plein ecran, il ne doit rester que l'image.
 *
 * Le retrait passe par un attribut sur la racine, et non par une classe ajoute
 * puis oubliee : l'attribut suit l'etat plein ecran reel, donc si
 * l'utilisateur quitte avec la touche Echap, la page redevient pilotable a la
 * souris sans qu'aucun script n'ait a revenir en arriere.
 *
 * Les controles du lecteur natif sont vises aussi. Ils ne sont pas dans le
 * HTML, donc aucune feuille de style ne les atteint — c'est le seul moyen
 * qu'ils restent caches sur une television. */
/* En mode cinema, la barre s'efface — mais pas le bouton de sortie.
 *
 * Masquer toute la barre enfermerait l'utilisateur : plus aucun moyen de
 * quitter le plein ecran depuis la television, et il ne reste que la touche
 * Echap de la commande ou un geste qu'il ne connait pas. La television est a
 * trois metres du telephone.
 *
 * Le bouton sort donc de la barre et se pose seul, en bas a droite, et
 * s'efface comme le reste. Il reste la seule chose d'interface visible en
 * mode cinema, ce qui est un prix raisonnable pour ne pas etre piege. */
[data-tv-cinema] .tv-screen-video::-webkit-media-controls {
    display: none;
}

[data-tv-cinema] .tv-screen-bar-copy,
[data-tv-cinema] .tv-screen-bar-error,
[data-tv-cinema] .tv-screen-bar-actions .tv-screen-bar-button:not([data-tv-local-fullscreen]) {
    display: none;
}

[data-tv-cinema] .tv-screen-bar {
    justify-content: flex-end;
    pointer-events: none;
}

[data-tv-cinema] .tv-screen-bar-actions {
    pointer-events: auto;
    opacity: 0.55;
    transition: opacity 400ms ease;
}

[data-tv-cinema] .tv-screen-bar:hover .tv-screen-bar-actions,
[data-tv-cinema] .tv-screen-bar-actions:focus-within {
    opacity: 1;
}

/* Le nom de la chaine, seul, puis disparu.
 *
 * Il apparait au changement de chaine et s'efface tout seul : c'est le
 * comportement d'un poste de television. Une barre de lecture permanente,
 * meme discrete, donnerait l'ecran d'un lecteur plutot que celui d'une
 * television — et sur un ecran de salon elle se voit de toute facon. */
.tv-screen-osd {
    position: absolute;
    top: clamp(1.5rem, 4vh, 3rem);
    left: 0;
    width: 100%;
    padding: 0 clamp(1rem, 4vw, 3rem);
    opacity: 0;
    pointer-events: none;
    transition: opacity 600ms ease;
}

.tv-screen-osd[data-visible] {
    opacity: 1;
}

.tv-screen-osd-title {
    display: inline-block;
    margin: 0;
    padding: 0.5rem 1.1rem;
    border-radius: 0.4rem;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    font-size: clamp(1.1rem, 2.8vw, 2rem);
    font-weight: 700;
    line-height: 1.2;
    text-shadow: 0 2px 12px rgba(0, 0, 0, 0.8);
}

/* Hors plein ecran, le nom reste dans la barre du bas : deux lignes a la fois
  occuperaient l'ecran pour rien. */
[data-tv-cinema] .tv-screen-osd-title {
    background: transparent;
    padding-left: 0;
}

/* La barre de lecture : en surimpression, elle ne vole pas de place a la
   video. Elle disparait au repos et reapparait au mouvement de la souris,
   comme les lecteurs du systeme. */
.tv-screen-bar {
    position: absolute;

    /* Au-dessus de la video, et non dessous.
     *
     * Sans cet ordre, la video — qui remplit la page — passe devant et
     * intercepte les clics : le bouton lecture devient inatteignable, et
     * l'utilisateur croit que la commande est morte. C'est exactement le
     * symptome qu'il signalait. */
    z-index: 20;
    right: 0;
    bottom: 0;
    left: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem 1rem;
    padding: 1rem clamp(1rem, 4vw, 3rem);
    padding-bottom: calc(1rem + env(safe-area-inset-bottom, 0px));
    background: linear-gradient(to top, rgba(4, 6, 12, 0.92), rgba(4, 6, 12, 0));

    /* La barre laisse passer les clics vers la video, mais pas vers ses
       propres boutons.
     *
     * `pointer-events: none` sur toute la barre rendait le bouton lecture et le
     * bouton plein ecran inatteignables : ils etaient peints, visibles,
     * et le clic allaitsur la video. L'utilisateur voit une commande et ne
     * peut pas la toucher — sans aucun signe que ce soit la cause.
     *
     * La zone qui n'a rien — le degrade au-dessus du texte — laisse passer,
     * donc un clic sur l'image hors des controles atteint toujours la video. */
    pointer-events: none;
}

.tv-screen-bar-actions,
.tv-screen-bar-copy,
.tv-screen-bar-error {
    pointer-events: auto;
}

.tv-screen-bar[hidden] {
    display: none;
}

.tv-screen-bar-copy {
    min-width: 0;
}

/* Les commandes de la barre, a cote du nom de la chaine.
   Elles ne sont pas dans le chemin critique : le telephone les porte aussi.
   Elles servent quand l'utilisateur n'a pas son telephone en main, ou quand
   une chaine a demarre sans que l'image suive. */
.tv-screen-bar-actions {
    display: inline-flex;
    flex: 0 0 auto;
    align-items: center;
    gap: 0.5rem;
}

.tv-screen-bar-button {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.45rem 0.9rem;
    border: 1px solid var(--line-strong);
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    font: inherit;
    font-size: 0.82rem;
    white-space: nowrap;
    cursor: pointer;
}

.tv-screen-bar-button:hover {
    border-color: var(--blue);
    color: var(--blue);
}

.tv-screen-bar-button:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 2px;
}

.tv-screen-bar-title {
    margin: 0;
    font-size: clamp(1rem, 2.4vw, 1.6rem);
    font-weight: 700;
    line-height: 1.2;
    text-shadow: 0 2px 12px rgba(0, 0, 0, 0.7);
}

.tv-screen-bar-sub {
    margin: 0.15rem 0 0;
    color: var(--muted);
    font-size: 0.9rem;
}

.tv-screen-error {
    margin: 0;
    color: #ffb0c0;
    font-size: 0.9rem;
}

/* Sur un telephone ou une petite fenetre de navigateur, la barre passe sur
   deux lignes : nom de la chaine lisible, message d erreur en dessous. */
@media (max-width: 620px) {
    .tv-screen-overlay {
        padding: 1.5rem 1rem;
    }

    .tv-screen-code {
        padding: 0.8rem 1rem;
        letter-spacing: 0.16em;
        text-indent: 0.16em;
    }

    .tv-screen-form {
        flex-direction: column;
    }

    .tv-screen-bar {
        flex-direction: column;
        align-items: flex-start;
    }
}

/* Sur un ecran de salon, le code gagne encore en taille, et la video garde
   toute la hauteur : un lecteur de television ne doit pas laisser de bande
   noire pour un titre. */
@media (min-width: 1400px) and (min-height: 800px) {
    .tv-screen-code {
        font-size: clamp(5rem, 9vw, 9rem);
        padding: 1.5rem 2.25rem;
    }

    .tv-screen-mark {
        width: 5rem;
        height: 5rem;
    }
}

@media (prefers-reduced-motion: reduce) {
    .tv-screen-bar {
        transition: none;
    }
}

/* ==========================================================================
   L appairage et la barre de commande, cote telephone.
   ========================================================================== */

/* Les etapes, enoncees dans l ordre ou elles se font. Une liste ordonnee
   dit l'ordre sans obliger a lire une phrase. */
.tv-remote-steps {
    margin: 0 0 1.1rem;
    padding-left: 1.35rem;
    color: var(--muted);
    font-size: 0.92rem;
    line-height: 1.6;
}

.tv-remote-steps strong {
    color: var(--text);
}

.tv-remote-steps code {
    padding: 0.1rem 0.35rem;
    border-radius: 0.3rem;
    background: var(--bg-panel);
    color: var(--cyan);
    font-size: 0.85em;
    white-space: nowrap;
}

/* Le champ de saisie du code.
   La lettre est grande et espacee : le code se tape en recopiant depuis la
   television, et les caracteres doivent rester separes pour ne pas se
   confondre. La verification retire la lettre erronee au lieu d interdire la
   frappe, pour ne pas contrarier une recopie imparfaite. */
.tv-remote-form {
    display: flex;
    gap: 0.5rem;
    margin-bottom: 0.75rem;
}

.tv-remote-input {
    flex: 1 1 auto;
    min-width: 0;
    padding: 0.85rem 1rem;
    border: 1px solid var(--line-strong);
    border-radius: 0.75rem;
    background: var(--bg);
    color: var(--text);
    font: inherit;
    font-size: 1.4rem;
    font-weight: 700;
    letter-spacing: 0.28em;
    text-align: center;
    text-transform: uppercase;
}

.tv-remote-input:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 1px;
}

.tv-remote-submit {
    flex: 0 0 auto;
    white-space: nowrap;
}

/* La barre de commande.
   Elle se place au-dessus du contenu, en bas de l'ecran, avec la marge de la
   zone sure des telephones a encoche : sans elle, le bouton du milieu tombe
   sous le trait d accueil. */
.tv-remote-bar {
    position: fixed;
    z-index: 60;
    right: 0;
    bottom: 0;
    left: 0;
    border-top: 1px solid var(--line-strong);
    background: color-mix(in srgb, var(--bg) 94%, transparent);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
}

.tv-remote-bar[hidden] {
    display: none;
}

.tv-remote-bar-inner {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.5rem;
    max-width: var(--container);
    margin: 0 auto;
    padding: 0.5rem 0.85rem calc(0.55rem + env(safe-area-inset-bottom, 0px));
}

/* La ligne du haut : l'etat de la liaison et le plein ecran. */
.tv-remote-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
}

.tv-remote-head-label {
    display: inline-flex;
    align-items: center;
    color: var(--green);
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    white-space: nowrap;
}

.tv-remote-head-dot {
    width: 0.45rem;
    height: 0.45rem;
    margin-right: 0.4rem;
    border-radius: 50%;
    background: var(--green);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--green) 22%, transparent);
}

/* Le bouton rond du plein ecran.
 *
 * Il est a l'ecart du pave : c'est une commande d'ecran, pas de lecture. Sur
 * une vraie commande, le bouton de mode se trouve seul, en haut. Le poser la
 * le evite de le confondre avec « arreter » au moment ou l'on cherche la
 * lecture. */
.tv-remote-round {
    display: inline-grid;
    place-items: center;
    /* 2.85rem, et non 2.6 : sous 44 pixels de large, le doigt vise a cote et
       appuie sur le pave. La cible tactile minimale fait 44 pixels, et elle
       s'applique aussi a un bouton rond. */
    width: 2.85rem;
    height: 2.85rem;
    padding: 0;
    border: 1px solid var(--line-strong);
    border-radius: 50%;
    background: var(--bg-panel);
    color: #fff;
    font-size: 1.1rem;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

.tv-remote-round:hover,
.tv-remote-round[aria-pressed='true'] {
    border-color: var(--blue);
    color: var(--blue);
}

.tv-remote-round:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 2px;
}

/* Le pave central : trois touches, celle du milieu deux fois plus grande.
 *
 * Deux touches : la lecture, qui change d'etat, et le son. Le premier bouton
 * de la premiere version etait « Arreter », separe de la lecture — deux
 * boutons pour une seule question, « est-ce que ca joue ? », et il fallait
 * choisir le bon avant d'appuyer. La lecture est l'action la plus frequente :
 * on la cherche au doigt, sans regarder. */
.tv-remote-pad {
    display: grid;
    grid-template-columns: 1.6fr 1fr;
    gap: 0.4rem;
    align-items: stretch;
}

.tv-remote-key {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.15rem;
    /* 3.15rem : au-dessus de 3.4rem, la barre prenait un tiers de l'ecran d'un
       telephone et repoussait la liste des chaines hors de vue. Cette hauteur
       garde une cible de toucher confortable. */
    min-height: 3.15rem;
    padding: 0.3rem 0.2rem;
    border: 1px solid var(--line-strong);
    border-radius: 0.9rem;
    background: var(--bg-panel);
    color: #fff;
    font: inherit;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;

    /* Une surface de toucher d'au moins 44 pixels : en dessous, le doigt
       rate la touche et appuie sur la voisine. C'etait la cause des
       « le bouton ne reagit pas » sur les petits telephones. */
    touch-action: manipulation;
}

.tv-remote-key:active {
    transform: scale(0.96);
}

.tv-remote-key:hover {
    border-color: var(--blue);
    color: var(--blue);
}

.tv-remote-key:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 2px;
}

.tv-remote-key-glyph {
    font-size: 1.2rem;
    line-height: 1;
}

/* La touche de lecture, deux fois et demie plus large : elle se trouve au
   doigt sans viser, ce qui est le but. Elle reste accentuee dans les deux
   etats — un bouton qui change de couleur quand il change de role ferait
   croire qu'il change de place. */
.tv-remote-key-main {
    background: var(--blue);
    border-color: var(--blue);
    color: #fff;
}

.tv-remote-key-main:hover {
    color: #fff;
    filter: brightness(1.08);
}

/* L'appui sur cette touche-la ne se voit pas, et c'est le pire endroit pour
   cela : la commande est la plus utilisee, et on ne sait pas si elle est
   partie. La touche s'enfonce au lieu de s'effacer. */
.tv-remote-key-main:active {
    transform: none;
    filter: brightness(0.88);
}

.tv-remote-key-label {
    font-size: 0.66rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

/* Le volume, en curseur tactile.
 *
 * Un pave qu'on pose au doigt, plutot que deux boutons de dix pour cent a
 * viser. Le telephone n'ecoute pas la video : le curseur ne sert donc pas a
 * montrer un niveau local, il envoie la valeur et affiche ce qu'il a pose.
 * La television est de l'autre cote de la piece — on ne voit pas la nuance,
 * on veut poser le doigt. */
.tv-remote-volume {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.15rem 0.3rem 0.1rem;
}

.tv-remote-volume-glyph {
    flex: 0 0 auto;
    font-size: 0.95rem;
    opacity: 0.75;
}

.tv-remote-volume-slider {
    flex: 1 1 auto;
    min-width: 0;
    /* 2.75rem : la piste est fine, mais la surface repondante fait plus de
       44 pixels de haut. Un rail mince sans zone tactile invisible dessous
       rate la moitie des appuis. */
    height: 2.75rem;
    margin: 0;
    background: transparent;
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
    touch-action: manipulation;
}

/* Le rail et le bouton du curseur, refaits : le rail par defaut est un
   rectangle de deux pixels, invisible sur fond sombre et impossible a
   viser. */
.tv-remote-volume-slider::-webkit-slider-runnable-track {
    height: 0.4rem;
    border-radius: 999px;
    background: var(--line-strong);
}

.tv-remote-volume-slider::-moz-range-track {
    height: 0.4rem;
    border-radius: 999px;
    background: var(--line-strong);
}

.tv-remote-volume-slider::-webkit-slider-thumb {
    width: 1.5rem;
    height: 1.5rem;
    margin-top: -0.55rem;
    border: 2px solid var(--bg);
    border-radius: 50%;
    background: var(--blue);
    box-shadow: 0 1px 6px rgba(0, 0, 0, 0.5);
    -webkit-appearance: none;
    appearance: none;
}

.tv-remote-volume-slider::-moz-range-thumb {
    width: 1.5rem;
    height: 1.5rem;
    border: 2px solid var(--bg);
    border-radius: 50%;
    background: var(--blue);
    box-shadow: 0 1px 6px rgba(0, 0, 0, 0.5);
}

.tv-remote-volume-slider:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 2px;
    border-radius: 999px;
}

.tv-remote-volume-level {
    flex: 0 0 2.6rem;
    color: var(--muted);
    font-size: 0.78rem;
    font-variant-numeric: tabular-nums;
    text-align: right;
}


/* Le message de deconnexion, en bas au-dessus de la barre. Il disparait tout
   seul : une information qui reste bloque l ecran et finit par etre ignoree. */
.tv-remote-toast {
    position: fixed;
    z-index: 70;
    right: 1rem;
    bottom: calc(4.5rem + env(safe-area-inset-bottom, 0px));
    left: 1rem;
    max-width: 26rem;
    margin: 0 auto;
    padding: 0.8rem 1rem;
    border: 1px solid var(--line-strong);
    border-radius: 0.75rem;
    background: var(--bg-panel);
    color: var(--muted);
    font-size: 0.85rem;
    line-height: 1.5;
    box-shadow: 0 1rem 2.5rem rgba(0, 0, 0, 0.45);
}

.tv-remote-toast[hidden] {
    display: none;
}

/* Meme emplacement que le message de deconnexion, au-dessus de la barre : c'est
   la ou l'utilisateur regarde apres avoir branche sa television.

   La hauteur est celle de la barre, mesuree au rendu, parce qu'elle change avec
   le nombre de controles et avec la taille de l'ecran. Une valeur en `rem`
   laissait le message au milieu du contenu sur un petit telephone, par-dessus
   le champ de recherche. */
.tv-remote-note {
    position: fixed;
    z-index: 61;
    right: 1rem;
    bottom: calc(var(--tv-remote-bar-height, 4rem) + 0.9rem + env(safe-area-inset-bottom, 0px));
    left: 1rem;
    max-width: 26rem;
    margin: 0 auto;
    padding: 0.7rem 1rem;
    border: 1px solid var(--blue-strong);
    border-radius: 0.75rem;
    background: var(--bg-panel);
    color: var(--text);
    font-size: 0.82rem;
    line-height: 1.5;
    text-align: center;
    box-shadow: 0 1rem 2.5rem rgba(0, 0, 0, 0.45);
}

.tv-remote-note[hidden] {
    display: none;
}

/* La barre occupe de la place en bas de l'ecran : le contenu remonte pour ne
   pas etre masque. Reserve au telephone, ou la barre est fixee.
 *
 * La barre elle-meme n'est plus restreinte : elle est en colonnes et non en
   rangee, donc elle tient sur n'importe quelle largeur. Seule la colonne du
   formulaire, qui a deux champs cote a cote, passe sur deux lignes. */
@media (max-width: 760px) {
    .tv-remote-form {
        flex-direction: column;
    }
}
