/* ==========================================================================
   VoxNote Design System v1.0 — Tokens CSS
   Source de vérité : documentation/DESIGN-SYSTEM.md (sections 2, 5.2, 6, 11).
   Ne pas éditer ad-hoc dans une fenêtre Code — toute évolution passe par
   une mise à jour du DESIGN-SYSTEM.md puis recopiage verbatim ici.
   ========================================================================== */


/* ==========================================================================
   1. Variables CSS — palette + sémantique (DS §2.1, §2.2, §2.3, §10)
   ========================================================================== */

/* DA v2 « Carnet d'atelier » (feat/da-carnet-socle, 2026-06-11).
   Source de vérité : /tmp/aa-da/direction-3.html (direction actée).
   Métaphore : papier, encre, tampon. Le carnet à spirale que VoxNote
   remplace devient le langage visuel.
   - MIGRATION DOUCE : les noms de variables existants sont conservés à
     concept identique (--bg-*, --text-*, --accent*, sémantiques) ; seules
     les VALEURS changent. Nouveaux tokens documentés inline.
   - Mode clair : fond kraft, cards papier (JAMAIS #FFF pur), encre.
     Le primaire passe ambre #F59E0B → ocre sombre #B45309 : sur kraft,
     l'ambre pur ne tient qu'à 3,1:1 ; l'ocre retenu tient 4,8:1 en aplat
     sous texte papier ET en lien sur papier (cf. note AJUSTEMENT --accent).
   - Mode sombre : ardoise verte de chantier, écriture craie. L'accent
     ambre revient éclairci (#F0A63C) — la craie a besoin de lumière. */
:root,
[data-theme="light"] {
  /* Backgrounds */
  --bg-primary: #F5EFE2;        /* kraft */
  --bg-card: #FDFAF2;           /* papier — jamais blanc pur */
  --bg-bloc: #EEE6D2;

  /* Borders */
  --border: #DCD2B9;
  --border-strong: #C2B493;

  /* Texts.
     AJUSTEMENT AAA vs direction-3.html (vérif WCAG 2026-06-11, même teinte
     taupe, ~5 % plus sombre) :
     - secondary #635A4B (6,5:1 papier) → #5C5345 (7,25:1, AAA tenu) ;
     - muted #99907E (3,0:1) → #8C8371 (3,6:1 — parité avec le borderline AA
       historiquement accepté par le DS v1 pour les helpers). */
  --text-primary: #2B2620;      /* encre noire */
  --text-secondary: #5C5345;
  --text-muted: #8C8371;

  /* Accent — ocre « action ». N'est PLUS constant entre modes (voir dark).
     AJUSTEMENT vs direction-3.html : sa note revendique un bouton « ocre à
     4,8:1 avec texte papier », mais #D97706/#FDFAF2 ne tient qu'à 3,05:1
     (et 3,0:1 en LIEN sur papier — illisible). #B45309 est la valeur qui
     donne exactement les 4,8:1 promis, en aplat ET en texte : on prend
     l'intention, pas la coquille. Hover un cran plus bas (7,3:1). */
  --accent: #B45309;
  --accent-hover: #8A3D08;
  /* NOUVEAU : --fill-on = texte posé sur un APLAT coloré moyen (accent,
     succès, erreur) : papier en clair, encre en sombre. --accent-on-primary
     (nom historique conservé) et --error-on pointent dessus. */
  --fill-on: #FDFAF2;
  --accent-on-primary: var(--fill-on); /* plus #1F2937 */

  /* NOUVEAU : encre bleue — liens, info, focus structurel. Remplace
     l'intrus saturé #1D4ED8 (dette DA n°4). --info pointe dessus. */
  --encre: #34557F;
  --encre-hover: #274163;
  --encre-bg: #E2EAF4;

  /* Semantic — encres de métier : sauge (succès), brique (erreur/tampon).
     AJUSTEMENT vs direction-3.html (texte des badges sur leur fond teinté,
     cible AA 4,5:1 ; même teinte, un cran plus sombre) :
     - warning #A16207 (4,2:1 sur F6ECCB) → #8F5606 (5,1:1) ;
     - sauge   #5F7A4A (4,0:1 sur E7ECDC) → #556E42 (4,7:1). */
  --success: #556E42;
  --success-bg: #E7ECDC;
  --error: #B5443C;
  --error-bg: #F5DFDC;
  --info: var(--encre);
  --info-bg: var(--encre-bg);
  --warning: #8F5606;
  --warning-bg: #F6ECCB;
  --error-on: var(--fill-on); /* texte sur fond --error (boutons danger) */
}

[data-theme="dark"] {
  /* Backgrounds — ardoise verte de chantier */
  --bg-primary: #242A27;
  --bg-card: #2E3531;
  --bg-bloc: #3A433E;

  /* Borders */
  --border: #48534D;
  --border-strong: #5E6B64;

  /* Texts — craie sur ardoise */
  --text-primary: #F1EEE3;
  --text-secondary: #C7C2B1;
  --text-muted: #8E8A7A;

  /* Accent — ambre éclairci pour l'ardoise (texte = encre, pas papier) */
  --accent: #F0A63C;
  --accent-hover: #FFBE5C;
  --fill-on: #2B2620;
  --accent-on-primary: var(--fill-on);

  /* Encre bleue passée craie */
  --encre: #82A6CE;
  --encre-hover: #A2C0E0;
  --encre-bg: #25364B;

  /* Semantic (strong ET bg theme-aware — les overrides [data-theme=dark]
     par composant deviennent inutiles et ont été supprimés) */
  --success: #93B16E;
  --success-bg: #2C3A24;
  --error: #DE8177;
  --error-bg: #4A2722;
  --info: var(--encre);
  --info-bg: var(--encre-bg);
  --warning: #E0AD4A;
  --warning-bg: #41351A;
  --error-on: var(--fill-on);
}

/* Typography (vars seulement — pas de règle globale html/body, géré par les
   classes Tailwind existantes du projet pour éviter collision cascade. Voir
   note d'orchestration dans le commit de wiring base.html.) */
:root {
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  /* DA Carnet — typo. Display et MONTANTS en serif SYSTÈME (zéro webfont,
     zéro octet) ; le labeur reste en sans système. Le serif est réservé au
     display (titres, montants, wordmark) — JAMAIS en texte courant.
     Remplace les webfonts Geist/Inter de la landing (dette : Google Fonts
     supprimables au lot Vitrine). */
  --font-display: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif;
  --font-body:    -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  /* Motion tokens (DS §5.2) — utilisés par theme transition + composants
     (.aa-step-connector, .aa-basket déjà câblés via fallback). Promotion en
     vraies variables : un seul point de réglage si la durée évolue.
     DA Carnet — gamme standardisée :
     - --dur-fast 140ms / --ease-standard : hover/press boutons, cartes,
       liens (fenêtre 120-180ms : perceptible sans traîner).
     - --dur-base 250ms : bascule thème, drawers (inchangé).
     - --ease-out : entrées d'éléments (modals, cartes) — décélération douce.
     - --ease-stamp : pose de tampon (léger overshoot, cf. @keyframes
       aa-stamp-in §50). */
  --dur-fast: 140ms;
  --dur-base: 250ms;
  --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-stamp: cubic-bezier(0.34, 1.56, 0.64, 1);

  /* Transition standard PWA (CANONIQUE) — durée + courbe partagées par les
     micro-animations d'interface : drawer Jarvis (translateX+opacity), bascule de
     thème, etc. Mêmes valeurs que les primitives --dur-base/--ease-standard
     (Motion DS §5.2) mais nom d'INTENTION « transition » : un seul point de réglage
     pour le ressenti d'anim global. Le reste se généralisera progressivement. */
  --aa-transition-dur: 250ms;
  --aa-transition-ease: cubic-bezier(0.4, 0, 0.2, 1);
}

/* fix/onglets-scroll-mobile (2026-06-13) — garde globale : le viewport ne
   panne JAMAIS horizontalement. Un conteneur non isolé (rangée d'onglets,
   table sans wrapper) ne doit plus transformer la page en scroller X
   (incident UAT /devis 390px : swipe gauche = liste + Voxa + filtre hors
   écran). `clip` ne crée pas de scroll container (sticky intact) ; le
   fallback `hidden` couvre les navigateurs sans clip (< Safari 16). */
html,
body {
  overflow-x: hidden;
  overflow-x: clip;
}

/* Plan 09-01 B1/D1 — bump font-size base 14→16 px (oncle pilote feedback,
   "polices trop petites partout"). Override la cascade Tailwind base 14px
   par un 16px global pour la lisibilité mobile. Les composants qui ont une
   font-size explicite (cards, chips, totaux, pdf-preview) restent
   verrouillés sur leur taille. */
body {
  font-size: 16px;
}

/* DA Carnet — utilitaires typo display. Posés sur les titres de page et les
   montants au fil des lots écrans ; définis dès le socle pour que chaque lot
   consomme le même vocabulaire.
   .aa-serif  : display serif système (titres, wordmark).
   .aa-amount : montant — serif + chiffres tabulaires (alignement colonnes). */
.aa-serif { font-family: var(--font-display); }
.aa-amount {
  font-family: var(--font-display);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
}

/* DA Carnet — CÂBLAGE serif des TITRES (fix/serif-da-partout, 2026-06-13).
   Constat CEO : .aa-serif/.aa-amount étaient définis mais JAMAIS posés sur les
   écrans → serif présente au wordmark/landing, absente des titres PWA (« Nouveau
   client », en-têtes interventions, home…). On câble la serif à la SOURCE : les
   classes de titre maison ET les utilitaires de TAILLE titre Tailwind (text-*),
   posés uniquement sur des titres/montants dans les templates (audit vérifié).
   La serif suit donc le DISPLAY, jamais le labeur (text-body* épargnés, corps,
   labels, helpers, tables, nav, boutons, badges restent en sans système).
   tokens.css charge APRÈS dist/tailwind.css (base.html) → ces déclarations
   gagnent ; les utilitaires text-* ne posent que size/line-height/weight,
   aucun conflit de font-family. */
.aa-page-title,
.aa-card-title,
.aa-empty-title,
.text-display,
.text-title,
.text-subtitle {
  font-family: var(--font-display);
}


/* ==========================================================================
   1.bis. Bascule de thème (DS §5.2) — INSTANTANÉE
   --------------------------------------------------------------------------
   Le thème est rendu serveur sur <html data-theme> (anti-FOUC) ; le clic
   toggle se contente de réécrire `data-theme` + le cookie voxnote_theme
   (theme-toggle.js). Le swap d'attribut est atomique : toute la palette
   (--bg-*, --text-*, --border) bascule en UNE frame, sans rechargement,
   sans réseau, sans état mixte → pas de flash.

   HISTORIQUE — fix/theme-toggle-lent (2026-06-13). Les versions ≤ v0606
   posaient ici une transition couleur UNIVERSELLE (sélecteur `*` + ::before/
   ::after) gardée par une classe ajoutée au clic, animant bg/color/border/
   fill/stroke sur 250 ms. Sur les pages riches (calendrier FullCalendar +
   carte Leaflet, factures avec grandes tables), poser une transition sur
   CHAQUE nœud à chaque clic = recalcul de style O(N) + animation fill/stroke
   sur tous les SVG = paint storm de 250 ms → bascule perçue « lente et buguée »
   de façon intermittente (sévérité ∝ taille du DOM). La transition « fluide »
   coûtait plus cher que le gain perçu : on l'a SUPPRIMÉE au profit d'un swap
   instantané.

   Si une transition devait revenir un jour : la cibler sur quelques surfaces
   chrome (body) avec une durée < 100 ms, JAMAIS sur `*` ni sur fill/stroke
   (SVG des cartes). Garde : tests/app/frontend/test_theme_toggle_instant.py.
   ========================================================================== */

/* Respect de la préférence système (a11y) — on coupe TOUTE transition si l'OS
   demande "reduced motion" (filet global ; les composants ont aussi leurs
   propres coupures plus bas). Indépendant de la bascule de thème, désormais
   instantanée pour tout le monde. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition: none;
  }
}


/* ==========================================================================
   2. Layouts — page wrapper, cards, dividers, section titles (DS §6.6-6.8, §11.2)
   ========================================================================== */

.aa-page {
  max-width: 1200px;
  margin: 0 auto;
  padding: 32px 24px;
  background: var(--bg-primary);
  min-height: 100vh;
  box-sizing: border-box;
}

.aa-page-title {
  color: var(--text-primary);
  font-size: 24px;
  font-weight: 600;
  margin: 0 0 6px;
  letter-spacing: -0.3px;
}

.aa-page-subtitle {
  color: var(--text-secondary);
  font-size: 14px;
  margin: 0 0 26px;
  line-height: 1.5;
}

/* DA Carnet — la card devient FICHE BRISTOL : papier, bordure 1px franche
   (le 0.5px disparaissait sur kraft), ombre-trait 0 1px 0 (épaisseur de la
   fiche posée, pas un halo). */
.aa-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 24px;
  box-shadow: 0 1px 0 var(--border);
  transition: border-color var(--dur-fast) var(--ease-standard),
              transform var(--dur-fast) var(--ease-standard),
              box-shadow var(--dur-fast) var(--ease-standard);
}

.aa-card-flat {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 16px;
  box-shadow: 0 1px 0 var(--border);
}

/* NOUVEAU (DA Carnet) — à poser sur les cards CLIQUABLES uniquement :
   micro-élévation au survol (la fiche se soulève d'1px), retour à plat au
   press. Les cards statiques ne bougent pas. */
.aa-card-hover { cursor: pointer; }
.aa-card-hover:hover {
  border-color: var(--border-strong);
  transform: translateY(-1px);
  box-shadow: 0 2px 0 var(--border), 0 3px 10px rgba(0, 0, 0, 0.05);
}
.aa-card-hover:active { transform: translateY(0); box-shadow: 0 1px 0 var(--border); }

/* NOUVEAU (DA Carnet) — en-tête RÉGLÉ de fiche bristol : un trait
   pleine largeur sous le titre, comme la ligne d'en-tête d'une fiche.
   Usage : <div class="aa-card aa-card-ruled"><div class="aa-rule-head">…
   Consommé par les lots écrans (clients, devis, factures). */
.aa-card-ruled { padding-top: 14px; }
.aa-card-ruled .aa-rule-head {
  border-bottom: 1px solid var(--border);
  margin: 0 -24px 14px;
  padding: 0 24px 10px;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
}

.aa-card-centered {
  max-width: 560px;
  margin: 0 auto;
}

.aa-divider {
  height: 1px;
  background: var(--border);
  margin: 24px 0;
  border: none;
}

.aa-section-title {
  color: var(--text-primary);
  font-size: 14px;
  font-weight: 500;
  margin: 0 0 14px;
  letter-spacing: -0.2px;
}


/* ==========================================================================
   3. Forms — inputs, selects, labels, helpers, radio cards (DS §6.2-6.5, §6.11)
   ========================================================================== */

/* DA Carnet — champs « formulaire papier » : fond carte papier (plus le
   bloc gris), bordure marquée 1px, focus à l'ENCRE bleue (la structure,
   pas l'action — l'ocre reste réservé aux CTA). */
.aa-input,
.aa-select {
  width: 100%;
  padding: 12px 14px;
  background: var(--bg-card);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  color: var(--text-primary);
  font-size: 14px;
  font-family: inherit;
  box-sizing: border-box;
  transition: border-color var(--dur-fast) var(--ease-standard),
              box-shadow var(--dur-fast) var(--ease-standard);
}

.aa-input:focus,
.aa-select:focus {
  outline: none;
  border-color: var(--encre);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--encre) 16%, transparent);
}

.aa-input::placeholder { color: var(--text-muted); }
.aa-input[aria-invalid="true"] { border-color: var(--error); }

.aa-select {
  cursor: pointer;
  appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none' stroke='%23635A4B' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='1 1 6 7 11 1'/></svg>");
  background-repeat: no-repeat;
  background-position: right 14px center;
  padding-right: 36px;
}

[data-theme="dark"] .aa-select {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none' stroke='%23C7C2B1' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='1 1 6 7 11 1'/></svg>");
}

/* DA Carnet — CONTRÔLES NATIFS thémés (dette audit n°6 : selects natifs,
   date pickers, messages de validation navigateur restaient au chrome OS).
   - color-scheme par thème : popups de <select>, calendriers natifs,
     scrollbars et bulles de validation suivent le mode clair/sombre.
   - accent-color global : checkboxes / radios / range natifs prennent
     l'ocre sans markup supplémentaire. */
:root, [data-theme="light"] { color-scheme: light; }
[data-theme="dark"] { color-scheme: dark; }
input[type="checkbox"],
input[type="radio"],
input[type="range"],
progress {
  accent-color: var(--accent);
}

.aa-label {
  display: block;
  color: var(--text-primary);
  font-size: 13px;
  font-weight: 500;
  margin-bottom: 6px;
}

.aa-helper {
  color: var(--text-secondary);
  font-size: 12px;
  margin: 6px 0 0;
  line-height: 1.4;
}
.aa-helper-error { color: var(--error); }

/* Radio en cards (§6.4) — DA Carnet : sélection = teinte ocre posée sur le
   papier (color-mix, theme-aware) + bordure accent. Plus de warning-bg. */
.aa-radio-card {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  background: var(--bg-bloc);
  border: 1px solid var(--border);
  border-radius: 8px;
  cursor: pointer;
  margin-bottom: 8px;
  transition: border-color var(--dur-fast) var(--ease-standard),
              background-color var(--dur-fast) var(--ease-standard);
}
.aa-radio-card:hover { border-color: var(--border-strong); }
.aa-radio-checked,
.aa-radio-card:has(input:checked) {
  background: color-mix(in srgb, var(--accent) 12%, var(--bg-card));
  border-color: var(--accent);
}
.aa-radio-card input[type="radio"] {
  width: 18px;
  height: 18px;
  accent-color: var(--accent);
  flex-shrink: 0;
}

/* --- 3.x Autocomplete dropdown (ville/CP via /api/v1/geo/search) --- */

.aa-autocomplete-wrap {
  position: relative;
}

.aa-autocomplete-list {
  position: absolute;
  z-index: 30;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  margin: 0;
  padding: 4px;
  list-style: none;
  background: var(--bg-bloc);
  border: 0.5px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
  max-height: 280px;
  overflow-y: auto;
}

.aa-autocomplete-item {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  padding: 10px 12px;
  background: transparent;
  border: none;
  border-radius: 6px;
  color: var(--text-primary);
  font-size: 14px;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  transition: background-color 0.12s ease;
}

.aa-autocomplete-item:hover,
.aa-autocomplete-item:focus-visible {
  background: var(--bg-hover, rgba(0, 0, 0, 0.04));
  outline: none;
}

.aa-autocomplete-item-nom {
  font-weight: 500;
  color: var(--text-primary);
}

.aa-autocomplete-item-cp {
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  font-size: 13px;
}


/* ==========================================================================
   4. Buttons — primary, ghost, secondary outline (DS §6.1)
   ========================================================================== */

/* DA Carnet — LE système unique (dette DA n°1 : 3 systèmes concurrents).
   `.aa-btn-amber-primary` est désormais un ALIAS strict de `.aa-btn-primary`
   (même liste de sélecteurs) : il ne survit que pour
   templates/interventions/cloturer.html (fenêtre parallèle 2026-06-11,
   intouchable depuis ce lot). À SUPPRIMER — alias + occurrences cloturer.html
   + allowlist du test test_da_carnet_socle.py — dès que cette fenêtre merge.

   Micro-interactions standardisées (exigence « un truc vivant ») :
   - hover : -1px de translation + ombre portée teintée accent, --dur-fast ;
   - press : retour à plat + scale .97 (feedback < 200 ms, tactile compris) ;
   - disabled : aplat bloc, aucune élévation. */
.aa-btn-primary,
.aa-btn-amber-primary {
  padding: 12px 24px;
  background: var(--accent);
  color: var(--accent-on-primary);
  border: none;
  border-radius: 8px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  font-family: inherit;
  transition: background var(--dur-fast) var(--ease-standard),
              transform var(--dur-fast) var(--ease-standard),
              box-shadow var(--dur-fast) var(--ease-standard);
}
.aa-btn-primary:hover,
.aa-btn-amber-primary:hover {
  background: var(--accent-hover);
  transform: translateY(-1px);
  box-shadow: 0 3px 8px color-mix(in srgb, var(--accent) 35%, transparent);
}
.aa-btn-primary:active,
.aa-btn-amber-primary:active {
  transform: translateY(0) scale(0.97);
  box-shadow: none;
}
.aa-btn-primary:disabled,
.aa-btn-amber-primary:disabled {
  background: var(--bg-bloc);
  color: var(--text-muted);
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}
.aa-btn-primary:focus-visible,
.aa-btn-amber-primary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.aa-btn-ghost {
  padding: 10px 16px;
  background: transparent;
  border: none;
  color: var(--text-secondary);
  font-size: 14px;
  font-family: inherit;
  cursor: pointer;
  border-radius: 8px;
  transition: color var(--dur-fast) var(--ease-standard),
              background-color var(--dur-fast) var(--ease-standard);
}
.aa-btn-ghost:hover { color: var(--text-primary); background: var(--bg-bloc); }
.aa-btn-ghost:active { transform: scale(0.97); }

.aa-btn-secondary {
  padding: 12px 24px;
  background: transparent;
  color: var(--text-primary);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  font-family: inherit;
  transition: background-color var(--dur-fast) var(--ease-standard),
              transform var(--dur-fast) var(--ease-standard);
}
.aa-btn-secondary:hover { background: var(--bg-bloc); transform: translateY(-1px); }
.aa-btn-secondary:active { transform: translateY(0) scale(0.97); }

/* Variant danger — brique. --error-on (papier en clair, encre en sombre)
   remplace le blanc strict : la brique éclaircie du mode ardoise (#DE8177)
   rendait le blanc illisible. Hover = brique enfoncée via color-mix. */
.aa-btn-danger {
  padding: 12px 24px;
  background: var(--error);
  color: var(--error-on);
  border: none;
  border-radius: 8px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  font-family: inherit;
  transition: background var(--dur-fast) var(--ease-standard),
              transform var(--dur-fast) var(--ease-standard),
              box-shadow var(--dur-fast) var(--ease-standard);
}
.aa-btn-danger:hover {
  background: color-mix(in srgb, var(--error) 88%, var(--text-primary));
  transform: translateY(-1px);
  box-shadow: 0 3px 8px color-mix(in srgb, var(--error) 30%, transparent);
}
.aa-btn-danger:active { transform: translateY(0) scale(0.97); box-shadow: none; }
.aa-btn-danger:disabled {
  background: var(--bg-bloc);
  color: var(--text-muted);
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}
.aa-btn-danger:focus-visible {
  outline: 2px solid var(--error);
  outline-offset: 2px;
}

/* HOTFIX v0.9.7.34 — textarea theme-aware (modals + forms).
   Cause originelle bug : `class="aa-textarea"` était utilisée mais non
   définie → fallback navigateur (bg-white) qui ignore [data-theme=dark]
   → texte blanc sur fond blanc en dark mode. Définition canonique ici. */
.aa-textarea {
  background: var(--bg-card);
  color: var(--text-primary);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  padding: 12px;
  font-family: inherit;
  font-size: 14px;
  line-height: 1.5;
  width: 100%;
  resize: vertical;
  transition: border-color var(--dur-fast) var(--ease-standard),
              box-shadow var(--dur-fast) var(--ease-standard);
}
/* DA Carnet — focus à l'encre, aligné .aa-input (l'ocre reste aux CTA). */
.aa-textarea:focus {
  border-color: var(--encre);
  outline: none;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--encre) 16%, transparent);
}
.aa-textarea:focus-visible {
  outline: none;
}
.aa-textarea::placeholder { color: var(--text-muted); }

