/**
 * Broadcast feature styles (ticker, live badges, TV/radio players).
 *
 * Populated in task 4 onward. Registered as the `misv-broadcast` handle by
 * MISV_Broadcast::enqueue_assets(), which loads it only on pages carrying one
 * of the widgets listed in MISV_Broadcast::WIDGET_NAMES.
 */

/* --- misv-ticker --- */

.misv-ticker,
button.misv-ticker{
  --misv-speed:34s;
  display:flex;
  align-items:stretch;
  overflow:hidden;
  background:var(--misv-ink);
}
/* Sans cette règle, `root.hidden = true` (misv-broadcast.js,
   updateTickerLiveItem()) ne masque RIEN à l'écran : en cascade CSS, une
   règle AUTEUR bat toujours une règle du NAVIGATEUR (la priorité se joue
   par origine avant la spécificité) -- le `[hidden]{display:none}` de la
   feuille par défaut du navigateur ne peut donc jamais l'emporter sur
   `.misv-ticker{display:flex}` ci-dessus. `.misv-ticker[hidden]` a une
   spécificité supérieure à `.misv-ticker` seule : elle gagne sans
   `!important`. Piège à connaître pour tout futur widget qui se
   retirerait conditionnellement via `hidden` sur un élément dont la
   feuille force déjà un `display`. */
.misv-ticker[hidden],
button.misv-ticker[hidden]{display:none}
.misv-ticker--green,
button.misv-ticker--green{background:var(--misv-green-deep)}

/* Vitesse : 34s desktop / 26s mobile (charte), définies ICI et nulle part
   ailleurs -- le renderer n'émet plus de `style="--misv-speed:…"` en ligne
   (un style inline aurait battu cette media query, portes fermées à toute
   variante future ; voir aussi l'interdiction du brief). Une vitesse
   personnalisée par l'éditeur (contrôle Elementor "speed", hors 34 = le
   défaut de la charte) arrive via la classe `.misv-ticker--speed-<n>`, posée
   par un `<style>` scoped généré par le renderer (nécessaire : la valeur est
   arbitraire, un sélecteur d'attribut ne peut pas être pré-énuméré dans
   cette feuille statique) ; deux classes lui donnent une spécificité
   supérieure aux deux règles ci-dessous, à toutes les largeurs. */
@media (max-width:767px){
.misv-ticker,
button.misv-ticker{--misv-speed:26s}
}

/* Badge : rendu correct côté serveur dès le premier paint (voir
   MISV_Ticker_Renderer::build_badge()). Le JS de misv-broadcast.js ne fait
   que réécrire ce bloc à réception de `misv:live-state` -- jamais de flash
   du mauvais état. */
