/* ===========================================================================
   RadioSMP VT — feuille de style unique.

   DIRECTION : le design system « Radio Broadcast UI », mesuré sur Zetta2GO
   (voir newsui.html à la racine, qui en est la planche de référence).

   Interface d'exploitation lue à trois mètres, en lumière de studio comme en
   pleine nuit. Fond quasi-noir neutre, densité avant confort, et surtout :
   LA COULEUR ENCODE LE TYPE DE CONTENU, elle ne décore rien. On reconnaît un
   jingle d'une musique à sa teinte avant d'avoir lu son titre.

   Six règles tenues partout :

   1. Zéro arrondi dans la zone d'exploitation (conducteur, transport,
      panneaux). Seuls les boutons gardent les 2 px du système.
   2. Quatre teintes et une seule, pas une de plus : cyan #72FFFE la musique,
      jaune #FFFF76 l'habillage et l'automatique, rose #FD69B4 la voix,
      vert #B7FF74 les points de cue et d'intro.
   3. Le violet #800080 est réservé à la SÉLECTION et au focus. Jamais
      décoratif, et c'est le seul endroit de l'interface qui se remplit.
   4. Le rouge #FF3333 est l'antenne et l'erreur — l'enregistrement, donc.
   5. Chiffres en monospace tabulaire partout : un chronomètre ne saute pas,
      et deux durées empilées s'alignent.
   6. Pas de police servie, pas de dépendance réseau : la pile système, Segoe
      UI sur Windows, Consolas pour les chiffres. Les woff2 d'IBM Plex restent
      dans font/ mais ne sont plus déclarés — voir font/README.md pour
      l'histoire.

   Tout est en jetons sur :root, redéfinis pour le thème clair, pour qu'un
   seul attribut data-theme bascule l'interface entière. Et la page de
   connexion garde son identité : c'est la seule surface de marque de
   l'application, elle porte ses violets et sa police en dur, et aucun de ces
   changements ne la touche.
   =========================================================================== */

:root {
  /* Surfaces du design system : quatre valeurs de gris neutre, du fond global
     aux en-têtes. Plus de bleu dans le graphite — le système ne porte qu'une
     couleur froide, le violet de sélection. */
  --ground:    #131414;
  --bg:        #131414;
  --bg-sunken: #0d0d0d;
  --panel:     #1e1e1e;
  --panel-2:   #2d2d2d;
  --panel-3:   #383838;

  /* Deux filets seulement : celui qui sépare deux surfaces (#444) et celui qui
     sépare deux lignes d'une même grille (#1a1a1a). */
  --line:      #444444;
  --line-soft: #1a1a1a;
  --line-lit:  #ffffff08;

  /* Quatre valeurs de texte : titres, corps, secondaire, désactivé. */
  --text-hi:   #ffffff;
  --text:      #d1d1d1;
  --text-dim:  #959595;
  --text-faint:#666666;

  /* Le violet du logo reste celui de la marque, pour la page de connexion et
     la pastille du logo. L'accent de l'interface, lui, est le violet de
     sélection du système : il ne sert QU'À DIRE « c'est celui-là ». */
  --brand:       #c66ce4;
  --accent:      #800080;
  --accent-2:    #5c005c;
  --accent-soft: #80008059;
  --accent-line: #800080;
  --accent-text: #ffffff;

  /* Enregistrement et erreurs — et rien d'autre. */
  --rec:      #ff3333;
  --rec-glow: #ff333355;

  /* Cue et intro : le vert du système. */
  --intro:      #b7ff74;
  --intro-soft: #b7ff7418;
  --intro-line: #b7ff7488;

  --warn:      #ffff76;
  --warn-soft: #ffff7614;
  --ok:        #4ade80;

  /* Couleurs de la timeline, reprises des éditeurs de transition de régie :
     forme d'onde périwinkle, bandeaux de titre colorés. Elles ne servent QUE
     là — ce sont des couleurs de matière, pas des couleurs d'interface.

     LE BANDEAU DIT LA NATURE DE L'ÉLÉMENT, par la teinte : jaune la musique,
     gris tout ce qui est jingle, pub ou chronique, rose le VT. Trois blocs
     empilés qui se ressemblent obligent à lire trois titres pour savoir où
     l'on est ; trois teintes se reconnaissent sans lire. Le rose n'est pas le
     rouge de l'enregistrement (règle 1), qui reste à lui seul. */
  --wave:       #3bb8f8;
  --wave-dim:   #3bb8f866;
  /* Fond de forme d'onde, isolé du fond global : c'est ce qui fait ressortir
     les ondes. */
  --wave-bg:    #111111;
  --clip-music:  #72fffe;
  --clip-jingle: #ffff76;
  --clip-voice:  #fd69b4;
  --clip-head:  #fd69b4;
  --clip-body:  #1a1a1a;
  --node:       #b7ff74;

  /* UNE COULEUR PAR REPÈRE. Les traits de la timeline se lisent de loin, en
     parlant : la teinte doit dire lequel c'est avant qu'on ait lu le mot.
     Les quatre premiers sont l'anatomie du disque — on ne les déplace pas ;
     les quatre suivants appartiennent au VT et se saisissent. Aucun n'est
     rouge (règle 1), et le vert de l'intro reste celui du chant (règle 2). */
  --mark-intro: #b7ff74;  /* intro du titre précédent      */
  --mark-fade:  #959595;  /* fondu                         */
  --mark-start: #3bb8f8;  /* StartNext du catalogue         */
  --mark-end:   #666666;  /* fin diffusée                   */
  --mark-voice: #fd69b4;  /* attaque de la voix             */
  --mark-word:  #ffff76;  /* premier mot : le duck part     */
  --mark-up:    #4ade80;  /* remontée : le duck s'arrête    */
  --mark-next:  #72fffe;  /* départ du titre suivant        */
  --mark-sing:  #b7ff74;  /* chant : la limite à ne pas passer */

  /* Catégories de cartouches. Pas de teinte nouvelle : le système n'en a que
     quatre, et une catégorie se range dans l'une d'elles — cyan ce qui est
     musical, jaune l'habillage, rose la parole, gris le reste. */
  --cat-jingle:   #ffff76;
  --cat-sweeper:  #ffff76;
  --cat-liner:    #ffff76;
  --cat-bed:      #72fffe;
  --cat-bruitage: #fd69b4;
  --cat-musique:  #72fffe;
  --cat-pub:      #959595;
  --cat-voix:     #fd69b4;
  --cat-autre:    #666666;

  /* Zéro arrondi dans la zone d'exploitation. Seuls les boutons gardent les
     2 px du système, et c'est le seul endroit où il y en a. */
  --radius:    0;
  --radius-s:  0;
  --radius-xs: 0;
  --radius-btn: 2px;
  --gap:       8px;

  /* Pas de police servie : la pile système, Segoe UI sur Windows. Les chiffres
     passent tous en Consolas, en largeur fixe. */
  --mono: "Consolas", "SF Mono", ui-monospace, "Cascadia Mono", Menlo, monospace;
  --sans: "Segoe UI", system-ui, -apple-system, Roboto, "Helvetica Neue",
          sans-serif;

  /* La pile d'avant, gardée nommée : la page de connexion s'y accroche, pour
     que le changement de DA ne la touche pas (voir § CONNEXION). */
  --sans-legacy: "Segoe UI Variable Text", "Segoe UI", system-ui, -apple-system,
                 Roboto, "Helvetica Neue", sans-serif;

  /* Pas d'ombre décorative dans la zone d'exploitation : la profondeur vient
     du filet. Les deux grandes ombres ne servent plus qu'aux surfaces qui
     flottent vraiment au-dessus du reste (dialogue, notification). */
  --shadow-s: 0 0 0 #0000;
  --shadow:   0 8px 24px -10px #000000c0;
  --shadow-l: 0 24px 60px -18px #000000e0;

  --panel-head-h: 24px;
}

/* Le système est pensé pour le noir — c'est l'interface de studio. Le thème
   clair reste disponible pour relire un conducteur en plein jour : mêmes gris
   retournés, mêmes teintes assombries juste assez pour tenir sur du blanc.
   Les néons du système (cyan, jaune, vert) sont illisibles sur fond clair ;
   ce sont leurs versions foncées qui portent la même signification. */
:root[data-theme="light"] {
  --ground:    #ececec;
  --bg:        #ececec;
  --bg-sunken: #e0e0e0;
  --panel:     #f7f7f7;
  --panel-2:   #ebebeb;
  --panel-3:   #dedede;

  --line:      #b8b8b8;
  --line-soft: #d4d4d4;
  --line-lit:  #ffffff;

  --text-hi:   #000000;
  --text:      #1f1f1f;
  --text-dim:  #5a5a5a;
  --text-faint:#8a8a8a;

  --brand:       #c66ce4;
  --accent:      #800080;
  --accent-2:    #5c005c;
  --accent-soft: #8000801f;
  --accent-line: #80008088;
  --accent-text: #ffffff;

  --rec:      #d92020;
  --rec-glow: #d9202038;

  --intro:      #3f7a00;
  --intro-soft: #3f7a0022;
  --intro-line: #3f7a0088;

  --warn:      #7e7612;
  --warn-soft: #7e761218;
  --ok:        #137a3d;

  --wave:       #1d7fb8;
  --wave-dim:   #1d7fb855;
  --wave-bg:    #ffffff;
  --clip-music:  #0f7f8a;
  --clip-jingle: #7e7612;
  --clip-voice:  #c0317d;
  --clip-head:  #c0317d;
  --clip-body:  #ffffff;
  --node:       #3f7a00;

  --mark-intro: #3f7a00;
  --mark-fade:  #5a5a5a;
  --mark-start: #1d7fb8;
  --mark-end:   #8a8a8a;
  --mark-voice: #c0317d;
  --mark-word:  #7e7612;
  --mark-up:    #137a3d;
  --mark-next:  #0f7f8a;
  --mark-sing:  #3f7a00;

  --cat-jingle:   #7e7612;
  --cat-sweeper:  #7e7612;
  --cat-liner:    #7e7612;
  --cat-bed:      #0f7f8a;
  --cat-bruitage: #c0317d;
  --cat-musique:  #0f7f8a;
  --cat-pub:      #5a5a5a;
  --cat-voix:     #c0317d;
  --cat-autre:    #8a8a8a;

  --shadow-s: 0 0 0 #0000;
  --shadow:   0 8px 22px -12px #1a243333;
  --shadow-l: 0 24px 54px -20px #1a243344;
}

* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }

body {
  background: var(--bg);
  color: var(--text);
  font: 13px/1.4 var(--sans);
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
}

[hidden] { display: none !important; }

/* ---------------------------------------------------------------- Icônes --
   Dessinées dans js/icons.js, injectées une fois en sprite, posées par
   `<use>`. Le trait et les jointures sont déclarés ICI et pas sur chaque
   dessin : une seule ligne à changer pour affiner tout le jeu.

   `currentColor` est le point important — une icône prend la couleur du texte
   qui l'entoure, donc elle suit les états (survol, actif, désactivé, thème
   clair) sans qu'aucune règle ne la nomme. C'est ce que les anciens glyphes
   Unicode faisaient par accident et l'emoji 🗑 ne faisait pas du tout. */

.ico {
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
  /* Les icônes sont posées dans des lignes de texte : sans ça elles
     s'assoient sur la ligne de base et flottent trop haut. */
  vertical-align: -.14em;
}

