/* =============================================================================
   BerryStream — charte graphique
   -----------------------------------------------------------------------------
   Ce fichier est la source unique de verite visuelle. Les deux interfaces
   (page publique et console d administration) l importent et ne redéfinissent
   ni couleur, ni police, ni espacement : toute evolution se fait ici, et se
   répercute partout.

   Principes :
     1. Sober avant spectaculaire. Une interface se regarde pendant des heures.
     2. Le contraste d'abord. Le direct est souvent sur un telephone tenu a
        une main, dans une piece mal eclairee.
     3. Sombre par defaut. Une webinaire se projette souvent dans le noir ; le
        mode sombre est donc la position de repos, le clair reste disponible.
     4. La couleur porte du sens, jamais de la decoration. Le vert signale
        l'eco-conception, l'ambre un avertissement, le rouge une panne. Aucune
        couleur n'est employee uniquement pour « faire joli ».
     5. Un systeme, pas des cas particuliers. Tout composant se compose avec
        les memes primitives.
   ========================================================================== */

/* ------------------------------------------------------------------ jetons */

:root {
  /* Declare explicitement, et pas seulement par defaut : sans cela, un
     navigateur qui n'a jamais vu « data-theme » garde le schema de couleurs
     systeme — les champs de formulaire et les barres de defilement restent
     clairs sur une page sombre. */
  color-scheme: dark;

  /* --- Marques primaries : le fruit, pas l'ecran ----------------------- */
  --berry-100: #f8dbe6;
  --berry-200: #f0b2c9;
  --berry-300: #e37fa3;
  --berry-400: #d4507f;
  --berry-500: #c2255c;   /* accent principal */
  --berry-600: #a11a4e;
  --berry-700: #7d1440;

  /* --- Semantiques ----------------------------------------------------- */
  --ok-400: #34c98a;
  --ok-600: #12a150;
  --warn-400: #f0a63c;
  --warn-600: #c77700;
  --danger-400: #f0736a;
  --danger-600: #d13b3b;
  --info-400: #6f9cf5;
  --info-600: #2563eb;

  /* --- Surfaces (mode sombre, position de repos) ---------------------- */
  --bg: #0e1013;
  --bg-soft: #131519;
  --surface: #171a1f;
  --surface-raised: #1d2128;
  --surface-sunken: #0a0c0e;
  --border: #262b33;
  --border-strong: #38404b;

  /* --- Textes ----------------------------------------------------------- */
  --text: #e9ecf1;
  --text-muted: #98a2b3;
  --text-faint: #6b7482;

  /* --- Rôles ------------------------------------------------------------ */
  --accent: var(--berry-500);
  --accent-hover: var(--berry-400);
  --accent-on: #ffffff;
  --accent-soft: rgba(194, 37, 92, .16);

  --ok: var(--ok-400);
  --warn: var(--warn-400);
  --danger: var(--danger-400);
  --info: var(--info-400);

  /* --- Typographie ------------------------------------------------------- */
  --font-sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  --text-2xs: 0.6875rem;  /* 11 */
  --text-xs: 0.75rem;     /* 12 */
  --text-sm: 0.8125rem;   /* 13 */
  --text-base: 0.875rem;  /* 14 */
  --text-md: 1rem;        /* 16 */
  --text-lg: 1.25rem;     /* 20 */
  --text-xl: 1.75rem;     /* 28 */
  --text-2xl: 2.25rem;    /* 36 */

  --leading-tight: 1.25;
  --leading-normal: 1.55;
  --leading-relaxed: 1.75;

  --weight-normal: 400;
  --weight-medium: 500;
  --weight-bold: 650;

  /* --- Espacement : base 4 px ------------------------------------------- */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;

  /* --- Formes ------------------------------------------------------------ */
  --radius-xs: 6px;
  --radius-sm: 8px;
  --radius-md: 12px;
  --radius-lg: 16px;
  --radius-full: 999px;

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .3);
  --shadow-md: 0 4px 16px rgba(0, 0, 0, .35);
  --shadow-lg: 0 16px 48px rgba(0, 0, 0, .45);

  /* --- Mouvement --------------------------------------------------------- */
  --ease: cubic-bezier(.2, .7, .3, 1);
  --duration-fast: 120ms;
  --duration: 200ms;

  /* --- Structure --------------------------------------------------------- */
  --header-height: 3.5rem;
  --content-max: 78rem;
  --focus-ring: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent);
}