/* HOTFIX v0.9.7.69 [BUG-2026-05-12-07] — checkbox disabled state cross-theme.
   La classe est posée par _table-row.html / _card-mobile.html quand
   `facture.statut != 'validee_a_envoyer'` (le backend rejette ces statuts
   au bulk-email service.py:789). Le visuel signale clairement que la
   checkbox est inactive sans la masquer (a11y : l'utilisateur voit qu'elle
   existe + tooltip explicite la raison).

   accent-color cible la teinte du <input type=checkbox> natif Webkit/Firefox
   pour rester theme-aware (vs. fallback navigateur bg-white qui ignore
   [data-theme=dark]). */
.aa-checkbox-disabled,
input[type="checkbox"].aa-checkbox-disabled {
  opacity: 0.5;
  cursor: not-allowed;
  accent-color: var(--text-muted);
}
.aa-checkbox-disabled:hover,
input[type="checkbox"].aa-checkbox-disabled:hover {
  cursor: not-allowed;
}


/* ==========================================================================
   5. Theme toggle — header bouton soleil/lune (DS §5.2)
   ========================================================================== */

.aa-toggle {
  background: transparent;
  border: 0.5px solid var(--border);
  border-radius: 50%;
  width: 38px;
  height: 38px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  color: var(--text-primary);
  transition: border-color 0.25s ease;
}
.aa-toggle:hover { border-color: var(--accent); }
.aa-toggle svg {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  transition: opacity 0.25s ease;
}
[data-theme="light"] .aa-toggle .icon-sun { opacity: 0; pointer-events: none; }
[data-theme="dark"] .aa-toggle .icon-moon { opacity: 0; pointer-events: none; }


/* ==========================================================================
   6. Stepper — wizard multi-étapes (DS §6.9)
   ========================================================================== */

.aa-stepper {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: 0;
  margin-bottom: 32px;
  max-width: 560px;
  margin-left: auto;
  margin-right: auto;
}
.aa-step { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.aa-step-bullet {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  font-weight: 600;
}
.aa-step-done .aa-step-bullet { background: var(--success); color: var(--fill-on); }
.aa-step-active .aa-step-bullet { background: var(--accent); color: var(--accent-on-primary); }
.aa-step-future .aa-step-bullet {
  background: var(--bg-card);
  border: 0.5px solid var(--border);
  color: var(--text-muted);
  font-weight: 400;
}
.aa-step-label { font-size: 10px; color: var(--text-secondary); }
.aa-step-active .aa-step-label { color: var(--text-primary); font-weight: 500; }
.aa-step-future .aa-step-label { color: var(--text-muted); }
.aa-step-line {
  flex: 1;
  height: 1px;
  background: var(--border);
  min-width: 24px;
  margin: 16px 4px 0;
}
.aa-step-line-done { background: var(--success); }

/* Stepper compact mobile (viewport < 600px) — barre de progression linéaire. */
.aa-stepper-mobile { margin-bottom: 24px; }
.aa-progress {
  width: 100%;
  height: 4px;
  background: var(--border);
  border-radius: 2px;
  overflow: hidden;
}
.aa-progress-fill {
  height: 100%;
  background: var(--accent);
  transition: width 0.3s ease;
}
.aa-stepper-mobile-label {
  font-size: 12px;
  color: var(--text-secondary);
  margin: 8px 0 0;
  text-align: center;
}


/* ==========================================================================
   7. Pills / badges — statuts (DS §6.10)
   ========================================================================== */

/* DA Carnet — badges TAMPONS encreurs. Capitales espacées, bordure
   currentColor 1.5px, coins 6px (fini la gélule 20px) : l'encre du tampon
   sur la fiche. Tokens theme-aware → plus d'overrides [data-theme=dark]
   par variant (supprimés, ex-v0.9.x). */
.aa-pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 10px;
  border-radius: 6px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  border: 1.5px solid currentColor;
}
.aa-pill-warning  { background: var(--warning-bg); color: var(--warning); }
.aa-pill-success  { background: var(--success-bg); color: var(--success); }
.aa-pill-error    { background: var(--error-bg);   color: var(--error); }
.aa-pill-info     { background: var(--info-bg);    color: var(--info); }
.aa-pill-neutral  { background: var(--bg-bloc); color: var(--text-secondary); border-color: var(--border-strong); }

/* LE tampon — « EN RETARD » posé à −2° sur la facture. S'ajoute à un variant
   (ex. `aa-pill aa-pill-error aa-pill-alarm`) : il passe le fond en
   transparent, double la bordure et tamponne. Entrée animée aa-stamp-in
   (§50 micro-interactions). */
.aa-pill-alarm {
  background: transparent;
  border: 2px solid currentColor;
  border-radius: 4px;
  letter-spacing: 1.6px;
  font-weight: 800;
  transform: rotate(-2deg);
  animation: aa-stamp-in var(--dur-fast) var(--ease-stamp) both;
}

/* DA Carnet — tags de FILTRES ACTIFS (DS §6.10b, fix/chips-filtres-sombre
   2026-06-12). Chips supprimables au-dessus des listes devis/factures.
   Distinct du tampon statut .aa-pill : casse normale (noms de clients),
   croix de retrait. Couleurs 100 % tokens theme-aware — un fallback
   #f1f5f9 sur une variable --surface-elevated jamais définie rendait les
   chips invisibles en sombre (1,06:1). AAA : texte sur --bg-bloc 12,05:1
   clair / 8,81:1 sombre. Le layout reste aux utilitaires des templates. */
.aa-filter-tag {
  background: var(--bg-bloc);
  color: var(--text-primary);
  border: 1px solid var(--border);
}
.aa-filter-tag-remove {
  font-size: 1.1em;
  line-height: 1;
  color: inherit;
}
.aa-filter-tag-remove:hover { color: var(--error); }
.aa-active-filters-label,
.aa-active-filters-clear { color: var(--text-primary); }
.aa-active-filters-clear:hover { color: var(--accent-hover); }


/* ==========================================================================
   8. Alerts / callouts — info, warning, error, success (DS §6.12)
   ========================================================================== */

.aa-alert {
  display: flex;
  gap: 12px;
  padding: 14px 16px;
  border-radius: 8px;
  border: 0.5px solid;
  margin: 16px 0;
}
/* DA Carnet — fonds teintés theme-aware, texte encre/craie (les *-bg
   basculent par thème, le texte primaire reste lisible dans les deux). */
.aa-alert-warning { background: var(--warning-bg); color: var(--text-primary); border-color: var(--warning); }
.aa-alert-success { background: var(--success-bg); color: var(--text-primary); border-color: var(--success); }
.aa-alert-error   { background: var(--error-bg);   color: var(--text-primary); border-color: var(--error); }
.aa-alert-info    { background: var(--info-bg);    color: var(--text-primary); border-color: var(--info); }
.aa-alert-icon  { flex-shrink: 0; width: 20px; height: 20px; margin-top: 2px; }
.aa-alert-title { font-weight: 500; margin: 0 0 4px; font-size: 13px; }
.aa-alert-body  { margin: 0; font-size: 13px; line-height: 1.4; }


/* ==========================================================================
   8bis. Toasts — pile de notifications (fix/toast-duree-et-voxa, UX 2026-06-14)
   --------------------------------------------------------------------------
   Chargé APRÈS dist/tailwind.css : ces règles GAGNENT sur le composant
   `.toast { position: fixed }` de DaisyUI (collision historique qui ancrait
   le toast au bord bas et faisait sauter le lanceur Voxa de ~110 px).

   - Le CONTENEUR `#toast-container.aa-toast-stack` porte seul le `position:
     fixed`. Mobile : bas pleine largeur, AU-DESSUS de la TabBar (64 px) pour
     ne plus être masqué par la nav. Desktop (≥1024, plus de TabBar) : bas-
     CENTRE, à l'écart du lanceur Voxa (bas-droite) → zéro recouvrement.
   - Le TOAST individuel est forcé EN FLUX (`position: relative`) : il ne peut
     plus être détecté comme « barre basse » par le scan d'obstacles Voxa
     (jarvis-chat.js `_scanBottomBars`, qui ne retient que fixed/sticky).
   - Visibilité : aplat coloré contrasté (var(--success)/--error/--warning +
     texte var(--fill-on), theme-aware), présence physique (min-height), ombre
     franche. Fini le fond pâle illisible.
   ========================================================================== */

.aa-toast-stack {
  position: fixed;
  z-index: 50; /* au-dessus de tout (cf. hiérarchie §T-09-03-01) */
  display: flex;
  flex-direction: column;
  gap: 10px;
  /* le conteneur ne capte pas les clics ; chaque toast les réactive. */
  pointer-events: none;
  /* Mobile : bas pleine largeur, dégage la TabBar (64 px) + safe-area. */
  left: 16px;
  right: 16px;
  bottom: calc(64px + 16px + env(safe-area-inset-bottom, 0px));
  align-items: stretch;
}

@media (min-width: 1024px) {
  .aa-toast-stack {
    /* Desktop : pas de TabBar → bas-centre, à l'écart du lanceur Voxa. */
    left: 50%;
    right: auto;
    transform: translateX(-50%);
    bottom: 24px;
    align-items: center;
    max-width: 32rem;
  }
}

.aa-toast-stack .toast {
  position: relative;        /* EN FLUX — neutralise le `fixed` DaisyUI. */
  pointer-events: auto;
  display: flex;
  flex-direction: row;       /* neutralise le `flex-direction: column` DaisyUI. */
  align-items: center;
  gap: 12px;
  width: 100%;
  max-width: 32rem;
  min-height: 56px;          /* présence physique — visible au coin de l'œil. */
  padding: 14px 18px;
  border-radius: 14px;
  font-size: 15px;
  font-weight: 600;
  line-height: 1.35;
  white-space: normal;       /* neutralise le `white-space: nowrap` DaisyUI. */
  border: 1px solid transparent;
  /* Ombre franche (l'encre du tampon, pas un voile noir). */
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.22), 0 3px 8px rgba(0, 0, 0, 0.14);
}

/* Aplats colorés contrastés, theme-aware (clair/sombre via les tokens). */
.aa-toast-stack .toast.success { background: var(--success); color: var(--fill-on); }
.aa-toast-stack .toast.error   { background: var(--error);   color: var(--error-on); }
.aa-toast-stack .toast.warning { background: var(--warning); color: var(--fill-on); }
.aa-toast-stack .toast.info    { background: var(--bg-card); color: var(--text-primary); border-color: var(--border); }

/* Tampon de tête : ✓ succès / ! alerte (glyphe système, zéro webfont). */
.aa-toast-stack .toast.success::before,
.aa-toast-stack .toast.error::before,
.aa-toast-stack .toast.warning::before {
  flex-shrink: 0;
  font-size: 18px;
  font-weight: 800;
  line-height: 1;
}
.aa-toast-stack .toast.success::before { content: "\2713"; } /* ✓ */
.aa-toast-stack .toast.error::before   { content: "\0021"; } /* ! */
.aa-toast-stack .toast.warning::before { content: "\0021"; } /* ! */

.aa-toast-stack .toast .flex-1 { flex: 1 1 auto; }

/* Croix de fermeture : hérite la couleur de l'aplat, contraste garanti. */
.aa-toast-stack .toast .btn-icon {
  flex-shrink: 0;
  color: inherit;
  opacity: 0.8;
  background: transparent;
}
.aa-toast-stack .toast .btn-icon:hover { opacity: 1; }


/* ==========================================================================
   9. Tables — dashboard list (DS §6.13)
   ========================================================================== */

.aa-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}
.aa-table th {
  text-align: left;
  padding: 12px 16px;
  background: var(--bg-bloc);
  color: var(--text-secondary);
  font-weight: 500;
  font-size: 12px;
  letter-spacing: 0.3px;
  text-transform: uppercase;
  border-bottom: 0.5px solid var(--border);
}
.aa-table td {
  padding: 14px 16px;
  border-bottom: 0.5px solid var(--border);
  color: var(--text-primary);
}
.aa-table tbody tr:hover { background: var(--bg-bloc); cursor: pointer; }
.aa-table th.text-right,
.aa-table td.text-right {
  text-align: right;
}
.aa-row-action {
  background: transparent;
  border: none;
  color: var(--text-muted);
  cursor: pointer;
  font-size: 16px;
}
.aa-row-action:hover { color: var(--text-primary); }


/* ==========================================================================
   10. Empty state (DS §6.14)
   ========================================================================== */

.aa-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 48px 24px;
  gap: 12px;
}
.aa-empty-icon {
  width: 48px;
  height: 48px;
  color: var(--text-muted);
  margin-bottom: 8px;
}
.aa-empty-title {
  font-size: 16px;
  font-weight: 500;
  color: var(--text-primary);
  margin: 0;
}
.aa-empty-body {
  font-size: 14px;
  color: var(--text-secondary);
  margin: 0 0 16px;
  max-width: 360px;
  line-height: 1.5;
}


/* ==========================================================================
   11. Header global + navigation (DS §11.1)
   ========================================================================== */

.aa-header {
  display: flex;
  align-items: center;
  gap: 24px;
  padding: 14px 20px;
  border-bottom: 0.5px solid var(--border);
  background: var(--bg-primary);
  position: sticky;
  top: 0;
  z-index: 10;
}
.aa-header-logo {
  display: flex;
  gap: 10px;
  align-items: center;
  text-decoration: none;
  color: var(--text-primary);
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.5px;
}
.aa-header-nav {
  display: flex;
  gap: 20px;
  flex: 1;
}
.aa-nav-link {
  color: var(--text-secondary);
  text-decoration: none;
  font-size: 14px;
  font-weight: 500;
  padding: 6px 0;
  border-bottom: 2px solid transparent;
}
.aa-nav-link:hover { color: var(--text-primary); }
.aa-nav-link.active { color: var(--text-primary); border-bottom-color: var(--accent); }
.aa-header-actions { display: flex; gap: 12px; align-items: center; }
.aa-avatar {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  /* HOTFIX v0.9.7.50 (lock v0978-AY, Bug 7) -- background amber DS + texte
     dark contrast (var(--accent-on-primary) = #1F2937, identique aux CTAs
     amber primary). Theme-safe : --accent + --accent-on-primary sont definis
     light+dark de facon identique dans tokens.css §1. Avant : background
     --bg-bloc gris detonnait dans le reskin amber-first. */
  background: var(--accent);
  color: var(--accent-on-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 600;
  text-decoration: none;
}

@media (max-width: 768px) {
  .aa-header-nav { display: none; } /* Replace by burger menu */
}


/* ==========================================================================
   12. Stepper connector (DS §6.9 — Plan 08-05 prep, Q7)
   ========================================================================== */

.aa-step-connector {
  flex: 1;
  height: 2px;
  background: var(--border);
  margin: 0 8px;
  align-self: center;
  transition: background var(--dur-base, 250ms) var(--ease-standard, cubic-bezier(0.4, 0, 0.2, 1));
}
.aa-step-connector[data-state="completed"] {
  background: var(--accent);
}


/* ==========================================================================
   13. Tabs — onglets de navigation interne (Plan 08-05 prep)
   ========================================================================== */

.aa-tabs {
  display: flex;
  flex-direction: row;
  gap: 8px;
  border-bottom: 0.5px solid var(--border);
  margin-bottom: 16px;
}

/* fix/onglets-scroll-mobile (2026-06-13) — débordement ISOLÉ.
   `width: 0` : la rangée ne contribue plus à la largeur intrinsèque des
   ancêtres flex (main.flex-1 s'étirait au contenu et tout le shell pannait
   au swipe — mécanique documentée à l'origine dans la rustine inline de
   parametres/_tabs-nav.html, désormais unifiée ici) ; `min-width: 100%` la
   fait remplir son parent. Le surplus scrolle DANS la rangée : scrollbar
   masquée, snap léger, fondu d'affordance posé par aa-scroll-x.js via
   data-aa-fade. `.aa-scroll-x` = même contrat pour les rangées hors
   système .aa-tabs (nav Paramètres). Garde pytest :
   tests/app/frontend/test_onglets_scroll_isole.py. */
.aa-tabs,
.aa-scroll-x {
  overflow-x: auto;
  width: 0;
  min-width: 100%;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  scroll-snap-type: x proximity;
}
.aa-tabs::-webkit-scrollbar,
.aa-scroll-x::-webkit-scrollbar {
  display: none;
}
.aa-tabs[data-aa-fade="right"],
.aa-scroll-x[data-aa-fade="right"] {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
}
.aa-tabs[data-aa-fade="left"],
.aa-scroll-x[data-aa-fade="left"] {
  -webkit-mask-image: linear-gradient(to left, #000 calc(100% - 28px), transparent);
  mask-image: linear-gradient(to left, #000 calc(100% - 28px), transparent);
}
.aa-tabs[data-aa-fade="both"],
.aa-scroll-x[data-aa-fade="both"] {
  -webkit-mask-image: linear-gradient(to right, transparent, #000 28px, #000 calc(100% - 28px), transparent);
  mask-image: linear-gradient(to right, transparent, #000 28px, #000 calc(100% - 28px), transparent);
}

.aa-tab {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
  white-space: nowrap;
  scroll-snap-align: start;
  padding: 12px 16px;
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--text-secondary);
  font-family: inherit;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: background 150ms ease, color 150ms ease, border-color 150ms ease;
}
.aa-tab:hover {
  background: var(--bg-bloc);
  color: var(--text-primary);
}
.aa-tab[aria-selected="true"],
.aa-tab.aa-tab-active {
  color: var(--accent);
  border-bottom-color: var(--accent);
}
.aa-tab[aria-selected="true"] .aa-tab-count,
.aa-tab.aa-tab-active .aa-tab-count {
  background: var(--accent);
  color: var(--accent-on-primary);
}
.aa-tab-count {
  display: inline-block;
  margin-left: 8px;
  padding: 2px 8px;
  background: var(--bg-bloc);
  color: var(--text-muted);
  border-radius: 9999px;
  font-size: 12px;
  font-weight: 500;
  line-height: 1.2;
}


/* ==========================================================================
   14. Chip — filtre dropdown bouton (Plan 08-05 prep)
   ========================================================================== */

.aa-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 14px;
  min-height: 36px;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 9999px;
  color: var(--text-primary);
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  flex-shrink: 0; /* Plan 09-01 A1/B5 — chips ne se déplacent plus mutuellement quand un dropdown popover s'ouvre. */
  transition: border-color 150ms ease, background 150ms ease, color 150ms ease;
}
.aa-chip:hover {
  border-color: var(--accent);
}
.aa-chip[aria-pressed="true"] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-on-primary);
}


/* ==========================================================================
   15. Alert strip — bandeau alerte horizontal full-width (Plan 08-05 prep)
   ========================================================================== */

.aa-alert-strip {
  display: flex;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 12px 16px;
  border-radius: 8px;
  border: 0.5px solid;
  margin: 0;
}
/* DA Carnet — tokens theme-aware, plus d'overrides dark par variant. */
.aa-alert-strip.aa-alert-warning {
  background: var(--warning-bg);
  border-color: var(--warning);
  border-left-width: 4px;
  color: var(--text-primary);
}
.aa-alert-strip.aa-alert-success {
  background: var(--success-bg);
  border-color: var(--success);
  border-left-width: 4px;
  color: var(--text-primary);
}
.aa-alert-strip.aa-alert-error {
  background: var(--error-bg);
  border-color: var(--error);
  border-left-width: 4px;
  color: var(--text-primary);
}
.aa-alert-strip.aa-alert-info {
  background: var(--info-bg);
  border-color: var(--info);
  border-left-width: 4px;
  color: var(--text-primary);
}


/* ==========================================================================
   16. Basket — sticky bottom multi-select panier (Plan 08-05 prep)
        Q3 desktop only MVP : masqué sur mobile (<md = <768px)
   ========================================================================== */

.aa-basket,
.aa-multiselect-bar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 50;
  display: flex;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
  padding: 12px 24px;
  background: var(--bg-card);
  border-top: 1px solid var(--border);
  box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.08);
  transition: transform var(--dur-base, 250ms) var(--ease-standard, cubic-bezier(0.4, 0, 0.2, 1));
}
.aa-basket[hidden],
.aa-multiselect-bar[hidden] {
  display: flex;
  transform: translateY(100%);
}

/* HOTFIX v0.13.2.2 (wave 4 UX factures) — bandeau permanent visible avec
   état "vide". L'utilisateur doit voir le panier dès l'arrivée sur /factures
   pour découvrir la fonction bulk (problème signalé prod 2026-05-20). Le
   modificateur `--empty` atténue les couleurs sans masquer le bandeau. */
.aa-basket.aa-basket--empty,
.aa-multiselect-bar.aa-basket--empty {
  color: var(--text-secondary);
  background: var(--bg-card);
  box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.04);
}

/* HOTFIX v0.13.2.2 — boutons désactivés visuellement quand le panier est vide.
   Pattern parité avec `aa-checkbox-disabled` (Phase 8 HOTFIX v0.9.7.69) :
   opacité 50% + cursor not-allowed. L'attribut HTML `disabled` est posé en
   parallèle (cf. _basket.html) pour bloquer aussi le clic clavier / a11y. */
.aa-btn-disabled,
.aa-btn-disabled:hover,
.aa-btn-disabled:focus {
  opacity: 0.5;
  cursor: not-allowed;
  pointer-events: none;
}

@media (max-width: 767px) {
  .aa-basket,
  .aa-multiselect-bar {
    display: none;
  }
}

/* fix/factures-bandeau-et-exclus — clairance pour le bandeau de sélection fixe.
   La page /factures inclut `.aa-basket` / `.aa-multiselect-bar` (_basket.html)
   en `position: fixed; bottom: 0`, OPAQUE, visible dès ≥768px (masqué <768px,
   règle ci-dessus). À ≥1024px, la seule réserve basse du shell,
   `.aa-tabbar-clearance` (aa-chrome.css §5), retombe à 0 (la TabBar mobile
   disparaît) : plus rien ne dégage le bandeau, la DERNIÈRE pré-facture de la
   liste passe SOUS le bandeau et devient illisible / inatteignable au scroll
   (même nature que fix/devis-colonne-scroll). On réserve donc une clairance
   dédiée `.aa-factures-list-clearance` (posée sur #factures-dashboard-root,
   dashboard.html) supérieure à la hauteur du bandeau, à ≥1024px uniquement —
   là où `.aa-tabbar-clearance` ne couvre plus. Sous 1024px : `.aa-tabbar-clearance`
   (8rem) couvre déjà le bandeau ; mobile (<768px, bandeau masqué) intact. */
@media (min-width: 1024px) {
  .aa-factures-list-clearance {
    padding-bottom: 7rem;
  }
}


/* ==========================================================================
   17. Wordmark fix — wm-icon / wm-v / wm-rest / wm-wrapper
        (Plan 08-05 prep + 09-02 hotfix Wave 5.5 + v0.9.5 monochrome amber
         + v0.9.6 inline-flex gap:0 whitespace collapse)
        Lockup VoxNote = 3 barres amber (.wm-icon) + V amber (.wm-v) +
        "oxNote" amber (.wm-rest). Asset source :
        frontend/static/img/voxnote-mark.svg (canon Claude Design export).

        Décision 2026-05-08 v0.9.5 (post smoke v0.9.4) : OVERRIDE du DS canon
        Claude Design Image 10 (V charcoal + oxNote amber). Wordmark désormais
        monochrome amber #F59E0B (V + oxNote couleur unique via var(--accent)).
        Cf. documentation/DESIGN-SYSTEM.md §3.

        Décision 2026-05-08 v0.9.6 (post smoke v0.9.5) : .wm-wrapper introduit
        en `inline-flex; gap: 0` pour collapse tout whitespace inter-spans
        (HTML newline OU baseline gap font kerning across flex items). Le
        letter-spacing/margin-left négatifs ajoutés en v0.9.5 sont retirés
        (le wrapper inline-flex gap:0 suffit, et un kerning artificiel rendrait
        le texte moins lisible). La structure 2-spans (.wm-v + .wm-rest) est
        préservée pour permettre re-bicolore plus tard sans refactor API.
   ========================================================================== */

