/* ═══════════════════════════════════════════════════════════════════════════
   NECObot — composants (panel, guide, pages d'autorisation)
   Servi sous /theme/components.css, après tokens.css et base.css.

   Règles communes :
   · l'état passe par les attributs ARIA (aria-current, aria-selected, aria-invalid,
     aria-expanded, aria-busy, role="switch") : le style suit l'accessibilité, pas l'inverse ;
   · jamais la couleur seule : icône, texte, forme (point plein ou creux) ou position ;
   · aucune ressource externe ni image data: (compatible avec une CSP stricte) ;
   · une seule couleur d'état : --color-danger (erreurs, actions dangereuses).
   Démonstration de chaque composant : apercu.html.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Mise en page ── */
.stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.cluster {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
}
.grid-auto {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
  gap: var(--space-4);
}

/* ── Marque : avatar + « NECObot »
   <a class="brand" href="/"><img class="brand-avatar" src="/theme/necotho-avatar.png" alt="">
   <span class="brand-mark"><b>NECO</b>bot</span></a> ── */
.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  color: var(--color-text);
  text-decoration: none;
}
.brand-avatar {
  width: 2rem;
  height: 2rem;
  flex: none;
  border-radius: var(--radius-full);
  background: var(--color-avatar-bg); /* pas de liseré pendant le chargement */
  object-fit: cover;
}
.brand-mark {
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: var(--fw-regular);
  letter-spacing: var(--tracking-tight);
  line-height: 1;
  white-space: nowrap;
}
.brand-mark b {
  font-weight: var(--fw-bold);
}

/* ── Boutons
   <button class="btn btn-primary">Enregistrer</button> ; variantes : btn-danger (contour),
   btn-danger-solid (confirmation), btn-ghost ; tailles btn-sm, btn-lg ; btn-icon, btn-block.
   Chargement : aria-busy="true" (le libellé reste lu par les lecteurs d'écran). ── */
.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--control-height);
  padding: 0 var(--space-4);
  border: var(--border-width) solid var(--color-border-strong);
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--color-text);
  font-family: var(--font-display);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--tracking-wide);
  line-height: 1.1;
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
  user-select: none;
  transition: background-color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.btn:hover {
  background: var(--color-hover);
  border-color: var(--color-text-muted);
}
.btn:active {
  background: var(--color-active);
  transform: translateY(1px);
}
.btn-primary {
  background: var(--color-brand);
  border-color: var(--color-brand);
  color: var(--color-on-brand);
}
.btn-primary:hover {
  background: rgb(var(--color-brand-rgb) / 0.86);
  border-color: rgb(var(--color-brand-rgb) / 0.86);
}
.btn-primary:active {
  background: rgb(var(--color-brand-rgb) / 0.78);
}
.btn-ghost {
  border-color: transparent;
}
.btn-ghost:hover {
  border-color: transparent;
}
.btn-danger {
  border-color: var(--color-danger-line);
  color: var(--color-danger);
}
.btn-danger:hover {
  border-color: var(--color-danger);
  background: var(--color-danger-soft);
}
.btn-danger-solid {
  background: var(--color-danger);
  border-color: var(--color-danger);
  color: var(--color-on-danger);
}
.btn-danger-solid:hover {
  background: rgb(var(--color-danger-rgb) / 0.88);
  border-color: rgb(var(--color-danger-rgb) / 0.88);
}
.btn-sm {
  min-height: var(--control-height-sm);
  padding: 0 var(--space-3);
  font-size: var(--fs-xs);
}
.btn-lg {
  min-height: 3rem;
  padding: 0 var(--space-6);
  font-size: var(--fs-md);
}
.btn-block {
  display: flex;
  width: 100%;
}
.btn-icon {
  width: var(--control-height);
  padding: 0;
}
.btn-icon.btn-sm {
  width: var(--control-height-sm);
}
.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
  transform: none;
}
.btn:disabled:hover,
.btn[aria-disabled="true"]:hover {
  background: transparent;
  border-color: var(--color-border-strong);
}
.btn-primary:disabled:hover,
.btn-primary[aria-disabled="true"]:hover {
  background: var(--color-brand);
  border-color: var(--color-brand);
}
.btn[aria-busy="true"] {
  color: transparent;
  pointer-events: none;
}
.btn[aria-busy="true"]::after {
  content: "";
  position: absolute;
  inset: 0;
  width: 1rem;
  height: 1rem;
  margin: auto;
  border: 2px solid var(--color-text);
  border-right-color: transparent;
  border-radius: 50%;
  animation: nb-spin 0.7s linear infinite;
}
.btn-primary[aria-busy="true"]::after {
  border-color: var(--color-on-brand);
  border-right-color: transparent;
}