/*
 * Le mode clair inverse les surfaces, jamais les sens.
 *
 * Un seul bloc, declenche par un attribut sur <html>, et non par une media
 * query. C'est ce qui permet d'avoir un bouton : une media query dit ce que
 * veut le systeme, elle ne dit pas ce que veut la personne. Avec les deux, il
 * faudrait ecrire la palette claire deux fois — une fois pour « le systeme
 * prefere clair », une fois pour « la personne a choisi clair » — et les deux
 * listes divergeraient des que quelqu'un oublierait une variable dans l'une des
 * deux.
 *
 * Le choix est donc porte par l'attribut, pose avant le premier affichage par
 * /theme.js. Sans JavaScript, la page reste en mode sombre : c'est une palette
 * qui se lit, pas une page qui casse.
 */
:root[data-theme='light'] {
  color-scheme: light;

  --bg: #f6f7f9;
  --bg-soft: #eef0f3;
  --surface: #ffffff;
  --surface-raised: #ffffff;
  --surface-sunken: #f0f2f5;
  --border: #e0e4ea;
  --border-strong: #c7cdd6;

  --text: #101319;
  --text-muted: #58616f;
  --text-faint: #858d99;

  --accent: var(--berry-600);
  --accent-hover: var(--berry-500);
  --accent-on: #ffffff;
  --accent-soft: rgba(161, 26, 78, .1);

  --ok: var(--ok-600);
  --warn: var(--warn-600);
  --danger: var(--danger-600);
  --info: var(--info-600);

  --shadow-sm: 0 1px 2px rgba(16, 19, 25, .06);
  --shadow-md: 0 4px 16px rgba(16, 19, 25, .1);
  --shadow-lg: 0 16px 48px rgba(16, 19, 25, .14);
}

/* ==========================================================================
   Base
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  min-height: 100vh;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4 {
  margin: 0;
  font-weight: var(--weight-bold);
  line-height: var(--leading-tight);
  letter-spacing: -.01em;
}

p { margin: 0; }

a {
  color: var(--accent);
  text-decoration-color: color-mix(in srgb, var(--accent) 40%, transparent);
  text-underline-offset: 3px;
}

a:hover { color: var(--accent-hover); }

:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--radius-xs);
}

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

[hidden] { display: none !important; }

/* Barres de defilement discretes, mais visibles au survol. */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}

*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border: 3px solid transparent;
  border-radius: var(--radius-full);
  background-clip: content-box;
}
*::-webkit-scrollbar-thumb:hover { background-color: var(--text-faint); background-clip: content-box; }

/* ==========================================================================
   Primitives
   ========================================================================== */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
}

.card--raised { box-shadow: var(--shadow-md); }

.card__header {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
  min-height: 2.75rem;
}