/* Plan 09-01 B2/D3 — élargir sélecteur pour couvrir les templates qui
   n'ont pas wrappé le wordmark dans .aa-header-logo. v0.9.5 hotfix : .wm-v
   suit var(--accent) comme .wm-rest (monochrome amber, plus de fork theme).
   v0.9.6 hotfix : letter-spacing + margin-left rollback, wm-wrapper gère le gap. */
.wm-v,    .aa-header-logo .wm-v    { color: var(--accent); }
.wm-rest, .aa-header-logo .wm-rest { color: var(--accent); }

/* v0.9.6 — wrapper inline-flex gap:0 collapse tout whitespace inter-spans
   (HTML newline + flex baseline gap). API 2-spans préservée. */
.wm-wrapper, .aa-header-logo .wm-wrapper {
  display: inline-flex;
  align-items: baseline;
  gap: 0;
}

/* Plan 09-02 Wave 5.5 — icône SVG inline 3 barres amber (lockup canon DS).
   currentColor + em-relative sizing : suit la couleur via .wm-icon { color }
   (théme-aware) et la taille via la font-size du wrapper parent (.aa-auth-wordmark
   font-size: 28px → icône ~28px de haut). Le V wordmark est cap-height ~24px
   sur 28-32px font-size, donc height: 1em aligne approximativement le sommet
   et le bas de l'icône avec le V. align-items: center sur .aa-auth-wordmark
   garantit l'alignement vertical du lockup. */
.wm-icon, .aa-header-logo .wm-icon {
  color: var(--accent);
  width: 0.875em;
  height: 1em;
  margin-right: 0.25em;
  flex-shrink: 0;
  display: inline-block;
  vertical-align: -0.05em;
}


/* ==========================================================================
   18. aa-detail-3col — grid 30/40/30 (Plan 09-01 spec tpl-detail prototype)
        Desktop only — grid scopé à @media md+ (v0.9.7.22 BUG-U13).
        Sur mobile : block flow, chaque .pf-col empile + Alpine x-show
        pilote la visibilité via tab toggle (cf. detail.html).
        Spec : prototype-5-screens.html lignes 124-129 (.pf-detail-grid).
   ========================================================================== */

.aa-detail-3col {
  display: block;  /* mobile fallback : block flow stacked */
}
.aa-detail-3col .pf-col {
  padding: 18px 16px;
}
.aa-detail-3col h3 {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--text-secondary);
  margin: 0 0 10px;
}

@media (min-width: 768px) {
  .aa-detail-3col {
    display: grid;
    grid-template-columns: 30% 40% 30%;
    gap: 0;
    min-height: 0;
  }
  .aa-detail-3col .pf-col {
    padding: 18px 24px;
    overflow-y: auto;
    border-right: 0.5px solid var(--border);
  }
  .aa-detail-3col .pf-col:last-child {
    border-right: none;
  }
}

/* fix/devis-colonne-scroll — clairance pour le bandeau d'envoi fixe.
   La page d'édition devis inclut `.aa-sticky-save-bar` (sticky-envoi-bar.html)
   en `position: fixed; bottom: 0` (≥md), OPAQUE (var(--bg-card)) et ~70px de
   haut en brouillon (boutons « Intervention #N » + « Envoyer le devis »). Le
   bandeau est hors flux : il chevauche le bas du contenu défilable. La seule
   réserve basse de ce shell, `.aa-tabbar-clearance` (aa-chrome.css §5), vaut
   8rem sous 1024px MAIS retombe à 0 à ≥1024px (la TabBar disparaît). Sans
   réserve dédiée, le bas de la colonne centrale (bloc Totaux) reste collé /
   masqué SOUS le bandeau et le scroll ne le dégage jamais.
   On réserve donc, à ≥1024px uniquement (là où `.aa-tabbar-clearance` ne
   couvre plus), une clairance > hauteur du bandeau pour que tout le contenu
   défile au-dessus. Sous 1024px : `.aa-tabbar-clearance` (8rem) suffit déjà. */
@media (min-width: 1024px) {
  .aa-devis-detail {
    padding-bottom: 5rem;
  }
}


/* ==========================================================================
   19. aa-info-card — card variant icon-leading (Plan 09-01 tpl-detail)
        Container + .name (h-weight) + .line rows avec leading svg slot.
        Spec : prototype-5-screens.html lignes 133-136 (.info-card).
   ========================================================================== */

.aa-info-card {
  background: var(--bg-card);
  border: 0.5px solid var(--border);
  border-radius: 10px;
  padding: 16px;
  margin-bottom: 14px;
}
.aa-info-card .name {
  font-size: 15px;
  font-weight: 600;
  color: var(--text-primary);
  margin: 0 0 6px;
}
.aa-info-card .line {
  font-size: 13px;
  color: var(--text-secondary);
  margin: 2px 0;
  display: flex;
  align-items: center;
  gap: 6px;
}
.aa-info-card .line svg {
  vertical-align: middle;
  color: var(--text-muted);
  flex-shrink: 0;
}


/* ==========================================================================
   20. aa-memo-quote — citation italique typée (Plan 09-01 tpl-detail)
        ⚠ EXCEPTION TYPÉE à l'anti-pattern DS §9 « pas de border-left coloré
        sur card » — ici utilisé pour citation memo verbatim de l'artisan
        (sémantique blockquote). Cf documentation/DESIGN-SYSTEM.md §9.
   ========================================================================== */

.aa-memo-quote {
  font-style: italic;
  border-left: 2px solid var(--accent);
  padding: 4px 12px;
  margin: 8px 0;
  color: var(--text-secondary);
  background: transparent;
  font-size: 13px;
  line-height: 1.5;
}


/* ==========================================================================
   21. aa-lines-table — table éditable inline (Plan 09-01 tpl-detail)
        Inputs transparents idle, focus ring amber, hover border.
        Spec : prototype-5-screens.html lignes 139-146.
   ========================================================================== */

.aa-lines-table {
  background: var(--bg-card);
  border: 0.5px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
  margin: 12px 0;
}
.aa-lines-table table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}
.aa-lines-table th {
  padding: 10px 12px;
  background: var(--bg-bloc);
  color: var(--text-secondary);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: 500;
  text-align: left;
}
.aa-lines-table th.amt-h,
.aa-lines-table td.amt {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.aa-lines-table td {
  padding: 10px 12px;
  border-top: 0.5px solid var(--border);
  color: var(--text-primary);
}
.aa-lines-input,
.aa-lines-table td input {
  width: 100%;
  padding: 6px 8px;
  background: transparent;
  border: 0.5px solid transparent;
  border-radius: 4px;
  color: inherit;
  font: inherit;
}
.aa-lines-input:hover,
.aa-lines-table td input:hover {
  border-color: var(--border);
}
.aa-lines-input:focus,
.aa-lines-table td input:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--bg-bloc);
}


/* ==========================================================================
   22. aa-totals — totals stack avec grand total emphasis (Plan 09-01)
        Spec : prototype-5-screens.html lignes 491-496 (.totals + .t-row + .grand).
   ========================================================================== */

.aa-totals {
  background: var(--bg-card);
  border: 0.5px solid var(--border);
  border-radius: 10px;
  padding: 14px 16px;
}
.aa-totals-row {
  display: flex;
  justify-content: space-between;
  padding: 4px 0;
  font-size: 13px;
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}
.aa-totals-row span:last-child {
  color: var(--text-primary);
}
.aa-totals-grand {
  display: flex;
  justify-content: space-between;
  padding: 8px 0 4px;
  margin-top: 8px;
  border-top: 1px solid var(--border-strong);
  font-size: 16px;
  font-weight: 700;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}


/* ==========================================================================
   23. aa-pdf-preview — mock papier compact (Plan 09-01 tpl-detail)
        ⚠ font-size 9-11px AUTORISÉ ici car simulation PDF imprimé (NON UI
        écran consommée). Cf IMPORT-NOTES.md §3.2.
        Spec : prototype-5-screens.html lignes 152-164.
   ========================================================================== */

.aa-pdf-preview {
  background: #FFFFFF;
  color: #1F2937;
  border-radius: 10px;
  padding: 18px;
  box-shadow: 0 0 0 0.5px var(--border), 0 8px 24px rgba(0, 0, 0, 0.06);
  font-size: 11px;
  line-height: 1.4;
}
.aa-pdf-preview h4 {
  margin: 0 0 12px;
  font-size: 16px;
  font-weight: 700;
}
.aa-pdf-preview .pdf-head {
  display: flex;
  justify-content: space-between;
  padding-bottom: 10px;
  border-bottom: 1px solid #E5E7EB;
}
.aa-pdf-preview .pdf-head .biz {
  font-size: 13px;
  font-weight: 600;
}
.aa-pdf-preview .pdf-meta {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  margin: 12px 0;
  font-size: 10px;
}
.aa-pdf-preview .pdf-meta div b {
  display: block;
  color: #6B7280;
  font-weight: 500;
  text-transform: uppercase;
  font-size: 9px;
  letter-spacing: 0.05em;
}
.aa-pdf-preview table {
  width: 100%;
  border-collapse: collapse;
  font-size: 10px;
}
.aa-pdf-preview th {
  text-align: left;
  padding: 6px 4px;
  border-bottom: 1px solid #1F2937;
  font-weight: 600;
}
.aa-pdf-preview td {
  padding: 5px 4px;
  border-bottom: 0.5px solid #E5E7EB;
}
.aa-pdf-preview .pdf-totals {
  margin-left: auto;
  width: 60%;
  margin-top: 10px;
  font-size: 10px;
}
.aa-pdf-preview .pdf-totals .row {
  display: flex;
  justify-content: space-between;
  padding: 3px 0;
}
.aa-pdf-preview .pdf-totals .row.g {
  font-weight: 700;
  font-size: 12px;
  border-top: 1px solid #1F2937;
  margin-top: 4px;
  padding-top: 6px;
}
.aa-pdf-preview .pdf-foot {
  margin-top: 14px;
  padding-top: 10px;
  border-top: 0.5px solid #E5E7EB;
  font-size: 9px;
  color: #6B7280;
  line-height: 1.5;
}


/* ==========================================================================
   24. Auth bundle — Plan 09-02 (D-23 Claude's discretion)
        Pattern DS §7.1 lignes 759-779 LOCKED.
        Mobile dark < md (375×) / desktop light >= md (>= 768px) responsive.
        Référence visuelle : Image #1a (mobile dark) + Image #1b (desktop light).
        Le mode (dark/light) est résolu côté serveur par
        `app.core.templates.render()` via cookie `voxnote_theme` puis fallback UA.
        La media-query est COSMÉTIQUE (padding, max-width) — la palette suit
        `data-theme` du root.
   ========================================================================== */

/* Container plein écran responsive — DS §7.1 mobile=charcoal #1F2937 / desktop=cream #FBF7F0.
   Hérite de --bg-primary qui flip selon data-theme (cf. §1). */
.aa-auth-shell {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 24px 16px;
  background: var(--bg-primary);
  transition: background 250ms cubic-bezier(0.4, 0, 0.2, 1);
}
@media (min-width: 768px) {
  .aa-auth-shell { padding: 48px 24px; }
}

/* Card centrée auth — DS §7.1 mobile=#374151 raised / desktop=#FFFFFF blanche.
   Hérite de --bg-card qui flip selon data-theme. Padding différentié mobile vs desktop. */
.aa-auth-card {
  width: 100%;
  max-width: 420px;
  padding: 24px 20px;
  border-radius: 12px;
  background: var(--bg-card);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
  transition: background 250ms cubic-bezier(0.4, 0, 0.2, 1),
              box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1);
}
@media (min-width: 768px) {
  .aa-auth-card {
    max-width: 400px;
    padding: 40px 32px;
  }
}

/* Wordmark wrapper centré au-dessus de la card — DS §7.1 ligne 764.
   align-items: center : Plan 09-02 Wave 5.5 — alignement vertical de
   l'icône SVG inline (.wm-icon) avec le wordmark texte (.wm-v / .wm-rest). */
.aa-auth-wordmark {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 24px;
  font-size: 28px;
  font-weight: 600;
  letter-spacing: -0.01em;
}
@media (min-width: 768px) {
  .aa-auth-wordmark { margin-bottom: 28px; }
}

/* Lien secondaire accent souligné — DS §7.1. DA Carnet : l'ocre clair tient
   sur kraft, les overrides par thème (ex #B45309/#F59E0B) sont supprimés —
   les tokens suffisent. */
.aa-link-amber {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
  font-weight: 500;
  transition: color var(--dur-fast) var(--ease-standard);
}
.aa-link-amber:hover { color: var(--accent-hover); }

/* Helper text — promotion (D-23) pour remplacer .helper-text legacy.
   Sub-text discret sous un input ou label info. */
.aa-helper-text {
  font-size: 14px;
  color: var(--text-muted);
  margin-top: 4px;
}
.aa-helper-text-error {
  color: var(--error);
}
.aa-helper-text-success {
  color: var(--success);
}

/* Step number — promotion (D-23) pour install/public iOS variant.
   Différent de .aa-step-bullet (icon-based stepper wizard onboarding).
   Numéroté 1/2/3, badge rond amber avec texte foncé. */
.aa-step-num {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--accent-on-primary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  font-size: 14px;
  flex-shrink: 0;
}

/* Spinner — promotion (D-23) pour remplacer .spinner legacy dans les forms auth.
   Animation spin via @keyframes (Tailwind animate-spin n'est pas garanti dispo
   selon build). Centré dans button avec display:inline-block. */
.aa-spinner {
  display: inline-block;
  animation: aa-spin 1s linear infinite;
}
@keyframes aa-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

/* Spinner inline 12px (fix/polish-uat — fetch du prix carburant national :
   1er appel d'un carburant = cache Redis froid, latence normale et visible). */
.aa-prix-spinner {
  display: inline-block;
  width: 12px;
  height: 12px;
  border: 2px solid var(--border, #E2E8F0);
  border-top-color: var(--accent, #F59E0B);
  border-radius: 50%;
  vertical-align: -2px;
  animation: aa-spin 0.8s linear infinite;
}


/* ==========================================================================
   25. Promotions Plan 09-03 — PWA core (D-34)
   ----------------------------------------------------------------------------
   Classes promues pour le bundle PWA core (home + memos/capture + memos/valider
   + layouts/pwa) selon DS §7.4 Pattern Detail page + §7.5 Pattern Capture mémo
   PWA + click-thru autoritatif `pwa-mobile/index.html`.

   Z-index hiérarchie (T-09-03-01) :
   - aa-tabbar           → z-index: 20 (sticky bottom)
   - aa-sticky-bottom-cta→ z-index: 25 (au-dessus TabBar, sous FAB)
   - aa-fab              → z-index: 30 (au-dessus TabBar, double affordance home D-27)
   - aa-toast-error      → z-index: 50 (au-dessus de tout)

   Refs : Plan 09-03 D-34, click-thru pwa-mobile/index.html lignes 14-50.
   ========================================================================== */

/* ----- TabBar 3-onglets MVP (D-26) ---------------------------------------- */

.aa-tabbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  height: 64px;
  background: var(--bg-card);
  border-top: 0.5px solid var(--border);
  display: flex;
  padding: 8px 0 12px;
  z-index: 20; /* T-09-03-01 — sous le FAB (z-index 30) */
}

.aa-tabbar-tab {
  flex: 1;
  background: transparent;
  border: none;
  color: var(--text-muted);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  font-size: 11px;
  cursor: pointer;
  padding: 0;
  text-decoration: none;
  transition: color 150ms cubic-bezier(0.4, 0, 0.2, 1);
  min-height: 44px; /* Tap target accessibility */
}

.aa-tabbar-tab:hover { color: var(--text-secondary); }

.aa-tabbar-tab-active { color: var(--accent); }
.aa-tabbar-tab-active svg { stroke: var(--accent); }

/* ----- FAB amber home only (D-27) ---------------------------------------- */

.aa-fab {
  position: fixed;
  bottom: 84px; /* 64px TabBar + 20px gap */
  right: 20px;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--accent-on-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  text-decoration: none;
  box-shadow: 0 8px 16px color-mix(in srgb, var(--accent) 30%, transparent);
  transition: transform 150ms cubic-bezier(0.4, 0, 0.2, 1);
  z-index: 30; /* T-09-03-01 — au-dessus de TabBar */
}
.aa-fab:active { transform: scale(0.96); }

/* ----- Mic button capture 160 (D-29 / DS §7.5 override click-thru 144) --- */

.aa-mic-btn {
  width: 160px;
  height: 160px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--accent-on-primary);
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: transform 150ms cubic-bezier(0.4, 0, 0.2, 1);
}
.aa-mic-btn:active { transform: scale(0.96); }

/* Pulse animation 1.6s amber pendant recording (DS §7.5 verbatim).
   Override click-thru pwa-mobile/index.html lignes ~30-40. */
.aa-mic-recording {
  animation: aa-pulse 1.6s cubic-bezier(0.4, 0, 0.2, 1) infinite;
}
@keyframes aa-pulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 50%, transparent); }
  70%  { box-shadow: 0 0 0 24px color-mix(in srgb, var(--accent) 0%, transparent); }
  100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 0%, transparent); }
}

/* ----- Spinner ring processing state (D-29) ------------------------------ */

.aa-spinner-ring {
  animation: aa-spin 1s linear infinite;
}

/* ----- Cards (interv-card, mini-card) D-28 -------------------------------- */

.aa-meta-tag {
  font-size: 11px;
  letter-spacing: 2.5px;
  text-transform: uppercase;
  color: var(--text-secondary);
  margin: 0 0 6px;
  font-variant-numeric: tabular-nums;
}

.aa-card-title {
  font-size: 16px;
  font-weight: 500;
  color: var(--text-primary);
  margin: 0 0 6px;
}

.aa-card-sub {
  font-size: 13px;
  color: var(--text-secondary);
  margin: 0;
  line-height: 1.4;
}

/* ----- Page heading + subtitle (D-28) ------------------------------------ */

.aa-page-title {
  font-size: 24px;
  font-weight: 600;
  color: var(--text-primary);
  letter-spacing: -0.3px;
  margin: 8px 0 4px;
}

.aa-page-sub {
  font-size: 14px;
  color: var(--text-secondary);
  margin: 0 0 24px;
  line-height: 1.5;
}

/* ----- Alert strip top-sticky home pending mémos (D-28) ------------------ */

.aa-alert-strip {
  position: sticky;
  top: 56px; /* sous le header sticky */
  z-index: 9;
  background: var(--warning-bg);
  color: var(--text-primary);
  padding: 10px 16px;
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  border-bottom: 0.5px solid var(--border);
  text-decoration: none;
  cursor: pointer;
  transition: background 150ms cubic-bezier(0.4, 0, 0.2, 1);
}
.aa-alert-strip:hover { background: var(--warning-bg); filter: brightness(0.95); }

/* ----- Toast inline error capture (D-29) --------------------------------- */

.aa-toast-error {
  position: fixed;
  bottom: 100px; /* au-dessus du FAB (84) + gap */
  left: 50%;
  transform: translateX(-50%);
  background: var(--error-bg);
  color: var(--error);
  padding: 12px 16px;
  border-radius: 8px;
  font-size: 14px;
  font-weight: 500;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  z-index: 50; /* au-dessus de tout (FAB / TabBar / sticky CTAs) */
  max-width: calc(100% - 40px);
  cursor: pointer;
}
/* DA Carnet — --error-bg/--error sont theme-aware : l'override dark se
   réduit à rien et a été supprimé. */

/* ----- Card stack vertical (D-30 valider Pattern Detail §7.4) ------------ */

.aa-card-stack {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 20px;
}

/* ----- Sticky bottom CTAs (D-30 valider, au-dessus TabBar) --------------- */

.aa-sticky-bottom-cta {
  position: fixed;
  bottom: 64px; /* au-dessus de TabBar */
  left: 0;
  right: 0;
  background: var(--bg-card);
  border-top: 0.5px solid var(--border);
  padding: 12px 16px;
  z-index: 25; /* T-09-03-01 — sous le FAB (30), au-dessus TabBar (20) */
  display: flex;
  flex-direction: column;
  gap: 8px;
}


/* ==========================================================================
   26. v0.9.7.5 Bundle E — Wordmark V/oxNote selection rect (BUG-2026-05-09-04)

        Symptom : when the user click-drags to select the wordmark text,
        a visible gap appears between "V" and "oxNote" inside the selection
        highlight. The HTML source has zero whitespace between the two spans
        (`<span class="wm-v">V</span><span class="wm-rest">oxNote</span>`),
        so it is NOT a text-content gap.

        Root cause : §17 set `.wm-wrapper { display: inline-flex; gap: 0 }`
        as the v0.9.6 hotfix to collapse a HTML-newline whitespace. With
        inline-flex, each child becomes a flex item with its own bounding
        box. Browsers draw selection HIGHLIGHTS per flex-item box → a tiny
        gap is visible across the flex boundary even with `gap: 0`.

        Fix (cascade override, end-of-file = wins over §17 declaration) :
        switch the wrapper to `display: inline`. V and oxNote spans become
        plain inline siblings ; the browser renders one contiguous selection
        rect across them. Visual collapse stays at zero (HTML has no
        whitespace, no letter-spacing on .wm-v).

        Why end-of-file (not in-place edit) : window n7e (Bundle D v0.9.7.4
        capture mémo, parallel) appends DOM/state-machine CSS to tokens.css
        ; an in-place edit at line 852 would risk a merge conflict. The
        cascade lets us patch with zero collision risk.

        2-spans API (.wm-v + .wm-rest) preserved to allow re-bicolor later
        without refactor.
   ========================================================================== */
.wm-wrapper, .aa-header-logo .wm-wrapper {
  display: inline;
  /* gap: 0 / align-items: baseline are no-ops on inline display, kept
     implicit via the §17 declaration ; only outer `display` is overridden. */
}

/* ==========================================================================
   27. v0.9.7.4 Bundle D — Capture mémo prototype-faithful tokens

        Source autoritaire : prototype-5-screens.html lines 47-67 (bundle
        Claude Design VoxNote). Spec autoritaire per CLAUDE.md
        <spec_first_protocol> + ADR-001 + ADR-004.

        Naming convention : on utilise les noms de classes VERBATIM du bundle
        (`.capture`, `.rec-btn`, `.wave`, etc.) sans alias `aa-capture-*`.
        Rationale : matche le bundle 1:1 → diff facilité, pas de mapping
        mental aa-* ↔ bundle. Scope limité : ces classes ne sont utilisées
        QUE par frontend/templates/memos/capture.html (capture-only). Pas
        de risque de collision cross-page.

        Override Plan 09-03 D-29 mic 160 → 120 (dette UI cosmétique
        acceptée — `aa-mic-btn` n'est plus référencé que par capture.html
        ; pas de propagation cross-écran nécessaire). Cf. ADR-004
        Conséquences §3.

        --ease-standard est défini en §3 (ligne 94). Fallback inline
        cubic-bezier(0.4, 0, 0.2, 1) au cas où le var fail.
        --accent / --accent-hover définis §1 (orange #F59E0B / #D97706
        clair, #F59E0B / #FBBF24 dark via [data-theme]).

        Numérotation : §26 occupé par v0.9.7.5 Bundle E wordmark fix
        (parallèle window n7e). Cette section prend §27.

        v0.9.7.23 (2026-05-10) — Theme-aware : tous les hardcoded #1F2937 /
        #374151 / #F9FAFB / #9CA3AF / #6B7280 ont été convertis en tokens
        (`var(--bg-primary)`, `var(--bg-card)`, `var(--text-primary)`,
        `var(--text-muted)`, `var(--border)`). Restent intentionnellement
        hardcoded (brand contrast volontaire, AAA both modes) :
          - `.rec-btn.idle { background: var(--accent); color: #1F2937 }`
            → orange brand + texte sombre (var(--accent-on-primary) value).
          - `.rec-btn.recording { background: #B91C1C; color: #FFF }` →
            recording state red, voulu intense both modes (DS §7.5).
          - `.wave span { background: var(--accent) }` → orange brand idem.
        Origine du verrou dark v0.9.7.12 (carré blanc résiduel) résolue à
        la racine : si `.capture` matche `--bg-primary`, plus de contraste
        body↔capture en light mode. Cf. lock v0978-AA-capture-light-mode.
   ========================================================================== */