/* ── Croix de fermeture dessinée en CSS (nette à toute taille, aucun glyphe) :
   <button class="btn btn-ghost btn-icon btn-sm" aria-label="Fermer"><span class="icon-x" aria-hidden="true"></span></button> ── */
.icon-x {
  position: relative;
  display: inline-block;
  width: 0.875rem;
  height: 0.875rem;
  flex: none;
}
.icon-x::before,
.icon-x::after {
  content: "";
  position: absolute;
  top: calc(50% - 1px);
  left: 0;
  width: 100%;
  height: 2px;
  border-radius: 1px;
  background: currentColor;
}
.icon-x::before { transform: rotate(45deg); }
.icon-x::after { transform: rotate(-45deg); }

/* ── Indicateur de chargement : <span class="spinner" role="status"><span class="sr-only">Chargement…</span></span> ── */
.spinner {
  display: inline-block;
  width: 1.25em;
  height: 1.25em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  vertical-align: -0.2em;
  animation: nb-spin 0.7s linear infinite;
}
@keyframes nb-spin {
  to { transform: rotate(360deg); }
}

/* ── Cartes
   <section class="card"><header class="card-header"><div><h2 class="card-title">…</h2>
   <p class="card-subtitle">…</p></div>…</header>…<footer class="card-footer">…</footer></section> ── */
.card {
  padding: var(--space-6);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  color: var(--color-text);
}
.card-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: var(--space-4);
}
.card-title {
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
  line-height: var(--lh-snug);
  letter-spacing: var(--tracking-tight);
}
.card-subtitle {
  margin-top: var(--space-1);
  color: var(--color-text-muted);
  font-size: var(--fs-sm);
}
.card-footer {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--space-3);
  margin-top: var(--space-6);
  padding-top: var(--space-4);
  border-top: var(--border-width) solid var(--color-border);
}
.card-inset {
  background: var(--color-bg-inset);
}
a.card,
.card-interactive {
  display: block;
  text-decoration: none;
  transition: border-color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease);
}
a.card:hover,
.card-interactive:hover {
  border-color: var(--color-border-strong);
  background: var(--color-surface-2);
}

/* Chiffre clé : <div class="stat"><p class="stat-label">Membres</p><p class="stat-value">1 284</p>
   <p class="stat-meta">+12 cette semaine</p></div> */
.stat-label {
  color: var(--color-text-muted);
  font-family: var(--font-display);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
}
.stat-value {
  margin-top: var(--space-2);
  font-family: var(--font-display);
  font-size: var(--fs-3xl);
  font-weight: var(--fw-bold);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--tracking-tight);
  line-height: 1;
}
.stat-meta {
  margin-top: var(--space-2);
  color: var(--color-text-muted);
  font-size: var(--fs-sm);
}

/* ── Champs
   <div class="field"><label class="field-label" for="x">Nom</label>
   <input class="input" id="x" aria-describedby="x-aide x-erreur" aria-invalid="true">
   <p class="field-hint" id="x-aide">…</p><p class="field-error" id="x-erreur">✕ …</p></div>
   Plusieurs champs : les espacer par le conteneur (.stack, grille avec gap), pas par des marges. ── */