.card__title {
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.card__body { padding: var(--space-4); }
/*
 * Un tableau a sept colonnes ne tient pas dans un telephone de quatre cents
 * pixels : les noms se coupent sur trois lignes, et les actions s'empilent sur
 * trois lignes aussi.
 *
 * Le defilement lateral ne suffit pas : un tableau sans largeur minimale se
 * comprime pour tenir, et « overflow-x » n'a alors rien a faire. Il faut donc
 * les deux — une largeur plancher, et un conteneur qui defile quand elle est
 * atteinte.
 *
 * 36rem reste tres en dessous de la largeur utile sur un ecran d'ordinateur :
 * la contrainte ne mord que sur un telephone, ou le defilement horizontal est
 * attendu et naturel.
 */
.card__body--flush { padding: 0; overflow-x: auto; }
.card__body--flush > .table { min-width: 36rem; }

.eyebrow {
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.muted { color: var(--text-muted); }
.faint { color: var(--text-faint); }
.mono { font-family: var(--font-mono); font-size: var(--text-sm); }
/*
 * Suite d'etapes numerotees.
 *
 * « list-style » est remis a none et le numero vient du pseudo-element : la
 * puce native se place au milieu de la premiere ligne en mode centree, et son
 * nombre ne se colore pas comme le reste du texte.
 */
.steps {
  list-style: none;
  counter-reset: etape;
  display: grid;
  gap: var(--space-3);
  max-width: 62ch;
  margin: 0;
  padding: 0;
}

.steps > li {
  counter-increment: etape;
  display: grid;
  grid-template-columns: 1.75rem minmax(0, 1fr);
  gap: var(--space-3);
  align-items: start;
  font-size: var(--text-sm);
  line-height: var(--leading-relaxed);
}

.steps > li::before {
  content: counter(etape);
  display: grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: var(--radius-full);
  background: var(--accent-soft);
  color: var(--accent);
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  font-variant-numeric: tabular-nums;
}

.steps code { font-family: var(--font-mono); font-size: var(--text-xs); }

.nowrap { white-space: nowrap; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.numeric { font-variant-numeric: tabular-nums; }

/* ==========================================================================
   Boutons
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: 2.25rem;
  padding: 0 var(--space-4);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface-raised);
  color: var(--text);
  font: inherit;
  font-size: var(--text-base);
  font-weight: var(--weight-medium);
  cursor: pointer;
  white-space: nowrap;
  /*
   * Un lien qui porte le style d'un bouton cesse d'etre un lien.
   *
   * `a` garde le soulignement du navigateur, et .btn ne l'annulait pas : un
   * <a class="btn"> gardait donc son trait, avec la marge, le fond et la hauteur
   * d'un bouton autour. C'etait visible dans l'onglet Invites — « Ouvrir » et
   * « Ouvrir la salle de controle » — et partout ailleurs ou un lien se deguise
   * en bouton, sans qu'aucun autre bouton de la console soit touche.
   *
   * Un bouton reste un bouton, meme lorsqu'il ouvre un onglet : c'est ce que le
   * style doit dire, et non la balise qui le porte.
   */
  text-decoration: none;
  transition: background var(--duration-fast) var(--ease),
              border-color var(--duration-fast) var(--ease),
              color var(--duration-fast) var(--ease),
              transform var(--duration-fast) var(--ease);
}

.btn:hover:not(:disabled) { background: var(--bg-soft); border-color: var(--text-faint); }
.btn:active:not(:disabled) { transform: translateY(1px); }
.btn:disabled { opacity: .45; cursor: not-allowed; }

/*
 * Bouton de theme.
 *
 * Un carre de deux fois la hauteur du texte, sans libelle : l'icone dit la
 * position — un soleil signifie « il fait sombre, voici comment voir plus clair ».
 * Le libelle reste dans « aria-label », et il annonce le geste plutot que
 * l'etat : « passer en mode clair » se comprend sans avoir a se rappeler ou on en
 * est.
 *
 * Les deux icones sont dans le meme element et l'une des deux est masquee. Les
 * alterner en JavaScript ferait clignoter la zone de clic et laisserait un
 * element sans largeur pendant une image.
 */
.btn--icon {
  width: 2.25rem;
  min-width: 2.25rem;
  padding: 0;
  color: var(--text-muted);
}

.btn--icon:hover { color: var(--text); }
.btn--icon svg { width: 1.125rem; height: 1.125rem; display: block; }

/* L'icone montre l'action possible : la lune quand on est en clair, le soleil
   quand on est dans le sombre. */
.btn--icon [data-theme-icon='sun'] { display: none; }
:root[data-theme='light'] .btn--icon [data-theme-icon='sun'] { display: block; }
:root[data-theme='light'] .btn--icon [data-theme-icon='moon'] { display: none; }

.btn--primary {
  background: var(--accent);
  border-color: transparent;
  color: var(--accent-on);
}
.btn--primary:hover:not(:disabled) { background: var(--accent-hover); border-color: transparent; }

/*
 * Un bouton fantome garde une BORDURE.
 *
 * Il etait `border-color: transparent` : donc ni fond, ni contour, ni rien qui
 * distingue le texte d'un bouton. Avec `btn--sm` — 28 px de haut, 12 px de marge
 * — il se lisait comme une etiquette posee entre deux vrais boutons. Sur la
 * ligne d'actions d'une fiche, « Copier la source », « Ouvrir » et « Nouveau
 * lien » ne se voyaient plus du tout ; « Copier le lien d invite » et « Retirer »,
 * eux, ont un fond ou un contour, et c'est l'echelle a laquelle on comparait.
 *
 * La bordure est donc celle que `.btn` declare deja, et le fond reste
 * transparent : le fantome demeure plus leger que le bouton par defaut, ce qui
 * garde la hierarchie — principal plein, defaut plein et neutre, fantome
 * contour et neutre, danger contour rouge.
 */
.btn--ghost { background: transparent; border-color: var(--border-strong); color: var(--text-muted); }
.btn--ghost:hover:not(:disabled) { background: var(--surface-raised); border-color: var(--text-faint); color: var(--text); }

.btn--danger { background: transparent; border-color: var(--danger); color: var(--danger); }
.btn--danger:hover:not(:disabled) { background: color-mix(in srgb, var(--danger) 12%, transparent); }

.btn--sm { min-height: 1.75rem; padding: 0 var(--space-3); font-size: var(--text-sm); }
.btn--block { width: 100%; }

/* ==========================================================================
   Champs
   ========================================================================== */

.field { display: grid; gap: var(--space-1); }

.field__label {
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  color: var(--text-muted);
}

/*
 * Formulaire en grille.
 *
 * Un formulaire de création tient cinq champs : login, nom, contact, rôle,
 * mot de passe. Les empiler en pleine largeur laisserait des lignes d'une
 * cinq-centimètre de haut et obligerait à faire défiler ; les mettre bout à
 * bout les rendrait illisibles dès que le nom est long.
 *
 * La grille s'adapte seule à la largeur : deux colonnes sur un écran
 * d'ordinateur, une seule sur un téléphone, sans media query ni classe
 * variante. Le bouton reste en dehors des colonnes — aligné sur la fin de la
 * grille, il ne se retrouve jamais au milieu d'une ligne sur un écran étroit.
 */
.form-grid {
  display: grid;
  gap: var(--space-3) var(--space-4);
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  align-items: end;
}

.form-grid > .field--submit {
  grid-column: -2 / -1;
  justify-self: end;
}

/* Sous deux colonnes, le bouton reprend toute la largeur : aligné à droite sur
   une demi-largeur, il ressemblerait à un bouton flottant sans propriétaire. */
@media (max-width: 34rem) {
  .form-grid > .field--submit { grid-column: 1 / -1; justify-self: stretch; }
  .form-grid > .field--submit .btn { width: 100%; }
}

/* Une adresse à copier doit être lisible telle quelle : c'est un caractère, pas
   une phrase. */
.copy {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-soft);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  overflow-wrap: anywhere;
}

.copy code { flex: 1; min-width: 0; }

.input, .select, .textarea {
  width: 100%;
  min-height: 2.25rem;
  padding: 0 var(--space-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface-sunken);
  color: var(--text);
  font: inherit;
  font-size: var(--text-base);
  transition: border-color var(--duration-fast) var(--ease);
}

.textarea { padding: var(--space-2) var(--space-3); resize: vertical; }

.input::placeholder, .textarea::placeholder { color: var(--text-faint); }

.input:hover, .select:hover, .textarea:hover { border-color: var(--text-faint); }

.input:focus, .select:focus, .textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.select {
  appearance: none;
  padding-right: var(--space-6);
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 1rem center, right .65rem center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}

.input-group { display: flex; gap: var(--space-2); }
.input-group .input { flex: 1; }

/* ==========================================================================
   Onglets
   ========================================================================== */

.tabs {
  display: flex;
  gap: var(--space-1);
  overflow-x: auto;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }

.tab {
  position: relative;
  padding: var(--space-2) var(--space-3);
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-muted);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  cursor: pointer;
  white-space: nowrap;
  transition: color var(--duration-fast) var(--ease), background var(--duration-fast) var(--ease);
}

