/* ══════════════════════════════════════════════════════════════════════════
   nexus — la feuille de style.

   ── La direction ─────────────────────────────────────────────────────────
   Poste de commandement. Noir d'armurerie, rouge de sécurité, angles vifs.

   Quatre règles tenues partout, et elles suffisent à faire le style :

   1. RIEN N'EST ARRONDI. 2 px, jamais plus. Le seul rond de l'app est la mire
      du logo — parce qu'une mire est ronde.
   2. LE ROUGE NE DÉCORE PAS. Il ne sort que pour la marque, ce qui est cassé,
      et ce qui n'a pas été vu. Du rouge sur un bouton « enregistrer » et le
      rouge ne veut plus rien dire.
   3. TOUT CE QUI DÉSIGNE EST STENCILÉ. Étiquettes, onglets, sujets :
      capitales condensées, interlettrées. La prose s'écrit normalement.
   4. UN BORD PAR BLOC, PAS PAR LIGNE. Une liste, c'est UN cadre et des traits
      d'un pixel à l'intérieur — jamais dix cadres empilés. Deux bordures qui
      se touchent font 2 px, se voient, et hachent l'écran en confettis.

   ── Les couleurs ─────────────────────────────────────────────────────────
     le noir     #0B0D0E   le fond
     le rouge    #FF2A2A   la marque · ce qui est down · ce qui n'est pas vu
     l'ambre     #FFAA00   une cible, un secret
     le vert     #00D46A   c'est en prod · c'est vu
     le cyan     #4DD2FF   un commit
   ══════════════════════════════════════════════════════════════════════════ */

@font-face {
  font-family: Archivo;
  font-style: normal;
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-display: swap;
  src: url("fonts/archivo.woff2") format("woff2");
}
@font-face {
  font-family: "Martian Mono";
  font-style: normal;
  font-weight: 100 800;
  font-stretch: 75% 112.5%;
  font-display: swap;
  src: url("fonts/martian.woff2") format("woff2");
}

:root {
  --ink:    #0B0D0E;
  --ink-2:  #121516;
  --ink-3:  #191D1F;
  --ink-4:  #232829;
  --black:  #000;      /* la ligne noire du tableau. Un vrai noir, pas un gris. */

  --line:   #262C2E;
  --line-2: #3A4245;

  --chalk:  #EDF1F2;
  --dim:    #97A2A6;
  --dim-2:  #6B7679;

  --rd:      #FF2A2A;
  --rd-2:    #FF5C5C;
  --rd-soft: rgba(255, 42, 42, .12);

  --am:      #FFAA00;
  --am-soft: rgba(255, 170, 0, .12);

  --gr:      #00D46A;
  --gr-soft: rgba(0, 212, 106, .12);
  --gr-ink:  #002A15;

  --cy:      #4DD2FF;
  --cy-soft: rgba(77, 210, 255, .12);

  --dm:      #97A2A6;

  --r: 2px;            /* LE rayon. Il n'y en a qu'un. */

  /* LA hauteur d'un contrôle d'en-tête. Le sélecteur de membre et le bouton
     rouge se calculaient chacun leur hauteur à partir de leurs paddings : 36,8
     contre 39,5. Deux pixels et demi, invisibles à l'œil nu mais suffisants
     pour que la ligne ne soit plus une ligne. Une hauteur qu'on additionne est
     une hauteur qu'on rate ; celle-ci, on la déclare. */
  --ctl-h: 40px;

  /* Les deux en-têtes de la page notes (liste « NOTES » + détail « titre /
     enregistré ») partagent CETTE hauteur, pour la cohérence. */
  --note-hd: 46px;

  --ease: cubic-bezier(.2, 0, 0, 1);
  --fast: 100ms;
  --med:  180ms;

  --font: Archivo, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --mono: "Martian Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;

  --side: 230px;
}

/* ══════════════════════════════════════════════════════════════════════════
   RESET
   ══════════════════════════════════════════════════════════════════════════ */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }

body {
  background: var(--ink);
  color: var(--chalk);
  font-family: var(--font);
  font-weight: 400;
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  min-height: 100dvh;
}

button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; background: none; border: none; }
textarea { resize: vertical; }
img, svg { display: block; max-width: 100%; }
a { color: var(--rd); text-decoration: none; }
a:hover { color: var(--rd-2); }

:focus-visible { outline: 2px solid var(--rd); outline-offset: 1px; }
:focus:not(:focus-visible) { outline: none; }
/* La sélection : franchement visible. Le rouge doux se confondait avec le fond ;
   un rouge plein à demi-opaque se voit d'un coup d'œil et le texte reste blanc
   par-dessus. */