.field {
  display: grid;
  align-content: start; /* dans une grille de formulaire, le libellé reste collé à son champ */
  gap: var(--space-2);
}
.field-label {
  color: var(--color-text);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
}
.field-hint {
  color: var(--color-text-muted);
  font-size: var(--fs-xs);
}
.field-error {
  display: flex;
  gap: var(--space-2);
  color: var(--color-danger);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
}
.field-error:empty {
  display: none;
}
.input,
.select,
.textarea {
  width: 100%;
  min-height: var(--control-height);
  padding: var(--space-2) var(--space-3);
  border: var(--border-width) solid var(--color-border-strong);
  border-radius: var(--radius-md);
  background-color: var(--color-bg-inset);
  color: var(--color-text);
  font-size: var(--fs-md);
  line-height: var(--lh-snug);
  transition: border-color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease);
}
.input:hover,
.select:hover,
.textarea:hover {
  border-color: var(--color-text-muted);
}
.input:focus,
.select:focus,
.textarea:focus {
  border-color: var(--color-focus);
  outline: var(--focus-width) solid var(--color-focus);
  outline-offset: 0;
}
.input[aria-invalid="true"],
.select[aria-invalid="true"],
.textarea[aria-invalid="true"] {
  border-color: var(--color-danger);
}
.input[aria-invalid="true"]:focus,
.select[aria-invalid="true"]:focus,
.textarea[aria-invalid="true"]:focus {
  outline-color: var(--color-danger);
}
.input:disabled,
.select:disabled,
.textarea:disabled {
  opacity: 0.55;
}
.input[readonly] {
  border-style: dashed;
}
.textarea {
  min-height: 6rem;
  resize: vertical;
  line-height: var(--lh-normal);
}
.input-sm {
  min-height: var(--control-height-sm);
  font-size: var(--fs-sm);
}
/* Liste déroulante : chevron dessiné par deux dégradés (pas d'image) */
.select {
  appearance: none;
  padding-right: var(--space-10);
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 1.25rem) 55%, calc(100% - 0.9rem) 55%;
  background-size: 0.35rem 0.35rem, 0.35rem 0.35rem;
  background-repeat: no-repeat;
}
/* Champ + bouton accolés : <div class="input-group"><input class="input">…<button class="btn">…</button></div> */
.input-group {
  display: flex;
}
.input-group > .input {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}
.input-group > .btn {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  margin-left: calc(var(--border-width) * -1);
}
/* Cases à cocher et boutons radio natifs aux couleurs de la marque */
.check {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  cursor: pointer;
}
.check input {
  width: 1.125rem;
  height: 1.125rem;
  margin: 0;
  accent-color: var(--color-brand);
  cursor: pointer;
}

/* ── Interrupteur (case à cocher native, donc utilisable au clavier et dans un formulaire)
   <label class="switch"><input class="switch-input" type="checkbox" role="switch" checked>
   <span class="switch-track" aria-hidden="true"></span><span class="switch-label">Annonces</span></label>
   Activé : piste pleine et pastille à droite (lisible sans couleur). Ne pas ajouter aria-checked :
   l'état natif de la case est celui qu'annoncent les lecteurs d'écran (règle ARIA in HTML). ── */
.switch {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  min-height: var(--control-height-sm);
  cursor: pointer;
}
.switch-input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.switch-track {
  position: relative;
  flex: none;
  width: 2.75rem;
  height: 1.5rem;
  border: var(--border-width) solid var(--color-border-strong);
  border-radius: var(--radius-full);
  background: var(--color-bg-inset);
  transition: background-color var(--dur-base) var(--ease), border-color var(--dur-base) var(--ease);
}
.switch-track::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: calc(1.5rem - 8px);
  height: calc(1.5rem - 8px);
  border-radius: 50%;
  background: var(--color-text-muted);
  transition: transform var(--dur-base) var(--ease), background-color var(--dur-base) var(--ease);
}
.switch:hover .switch-track {
  border-color: var(--color-text-muted);
}
.switch-input:checked + .switch-track {
  border-color: var(--color-brand);
  background: var(--color-brand);
}
.switch-input:checked + .switch-track::after {
  background: var(--color-on-brand);
  transform: translateX(1.25rem);
}
.switch-input:focus-visible + .switch-track {
  outline: var(--focus-width) solid var(--color-focus);
  outline-offset: var(--focus-offset);
}
.switch-input:disabled + .switch-track,
.switch-input:disabled ~ .switch-label {
  opacity: 0.45;
}
.switch-label {
  font-size: var(--fs-sm);
}

/* ── Tableaux
   <div class="table-wrap"><table class="table">…</table></div> ; .num aligne les chiffres ;
   .table-dense pour les tableaux d'édition ; ligne choisie : aria-selected="true". ── */