.tab:hover { color: var(--text); background: var(--surface-raised); }

.tab[aria-selected="true"] { color: var(--text); background: var(--accent-soft); }

/* ==========================================================================
   Badges et etats
   ========================================================================== */

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 0.0625rem var(--space-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-full);
  color: var(--text-muted);
  font-size: var(--text-2xs);
  font-weight: var(--weight-medium);
  letter-spacing: .02em;
  white-space: nowrap;
}

.badge--ok { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 40%, transparent); background: color-mix(in srgb, var(--ok) 10%, transparent); }
.badge--warn { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 40%, transparent); background: color-mix(in srgb, var(--warn) 10%, transparent); }
.badge--danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 40%, transparent); background: color-mix(in srgb, var(--danger) 10%, transparent); }
.badge--info { color: var(--info); border-color: color-mix(in srgb, var(--info) 40%, transparent); background: color-mix(in srgb, var(--info) 10%, transparent); }
.badge--accent { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 40%, transparent); background: var(--accent-soft); }

.dot {
  width: .4375rem;
  height: .4375rem;
  border-radius: var(--radius-full);
  background: currentColor;
  flex: none;
}

.dot--pulse { animation: pulse 2s var(--ease) infinite; }

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: .35; }
}

/* ==========================================================================
   Jauges
   ========================================================================== */