::selection { background: rgba(255, 42, 42, .55); color: #fff; }
.note-ed ::selection { background: rgba(255, 42, 42, .55); color: #fff; }

/* ══════════════════════════════════════════════════════════════════════════
   LE STENCIL
   ══════════════════════════════════════════════════════════════════════════ */
.eyebrow, .lbl, .nav-l, .sub-l, .blk-h, .board-k, .note-item-k, .seg, .dd-o, .vault-n, .vmem {
  font-weight: 700;
  font-stretch: 68%;
  text-transform: uppercase;
  letter-spacing: .13em;
  font-size: 12px;
  line-height: 1.4;
}
.eyebrow { color: var(--dim-2); }

/* ── LES COFFRES (page admin) ──────────────────────────────────────────────
   Une carte par coffre. Le geste est l'ACCÈS : des pastilles, chacune à la
   couleur de son porteur — pleine quand l'accès est ouvert, creuse sinon. La
   couleur (--c) est posée en style par le JS, jamais dans une règle : une teinte
   est une clé, jamais une couleur. */
/* Les coffres : PLEINE LARGEUR de la page (plus de cap 720px). */
.vaults { display: flex; flex-direction: column; gap: 10px; width: 100%; }
/* Une carte = un bouton : tout le bloc (hauteur ET largeur) ouvre les réglages. */
.vault-c {
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--ink); padding: 14px 16px; width: 100%; text-align: left; display: block;
  transition: background var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.vault-c:hover { background: var(--ink-2); border-color: var(--line-2); }
.vault-c:hover .vault-gear { color: var(--chalk); }
.vault-gear { flex: none; display: inline-flex; color: var(--dim-2);
  transition: color var(--fast) var(--ease); }
.vault-gear .ico { width: 18px; height: 18px; }
.vault-hd { display: flex; align-items: center; gap: 10px; }
.vault-n { font-size: 13px; color: var(--chalk); }
.vault-k { font-family: var(--mono); font-stretch: 75%; font-size: 10px; color: var(--dim-2); }
.vault-note { color: var(--dim); font-size: 12.5px; line-height: 1.55; margin: 8px 0 0; }
.vault-c .vault-note:first-child { margin-top: 0; }
/* La bande de STATS d'un coffre : des tuiles icône + nombre, en bas de la carte. */
.vault-stats { display: flex; flex-wrap: wrap; gap: 6px 14px; margin-top: 12px;
  padding-top: 10px; border-top: 1px solid var(--line); }
.vault-stat { display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--mono); font-stretch: 75%; font-size: 12px; color: var(--chalk); }
.vault-stat-i { display: inline-flex; color: var(--dim-2); }
.vault-stat-i .ico { width: 14px; height: 14px; }
.vmems { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
/* Une pastille = un accès. Éteinte : creuse, grise. Allumée : la couleur du
   membre, posée doucement en fond, la puce pleine. Le tout reste en coins 2px. */
.vmem {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 6px 10px; border: 1px solid var(--line); border-radius: var(--r);
  background: none; color: var(--dim); cursor: pointer;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease),
              border-color var(--fast) var(--ease);
}
.vmem:hover { border-color: var(--dim-2); color: var(--chalk); }
.vmem-d {
  width: 9px; height: 9px; border-radius: 50%;
  border: 1.5px solid var(--c); background: none;
}
.vmem.on {
  color: var(--chalk); border-color: color-mix(in srgb, var(--c) 55%, transparent);
  background: color-mix(in srgb, var(--c) 15%, transparent);
}
.vmem.on .vmem-d { background: var(--c); }
/* Sur la CARTE, les membres sont en LECTURE seule (pas de bascule) : ni curseur
   main, ni survol qui promet un clic. On ajoute/retire dans les réglages. */
.vmem.read { cursor: default; }
.vmem.read:hover { border-color: color-mix(in srgb, var(--c) 55%, transparent); }

/* Les réglages d'un coffre (popup) : même rythme que le reste, la poubelle en bas. */
.vault-set { display: flex; flex-direction: column; gap: 16px; }
.vault-set .field { margin-bottom: 0; }
.vault-danger { display: flex; }
.vault-danger .btn { justify-content: flex-start; }

/* ── LA SURVEILLANCE (Phase 4) ─────────────────────────────────────────────
   Un tableau de bord sobre : résumé par personne, puis le fil des actions. Rien
   d'arrondi, la couleur du membre en pastille, l'IP et l'op en mono. */
/* Plus de séparateur avec les coffres : juste un peu d'air au-dessus. */
.surv { margin-top: 22px; }
.surv-hd { display: flex; flex-direction: column; gap: 4px; margin-bottom: 12px; }
.surv-sum { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
.surv-u { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.surv-name, .surv-who {
  position: relative; padding-left: 14px; color: var(--chalk);
  font-stretch: 70%; text-transform: uppercase; letter-spacing: .04em; font-size: 12.5px;
}
.surv-name::before, .surv-who::before {
  content: ""; position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  width: 8px; height: 8px; border-radius: 50%; background: var(--c);
}
.surv-meta { color: var(--dim); font-size: 12px; }
.surv-list {
  display: grid; grid-template-columns: auto auto 1fr auto auto; gap: 3px 12px;
  align-items: baseline; max-height: 340px; overflow: auto;
  border: 1px solid var(--line); border-radius: var(--r); padding: 10px 12px;
}
.surv-row { display: contents; }
.surv-when { color: var(--dim-2); font-size: 11px; white-space: nowrap; }
.surv-op { color: var(--dim); font-size: 11.5px; }
.surv-dev { color: var(--dim); font-size: 11.5px; white-space: nowrap; }
.surv-ip { color: var(--dim-2); font-size: 11px; white-space: nowrap; }
@media (max-width: 640px) {
  .surv-list { grid-template-columns: 1fr auto; }
  .surv-dev, .surv-ip { display: none; }
}

/* ══ LA PAGE MEMBRES ═══════════════════════════════════════════════════════ */
/* La loupe DANS le champ : superposée à gauche, sans prendre les clics. */
.mem-q-w { position: relative; display: flex; align-items: center; }
.mem-q-i { position: absolute; left: 9px; display: flex; color: var(--dim-2); pointer-events: none; }
.mem-q-i .ico { width: 15px; height: 15px; }
/* Cale sur --ctl-h : sans hauteur, un `.input` fait ~42px (padding 10 + police
   14,5) et poussait l'en-tête Membres 2px plus haut que les autres pages admin
   (dont les boutons plafonnent à --ctl-h) → le titre sautait au changement de
   page. Padding vertical à 0 pour tenir dans les 40px (border-box). */
/* `input.mem-q` (spécificité 0,1,1) bat `.input` (0,1,0) déclaré plus bas : sinon
   son `padding: 10px 12px` réinitialisait le padding-left qui laisse la place à la
   loupe (la loupe se retrouvait sous le texte). */
input.mem-q { width: min(240px, 46vw); height: var(--ctl-h); padding: 0 12px 0 30px; }
/* Pleine largeur : le tableau des membres remplit la page (plus de cap 1000px). */
.mem-wrap { width: 100%; }
.mem-load, .mem-none {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 40px; color: var(--dim-2);
}

/* La LISTE des membres — le composant `.team` des Réglages, pleine largeur : une
   ligne cliquable par membre, l'engrenage à droite. Le détail est dans la popup. */
.mem-list { border: 1px solid var(--line); border-radius: var(--r);
  background: var(--ink-2); overflow: hidden; }
.mem-list .mem-line { border-radius: 0; border-top: 1px solid var(--line); }
.mem-list .mem-line:first-child { border-top: none; }
.mem-line { width: 100%; text-align: left; }
.mem-line:hover { background: var(--ink-3); }
.mem-line.banned { opacity: .6; }
.mem-line-l { gap: 10px; padding: 12px 14px; }
.mem-av { width: 10px; height: 10px; border-radius: 50%; flex: none; }
.mem-av.on { animation: live 2.4s ease-in-out infinite; }
.mem-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-weight: 600; color: var(--chalk); }
.mem-tag {
  flex: none; font-size: 9.5px; font-weight: 700; font-stretch: 70%;
  text-transform: uppercase; letter-spacing: .08em;
  color: var(--rd-2); border: 1px solid rgba(255,42,42,.32); border-radius: var(--r);
  padding: 1px 5px;
}
.mem-line-st { flex: none; color: var(--dim-2); font-size: 12px; white-space: nowrap; }
.mem-line-st.on { color: var(--dim); }

/* ── LE TABLEAU DES MEMBRES — tout centralisé ── */
.mem-tbl-w { border: 1px solid var(--line); border-radius: var(--r);
  background: var(--ink-2); overflow-x: auto; }
.mem-tbl { width: 100%; border-collapse: collapse; font-size: 13px; }
.mem-tbl th {
  text-align: left; padding: 9px 12px; white-space: nowrap;
  color: var(--dim-2); font-size: 10px; font-weight: 700; font-stretch: 70%;
  text-transform: uppercase; letter-spacing: .08em;
  border-bottom: 1px solid var(--line); background: var(--ink);
  position: sticky; top: 0;
}
.mem-tbl th.num, .mem-tbl td.num { text-align: right; font-variant-numeric: tabular-nums; }
.mem-tbl td { padding: 9px 12px; border-top: 1px solid var(--line); white-space: nowrap;
  color: var(--chalk); }
.mem-tbl tbody tr { cursor: pointer; transition: background var(--fast) var(--ease); }
.mem-tbl tbody tr:hover { background: var(--ink-3); }
.mem-tbl tbody tr.banned { opacity: .55; }
.mem-tbl tbody tr:focus-visible { outline: 2px solid var(--rd); outline-offset: -2px; }
.mem-td-n { display: flex; align-items: center; gap: 8px; }
.mem-td-n .mem-name { font-weight: 600; }
.mem-tbl td.num { color: var(--chalk); }
/* Un zéro compte pour rien : on l'atténue pour que les vrais nombres ressortent. */
.mem-tbl td.num:not(:empty) { color: var(--chalk); }
.mem-td-geo { color: var(--dim); }
.mem-td-seen { color: var(--dim-2); }
.mem-td-seen.on { color: var(--gr); }
.mem-gear { flex: none; display: inline-flex; color: var(--dim-2);
  transition: color var(--fast) var(--ease); }
.mem-gear .ico { width: 18px; height: 18px; }
.mem-line:hover .mem-gear { color: var(--chalk); }
.mem-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; background: var(--line-2); }

/* La popup de gestion : un spacing cohérent partout (plus de séparateur). */
.mem-edit { display: flex; flex-direction: column; gap: 16px; }
.mem-edit .field { margin-bottom: 0; }
/* Le résumé (connexion, pays, créations) en tête de popup. */
.mem-info { display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 10px 12px; background: var(--ink); border: 1px solid var(--line); border-radius: var(--r); }
.mem-info-st { display: inline-flex; align-items: center; gap: 7px; font-size: 12.5px; color: var(--dim-2); }
.mem-info-st.on { color: var(--chalk); }
.mem-info-st.on .mem-dot { animation: live 2.4s ease-in-out infinite; }
.mem-info-geo { font-family: var(--mono); font-stretch: 75%; font-size: 11.5px; color: var(--dim); }
.mem-info-c { display: inline-flex; gap: 12px; }
.mem-info-cc { display: inline-flex; align-items: center; gap: 3px; font-size: 12px; color: var(--dim); }
.mem-info-cc .ico { width: 13px; height: 13px; color: var(--dim-2); }
.mem-clr { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.mem-sw {
  width: 24px; height: 24px; border-radius: 50%; flex: none;
  box-shadow: 0 0 0 2px var(--ink-2), 0 0 0 2px var(--ink-2);
  transition: transform var(--fast) var(--ease);
}
.mem-sw:hover { transform: scale(1.12); }
.mem-sw.on { box-shadow: 0 0 0 2px var(--ink-2), 0 0 0 4px var(--chalk); }
.mem-acc { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.mem-vac {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 9px; border: 1px solid var(--line); border-radius: var(--r);
  color: var(--dim); font-size: 12px;
  transition: border-color var(--fast) var(--ease), color var(--fast) var(--ease);
}
.mem-vac:hover { border-color: var(--dim-2); color: var(--chalk); }
.mem-vac-d { width: 7px; height: 7px; border-radius: 50%; background: var(--line-2); flex: none; }
.mem-vac.on { border-color: var(--gr); color: var(--chalk); }
.mem-vac.on .mem-vac-d { background: var(--gr); }
/* Les actions lourdes : plus de séparateur ni de marge à part — le `gap` de
   `.mem-edit` les pose au même rythme que le reste. */
.mem-danger { display: flex; flex-direction: column; gap: 8px; }
.mem-danger .btn { justify-content: flex-start; }

@media (max-width: 720px) {
  .mem-line-st { display: none; }
}

/* Le sélecteur de coffre, dans la sidebar : le même segment que le filtre
   membre, mais sur toute la largeur de la colonne, et il défile s'il déborde. */
.vault-seg { display: flex; margin: 0 0 4px; }

.mono, code, .num, .time { font-family: var(--mono); font-stretch: 75%; font-variant-numeric: tabular-nums; }

h1 {
  font-size: 24px;
  font-weight: 800;
  font-stretch: 70%;
  text-transform: uppercase;
  letter-spacing: .1em;
  line-height: 1.2;
}
h2 { font-size: 15px; font-weight: 700; font-stretch: 86%; line-height: 1.3; }
h3 { font-size: 14px; font-weight: 600; line-height: 1.35; }

.trunc { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dim { color: var(--dim); }
.sp { flex: 1; }

.sr { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden;
      clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.skip {
  position: fixed; top: 6px; left: 6px; z-index: 100;
  background: var(--rd); color: #fff; padding: 8px 14px; border-radius: var(--r);
  font-weight: 700; transform: translateY(-200%); transition: transform var(--med) var(--ease);
}
.skip:focus { transform: translateY(0); }

.ico { width: 20px; height: 20px; flex: none; fill: currentColor; }

/* L'enveloppe d'une icône CENTRE son icône.
   ─────────────────────────────────────────────────────────────────────────
   align-items/justify-content ne sont pas décoratifs ici. Cette règle
   (element + :has = spécificité 0-1-1) écrase le `display:grid;place-items`
   d'une classe comme .ev-ico (0-1-0) : sans le centrage POSÉ ICI, toutes les
   pastilles d'icône collaient leur icône en haut à gauche, et l'app entière
   avait l'air cassée. */
span:has(> .ico) {
  display: inline-flex; flex: none;
  align-items: center; justify-content: center;
}

/* ══════════════════════════════════════════════════════════════════════════
   MOUVEMENT

   Aucune animation d'ENTRÉE d'écran. C'est délibéré, et c'est une correction :
   un `animation: fade` sur le contenu rejoue à CHAQUE rendu — or l'app se
   redessine dès que quelqu'un coche quelque chose. Résultat : un battement noir
   à chaque clic, qui donnait l'impression d'un rechargement de page. Une app
   locale ne « charge » jamais ; elle ne doit donc jamais faire semblant.

   Ne restent que les mouvements qui DISENT quelque chose : le point qui
   clignote (pas vu), l'anneau du micro (ça enregistre), les confettis (c'est
   validé). Trois, et pas un de plus.
   ══════════════════════════════════════════════════════════════════════════ */
@keyframes rise { from { opacity: 0; transform: translateY(4px); } }
@keyframes beat { 50% { opacity: .35; } }
@keyframes recRing {
  0%   { box-shadow: 0 0 0 0 var(--rd-soft); }
  100% { box-shadow: 0 0 0 14px transparent; }
}
@keyframes pop { 50% { transform: scale(1.35); } }
@keyframes confetti {
  0%   { opacity: 1; transform: translate(0, 0) scale(1); }
  100% { opacity: 0; transform: translate(var(--dx), var(--dy)) scale(.3); }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* ══════════════════════════════════════════════════════════════════════════
   BOUTONS
   ══════════════════════════════════════════════════════════════════════════ */
/* `min-height` et pas `height` : un bouton dont le texte passe à la ligne doit
   pouvoir grandir. On pose un plancher commun, pas un carcan. */
.btn, .dd-b, .seg-group { min-height: var(--ctl-h); }
/* Sauf eux : `btn-sm` et `bare` existent précisément pour être plus petits.
   Le plancher de 40 px sert à aligner les contrôles d'un EN-TÊTE de page ; le
   tri des notes vit dans une bande de 230 px et n'a rien à y aligner. */
.btn-sm, .dd-b.bare { min-height: 0; }

.btn {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 9px 14px;
  background: var(--ink-2);
  border: 1px solid var(--line);
  border-radius: var(--r);
  color: var(--chalk);
  font-size: 13px; font-weight: 700; font-stretch: 74%;
  text-transform: uppercase; letter-spacing: .09em;
  white-space: nowrap;
  transition: background var(--fast) var(--ease), border-color var(--fast) var(--ease),
              color var(--fast) var(--ease);
}
.btn:hover { background: var(--ink-3); border-color: var(--line-2); }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: .35; pointer-events: none; }
.btn .ico { width: 17px; height: 17px; }

.btn-primary { background: var(--rd); border-color: var(--rd); color: #fff; }
.btn-primary:hover { background: var(--rd-2); border-color: var(--rd-2); }
.btn-go { background: var(--gr); border-color: var(--gr); color: var(--gr-ink); }
.btn-go:hover { filter: brightness(1.12); }
.btn-danger { color: var(--rd-2); border-color: rgba(255,42,42,.32); }
.btn-danger:hover { background: var(--rd-soft); border-color: var(--rd); }
.btn-ghost { background: transparent; border-color: transparent; color: var(--dim); }
.btn-ghost:hover { background: var(--ink-2); color: var(--chalk); border-color: var(--line); }
.btn-sm { padding: 6px 10px; font-size: 11.5px; }
.btn-block { width: 100%; justify-content: center; }

/* La touche du bouton principal, écrite dessus. Un raccourci qu'on ne voit pas
   est un raccourci que personne n'utilise. */
.kbd {
  display: inline-grid; place-items: center;
  min-width: 17px; height: 17px; padding: 0 4px;
  margin-left: 2px;
  border: 1px solid currentColor; border-radius: var(--r);
  opacity: .5;
  font-family: var(--mono); font-stretch: 75%; font-size: 9px; font-weight: 700;
  letter-spacing: 0;
}

.btn-ico {
  display: inline-grid; place-items: center;
  width: 32px; height: 32px; flex: none;
  border-radius: var(--r);
  color: var(--dim);
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.btn-ico:hover { background: var(--ink-3); color: var(--chalk); }
.btn-ico:active { transform: scale(.94); }
.btn-ico:disabled { opacity: .3; pointer-events: none; }
.btn-ico.danger:hover { background: var(--rd-soft); color: var(--rd-2); }

/* ══════════════════════════════════════════════════════════════════════════
   CHAMPS
   ══════════════════════════════════════════════════════════════════════════ */
.field { display: block; margin-bottom: 14px; }
.lbl { display: block; color: var(--dim); margin-bottom: 6px; }
.field:focus-within .lbl { color: var(--rd); }

.input, .area {
  width: 100%;
  padding: 10px 12px;
  background: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--r);
  color: var(--chalk);
  font-size: 14.5px;
  transition: border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
.input:hover, .area:hover { border-color: var(--line-2); }
.input:focus, .area:focus {
  outline: none; border-color: var(--rd); box-shadow: inset 0 0 0 1px var(--rd);
}
.input::placeholder, .area::placeholder { color: var(--dim-2); }
.err { display: block; margin-top: 6px; color: var(--rd-2); font-size: 13px; font-weight: 600; }

/* ══════════════════════════════════════════════════════════════════════════
   LE MENU DÉROULANT — le nôtre, pas celui du système.

   Un <select> natif ouvre une liste dessinée par macOS : fond clair, police du
   système, coins arrondis. Au milieu d'un poste de commandement noir, c'est un
   morceau d'un autre logiciel qui s'ouvre par-dessus le nôtre.
   ══════════════════════════════════════════════════════════════════════════ */
.dd { position: relative; display: inline-block; }
.dd-b {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 8px 11px;
  background: var(--ink-2); border: 1px solid var(--line); border-radius: var(--r);
  color: var(--chalk);
  font-size: 12.5px; font-weight: 700; font-stretch: 74%;
  text-transform: uppercase; letter-spacing: .09em;
  white-space: nowrap;
  transition: border-color var(--fast) var(--ease);
}
.dd-b:hover { border-color: var(--line-2); }
.dd-b .ico { width: 15px; height: 15px; color: var(--dim); }
.dd-b.on { border-color: var(--rd); }
/* Icône seule, là où la place manque. */
.dd-c { padding: 8px; }
/* `bare` : l'icône, et rien d'autre. Dans un en-tête de 230 px qui ne porte
   qu'un compteur, un bouton encadré pèse plus lourd que ce qu'il commande. Le
   cadre revient au survol — il n'a jamais servi qu'à dire « ça se clique »,
   et c'est au moment où l'on approche qu'on a besoin de l'entendre. */
.dd-b.bare {
  padding: 7px; background: none; border-color: transparent; color: var(--dim);
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.dd-b.bare:hover, .dd-b.bare.on { background: var(--ink-3); color: var(--chalk); }
.dd-b.bare .ico { width: 16px; height: 16px; color: currentColor; }
.dd-c .ico { width: 17px; height: 17px; color: var(--chalk); }
.dd-car { transition: transform var(--fast) var(--ease); }
.dd-b.on .dd-car { transform: rotate(180deg); }

/* Posé sur <body> en position fixe : dans la sidebar, un menu en `absolute`
   était coupé par l'`overflow` du panneau — on n'en voyait que la première
   ligne. Un menu ne peut pas dépendre de la boîte qui le porte. */
.dd-pop {
  position: fixed; z-index: 80;
  min-width: 170px; max-height: 340px; overflow-y: auto;
  background: var(--ink-2);
  border: 1px solid var(--line-2); border-radius: var(--r);
  box-shadow: 0 14px 40px -10px rgba(0,0,0,.9);
  animation: rise 120ms var(--ease);
  padding: 4px;
}
.dd-o {
  display: flex; align-items: center; gap: 8px;
  width: 100%; text-align: left;
  padding: 8px 9px; border-radius: var(--r);
  color: var(--dim);
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.dd-o:hover, .dd-o.cur { background: var(--ink-3); color: var(--chalk); }
.dd-o.on { color: var(--rd); }
.dd-o .ico { width: 16px; height: 16px; }
.dd-o-c { width: 16px; flex: none; }
/* Le TITRE d'un bloc du menu : non cliquable, il nomme et regroupe. Icône explicite
   + libellé en petites capitales. Un filet au-dessus (sauf le premier) sépare les blocs. */
.dd-hdr {
  display: flex; align-items: center; gap: 7px;
  padding: 8px 9px 4px; margin-top: 4px;
  border-top: 1px solid var(--line);
  color: var(--dim-2); font-family: var(--mono); font-stretch: 75%;
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .1em;
}
.dd-hdr:first-child { margin-top: 0; border-top: none; }
.dd-hdr .ico { width: 14px; height: 14px; flex: none; }

/* ══════════════════════════════════════════════════════════════════════════
   LE FILTRE PAR MEMBRE — dans le header, sur toutes les pages.
   Des segments, comme le filtre société de horizon : les cinq prénoms tiennent
   à l'écran, donc on les montre. Un menu déroulant cacherait quatre noms
   derrière un clic pour ne rien gagner.
   ══════════════════════════════════════════════════════════════════════════ */
.seg-group {
  display: inline-flex; align-items: stretch; gap: 2px;
  background: var(--ink-2); border: 1px solid var(--line);
  border-radius: var(--r); padding: 3px;
  max-width: 100%; overflow-x: auto;
}
.seg {
  /* Le segment s'étire sur toute la hauteur du groupe : la cible de clic fait
     40 px de haut au lieu de 25, et personne n'a à viser. */
  display: inline-flex; align-items: center;
  border: none; background: none; color: var(--dim);
  padding: 0 11px; border-radius: var(--r);
  white-space: nowrap;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.seg:hover { color: var(--chalk); background: var(--ink-3); }
.seg.on { background: var(--ink-4); color: var(--chalk); }
/* Plus de rouge sur « moi » : la couleur du membre est posée en style par le
   JS, et `var(--rd)` la volerait. Mon segment se signale par sa graisse. */
.seg.on.me { font-weight: 700; }

/* ── L'ÉQUIPE, DANS LES PARAMÈTRES ─────────────────────────────────────────
   Une ligne par membre : son prénom (modifiable), sa couleur, la poubelle.
   Tout le monde peut tout changer — voir la note dans app.js. */
/* Une rangée par membre, et plus de cadre autour de chacune. Quatre boîtes
   encadrées empilées, c'est quatre fois le même trait pour dire une chose que
   l'alignement dit déjà. Le survol suffit à désigner la ligne qu'on vise. */
.team { display: flex; flex-direction: column; gap: 1px; }
.team-r { border-radius: var(--r); }
.team-r:hover { background: var(--ink-3); }
.team-l { display: flex; align-items: center; gap: 6px; padding: 5px 6px; }
.team-n { flex: 0 1 190px; min-width: 0; }
.team-d { flex: none; width: 10px; height: 10px; border-radius: 50%; }
.team-c { padding: 0 6px 8px; }
.team-add { display: flex; gap: 6px; margin-top: 8px; }
/* Le catalogue d'icônes, dans l'éditeur de type. Une grille qui défile : 69
   icônes en pleine hauteur pousseraient les boutons de la modale hors écran. */
.icos {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(38px, 1fr));
  gap: 2px; max-height: 168px; overflow-y: auto;
  padding: 5px; border: 1px solid var(--line); border-radius: var(--r);
  background: var(--ink);
}
.ico-b {
  display: grid; place-items: center; height: 38px;
  border-radius: var(--r); color: var(--dim);
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.ico-b:hover { background: var(--ink-3); color: var(--chalk); }
.ico-b.on { background: var(--rd-soft); color: var(--rd-2); box-shadow: inset 0 0 0 1px var(--rd); }
.ico-b .ico { width: 19px; height: 19px; }
.team-add .input { flex: 1; }

/* ══════════════════════════════════════════════════════════════════════════
   LA PORTE
   ══════════════════════════════════════════════════════════════════════════ */
.gate { min-height: 100dvh; display: grid; place-items: center; padding: 24px; }
.gate-c { width: 100%; max-width: 340px; }
.gate-logo {
  display: flex; align-items: center; gap: 11px; justify-content: center;
  color: var(--chalk);
  font-size: 28px; font-weight: 800; font-stretch: 70%;
  letter-spacing: .16em; text-transform: uppercase;
}
.gate-logo svg { width: 31px; height: 31px; color: var(--rd); }
.gate-sub { color: var(--dim); font-size: 14px; }
/* ── LE CHAMP DE LA CLÉ ───────────────────────────────────────────────────
   En mono, et espacé : une clé se relit caractère par caractère quand on la
   tape à la main. La chasse fixe est ce qui permet de compter les groupes d'un
   coup d'œil — c'est le seul endroit de la porte où l'on lit des SIGNES et pas
   un mot.

   `.name-in` (le prénom, centré, en capitales de 21 px) est parti avec le
   login par prénom, et `.who-list` / `.who-b` avec la liste des habitants
   affichée à qui frappait. */
.key-w { position: relative; display: flex; align-items: center; }
.key-in {
  font-family: var(--mono); font-stretch: 75%;
  text-align: center; font-size: 15px; letter-spacing: .08em;
  padding: 14px 40px 14px 12px;
}
.key-in::placeholder { color: var(--dim-2); letter-spacing: .08em; }
.key-eye { position: absolute; right: 4px; }

/* La clé, montrée une fois. Elle doit se lire ET se copier sans erreur : on ne
   la relira jamais. */
.key-row { display: flex; gap: 6px; align-items: stretch; }
.key-show {
  flex: 1; min-width: 0;
  font-size: 15px; letter-spacing: .08em; text-align: center;
  color: var(--chalk); background: var(--ink);
  border-color: var(--rd);
}

/* Le mot de la porte. Il n'est plus une ALERTE : il n'y a plus rien à alerter,
   il y a quelque chose à savoir. D'où le gris, et plus l'ambre — le registre
   des couleurs veut dire quelque chose ici, et « attention » n'est plus vrai.
   Réutilisé par la fenêtre de la clé, où l'ambre, lui, est mérité. */
.gate-note {
  margin-top: 18px; padding: 10px 12px;
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--ink-2);
  color: var(--dim); font-size: 12px; line-height: 1.5;
  display: flex; gap: 8px;
}
.gate-note .ico { width: 15px; height: 15px; margin-top: 1px; flex: none; }

/* ══════════════════════════════════════════════════════════════════════════
   LE CHÂSSIS
   ══════════════════════════════════════════════════════════════════════════ */
/* La coquille tient l'écran, exactement. Le SEUL endroit qui défile est le
   contenu — la sidebar, elle, ne bouge jamais.
   ─────────────────────────────────────────────────────────────────────────
   `sticky` ne suffisait pas : la sidebar restait collée en haut, mais son bas
   sortait de l'écran, et il fallait faire défiler LA PAGE pour l'atteindre —
   ce qui déplaçait aussi le contenu. Une barre de navigation qu'on doit
   chercher n'est plus une barre de navigation.

   D'où : la coquille fait 100dvh et ne déborde pas ; la sidebar en prend toute
   la hauteur ; c'est `.main` qui porte le défilement. */
.shell { display: flex; flex-direction: column; height: 100dvh; overflow: hidden; }
/* La RANGÉE est plafonnée à la hauteur dispo (minmax(0,1fr)) — sans ça, la
   grille s'auto-dimensionne au contenu et la sidebar dépasse le viewport (son
   `height:100%` ne plafonne plus), reléguant le mini-lecteur sous le pli. */
.app {
  display: grid; grid-template-columns: var(--side) 1fr; grid-template-rows: minmax(0, 1fr);
  flex: 1; min-height: 0;
}
/* Sidebar REPLIÉE (bouton plein écran du todo) : la grille rend toute la largeur au
   contenu, la sidebar s'efface. Réglage d'écran, retenu par navigateur. */
/* UNE seule piste quand la sidebar est masquée : sinon `main` (seul enfant visible)
   se placerait dans la 1re piste (0px) et disparaîtrait. */
.app.side-off { grid-template-columns: 1fr; }
.app.side-off .side { display: none; }

/* ── L'ANNONCE ────────────────────────────────────────────────────────────
   Au-dessus de TOUT, sidebar comprise. C'est le seul élément qui passe devant
   le châssis — et c'est pour ça qu'il n'y en a qu'une. */
/* La barre entière clique — c'est le seul geste qu'on peut vouloir faire
   dessus. Le texte est centré et seul : une annonce n'a pas besoin qu'on lui
   explique que c'est une annonce, elle est rouge et en travers de l'écran. */
/* La teinte vient d'une CLÉ posée par le JS (`data-tone`), jamais d'une
   couleur écrite dans le document : le pire cas d'une clé inconnue, c'est le
   rouge — la valeur par défaut ci-dessous. Le texte bascule au noir sur les
   fonds clairs : du blanc sur de l'ambre ne se lit pas. */
.notice {
  display: flex; align-items: center;
  width: 100%;
  background: var(--tone, var(--rd)); color: var(--tone-ink, #fff);
  flex: none;
}
.notice[data-tone="ambre"]  { --tone: var(--am); --tone-ink: #0B0D0E; }
.notice[data-tone="vert"]   { --tone: var(--gr); --tone-ink: #0B0D0E; }
.notice[data-tone="cyan"]   { --tone: #22D3EE;  --tone-ink: #0B0D0E; }
.notice[data-tone="violet"] { --tone: #A855F7;  --tone-ink: #fff; }

/* Le choix de la teinte, dans la popup. Chaque teinte porte son nom : une
   rangée de ronds colorés oblige à deviner ce que le vert veut dire. */
.tones { display: flex; flex-wrap: wrap; gap: 4px; }
.tone {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 8px 11px;
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--ink-2); color: var(--dim);
  font-size: 12px; font-weight: 700; font-stretch: 74%;
  text-transform: uppercase; letter-spacing: .07em;
  transition: border-color var(--fast) var(--ease), color var(--fast) var(--ease);
}
.tone:hover { color: var(--chalk); }
.tone.on { border-color: var(--tone); color: var(--chalk); background: var(--ink-3); }
.tone-d {
  flex: none; width: 9px; height: 9px; border-radius: 50%;
  background: var(--tone, var(--rd));
}
.tone.on .tone-d { box-shadow: 0 0 8px var(--tone, var(--rd)); }
/* Le sélecteur de couleur d'un membre pose `--tone` en style : la pastille et
   le liseré s'allument tout seuls, sans une règle par couleur. */
.tone[data-tone="ambre"]  { --tone: var(--am); }
.tone[data-tone="vert"]   { --tone: var(--gr); }
.tone[data-tone="cyan"]   { --tone: #22D3EE; }
.tone[data-tone="violet"] { --tone: #A855F7; }
/* Fine. Elle passe devant TOUT et ne se ferme que par sa croix : ce qui vole de
   la hauteur en haut la vole à la page, sur tous les écrans, tout le temps. Elle
   se remarque parce qu'elle est colorée et posée là, pas parce qu'elle est
   épaisse — l'épaisseur ne rend rien plus lisible passé ce point. */
.notice-b {
  flex: 1; display: flex; align-items: center; justify-content: center; gap: 12px;
  padding: 6px 0;
  transition: filter var(--fast) var(--ease);
}
.notice-b:hover { filter: brightness(1.12); }
.notice-t { font-size: 13px; font-weight: 700; text-align: center; color: inherit; }
.notice-by {
  font-family: var(--mono); font-stretch: 75%; font-size: 10px;
  flex: none; color: rgba(255,255,255,.72);
}
.notice-x {
  flex: none; width: 22px; height: 22px; margin-right: 6px;
  display: grid; place-items: center; border-radius: var(--r);
  color: #fff; opacity: .75;
}
.notice-x:hover { background: rgba(0,0,0,.3); opacity: 1; }
.notice-x .ico { width: 15px; height: 15px; }

/* ── LE BANDEAU QUI DÉFILE ────────────────────────────────────────────────
   Sous l'annonce, au-dessus du châssis. Les trois blocs de la sidebar, mais
   qui passent devant les yeux au lieu d'attendre qu'on aille les lire.

   Le seul mouvement permanent de l'app, et c'est une exception ASSUMÉE à « pas
   d'animation ». La règle visait les animations d'ENTRÉE, qui rejouent à chaque
   rendu et font clignoter l'écran sans rien dire. Ici le mouvement EST le
   message : un bandeau de salle des marchés dit « ça tourne, c'est maintenant ».
   Il ne rejoue pas — il ne s'arrête jamais (voir TICK_T0 dans app.js).

   26 px. Un ticker épais n'est plus un ticker, c'est une barre. */
.tick {
  flex: none;
  height: 26px;
  display: flex; align-items: center;
  overflow: hidden;
  background: var(--ink-2);
  border-bottom: 1px solid var(--line);
}
.tick-run {
  display: flex; align-items: center;
  white-space: nowrap;
  will-change: transform;
  animation-name: tick-run;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  /* durée et décalage posés par app.js : ils dépendent de la largeur du texte */
}
/* -50 % : la seconde copie prend exactement la place de la première. Le tour
   est invisible, et c'est le seul réglage qui compte ici. */
@keyframes tick-run { from { transform: translateX(0); } to { transform: translateX(-50%); } }

.tick-i { display: inline-flex; align-items: center; gap: 8px; padding: 0 20px; }
/* Le trait, pas une puce : c'est le séparateur de toute l'app. */
.tick-i + .tick-i { border-left: 1px solid var(--line-2); }
.tick-ico { display: grid; place-items: center; flex: none; color: var(--tone, var(--dim)); }
.tick-ico .ico { width: 13px; height: 13px; }
.tick-k {
  font-weight: 700; font-stretch: 68%; text-transform: uppercase;
  letter-spacing: .13em; font-size: 10.5px;
  color: var(--tone, var(--dim));
}
.tick-v {
  font-family: var(--mono); font-stretch: 75%; font-size: 11.5px;
  color: var(--dim);
}

/* Reduced motion : on ne fait pas défiler, et la copie de service disparaît —
   elle n'existait que pour le tour. Ce qui dépasse se lit à la molette. */
@media (prefers-reduced-motion: reduce) {
  .tick { overflow-x: auto; }
  .tick-run { animation: none; }
  .tick-i[aria-hidden="true"] { display: none; }
}

.logo-a { display: block; flex: none; }
.logo-a:hover .logo { color: var(--rd); }

/* Le repli des raccourcis, dans les paramètres. */
/* ── LES VOLETS — les paramètres ──────────────────────────────────────────
   Quatre volets, un seul ouvert à la fois, et AUCUN trait entre eux : c'est
   l'espace qui sépare. Un filet gris entre deux blocs déjà distincts ne dit
   rien de plus qu'un blanc, et le poste de commandement en compte assez.

   Ce panneau était une colonne de quatre sections encadrées, toutes déployées,
   chacune précédée d'une phrase d'explication. Il fallait le faire défiler pour
   trouver l'annonce. */
.folds { display: flex; flex-direction: column; }
.fold {
  display: flex; align-items: center; gap: 9px; width: 100%; text-align: left;
  padding: 14px 2px;
  border-radius: var(--r);
}
.fold-car { display: flex; flex: none; color: var(--dim-2); transition: transform var(--fast) var(--ease); }
.fold-car .ico { width: 15px; height: 15px; }
.fold.open .fold-car { transform: rotate(90deg); color: var(--rd); }
.fold .lbl { color: var(--dim); transition: color var(--fast) var(--ease); }
.fold:hover .lbl, .fold.open .lbl { color: var(--chalk); }
/* L'état du volet, à droite : combien de membres, quelle annonce, combien de
   types. C'est lui qui rend le repli acceptable — sans lui, quatre portes
   closes remplaceraient un panneau long, et il faudrait toutes les ouvrir pour
   retrouver ce qu'on cherchait. Ouvert, il n'apprend plus rien : il s'efface. */
.fold-hint {
  font-family: var(--mono); font-size: 11px; font-stretch: 75%;
  color: var(--dim-2);
  max-width: 210px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  transition: opacity var(--fast) var(--ease);
}
.fold.open .fold-hint { opacity: 0; }
.fold-b { padding: 2px 2px 20px; }
.keys { margin-top: 0; }

/* overflow: visible — et c'est important. Avec `overflow-y: auto`, le menu des
   notifications était coupé net au bord du panneau : on n'en voyait que le
   haut. Un panneau qui rogne ce qui en sort ne peut pas porter de menu. Ce qui
   dépasse (les menus) est désormais posé sur <body>, en position fixe. */
.side {
  height: 100%; position: relative;
  display: flex; flex-direction: column;
  background: var(--ink-2);
  border-right: 1px solid var(--line);
  /* Plus de réserve en bas : le mini-lecteur est DANS le flux (dernier enfant),
     collé au bandeau sans le recouvrir. `.side-sp` (flex:1) pousse qui-est-là,
     le bandeau et le lecteur en bas, ensemble. */
  padding: 14px 11px 0;
  /* visible, et surtout pas `auto` : avec `overflow-y:auto`, le menu des
     notifications était coupé net au bord du panneau. Ce qui dépasse (les
     menus) est posé sur <body>, en position fixe. */
  overflow: visible;
}
.side-top { display: flex; align-items: center; gap: 6px; padding: 0 2px 14px 6px; }

/* Le SÉLECTEUR DE COFFRE, entre le logo et la navigation : pleine largeur, comme
   un rang de plus. Le libellé pousse le chevron à droite. Absent (vaultDrop rend
   null) pour l'admin et pour qui n'a qu'un coffre — la règle ne coûte rien alors. */
.side > .dd { display: block; margin: 0 0 12px; }
.side > .dd .dd-b { width: 100%; justify-content: flex-start; }
.side > .dd .dd-b .trunc { flex: 1; text-align: left; overflow: hidden; text-overflow: ellipsis; }
.side > .dd .dd-b .dd-car { margin-left: auto; }
.logo {
  display: flex; align-items: center; gap: 9px;
  font-size: 17px; font-weight: 800; font-stretch: 70%;
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--chalk); flex: none;
}
.logo svg { width: 21px; height: 21px; color: var(--rd); }
/* « beta » : un petit tag rouge, en exposant, collé au nom. Relatif (`em`) pour
   suivre la taille du logo (sidebar 17px, page de connexion 28px). */
.logo-beta {
  font-size: .52em; font-weight: 700; letter-spacing: .1em; color: var(--rd);
  align-self: flex-start; margin-left: -5px; line-height: 1.3;
}
.gate-logo .logo-beta { margin-left: -7px; }

/* ── LE BANDEAU — au-dessus de la navigation, en UN seul bloc ──────────────
   Trois lignes dans un seul cadre, séparées par un trait d'un pixel. Trois
   cadres empilés, c'était trois fois la même bordure et un escalier de traits
   doubles : un bloc, un bord. */
.board {
  border: 1px solid var(--line); border-radius: var(--r);
  overflow: hidden;
  margin-top: 10px;
  background: var(--ink);
}
.board-i {
  display: block; width: 100%; min-width: 0; text-align: left;
  padding: 8px 10px;
  border: none;
  border-top: 1px solid var(--line);
  transition: background var(--fast) var(--ease);
}
.board-i:first-child { border-top: none; }
.board-i:hover { background: var(--ink-3); }
/* La teinte vient du DOCUMENT (`--tone`, posé par app.js depuis une clé), plus
   d'une classe par bloc. Il y avait ici `.board-i.friday { color: var(--am) }`
   et sa jumelle pour le dimanche : trois blocs, deux règles, et repeindre le
   sien voulait dire ouvrir la feuille de style. C'est le même défaut que les
   `.t-cy` / `.t-gr` des sujets, corrigé au même endroit et de la même façon. */
.board-k { display: flex; align-items: center; gap: 5px; color: var(--tone, var(--dim-2)); font-size: 10.5px; }
.board-k .ico { width: 12px; height: 12px; }
/* display:block — text-overflow n'a AUCUN effet sur un élément inline : la
   valeur était coupée net, sans les points de suspension. */
.board-v {
  display: block;
  font-family: var(--mono); font-stretch: 75%; font-size: 12px;
  color: var(--chalk); margin-top: 3px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Bloc VIDE : PETITE hauteur, et la TYPO + l'ICÔNE « + » des boutons rouges (comme
   « + Tableau ») — rouge, capitales condensées. */
/* Bloc VIDE, reconstruit : UN rôle — le texte « Ajouter un board » posé au centre
   exact du bloc. On neutralise tout ce que `.board-i` (bloc rempli) apporte :
   padding et text-align n'ont rien à faire dans un flex centré. Le letter-spacing
   pousse un blanc APRÈS la dernière lettre — sans text-indent équivalent, le texte
   paraît décalé d'un demi-cran à gauche. line-height:1 : pas de hauteur fantôme. */
.board-i.empty {
  display: flex; align-items: center; justify-content: center;
  min-height: 40px; padding: 0; text-align: center;
}
.board-add {
  color: var(--rd); font-size: 12px; font-weight: 700; font-stretch: 74%;
  text-transform: uppercase; letter-spacing: .09em; text-indent: .09em;
  line-height: 1;
  transition: color var(--fast) var(--ease);
}
.board-i.empty:hover .board-add { color: var(--rd-2); }

.nav { display: flex; flex-direction: column; gap: 1px; }
.nav-item {
  position: relative;
  display: flex; align-items: center; gap: 11px;
  padding: 10px 11px;
  border-radius: var(--r);
  color: var(--dim);
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.nav-item:hover { background: var(--ink-3); color: var(--chalk); }
.nav-item[aria-current="page"] {
  background: var(--ink-3); color: var(--chalk);
  box-shadow: inset 2px 0 0 var(--rd);
  border-radius: 0;
}
.nav-item .ico { width: 19px; height: 19px; }
.nav-n {
  margin-left: auto;
  min-width: 19px; height: 18px; padding: 0 5px;
  display: grid; place-items: center;
  background: var(--rd); color: #fff;
  border-radius: var(--r);
  font-family: var(--mono); font-stretch: 75%; font-size: 10px; font-weight: 700;
}

.side-sp { flex: 1; }

/* ── QUI EST LÀ ───────────────────────────────────────────────────────────
   Le point porte la COULEUR de la personne quand elle est là, et s'éteint
   sinon. Un vert générique dirait « quelqu'un est en ligne » ; sa couleur dit
   qui. */
.who { display: flex; flex-direction: column; gap: 1px; margin-bottom: 8px; }
.who-i {
  display: flex; align-items: center; gap: 8px;
  padding: 5px 8px; border-radius: var(--r);
}
.who-d {
  flex: none; width: 7px; height: 7px; border-radius: 50%;
  background: var(--line-2);
}
.who-i.on .who-d { box-shadow: 0 0 7px currentColor; }
.who-n {
  flex: 1; min-width: 0;
  font-size: 12px; font-weight: 700; font-stretch: 74%;
  text-transform: uppercase; letter-spacing: .07em;
  color: var(--dim-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* La couleur vient du style posé par le JS ; on ne la reprend pas ici. */
.who-i.on .who-n { }
.who-t {
  flex: none; font-family: var(--mono); font-stretch: 75%;
  font-size: 9.5px; color: var(--dim-2);
}
.who-i.on .who-t { color: var(--gr); }
/* La 5e ligne « N autres personnes » : le reste des gens, au survol dans la
   bulle (title). Curseur d'aide pour dire qu'il y a quelque chose à survoler. */
.who-more { cursor: help; }
.who-more .who-n { color: var(--dim); }
.who-more-d { background: var(--dim-2); }

/* Le seul endroit de l'app qui défile. */
.main { min-width: 0; padding: 18px 22px 70px; overflow-y: auto; }
.main:focus { outline: none; }

.head {
  display: flex; align-items: center; gap: 12px;
  margin-bottom: 16px;
  /* La hauteur d'un CONTRÔLE (--ctl-h), pas 34 : sinon une page sans bouton
     d'action ni filtre (le Todo à moins de 2 membres) a un header plus court, et
     le titre SAUTE de 6px en changeant de page. Ancrée à --ctl-h, toute page —
     avec bouton, avec filtre, ou avec rien — garde la même hauteur d'en-tête. */
  min-height: var(--ctl-h);
  flex-wrap: wrap;
}

.off {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 4px 8px; border-radius: var(--r);
  background: var(--rd-soft); border: 1px solid var(--rd);
  color: var(--rd-2); font-size: 11px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .1em;
}
.off .ico { width: 13px; height: 13px; }

/* ══════════════════════════════════════════════════════════════════════════
   LES BANDEAUX & LES NOTIFICATIONS
   ══════════════════════════════════════════════════════════════════════════ */
.banners {
  border: 1px solid var(--line); border-radius: var(--r);
  overflow: hidden; margin-bottom: 16px;
}
.banner {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px;
  border-top: 1px solid var(--line);
  background: var(--ink-2);
  width: 100%;
}
.banner:first-child { border-top: none; }
.banner.hot { background: var(--rd-soft); }
.banner.hot .banner-n { color: var(--rd); }
.banner-main {
  flex: 1; min-width: 0;
  display: flex; align-items: center; gap: 10px;
  text-align: left;
}
.banner-n {
  font-family: var(--mono); font-stretch: 75%;
  font-size: 22px; font-weight: 700; line-height: 1;
  color: var(--dim); min-width: 30px;
}
.banner-t { flex: 1; font-size: 14px; }
.banner-a { color: var(--dim-2); }

.notif-wrap { position: relative; }
.notif-dot {
  position: absolute; top: 3px; right: 3px;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--rd);
  animation: beat 1.6s var(--ease) infinite;
  pointer-events: none;
}
.notif-pop {
  position: fixed; z-index: 80;
  width: 300px;
  background: var(--ink-2);
  border: 1px solid var(--line-2); border-radius: var(--r);
  box-shadow: 0 14px 40px -10px rgba(0,0,0,.9);
  animation: rise 120ms var(--ease);
  overflow: hidden;
}
.notif-h { padding: 9px 11px; border-bottom: 1px solid var(--line); }
.notif-empty { padding: 16px 12px; text-align: center; color: var(--dim-2); font-size: 12.5px; }

/* Une ligne de notification : l'icône de la PAGE concernée à gauche (on sait
   où on va avant de cliquer), le compte, le libellé, et la flèche qui dit que
   c'est un chemin. */
.nrow {
  display: flex; align-items: center; gap: 10px;
  width: 100%; text-align: left;
  padding: 11px 12px;
  border-top: 1px solid var(--line);
  transition: background var(--fast) var(--ease);
}
.nrow:first-child { border-top: none; }
.nrow:hover { background: var(--ink-3); }
.nrow-i {
  width: 30px; height: 30px; flex: none;
  border-radius: var(--r);
  background: var(--rd-soft); color: var(--rd);
}
.nrow-i .ico { width: 17px; height: 17px; }
.nrow-n {
  font-family: var(--mono); font-stretch: 75%;
  font-size: 18px; font-weight: 700; color: var(--rd); min-width: 22px;
}
.nrow-t { flex: 1; min-width: 0; font-size: 13px; }
.nrow-go { flex: none; color: var(--dim-2); }
.nrow-go .ico { width: 15px; height: 15px; }
.nrow:hover .nrow-go { color: var(--chalk); }

/* ══════════════════════════════════════════════════════════════════════════
   LE FIL

   Un jour = UN bloc. Un cadre autour, des traits d'un pixel dedans, zéro
   espace. Les événements empilés chacun dans leur cadre faisaient un escalier
   de bordures doubles et un fil qui se lisait comme une grille.
   ══════════════════════════════════════════════════════════════════════════ */
.day-block { margin-bottom: 18px; }
/* Toute la barre du jour clique — pas seulement la flèche. */
.day-h {
  display: flex; align-items: center; gap: 9px;
  width: 100%; text-align: left;
  padding: 8px 4px;
  border-radius: var(--r);
  transition: background var(--fast) var(--ease);
}
.day-h:hover { background: var(--ink-2); }
.day-h h2 { flex: none; text-transform: capitalize; }
.day-car { display: flex; flex: none; color: var(--dim-2); transition: transform var(--fast) var(--ease); }
.day-car .ico { width: 16px; height: 16px; }
.day-h.open .day-car { transform: rotate(90deg); }
.day-n { color: var(--dim-2); font-family: var(--mono); font-stretch: 75%; font-size: 11px; }
.day-new {
  min-width: 17px; height: 16px; padding: 0 5px;
  display: grid; place-items: center;
  background: var(--rd); color: #fff; border-radius: var(--r);
  font-family: var(--mono); font-stretch: 75%; font-size: 9.5px; font-weight: 700;
}

.evs {
  border: 1px solid var(--line); border-radius: var(--r);
  overflow: hidden;
}
/* Un jour replié n'a pas de liste. Sans ça, le cadre vide se refermait sur
   lui-même et laissait un trait de 2 px sous chaque en-tête : dix jours
   fermés, dix traits qui ne veulent rien dire. */
.evs:empty { display: none; }
/* Aucun trait entre les événements d'un même jour : ils forment UN bloc. Le
   survol et la tranche rouge suffisent à les distinguer — une grille de traits
   sur dix lignes se lit comme un tableur. */
.ev { background: var(--ink-2); }
/* La tranche rouge va d'un angle à l'autre : un `border-radius` sur le bloc
   l'arrondirait aux deux bouts, et une amorce arrondie n'est plus une amorce.
   Le bloc porte donc l'angle vif, et c'est le cadre extérieur qui arrondit. */
.ev.new { box-shadow: inset 3px 0 0 var(--rd); border-radius: 0; }
.ev.seen { opacity: .55; }
.ev.seen:hover { opacity: 1; }

/* L'en-tête ENTIER déplie (voir components.js) : c'est lui qui porte le clic,
   pas le seul `.ev-b`. D'où le curseur ici — la flèche et les vides autour sont
   des cibles, et doivent le dire. */
.ev-h { display: flex; align-items: center; gap: 4px; padding: 0 8px 0 8px; cursor: pointer; }
/* Le pouce fait autre chose : il reprend le curseur du geste qu'il est. */
.ev-h .ev-see { cursor: pointer; }
/* Le bouton PORTE le rembourrage : c'est lui la zone cliquable, et elle doit
   aller jusqu'aux bords. Un rembourrage sur le parent laisserait une bande
   morte tout autour — la surface où l'on croit cliquer sans que rien ne se
   passe. On ne doit jamais avoir à viser. */
.ev-b {
  display: flex; align-items: center; gap: 10px;
  flex: 1; min-width: 0; text-align: left;
  padding: 10px 4px 10px 6px;
}
/* Le pouce est posé APRÈS la flèche, avec de l'air : il fait autre chose que
   déplier, et deux gestes voisins qui ne font pas la même chose doivent se
   voir séparés. */
.ev-see { margin-right: 6px; }
.ev-ico {
  width: 30px; height: 30px; flex: none;
  border-radius: var(--r);
  background: var(--ink-4); color: var(--dim);
}
.ev-ico .ico { width: 17px; height: 17px; }
/* La teinte d'un sujet arrive en `--tone`, posée par le JS depuis NOTRE table.
   C'était une règle par couleur (`.t-cy`, `.t-gr`…) : il fallait toucher le CSS
   pour ajouter un type, donc une équipe ne pouvait pas en créer. `color-mix`
   fabrique le fond doux à partir de la teinte, au lieu d'exiger une variable
   `-soft` par couleur. */
.ev-ico { background: color-mix(in srgb, var(--tone, var(--dim)) 15%, transparent);
          color: var(--tone, var(--dim)); }

.ev-t { font-size: 15px; font-weight: 600; line-height: 1.35;
        overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ev-m {
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  margin-top: 2px; font-size: 12px; color: var(--dim-2);
}
.ev-m .sep { color: var(--line-2); }
.sub-l { font-size: 10.5px; }
/* Qui a marqué « vu », en un coup d'œil : une pastille par personne, à sa couleur,
   sur la ligne de l'auteur. Les pastilles se chevauchent légèrement pour rester
   compactes quand l'équipe grandit. */
.ev-seen { display: inline-flex; align-items: center; margin-left: 2px; }
.ev-seen-d {
  width: 9px; height: 9px; border-radius: 50%; flex: none;
  margin-left: -3px; box-shadow: 0 0 0 1.5px var(--black);
}
.ev-seen-d:first-child { margin-left: 0; }
.sub-l { color: var(--tone, var(--dim)); }

/* ── LES TUILES DE SUJET (popup « + Événement ») ──────────────────────────
   Chaque tuile porte SA couleur, en permanence. Grises jusqu'au clic, les sept
   tuiles se ressemblaient — alors que la couleur d'un sujet est exactement ce
   qui permet de le reconnaître sans lire partout ailleurs dans le fil. Et la
   tuile choisie virait au rouge de la marque, ce qui faisait dire « cassé » à
   « Mise en prod ».

   `--tone` est posée par le JS depuis NOTRE table ; `--tone-ink` est calculée
   (inkOn) pour que le libellé tienne sur une teinte claire comme sur une
   sombre. Aucune règle par couleur : une équipe crée ses teintes, la CSS ne
   doit pas avoir à les connaître. */
.sub-tiles {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(88px, 1fr));
  gap: 5px; margin-bottom: 14px;
}
.sub-tile {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; padding: 11px 6px;
  background: color-mix(in srgb, var(--tone) 9%, var(--ink-2));
  border: 1px solid color-mix(in srgb, var(--tone) 26%, var(--line));
  border-radius: var(--r);
  color: var(--dim);
  font-size: 11px; font-weight: 700; font-stretch: 74%;
  text-transform: uppercase; letter-spacing: .07em;
  text-align: center;
  transition: background var(--fast) var(--ease), border-color var(--fast) var(--ease),
              color var(--fast) var(--ease);
}
.sub-tile-i { display: flex; color: var(--tone); transition: color var(--fast) var(--ease); }
.sub-tile-i .ico { width: 19px; height: 19px; }
.sub-tile-l { min-width: 0; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
.sub-tile:hover {
  background: color-mix(in srgb, var(--tone) 18%, var(--ink-2));
  border-color: color-mix(in srgb, var(--tone) 50%, var(--line));
  color: var(--chalk);
}
/* Choisie : la teinte pleine. On ne cherche pas laquelle est prise. */
.sub-tile.on {
  background: var(--tone);
  border-color: var(--tone);
  color: var(--tone-ink, #0B0D0E);
}
.sub-tile.on .sub-tile-i { color: var(--tone-ink, #0B0D0E); }

/* ── QUAND — la date et l'heure d'un événement ────────────────────────────
   Deux champs natifs côte à côte. La fin est sur sa propre ligne, et elle
   n'existe que si on la réclame : un événement est un instant par défaut. */
.when { display: flex; align-items: center; gap: 6px; }
.when + .when { margin-top: 6px; }
.when-d { flex: 1 1 auto; min-width: 0; }
.when-t { flex: 0 0 auto; width: 116px; }
.when-ar { display: flex; flex: none; color: var(--dim-2); }
.when-ar .ico { width: 16px; height: 16px; }
/* Le champ natif pose sa propre icône de sélecteur, et elle arrive blanche :
   un carré lumineux au milieu du poste de commandement. On la retourne pour la
   ramener au gris de tout le reste. */
.when input[type="date"]::-webkit-calendar-picker-indicator,
.when input[type="time"]::-webkit-calendar-picker-indicator {
  filter: invert(1) opacity(.4);
  cursor: pointer;
}
.when input[type="date"]:hover::-webkit-calendar-picker-indicator,
.when input[type="time"]:hover::-webkit-calendar-picker-indicator { filter: invert(1) opacity(.75); }
.ev-who { color: var(--dim); font-weight: 600; }
.time { font-size: 12px; color: var(--dim-2); flex: none; }

/* Le badge de récurrence dans la ligne meta (« ⟳ Chaque semaine · lun. »). */
.ev-recur { display: inline-flex; align-items: center; gap: 3px; color: var(--dim); font-weight: 600; }
.ev-recur .ico { width: 13px; height: 13px; }
/* Un événement À VENIR : une pastille d'accent à gauche, discrète. Le titre du
   jour dit déjà « demain » ; la ligne, elle, se signale d'un trait. */
.ev.upcoming { box-shadow: inset 2px 0 0 color-mix(in srgb, var(--tone, var(--dim)) 70%, transparent); }

/* ── LE CONSTRUCTEUR DE RÉCURRENCE (form « Répéter ») ──────────────────────── */
.recur { display: flex; flex-direction: column; gap: 8px; }
.recur-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.recur-l { flex: none; min-width: 64px; color: var(--dim); font-size: 12.5px; }
.recur-sub { padding-left: 72px; }
.recur-n { width: 68px; text-align: center; }
.recur-u { color: var(--dim); font-size: 12.5px; }
.recur-days { display: inline-flex; gap: 4px; }
.recur-day {
  width: 30px; height: 30px; border-radius: 999px;
  display: grid; place-items: center;
  border: 1px solid var(--line-2); background: var(--ink-3); color: var(--dim);
  font-size: 12px; font-weight: 700;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.recur-day:hover { color: var(--chalk); }
.recur-day.on { background: var(--rd); border-color: var(--rd); color: #fff; }
.recur-end-seg { display: inline-flex; gap: 2px; background: var(--ink-3); border-radius: var(--r); padding: 3px; }
.recur-end-seg .seg { min-height: 30px; }

/* ── LE CHOIX DE PORTÉE (cette occurrence / toute la série) ───────────────── */
.scope-q { color: var(--dim); font-size: 14px; margin: 0; }
.modal-prompt .scope-b { padding: 4px 0 2px; }

.ev-car { display: flex; flex: none; color: var(--dim-2); transition: transform var(--fast) var(--ease); }
.ev-car .ico { width: 17px; height: 17px; }
.ev.open .ev-car { transform: rotate(90deg); }

/* Le pouce. Vide = pas vu. Plein = vu, et il vient de le dire.
   Pas de « tout marquer vu » : le geste doit coûter un clic PAR événement,
   sinon personne ne les lit. */
.ev-see {
  position: relative;
  flex: none; width: 32px; height: 32px;
  display: grid; place-items: center;
  border-radius: var(--r);
  color: var(--dim-2);
  transition: color var(--fast) var(--ease), background var(--fast) var(--ease);
}
.ev-see:hover { background: var(--ink-4); color: var(--gr); }
.ev-see .ico { width: 20px; height: 20px; }
.ev.seen .ev-see { color: var(--gr); }
.ev-see.just .ico { animation: pop 280ms var(--ease); }

/* fixed, sur <body> : posé dans le bouton, le rendu qui suit la réponse du
   serveur le balayait avant qu'il n'ait bougé. */
.cft {
  position: fixed; z-index: 95;
  width: 5px; height: 5px; border-radius: 1px;
  margin: -2px 0 0 -2px;
  pointer-events: none;
  animation: confetti 640ms var(--ease) forwards;
}

.ev-body { padding: 0 12px 12px 52px; }
.ev-desc { font-size: 14px; line-height: 1.6; color: var(--chalk); white-space: pre-wrap;
           word-wrap: break-word; }
.ev-seenby { margin-top: 9px; font-size: 11.5px; color: var(--dim-2); }
.ev-act { display: flex; gap: 4px; margin-top: 10px; }

/* ══════════════════════════════════════════════════════════════════════════
   LA VOIX
   ══════════════════════════════════════════════════════════════════════════ */
.au-box {
  display: flex; align-items: center; gap: 9px;
  padding: 5px 12px 5px 5px;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--ink);
}
/* .none, pas .empty : « .empty » est la classe des écrans vides (colonne,
   centrée, bordure tiretée). La bulle la portait et héritait de tout ça en
   silence — le micro se retrouvait centré, avec son libellé dessous. */
.au-box.none { color: var(--dim-2); font-size: 13px; }
.au-play, .au-mic, .au-stop {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%; border: none;
  transition: transform var(--fast) var(--ease), filter var(--fast) var(--ease);
}
.au-play { width: 34px; height: 34px; background: var(--rd); color: #fff; }
.au-play:hover { filter: brightness(1.15); transform: scale(1.06); }
.au-play .ico { width: 19px; height: 19px; }
.au-mic { width: 34px; height: 34px; background: var(--ink-3); border: 1px solid var(--line); color: var(--chalk); }
.au-mic:hover { background: var(--ink-4); transform: scale(1.06); }
.au-mic .ico { width: 18px; height: 18px; }
.au-stop {
  width: 40px; height: 40px; background: var(--rd); color: #fff;
  animation: recRing 1.6s var(--ease) infinite;
}
.au-stop .ico { width: 22px; height: 22px; }
.au-wave { flex: 1; min-width: 0; height: 34px; display: flex; align-items: center; gap: 2px; cursor: pointer; }
.au-wave .bar { flex: 1; min-width: 2px; max-width: 3px; border-radius: 1px; background: var(--line-2);
                transition: background var(--fast) var(--ease); }
.au-wave .bar.played { background: var(--rd); }
.au-time { flex: none; min-width: 32px; text-align: right; font-family: var(--mono);
           font-stretch: 75%; font-size: 11.5px; color: var(--dim-2); }
.au-del { flex: none; width: 26px; height: 26px; border-radius: 50%; color: var(--dim-2);
          display: inline-flex; align-items: center; justify-content: center; }
.au-del:hover { background: var(--ink-3); color: var(--rd-2); }
.au-del .ico { width: 16px; height: 16px; }
.au-box.rec { border-color: var(--rd); background: var(--rd-soft); }
.au-dot { flex: none; width: 8px; height: 8px; margin-left: 6px; border-radius: 50%;
          background: var(--rd); animation: beat 1.1s ease-in-out infinite; }
.au-rectime { flex: 1; font-family: var(--mono); font-stretch: 75%; font-size: 13px; color: var(--rd-2); }

/* La description ET la voix dans le MÊME cadre : c'est la même chose — ce
   qu'on a à dire. Deux sections en auraient fait deux corvées. */
.say { border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; }
.say:focus-within { border-color: var(--rd); }
.say .area {
  border: none; border-radius: 0; background: var(--ink);
}
.say .area:focus { box-shadow: none; }
.say-au { padding: 6px; border-top: 1px solid var(--line); background: var(--ink-2); }
.say-au .au-box { border: none; background: transparent; padding: 0 6px 0 0; }

/* ══════════════════════════════════════════════════════════════════════════
   LE TABLEAU
   ══════════════════════════════════════════════════════════════════════════ */
/* Deux colonnes dès qu'il y a la place. Les tableaux des membres sont des
   blocs indépendants : là, la largeur sert à quelque chose — on voit deux
   personnes à la fois au lieu de faire défiler. */
/* UN tableau par ligne, pleine largeur, l'un après l'autre.

   Ils étaient sur deux colonnes au-delà de 1320 px. Une feuille de calcul à
   mi-largeur, c'est un texte de tâche sur deux lignes une fois sur trois, et
   deux tableaux qu'on compare des yeux au lieu de lire celui qu'on est venu
   voir. La verticale ne coûte qu'un défilement — et le tableau de chacun a
   enfin la place d'une vraie phrase. */
.boards { display: flex; flex-direction: column; gap: 22px; }

/* Le tableau : un panneau bordé, coins arrondis (comme avant). Ce sont ses LIGNES,
   à l'intérieur, qui n'ont ni bordure ni marge et remplissent 100 % de sa largeur. */
.tb { border: 1px solid var(--line); border-radius: var(--r); overflow: hidden;
      background: var(--ink-2); }
.tb.drop { border-color: var(--rd); }
.tb-h {
  display: flex; align-items: center; gap: 10px;
  height: 54px; box-sizing: border-box; padding: 0 12px;
  border-bottom: 1px solid var(--line);
  background: var(--ink-3);
}
/* Hauteur FIXE : le sélecteur de largeur (×0,5 ×1 ×2), présent en vue Colonnes
   seulement, ne doit pas faire grandir l'en-tête — il reste identique aux deux
   vues. Les contrôles sont plus courts que 54px, ils se centrent. */
/* Le NOM du tableau — éditable en place (plus une identité de membre). Champ nu :
   il a l'air d'un titre, pas d'un input, tant qu'on n'écrit pas dedans. */
.tb-name {
  flex: 1 1 auto; min-width: 0; max-width: 24em;
  background: transparent; border: none; outline: none; border-radius: var(--r);
  color: var(--chalk);
  font-size: 21px; font-weight: 800; font-stretch: 72%;
  text-transform: uppercase; letter-spacing: .1em;
  padding: 2px 4px;
}
.tb-name:hover { background: color-mix(in srgb, var(--chalk) 6%, transparent); }
.tb-name:focus { background: var(--ink); box-shadow: inset 0 0 0 1px var(--rd); }
.tb-name::placeholder { color: var(--dim-2); }
.tb-c { color: var(--dim-2); font-family: var(--mono); font-stretch: 75%; font-size: 12.5px; }
/* Le bouton « Partager » S'ALLUME EN ROUGE quand le tableau est partagé (coffre ou
   personnes) — comme le partage d'une note. */
.share-b.on { color: var(--rd); }

/* LE NOM DU COFFRE d'un élément (événement, note, tableau todo), INTÉGRÉ à sa
   charte — plus un badge/pastille. Icône « group » + nom en ROUGE, inline, exactement
   comme le coffre dans la carte d'une note (`.note-item-coffre`). La typo est HÉRITÉE
   du contexte (l'icône suit la taille du texte en `em`) : le nom se fond dans
   l'élément, il ne s'y pose plus par-dessus. */
.vault-name {
  display: inline-flex; align-items: center; gap: 4px; flex: none;
  min-width: 0; max-width: 200px; color: var(--rd);
}
.vault-name > span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vault-name .ico { width: 1.05em; height: 1.05em; flex: none; }
/* L'œil de visibilité d'un type, dans le coffre : éteint = type masqué. */
.sub-eye.off { color: var(--rd); }

.sheet { display: flex; flex-direction: column; font-size: 16px; }

/* LA LIGNE NOIRE. Un vrai noir : c'est ce qui la détache du panneau. */
.blk {
  display: flex; align-items: center; gap: 4px;
  min-height: 42px;
  background: var(--black);
  padding: 0 8px 0 2px;
  width: 100%; border: 0; margin: 0;
}
.blk-car { display: flex; flex: none; color: var(--dim); transition: transform var(--fast) var(--ease); }
.blk-car .ico { width: 19px; height: 19px; }
.blk.open .blk-car { transform: rotate(90deg); }
.blk-in {
  flex: 1; min-width: 0;
  background: transparent; border: none; outline: none;
  color: var(--chalk);
  font-weight: 700; font-stretch: 68%; font-size: 15px;
  text-transform: uppercase; letter-spacing: .13em;
  padding: 10px 4px;
}
.blk-in:focus { box-shadow: inset 0 0 0 1px var(--rd); }
.blk-n { flex: none; color: var(--dim-2); font-family: var(--mono); font-stretch: 75%; font-size: 12px; }

/* LA LIGNE NEUTRE */
/* Aucun trait entre les lignes. Le survol suffit à les séparer, et une grille
   de traits gris sur trente lignes se lit comme un tableur d'inventaire. */
/* padding-left: 2px COMME `.blk` — sans ça la poignée d'une tâche est 2px à gauche
   de celle d'un groupe, et les deux colonnes de poignées ne s'alignent pas. */
.row { display: flex; align-items: center; min-height: 44px; padding-left: 2px;
       width: 100%; border: 0; margin: 0; }
.row:hover { background: var(--ink-3); }

/* La TEINTE d'une ligne (« #red … », « #vert … » en tête du texte) : un voile
   DOUX de sa couleur — 12 %, tamisé, le texte reste chalk. `--tone` est posé par
   applyTone (pages-todo) depuis une clé TINTS, jamais une couleur saisie. Le
   `:not(.sel)` laisse le dernier mot à la sélection (rouge). */
.row.tinted:not(.sel), .col-card.tinted:not(.sel) {
  background: color-mix(in srgb, var(--tone) 12%, transparent);
}
.row.tinted:not(.sel):hover, .col-card.tinted:not(.sel):hover {
  background: color-mix(in srgb, var(--tone) 20%, transparent);
}
/* Le TEXTE prend la teinte aussi — adoucie d'un fond de chalk (78/22) pour
   rester agréable à lire sur le voile, pas un aplat criard. */
.row.tinted .row-in, .col-card.tinted .col-card-in {
  color: color-mix(in srgb, var(--tone) 78%, var(--chalk));
}

/* ── LA SÉLECTION MULTIPLE (shift+clic) ───────────────────────────────────
   Du rouge, et il a le droit : sélectionner du texte est DÉJÀ rouge ici
   (`::selection`). C'est le même geste, à l'échelle de la ligne — pas une
   décoration de plus, le même sens à un cran au-dessus.

   Un fond, pas un liseré : le liseré est pris (`:focus` en pose un, et deux
   traits imbriqués sur la même ligne ne se lisent plus). Le fond dit « prise »,
   le liseré dit « c'est ici que j'écris » — sur la ligne ancre, les deux se
   superposent sans se gêner, et c'est exactement ce qu'il faut voir.

   La barre de gauche donne le bord franc du bloc : sur dix lignes prises, c'est
   elle qui fait la sélection UNE au lieu de dix, sans arrondi ni encadré. */
.row.sel, .blk.sel { background: color-mix(in srgb, var(--rd) 14%, transparent); }
.row.sel:hover, .blk.sel:hover { background: color-mix(in srgb, var(--rd) 20%, transparent); }
/* La barre de gauche vivait sur la poignée (.acts) — retirée des tâches, elle se
   pose sur la ligne elle-même. Le groupe la garde sur sa poignée. */
.row.sel, .col-card.sel { box-shadow: inset 2px 0 0 var(--rd); }
.blk.sel .acts { box-shadow: inset 2px 0 0 var(--rd); }
/* Une ligne noire prise garde son fond noir — c'est ce qui la distingue d'une
   tâche, et le perdre reviendrait à effacer la structure pour montrer l'état.
   Elle porte donc la prise sur son seul bord. */
.blk.sel { background: var(--black); }
.blk.sel .blk-in { background: color-mix(in srgb, var(--rd) 16%, transparent); }
.row-in {
  flex: 1; min-width: 0;
  background: transparent; border: none; outline: none;
  color: var(--chalk); font-size: 16px; font-weight: 500;
  padding: 6px 10px; height: 44px;
}
.row-in:focus { background: var(--ink); box-shadow: inset 0 0 0 1px var(--rd); }
.row-in::placeholder { color: var(--dim-2); }
/* La mise en forme porte sur la LIGNE ENTIÈRE : « chaque ligne = une tâche »,
   donc c'est la tâche qu'on barre, pas trois mots dedans. */
/* 800, pas 700 : la ligne est à 500 maintenant, et un gras à 700 ne se
   détacherait plus que de deux crans. L'écart de 300 est celui d'avant. */
.f-b { font-weight: 800; }
.f-i { font-style: italic; }
.f-s { text-decoration: line-through; text-decoration-color: var(--rd); color: var(--dim-2); }

/* LES ACTIONS, À GAUCHE. Elles sont là où va la main : au bord, avant le
   texte. À droite, il fallait traverser la ligne des yeux pour les trouver. */
.acts { display: flex; align-items: center; flex: none; gap: 1px; padding-left: 3px; }
.acts .btn-ico { width: 32px; height: 32px; }
.acts .ico { width: 19px; height: 19px; }
/* La poignée ne vit plus que sur les GROUPES (blackRow) : une tâche se glisse
   par toute sa surface et se supprime au ⌘⌫ — ses icônes lui rendaient la place. */
.grab { cursor: grab; color: var(--dim-2); touch-action: none; }
.grab:active { cursor: grabbing; }
.acts .hid, .hid { opacity: 0; transition: opacity var(--fast); }
.row:hover .hid, .row:focus-within .hid,
.blk:hover .hid, .blk:focus-within .hid { opacity: 1; }

/* La poubelle, calée tout à droite de la ligne, révélée au survol comme la
   poignée. `margin-left:auto` la pousse au bout. */
.row-del, .blk .row-del { flex: none; margin-left: auto; margin-right: 4px; width: 32px; height: 32px; }
.row-del .ico { width: 18px; height: 18px; }

/* Les deux ajouts sont montés dans `.tb-h` : en pied, ils s'éloignaient à
   chaque ligne écrite, et « + Ligne » finissait sous l'horizon d'un tableau de
   trente lignes. L'en-tête ne bouge pas. */

/* Le glisser-déposer. La ligne prise s'efface, et un trait rouge dit où elle
   va tomber. */
.drag { opacity: .35; }
.drop-line { height: 2px; background: var(--rd); margin: -1px 0; }

/* ── LA VUE COLONNES (Kanban) ──────────────────────────────────────────────
   Chaque GROUPE devient une colonne ; ses tâches, des cartes empilées. Les
   tableaux des membres se posent CÔTE À CÔTE (deux par rangée sur large écran,
   ils s'empilent sur étroit), chacun faisant défiler ses colonnes s'il en a
   beaucoup. Le drag d'une carte la range par position, comme en vue liste. */
.board-cols { display: flex; flex-direction: column; gap: 16px; align-items: stretch; }
.tb.cols { width: 100%; max-width: 100%; }
/* En ×0,25, la colonne (200px) est trop étroite pour la coche : on la masque, la
   place va au texte. Cocher « fait » reste possible en ×0,5+ ou en vue Liste. */
.tb.cols.cw-mini .col-card .row-check { display: none; }
/* Les colonnes COLLÉES : aucun gap ni marge, séparées d'un seul liseré (1px). Chaque
   colonne fait au moins le DOUBLE de l'ancienne largeur (212 → 424) et GRANDIT pour
   remplir toute la largeur du tableau — zéro espace perdu ; au-delà, on scrolle. */
/* `align-items: flex-start` : chaque colonne s'arrête à son contenu — plus de vide
   (le « margin bottom ») sous les cartes d'une colonne plus courte que ses voisines. */
/* `stretch` (et pas `flex-start`) : chaque colonne remplit la hauteur du tableau,
   donc sa bordure droite court de l'en-tête au pied — deux groupes de hauteurs
   très différentes restent deux colonnes nettes. Le fond sous les cartes est le
   même noir-gris que les cartes : pas de « vide » visible (l'objection de 2.57). */
.tb-cols { display: flex; gap: 0; align-items: stretch; padding: 0;
  overflow-x: auto; overflow-y: hidden;   /* overflow-y explicite : pas de réservation implicite */
  scrollbar-width: none; }              /* barre horizontale masquée (aucune gouttière réservée) */
/* Pas de `border-top` ici : l'en-tête du tableau (`.tb-h`) porte déjà son `border-bottom`.
   Les deux ensemble faisaient un double filet (1px + 1px) sous l'en-tête en vue Colonnes. */
.tb-cols::-webkit-scrollbar { display: none; }
.col {
  /* La largeur est un RÉGLAGE (×0,5 / ×1 / ×2) : `--col-w` posé sur `.tb.cols`,
     dont `.col` hérite. Défaut si l'inline manque : normal.
     ⚠️ EXACTE (`flex: 0 0`), pas `1 0` : avec un grow, deux colonnes sur un grand
     écran s'étiraient à se partager le tableau — ×0,5 rendait la MÊME largeur que
     ×1 tant que rien ne débordait, et le réglage semblait mort. Une colonne fait
     sa largeur, point ; le reste du panneau reste vide, du même noir-gris. */
  flex: 0 0 var(--col-w, 440px); min-width: var(--col-w, 440px);
  display: flex; flex-direction: column;
  /* Le MÊME noir-gris que le panneau de la vue Liste (`.tb`) : une ligne todo a la
     même couleur dans les deux vues. Seuls les en-têtes (`.col-h`) restent noirs,
     comme la ligne noire (`.blk`) de la vue Liste. */
  background: var(--ink-2); border: none; border-right: 1px solid var(--line); border-radius: 0;
  overflow: hidden;
}
.col.col-drop { border-color: var(--rd); }
.col-h { display: flex; align-items: center; gap: 4px; min-height: 40px;
  padding: 0 3px 0 8px; background: var(--black); }
.col-h-none { background: var(--ink-3); padding: 0 10px; }
/* Mention « partagé avec moi » dans l'en-tête d'un tableau qu'on ne possède pas :
   d'où il vient (Coffre · Personne, ou juste Personne pour un partage nominatif). */
/* Modal « Partagé avec vous » (lecture seule) — qui m'a partagé le tableau, et
   comment (directement / via un coffre). Aucun contrôle : je ne possède pas ce
   tableau. */
.sh-info { display: flex; flex-direction: column; gap: 14px; }
.sh-line { display: flex; align-items: center; gap: 12px; }
.sh-ico { flex: none; width: 34px; height: 34px; display: grid; place-items: center;
  border-radius: var(--r); background: var(--ink-3); color: var(--rd); }
.sh-ico .ico { width: 18px; height: 18px; }
.sh-line > div { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.sh-lbl { font-size: 10.5px; font-weight: 700; font-stretch: 74%; text-transform: uppercase;
  letter-spacing: .09em; color: var(--dim-2); }
.sh-val { font-size: 15px; color: var(--chalk); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sh-note { margin: 2px 0 0; font-size: 12.5px; color: var(--dim); }
.col-in {
  flex: 1; min-width: 0; background: transparent; border: none; outline: none;
  color: var(--chalk); font-weight: 700; font-stretch: 68%; font-size: 13px;
  text-transform: uppercase; letter-spacing: .1em; padding: 10px 2px;
}
.col-in:focus { box-shadow: inset 0 0 0 1px var(--rd); }
.col-h.sel .col-in { background: color-mix(in srgb, var(--rd) 16%, transparent); }
.col-t { flex: 1; color: var(--dim); font-weight: 700; font-stretch: 68%; font-size: 12px;
  text-transform: uppercase; letter-spacing: .1em; }
.col-n { flex: none; color: var(--dim-2); font-family: var(--mono); font-stretch: 75%; font-size: 11px; padding: 0 2px; }
.col-del { flex: none; width: 28px; height: 28px; }
.col-del .ico { width: 16px; height: 16px; }
/* Les cartes remplissent la colonne, à plat : plus de padding autour (`.col-body`),
   plus de bordure/rayon/fond sur la carte. Comme les lignes de la vue Liste, c'est
   le survol qui les sépare — pas un encadré par tâche. */
/* `min-height` réduit SOUS la hauteur d'une carte : plus de zone morte de ~qq px
   entre la dernière ligne et le bas du tableau (elle venait d'un min-height > 1 carte).
   Assez pour rester une cible de dépôt sur une colonne vide, jamais assez pour créer du vide. */
.col-body { display: flex; flex-direction: column; gap: 0; padding: 0; min-height: 22px; }
.col-card {
  display: flex; align-items: center; gap: 2px; padding-left: 2px;
  background: none; border: none; border-radius: 0;
  transition: background var(--fast) var(--ease);
}
.col-card:hover { background: var(--ink-3); }
.col-card:hover .hid, .col-card:focus-within .hid { opacity: 1; }
/* L'en-tête de colonne (fond noir) révèle aussi ses icônes au survol : la note du
   groupe ET la poubelle n'apparaissent qu'ici — comme la poubelle des lignes. */
.col-h:hover .hid, .col-h:focus-within .hid { opacity: 1; }
.col-card .grab { flex: none; width: 22px; height: 30px; display: grid; place-items: center; }
.col-card .grab .ico { width: 16px; height: 16px; }
.col-card.sel { background: color-mix(in srgb, var(--rd) 16%, transparent); }
.col-card-in {
  flex: 1; min-width: 0; background: transparent; border: none; outline: none;
  color: var(--chalk); font-size: 13.5px; padding: 8px 4px;
}
.col-card-in:focus { background: var(--ink); box-shadow: inset 0 0 0 1px var(--rd); border-radius: var(--r); }
.col-card .row-del { flex: none; width: 28px; height: 28px; margin-left: auto; margin-right: 2px; }
.col-card .row-del .ico { width: 16px; height: 16px; }
.col-add, .col-new {
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  color: var(--dim); font-size: 12px; font-weight: 600;
  border: 1px dashed var(--line-2); border-radius: var(--r); background: none;
  transition: color var(--fast) var(--ease), border-color var(--fast) var(--ease), background var(--fast) var(--ease);
}
.col-add:hover, .col-new:hover { color: var(--chalk); border-color: var(--dim-2); background: var(--ink-3); }
.col-add { margin: 0 6px 6px; padding: 7px; }
.col-new { flex: none; width: 128px; align-self: stretch; flex-direction: column; gap: 6px; padding: 14px 8px; }
.col-new .ico { width: 18px; height: 18px; }

/* Le sélecteur de vue Liste/Colonnes, dans l'en-tête : deux segments clairs. */
/* Le sélecteur de vue — le MÊME design pour le todo (Liste/Colonnes) et le
   calendrier (Mois/Semaine) : icône 16px + libellé, même gap, même graisse. */
.todo-view .seg, .cal-view .seg { gap: 6px; font-size: 12.5px; font-weight: 600; }
.todo-view .seg .ico, .cal-view .seg .ico { width: 16px; height: 16px; }
/* Le sélecteur de largeur (×0,5 / ×1 / ×2) dans l'en-tête de CHAQUE tableau. */
.tb-colw { flex: none; }
.tb-colw .seg { font-size: 12px; font-weight: 700; font-stretch: 74%; padding: 0 9px; letter-spacing: .04em; }
@media (max-width: 560px) { .todo-view-l, .cal-view-l { display: none; } }

/* ── LA CASE « FAIT » ─────────────────────────────────────────────────────
   Toujours visible (contrairement à la poubelle) : c'est le geste principal
   d'une tâche, il ne se cache pas au repos. Grise au repos, VERTE au survol —
   le vert dit « valider », comme partout. La cocher archive la ligne. */
.row-check { flex: none; color: var(--dim-2); }
.row .row-check { width: 32px; height: 32px; }
.row .row-check .ico { width: 19px; height: 19px; }
.col-card .row-check { width: 26px; height: 30px; }
.col-card .row-check .ico { width: 17px; height: 17px; }
.row-check:hover { color: var(--gr); }

/* ── DATER une ligne (échéance / événement) ───────────────────────────────
   Programmer une date se fait au CLIC DROIT (rowMenu, comme une catégorie de
   notes) — pas d'icône sur la ligne. Une ligne datée le montre par sa pastille
   d'échéance (vue liste) ou un liseré rouge sous la carte (vue colonne). */
/* La pastille d'échéance, à la suite du texte : date lisible + récurrence. */
.row-chip { flex: none; display: inline-flex; align-items: center; gap: 5px;
  height: 22px; padding: 0 9px; margin-left: 4px; border: none; cursor: pointer;
  background: color-mix(in srgb, var(--rd) 14%, transparent); color: var(--rd);
  border-radius: 999px; font-family: var(--mono); font-stretch: 75%; font-size: 10.5px;
  white-space: nowrap; transition: background var(--fast); }
.row-chip:hover { background: color-mix(in srgb, var(--rd) 24%, transparent); }
.row-chip .ico { width: 13px; height: 13px; }
.row-chip-rec { display: inline-flex; }
/* En colonne (place comptée) : pas de pastille, mais un liseré sous la carte datée. */
.col-card.dated { box-shadow: inset 0 -2px 0 color-mix(in srgb, var(--rd) 55%, transparent); }
.col-card.dated.sel { box-shadow: inset 2px 0 0 var(--rd), inset 0 -2px 0 color-mix(in srgb, var(--rd) 55%, transparent); }

/* ── L'ÉCRAN DES ARCHIVES ─────────────────────────────────────────────────
   Un journal : bloc par jour (date + compte), lignes barrées d'un ✓ vert. */
.arch { display: flex; flex-direction: column; gap: 18px; padding-top: 4px; }
.arch-day { display: flex; align-items: baseline; gap: 10px; padding: 0 2px 6px;
  border-bottom: 1px solid var(--line); }
.arch-day-t { color: var(--chalk); font-weight: 700; font-stretch: 68%; font-size: 13px;
  text-transform: capitalize; }
.arch-day-n { color: var(--dim-2); font-family: var(--mono); font-stretch: 75%; font-size: 11px; }
.arch-list { display: flex; flex-direction: column; }
.arch-row { display: flex; align-items: center; gap: 10px; min-height: 40px;
  padding: 0 4px 0 2px; border-bottom: 1px solid var(--ink-3); }
.arch-check { flex: none; display: grid; place-items: center; width: 26px; color: var(--gr); }
.arch-check .ico { width: 18px; height: 18px; }
.arch-text { flex: 1; min-width: 0; color: var(--dim); font-size: 13px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.arch-grp { flex: none; color: var(--dim-2); font-family: var(--mono); font-stretch: 75%;
  font-size: 10.5px; padding: 1px 6px; background: var(--ink-3); border-radius: 999px; }
.arch-who { flex: none; font-family: var(--mono); font-stretch: 75%; font-size: 11px; }
.arch-at { flex: none; color: var(--dim-2); font-family: var(--mono); font-stretch: 75%; font-size: 11px; }
.arch-row .arch-del { flex: none; width: 30px; height: 30px; }
.arch-row:hover .hid { opacity: 1; }
.arch-row .arch-del .ico { width: 16px; height: 16px; }

@media (hover: none) { .hid { opacity: 1; } }

/* ══════════════════════════════════════════════════════════════════════════
   LES NOTES

   Pleine hauteur, et rien ne défile — sauf la liste. Un éditeur qui pousse la
   page vers le bas fait défiler le titre et la barre d'outils hors de vue dès
   qu'on écrit trois paragraphes.
   ══════════════════════════════════════════════════════════════════════════ */
@media (min-width: 861px) {
  /* height: 100% — et surtout PAS 100dvh.
     ───────────────────────────────────────────────────────────────────────
     À 100dvh, cette colonne était plus haute que la grille qui la contient
     (l'écran moins la barre d'annonce). La grille s'étirait à sa hauteur, la
     sidebar suivait — et son pied sortait sous le bord de l'écran, coupé par
     l'overflow de la coquille. Le footer avait disparu de la page Notes, et
     seulement d'elle. Une hauteur relative se lit sur le parent ; une hauteur
     en dvh ignore tout ce qui est au-dessus. */
  .main-notes {
    padding-left: 0; padding-bottom: 0; padding-top: 18px;
    height: 100%; display: flex; flex-direction: column; overflow: hidden;
  }
  .main-notes .head { padding-left: 22px; flex: none; }
}
.notes-wrap {
  display: grid; grid-template-columns: 230px minmax(0, 1fr);
  flex: 1; min-height: 0;
  border: 1px solid var(--line); border-left: none;
  border-radius: 0 var(--r) var(--r) 0;
  overflow: hidden;
}
.notes-list {
  border-right: 1px solid var(--line);
  background: var(--ink-2);
  display: flex; flex-direction: column;
  min-width: 0; min-height: 0;
}
/* `display:flex` est ce qui manquait : `.sp` porte `flex:1`, et un `flex:1`
   dans un bloc ne pousse rien. Le tri restait collé au compteur, à gauche. */
/* L'EN-TÊTE de la liste EST devenu la recherche : le champ remplit la bande
   (90 %, hauteur pleine), le tri reste le carré de droite. */
.notes-list-h {
  display: flex; align-items: stretch; gap: 0;
  /* Hauteur EXACTE de l'en-tête d'une note (`.note-top`, `--note-hd`), bord compris :
     les deux bandes s'alignent pile. `height` (pas `min-height`) + enfants en 100 %
     pour ne pas déborder du liseré. */
  height: var(--note-hd); padding: 0 4px 0 0;
  border-bottom: 1px solid var(--line); flex: none; box-sizing: border-box;
  /* Bande recherche + tri : fond NOIR uniforme (comme le champ de recherche au
     focus) — le tri ne tranche plus par un fond plus clair. */
  background: var(--ink);
}
.notes-list-h .note-q-w { flex: 1; min-width: 0; border-bottom: none; }
.notes-list-h .note-q { height: 100%; }
.notes-list-h .dd { flex: none; align-self: center; }   /* centré vertical (la bande est en align-items:stretch) */
/* Le seul endroit de la page qui défile. */
/* La barre de recherche : posée sous l'en-tête, au-dessus de la liste qu'elle
   commande. L'icône est DANS le champ — un champ de recherche sans loupe se
   confond avec un champ de titre, et il n'y a pas la place pour un libellé. */
/* La recherche prend toute la bande, bord à bord : pas de marge autour, pas de
   cadre interne. C'est un champ pleine largeur et haut, pas une boîte posée
   dans une boîte. Le seul trait est le bord bas, qui la sépare de la liste. */
.note-q-w { position: relative; display: flex; align-items: center; flex: none;
            border-bottom: 1px solid var(--line); }
.note-q-i { position: absolute; left: 14px; display: flex; pointer-events: none; color: var(--dim-2); }
.note-q-i .ico { width: 17px; height: 17px; }
.note-q {
  width: 100%; height: 37px;          /* + 1px de bord bas = 38, comme la toolbar */
  background: none; border: none; border-radius: 0;
  color: var(--chalk); font-size: 14px;
  padding: 0 38px 0 40px;
  outline: none;
}
.note-q:focus { background: var(--ink); }
.note-q::placeholder { color: var(--dim-2); }
/* La croix native de Safari/Chrome sur type=search : elle est grise, ronde, et
   d'un autre logiciel. La nôtre est à côté. */
.note-q::-webkit-search-cancel-button { display: none; }
.note-q-x { position: absolute; right: 10px; width: 26px; height: 26px; }
.note-q-x .ico { width: 14px; height: 14px; }

/* ── LA BANDE DE DOSSIERS (façon Telegram) ─────────────────────────────────
   Sous l'en-tête : « Tout » + les dossiers, en pastilles qui défilent à
   l'horizontale (pas de barre de défilement — on glisse), et « ＋ » à demeure. */
/* LA BANDE DE CATÉGORIES (dossiers de notes). Compacte : hauteur fixe, onglets
   PLEINE HAUTEUR, aucun gap ni marge — les catégories se touchent, séparées par un
   simple liseré. On scrolle à l'horizontale pour toutes les voir. Chaque catégorie
   se GLISSE pour choisir sa position (liseré rouge = point d'insertion). */
.notes-fold {
  display: flex; align-items: stretch; flex: none;
  height: 38px;                        /* aligné sur la barre d'outils de formatage */
  border-bottom: 1px solid var(--line);
}
.notes-fold-tabs {
  flex: 1; min-width: 0;
  display: flex; align-items: stretch; gap: 0;
  overflow-x: auto; overflow-y: hidden;
  padding: 0;
  scrollbar-width: none;
}
.notes-fold-tabs::-webkit-scrollbar { display: none; }
.fold-tab {
  flex: none;
  display: inline-flex; align-items: center; gap: 4px;
  height: 100%; padding: 0 13px;
  border: none; border-right: 1px solid var(--line); border-radius: 0;
  color: var(--dim); background: none;
  font-size: 11.5px; font-weight: 600; font-stretch: 90%; white-space: nowrap;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.fold-tab:hover { color: var(--chalk); background: var(--ink-3); }
.fold-tab.on { color: var(--chalk); background: var(--ink-4); }
.fold-tab.drop { box-shadow: inset -2px 0 0 var(--rd); }   /* on pose APRÈS cet onglet */
.fold-lock { display: inline-flex; color: var(--rd); }
.fold-lock .ico { width: 12px; height: 12px; }
/* La pastille de couleur d'un groupe, dans son onglet. */
.fold-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; }
/* Un onglet CARRÉ : « / », et les boutons « + »/« settings » posés en fin de liste.
   Même gabarit (38px, pleine hauteur), contenu centré — ils défilent avec les tabs. */
.fold-tab-sq {
  flex: none; width: 38px; padding: 0; justify-content: center;
}
.fold-tab-sq .ico { width: 17px; height: 17px; }

/* ── LA POPUP « GROUPES » (réglages) : ordre au glisser, couleur, renommer. ── */
.grp-set-modal { max-width: 520px; }
.grp-set-list { display: flex; flex-direction: column; gap: 4px; max-height: 60vh; overflow-y: auto; }
.grp-set-empty { color: var(--dim-2); font-size: 13px; padding: 8px 2px; }
.grp-set-row {
  display: flex; align-items: center; gap: 10px;
  padding: 6px; border-radius: var(--r); background: var(--ink);
}
.grp-set-row.drop { box-shadow: inset 0 -2px 0 var(--rd); }   /* on pose APRÈS cette ligne */
.grp-set-grip { flex: none; display: grid; place-items: center; width: 22px; color: var(--dim-2); cursor: grab; }
.grp-set-grip .ico { width: 17px; height: 17px; }
.grp-set-name {
  flex: 1; min-width: 0;
  background: var(--ink-2); border: 1px solid var(--line-2); border-radius: var(--r);
  color: var(--chalk); font-size: 13px; padding: 6px 8px; outline: none;
}
.grp-set-name:focus { box-shadow: inset 0 0 0 1px var(--rd); }
.grp-set-name[readonly] { background: none; border-color: transparent; }
.grp-set-tints { flex: none; display: flex; gap: 4px; }
.grp-set-tint {
  width: 18px; height: 18px; border-radius: 50%; flex: none;
  border: 2px solid transparent; box-sizing: border-box; cursor: pointer;
  transition: transform var(--fast) var(--ease);
}
.grp-set-tint:hover { transform: scale(1.15); }
.grp-set-tint.on { border-color: var(--chalk); }
.grp-set-shared { flex: none; color: var(--dim-2); font-family: var(--mono); font-stretch: 75%; font-size: 10.5px;
  text-transform: uppercase; letter-spacing: .06em; }
/* La popup de réglages d'une catégorie (double-clic) : nom, position, suppression. */
.fset { display: flex; flex-direction: column; gap: 13px; }
.fset-del { justify-content: center; margin-top: 2px; }

/* Le sélecteur de dossier dans l'en-tête de la note : borné pour ne pas pousser
   le titre. Le nom tronque (`.trunc`). */
.note-fold-pick .dd-b { max-width: 132px; }

/* Le repère de dossier sur une ligne de la liste (surtout en « Tout »). */
.fold-chip { display: inline-flex; align-items: center; gap: 3px; }
.fold-chip .ico { width: 11px; height: 11px; }

/* Le menu d'un dossier (clic droit / appui long) : renommer, privé, supprimer. */
.fmenu {
  display: flex; flex-direction: column; padding: 4px; min-width: 184px;
  background: var(--ink-2); border: 1px solid var(--line-2); border-radius: var(--r);
  box-shadow: 0 12px 34px rgba(0, 0, 0, .5);
}
.fmenu-i {
  display: flex; align-items: center; gap: 9px; padding: 8px 10px;
  border-radius: var(--r); color: var(--chalk); text-align: left; background: none;
  transition: background var(--fast) var(--ease);
}
.fmenu-i:hover { background: var(--ink-4); }
.fmenu-i.danger { color: var(--rd); }
.fmenu-i .ico { width: 16px; height: 16px; }

/* La création d'un dossier : les deux segments de visibilité + l'aide. */
.fold-seg {
  display: inline-flex; gap: 2px; background: var(--ink-3);
  border-radius: var(--r); padding: 3px;
}
.fold-seg .seg { min-height: 32px; }
.fold-hint { margin-top: 10px; color: var(--dim-2); font-size: 12px; line-height: 1.4; }

/* « Rien trouvé », dans la liste elle-même — pas un écran de page : le champ de
   recherche qui l'a provoqué doit rester sous les doigts. */
.note-none {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 34px 14px; text-align: center;
  color: var(--dim-2); font-size: 12.5px;
}
.note-none .ico { width: 22px; height: 22px; opacity: .5; }
.note-none-s { font-family: var(--mono); font-stretch: 75%; font-size: 10.5px; opacity: .7; }
/* Dossier vide : pas de laïus, un petit bouton rouge qui appelle à créer. */
.note-none-cta {
  display: inline-flex; align-items: center; gap: 4px;
  margin-top: 2px; padding: 4px 10px 4px 8px; border-radius: 999px;
  font-family: var(--mono); font-stretch: 75%; font-size: 11px; letter-spacing: .04em;
  color: var(--rd); background: color-mix(in srgb, var(--rd) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--rd) 34%, transparent);
  transition: background var(--fast) var(--ease);
}
.note-none-cta:hover { background: color-mix(in srgb, var(--rd) 24%, transparent); }
.note-none-cta .ico { width: 15px; height: 15px; }

.notes-items { flex: 1; overflow-y: auto; padding: 5px; display: flex; flex-direction: column; gap: 1px; }
.note-item {
  position: relative;
  display: block; width: 100%; text-align: left;
  padding: 9px 10px; border-radius: var(--r);
  color: var(--dim);
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.note-item:hover { background: var(--ink-3); color: var(--chalk); }
.note-item.on { background: var(--ink-3); color: var(--chalk);
                box-shadow: inset 2px 0 0 var(--rd); border-radius: 0; }
.note-item-k { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
               letter-spacing: .06em; font-size: 15px; }
.note-item-d { display: block; margin-top: 2px; font-family: var(--mono); font-stretch: 75%;
               font-size: 10px; color: var(--dim-2); }
/* Le coffre de partage : même place que l'ancien nom d'auteur, mais plus affirmé
   (ce n'est plus une info secondaire, c'est LE statut de la note). */
/* Note PARTAGÉE à un coffre : icône « group » + nom du coffre en ROUGE — le
   partage se repère d'un coup d'œil, sans lire. Suivi du délai d'édition. */
.note-item-coffre { display: inline-flex; align-items: center; gap: 4px; color: var(--rd);
  vertical-align: bottom; margin-right: 7px; }   /* espace le coffre du délai — plus de point */
.note-item-coffre .ico { width: 13px; height: 13px; flex: none; }

/* `color` est POSÉ ici, et ce n'est pas une redite de `body`.

   Une propriété personnalisée s'hérite, mais `color: var(--chalk)` se RÉSOUT là
   où il est écrit. `body` le déclare, donc la couleur y devient #EDF1F2 une fois
   pour toutes, et tout ce qui descend hérite de cette VALEUR — pas de la
   référence à la variable. Redéfinir `--chalk` plus bas ne réveille personne :
   plus rien ne la relit.

   D'où le mode papier, hier : fond blanc, encre blanche. La variable était bien
   changée ; il n'y avait simplement aucun lecteur dans le volet. Cette ligne est
   ce lecteur — elle relit `--chalk` DANS le volet, donc dans la portée où
   `.paper` la redéfinit. */
.note-pane {
  display: flex; flex-direction: column; min-width: 0; min-height: 0;
  background: var(--ink);
  color: var(--chalk);
}

/* ── LE MODE PAPIER ───────────────────────────────────────────────────────
   Une seule règle, et tout le volet suit : l'en-tête, la barre d'outils,
   l'éditeur, le lecteur audio, les barres de taille des médias. C'est le
   bénéfice d'avoir peint l'app entière avec des variables — le thème n'est pas
   repeint pièce par pièce, il est REDÉFINI, ici et nulle part ailleurs.

   Il ne bascule QUE la rédaction. Un thème clair global dissoudrait le poste de
   commandement pour servir un besoin qui n'existe que dans une note : le fil,
   la grille et le tableau sont faits pour être surveillés, pas rédigés.

   L'encre est `#0B0D0E` — exactement le fond de l'app. Le noir du poste de
   commandement devient l'encre sur la feuille : le mode clair reste de la même
   maison, ce n'est pas un autre logiciel qui s'ouvre par-dessus.

   Le papier n'est pas crème : c'est un blanc froid, celui d'un tirage
   technique, pas celui d'un roman. Et le rouge ne bouge pas — il appartient à
   la marque, ici comme partout. */
.note-pane.paper {
  --ink:    #F2F4F5;
  --ink-2:  #E8EBEC;
  --ink-3:  #DEE2E4;
  --ink-4:  #D3D8DA;
  --chalk:  #0B0D0E;
  --dim:    #565F63;
  --dim-2:  #838C90;
  --line:   #D4D9DB;
  --line-2: #B8C0C3;
  /* Le CYAN des titres se délave sur du blanc : une variante FONCÉE pour le papier
     (les titres `.note-ed h1/h2/h3` sont en `--cy`). */
  --cy:     #0E7490;
}
/* Un texte que l'utilisateur a coloré en BLANC (ancien) reste blanc sur le papier
   → illisible. On le rattrape en mode papier : `execCommand("foreColor")` écrit
   `rgb(237, 241, 242)` — on le ramène à l'encre du papier. (Le blanc n'est plus
   proposé dans la palette, mais les vieilles notes en ont.) */
.note-pane.paper .note-ed [style*="rgb(237, 241, 242)"] { color: var(--chalk) !important; }
/* Le rouge à 55 % derrière du blanc marchait sur du noir ; sur du papier, il
   fallait choisir entre lire le texte et voir la sélection. */
.note-pane.paper ::selection { background: rgba(255, 42, 42, .26); color: #0B0D0E; }
.note-top {
  display: flex; align-items: center; gap: 6px;
  min-height: var(--note-hd); padding: 0 9px; border-bottom: 1px solid var(--line);
  background: var(--ink-2); flex: none; box-sizing: border-box;
}
/* La flèche « retour à la liste » n'existe QUE sur mobile (voir le bloc ≤640). */
.note-back { display: none; }
.note-title {
  flex: 1; min-width: 0;
  background: transparent; border: none; outline: none;
  color: var(--chalk);
  font-size: 16px; font-weight: 800; font-stretch: 72%;
  text-transform: uppercase; letter-spacing: .1em;
  padding: 5px 3px;
}
.note-title:focus { box-shadow: inset 0 0 0 1px var(--rd); }
.note-count {
  flex: none; font-family: var(--mono); font-stretch: 75%;
  font-size: 10.5px; color: var(--dim-2);
}
/* L'état de l'enregistrement. Sans lui, un enregistrement automatique est
   indiscernable d'une perte de données : on ne sait pas si c'est parti. */
.note-state {
  display: inline-flex; align-items: center; gap: 5px; flex: none;
  font-size: 11px; font-weight: 700; font-stretch: 74%;
  text-transform: uppercase; letter-spacing: .08em;
  color: var(--dim-2);
}
/* Le mot descend de 2 px : son oeil optique remontait par rapport au centre de
   l'icône « vu ». */
.note-state > span:last-child { padding-top: 2px; }
.note-state .ico { width: 14px; height: 14px; }
.note-state.saved { color: var(--gr); }
.note-state.offline { color: var(--am); }
.note-state.bad { color: var(--rd-2); }

.note-tools {
  display: flex; gap: 1px; padding: 4px 7px;
  border-bottom: 1px solid var(--line); background: var(--ink-2); flex: none;
}
.note-tools .btn-ico { width: 28px; height: 28px; }
.note-tools .ico { width: 17px; height: 17px; }
/* Un séparateur entre GROUPES d'outils (format · couleurs · structure · liens ·
   tableaux) : un trait fin, vertical, pour organiser une barre bien remplie. */
.tool-sep { flex: none; align-self: center; width: 1px; height: 18px; background: var(--line); margin: 0 5px; }
.note-ed {
  flex: 1; min-height: 0; overflow-y: auto;
  padding: 20px 22px;
  /* 16px : un cahier des charges se lit sur trois écrans, pas en diagonale.
     500 : Archivo est une police VARIABLE (100-900), donc 500 est un vrai
     dessin, pas un gras simulé. Le 400 par défaut se délave sur un fond noir —
     un texte clair sur sombre paraît toujours plus fin qu'il ne l'est. */
  font-size: 16px; line-height: 1.75; font-weight: 500;
  outline: none;
}
.note-ed:empty::before { content: attr(data-ph); color: var(--dim-2); }
.note-ed ul, .note-ed ol { padding-left: 22px; margin: 8px 0; }
.note-ed b, .note-ed strong { font-weight: 800; }   /* le corps est à 500 */
.note-ed [style*="background"] { color: #12100A !important; }
/* Le séparateur : un trait, pas un relief. `border:0` d'abord — le <hr> par
   défaut du navigateur est une gravure en deux tons (inset), le seul endroit de
   l'app qui aurait du volume. Il lit `--line`, donc le mode papier le repeint
   sans qu'on ait à le prévoir : c'est la palette qui est redéfinie, pas la règle. */
.note-ed hr {
  border: 0; border-top: 1px solid var(--line);
  margin: 18px 0;
}
/* Le markdown : listes visibles, titres bleus et gras, liens, tableaux. */
.note-ed ul { list-style: disc; }
.note-ed ol { list-style: decimal; }
.note-ed li { margin: 2px 0; }
.note-ed h1, .note-ed h2, .note-ed h3 {
  color: var(--cy); font-weight: 800; line-height: 1.3; margin: 12px 0 4px;
}
.note-ed h1 { font-size: 22px; }
.note-ed h2 { font-size: 19px; }
.note-ed h3 { font-size: 16.5px; }
/* Un LIEN : ROUGE, souligné en rouge. Le rouge est réservé partout ailleurs
   (marque / cassé) ; ici il dit « ça mène quelque part », et c'est voulu. */
.note-ed a {
  color: var(--rd); text-decoration: underline; text-decoration-color: var(--rd);
  text-underline-offset: 2px; cursor: pointer; word-break: break-word;
}
/* Un texte à COULEUR de police garde SA couleur sur son soulignement (et son
   barré) : `currentColor` suit la couleur posée, au lieu du soulignement par
   défaut. Vaut pour le span coloré comme pour un <u> qui en hérite. */
.note-ed [style*="color"] { text-decoration-color: currentColor; }
.note-ed u { text-decoration-color: currentColor; }
/* Une cible d'ancre : un pointillé discret. Stylée par l'ATTRIBUT `data-anchor`
   (pas une classe — le nettoyeur la retire), donc le marqueur PERSISTE. */
.note-ed [data-anchor] { border-bottom: 1px dotted var(--cy); }
.anchor-hit { animation: anchor-flash 1.2s var(--ease); border-radius: var(--r); }
@keyframes anchor-flash {
  0%, 100% { background: transparent; }
  25% { background: color-mix(in srgb, var(--cy) 28%, transparent); }
}
/* Les tableaux simples : coins vifs, un trait par cellule (comme partout). Le
   texte d'une cellule COLORÉE passe en sombre via `.note-ed [style*="background"]`
   plus haut — lisible sur la teinte claire, comme le surligneur. */
.note-ed table { border-collapse: collapse; margin: 12px 0; max-width: 100%; }
.note-ed td, .note-ed th {
  border: 1px solid var(--line-2); padding: 5px 9px;
  min-width: 46px; vertical-align: top; line-height: 1.5;
}
.note-ed th { font-weight: 800; background: var(--ink-3); }
/* Pleine largeur (bouton dédié, persiste par data-full) : le tableau prend toute
   la largeur, colonnes ÉGALES (table-layout: fixed). */
.note-ed table[data-full] { width: 100%; table-layout: fixed; }
.note-ed table[data-full] td, .note-ed table[data-full] th { min-width: 0; }

/* La petite modale de saisie (lien, ancre). */
.modal-prompt { max-width: 430px; }
.prompt-b { display: flex; flex-direction: column; gap: 8px; }
.prompt-in {
  width: 100%; padding: 9px 11px;
  background: var(--ink); border: 1px solid var(--line-2); border-radius: var(--r);
  color: var(--chalk); font-size: 13.5px; font-family: var(--mono); font-stretch: 90%;
}
.prompt-in:focus { outline: none; border-color: var(--cy); }
.prompt-in::placeholder { color: var(--dim-2); }

/* ══════════════════════════════════════════════════════════════════════════
   LE CALENDRIER
   ══════════════════════════════════════════════════════════════════════════ */
.cal-tools { display: flex; align-items: center; gap: 7px; margin-bottom: 12px; flex-wrap: wrap; }
.cal-m {
  font-weight: 800; font-stretch: 72%; font-size: 15px;
  text-transform: uppercase; letter-spacing: .1em;
  min-width: 175px; text-align: center;
}
/* minmax(0,1fr), et surtout PAS 1fr : « 1fr » vaut « minmax(auto, 1fr) », dont
   la largeur minimale est celle du contenu. Un titre d'événement un peu long
   imposait sa largeur à toute la colonne, et la grille débordait — samedi et
   dimanche sortaient de l'écran, sans une seule erreur. */
/* ══════════════════════════════════════════════════════════════════════════
   LE CHAMP « QUAND » ET SA POPUP

   La popup est celle de tally — deux mois, une plage en deux clics — mais elle
   arrive ici, donc elle s'habille ici : angles vifs (`--r`), rouge de la
   maison, stencil sur les mois. Reprendre le geste n'est pas reprendre la peau
   d'un autre projet.
   ══════════════════════════════════════════════════════════════════════════ */
.when-b {
  display: flex; align-items: center; gap: 8px; min-width: 0; flex: 1;
  height: var(--ctl-h); padding: 0 11px;
  background: var(--ink); border: 1px solid var(--line); border-radius: var(--r);
  color: var(--chalk); text-align: left;
  font-family: var(--mono); font-stretch: 75%; font-size: 13px;
  transition: border-color var(--fast) var(--ease);
}
.when-b:hover { border-color: var(--line-2); }
.when-b:focus-visible { border-color: var(--rd); outline: none; }
.when-ico { display: grid; place-items: center; flex: none; color: var(--dim-2); }
.when-ico .ico { width: 15px; height: 15px; }
.when-b:hover .when-ico { color: var(--rd); }


.datepop {
  position: fixed; z-index: 90;
  background: var(--ink-2); border: 1px solid var(--line-2); border-radius: var(--r);
  box-shadow: 0 18px 44px rgba(0,0,0,.6);
  padding: 10px;
}
.dp-nav { display: flex; align-items: center; gap: 4px; margin-bottom: 8px; }
.dp-t {
  flex: 1; text-align: center;
  font-weight: 700; font-stretch: 68%; text-transform: uppercase;
  letter-spacing: .12em; font-size: 11px; color: var(--chalk);
}
.dp-x { margin-left: 2px; }
.dp-mos { display: flex; gap: 18px; }
.dp-mo-h {
  text-align: center; margin-bottom: 6px;
  font-weight: 700; font-stretch: 68%; text-transform: uppercase;
  letter-spacing: .1em; font-size: 10px; color: var(--dim-2);
}
.dp-grid { display: grid; grid-template-columns: repeat(7, 30px); gap: 2px; }
.dp-dow {
  text-align: center; font-size: 9px; color: var(--dim-2);
  font-weight: 700; font-stretch: 68%; letter-spacing: .08em;
}
.dp-d {
  height: 28px; border-radius: var(--r);
  font-family: var(--mono); font-stretch: 75%; font-size: 12px;
  color: var(--dim); background: transparent;
  transition: background var(--fast) var(--ease);
}
.dp-d.void { visibility: hidden; }
.dp-d:hover { background: var(--ink-4); color: var(--chalk); }
.dp-d.today { color: var(--rd); font-weight: 700; }
/* La plage : le dedans est teinté, les deux bornes sont pleines. Pas de pilule
   arrondie — ici rien n'est arrondi, et un fil de sélection carré se lit
   exactement pareil. */
.dp-d.inr { background: color-mix(in srgb, var(--rd) 18%, transparent); color: var(--chalk); }
.dp-d.bnd { background: var(--rd); color: #fff; font-weight: 700; }
.dp-d.bnd.a { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.dp-d.bnd.b { border-top-left-radius: 0; border-bottom-left-radius: 0; }
.dp-foot {
  display: flex; align-items: center; gap: 8px;
  margin-top: 10px; padding-top: 9px; border-top: 1px solid var(--line);
}
.dp-hint { flex: 1; font-size: 11px; color: var(--dim-2); }

/* Six SEMAINES empilées, plus une grille plate de 42 cases : un fil ne se tire
   pas à travers une grille plate. Chaque semaine a son fond (les sept cases) et
   sa couche d'événements par-dessus, dans le même repère de sept colonnes —
   c'est ce qui permet à une barre d'aller du mardi au vendredi d'un seul tenant.

   `minmax(0, 1fr)` partout : `1fr` seul vaut `minmax(auto, 1fr)`, dont la
   largeur minimale suit le contenu — un titre long poussait la colonne et le
   calendrier perdait samedi et dimanche, sans une erreur. */
.cal { display: flex; flex-direction: column; gap: 4px; }
.cal-h { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 4px; }
.cw { position: relative; }
.cw-bg { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 4px; }
/* La couche des fils, posée SUR les cases. Elle ne prend pas les clics — sauf
   les barres elles-mêmes : le reste de la case doit rester cliquable, sinon on
   ne pourrait plus ouvrir un jour dès qu'un événement le traverse. */
.cw-ev {
  position: absolute; left: 0; right: 0; top: 26px; bottom: 4px;
  display: grid; grid-template-columns: repeat(7, minmax(0, 1fr));
  grid-auto-rows: 18px; gap: 4px 4px; align-content: start;
  pointer-events: none;
}
.cal .h { color: var(--dim-2); font-size: 11px; text-align: center; padding: 4px;
          font-weight: 700; font-stretch: 68%; text-transform: uppercase; letter-spacing: .12em; }
/* 114 px : la date (26) + trois voies de fils (3 × 18 + les gouttières) + la
   ligne des « +N ». La case ne se calcule pas au jugé — si elle rétrécit, les
   fils du bas sortent de leur semaine et vont se poser sur la suivante. */
.cday {
  background: var(--ink-2); border: 1px solid var(--line); border-radius: var(--r);
  min-height: 114px; padding: 6px;
  display: flex; flex-direction: column; gap: 2px;
  text-align: left;
  transition: border-color var(--fast) var(--ease);
}
.cday:hover { border-color: var(--line-2); }
.cday.out-m { opacity: .32; }
.cday .d { font-size: 12px; color: var(--dim-2); display: flex; align-items: center;
           justify-content: space-between; gap: 4px; font-family: var(--mono); font-stretch: 75%; }
.cday.today .d { color: var(--rd); font-weight: 700; }

/* Le jour présélectionné au clavier. Il n'est pas « aujourd'hui » et il n'est
   pas un survol : c'est là où les flèches ont amené le regard, et Entrée
   l'ouvre. Sans ce liseré, les flèches ne feraient rien de visible. */
.cday.sel { box-shadow: inset 0 0 0 2px var(--rd); }
.cday.sel.today { box-shadow: inset 0 0 0 2px var(--rd); }

/* La ligne visée par ↑/↓ dans la popup d'un jour. */
.evs .ev.kb { box-shadow: inset 0 0 0 1px var(--rd); }
.cday.today { border-color: rgba(255,42,42,.45); }
.cday-new { width: 7px; height: 7px; border-radius: 50%; background: var(--rd); flex: none; }
/* ── LE FIL D'UN ÉVÉNEMENT QUI DURE ───────────────────────────────────────
   Une barre pleine, à la teinte de son sujet, tirée d'un jour à l'autre. Elle
   porte l'encre calculée sur son fond (`inkOn` côté JS ne sert qu'aux tuiles ;
   ici la teinte est adoucie, donc l'encre reste la craie).

   Le fond est un `color-mix` et non une couleur écrite : le jour où l'équipe
   crée un huitième sujet, sa barre existe sans qu'on touche à cette feuille.
   C'est la leçon des `.t-cy` / `.t-gr`. */
.cbar {
  pointer-events: auto;
  display: flex; align-items: center; gap: 5px; min-width: 0;
  padding: 0 6px; height: 18px;
  font-size: 11px; line-height: 1;
  text-align: left;
  color: var(--chalk);
  background: color-mix(in srgb, var(--tone) 30%, var(--ink-2));
  border-left: 2px solid var(--tone);
  border-radius: var(--r);
  transition: background var(--fast) var(--ease);
}
.cbar:hover { background: color-mix(in srgb, var(--tone) 46%, var(--ink-2)); }
.cbar .cbar-i { display: grid; place-items: center; flex: none; color: var(--tone); }
.cbar .cbar-i .ico { width: 11px; height: 11px; }
.cbar .trunc { min-width: 0; }
/* Un fil coupé par le bord de la semaine le DIT : pas de bord, pas d'angle, il
   part ailleurs. Sans ça, un événement de dix jours ressemblerait à deux
   événements de cinq. */
.cbar.cut-l { border-left: 0; border-top-left-radius: 0; border-bottom-left-radius: 0; padding-left: 8px; }
.cbar.cut-r { border-top-right-radius: 0; border-bottom-right-radius: 0;
              -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 10px), transparent);
              mask-image: linear-gradient(to right, #000 calc(100% - 10px), transparent); }
/* Pas encore vu : la tranche rouge du fil, comme sur le fil des événements. */
.cbar.neuf { box-shadow: inset 0 0 0 1px rgba(255,42,42,.55); }
.cbar-more {
  pointer-events: auto;
  font-size: 10px; color: var(--dim-2);
  font-family: var(--mono); font-stretch: 75%;
  text-align: left; padding: 0 6px; height: 18px;
}
.cbar-more:hover { color: var(--chalk); }

/* ══ LA VUE SEMAINE (façon Apple Calendar) ══════════════════════════════════
   Sept colonnes (lundi → dimanche), chacune = un en-tête de jour + une pile de
   BLOCS D'ÉVÉNEMENTS COLLÉS. Pas de grille horaire, pas de défilement : la journée
   se lit d'un coup. */
.cw7-blocks {
  display: grid; grid-template-columns: repeat(7, var(--cw7-w, minmax(0, 1fr)));
  border: 1px solid var(--line); border-radius: var(--r);
  overflow-x: auto; overflow-y: visible;   /* largeur fixe → défilement horizontal */
  align-items: start;
}
.cw7-col { border-left: 1px solid var(--line); min-width: 0; }
.cw7-col:first-child { border-left: none; }
.cw7-col.today { background: color-mix(in srgb, var(--rd) 4%, transparent); }
.cw7-day {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  width: 100%; padding: 7px 2px 6px;
  background: var(--ink-2); border: none; border-bottom: 1px solid var(--line);
  color: var(--dim); cursor: pointer; box-sizing: border-box;
  transition: background var(--fast) var(--ease);
}
.cw7-day:hover { background: var(--ink-3); }
.cw7-dow { font-size: 10px; font-weight: 700; font-stretch: 68%; text-transform: uppercase; letter-spacing: .1em; }
.cw7-num { font-family: var(--mono); font-stretch: 75%; font-size: 15px; color: var(--chalk);
  width: 26px; height: 26px; display: grid; place-items: center; border-radius: 50%; }
.cw7-day.today .cw7-num { background: var(--rd); color: #fff; }
.cw7-day.today .cw7-dow { color: var(--rd); }

/* La pile de blocs — COLLÉS (aucun écart), un SÉPARATEUR GRIS entre chacun. */
.cw7-evs { display: flex; flex-direction: column; gap: 0; }
.cw7-ev {
  display: flex; flex-direction: column; gap: 2px; width: 100%; text-align: left;
  padding: 7px 9px; border: none; border-top: 1px solid var(--line);
  border-left: 3px solid var(--tone);
  background: color-mix(in srgb, var(--tone) 16%, transparent);
  color: var(--chalk); cursor: pointer; box-sizing: border-box;
  transition: background var(--fast) var(--ease);
}
.cw7-col > .cw7-evs > .cw7-ev:first-child { border-top: none; }
.cw7-ev:hover { background: color-mix(in srgb, var(--tone) 28%, transparent); }
/* L'heure AU-DESSUS du titre ; le titre sur 2 lignes maximum. */
.cw7-ev-time { font-family: var(--mono); font-stretch: 75%; font-size: 13px; color: var(--dim); }
.cw7-ev-t {
  font-size: 15px; line-height: 1.2; font-weight: 600; min-width: 0;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.cw7-empty { display: block; padding: 10px 8px; color: var(--dim-2); font-size: 12px; text-align: center; }
@media (max-width: 720px) {
  .cw7-dow { font-size: 9px; }
  .cw7-num { font-size: 13px; width: 22px; height: 22px; }
}

/* ══════════════════════════════════════════════════════════════════════════
   LES TROPHÉES — le mur, puis la ligne de mire.

   Le rang (bronze/argent/or/platine) porte la couleur. C'est ce qui règle
   « ils se ressemblent tous » : un mur de médailles identiques ne raconte
   rien ; un mur où l'or se voit de loin, si.
   ══════════════════════════════════════════════════════════════════════════ */
.tro-score {
  display: inline-flex; align-items: baseline; gap: 3px;
  font-family: var(--mono); font-stretch: 75%;
}
.tro-score-n { font-size: 21px; font-weight: 700; color: var(--am); }
.tro-score-d { font-size: 12px; color: var(--dim-2); }

/* Pas de trait à droite du titre : il ne séparait rien — la grille en dessous
   se sépare déjà toute seule. Un filet qui court sur 1200 px pour accompagner
   trois mots, c'est une ligne de plus à regarder. */
.section-h { display: flex; align-items: baseline; gap: 10px; margin: 26px 0 12px; }
.section-h:first-of-type { margin-top: 4px; }
.section-h h2 { flex: none; text-transform: uppercase; font-stretch: 70%;
                letter-spacing: .11em; font-size: 13px; }
.section-n { color: var(--dim-2); font-family: var(--mono); font-stretch: 75%; font-size: 11px; }

.tro-none {
  display: flex; align-items: center; gap: 10px;
  padding: 18px; color: var(--dim-2); font-size: 13px;
  border: 1px dashed var(--line); border-radius: var(--r);
}
.tro-none .ico { width: 18px; height: 18px; }

/* ── LE MUR ───────────────────────────────────────────────────────────────
   Des cartes de trophée, pas des lignes de tableau.

   Chaque carte porte SA couleur en grand : le dégradé, le halo, l'emblème, le
   liseré, tout descend de --c (posée en JS depuis le rang). Une carte qui se
   distingue par un détail de 12 px ne se distingue pas — c'est ce qui donnait
   ce mur uniforme où tout se ressemblait.

   Le balayage passe une fois au survol. Il ne décore pas : il donne à la carte
   la matière d'un objet en métal, ce qui est exactement ce qu'un trophée doit
   avoir l'air d'être. */
.wall {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 10px;
}
.medal {
  position: relative; overflow: hidden; isolation: isolate;
  display: flex; flex-direction: column; align-items: center;
  gap: 8px; padding: 18px 14px 14px; text-align: center;
  border: 1px solid color-mix(in srgb, var(--c) 45%, transparent);
  border-radius: var(--r);
  background:
    radial-gradient(120% 90% at 50% 0%, color-mix(in srgb, var(--c) 30%, transparent), transparent 70%),
    linear-gradient(180deg, color-mix(in srgb, var(--c) 12%, var(--ink-2)), var(--ink));
  transition: transform var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.medal:hover {
  transform: translateY(-2px);
  border-color: var(--c);
}
/* Le halo derrière l'emblème : c'est lui qui fait la lumière de la carte. */
.medal-glow {
  position: absolute; top: -34px; left: 50%; transform: translateX(-50%);
  width: 130px; height: 130px; border-radius: 50%;
  background: radial-gradient(circle, color-mix(in srgb, var(--c) 42%, transparent), transparent 65%);
  pointer-events: none; z-index: -1;
}
.medal-i { color: var(--c); filter: drop-shadow(0 0 12px color-mix(in srgb, var(--c) 60%, transparent)); }
.medal-i .ico { width: 44px; height: 44px; }
.medal-b { min-width: 0; width: 100%; }
.medal-rk {
  display: block;
  font-size: 10px; font-weight: 800; font-stretch: 68%;
  text-transform: uppercase; letter-spacing: .22em;
  color: var(--c);
}
.medal-v {
  display: block; margin-top: 4px;
  font-family: var(--mono); font-stretch: 75%;
  font-size: 16px; font-weight: 700; color: var(--chalk);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.medal-n {
  display: block; font-size: 11.5px; color: var(--dim); margin-top: 3px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.medal-at {
  font-family: var(--mono); font-stretch: 75%; font-size: 10px; color: var(--dim-2);
  padding-top: 7px; width: 100%;
  border-top: 1px solid color-mix(in srgb, var(--c) 22%, transparent);
}
/* Le balayage. Une seule passe, au survol — un reflet qui court sur du métal. */
.medal-shine {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(105deg, transparent 38%,
              color-mix(in srgb, var(--c) 22%, transparent) 47%,
              rgba(255,255,255,.16) 50%,
              color-mix(in srgb, var(--c) 22%, transparent) 53%, transparent 62%);
  transform: translateX(-120%);
}
.medal:hover .medal-shine { animation: shine 700ms var(--ease); }
@keyframes shine { to { transform: translateX(120%); } }

/* Le platine respire. C'est le seul rang qui bouge tout seul : il n'y en a
   qu'un ou deux dans une vie d'équipe, et il a le droit de se faire voir. */
.m-platine { animation: plat 3.4s var(--ease) infinite; }
@keyframes plat {
  50% { border-color: color-mix(in srgb, var(--c) 90%, transparent);
        box-shadow: 0 0 22px -6px color-mix(in srgb, var(--c) 55%, transparent); }
}

@media (prefers-reduced-motion: reduce) {
  .medal-shine { display: none; }
}

/* ── LA LIGNE DE MIRE ───────────────────────────────────────────────────── */
.tro-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(330px, 1fr));
  gap: 12px; align-items: start;
}
.shot {
  display: block; width: 100%; text-align: left;
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--ink-2); overflow: hidden;
  transition: border-color var(--fast) var(--ease);
}
.shot:hover { border-color: var(--line-2); }
.shot-h { display: flex; align-items: center; gap: 11px; padding: 13px; }
.shot-rk .ico { width: 24px; height: 24px; }
.shot-n { display: block; font-size: 14.5px; font-weight: 600; line-height: 1.3;
          overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.shot-w {
  display: block; margin-top: 2px; font-size: 10.5px;
  font-weight: 700; font-stretch: 68%; text-transform: uppercase; letter-spacing: .12em;
  color: var(--dim-2);
}
.shot-t { flex: none; text-align: right; }
.shot-t-n { display: inline-flex; align-items: center; justify-content: flex-end; gap: 4px;
            font-family: var(--mono); font-stretch: 75%;
            font-size: 19px; font-weight: 700; line-height: 1.1; }
/* La valeur du jour est vive ; le « / » et le palier visé restent en retrait —
   on lit d'abord où on en est, puis ce qu'on vise. */
.shot-cur { color: var(--chalk); }
.shot-sep { color: var(--dim-2); font-weight: 400; }
.shot-tgt { color: var(--dim-2); }
.shot-t-u { display: block; margin-top: 1px; font-size: 10px; color: var(--dim-2); }
.shot-bar { height: 3px; background: var(--ink-4); }
.shot-fill { display: block; height: 100%; transition: width var(--med) var(--ease); }
.shot-f {
  display: flex; align-items: center; gap: 8px;
  padding: 9px 13px; font-size: 12px; color: var(--dim-2);
}
.shot-pct { font-family: var(--mono); font-stretch: 75%; }
.shot-gap b { color: var(--chalk); font-weight: 700; }

/* ── LA POPUP DES RACCOURCIS ─────────────────────────────────────────────
   Ils n'apparaissent nulle part ailleurs : c'est ici, ou pas du tout. */
.keys { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 16px; }
.keys-s { min-width: 0; }
.keys-l { display: flex; flex-direction: column; gap: 1px; margin-top: 7px; }
.keys-r { display: flex; align-items: center; gap: 10px; padding: 4px 0; }
.keys-k {
  flex: none; min-width: 74px;
  font-family: var(--mono); font-stretch: 75%; font-size: 11px;
  color: var(--chalk); background: var(--ink); border: 1px solid var(--line);
  border-radius: var(--r); padding: 3px 6px; text-align: center;
}
.keys-w { font-size: 12.5px; color: var(--dim); min-width: 0; }

/* ══════════════════════════════════════════════════════════════════════════
   MODALE, TOASTS, VIDE
   ══════════════════════════════════════════════════════════════════════════ */
.veil {
  position: fixed; inset: 0; z-index: 60;
  display: grid; place-items: center; padding: 20px;
  background: rgba(0,0,0,.76);
  overflow-y: auto;
}
.modal {
  width: 100%; max-width: 470px;
  background: var(--ink-2);
  border: 1px solid var(--line-2); border-radius: var(--r);
  padding: 18px;
  box-shadow: 0 20px 50px -12px rgba(0,0,0,.9);
  animation: rise 140ms var(--ease);
  margin: auto;
}
.modal-wide { max-width: 700px; }
.modal-h { display: flex; align-items: center; gap: 9px; margin-bottom: 15px; }
.modal-h h2 { flex: 1; min-width: 0; text-transform: uppercase; font-stretch: 70%;
              letter-spacing: .11em; font-size: 14px; }
.modal-ico { color: var(--rd); }
.modal-act { display: flex; gap: 7px; justify-content: flex-end; margin-top: 16px; flex-wrap: wrap; }

/* LA NOTE D'UN GROUPE (todo) — icône qui s'allume quand la note est remplie, et
   la zone de texte libre de sa popup. Même grammaire que le reste : un seul
   rayon, le rouge de marque au focus. */
.grp-note-btn.on { color: var(--rd); }
/* Même taille que la poubelle (18px) — l'icône note paraissait plus grosse. */
.grp-note-btn .ico { width: 18px; height: 18px; }
.grp-note-modal { max-width: 560px; }
.grp-note-ta {
  width: 100%; min-height: 260px;
  background: var(--ink); color: var(--chalk);
  border: 1px solid var(--line-2); border-radius: var(--r);
  padding: 12px 13px; line-height: 1.6; font-size: 14px;
}
.grp-note-ta::placeholder { color: var(--dim-2); }
.grp-note-ta:focus { outline: none; border-color: var(--rd); }

/* DÉPLACER UN TABLEAU vers un autre coffre — la liste des coffres cible. */
.movev-modal { max-width: 440px; }
.movev-hint { color: var(--dim); line-height: 1.6; font-size: 13px; margin-bottom: 12px; }
.movev-list { display: flex; flex-direction: column; gap: 6px; }
.movev-opt {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 11px 12px; text-align: left;
  background: var(--ink); border: 1px solid var(--line-2); border-radius: var(--r);
  color: var(--chalk); cursor: pointer;
}
.movev-opt:hover { border-color: var(--rd); background: var(--ink-3); }
.movev-ico { display: inline-flex; color: var(--dim); }
.movev-nm { flex: 1; min-width: 0; }
.movev-tag {
  font-size: 11px; text-transform: uppercase; letter-spacing: .08em;
  color: var(--rd-2); border: 1px solid var(--line-2); border-radius: var(--r);
  padding: 1px 6px;
}

/* LE PARTAGE À 3 OPTIONS — Perso / Coffre / Personnes. */
.share-modal { max-width: 460px; }
.share-body { display: flex; flex-direction: column; gap: 14px; }
.share-seg { width: 100%; }
.share-seg .seg { flex: 1; justify-content: center; gap: 7px; }
.share-seg-i { display: inline-flex; }
.share-seg-i .ico { width: 15px; height: 15px; }
.share-pane { min-height: 56px; }
.share-hint { color: var(--dim); line-height: 1.6; font-size: 13px; }
/* L'onglet Utilisateurs fait UN bloc : la recherche est la première RANGÉE du
   panneau (filet dessous), la liste défile sous elle — un seul cadre. */
.share-tbl { border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; }
.share-tbl .share-q {
  margin: 0; width: 100%; background: transparent;
  border: 0; border-bottom: 1px solid var(--line); border-radius: 0;
}
.share-tbl .share-list { border: 0; border-radius: 0; }
/* La liste en TABLEAU GROUPÉ : un seul panneau bordé, rangées COLLÉES séparées
   d'un filet, compactes (32px) — un bloc qui se lit d'un regard, pas une pile de
   boutons encadrés. Le choix se marque au fond doux + coche, plus au liseré. */
.share-list {
  display: flex; flex-direction: column; gap: 0;
  border: 1px solid var(--line); border-radius: var(--r);
  max-height: 46vh; overflow-y: auto;
}
.share-opt {
  display: flex; align-items: center; gap: 9px; width: 100%;
  min-height: 32px; padding: 5px 10px; text-align: left;
  background: transparent; border: 0; border-radius: 0;
  border-top: 1px solid var(--line);
  color: var(--chalk); font-size: 13px; cursor: pointer;
}
.share-opt:first-child { border-top: 0; }
.share-opt:hover { background: var(--ink-3); }
.share-opt.on { background: color-mix(in srgb, var(--rd) 10%, transparent); }
/* La COCHE d'un coffre (choix unique) : un simple check rouge, sans boîte. */
.share-c { width: 15px; flex: none; display: inline-flex; color: var(--rd); }
.share-c .ico { width: 13px; height: 13px; }
/* La CASE d'un utilisateur (choix multiple) : cochée = il a accès. Un carré à la
   charte (2px de rayon), la coche en rouge — l'état se lit sans lire le nom. */
.share-ck {
  width: 16px; height: 16px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--line); border-radius: var(--r);
  color: var(--rd);
}
.share-ck.on { border-color: var(--rd); }
.share-ck .ico { width: 12px; height: 12px; }
.share-ico { display: inline-flex; color: var(--dim); }
.share-nm { flex: 1; min-width: 0; }
.share-dot { width: 12px; height: 12px; border-radius: 50%; flex: none; }

.toasts {
  position: fixed; z-index: 90; left: 50%; bottom: 18px;
  transform: translateX(-50%);
  display: flex; flex-direction: column; gap: 6px; align-items: center;
  pointer-events: none;
}
.toast {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 15px;
  background: var(--ink-4); border: 1px solid var(--line-2); border-radius: var(--r);
  font-size: 13.5px; font-weight: 600;
  animation: rise 140ms var(--ease);
  max-width: min(92vw, 440px);
}
.toast .ico { width: 16px; height: 16px; }
.toast.ok .ico { color: var(--gr); }
.toast.bad { border-color: var(--rd); }
.toast.bad .ico { color: var(--rd); }
/* Un toast CTA : cliquable, une flèche à droite le dit. Le survol l'éclaire. */
.toast-cta { cursor: pointer; padding-right: 10px; }
.toast-cta:hover { border-color: var(--rd); background: var(--ink-3); }
.toast-cta .toast-go { margin-left: auto; display: inline-flex; color: var(--rd); }
.toast-cta .toast-go .ico { width: 16px; height: 16px; }

.empty {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  padding: 48px 20px;
  border: 1px dashed var(--line); border-radius: var(--r);
}
.empty > span { color: var(--dim-2); margin-bottom: 10px; }
.empty .ico { width: 28px; height: 28px; }
.empty h3 { font-size: 14px; margin-bottom: 6px; text-transform: uppercase;
            font-stretch: 70%; letter-spacing: .11em; }
.empty p { color: var(--dim); font-size: 13.5px; max-width: 40ch; line-height: 1.6; margin-bottom: 14px; }
.empty p:last-child { margin-bottom: 0; }

/* ══════════════════════════════════════════════════════════════════════════
   LES ÉCRANS ÉTROITS — la sidebar passe en barre du haut.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 860px) {
  /* DEUX pistes explicites : `.side` (barre du haut) = AUTO (sa hauteur), `.main`
     = 1fr (le reste). Sans ça, `.app` n'a qu'une piste `minmax(0,1fr)` et le `.side`
     la remplit ENTIÈRE → header géant, contenu repoussé sous le pli. */
  .app { grid-template-columns: 1fr; grid-template-rows: auto minmax(0, 1fr); }
  .side {
    position: sticky; top: 0; height: auto;
    flex-direction: row; align-items: center; gap: 8px;
    border-right: none; border-bottom: 1px solid var(--line);
    padding: 8px 10px; box-sizing: border-box;
    z-index: 30;
  }
  .side-top { padding: 0; }
  .logo span { display: none; }   /* à l'étroit : le mark seul (nexus ET beta cachés) */
  .nav { flex-direction: row; gap: 2px; }
  .nav-item { padding: 9px 10px; }
  .nav-l, .nav-k { display: none; }
  .nav-item[aria-current="page"] { box-shadow: inset 0 -2px 0 var(--rd); }
  /* Le bandeau et le lecteur ne tiennent pas dans une barre : ils partent (le menu
     notifications, lui, reste). Le sélecteur de coffre redevient inline.
     ⚠️ `.side .side-player` (0,2,0), PAS `.side-player` (0,1,0) : la règle de base
     `.side-player{display:flex}` est déclarée PLUS BAS dans le fichier et gagnerait
     à spécificité égale — le lecteur restait visible sur mobile (radio « pas tuée »). */
  .board, .side-sp { display: none; }
  .side .side-player { display: none; }
  .side > .dd { display: inline-block; margin: 0; }
  .side > .dd .dd-b { width: auto; }
  .main { padding: 14px 12px 60px; }
  .notes-wrap {
    grid-template-columns: minmax(0, 1fr);
    border-left: 1px solid var(--line); border-radius: var(--r);
  }
  .notes-list { border-right: none; border-bottom: 1px solid var(--line); max-height: 190px; }
  .cal { gap: 2px; }
  .cday { min-height: 64px; padding: 3px; }
  .cev { font-size: 10px; }
}

/* ══════════════════════════════════════════════════════════════════════════
   LE TÉLÉPHONE — nexus en app mobile.

   Placé APRÈS le bloc 860 (tablette) : à ≤ 640px il le complète et le corrige.
   Le parti pris : une BARRE D'ONGLETS EN BAS (le pouce y arrive), un bandeau
   compact en haut (logo + coffre + cloche + réglages), le contenu plein cadre.
   Rien de tout ça n'existe au-dessus de 640px — le desktop est intact.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 640px) {
  /* iOS zoome sur un champ sous 16px : on force 16 pour que le focus ne fasse
     pas sauter la mise en page. */
  .input, .area, input, textarea, select { font-size: 16px; }

  /* ── LE BANDEAU DU HAUT : MINIMAL, en flux normal (PAS sticky) ──
     Le contenu de la page est la priorité : le bandeau se réduit à une rangée
     (logo · cloche · réglages), en flux normal — on annule le `sticky` du bloc
     860 pour qu'il ne flotte pas. Le sélecteur de coffre a migré dans les réglages
     (sur mobile), la radio et « qui est là » sont retirés. */
  .side {
    position: static;
    flex-wrap: nowrap; gap: 6px;
    height: 70px; padding: 0 14px;      /* header téléphone : 70px pile */
  }
  .side-top { flex: 1 1 auto; padding: 0; }
  .logo span:not(.logo-beta) { display: inline; }   /* la place existe : « nexus » */
  .who, .side > .dd { display: none; }
  /* Le repli « plein écran » du todo n'a pas de sens sur mobile et masquerait la
     barre du haut ET la nav (enfant de .side) : on le neutralise. */
  .app.side-off .side { display: flex; }

  /* ── LA BARRE D'ONGLETS, EN BAS ── */
  .nav {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
    flex-direction: row; justify-content: space-around; gap: 0;
    background: var(--ink-2); border-top: 1px solid var(--line);
    padding: 5px 4px calc(5px + env(safe-area-inset-bottom, 0px));
  }
  .nav-item {
    flex: 1; flex-direction: column; gap: 3px;
    padding: 5px 2px; min-height: 52px;
    align-items: center; justify-content: center; border-radius: var(--r);
  }
  .nav-item .ico { width: 23px; height: 23px; }
  .nav-l { display: block; font-size: 9.5px; font-weight: 700; font-stretch: 74%;
           letter-spacing: .02em; text-transform: uppercase; line-height: 1; }
  /* L'onglet courant : rouge (icône + libellé), pas un liseré sous une barre du haut
     qui n'existe plus ici. */
  .nav-item[aria-current="page"] { color: var(--rd); box-shadow: none; background: var(--ink-3); }
  .nav-item[aria-current="page"] .nav-l { color: var(--rd); }
  /* La pastille du neuf, en coin d'onglet plutôt qu'en fin de ligne. */
  .nav-n { position: absolute; top: 3px; right: 50%; margin-right: -18px; }

  /* ── LE CONTENU ── plein cadre, et de la place sous la barre d'onglets. */
  .main { padding: 12px 12px calc(74px + env(safe-area-inset-bottom, 0px)); }
  h1 { font-size: 20px; }
  .head { gap: 8px 10px; margin-bottom: 12px; }

  /* ── LES MODALES en feuille : pleine largeur, collées en bas (pouce). ── */
  .veil { padding: 0; align-items: flex-end; }
  .modal, .modal-wide, .share-modal, .movev-modal, .grp-note-modal,
  .grp-set-modal, .modal-prompt {
    max-width: 100%; width: 100%;
    /* `margin: auto` (base) absorbe l'espace libre dans la grille du voile et
       PRIME sur `align-items` → la feuille resterait centrée. On plaque en bas. */
    margin: auto 0 0;
    border-bottom-left-radius: 0; border-bottom-right-radius: 0;
    max-height: 92dvh; overflow-y: auto;
    padding-bottom: calc(18px + env(safe-area-inset-bottom, 0px));
  }
  /* La roue radio garde son centrage (elle se dimensionne déjà au viewport). */
  .veil:has(.modal-radio) { align-items: center; }
  .modal-radio { margin: auto; border-radius: 0; }

  /* ── NOTES — navigation façon Notes d'Apple ──
     Pas de deux panneaux côte à côte : on est SOIT sur la liste plein écran
     (par défaut en arrivant), SOIT dans la note plein écran (après un tap). La
     flèche « retour » (`.note-back`) ramène à la liste. `.notes-wrap` porte la
     classe `m-list` ou `m-note` (posée par le JS). */
  .notes-wrap { display: flex; flex-direction: column; border: none; border-radius: 0; }
  .notes-list {
    max-height: none; height: auto; flex: 1; min-height: 0;
    border: none; border-bottom: none;
  }
  .note-pane { flex: 1; min-height: 0; }
  .notes-wrap.m-list .note-pane { display: none; }
  .notes-wrap.m-list .notes-list { display: flex; }
  .notes-wrap.m-note .notes-list { display: none; }
  .notes-wrap.m-note .note-pane { display: flex; }
  .note-back { display: inline-grid; }   /* la flèche retour n'existe que sur mobile */
  /* Les sections des notes prennent toute la largeur ; l'éditeur colle aux bords. */
  .note-ed { padding: 16px 14px; }
  /* La BARRE D'OUTILS des notes SLIDE : une seule rangée qui défile à l'horizontale
     au lieu de déborder ou de passer sur plusieurs lignes. */
  .note-tools { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  .note-tools::-webkit-scrollbar { display: none; }
  .note-tools > * { flex: none; }

  /* ── LA FEUILLE D'ACTIONS (bouton « Actions » du header mobile) ──
     Les actions de la page, empilées pleine largeur. */
  .act-sheet { display: flex; flex-direction: column; gap: 10px; }
  .act-sheet > * { width: 100%; }
  .act-sheet .btn, .act-sheet .btn-ico {
    width: 100%; justify-content: center; min-height: 46px; box-sizing: border-box;
  }
  .act-sheet .seg-group { width: 100%; }
  .act-sheet .seg { flex: 1; justify-content: center; }
  .head-act .ico { width: 15px; height: 15px; }

  /* ── TROPHÉES ── une colonne, ça respire. (.vaults est déjà en colonne.) */
  .wall { grid-template-columns: 1fr; }

  /* ── CALENDRIER SEMAINE ── plus compact à 7 colonnes étroites. */
  .cw7-num { font-size: 12px; width: 20px; height: 20px; }
  .cw7-ev { padding: 5px 5px; gap: 3px; flex-direction: column; align-items: flex-start; }
  .cw7-ev-t { font-size: 10px; }
  .cw7-ev-time { font-size: 9px; }
}

/* ══════════════════════════════════════════════════════════════════════════
   LA VIDÉO

   Le lecteur est celui de Bunny, en iframe : il porte le HLS, les qualités, la
   barre de lecture. On ne réécrira pas ça, et on n'a rien à y gagner.

   16/9 par `aspect-ratio` plutôt que le vieux bidouillage du padding-bottom :
   l'iframe n'a pas de taille intrinsèque, et sans ratio elle s'écrase à 150 px.
   ══════════════════════════════════════════════════════════════════════════ */
.vid {
  margin-top: 10px;
  aspect-ratio: 16 / 9;
  background: var(--black);
  border-radius: var(--r);
  overflow: hidden;
  position: relative;
}
.vid iframe { width: 100%; height: 100%; border: 0; display: block; }
/* Notre placeholder, montré TOUT DE SUITE pendant que Bunny transcode. */
.vid-ph {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 12px;
  color: var(--dim); background:
    linear-gradient(135deg, var(--ink-3), var(--black));
}
.vid-ph-t { font-size: 12.5px; font-family: var(--mono); font-stretch: 75%; letter-spacing: .05em; }
.vid-spin {
  width: 26px; height: 26px; border-radius: 50%;
  border: 2px solid var(--line-2); border-top-color: var(--rd);
  animation: vid-spin 0.8s linear infinite;
}
@keyframes vid-spin { to { transform: rotate(360deg); } }

/* Le choix du fichier, dans le formulaire. */
.vid-box { display: flex; align-items: center; gap: 8px; margin-top: 10px; position: relative; }
.vid-n {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--dim); font-size: 12.5px;
}
.vid-t { margin-left: auto; flex: none; font-family: var(--mono); font-stretch: 75%;
         font-size: 10.5px; color: var(--dim-2); }
/* La barre d'avancement : posée SOUS la rangée, sur toute sa largeur. Un envoi
   de 200 Mo prend une minute — sans elle, on croit que c'est planté. */
.vid-bar {
  position: absolute; left: 0; bottom: -5px; height: 2px; width: 0;
  background: var(--rd); transition: width 200ms linear;
}

/* ── LES PHOTOS D'UN ÉVÉNEMENT ─────────────────────────────────────────────
   Une grille de vignettes carrées : on voit d'un coup ce qu'il y a, et on
   clique pour ouvrir en grand. Carrées par `aspect-ratio` + `object-fit`, pas
   par recadrage serveur — le fichier reste entier, seule la vignette est rognée. */
/* Renommées `ev-*` : « shot » entrait en collision avec les cartes de trophées
   en cours (mêmes `.shot`), dont le `aspect-ratio:1` d'ici cassait la hauteur. */
.ev-gal {
  margin-top: 10px;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 4px;
}
.ev-shot {
  aspect-ratio: 1; overflow: hidden; border-radius: var(--r);
  background: var(--ink-3); display: block;
}
.ev-shot img { width: 100%; height: 100%; object-fit: cover; display: block;
            transition: transform var(--fast) var(--ease); }
.ev-shot:hover img { transform: scale(1.05); }

/* Les vignettes DANS le formulaire, avec leur croix de retrait. */
.form-shots { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.form-shot { position: relative; width: 72px; height: 72px; border-radius: var(--r); overflow: hidden; }
.form-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.form-shot-x {
  position: absolute; top: 2px; right: 2px;
  width: 22px; height: 22px; display: grid; place-items: center;
  background: rgba(0,0,0,.6); color: #fff; border-radius: var(--r);
}
.form-shot-x .ico { width: 13px; height: 13px; }

/* ── LES MÉDIAS DANS UNE NOTE ─────────────────────────────────────────────
   L'image ne déborde jamais de la colonne : une photo de téléphone fait 4000 px
   de large, et sans `max-width` elle pousserait toute la page en travers. */
.note-ed img {
  max-width: 100%; height: auto; display: block;
  margin: 10px 0; border-radius: var(--r);
  background: var(--ink-3);        /* le temps qu'elle arrive */
}
.note-ed figure {
  position: relative; margin: 12px 0; max-width: 100%;
}
.note-ed figure .vid { margin-top: 0; }
/* Le capteur de clic sur une vidéo de note : au-dessus de l'iframe (z 2, sous la
   barre de tailles à z 3). Il prend le premier clic pour SÉLECTIONNER ; une fois
   sélectionnée, il s'efface aux clics et l'iframe se lit normalement. */
.fig-catch { position: absolute; inset: 0; z-index: 2; cursor: pointer; }
.note-ed figure.msel .fig-catch { pointer-events: none; }
.note-ed figure iframe { width: 100%; height: 100%; border: 0; display: block; }
/* La barre de tailles d'une vidéo, en haut du widget, révélée au survol. */
.fig-sz {
  position: absolute; top: 6px; left: 6px; z-index: 3; display: flex; gap: 2px;
  padding: 3px; border-radius: var(--r);
  background: rgba(11,13,14,.82); backdrop-filter: blur(4px);
  opacity: 0; transition: opacity var(--fast) var(--ease);
}
.note-ed figure:hover .fig-sz { opacity: 1; }
.fig-b {
  padding: 4px 8px; border-radius: var(--r);
  background: none; color: #cfd3d6; font-size: 11px; font-weight: 700; cursor: pointer;
}
.fig-b:hover { background: rgba(255,255,255,.12); color: #fff; }
.fig-b.on { background: var(--rd); color: #fff; }
/* Le fichier survole la note : on le dit, sinon on ne sait pas si ça va marcher. */
.note-ed.over { box-shadow: inset 0 0 0 2px var(--rd); }

.note-drop {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 14px; border-bottom: 1px solid var(--line);
  background: var(--ink-2); color: var(--dim);
  font-size: 12px; font-family: var(--mono); font-stretch: 75%;
}
.note-drop .ico { width: 15px; height: 15px; }

/* Les boutons d'insertion de la barre : icône + un mot. Distincts des icônes de
   formatage, parce qu'ils font quelque chose de plus lourd (un envoi), et parce
   qu'un bouton d'insertion sans libellé ne se trouve pas. */
/* Les insertions : icône + mot, SANS bordure ni fond — comme les outils de mise
   en forme à côté. Le fond n'apparaît qu'au survol, le temps de dire « ça se
   clique ». */
.ins {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 6px 8px; border-radius: var(--r);
  background: none; border: none; color: var(--dim);
  font-size: 11px; font-weight: 700; font-stretch: 74%;
  text-transform: uppercase; letter-spacing: .07em;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.ins:hover { background: var(--ink-3); color: var(--chalk); }
.ins.on { color: var(--rd); }
.ins .ico { width: 16px; height: 16px; }
.ins-l { white-space: nowrap; }

/* ── LE PANNEAU D'ENREGISTREMENT ──────────────────────────────────────────
   Il flotte sous la barre : point qui bat, temps, égaliseur vivant, et deux
   issues nettes. On enregistre sa voix — ça vaut mieux qu'un bouton rouge. */
.reco {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 14px; border-bottom: 1px solid var(--line);
  background: var(--rd-soft);
}
.reco-dot { flex: none; width: 10px; height: 10px; border-radius: 50%; background: var(--rd);
            box-shadow: 0 0 0 0 var(--rd); animation: reco-pulse 1.4s ease-out infinite; }
@keyframes reco-pulse {
  0% { box-shadow: 0 0 0 0 rgba(255,42,42,.5); }
  100% { box-shadow: 0 0 0 10px rgba(255,42,42,0); }
}
.reco-time { font-family: var(--mono); font-stretch: 75%; font-size: 13px; color: var(--rd-2);
             min-width: 40px; }
.reco-bars { display: flex; align-items: center; gap: 3px; height: 22px; }
.reco-bar { width: 3px; height: 100%; border-radius: 2px; background: var(--rd-2);
            transform-origin: center; animation: reco-eq .9s ease-in-out infinite; }
@keyframes reco-eq { 0%, 100% { transform: scaleY(.25); } 50% { transform: scaleY(1); } }

/* ── LE PANNEAU DE PROGRESSION VIDÉO ──────────────────────────────────────
   Une vidéo prend du temps ; on nomme chaque étape et une barre avance. Jamais
   un écran muet pendant que Bunny travaille. */
.vp {
  padding: 12px 14px; border-bottom: 1px solid var(--line);
  background: var(--ink-2);
}
.vp-row { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.vp-ico { flex: none; color: var(--rd); display: flex; }
.vp-ico .ico { width: 18px; height: 18px; }
.vp-l { font-size: 13px; color: var(--chalk); }
.vp-pct { flex: none; font-family: var(--mono); font-stretch: 75%; font-size: 12px; color: var(--dim-2); }
.vp-bar { height: 4px; border-radius: 2px; background: var(--line-2); overflow: hidden; }
.vp-fill { display: block; height: 100%; width: 0; border-radius: 2px; background: var(--rd);
           transition: width 200ms linear; }
/* Étape sans pourcentage (préparation, encodage) : la barre balaie au lieu de
   rester figée à 100 %. */
.vp.vp-wait .vp-fill {
  width: 40% !important; animation: vp-sweep 1.1s ease-in-out infinite;
}
@keyframes vp-sweep { 0% { margin-left: -40%; } 100% { margin-left: 100%; } }

/* ── LE LECTEUR AUDIO — façon Telegram ────────────────────────────────────
   Une pastille lecture, la FORME D'ONDE de la voix qu'on parcourt de gauche à
   droite, le temps, et la vitesse (1× / 1,5× / 2×). */
.na {
  display: inline-flex; flex-direction: column; gap: 7px;
  width: min(100%, 440px); margin: 10px 0; padding: 10px 14px;
  border: 1px solid var(--line); border-radius: 16px; background: var(--ink-2);
  vertical-align: middle; user-select: none;
}
/* Le composant vocal du FORMULAIRE garde la MÊME hauteur dans ses trois états
   (repos, enregistrement, lecture) : sinon la popup saute quand on démarre. Le
   plancher tient la plus haute (le lecteur), et chaque état s'y centre. */
.vr { min-height: 88px; display: flex; flex-direction: column; justify-content: center; }
/* Au repos, le composant vocal est VIDE (le bouton « Vocal » le déclenche) :
   il ne doit alors prendre AUCUNE place. Le plancher de 88 px ne vaut que
   pendant l'enregistrement et la lecture, pour ne pas sauter entre les deux. */
.vr:empty { min-height: 0; }
.form-au:has(.vr:empty) { margin: 0; }
.vr > .au-box, .vr > .reco, .vr > .na { margin: 0; width: 100%; }
/* Dans le formulaire d'événement, le lecteur audio est PLEINE LARGEUR (pas
   plafonné à 440 px), rectangle, à hauteur constante. */
.form-au .na, .form-au .reco, .form-au .au-box { width: 100%; max-width: none; }
.form-au { margin-top: 4px; }
.vr > .reco { border-bottom: none; border-radius: 999px; }
.vr > .na { border-radius: 16px; }
/* ── LE HEADER MÉDIA — fin, minimaliste, le MÊME partout ───────────────────
   Qui a publié, quand. Réutilisé pour chaque média (notes ET événements). */
.mh { display: flex; align-items: center; gap: 7px; font-size: 11.5px; }
.mh-who { font-weight: 700; font-stretch: 74%; text-transform: uppercase; letter-spacing: .06em; }
.mh-sep { color: var(--dim-2); }
.mh-when { font-family: var(--mono); font-stretch: 75%; font-size: 10.5px; color: var(--dim-2); }

/* Le header du lecteur audio hérite de .mh, plus la durée et la vitesse. */
.na-head { }
.na-dur { font-family: var(--mono); font-stretch: 75%; font-size: 10.5px; color: var(--dim-2); }
.na-del { flex: none; width: 24px; height: 24px; }
.na-del .ico { width: 14px; height: 14px; }
.na-row { display: flex; align-items: center; gap: 10px; }

/* Un bloc média d'événement : le header, puis le média. */
.ev-media { margin-top: 10px; }
.ev-media .mh { margin-bottom: 6px; }
.ev-media .ev-gal, .ev-media .files, .ev-media .vid { margin-top: 0; }
.na-btn {
  flex: none; width: 34px; height: 34px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--rd); color: #fff; cursor: pointer;
}
.na-btn .ico { width: 18px; height: 18px; }
/* L'onde : des barres, cliquables et « scrubbables ». Hauteur ∝ volume. */
.na-wave {
  flex: 1; display: flex; align-items: center; gap: 2px;
  height: 30px; cursor: pointer; touch-action: none;
}
.na-bar {
  flex: 1; min-width: 2px; border-radius: 2px;
  background: var(--line-2);
  transition: background var(--fast) var(--ease);
}
.na-bar.on { background: var(--rd); }        /* la part déjà écoutée */
.na-time { flex: none; font-family: var(--mono); font-stretch: 75%; font-size: 11px;
           color: var(--dim-2); min-width: 34px; text-align: right; }
/* La vitesse : discrète à 1×, rouge dès qu'on accélère. */
.na-rate {
  flex: none; min-width: 34px; padding: 3px 6px; border-radius: var(--r);
  background: var(--ink-3); color: var(--dim);
  font-family: var(--mono); font-stretch: 75%; font-size: 11px; font-weight: 700; cursor: pointer;
}
.na-rate:hover { color: var(--chalk); }
.na-rate.on { background: var(--rd); color: #fff; }

/* ── REDIMENSIONNER UNE PHOTO ─────────────────────────────────────────────
   L'éditeur porte la barre de tailles ; elle se pose sur l'image cliquée. */
/* Le wrapper de l'éditeur (il porte la barre de taille en position absolue). Il
   DOIT relayer la hauteur : c'est lui qui l'avait cassée — l'éditeur ne
   défilait plus parce que son parent n'était pas un flex borné. */
.note-ed-w { position: relative; flex: 1; min-height: 0; display: flex; flex-direction: column; }
.note-ed-w > .note-ed { flex: 1; min-height: 0; }

/* ── LES CURSEURS DES AUTRES ──────────────────────────────────────────────
   Une couche posée PAR-DESSUS l'éditeur, et jamais dedans. `pointer-events:
   none` est ce qui la rend inoffensive : on écrit, on sélectionne, on clique à
   travers elle sans jamais la rencontrer. `overflow: hidden` découpe ce qui
   sort quand le texte défile — la couche, elle, ne défile pas.

   z-index 4 : au-dessus du texte, sous la barre de taille d'image (5). Un
   curseur ne doit jamais passer devant un bouton qu'on vise. */
.cur-layer { position: absolute; inset: 0; z-index: 4; pointer-events: none; overflow: hidden; }

/* La sélection de quelqu'un. Assez pâle pour qu'on lise le texte AU TRAVERS —
   c'est du texte qu'on partage, pas un surlignage qu'on admire. */
.cur-sel { position: absolute; opacity: .2; border-radius: 1px; }

/* Le curseur : 2 px, et il bat. Le battement n'est pas décoratif — c'est ce qui
   le distingue d'un trait dans le texte, et c'est le seul mouvement que l'œil
   reconnaît comme « quelqu'un est là ». Il reprend le rythme du point de la
   sidebar : deux clignotants à deux vitesses feraient deux alertes. */
.cur-caret { position: absolute; width: 2px; border-radius: 1px; animation: beat 1.1s var(--ease) infinite; }

/* L'étiquette. Elle ne s'efface pas au bout de deux secondes comme ailleurs :
   à cinq dans une note, savoir QUI tient ce curseur vaut mieux qu'un pixel de
   propreté. Elle ne prend pas de place — elle flotte au-dessus de la ligne. */
.cur-name {
  position: absolute;
  padding: 1px 5px; border-radius: var(--r);
  font-size: 10px; font-weight: 700; font-stretch: 74%;
  text-transform: uppercase; letter-spacing: .06em;
  white-space: nowrap; pointer-events: none;
  transform: translateX(-1px);
}

/* Qui est dans la note, en en-tête. Une pastille par personne, dans SA couleur
   — la même que son curseur : on relie les deux sans lire. */
.note-who { display: flex; align-items: center; gap: 4px; flex: none; }
.note-who-d { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.img-sizer {
  position: absolute; z-index: 5; display: flex; gap: 2px;
  padding: 3px; border-radius: var(--r);
  background: var(--black); border: 1px solid var(--line-2);
  box-shadow: 0 4px 14px rgba(0,0,0,.5);
}
.img-sz {
  min-width: 30px; padding: 5px 8px; border-radius: var(--r);
  background: none; color: var(--dim); font-size: 12px; font-weight: 700; cursor: pointer;
}
.img-sz:hover { background: var(--ink-3); color: var(--chalk); }
.img-sz.on { background: var(--rd); color: #fff; }
/* Une image cliquable : le curseur le dit. */
.note-ed img { cursor: pointer; }

/* Le conteneur flottant de `popover()` (ui.js) — palette de couleurs, menus
   d'insertion des vocaux. `popover()` pose `top`/`left` en coordonnées viewport
   (getBoundingClientRect), donc il FAUT `position: fixed` : sans cette règle, la
   boîte restait en `position: static`, les décalages inline étaient ignorés, et
   elle tombait tout en bas de la page, hors écran — le clic ratait la pastille et
   « les couleurs ne marchaient pas ». Même palier que `.dd-pop`. */
.pop {
  position: fixed; z-index: 80;
  background: var(--ink-2);
  border: 1px solid var(--line-2); border-radius: var(--r);
  box-shadow: 0 14px 40px -10px rgba(0,0,0,.9);
}
/* La palette de couleurs (texte / fond), flottante via popover. */
.clr-pop { display: flex; flex-wrap: wrap; gap: 5px; padding: 7px; max-width: 150px; }
.clr-sw {
  width: 22px; height: 22px; border-radius: var(--r); cursor: pointer;
  border: 1px solid var(--line-2);
}
.clr-sw:hover { outline: 2px solid var(--chalk); outline-offset: 1px; }
.clr-none { background: var(--black); position: relative; }
.clr-none::after {
  content: ""; position: absolute; inset: 2px; border-radius: 2px;
  background: linear-gradient(45deg, transparent 44%, var(--rd) 44%, var(--rd) 56%, transparent 56%);
}

/* L'épingle d'une note (personnelle) : discrète, apparaît au survol, s'allume
   quand la note est épinglée. Le rouge reste réservé aux alertes — l'épingle
   allumée prend le blanc franc, pas une couleur de personne. */
/* Absolue, dans le coin droit : dans un conteneur `block`, l'épingle en flux
   (inline-flex) retombait sur sa PROPRE ligne sous le titre et la date — une
   bande vide qui gonflait la hauteur de chaque note. Ici elle flotte, la ligne
   reprend sa hauteur d'avant (titre + date), et le padding-droit de `.note-item`
   lui garde sa place sans que le titre passe dessous. */
.note-pin {
  position: absolute; top: 8px; right: 7px;
  display: inline-flex; align-items: center; opacity: 0;
  color: var(--dim); cursor: pointer; padding: 2px;
  transition: opacity .12s;
}
.note-item:hover .note-pin, .note-pin.on { opacity: 1; }
.note-pin.on { color: var(--chalk); }
.note-pin svg { width: 15px; height: 15px; }

/* ── MÉDIA SÉLECTIONNÉ ─────────────────────────────────────────────────────
   Un clic sélectionne un média ; le contour rouge le dit, et Backspace le
   retire. `outline` plutôt que `border` : il ne pousse rien, ne change aucune
   taille — il se pose PAR-DESSUS. */
/* Le contour suit le rayon PROPRE du média (l'audio est arrondi à 16 px, l'image
   à 2). On ne fixe donc pas de border-radius ici : on écraserait celui de
   l'audio. Les navigateurs modernes font suivre l'outline au border-radius. */
.note-ed .msel { outline: 2px solid var(--rd); outline-offset: 2px; }
/* Un média qu'on DÉPLACE (drag-drop pour réordonner) : estompé le temps du glissé. */
.note-ed [data-m].mdrag, .note-ed [data-v].mdrag { opacity: .4; }
.note-ed img[data-m], .note-ed figure[data-v] { cursor: grab; }
.note-ed table.msel { outline-offset: 0; }
.note-ed figure.msel { outline-offset: 0; border-radius: var(--r); }

/* ── LES PIÈCES JOINTES D'UN ÉVÉNEMENT ─────────────────────────────────────
   Tout format qui n'est ni image ni vidéo : pdf, csv, zip. On ne les affiche
   pas — on les télécharge. Une puce sobre : icône, nom, taille. */
.files { margin-top: 10px; display: flex; flex-direction: column; gap: 4px; }
.file {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--r);
  background: var(--ink-2); color: var(--chalk);
  transition: border-color var(--fast) var(--ease);
}
.file:hover { border-color: var(--rd); }
.file-ic { flex: none; color: var(--dim); display: flex; }
.file-ic .ico { width: 16px; height: 16px; }
.file-n { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }
.file-s { flex: none; color: var(--dim-2); font-family: var(--mono); font-stretch: 75%; font-size: 10.5px; }

/* La zone de dépôt du formulaire, et les puces des fichiers déjà pris. */
.form-drop {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  margin-top: 10px; padding: 14px;
  border: 1px dashed var(--line-2); border-radius: var(--r);
  background: var(--ink); color: var(--dim);
  font-size: 12.5px; cursor: pointer;
  transition: border-color var(--fast) var(--ease), color var(--fast) var(--ease);
}
.form-drop:hover { color: var(--chalk); border-color: var(--dim); }
.form-drop.over { border-color: var(--rd); border-style: solid; color: var(--chalk); }
.form-drop .ico { width: 16px; height: 16px; }
.form-files { display: flex; flex-direction: column; gap: 4px; margin-top: 8px; }
.form-file {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 8px; border: 1px solid var(--line); border-radius: var(--r); background: var(--ink-2);
}

/* La note privée : le cadenas devient rouge quand elle l'est. */
.note-top .btn-ico.on { color: var(--rd); }
.note-lock { display: inline-flex; vertical-align: -2px; margin-right: 5px; color: var(--rd); }
.note-lock .ico { width: 13px; height: 13px; }

/* La rangée de boutons médias du formulaire d'événement : mêmes boutons que la
   barre des notes, alignés à gauche, pas de fond de barre. */
.form-media-bar { border: none; padding: 0; gap: 2px; }

/* ══════════════════════════════════════════════════════════════════════════
   LE MIX DU SON — trois curseurs.

   Le `range` natif est dessiné par le système : piste bleue, pastille ronde
   brillante. Au milieu d'un poste de commandement noir, c'est un morceau d'un
   autre logiciel. On le refait — une piste, un curseur carré, du rouge.
   ══════════════════════════════════════════════════════════════════════════ */
.mix { display: flex; flex-direction: column; gap: 12px; margin-top: 14px; }
.mix-row { display: flex; align-items: center; gap: 12px; }
.mix-l { flex: 1; min-width: 0; }
.mix-h { display: block; font-size: 11px; color: var(--dim-2); margin-top: 2px; }
.mix-v {
  width: 30px; text-align: right; flex: none;
  font-family: var(--mono); font-stretch: 75%; font-size: 12px; color: var(--dim);
}
.mix-r {
  -webkit-appearance: none; appearance: none;
  width: 148px; flex: none; height: 3px; padding: 0;
  background: var(--line-2); border-radius: var(--r); outline: none;
}
.mix-r::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 4px; height: 16px; border-radius: 1px;
  background: var(--rd); cursor: pointer;
}
.mix-r::-moz-range-thumb {
  width: 4px; height: 16px; border: 0; border-radius: 1px;
  background: var(--rd); cursor: pointer;
}
.mix-r:focus-visible { box-shadow: 0 0 0 2px color-mix(in srgb, var(--rd) 40%, transparent); }

/* La version, sur la porte. Discrète : elle sert le jour où quelque chose ne va
   pas, et elle ne doit pas se voir les autres jours. */
.gate-v {
  margin-top: 18px; text-align: center;
  font-family: var(--mono); font-stretch: 75%; font-size: 10.5px;
  color: var(--dim-2); letter-spacing: .08em;
}
/* La date du dernier push, sous la version — plus discrète encore (elle répond à
   une question qu'on ne pose qu'après la version). */
.gate-rel { display: block; margin-top: 3px; color: var(--dim); opacity: .7; }

/* Le point du direct, sur la ligne de mire. Il bat — c'est le SEUL moyen de
   dire « ce nombre est celui de maintenant » sans écrire un mot. Le rouge y a
   droit : ce n'est pas de la décoration, c'est un état.
   Le battement est lent (2,4 s) : un point qui clignote vite est une alarme, et
   il n'y a pas d'alarme ici. */
.shot-live {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--rd); flex: none;
  animation: live 2.4s ease-in-out infinite;
}
@keyframes live { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }
.shot-u { color: var(--dim-2); }
@media (prefers-reduced-motion: reduce) { .shot-live { animation: none; } }

/* ══════════════════════════════════════════════════════════════════════════
   LA CLOCHE QUI APPELLE

   Elle ne bouge QUE si elle a quelque chose — et 6 % du temps seulement : une
   secousse d'un tiers de seconde, puis six secondes d'immobilité. C'est ce
   rapport qui fait tout. Un mouvement continu devient un décor qu'on ne voit
   plus au bout de deux minutes ; un mouvement rare, dans un coin par ailleurs
   figé, attrape l'œil à chaque fois.

   Trois choses à la fois — elle grossit, elle penche, elle tremble — parce
   qu'une cloche qui sonne fait exactement ça : elle ne tourne pas, elle bat.
   Le `transform-origin` est en HAUT : une cloche pivote sur son attache, pas
   sur son centre. Suspendue par le milieu, elle ferait un essuie-glace. */
.btn-ico.sonne { animation: sonne 6.5s ease-in-out infinite; transform-origin: 50% 15%; }
@keyframes sonne {
  0%, 88%, 100% { transform: scale(1) rotate(0); }
  89%  { transform: scale(1.25) rotate(-13deg); }
  90.5%{ transform: scale(1.25) rotate(11deg); }
  92%  { transform: scale(1.22) rotate(-9deg); }
  93.5%{ transform: scale(1.2) rotate(7deg); }
  95%  { transform: scale(1.15) rotate(-4deg); }
  96.5%{ transform: scale(1.08) rotate(2deg); }
  98%  { transform: scale(1.02) rotate(0); }
}
/* Le point rouge bat avec elle : les deux disent la même chose, ils le disent
   ensemble. */
.notif-wrap:has(.sonne) .notif-dot { animation: sonne-dot 6.5s ease-in-out infinite; }
@keyframes sonne-dot {
  0%, 88%, 100% { transform: scale(1); }
  90%  { transform: scale(1.6); }
  96%  { transform: scale(1.15); }
}
@media (prefers-reduced-motion: reduce) {
  .btn-ico.sonne, .notif-wrap:has(.sonne) .notif-dot { animation: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   LES RADIOS — le lecteur de fond (popup) et la page admin.

   La signature : une grille de pochettes carrées (1:1) et un égaliseur qui ne
   bat QUE quand ça joue. Aucune couleur décorative — la couleur reste une clé
   (rouge = marque/cassé, etc.) ; le fond et l'état « en cours » se disent en
   blanc craie sur le noir. Coins vifs, comme partout.
   ══════════════════════════════════════════════════════════════════════════ */
/* Le fond FLOUTÉ derrière la roue (façon GTA V) : le poste de commandement passe en
   arrière-plan, net sur la seule roue. `:has` cible le voile qui porte la radio. */
.veil:has(.modal-radio) {
  background: rgba(0, 0, 0, .42);
  backdrop-filter: blur(14px) saturate(.85) brightness(.8);
  -webkit-backdrop-filter: blur(14px) saturate(.85) brightness(.8);
}
/* La largeur du modal ENVELOPPE la roue : la croix (au coin haut-droit du header)
   tombe alors pile au bord du cercle, plus 72 px dans le vide à droite. La roue et
   la barre du dessous prennent 100 % de cette largeur (voir plus bas) — tout est
   aligné, header compris. */
/* ⚠️ `max-width: none` OBLIGATOIRE : `.modal` de base plafonne à 470px, et `width`
   seul ne lève PAS un max-width — toutes les « roues agrandies » (616, 900px…)
   étaient silencieusement rabotées à 470 depuis le début. */
.modal-radio { width: min(1240px, 98vw); max-width: none; background: transparent; border: none; box-shadow: none; padding: 8px; }
.rd-body { display: flex; flex-direction: column; gap: 22px; align-items: center; width: 100%; }

/* ══ LA ROUE (façon roue radio de GTA V) — les stations forment un cercle, les
   contrôles au centre. Les secteurs sont un SVG cliquable ; les pochettes et le
   moyeu, des calques HTML posés dessus. ══ */
/* Cercle LARGEMENT agrandi : plus les centres de secteurs s'écartent, moins les
   pochettes (largeur plafonnée à 58px) se chevauchent. Borné AUSSI par la hauteur
   d'écran (88vh) : un cercle est aussi haut que large, il doit tenir dedans. */
/* La borne HAUTEUR retire le CHROME du modal (header, barre du dessous, marges,
   padding du veil — ~210px) : la roue ENTIÈRE tient à l'écran, jamais de scroll. */
.rd-wheel { position: relative; width: 100%; max-width: min(1200px, calc(100dvh - 210px)); aspect-ratio: 1 / 1; margin: 0 auto; }
.rd-wheel-svg { display: block; width: 100%; height: 100%; overflow: visible; }
.rd-wedge {
  fill: var(--ink-3); stroke: var(--ink); stroke-width: 2; cursor: pointer;
  transition: fill var(--fast) var(--ease);
}
.rd-wedge:hover { fill: var(--ink-4); }
.rd-wedge.on { fill: color-mix(in srgb, var(--rd) 34%, var(--ink-3)); }

/* Les pochettes + NOM, au milieu de leur secteur. Le calque laisse passer le clic
   vers le secteur SVG (pointer-events:none) — sauf l'étoile, qui le capte. */
.rd-seg-layer { position: absolute; inset: 0; pointer-events: none; }
.rd-seg {
  position: absolute; transform: translate(-50%, -50%);
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  width: 15%; min-width: 38px; max-width: 58px;
  z-index: 1;
}
/* Le secteur SURVOLÉ passe DEVANT ses voisins : sinon, sur la moitié gauche, la
   station suivante (plus loin dans le DOM) recouvrait l'étoile favori — impossible
   à viser. `transform` fait de chaque `.rd-seg` un contexte d'empilement propre :
   c'est bien sur le SEG qu'il faut le z-index, pas sur l'étoile. */
.rd-seg.hover { z-index: 20; }
.rd-seg-cw { position: relative; width: 100%; aspect-ratio: 1 / 1; }
.rd-seg .rd-cover { width: 100%; height: 100%; box-shadow: 0 2px 6px rgba(0, 0, 0, .55); }
.rd-seg.on .rd-cover { box-shadow: 0 0 0 2px var(--rd), 0 2px 8px rgba(0, 0, 0, .6); }
.rd-seg.hover .rd-cover { box-shadow: 0 0 0 2px var(--chalk), 0 2px 8px rgba(0, 0, 0, .6); }
/* Le NOM de la station, sous sa pochette. Tronqué, ombré pour tenir sur le fond. */
.rd-seg-name {
  max-width: 74px; font-size: 9.5px; font-weight: 800; font-stretch: 78%;
  text-transform: uppercase; letter-spacing: .02em; line-height: 1.1;
  color: var(--chalk); text-align: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  text-shadow: 0 1px 3px rgba(0, 0, 0, .95);
}
.rd-seg.on .rd-seg-name { color: var(--rd-2); }
/* L'étoile FAVORI : sur une pastille sombre pour le contraste (accessible sur
   n'importe quelle pochette). Ambre = favori. */
.rd-seg-fav {
  position: absolute; top: -9px; right: -9px; pointer-events: auto; cursor: pointer;
  display: grid; place-items: center; width: 26px; height: 26px; border-radius: 50%;
  background: rgba(10, 12, 14, .82); border: 1px solid rgba(255, 255, 255, .14);
  color: var(--dim); opacity: 0;
  transition: color var(--fast) var(--ease), transform var(--fast) var(--ease),
              background var(--fast) var(--ease), opacity var(--fast) var(--ease);
}
/* L'étoile n'apparaît QU'AU SURVOL du secteur — favoris compris : au repos, la
   roue ne montre que les pochettes et les noms (l'anneau du moyeu dit déjà ce qui
   joue ; le statut favori se lit en survolant). */
.rd-seg.hover .rd-seg-fav { opacity: 1; }
/* Ceinture CSS : l'étoile survolée reste visible par elle-même, même le temps
   que le JS repose la classe .hover sur le secteur. */
.rd-seg-fav:hover { opacity: 1; transform: scale(1.14); color: var(--chalk); }
.rd-seg-fav.on { color: var(--am); background: rgba(10, 12, 14, .9); }
.rd-seg-fav .ico { width: 17px; height: 17px;
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, .8)); }

/* LE MOYEU — le trou central, où vivent les contrôles. */
.rd-hub {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 60%; aspect-ratio: 1 / 1; border-radius: 50%;
  background: var(--ink-2); border: 1px solid var(--line);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 8px; padding: 8%; text-align: center;
}
/* Slot de pochette FIGÉ (bloc, dimensions fixes) : que le survol montre une image,
   un placeholder ou rien, la hauteur ne bouge pas — sinon le moyeu se recentrait
   et les contrôles sautaient. */
.rd-hub-cover { display: block; flex: none; width: 54px; height: 54px; }
.rd-hub-cover .rd-cover { width: 100%; height: 100%; }
.rd-hub-cover .rd-ph .ico { width: 26px; height: 26px; }
.rd-wheel.playing .rd-hub-cover .rd-cover { box-shadow: inset 0 0 0 1.5px var(--line-2); }
.rd-hub-meta { display: flex; flex-direction: column; align-items: center; gap: 4px; max-width: 100%; }
/* L'égaliseur du moyeu bat pour la lecture réelle, PAS pour l'aperçu au survol.
   Mais il doit TOUJOURS occuper sa place (visibility, pas display) : sinon il
   disparaissait au survol, le moyeu se recentrait, et les contrôles se décalaient —
   c'est CE saut que l'œil attribuait à la pochette. */
.rd-hub .rd-hub-eq { display: inline-flex; align-items: flex-end; gap: 2px; height: 12px; visibility: hidden; }
.rd-hub .rd-hub-eq.on { visibility: visible; }
.rd-hub-name {
  font-weight: 700; font-stretch: 72%; text-transform: uppercase; letter-spacing: .05em;
  font-size: 13.5px; color: var(--chalk); line-height: 1.25;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rd-hub-ctl { display: flex; align-items: center; gap: 6px; }
.rd-hub-b { width: 34px; height: 34px; }
.rd-hub-b .ico { width: 22px; height: 22px; }
.rd-hub-play { width: 46px; height: 46px; background: var(--ink-4); }
.rd-hub-play:hover { background: var(--line); }
.rd-hub-play .ico { width: 26px; height: 26px; }

/* Sous la roue : la position (seek) + le volume, sur une ligne. */
.rd-under { display: flex; align-items: center; gap: 16px; width: 100%; max-width: min(1200px, calc(100dvh - 210px)); }
.rd-under-seek { flex: 1; min-width: 0; }
.rd-under-vol { flex: none; }

/* La pochette : un carré. Image, ou tuile de repli avec l'icône. */
.rd-cover {
  position: relative; display: block; width: 100%; aspect-ratio: 1 / 1;
  border-radius: var(--r); overflow: hidden; background: var(--ink-3);
}
.rd-img { width: 100%; height: 100%; object-fit: cover; }
.rd-ph {
  position: absolute; inset: 0; display: grid; place-items: center;
  background: linear-gradient(135deg, var(--ink-3), var(--ink));
  color: var(--line-2);
}
.rd-ph .ico { width: 46%; height: 46%; }

/* L'ÉGALISEUR — il ne bat que quand ça joue (à droite du rang courant). */
.rd-eq { display: none; }
.rd-row .rd-eq { flex: none; align-items: flex-end; gap: 2px; height: 14px; margin-right: 4px; }
.rd-eq.on { display: inline-flex; align-items: flex-end; gap: 2px; }
.rd-eq i {
  width: 3px; height: 100%; background: var(--chalk);
  transform-origin: bottom; transform: scaleY(.3);
  animation: rd-eq 900ms ease-in-out infinite;
  animation-delay: calc(var(--i) * 140ms);
}
@keyframes rd-eq { 0%, 100% { transform: scaleY(.3); } 50% { transform: scaleY(1); } }
@media (prefers-reduced-motion: reduce) { .rd-eq i { animation: none; transform: scaleY(.6); } }

.rd-empty {
  text-align: center; color: var(--dim);
  padding: 22px 12px; display: flex; flex-direction: column; gap: 8px; align-items: center;
}
.rd-empty .ico { width: 30px; height: 30px; color: var(--dim-2); }
.rd-empty p { font-size: 13px; max-width: 340px; line-height: 1.5; }

/* ── LE MINI-LECTEUR — pied de la sidebar. ─────────────────────────────────── */
/* DANS LE FLUX (dernier enfant de `.side`), collé au bandeau sans le recouvrir :
   `.side-sp` (flex:1) pousse qui-est-là + bandeau + lecteur tout en bas. Marges
   négatives : il déborde le padding latéral (11px) pour toucher les deux bords. */
.side-player {
  /* Même espacement bandeau→radio que liste-en-ligne→bandeau (18px, marges de flex
     qui s'ADDITIONNENT : `.who` mb 8 + `.board` mt 10). Le padding-top du footer
     compte déjà pour 8px de vide visuel — d'où margin-top 10px (10 + 8 = 18). */
  margin: 10px -11px 0;
  display: flex; flex-direction: column; gap: 6px;
  padding: 8px 11px 9px;
  background: var(--ink-2);
}
.sp-top { display: flex; align-items: center; gap: 8px; }
.sp-info {
  display: flex; align-items: center; gap: 9px; flex: 1; min-width: 0;
  padding: 2px; text-align: left; border-radius: var(--r);
}
.sp-info:hover { background: var(--ink-3); }
.sp-cover { flex: none; }
.sp-cover .rd-cover { width: 36px; height: 36px; }
.sp-cover .rd-ph .ico { width: 20px; height: 20px; }
.sp-meta { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.sp-name {
  font-weight: 700; font-stretch: 70%; text-transform: uppercase; letter-spacing: .05em;
  font-size: 11.5px; color: var(--chalk);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sp-subrow { display: flex; align-items: center; gap: 7px; }
.sp-sub {
  font-family: var(--mono); font-stretch: 75%; font-size: 8.5px;
  letter-spacing: .06em; text-transform: uppercase; color: var(--dim-2);
}
.sp-ctl { flex: none; display: flex; align-items: center; gap: 1px; }
.sp-b { width: 28px; height: 28px; }
.sp-b .ico { width: 20px; height: 20px; }
.sp-play .ico { width: 22px; height: 22px; }
/* Un liseré ténu quand ça joue : la pochette « vit ». */
.side-player.playing .sp-cover .rd-cover { box-shadow: inset 0 0 0 1.5px var(--line-2); }

/* ══ L'ÉGALISEUR des LECTEURS (pied de sidebar + pied « en cours »). ══════════
   Le MÊME indicateur « en lecture » que sur les rangs de la popup : 4 barres qui
   battent, affichées seulement sous `.on` (posé selon l'état réel du son). Le
   `.rd-eq.on` d'origine porte déjà `display` et l'animation des barres ; on ne
   fait ici que le dimensionner hors d'un rang. */
.rd-eq.sp-eq { flex: none; align-items: flex-end; gap: 2px; height: 9px; }
.rd-eq.np-eq { flex: none; align-items: flex-end; gap: 2.5px; height: 16px; }

/* ══ LE VOLUME compact (façon Spotify) — l'icône COUPE/RÉTABLIT, le curseur court
   règle finement. Court par choix : un volume n'a pas besoin de toute la largeur. */
.rd-vol2 { flex: none; display: flex; align-items: center; gap: 4px; }
.vol-ico { width: 30px; height: 30px; }
.vol-ico .ico { width: 19px; height: 19px; }
.vol-r { width: 74px; flex: none; }

/* ══ LA BARRE DE SEEK — la navigation curseur (rail cliquable + horodatage). ══ */
.rd-seek { display: flex; align-items: center; gap: 8px; }
.sk-t {
  flex: none; font-family: var(--mono); font-size: 9px; font-stretch: 75%;
  letter-spacing: .04em; color: var(--dim-2); min-width: 26px;
}
.sk-dur { text-align: right; }
.sk-track {
  position: relative; flex: 1; height: 4px; border-radius: 2px;
  background: var(--ink-4); cursor: pointer; touch-action: none;
}
.sk-track:focus-visible { outline: 1.5px solid var(--line-2); outline-offset: 3px; }
.sk-fill {
  position: absolute; left: 0; top: 0; bottom: 0; width: 0;
  background: var(--chalk); border-radius: 2px;
}
.sk-thumb {
  position: absolute; top: 50%; left: 0; width: 9px; height: 9px; margin: -4.5px 0 0 -4.5px;
  border-radius: 50%; background: var(--chalk);
  opacity: 0; transition: opacity var(--fast) var(--ease);
}
.sk-track:hover .sk-thumb, .rd-seek:hover .sk-thumb, .playing .sk-thumb { opacity: 1; }
.sp-seek .sk-t { font-size: 8.5px; min-width: 22px; }
/* Le seek du pied de sidebar est DUR à viser (4 px de haut). On étend sa zone de
   CLIC vers le bas — jusqu'au bord de la sidebar — et un peu vers le haut, sans
   rien changer visuellement : la valeur ne dépend que de X, donc cliquer bas
   revient au même. Vers le bas seulement il n'y a que du vide (pas de bouton). */
.sp-seek .sk-track::before {
  content: ""; position: absolute; left: 0; right: 0; top: -8px; bottom: -16px;
}


/* ── La page admin Radios : une ligne par radio. ──────────────────────────── */
.rad-wrap { max-width: 720px; }
.rad-list { display: flex; flex-direction: column; gap: 8px; }
.rad-row {
  display: flex; align-items: center; gap: 12px;
  padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--r);
  background: var(--ink);
}
.rad-cover {
  flex: none; width: 52px; height: 52px; position: relative; padding: 0;
  border: 1px solid var(--line); border-radius: var(--r); overflow: hidden;
  background: var(--ink-3);
}
.rad-cover .rad-img { width: 100%; height: 100%; object-fit: cover; }
.rad-cover .rad-ph { position: absolute; inset: 0; display: grid; place-items: center; color: var(--line-2); }
.rad-cover .rad-ph .ico { width: 22px; height: 22px; }
.rad-cover-ed {
  position: absolute; inset: 0; display: grid; place-items: center;
  background: rgba(0, 0, 0, .55); color: var(--chalk);
  opacity: 0; transition: opacity var(--fast) var(--ease);
}
.rad-cover:hover .rad-cover-ed, .rad-cover:focus-visible .rad-cover-ed { opacity: 1; }
.rad-cover-ed .ico { width: 18px; height: 18px; }
.rad-cover.busy { opacity: .5; pointer-events: none; }
.rad-mid { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.rad-name { width: 100%; }
.rad-file {
  font-family: var(--mono); font-stretch: 75%; font-size: 10px; color: var(--dim-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.rad-del { flex: none; }
.rad-del:hover { background: var(--rd-soft); color: var(--rd-2); }
.rad-audio { flex: none; }

/* La progression d'un envoi audio chunké — sur la ligne, ou dans la modale d'ajout. */
.rad-prog, .rad-add-prog { position: relative; display: flex; align-items: center; gap: 8px;
  height: 16px; margin-top: 2px; }
.rad-add-prog { height: 22px; margin-top: 10px; }
.rad-prog-bar { flex: 1; height: 4px; border-radius: 999px; background: var(--rd);
  transition: width .15s linear; min-width: 2px; }
.rad-prog, .rad-add-prog { background: linear-gradient(var(--line), var(--line)) left center / 100% 4px no-repeat; }
.rad-prog-t { flex: none; font-family: var(--mono); font-stretch: 75%; font-size: 10px; color: var(--dim); }
.rad-add-file { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 6px; }
.rad-add-f { color: var(--dim); font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; }

/* ── LA PAGE SERVEUR — un tableau de bord en cartes. ───────────────────────── */
.srv-wrap { width: 100%; }
.srv-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 12px; }
.srv-card { border: 1px solid var(--line); border-radius: var(--r); background: var(--ink-2);
  padding: 14px 16px; display: flex; flex-direction: column; gap: 12px; }
.srv-card-h { display: flex; align-items: center; gap: 7px;
  color: var(--dim-2); font-weight: 700; font-stretch: 70%;
  text-transform: uppercase; letter-spacing: .1em; font-size: 10.5px; }
.srv-card-h .ico { width: 15px; height: 15px; }
.srv-card-b { display: flex; flex-direction: column; gap: 9px; }
.srv-kv { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.srv-k { color: var(--dim-2); font-size: 12.5px; }
.srv-v { color: var(--chalk); font-size: 12.5px; text-align: right; font-variant-numeric: tabular-nums; }
.srv-big { display: flex; align-items: baseline; gap: 8px; }
.srv-big-v { font-size: 30px; font-weight: 800; font-stretch: 70%; color: var(--chalk); line-height: 1; }
.srv-big-k { color: var(--dim); font-size: 13px; }
.srv-counts { display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: 8px; }
.srv-cc { display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  padding: 8px 10px; background: var(--ink); border: 1px solid var(--line); border-radius: var(--r); }
.srv-cc-i { color: var(--dim-2); } .srv-cc-i .ico { width: 15px; height: 15px; }
.srv-cc-n { font-size: 19px; font-weight: 800; font-stretch: 72%; color: var(--chalk); }
.srv-cc-l { font-size: 10.5px; color: var(--dim-2); font-stretch: 80%; }
.srv-disk { display: flex; flex-direction: column; gap: 6px; }
.srv-diskbar { height: 6px; border-radius: 999px; background: var(--ink-4); overflow: hidden; }
.srv-diskbar-f { display: block; height: 100%; background: var(--gr, var(--rd)); }
.srv-diskbar-f.hot { background: var(--rd); }
.srv-vaults { grid-column: 1 / -1; }
.srv-vtbl { display: flex; flex-direction: column; }
.srv-vrow { display: grid; grid-template-columns: minmax(120px, 2fr) repeat(4, minmax(60px, 1fr));
  gap: 10px; align-items: center; padding: 8px 4px; border-top: 1px solid var(--line); }
.srv-vrow:first-child { border-top: none; }
.srv-vhead { color: var(--dim-2); font-weight: 700; font-stretch: 68%;
  text-transform: uppercase; letter-spacing: .09em; font-size: 10px; }
.srv-vhead span:not(:first-child), .srv-vrow .mono { text-align: right; }
.srv-vname { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--chalk); font-weight: 600; }

/* ── Le « Changer de prénom » relégué dans les Paramètres (Mon compte). ────── */
/* « Se déconnecter » : poussé tout à GAUCHE de la ligne d'actions (Annuler /
   Enregistrer restent à droite). */
.modal-act .act-left { margin-right: auto; gap: 7px; }
.modal-act .act-left .ico { width: 16px; height: 16px; }