.capture {
  /* v0.9.7.23 — theme-aware : matche `body { background: var(--bg-primary) }`
     du base layout. Plus jamais de "carré blanc" résiduel : le bg de la page
     suit le toggle light/dark, donc rien à transparaître. */
  background: var(--bg-primary);
  /* Hotfix v0.9.7.12 (BUG 2) : `min-height: 100dvh` (au lieu de `height: 100%`)
     pour que le dictaphone fill le viewport entier. Le parent <main class="flex-1
     pb-32"> de pwa.html crée 128px de padding-bottom (clearance TabBar) ; sur
     /memos/capture la TabBar est override en vide → ce padding devenait un
     "carré blanc" visible en light mode. capture.html neutralise pb-32 via
     {% block head_extra %}, et 100dvh garantit le full-viewport sur mobile
     (gère iOS Safari address bar collapse). */
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

.capture-top {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 14px 18px;
}

.capture-stop {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  /* v0.9.7.23 — theme-aware (cercle Retour, fond pastille discret). */
  background: var(--bg-card);
  border: 0.5px solid var(--border);
  color: var(--text-primary);
  cursor: pointer;
  display: grid;
  place-items: center;
  text-decoration: none;       /* override <a> default underline */
}
.capture-stop:hover { background: var(--border); }

.capture-tag {
  font-size: 11px;
  /* v0.9.7.23 — theme-aware. */
  color: var(--text-muted);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.capture-stage {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 28px;
  padding: 0 24px;
  position: relative;          /* anchor pour bouton camera floating D-03 */
}

.capture-timer {
  font-size: 56px;
  font-weight: 200;
  /* v0.9.7.23 — theme-aware. */
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
  letter-spacing: -2px;
}

.wave {
  display: flex;
  align-items: center;
  gap: 4px;
  height: 80px;
}
.wave span {
  width: 4px;
  /* En recording : orange brand animée — INCHANGÉ (var(--accent) déjà cross-theme). */
  background: var(--accent);
  border-radius: 1px;
  animation: wv 0.9s var(--ease-standard, cubic-bezier(0.4, 0, 0.2, 1)) infinite;
  transform-origin: center;
}
.wave.idle span {
  /* v0.9.7.23 — waveform idle = ardoise discrète theme-aware (avant: #6B7280 hardcoded). */
  background: var(--text-muted);
  opacity: 0.5;
  animation: none;
  transform: scaleY(0.3);
}
@keyframes wv {
  0%, 100% { transform: scaleY(0.25); }
  50% { transform: scaleY(1); }
}

.rec-btn {
  width: 120px;
  height: 120px;
  border-radius: 50%;
  border: none;
  cursor: pointer;
  transition: all 250ms var(--ease-standard, cubic-bezier(0.4, 0, 0.2, 1));
  display: grid;
  place-items: center;
}
.rec-btn.idle {
  background: var(--accent);
  color: #1F2937;
}
.rec-btn.idle:hover {
  background: var(--accent-hover, #FBBF24);
  transform: scale(1.03);
}
.rec-btn.recording {
  background: var(--error);
  color: var(--error-on);
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--error) 60%, transparent);
  animation: pulse 1.4s var(--ease-standard, cubic-bezier(0.4, 0, 0.2, 1)) infinite;
}
@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--error) 55%, transparent); }
  100% { box-shadow: 0 0 0 24px color-mix(in srgb, var(--error) 0%, transparent); }
}

.rec-square {
  width: 36px;
  height: 36px;
  background: currentColor;
  border-radius: 6px;
}

.rec-mic {
  width: 48px;
  height: 48px;
}

.capture-bottom {
  padding: 0 24px 32px;
}
.capture-bottom .aa-btn-primary {
  width: 100%;
  padding: 16px;
  font-size: 15px;
}
.capture-bottom .aa-btn-primary:disabled {
  /* v0.9.7.23 — theme-aware disabled state (avant: #374151/#6B7280 hardcoded). */
  background: var(--bg-card);
  color: var(--text-muted);
  border: 0.5px solid var(--border);
  cursor: not-allowed;
}
.capture-hint {
  text-align: center;
  /* v0.9.7.23 — theme-aware. */
  color: var(--text-muted);
  font-size: 12px;
  margin: 0 0 12px;
}

/* Bouton camera floating D-03 — déplacé du header bundle (qui n'a pas de
   camera). Position absolute relative au .capture-stage (position:relative
   ci-dessus). z-index 5 sous toast (10) et modal (40).
   v0.9.7.23 — theme-aware (avant: rgba(55,65,81,0.7) hardcoded dark). */
.aa-camera-floating {
  position: absolute;
  bottom: 16px;
  right: 16px;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--bg-card);
  color: var(--text-primary);
  border: 0.5px solid var(--border);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
  display: grid;
  place-items: center;
  cursor: pointer;
  z-index: 5;
}
.aa-camera-floating:hover { background: var(--border); }

/* ==========================================================================
   §29 — Theme toggle icon dynamic visibility (Bundle I v0.9.7.9 / BUG-2026-05-09-06)

        Pilote la visibilité des 2 SVG (sun + moon) du fragment
        `frontend/templates/fragments/theme-toggle-button.html` selon le
        `data-theme` posé sur <html> par templates.py:render() (SSR cookie/UA).

        Architecture cohérente avec le rail tokens.css §1 [data-theme="dark|light"]
        (PAS Tailwind `dark:` qui dépend de prefers-color-scheme — désynchronisé,
        cf. .planning/debug/dark-mode-mobile-broken.md §1.4).

        Numérotation : §27 = v0.9.7.4 capture mémo. §29 alloué à Bundle I (Bundle H
        ne touche pas tokens.css). Bundle H lock = .planning/lock/v0979-dark-mode-toggle-global.md.

        Plain CSS (pas de directive Tailwind PostCSS) — tokens.css est servi
        statique par FastAPI, n'est pas processed par Tailwind. Variables
        --text-primary / --border / --bg-elevated existent déjà §1 (theme-aware).
   ========================================================================== */

/* Default state (cas dégénéré : aucun [data-theme] sur <html>) — montre la lune
   pour suggérer le passage en sombre. SSR pose toujours data-theme="light|dark",
   donc cette règle agit en ceinture-bretelles uniquement. */
.aa-theme-toggle [data-theme-icon] { display: none; }
.aa-theme-toggle [data-theme-icon="light"] { display: inline-block; }

/* Mode sombre actif → on affiche le SOLEIL (l'icône invite à passer en clair). */
[data-theme="dark"] .aa-theme-toggle [data-theme-icon="light"] { display: none; }
[data-theme="dark"] .aa-theme-toggle [data-theme-icon="dark"]  { display: inline-block; }

/* Mode clair actif → on affiche la LUNE (l'icône invite à passer en sombre). */
[data-theme="light"] .aa-theme-toggle [data-theme-icon="dark"]  { display: none; }
[data-theme="light"] .aa-theme-toggle [data-theme-icon="light"] { display: inline-block; }

/* Hover affordance — utilise les variables --bg-elevated / --border-strong
   définies §1 (theme-aware). Plain CSS uniquement. */
.aa-theme-toggle:hover {
  background: var(--bg-elevated, rgba(0, 0, 0, 0.04));
  border-color: var(--border-strong, var(--border));
}
.aa-theme-toggle:focus-visible {
  outline: 2px solid var(--accent, #F59E0B);
  outline-offset: 2px;
}


/* ==========================================================================
   §30 — v0.9.7.17 hotfix `.input` dark-mode bg invisible (BUG-2026-05-10)

        Symptôme : sur PWA mobile en dark mode, page /clients (et tous les
        formulaires utilisant `class="input"`), le texte tapé devient blanc
        sur fond blanc → invisible.

        Root cause : `frontend/static/src/input.css` §components définit
        `.input { @apply ... bg-white text-foreground; }` :
        - `bg-white` → background blanc statique (ne flippe pas via data-theme).
        - `text-foreground` → résolu en `var(--text-primary)` qui flippe à
          #F9FAFB en dark mode → texte blanc sur bg blanc.

        Fix : override en dark mode uniquement, theme-aware. Light mode
        100% préservé (aucune règle ne touche light). tokens.css charge
        APRÈS tailwind.css (cf. base.html:14-18) donc gagne la cascade par
        ordre source — pas de !important.

        Touche tous les inputs PWA partageant `class="input"` :
        - `clients-search-input` (recherche.html:80)
        - `factures-search-input` (_toolbar.html:28)
        - tous les formulaires PWA (auth, clients/new, etc.).
   ========================================================================== */

[data-theme="dark"] .input {
  background-color: var(--bg-card);
  border-color: var(--border);
}


/* ==========================================================================
   §31 — v0.9.7.17 hotfix #search-skeleton no layout reservation (BUG-2026-05-10)

        Symptôme : page /clients, le rectangle résultat (ou empty state)
        apparaît ~280px sous la barre de recherche. Sur mobile clavier
        ouvert, le résultat est hors-viewport → l'utilisateur doit scroller
        pour voir ce qu'il a tapé apparaître en résultat.

        Root cause : `#search-skeleton` (recherche.html:100) utilise la
        classe HTMX core `.htmx-indicator` qui ne fait que `opacity: 0` au
        repos (legacy default). Le skeleton 3 cards × 88px + gap-3 réserve
        donc 264px de hauteur SOUS l'input même quand idle, et le parent
        flex-col gap-3 (12px) ajoute encore 12px. Résultat ~280px de blanc.

        Fix : `display: none` quand idle, `display: flex` quand HTMX a posé
        `.htmx-request` sur l'indicator pendant la requête (~300ms cf.
        hx-trigger="keyup changed delay:300ms" recherche.html:84). Spec
        HTMX : https://htmx.org/docs/#indicators.

        Scope STRICT `#search-skeleton` (sélecteur ID) → aucun side-effect
        sur :
        - `#factures-search-skeleton` (_toolbar.html:131-134) : div simple
          non-flex, garde son comportement actuel (Tailwind opacity).
        - Tout autre `.htmx-indicator` qui pourrait être ajouté ailleurs.

        UX preservée : pendant la requête live search, le skeleton 3 cards
        flash brièvement (animation shimmer §6.7) → feedback visuel < 300ms
        cf. CLAUDE.md design_principles.
   ========================================================================== */

#search-skeleton { display: none; }
#search-skeleton.htmx-request { display: flex; }

/* ==========================================================================
   §32 — v0.9.7.20 hotfix .aa-btn-mic-inline (BUG-U12)

        Bouton compact "rond amber + label" pour la CTA secondaire
        "Ajouter une note" sur /interventions/{id} (footer, au-dessus de
        Clôturer). Pattern dérivé de .aa-fab (§ inline ligne 1318) — même
        cercle amber 48px shadow, mais positionné inline (pas fixed) avec
        un label texte à droite, vertical-align middle.

        Couleurs theme-aware via tokens §1 (--accent / --accent-hover /
        --accent-on-primary / --text-primary). Pas de hex hardcodé →
        light & dark se flippent automatiquement.

        Différencié de .btn-mic (input.css:154, 200x200 dictaphone principal
        sur /memos/capture) et .aa-fab (1318, FAB home D-27 fixed bottom-right).

        Plain CSS — tokens.css est servi statique sans Tailwind PostCSS.

        Référence : .planning/lock/v0978-C-vocale-button-reskin.md /
        .planning/quick/260510-c-v09720-vocale-button-reskin/PLAN.md.
   ========================================================================== */

.aa-btn-mic-inline {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 56px;
  padding: 4px 8px;
  border-radius: 12px;
  text-decoration: none;
  color: var(--text-primary);
  font-weight: 600;
  font-size: 16px;
  background: transparent;
  cursor: pointer;
}

.aa-btn-mic-inline-circle {
  flex: 0 0 48px;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--accent-on-primary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 6px 12px color-mix(in srgb, var(--accent) 30%, transparent);
  transition: background 150ms cubic-bezier(0.4, 0, 0.2, 1),
              transform 150ms cubic-bezier(0.4, 0, 0.2, 1);
}

.aa-btn-mic-inline-circle svg {
  stroke: var(--accent-on-primary);
}

.aa-btn-mic-inline:hover .aa-btn-mic-inline-circle {
  background: var(--accent-hover);
}

.aa-btn-mic-inline:active .aa-btn-mic-inline-circle {
  transform: scale(0.95);
}

.aa-btn-mic-inline:focus-visible {
  outline: none;
}
.aa-btn-mic-inline:focus-visible .aa-btn-mic-inline-circle {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .aa-btn-mic-inline-circle { transition: none; }
  .aa-btn-mic-inline:active .aa-btn-mic-inline-circle { transform: none; }
}


/* ==========================================================================
   32. aa-tab-pill / aa-btn-pdf-amber — préfacture mobile pill toggle
        HOTFIX v0.9.7.22 BUG-U13.
        Mobile-only : pill toggle 3 onglets (Client / En-tête / Aperçu)
        sticky top + bouton "Voir l'aperçu PDF" amber identique light/dark.
        Référence : .planning/lock/v0978-E-prefacture-mobile-toggle.md /
        .planning/quick/260510-kt3-v09722-prefacture-mobile-toggle/PLAN.md.
   ========================================================================== */

.aa-tab-toggle-group {
  display: flex;
  gap: 8px;
  padding: 8px 16px;
  background: var(--bg-card);
  border-bottom: 0.5px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 10;
}

.aa-tab-pill {
  flex: 1;
  min-height: 44px;
  padding: 10px 12px;
  font-family: inherit;
  font-size: 14px;
  font-weight: 500;
  text-align: center;
  border: none;
  border-radius: 9999px;
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  transition: background 200ms cubic-bezier(0.4, 0, 0.2, 1),
              color 200ms cubic-bezier(0.4, 0, 0.2, 1);
}

.aa-tab-pill:hover:not([aria-selected="true"]) {
  background: var(--bg-bloc);
  color: var(--text-primary);
}

.aa-tab-pill[aria-selected="true"] {
  background: var(--accent);
  color: var(--accent-on-primary);
  font-weight: 600;
}

.aa-tab-pill:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.aa-btn-pdf-amber {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  min-height: 48px;
  padding: 10px 16px;
  background: var(--accent);
  color: var(--accent-on-primary);
  border: none;
  border-radius: 8px;
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: background 150ms cubic-bezier(0.4, 0, 0.2, 1);
}

.aa-btn-pdf-amber:hover {
  background: var(--accent-hover);
}

.aa-btn-pdf-amber svg {
  stroke: var(--accent-on-primary);
}

.aa-btn-pdf-amber:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* Mobile-only enforcement : tokens.css charge après Tailwind, donc
   `md:hidden` (display: none @md+) est sinon overridden par le `display:flex`
   ci-dessus. On scope explicitement la visibilité ici. */
@media (min-width: 768px) {
  .aa-tab-toggle-group,
  .aa-btn-pdf-amber {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .aa-tab-pill { transition: none; }
  .aa-btn-pdf-amber { transition: none; }
}


/* ==========================================================================
   33. aa-lignes-table — responsive cards mobile (HOTFIX v0.9.7.25 BUG-U13bis).

        Mobile (< 768px) : la table 5-cols TVA/Désignation/Qté/PU HT/Total HT
        + actions devient un stack vertical de cards lisibles. Desktop (≥ 768px)
        INCHANGÉ : aucune règle ne s'applique → table classique avec thead.

        Pattern : DOM `<tr><td>` préservé (HTMX `hx-include="closest tr"` continue
        de fonctionner avec `<tr>` rendu `display: block` car le DOM-tree reste
        identique, seul le rendu visuel passe en flow block).

        Labels via pseudo-élément `td::before { content: attr(data-label) }` —
        chaque `<td>` du `ligne-edit-row.html` porte `data-label="TVA|Designation|
        Qte|PU HT|Total HT|Actions"`.

        Variables CSS theme-aware tokens.css §1 :
          - --bg-card     (#FFFFFF light / #374151 dark)  fond carte
          - --border      (#E2E8F0 light / #475569 dark)  bordure carte
          - --text-secondary (#475569 light / #E5E7EB dark) label

        Référence : .planning/lock/v0978-AC-lignes-table-mobile.md
                    .planning/quick/260510-m1g-hotfix-v0-9-7-25-lignes-edit-table-mobil/
   ========================================================================== */

@media (max-width: 767px) {
  .aa-lignes-table thead {
    display: none;
  }

  .aa-lignes-table tr {
    display: block;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 8px;
    margin-bottom: 12px;
    padding: 12px;
  }

  .aa-lignes-table td {
    display: block;
    padding: 6px 0;
    text-align: left;
    border: none;
  }

  .aa-lignes-table td::before {
    content: attr(data-label);
    display: block;
    font-size: 12px;
    font-weight: 500;
    color: var(--text-secondary);
    margin-bottom: 4px;
  }

  /* Total HT : Tailwind `text-right` posé sur le td desktop est neutralisé
     ici car en mode card l'alignement gauche reste cohérent avec les autres
     labels. La classe `aa-ligne-total` est ajoutée par T-1. */
  .aa-lignes-table td.aa-ligne-total {
    text-align: left;
  }
}


/* ==========================================================================
   34. aa-trial-badge — Phase 10 Trial System (LOCK-06 mobile only + AC-1A.1.3)

        Mobile (< 768px) : badge "Essai — J-X restants" injecté SSR dans le
        header sticky pwa.html via _components/trial_badge.html. Hidden desktop
        (DS LOCK-06 — sidebar desktop reskinable Phase 12).

        Données : request.state.trial_days_remaining posé par
        CheckTrialExpiredMiddleware (backend/app/middleware/trial.py — Plan 10-03).
        SSR pur anti-FOUC (Anti-pattern 5 RESEARCH §374).

        Variables CSS theme-aware tokens.css §1 :
          - --info / --info-bg     (badge normal)
          - --warning / --warning-bg (badge urgent J-3 et moins)

        Référence : .planning/phases/10-trial-system-sprint-1a-v2-1/10-CONTEXT.md
                    LOCK-06 + RESEARCH Code Example 5 §919-973
   ========================================================================== */

.aa-trial-badge {
  display: none;  /* hidden desktop by default */
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 12px;
  font-size: 13px;
  font-weight: 500;
  background: var(--info-bg);
  color: var(--info);
  white-space: nowrap;
  text-decoration: none;
}

.aa-trial-badge-urgent {
  background: var(--warning-bg);
  color: var(--warning);
}

@media (max-width: 767px) {
  .aa-trial-badge { display: inline-flex; }
}

/* ==========================================================================
   35. aa-phone-deeplink — bouton tel: + copy-to-clipboard /trial-expired
         Pattern Phase 6 D-13 contact-actions.js réutilisable
         (cf. frontend/static/js/contact-actions.js).
   ========================================================================== */
.aa-phone-deeplink {
  font-size: 24px;
  font-weight: 600;
  color: var(--accent);
  text-decoration: none;
  letter-spacing: 0.5px;
  display: inline-block;
  padding: 8px 12px;
}
.aa-phone-deeplink:hover { text-decoration: underline; }

/* ==========================================================================
   36. aa-sidebar-link / aa-sidebar-link-active — Dashboard sidebar (Plan 12-01)
         5 items LOCKED D-14 (Tableau de bord / Clients / Interventions /
         Pré-factures / Paramètres). Visible UNIQUEMENT lg ≥ 1024px (driven by
         parent <aside class="hidden lg:flex">).
         Active state SSR via Jinja `request.url.path` (no JS, no FOUC).
         Réf : .planning/phases/12-dashboard-web-landing-cohorte-beta-sprint-3-v2-1/
               12-UI-SPEC.md §Surface 2 + 12-PATTERNS.md R-12-01-b-03.
   ========================================================================== */
.aa-sidebar-link {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  margin: 0 8px;
  border-radius: 8px;
  color: var(--text-secondary);
  font-size: 16px;
  font-weight: 500;
  text-decoration: none;
  transition: background-color 150ms ease, color 150ms ease;
  min-height: 48px; /* design_principles.md : touch target AAA */
}
.aa-sidebar-link:hover {
  background: var(--bg-bloc);
  color: var(--text-primary);
}
.aa-sidebar-link:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
.aa-sidebar-link-active {
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  color: var(--text-primary);
  position: relative;
}
.aa-sidebar-link-active::before {
  content: "";
  position: absolute;
  left: 0;
  top: 8px;
  bottom: 8px;
  width: 4px;
  background: var(--accent);
  border-radius: 0 2px 2px 0;
}

/* ==========================================================================
   37. aa-skeleton — Loading shimmer placeholders (Plan 12-01 dashboard cards)
         Réf : UI-SPEC §Surface 1 (R-12-01-b-03 — tokens to create in §27/§28
         of the spec but registered here as §36/§37 in actual numbering).
         Respects `prefers-reduced-motion: reduce` (disable animation,
         fallback flat `var(--bg-bloc)` per WCAG 2.1 SC 2.3.3).
   ========================================================================== */
.aa-skeleton {
  background: linear-gradient(
    90deg,
    var(--bg-bloc) 0%,
    var(--border) 50%,
    var(--bg-bloc) 100%
  );
  background-size: 200% 100%;
  animation: aa-shimmer 800ms infinite linear;
  border-radius: 6px;
}
@keyframes aa-shimmer {
  from { background-position: 200% 0; }
  to   { background-position: -200% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .aa-skeleton {
    animation: none;
    background: var(--bg-bloc);
  }
}

/* ==========================================================================
   38. aa-dashboard-col-transition — Plan 12-03 (DASH-05)
         Réf : UI-SPEC §Surface 1 Interactions (collapse animation 200ms).
         Spec-numbered §29 ; actual numbering offset to §38 (Plan 12-01
         deviation 5 carry-forward — §27..§37 already allocated).

         Smooth opacity + max-width transition when the col-left section
         toggles `md:hidden` via Alpine `:class`. The `prefers-reduced-motion`
         override snaps to instant per WCAG 2.1 SC 2.3.3.
   ========================================================================== */
.aa-dashboard-col-transition {
  transition: opacity 200ms cubic-bezier(0.4, 0, 0.2, 1),
              max-width 200ms cubic-bezier(0.4, 0, 0.2, 1);
}
@media (prefers-reduced-motion: reduce) {
  .aa-dashboard-col-transition {
    transition: none;
  }
}

/* ==========================================================================
   39. aa-btn-icon — Plan 12-03 (used by dashboard collapse toggle)
         Réf : UI-SPEC §Surface 1 Interactions + DS 4-point system 48x48
         touch target. Spec-numbered §30 ; actual offset §39.

         Square 48x48 icon button with border + transparent bg, hover lift,
         focus-visible amber outline (AAA contrast via --accent token).
         Used by the collapse toggle ; future icon-only buttons can reuse.
   ========================================================================== */
.aa-btn-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: 8px;
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-secondary);
  cursor: pointer;
  transition: background-color 150ms ease, color 150ms ease;
}
.aa-btn-icon:hover {
  background: var(--bg-bloc);
  color: var(--text-primary);
}
.aa-btn-icon:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ==========================================================================
   40. Landing polish v2 — TYPOGRAPHIE (commit 1/7)
        Scope : body.aa-landing uniquement (cf. landing/index.html bloc
        body_class). Aucune fuite cascade sur PWA / dashboard.
        Polices Geist (display) + Inter (body) — chargées Google Fonts dans
        landing/index.html head_extra avec preconnect.
        Renuméroté §38 → §40 lors du rebase sur master (§38 collapse +
        §39 .aa-btn-icon déjà alloués par Plan 12-03).
   ========================================================================== */

body.aa-landing {
  font-family: var(--font-body);
  background: var(--bg-primary);
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

body.aa-landing h1,
body.aa-landing h2,
body.aa-landing h3,
body.aa-landing .aa-landing-display {
  font-family: var(--font-display);
  letter-spacing: -0.025em;
}

body.aa-landing .aa-landing-eyebrow {
  font-family: var(--font-body);
  text-transform: uppercase;
  font-size: 12px;
  letter-spacing: 0.18em;
  font-weight: 500;
  color: var(--text-muted);
}


/* ==========================================================================
   §38.b Landing polish v2 — PALETTE COULEURS + CARDS SHADOW (commit 2/7)
        Hero halo amber subtil, feature cards lift, pricing ring amber,
        footer plus dark, FAQ items en cards arrondis.
   ========================================================================== */

/* Hero background — halo amber 8 % top-right + ardoise 5 % bottom-left.
   Donne du relief sans saturer la palette ardoise principale. */
body.aa-landing .aa-landing-hero-bg {
  background:
    radial-gradient(ellipse 80% 60% at 80% -10%, color-mix(in srgb, var(--accent) 08%, transparent) 0%, transparent 50%),
    radial-gradient(ellipse 60% 50% at 10% 100%, rgba(71, 85, 105, 0.05) 0%, transparent 60%),
    var(--bg-primary);
}
[data-theme="dark"] body.aa-landing .aa-landing-hero-bg {
  background:
    radial-gradient(ellipse 80% 60% at 80% -10%, color-mix(in srgb, var(--accent) 12%, transparent) 0%, transparent 55%),
    radial-gradient(ellipse 60% 50% at 10% 100%, rgba(100, 116, 139, 0.10) 0%, transparent 60%),
    var(--bg-primary);
}

/* Feature card — shadow-sm puis lift on hover. */
body.aa-landing .aa-landing-feature-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 28px 24px;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04), 0 1px 1px rgba(15, 23, 42, 0.03);
  transition:
    transform 200ms var(--ease-standard),
    box-shadow 200ms var(--ease-standard),
    border-color 200ms var(--ease-standard);
}
body.aa-landing .aa-landing-feature-card:hover {
  transform: translateY(-2px);
  border-color: var(--border-strong);
  box-shadow: 0 10px 30px -8px rgba(15, 23, 42, 0.10), 0 4px 8px -2px rgba(15, 23, 42, 0.06);
}
[data-theme="dark"] body.aa-landing .aa-landing-feature-card {
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.30), 0 1px 1px rgba(0, 0, 0, 0.20);
}
[data-theme="dark"] body.aa-landing .aa-landing-feature-card:hover {
  box-shadow: 0 10px 30px -8px rgba(0, 0, 0, 0.50), 0 4px 8px -2px rgba(0, 0, 0, 0.30);
}