/* Chiffres à largeur fixe : un chronomètre ne doit pas « sauter ». */
.tnum {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

* { scrollbar-width: thin; scrollbar-color: var(--line) transparent; }
*::-webkit-scrollbar { width: 9px; height: 9px; }
*::-webkit-scrollbar-thumb {
  background: var(--line);
  border: 2px solid transparent;
  border-radius: 999px;
  background-clip: content-box;
}
*::-webkit-scrollbar-thumb:hover { background: var(--text-faint); background-clip: content-box; }
*::-webkit-scrollbar-track { background: transparent; }

::selection { background: var(--accent); color: var(--accent-text); }

/* --------------------------------------------------------------- Boutons -- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  font: inherit; font-size: 11px; font-weight: 500;
  color: var(--text);
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-btn);
  padding: 3px 8px;
  cursor: pointer;
  white-space: nowrap;
  transition: background .1s linear, border-color .1s linear, color .1s linear;
}
.btn:hover:not(:disabled) { background: var(--panel-3); border-color: var(--text-faint); }
.btn:active:not(:disabled) { background: var(--bg-sunken); }
.btn:disabled { opacity: .38; cursor: not-allowed; }

.btn--primary {
  background: var(--accent); border-color: var(--accent);
  color: var(--accent-text); font-weight: 600;
}
.btn--primary:hover:not(:disabled) { filter: brightness(1.12); border-color: var(--accent); }

.btn--ghost { background: transparent; border-color: transparent; color: var(--text-dim); }
.btn--ghost:hover:not(:disabled) { background: var(--panel-2); color: var(--text); border-color: transparent; }

.btn--icon {
  padding: 5px 8px; background: transparent; border-color: transparent;
  color: var(--text-dim);
}
.btn--icon:hover:not(:disabled) { background: var(--panel-2); color: var(--text); border-color: transparent; }

.btn--small { padding: 3px 8px; font-size: 11.5px; }
.btn--lg { padding: 11px 18px; font-size: 14px; }
.btn--block { width: 100%; }
.btn--danger:hover:not(:disabled) { border-color: var(--warn); color: var(--warn); }

:where(button, input, select, a, [tabindex]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-radius: var(--radius-xs);
}

/* Un bouton de la barre d'outils : icône et libellé SUR LA MÊME LIGNE, en
   11 px. L'ancien bouton empilait l'icône au-dessus du mot et imposait
   50 px de hauteur à toute la barre ; une régie met ses outils sur une ligne
   et garde la hauteur pour les formes d'onde. */
.tool {
  display: flex; align-items: center; gap: 4px;
  flex: none;
  font: inherit; font-size: 11px;
  color: var(--text);
  background: transparent; border: 1px solid transparent;
  border-radius: var(--radius-btn);
  padding: 3px 8px;
  cursor: pointer;
  white-space: nowrap;
  transition: background .1s linear, color .1s linear, border-color .1s linear;
}
.tool__ico { display: block; width: 13px; height: 13px; color: currentColor; }
.tool:hover:not(:disabled) { background: var(--panel-2); border-color: var(--line); }
.tool:disabled { opacity: .34; cursor: not-allowed; }
/* Un outil enclenché : le violet au filet, le jaune au texte. C'est l'état
   « actif » du système, et il ne remplit pas le fond — un bouton plein se lit
   comme un voyant d'antenne, qui est rouge et réservé. */
.tool.is-on { background: transparent; border-color: var(--accent); color: var(--warn); }
/* Sauf la prise en cours : là, c'est bien l'antenne. */
.tool.is-recording { color: var(--rec); border-color: var(--rec); }

/* ------------------------------------------------------- Champs de saisie - */

.field { display: block; margin-bottom: 12px; }
.field > span {
  display: block; font-size: 11px; color: var(--text-dim);
  margin-bottom: 4px; letter-spacing: .2px;
}
.field input:not([type="range"]), .field select {
  width: 100%; font: inherit; font-size: 11px; color: var(--text);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  padding: 4px 8px;
  transition: border-color .1s linear;
}
.field input:not([type="range"]):focus, .field select:focus {
  outline: 0; border-color: var(--accent);
}
.field input[type="range"] { width: 100%; }
.field select option { background: var(--panel); color: var(--text); }
input[type="range"] { accent-color: var(--accent); }

.check {
  display: inline-flex; align-items: center; gap: 7px;
  color: var(--text-dim); font-size: 12px; cursor: pointer;
}
.check input { accent-color: var(--accent); }
.check:hover { color: var(--text); }

.hint { color: var(--text-dim); font-size: 12px; margin: 6px 0 0; }
.empty { color: var(--text-faint); padding: 22px 14px; text-align: center; font-size: 12.5px; }
.spacer { flex: 1; }

.pill {
  font-size: 10px; font-family: var(--mono);
  color: var(--text-dim);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-btn);
  padding: 2px 8px;
}
.pill:empty { display: none; }

/* ============================================================== CONNEXION ==
   Inchangée : c'est la seule surface de marque de l'application, et elle
   fonctionne. Elle porte ses propres violets en dur, de sorte que le
   changement de DA du reste ne la touche pas.
   =========================================================================== */

/* Y compris la police : on REDÉFINIT les deux jetons ici plutôt que de poser
   un `font-family` :
   ainsi les descendants qui demandent explicitement var(--sans) ou var(--mono)
   — la pastille numérotée des étapes, par exemple — reçoivent eux aussi
   l'ancienne pile, sans qu'aucun d'eux ait à le savoir. */
.login {
  --sans: var(--sans-legacy);
  --mono: ui-monospace, "SF Mono", "Cascadia Mono", "JetBrains Mono", Menlo, monospace;
  font-family: var(--sans);
  /* Et l'ambre de ses messages d'erreur : le reste de l'interface est passé au
     jaune du système, cette page garde ce qu'elle avait. */
  --warn: #f0a93b;
  --warn-soft: #f0a93b1c;
}

.login {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  height: 100%;
  background: #0a0810;
}