.meter {
  height: .375rem;
  border-radius: var(--radius-full);
  background: var(--surface-sunken);
  overflow: hidden;
}

.meter__fill {
  height: 100%;
  border-radius: inherit;
  background: var(--accent);
  transition: width var(--duration) var(--ease);
}

.meter--ok .meter__fill { background: var(--ok); }
.meter--warn .meter__fill { background: var(--warn); }
.meter--danger .meter__fill { background: var(--danger); }
.meter--info .meter__fill { background: var(--info); }

/* ==========================================================================
   Alertes
   ========================================================================== */

.alert {
  display: flex;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--border);
  border-left: 3px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
}

.alert__body { display: grid; gap: var(--space-1); min-width: 0; }
.alert__title { font-weight: var(--weight-medium); }
.alert__hint { font-size: var(--text-sm); color: var(--text-muted); }

.alert--ok { border-left-color: var(--ok); }
.alert--warn { border-left-color: var(--warn); }
.alert--danger { border-left-color: var(--danger); }
.alert--info { border-left-color: var(--info); }

/* ==========================================================================
   Tableaux
   ========================================================================== */

.table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); }

.table th {
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border);
  color: var(--text-faint);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  letter-spacing: .05em;
  text-transform: uppercase;
  text-align: left;
  white-space: nowrap;
}

.table td {
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}

.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr:hover { background: var(--bg-soft); }
.table .num { text-align: right; font-variant-numeric: tabular-nums; }
.table__actions { display: flex; gap: var(--space-1); justify-content: flex-end; }

/* ==========================================================================
   Blocs de mesure (tableau de bord)
   ========================================================================== */

.stat {
  display: grid;
  gap: var(--space-1);
  padding: var(--space-3) var(--space-4);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}

.stat__value {
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  font-variant-numeric: tabular-nums;
  letter-spacing: -.02em;
}

.stat__label {
  font-size: var(--text-xs);
  color: var(--text-muted);
}

.stat-row {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
}

.measure { display: grid; gap: var(--space-1); }
.measure__head { display: flex; justify-content: space-between; gap: var(--space-3); font-size: var(--text-sm); }
.measure__value { font-variant-numeric: tabular-nums; font-weight: var(--weight-medium); }

/* ==========================================================================
   Etats vides
   ========================================================================== */

.empty {
  display: grid;
  place-items: center;
  gap: var(--space-2);
  padding: var(--space-6) var(--space-4);
  text-align: center;
  color: var(--text-faint);
}

.empty__title { color: var(--text-muted); font-weight: var(--weight-medium); }

.skeleton {
  height: 2.25rem;
  border-radius: var(--radius-sm);
  background: linear-gradient(90deg, var(--surface-raised), var(--bg-soft), var(--surface-raised));
  background-size: 200% 100%;
  animation: shimmer 1.4s linear infinite;
}

@keyframes shimmer {
  from { background-position: 200% 0; }
  to { background-position: -200% 0; }
}

/* ==========================================================================
   Journal
   ========================================================================== */