.misv-ticker__badge,
button.misv-ticker__badge{
  flex:none;
  display:flex;
  align-items:center;
  gap:8px;
  padding:11px 18px;
  font-family:var(--misv-font);
  font-size:10.5px;
  font-weight:800;
  letter-spacing:.18em;
  text-transform:uppercase;
  white-space:nowrap;
}
.misv-ticker__badge--red,
button.misv-ticker__badge--red{background:var(--misv-red);color:#fff}
.misv-ticker__badge--gold,
button.misv-ticker__badge--gold{background:var(--misv-gold);color:var(--misv-ink)}
/* Fond `--misv-green-hover`, pas `--misv-green` (correction ronde 2, trouvée
   par mesure au rendu). Le libellé est en 10.5px/800, donc du texte normal :
   seuil 4,5:1. Sur `--misv-green #2E8B4A`, le blanc pur plafonne à 4,28:1 —
   c'est le maximum atteignable sur cet aplat, aucune couleur de texte ne peut
   le sauver. `--misv-green-hover #257A3F` donne 5,34:1 avec le même blanc.
   Même famille de défaut que les étiquettes vertes de misv-video-grid : c'est
   l'aplat qui est en cause, jamais le texte. */
.misv-ticker__badge--green,
button.misv-ticker__badge--green{background:var(--misv-green-hover);color:#fff}

.misv-ticker__dot,
button.misv-ticker__dot{
  flex:none;
  width:6px;height:6px;
  border-radius:50%;
  background:#fff;
  animation:misv-ticker-dot 1.6s ease-in-out infinite;
}
@keyframes misv-ticker-dot{
  0%,100%{opacity:1;transform:scale(1)}
  50%{opacity:.4;transform:scale(.8)}
}

/* Mini égaliseur : construit par MISV_Equalizer_Renderer::markup(bars:3,
   color:white, extraClasses:'misv-ticker__eq') côté PHP, et par le même
   `buildEqualizerEl()` côté JS (misv-broadcast.js) -- review round 1,
   constat 4 : les deux étaient auparavant deux copies indépendantes du même
   balisage, en plus du composant lui-même (trois producteurs). La forme/les
   couleurs/les délais viennent du composant (`.misv-eq`/`.misv-eq__bar`,
   misv-tokens.css) ; SA TAILLE reste déclarée ICI, en dur, plutôt que via
   les props `height`/`barWidth` du composant (qui émettraient un `<style>`
   scoped par le rendu SERVEUR uniquement) -- un élément équaliseur peut
   apparaître dans le DOM après coup, construit par le JS ci-dessus, sans
   qu'aucun rendu PHP n'ait eu lieu pour cet état précis ; une règle CSS
   statique et toujours présente dès le chargement de la feuille couvre les
   deux origines (SSR et JS) sans dépendre de QUAND l'élément apparaît,
   contrairement à un `<style>` scoped injecté par un seul rendu PHP
   particulier. C'est exactement l'usage documenté d'`extraClasses` sur
   MISV_Equalizer_Renderer : un hook pour la mise en place propre à
   l'appelant, la taille y compris quand elle doit survivre à une
   (re)construction côté client. Le composant neutralise déjà sa propre
   animation sous prefers-reduced-motion (misv-tokens.css) -- rien à refaire
   ici pour ce cas. */
.misv-ticker__eq,
button.misv-ticker__eq{height:10px;gap:2px}
.misv-ticker__eq .misv-eq__bar,
.misv-ticker__eq button.misv-eq__bar{width:2px;height:10px}
.misv-ticker__eq .misv-eq__bar:nth-child(1),
.misv-ticker__eq button.misv-eq__bar:nth-child(1){height:6px;animation-delay:-.15s}
.misv-ticker__eq .misv-eq__bar:nth-child(2),
.misv-ticker__eq button.misv-eq__bar:nth-child(2){height:10px;animation-delay:-.5s}
.misv-ticker__eq .misv-eq__bar:nth-child(3),
.misv-ticker__eq button.misv-eq__bar:nth-child(3){height:5px;animation-delay:-.8s}

.misv-ticker__viewport,
button.misv-ticker__viewport{
  flex:1 1 auto;
  overflow:hidden;
  position:relative;
}

/* Piste : deux copies identiques de la même liste (MISV_Ticker_Renderer::
   render_group(), appelée deux fois avec les mêmes items) et
   translate(-50%) -- c'est ce qui garantit qu'il n'y a pas de saut au
   raccord des deux copies. `width:max-content` empêche le flex parent de
   comprimer la piste avant l'animation.

   IMPORTANT (corrigé en ronde 1) : `gap` sur `.misv-ticker__track` s'applique
   aussi ENTRE les deux `.misv-ticker__group`. Avec gap:56px ici, la largeur
   de piste vaut 2W+56 et translateX(-50%) déplace de W+28, alors que la
   période visuelle du motif (bord gauche du groupe 1 au bord gauche du
   groupe 2) vaut W+56 -- il manquait 28px (la moitié du gap inter-groupes) à
   chaque tour. D'où gap:0 ici, et le rythme "56px après chaque item" reporté
   DANS le groupe via son propre `gap` interne + un `padding-right` qui
   réplique ce même rythme après le dernier séparateur, pour que le raccord
   entre les deux groupes ait exactement le même espacement que partout
   ailleurs sur la piste. */
.misv-ticker__track,
button.misv-ticker__track{
  display:flex;
  align-items:center;
  gap:0;
  width:max-content;
  padding:10px 0;
  animation:misv-marq var(--misv-speed) linear infinite;
}
.misv-ticker__group,
button.misv-ticker__group{
  display:flex;
  align-items:center;
  gap:56px;
  padding-right:56px;
}

/* Enveloppe de l'item "regarder en direct" -- voir render_group() (PHP) et
   updateTickerLiveItem() (JS). `display:contents` la retire du calcul du
   flex parent : ses enfants (item + séparateur) comptent comme des enfants
   directs de `.misv-ticker__group` pour le `gap`, la largeur des deux
   groupes varie donc toujours de concert quand le JS l'ajoute/la retire. */
.misv-ticker__live-wrap,
button.misv-ticker__live-wrap{display:contents}

.misv-ticker__item,
button.misv-ticker__item{
  display:inline-flex;
  align-items:center;
  font-family:var(--misv-font);
  font-size:11.5px;
  font-weight:600;
  letter-spacing:.1em;
  text-transform:uppercase;
  color:var(--misv-paper);
  text-decoration:none;
  white-space:nowrap;
}
a.misv-ticker__item:hover{text-decoration:underline}
a.misv-ticker__item:focus-visible{
  text-decoration:underline;
  outline:2px solid var(--misv-gold);
  outline-offset:2px;
}
.misv-ticker__item--verse,
button.misv-ticker__item--verse{
  color:var(--misv-on-dark);
  text-transform:none;
  letter-spacing:0;
  font-weight:500;
}
.misv-ticker--green .misv-ticker__item--verse,
.misv-ticker--green button.misv-ticker__item--verse{color:#B9D9C4}

.misv-ticker__sep,
button.misv-ticker__sep{flex:none;color:var(--misv-gold)}
.misv-ticker--green .misv-ticker__sep,
.misv-ticker--green button.misv-ticker__sep{color:var(--misv-green-light)}

/* Pause au survol (conditionnée par `pauseOnHover`) et au focus clavier
   (toujours active, quel que soit le réglage -- exigence d'accessibilité,
   voir class-misv-ticker-widget.php). */
.misv-ticker:not(.misv-ticker--no-hover-pause):hover .misv-ticker__track,
.misv-ticker:not(.misv-ticker--no-hover-pause):hover button.misv-ticker__track{
  animation-play-state:paused;
}
.misv-ticker:focus-within .misv-ticker__track,
.misv-ticker:focus-within button.misv-ticker__track{
  animation-play-state:paused;
}

/* Reduced motion : le socle (misv-tokens.css) coupe déjà l'animation de
   `.misv-ticker__track`. Ce qui manque ici, propre à ce widget, c'est le
   signal visuel de troncature (fondu à droite) et le repli sur une seule
   copie de la liste -- sans ça la piste statique afficherait les deux
   copies bout à bout, deux fois plus longue que nécessaire. */
@media (prefers-reduced-motion:reduce){
.misv-ticker__viewport,
button.misv-ticker__viewport{
    -webkit-mask-image:linear-gradient(to right,#000 85%,transparent 100%);
    mask-image:linear-gradient(to right,#000 85%,transparent 100%);
  }
.misv-ticker__group:nth-of-type(2),
button.misv-ticker__group:nth-of-type(2){display:none}
  /* Le point pulsé n'est neutralisé ni par la liste du socle
     (misv-tokens.css) ni par le reste de ce bloc -- ni misv-ticker-dot ni
     .misv-ticker__dot n'y figurent. */
.misv-ticker__dot,
button.misv-ticker__dot{animation:none;opacity:1;transform:scale(1)}
}

/* --- misv-live-status --- */

/* Enveloppe neutre commune aux 4 modes : porte data-misv-live-status,
   data-mode/data-status et l'attribut `hidden` (hideWhenOffline). Le contenu
   visuel réel (pilule, point, pastille, tuile) vient des classes du socle
   (misv-tokens.css) posées par le renderer -- cette classe ne fait que
   l'englober et gérer sa propre visibilité. */
.misv-live-status,
button.misv-live-status{display:inline-flex;align-items:center;gap:10px}
/* Même piège que `.misv-ticker[hidden]` : sans cette règle, `hidden` posé par
   le renderer (offline + hideWhenOffline) ou par misv-broadcast.js au
   changement d'état ne masquerait rien, `display:inline-flex`/`block`
   ci-dessous étant une règle AUTEUR qui bat la règle du navigateur. */
.misv-live-status[hidden],
button.misv-live-status[hidden]{display:none}

.misv-live-status--tile,
button.misv-live-status--tile{display:block;position:relative;border-radius:24px;overflow:hidden}
a.misv-live-status--tile{text-decoration:none;color:inherit}
/* min-height + fond : UNIQUEMENT quand la tuile n'a réellement aucune
   vignette (`misv-live-status--no-thumb`, posée par le renderer -- review
   round 1 constat 1, corrigé en round 2 "petite correction" : c'était
   inconditionnel, ce qui laissait une bande de fond ivoire visible sous une
   vraie vignette 16/9 dans un conteneur étroit). Sans vignette, un
   conteneur `display:block` sans enfant en flux (le badge/la pastille sont
   en `position:absolute`, hors flux) mesure 0px de haut : la pastille
   "DERNIER CULTE" se retrouvait en surimpression sur le contenu suivant. */
.misv-live-status--tile.misv-live-status--no-thumb,
button.misv-live-status--tile.misv-live-status--no-thumb{min-height:160px;background:var(--misv-ivory)}

.misv-live-status__thumb,
button.misv-live-status__thumb{display:block;border-radius:24px;overflow:hidden}
/* Rayon 24 -- "grandes cartes média & carte géo" (charte §Rayons). L'antenne
   (badge ● LIVE) reste à angles droits, seule exception listée dans cette
   même règle -- elle n'hérite de rien ici, .misv-badge-live--antenne fixe
   déjà border-radius:0 dans misv-tokens.css. */

.misv-live-status__badge,
button.misv-live-status__badge{position:absolute;top:14px;left:14px}

.misv-live-status__viewers,
button.misv-live-status__viewers{
  font-family:var(--misv-font);font-size:11px;font-weight:600;
  color:var(--misv-muted);white-space:nowrap;
}
.misv-live-status--tile .misv-live-status__viewers,
.misv-live-status--tile button.misv-live-status__viewers{display:block;margin-top:8px}

.misv-live-status--inline,
button.misv-live-status--inline{
  font-family:var(--misv-font);font-weight:700;font-size:12px;
  text-transform:uppercase;letter-spacing:.08em;color:var(--misv-body);
}
.misv-live-status--inline[data-status="live"],
button.misv-live-status--inline[data-status="live"]{color:var(--misv-red)}
.misv-live-status--inline[data-status="upcoming"],
button.misv-live-status--inline[data-status="upcoming"]{color:var(--misv-gold-text)}
.misv-live-status--inline[data-status="offline"],
button.misv-live-status--inline[data-status="offline"]{color:var(--misv-muted)}

/* Point pulsé isolé (mode dot) : variantes statiques pour upcoming/offline --
   .misv-dot (socle) pulse toujours en rouge, ce qui ne convient qu'à 'live'. */
.misv-dot--upcoming,
button.misv-dot--upcoming{background:var(--misv-gold);animation:none}
.misv-dot--offline,
button.misv-dot--offline{background:var(--misv-muted);animation:none}

/* --- misv-equalizer ---
   Rien à déclarer ici : `.misv-eq`/`.misv-eq__bar` (formes, couleurs,
   délais décalés, `--paused`, prefers-reduced-motion) vivent déjà dans
   misv-tokens.css et ne sont pas réécrits. Un `height`/`barWidth`
   personnalisé (hors 24px/3px par défaut) obtient sa propre règle scoped
   par une classe dérivée de sa valeur (`.misv-eq--h<n>`/`.misv-eq--bw<n>`),
   émise en `<style>` par MISV_Equalizer_Renderer lui-même -- même raison
   que la vitesse personnalisée du ticker : une valeur admin arbitraire ne
   peut pas être pré-énumérée dans cette feuille statique. */

/* --- misv-radio-player ---
   Cinq registres du même lecteur (tile / bar / card / mini / button). Les
   valeurs numériques viennent du brief §tâche 7 et ne sont pas arrondies.
   Rien ici ne redéfinit un composant du socle : les boutons passent par
   `.misv-btn*`, le point pulsé par `.misv-dot`, l'égaliseur par `.misv-eq*`
   (misv-tokens.css). Cette section ne déclare que la mise en place propre à
   ce widget -- exactement le rôle documenté d'`extraClasses` sur
   MISV_Equalizer_Renderer. */

.misv-radio-player,
button.misv-radio-player{
  display:block;
  font-family:var(--misv-font);
  position:relative;
}
/* Même piège que `.misv-ticker[hidden]` / `.misv-live-status[hidden]` : une
   règle AUTEUR qui pose un `display` bat le `[hidden]{display:none}` de la
   feuille du navigateur (la cascade se joue par origine avant la
   spécificité). Sans cette règle, le bouton « fermer » du mode mini ne
   masquerait rien. */
.misv-radio-player[hidden],
button.misv-radio-player[hidden]{display:none}

/* Note d'indisponibilité : toujours présente dans le DOM, seulement `hidden`
   quand une URL de flux est configurée (voir MISV_Radio_Player_Renderer::note()).
   Même piège `[hidden]` que ci-dessus, sur un élément dont on force le
   display.

   COULEUR : `--misv-body`, pas le `--misv-muted` des textes secondaires
   (ruling B du contrôleur, ronde 2). Sur `--misv-paper #FCFBF8` :
     --misv-muted #8A8073 -> 3,75:1   ECHEC (seuil AA texte normal 4,5:1)
     --misv-body  #5E564B -> 6,98:1   OK
   Ce n'est pas une exception locale à la charte mais le bon NIVEAU de jeton
   pour ce rôle : cette note n'est pas du texte secondaire décoratif, c'est le
   message qui explique pourquoi une commande ne répond pas -- il doit être lu,
   pas deviné. Les modes à racine sombre le surchargent juste en dessous. */
.misv-radio-player__note,
button.misv-radio-player__note{
  display:block;
  margin-top:10px;
  font-size:12px;
  font-weight:300;
  line-height:1.5;
  color:var(--misv-body);
}
.misv-radio-player__note[hidden],
button.misv-radio-player__note[hidden]{display:none}
/* Modes dont la RACINE est elle-même la surface sombre. `--tile` en est
   volontairement absent (ronde 1, constat 1) : la racine du mode tuile est un
   conteneur transparent, le vert ne vit que sur le `<button>`, et la note est
   sa SŒUR -- elle retombe donc sur le fond de section `--misv-paper #FCFBF8`,
   où `--misv-on-dark #B8AE9E` ne donnerait que 2,12:1. Elle garde donc le
   `--misv-body` de la règle de base (6,98:1). `--mini` est en revanche ajouté
   ici : sa racine EST la surface sombre, et `--misv-on-dark` y donne 8,53:1
   sur `--misv-ink` -- c'était le seul mode sombre absent de la liste.

   DETTE DE CHARTE, HORS PÉRIMÈTRE DE CE WIDGET : `--misv-muted #8A8073` échoue
   AA comme texte courant sur `--misv-paper` (3,75:1) PARTOUT où la charte
   l'emploie (misv-tokens.css : `.misv-small`, `.misv-counter__label`,
   `.misv-section-head__aside`…). Constat consigné au registre par le
   contrôleur pour la tâche 22 (passe accessibilité), qui le traitera à
   l'échelle du site. Ne pas l'attaquer d'ici. */
.misv-radio-player--bar .misv-radio-player__note,
.misv-radio-player--card .misv-radio-player__note,
.misv-radio-player--mini .misv-radio-player__note,
.misv-radio-player--mini button.misv-radio-player__note{color:var(--misv-on-dark)}
/* La barre et la mini-barre sont des conteneurs flex : sans base explicite,
   la note se retrouverait comprimée entre deux commandes. Elle prend donc
   toute la ligne (la barre autorise le retour à la ligne), et dans la
   mini-barre — haute de 56px, aucun retour possible — elle remplace les
   lignes de programme, qui n'ont de toute façon rien à annoncer quand aucun
   flux n'est configuré. */
.misv-radio-player--bar .misv-radio-player__note,
.misv-radio-player--bar button.misv-radio-player__note{flex:1 1 100%;margin-top:0}
.misv-radio-player--mini .misv-radio-player__note,
.misv-radio-player--mini button.misv-radio-player__note{
  flex:1 1 auto;min-width:0;margin-top:0;font-size:11.5px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.misv-radio-player--mini[data-available="0"] .misv-radio-player__schedule,
.misv-radio-player--mini[data-available="0"] button.misv-radio-player__schedule{display:none}

/* Tailles d'égaliseur propres à chaque emplacement. Déclarées en statique,
   scopées par la classe posée via `extraClasses` -- jamais par les props
   `height`/`barWidth` du composant, pour la même raison que
   `.misv-ticker__eq` (voir sa propre note plus haut) : un `<style>` scoped
   n'existe que dans le HTML qu'un rendu PHP précis a produit. Les hauteurs
   par barre reprennent les proportions du socle (12/20/9 sur 24px) à
   l'échelle demandée -- le décalage des délais d'animation, lui, reste au
   socle et n'est jamais redéclaré ici. */
.misv-radio-player__eq--tile,
.misv-radio-player__eq--card,
button.misv-radio-player__eq--card{height:12px;gap:2px}
.misv-radio-player__eq--tile .misv-eq__bar,
.misv-radio-player__eq--card .misv-eq__bar,
.misv-radio-player__eq--card button.misv-eq__bar{width:2.5px}
.misv-radio-player__eq--tile .misv-eq__bar:nth-child(1),
.misv-radio-player__eq--card .misv-eq__bar:nth-child(1),
.misv-radio-player__eq--card button.misv-eq__bar:nth-child(1){height:6px}
.misv-radio-player__eq--tile .misv-eq__bar:nth-child(2),
.misv-radio-player__eq--card .misv-eq__bar:nth-child(2),
.misv-radio-player__eq--card button.misv-eq__bar:nth-child(2){height:10px}
.misv-radio-player__eq--tile .misv-eq__bar:nth-child(3),
.misv-radio-player__eq--card .misv-eq__bar:nth-child(3),
.misv-radio-player__eq--card button.misv-eq__bar:nth-child(3){height:5px}

.misv-radio-player__eq--mini,
button.misv-radio-player__eq--mini{height:16px;gap:2px}
.misv-radio-player__eq--mini .misv-eq__bar,
.misv-radio-player__eq--mini button.misv-eq__bar{width:3px}
.misv-radio-player__eq--mini .misv-eq__bar:nth-child(1),
.misv-radio-player__eq--mini button.misv-eq__bar:nth-child(1){height:8px}
.misv-radio-player__eq--mini .misv-eq__bar:nth-child(2),
.misv-radio-player__eq--mini button.misv-eq__bar:nth-child(2){height:13px}
.misv-radio-player__eq--mini .misv-eq__bar:nth-child(3),
.misv-radio-player__eq--mini button.misv-eq__bar:nth-child(3){height:6px}

/* Bouton play/pause circulaire, or. Taille et rayon par mode ci-dessous. */
.misv-radio-player__play,
button.misv-radio-player__play{
  flex:none;
  display:inline-flex;
  align-items:center;
  justify-content:center;
  border:0;
  border-radius:50%;
  background:var(--misv-gold);
  color:var(--misv-ink);
  cursor:pointer;
  padding:0;
  transition:all .3s var(--misv-ease);
}
.misv-radio-player__play:hover,
button.misv-radio-player__play:hover{background:var(--misv-gold-light);transform:translateY(-2px)}
.misv-radio-player__play:active,
button.misv-radio-player__play:active{transform:translateY(0)}
.misv-radio-player__play:focus-visible,
button.misv-radio-player__play:focus-visible{outline:2px solid var(--misv-gold);outline-offset:3px}

/* ---------------------------------------------------------------------------
   ÉTAT « FLUX NON CONFIGURÉ » : AUCUNE ATTÉNUATION D'OPACITÉ.
   (Correction du 2026-09-07, constatée AU RENDU, pas en revue.)

   Ces trois contrôles portaient `opacity:.7` / `.55` quand aucune URL de flux
   n'est configurée. Mesuré au pixel réellement peint sur la page en ligne :
     tuile radio   eyebrow « RADIO »  2,60:1   sous-titre 2,57:1
                   (fond mesuré rgb(100,160,117), c'est-à-dire
                    --misv-green-hover composité par l'opacité)
     bouton or     « Écouter la radio » 3,45:1 sur rgb(127,103,44)
   Les trois rondes de la tâche 7 avaient validé 4,51:1 et 7,69:1 : exacts sur
   les JETONS, faux à l'écran, parce que l'`opacity` s'applique APRÈS la
   composition — un ratio calculé sur une valeur de jeton ne dit rien de ce qui
   est peint.

   Ce n'est pas un cas de bord : sans `radio_stream_url` configurée, c'est
   l'état de TOUS les lecteurs radio du site aujourd'hui. WCAG exempte les
   contrôles désactivés, mais ici « désactivé » ne veut pas dire « l'utilisateur
   ne peut pas s'en servir », ça veut dire « le site n'est pas encore
   configuré » : le texte doit rester lisible.

   L'indisponibilité est donc signalée par ce qui existait déjà et qui ne touche
   à aucun contraste : la note explicative visible (`__note`, pointée par
   `aria-describedby`), l'attribut `aria-disabled`, l'attribut `disabled`, et
   `cursor:not-allowed`. AUCUN signal visuel n'est ajouté à la place : le seul
   candidat sans texte était le triangle du bouton play, et une opacité de .55
   l'aurait mis à 3,09:1 sur l'or — au ras du seuil non textuel de 3:1, donc à
   refaire à la première retouche de palette. Une règle qui ne survit pas à une
   décimale ne vaut pas d'être écrite.

   RÈGLE GÉNÉRALE À TENIR : ne jamais atténuer par `opacity` un élément qui
   contient du texte. Si un état doit se voir, il se voit sur une bordure, une
   icône, un fond — jamais sur le conteneur du texte.
   --------------------------------------------------------------------------- */
.misv-radio-player__play[disabled],
.misv-radio-player__play[aria-disabled="true"],
button.misv-radio-player__play[aria-disabled="true"]{cursor:not-allowed}
.misv-radio-player__play[disabled]:hover,
.misv-radio-player__play[aria-disabled="true"]:hover,
button.misv-radio-player__play[aria-disabled="true"]:hover{background:var(--misv-gold);transform:none}

/* Icône : triangle « play » en bordures (aucun SVG, aucune fonte d'icônes à
   charger), permuté en deux barres « pause » quand aria-pressed passe à true.
   L'attribut ARIA porte donc à la fois l'état accessible et l'état visuel --
   une seule source de vérité, réécrite par misv-broadcast.js. */
.misv-radio-player__icon,
button.misv-radio-player__icon{
  position:relative;
  display:block;
  width:0;height:0;
  border-style:solid;
  border-width:7px 0 7px 12px;
  border-color:transparent transparent transparent currentColor;
  margin-left:3px;
}
.misv-radio-player__play[aria-pressed="true"] .misv-radio-player__icon,
.misv-radio-player__play[aria-pressed="true"] button.misv-radio-player__icon{
  width:12px;height:14px;
  border:0;
  margin-left:0;
}
.misv-radio-player__play[aria-pressed="true"] .misv-radio-player__icon::before,
.misv-radio-player__play[aria-pressed="true"] .misv-radio-player__icon::after,
.misv-radio-player__play[aria-pressed="true"] button.misv-radio-player__icon::after{
  content:"";position:absolute;top:0;
  width:4px;height:100%;
  background:currentColor;border-radius:1px;
}
.misv-radio-player__play[aria-pressed="true"] .misv-radio-player__icon::before,
.misv-radio-player__play[aria-pressed="true"] button.misv-radio-player__icon::before{left:0}
.misv-radio-player__play[aria-pressed="true"] .misv-radio-player__icon::after,
.misv-radio-player__play[aria-pressed="true"] button.misv-radio-player__icon::after{right:0}

/* Points d'entrée textuels : le système de boutons de la charte fait tout
   (rayon 12, Outfit 12/700/uppercase/ls .12em, padding 15/26, hover
   translateY(-2px), focus anneau or, jamais de box-shadow). Rien à
   redéclarer, seulement l'état inerte. */
.misv-radio-player__cta[disabled],
.misv-radio-player__cta[aria-disabled="true"],
button.misv-radio-player__cta[aria-disabled="true"]{cursor:not-allowed}
.misv-radio-player__cta[disabled]:hover,
.misv-radio-player__cta[aria-disabled="true"]:hover,
button.misv-radio-player__cta[aria-disabled="true"]:hover{transform:none}
.misv-radio-player__cta--block,
button.misv-radio-player__cta--block{width:100%;justify-content:center}

/* Lignes de programme, communes à tous les modes qui les affichent. */
.misv-radio-player__schedule,
button.misv-radio-player__schedule{display:block}
.misv-radio-player__now,
.misv-radio-player__next,
button.misv-radio-player__next{
  display:block;
  font-size:12.5px;
  font-weight:300;
  line-height:1.5;
  color:var(--misv-on-dark);
}
.misv-radio-player__now[hidden],
.misv-radio-player__next[hidden],
button.misv-radio-player__next[hidden]{display:none}

/* --- mode tile (hero) ---
   Angles droits : registre antenne (charte §Rayons). */
.misv-radio-player__tile,
button.misv-radio-player__tile{
  display:flex;
  flex-direction:column;
  gap:8px;
  width:100%;
  text-align:left;
  border:0;border-radius:0;
  /* FOND : `--misv-green-hover`, pas `--misv-green` que le brief donnait
     verbatim (ronde 1, constat 2c). Ce n'est PAS un choix esthétique, c'est
     la seule variable qui pouvait rendre AA atteignable, et les deux options
     envisagées (« blanc, ou une variante assombrie du vert pâle ») sont l'une
     et l'autre inatteignables sur `--misv-green` -- mesuré :
       luminance relative de #2E8B4A = 0,1954
       plafond ABSOLU en texte clair (blanc pur)  -> 4,28:1   ECHEC
       plafond ABSOLU en texte sombre (noir pur)  -> 4,91:1
       --misv-green-pale #D9F2E2                  -> 3,62:1   ECHEC
       --misv-ink #141210                         -> 4,37:1   ECHEC
     Autrement dit : sur ce fond, AUCUNE couleur de texte clair de la charte
     n'atteint 4,5:1, et seul du noir quasi pur y parviendrait -- ce qui
     inverserait complètement le registre voulu (eyebrow pâle sur tuile
     verte). Le fond descend donc d'un cran DANS LA MÊME FAMILLE, sur un jeton
     qui existe déjà (aucun jeton neuf) :
       --misv-green-hover #257A3F + --misv-green-pale -> 4,51:1  OK
       --misv-green-hover #257A3F + blanc             -> 5,34:1  OK
     Le survol descend d'autant, sur `--misv-green-deep` (9,84:1 avec le vert
     pâle) : le sens « le survol assombrit » est conservé. */
  padding:26px;
  background:var(--misv-green-hover);
  cursor:pointer;
  transition:background .3s var(--misv-ease);
}
.misv-radio-player__tile:hover,
button.misv-radio-player__tile:hover{background:var(--misv-green-deep)}
.misv-radio-player__tile:focus-visible,
button.misv-radio-player__tile:focus-visible{outline:2px solid var(--misv-gold);outline-offset:3px}
.misv-radio-player__tile[disabled],
.misv-radio-player__tile[aria-disabled="true"],
button.misv-radio-player__tile[aria-disabled="true"]{cursor:not-allowed}
.misv-radio-player__tile[disabled]:hover,
.misv-radio-player__tile[aria-disabled="true"]:hover,
button.misv-radio-player__tile[aria-disabled="true"]:hover{background:var(--misv-green-hover)}
/* Eyebrow : `--misv-green-pale` CONSERVÉ (valeur du brief), 10px/800 = texte
   normal, seuil 4,5:1 -> 4,51:1 sur le fond `--misv-green-hover`. La marge est
   mince d'un centième : si le contrôleur la juge trop juste, un passage au
   blanc (5,34:1) est un changement d'une ligne -- signalé, pas décidé seul.
   Ce calcul n'est redevenu VRAI qu'avec le retrait de l'`opacity:.7` de l'état
   « flux non configuré » ci-dessus : mesuré au pixel, le même couple donnait
   2,60:1 tant que l'opacité s'appliquait par-dessus. */
.misv-radio-player--tile .misv-radio-player__eyebrow,
.misv-radio-player--tile button.misv-radio-player__eyebrow{
  display:flex;align-items:center;gap:10px;
  font-size:10px;font-weight:800;letter-spacing:.2em;text-transform:uppercase;
  color:var(--misv-green-pale);
}
/* Titre 21px/900 = texte LARGE au sens WCAG (>= 18,66px gras), seuil 3:1 ->
   5,34:1. Valeur du brief (blanc), inchangée. */
.misv-radio-player--tile .misv-radio-player__title,
.misv-radio-player--tile button.misv-radio-player__title{
  font-weight:900;font-size:21px;line-height:1.15;color:#fff;
}
/* Sous-titre 11.5px/600 = texte normal, seuil 4,5:1 -> `--misv-green-pale`,
   4,51:1 sur le fond `--misv-green-hover`, comme l'eyebrow.
   La ronde 1 l'avait passé au BLANC (5,34:1) au motif que le brief ne fixe pas
   sa couleur et qu'on pouvait donc y prendre de la marge. Le ruling A de la
   ronde 2 tranche l'inverse et c'est lui qui s'applique : le brief a choisi un
   traitement TONAL, sourd ; le blanc rendrait le bloc plus criard que la
   charte ne le veut pour gagner une marge dont personne n'a besoin dès lors
   que 4,51:1 satisfait le critère. Cohérence visuelle eyebrow/sous-titre
   rétablie par la même occasion. */
.misv-radio-player--tile .misv-radio-player__subtitle,
.misv-radio-player--tile button.misv-radio-player__subtitle{
  font-size:11.5px;font-weight:600;color:var(--misv-green-pale);
}

/* --- mode bar (accueil) ---
   #211D18 / #2A241C sont exactement --misv-media-1 / --misv-media-2
   (misv-tokens.css) : le dégradé du brief, écrit avec les jetons qui portent
   déjà ces deux valeurs plutôt qu'une seconde copie littérale. */
.misv-radio-player--bar,
button.misv-radio-player--bar{
  display:flex;
  align-items:center;
  gap:22px;
  flex-wrap:wrap;
  background:linear-gradient(90deg,var(--misv-media-1),var(--misv-media-2));
  border:1px solid rgba(201,161,59,.35);
  border-radius:18px;
  padding:20px 28px;
}
.misv-radio-player__play--bar,
button.misv-radio-player__play--bar{width:54px;height:54px}
.misv-radio-player--bar .misv-radio-player__text,
.misv-radio-player--bar button.misv-radio-player__text{
  flex:1 1 240px;display:flex;flex-direction:column;gap:4px;min-width:0;
}
.misv-radio-player__heading,
button.misv-radio-player__heading{display:flex;align-items:center;gap:14px;flex-wrap:wrap}
.misv-radio-player--bar .misv-radio-player__title,
.misv-radio-player--bar button.misv-radio-player__title{
  font-size:16px;font-weight:600;color:var(--misv-paper);
}
/* « SUR LES ONDES » : `--misv-live-light`, PAS `--misv-red` que le brief
   donnait verbatim (ruling R23 du contrôleur, application de la « Règle des
   Deux Familles » du DESIGN.md -- même précédent que `--misv-gold-text` pour
   l'or des eyebrows). Mesuré sur le dégradé de la barre, en 10px/700 (texte
   normal, seuil 4,5:1) :
     --misv-red        #D1202A -> 3,15:1 sur #211D18, 2,89:1 sur #2A241C  ECHEC
     --misv-live-light #FF6B72 -> 6,06:1 sur #211D18, 5,56:1 sur #2A241C  OK */
.misv-radio-player__status,
button.misv-radio-player__status{
  display:inline-flex;align-items:center;gap:7px;
  font-size:10px;font-weight:700;letter-spacing:.16em;text-transform:uppercase;
  color:var(--misv-live-light);
}
/* Hors antenne : teinte mate, accordée au point non animé
   (.misv-dot--offline) -- le rouge « sur les ondes » ne doit pas subsister
   sans antenne.
   `--misv-placeholder` et non `--misv-muted` (ronde 1, constat 2) :
     --misv-muted       #8A8073 -> 4,32:1 sur #211D18, 3,96:1 sur #2A241C ECHEC
     --misv-placeholder #B0A695 -> 6,97:1 sur #211D18, 6,39:1 sur #2A241C OK
   Choisi parmi les jetons existants qui passent (placeholder 6,39 · on-dark
   7,01 · sand 7,97 · line-3 10,36 · on-dark-link 10,03 · ivory 14,36 · paper
   14,84, au pire point du dégradé) comme LE PLUS SOMBRE d'entre eux : c'est
   celui qui préserve la hiérarchie visuelle voulue -- « hors antenne » doit
   rester plus discret que le direct -- tout en franchissant le seuil. Son nom
   (« placeholder ») porte déjà cette sémantique d'état inactif. Aucun jeton
   neuf créé. */
.misv-radio-player--bar[data-on-air="0"] .misv-radio-player__status,
.misv-radio-player--bar[data-on-air="0"] button.misv-radio-player__status{color:var(--misv-placeholder)}
.misv-radio-player__desc,
button.misv-radio-player__desc{
  font-size:12.5px;font-weight:300;line-height:1.55;color:var(--misv-on-dark);
}
/* Égaliseur or 6 barres du brief : 4px de large, hauteurs 22/14/24/12/18/21.
   Déclaré ICI en statique (et non via les props `height`/`barWidth` du
   composant) pour la même raison que `.misv-ticker__eq` : un `<style>` scoped
   n'existe que dans le HTML qu'un rendu PHP précis a produit, alors qu'un
   égaliseur peut aussi être manipulé côté client. */
.misv-radio-player__eq--bar,
button.misv-radio-player__eq--bar{height:24px;gap:3px}
.misv-radio-player__eq--bar .misv-eq__bar,
.misv-radio-player__eq--bar button.misv-eq__bar{width:4px}
.misv-radio-player__eq--bar .misv-eq__bar:nth-child(1),
.misv-radio-player__eq--bar button.misv-eq__bar:nth-child(1){height:22px}
.misv-radio-player__eq--bar .misv-eq__bar:nth-child(2),
.misv-radio-player__eq--bar button.misv-eq__bar:nth-child(2){height:14px}
.misv-radio-player__eq--bar .misv-eq__bar:nth-child(3),
.misv-radio-player__eq--bar button.misv-eq__bar:nth-child(3){height:24px}
.misv-radio-player__eq--bar .misv-eq__bar:nth-child(4),
.misv-radio-player__eq--bar button.misv-eq__bar:nth-child(4){height:12px}
.misv-radio-player__eq--bar .misv-eq__bar:nth-child(5),
.misv-radio-player__eq--bar button.misv-eq__bar:nth-child(5){height:18px}
.misv-radio-player__eq--bar .misv-eq__bar:nth-child(6),
.misv-radio-player__eq--bar button.misv-eq__bar:nth-child(6){height:21px}
.misv-radio-player__cta--bar,
button.misv-radio-player__cta--bar{flex:none}

/* --- mode card (Grille & direct, Médias) --- */
.misv-radio-player--card,
button.misv-radio-player--card{
  display:flex;
  flex-direction:column;
  align-items:flex-start;
  gap:14px;
  background:linear-gradient(160deg,var(--misv-media-1),var(--misv-media-2));
  border:1px solid rgba(201,161,59,.35);
  border-radius:18px;
  padding:24px;
}
.misv-radio-player--card .misv-radio-player__eyebrow,
.misv-radio-player--card button.misv-radio-player__eyebrow{
  display:flex;align-items:center;gap:10px;
  font-size:10px;font-weight:800;letter-spacing:.2em;text-transform:uppercase;
  color:var(--misv-gold);
}
.misv-radio-player--card .misv-radio-player__title,
.misv-radio-player--card button.misv-radio-player__title{
  font-size:18px;font-weight:600;line-height:1.3;color:var(--misv-paper);
}
.misv-radio-player--card-media,
button.misv-radio-player--card-media{padding:36px;min-height:300px}
.misv-radio-player--card-media .misv-radio-player__title,
.misv-radio-player--card-media button.misv-radio-player__title{font-size:20px}

/* Play or 60 ceinturé d'un anneau ripple. L'anneau n'existe visuellement que
   pendant la lecture (classe `--playing` posée par misv-broadcast.js) : un
   anneau qui pulse en permanence sur un lecteur à l'arrêt mentirait sur
   l'état. */
.misv-radio-player__stage,
button.misv-radio-player__stage{
  position:relative;display:inline-flex;align-items:center;justify-content:center;
  align-self:center;
}
.misv-radio-player__play--media,
button.misv-radio-player__play--media{width:60px;height:60px;position:relative;z-index:1}
.misv-radio-player__play--media .misv-radio-player__icon,
.misv-radio-player__play--media button.misv-radio-player__icon{border-width:9px 0 9px 15px;margin-left:4px}
.misv-radio-player__play--media[aria-pressed="true"] .misv-radio-player__icon,
.misv-radio-player__play--media[aria-pressed="true"] button.misv-radio-player__icon{width:15px;height:18px;margin-left:0}
.misv-radio-player__ripple,
button.misv-radio-player__ripple{
  position:absolute;top:50%;left:50%;
  width:60px;height:60px;margin:-30px 0 0 -30px;
  border:2px solid var(--misv-gold);border-radius:50%;
  opacity:0;pointer-events:none;
}
.misv-radio-player--playing .misv-radio-player__ripple,
.misv-radio-player--playing button.misv-radio-player__ripple{
  animation:misv-ripple 2.4s var(--misv-ease) infinite;
}

/* Barre de progression du créneau. Remplissage à 0 par DÉFAUT CSS (aucun
   style en ligne côté serveur, et un rendu mis en cache ne fige donc aucune
   valeur fausse) ; misv-broadcast.js pose `--misv-radio-progress` à l'exécution
   depuis /radio-state -- c'est la seule valeur de ce widget qui varie en
   continu et ne peut donc être ni pré-énumérée dans cette feuille ni figée
   dans le balisage. */
.misv-radio-player__progress-row,
button.misv-radio-player__progress-row{
  display:flex;align-items:center;gap:12px;width:100%;
}
.misv-radio-player__progress,
button.misv-radio-player__progress{
  flex:1 1 auto;
  display:block;height:5px;border-radius:3px;overflow:hidden;
  background:rgba(250,247,241,.18);
}
.misv-radio-player__progress-fill,
button.misv-radio-player__progress-fill{
  display:block;height:100%;border-radius:3px;
  background:var(--misv-gold);
  width:var(--misv-radio-progress,0%);
  transition:width .6s linear;
}
.misv-radio-player__direct,
button.misv-radio-player__direct{
  flex:none;
  font-size:11px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;
  color:var(--misv-gold);
}

/* --- mode mini (barre fixe en bas) --- */
.misv-radio-player--mini,
button.misv-radio-player--mini{
  position:fixed;left:0;right:0;bottom:0;z-index:9990;
  display:flex;align-items:center;gap:16px;
  height:56px;
  padding:0 var(--misv-gutter);
  background:var(--misv-ink);
}
.misv-radio-player__audio,
button.misv-radio-player__audio{display:none}
.misv-radio-player__play--mini,
button.misv-radio-player__play--mini{width:36px;height:36px}
.misv-radio-player__play--mini .misv-radio-player__icon,
.misv-radio-player__play--mini button.misv-radio-player__icon{border-width:5px 0 5px 9px;margin-left:2px}
.misv-radio-player__play--mini[aria-pressed="true"] .misv-radio-player__icon,
.misv-radio-player__play--mini[aria-pressed="true"] button.misv-radio-player__icon{width:9px;height:10px;margin-left:0}
.misv-radio-player__play--mini[aria-pressed="true"] .misv-radio-player__icon::before,
.misv-radio-player__play--mini[aria-pressed="true"] .misv-radio-player__icon::after,
.misv-radio-player__play--mini[aria-pressed="true"] button.misv-radio-player__icon::after{width:3px}
.misv-radio-player--mini .misv-radio-player__text,
.misv-radio-player--mini button.misv-radio-player__text{
  flex:1 1 auto;display:flex;align-items:baseline;gap:14px;min-width:0;overflow:hidden;
}
.misv-radio-player--mini .misv-radio-player__title,
.misv-radio-player--mini button.misv-radio-player__title{
  font-size:13px;font-weight:600;color:var(--misv-paper);white-space:nowrap;
}
.misv-radio-player--mini .misv-radio-player__schedule,
.misv-radio-player--mini button.misv-radio-player__schedule{display:flex;gap:14px;min-width:0}
.misv-radio-player--mini .misv-radio-player__now,
.misv-radio-player--mini .misv-radio-player__next,
.misv-radio-player--mini button.misv-radio-player__next{
  font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.misv-radio-player__close,
button.misv-radio-player__close{
  flex:none;width:32px;height:32px;
  display:inline-flex;align-items:center;justify-content:center;
  background:transparent;border:0;border-radius:50%;cursor:pointer;
  transition:background .3s var(--misv-ease);
}
.misv-radio-player__close:hover,
button.misv-radio-player__close:hover{background:rgba(250,247,241,.12)}
.misv-radio-player__close:focus-visible,
button.misv-radio-player__close:focus-visible{outline:2px solid var(--misv-gold);outline-offset:3px}
.misv-radio-player__close-glyph,
button.misv-radio-player__close-glyph{position:relative;display:block;width:14px;height:14px}
.misv-radio-player__close-glyph::before,
.misv-radio-player__close-glyph::after,
button.misv-radio-player__close-glyph::after{
  content:"";position:absolute;top:6px;left:0;
  width:14px;height:2px;background:var(--misv-on-dark);border-radius:1px;
}
.misv-radio-player__close-glyph::before,
button.misv-radio-player__close-glyph::before{transform:rotate(45deg)}
.misv-radio-player__close-glyph::after,
button.misv-radio-player__close-glyph::after{transform:rotate(-45deg)}
.misv-radio-player__close:hover .misv-radio-player__close-glyph::before,
.misv-radio-player__close:hover .misv-radio-player__close-glyph::after,
.misv-radio-player__close:hover button.misv-radio-player__close-glyph::after{background:var(--misv-paper)}

/* Compensation de la barre fixe (ronde 1, constat 9). `position:fixed;
   bottom:0; height:56px` recouvre sinon les 56 derniers pixels de la page,
   liens de pied de page compris.
   La classe est posée sur <body> PAR LE JS (syncMiniBodyClass(),
   misv-broadcast.js) -- jamais un `padding-bottom` inconditionnel, qui
   laisserait un trou de 56px sur toute page sans lecteur mini.
   Elle est retirée sur TOUS les chemins où la barre cesse d'être rendue
   (ronde 3, constat 1) : fermeture explicite, démontage du balisage, et
   masquage par un ancêtre -- une popup Elementor au repos est en
   `display:none` sur son conteneur de dialogue, pas en `hidden` sur notre
   racine. C'est le JS qui décide, en mesurant (`getClientRects()`), pas en
   supposant ; cette feuille n'a donc aucune condition à porter ici.
   La hauteur est répétée ici plutôt que partagée : c'est la seule autre
   occurrence, une variable pour deux usages voisins coûterait plus qu'elle ne
   rapporte. */
body.misv-has-radio-mini{padding-bottom:56px}

/* --- mode button --- */
.misv-radio-player--button{display:inline-block}

/* --- Responsive --- */
@media (max-width:767px){
.misv-radio-player--bar,
button.misv-radio-player--bar{gap:16px;padding:18px 20px}
.misv-radio-player__eq--bar,
button.misv-radio-player__eq--bar{display:none}
.misv-radio-player--card-media,
button.misv-radio-player--card-media{padding:28px;min-height:0}
.misv-radio-player--mini .misv-radio-player__schedule,
.misv-radio-player--mini button.misv-radio-player__schedule{display:none}
}

/* Reduced motion : le socle neutralise déjà `.misv-eq__bar`, `.misv-dot` et
   `.misv-ripple` (misv-tokens.css). Restent les transitions propres à ce
   widget que le socle ne connaît pas -- la progression, qui glisserait sur
   .6s, et les translations de survol. */
@media (prefers-reduced-motion:reduce){
.misv-radio-player__progress-fill,
button.misv-radio-player__progress-fill{transition:none}
.misv-radio-player__play:hover,
button.misv-radio-player__play:hover{transform:none}
.misv-radio-player__ripple,
button.misv-radio-player__ripple{animation:none;opacity:0}
}

/* --- misv-video-grid ---
   Trois habillages de la même liste (dark / light / featured+list). Les
   valeurs numériques viennent du brief §tâche 8 et des maquettes, elles ne
   sont pas arrondies. Rien ici ne redéfinit un composant du socle : la
   pastille d'étiquette passe par `.misv-pastille--gold`, le lien de pied par
   `.misv-goldlink`, la vignette animée par `.misv-kb-wrap`/`.misv-kb`
   (misv-tokens.css, qui porte déjà le traitement `prefers-reduced-motion`).
   Cette section ne déclare que la mise en place propre à ce widget. */

.misv-video-grid{
  display:block;
  font-family:var(--misv-font);
  position:relative;
}
/* Même piège que `.misv-ticker[hidden]` / `.misv-radio-player[hidden]` : une
   règle AUTEUR qui pose un `display` bat le `[hidden]{display:none}` de la
   feuille du navigateur (la cascade se joue par origine avant la
   spécificité). Sans ces règles, rien de ce que le JS masque ne disparaîtrait. */
.misv-video-grid[hidden]{display:none}

.misv-video-grid__items{display:grid;gap:20px}
.misv-video-grid--light .misv-video-grid__items{gap:18px}
.misv-video-grid--cols2 .misv-video-grid__items{grid-template-columns:repeat(2,1fr)}
.misv-video-grid--cols3 .misv-video-grid__items{grid-template-columns:repeat(3,1fr)}
.misv-video-grid--cols4 .misv-video-grid__items{grid-template-columns:repeat(4,1fr)}

/* --- carte de grille --- */
.misv-video-card{
  display:block;
  border-radius:18px;
  overflow:hidden;
  text-decoration:none;
  background:var(--misv-media-1);
  transition:transform .35s var(--misv-ease),box-shadow .35s var(--misv-ease);
}
.misv-video-card:hover{transform:translateY(-5px)}
.misv-video-card:focus-visible{outline:2px solid var(--misv-gold);outline-offset:3px}

.misv-video-card__media{
  display:block;position:relative;
  height:200px;overflow:hidden;
  background:var(--misv-media-3);
}
.misv-video-card__thumb{display:block;width:100%;height:100%;object-fit:cover;opacity:.85}

/* Pastille de lecture : 52px, rouge à 92 % (vert à 94 % en `playColor=green`).
   Le triangle blanc 16px est un ::before en bordures, comme
   `.misv-radio-player__icon` — pas d'image, pas de SVG à charger. */
.misv-video-card__play{
  position:absolute;inset:0;margin:auto;
  width:52px;height:52px;border-radius:50%;
  background:rgba(209,32,42,.92);
  display:flex;align-items:center;justify-content:center;
  color:#fff;
}
.misv-video-card__play::before{
  content:"";display:block;width:0;height:0;
  border-style:solid;border-width:9px 0 9px 16px;
  border-color:transparent transparent transparent currentColor;
  margin-left:4px;
}
.misv-video-grid--play-green .misv-video-card__play{background:rgba(46,139,74,.94)}

.misv-video-card__duration{
  position:absolute;right:12px;bottom:12px;
  background:rgba(20,18,16,.85);color:#fff;
  font-size:11px;font-weight:600;padding:4px 9px;border-radius:6px;
}
.misv-video-card__tag{position:absolute;left:12px;top:12px}

.misv-video-card__body{display:block;padding:18px 20px 22px}
.misv-video-card__title{
  display:block;
  font-size:14.5px;font-weight:500;line-height:1.45;
  color:var(--misv-ivory);
}
/* `--misv-green-text`, pas `--misv-green` : sur blanc pur, #2E8B4A plafonne à
   4,28:1 et aucune taille de texte normal ne peut y satisfaire AA — c'est
   l'aplat qui est en cause, pas le texte. #257A3F donne 5,34:1 (calculé sur
   les jetons). Même logique que `--misv-gold-text`. */
.misv-video-card__tag-text{
  display:block;margin-bottom:7px;
  font-size:10px;font-weight:700;letter-spacing:.16em;text-transform:uppercase;
  color:var(--misv-green-text);
}

/* Habillage clair : carte blanche bordée, vignette pleine opacité, titre
   sombre. La maquette « Rediffusions récentes » pose en plus un voile
   rgba(20,18,16,.15) sur toute la vignette ; il n'est pas repris, le brief ne
   le mentionne pas et le seul moyen de l'obtenir ici serait un pseudo-élément
   qui recouvrirait la pastille de lecture. */
.misv-video-grid--light .misv-video-card{
  background:#fff;border:1px solid var(--misv-line-1);border-radius:16px;
}
.misv-video-grid--light .misv-video-card:hover{box-shadow:0 20px 44px rgba(28,25,22,.12)}
.misv-video-grid--light .misv-video-card__media{background:var(--misv-line-2)}
.misv-video-grid--light .misv-video-card__thumb{opacity:1}
.misv-video-grid--light .misv-video-card__title{color:var(--misv-title);font-size:15px}

/* --- habillage « grande carte + liste » --- */
.misv-video-grid--featured{display:grid;grid-template-columns:1.15fr .85fr;gap:18px}
.misv-video-grid__featured{display:block;min-width:0}

.misv-video-featured{
  position:relative;display:block;overflow:hidden;
  min-height:460px;border-radius:24px;
  background:var(--misv-media-4);text-decoration:none;
}
.misv-video-featured:focus-visible{outline:2px solid var(--misv-gold);outline-offset:3px}
.misv-video-featured__thumb{position:absolute;inset:0}
.misv-video-featured__veil{
  position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(14,12,9,.05),rgba(14,12,9,.8) 85%);
}

/* ANNEAU D'IDENTIFICATION (WCAG 1.4.11, seuil 3:1 pour ce qui permet
   d'identifier un contrôle). La pastille rouge posée sur le voile ne se
   détache pas de son fond : au pire cas (vignette blanche sous le voile, à
   mi-hauteur, là où la pastille est centrée) elle n'est qu'à 1,22:1. Le
   triangle blanc, lui, est à 5,43:1 contre la pastille — c'est ce qui sauvait
   l'identification jusqu'ici ; l'anneau ferme le sujet.

   BORDURE, PAS `box-shadow` : la charte interdit le box-shadow sur un bouton,
   et cette pastille est le point d'entrée du contrôle.

   BLANC PLEIN, ET NON `rgba(255,255,255,.5)` : le semi-transparent se compose
   sur le rouge de la pastille et n'atteint pas le seuil. Ratios calculés sur
   les jetons, anneau contre le voile immédiatement adjacent, pire cas
   (vignette blanche, y=50 %) :
     rgba(255,255,255,.5)  1,89:1  (lecteur)   1,52:1  (grande carte)  ÉCHEC
     rgba(255,255,255,.7)  2,72:1              2,17:1                  ÉCHEC
     #fff                  4,46:1              3,56:1                  OK
   Sur une vignette sombre le même anneau est à 20:1.

   `box-sizing:border-box` explicite : le diamètre reste 76px (66px), quelle
   que soit la valeur que le thème donne à `box-sizing` par ailleurs.

   PAS D'ANNEAU sur `.misv-video-card__play` : cette pastille-là n'a aucun
   voile sous elle, elle est posée directement sur la vignette. Aucune couleur
   d'anneau ne peut y garantir 3:1 — le blanc tombe à 1,33:1 sur une zone
   claire, un anneau sombre échouerait symétriquement sur une zone sombre. Y
   poser un anneau serait une garantie fausse ; l'identification y repose sur
   le triangle blanc (5,43:1 contre la pastille), comme avant. */
.misv-video-featured__play{
  position:absolute;inset:0;margin:auto;
  width:66px;height:66px;border-radius:50%;
  box-sizing:border-box;border:2px solid #fff;
  background:rgba(209,32,42,.94);
  display:flex;align-items:center;justify-content:center;color:#fff;
  transition:transform .3s var(--misv-ease);
}
.misv-video-featured__play::before{
  content:"";display:block;width:0;height:0;
  border-style:solid;border-width:11px 0 11px 19px;
  border-color:transparent transparent transparent currentColor;
  margin-left:5px;
}
.misv-video-featured:hover .misv-video-featured__play{transform:scale(1.08)}
.misv-video-grid--play-green .misv-video-featured__play{background:rgba(46,139,74,.94)}
.misv-video-featured__badge{position:absolute;left:16px;top:16px}
.misv-video-featured__caption{position:absolute;left:22px;right:22px;bottom:20px;display:block}
.misv-video-featured__title{
  display:block;
  font-family:var(--misv-serif);font-weight:500;font-size:30px;line-height:1.1;
  color:var(--misv-ivory);
}
.misv-video-featured__meta{
  display:block;margin-top:8px;
  font-size:12px;font-weight:300;color:var(--misv-on-dark);
}
.misv-video-featured__meta[hidden]{display:none}

.misv-video-grid__list{
  display:flex;flex-direction:column;min-width:0;
  background:#fff;border:1px solid var(--misv-line-1);border-radius:24px;overflow:hidden;
}
.misv-video-grid__rows{display:flex;flex-direction:column;flex:1}
.misv-video-row{
  display:flex;align-items:center;gap:16px;flex:1;
  padding:16px 20px;border-bottom:1px solid var(--misv-line-2);
  text-decoration:none;transition:background .3s var(--misv-ease);
}
.misv-video-row:hover{background:var(--misv-hover-row)}
.misv-video-row:focus-visible{outline:2px solid var(--misv-gold);outline-offset:-2px}
.misv-video-row__media{
  position:relative;flex:none;width:150px;height:88px;overflow:hidden;
  background:var(--misv-line-2);border-radius:10px;
}
.misv-video-row__thumb{display:block;width:100%;height:100%;object-fit:cover}
.misv-video-row__duration{
  position:absolute;right:6px;bottom:6px;
  background:rgba(20,18,16,.85);color:#fff;
  font-size:9px;font-weight:600;padding:3px 7px;border-radius:5px;
}
.misv-video-row__body{display:block;min-width:0}
.misv-video-row__tag{
  display:block;
  font-size:9.5px;font-weight:700;letter-spacing:.16em;text-transform:uppercase;
  color:var(--misv-green-text); /* voir la note sur .misv-video-card__tag-text */
}
.misv-video-row__title{
  display:block;margin-top:5px;
  font-size:14px;font-weight:500;line-height:1.4;color:var(--misv-title);
}
.misv-video-grid__footer{display:flex;align-items:center;justify-content:center;padding:16px 20px}

/* --- squelettes ---
   Fond `--misv-line-2`, valeur du brief. Ils ne sont qu'un décor d'attente :
   `pointer-events:none` pour qu'un clic pendant le chargement ne fasse rien,
   et une pulsation d'opacité que `prefers-reduced-motion` neutralise plus
   bas. */
@keyframes misv-skeleton{0%,100%{opacity:1}50%{opacity:.55}}
.misv-skeleton-line{display:block;height:11px;border-radius:6px;background:var(--misv-line-2)}
.misv-skeleton-line--short{width:60%;margin-top:8px}
.misv-video-card--skeleton,
.misv-video-featured--skeleton,
.misv-video-row--skeleton{pointer-events:none;animation:misv-skeleton 1.4s ease-in-out infinite}
.misv-video-card--skeleton .misv-video-card__media,
.misv-video-row--skeleton .misv-video-row__media{background:var(--misv-line-2)}
.misv-video-featured--skeleton{
  display:block;min-height:460px;border-radius:24px;background:var(--misv-line-2);
}

/* --- état vide ---
   Toujours présent dans le DOM, seulement `hidden` : le JS doit pouvoir
   l'afficher comme le remasquer sans reconstruire de nœud (leçon 9). Couleur
   `--misv-body` sur fond papier et `--misv-on-dark` sur panneau sombre : ce
   n'est pas du texte secondaire décoratif mais le message qui explique
   pourquoi la grille est vide -- il doit être lu, pas deviné. */
.misv-video-grid__empty{
  margin:0;padding:28px 0;text-align:center;
  font-family:var(--misv-font);font-size:14px;font-weight:400;
  color:var(--misv-body);
}
.misv-video-grid__empty[hidden]{display:none}
.misv-video-grid--dark .misv-video-grid__empty{color:var(--misv-on-dark)}
.misv-video-grid--featured .misv-video-grid__empty{grid-column:1/-1}

/* --- lightbox ---
   Fabriquée par misv-broadcast.js AU CLIC, jamais avant : c'est là que vit
   l'unique `<iframe>` YouTube de la page côté grille. Le bouton de fermeture
   est un `<button>` : sa règle est donc doublée en `button.x`, sinon le bloc
   de neutralisation `button[class*="misv-"]` de misv-tokens.css l'emporterait
   et le bouton resterait nu (constat au rendu du 2026-09-07). */
.misv-video-lightbox{
  position:fixed;inset:0;z-index:100000;
  display:flex;align-items:center;justify-content:center;
  padding:24px;background:rgba(10,9,8,.88);
}
.misv-video-lightbox[hidden]{display:none}
.misv-video-lightbox__frame{
  position:relative;width:min(100%,1100px);aspect-ratio:16/9;
  background:#000;border-radius:16px;overflow:hidden;
}
.misv-video-lightbox__frame iframe{position:absolute;inset:0;width:100%;height:100%;border:0}
.misv-video-lightbox__close,
button.misv-video-lightbox__close{
  position:absolute;top:-46px;right:0;
  width:38px;height:38px;border-radius:50%;
  background:var(--misv-ivory);color:var(--misv-ink);
  display:inline-flex;align-items:center;justify-content:center;
  font-family:var(--misv-font);font-size:20px;line-height:1;cursor:pointer;
  transition:background .3s var(--misv-ease);
}
.misv-video-lightbox__close:hover,
button.misv-video-lightbox__close:hover{background:#fff}
.misv-video-lightbox__close:focus-visible,
button.misv-video-lightbox__close:focus-visible{outline:2px solid var(--misv-gold);outline-offset:3px}

/* --- misv-live-player ---
   Scène radius 22, bord or à 35 %. Hors direct (et en direct non confirmé par
   l'API — voir le contrat de `videoId`), la scène est une FAÇADE : aucune
   `<iframe>` n'existe dans le DOM au chargement, elle n'est fabriquée qu'au
   clic. La façade reste présente derrière le lecteur embarqué, simplement
   `hidden`, pour que le retour à l'état hors direct ne demande jamais de
   reconstruire un nœud. */

.misv-live-player{
  display:block;
  font-family:var(--misv-font);
  position:relative;
}
.misv-live-player[hidden]{display:none}

.misv-live-player__stage{
  position:relative;height:480px;overflow:hidden;
  border:1px solid rgba(201,161,59,.35);border-radius:22px;
  background:var(--misv-media-4);
}

.misv-live-player__facade{position:absolute;inset:0;display:block;text-decoration:none;cursor:pointer}
.misv-live-player__facade[hidden]{display:none}
/* Aucune destination et aucun embed possible : le lien est inerte (un `<a>`
   sans `href` l'est déjà pour le clavier et les technologies d'assistance),
   la feuille se contente de ne plus le faire *paraître* cliquable. */
.misv-live-player__facade:not([href]):not([data-video-id]){cursor:default}
.misv-live-player__facade:not([href]):not([data-video-id]) .misv-live-player__play{display:none}
.misv-live-player__facade:focus-visible{outline:2px solid var(--misv-gold);outline-offset:-3px}

.misv-live-player__thumb{position:absolute;inset:0}
.misv-live-player__thumb[hidden]{display:none}
/* Butée basse APPROFONDIE : .78 -> .88 (le début et la position de butée du
   brief sont conservés). Le sous-titre 12px `--misv-on-dark` est ancré en bas,
   donc sa position relative REMONTE quand la scène rapetisse : à la hauteur
   minimale du contrôle (240px) il se retrouve à y≈77,9 %, où le voile d'origine
   n'était plus qu'à .723 -> 3,71:1 sur une vignette blanche, sous le seuil AA.
   Le dégradé doit tenir à TOUTES les hauteurs, pas seulement à 480.
   Ratios calculés sur les jetons (pire cas : vignette entièrement blanche) :
     y=.779 (scène 240, hauteur minimale) -> 5,26:1
     y=.890 (scène 480, valeur du brief)  -> 6,61:1
     y=.777 (plancher mobile, voir plus bas) -> 5,05:1
   Le titre 30px, texte large (seuil 3:1), passe de 4,59:1 à 5,95:1 au même
   point. */
.misv-live-player__veil{
  position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(14,12,9,.1),rgba(14,12,9,.88) 85%);
}
/* Anneau d'identification : voir la note sur `.misv-video-featured__play`. */
.misv-live-player__play{
  position:absolute;inset:0;margin:auto;
  width:76px;height:76px;border-radius:50%;
  box-sizing:border-box;border:2px solid #fff;
  background:rgba(209,32,42,.94);
  display:flex;align-items:center;justify-content:center;color:#fff;
  transition:transform .3s var(--misv-ease);
}
.misv-live-player__play::before{
  content:"";display:block;width:0;height:0;
  border-style:solid;border-width:12px 0 12px 21px;
  border-color:transparent transparent transparent currentColor;
  margin-left:6px;
}
.misv-live-player__facade:hover .misv-live-player__play{transform:scale(1.08)}

.misv-live-player__caption{position:absolute;left:22px;right:22px;bottom:20px;display:block}
.misv-live-player__title{
  display:block;
  font-family:var(--misv-serif);font-weight:500;font-size:30px;line-height:1.1;
  color:var(--misv-ivory);
}
.misv-live-player__subtitle{
  display:block;margin:8px 0 12px;
  font-size:12px;font-weight:300;color:var(--misv-on-dark);
}

/* Progression : rendue à 0 par le serveur (aucun style en ligne, aucune
   valeur figée dans une page mise en cache), alimentée au runtime par
   `--misv-live-progress` — même décision, et même forme de valeur (un
   POURCENTAGE, pas une fraction), que la barre de créneau du lecteur radio et
   son `--misv-radio-progress`. */
.misv-live-player__progress{
  display:block;height:4px;border-radius:2px;overflow:hidden;
  background:rgba(250,243,227,.22);
}
.misv-live-player__progress[hidden]{display:none}
.misv-live-player__progress-fill{
  display:block;height:100%;
  width:var(--misv-live-progress,0%);
  background:linear-gradient(90deg,#C9A13B,#D1202A);
  transition:width .6s var(--misv-ease);
}

/* Pilule spectateurs : `pointer-events:none` pour qu'elle ne perce pas un
   trou dans la surface cliquable de la façade, sur laquelle elle est posée. */
.misv-live-player__viewers{
  position:absolute;right:16px;top:16px;
  background:rgba(14,12,9,.75);
  -webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);
  color:var(--misv-ivory);
  font-size:11.5px;font-weight:600;padding:7px 14px;border-radius:999px;
  pointer-events:none;
}
.misv-live-player__viewers[hidden]{display:none}
/* Seul consommateur de `data-embedded` : quand le lecteur embarqué occupe la
   scène, c'est YouTube qui porte le titre et le compteur. Le titre, le
   sous-titre et la progression disparaissent d'eux-mêmes (ils vivent DANS la
   façade, qui est alors `hidden`) ; la pilule spectateurs, elle, est sœur de
   la façade et doit être masquée ici — sinon elle flotterait par-dessus le
   lecteur de YouTube. */
.misv-live-player[data-embedded="1"] .misv-live-player__viewers{display:none}

.misv-live-player__embed{position:absolute;inset:0;display:block;width:100%;height:100%;border:0}

.misv-live-player__actions{display:flex;flex-wrap:wrap;gap:12px;margin-top:18px}
.misv-live-player__yt{flex:none}
/* Le bouton de rappel est un `<button>` : règle doublée en `button.x`, sinon
   le bloc de neutralisation `button[class*="misv-"]` de misv-tokens.css
   l'emporterait sur la classe seule. Le système de boutons de la charte
   (`.misv-btn`, déjà doublé dans le socle) fait tout le reste. */
.misv-live-player__reminder,
button.misv-live-player__reminder{cursor:pointer}
.misv-live-player__reminder[hidden],
button.misv-live-player__reminder[hidden]{display:none}

/* Utilitaire de texte réservé aux lecteurs d'écran. Introduit ici parce que
   la façade en a besoin (préfixe d'action de son nom accessible, voir
   MISV_Live_Player_Renderer::facade()) et qu'aucun équivalent n'existait
   dans le socle -- `.screen-reader-text` appartient au thème, pas au plugin,
   et rien ne garantit sa présence. */
.misv-sr-only{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;
}

/* --- misv-schedule ---
   Grille horaire de la semaine. Les valeurs numériques viennent du brief
   §tâche 9 et ne sont pas arrondies : en-tête 16px 28px, lignes
   `220px 200px 1fr 220px` / gap 20 / padding 24px 28px, jour Cormorant 30,
   horaire 13/600/.08em, nom 15/500, description 12.5/300, diffusion 12/500,
   note 12.5/300 avec un point rouge de 7px.

   DEUX ÉCARTS ASSUMÉS AU LIBELLÉ DU BRIEF, tous deux de contraste, tous deux
   calculés sur les jetons de la charte :

   1. « horaire … or » est rendu avec `--misv-gold-text` (#8A6E28) et non
      `--misv-gold` (#C9A13B). Sur la carte blanche, #C9A13B ne donne que
      2,43:1 ; à 13px aucune taille de texte normal ne peut y satisfaire AA.
      #8A6E28 donne 4,84:1 sur #fff et 4,67:1 sur `--misv-paper` (le fond de
      survol) : les deux états passent. C'est la Règle des Deux Familles déjà
      écrite dans misv-tokens.css, pas une décision propre à ce widget.
      L'en-tête, lui, garde bien `--misv-gold` : sur `--misv-ink` il donne
      7,69:1.
   2. Les libellés de cellule visibles en mobile sont en `--misv-body`
      (7,22:1) et non en `--misv-muted` (3,88:1). Le brief ne leur impose
      qu'une taille (« Label 10px »), pas une couleur.

   Ce qui reste EN DESSOUS du seuil AA et n'a PAS été corrigé ici, parce que
   le brief l'impose explicitement : `--misv-muted` sur blanc = 3,88:1
   (3,75:1 sur le fond de survol `--misv-paper`), utilisé par la description
   (12,5px), la diffusion (12px) et la note (12,5px). C'est un couple de la
   charte, pas une invention de ce widget — signalé dans le rapport de tâche
   pour arbitrage, pas rattrapé de mon chef. */

.misv-schedule{
  display:block;
  background:#fff;
  border:1px solid var(--misv-line-1);
  border-radius:16px;
  overflow:hidden;
  font-family:var(--misv-font);
}
/* Même piège que `.misv-video-grid[hidden]` : une règle AUTEUR qui pose un
   `display` bat le `[hidden]{display:none}` de la feuille du navigateur (la
   cascade se joue par origine avant la spécificité). Toutes les cellules et
   fragments que ce widget peut masquer sont donc listés ici. */
.misv-schedule[hidden],
.misv-schedule__cell[hidden],
.misv-schedule__time[hidden],
.misv-schedule__desc[hidden],
.misv-schedule__live[hidden],
.misv-schedule__empty[hidden]{display:none}

/* En-tête noir. Décoratif (`aria-hidden` côté rendu) : ce sont les libellés
   de cellule qui portent l'association colonne -> valeur. */
.misv-schedule__head{
  display:grid;
  grid-template-columns:220px 200px 1fr 220px;
  gap:20px;
  padding:16px 28px;
  background:var(--misv-ink);
}
.misv-schedule--cols3 .misv-schedule__head{grid-template-columns:220px 200px 1fr}
.misv-schedule__head-cell{
  font-size:10.5px;font-weight:700;letter-spacing:.16em;text-transform:uppercase;
  color:var(--misv-gold);
}

.misv-schedule__rows{display:block}

.misv-schedule__row{
  display:grid;
  grid-template-columns:220px 200px 1fr 220px;
  gap:20px;
  align-items:center;
  padding:24px 28px;
  transition:background .3s var(--misv-ease);
}
/* Séparateur ENTRE les lignes seulement : un `border-top` posé sur toutes
   tracerait un filet clair juste sous l'en-tête noir, que le brief ne
   demande pas. */
.misv-schedule__row + .misv-schedule__row{border-top:1px solid var(--misv-line-2)}
.misv-schedule__row:hover{background:var(--misv-paper)}

/* 3 colonnes : la diffusion n'est pas supprimée, elle passe SOUS le nom —
   même traitement qu'au point de rupture tablette, d'où le même jeu de
   zones. */
.misv-schedule--cols3 .misv-schedule__row{
  grid-template-columns:220px 200px 1fr;
  grid-template-areas:"day time name" "day time broadcast";
  row-gap:6px;
}
.misv-schedule--cols3 .misv-schedule__cell--day{grid-area:day;align-self:center}
.misv-schedule--cols3 .misv-schedule__cell--time{grid-area:time;align-self:center}
.misv-schedule--cols3 .misv-schedule__cell--name{grid-area:name;align-self:end}
.misv-schedule--cols3 .misv-schedule__cell--broadcast{grid-area:broadcast;align-self:start}

.misv-schedule__cell{display:block;min-width:0}

/* Libellé de cellule : masqué VISUELLEMENT (jamais `display:none`, qui le
   retirerait aussi de l'arbre d'accessibilité) jusqu'au point de rupture
   mobile, où il devient le « libellé inline en Label 10px » du brief. Même
   technique de rognage que `.misv-sr-only`, mais une classe distincte : cette
   règle-ci doit pouvoir être annulée par une media query, ce que
   `.misv-sr-only` ne doit jamais être. */
.misv-schedule__cell-label{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;
}

.misv-schedule__day{
  display:block;
  font-family:var(--misv-serif);font-size:30px;font-weight:500;line-height:1.15;
  color:var(--misv-title);
}
.misv-schedule__time{
  display:block;
  font-size:13px;font-weight:600;letter-spacing:.08em;
  color:var(--misv-gold-text);
}
.misv-schedule__name-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.misv-schedule__name{font-size:15px;font-weight:500;line-height:1.4;color:var(--misv-title)}
.misv-schedule__desc{
  display:block;margin-top:5px;
  font-size:12.5px;font-weight:300;line-height:1.5;color:var(--misv-muted);
}
.misv-schedule__broadcast{
  display:block;
  font-size:12px;font-weight:500;line-height:1.4;color:var(--misv-muted);
}

.misv-schedule__note{
  display:flex;align-items:flex-start;gap:10px;
  margin:0;padding:18px 28px 22px;
  font-size:12.5px;font-weight:300;line-height:1.6;color:var(--misv-muted);
}
.misv-schedule__note-dot{
  flex:0 0 auto;width:7px;height:7px;margin-top:6px;
  border-radius:50%;background:var(--misv-red);
}

.misv-schedule__empty{
  display:block;margin:0;padding:32px 28px;text-align:center;
  font-size:13px;font-weight:300;line-height:1.6;color:var(--misv-muted);
}

/* --- misv-schedule, responsive ---
   Tablette : 3 colonnes, la diffusion sous le nom (exigence du brief), quel
   que soit le réglage `columns` — d'où la reprise des mêmes zones sans le
   préfixe `.misv-schedule--cols3`. */
@media (max-width:1024px){
  /* Les deux sélecteurs, pas seulement le court : les règles `--cols3`
     ci-dessus sont en (0,2,0) et une media query n'ajoute AUCUNE
     spécificité — un `.misv-schedule__head` seul, en (0,1,0), n'aurait rien
     changé pour un widget réglé sur 3 colonnes, qui serait resté en
     220px/200px sur tablette. */
  .misv-schedule__head,
  .misv-schedule--cols3 .misv-schedule__head{grid-template-columns:180px 150px 1fr;gap:16px;padding:14px 20px}
  .misv-schedule__head-cell--broadcast{display:none}
  .misv-schedule__row,
  .misv-schedule--cols3 .misv-schedule__row{
    grid-template-columns:180px 150px 1fr;
    grid-template-areas:"day time name" "day time broadcast";
    gap:16px;row-gap:6px;padding:20px;
  }
  .misv-schedule__cell--day{grid-area:day;align-self:center}
  .misv-schedule__cell--time{grid-area:time;align-self:center}
  .misv-schedule__cell--name{grid-area:name;align-self:end}
  .misv-schedule__cell--broadcast{grid-area:broadcast;align-self:start}
  .misv-schedule__day{font-size:25px}
}
/* Mobile : une carte par ligne, en-tête noir masqué, libellés inline. */
@media (max-width:767px){
  .misv-schedule{background:transparent;border:0;border-radius:0;overflow:visible}
  .misv-schedule__head{display:none}
  .misv-schedule__rows{display:grid;gap:12px}
  /* Même raison qu'au point de rupture tablette : le `padding:20px` que la
     media query 1024px pose sur `.misv-schedule--cols3 .misv-schedule__row`
     s'applique encore ici (767 est inclus dans 1024) et, à (0,2,0), il
     battrait un `.misv-schedule__row` seul. */
  .misv-schedule__row,
  .misv-schedule--cols3 .misv-schedule__row{
    display:block;
    background:#fff;border:1px solid var(--misv-line-1);border-radius:16px;
    padding:18px 20px;
  }
  /* La règle de séparation ci-dessus est en (0,2,0) : sans cette reprise à
     spécificité égale, son `--misv-line-2` l'emporterait ici sur le
     `border:1px solid var(--misv-line-1)` de la carte, qui n'est qu'en
     (0,1,0). Une media query n'ajoute aucune spécificité. */
  .misv-schedule__row + .misv-schedule__row{border-top-color:var(--misv-line-1)}
  .misv-schedule__cell + .misv-schedule__cell{margin-top:14px}
  .misv-schedule__cell-label{
    position:static;display:block;width:auto;height:auto;margin:0 0 4px;
    overflow:visible;clip:auto;white-space:normal;
    font-size:10px;font-weight:700;letter-spacing:.16em;text-transform:uppercase;
    color:var(--misv-body);
  }
  .misv-schedule__day{font-size:26px}
  .misv-schedule__note{padding:16px 4px 0}
  .misv-schedule__empty{
    background:#fff;border:1px solid var(--misv-line-1);border-radius:16px;
    padding:26px 20px;
  }
}
/* Reduced motion : la seule animation de ce widget est la transition de fond
   au survol. Le point pulsé du badge « Direct » est neutralisé par le socle,
   qui possède `.misv-badge-live__dot`. */
@media (prefers-reduced-motion:reduce){
  .misv-schedule__row{transition:none}
}

/* --- Responsive (tâche 8) ---
   La borne 768px DOIT rester alignée sur
   MISV_Live_Player_Renderer::DESKTOP_MIN_WIDTH : c'est elle qui enferme le
   `<style>` scoped d'une hauteur personnalisée, sans quoi ce dernier
   gagnerait ici (spécificité supérieure ET impression plus tardive). */
@media (max-width:1024px){
  .misv-video-grid--cols3 .misv-video-grid__items,
  .misv-video-grid--cols4 .misv-video-grid__items{grid-template-columns:repeat(2,1fr)}
  .misv-video-grid--featured{grid-template-columns:1fr}
  .misv-video-featured{min-height:380px}
}
@media (max-width:767px){
  .misv-video-grid--cols2 .misv-video-grid__items,
  .misv-video-grid--cols3 .misv-video-grid__items,
  .misv-video-grid--cols4 .misv-video-grid__items{grid-template-columns:1fr}
  .misv-video-featured{min-height:300px}
  .misv-video-featured__title{font-size:24px}
  .misv-video-row{gap:12px;padding:14px 16px}
  .misv-video-row__media{width:120px;height:70px}
  .misv-video-lightbox{padding:16px}
  .misv-video-lightbox__close,
  button.misv-video-lightbox__close{top:auto;bottom:-46px}
  /* `min-height` en plus du 16/9 : sur un écran très étroit (conteneur de
     280px), 16/9 ne donne que 157px de haut et le sous-titre remonterait à
     y≈.69, où même le voile approfondi ne tient plus le seuil (4,34:1). Le
     plancher borne la géométrie au lieu d'assombrir davantage l'image ; la
     vignette est en `object-fit:cover`, elle ne se déforme pas. */
  .misv-live-player__stage{height:auto;aspect-ratio:16/9;min-height:220px}
  .misv-live-player__play{width:60px;height:60px} /* box-sizing:border-box hérité de la règle de base */
  .misv-live-player__play::before{border-width:10px 0 10px 17px;margin-left:5px}
  .misv-live-player__caption{left:16px;right:16px;bottom:16px}
  .misv-live-player__title{font-size:22px}
  .misv-live-player__actions .misv-btn,
  .misv-live-player__actions button.misv-btn{flex:1 1 100%;justify-content:center}
}

/* Reduced motion : le socle neutralise déjà `.misv-kb` (Ken Burns). Restent
   les transitions et animations propres à ces deux widgets. */
@media (prefers-reduced-motion:reduce){
  .misv-video-card,
  .misv-video-featured,
  .misv-video-row{transition:none}
  .misv-video-card:hover{transform:none}
  .misv-video-featured:hover .misv-video-featured__play,
  .misv-live-player__facade:hover .misv-live-player__play{transform:none}
  .misv-video-card--skeleton,
  .misv-video-featured--skeleton,
  .misv-video-row--skeleton{animation:none}
  .misv-live-player__progress-fill{transition:none}
}