.login__brand {
  display: flex; flex-direction: column; justify-content: center;
  gap: 18px;
  padding: 48px clamp(28px, 5vw, 72px);
  color: #f3e9fb;
  background:
    radial-gradient(900px 480px at 15% 0%, #c66ce455, transparent 65%),
    radial-gradient(700px 520px at 85% 100%, #8b3fd055, transparent 60%),
    linear-gradient(150deg, #1a0d28, #0a0810 65%);
  border-right: 1px solid #332b44;
  position: relative;
  overflow: hidden;
}
.login__brand::after {
  content: ""; position: absolute; inset: auto -10% -35% -10%;
  height: 60%;
  background: radial-gradient(60% 100% at 50% 100%, #c66ce42e, transparent 70%);
  pointer-events: none;
}
.login__logo {
  width: min(340px, 72%); height: auto; display: block;
  filter: drop-shadow(0 10px 30px #00000080);
}
.login__claim {
  margin: 0; font-size: 15px; font-weight: 600; letter-spacing: .2px;
  color: #c66ce4;
}
.login__pitch { margin: 0; max-width: 42ch; font-size: 14px; line-height: 1.65; color: #d7c9e4; }
.login__steps {
  margin: 6px 0 0; padding: 0; list-style: none;
  display: flex; flex-direction: column; gap: 9px;
  counter-reset: step;
}
.login__steps li {
  display: flex; align-items: center; gap: 11px;
  font-size: 13.5px; color: #c9b9d9;
  counter-increment: step;
}
.login__steps li::before {
  content: counter(step);
  flex: none;
  width: 24px; height: 24px; border-radius: 50%;
  display: grid; place-items: center;
  font-family: var(--mono); font-size: 11px; font-weight: 700;
  color: #c66ce4;
  background: #c66ce41f;
  border: 1px solid #c66ce455;
}

/* Variante sans volet de marque (supervision) : une seule colonne. */
.login--solo { grid-template-columns: 1fr; background: var(--bg); }
.login--solo .login__panel {
  background: radial-gradient(900px 520px at 50% -120px, #c66ce414, transparent 70%);
}
.login__mark {
  height: 34px; width: auto; display: block; margin-bottom: 22px;
  filter: drop-shadow(0 6px 18px #00000066);
}
:root[data-theme="light"] .login__mark {
  filter: invert(1) hue-rotate(180deg) saturate(1.3) brightness(.75);
}

.login__panel { display: grid; place-items: center; padding: 32px 20px; overflow: auto; }
.login__box { width: 100%; max-width: 380px; }
.login__title { margin: 0; font-size: 27px; font-weight: 650; letter-spacing: -.6px; color: #f1ecf6; }
.login--solo .login__title { color: var(--text); }
.login__sub {
  margin: 5px 0 26px; color: #a99db8; font-size: 12.5px;
  text-transform: uppercase; letter-spacing: 1px; font-weight: 600;
}
.login--solo .login__sub { color: var(--text-dim); }
.login .field > span { color: #a99db8; font-size: 12px; }
.login--solo .field > span { color: var(--text-dim); }
.login .field input {
  background: #06050a; border-color: #332b44; color: #f1ecf6;
  border-radius: 9px; padding: 9px 11px; font-size: 13px;
}
.login--solo .field input {
  background: var(--bg-sunken); border-color: var(--line); color: var(--text);
}
.login .field input:focus { border-color: #c66ce4; box-shadow: 0 0 0 3px #c66ce422; }
.login .btn--primary {
  background: linear-gradient(135deg, #c66ce4, #8b3fd0);
  border-color: transparent; color: #150a1c; border-radius: 9px;
  box-shadow: 0 8px 22px -10px #c66ce4;
}
.login--solo .btn--primary {
  background: var(--accent); color: var(--accent-text); box-shadow: none;
}
.login__error {
  color: var(--warn); font-size: 12.5px; margin: 14px 0 0;
  padding: 9px 12px;
  background: var(--warn-soft);
  border-radius: 9px;
  box-shadow: inset 3px 0 0 var(--warn);
}
.login__foot {
  margin: 26px 0 0; padding-top: 18px;
  border-top: 1px solid #221c2e;
  font-size: 12px; color: #776b88;
}
.login--solo .login__foot { border-top-color: var(--line-soft); color: var(--text-faint); }

@media (max-width: 860px) {
  .login { grid-template-columns: 1fr; grid-template-rows: auto 1fr; }
  .login__brand {
    padding: 24px 20px; gap: 0; align-items: center;
    border-right: 0; border-bottom: 1px solid #332b44;
  }
  .login__claim, .login__pitch, .login__steps { display: none; }
  .login__logo { width: 180px; }
}

/* ================================================================ SHELL ====
   La disposition de la planche de référence, de haut en bas : une barre de
   menus, une barre d'outils d'une seule ligne, le plan de travail, et une
   barre d'état. Le gros ruban à deux étages a disparu : il mangeait 50 px de
   haut pour dire le nom des familles de boutons, alors qu'une régie met tout
   sur une ligne de 26 px et garde la hauteur pour les formes d'onde.
   =========================================================================== */

/* Colonne en flex, et non une grille à rangées nommées par leur ordre : le
   bandeau casque disparaît dès qu'on l'acquitte, et une grille rejouerait
   alors ses rangées d'un cran — le plan de travail héritait de la rangée
   `auto` et se refermait à zéro, écran vide. Ici chaque bloc porte sa propre
   hauteur, l'ordre ne décide plus de rien. */
.app { display: flex; flex-direction: column; height: 100%; min-height: 0; }
.app > :not(.desk) { flex: none; }

/* Un plan de travail par panel, et un seul visible. Il reprend le rôle que
   `.desk` tenait seul : c'est lui qui prend toute la hauteur restante sous la
   barre de menus, et c'est en lui que la barre d'outils, le plan et la barre
   d'état se répartissent.

   La règle vient APRÈS `.app > :not(.desk)` et non avant : les deux ont la
   même spécificité, donc c'est l'ordre du fichier qui tranche. Placée plus
   haut, elle se ferait écraser et le plan de travail se refermerait à zéro. */
.workspace {
  display: flex; flex-direction: column;
  min-height: 0; min-width: 0;
}
.app > .workspace { flex: 1 1 auto; }
/* Reprise de la règle ci-dessus, pour les blocs qui sont maintenant enfants du
   plan de travail et non plus de `.app` : barre d'outils, bandeau casque,
   barre d'état, onglets mobiles. Sans elle, ils se partageraient la hauteur
   avec le plan au lieu de garder la leur. */
.workspace > :not(.desk) { flex: none; }

/* Un panel pas encore construit le dit, au lieu d'ouvrir sur un écran vide
   qu'on prendrait pour une panne. */
.holding {
  display: flex; flex-direction: column; justify-content: center;
  gap: 10px;
  flex: 1 1 auto; min-height: 0;
  max-width: 54ch;
  margin: 0 auto; padding: 24px;
  background: var(--bg-sunken);
}
.holding h2 { margin: 0; font-size: 15px; color: var(--text-hi); }
.holding p { margin: 0; font-size: 12.5px; line-height: 1.55; color: var(--text); }
.holding__state {
  font-family: var(--mono); font-size: 11px;
  color: var(--text-faint); text-transform: uppercase; letter-spacing: .06em;
}

/* ---------------------------------------------------- Barre de menus ------ */

/* Le gris haut du système, le même que les en-têtes de panneau et de grille :
   tout ce qui surplombe a cette valeur. Au centre l'antenne alimentée, à
   droite la session — c'est l'ordre de la planche. */
.menubar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px;
  padding: 3px 10px;
  font-size: 12px;
  background: var(--panel-3);
  border-bottom: 1px solid var(--line-soft);
  min-width: 0;
}
.menubar__left, .menubar__right {
  display: flex; align-items: center; gap: 10px; flex: none; min-width: 0;
}
.menubar__center {
  color: var(--text-hi); font-size: 12px; letter-spacing: .05em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.menubar__center b { font-weight: 600; }
.menubar__sep { color: var(--text-faint); }

/* Un item de menu : rien jusqu'au survol, et le survol le prend en violet.
   C'est le comportement d'une barre de menus, pas celui d'un bouton. */
.menu-item {
  font: inherit; font-size: 12px;
  color: var(--text);
  background: transparent; border: 0; border-radius: 0;
  padding: 2px 6px;
  cursor: pointer;
  white-space: nowrap;
  user-select: none;
}
.menu-item:hover { background: var(--accent); color: #fff; }
/* La supervision est un vrai lien (`<a href>`) pour s'ouvrir dans un onglet au
   clic du milieu. Il doit pour autant se fondre dans la rangée : ni
   soulignement, ni couleur de lien, et la même hauteur que ses voisins. */
a.menu-item { text-decoration: none; display: inline-flex; align-items: center; }
a.menu-item:hover { color: #fff; }

/* Le choix du panel : des onglets dans la barre de menus, pas un menu
   déroulant. On change d'outil plusieurs fois par séance, et un déroulant
   demanderait deux gestes là où un suffit — en plus de cacher ce qui existe à
   celui qui vient d'obtenir un accès. */
.panelpick { display: flex; align-items: center; gap: 2px; margin-left: 6px; }
.panelpick__tab {
  font: inherit; font-size: 12px;
  color: var(--text-faint);
  background: transparent; border: 0;
  border-bottom: 2px solid transparent;
  padding: 2px 8px 1px;
  cursor: pointer; white-space: nowrap; user-select: none;
}
.panelpick__tab:hover { color: var(--text-hi); }
/* L'onglet ouvert est souligné et non rempli : rempli, il se confondrait avec
   le survol violet des items de menu juste à côté. */
.panelpick__tab.is-active {
  color: var(--text-hi); font-weight: 650;
  border-bottom-color: var(--accent);
}

/* Le logo, posé sur une pastille sombre : il est blanc et violet, dessiné
   pour un fond sombre, et disparaîtrait tel quel en thème clair. */
.menubar__brand { display: flex; align-items: center; flex: none; }
.menubar__logo {
  height: 15px; width: auto; display: block;
  padding: 2px 6px;
  background: #0f0a14;
  border: 1px solid var(--line);
}

/* ---------------------------------------------------- Barre d'outils ------ */

.toolbar {
  display: flex; align-items: center; gap: 5px;
  padding: 3px 8px;
  font-size: 11px;
  color: var(--text-dim);
  background: var(--panel);
  border-bottom: 1px solid var(--line-soft);
  min-width: 0;
  overflow-x: auto;
  overflow-y: hidden;
}
.toolbar__sep {
  flex: none; width: 1px; height: 18px;
  background: var(--line);
  margin: 0 3px;
}

/* Le voyant d'antenne : le seul aplat rouge de l'interface, et il ne s'allume
   que pendant la prise. Hors prise il ne s'éteint pas en gris — il
   disparaît : un voyant éteint se confond avec un bouton. */
.onair {
  flex: none;
  font-size: 11px; font-weight: 700; letter-spacing: .08em;
  color: #fff; background: var(--rec);
  border: 0; border-radius: var(--radius-btn);
  padding: 2px 10px;
  user-select: none;
}
.onair[hidden] { display: none; }

/* Horloge et créneau : le bloc que l'animateur regarde le plus souvent. */
.slotpick {
  display: flex; align-items: center; gap: 3px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  padding: 2px;
  flex: none;
}
.slotpick__date, .slotpick__hour {
  font: inherit; font-size: 11.5px; color: var(--text-hi);
  background: transparent; border: 0; border-radius: var(--radius-xs);
  padding: 4px 6px;
}
.slotpick__date { font-family: var(--mono); }
.slotpick__hour { cursor: pointer; font-family: var(--mono); font-weight: 600; }
.slotpick__date:focus, .slotpick__hour:focus { outline: 0; background: var(--panel-2); }
.slotpick__hour option { background: var(--panel); color: var(--text); }
.slotpick .btn--icon { padding: 3px 6px; }

/* L'horloge tient sur la ligne d'outils : l'heure en clair, la date en gris
   juste à côté. Elle ne se met plus sur deux étages — c'est elle qui imposait
   la hauteur du ruban. */
.clock {
  display: flex; align-items: baseline; gap: 7px;
  padding: 0 6px; flex: none;
}
.clock__time {
  font-family: var(--mono); font-size: 14px; font-weight: 400; line-height: 1;
  color: var(--text-hi);
  font-variant-numeric: tabular-nums;
}
.clock__date {
  font-family: var(--mono); font-size: 10px; color: var(--text-faint);
  text-transform: uppercase; letter-spacing: .5px;
  font-variant-numeric: tabular-nums;
}

.who { color: var(--text-dim); font-size: 12px; white-space: nowrap; }

/* Bandeau d'information (casque, avertissements). */
.notice {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 7px 12px;
  font-size: 12.5px;
  color: var(--text);
  background: var(--panel-2);
  border-bottom: 1px solid var(--line-soft);
  box-shadow: inset 3px 0 0 var(--accent);
}
.notice--headphones strong { color: var(--accent); }
.notice .btn { margin-left: auto; }
.notice--warn { box-shadow: inset 3px 0 0 var(--warn); background: var(--warn-soft); }

/* ---------------------------------------------------- Barre d'état -------- */

/* Trois lignes de bas d'écran, en chiffres tabulaires. Elle ne porte QUE des
   valeurs que l'application connaît : l'emplacement choisi, l'avancement de
   l'heure, la latence mesurée. La planche y affichait un « ETM / Gap Time »
   venu du playout, qu'on n'a pas — on ne met pas de chiffre inventé sous les
   yeux de quelqu'un qui est à l'antenne. */
.statusbar {
  display: flex; align-items: center; gap: 16px;
  padding: 3px 12px;
  font-family: var(--mono); font-size: 10px;
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
  background: var(--panel);
  border-top: 1px solid var(--line-soft);
  min-width: 0;
  overflow: hidden;
}
.statusbar b { font-weight: 400; color: var(--warn); }
.statusbar__item {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
}
.statusbar__item:empty { display: none; }
.statusbar__item--wide { flex: 1; }

/* ====================================================== DISPOSITION ========
   Trois colonnes de panneaux, redimensionnables, et chaque panneau se glisse
   d'une colonne à l'autre par sa barre de titre. La disposition est gardée
   dans le navigateur (voir js/layout.js) : on règle son écran une fois.
   =========================================================================== */

/* À fleur, sans marge autour ni vide entre les panneaux : la planche de
   référence accole ses blocs et les sépare d'un filet d'un pixel. Les 5 px de
   vide d'avant faisaient lire les panneaux comme des appareils posés sur un
   plan de travail ; une régie n'a pas de plan de travail, elle a une face
   avant. */
.desk {
  display: flex; align-items: stretch;
  flex: 1 1 auto;
  min-height: 0; min-width: 0;
  background: var(--bg-sunken);
  padding: 0;
  overflow: hidden;
}

.dock {
  display: flex; flex-direction: column;
  min-width: 0; min-height: 0;
  overflow: hidden;
}
/* Une colonne vide ne disparaît pas : elle reste comme cible de dépôt,
   sinon un panneau sorti de là ne pourrait jamais y revenir. */
.dock:empty { min-width: 46px; }
.dock.is-drop { background: var(--accent-soft); box-shadow: inset 0 0 0 1px var(--accent-line); }

/* Poignée de redimensionnement : un filet d'un pixel, comme toutes les
   séparations du système. On ne la voit donc pas en tant que poignée — mais sa
   zone de saisie dépasse de 4 px de chaque côté, parce que viser un pixel à la
   souris est impossible. */
.split {
  flex: none; position: relative;
  background: var(--line-soft);
  z-index: 3;
}
.split--v { width: 1px; cursor: col-resize; }
.split--h { height: 1px; cursor: row-resize; }
.split::after {
  content: ""; position: absolute; inset: -4px;
  background: transparent;
}
.split--v::after { inset: 0 -4px; }
.split--h::after { inset: -4px 0; }
/* On ne colore que la poignée survolée, et sur son axe seulement : une barre
   pleine entre deux panneaux les recollerait visuellement. */
.split:hover::before, .split.is-active::before {
  content: ""; position: absolute; inset: 0;
  background: var(--accent);
}

.panel {
  display: grid; grid-template-rows: var(--panel-head-h) 1fr;
  min-height: 0; min-width: 0;
  background: var(--panel);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-s);
  overflow: hidden;
}
.panel.is-dragging { opacity: .5; }
.panel.is-collapsed { grid-template-rows: var(--panel-head-h); flex: none !important; }
.panel.is-collapsed .panel__body { display: none; }

/* La barre de titre d'un panneau : le gris haut du système (#383838), le même
   que les en-têtes de grille — on reconnaît un en-tête à sa valeur, où qu'il
   soit. */
.panel__head {
  display: flex; align-items: center; gap: 8px;
  padding: 0 4px 0 10px;
  background: var(--panel-3);
  border-bottom: 1px solid var(--line-soft);
  cursor: grab;
  user-select: none;
  min-width: 0;
}
.panel__head:active { cursor: grabbing; }
.panel__title {
  margin: 0; font-size: 10px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .1em;
  color: var(--text-dim);
  white-space: nowrap;
}
.panel__meta {
  margin-left: auto; font-size: 10px; color: var(--text-faint);
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-family: var(--mono);
}
.panel__acts { display: flex; align-items: center; gap: 1px; flex: none; }
.panel__act {
  font: inherit; font-size: 12px; line-height: 1;
  background: transparent; border: 0; color: var(--text-faint);
  padding: 4px 5px; cursor: pointer; border-radius: var(--radius-xs);
}
.panel__act:hover { background: var(--panel-3); color: var(--text); }
.panel__body { overflow: auto; min-height: 0; min-width: 0; }
.panel__body--pad { padding: 10px; }

/* Fantôme affiché pendant qu'on déplace un panneau : la barre d'insertion
   entre deux panneaux de la colonne visée. */
.dropline {
  height: 2px; background: var(--accent); flex: none;
  box-shadow: 0 0 6px var(--accent);
}

/* ============================================================== PLAYLIST ===
   Densité de conducteur : une ligne par élément, hauteur fixe, heure de
   passage à gauche, intro et durée à droite. C'est la grille de Myriad, et
   c'est elle que l'animateur lit en diagonale pour trouver où parler.
   =========================================================================== */

/* L'en-tête de colonnes, collé en haut du défilement : sans lui, « :20 » et
   « 3:42 » en bout de ligne sont deux nombres qu'il faut deviner. Il porte la
   même valeur de gris que les barres de titre — tout ce qui surplombe est à
   #383838 — et il reste visible pendant qu'on fait défiler l'heure. */
.pl-head {
  position: sticky; top: 0; z-index: 2;
  display: grid;
  grid-template-columns: 50px 14px 1fr auto auto;
  align-items: center;
  gap: 6px;
  height: 18px;
  padding: 0 6px;
  background: var(--panel-3);
  border-bottom: 1px solid var(--line-soft);
  font-size: 9px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .06em;
  color: var(--text-dim);
  user-select: none;
}
.pl-head span:nth-child(4), .pl-head span:nth-child(5) { text-align: right; }

/* LA COULEUR DIT LA NATURE DE LA LIGNE, et c'est la seule chose qu'elle dit :
   cyan la musique, jaune l'habillage, rose la voix, blanc la tête d'heure,
   gris le reste. Un conducteur se lit en diagonale — on repère un jingle à sa
   teinte, pas en lisant son titre. */
.pl-item {
  display: grid;
  grid-template-columns: 50px 14px 1fr auto auto;
  align-items: center;
  gap: 6px;
  height: 22px;
  padding: 0 6px;
  border-bottom: 1px solid var(--line-soft);
  cursor: pointer;
  position: relative;
  font-family: var(--mono);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
}
.pl-item::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
  background: var(--accent); opacity: 0;
}
.pl-item:hover { background: #ffffff08; }
.pl-item:hover::before { opacity: .5; }

.pl-item.k-music  { color: var(--clip-music); }
.pl-item.k-jingle { color: var(--clip-jingle); }
.pl-item.k-voice  { color: var(--clip-voice); }
.pl-item.k-ad     { color: var(--text-dim); }
.pl-item.k-other  { color: var(--text-dim); }

.pl-item__at {
  font-family: var(--mono); font-size: 11px; color: inherit; opacity: .75;
  font-variant-numeric: tabular-nums;
}
/* La pastille de type, comme la colonne d'icônes du conducteur : on
   distingue une musique d'un jingle sans lire le titre. */
/* Les lettres J V P ♪ sont devenues des dessins. 14 px ne suffisaient pas :
   un trait de 1,75 réduit à cette taille se referme, là où une capitale
   grasse tenait. La pastille passe donc à 16 px et son trait s'épaissit —
   c'est le seul endroit de l'interface où les icônes sont aussi petites, donc
   le seul qui le demande. */
/* La pastille n'est plus une étiquette colorée mais un dessin de la couleur de
   la ligne : la teinte est déjà portée par le texte, la remplir ferait deux
   fois la même chose en plus gros. */
.pl-item__kind {
  width: 14px; height: 14px;
  display: grid; place-items: center;
  color: currentColor; background: transparent;
}
.pl-item__kind .ico { stroke-width: 2.2; vertical-align: 0; }

/* LE TITRE N'EST PAS UN NOMBRE : il est écrit dans la police de l'interface,
   pas en Consolas. Le monospace sert à ce qui s'aligne en colonne — l'heure,
   l'intro, la durée — et il le sert bien ; appliqué à « Rockabye | Clean
   Bandit » il écarte les lettres, hache les mots et fait lire un titre comme
   une référence de fichier. Le gain de place n'est pas rien non plus : à
   largeur égale, un titre en Segoe tient deux ou trois mots de plus avant de
   se faire couper. */
.pl-item__body {
  min-width: 0; display: flex; align-items: baseline; gap: 6px;
  font-family: var(--sans);
  font-size: 12px;
  letter-spacing: 0;
}
.pl-item__title {
  font-weight: 400; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.pl-item__artist {
  font-size: 11px; color: var(--text-dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  flex: none; max-width: 45%;
}
/* Rampe d'intro, en vert : le temps dont l'animateur dispose pour parler sur
   ce titre. Règle 2 — c'est le seul vert de la liste. */
.pl-item__intro {
  font-family: var(--mono); font-size: 10px; font-weight: 400;
  color: var(--intro);
  background: transparent;
  padding: 0 2px;
  min-width: 32px; text-align: right;
}
.pl-item__intro.is-none { color: var(--text-faint); background: transparent; }
.pl-item__dur {
  font-family: var(--mono); font-size: 10px; color: inherit; opacity: .8;
  font-variant-numeric: tabular-nums;
}
/* La lettre de fin : F pour un fondu, E pour une fin nette. */
.pl-item__dur b { font-weight: 700; color: var(--text-faint); }

.pl-item--fixed { cursor: default; }
.pl-item--fixed:hover { background: transparent; }
.pl-item--fixed:hover::before { opacity: 0; }
/* La ligne choisie : le violet de sélection au fond, le jaune au texte. C'est
   le seul endroit de l'interface où le violet remplit quelque chose. */
.pl-item.is-selected { background: var(--accent-soft); color: var(--warn); }
.pl-item.is-selected::before { opacity: 1; }
.pl-item.is-selected .pl-item__title,
.pl-item.is-selected .pl-item__artist { color: var(--warn); }

/* L'intervalle entre deux titres : c'est LÀ qu'on parle.
   Trois états, et un seul attire l'œil à la fois :
     — emplacement prévu par le conducteur : affiché en permanence ;
     — VT déjà posé : affiché, avec son statut ;
     — n'importe où ailleurs : muet, révélé au survol.
   Depuis qu'on peut parler partout, l'intervalle muet affiche quand même un
   « + » discret au survol : sans lui, rien ne disait que c'était cliquable. */
.pl-mark {
  display: flex; align-items: center; gap: 6px;
  height: 11px; padding: 0 8px 0 62px;
  cursor: pointer;
  position: relative;
}
.pl-mark__line {
  flex: 1; height: 1px;
  background: repeating-linear-gradient(90deg, var(--text-faint) 0 3px, transparent 3px 7px);
  opacity: 0;
}
.pl-mark__chip {
  font-family: var(--sans);
  font-size: 9px; font-weight: 700; line-height: 1.3;
  text-transform: uppercase; letter-spacing: .6px;
  color: var(--accent);
  padding: 0 6px; border-radius: 2px;
  border: 1px solid currentColor;
  opacity: 0;
  white-space: nowrap;
}
.pl-item:hover + .pl-mark .pl-mark__line,
.pl-mark:hover .pl-mark__line { opacity: .45; }
.pl-item:hover + .pl-mark .pl-mark__chip,
.pl-mark:hover .pl-mark__chip { opacity: 1; }

.pl-mark.is-planned {
  height: 22px;
  background: var(--accent-soft);
  box-shadow: inset 2px 0 0 var(--accent);
}
.pl-mark.is-planned .pl-mark__line { opacity: .4; }
.pl-mark.is-planned .pl-mark__chip {
  opacity: 1; font-size: 9.5px; padding: 2px 8px;
  color: var(--accent-text); background: var(--accent); border-color: var(--accent);
}
.pl-mark.is-posed { height: 22px; background: var(--bg-sunken); }
.pl-mark.is-posed .pl-mark__line { opacity: .3; }
.pl-mark.is-posed .pl-mark__chip {
  opacity: 1; font-size: 9.5px; padding: 2px 8px;
  color: inherit; background: var(--panel); border-color: currentColor;
}
.pl-mark.is-selected .pl-mark__chip { opacity: 1; }
.pl-mark.is-selected .pl-mark__line { opacity: .5; }

/* Statuts — même convention sur le studio et la supervision. */
.st-libre, .st-a_faire     { color: var(--text-faint); }
.st-recu, .st-traite       { color: var(--accent); }
.st-livre, .st-insere      { color: var(--ok); }
.st-rejete, .st-trop_tard  { color: var(--warn); }

/* ============================================================ ENREGISTREUR = */

.recorder { display: flex; flex-direction: column; gap: 8px; padding: 10px; }
.recorder__main { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

.rec {
  display: flex; align-items: center; gap: 9px;
  font: inherit; font-size: 13px; font-weight: 600;
  color: var(--text);
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-btn);
  padding: 9px 16px;
  cursor: pointer;
  transition: background .1s linear, border-color .1s linear;
}
.rec:hover:not(:disabled) { border-color: var(--text-faint); background: var(--panel-3); }
.rec:disabled { opacity: .38; cursor: not-allowed; }
.rec__dot { width: 11px; height: 11px; border-radius: 50%; background: var(--text-faint); }
.rec:not(:disabled) .rec__dot { background: var(--rec); }
.rec.is-recording {
  background: var(--rec); border-color: var(--rec); color: #fff;
  box-shadow: 0 0 0 3px var(--rec-glow);
}
.rec.is-recording .rec__dot {
  background: #fff;
  animation: pulse 1s steps(2, end) infinite;
}
@keyframes pulse { 50% { opacity: .15; } }
@media (prefers-reduced-motion: reduce) { .rec.is-recording .rec__dot { animation: none; } }

/* NEXT : lancer le titre suivant pendant qu'on parle. Vert, parce qu'il
   déclenche l'intro — c'est le même geste et le même repère (règle 2). */
.nextbtn {
  display: flex; align-items: center; gap: 9px;
  font: inherit; font-size: 13px; font-weight: 600;
  color: var(--intro);
  background: var(--intro-soft);
  border: 1px solid var(--intro-line);
  border-radius: var(--radius-btn);
  padding: 9px 16px;
  cursor: pointer;
  transition: background .1s linear, color .1s linear;
}
.nextbtn:hover:not(:disabled) { background: var(--intro); color: var(--bg); }
.nextbtn:disabled { opacity: .3; cursor: not-allowed; }
.nextbtn.is-fired {
  background: var(--intro); color: var(--bg); border-color: var(--intro);
}
.nextbtn__ico { width: 17px; height: 17px; }
.nextbtn__sub { font-size: 10px; font-weight: 500; opacity: .85; display: block; }

.recorder__time { display: flex; flex-direction: column; min-width: 0; gap: 1px; }
.recorder__hint { font-size: 11.5px; color: var(--text-dim); }
.recorder__row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

.timer {
  font-family: var(--mono);
  font-size: 30px; font-weight: 300; letter-spacing: 0; line-height: 1;
  color: var(--text-hi);
  font-variant-numeric: tabular-nums;
}
.timer--small { font-size: 15px; font-weight: 400; letter-spacing: 0; color: var(--text-dim); }
.is-recording-session .timer { color: var(--rec); }

/* Le compte à rebours d'intro : une fois NEXT appuyé, il dit combien de
   secondes de chant restent avant qu'on parle dessus. C'est l'information
   que l'animateur cherche, et elle vire à l'ambre quand il la dépasse. */
.introbar {
  display: flex; align-items: center; gap: 9px;
  padding: 5px 9px;
  background: var(--intro-soft);
  border: 1px solid var(--intro-line);
  border-radius: var(--radius-s);
  font-size: 12px;
  color: var(--intro);
}
.introbar__left {
  font-family: var(--mono); font-size: 18px; font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.introbar__gauge {
  flex: 1; height: 4px; border-radius: 999px;
  background: #00000040; overflow: hidden; position: relative;
}
.introbar__fill { position: absolute; inset: 0 100% 0 0; background: var(--intro); }
.introbar.is-over {
  background: var(--warn-soft); border-color: var(--warn); color: var(--warn);
}
.introbar.is-over .introbar__fill { background: var(--warn); }

/* Le vumètre. Échelle de 60 dB : -60 dBFS à gauche, 0 à droite.

   Le dégradé couvre TOUTE la largeur et c'est un découpage qui le révèle.
   Auparavant il était peint sur le remplissage lui-même, donc ses bornes —
   60 %, 85 % — se rapportaient à la largeur courante de la barre et non à
   l'échelle : à n'importe quel niveau la barre était 60 % verte, 25 % ambre
   et 15 % rouge, avec du rouge au bout en permanence. Un vumètre dont les
   couleurs suivent l'aiguille ne dit rien du niveau.

   Les bornes sont maintenant des valeurs : -12 dBFS (80 %) et -6 dBFS (90 %).
   Une voix bien réglée vit dans le vert et tape l'ambre sur les attaques. */
.meter {
  position: relative; height: 8px;
  background: var(--bg-sunken);
  border: 1px solid var(--line-soft);
  border-radius: 2px;
  overflow: hidden;
}
.meter__fill {
  position: absolute; inset: 0;
  background: linear-gradient(
    90deg,
    var(--ok) 0 80%,
    var(--cat-jingle) 80% 90%,
    var(--rec) 90%
  );
  /* `clip-path` plutôt que `width` ou `right` : le découpage se fait à la
     composition, sans refaire la mise en page soixante fois par seconde. */
  clip-path: inset(0 100% 0 0);
}
/* Les deux graduations, visibles même quand la barre est basse : on doit
   savoir où est la zone rouge AVANT d'y entrer. */
.meter::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(90deg, transparent 80%, var(--line) 80% calc(80% + 1px), transparent calc(80% + 1px)),
    linear-gradient(90deg, transparent 90%, var(--line) 90% calc(90% + 1px), transparent calc(90% + 1px));
  opacity: .55;
}
/* La crête, retenue puis relâchée : c'est elle qui dit qu'on a saturé il y a
   une seconde, quand l'aiguille est déjà redescendue. */
.meter__peak {
  position: absolute; top: 0; bottom: 0; left: 0; width: 2px;
  margin-left: -1px;
  background: var(--text); opacity: .85;
}
.meter__peak.is-hot { background: var(--rec); opacity: 1; }

/* =========================================================== SEGUE EDITOR ==
   L'éditeur de transition, refait sur le modèle du Segue Editor de Myriad :
   les éléments sont empilés en cascade sur une seule échelle de temps, chacun
   avec son bandeau de titre et sa forme d'onde, et la courbe de volume se
   tire à la main par ses nœuds.

   Tout le dessin est fait au canvas (js/segue.js) : à ce niveau de densité —
   quatre formes d'onde, une grille, une enveloppe, un curseur à 60 images par
   seconde — le DOM ne suit pas. Le CSS ne pose donc que le cadre.
   =========================================================================== */

.segue {
  position: relative;
  display: grid; grid-template-rows: auto auto 1fr auto;
  height: 100%; min-height: 0;
}

.segue__bar {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  padding: 4px 10px;
  background: var(--panel);
  border-bottom: 1px solid var(--line-soft);
}

/* Le gros bouton rond de lecture, comme au centre de l'éditeur de Myriad. */
.playbtn {
  width: 32px; height: 28px; border-radius: var(--radius-btn);
  display: grid; place-items: center;
  color: var(--ok); background: var(--panel-2);
  border: 1px solid var(--line);
  cursor: pointer; flex: none;
  /* Plus de décalage optique : le triangle était un glyphe, posé de travers
     dans sa case, qu'il fallait repousser de 2 px pour qu'il ait l'air
     centré. Le dessin SVG est centré dans sa grille, lui. */
}
.playbtn:hover:not(:disabled) { background: var(--panel-3); }
.playbtn:disabled { opacity: .35; cursor: not-allowed; }
.playbtn.is-playing { color: var(--warn); border-color: var(--accent); }

.knob {
  display: flex; align-items: center; gap: 6px;
  font-size: 10px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--text-dim);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  padding: 3px 8px;
}
.knob input[type="range"] { width: 84px; }
.knob output {
  min-width: 50px; color: var(--text-hi); font-size: 11px; font-weight: 400;
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  text-transform: none; letter-spacing: 0;
}
/* Le libellé du niveau porte le nom de l'élément visé : un titre de cartouche
   entier pousserait tout le reste de la barre hors de l'écran. */
#knob-gain > span {
  max-width: 94px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.knob.is-overridden { opacity: .45; }
.knob.is-overridden input { pointer-events: none; }

/* La bande d'aperçu : toute la transition en miniature, avec le rectangle de
   la portion affichée en dessous — on le glisse pour se déplacer, on tire ses
   bords pour zoomer. C'est la barre du haut de l'éditeur de Myriad. */
.segue__overview {
  position: relative; height: 26px;
  background: var(--wave-bg);
  border-bottom: 1px solid var(--line-soft);
  cursor: grab;
  touch-action: none;
}
.segue__overview:active { cursor: grabbing; }
.segue__overview canvas { display: block; width: 100%; height: 100%; }
.segue__view {
  position: absolute; top: 0; bottom: 0;
  border: 1px solid var(--accent);
  background: var(--accent-soft);
  pointer-events: none;
}

/* Fond de forme d'onde isolé du fond global : c'est la règle qui fait
   ressortir les ondes, et le canvas lit le même jeton (js/segue.js). */
.segue__stage {
  position: relative; min-height: 0;
  background: var(--wave-bg);
  overflow: hidden;
  touch-action: none;
}
.segue__stage canvas { display: block; width: 100%; height: 100%; }
.segue__stage.is-dragover { box-shadow: inset 0 0 0 2px var(--accent); }
/* Les curseurs changent selon ce qui est sous le pointeur : le canvas ne
   peut pas le dire tout seul, js/segue.js pose la classe. */
.segue__stage[data-cursor="move"]   { cursor: ew-resize; }
.segue__stage[data-cursor="node"]   { cursor: grab; }
.segue__stage[data-cursor="drag"]   { cursor: grabbing; }
.segue__stage[data-cursor="line"]   { cursor: crosshair; }
.segue__stage[data-cursor="pan"]    { cursor: default; }

.segue__foot {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  padding: 3px 10px;
  font-size: 10px; color: var(--text-dim);
  background: var(--panel);
  border-top: 1px solid var(--line-soft);
  min-height: 22px;
}
.segue__legend { display: flex; align-items: center; gap: 4px; }
.segue__legend i {
  width: 9px; height: 9px; display: block;
}
.segue__hint { margin-left: auto; color: var(--text-faint); }

/* Le message d'attente se POSE sur la scène, il ne s'ajoute pas dessous : en
   enfant de plus de la grille, il ajoutait une rangée pleine hauteur et
   faisait défiler tout le panneau. La barre d'outils reste donc atteignable,
   notamment le bouton d'écoute. */
.segue__empty {
  position: absolute; inset: 0; z-index: 2;
  display: grid; place-content: center; gap: 6px; align-content: center;
  background: var(--wave-bg);
  color: var(--text-faint); font-size: 12px; text-align: center; padding: 20px;
}
.segue__empty small { color: var(--text-faint); opacity: .75; font-size: 11px; }

/* =========================================================== CARTOUCHIER === */

/* Le cartouchier : SEIZE LECTEURS, pas seize vignettes.
   Chacun a l'anatomie d'un cartoucheur de régie (FreQQ, MediaWall) : plaque de
   titre, barre de défilement, gros compteur monospace, commandes. Le compteur
   est l'élément le plus gros du panneau, devant le titre — en parlant on ne
   relit pas le nom du jingle qu'on vient de lancer, on regarde le temps qui
   reste avant de reprendre.
   Deux écarts assumés avec FreQQ : son PLAY vert et son STOP rouge sont ici
   impossibles (règles 1 et 2 de cette feuille), donc le départ prend l'accent
   et l'arrêt reste neutre. On ne perd rien : le lecteur en l'air s'allume
   entièrement, ce qui se voit de plus loin qu'un bouton coloré.
   Les cases vides restent dessinées : un mur à trous se mémorise par ses
   positions, un mur qui se referme sur lui-même se réapprend à chaque fois. */

/* Les pages vivent dans la barre de titre du panneau, à côté du nom : c'est la
   place qu'elles ont sur une console, et ça rend toute la hauteur au mur. */
/* Sur une seule ligne, et rognées plutôt qu'enroulées : la barre de titre fait
   26 px de haut, un retour à la ligne y serait coupé en deux. */
.wall__tabs {
  display: flex; flex-wrap: nowrap; gap: 1px;
  margin-left: 6px; margin-right: auto;
  min-width: 0; overflow: hidden;
}
.wall__tab {
  font: inherit; font-size: 10px; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase;
  padding: 2px 8px;
  color: var(--text-faint);
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-btn);
  cursor: pointer;
}
.wall__tab:hover { color: var(--text); background: var(--panel-2); border-color: var(--line); }
.wall__tab.is-active {
  color: var(--warn); background: transparent; border-color: var(--accent);
}
.wall__tab--add { letter-spacing: 0; font-size: 12px; padding: 1px 7px; }

.wall {
  display: grid;
  /* 150 px : deux lecteurs tiennent dans une colonne étroite, quatre dans un
     panneau tiré en pleine largeur — la disposition à quatre colonnes d'un
     cartoucheur se retrouve dès qu'on lui en laisse la place. */
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  align-content: start;
  /* Un filet d'un pixel entre les cases, et non un écart : c'est la grille de
     cartouches du système — les lecteurs se touchent, séparés par le trait. */
  gap: 1px;
  padding: 1px;
  background: var(--line-soft);
}
.wall .empty { grid-column: 1 / -1; }

.cp {
  position: relative;
  display: flex; flex-direction: column;
  min-height: 120px;
  color: var(--text);
  background: var(--ground);
  border: 0;
  border-left: 2px solid var(--cat-autre);
  border-radius: var(--radius-xs);
  overflow: hidden;
  cursor: pointer;
}
.cp:hover { background: var(--panel); }
.cp.is-playing {
  background: var(--accent-soft);
}
.cp.is-dragging { opacity: .45; }

/* La plaque de titre : fond enfoncé, titre sur une ligne. Deux lignes de titre
   feraient danser le compteur d'un lecteur à l'autre, et c'est le compteur
   qu'on vise. */
.cp__plate {
  display: flex; align-items: center; gap: 6px;
  padding: 5px 8px 4px;
  background: transparent;
  border-bottom: 1px solid var(--line-soft);
}
.cp__num {
  font-family: var(--mono); font-size: 10px;
  color: var(--text-faint); flex: none;
}
.cp__title {
  flex: 1; min-width: 0;
  font-size: 11px; font-weight: 400; line-height: 1.3;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* La touche du clavier : c'est elle qu'on cherche des yeux en parlant. */
.cp__key {
  flex: none;
  font-family: var(--mono); font-size: 11px; font-weight: 400; line-height: 1;
  color: var(--text-dim);
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-btn);
  padding: 1px 4px;
}
.cp.is-playing .cp__key { color: var(--warn); border-color: var(--accent); }

/* Le défilement, en vraie position de lecture — pas une animation lancée à
   l'aveugle : l'aiguille est calculée sur l'horloge audio. */
.cp__rail {
  height: 3px; background: var(--panel);
  border-bottom: 1px solid var(--line-soft);
}
.cp__fill {
  display: block; height: 100%;
  background: var(--accent);
  transform: scaleX(0); transform-origin: left;
}

.cp__body {
  flex: 1;
  display: flex; flex-direction: column; justify-content: center;
  gap: 2px; padding: 5px 7px 3px;
}
.cp__count {
  font-family: var(--mono); font-size: 18px; font-weight: 300;
  line-height: 1; letter-spacing: 0;
  font-variant-numeric: tabular-nums;
  color: var(--text-hi);
}
.cp.is-playing .cp__count { color: var(--warn); }
.cp__meta {
  display: flex; justify-content: space-between; gap: 6px;
  font-family: var(--mono); font-size: 10px;
  color: var(--text-faint);
}
.cp__loopmark {
  display: inline-flex; align-items: center; gap: 3px;
  color: var(--text-dim);
}

/* Le niveau du lecteur, comme le VOL d'une colonne de cartoucheur. Discret au
   repos, il s'éclaire au survol : on le règle une fois par cartouche, pas à
   chaque lancement. */
.cp__vol {
  display: flex; align-items: center; gap: 5px;
  padding: 0 7px 3px;
  cursor: pointer;
}
.cp__voltag {
  font-size: 8.5px; font-weight: 700; letter-spacing: .5px;
  color: var(--text-faint);
}
.cp__vol input[type="range"] {
  flex: 1; min-width: 0; height: 12px; margin: 0;
  accent-color: var(--text-faint);
  cursor: pointer;
}
.cp:hover .cp__vol input[type="range"],
.cp.is-playing .cp__vol input[type="range"] { accent-color: var(--accent); }

/* Les commandes. Toujours visibles : un bouton qui n'apparaît qu'au survol
   oblige à chercher à la souris ce qu'on devrait frapper du doigt. */
.cp__acts {
  display: flex; gap: 2px;
  padding: 4px 5px 5px;
  border-top: 1px solid var(--line-soft);
}
.cpb {
  font: inherit; font-size: 9.5px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .08em;
  color: var(--text-dim);
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-btn);
  padding: 3px 0;
  cursor: pointer;
  flex: 1;
}
.cpb:hover { color: var(--text); background: var(--panel-3); }
.cpb--play { color: var(--ok); }
.cpb--play:hover { color: var(--ok); background: var(--panel-3); }
.cpb--icon { flex: 0 0 24px; font-size: 11px; letter-spacing: 0; }
.cpb--icon.is-on {
  color: var(--warn); border-color: var(--accent); background: transparent;
}
.cpb--del:hover { color: var(--rec); border-color: var(--rec); }

/* Une case libre reste une case : son numéro est écrit, sa place est tenue. */
.cp--empty {
  justify-content: flex-start;
  background: transparent;
  border: 1px dashed var(--line);
  border-left: 3px dashed var(--line);
  color: var(--text-faint);
  cursor: default;
}
.cp--empty .cp__plate { background: transparent; border-bottom-color: transparent; }
.cp--empty .cp__key { opacity: .35; }
.cp__hint {
  flex: 1; display: grid; place-items: center;
  font-size: 9.5px; text-transform: uppercase; letter-spacing: .5px;
  opacity: .7; text-align: center; padding: 0 6px 8px;
}
.cp.is-target {
  border-style: solid; border-color: var(--accent); background: var(--accent-soft);
}

.cat-jingle   { border-left-color: var(--cat-jingle); }
.cat-sweeper  { border-left-color: var(--cat-sweeper); }
.cat-liner    { border-left-color: var(--cat-liner); }
.cat-bed      { border-left-color: var(--cat-bed); }
.cat-bruitage { border-left-color: var(--cat-bruitage); }
.cat-musique  { border-left-color: var(--cat-musique); }
.cat-pub      { border-left-color: var(--cat-pub); }
.cat-voix     { border-left-color: var(--cat-voix); }

.segmented {
  display: flex; margin-left: auto; flex: none;
  background: var(--bg-sunken);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-xs); padding: 1px;
}
.seg {
  font: inherit; font-size: 10px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .4px;
  color: var(--text-faint);
  background: transparent; border: 0; border-radius: 2px;
  padding: 2px 8px; cursor: pointer;
}
.seg:hover { color: var(--text); }
.seg.is-active { background: var(--panel-3); color: var(--text); }

/* La recherche reste en haut quand la liste défile : c'est un panneau à part
   maintenant, on y descend loin, et remonter chercher le champ à chaque
   requête serait une corvée. */
.library__search {
  position: sticky; top: 0; z-index: 2;
  display: flex; gap: 5px; padding: 7px;
  background: var(--panel);
  border-bottom: 1px solid var(--line-soft);
}
.library__search input, .library__search select {
  font: inherit; font-size: 11px; color: var(--text);
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius-s); padding: 4px 8px; min-width: 0;
}
.library__search input { flex: 1; }
.library__search select { max-width: 42%; }
.library__search option { background: var(--panel); color: var(--text); }
.library__search input:focus, .library__search select:focus {
  outline: 0; border-color: var(--accent);
}

.breadcrumb {
  display: flex; flex-wrap: wrap; align-items: center; gap: 1px;
  padding: 5px 8px; font-size: 11.5px;
  border-bottom: 1px solid var(--line-soft);
}
.breadcrumb button {
  font: inherit; font-size: 11.5px; background: transparent; border: 0;
  color: var(--accent); cursor: pointer; padding: 1px 4px; border-radius: 2px;
}
.breadcrumb button:hover { background: var(--accent-soft); }
.breadcrumb span { color: var(--text-faint); }

/* Même règle que le conducteur : la police de l'interface pour les noms, le
   monospace réservé à la durée, qui s'aligne. */
.lib-row {
  display: grid; grid-template-columns: 1fr auto auto;
  align-items: center; gap: 7px;
  padding: 3px 8px;
  border-bottom: 1px solid var(--line-soft);
  font-family: var(--sans); font-size: 12px;
}
.lib-row--folder { cursor: pointer; }
.lib-row--item { cursor: grab; }
.lib-row:hover { background: #ffffff08; }
.lib-row__title { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* La ligne de dossier porte une icône devant son nom. `gap` plutôt qu'une
   espace écrite dans le texte : l'espace ferait partie du nom, donc du
   découpage par points de suspension. */
.lib-row--folder .lib-row__title {
  display: flex; align-items: center; gap: 5px;
  color: var(--text-dim);
}
/* La ligne qu'on écoute se tient allumée : le bouton passe à STOP, et la
   ligne se marque — on la retrouve après avoir fait défiler la liste. */
.lib-row--item.is-playing { background: var(--accent-soft); }
.lib-row--item.is-playing .lib-row__title { color: var(--warn); }
.lib-row__artist { font-size: 10.5px; color: var(--text-dim); }
.lib-row__dur { font-family: var(--mono); font-size: 10.5px; color: var(--text-faint); }

/* =============================================================== DIALOGUE == */

.dialog { border: 0; padding: 0; background: transparent; max-width: 470px; width: calc(100% - 32px); }
.dialog::backdrop { background: #04060abb; }
.dialog__box {
  background: var(--panel); color: var(--text);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 18px;
  box-shadow: var(--shadow-l);
  max-height: 86vh; overflow: auto;
}
.dialog__box h2 { margin: 0 0 14px; font-size: 15px; font-weight: 650; }
.dialog fieldset {
  border: 1px solid var(--line-soft); border-radius: var(--radius-s);
  padding: 12px; margin: 0 0 12px;
  background: var(--panel-2);
}
.dialog legend {
  padding: 0 6px; font-size: 10px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .8px; color: var(--text-dim);
}
.dialog select option { background: var(--panel); color: var(--text); }
.dialog__actions { display: flex; justify-content: flex-end; gap: 7px; margin: 0; padding: 0; }

/* Une question, pas un formulaire : plus étroite, et la réponse se lit d'un
 * coup d'œil depuis la régie. */
.dialog--ask { max-width: 400px; }
.dialog--ask .dialog__box { padding: 20px; }
.dialog--ask .dialog__actions { margin-top: 18px; }
.ask__text { margin: 0 0 8px; font-size: 12.5px; line-height: 1.5; color: var(--text-dim); }
.ask__text:last-of-type { margin-bottom: 0; }
.keys { margin: 0; padding-left: 16px; font-size: 12px; color: var(--text-dim); }
.keys li { margin: 3px 0; }
kbd {
  font-family: var(--mono); font-size: 10.5px;
  background: var(--bg-sunken); border: 1px solid var(--line);
  border-bottom-width: 2px;
  border-radius: 2px; padding: 1px 5px; color: var(--text);
}

/* ================================================================ TOASTS === */

.toasts {
  position: fixed; bottom: 14px; left: 50%; transform: translateX(-50%);
  display: flex; flex-direction: column; gap: 6px; z-index: 50;
  pointer-events: none;
}
.toast {
  background: var(--panel); color: var(--text);
  border: 1px solid var(--line);
  border-radius: var(--radius-s); padding: 8px 13px;
  font-size: 12.5px;
  box-shadow: var(--shadow-l), inset 3px 0 0 var(--accent);
  max-width: min(520px, calc(100vw - 32px));
  animation: toast-in .14s ease-out;
}
.toast--warn { box-shadow: var(--shadow-l), inset 3px 0 0 var(--warn); }
.toast--ok   { box-shadow: var(--shadow-l), inset 3px 0 0 var(--ok); }
@keyframes toast-in { from { opacity: 0; transform: translateY(6px); } }
@media (prefers-reduced-motion: reduce) { .toast { animation: none; } }

/* ================================================================ MOBILE === */

.mobilebar { display: none; }

@media (max-width: 900px) {
  /* Au doigt, la disposition libre n'a pas de sens : pas de souris pour
     tirer une poignée de 5 px, et un écran qui ne tient qu'un panneau. On
     repasse donc en empilement, un panneau à la fois, choisi en bas. */
  body { overflow: auto; }
  .app { grid-template-rows: auto auto 1fr auto; }
  .menubar__center { display: none; }
  /* La rangée de gauche défile au doigt au lieu de pousser la session hors de
     l'écran. `flex: none` la rendait incompressible : au large c'est ce qu'on
     veut, mais sur un téléphone le choix de panel — quatre vues nommées en
     clair sur la supervision — débordait du bandeau et emportait le nom du
     compte avec lui. */
  .menubar__left { flex: 1 1 auto; overflow-x: auto; }
  .toolbar { flex-wrap: nowrap; }
  .clock { display: none; }
  .statusbar { display: none; }

  .desk { flex-direction: column; }
  .split { display: none; }
  .dock { flex: 1 !important; width: auto !important; }
  .dock:not([data-pane-active]) { display: none; }
  .panel { flex: 1 !important; }
  .panel__head { cursor: default; }

  .mobilebar {
    display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
    background: var(--panel); border-top: 1px solid var(--line);
    padding-bottom: env(safe-area-inset-bottom);
  }
  .mb {
    font: inherit; font-size: 11px; color: var(--text-dim);
    background: transparent; border: 0; padding: 10px 4px; cursor: pointer;
  }
  .mb.is-active { color: var(--accent); box-shadow: inset 0 2px 0 var(--accent); }

  /* Sans survol, les intervalles muets disparaissent plutôt que d'afficher
     vingt « parler ici » par heure : on touche la LIGNE, qui porte le même
     ancrage. Les emplacements prévus, eux, restent visibles. */
  .pl-mark { height: 6px; }
  .pl-mark.is-planned, .pl-mark.is-posed { height: 26px; }
  .pl-item { height: 32px; }
}

/* ===========================================================================
   Supervision (admin.html)

   Mêmes jetons et même convention de statut (`st-*`) que le studio. C'est la
   seule page où l'échéance de livraison est encore affichée : elle n'interdit
   plus rien, mais un responsable d'antenne doit pouvoir la voir.
   =========================================================================== */

/* --- La barre de la supervision --------------------------------------------
   Il n'y en a plus. La page portait `.topbar`, `.tabs` et `.tab`, un jeu de
   classes à elle seule, hérité du studio d'avant : la refonte de direction
   artistique a renommé le shell en `.menubar` / `.toolbar` / `.workspace` et
   n'a pas emmené la supervision avec elle. On lui a d'abord redessiné sa barre
   à part — d'où ces règles, maintenant retirées — et elle a redérivé aussitôt,
   parce qu'une barre à part ne reçoit pas les règles d'étroitesse écrites pour
   `.menubar` : sur un téléphone, quatre onglets, un calendrier, un menu de
   statuts et quatre boutons tenaient sur une ligne qui ne se replie pas, et
   le bandeau débordait de l'écran.

   `admin.html` emploie donc le shell du studio tel quel. Il n'y a plus rien à
   tenir en double, et la page suivra la prochaine refonte sans qu'on y
   pense. */

.admin {
  display: grid; grid-template-rows: auto 1fr;
  gap: var(--gap); padding: var(--gap);
  min-height: 0; overflow: hidden;
}
/* Et la vue prend la hauteur restante. Sans cette ligne, le `flex: none` du
   shell — écrit pour le studio, où seul `.desk` grandit — figeait la vue à sa
   hauteur naturelle : la rangée `1fr` tombait à zéro, les tableaux se
   refermaient, et la page se réduisait à son bandeau. C'est ce qui faisait
   qu'on ne pouvait rien faire une fois connecté.

   Les deux parentés sont couvertes, et non la seule qui sert aujourd'hui : la
   vue est posée dans `.workspace`, mais `.app` reste le parent si on la sort
   un jour du plan de travail, et la ligne ne coûte rien. */
.app > .admin,
.workspace > .admin { flex: 1 1 auto; min-height: 0; }
.admin--single { grid-template-rows: 1fr; }
.admin .panel {
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
}

/* Les blocs de la page d'admin. Ce sont les panneaux du studio sans le
   glisser-déposer : on ne range pas une page de surveillance, on la lit. Le
   titre y est donc lisible et non une étiquette minuscule — il n'y a pas
   trente panneaux à distinguer d'un coup d'œil, il y en a deux. */
.zone {
  display: grid; grid-template-rows: auto 1fr;
  min-height: 0; min-width: 0;
  background: var(--panel);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-s);
  overflow: hidden;
}
.zone__head {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 10px;
  background: var(--panel-3);
  border-bottom: 1px solid var(--line-soft);
}
.zone__head h2 {
  margin: 0; font-size: 12px; font-weight: 650;
  letter-spacing: .01em; color: var(--text);
}
/* `margin-left: auto` sur le premier élément qui suit le titre : le méta
   comme le bouton se posent à droite, quel que soit celui des deux qui est
   là. Un sélecteur par cas aurait laissé le bouton collé au titre sur la
   zone qui n'a pas de méta. */
.zone__head > :nth-child(2) { margin-left: auto; }
.zone__meta { font-size: 11px; color: var(--text-faint); font-family: var(--mono); }
.zone__body { overflow: auto; min-height: 0; min-width: 0; }
.zone__body--pad { padding: 11px; }

.statline { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
.stat {
  display: flex; align-items: baseline; gap: 6px;
  padding: 4px 10px;
  background: var(--panel-2);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-s);
  font-size: 11.5px; color: var(--text-dim);
}
.stat b { font-family: var(--mono); font-size: 14px; font-weight: 700; }
.stat--total { background: transparent; border-style: dashed; }

.risk {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 8px 11px;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-s);
  background: var(--panel-2);
  box-shadow: inset 3px 0 0 var(--warn);
  margin-bottom: 6px;
  font-size: 12.5px;
}
.risk__when { font-size: 12px; color: var(--text-dim); }
.risk__left { color: var(--warn); font-weight: 650; }
.risk__what { color: var(--text-faint); font-size: 11.5px; }
.risk .btn { margin-left: auto; }

/* La grille de supervision suit la grille du système : en-tête au gris haut,
   chiffres en Consolas tabulaire, lignes séparées d'un filet. */
.grid {
  width: 100%; border-collapse: collapse;
  font-family: var(--mono); font-size: 11px;
  font-variant-numeric: tabular-nums;
}
.grid th {
  position: sticky; top: 0; z-index: 1;
  background: var(--panel-3);
  text-align: left;
  font-family: var(--sans);
  font-size: 10px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--text-dim); font-weight: 600;
  padding: 4px 6px;
  border-bottom: 1px solid var(--line-soft);
  border-right: 1px solid var(--line-soft);
}
.grid td {
  padding: 3px 6px;
  border-bottom: 1px solid var(--line-soft);
  border-right: 1px solid var(--line-soft);
  vertical-align: top;
}
.grid .num, .grid th.num { text-align: right; }
.grid tbody tr:hover { background: #ffffff08; }
.grid tr.is-faulty { box-shadow: inset 3px 0 0 var(--warn); background: var(--warn-soft); }
.cell-note { font-size: 11px; color: var(--warn); margin-top: 3px; max-width: 42ch; }
.tag { font-size: 11.5px; font-weight: 650; }
.mono-soft { font-family: var(--mono); font-size: 11px; color: var(--text-faint); }
.grid td .btn { margin-left: 6px; }
/* Note neutre, par opposition à `.cell-note` qui est une alerte : un
   identifiant ou un rappel de règle n'est pas un problème, et le mettre en
   orange ferait croire qu'il y en a un sur chaque ligne. */
.cell-sub { font-size: 11px; color: var(--text-faint); margin-top: 3px; max-width: 46ch; }

/* ---------- Comptes et droits ---------- */

.who-name { font-family: var(--sans); font-size: 12.5px; font-weight: 600; }
/* Un compte désactivé reste lisible mais se retire : il est dans la liste
   pour qu'on puisse le rouvrir, pas pour qu'on le compte parmi les autres. */
.grid tr.is-off { opacity: .55; }
.grid tr.is-off .who-name { text-decoration: line-through; }

.rights { display: flex; flex-wrap: wrap; gap: 4px 6px; }
.right {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 8px 3px 6px;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-s);
  background: var(--panel-2);
  font-family: var(--sans); font-size: 11.5px; color: var(--text-dim);
  cursor: pointer; user-select: none;
}
.right:hover { border-color: var(--line); color: var(--text); }
/* La case cochée tient sur `:has()` et non sur une classe posée par le JS :
   l'état vrai est celui de la case, et deux représentations du même état se
   désynchronisent toujours un jour. */
.right:has(input:checked) {
  border-color: var(--accent);
  color: var(--text);
  background: var(--accent-soft);
}
.right input { accent-color: var(--accent); margin: 0; }

.row-actions { white-space: nowrap; }
/* Première action de la cellule : pas de marge à gauche, elle ne suit rien. */
.row-actions .btn:first-child { margin-left: 0; }

.form-grid { display: grid; gap: 11px; margin-top: 4px; }
.dialog--form { max-width: 420px; }
.dialog--form .dialog__box { padding: 20px; }

.logs {
  margin: 0; padding: 11px;
  font-family: var(--mono); font-size: 11px; line-height: 1.55;
  color: var(--text-dim);
  background: var(--bg-sunken);
  white-space: pre-wrap; word-break: break-word;
  max-height: 100%; overflow: auto;
}

.trail { list-style: none; margin: 0; padding: 0; }
.trail__step {
  display: grid; grid-template-columns: 136px 92px 1fr;
  gap: 9px;
  padding: 7px 0 7px 11px;
  border-left: 2px solid var(--line);
  font-size: 12px;
}
.trail__step--warn  { border-left-color: var(--warn); }
.trail__step--error { border-left-color: var(--warn); background: var(--warn-soft); }
.trail__at { color: var(--text-faint); font-size: 11px; font-family: var(--mono); }
.trail__event { font-weight: 650; color: var(--accent); }
.trail__detail { color: var(--text-dim); word-break: break-word; }

.facts { display: grid; grid-template-columns: max-content 1fr; gap: 7px 16px; margin: 0; font-size: 12.5px; }
.facts dt { color: var(--text-dim); font-size: 11.5px; }
.facts dd { margin: 0; word-break: break-word; }
.facts dd.yes { color: var(--ok); }
.facts dd.no  { color: var(--warn); font-weight: 650; }

@media (max-width: 760px) {
  .admin { grid-template-rows: auto auto; overflow: auto; }
  .trail__step { grid-template-columns: 1fr; gap: 2px; }
  .panel__body { overflow-x: auto; }
  .grid { min-width: 620px; }
}

#events.dialog { max-width: 780px; }
#events .dialog__box { max-height: min(82vh, 760px); overflow: auto; }

/* ======================================================= Import FTP ====== */

/* La liste des serveurs. Une ligne dit quatre choses — un voyant, un nom, un
   hôte, l'état de la dernière relève — et c'est le voyant qu'on lit en
   premier : on ouvre ce panel parce qu'on se demande si tout rentre. */
.ftprow {
  display: grid;
  grid-template-columns: 10px 1fr auto;
  grid-template-areas: "dot name host" ". state state";
  align-items: center;
  gap: 2px 9px;
  width: 100%;
  padding: 8px 11px;
  font: inherit; text-align: left;
  color: var(--text);
  background: transparent;
  border: 0; border-bottom: 1px solid var(--line);
  cursor: pointer;
}
.ftprow:hover { background: var(--accent-soft); }
.ftprow.is-active { background: var(--accent-soft); box-shadow: inset 2px 0 0 var(--accent); }
.ftprow__dot {
  grid-area: dot;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--ok);
}
/* Inactif : le serveur est déclaré mais personne ne le relève. Ce n'est pas
   une panne, donc ce n'est pas la couleur d'une panne — c'est une absence. */
.ftprow.is-off { color: var(--text-dim); }
.ftprow.is-off .ftprow__dot { background: var(--line); }
.ftprow.is-bad .ftprow__dot { background: var(--warn); }
.ftprow__name { grid-area: name; font-weight: 650; font-size: 12.5px; }
.ftprow__host { grid-area: host; font-family: var(--mono); font-size: 10.5px; color: var(--text-faint); }
.ftprow__state { grid-area: state; font-size: 11px; color: var(--text-dim); }

/* La fiche. Deux colonnes dès qu'il y a la place : trente-quatre réglages
   empilés sur une seule font un rouleau que personne ne lit jusqu'en bas. */
.ftpform { padding: 12px 14px 18px; }
.ftpform__group {
  margin: 0 0 16px; padding: 12px 13px 4px;
  border: 1px solid var(--line); border-radius: var(--radius-s);
  background: var(--bg-sunken);
}
.ftpform__group legend {
  padding: 0 6px; font-size: 11px; letter-spacing: .3px;
  text-transform: uppercase; color: var(--accent);
}
.ftpform__check { display: flex; flex-wrap: wrap; margin-bottom: 12px; }
/* L'explication passe à la ligne sous la case, pas à côté : à côté, elle
   repousse le libellé et deux cases voisines ne s'alignent plus. */
.ftpform__check .ftpform__hint { flex: 1 0 100%; margin-left: 24px; }
.ftpform__hint {
  margin: 4px 0 0; font-size: 11px; line-height: 1.45;
  color: var(--text-faint);
}
.ftpform__acts {
  display: flex; gap: 9px; justify-content: flex-end;
  position: sticky; bottom: 0;
  padding: 11px 0 2px;
  background: linear-gradient(to top, var(--panel) 72%, transparent);
}

@media (min-width: 1100px) {
  .ftpform__group {
    display: grid; grid-template-columns: 1fr 1fr; gap: 0 16px;
    align-items: start;
  }
  .ftpform__group legend { grid-column: 1 / -1; }
  /* Les cases à cocher et leurs explications prennent la largeur : une
     explication de deux lignes dans une demi-colonne décale tout ce qui
     suit. */
  .ftpform__check { grid-column: 1 / -1; }
}

/* Le compte rendu de relève. */
.ftprep { padding: 12px 14px; }
.ftprep__head {
  margin: 0 0 10px; font-weight: 650; font-size: 13px;
  color: var(--ok);
}
.ftprep--bad .ftprep__head { color: var(--warn); }
.ftprep__list { padding: 10px 14px; border-top: 1px solid var(--line); }
.ftprep__list h4 {
  margin: 0 0 6px; font-size: 11px; letter-spacing: .3px;
  text-transform: uppercase; color: var(--text-dim);
}
.ftprep__list ul {
  margin: 0; padding: 0; list-style: none;
  font-family: var(--mono); font-size: 11px; line-height: 1.6;
  color: var(--text-dim);
  word-break: break-all;
}
.ftprep__ok li { color: var(--text); }
.ftprep__err li { color: var(--warn); }
.ftprep__skip li { color: var(--text-faint); }
.ftprep__hist li {
  display: grid; grid-template-columns: 132px 1fr auto;
  gap: 9px; align-items: baseline;
  padding: 2px 0;
}
.ftprep__when { color: var(--text-faint); }
.ftprep__path { color: var(--text); word-break: break-all; }
.ftprep__id { color: var(--accent); white-space: nowrap; }

@media (max-width: 760px) {
  .ftprep__hist li { grid-template-columns: 1fr; gap: 0; }
}

/* ===========================================================================
   Panel « Programmation ».

   Un parti pris de lisibilité traverse tout ce bloc : rien ne se jauge à une
   couleur seule. La grille marque une heure programmée par sa teinte ET par
   le nom de l'horloge ; une ligne de conducteur repêchée porte une étiquette
   écrite, pas un fond orange ; une règle dure se signale par un filet ET par
   sa case cochée. Un conducteur se relit à 6 h du matin sur l'écran mal réglé
   d'un studio, et il faut qu'il s'y relise.
   ======================================================================== */

/* --- la grille des 168 heures --- */
.pgrid__bar {
  display: flex; align-items: center; gap: 8px;
  padding: 9px 12px; border-bottom: 1px solid var(--line);
  background: var(--bg-sunken);
}
.pgrid__count { flex: 1; font-size: 11.5px; color: var(--text-dim); }
.pgrid__bar select {
  min-width: 0; max-width: 190px;
  padding: 4px 7px; font: inherit; font-size: 12px;
  color: var(--text); background: var(--panel);
  border: 1px solid var(--line); border-radius: var(--radius-s);
}
.pgrid__bar select option { background: var(--panel); color: var(--text); }
.pgrid__scroll { overflow: auto; }
.pgrid { width: 100%; border-collapse: collapse; table-layout: fixed; }
/* L'en-tête des jours reste visible pendant qu'on descend les vingt-quatre
   heures : sans cela, arrivé à 19 h on ne sait plus quelle colonne est
   samedi. */
.pgrid thead th {
  position: sticky; top: 0; z-index: 2;
  padding: 5px 2px;
  font-size: 10.5px; letter-spacing: .3px; text-transform: uppercase;
  color: var(--text-dim); background: var(--panel);
  border-bottom: 1px solid var(--line);
  cursor: pointer; user-select: none;
}
.pgrid thead th:hover { color: var(--accent); }
.pgrid__hour {
  position: sticky; left: 0; z-index: 1;
  width: 30px; padding: 0 5px;
  font-size: 10.5px; font-weight: 400; text-align: right;
  color: var(--text-faint); background: var(--panel);
}
.pgrid__cell {
  height: 20px; padding: 0 4px;
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
  font-size: 10.5px; color: var(--text-faint);
  border: 1px solid var(--line);
  cursor: pointer;
}
.pgrid__cell:hover { background: var(--accent-soft); }
.pgrid__cell.is-set { color: var(--text); background: #ffffff0a; }
/* Choisie : le filet, et pas seulement le fond. Une case choisie au milieu de
   quarante cases remplies doit se repérer d'un coup d'oeil. */
.pgrid__cell.is-picked {
  color: var(--accent);
  background: var(--accent-soft);
  box-shadow: inset 0 0 0 1px var(--accent);
}
.pgrid__hint { padding: 9px 12px 12px; margin: 0; }

/* --- les éditeurs d'horloge et de rotation --- */
.pedit__bar {
  display: flex; align-items: center; gap: 8px;
  padding: 9px 12px; border-bottom: 1px solid var(--line);
  background: var(--bg-sunken);
}
.pedit__pick {
  flex: 1; min-width: 0;
  padding: 4px 7px; font: inherit; font-size: 12px; font-weight: 600;
  color: var(--text); background: var(--panel);
  border: 1px solid var(--line); border-radius: var(--radius-s);
}
.pedit__pick option { background: var(--panel); color: var(--text); font-weight: 400; }
.pedit__fields { padding: 12px 14px 0; }
.pedit__fields .field { margin-bottom: 10px; }
.pedit__h {
  margin: 14px 0 7px; padding: 0 14px;
  font-size: 11px; letter-spacing: .3px; text-transform: uppercase;
  color: var(--accent);
}
.pedit__sum {
  margin: 0; padding: 7px 14px;
  font-size: 11px; color: var(--text-dim);
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  background: var(--bg-sunken);
}
.pedit__fields + .pedit__h { margin-top: 4px; }

@media (min-width: 1100px) {
  .pedit__fields { display: grid; grid-template-columns: 1fr 1fr; gap: 0 14px; }
}

/* --- les emplacements d'une horloge --- */
.pslots, .psrcs, .prules { margin: 0; padding: 0; list-style: none; }
.pslot {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  padding: 5px 14px;
  border-bottom: 1px solid var(--line);
}
.pslot:hover { background: #ffffff06; }
/* Le filet de nature, à gauche. Les mêmes trois teintes que le conducteur du
   studio : un emplacement de voix est rose là comme ici. */
.pslot--rotation { box-shadow: inset 2px 0 0 var(--clip-music); }
.pslot--element { box-shadow: inset 2px 0 0 var(--clip-jingle); }
.pslot--vt { box-shadow: inset 2px 0 0 var(--clip-voice); }
.pslot--marqueur { box-shadow: inset 2px 0 0 var(--line); }
.pslot__pos {
  width: 18px; font-size: 10.5px; text-align: right; color: var(--text-faint);
}
.pslot select, .pslot input {
  padding: 3px 6px; font: inherit; font-size: 11.5px;
  color: var(--text); background: var(--panel-2);
  border: 1px solid var(--line); border-radius: var(--radius-s);
}
.pslot select option { background: var(--panel); color: var(--text); }
.pslot__kind { width: 104px; }
.pslot__rot { width: 130px; }
.pslot__id, .pslot__label { flex: 1; min-width: 80px; }
.pslot__sec, .pslot__fix { width: 58px; }
.pslot__acts { display: flex; gap: 2px; margin-left: auto; }

/* --- les rotations, leurs sources et leurs règles --- */
.prots { display: flex; flex-wrap: wrap; gap: 5px; padding: 10px 14px 0; }
.prot {
  display: flex; flex-direction: column; gap: 1px;
  padding: 5px 9px;
  font: inherit; text-align: left;
  color: var(--text); background: var(--panel-2);
  border: 1px solid var(--line); border-radius: var(--radius-s);
  cursor: pointer;
}
.prot:hover { border-color: var(--accent); }
.prot.is-active {
  color: var(--accent); border-color: var(--accent); background: var(--accent-soft);
}
/* Inactive : une rotation déclarée, que les horloges peuvent encore nommer,
   mais qui ne tire plus. Mise en retrait, pas en alerte. */
.prot.is-off { color: var(--text-faint); }
.prot.is-off .prot__name { text-decoration: line-through; }
.prot__name { font-size: 12px; font-weight: 650; }
.prot__meta { font-size: 10.5px; color: var(--text-faint); }

.psrc, .prule {
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  padding: 6px 14px;
  border-bottom: 1px solid var(--line);
}
.psrc select, .psrc input[type="text"],
.prule select, .prule input[type="text"], .prule input[type="number"] {
  padding: 3px 6px; font: inherit; font-size: 11.5px;
  color: var(--text); background: var(--panel-2);
  border: 1px solid var(--line); border-radius: var(--radius-s);
}
.psrc select option, .prule select option { background: var(--panel); color: var(--text); }
.psrc__value { flex: 1; min-width: 110px; }
/* Une source d'exclusion se lit à l'envers des autres : elle retire. Le filet
   d'alerte le dit avant qu'on ne cherche pourquoi le vivier est vide. */
.psrc.is-excl { box-shadow: inset 2px 0 0 var(--warn); }
.prule__kind { width: 150px; }
.prule__val { flex: 1; min-width: 90px; }
.prule__min, .prule__w { width: 62px; }
.prule.is-hard { box-shadow: inset 2px 0 0 var(--warn); }
.prule.is-off { opacity: .5; }
.prule__hint {
  flex: 1 0 100%; margin: 2px 0 0;
  font-size: 10.5px; line-height: 1.4; color: var(--text-faint);
}
.psrc .btn--icon, .prule .btn--icon { margin-left: auto; }

/* --- le vivier résolu --- */
.ppool {
  margin: 12px 0 0; padding: 10px 14px;
  border-top: 1px solid var(--line); background: var(--bg-sunken);
}
.ppool__head { margin: 0 0 6px; font-size: 11.5px; font-weight: 650; color: var(--ok); }
.ppool ul { margin: 0; padding: 0; list-style: none; }
.ppool li {
  display: grid; grid-template-columns: 1fr auto 110px;
  gap: 8px; align-items: baseline;
  padding: 2px 0; font-size: 11px;
}
.ppool__t, .ppool__s {
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
}
.ppool__t { color: var(--text); }
.ppool__d { color: var(--text-dim); font-family: var(--mono); }
.ppool__s { color: var(--text-faint); font-size: 10.5px; }

/* --- le conducteur généré --- */
.pplan__head {
  padding: 10px 14px; border-bottom: 1px solid var(--line);
  background: var(--bg-sunken);
}
.pplan__clock { margin: 0 0 3px; font-size: 12px; font-weight: 650; color: var(--text); }
.pplan__sum { margin: 0; font-size: 11px; color: var(--ok); }
/* Hors tolérance : c'est l'écart qui change de couleur, pas toute l'heure. Un
   conducteur juste dont la durée dérive de quarante secondes reste juste. */
.pplan__head.is-off .pplan__sum { color: var(--warn); }
.pplan__warn {
  margin: 0; padding: 8px 14px 8px 30px;
  font-size: 11px; line-height: 1.5; color: var(--warn);
  border-bottom: 1px solid var(--line);
}
.prows { margin: 0; padding: 0; list-style: none; }
.prow {
  display: grid;
  grid-template-columns: 42px 18px 1fr auto auto auto;
  grid-template-areas: "at ico title fix len cost" ". . why why why why";
  gap: 1px 8px; align-items: center;
  padding: 5px 12px;
  font-size: 12px;
  border-bottom: 1px solid var(--line);
}
.prow:hover { background: #ffffff06; }
.prow__at { grid-area: at; font-size: 11px; color: var(--text-faint); }
.prow__ico { grid-area: ico; display: flex; color: var(--text-dim); }
.prow--rotation .prow__ico { color: var(--clip-music); }
.prow--element .prow__ico { color: var(--clip-jingle); }
.prow--vt .prow__ico { color: var(--clip-voice); }
.prow__title {
  grid-area: title; min-width: 0;
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
}
.prow__fix { grid-area: fix; font-size: 10.5px; color: var(--accent); }
.prow__len { grid-area: len; font-size: 11px; color: var(--text-dim); }
/* Le coût reste pâle : il sert à régler les poids, pas à se lire en conduite.
   En évidence, il ferait croire à une anomalie à chaque ligne. */
.prow__cost {
  grid-area: cost; width: 30px; text-align: right;
  font-size: 10.5px; color: var(--text-faint);
}
.prow__why { grid-area: why; font-size: 10.5px; line-height: 1.4; color: var(--text-faint); }
.prow__tag {
  grid-area: cost; justify-self: end;
  padding: 0 4px; font-size: 9.5px; letter-spacing: .3px; text-transform: uppercase;
  color: var(--warn); border: 1px solid var(--warn);
}
.prow.is-rescued .prow__cost { display: none; }
.prow.is-rescued .prow__why { color: var(--warn); }
/* Non pourvu : l'emplacement RESTE dans la liste, barré, avec son motif. Un
   trou silencieux ne se voit pas, et c'est précisément ce qu'il faut voir
   avant l'antenne. */
.prow.is-missing { background: #ff6b6b0f; }
.prow.is-missing .prow__title { color: var(--warn); text-decoration: line-through; }
.prow.is-missing .prow__why { color: var(--warn); }

@media (max-width: 760px) {
  .prow {
    grid-template-columns: 40px 18px 1fr auto;
    grid-template-areas: "at ico title len" ". . why why";
  }
  .prow__fix, .prow__cost { display: none; }
  .ppool li { grid-template-columns: 1fr auto; }
  .ppool__s { display: none; }
  .pslot__kind, .pslot__rot { width: 100%; }
}

/* ======================================================================
   Panel Automatisation — module Timing
   ======================================================================

   Même parti pris d'accessibilité que la programmation : rien ne se juge à
   la couleur seule. Un écart hors des clous porte une couleur ET son chiffre
   signé ; une durée estimée porte une teinte ET l'étiquette « estimé » ; un
   point fixe verrouillé porte un fond ET le mot « verrouillé ». Un module de
   surveillance se relit sur l'écran mal réglé d'un studio, à 6 h du matin, et
   parfois par quelqu'un qui ne distingue pas le rouge du vert.

   Les trois états d'écart sont nommés par ce qu'ils valent et non par leur
   couleur — `is-ok`, `is-warn`, `is-bad` — pour que changer la palette ne
   demande pas de relire le JavaScript. */

/* --- La liste des modules --------------------------------------------- */
.amod {
  display: grid; grid-template-columns: 20px 1fr auto; gap: 8px;
  align-items: start; width: 100%;
  padding: 8px 10px; margin-bottom: 4px; text-align: left;
  background: var(--panel-2); border: 1px solid var(--line);
  border-radius: var(--radius-s); color: var(--text); cursor: pointer;
}
.amod:hover { border-color: var(--accent-soft); }
.amod.is-active { border-color: var(--accent); background: var(--bg-sunken); }
.amod.is-soon { opacity: .55; cursor: default; }
.amod__ico { width: 18px; height: 18px; margin-top: 1px; color: var(--accent); }
.amod.is-soon .amod__ico { color: var(--text-faint); }
.amod__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.amod__name { font-weight: 600; }
.amod__blurb { font-size: 11.5px; color: var(--text-dim); }
.amod__soon {
  padding: 0 4px; font-size: 9.5px; letter-spacing: .3px; text-transform: uppercase;
  color: var(--text-faint); border: 1px solid var(--line);
}

/* --- La tranche d'heures ----------------------------------------------
   Une heure par ligne, et la barre de dérive part du MILIEU : à gauche
   l'heure est courte, à droite elle déborde. Une barre partant de zéro
   obligerait à lire le signe pour savoir de quel côté on penche, ce qui est
   exactement le coup d'œil qu'on vient chercher ici. */
.ahour {
  display: grid; gap: 8px; align-items: center; width: 100%;
  grid-template-columns: 44px minmax(0, 1fr) 120px 62px;
  padding: 6px 8px; margin-bottom: 3px; text-align: left;
  background: var(--panel-2); border: 1px solid var(--line);
  border-left: 3px solid var(--line);
  border-radius: var(--radius-s); color: var(--text); cursor: pointer;
}
.ahour:hover { border-color: var(--accent-soft); }
.ahour.is-open { background: var(--bg-sunken); border-color: var(--accent); }
.ahour.is-ok   { border-left-color: var(--ok); }
.ahour.is-warn { border-left-color: var(--warn); }
.ahour.is-bad  { border-left-color: #ff6b6b; }
.ahour.is-void { opacity: .6; border-left-color: var(--text-faint); }
.ahour__at { font-weight: 600; color: var(--text-dim); }
.ahour__body { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.ahour__title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ahour__sub { font-size: 11px; color: var(--text-faint); }
.ahour__bar {
  position: relative; height: 8px;
  background: var(--bg-sunken); border-radius: 4px; overflow: hidden;
}
/* Le repère du milieu : sans lui, une barre courte ne dit pas de quel côté
   elle est partie. */
.ahour__bar::before {
  content: ""; position: absolute; left: 50%; top: 0; bottom: 0;
  width: 1px; background: var(--line);
}
.ahour__fill { position: absolute; top: 1px; bottom: 1px; border-radius: 3px; }
.ahour.is-ok   .ahour__fill { background: var(--ok); }
.ahour.is-warn .ahour__fill { background: var(--warn); }
.ahour.is-bad  .ahour__fill { background: #ff6b6b; }
.ahour__drift { justify-self: end; font-weight: 600; }
.ahour.is-ok   .ahour__drift { color: var(--ok); }
.ahour.is-warn .ahour__drift { color: var(--warn); }
.ahour.is-bad  .ahour__drift { color: #ff6b6b; }
/* L'héritage est rare — il n'apparaît que sur une antenne dont les tops ne
   sont pas verrouillés — donc il se pose sous le reste plutôt que de réserver
   une colonne vide à longueur d'écran. */
.ahour__carry {
  grid-column: 2 / -1; font-size: 10.5px; color: var(--warn);
}

/* --- Le verdict d'une heure ------------------------------------------- */
.averdict {
  display: grid; grid-template-columns: auto 1fr; gap: 12px; align-items: center;
  padding: 10px 12px; margin-bottom: 10px;
  background: var(--panel-2); border: 1px solid var(--line);
  border-radius: var(--radius);
}
.averdict.is-ok   { border-color: var(--ok); }
.averdict.is-warn { border-color: var(--warn); }
.averdict.is-bad  { border-color: #ff6b6b; }
.averdict__drift { font-size: 26px; font-weight: 700; line-height: 1; }
.averdict.is-ok   .averdict__drift { color: var(--ok); }
.averdict.is-warn .averdict__drift { color: var(--warn); }
.averdict.is-bad  .averdict__drift { color: #ff6b6b; }
.averdict__body { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.averdict__line { margin: 0; font-size: 12px; }
.averdict__line--dim { color: var(--text-dim); font-size: 11.5px; }

.asec {
  margin: 14px 0 6px; font-size: 10.5px; font-weight: 600;
  letter-spacing: .5px; text-transform: uppercase; color: var(--text-faint);
}

/* --- Les points fixes ------------------------------------------------- */
.aanchor {
  display: grid; grid-template-columns: 62px minmax(0, 1fr) auto auto; gap: 8px;
  align-items: center; padding: 4px 8px; margin-bottom: 3px;
  background: var(--panel-2); border-left: 3px solid var(--line);
  border-radius: var(--radius-s); font-size: 12px;
}
.aanchor.is-ok   { border-left-color: var(--ok); }
.aanchor.is-warn { border-left-color: var(--warn); }
.aanchor__at { color: var(--text-dim); }
.aanchor__title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.aanchor__lock {
  padding: 0 4px; font-size: 9.5px; letter-spacing: .3px; text-transform: uppercase;
  color: var(--text-dim); border: 1px solid var(--line);
}
.aanchor__drift { justify-self: end; font-weight: 600; }
.aanchor.is-warn .aanchor__drift { color: var(--warn); }

/* --- Les avertissements ----------------------------------------------- */
.awarn { margin: 0; padding-left: 18px; font-size: 12px; color: var(--warn); }
.awarn li { margin-bottom: 3px; }

/* --- Le déroulé ------------------------------------------------------- */
.aitem {
  display: grid; gap: 7px; align-items: center;
  grid-template-columns: 62px 16px minmax(0, 1fr) repeat(3, auto) 46px;
  padding: 3px 8px; border-bottom: 1px solid var(--line);
  font-size: 12px;
}
.aitem:last-child { border-bottom: 0; }
.aitem__at { color: var(--text-faint); font-size: 11px; }
.aitem__ico { width: 14px; height: 14px; stroke-width: 2.2; }
.aitem__ico.k-music  { color: var(--clip-music); }
.aitem__ico.k-jingle { color: var(--clip-jingle); }
.aitem__ico.k-voice  { color: var(--clip-voice); }
.aitem__ico.k-ad     { color: var(--text-dim); }
.aitem__ico.k-other  { color: var(--text-dim); }
.aitem__title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.aitem__len { justify-self: end; color: var(--text-dim); }
/* Ce que le fondu avale. En négatif et en teinte faible : c'est du temps que
   l'heure ne consomme pas, et c'est l'écart entre les deux hypothèses de
   mesure — celle qui donnait trois minutes de trop par heure. */
.aitem__ovl { color: var(--accent); font-size: 10.5px; }
.aitem__fix { color: var(--warn); font-size: 10.5px; }
.aitem__tag {
  padding: 0 4px; font-size: 9.5px; letter-spacing: .3px; text-transform: uppercase;
  color: var(--clip-voice); border: 1px solid currentColor;
}
.aitem__tag--est { color: var(--text-faint); }
/* Une durée estimée se voit sur la LIGNE entière, parce que c'est la ligne
   entière qui est approximative, pas son étiquette. */
.aitem.is-estimated { background: #ffffff06; }
.aitem.is-estimated .aitem__len { font-style: italic; }
.aitem.is-locked { background: var(--bg-sunken); }
.aitem.is-vt .aitem__title { color: var(--clip-voice); }

/* --- Les corrections proposées ---------------------------------------- */
.aadv {
  padding: 8px 10px; margin-bottom: 8px;
  background: var(--panel-2); border: 1px solid var(--line);
  border-left: 3px solid var(--accent); border-radius: var(--radius-s);
}
.aadv__head {
  display: flex; gap: 8px; align-items: baseline; justify-content: space-between;
  margin-bottom: 4px;
}
.aadv__at { font-weight: 600; }
.aadv__shift { font-size: 15px; font-weight: 700; }
.aadv__shift.is-cut { color: var(--warn); }
.aadv__shift.is-add { color: var(--ok); }
.aadv__why { margin: 0 0 7px; font-size: 11.5px; color: var(--text-dim); }
.aadv__acts { display: flex; gap: 6px; justify-content: flex-end; }

@media (max-width: 760px) {
  .ahour { grid-template-columns: 38px minmax(0, 1fr) 58px; }
  .ahour__bar { display: none; }
  .aitem {
    grid-template-columns: 52px 16px minmax(0, 1fr) 42px;
  }
  .aitem__ovl, .aitem__fix, .aitem__tag { display: none; }
  .aanchor { grid-template-columns: 56px minmax(0, 1fr) auto; }
  .aanchor__lock { display: none; }
}