.table-wrap {
  overflow-x: auto;
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
}
.table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-sm);
}
.table caption {
  padding: var(--space-3) var(--space-4);
  color: var(--color-text-muted);
  font-size: var(--fs-xs);
  text-align: left;
  caption-side: bottom;
}
.table th,
.table td {
  padding: var(--space-3) var(--space-4);
  border-bottom: var(--border-width) solid var(--color-border);
  text-align: left;
  vertical-align: middle;
}
.table thead th {
  position: sticky;
  top: 0;
  background: var(--color-surface-2);
  color: var(--color-text-muted);
  font-family: var(--font-display);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  white-space: nowrap;
}
.table tbody tr {
  transition: background-color var(--dur-fast) var(--ease);
}
.table tbody tr:hover {
  background: var(--color-hover);
}
.table tbody tr:last-child > * {
  border-bottom: 0;
}
.table tr[aria-selected="true"] {
  background: var(--color-selected);
  box-shadow: inset 3px 0 0 var(--color-brand);
}
.table .num,
.table th.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.table-dense th,
.table-dense td {
  padding: var(--space-2) var(--space-3);
}

/* ── Badges et pastilles d'état
   <span class="badge badge-success"><span class="dot" aria-hidden="true"></span>Connecté</span>
   <span class="badge badge-danger"><span aria-hidden="true">✕</span>Hors ligne</span>
   .dot plein = actif ; .dot.dot-off creux = inactif (la forme porte l'information). ── */
.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 1.5rem;
  padding: 0 var(--space-2);
  border: var(--border-width) solid var(--color-border-strong);
  border-radius: var(--radius-sm);
  color: var(--color-text-muted);
  font-family: var(--font-display);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--tracking-caps);
  line-height: 1;
  text-transform: uppercase;
  white-space: nowrap;
}
.badge-solid {
  border-color: var(--color-brand);
  background: var(--color-brand);
  color: var(--color-on-brand);
}
.badge-success {
  border-color: var(--color-success);
  color: var(--color-success);
}
.badge-danger {
  border-color: var(--color-danger-line);
  background: var(--color-danger-soft);
  color: var(--color-danger);
}
.dot {
  width: 0.5rem;
  height: 0.5rem;
  flex: none;
  border-radius: 50%;
  background: currentColor;
}
.dot-off {
  background: transparent;
  box-shadow: inset 0 0 0 1.5px currentColor;
}
.dot-live {
  animation: nb-pulse 1.6s var(--ease) infinite;
}
@keyframes nb-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}

/* ── Encadré dans la page
   <div class="callout" role="status"><span class="callout-icon" aria-hidden="true">i</span><div>…</div></div>
   Erreur : .callout-danger avec role="alert" et « ! ». Pas de symbole ℹ : Windows l'affiche en emoji bleu. ── */
.callout {
  display: flex;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: var(--border-width) solid var(--color-border);
  border-left: 3px solid var(--color-text);
  border-radius: var(--radius-md);
  background: var(--color-surface-2);
  font-size: var(--fs-sm);
}
.callout-icon {
  display: grid;
  flex: none;
  place-items: center;
  width: 1.25rem;
  height: 1.25rem;
  margin-top: 0.05rem;
  border: 1.5px solid currentColor;
  border-radius: 50%;
  font-family: var(--font-display);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  line-height: 1;
}
.callout-danger {
  border-color: var(--color-danger-line);
  border-left-color: var(--color-danger);
  background: var(--color-danger-soft);
}
.callout-danger .callout-icon {
  color: var(--color-danger);
}

/* ── Notifications (toasts)
   <div class="toast-region" aria-live="polite">
   <div class="toast toast-success" role="status"><span class="toast-icon" aria-hidden="true">✓</span>
   <div><p class="toast-title">Enregistré</p><p class="toast-body">…</p></div>
   <button class="btn btn-ghost btn-icon btn-sm toast-close" aria-label="Fermer"><span class="icon-x" aria-hidden="true"></span></button></div></div>
   Erreur : .toast-danger avec role="alert" (icône ✕). ── */