/* Pastille fond accent 10 % derrière le SVG de chaque card. */
body.aa-landing .aa-landing-feature-icon {
  width: 56px;
  height: 56px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 14px;
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  color: var(--accent);
  margin-bottom: 20px;
}
[data-theme="dark"] body.aa-landing .aa-landing-feature-icon {
  background: color-mix(in srgb, var(--accent) 15%, transparent);
}

/* Pricing card highlighted — anneau accent + halo subtle + badge popular. */
body.aa-landing .aa-landing-pricing-card {
  position: relative;
  border: 1px solid var(--border);
  border-radius: 20px;
  background: var(--bg-card);
  padding: 40px 32px;
  box-shadow:
    0 0 0 6px color-mix(in srgb, var(--accent) 08%, transparent),
    0 30px 60px -20px rgba(15, 23, 42, 0.15),
    0 0 0 1px color-mix(in srgb, var(--accent) 35%, transparent);
}
[data-theme="dark"] body.aa-landing .aa-landing-pricing-card {
  box-shadow:
    0 0 0 6px color-mix(in srgb, var(--accent) 12%, transparent),
    0 30px 60px -20px rgba(0, 0, 0, 0.55),
    0 0 0 1px color-mix(in srgb, var(--accent) 45%, transparent);
}
body.aa-landing .aa-landing-pricing-badge {
  position: absolute;
  top: -14px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--accent);
  color: #1F2937;
  padding: 6px 14px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* Footer plus dark que le body — sépare visuellement le contenu de la
   navigation légale. Dark mode : reste cohérent (bg-bloc déjà plus foncé). */
body.aa-landing .aa-landing-footer {
  background: var(--bg-bloc);
  border-top: 1px solid var(--border);
}

/* FAQ items — bordure radius + lift on hover (cohérent avec feature cards). */
body.aa-landing .aa-landing-faq-item {
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 4px 20px;
  margin-bottom: 12px;
  background: var(--bg-card);
  transition: border-color 200ms var(--ease-standard), box-shadow 200ms var(--ease-standard);
}
body.aa-landing .aa-landing-faq-item:hover {
  border-color: var(--border-strong);
}


/* ==========================================================================
   §38.c Landing polish v2 — DARK/LIGHT TOGGLE (commit 3/7)
        Bouton 40 × 40 px, sun/moon swap via CSS toggle data-theme.
        Persist localStorage clé `aaThemeMode` (cf. landing-animations.js).
   ========================================================================== */

body.aa-landing .aa-landing-theme-toggle {
  width: 40px;
  height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 10px;
  color: var(--text-secondary);
  cursor: pointer;
  padding: 0;
  transition: background 150ms ease, color 150ms ease, border-color 150ms ease, transform 100ms ease;
}
body.aa-landing .aa-landing-theme-toggle:hover {
  background: var(--bg-bloc);
  color: var(--text-primary);
  border-color: var(--border-strong);
}
body.aa-landing .aa-landing-theme-toggle:active { transform: scale(0.95); }
body.aa-landing .aa-landing-theme-toggle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
body.aa-landing .aa-landing-theme-toggle .aa-icon-sun { display: none; }
body.aa-landing .aa-landing-theme-toggle .aa-icon-moon { display: block; }
[data-theme="dark"] body.aa-landing .aa-landing-theme-toggle .aa-icon-sun { display: block; }
[data-theme="dark"] body.aa-landing .aa-landing-theme-toggle .aa-icon-moon { display: none; }


/* ==========================================================================
   §38.d Landing polish v2 — SCROLL REVEAL ANIMATIONS (commit 4/7)
        Fallback CSS-only : `data-reveal` est invisible par défaut,
        passe visible quand `.is-visible` est posé (par GSAP ScrollTrigger
        ou par IntersectionObserver fallback).

        Respect prefers-reduced-motion : opacity 1 immédiat, transform none.
        Si JS désactivé, body.aa-no-js force le bypass (posé par index.html).
   ========================================================================== */

body.aa-landing [data-reveal] {
  opacity: 0;
  transform: translateY(24px);
  transition:
    opacity 600ms var(--ease-standard),
    transform 600ms var(--ease-standard);
  will-change: opacity, transform;
}
body.aa-landing [data-reveal].is-visible {
  opacity: 1;
  transform: none;
}
@media (prefers-reduced-motion: reduce) {
  body.aa-landing [data-reveal] {
    opacity: 1;
    transform: none;
    transition: none;
  }
}


/* ==========================================================================
   §38.e Landing polish v2 — DÉMO PLACEHOLDER (commit 6/7)
        En attendant le tournage demo.mp4 (deferred-item Alexis ffmpeg recipe),
        placeholder card élégant gradient + glow amber.
   ========================================================================== */

body.aa-landing .aa-landing-demo-card {
  background: linear-gradient(135deg, var(--bg-bloc) 0%, var(--bg-card) 100%);
  border: 1px solid var(--border);
  border-radius: 24px;
  padding: 64px 32px;
  box-shadow: 0 20px 50px -15px rgba(15, 23, 42, 0.10);
  text-align: center;
  position: relative;
  overflow: hidden;
}
body.aa-landing .aa-landing-demo-card::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 60% 50% at 50% 0%, color-mix(in srgb, var(--accent) 10%, transparent), transparent 60%);
  pointer-events: none;
}
body.aa-landing .aa-landing-demo-card > * { position: relative; }
[data-theme="dark"] body.aa-landing .aa-landing-demo-card {
  box-shadow: 0 20px 50px -15px rgba(0, 0, 0, 0.45);
}

body.aa-landing .aa-landing-demo-icon {
  width: 72px;
  height: 72px;
  margin: 0 auto 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 20px;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent);
}


/* ==========================================================================
   §38.f Landing polish v2 — HEADER NAV REFONTE (commit 7/7)
        Sticky avec blur backdrop, "Connexion" texte simple, "Essai gratuit"
        outline accent (pas filled). Toggle dark/light entre les deux.
   ========================================================================== */

body.aa-landing .aa-landing-header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: rgba(251, 247, 240, 0.72);
  backdrop-filter: saturate(180%) blur(12px);
  -webkit-backdrop-filter: saturate(180%) blur(12px);
  border-bottom: 1px solid transparent;
  transition: background-color 200ms var(--ease-standard), border-color 200ms var(--ease-standard);
}
[data-theme="dark"] body.aa-landing .aa-landing-header {
  background: rgba(31, 41, 55, 0.72);
}
body.aa-landing .aa-landing-header-scrolled {
  background: var(--bg-card);
  border-bottom-color: var(--border);
}
[data-theme="dark"] body.aa-landing .aa-landing-header-scrolled {
  background: var(--bg-card);
}

/* Nav text link (Connexion) — subtle, hover darken. */
body.aa-landing .aa-landing-nav-link {
  color: var(--text-secondary);
  font-size: 14px;
  font-weight: 500;
  padding: 8px 12px;
  border-radius: 6px;
  text-decoration: none;
  transition: color 150ms ease;
}
body.aa-landing .aa-landing-nav-link:hover {
  color: var(--text-primary);
}

/* CTA outline accent (Essai gratuit header — pas filled). */
body.aa-landing .aa-landing-nav-cta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 9px 16px;
  background: transparent;
  color: var(--accent);
  border: 1px solid var(--accent);
  border-radius: 8px;
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
  transition: background 150ms ease, color 150ms ease, transform 100ms ease;
}
body.aa-landing .aa-landing-nav-cta:hover {
  background: var(--accent);
  color: #1F2937;
}
body.aa-landing .aa-landing-nav-cta:active { transform: scale(0.97); }

/* ==========================================================================
   §39 Install PWA — iPhone captures (feat/install-iphone-step-6)
   ----------------------------------------------------------------------------
   3 captures iOS 26 illustrant Safari -> Share Sheet -> "Sur l'écran
   d'accueil". Annotation overlay = ring rouge animé + badge numéro top-left.
   Utilisé par :
   - frontend/templates/install/public.html  (iOS variant, user sur iPhone)
   - frontend/templates/onboarding/install.html (étape 6, admin sur PC)
   ========================================================================== */

.aa-install-captures {
  display: grid;
  grid-template-columns: 1fr;
  gap: 24px;
  justify-items: center;
}
@media (min-width: 640px) {
  .aa-install-captures {
    grid-template-columns: repeat(3, 1fr);
    gap: 16px;
  }
}

.aa-install-capture {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  margin: 0;
}

.aa-install-capture-frame {
  position: relative;
  width: 100%;
  max-width: 220px;
  border-radius: 18px;
  overflow: hidden;
  background: #000;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.18);
}

.aa-install-capture-frame img {
  display: block;
  width: 100%;
  height: auto;
}

.aa-install-capture-num {
  position: absolute;
  top: 8px;
  left: 8px;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--accent-on-primary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 14px;
  z-index: 2;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.35);
}

.aa-install-capture-target {
  position: absolute;
  width: 64px;
  height: 64px;
  border: 3px solid #dc2626;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  box-shadow:
    0 0 0 4px rgba(220, 38, 38, 0.22),
    0 0 14px rgba(220, 38, 38, 0.55);
  animation: aa-install-pulse 1.6s ease-in-out infinite;
  pointer-events: none;
  z-index: 3;
}

@keyframes aa-install-pulse {
  0%, 100% { opacity: 0.85; transform: translate(-50%, -50%) scale(1); }
  50%      { opacity: 1;    transform: translate(-50%, -50%) scale(1.08); }
}

@media (prefers-reduced-motion: reduce) {
  .aa-install-capture-target { animation: none; }
}

.aa-install-capture-caption {
  margin-top: 12px;
  font-size: 14px;
  line-height: 1.4;
  color: var(--text-primary);
  max-width: 220px;
}
.aa-install-capture-caption strong {
  color: var(--text-primary);
  font-weight: 600;
}

/* Android tip card — pas de capture, juste un encart texte. */
.aa-install-android-tip {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 16px;
  background: var(--bg-bloc, var(--bg-card));
  font-size: 14px;
  line-height: 1.55;
  color: var(--text-primary);
}
.aa-install-android-tip strong {
  font-weight: 600;
}

/* Toggle iPhone / Android — pill segmented control utilisé dans
   onboarding/install.html (admin sur PC choisit la plateforme à afficher). */