.log {
  margin: 0;
  padding: var(--space-3);
  max-height: 28rem;
  overflow: auto;
  border-radius: var(--radius-sm);
  background: var(--surface-sunken);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  line-height: var(--leading-relaxed);
  white-space: pre-wrap;
  word-break: break-word;
  color: var(--text-muted);
}

/* ==========================================================================
   Avatars
   ========================================================================== */

/*
 * Un avatar porte soit une initiale, soit une photo.
 *
 * La meme regle sert les deux : un cercle de meme diametre, quelle que soit sa
 * source. C'est ce qui evite qu'une photo se distingue d'une initiale par sa
 * taille — et qu'un participant sans photo paraisse avoir un role different.
 *
 * « object-fit: cover » cadre une image qui n'est pas carree sans la
 * deformer : une photo rectangulaire est rognee, pas allongee. Le cas est
 * deja rare — la carte renvoie un carre de 256 px — mais une etiquette posee
 * par erreur, ou un fichier remplace a la main, ne doivent pas deformer la
 * mise en page.
 */
.avatar {
  display: grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  flex: none;
  border-radius: var(--radius-full);
  background: var(--accent-soft);
  color: var(--accent);
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  text-transform: uppercase;
  object-fit: cover;
}

.avatar--photo {
  background: var(--bg-soft);
  color: var(--text-faint);
}

.avatar--muted { background: var(--surface-raised); color: var(--text-muted); }
.avatar--ok { background: color-mix(in srgb, var(--ok) 14%, transparent); color: var(--ok); }
.avatar--warn { background: color-mix(in srgb, var(--warn) 14%, transparent); color: var(--warn); }

/* --------------------------------------------------------------------------
 * QR code
 *
 * Un code est noir sur fond blanc, quelle que soit la palette. Le laisser
 * heriter du thème le rendrait illisible en sombre — donc inutilisable — et
 * surtout invisible sans que rien ne le signale.
 *
 * Le fond blanc est pose sur le cadre, pas sur l'image : le SVG est genere par
 * le serveur et sa couleur n'est pas sous le controle de la feuille de style.
 * Un liseré de la couleur du texte le detache du fond sombre.
 */
.qr {
  display: block;
  width: 11rem;
  height: auto;
  margin-inline: auto;
  padding: var(--space-2);
  background: #fff;
  border-radius: var(--radius-md);
  box-shadow: 0 0 0 1px var(--border);
}

/* --------------------------------------------------------------------------
 * Adresse longue
 *
 * Un lien d'invité fait une centaine de caractères et ne contient aucun espace :
 * sans traitement, il déborde de sa colonne et pousse la mise en page de côté,
 * ce qui décale tout le panneau. Il est donc coupé, et lisible en entier dans
 * le title — c'est ce que l'utilisateur copie de toute façon.
 */
.wrap {
  display: inline-block;
  max-width: 100%;
  overflow-wrap: anywhere;
  line-height: var(--leading-normal);
}

/* ==========================================================================
   Utilitaires
   ========================================================================== */

.stack { display: grid; gap: var(--space-3); }
.stack--sm { gap: var(--space-2); }
.stack--lg { gap: var(--space-5); }
.row { display: flex; align-items: center; gap: var(--space-3); }

/*
 * Un champ dans une ligne flex doit pouvoir retrecir.
 *
 * Un « input » a une largeur par defaut d'une vingtaine de caracteres, et
 * « min-width: auto » l'empeche de descendre en dessous. Trois champs sur une
 * ligne imposaient donc 736 px de largeur minimale : sur un telephone, la page
 * entiere debordait de 366 px, cartes et tableaux compris, et rien ne pouvait
 * etre defile parce que rien ne pouvait se comprimer.
 *
 * « 1 1 8rem » : ces trois facteurs disent « occupe la place disponible, mais
 * ne descend pas sous 8 rem et ne grossis pas au-dela de la part qui te revient ».
 * Un champ unique garde ainsi sa largeur naturelle dans une ligne large.
 */
.row > .input { flex: 1 1 8rem; min-width: 0; }