.toast-region {
  position: fixed;
  right: var(--space-4);
  bottom: var(--space-4);
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  width: min(24rem, calc(100vw - 2 * var(--space-4)));
  pointer-events: none;
}
.toast {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-3) var(--space-3) var(--space-4);
  border: var(--border-width) solid var(--color-border);
  border-left: 3px solid var(--color-text);
  border-radius: var(--radius-md);
  background: var(--color-surface-3);
  box-shadow: var(--shadow-lg);
  color: var(--color-text);
  pointer-events: auto;
  animation: nb-toast-in var(--dur-slow) var(--ease-out);
}
.toast-icon {
  display: grid;
  place-items: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  background: var(--color-text);
  color: var(--color-bg);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  line-height: 1;
}
.toast-title {
  font-weight: var(--fw-bold);
  line-height: var(--lh-snug);
}
.toast-body {
  margin-top: var(--space-1);
  color: var(--color-text-muted);
  font-size: var(--fs-sm);
}
.toast-close {
  margin: calc(var(--space-1) * -1) 0;
}
.toast-danger {
  border-color: var(--color-danger-line);
  border-left-color: var(--color-danger);
  background: linear-gradient(var(--color-danger-soft), var(--color-danger-soft)), var(--color-surface-3);
}
.toast-danger .toast-icon {
  background: var(--color-danger);
  color: var(--color-on-danger);
}
@keyframes nb-toast-in {
  from { opacity: 0; transform: translateY(0.5rem); }
}

/* ── Fenêtres : <dialog class="modal" aria-labelledby="t"> ouvert par dialog.showModal()
   <header class="modal-header"><h2 class="modal-title" id="t">…</h2>
   <button class="btn btn-ghost btn-icon btn-sm" aria-label="Fermer"><span class="icon-x" aria-hidden="true"></span></button></header>
   <div class="modal-body">…</div><footer class="modal-footer">…</footer>
   Confirmation dangereuse : .modal-danger (liseré rouge) + bouton .btn-danger-solid. ── */
.modal {
  width: min(32rem, calc(100vw - 2 * var(--space-4)));
  max-height: calc(100dvh - 2 * var(--space-4));
  margin: auto;
  padding: 0;
  overflow: auto;
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface-3);
  box-shadow: var(--shadow-lg);
  color: var(--color-text);
}
.modal::backdrop {
  /* Repli si le navigateur ne propage pas les jetons à ::backdrop (Chromium < 122) */
  background: var(--color-backdrop, rgb(0 0 0 / 0.6));
}
.modal[open] {
  animation: nb-modal-in var(--dur-base) var(--ease-out);
}
.modal-danger {
  border-top: 3px solid var(--color-danger);
}
.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-5) var(--space-6) 0;
}
.modal-title {
  font-family: var(--font-display);
  font-size: var(--fs-xl);
  font-weight: var(--fw-bold);
  line-height: var(--lh-snug);
}
.modal-body {
  padding: var(--space-4) var(--space-6);
}
.modal-footer {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-6) var(--space-6);
}
@keyframes nb-modal-in {
  from { opacity: 0; transform: translateY(0.5rem) scale(0.98); }
}

/* ── Navigation principale
   <header class="topnav" data-open="false"><a class="brand" href="/">…</a>
   <button class="btn btn-ghost btn-icon topnav-toggle" aria-expanded="false" aria-controls="menu">
   <span class="burger" aria-hidden="true"></span><span class="sr-only">Menu</span></button>
   <nav class="topnav-links" id="menu" aria-label="Sections"><a class="topnav-link" aria-current="page" href="…">…</a></nav>
   <div class="topnav-actions">…</div></header>
   Sous 768 px, les liens se replient derrière le bouton : le script passe data-open à "true" et
   aria-expanded à "true" (l'un ou l'autre suffit à ouvrir le menu). ── */