.aa-install-platform-toggle {
  display: inline-flex;
  background: var(--bg-bloc, var(--bg-card));
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 4px;
  gap: 4px;
  margin-bottom: 20px;
}
.aa-install-platform-toggle button {
  appearance: none;
  border: none;
  background: transparent;
  color: var(--text-muted);
  font-size: 14px;
  font-weight: 500;
  padding: 6px 18px;
  border-radius: 999px;
  cursor: pointer;
  transition: background 150ms ease, color 150ms ease;
}
.aa-install-platform-toggle button[aria-pressed="true"] {
  background: var(--accent);
  color: var(--accent-on-primary);
  font-weight: 600;
}
.aa-install-platform-toggle button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* BUG-REGR-08 (2026-05-19) — wrapper transparent pour les tags appareils
   "overflow" (index ≥ 5) de la fiche client. Permet à Alpine de toggle
   l'affichage via x-show sans casser le layout flex parent : `display:
   contents` rend le wrapper inerte pour le calcul de flex, le tag chip
   inline-flex à l'intérieur devient enfant direct du `.flex.flex-wrap.gap-2`.
   Quand x-show=false, Alpine override en `display: none` (priorité style
   inline > règle CSS). Quand x-show=true, Alpine remet `style.display = ''`
   et on retombe sur `contents` (visible, transparent layout). */
.aa-appareil-overflow {
  display: contents;
}


/* ==========================================================================
   40. FullCalendar theming (Phase 14 Plan 14-03 — CAL-03)
   Threads the VoxNote DS tokens into FullCalendar's CSS variables so the
   /calendrier grid does NOT render as stock-blue FullCalendar (UI-SPEC FLAG 3
   / §FullCalendar theming). The grid bundle injects its own styles at runtime
   in v6 (no separate stylesheet) ; we override the public --fc-* vars.

   Event chips get their color from the statut classNames the feed emits
   (`aa-evt aa-evt-{statut}`), NOT the default --fc-event-bg-color (neutralised
   below). The .aa-evt-* classes mirror the .aa-pill-warning/success/error
   palettes (incl. their dark overrides) so chips match the rest of the app.
   ========================================================================== */
/* --------------------------------------------------------------------------
   RESTYLE v0140-03b (iron-law grid gate fixes) — "tableur → Google Calendar"
   Lifts the mockup prior-art CSS techniques (calendrier-mockup.html): the grid
   becomes a soft white card; events become rounded floating chips with a left
   status-accent bar + soft shadow; grid lines softened (minor lines erased);
   amber today tint kept subtle. STATUT colors raised to vivid SATURATED FILLS
   with WHITE text (Google-style) for guaranteed AA contrast in BOTH themes —
   fixes the prior pale-text-on-pale-fill legibility bug (issue 1).
   CSS-ONLY: no FullCalendar config / structure / feed changes.
   -------------------------------------------------------------------------- */
#calendar {
  --fc-border-color: var(--border);
  --fc-page-bg-color: var(--bg-card);
  --fc-neutral-bg-color: var(--bg-card);
  --fc-today-bg-color: color-mix(in srgb, var(--accent) 06%, transparent); /* SUBTLE amber today tint (issue 4) */
  --fc-list-event-hover-bg-color: var(--bg-bloc);
  --fc-event-text-color: #FFFFFF; /* white text on saturated fills (issue 1) */
  --fc-now-indicator-color: var(--error);
  /* Neutralise the stock blue — chips colored via .aa-evt-* classNames. */
  --fc-event-bg-color: transparent;
  --fc-event-border-color: transparent;
  --aa-evt-ease: cubic-bezier(.4, 0, .2, 1);
  color: var(--text-primary);
  font-size: 13px;
}

/* — the grid becomes ONE soft white card on the cream page (issue 4) — */
#calendar .fc {
  background: var(--bg-card);
  border: 0.5px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 1px 3px rgba(15, 23, 42, 0.06);
}
/* kill the hard double internal frame (spreadsheet look) */
#calendar .fc-scrollgrid,
#calendar .fc-scrollgrid > tbody > tr > td,
#calendar .fc-scrollgrid-section > td { border: none !important; }

/* — day headers (iter 2): Google-style — TRANSPARENT block (no grey fill),
     2-level hierarchy (muted lowercase day-name + big day number), today =
     amber round pill on the NUMBER. Rendered via dayHeaderContent (.fc-dh). — */
#calendar .fc-col-header-cell { background: transparent; padding: 8px 0; }
#calendar .fc-col-header-cell-cushion {
  display: inline-block;
  padding: 4px 4px;
  text-decoration: none;
  color: inherit;
}
/* the 2-level header structure injected by dayHeaderContent */
#calendar .fc-dh {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  line-height: 1.05;
}
#calendar .fc-dh-name {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-muted);
  text-transform: lowercase;
  letter-spacing: 0.1px;
}
#calendar .fc-dh-num {
  font-size: 22px;
  font-weight: 600;
  color: var(--text-primary);
  letter-spacing: -0.3px;
  /* reserve the pill footprint so today doesn't shift the row height (FIX 4: 32->38px) */
  width: 38px;
  height: 38px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 9999px;
}
/* TODAY: amber filled round pill on the number (texte FONCÉ AA), name stays muted */
#calendar .fc-dh--today .fc-dh-num {
  background: var(--accent);
  color: #1C1917; /* texte FONCÉ sur le pill amber : ~7:1 (blanc était 2.1:1, KO AA) — cohérent FIX 3 */
}
/* month view: weekday-label-only header (no number) -> simple muted label */
#calendar .fc-dh-name:only-child {
  font-size: 12px;
  font-weight: 600;
  padding: 2px 0;
}
#calendar .fc-timegrid-axis-cushion,
#calendar .fc-timegrid-slot-label-cushion { font-size: 11px; color: var(--text-muted); }
/* month-view day-number: keep DS look; today number in accent (no big pill here) */
#calendar .fc-daygrid-day-number {
  color: var(--text-secondary); text-decoration: none;
  font-size: 12px; padding: 6px 8px; font-weight: 500;
}
#calendar .fc-day-today .fc-daygrid-day-number { color: var(--accent); font-weight: 700; }

/* — airier grid: full-hour lines very light, half-hour lines near-invisible (issue 4) — */
#calendar .fc-timegrid-slot { height: 2.3em; }
#calendar .fc-theme-standard td,
#calendar .fc-theme-standard th { border-color: var(--border); }
#calendar .fc-timegrid-slot-minor { border-top-style: dotted; border-top-color: transparent; }
/* now-indicator (iter 2 tweak 3): thin crisp full-width red line, tidy arrow */
#calendar .fc-timegrid-now-indicator-line {
  border-color: var(--error);
  border-top-width: 1.5px;
  margin-right: 1px;
}
#calendar .fc-timegrid-now-indicator-arrow {
  border-color: var(--error);
  border-width: 4px;
  color: var(--error);
  margin-top: -4px;
}

/* ==========================================================================
   Lighter filter column (iter 2 tweak 2): compact spacing + discreet labels +
   tighter controls so the column reads light and doesn't dominate the grid.
   Compact CSS only — no collapsible toggle (gate-safe, no interaction risk).
   ========================================================================== */
.aa-cal-filters-inner .aa-cal-flabel {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.2px;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 4px;
}
.aa-cal-filters-inner .aa-cal-fctl {
  padding: 7px 10px;
  font-size: 13px;
  border-radius: 7px;
}
/* hairline separator under the date picker -> visual "navigation vs filtres" split */
.aa-cal-filters-inner .aa-cal-fsep {
  height: 0.5px;
  background: var(--border);
  margin: 2px 0;
}

/* ==========================================================================
   Event chips — rounded "floating" chips + left status-accent bar (issue 3)
   Vivid SATURATED fills + WHITE text = guaranteed AA in light & dark (issue 1+2).
   ========================================================================== */
.aa-evt { cursor: pointer; }
/* statut palette: vivid fill (--evt-bg) + darker accent bar (--evt-accent) +
   per-statut text colour (--evt-text), choisi pour WCAG AA ≥4.5:1 sur le fill.
   FIX 3 (a11y) : le blanc sur amber ne faisait que 2.1:1 → texte FONCÉ sur amber.
   Le vert #16A34A (blanc 3.3:1, KO) est assombri en #15803D (blanc 5.0:1). */
.aa-evt-planifie { --evt-bg: #F59E0B; --evt-accent: #B45309; --evt-text: #1C1917; } /* amber → texte FONCÉ (blanc=2.1:1 KO) */
.aa-evt-confirme { --evt-bg: #15803D; --evt-accent: #166534; --evt-text: #FFFFFF; } /* vert assombri → blanc=5.0:1 */
.aa-evt-annule   { --evt-bg: #DC2626; --evt-accent: #B91C1C; --evt-text: #FFFFFF; opacity: 0.8; } /* rouge → blanc=4.8:1. a11y 2026-05-27 : opacity 0.55→0.8. Le chip composité (texte blanc inclus) tombait à 2.49:1 en clair — illisible, alors qu'on veut lire QUI annuler pour le réactiver. À 0.8 : ~3.77:1 clair / ~5.46:1 sombre (AA OK en sombre). Le barré (.fc-event-title) + le rouge atténué portent le signal "inactif", pas une transparence qui efface le texte. */
/* dark theme: keep fills vivid. --evt-text est hérité du bloc clair ci-dessus :
   planifie garde son texte FONCÉ (amber #D97706 + #1C1917 = 5.5:1), confirme/annule
   restent en blanc (#15803D=5.0:1, #B91C1C=6.5:1). Tous ≥ AA 4.5:1. */
[data-theme="dark"] .aa-evt-planifie { --evt-bg: #D97706; --evt-accent: #FBBF24; }
[data-theme="dark"] .aa-evt-confirme { --evt-bg: #15803D; --evt-accent: #4ADE80; }
[data-theme="dark"] .aa-evt-annule   { --evt-bg: #B91C1C; --evt-accent: #F87171; }

/* Phase 16 — provisional IA suggestion chip. Slate-neutral (NOT a semantic
   status colour), dashed outline + hatch + reduced opacity so it reads as
   "not yet real" at a glance, distinct from planifie/confirme/annule. */
.aa-evt-suggere {
  --evt-bg: #94A3B8;          /* slate-400 — neutral, never confused with amber/green/red */
  --evt-accent: #475569;      /* slate-600 left bar (brand ardoise) */
  /* FIX 5 (a11y, 2026-05-26) — texte FONCÉ. Le blanc (fallback) sur ce slate ne
     faisait que 2.56:1 (KO, illisible surtout en vue Mois) ; #1C1917 sur #94A3B8
     = 6.82:1 (AA OK), cohérent avec le texte foncé de planifie (FIX 3). */
  --evt-text: #1C1917;
  /* opacity:0.72 SUPPRIMÉE (FIX 5) : composité sur le fond, elle écrasait le
     texte foncé à 3.65:1 (< AA 4.5). Le caractère "provisoire" reste porté par
     la bordure dashed + le hachurage diagonal (les 2 cues forts), pas par une
     transparence qui nuit à la lisibilité. */
  background-image: repeating-linear-gradient(
    45deg, transparent, transparent 6px,
    rgba(255,255,255,.22) 6px, rgba(255,255,255,.22) 12px);
}
#calendar .fc-timegrid-event.aa-evt-suggere,
#calendar .fc-daygrid-event.aa-evt-suggere {
  border: 1.5px dashed var(--evt-accent);   /* dashed = provisional, the strongest single cue */
}
/* Phase 16 iron-law human feedback (2026-05-24): chip must read clearly ABOVE
   the dark grid tone (#1F2937 / card #374151). FIX 5 (a11y, 2026-05-26) revisits
   the dark fill : now that event text is DARK (#1C1917), #64748B was too dark
   (texte foncé = 3.67:1 KO ; et le blanc sur les hachures = 3.12:1 KO). On remonte
   le fill à slate-400 #94A3B8 (identique au mode clair) → texte foncé 6.82:1
   (AA OK) ET le chip ressort MIEUX sur la grille sombre (4.02:1 vs 2.17:1 avant).
   Bordure dashed slate-300 #CBD5E1 2px conservée comme cue "provisoire".
   --evt-text #1C1917 est hérité du bloc clair. Toujours slate-neutre — jamais
   amber/green/red. */
[data-theme="dark"] .aa-evt-suggere { --evt-bg: #94A3B8; --evt-accent: #CBD5E1; }
[data-theme="dark"] #calendar .fc-timegrid-event.aa-evt-suggere,
[data-theme="dark"] #calendar .fc-daygrid-event.aa-evt-suggere {
  border: 2px dashed var(--evt-accent);
}

/* Lot 2 (RFC voxa-v2) — RDV « à recaser » (à reprogrammer). Violet (jamais
   confondu avec amber=planifié / vert=confirmé / rouge=annulé / slate=suggéré)
   + bordure dashed = « action requise ». Texte blanc sur #7C3AED = 5.3:1 (AA OK) ;
   en sombre #6D28D9 + blanc = 6.4:1. Le RDV garde sa position (dernière connue) mais
   le dashed signale qu'il attend une nouvelle date. */
.aa-evt-a_recaser { --evt-bg: #7C3AED; --evt-accent: #5B21B6; --evt-text: #FFFFFF; }
#calendar .fc-timegrid-event.aa-evt-a_recaser,
#calendar .fc-daygrid-event.aa-evt-a_recaser {
  border: 1.5px dashed var(--evt-accent);
}
[data-theme="dark"] .aa-evt-a_recaser { --evt-bg: #6D28D9; --evt-accent: #C4B5FD; }
[data-theme="dark"] #calendar .fc-timegrid-event.aa-evt-a_recaser,
[data-theme="dark"] #calendar .fc-daygrid-event.aa-evt-a_recaser {
  border: 2px dashed var(--evt-accent);
}

/* Area 4 — APERÇU IA (Jarvis preview). Violet saturé + bordure pointillée
   violet-800 + badge inline « ✨ IA » : distinct du suggéré (algo, slate-neutre)
   ET des statuts amber/vert/rouge. Le violet n'est porteur d'aucune sémantique de
   statut → lecture immédiate « proposition IA non confirmée ».
   Contrastes texte blanc / fill (calculés WCAG 2.1, sur le fill plein) :
     clair  #FFFFFF sur #7C3AED (violet-600) = 5.70:1  (AA OK ≥ 4.5)
     sombre #FFFFFF sur #6D28D9 (violet-700) = 7.10:1  (AA OK, AAA large) */
.aa-evt-ia-preview {
  --evt-bg: #7C3AED;          /* violet-600 */
  --evt-text: #FFFFFF;        /* AA 5.70:1 sur le fill */
  --evt-border: #5B21B6;      /* violet-800 — bordure pointillée */
  /* --evt-accent défini (= --evt-border) pour que le box-shadow partagé des chips
     (#calendar .fc-*-event { box-shadow: inset 3px 0 0 0 var(--evt-accent), … })
     reste VALIDE : un var() non résolu invaliderait toute la déclaration box-shadow
     (barre + ombre portée disparaîtraient). Cf. .aa-evt-suggere qui pose aussi son accent. */
  --evt-accent: #5B21B6;
}
/* FIX3 — mêmes coins arrondis que les events normaux (8px) : le bloc et son
   inner border pointillée étaient carrés. */
.aa-evt-ia-preview { border-radius: 8px; }
.aa-evt-ia-preview .fc-event-main {
  border: 2px dashed var(--evt-border);
  border-radius: 8px;
}
.aa-evt-ia-preview .fc-event-title::before {
  content: "✨ IA ";          /* badge inline */
  font-size: 0.75em;
  opacity: 0.85;
}
[data-theme="dark"] .aa-evt-ia-preview { --evt-bg: #6D28D9; }  /* violet-700 */

/* Lot V1.1 B — event source grisé pendant le preview IA (deplacer/decaler).
   Posée dynamiquement par addPendingEvent, retirée par clearPendingEvents.
   opacity 0.4 = visuellement "fantôme", pas de pointer-events (l'artisan ne
   doit pas cliquer dessus pendant la preview, l'event va bouger). */
.aa-evt-ia-dimmed { opacity: 0.4 !important; pointer-events: none; }

/* Area 4 — légende code couleurs. Les swatches NE sont PAS dans #calendar : les
   règles #calendar .fc-*-event ne s'y appliquent pas, on repeint donc le fill ici
   via les mêmes customs --evt-* posées par chaque classe .aa-evt-* (overrides dark
   inclus, hérités automatiquement). flex + wrap = lisible mobile (swatches passent
   à la ligne) comme desktop. */
.aa-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 8px;
  margin-bottom: 12px;
}
.aa-legend .aa-evt {
  display: inline-flex;
  align-items: center;
  padding: 4px 10px;
  border-radius: 7px;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.2;
  cursor: default;                            /* override .aa-evt { cursor: pointer } */
  background: var(--evt-bg, var(--accent));
  color: var(--evt-text, #FFFFFF);
  box-shadow: inset 3px 0 0 0 var(--evt-accent, transparent);
}
/* rappel des cues "provisoire" (bordure pointillée) dans la légende */
.aa-legend .aa-evt-suggere   { border: 1.5px dashed var(--evt-accent); }
.aa-legend .aa-evt-a_recaser { border: 1.5px dashed var(--evt-accent); }
.aa-legend .aa-evt-ia-preview { border: 2px dashed var(--evt-border); }

/* Rangée légende + corbeille (FIX calendrier-corbeille). La légende garde son
   flex-wrap ; la corbeille est poussée en bout de rangée (margin-left:auto).
   margin-bottom porté par la RANGÉE (la légende y est forcée à 0) : sans lui,
   la corbeille (44px, plus haute que les chips) est bord à bord avec la
   grille — UAT 2026-06-12, gap mesuré 0px en Semaine et Agenda. */
.aa-legend-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 12px;
}
.aa-legend-row .aa-legend { margin-bottom: 0; flex: 1 1 auto; }

/* Corbeille drop-target. État REPOS : discret (gris, pointillés) mais visible
   pour la découvrabilité. État ARMÉ (is-armed, posé pendant un drag) : plus
   contrasté + label révélé. État SURVOL (is-over, drag au-dessus) : rouge plein,
   scale, = signal « lâchez pour supprimer ». Cible tap >= 44px (mobile-first). */
.aa-cal-trash {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
  min-height: 44px;
  padding: 6px 14px;
  border-radius: 9px;
  border: 1.5px dashed var(--border, #CBD5E1);
  background: transparent;
  color: var(--text-secondary, #64748B);
  font-size: 13px;
  font-weight: 600;
  cursor: default;
  user-select: none;
  transition: background 120ms ease, color 120ms ease, border-color 120ms ease,
              transform 120ms ease, box-shadow 120ms ease;
}
.aa-cal-trash-icon { flex: none; }
.aa-cal-trash-label { line-height: 1.2; }
/* Armé pendant un drag d'événement : on appelle à viser la corbeille. */
.aa-cal-trash.is-armed {
  border-color: var(--error, #B91C1C);
  border-style: solid;
  color: var(--error, #B91C1C);
  background: var(--error-bg, #FEE2E2);
}
/* Drag survolant la corbeille : confirmation visuelle « lâchez ici ». */
.aa-cal-trash.is-over {
  border-color: var(--error, #B91C1C);
  background: var(--error, #B91C1C);
  color: #FFFFFF;
  transform: scale(1.06);
  box-shadow: 0 4px 14px rgba(185, 28, 28, 0.35);
}

/* timegrid (Semaine / Jour) chips */
#calendar .fc-timegrid-event {
  border: none;
  border-radius: 8px;
  padding: 0;
  margin: 1px 3px 1px 2px;
  background: var(--evt-bg, var(--accent));
  color: var(--evt-text, #FFFFFF);
  box-shadow: inset 3px 0 0 0 var(--evt-accent), 0 1px 2px rgba(15, 23, 42, 0.18);
  transition: box-shadow .15s var(--aa-evt-ease), transform .15s var(--aa-evt-ease);
}
#calendar .fc-timegrid-event .fc-event-main { padding: 4px 8px 4px 10px; color: var(--evt-text, #FFFFFF); }
#calendar .fc-timegrid-event:hover {
  box-shadow: inset 3px 0 0 0 var(--evt-accent), 0 6px 14px rgba(15, 23, 42, 0.24);
  transform: translateY(-1px);
}

/* daygrid (Mois + all-day row) chips */
#calendar .fc-daygrid-event {
  border: none;
  border-radius: 7px;
  padding: 3px 8px 3px 9px;
  margin: 1px 4px;
  background: var(--evt-bg, var(--accent));
  color: var(--evt-text, #FFFFFF);
  font-weight: 500;
  box-shadow: inset 3px 0 0 0 var(--evt-accent);
  transition: box-shadow .15s var(--aa-evt-ease);
}
#calendar .fc-daygrid-event:hover {
  box-shadow: inset 3px 0 0 0 var(--evt-accent), 0 3px 8px rgba(15, 23, 42, 0.20);
}
/* month-view dot events: tint the dot with the statut accent */
#calendar .fc-daygrid-dot-event { background: transparent; color: var(--text-primary); box-shadow: none; }
#calendar .fc-daygrid-dot-event .fc-daygrid-event-dot { border-color: var(--evt-bg, var(--accent)); }

/* — event TEXT: hierarchized time (smaller/lighter) + title (stronger) + clean ellipsis (issue 3) — */
#calendar .fc-event-main-frame { overflow: hidden; }
#calendar .fc-event-title,
#calendar .fc-event-time {
  color: var(--evt-text, #FFFFFF);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#calendar .fc-event-time { font-size: 11px; font-weight: 500; opacity: 0.85; }
#calendar .fc-event-title { font-size: 12px; font-weight: 600; line-height: 1.3; }
.aa-evt-annule .fc-event-title { text-decoration: line-through; }

/* soft keyboard focus ring (a11y) */
#calendar .fc-event:focus-visible {
  outline: none;
  box-shadow: inset 3px 0 0 0 var(--evt-accent), 0 0 0 3px color-mix(in srgb, var(--accent) 40%, transparent);
}

/* mobile (<768px / timeGridDay): taller slots + rounder/airier chips for the thumb */
@media (max-width: 767px) {
  #calendar .fc-timegrid-slot { height: 2.7em; }
  #calendar .fc-timegrid-event {
    margin: 2px 5px;
    border-radius: 10px;
    box-shadow: inset 4px 0 0 0 var(--evt-accent), 0 1px 3px rgba(15, 23, 42, 0.20);
  }
  #calendar .fc-timegrid-event .fc-event-main { padding: 6px 10px 6px 12px; }
  #calendar .fc-event-title { font-size: 13px; }
  #calendar .fc-daygrid-event { border-radius: 8px; padding: 4px 9px; }
  /* FIX 4 — jour courant + 3 lettres du jour plus gros sur mobile / vue Jour */
  #calendar .fc-dh-name { font-size: 14px; }
  #calendar .fc-dh-num { font-size: 24px; width: 42px; height: 42px; }
}

/* Area 5 — fade-in d'ENTRÉE, appliqué aux aperçus IA pending UNIQUEMENT (via
   eventDidMount guardé sur extendedProps.pending, calendrier.js). NE PAS étendre
   aux events réels : eventDidMount fire à chaque (re)montage → ils re-clignoteraient
   à chaque refetchEvents / redraw (pitfall audit confirmé). */
@keyframes aa-evt-enter {
  from { opacity: 0; transform: scale(0.9); }
  to   { opacity: 1; transform: scale(1); }
}
.aa-evt-enter { animation: aa-evt-enter 0.25s ease-out; }

/* feat/maps-polish — anim de SORTIE (disparition) symétrique de aa-evt-enter,
   appliquée par window.voxCalAnim.leave() sur un CLONE overlay du nœud partant
   (l'original est détruit par le refetch FullCalendar). Centralisée pour toutes
   les mutations (Voxa annuler/supprimer, modal delete, suggestion reject). */
@keyframes aa-evt-leave {
  from { opacity: 1; transform: scale(1); }
  to   { opacity: 0; transform: scale(0.9); }
}
.aa-evt-leave { animation: aa-evt-leave 0.28s ease-in forwards; }
/* Clone overlay positionné en fixed par voxCalAnim.leave() — neutre, non cliquable. */
.aa-evt-leave-clone { position: fixed; margin: 0; pointer-events: none; z-index: 7; }

/* ==========================================================================
   17. Voxa Compagnon — FENÊTRE FLOTTANTE (V2-S1.1 + feat/voxa-ui-fenetre-flottante)
        #jarvis-drawer : fenêtre fixed, fade+slide à l'ouverture. z-index 35 : sous
        le bandeau preview (z-40), le header sticky (z-40) et le toast (z-50).
        - PC ≥768 : carte 400px ancrée bas-droite par défaut, NON-MODALE (pas de
          backdrop, le calendrier reste interactif). DÉPLAÇABLE (poignée = en-tête)
          + REDIMENSIONNABLE (poignée coin bas-droit #jarvis-resize). Position/taille
          persistées localStorage (voxa.panel.pos/.size/.dock), hydratées à l'init.
        - Mobile <768 : fenêtre flottante (insets + radius + ombre + max-height 85vh)
          au-dessus d'un backdrop semi-transparent (#jarvis-backdrop, z-34) — la page
          reste visible derrière ; tap backdrop = ferme.
        Lanceur UNIFIÉ #jarvis-tab : pilule bas-droite identique PC + mobile, visible
        quand la fenêtre est rangée. L'état ouvert/fermé n'est PAS persisté (toujours
        rangé au chargement).
   ========================================================================== */
.aa-jarvis-drawer {
  position: fixed;
  /* Carte FLOTTANTE détachée des bords : marge 16px à droite + haut/bas -> une
     bande d'arrière-plan reste visible à droite (effet carte posée sur la page). */
  top: calc(var(--aa-header-h, 56px) + 16px);
  right: 16px;
  /* fix/voxa-drawer-offset — `--aa-voxa-drawer-lift` (posé par placeDrawer()
     en JS, 0 par défaut) rehausse la fenêtre au-dessus des barres fixes/sticky
     du bas (panier pré-factures z-50 > drawer z-35 : il recouvrait le composer
     — micro, champ « Écrivez à Voxa », Envoyer). La safe-area reste gérée ici
     (le JS ne raisonne qu'en px viewport). */
  bottom: calc(var(--aa-voxa-drawer-lift, 0px) + 16px + env(safe-area-inset-bottom, 0px));
  width: 400px;
  max-width: calc(100vw - 32px);
  z-index: 35;
  display: flex;
  flex-direction: column;
  background: var(--bg-card);
  /* Match panneau filtres (.aa-card) : radius 12px + bordure 0.5px ; PLUS une
     élévation (box-shadow) car la carte flotte PAR-DESSUS la page. */
  border: 0.5px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.16);
  overflow: hidden; /* clippe header/composer aux coins arrondis. */
  /* Rangé : la carte se rétracte vers la droite + fade vers l'intercalaire. */
  transform: translateX(calc(100% + 24px));
  opacity: 0;
  pointer-events: none;
  /* Courbe d'anim canonique (--aa-transition-*). Valeurs identiques à l'actuel
     (250ms / cubic-bezier(0.4,0,0.2,1)) — ressenti du drawer INCHANGÉ. */
  transition: transform var(--aa-transition-dur) var(--aa-transition-ease),
              opacity var(--aa-transition-dur) var(--aa-transition-ease);
  /* Safe-areas exposées au JS du sheet mobile (env() n'est lisible qu'en CSS) :
     jarvis-chat.js les lit via getComputedStyle pour borner plancher/plafond. */
  --aa-voxa-sab: env(safe-area-inset-bottom, 0px);
  --aa-voxa-sat: env(safe-area-inset-top, 0px);
}
.aa-jarvis-drawer.is-open {
  transform: translateX(0);
  opacity: 1;
  pointer-events: auto;
}
/* Drag mobile (sheet) : le suivi du doigt est un translateY INLINE — la
   transition transform/opacity d'ouverture le lisserait sur 250ms (fenêtre
   élastique traînant derrière le doigt) → neutralisée pendant le geste.
   Inoffensif côté PC (left/top mutés ne sont pas transitionnés). */
.aa-jarvis-drawer.is-dragging { transition: none; }
/* Posé du sheet au relâcher (mobile) : le bord bas rejoint le plancher en
   douceur — seule la hauteur est animée, UN re-layout déclenché au release. */
.aa-jarvis-drawer.is-settling {
  transition: height var(--aa-transition-dur) var(--aa-transition-ease);
}
/* Pendant drag/resize (PC) : pas de sélection de texte parasite. La transition
   ne porte que sur transform+opacity, donc left/top/width/height suivent le
   pointeur instantanément (aucun lag). */
.aa-jarvis-drawer.is-dragging,
.aa-jarvis-drawer.is-resizing { user-select: none; }

/* Backdrop semi-transparent (Lot 3) — MOBILE uniquement (masqué ≥768px plus bas).
   Page visible derrière (pas opaque) -> impression de fenêtre par-dessus la page.
   Tap = ferme (câblé JS). z-34 : sous la fenêtre (z-35), au-dessus du contenu. */
.aa-jarvis-backdrop {
  position: fixed;
  inset: 0;
  z-index: 34;
  background: rgba(0, 0, 0, 0.4);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--aa-transition-dur) var(--aa-transition-ease);
}
.aa-jarvis-backdrop.is-open {
  opacity: 1;
  pointer-events: auto;
}

/* Poignée de redimensionnement (coin bas-droit) — PC ≥768px (affichée plus bas).
   Cachée par défaut (mobile <768px = fenêtre non redimensionnable). */
.aa-jarvis-resize {
  display: none;
  position: absolute;
  right: 0;
  bottom: 0;
  width: 20px;
  height: 20px;
  cursor: nwse-resize;
  touch-action: none; /* Pointer Events : pas de scroll/zoom natif pendant le geste. */
  z-index: 1;
}
.aa-jarvis-resize::after {
  content: "";
  position: absolute;
  right: 4px;
  bottom: 4px;
  width: 8px;
  height: 8px;
  border-right: 2px solid var(--text-muted);
  border-bottom: 2px solid var(--text-muted);
  border-bottom-right-radius: 3px;
}

.aa-jarvis-drawer-header {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 8px 8px 16px;
  border-bottom: 1px solid var(--border);
}
.aa-jarvis-drawer-title {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-weight: 600;
  font-size: 15px;
  color: var(--text-primary);
}
.aa-jarvis-title-icon { display: inline-flex; }
.aa-jarvis-collapse {
  flex: 0 0 auto;
  /* Cible ≥48px, chevron SVG centré (flex center + padding symétrique). */
  width: 48px;
  height: 48px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: none;
  cursor: pointer;
  color: var(--text-secondary);
  border-radius: 8px;
}
.aa-jarvis-collapse:hover { background: var(--bg-bloc); color: var(--text-primary); }

/* Poignée mobile (feat/voxa-drawer-mobile-resize) — la zone LIBRE de l'en-tête
   entre le titre et le chevron, injectée par jarvis-chat.js. Cible tactile
   pleine hauteur d'en-tête (≥48px), s'étire entre ses deux voisins (flex:1).
   touch-action: none : le drag vertical est un geste Pointer Events, jamais un
   scroll de page (même précédent que .aa-jarvis-resize). PC ≥768px : passive —
   le drag fenêtre reste porté par l'en-tête entier (l'événement bubble), le
   curseur grab est hérité de l'en-tête, le grip n'est affiché qu'en mobile. */
.aa-jarvis-handle {
  flex: 1 1 auto;
  align-self: stretch;
  min-height: 48px;
  touch-action: none;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Panel = colonne flex sous le header du drawer (messages scrollables + composer).
   pointer-events hérités du drawer (none quand rangé -> ne capture aucun clic). */
.aa-jarvis-chat-panel {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-height: 0;
}

/* Lanceur UNIFIÉ (Lot 1 — feat/voxa-ui-fenetre-flottante). Pilule horizontale
   ancrée bas-droite, IDENTIQUE PC et mobile (fini l'intercalaire vertical du bord
   droit). Icône bulle SVG + « Voxa » en ligne. Respecte les safe-area (PWA).
   z-index 34 : au-dessus du contenu, SOUS la fenêtre ouverte (z-35) — de toute
   façon masqué quand la fenêtre est ouverte (tab.hidden côté JS). */
.aa-jarvis-tab {
  position: fixed;
  /* Lot 5 → fix/voxa-drawer-offset — placement contextuel : `--aa-voxa-tab-lift`
     (posé par placeTab() en JS, 0 par défaut) rehausse le lanceur au-dessus de
     la plus haute barre fixe/sticky basse qui chevauche sa colonne — barres
     détectées DYNAMIQUEMENT (elementsFromPoint, plus de liste de sélecteurs).
     La safe-area reste gérée ici (le JS ne raisonne qu'en px viewport). PLUS de
     règle statique `[data-test-id="pwa-root"]` : la TabBar est mesurée. */
  bottom: calc(var(--aa-voxa-tab-lift, 0px) + 16px + env(safe-area-inset-bottom, 0px));
  right: calc(16px + env(safe-area-inset-right, 0px));
  z-index: 34;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 52px; /* cible tactile ≥48px. */
  padding: 0 18px;
  background: var(--accent);
  color: var(--accent-on-primary);
  border: none;
  border-radius: 26px;
  /* DA Carnet — ombre teintée accent (l'encre du tampon, pas un voile noir). */
  box-shadow: 0 5px 12px color-mix(in srgb, var(--accent) 32%, transparent);
  cursor: pointer;
  /* `bottom` transitionné -> le repositionnement (lift) glisse au lieu de sauter. */
  transition: transform 150ms cubic-bezier(0.4, 0, 0.2, 1),
              background 150ms cubic-bezier(0.4, 0, 0.2, 1),
              bottom 200ms cubic-bezier(0.4, 0, 0.2, 1);
}
.aa-jarvis-tab:hover { background: var(--accent-hover); transform: translateY(-1px); }
.aa-jarvis-tab:active { transform: translateY(0) scale(0.97); }
.aa-jarvis-tab[hidden] { display: none; }
.aa-jarvis-tab-icon { display: inline-flex; }
.aa-jarvis-tab-name {
  font-weight: 700;
  font-size: 14px;
  line-height: 1;
  letter-spacing: 0.3px;
}

/* feat/cal-remove-filter-tab-doublon — l'onglet latéral filtres/tournée du
   calendrier mobile a été SUPPRIMÉ (markup + CSS) : c'était un doublon de la chip
   « Filtres » de la barre d'outils (toutes deux posaient filtersOpen = true → même
   bottom-sheet contenant filtres + « Optimiser ma tournée »), et il masquait les
   dates du calendrier mobile. La chip de la barre d'outils reste le seul accès. */

/* ── PC ≥768px (Lot 2) : fenêtre déplaçable + redimensionnable ──────────────
   L'en-tête est la poignée de déplacement (curseur grab/grabbing), la poignée de
   coin bas-droit devient visible. Le backdrop reste masqué (fenêtre NON-MODALE :
   le contenu sous-jacent — calendrier — reste interactif). */
@media (min-width: 768px) {
  .aa-jarvis-drawer-header { cursor: grab; }
  .aa-jarvis-drawer.is-dragging .aa-jarvis-drawer-header { cursor: grabbing; }
  .aa-jarvis-resize { display: block; }
  .aa-jarvis-backdrop { display: none; }
}

@media (max-width: 767px) {
  /* Mobile (Lot 3) : FENÊTRE FLOTTANTE au-dessus de la page (fini le plein écran).
     Marges (insets) tout autour + coins arrondis + ombre + max-height ~85vh :
     on VOIT la page derrière (backdrop semi-transparent). Respecte les safe-area. */
  .aa-jarvis-drawer {
    top: calc(var(--aa-header-h, 56px) + 12px + env(safe-area-inset-top, 0px));
    right: calc(12px + env(safe-area-inset-right, 0px));
    left: calc(12px + env(safe-area-inset-left, 0px));
    bottom: auto;
    width: auto;
    max-width: none;
    height: auto;
    max-height: 85vh;
    border: 0.5px solid var(--border);
    border-radius: 16px;
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.28);
    /* Rangé : la fenêtre se rétracte vers le bas + fade (pas de slide latéral —
       c'est une fenêtre, plus un tiroir). Lot 5b : entrée portée de 12px → 28px
       pour que le mouvement soit NETTEMENT perceptible à l'ouverture mobile
       (à 12px la transition existe mais le déplacement passe inaperçu vs le slide
       PC ~426px). La transition transform/opacity de base s'applique ; le bloc
       prefers-reduced-motion plus bas la réduit à un simple fondu (pas de glisse). */
    transform: translateY(28px);
  }
  .aa-jarvis-drawer.is-open { transform: translateY(0); }
  /* Collapse = flèche vers le HAUT pour ranger (réaffiche la page). */
  .aa-jarvis-collapse { transform: rotate(-90deg); }

  /* Grip de la poignée mobile (discoverability du geste) : barrette centrale
     type bottom-sheet, encre discrète du DS (aucune couleur en dur). */
  .aa-jarvis-handle::after {
    content: "";
    width: 36px;
    height: 4px;
    border-radius: 2px;
    background: var(--text-muted);
    opacity: 0.55;
  }
  /* Géométrie UTILISATEUR (sheet déplacé/redimensionné, persistée) : la
     hauteur inline est calculée depuis le plancher — le plafond auto-size
     85vh est levé, sinon il clampe la hauteur et le bas du drawer flotte
     au-dessus des barres au lieu de s'y poser. */
  .aa-jarvis-drawer.aa-jarvis-mobile-custom { max-height: none; }

  /* Lot 5 — La dé-confliction du lanceur vs TabBar PWA n'est PLUS statique :
     placeTab() (jarvis-chat.js) mesure la TabBar comme obstacle et pose
     `--aa-voxa-tab-lift`. L'ancienne règle `[data-test-id="pwa-root"]
     .aa-jarvis-tab { bottom: 84+16 }` est supprimée (sinon double offset). */
}

@media (prefers-reduced-motion: reduce) {
  .aa-jarvis-drawer { transition: opacity var(--aa-transition-dur) var(--aa-transition-ease); }
  .aa-jarvis-drawer.is-settling { transition: none; } /* posé du sheet : pas d'anim. */
  .aa-jarvis-backdrop { transition: none; }
  .aa-jarvis-tab { transition: background 150ms cubic-bezier(0.4, 0, 0.2, 1); }
}

/* Contenu chat (theme-aware, layout-agnostic — réutilisé en S1.1 dans le drawer).
   C1 contraste vérifié thème CLAIR ET SOMBRE : les tokens de couleur (--bg- et
   --text-) basculent par [data-theme] (plus AUCUNE couleur en dur).
   GARDE BUG C (2026-05-31) : ne JAMAIS accoler un astérisque puis un slash dans
   ce commentaire — la séquence fermerait le bloc prématurément et neutraliserait
   la règle .aa-jarvis-messages juste dessous (overflow-y perdu → drawer sans scroll).
   Bulle user = accent amber + texte
   --accent-on-primary (JAMAIS blanc-sur-amber). Bulle IA = surface --bg-bloc + texte
   --text-primary. Input/placeholder lisibles dans les deux thèmes. */
.aa-jarvis-messages {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 12px;
  min-height: 0;
}
.aa-jarvis-row { display: flex; margin: 6px 0; }
.aa-jarvis-row-user { justify-content: flex-end; }
.aa-jarvis-row-ia { justify-content: flex-start; }
.aa-jarvis-bubble {
  max-width: 85%; /* C1 : un peu plus large pour respirer. */
  padding: 8px 12px;
  font-size: 14px;
  line-height: 1.4;
  white-space: pre-wrap;
  word-break: break-word;
}
.aa-jarvis-bubble-user {
  background: var(--accent);
  color: var(--accent-on-primary);
  border-radius: 14px 14px 2px 14px;
  margin-right: 8px; /* inset bord droit (au-delà du padding container). */
}
.aa-jarvis-bubble-ia {
  background: var(--bg-bloc);
  color: var(--text-primary);
  border-radius: 14px 14px 14px 2px;
  margin-left: 8px; /* C1 : symétrie — miroir de la marge droite des bulles user. */
}
.aa-jarvis-bubble-error {
  background: var(--error-bg);
  color: var(--error);
}
.aa-jarvis-composer {
  display: flex;
  gap: 8px;
  align-items: center;
  padding: 8px 12px;
  border-top: 1px solid var(--border);
  background: var(--bg-card);
}
.aa-jarvis-input {
  flex: 1 1 auto;
  min-width: 0;
  height: 48px;
  padding: 0 14px;
  font-size: 14px;
  color: var(--text-primary);
  background: var(--bg-card);
  border: 1px solid var(--border-strong);
  border-radius: 24px;
}
.aa-jarvis-input::placeholder { color: var(--text-muted); }
.aa-jarvis-input:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: var(--accent);
}
.aa-jarvis-send {
  flex: 0 0 auto;
  min-height: 48px;
  padding: 0 16px;
  font-size: 14px;
  font-weight: 600;
  color: var(--accent-on-primary);
  background: var(--accent);
  border: none;
  border-radius: 24px;
  cursor: pointer;
}
.aa-jarvis-send:hover { background: var(--accent-hover); }
.aa-jarvis-send:disabled { opacity: 0.55; cursor: default; }

/* Chips de suggestion PERSISTANTS (feat/voxa-chips) — rangée en FLUX NORMAL, juste
   au-dessus de la barre input+dictaphone. Le fil (.aa-jarvis-messages, flex:1)
   scrolle au-dessus : les chips ne le recouvrent JAMAIS (pas de position:absolute).
   Pas de séparateur lourd au-dessus (le seul filet est le border-top du composer,
   en dessous). Pastilles DS (même langage que Envoyer/dictaphone), wrap si étroit,
   cibles tactiles ≥44px. Fond = bg-card pour souder le bloc bas (chips + composer)
   dans le rectangle conversationnel. */
.aa-jarvis-suggestions {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 10px 12px 2px;
  background: var(--bg-card);
}
.aa-jarvis-suggestion {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 14px;
  font-size: 13px;
  font-weight: 500;
  line-height: 1.25;
  text-align: left;
  color: var(--text-secondary);
  background: var(--bg-bloc);
  border: 1px solid var(--border);
  border-radius: 22px;
  cursor: pointer;
  transition: border-color 120ms ease, color 120ms ease, background 120ms ease;
}
.aa-jarvis-suggestion:hover {
  color: var(--text-primary);
  border-color: var(--accent);
}
.aa-jarvis-suggestion:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: var(--accent);
}
/* feat/chips-relance-devis — chip ACTION « relancer » (devis ET factures) :
   tampon accent REMPLI (langage CTA primary, theme-safe clair+sombre, AAA via
   --accent/--accent-on-primary). La distingue des chips LECTURE neutres pour
   hisser le geste le plus stratégique de la page (relancer = acquérir/encaisser)
   en tête de regard. MÊME gabarit (44px, radius 22px) → la rangée reste
   cohérente, seule l'encre change. Glyphe ↻ purement décoratif (::before) :
   le libellé envoyé à Voxa (data-voxa-prompt) reste INTACT. Déclaré APRÈS la
   base → l'emporte sur :hover/:focus-visible neutres (spécificité égale). */
.aa-jarvis-suggestion--action {
  color: var(--accent-on-primary);
  background: var(--accent);
  border-color: var(--accent);
  font-weight: 600;
}
.aa-jarvis-suggestion--action::before {
  content: "↻";
  margin-right: 6px;
  font-size: 14px;
  line-height: 1;
}
.aa-jarvis-suggestion--action:hover {
  color: var(--accent-on-primary);
  background: var(--accent-hover);
  border-color: var(--accent-hover);
}
.aa-jarvis-suggestion--action:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-color: var(--accent);
}

/* C1 — bulle « Voxa réfléchit… » pendant le tool-calling (2-4s). Pulse DISCRET
   (opacity only — pas de grossissement ni glow). Réutilisé par le dicto (C3). */
.aa-jarvis-bubble-pending {
  animation: aa-jarvis-pulse 1.4s ease-in-out infinite;
}
@keyframes aa-jarvis-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.55; }
}
@media (prefers-reduced-motion: reduce) {
  .aa-jarvis-bubble-pending { animation: none; }
}

/* C2 — transparence tools : chips discrètes sous la bulle Voxa (sources
   consultées). Pilule subtile sur la bulle IA, lisible thème clair ET sombre. */
.aa-jarvis-tools {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 6px;
}
.aa-jarvis-tool-chip {
  display: inline-flex;
  align-items: center;
  font-size: 12px;
  line-height: 1.4;
  padding: 1px 8px;
  border-radius: 10px;
  background: var(--bg-card);
  color: var(--text-secondary);
  border: 0.5px solid var(--border);
}

/* fix/voxa-factures-filtre — lien profond serveur sous la bulle Voxa (ex
   « Ouvrir la liste filtrée » vers /factures pré-filtrée). Bouton de
   navigation pleine cible (48px), même langage que les boutons de carte. */
.aa-jarvis-lien { margin-top: 8px; }
.aa-jarvis-lien-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: 8px 14px;
  font-size: 14px;
  font-weight: 600;
  border-radius: 10px;
  text-decoration: none;
  color: var(--accent-on-primary);
  background: var(--accent);
  border: 1px solid var(--accent);
}
.aa-jarvis-lien-btn:hover { background: var(--accent-hover); }

/* V3-S0 — carte de confirmation d'une action PROPOSÉE par Voxa. Preview VISIBLE
   (résumé + aperçu) + tap humain obligatoire avant tout effet (invariant a). */
.aa-jarvis-proposition {
  margin: 4px 8px;
  padding: 10px 12px;
  border: 1px solid var(--border-strong);
  border-radius: 12px;
  background: var(--bg-card);
}
.aa-jarvis-prop-resume {
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
}
.aa-jarvis-prop-apercu {
  font-size: 13px;
  color: var(--text-secondary);
  margin-top: 2px;
}
.aa-jarvis-prop-warn { color: var(--warning); font-weight: 600; }
.aa-jarvis-prop-actions { display: flex; gap: 8px; margin-top: 10px; }
/* Footer ÉPINGLÉ du drawer (BUG C 2026-05-31) : porte Annuler/Confirmer d'une
   proposition HORS de la carte scrollable. flex:0 0 auto entre .aa-jarvis-messages
   (scrollable) et le composer -> les boutons restent TOUJOURS visibles, zéro scroll
   requis pour atteindre Confirmer. Masqué tant qu'aucune proposition en attente. */
.aa-jarvis-prop-footer {
  flex: 0 0 auto;
  padding: 8px 12px;
  border-top: 1px solid var(--border);
  background: var(--bg-card);
}
.aa-jarvis-prop-footer[hidden] { display: none; }
.aa-jarvis-prop-footer .aa-jarvis-prop-actions { margin-top: 0; }
.aa-jarvis-prop-btn {
  flex: 1 1 0;
  min-height: 48px;
  font-size: 14px;
  font-weight: 600;
  border-radius: 10px;
  cursor: pointer;
  border: 1px solid var(--border-strong);
}
.aa-jarvis-prop-confirm {
  color: var(--accent-on-primary);
  background: var(--accent);
  border-color: var(--accent);
}
.aa-jarvis-prop-confirm:hover { background: var(--accent-hover); }
.aa-jarvis-prop-cancel { color: var(--text-secondary); background: var(--bg-bloc); }
.aa-jarvis-prop-btn:disabled { opacity: 0.55; cursor: default; }
.aa-jarvis-prop-done {
  font-size: 13px;
  font-weight: 600;
  color: var(--success);
  margin-top: 8px;
}

/* Réconciliation cycle jour (fix/reconciliation-cycle-jour) : chips de durée
   (+15/+30/+1 h/Autre…) + sélecteur date+heure du Reporter de digest. Les chips
   sont des boutons COMPACTS (flex 0) — la rangée principale garde ses boutons
   pleine largeur (flex 1). */
.aa-jarvis-reconcil-chips { flex-wrap: wrap; align-items: center; margin-top: 8px; }
.aa-jarvis-reconcil-chips-label {
  font-size: 12px;
  color: var(--text-muted);
  flex: 0 0 auto;
}
.aa-jarvis-reconcil-chip {
  flex: 0 0 auto;
  min-height: 40px;
  padding: 0 12px;
  background: var(--bg-bloc);
  color: var(--text-primary);
}
.aa-jarvis-reconcil-min { flex: 0 0 auto; width: 72px; min-height: 40px; }
.aa-jarvis-reconcil-replan { flex-wrap: wrap; }
.aa-jarvis-reconcil-date { flex: 1 1 130px; min-height: 40px; }
/* 104px mini : le rendu 12h natif (« 02:15 PM ») tronquait à 88px. */
.aa-jarvis-reconcil-time { flex: 0 1 104px; min-width: 104px; min-height: 40px; }

/* V3 MULTI — liste à validation sélective (cases à cocher + alternatives). */
.aa-jarvis-prop-list { margin-top: 8px; display: flex; flex-direction: column; gap: 6px; }
.aa-jarvis-prop-line {
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-bloc);
}
.aa-jarvis-prop-line-ambigu { opacity: 0.6; }
.aa-jarvis-prop-checkrow {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 40px;
  cursor: pointer;
}
.aa-jarvis-prop-check { width: 20px; height: 20px; flex: 0 0 auto; accent-color: var(--accent); }
.aa-jarvis-prop-check:disabled { cursor: default; }
.aa-jarvis-prop-linelabel { font-size: 13px; color: var(--text-primary); }
.aa-jarvis-prop-conflit { font-size: 12px; color: var(--warning); font-weight: 600; margin: 4px 0 2px 28px; }
.aa-jarvis-prop-alts { display: flex; flex-wrap: wrap; gap: 6px; margin-left: 28px; }
.aa-jarvis-prop-alt {
  min-height: 32px;
  padding: 4px 10px;
  font-size: 12px;
  border-radius: 999px;
  border: 1px solid var(--border-strong);
  background: var(--bg-card);
  color: var(--text-secondary);
  cursor: pointer;
}
.aa-jarvis-prop-alt.is-active {
  background: var(--accent);
  color: var(--accent-on-primary);
  border-color: var(--accent);
}

/* Planner — justification de carte (FIX 2). Synthèse globale (1 ligne) sous le
   résumé + badges PAR RDV : 🔴 contrainte/urgence dure (rouge DS) / 🔵 optimisation
   (ardoise DS). Lisible d'un coup d'œil, zéro prose. */
.aa-jarvis-prop-synthese {
  font-size: 12px;
  color: var(--text-secondary);
  font-style: italic;
  margin-top: 3px;
}
.aa-jarvis-prop-justif {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin: 4px 0 0 28px;  /* aligné sous le label, après la case à cocher */
}
.aa-jarvis-justif-badge {
  font-size: 11px;
  line-height: 1.3;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg-card);
  color: var(--text-secondary);
}
.aa-jarvis-justif-badge.is-contrainte {
  background: var(--error-bg);
  color: var(--error);
  border-color: var(--error);
  font-weight: 600;
}
/* Sombre : --error (#B91C1C) sur --error-bg (#7F1D1D) ≈ 1.5:1 — illisible
   (chip « Devis accepté » rouge-sur-rouge). Texte/bord rouge CLAIR sur le
   fond sombre, ratio > 7:1 (AAA). */
[data-theme="dark"] .aa-jarvis-justif-badge.is-contrainte {
  color: #FECACA;
  border-color: #F87171;
}
.aa-jarvis-justif-badge.is-optim {
  background: var(--bg-bloc);
  color: var(--text-secondary);
  border-color: var(--border-strong);
}

/* Planner (PROBLÈME 3) — séparation nette « À créer (N) » / « Non plaçables (M) ».
   Titres de section + bloc grisé display-only (interventions non casées + raison). */
.aa-jarvis-prop-section-title {
  font-size: 12px;
  font-weight: 700;
  color: var(--text-primary);
  margin: 10px 0 4px;
}
.aa-jarvis-prop-section-title.is-muted { color: var(--text-secondary); }
/* Planner multi-scénarios : 3 cartes sélectionnables (mobile-first, ≥44px cibles).
   Tokens DS THEME-AWARE (clair + sombre) — PAS de couleur en dur (sinon texte
   clair sur fond clair en dark : bug corrigé). */
.aa-jarvis-scenarios {
  display: flex; flex-direction: column; gap: 8px; margin: 8px 0;
}
.aa-jarvis-scenario {
  display: flex; flex-direction: column; gap: 3px; width: 100%;
  text-align: left; padding: 10px 12px; min-height: 44px;
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--bg-bloc); color: var(--text-primary); cursor: pointer;
}
.aa-jarvis-scenario:hover { border-color: var(--border-strong); }
.aa-jarvis-scenario.is-active {
  border-color: var(--accent); border-width: 2px; padding: 9px 11px;
}
.aa-jarvis-scenario-titre { font-weight: 700; color: var(--text-primary); }
.aa-jarvis-scenario-arg { font-size: 0.82em; color: var(--text-secondary); }
.aa-jarvis-scenario-stats {
  font-size: 0.86em; font-weight: 600; color: var(--text-primary);
  display: flex; flex-wrap: wrap; gap: 4px 8px;
}
.aa-jarvis-scenario-stats .aa-stat-km,
.aa-jarvis-scenario-stats .aa-stat-eur { color: var(--accent); }
@media (min-width: 560px) {
  .aa-jarvis-scenarios { flex-direction: row; }
  .aa-jarvis-scenario { flex: 1 1 0; }
}
.aa-jarvis-prop-np-list { display: flex; flex-direction: column; gap: 4px; }
.aa-jarvis-prop-np {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 6px 8px;
  border: 1px dashed var(--border);
  border-radius: 8px;
  background: var(--bg-bloc);
  opacity: 0.75;  /* grisé : non actionnable */
}
.aa-jarvis-prop-np-label { font-size: 13px; color: var(--text-secondary); }
.aa-jarvis-prop-np-raison { font-size: 11px; color: var(--text-secondary); font-style: italic; }

/* Bouton dicto chat : réutilise le cercle mic canon DS (.aa-btn-mic-inline-circle,
   amber 48px + SVG mic VERBATIM) au lieu d'un emoji. Cohérence design system. */
.aa-jarvis-mic {
  flex: 0 0 auto;
  border: none;
  background: none;
  padding: 0;
  cursor: pointer;
}
/* État enregistrement (V2-S2 C3) — SIMPLIFIÉ (retour Alexis : ancienne anim
   aa-pulse trop chargée = grossissement + halo amber qui s'éclaircit). Désormais :
   cercle rouge plein + pulse DISCRET en opacity uniquement (aa-jarvis-pulse).
   PAS de grossissement (transform), PAS de halo/box-shadow (donc pas
   d'éclaircissement). On comprend « ça enregistre » sans effet too much. */
.aa-jarvis-mic.is-recording .aa-btn-mic-inline-circle {
  background: var(--error);
  color: #fff;
  box-shadow: none;
  animation: aa-jarvis-pulse 1.4s ease-in-out infinite;
}
.aa-jarvis-mic.is-recording .aa-btn-mic-inline-circle svg { stroke: #fff; }
@media (prefers-reduced-motion: reduce) {
  .aa-jarvis-mic.is-recording .aa-btn-mic-inline-circle { animation: none; }
}
/* C4 : plus d'offset --jarvis-dock-height. Le drawer S1.1 est à DROITE, plus en
   bas — le bandeau preview V1.1 (.aa-voice-banner) reprend sa place standard
   bottom:0 (classe Tailwind `.bottom-0` du fragment, aucun override nécessaire). */

/* ==========================================================================
   Pricing 3 tiers HORIZONTAL (AFFICHAGE marketing — _components/pricing_tiers.html).
   Mobile-first : 1 colonne empilée < 768px, 3 colonnes côte à côte ≥ 768px.
   Premium « featured » : bordure accent + badge + léger zoom (desktop).
   ========================================================================== */
.aa-pricing-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
  align-items: stretch;
  width: 100%;
}
@media (min-width: 768px) {
  .aa-pricing-grid { grid-template-columns: repeat(3, 1fr); gap: 20px; }
}
.aa-pricing-tier {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: 24px 20px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--bg-card);
}
.aa-pricing-tier--featured {
  border: 2px solid var(--accent);
  box-shadow: 0 8px 24px color-mix(in srgb, var(--accent) 18%, transparent);
}
@media (min-width: 768px) {
  .aa-pricing-tier--featured { transform: scale(1.04); }
}
.aa-pricing-badge {
  position: absolute;
  top: -12px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--accent);
  color: #1F2937;
  font-size: 12px;
  font-weight: 700;
  padding: 4px 12px;
  border-radius: 999px;
  white-space: nowrap;
}
.aa-pricing-name {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 8px;
}
.aa-pricing-price-row { display: flex; align-items: baseline; gap: 6px; margin-bottom: 4px; }
.aa-pricing-price { font-size: 34px; font-weight: 700; color: var(--text-primary); line-height: 1; }
.aa-pricing-price--featured { color: var(--accent); }
.aa-pricing-per { font-size: 14px; color: var(--text-muted); }
.aa-pricing-tagline { font-size: 13px; color: var(--text-secondary); margin: 8px 0 14px; }
.aa-pricing-features {
  list-style: none;
  padding: 0;
  margin: 0 0 20px;
  flex: 1 1 auto;
  font-size: 14px;
  color: var(--text-secondary);
}
.aa-pricing-features li { display: flex; gap: 8px; margin-bottom: 8px; line-height: 1.35; }
.aa-pricing-features li::before { content: "✓"; color: var(--accent); font-weight: 700; flex-shrink: 0; }
.aa-pricing-features li.is-plain::before { display: none; }
.aa-pricing-soon {
  display: inline-block;
  font-size: 11px;
  font-weight: 700;
  color: var(--accent);
  border: 1px solid var(--accent);
  border-radius: 6px;
  padding: 0 6px;
  margin-right: 4px;
}
.aa-pricing-cta { margin-top: auto; }
/* La card auth (login/abonnements/trial-expired) est étroite (~420px). Quand elle
   héberge la grille pricing, on l'élargit pour permettre le 3-up horizontal ≥768px.
   :has() (support universel 2026) + spécificité supérieure → override le max-width
   desktop sans toucher au layout ni ajouter d'inline CSS (CSP-safe). */
.aa-auth-card:has(.aa-pricing-grid) { max-width: 1040px; }

/* ============================================================================
   Maps UX (PROTOTYPE) — Optimisation de tournée.
   Zone CTA dans la sidebar filtres (.aa-opt-*) + panneau de gain (.aa-gain-*).
   Données MOCKÉES côté JS (calendrier-maps-ux.js). Tokens DS réutilisés →
   support dark/light automatique.
============================================================================ */
.aa-opt-zone {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px;
  border-radius: 10px;
  background: var(--bg-bloc);
  border: 1px solid var(--border);
}
.aa-opt-title {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--text-muted);
}
.aa-opt-btn {
  width: 100%;
  min-height: 44px;
}
.aa-opt-sub {
  font-size: 12px;
  color: var(--text-secondary);
  text-align: center;
}
/* Bonus 2 (feat/maps-polish) — invite affichée hors vue Jour (bouton désactivé). */
.aa-opt-hint {
  font-size: 12px;
  color: var(--text-muted);
  text-align: center;
  line-height: 1.35;
}

/* Panneau de gain — latéral droit desktop, bottom-sheet mobile. */
.aa-gain-panel {
  position: fixed;
  z-index: 55;
  right: 24px;
  bottom: 24px;
  width: 300px;
  max-width: calc(100vw - 32px);
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  box-shadow: 0 12px 32px rgba(15, 23, 42, 0.22);
}
.aa-gain-head {
  display: flex;
  align-items: center;
  gap: 8px;
}
.aa-gain-spark {
  font-size: 18px;
  line-height: 1;
}
.aa-gain-title {
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.2px;
}
.aa-gain-intro {
  font-size: 13px;
  color: var(--text-secondary);
  margin: 0;
}
.aa-gain-metrics {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  padding: 12px 0;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}
.aa-gain-metric {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  flex: 1;
}
.aa-gain-value {
  font-size: 18px;
  font-weight: 700;
  color: var(--success);
  white-space: nowrap;
}
.aa-gain-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.3px;
  color: var(--text-muted);
}
.aa-gain-actions {
  display: flex;
  gap: 8px;
}
.aa-gain-actions > button {
  flex: 1;
}
.aa-gain-demo {
  font-size: 11px;
  color: var(--text-muted);
  text-align: center;
  margin: 0;
  font-style: italic;
}
/* Bug 1 (feat/maps-polish) — bouton « Ouvrir dans Google Maps » : pleine largeur,
   icône + libellé centrés, hauteur alignée sur Appliquer/Annuler. Sorti au niveau
   panneau (x-show=mapsUrl) → survit à la transition optimisable → appliqué (Bug 2).
   Repose sur .aa-btn-secondary pour les couleurs ; ne fixe QUE la mise en page. */
.aa-gain-maps-link {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  min-height: 48px;
  padding: 0 16px;
  font-weight: 600;
  text-decoration: none;
}
.aa-gain-maps-icon {
  display: inline-flex;
  font-size: 16px;
  line-height: 1;
}
/* Bug 2 — bloc « tournée appliquée » : confirmation succès + bouton Maps conservé. */
.aa-gain-applied {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  font-weight: 600;
  color: var(--success);
}
.aa-gain-applied-check {
  font-size: 18px;
  line-height: 1;
}
/* feat/maps-depart — invite « renseignez l'adresse de départ » (base absente,
   le trajet part du 1er client). Ton avertissement (warning), discret. */
.aa-gain-base-hint {
  font-size: 12px;
  line-height: 1.4;
  color: var(--warning, #b45309);
  background: var(--warning-bg, color-mix(in srgb, var(--accent) 10%, transparent));
  border-radius: 8px;
  padding: 8px 10px;
  margin: 0;
}

@media (max-width: 767px) {
  .aa-gain-panel {
    right: 0;
    left: 0;
    bottom: 0;
    width: auto;
    max-width: none;
    border-radius: 16px 16px 0 0;
    padding-bottom: calc(20px + env(safe-area-inset-bottom, 0px));
  }
}

/* ==========================================================================
   Section 31 — feat/carte-zones : carte Leaflet sous /calendrier.
   Marqueurs = circleMarker SVG colorés PAR STATUT (réutilise les hex .aa-evt-*
   sect. 30) via className -> fill/stroke CSS (le thème sombre s'applique sans
   JS). Mode scénario : couleurs de zone posées en style inline par
   calendrier-carte.js (inline > CSS), cercles .aa-map-zone.
   Anim SOBRES : fade-in des marqueurs + transition d'opacité (pas d'anime.js).
   NOTE commentaire : ne JAMAIS écrire d'étoile-slash dans ces commentaires
   (FRICTIONS — un commentaire CSS mal fermé tue la règle suivante).
   ========================================================================== */

.aa-carte-section {
  background: var(--bg-card, #FFFFFF);
  border: 1px solid var(--border, #E2E8F0);
  border-radius: 12px;
  padding: 16px;
}
.aa-carte-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 4px 12px;
  margin-bottom: 12px;
}
.aa-carte-title {
  font-weight: 600;
  font-size: 16px;
  color: var(--text-primary, #1F2937);
}
.aa-carte-hint {
  font-size: 12px;
  color: var(--text-muted, #94A3B8);
}
.aa-carte-map-wrap { position: relative; }
.aa-carte-map {
  height: 420px;
  border-radius: 12px;
  overflow: hidden;
  border: 1px solid var(--border, #E2E8F0);
  box-shadow: 0 4px 16px rgba(15, 23, 42, 0.08), 0 1px 3px rgba(15, 23, 42, 0.06);
  /* Leaflet pose position:relative lui-même ; z-index bas pour ne jamais
     passer au-dessus des modals/drawer (drawer Voxa ~z50). */
  z-index: 0;
}
[data-theme="dark"] .aa-carte-map {
  border-color: var(--border, #4B5563);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
}
@media (max-width: 767px) {
  .aa-carte-map { height: 320px; }
}

/* Habillage carte-v3 : les tuiles OSM brutes datent visuellement (saturation
   criarde). Désaturation douce -> look pastel sobre, les marqueurs/tracés DS
   (couleurs saturées) ressortent au-dessus. Raster OSM conservé : fond vecteur
   (OpenFreeMap/MapLibre) = vendorer ~800 Ko de runtime GL, écarté (solo op). */
.aa-carte-map .leaflet-tile {
  filter: saturate(0.72) contrast(1.04) brightness(1.02);
}

/* Contrôles de zoom : facture DS (coins arrondis, bord token, theme-aware). */
.aa-carte-map .leaflet-control-zoom {
  border: 1px solid var(--border, #E2E8F0);
  border-radius: 10px;
  overflow: hidden;
  box-shadow: 0 2px 8px rgba(15, 23, 42, 0.12);
}
.aa-carte-map .leaflet-control-zoom a {
  background: var(--bg-card, #FFFFFF);
  color: var(--text-primary, #1F2937);
  border-bottom-color: var(--border, #E2E8F0);
}
.aa-carte-map .leaflet-control-zoom a:hover {
  background: var(--bg-bloc, #F8FAFC);
}
[data-theme="dark"] .aa-carte-map .leaflet-control-zoom a {
  background: var(--bg-card, #374151);
  color: var(--text-primary, #F9FAFB);
  border-bottom-color: var(--border, #4B5563);
}

/* Popups : coins arrondis + ombre douce DS (les défauts Leaflet font 2010). */
.aa-carte-map .leaflet-popup-content-wrapper {
  border-radius: 10px;
  box-shadow: 0 6px 20px rgba(15, 23, 42, 0.16);
}
.aa-carte-map .leaflet-popup-content { margin: 10px 14px; }

/* Hint molette (carte-v2) : chip discrète en bas de carte, au-dessus des panes
   Leaflet (controls ~z1000 dans leur contexte ; le wrap crée le nôtre).
   pointer-events none : le clic passe À TRAVERS vers la carte (qui active la
   molette et masque le hint). Masqué au doigt (le mobile pince, pas de molette). */
.aa-carte-wheel-hint {
  position: absolute;
  bottom: 10px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 2;
  pointer-events: none;
  font-size: 11px;
  padding: 3px 10px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.85);
  color: var(--text-secondary, #475569);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
  transition: opacity 240ms ease;
  white-space: nowrap;
}
.aa-carte-wheel-hint.is-hidden { opacity: 0; }
[data-theme="dark"] .aa-carte-wheel-hint {
  background: rgba(31, 41, 55, 0.85);
  color: #CBD5E1;
}
@media (pointer: coarse) {
  .aa-carte-wheel-hint { display: none; }
}

/* Bandeau post-confirmation (carte-v2) : explique le retour à l'affichage
   statut après apply d'un scénario (zones/numéros retirés = planning créé). */
.aa-carte-bandeau {
  position: absolute;
  top: 10px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 2;
  pointer-events: none;
  font-size: 12px;
  font-weight: 600;
  padding: 5px 12px;
  border-radius: 999px;
  background: rgba(22, 163, 74, 0.92);
  color: #FFFFFF;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
  /* Carte étroite (drawer ouvert) / mobile : 2 lignes plutôt que tronquer. */
  white-space: normal;
  text-align: center;
  border-radius: 12px;
  max-width: calc(100% - 20px);
}

/* Marqueur d'itinéraire numéroté (carte-v2/v3) : ordre de passage du jour.
   PLEIN = RDV proposé (background inline = couleur du jour) ; is-existant =
   RDV déjà planifié (anneau : fond carte, texte/bord couleur du jour inline). */
.aa-map-iti-pin {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  color: #FFFFFF;
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  border: 2px solid #FFFFFF;
  box-shadow: 0 2px 6px rgba(15, 23, 42, 0.35);
}
[data-theme="dark"] .aa-map-iti-pin {
  border-color: #1F2937;
  color: #111827;
}
.aa-map-iti-pin.is-existant {
  background: var(--bg-card, #FFFFFF);
  /* color + border-color posés inline (couleur du jour) ; le sombre garde le
     fond carte sombre, le numéro reste dans la couleur claire du jour. */
}
[data-theme="dark"] .aa-map-iti-pin.is-existant {
  background: var(--bg-card, #374151);
}

/* Pins de légende (proposé plein / existant anneau) — neutres, théme-aware. */
.aa-map-leg-pin {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  display: inline-block;
  background: var(--text-secondary, #475569);
  border: 2px solid var(--text-secondary, #475569);
}
.aa-map-leg-pin.is-existant { background: var(--bg-card, #FFFFFF); }

/* Mention « tracé indicatif » : note discrète quand ≥1 jour n'a pas de
   géométrie routière (les pointillés droits montrent l'ORDRE, pas la route). */
.aa-carte-leg-indicatif {
  font-style: italic;
  color: var(--text-muted, #94A3B8);
}

/* Bouton « Tournée du jour » (fix/polish-uat) : icône inline + libellé qui
   change selon l'état (le disabled DIT pourquoi, pas juste un tooltip). */
.aa-chip-tournee {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  /* fix/carte-mobile : ne JAMAIS déborder du viewport — le libellé long
     (« Tournée du jour → Google Maps ») passe sur 2 lignes au besoin,
     cible tactile >= 48px conservée (min-height inline). */
  max-width: 100%;
  white-space: normal;
  text-align: left;
}
.aa-chip-tournee:disabled {
  opacity: 0.6;
  cursor: not-allowed;
  font-style: italic;
}

/* Rapport d'apply HONNÊTE (fix/polish-uat) : lignes sautées sous « X/Y
   appliqués » — chaque échec avec sa raison, lisible dans la bulle. */
.aa-jarvis-prop-echecs {
  margin: 4px 0 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.aa-jarvis-prop-echec {
  font-size: 12px;
  line-height: 1.45;
  color: var(--error, #B91C1C);
}
[data-theme="dark"] .aa-jarvis-prop-echec { color: #FCA5A5; }

/* Légende sous la carte (chips discrètes). */
.aa-carte-legende {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 14px;
  margin-top: 10px;
  font-size: 12px;
  color: var(--text-secondary, #475569);
}
.aa-carte-leg-item { display: inline-flex; align-items: center; gap: 5px; }

/* Chip de JOUR cliquable (carte-v31) : isole le jour sur la carte. Reset
   button + affordance discrète ; is-active = fond bloc + bord fort. */
button.aa-carte-leg-jour {
  background: none;
  border: 1px solid transparent;
  border-radius: 999px;
  padding: 3px 9px;
  margin: -3px 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  min-height: 28px;
  transition: background 160ms ease, border-color 160ms ease;
}
button.aa-carte-leg-jour:hover {
  background: var(--bg-bloc, #F1F5F9);
}
button.aa-carte-leg-jour.is-active {
  background: var(--bg-bloc, #F1F5F9);
  border-color: var(--border-strong, #94A3B8);
  font-weight: 600;
  color: var(--text-primary, #1F2937);
}

.aa-map-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  display: inline-block;
}
/* BUG légende (carte-v2) : les règles .aa-map-pt-* posent fill/stroke (SVG),
   inopérants sur un <i> HTML -> pastilles invisibles. Les pastilles de légende
   reçoivent leur background EXPLICITEMENT (mêmes hex que les marqueurs). */
.aa-map-dot.aa-map-pt-planifie  { background: #F59E0B; }
.aa-map-dot.aa-map-pt-confirme  { background: #15803D; }
.aa-map-dot.aa-map-pt-annule    { background: #DC2626; opacity: 0.65; }
.aa-map-dot.aa-map-pt-suggere   { background: #94A3B8; }
.aa-map-dot.aa-map-pt-a_recaser { background: #7C3AED; }
[data-theme="dark"] .aa-map-dot.aa-map-pt-planifie  { background: #D97706; }
[data-theme="dark"] .aa-map-dot.aa-map-pt-annule    { background: #B91C1C; }
[data-theme="dark"] .aa-map-dot.aa-map-pt-a_recaser { background: #6D28D9; }
/* Pastille « Zone » (légende mode scénario) : anneau neutre semi-plein. */
.aa-map-dot-zone {
  background: transparent;
  border: 2px solid var(--text-muted, #94A3B8);
}
/* Tiret de jour d'itinéraire (couleur posée inline par calendrier-carte.js). */
.aa-map-dash {
  width: 16px;
  height: 3px;
  border-radius: 2px;
  display: inline-block;
}
.aa-map-dot-base {
  border-radius: 2px;
  background: var(--accent, #F59E0B);
}

/* --- Marqueurs RDV par STATUT (mêmes hex que .aa-evt-*, sect. 30) -------- */
/* Fade-in à l'ajout : Leaflet insère le <path> -> l'animation CSS joue une
   fois. Transition douce quand le mode zone retire/repose les couleurs. */
@keyframes aa-map-pt-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.aa-map-pt {
  animation: aa-map-pt-in 320ms ease-out;
  transition: fill 240ms ease, stroke 240ms ease, fill-opacity 240ms ease,
              opacity 240ms ease;
}
.aa-map-pt-planifie  { fill: #F59E0B; stroke: #B45309; }
.aa-map-pt-confirme  { fill: #15803D; stroke: #166534; }
.aa-map-pt-annule    { fill: #DC2626; stroke: #B91C1C; opacity: 0.65; }
.aa-map-pt-suggere   { fill: #94A3B8; stroke: #64748B; }
.aa-map-pt-a_recaser { fill: #7C3AED; stroke: #5B21B6; }
[data-theme="dark"] .aa-map-pt-planifie  { fill: #D97706; stroke: #FBBF24; }
[data-theme="dark"] .aa-map-pt-confirme  { fill: #15803D; stroke: #4ADE80; }
[data-theme="dark"] .aa-map-pt-annule    { fill: #B91C1C; stroke: #F87171; }
[data-theme="dark"] .aa-map-pt-suggere   { fill: #94A3B8; stroke: #CBD5E1; }
[data-theme="dark"] .aa-map-pt-a_recaser { fill: #6D28D9; stroke: #C4B5FD; }

/* Mode scénario : points hors zones estompés (les couleurs de zone des
   membres sont posées en style inline par calendrier-carte.js). */
.aa-map-pt-horszone { opacity: 0.25; }

/* Cercles de zone (semi-transparents, transition douce à l'apparition). */
.aa-map-zone {
  animation: aa-map-pt-in 360ms ease-out;
  transition: opacity 240ms ease;
}

/* Marqueur base (point de départ tournée) : pin maison amber DS. */
.aa-map-base-pin {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 50% 50% 50% 4px;
  background: var(--accent, #F59E0B);
  color: #1C1917;
  font-size: 17px;
  line-height: 1;
  border: 2px solid #B45309;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);
}
[data-theme="dark"] .aa-map-base-pin { border-color: #FBBF24; }

/* Popup Leaflet — typo DS + variante sombre (fond blanc Leaflet par défaut). */
.aa-carte-popup { min-width: 160px; }
.aa-carte-popup-titre {
  font-weight: 600;
  font-size: 14px;
  margin: 0 0 2px;
  color: var(--text-primary, #1F2937);
}
.aa-carte-popup-heure {
  font-size: 12px;
  margin: 0 0 6px;
  color: var(--text-secondary, #475569);
}
.aa-carte-popup-lien {
  font-size: 13px;
  font-weight: 600;
  color: var(--accent, #F59E0B);
  text-decoration: underline;
}
[data-theme="dark"] .aa-carte-map .leaflet-popup-content-wrapper,
[data-theme="dark"] .aa-carte-map .leaflet-popup-tip {
  background: var(--bg-card, #374151);
  color: var(--text-primary, #F9FAFB);
}

/* Tuiles OSM en mode sombre : inversion contrôlée (pattern standard Leaflet
   dark) — les marqueurs/cercles SVG ne sont PAS affectés (calque séparé). */
[data-theme="dark"] .aa-carte-map .leaflet-tile {
  /* carte-v3 : + désaturation (cohérent avec le filtre pastel du mode clair). */
  filter: invert(100%) hue-rotate(180deg) brightness(95%) contrast(90%) saturate(0.75);
}
[data-theme="dark"] .aa-carte-map .leaflet-container,
[data-theme="dark"] .aa-carte-map { background: #1F2937; }

/* Attribution lisible dans les deux thèmes. */
[data-theme="dark"] .aa-carte-map .leaflet-control-attribution {
  background: rgba(31, 41, 55, 0.8);
  color: #CBD5E1;
}
[data-theme="dark"] .aa-carte-map .leaflet-control-attribution a {
  color: var(--accent);
}

/* fix/polish-uat-2 — zone de drop import clients : feedback visuel pendant
   le survol drag (classe posée par import-clients.js). */
#import-dropzone.is-dragover {
  border-color: var(--accent, #475569);
  background: var(--bg-bloc, #f1f5f9);
}


/* ==========================================================================
   §50. DA Carnet d'atelier — micro-interactions (feat/da-carnet-socle)
   --------------------------------------------------------------------------
   « Un truc vivant » : le socle d'animations de l'app, en CSS pur (PAS
   d'anime.js dans la PWA — réservé à la vitrine, lot ultérieur).
   Vocabulaire :
   - hover/press boutons & cartes : -1px + ombre / scale .97 — déjà câblés
     dans §2 (.aa-card-hover) et §4 (.aa-btn-*), durée --dur-fast.
   - pose de tampon : aa-stamp-in (scale 1.06 → 1 avec léger overshoot
     --ease-stamp + rotation finale portée par la classe, ex. -2°
     .aa-pill-alarm).
   - entrée de modal : fade du voile + la fiche se pose (translateY + scale).
     CSS `animation` re-déclenche à chaque passage display:none → visible :
     couvre TOUS les modals existants (x-show Alpine, injection HTMX
     #modal-mount) sans toucher aux templates.
   - feedback tactile : sur écran sans hover, le press assombrit ET tasse
     le bouton (retour < 200 ms sur la cible 48px).
   ========================================================================== */

@keyframes aa-stamp-in {
  from { transform: scale(1.06) rotate(-2deg); opacity: 0; }
  to   { transform: scale(1) rotate(-2deg); opacity: 1; }
}

@keyframes aa-overlay-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes aa-modal-in {
  from { opacity: 0; transform: translateY(8px) scale(0.985); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

/* Voiles de modal (les deux conventions de nommage du repo). */
.aa-modal-overlay,
.modal-overlay {
  animation: aa-overlay-in var(--dur-fast) var(--ease-out) both;
}

/* La fiche modale se pose sur le bureau. Cible les conteneurs internes
   usuels ; inerte si la classe n'existe pas sur la page. */
.aa-modal,
.aa-modal-overlay > [class*="rounded"],
.modal-overlay > [class*="rounded"] {
  animation: aa-modal-in 180ms var(--ease-out) both;
}

/* Feedback tactile mobile — écrans sans hover : le press porte tout le
   retour visuel (assombrissement + tassement), cibles 48px. */
@media (hover: none) {
  .aa-btn-primary:active,
  .aa-btn-amber-primary:active,
  .aa-btn-secondary:active,
  .aa-btn-danger:active,
  .aa-jarvis-tab:active {
    filter: brightness(0.94);
  }
  .aa-card-hover:active { background: var(--bg-bloc); }
}

/* a11y — reduced motion : plus aucune entrée animée (le bloc transition:none
   de §1.bis couvre déjà les transitions ; ceci couvre les `animation`). */
@media (prefers-reduced-motion: reduce) {
  .aa-pill-alarm,
  .aa-modal-overlay,
  .modal-overlay,
  .aa-modal,
  .aa-modal-overlay > [class*="rounded"],
  .modal-overlay > [class*="rounded"] {
    animation: none;
  }
}

/* ============================================================================
   §51. Sweep DA 2026-06-13 (chore/sweep-emojis-icones) — reliquats vieux front.
   ============================================================================ */

/* §51.a Input file DA — bouton natif « Choisir un fichier » (logo entreprise).
   Reliquat ancien front : utilities Tailwind file:bg-amber-500 figées clair.
   Ici : miroir .aa-btn-primary via ::file-selector-button, les deux thèmes
   suivent les tokens (--accent kraft clair / ambre sombre). */
.aa-input-file {
  color: var(--text-primary);
  font-size: 14px;
  cursor: pointer;
}
.aa-input-file::file-selector-button {
  margin-right: 16px;
  padding: 10px 20px;
  background: var(--accent);
  color: var(--accent-on-primary);
  border: none;
  border-radius: 8px;
  font-size: 14px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-standard);
}
.aa-input-file::file-selector-button:hover { background: var(--accent-hover); }
.aa-input-file:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* §51.b Tables zebra theme-aware — fix CEO 2026-06-13 : ligne utilisateur
   « En attente » illisible en sombre (Paramètres > Utilisateurs). DaisyUI
   .table-zebra raye les lignes paires avec SES variables de thème (--b2,
   jamais flippées par notre [data-theme]) -> rayure claire forcée + texte
   clair du thème sombre = illisible. On re-raye avec le token DA --bg-bloc
   (papier en clair, ardoise en sombre) ; tokens.css chargé APRÈS tailwind,
   même sélecteur -> la déclaration tardive gagne. */
.table-zebra tbody tr:nth-child(even) {
  background-color: var(--bg-bloc);
}