/*
 * Sous 34 rem, une ligne passe a la ligne.
 *
 * Trois champs dans 386 px font quatre-vingts pixels chacun : les libelles
 * places sont coupes en deux mots et l'écran ne dit plus ce qu'on attend. Une
 * ligne qui se replie est plus lisible qu'une ligne qui tient mal — et c'est le
 * meme comportement que « .row--wrap », applique automatiquement quand la place
 * manque.
 */
@media (max-width: 34rem) {
  .row { flex-wrap: wrap; }
  .row > .input { flex: 1 1 100%; }
}
.row--between { justify-content: space-between; }
.row--wrap { flex-wrap: wrap; }
.spacer { flex: 1; }

.scroll { overflow-y: auto; }

/* Le mouvement ne doit jamais etre subi. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* ==========================================================================
 * Interface du webinaire
 *
 * Ces regles etaient dans la page d'entree, en ligne. Elles y sont restees
 * jusqu'a ce qu'une deuxieme page ait besoin de la meme disposition — la page
 * de l'intervenant, qui recoit la camera et la discussion dans le meme ecran.
 *
 * Une feuille de style posee sur une seule page n'est pas une charte : elle
 * diverge des autres des la premiere correction, et le resultat est deux pages
 * qui ne se ressemblent plus. La charte est le lieu ou une disposition se
 * corrige une fois.
 * ========================================================================== */
  /* ---------------------------------------------------------------- entree */
  .gate {
    display: grid;
    gap: var(--space-5);
    place-items: center;
    min-height: 100dvh;
    padding: var(--space-5);
  }

  .gate__card { width: min(26rem, 100%); padding: var(--space-6); }

  /* Le bouton de theme se pose a droite de la marque : meme rangee, meme
     hauteur, et la page n'a pas besoin d'un en-tete supplementaire. */
  .gate__brand {
    display: flex;
    align-items: center;
    gap: var(--space-3);
  }

  .gate__brand .eyebrow { white-space: nowrap; }

  .gate__mark {
    /*
     * La marque est le LOGO, pas une lettre.
     *
     * C'etait un carre degrade avec un « B » pose dessus. Le projet a un logo
     * — une baie, trois ondes, une source au centre — et il est en SVG : net a
     * toutes les tailles, quelques kilo-octets. Le carre disparait, le cadre
     * arrondi reste pour que l'image ne flotte pas.
     *
     * `overflow` est ce qui fait suivre l'arrondi a l'image.
     */
    display: grid;
    place-items: center;
    width: 2.75rem;
    height: 2.75rem;
    border-radius: var(--radius-md);
    overflow: hidden;
  }
  .gate__mark img { width: 100%; height: 100%; display: block; }

  /*
   * Le meme espace avant le formulaire que sur la page de connexion.
   *
   * Sans lui, le premier libellé colle a la marque : l'ecart mesure etait de
   * zero pixel. Le role passant en premier champ, c'est lui qui porte ce
   * defaut — avant, le nom du participant occupait cette place et son libelle
   * se confondait moins avec la marque.
   */
  .gate__form { display: grid; gap: var(--space-4); margin-top: var(--space-6); }
  .gate__error { min-height: 1.25rem; color: var(--danger); font-size: var(--text-sm); }
  .gate__foot { text-align: center; font-size: var(--text-sm); }

  /* ------------------------------------------------------------ structure */
  .app { display: flex; flex-direction: column; min-height: 100dvh; }

  .topbar {
    position: sticky;
    top: 0;
    z-index: 20;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-height: var(--header-height);
    padding: 0 var(--space-4);
    border-bottom: 1px solid var(--border);
    background: color-mix(in srgb, var(--bg) 88%, transparent);
    backdrop-filter: blur(12px);
  }

  .topbar__brand { display: flex; align-items: center; gap: var(--space-2); font-weight: var(--weight-bold); }
  .topbar__mark {
    /*
     * Le meme LOGO, en plus petit — 1,5 rem, dans la barre du direct.
     *
     * La regle est la meme qu'a l'entree : le dessin est identique a toutes les
     * tailles, seule la boite change. L'image est bloquee a la taille de la
     * boite pour ne pas deborder sur le bouton Quitter.
     */
    display: grid; place-items: center;
    width: 1.5rem; height: 1.5rem;
    border-radius: var(--radius-xs);
    overflow: hidden;
  }
  .topbar__mark img { width: 100%; height: 100%; display: block; }

  .live {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: 0.0625rem var(--space-2);
    border-radius: var(--radius-full);
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
    color: var(--text-muted);
  }

  .live--on {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 12%, transparent);
  }

  .live--on .dot { animation: pulse 2s var(--ease) infinite; }

  .layout {
    flex: 1;
    display: grid;
    grid-template-columns: minmax(0, 1fr) 22rem;
    gap: var(--space-4);
    align-items: start;
    width: 100%;
    max-width: var(--content-max);
    margin: 0 auto;
    padding: var(--space-4);
  }

  .column { display: grid; gap: var(--space-4); align-content: start; min-width: 0; }

  @media (max-width: 64rem) {
    .layout { grid-template-columns: minmax(0, 1fr); }
  }

  /* ---------------------------------------------------------------- direct */
  .stage { position: relative; background: #000; }
  .stage video { display: block; width: 100%; aspect-ratio: 16 / 9; background: #000; }


  .stage__placeholder {
    position: absolute;
    inset: 0;
    display: grid;
    place-content: center;
    justify-items: center;
    gap: var(--space-3);
    text-align: center;
    padding: var(--space-5);
    color: var(--text-muted);
    background: var(--surface-sunken);
  }

  .captions {
    padding: var(--space-3) var(--space-4);
    border-top: 1px solid var(--border);
    font-size: var(--text-md);
    line-height: var(--leading-relaxed);
    color: var(--text-faint);
    min-height: 3.25rem;
  }

  .captions--on { color: var(--text); }

  /* -------------------------------------------------------------- messages */
  .feed { display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-3); }

  .msg { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--space-2); align-items: start; }
  .msg__name { font-size: var(--text-sm); font-weight: var(--weight-medium); }
  .msg__role { font-size: var(--text-2xs); color: var(--text-faint); }
  .msg__text { overflow-wrap: anywhere; }

  .msg--speaker .msg__name { color: var(--accent); }
  .msg--moderator .msg__name { color: var(--warn); }
  .msg--self { background: var(--accent-soft); border-radius: var(--radius-sm); padding: var(--space-1) var(--space-2); }

  .feed__empty { padding: var(--space-5) var(--space-3); text-align: center; color: var(--text-faint); font-size: var(--text-sm); }

  .composer {
    display: flex;
    gap: var(--space-2);
    padding: var(--space-3);
    border-top: 1px solid var(--border);
  }
  .composer .input { flex: 1; }

  /* -------------------------------------------------------------- questions */
  .questions { display: grid; gap: var(--space-2); padding: var(--space-3); }

  .question {
    display: grid;
    grid-template-columns: 2.25rem minmax(0, 1fr);
    gap: var(--space-3);
    padding: var(--space-3);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
  }

  .question--pinned { border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); background: var(--accent-soft); }

  .vote {
    display: grid;
    place-items: center;
    height: 2.25rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--text-muted);
    font: inherit;
    font-weight: var(--weight-bold);
    font-variant-numeric: tabular-nums;
    cursor: pointer;
  }

  .vote:hover { color: var(--accent); border-color: var(--accent); }

  .question__actions { display: flex; gap: var(--space-1); flex-wrap: wrap; margin-top: var(--space-2); }

  /* -------------------------------------------------------------------- eco */
  .eco { display: grid; gap: var(--space-4); padding: var(--space-4); }
  .eco__headline { display: flex; align-items: baseline; gap: var(--space-2); }
  .eco__figure { font-size: var(--text-2xl); font-weight: var(--weight-bold); letter-spacing: -.03em; }
  .eco__unit { color: var(--text-muted); font-size: var(--text-sm); }

  /* ------------------------------------------------------------------ regie */
  .regie { display: grid; gap: var(--space-4); padding: var(--space-4); }
  .regie__row { display: grid; grid-template-columns: 9rem minmax(0, 1fr) 3rem; align-items: center; gap: var(--space-3); }
  .regie__row output { font-variant-numeric: tabular-nums; color: var(--text-muted); text-align: right; }
  .regie input[type=range] { width: 100%; accent-color: var(--accent); }