.topnav {
  position: sticky;
  top: 0;
  z-index: var(--z-nav);
  display: flex;
  align-items: center;
  gap: var(--space-4);
  min-height: var(--nav-height);
  padding: 0 var(--space-4);
  border-bottom: var(--border-width) solid var(--color-border);
  background: rgb(var(--color-bg-rgb) / 0.92);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
.topnav-links {
  display: flex;
  flex: 1;
  align-items: center;
  gap: var(--space-1);
  min-width: 0;
}
.topnav-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 2.25rem;
  padding: 0 var(--space-3);
  border-radius: var(--radius-md);
  color: var(--color-text-muted);
  font-family: var(--font-display);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  text-decoration: none;
  white-space: nowrap;
  transition: color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease);
}
.topnav-link:hover {
  background: var(--color-hover);
  color: var(--color-text);
}
.topnav-link[aria-current="page"] {
  background: var(--color-selected);
  box-shadow: inset 0 -2px 0 var(--color-brand);
  color: var(--color-text);
  font-weight: var(--fw-semibold);
}
.topnav-actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-left: auto;
}
.topnav-toggle {
  display: none;
}
.burger,
.burger::before,
.burger::after {
  display: block;
  width: 1.125rem;
  height: 2px;
  border-radius: 1px;
  background: currentColor;
}
.burger {
  position: relative;
}
.burger::before,
.burger::after {
  content: "";
  position: absolute;
  left: 0;
}
.burger::before { top: -6px; }
.burger::after { top: 6px; }
@media (max-width: 47.99rem) {
  .topnav {
    flex-wrap: wrap;
    padding: var(--space-2) var(--space-4);
  }
  .topnav-toggle {
    display: inline-flex;
    order: 2;
  }
  .topnav-actions {
    order: 1;
  }
  .topnav-links {
    display: none;
    order: 3;
    flex-basis: 100%;
    flex-direction: column;
    align-items: stretch;
    padding: var(--space-2) 0;
  }
  .topnav[data-open="true"] .topnav-links,
  .topnav:has(.topnav-toggle[aria-expanded="true"]) .topnav-links {
    display: flex;
  }
  .topnav-link {
    min-height: var(--control-height);
  }
  .topnav-link[aria-current="page"] {
    box-shadow: inset 3px 0 0 var(--color-brand);
  }
}

/* ── Navigation latérale (sections Discord / Twitch du panel)
   <nav class="sidenav" aria-label="Discord"><p class="sidenav-title">Discord</p>
   <a class="sidenav-link" aria-current="page" href="…"><span class="sidenav-icon" aria-hidden="true">#</span>Salons vocaux</a></nav>
   Sous 768 px : bandeau horizontal défilant au-dessus du contenu. ── */
.sidenav {
  display: flex;
  flex-direction: column;
  gap: 2px;
  width: var(--sidebar-width);
  padding: var(--space-4) var(--space-3);
  border-right: var(--border-width) solid var(--color-border);
  background: var(--color-bg);
}
.sidenav-title {
  padding: var(--space-3) var(--space-3) var(--space-2);
  color: var(--color-text-subtle);
  font-family: var(--font-display);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
}
.sidenav-link {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 2.25rem;
  padding: 0 var(--space-3);
  border-radius: var(--radius-md);
  color: var(--color-text-muted);
  font-size: var(--fs-sm);
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease);
}
.sidenav-link:hover {
  background: var(--color-hover);
  color: var(--color-text);
}
.sidenav-link[aria-current="page"] {
  background: var(--color-selected);
  box-shadow: inset 3px 0 0 var(--color-brand);
  color: var(--color-text);
  font-weight: var(--fw-semibold);
}
.sidenav-icon {
  display: inline-grid;
  place-items: center;
  width: 1.25rem;
  flex: none;
  color: var(--color-text-subtle);
  font-family: var(--font-mono);
}
.sidenav-link[aria-current="page"] .sidenav-icon {
  color: var(--color-text);
}
@media (max-width: 47.99rem) {
  .sidenav {
    width: auto;
    flex-direction: row;
    overflow-x: auto;
    padding: var(--space-2);
    border-right: 0;
    border-bottom: var(--border-width) solid var(--color-border);
    scrollbar-width: thin;
  }
  .sidenav-title {
    display: none;
  }
  .sidenav-link {
    flex: none;
  }
  .sidenav-link[aria-current="page"] {
    box-shadow: inset 0 -2px 0 var(--color-brand);
  }
}

/* ── Onglets : <div class="tabs" role="tablist"><button class="tab" role="tab" aria-selected="true">…</button></div> ── */
.tabs {
  display: flex;
  gap: var(--space-1);
  overflow-x: auto;
  border-bottom: var(--border-width) solid var(--color-border);
  scrollbar-width: thin;
}
.tab {
  position: relative;
  flex: none;
  padding: var(--space-3) var(--space-4);
  border: 0;
  background: none;
  color: var(--color-text-muted);
  font-family: var(--font-display);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  white-space: nowrap;
  transition: color var(--dur-fast) var(--ease);
}
.tab:hover {
  color: var(--color-text);
}
.tab[aria-selected="true"] {
  color: var(--color-text);
  font-weight: var(--fw-semibold);
}
.tab[aria-selected="true"]::after {
  content: "";
  position: absolute;
  right: var(--space-3);
  bottom: -1px;
  left: var(--space-3);
  height: 2px;
  background: var(--color-brand);
}

/* ── Écran de connexion (panel, pages d'autorisation)
   <main class="login"><div class="login-card"><img class="login-avatar" src="/theme/necotho-avatar.png" alt="">
   <p class="login-brand brand-mark"><b>NECO</b>bot</p><h1 class="login-title">Connexion des modérateurs</h1>
   <p class="login-subtitle">…</p><form class="login-form">…champs…
   <p class="login-error" role="alert" id="erreur"></p><button class="btn btn-primary btn-lg btn-block">Se connecter</button>
   </form><p class="login-footer">NECObot</p></div></main>
   Le message d'erreur reste vide tant qu'il n'y a rien à dire (il est alors masqué). ── */
.login {
  position: relative;
  display: grid;
  place-items: center;
  min-height: 100vh;
  min-height: 100dvh;
  padding: var(--space-8) var(--space-4);
  background: var(--color-bg);
  isolation: isolate;
}
/* Damier « pixel » en vignette : présent sur les bords, effacé au centre */
.login::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--texture-pixel);
  -webkit-mask-image: radial-gradient(ellipse at center, transparent 35%, black 100%);
  mask-image: radial-gradient(ellipse at center, transparent 35%, black 100%);
  pointer-events: none;
}
.login-card {
  width: min(100%, 26rem);
  padding: var(--space-8) var(--space-6) var(--space-6);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-xl);
  background: var(--color-surface);
  box-shadow: var(--shadow-lg);
  text-align: center;
}
.login-avatar {
  width: 5.5rem;
  height: 5.5rem;
  margin: 0 auto var(--space-4);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-full);
  background: var(--color-avatar-bg);
  object-fit: cover;
}
.login-brand {
  font-size: var(--fs-xl);
}
.login-title {
  margin-top: var(--space-3);
  font-size: var(--fs-2xl);
}
.login-subtitle {
  margin-top: var(--space-2);
  color: var(--color-text-muted);
  font-size: var(--fs-sm);
}
.login-form {
  display: grid;
  gap: var(--space-4);
  margin-top: var(--space-6);
  text-align: left;
}
.login-error {
  display: flex;
  gap: var(--space-2);
  padding: var(--space-3);
  border: var(--border-width) solid var(--color-danger-line);
  border-radius: var(--radius-md);
  background: var(--color-danger-soft);
  color: var(--color-danger);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
}
.login-error:empty {
  display: none;
}
.login-footer {
  margin-top: var(--space-6);
  color: var(--color-text-subtle);
  font-family: var(--font-display);
  font-size: var(--fs-xs);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
}

/* ── Séparateur « pixel » : <hr class="rule-pixel"> ── */
.rule-pixel {
  height: var(--pixel);
  border: 0;
  background: repeating-linear-gradient(90deg, var(--color-border-strong) 0 var(--pixel), transparent var(--pixel) calc(var(--pixel) * 2));
}

/* ── Contraste élevé de Windows : les ombres disparaissent, les indicateurs passent en contours ── */
@media (forced-colors: active) {
  .topnav-link[aria-current="page"],
  .sidenav-link[aria-current="page"],
  .table tr[aria-selected="true"] {
    outline: 2px solid CanvasText;
    outline-offset: -2px;
  }
  .tab[aria-selected="true"]::after,
  .switch-input:checked + .switch-track {
    background: Highlight;
  }
  .switch-track {
    border-color: CanvasText;
  }
  .switch-track::after {
    background: CanvasText;
  }
  .toast,
  .callout,
  .modal {
    border: 1px solid CanvasText;
  }
}
