/* ============================================================================
   VINASTIE ADMIN — DESIGN TOKENS (story 18.1, lot 0 « admin v2 »)
   Source : claudedocs/design-handoff-admin-refonte-2026-09/README.md §10 (clair)
            et §4.1 (sombre, via prefers-color-scheme). Valeurs définitives.
   Règle : toute nouvelle règle CSS (composants .v2-*, pages 18.2+) consomme
   ces variables — aucune couleur en dur. L'ancien CSS (avant 18.1) n'est PAS
   entièrement migré ici : seules les couleurs de la charte qui y apparaissaient
   déjà en dur (#520024, #990033, #F7F7D0, #2F2F2F, #FAF9F6, via les anciennes
   variables --vinastie-*) ont été rebranchées sur ces tokens. Le reste de
   l'ancien CSS (palette ad hoc des anciens onglets) sera réécrit page par page
   dans les stories 18.4 à 18.10.
   ============================================================================ */

:root {
  /* --- Couleurs de marque (brutes) --------------------------------------- */
  --color-bordeaux: #520024;
  --color-ruby: #990033;
  --color-ruby-light: #c2405f;   /* funnel, bouton barre flottante en sombre */
  --color-ruby-mid: #7a1a44;     /* funnel (2e barre) */
  --color-beige: #F7F7D0;        /* puce (chip) / badge métadonnée */

  /* --- Fond & surfaces ----------------------------------------------------
     --bg : fond de page. --surface-card : fond des cartes/modales/tableaux
     (blanc en clair). --surface-muted / --surface-muted-2 : fonds neutres
     (segment, mini-tuiles de résultat, lignes de tableau). */
  --bg: #FAF9F6;
  --surface-card: #fff;
  --surface-muted: #F3F1EA;
  --surface-muted-2: #F1EFE8;

  /* --- En-tête / sidebar / carte highlight (fond bordeaux plein) --------- */
  --header-bg: var(--color-bordeaux);
  --surface-highlight: var(--color-bordeaux);

  /* --- Puce (chip) --------------------------------------------------------
     Fond de la puce de valeur secondaire (§3.3) et du badge « métadonnée ». */
  --chip-bg: var(--color-beige);

  /* --- Texte --------------------------------------------------------------
     --text : texte principal. --text-secondary/--text-tertiary/--text-quiet :
     les 3 gris secondaires du handoff (#666 / #777 / #555), utilisés selon
     le contexte (voir composants). --placeholder : placeholder de champ.
     --text-inverse* : texte blanc sur fond bordeaux (en-tête, sidebar). */
  --text: #2F2F2F;
  --text-secondary: #666;
  --text-tertiary: #777;
  --text-quiet: #555;
  --placeholder: #8a8a8a;
  --text-inverse: #fff;
  --text-inverse-muted: rgba(255, 255, 255, .8);
  --text-inverse-subtle: rgba(255, 255, 255, .7);
  --text-inverse-faint: rgba(255, 255, 255, .6);
  /* --- Surfaces blanches fixes sur fond bordeaux (connexion, pastille nav,
     CTA de la barre de sélection) : contrairement à --surface-card, ne varie
     PAS en thème sombre (story 18.11, nettoyage des couleurs en dur .v2-*
     restantes — mêmes raisons que --text-inverse ci-dessus). --- */
  --surface-inverse: #fff;
  --surface-inverse-10: rgba(255, 255, 255, .1);

  /* --- Titre de section (Accueil, Produit…) : bordeaux en clair, --section-title (dark) --- */
  --section-title: var(--color-bordeaux);

  /* --- Bordures teintées bordeaux (champs, boutons secondaires, pills) --- */
  --border-bordeaux-10: rgba(82, 0, 36, .1);
  --border-bordeaux-15: rgba(82, 0, 36, .15);
  --border-bordeaux-20: rgba(82, 0, 36, .2);
  --border-bordeaux-25: rgba(82, 0, 36, .25);
  --border-bordeaux-35: rgba(82, 0, 36, .35);

  /* --- Bordures / voile blancs (sur fond bordeaux : en-tête, sidebar) ---- */
  --border-inverse-12: rgba(255, 255, 255, .12);
  --border-inverse-25: rgba(255, 255, 255, .25);
  --border-inverse-45: rgba(255, 255, 255, .45);
  --surface-inverse-14: rgba(255, 255, 255, .14);
  --surface-inverse-16: rgba(255, 255, 255, .16); /* pastille de tendance sur carte KPI highlight */

  /* --- Séparateurs ---------------------------------------------------------
     --divider : bordures de modale / bandeaux. --divider-table : lignes de
     tableau (légèrement plus discrètes dans le handoff). */
  --divider: #EEE9E0;
  --divider-table: #F1EFE8;

  /* --- Squelettes de chargement ------------------------------------------- */
  --skeleton: #EAE3DC;
  --skeleton-alt: #F1EDE6;

  /* --- Voile (drawer / modale) --------------------------------------------- */
  --overlay: rgba(47, 47, 47, .45);

  /* --- États sémantiques (succès / erreur / ambre / info) -----------------
     Convention : <ton> = couleur pleine (texte sur fond clair, ou fond plein
     de bouton) ; <ton>-text = teinte de texte utilisée dans les badges/alertes
     (parfois différente de la couleur pleine) ; <ton>-bg = fond pastel. */
  --success: #1f7a3c;
  --success-bg: #e7f5ea;

  --danger: #b3261e;
  --danger-text: #8a1f16;
  --danger-bg: #fde6e3;

  --amber: #c98a00;
  --amber-text: #7a5200;
  --amber-text-strong: #9a6b00; /* variante utilisée pour « ❓ N questions », « à valider » */
  --amber-bg: #fff3d6;

  --info: #2f6fb5;
  --info-text: #1f4f8a;
  --info-bg: #e7f0fb;

  /* --- Tendances (carte KPI, §3.2) ------------------------------------------ */
  --trend-up: var(--success);
  --trend-down: var(--danger);
  --trend-flat: var(--text-tertiary);

  /* --- Barre flottante de capture d'idée (§3.4) ----------------------------- */
  --float-bar-bg: var(--surface-card);
  --float-bar-border: var(--border-bordeaux-10);
  --float-bar-btn: var(--color-ruby);
  /* Distance entre le bas de l'écran et le toast (§3.10) : au-dessus de la
     barre flottante (story 18.3) — hauteur repliée (~54px) + 14px de marge
     basse + ~16px d'air, arrondi à 88px, valable mobile et desktop. */
  --toast-bottom: 88px;

  /* --- Barre de répartition abonnements (Accueil §4.1) : segment « sans »
     (Plus/Premium réutilisent --info/--amber, déjà alignés sur les badges
     §3.8). --- */
  --sub-none-bar: #ddd;

  /* --- Story 18.6 : étoiles de note (Feedback avis, §5.2, `#c98a00` = --amber)
     et voile/boutons du lightbox de captures (Tests, §5.3, inchangé du CSS
     legacy) — toujours sombres/blancs quel que soit le thème (superposés à
     une photo, jamais au fond de l'app). --- */
  --star-filled: var(--amber);
  --star-empty: #ddd;
  --lightbox-overlay: rgba(0, 0, 0, .85);
  --thumb-overlay: rgba(0, 0, 0, .6);
  --lightbox-btn-bg: rgba(255, 255, 255, .15);
  --lightbox-btn-bg-hover: rgba(255, 255, 255, .3);

  /* --- Ombres (§10) ---------------------------------------------------------- */
  --shadow-card: 0 1px 2px rgba(82, 0, 36, .08);
  --shadow-card-selected: 0 2px 8px rgba(82, 0, 36, .12);
  --shadow-float: 0 8px 28px rgba(82, 0, 36, .22);
  --shadow-modal: 0 24px 80px rgba(0, 0, 0, .35);
  --shadow-drawer: 12px 0 40px rgba(0, 0, 0, .25);
  --shadow-segment: 0 1px 2px rgba(0, 0, 0, .08); /* option active de segment (§3.5) */

  /* --- Rayons (§10) : 5-6 badges · 8-9 boutons desktop/champs ·
     10-12 boutons mobile/cartes kanban/bandeaux · 14 cartes ·
     16-18 modales/barre flottante · 999 pills. -------------------------------- */
  --radius-xs: 6px;
  --radius-sm: 9px;
  --radius-md: 11px;
  --radius-card: 14px;
  --radius-lg: 18px;
  --radius-pill: 999px;
  --radius-tile: 8px; /* mini-tuiles du bloc résultat (§3.10) */

  /* --- Typographie (§10, inchangée sauf alignement exact sur le token) ------ */
  --font-sans: -apple-system, "Helvetica Neue", Helvetica, sans-serif;
  --font-mono: ui-monospace, Menlo, monospace;

  /* --- Histogramme « Activité 30 jours » (Produit, story 18.4) : les 3 séries
     (Scans/Vins ajoutés/Recos) coïncident avec des tons déjà définis ci-dessus
     (rubis/ambre/info) — alias dédiés pour ne pas coupler le graphique à leur
     usage badge/bouton d'origine. --- */
  --chart-scans: var(--color-ruby);
  --chart-wines: var(--amber);
  --chart-recos: var(--info);
}

/* ----------------------------------------------------------------------------
   THÈME SOMBRE (prefers-color-scheme: dark) — handoff §4.1.
   Les valeurs marquées [dérivé] ne sont PAS spécifiées explicitement par le
   handoff (qui ne détaille que l'écran Accueil sombre) : elles sont dérivées
   par cohérence avec la palette donnée (mêmes teintes que les tendances et le
   bouton de barre flottante) pour qu'aucun composant ne reste en clair. Elles
   sont donc candidates à un ajustement visuel lors des stories 18.2+ si Théau
   les juge non conformes à l'esprit du handoff.
   ---------------------------------------------------------------------------- */
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #1A1215;
    --surface-card: #261B1F;
    --surface-highlight: #5C1236;
    --header-bg: #3A0A20;
    --chip-bg: #3A2C31;
    --surface-muted: #2A1F23;   /* [dérivé] segment/table hover, proche de --surface-card */
    --surface-muted-2: #241A1E; /* [dérivé] */

    --text: #F3ECE8;
    --text-secondary: #a89aa0;
    --text-tertiary: #a89aa0;   /* [dérivé] une seule teinte secondaire donnée par le handoff */
    --text-quiet: #a89aa0;      /* [dérivé] idem */
    --placeholder: #8a7a80;     /* [dérivé] */

    --section-title: #E8A0B4;

    --divider: rgba(255, 255, 255, .1);   /* [dérivé] */
    --divider-table: rgba(255, 255, 255, .08); /* [dérivé] */

    --skeleton: #2E2226;    /* [dérivé] */
    --skeleton-alt: #362830; /* [dérivé] */

    --border-bordeaux-10: rgba(255, 255, 255, .1);
    --border-bordeaux-15: rgba(255, 255, 255, .14); /* [dérivé] */
    --border-bordeaux-20: rgba(255, 255, 255, .18); /* [dérivé] */
    --border-bordeaux-25: rgba(255, 255, 255, .22); /* [dérivé] */
    --border-bordeaux-35: rgba(255, 255, 255, .3);  /* [dérivé] */

    --trend-up: #6ecf8a;
    --trend-down: #f08a7e;
    --trend-flat: var(--text-tertiary); /* [dérivé] */

    --float-bar-bg: #2B1F23;
    --float-bar-border: rgba(255, 255, 255, .1);
    --float-bar-btn: #C2405F;

    --sub-none-bar: rgba(255, 255, 255, .18); /* [dérivé] */

    --star-empty: rgba(255, 255, 255, .2); /* [dérivé] */

    /* Badges/alertes de statut : teintes non spécifiées par le handoff pour
       le mode sombre hors Accueil. [dérivé] par tons pastel assombris,
       cohérents avec --trend-up/--trend-down. */
    --success: #6ecf8a;
    --success-bg: rgba(110, 207, 138, .16);
    --danger: #f08a7e;
    --danger-text: #f4a89e;
    --danger-bg: rgba(240, 138, 126, .16);
    --amber: #e0b040;
    --amber-text: #f0c876;
    --amber-text-strong: #f0c876;
    --amber-bg: rgba(224, 176, 64, .16);
    --info: #7fb1e8;
    --info-text: #a8cdf0;
    --info-bg: rgba(47, 111, 181, .22);

    /* --chart-scans/--chart-wines/--chart-recos (Produit, story 18.4) ne sont
       PAS redéfinis ici : ce sont des alias var(--color-ruby)/var(--amber)/
       var(--info) posés dans :root, donc déjà à jour avec les valeurs
       sombres ci-dessus par la simple cascade (--color-ruby n'a volontairement
       pas de variante sombre, comme le reste des couleurs de marque). */

    --shadow-card: 0 1px 2px rgba(0, 0, 0, .3);       /* [dérivé] */
    --shadow-card-selected: 0 2px 8px rgba(0, 0, 0, .4); /* [dérivé] */
    --shadow-float: 0 8px 28px rgba(0, 0, 0, .45);    /* [dérivé] */
    --shadow-segment: 0 1px 4px rgba(0, 0, 0, .35);   /* [dérivé] */
    /* --shadow-modal / --shadow-drawer : neutres (noir), inchangées en sombre. */

    --overlay: rgba(0, 0, 0, .6); /* [dérivé], le voile clair rgba(47,47,47,.45) manque de contraste sur fond déjà sombre */
  }
}

    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    body {
      font-family: var(--font-sans);
      background: var(--bg);
      color: var(--text);
      min-height: 100vh;
    }

    /* Story 18.3 (correctif coquille) : le bandeau <header> legacy (logo+h1,
       dupliqué au-dessus de la coquille v2) est retiré du DOM — ces 3 règles
       n'ont plus de sélecteur à cibler. */

    /* Story 18.2 : `main` ne porte plus de padding/max-width global — chaque
       écran gère désormais le sien (`.v2-login` plein écran, `.v2-denied`,
       `.v2-page-content` de la coquille authentifiée §2.1/2.4) pour éviter un
       double padding avec la nouvelle coquille. */
    main {
      display: block;
    }

    /* Loading state */
    #loading {
      text-align: center;
      padding: 4rem 2rem;
      color: var(--text);
    }

    #loading .spinner {
      width: 40px;
      height: 40px;
      border: 3px solid var(--color-beige);
      border-top-color: var(--color-ruby);
      border-radius: 50%;
      animation: spin 1s linear infinite;
      margin: 0 auto 1rem;
    }

    @keyframes spin {
      to { transform: rotate(360deg); }
    }

    /* ==========================================================================
       Écrans de connexion / accès refusé (story 18.2, handoff §9). `showSection()`
       (admin.js) ne force plus `display:block` en inline : il vide juste la
       propriété pour la section affichée, afin que ces règles (`display:flex`)
       s'appliquent normalement.
       ========================================================================== */

    /* Connexion — plein écran bordeaux, sans carte (§9). */
    .v2-login {
      position: fixed;
      inset: 0;
      z-index: 20;
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 24px 20px;
      background: var(--color-bordeaux);
      overflow-y: auto;
    }

    .v2-login__box {
      width: 100%;
      max-width: 360px;
      display: flex;
      flex-direction: column;
      align-items: center;
      text-align: center;
    }

    .v2-login__logo {
      width: 56px;
      height: 56px;
      border-radius: var(--radius-lg);
      margin-bottom: 18px;
    }

    .v2-login__title {
      color: var(--text-inverse);
      font-size: 26px;
      font-weight: 600;
      margin-bottom: 6px;
    }

    .v2-login__subtitle {
      color: var(--text-inverse-subtle);
      font-size: 14px;
      margin-bottom: 28px;
    }

    .v2-login__error {
      width: 100%;
      background: var(--danger-bg);
      color: var(--danger-text);
      border-radius: var(--radius-sm);
      padding: 10px 14px;
      font-size: 13px;
      margin-bottom: 14px;
      text-align: left;
    }

    .v2-login__field {
      display: flex;
      align-items: flex-end;
      gap: 8px;
      width: 100%;
      background: var(--surface-inverse-10);
      border: 1px solid var(--border-inverse-25);
      border-radius: var(--radius-card);
      padding: 10px 16px;
      margin-bottom: 14px;
      text-align: left;
      transition: border-color .15s;
    }

    .v2-login__field:focus-within {
      border-color: var(--border-inverse-45);
    }

    .v2-login__field-text {
      flex: 1;
      min-width: 0;
    }

    .v2-login__label {
      display: block;
      font-size: 11px;
      text-transform: uppercase;
      letter-spacing: .06em;
      color: var(--text-inverse-subtle);
      margin-bottom: 2px;
    }

    .v2-login__input {
      width: 100%;
      border: none;
      background: transparent;
      color: var(--text-inverse);
      font-size: 16px;
      font-family: var(--font-sans);
      padding: 0;
    }

    .v2-login__input:focus {
      outline: none;
    }

    .v2-login__toggle-pw {
      background: none;
      border: none;
      color: var(--text-inverse-subtle);
      font-size: 12px;
      font-weight: 600;
      cursor: pointer;
      padding: 0 0 2px;
      white-space: nowrap;
    }

    .v2-login__toggle-pw:hover {
      color: var(--text-inverse);
    }

    .v2-login__submit {
      width: 100%;
      background: var(--surface-inverse);
      color: var(--color-bordeaux);
      border: none;
      border-radius: var(--radius-card);
      padding: 15px;
      font-size: 16px;
      font-weight: 600;
      cursor: pointer;
      margin-top: 6px;
      transition: opacity .15s;
    }

    .v2-login__submit:disabled {
      opacity: .7;
      cursor: default;
    }

    .v2-login__footer {
      position: absolute;
      bottom: 18px;
      left: 0;
      right: 0;
      text-align: center;
      color: var(--text-inverse-faint);
      font-size: 11px;
    }

    /* Accès refusé (§9) : carte centrée sur le fond normal de l'app. */
    .v2-denied {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      min-height: 60vh;
      padding: 40px 20px;
      text-align: center;
    }

    .v2-denied__badge {
      width: 72px;
      height: 72px;
      border-radius: 50%;
      background: var(--danger-bg);
      color: var(--danger-text);
      font-size: 32px;
      font-weight: 700;
      display: flex;
      align-items: center;
      justify-content: center;
      margin-bottom: 18px;
    }

    .v2-denied__title {
      color: var(--color-bordeaux);
      font-size: 24px;
      font-weight: 600;
      margin-bottom: 8px;
    }

    .v2-denied__text {
      color: var(--text-quiet);
      font-size: 15px;
      max-width: 340px;
      margin-bottom: 20px;
    }

    .v2-denied__card {
      background: var(--surface-card);
      border-radius: var(--radius-card);
      box-shadow: var(--shadow-card);
      padding: 12px 18px;
      margin-bottom: 22px;
      display: flex;
      flex-direction: column;
      gap: 2px;
    }

    .v2-denied__card span {
      font-size: 12px;
      color: var(--text-tertiary);
    }

    .v2-denied__card strong {
      font-size: 14px;
      color: var(--text);
      word-break: break-all;
    }

    .v2-denied__actions {
      display: flex;
      gap: 10px;
      flex-wrap: wrap;
      justify-content: center;
    }

    /* Dashboard */
    #dashboard {
      display: none;
    }

    /* Story 18.6 : anciens sous-onglets Feedback (.feedback-subtabs/.subtab)
       retirés — remplacés par le segment d'en-tête Avis/Bugs (voir bloc dédié
       « STORY 18.6 — FEEDBACK » en fin de fichier). */

    /* KPI Grid */
    .kpi-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
      gap: 1rem;
    }

    .kpi-card {
      background: white;
      border-radius: 12px;
      padding: 1.25rem;
      box-shadow: 0 2px 8px rgba(0,0,0,0.1);
    }

    .kpi-card.highlight {
      background: var(--color-bordeaux);
      color: white;
    }

    .kpi-card.highlight .kpi-label {
      color: rgba(255,255,255,0.8);
    }

    .kpi-label {
      font-size: 0.85rem;
      color: #666;
      margin-bottom: 0.5rem;
    }

    .kpi-value {
      font-size: 1.75rem;
      font-weight: 700;
      color: var(--color-bordeaux);
    }

    .kpi-card.highlight .kpi-value {
      color: white;
    }

    .kpi-section {
      margin-bottom: 1.5rem;
    }

    .kpi-section-title {
      font-size: 1rem;
      font-weight: 600;
      color: var(--text);
      margin-bottom: 0.75rem;
      padding-bottom: 0.5rem;
      border-bottom: 2px solid var(--color-beige);
    }

    .kpi-error {
      text-align: center;
      padding: 1.5rem;
      background: #fef2f2;
      border-radius: 8px;
      color: #dc2626;
    }

    /* Users table */
    .admin-table {
      width: 100%;
      border-collapse: collapse;
      background: white;
      border-radius: 12px;
      overflow: hidden;
      box-shadow: 0 2px 8px rgba(0,0,0,0.1);
      margin-top: 12px;
    }

    .admin-table th,
    .admin-table td {
      padding: 8px 12px;
      text-align: left;
      border-bottom: 1px solid #e0d6c8;
      font-size: 14px;
    }

    .admin-table th {
      background: var(--color-bordeaux);
      color: white;
      font-weight: 600;
      cursor: pointer;
      white-space: nowrap;
      user-select: none;
    }

    .admin-table th:hover {
      background: var(--color-ruby);
    }

    .admin-table th .sort-indicator {
      margin-left: 4px;
      opacity: 0.6;
    }

    .admin-table tbody tr:hover {
      background: var(--color-beige);
    }

    .admin-table tbody tr:nth-child(even) {
      background-color: #f8f5f0;
    }

    .admin-table tbody tr:nth-child(even):hover {
      background: var(--color-beige);
    }

    .admin-table tbody tr:last-child td {
      border-bottom: none;
    }

    .admin-table td.numeric-cell {
      text-align: right;
      font-variant-numeric: tabular-nums;
    }

    /* Story 14.2 : wrapper scrollable autour des tableaux (mobile).
       Le wrapper reprend l'ombre + les coins arrondis du tableau pour un
       rendu desktop strictement identique ; sur mobile, le tableau scrolle
       dans son conteneur au lieu de faire déborder la page. */
    .table-scroll {
      overflow-x: auto;
      -webkit-overflow-scrolling: touch;
      border-radius: 12px;
      box-shadow: 0 2px 8px rgba(0,0,0,0.1);
      margin-top: 12px;
    }

    .table-scroll > .admin-table {
      margin-top: 0;
      box-shadow: none;
    }

    .users-toolbar {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 12px;
      margin-top: 8px;
    }

    .users-search-input {
      flex: 1;
      max-width: 300px;
      padding: 8px 12px;
      border-radius: 8px;
      border: 1px solid #d0c4b5;
      font-size: 14px;
    }

    .users-search-input:focus {
      outline: none;
      border-color: var(--color-ruby);
    }

    .users-count {
      font-size: 13px;
      color: #7a6b5c;
      white-space: nowrap;
    }

    .loading-spinner {
      display: inline-block;
      width: 14px;
      height: 14px;
      border: 2px solid #ddd;
      border-top-color: var(--color-ruby);
      border-radius: 50%;
      animation: spin 0.8s linear infinite;
      vertical-align: middle;
      margin-right: 8px;
    }

    @keyframes spin {
      to { transform: rotate(360deg); }
    }

    .users-loading {
      text-align: center;
      padding: 2rem;
      color: #888;
    }

    .error-text {
      text-align: center;
      padding: 1rem;
      background: #fef2f2;
      border-radius: 8px;
      color: #dc2626;
    }

    .primary-button {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      padding: 10px 20px;
      background: var(--color-ruby);
      color: white;
      border: none;
      border-radius: 8px;
      font-size: 14px;
      font-weight: 600;
      cursor: pointer;
      margin-top: 16px;
      transition: background 0.2s;
    }

    .primary-button:hover {
      background: var(--color-bordeaux);
    }

    .primary-button:disabled {
      background: #ccc;
      cursor: not-allowed;
    }

    .secondary-button {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      padding: 8px 16px;
      background: white;
      color: var(--text);
      border: 1px solid #d0c4b5;
      border-radius: 8px;
      font-size: 14px;
      font-weight: 500;
      cursor: pointer;
      transition: all 0.2s;
    }

    .secondary-button:hover:not(:disabled) {
      border-color: var(--color-ruby);
      color: var(--color-ruby);
    }

    .secondary-button:disabled {
      background: #f5f5f5;
      color: #bbb;
      border-color: #e0e0e0;
      cursor: not-allowed;
    }

    .users-pagination {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 16px;
      margin-top: 16px;
    }

    .users-page-info {
      font-size: 14px;
      color: var(--text);
      min-width: 100px;
      text-align: center;
    }

    /* Articles styles */
    .articles-toolbar {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 12px;
      margin-bottom: 16px;
    }

    .articles-toolbar h2 {
      color: var(--color-bordeaux);
      margin: 0;
    }

    .status-badge {
      display: inline-block;
      padding: 4px 10px;
      border-radius: 12px;
      font-size: 12px;
      font-weight: 600;
    }

    .status-badge.published {
      background: #dcfce7;
      color: #166534;
    }

    /* Story 18.7 : .status-badge.draft/.scheduled/.liked et .featured-badge n'ont plus de
       consommateur (page Articles réécrite en .v2-badge/.v2-chip) — .status-badge et
       .status-badge.published restent (Emails, story 18.8, les utilise encore). */

    .action-btn {
      padding: 6px 12px;
      border: none;
      border-radius: 6px;
      cursor: pointer;
      font-size: 13px;
      font-weight: 500;
      transition: all 0.2s;
    }

    /* Story 18.7 : .action-btn.edit/.publish/.unpublish/.delete/.like(.liked), .actions-cell,
       .weeks-band, .week-chip*, .candidates-grid, .candidate-card*, .push-badge, le bloc
       details.kpi-section (aucun <details> n'a plus la classe kpi-section — remplacés par
       .v2-collapsible), .inline-datetime* et .articles-more-note n'ont plus de consommateur
       (page Articles réécrite en .v2-*). .action-btn (base, sans modificateur, ou avec le
       modificateur "danger") reste : Emails et Légal l'utilisent encore. */

    /* Modal styles */
    .modal-overlay {
      display: none;
      position: fixed;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background: rgba(0, 0, 0, 0.5);
      z-index: 1000;
      align-items: center;
      justify-content: center;
    }

    .modal-overlay.active {
      display: flex;
    }

    .modal-content,
    .modal-container {
      background: white;
      border-radius: 16px;
      width: 90%;
      max-width: 700px;
      max-height: 90vh;
      overflow-y: auto;
      box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
    }

    .modal-header {
      padding: 20px 24px;
      border-bottom: 1px solid #e5e7eb;
      display: flex;
      align-items: center;
      justify-content: space-between;
    }

    .modal-header h3 {
      margin: 0;
      color: var(--color-bordeaux);
      font-size: 1.25rem;
    }

    .modal-close {
      background: none;
      border: none;
      font-size: 24px;
      cursor: pointer;
      color: #6b7280;
      padding: 4px;
      line-height: 1;
    }

    .modal-close:hover {
      color: var(--text);
    }

    .modal-body {
      padding: 24px;
    }

    .form-group {
      margin-bottom: 20px;
    }

    .form-group label {
      display: block;
      margin-bottom: 6px;
      font-weight: 500;
      color: var(--text);
      font-size: 14px;
    }

    .form-group input[type="text"],
    .form-group input[type="url"],
    .form-group input[type="datetime-local"],
    .form-group textarea {
      width: 100%;
      padding: 10px 14px;
      border: 1px solid #d1d5db;
      border-radius: 8px;
      font-size: 14px;
      font-family: inherit;
    }

    .form-group input:focus,
    .form-group textarea:focus {
      outline: none;
      border-color: var(--color-ruby);
      box-shadow: 0 0 0 3px rgba(153, 0, 51, 0.1);
    }

    .form-group textarea {
      min-height: 150px;
      resize: vertical;
    }

    .form-group .help-text {
      font-size: 12px;
      color: #6b7280;
      margin-top: 4px;
    }

    /* Story 18.7 : .source-row*, .source-remove-btn, #article-sources-container:empty +
       #add-source-btn, .image-upload-container/.image-file-input/.image-upload-btn/
       .image-preview-container/.image-preview/.image-remove-btn/.upload-progress/
       .progress-bar*/#progress-text et .form-row n'ont plus de consommateur (éditeur
       Articles réécrit en .v2-editor-*). .checkbox-group reste : Actions et Légal
       l'utilisent encore. */

    .checkbox-group {
      display: flex;
      align-items: center;
      gap: 8px;
    }

    .checkbox-group input[type="checkbox"] {
      width: 18px;
      height: 18px;
      accent-color: var(--color-ruby);
    }

    .checkbox-group label {
      margin: 0;
      font-weight: normal;
    }

    .modal-footer {
      padding: 16px 24px;
      border-top: 1px solid #e5e7eb;
      display: flex;
      justify-content: flex-end;
      gap: 12px;
    }

    .btn-cancel {
      padding: 10px 20px;
      background: white;
      color: var(--text);
      border: 1px solid #d1d5db;
      border-radius: 8px;
      font-size: 14px;
      font-weight: 500;
      cursor: pointer;
    }

    .btn-cancel:hover {
      background: #f3f4f6;
    }

    .btn-save {
      padding: 10px 24px;
      background: var(--color-ruby);
      color: white;
      border: none;
      border-radius: 8px;
      font-size: 14px;
      font-weight: 600;
      cursor: pointer;
    }

    .btn-save:hover {
      background: var(--color-bordeaux);
    }

    .btn-save:disabled {
      background: #9ca3af;
      cursor: not-allowed;
    }

    /* Story 18.7 : .toggle-switch/.toggle-slider/.toggle-cell n'ont plus de consommateur
       (le statut de publication de l'éditeur est maintenant une simple case à cocher
       .v2-editor-checkbox-row, cohérente avec le reste du formulaire). */

    /* Toast notifications : rendu déplacé vers .v2-toast-container / .v2-toast
       (story 18.1, handoff §3.10). showToast() dans admin.js génère désormais
       ces classes ; l'ancien conteneur #toast-container porte la classe
       v2-toast-container (index.html). */


    /* Onglet Produit : voir bloc dédié « STORY 18.4 — PAGE PRODUIT » plus bas
       dans ce fichier (.v2-produit-*) — l'ancien CSS ad hoc (.product-*,
       .trend-badge, .funnel-*, .chart-*) a été retiré avec le markup qui le
       consommait. */

    /* Responsive */
    @media (max-width: 768px) {
      .articles-toolbar {
        flex-direction: column;
        align-items: flex-start;
      }

      /* Story 14.2 : garde-fou — la page ne doit jamais scroller en X
         (les tableaux scrollent dans leurs wrappers .table-scroll) */
      body {
        overflow-x: hidden;
      }

      /* Story 14.2 : barres d'outils/filtres (Utilisateurs, Feedback…) en wrap */
      .users-toolbar {
        flex-wrap: wrap;
      }


      /* Story 14.2 : la ligne « email de test » CGU (input + bouton en flex
         inline) doit pouvoir passer à la ligne sur mobile */
      #legal-content .form-group > div {
        flex-wrap: wrap;
      }
    }

    /* Filter group for feedback tab */
    .filter-group {
      display: inline-flex;
      align-items: center;
      gap: 8px;
      margin-right: 16px;
    }

    .filter-group label {
      font-size: 14px;
      color: var(--dark-text);
    }

    .filter-group select {
      padding: 6px 12px;
      border: 1px solid #ddd;
      border-radius: 6px;
      font-size: 14px;
      background: white;
      cursor: pointer;
    }

    /* Story 14.3 : cellule commentaire Feedback à double rendu — la version
       tronquée (.feedback-comment-short) est celle du tableau desktop (avec
       tooltip title = texte complet, comme avant) ; la version complète
       (.feedback-comment-full) n'est affichée qu'en mode cartes ≤480px. */
    .feedback-comment-full {
      display: none;
    }

    .filter-group select:focus {
      outline: none;
      border-color: var(--color-bordeaux);
    }

    /* Story 18.7 : ancien CSS de la modale "Générer avec l'IA" (.ai-section, .ai-divider,
       .ai-draft-card, .ai-suggestion-chip, .ai-prompt-card…) et de l'éditeur markdown
       (.markdown-toolbar, .md-btn, .content-editor-split, .editor-pane, .preview-pane,
       .markdown-preview…) retiré : plus aucun consommateur (page Articles réécrite en
       .v2-*, voir fin de fichier). */


    /* Story 16-3 : affichage discret de la version de l'admin. Story 18.3:
       ce pied est un frère de #dashboard (hors .v2-page-content, qui a son
       propre padding-bottom) — sans cette marge, la barre flottante (fixed)
       le masquait complètement en bas de page une fois authentifié. */
    .admin-version-footer {
      text-align: center;
      font-size: 0.72rem;
      color: #a8a294;
      padding: 0.75rem 0 1rem;
      padding-bottom: calc(1rem + 80px + env(safe-area-inset-bottom, 0px));
    }

    /* Story 18.6 : ancien CSS Tests (.tests-*, #scenario-lightbox-*) et sa
       variante ≤480px retirés — la page est entièrement reconstruite sur les
       composants .v2-* (voir bloc dédié « STORY 18.6 — TESTS » en fin de
       fichier). */

    @media (max-width: 480px) {
      /* Story 14.2 : layout fluide ≤480px */
      main {
        padding: 0.75rem;
      }

      /* Story 14.2 : modales utilisables ≤480px — largeur quasi pleine,
         hauteur bornée au viewport visible (dvh, fallback 90vh global),
         scroll interne déjà assuré par overflow-y:auto ; le plein écran
         complet de la popup Idées est le périmètre de la story 14.3. */
      .modal-content,
      .modal-container {
        width: calc(100vw - 16px);
        max-height: calc(100dvh - 24px);
      }

      .modal-header {
        padding: 14px 16px;
      }

      .modal-body {
        padding: 16px;
      }

      .modal-footer {
        padding: 12px 16px;
        flex-wrap: wrap;
      }

      /* Story 18.5 : le kanban/détail Idées mobile est reconstruit sur les
         composants .v2-* (voir bloc dédié « STORY 18.5 — IDÉES » en fin de
         fichier) — anciennes règles .ideas-board/.ideas-column/.ideas-card/
         #idea-detail-modal .modal-* retirées (orphelines). */

      /* Story 18.6 : Tests et Feedback (avis/bugs) mobile reconstruits sur les
         composants .v2-* (voir blocs dédiés en fin de fichier) — anciennes
         règles .tests-*/#scenario-lightbox-*/#reviews-table/#bugs-table
         (cartes CSS-only) retirées (orphelines). */
    }

/* ============================================================================
   VINASTIE ADMIN — BIBLIOTHÈQUE DE COMPOSANTS v2-* (story 18.1, lot 0)
   Handoff design §3.1-3.11. Classes préfixées « v2- » : elles coexistent avec
   l'ancien rendu (classes sans préfixe) jusqu'à ce que chaque page bascule
   (stories 18.2 à 18.10). Toutes les couleurs viennent des tokens ci-dessus —
   n'ajoutez jamais de couleur en dur ici.
   Chaque section documente le markup attendu en commentaire.
   ============================================================================ */

/* ----------------------------------------------------------------------------
   3.1 — Carte (.v2-card)
   <div class="v2-card"> … </div>
   <div class="v2-card v2-card--highlight"> … </div>   (fond bordeaux, texte blanc)
   <div class="v2-card v2-card--kanban"> … </div>       (carte compacte de kanban desktop)
   ---------------------------------------------------------------------------- */
.v2-card {
  background: var(--surface-card);
  border-radius: var(--radius-card);
  padding: 12px 14px;
  box-shadow: var(--shadow-card);
}
@media (min-width: 768px) {
  .v2-card {
    padding: 16px 18px;
  }
}
.v2-card--highlight {
  background: var(--surface-highlight);
  color: var(--text-inverse);
}
.v2-card--kanban {
  border-radius: var(--radius-md);
  padding: 10px;
}

/* ----------------------------------------------------------------------------
   3.2 — Carte KPI (.v2-kpi)
   <div class="v2-card v2-kpi">
     <div class="v2-kpi__label">DAU</div>
     <div class="v2-kpi__value">128</div>
     <div class="v2-kpi__trend v2-kpi__trend--up">▲ 12 %</div>
   </div>
   Modificateurs de tendance : v2-kpi__trend--up / --down / --flat (▲ ▼ ▶).
   ---------------------------------------------------------------------------- */
.v2-kpi__label {
  font-size: 12px;
  color: var(--text-tertiary);
}
.v2-card--highlight .v2-kpi__label {
  color: var(--text-inverse-subtle);
}
.v2-kpi__value {
  font-size: 24px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  margin-top: 4px;
}
@media (min-width: 768px) {
  .v2-kpi__value {
    font-size: 30px;
  }
}
.v2-card--highlight .v2-kpi__value {
  font-size: 34px;
}
@media (min-width: 768px) {
  .v2-card--highlight .v2-kpi__value {
    font-size: 32px;
  }
}
.v2-kpi__trend {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  font-size: 11px;
  font-weight: 600;
  margin-top: 6px;
}
@media (min-width: 768px) {
  .v2-kpi__trend {
    font-size: 12px;
  }
}
.v2-kpi__trend--up { color: var(--trend-up); }
.v2-kpi__trend--down { color: var(--trend-down); }
.v2-kpi__trend--flat { color: var(--trend-flat); }
.v2-card--highlight .v2-kpi__trend {
  background: var(--surface-inverse-16);
  border-radius: 7px;
  padding: 3px 7px;
}
@media (min-width: 768px) {
  .v2-card--highlight .v2-kpi__trend {
    border-radius: 8px;
    padding: 4px 8px;
  }
}

/* ----------------------------------------------------------------------------
   3.3 — Puce (chip) de valeur secondaire (.v2-chip)
   <div class="v2-chip-row">
     <span class="v2-chip">Scans <b>128</b></span>
   </div>
   Variante tuile desktop :
   <div class="v2-chip v2-chip--tile">
     <span class="v2-chip__label">Scans</span>
     <b class="v2-chip__value">128</b>
   </div>
   ---------------------------------------------------------------------------- */
.v2-chip-row {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}
.v2-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: var(--chip-bg);
  border-radius: var(--radius-xs);
  padding: 2px 7px;
  font-size: 11px;
  color: var(--text-secondary);
  white-space: nowrap;
}
.v2-chip b {
  color: var(--text);
}
.v2-chip--tile {
  display: block;
  border-radius: 7px;
  padding: 5px 0;
  text-align: center;
}
.v2-chip--tile .v2-chip__value {
  display: block;
  font-size: 14px;
  margin-top: 2px;
}

/* ----------------------------------------------------------------------------
   3.4 — Barre flottante de capture d'idée (story 18.3)
   <div class="v2-floatbar" id="v2-floatbar">                 <!-- + .is-expanded, .is-hidden -->
     <form id="v2-floatbar-form" novalidate>
       <div class="v2-floatbar__fields">                      <!-- masqué hors .is-expanded -->
         <textarea class="v2-floatbar__textarea">…</textarea>
         <div class="v2-floatbar__row">
           <select class="v2-floatbar__select">…</select>
           <select class="v2-floatbar__select">…</select>
         </div>
         <input class="v2-floatbar__input" type="text">
         <p class="v2-floatbar__error" role="alert" hidden>…</p>
       </div>
       <div class="v2-floatbar__bar">
         <input class="v2-floatbar__title-input" placeholder="Noter une idée…">
         <span class="v2-floatbar__kbd">⌘ K</span>              <!-- desktop uniquement -->
         <button type="submit" class="v2-floatbar__submit">+ Ajouter</button>
       </div>
     </form>
   </div>
   Comportement (admin.js, initFloatingBar()) : focus du champ titre → ajoute
   .is-expanded (la feuille pousse vers le haut, bottom fixe) ; Échap ou perte
   de focus hors de la barre avec tous les champs vides → la retire ; ⌘/Ctrl+K
   global expand + focus ; submit → createIdea() (inchangé), reset, focus
   titre, aucun toast, erreur → #v2-floatbar-error. setFloatingBarHidden(bool)
   (story 18.9) bascule .is-hidden.
   ---------------------------------------------------------------------------- */
.v2-floatbar {
  position: fixed;
  left: 12px;
  right: 12px;
  bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  z-index: 30; /* sous le voile (40) et le tiroir (50), au-dessus du contenu */
  background: var(--float-bar-bg);
  border: 1px solid var(--float-bar-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-float);
}
.v2-floatbar.is-hidden {
  display: none;
}
.v2-floatbar__fields {
  display: none;
  flex-direction: column;
  gap: 8px;
  padding: 12px 14px 0;
}
.v2-floatbar.is-expanded .v2-floatbar__fields {
  display: flex;
}
.v2-floatbar__row {
  display: flex;
  gap: 8px;
}
.v2-floatbar__row .v2-floatbar__select {
  flex: 1;
  min-width: 0;
}
.v2-floatbar__textarea,
.v2-floatbar__select,
.v2-floatbar__input {
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--text);
  background: var(--surface-card);
  border: 1px solid var(--border-bordeaux-15);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
}
.v2-floatbar__textarea {
  resize: vertical;
  min-height: 44px;
}
.v2-floatbar__error {
  margin: 0;
  font-size: 12px;
  color: var(--danger-text);
  background: var(--danger-bg);
  border-radius: var(--radius-xs);
  padding: 6px 10px;
}
.v2-floatbar__bar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 8px 8px 16px;
}
.v2-floatbar__title-input {
  flex: 1;
  min-width: 0;
  border: none;
  background: transparent;
  font-family: var(--font-sans);
  font-size: 15px;
  color: var(--text);
}
.v2-floatbar__title-input::placeholder {
  color: var(--placeholder);
}
.v2-floatbar__title-input:focus {
  outline: none;
}
.v2-floatbar__kbd {
  display: none;
  flex: none;
  font-size: 11px;
  color: var(--text-tertiary);
}
.v2-floatbar__submit {
  flex: none;
  min-height: 44px;
  background: var(--float-bar-btn);
  color: var(--text-inverse);
  border: none;
  border-radius: var(--radius-md);
  padding: 10px 16px;
  font-size: 14px;
  font-weight: 600;
  font-family: var(--font-sans);
  cursor: pointer;
}
@media (min-width: 768px) {
  .v2-floatbar {
    /* Centrée sur la zone de contenu (sidebar 240px exclue), pas sur tout le
       viewport : left/right définissent le conteneur de positionnement,
       max-width + margin auto centrent la barre dedans. */
    left: 240px;
    right: 0;
    bottom: 20px;
    max-width: 640px;
    margin: 0 auto;
  }
  .v2-floatbar__bar {
    padding: 8px 8px 8px 18px;
  }
  .v2-floatbar__kbd {
    display: inline;
  }
  .v2-floatbar__submit {
    min-height: auto;
    border-radius: var(--radius-sm);
  }
}

/* ----------------------------------------------------------------------------
   3.5 — Segment (segmented control) (.v2-segment)
   <div class="v2-segment" role="tablist">
     <button type="button" class="v2-segment__option is-active" role="tab" aria-selected="true">Liste</button>
     <button type="button" class="v2-segment__option" role="tab" aria-selected="false">Colonnes</button>
   </div>
   Variantes : v2-segment--on-bordeaux (en-tête mobile bordeaux) ;
   v2-segment--okko (Tests de recette, options data-value="ok"/"ko", 44 px) ;
   v2-segment--sim-real (Administration, option data-value="real" en rouge).
   ---------------------------------------------------------------------------- */
.v2-segment {
  display: flex;
  gap: 2px;
  background: var(--surface-muted);
  border-radius: var(--radius-sm);
  padding: 3px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-secondary);
}
@media (min-width: 768px) {
  .v2-segment {
    font-size: 13px;
  }
}
.v2-segment__option {
  /* Story 18.10 : `flex: 1` seul (= flex-basis 0) fait que dans un conteneur
     dont la largeur n'est pas explicitement contrainte (ex. le slot segment
     de l'en-tête Administration, #v2-page-segment-slot — le seul cas actuel),
     les 3 options se partagent une largeur calculée sans tenir compte de leur
     texte et « Légal (CGU) » se retrouve tronqué/en 2 lignes. `flex-basis:auto`
     garde le même comportement « options à largeur égale » dès que le
     conteneur a une largeur connue (segments existants, tous dans un contexte
     déjà dimensionné), tout en réservant la largeur de leur contenu sinon. */
  flex: 1 1 auto;
  border: none;
  background: transparent;
  border-radius: var(--radius-xs);
  padding: 6px 10px;
  font: inherit;
  color: inherit;
  cursor: pointer;
  white-space: nowrap;
}
.v2-segment__option.is-active {
  background: var(--surface-card);
  /* --color-bordeaux (couleur de marque brute, jamais redéfinie en sombre)
     donne un texte illisible sur --surface-card une fois ce dernier assombri
     (story 18.1) : --section-title est le token déjà prévu pour ce cas
     précis (bordeaux en clair, rose clair #E8A0B4 en sombre, §4.1) — repéré
     en vérifiant le thème sombre de la page Emails (story 18.8), corrigé ici
     car ce composant est partagé par toutes les pages à segment. */
  color: var(--section-title);
  box-shadow: var(--shadow-segment);
}
.v2-segment--on-bordeaux {
  background: var(--surface-inverse-14);
  color: var(--text-inverse-subtle);
}
.v2-segment--on-bordeaux .v2-segment__option.is-active {
  background: var(--text-inverse);
  color: var(--color-bordeaux);
}
.v2-segment--okko .v2-segment__option {
  min-height: 44px;
}
.v2-segment--okko .v2-segment__option[data-value="ok"].is-active {
  background: var(--success);
  color: var(--text-inverse);
}
.v2-segment--okko .v2-segment__option[data-value="ko"].is-active {
  background: var(--danger);
  color: var(--text-inverse);
}
.v2-segment--sim-real .v2-segment__option[data-value="real"].is-active {
  background: var(--danger);
  color: var(--text-inverse);
}

/* ----------------------------------------------------------------------------
   3.6 — Puces de filtre (pills) (.v2-pill)
   <div class="v2-pill-row">
     <button type="button" class="v2-pill is-active">Inbox 12</button>
     <button type="button" class="v2-pill">Triées 8</button>
   </div>
   Rangée scrollable horizontalement, scrollbar masquée.
   ---------------------------------------------------------------------------- */
.v2-pill-row {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  white-space: nowrap;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.v2-pill-row::-webkit-scrollbar {
  display: none;
}
.v2-pill {
  display: inline-flex;
  flex: none;
  border-radius: var(--radius-pill);
  padding: 6px 12px;
  font-size: 12px;
  font-weight: 600;
  background: var(--surface-card);
  border: 1px solid var(--border-bordeaux-20);
  /* Story 18.5 : --section-title (pas --color-bordeaux, fixe) — ce composant
     (défini en 18.1) n'avait pas encore de consommateur réel ; le bug de
     contraste sombre (même famille que le titre de page corrigé en 18.2)
     n'était donc pas visible avant les pills de statut Idées. */
  color: var(--section-title);
  cursor: pointer;
}
@media (min-width: 768px) {
  .v2-pill {
    padding: 7px 12px;
  }
}
.v2-pill.is-active {
  background: var(--color-ruby);
  border-color: var(--color-ruby);
  color: var(--text-inverse);
}

/* ----------------------------------------------------------------------------
   3.7 — Boutons (.v2-btn)
   <button type="button" class="v2-btn v2-btn--primary">Enregistrer</button>
   <button type="button" class="v2-btn v2-btn--secondary">Annuler</button>
   <button type="button" class="v2-btn v2-btn--danger">Supprimer</button>       (lien texte)
   <button type="button" class="v2-btn v2-btn--danger-solid">Supprimer</button> (plein, modale de confirmation)
   <button type="button" class="v2-btn v2-btn--success">Envoyer</button>
   <button type="button" class="v2-btn v2-btn--icon" aria-label="Menu">≡</button> (36×36, en-tête bordeaux)
   Cibles ≥ 44 px sur mobile (hors variante icône, déjà 36×36 dans l'en-tête compact).
   ---------------------------------------------------------------------------- */
.v2-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border: none;
  cursor: pointer;
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 13px;
  border-radius: var(--radius-sm);
  padding: 7px 12px;
}
@media (min-width: 768px) {
  .v2-btn {
    font-size: 14px;
  }
}
@media (max-width: 767px) {
  .v2-btn {
    border-radius: var(--radius-md);
    padding: 10px 14px;
    min-height: 44px;
    font-size: 14px;
  }
}
.v2-btn:disabled {
  opacity: .6;
  cursor: not-allowed;
}
.v2-btn--primary {
  background: var(--color-ruby);
  color: var(--text-inverse);
}
.v2-btn--secondary {
  background: var(--surface-card);
  border: 1px solid var(--border-bordeaux-25);
  /* --color-bordeaux non lisible sur --surface-card assombri : même correctif
     que .v2-segment__option.is-active ci-dessus (repéré ici lors de la
     vérification thème sombre de la story 18.8, composant partagé). */
  color: var(--section-title);
}
.v2-btn--danger {
  background: transparent;
  color: var(--danger);
}
.v2-btn--danger-solid {
  background: var(--danger);
  color: var(--text-inverse);
}
.v2-btn--success {
  background: var(--success);
  color: var(--text-inverse);
}
.v2-btn--icon {
  width: 36px;
  height: 36px;
  min-height: 0;
  border-radius: 10px;
  border: 1px solid var(--border-inverse-25);
  background: transparent;
  color: var(--text-inverse);
  padding: 0;
  font-size: 16px;
}

/* ----------------------------------------------------------------------------
   3.8 — Badges de statut (.v2-badge)
   <span class="v2-badge v2-badge--neutral">Actif</span>
   <span class="v2-badge v2-badge--info">Plus</span>
   <span class="v2-badge v2-badge--amber">Premium</span>
   <span class="v2-badge v2-badge--success">Testé</span>
   <span class="v2-badge v2-badge--error">Bug</span>
   <span class="v2-badge v2-badge--beige">iOS</span>   (métadonnée : plateforme, zone, tags, score)
   ---------------------------------------------------------------------------- */
.v2-badge {
  display: inline-block;
  border-radius: var(--radius-xs);
  padding: 2px 7px;
  font-size: 11px;
  font-weight: 600;
  line-height: 1.5;
}
.v2-badge--neutral { background: var(--surface-muted-2); color: var(--text-secondary); }
.v2-badge--info { background: var(--info-bg); color: var(--info-text); }
.v2-badge--amber { background: var(--amber-bg); color: var(--amber-text); }
.v2-badge--success { background: var(--success-bg); color: var(--success); }
.v2-badge--error { background: var(--danger-bg); color: var(--danger-text); }
.v2-badge--beige { background: var(--chip-bg); color: var(--text-secondary); }

/* ----------------------------------------------------------------------------
   3.9 — Modales (.v2-modal)
   Générique (desktop centrée / mobile plein écran) :
   <div class="v2-modal-overlay">
     <div class="v2-modal" role="dialog" aria-modal="true" aria-labelledby="…">
       <div class="v2-modal__header">
         <button type="button" class="v2-modal__header-back">‹</button>   (ou "Annuler")
         <h2 class="v2-modal__title" id="…">Titre</h2>
         <button type="button" class="v2-modal__header-action v2-modal__header-action--save">Enregistrer</button>
       </div>
       <div class="v2-modal__body">…</div>
       <div class="v2-modal__footer">
         <button type="button" class="v2-btn v2-btn--secondary">Annuler</button>
         <button type="button" class="v2-btn v2-btn--primary">Enregistrer</button>
       </div>
     </div>
   </div>
   Largeurs desktop : v2-modal--idea-detail (820), v2-modal--campaign (760),
   v2-modal--bulk (520), v2-modal--preview (700), v2-modal--group (640, story 18.8).
   Confirmation (utilisée par confirmDialog(), voir admin.js) — feuille en bas
   sur mobile :
   <div class="v2-modal-overlay">
     <div class="v2-modal v2-modal--confirm" role="dialog" aria-modal="true">
       <h3 class="v2-modal__title">Titre</h3>
       <p class="v2-modal__text">Texte…</p>
       <div class="v2-modal__actions">
         <button type="button" class="v2-btn v2-btn--secondary">Annuler</button>
         <button type="button" class="v2-btn v2-btn--danger-solid">Supprimer</button>
       </div>
     </div>
   </div>
   ---------------------------------------------------------------------------- */
.v2-modal-overlay {
  position: fixed;
  inset: 0;
  background: var(--overlay);
  z-index: 3000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
}
.v2-modal {
  background: var(--bg);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-modal);
  width: 100%;
  max-width: 480px;
  max-height: calc(100dvh - 32px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.v2-modal--idea-detail { max-width: 820px; }
.v2-modal--campaign { max-width: 760px; }
.v2-modal--bulk { max-width: 520px; }
.v2-modal--preview { max-width: 700px; }
/* Story 18.5 : hauteur fixe 600px en desktop (handoff §5.1 « modale 820×600 »)
   — donne au volet droit (fil) une hauteur définie pour son propre scroll
   interne, sans quoi la grille 2 colonnes grandirait avec le contenu. */
@media (min-width: 768px) {
  .v2-modal--idea-detail { height: 600px; }
}

.v2-modal--group { max-width: 640px; }

.v2-modal__header,
.v2-modal__footer {
  background: var(--surface-card);
  padding: 14px 16px 12px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex: none;
}
.v2-modal__header { border-bottom: 1px solid var(--divider); }
.v2-modal__footer { border-top: 1px solid var(--divider); padding: 12px 16px; }
.v2-modal__title {
  font-size: 17px;
  font-weight: 600;
  /* Idem .v2-btn--secondary : --color-bordeaux illisible sur le fond de
     modale assombri en thème sombre. */
  color: var(--section-title);
  margin: 0;
}
.v2-modal__body {
  padding: 16px;
  overflow-y: auto;
  flex: 1;
  color: var(--text);
}
.v2-modal__close,
.v2-modal__header-back {
  border: none;
  background: transparent;
  font-size: 20px;
  line-height: 1;
  color: var(--text-tertiary);
  cursor: pointer;
  padding: 4px 8px 4px 0;
}
.v2-modal__header-action {
  border: none;
  background: none;
  font-size: 15px;
  font-weight: 600;
  color: var(--color-bordeaux);
  cursor: pointer;
  padding: 4px 2px;
}
.v2-modal__header-action--save { color: var(--color-ruby); font-weight: 700; }

/* Confirmation */
.v2-modal--confirm {
  max-width: 400px;
  padding: 20px;
}
.v2-modal--confirm .v2-modal__title { padding: 0; }
.v2-modal--confirm .v2-modal__text {
  font-size: 13px;
  color: var(--text-secondary);
  margin: 10px 0 16px;
}
.v2-modal--confirm .v2-modal__actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}

@media (max-width: 767px) {
  .v2-modal-overlay {
    padding: 0;
    align-items: stretch;
  }
  .v2-modal:not(.v2-modal--confirm) {
    max-width: none;
    width: 100%;
    height: 100dvh;
    max-height: none;
    border-radius: 0;
  }
  .v2-modal--confirm {
    align-self: flex-end;
    width: 100%;
    max-width: none;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    margin-top: auto;
  }
}

/* ----------------------------------------------------------------------------
   3.10 — Retours d'action
   Toast (au-dessus de la barre flottante — bottom réservé via --toast-bottom,
   ajusté par 18.3) :
   <div id="toast-container" class="v2-toast-container">
     <div class="v2-toast v2-toast--success">Enregistré ✓</div>
   </div>
   Le conteneur est déjà présent dans index.html (#toast-container) ; showToast()
   dans admin.js y ajoute/retire les .v2-toast.

   Bloc résultat inline unifié :
   <div class="v2-result v2-result--success">
     <div class="v2-result__header">
       <span class="v2-result__title">Simulation terminée</span>
       <span class="v2-result__timestamp">06/09 · 4 min 12 s</span>
     </div>
     <div class="v2-result__stats">
       <div class="v2-result__stat"><b>128</b><span>analysés</span></div>
     </div>
   </div>
   (généré par renderResultBlock(), voir admin.js)

   Alerte ambre :
   <div class="v2-alert">⚠️ 3 jours sans données</div>
   ---------------------------------------------------------------------------- */
.v2-toast-container {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: var(--toast-bottom);
  z-index: 4000;
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: calc(100% - 32px);
  max-width: 420px;
  pointer-events: none;
}
.v2-toast {
  pointer-events: auto;
  border-radius: var(--radius-md);
  padding: 12px 16px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-inverse);
  box-shadow: var(--shadow-modal);
  text-align: center;
  animation: v2-toast-in .2s ease;
}
.v2-toast--info { background: var(--info); }
.v2-toast--success { background: var(--success); }
.v2-toast--error { background: var(--danger); }
@keyframes v2-toast-in {
  from { transform: translateY(8px); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}

.v2-result {
  border-radius: var(--radius-sm);
  padding: 10px 12px;
}
.v2-result--success { background: var(--success-bg); color: var(--success); }
.v2-result--error { background: var(--danger-bg); color: var(--danger-text); }
.v2-result__header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  font-weight: 600;
  font-size: 13px;
}
.v2-result__timestamp {
  font-size: 11px;
  font-weight: 400;
  opacity: .8;
  white-space: nowrap;
}
.v2-result__stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(72px, 1fr));
  gap: 6px;
  margin-top: 8px;
}
.v2-result__stat {
  background: var(--bg);
  border-radius: var(--radius-tile);
  padding: 8px 4px;
  text-align: center;
}
.v2-result__stat b {
  display: block;
  font-size: 18px;
  font-weight: 600;
  color: var(--text);
}
.v2-result__stat span {
  display: block;
  font-size: 11px;
  color: var(--text-tertiary);
  margin-top: 2px;
}
/* Story 18.10 : tuile « à modifier »/« modifiés » du recalcul vintage &
   apogée, seule tuile du handoff §8 qui prévoit une couleur différenciée. */
.v2-result__stat--ruby b {
  color: var(--color-ruby);
}

/* Story 18.9 : tons colorés par tuile (stat.tone = 'success'|'amber'|'error',
   voir renderResultBlock() dans admin.js — jusqu'ici accepté mais ignoré) +
   variante grande taille (résultat du lot Premium, handoff §7 « 3 tuiles…
   26 px »). Rétro-compatible : les appelants existants (tests emails, recalcul,
   CGU) ne passent pas `tone`/`large` et gardent le rendu neutre d'origine. */
.v2-result__stat--success { background: var(--success-bg); }
.v2-result__stat--amber { background: var(--amber-bg); }
.v2-result__stat--error { background: var(--danger-bg); }
.v2-result__stat--success b, .v2-result__stat--success span { color: var(--success); }
.v2-result__stat--amber b, .v2-result__stat--amber span { color: var(--amber-text); }
.v2-result__stat--error b, .v2-result__stat--error span { color: var(--danger-text); }
.v2-result--tiles-lg .v2-result__stat { padding: 14px 8px; }
.v2-result--tiles-lg .v2-result__stat b { font-size: 26px; }

.v2-alert {
  background: var(--amber-bg);
  color: var(--amber-text);
  border-radius: var(--radius-sm);
  padding: 9px 12px;
  font-size: 12px;
}

/* ----------------------------------------------------------------------------
   Squelettes de chargement (.v2-skeleton)
   <div class="v2-skeleton-group">
     <div class="v2-skeleton v2-skeleton--title"></div>
     <div class="v2-skeleton v2-skeleton--text"></div>
     <div class="v2-skeleton v2-skeleton--block"></div>
   </div>
   (généré par renderSkeleton(container, { rows, height }), voir admin.js)
   ---------------------------------------------------------------------------- */
.v2-skeleton-group {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.v2-skeleton {
  background: linear-gradient(90deg, var(--skeleton) 25%, var(--skeleton-alt) 37%, var(--skeleton) 63%);
  background-size: 400% 100%;
  animation: v2-skeleton-shimmer 1.4s ease infinite;
  border-radius: var(--radius-xs);
}
.v2-skeleton--text { height: 12px; }
.v2-skeleton--title { height: 18px; width: 60%; }
.v2-skeleton--block { height: 64px; border-radius: var(--radius-card); }
@keyframes v2-skeleton-shimmer {
  0% { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}
@media (prefers-reduced-motion: reduce) {
  .v2-skeleton { animation: none; }
}

/* ----------------------------------------------------------------------------
   3.11 — Tableau desktop uniquement (.v2-table)
   <div class="v2-table-wrap">
     <table class="v2-table">
       <thead><tr><th>Date <span class="v2-table__sort-arrow">↓</span></th>…</tr></thead>
       <tbody><tr class="is-selected">…</tr></tbody>
     </table>
     <div class="v2-table__footer">
       <span>Page 1 / 4</span>
       <button type="button" class="v2-btn v2-btn--secondary">Suivant ›</button>
     </div>
   </div>
   Sur mobile, tout tableau devient une liste de cartes (.v2-card) — ce composant
   se masque lui-même sous 768 px ; à chaque page d'utiliser .v2-card pour la
   liste mobile équivalente.
   ---------------------------------------------------------------------------- */
.v2-table-wrap {
  background: var(--surface-card);
  border-radius: var(--radius-card);
  overflow: hidden;
  box-shadow: var(--shadow-card);
}
.v2-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}
.v2-table thead th {
  background: var(--header-bg);
  color: var(--text-inverse);
  font-size: 11px;
  font-weight: 600;
  text-align: left;
  padding: 10px 16px;
  white-space: nowrap;
}
.v2-table tbody td {
  padding: 12px 16px;
  border-bottom: 1px solid var(--divider-table);
}
.v2-table tbody tr:hover,
.v2-table tbody tr.is-selected {
  background: var(--chip-bg);
}
.v2-table__sort-arrow {
  font-size: 10px;
  margin-left: 4px;
}
.v2-table__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 16px;
  font-size: 12px;
  color: var(--text-tertiary);
}
@media (max-width: 767px) {
  .v2-table-wrap {
    display: none;
  }
}

/* ============================================================================
   STORY 18.10 — ADMINISTRATION : ACTIONS, LÉGAL, AUDIT. Handoff §8, inventaire
   §5/§10/§11. Grille 2 colonnes de cartes (desktop) → 1 colonne (mobile) pour
   les pages Actions et Légal ; Audit a son propre gabarit, voir plus bas.
   ---------------------------------------------------------------------------- */
.v2-admin-grid {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
@media (min-width: 768px) {
  .v2-admin-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    align-items: start;
  }
}

.v2-admin-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  font-size: 13px;
}
.v2-admin-card__title {
  font-size: 15px;
  font-weight: 600;
  color: var(--section-title);
}
.v2-admin-card__header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
}
.v2-admin-card__hint {
  color: var(--text-tertiary);
  font-size: 12px;
}
.v2-admin-card__hint b {
  color: var(--text);
}
.v2-admin-card__desc {
  color: var(--text-tertiary);
  line-height: 1.45;
}
.v2-admin-card__desc b {
  color: var(--text);
  font-weight: 600;
}
.v2-admin-card__row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.v2-admin-card__row--wrap {
  flex-wrap: wrap;
}
.v2-admin-card__row--divider {
  border-top: 1px solid var(--divider);
  padding-top: 12px;
}
.v2-admin-card__cta {
  margin-left: auto;
  white-space: nowrap;
}
.v2-admin-card__note {
  font-size: 12px;
  margin-top: -4px;
}
.v2-admin-card__note--success {
  color: var(--success);
}
.v2-admin-card__note--error {
  color: var(--danger-text);
}

/* --- Champs de formulaire (Notification CGU, Nouvelle/Modifier version) --- */
.v2-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 13px;
  margin-bottom: 12px;
}
.v2-field:last-child {
  margin-bottom: 0;
}
.v2-field__label {
  font-size: 11px;
  color: var(--text-tertiary);
}
.v2-field__control {
  border: 1px solid var(--border-bordeaux-15);
  border-radius: var(--radius-sm);
  padding: 8px 11px;
  font: inherit;
  font-family: var(--font-sans);
  background: var(--surface-card);
  color: var(--text);
  width: 100%;
}
.v2-field__control:focus {
  outline: none;
  border-color: var(--border-bordeaux-35);
}
textarea.v2-field__control {
  resize: vertical;
}
.v2-field-row {
  display: flex;
  flex-direction: column;
  gap: 0;
}
@media (min-width: 480px) {
  .v2-field-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0 10px;
  }
}
.v2-form-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 4px;
}

/* --- Historique des campagnes CGU (carte-tableau compact, §8) --- */
.v2-cgu-history__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
}
.v2-cgu-history__versions-link {
  border: none;
  background: none;
  color: var(--color-ruby);
  font-weight: 600;
  font-size: 13px;
  cursor: pointer;
  font-family: var(--font-sans);
  padding: 0;
}
.v2-cgu-history__list {
  display: flex;
  flex-direction: column;
}
.v2-cgu-history__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 10px;
  padding: 9px 0;
  border-top: 1px solid var(--divider-table);
  font-size: 12px;
}
.v2-cgu-history__row:first-child {
  border-top: none;
}
.v2-cgu-history__date {
  color: var(--text-tertiary);
}
.v2-cgu-history__version {
  font-weight: 600;
}
.v2-cgu-history__stats {
  margin-left: auto;
  white-space: nowrap;
  color: var(--text);
}
.v2-cgu-history__failed {
  color: var(--danger);
  font-weight: 600;
}
.v2-cgu-history__empty {
  color: var(--text-tertiary);
  font-style: italic;
  padding: 8px 0;
}

/* --- Versions CGU : liste dans la modale (#tos-versions-modal) --- */
.v2-version-row {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 12px 0;
  border-top: 1px solid var(--divider);
  font-size: 13px;
}
.v2-version-row:first-child {
  border-top: none;
  padding-top: 0;
}
.v2-version-row__main {
  display: flex;
  align-items: baseline;
  gap: 8px;
  justify-content: space-between;
}
.v2-version-row__title {
  font-weight: 600;
  color: var(--text);
}
.v2-version-row__date {
  color: var(--text-tertiary);
  font-size: 12px;
  white-space: nowrap;
}
.v2-version-row__link {
  color: var(--color-ruby);
  font-size: 12px;
  font-weight: 600;
  text-decoration: none;
}
.v2-version-row__notes {
  color: var(--text-tertiary);
  font-size: 12px;
  line-height: 1.4;
}
.v2-version-row__actions {
  display: flex;
  justify-content: flex-end;
}

/* --- Audit : puces de filtre (mobile uniquement, handoff §8) --- */
.v2-audit-pills {
  margin-bottom: 12px;
}
@media (min-width: 768px) {
  .v2-audit-pills {
    display: none;
  }
}

/* --- Audit : journal mobile en cartes groupées par jour (masqué desktop,
   la table .v2-table prend le relais — voir §3.11) --- */
.v2-audit-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
@media (min-width: 768px) {
  .v2-audit-list {
    display: none;
  }
}
.v2-audit-list__day {
  font-size: 11px;
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: .06em;
  padding: 10px 2px 2px;
}
.v2-audit-list__day:first-child {
  padding-top: 4px;
}
.v2-audit-list__empty {
  color: var(--text-tertiary);
  font-style: italic;
  padding: 12px 2px;
}
.v2-audit-list__footnote {
  font-size: 12px;
  color: var(--text-tertiary);
  text-align: center;
  padding: 10px 2px 4px;
}
.v2-audit-row {
  display: flex;
  gap: 12px;
  align-items: flex-start;
}
.v2-audit-row__dot {
  width: 8px;
  height: 8px;
  border-radius: 4px;
  margin-top: 6px;
  flex: none;
}
.v2-audit-row__dot--grant { background: var(--success); }
.v2-audit-row__dot--extend { background: var(--amber); }
.v2-audit-row__dot--revoke { background: var(--danger); }
.v2-audit-row__dot--admin { background: var(--color-bordeaux); }
.v2-audit-row__body {
  flex: 1;
  min-width: 0;
}
.v2-audit-row__action {
  font-weight: 600;
}
.v2-audit-row__target {
  font-size: 12px;
  color: var(--text-tertiary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.v2-audit-row__time {
  font-size: 12px;
  color: var(--text-tertiary);
  white-space: nowrap;
  flex: none;
}

/* ============================================================================
   STORY 18.2 — COQUILLE : tiroir/sidebar de navigation, en-tête de page,
   voile de tiroir. Handoff §2. Mobile ≤767px : tiroir en overlay + en-tête
   bordeaux compact (§2.2/§2.3). Desktop ≥768px : sidebar fixe 240px + en-tête
   de page (§2.4) ; pas de repli de sidebar (D5).
   `#dashboard` passe en `display:flex` via showSection() (admin.js) une fois
   authentifié : il devient le conteneur flex [voile (fixed, hors flux), nav,
   .v2-main].
   ============================================================================ */

.v2-drawer-overlay {
  position: fixed;
  inset: 0;
  background: var(--overlay);
  z-index: 40;
  opacity: 0;
  pointer-events: none;
  transition: opacity .2s ease;
}
.v2-drawer-overlay.is-open {
  opacity: 1;
  pointer-events: auto;
}
@media (min-width: 768px) {
  .v2-drawer-overlay {
    display: none;
  }
}

/* --- Tiroir (mobile) / Sidebar (desktop) — handoff §2.3 -------------------
   L'ombre n'est posée que lorsque le tiroir est ouvert (.is-open) : sinon,
   translateX(-100%) place son bord droit exactement sur x=0, et le flou de
   l'ombre (40px) déborderait quand même sur la gauche de l'écran alors que
   le tiroir est censé être invisible. */
.v2-nav {
  display: flex;
  flex-direction: column;
  background: var(--surface-card);
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  width: 300px;
  max-width: 86vw;
  z-index: 50;
  transform: translateX(-100%);
  transition: transform .22s ease;
  overflow-y: auto;
}
.v2-nav.is-open {
  transform: translateX(0);
  box-shadow: var(--shadow-drawer);
}

.v2-nav__header {
  background: var(--header-bg);
  padding: 52px 20px 18px;
  display: flex;
  align-items: center;
  gap: 12px;
  flex: none;
}
.v2-nav__logo {
  width: 40px;
  height: 40px;
  border-radius: var(--radius-sm);
  flex: none;
  object-fit: cover;
}
.v2-nav__brand-title {
  color: var(--text-inverse);
  font-size: 16px;
  font-weight: 600;
}
.v2-nav__brand-sub {
  color: var(--text-inverse-subtle);
  font-size: 12px;
  margin-top: 2px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.v2-nav__groups {
  flex: 1;
  padding: 12px 0;
}
.v2-nav__group {
  padding: 10px 0;
}
.v2-nav__group-label {
  padding: 4px 20px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--color-ruby);
}
.v2-nav__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: calc(100% - 16px);
  margin: 2px 8px;
  padding: 10px 12px;
  border-radius: var(--radius-md);
  font-size: 14px;
  color: var(--text);
  background: transparent;
  border: none;
  text-align: left;
  cursor: pointer;
  font-family: var(--font-sans);
  min-height: 44px;
}
.v2-nav__item:hover {
  background: var(--surface-muted);
}
.v2-nav__item.is-active {
  background: var(--chip-bg);
  color: var(--color-bordeaux);
  font-weight: 600;
}
.v2-nav__item-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.v2-nav__badge {
  flex: none;
  background: var(--color-ruby);
  color: var(--text-inverse);
  border-radius: var(--radius-pill);
  padding: 1px 8px;
  font-size: 12px;
  font-weight: 600;
}
.v2-nav__badge--dot {
  padding: 0;
  width: 8px;
  height: 8px;
  min-width: 0;
  border-radius: 50%;
}

.v2-nav__footer {
  flex: none;
  border-top: 1px solid var(--divider);
  padding: 14px 20px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.v2-nav__email {
  font-size: 12px;
  color: var(--text-tertiary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.v2-nav__logout {
  background: none;
  border: none;
  color: var(--color-ruby);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  padding: 4px;
  flex: none;
}

@media (min-width: 768px) {
  .v2-nav {
    position: sticky;
    top: 0;
    left: auto;
    height: 100vh;
    width: 240px;
    max-width: none;
    transform: none;
    box-shadow: none;
    flex: none;
    background: var(--header-bg);
  }
  .v2-nav__header {
    padding: 22px 20px 18px;
    border-bottom: 1px solid var(--border-inverse-12);
  }
  .v2-nav__logo {
    width: 34px;
    height: 34px;
  }
  .v2-nav__brand-sub {
    display: none;
  }
  .v2-nav__group-label {
    color: var(--text-inverse-faint);
  }
  .v2-nav__item {
    color: var(--text-inverse-muted);
  }
  .v2-nav__item:hover {
    background: var(--surface-inverse-14);
  }
  .v2-nav__item.is-active {
    background: var(--surface-inverse-14);
    color: var(--text-inverse);
    font-weight: 600;
  }
  .v2-nav__footer {
    border-top-color: var(--border-inverse-12);
  }
  .v2-nav__email {
    color: var(--text-inverse-faint);
  }
  .v2-nav__logout {
    color: var(--text-inverse-muted);
  }
}

/* --- Zone principale : en-tête de page + contenu -------------------------- */
.v2-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

/* En-tête : mobile = bandeau bordeaux compact (§2.2) ; desktop = en-tête de
   page claire (§2.4). Un seul élément, habillage responsive en CSS — rempli
   par admin.js (renderPageHeader()) à chaque navigation. */
.v2-page-header {
  flex: none;
  /* Story 18.3 (correctif coquille) : le document défile en entier (body),
     .v2-page-content n'a pas son propre scroll interne — sans position:sticky,
     l'en-tête défilait avec le reste du contenu au lieu de rester collé en
     haut de l'écran mobile (§2.2). z-index sous le voile (40) et le tiroir
     (50) : il doit rester visible mais jamais au-dessus d'eux. */
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--header-bg);
  color: var(--text-inverse);
  padding: 14px 18px 12px;
  padding-top: calc(14px + env(safe-area-inset-top, 0px));
  display: flex;
  align-items: center;
  gap: 10px;
}
.v2-page-header__title-block {
  flex: 1;
  min-width: 0;
}
.v2-page-header__eyebrow {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .06em;
  opacity: .7;
  margin-bottom: 1px;
  /* Story 18.10 : sans ça, sur mobile avec le segment Administration (large :
     3 options) dans la zone droite, le texte de l'eyebrow (ex. « ADMINISTRATION »)
     déborde de son bloc rétréci et se retrouve peint sous le segment plutôt que
     tronqué proprement — même correctif que le titre juste en dessous. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.v2-page-header__title-row {
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
}
.v2-page-header__title {
  font-size: 18px;
  font-weight: 600;
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.v2-page-header__suffix {
  font-size: 13px;
  opacity: .7;
  white-space: nowrap;
}
.v2-page-header__right {
  flex: none;
  display: flex;
  align-items: center;
  gap: 8px;
}
.v2-page-header__segment-slot:empty {
  display: none;
}
/* Story 18.10 : le slot segment (Administration, seul consommateur de cet id
   — voir renderPageHeaderChrome) est posé en JS avec la classe de base
   .v2-segment, pensée pour un fond clair (§3.5). Sur mobile, l'en-tête est
   pleine bordeaux (.v2-page-header, règle de base plus haut) : sans la
   variante --on-bordeaux (scopée ici au mobile, le seul cas concerné —
   l'en-tête redevient clair dès 768px, règle plus bas), le segment rendait
   dans son fond clair habituel, illisible sur bordeaux et débordant sur
   l'eyebrow faute de place. */
@media (max-width: 767px) {
  .v2-page-header__segment-slot.v2-segment--on-bordeaux {
    background: var(--surface-inverse-14);
    color: var(--text-inverse-subtle);
  }
  .v2-page-header__segment-slot.v2-segment--on-bordeaux .v2-segment__option.is-active {
    background: var(--text-inverse);
    color: var(--color-bordeaux);
  }
}
.v2-page-header__freshness {
  font-size: 11px;
  opacity: .8;
  text-align: right;
  line-height: 1.3;
  max-width: 90px;
}
.v2-page-header__freshness:empty {
  display: none;
}

.v2-icon-btn {
  width: 36px;
  height: 36px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-inverse-25);
  background: transparent;
  color: var(--text-inverse);
  font-size: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex: none;
  font-family: var(--font-sans);
}
.v2-icon-btn:hover {
  background: var(--surface-inverse-14);
}

@media (min-width: 768px) {
  .v2-drawer-toggle {
    display: none;
  }
  .v2-page-header {
    background: transparent;
    color: var(--text);
    align-items: flex-end;
    justify-content: space-between;
    padding: 22px 28px 18px;
    padding-top: 22px;
  }
  .v2-page-header__eyebrow {
    color: var(--color-ruby);
    opacity: 1;
  }
  .v2-page-header__title {
    font-size: 26px;
    /* --section-title (pas --color-bordeaux, fixe) : a un pendant sombre
       (§4.1) — sans ça le titre devient quasi illisible en thème sombre. */
    color: var(--section-title);
    white-space: normal;
  }
  .v2-page-header__suffix {
    color: var(--text-tertiary);
    opacity: 1;
  }
  .v2-page-header__freshness {
    color: var(--text-tertiary);
    opacity: 1;
    max-width: none;
  }
  .v2-icon-btn {
    border-color: var(--border-bordeaux-25);
    /* --text (pas --color-bordeaux, fixe) : a un pendant sombre — sinon
       l'icône devient quasi invisible sur fond sombre en desktop. */
    color: var(--text);
  }
  .v2-icon-btn:hover {
    background: var(--surface-muted);
  }
}

@media (max-width: 767px) {
  /* Sur mobile, une page peut remplacer la zone droite par un segment ou un
     bouton blanc (§2.2) : quand elle le fait, la mention « à jour » et le
     bouton ↻ génériques s'effacent pour laisser la place. */
  .v2-page-header__right.has-custom-slot .v2-page-header__freshness,
  .v2-page-header__right.has-custom-slot .v2-page-header__refresh {
    display: none;
  }
}

.v2-page-content {
  flex: 1;
  min-width: 0;
  padding: 16px;
  /* Story 18.3 (handoff §3.4) : dégage la place de la barre flottante, fixe
     en bas de l'écran, pour qu'elle ne masque jamais le dernier élément de
     la page. */
  padding-bottom: calc(80px + env(safe-area-inset-bottom, 0px));
}
@media (min-width: 768px) {
  .v2-page-content {
    padding: 22px 28px;
    padding-bottom: 80px;
    max-width: 1200px;
  }
}

/* Note affichée sous les squelettes de chargement de l'Accueil (§9 « chargement ») :
   rappelle que la capture d'idée reste utilisable pendant le chargement des métriques. */
.v2-loading-note {
  margin-top: 12px;
  font-size: 13px;
  color: var(--text-tertiary);
  text-align: center;
}

/* ============================================================================
   STORY 18.3 — PAGE ACCUEIL (handoff §4.1)
   Deux sections (Vitaux / Cumul), chacune avec un titre 13px/600 uppercase
   (.v2-accueil-section__title). Vitaux réutilise la carte KPI de 18.1
   (.v2-card.v2-kpi) avec 2 variantes additives introduites ici :
   - .v2-kpi__row : valeur + tendance côte à côte (au lieu de l'empilement
     vertical d'origine) — WAU/MAU/Nouveaux comptes/Abonnés payants.
   - .v2-kpi--split : carte highlight pleine largeur, libellé+valeur à gauche
     / tendance à droite (DAU). Ces 2 variantes sont additives : elles ne
     changent rien pour un futur consommateur de .v2-kpi qui ne les utilise
     pas (ex. Produit, story 18.4, qui migrera ses propres cartes KPI).
   Cumul : mobile = carte-liste (.v2-cumul-list, une ligne par famille) ;
   desktop = grille 3 colonnes (.v2-cumul-grid) — les 2 blocs coexistent dans
   le DOM, la bascule est purement en CSS (media query), remplis ensemble par
   renderAccueilCumul() (admin.js). Familles Utilisateurs/Recommandations/Vins
   processés : total + 4 tuiles (réutilise .v2-chip--tile en grille desktop).
   Engagement/Web : 3 valeurs. Abonnements : fusion des 2 familles « Abonnements
   actifs » et « Répartition Abonnements » de l'inventaire §3.1 sur un seul
   visuel (bar + puces), cf. commentaire dans index.html.
   ============================================================================ */

.v2-accueil-section {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 18px;
}
.v2-accueil-section:last-child {
  margin-bottom: 0;
}
.v2-accueil-section__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
}
.v2-accueil-section__title {
  margin: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--section-title);
  text-transform: uppercase;
  letter-spacing: .04em;
}
.v2-accueil-section__hint {
  font-size: 11px;
  color: var(--text-tertiary);
}

/* --- Vitaux : variantes additives de .v2-kpi (voir bannière ci-dessus) --- */
.v2-kpi__row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
}
.v2-kpi__row .v2-kpi__value,
.v2-kpi__row .v2-kpi__trend {
  margin-top: 0;
}
.v2-kpi--split {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 12px;
}
.v2-kpi--split .v2-kpi__trend {
  margin-top: 0;
}

.v2-vitals-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 8px;
}
.v2-vitals-grid__highlight {
  grid-column: 1 / -1;
}
@media (min-width: 768px) {
  .v2-vitals-grid {
    grid-template-columns: repeat(3, 1fr);
    gap: 12px;
  }
  .v2-vitals-grid__highlight {
    grid-column: auto;
  }
}
@media (min-width: 1100px) {
  .v2-vitals-grid {
    grid-template-columns: repeat(5, 1fr);
  }
}

/* --- Cumul : mobile carte-liste --- */
.v2-cumul-list {
  background: var(--surface-card);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}
.v2-cumul-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 2px 12px;
  align-items: center;
  padding: 12px 14px;
  border-bottom: 1px solid var(--divider-table);
}
.v2-cumul-row:last-child {
  border-bottom: none;
}
.v2-cumul-row__name {
  font-size: 13px;
  font-weight: 600;
}
.v2-cumul-row__total {
  font-size: 20px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: right;
}
.v2-cumul-row__total-suffix {
  font-size: 11px;
  font-weight: 400;
  color: var(--text-tertiary);
}
.v2-cumul-row__chips {
  grid-column: 1 / -1;
}
@media (min-width: 768px) {
  .v2-cumul-list {
    display: none;
  }
}

/* --- Cumul : desktop grille 3 colonnes --- */
.v2-cumul-grid {
  display: none;
}
@media (min-width: 768px) {
  .v2-cumul-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 12px;
  }
}
.v2-cumul-card__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
}
.v2-cumul-card__name {
  font-size: 13px;
  font-weight: 600;
}
.v2-cumul-card__total {
  font-size: 26px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.v2-cumul-card__tiles {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
  margin-top: 10px;
}
.v2-cumul-card__triple {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 6px;
  font-size: 11px;
  color: var(--text-secondary);
}
.v2-cumul-card__triple b {
  display: block;
  margin-top: 2px;
  font-size: 20px;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}
.v2-cumul-card__counts {
  margin-bottom: 8px;
  font-size: 11px;
  color: var(--text-secondary);
}
.v2-cumul-card__counts b {
  color: var(--text);
}
.v2-cumul-card__chips {
  margin-top: 8px;
}

/* Barre de répartition abonnements (8px, §4.1) : segments sans/Plus/Premium,
   largeurs posées en JS (style.width) depuis subscriptionBreakdown.*.pct. */
.v2-sub-bar {
  height: 8px;
  border-radius: 4px;
  background: var(--surface-muted-2);
  display: flex;
  overflow: hidden;
}
.v2-sub-bar__seg--none { background: var(--sub-none-bar); }
.v2-sub-bar__seg--plus { background: var(--info); }
.v2-sub-bar__seg--premium { background: var(--amber); }
/* ============================================================================
   STORY 18.4 — PAGE PRODUIT (handoff §4.2, inventaire §4)
   Vitaux : réutilise tel quel .v2-card/.v2-kpi (§3.2 ci-dessus) + les
   variantes additives .v2-kpi__row/.v2-kpi--split/.v2-vitals-grid
   introduites par 18.3 (bloc Accueil, juste au-dessus) — aucune de ces
   règles n'est modifiée ici, seulement consommée (voir index.html
   #product-vitals-grid et admin.js renderAccueilTrendBadge(), réutilisée
   telle quelle pour les badges de tendance). Le titre de section ci-dessous
   (.v2-produit-section*) reprend le même habillage que .v2-accueil-section
   sous un nom dédié, pour ne pas toucher au bloc d'une autre story.
   Histogramme (.v2-produit-chart*), funnel (.v2-produit-funnel*) et
   rétention (.v2-produit-retention*) sont propres à cette page.
   ============================================================================ */

.v2-produit-section {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 22px;
}
.v2-produit-section:last-child {
  margin-bottom: 0;
}
.v2-produit-section__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
}
.v2-produit-section__title {
  margin: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--section-title);
  text-transform: uppercase;
  letter-spacing: .04em;
}
.v2-produit-section__hint {
  font-size: 11px;
  color: var(--text-tertiary);
}

/* --- Alerte ambre « ⚠️ N jours sans données » (handoff §4.2) --------------
   Desktop : pastille compacte dans le slot statique #v2-page-header-alert
   (index.html, #v2-page-header-right), affichée seulement sur la route
   Produit par updateProduitHeaderChrome() (admin.js, appelée depuis
   renderPageHeaderChrome() comme updateIdeasHeaderChrome()). Mobile : même
   texte dans un bandeau pleine largeur en tête de page (.v2-alert générique,
   §3.10 — #product-gap-banner). Les 2 coexistent dans le DOM, CSS bascule.
   Modificateur .is-visible (pas seulement l'attribut `hidden`) : une règle
   `display` posée sans condition sur ce sélecteur primerait sur le `display:
   none` par défaut du navigateur pour `[hidden]` (les styles auteur gagnent
   toujours sur le style UA, quelle que soit la spécificité) — même
   convention que .v2-idees-header-filters.is-visible plus bas dans ce
   fichier. */
.v2-page-header__alert {
  display: none;
}
@media (min-width: 768px) {
  .v2-page-header__alert.is-visible {
    display: inline-flex;
    align-items: center;
    background: var(--amber-bg);
    color: var(--amber-text);
    border-radius: var(--radius-sm);
    padding: 6px 10px;
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
  }
}
.v2-produit-gap-banner {
  margin-bottom: 14px;
}
@media (min-width: 768px) {
  .v2-produit-gap-banner {
    display: none !important;
  }
}

.v2-produit-empty-hint {
  margin-top: 8px;
  font-size: 13px;
  color: var(--text-tertiary);
}

/* --- Activité 30 jours : histogramme maison (DOM, pas de lib) --------------
   3 séries (Scans/Vins ajoutés/Recos) toujours calculées côté JS ; desktop
   montre les 3 pistes empilées (grid-template-rows 1fr 1fr 1fr, via flex ici
   — équivalent visuel), mobile une seule à la fois (.is-active-mobile posé
   en JS selon le segment sélectionné). Une barre = 1 jour, flex:1 (jamais
   plus large que le conteneur), gap 3-4px, coins hauts arrondis. Jour sans
   donnée = barre pleine hauteur hachurée, opacité .5. -------------------- */
.v2-produit-chart {
  position: relative;
  background: var(--surface-card);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: 14px;
}
@media (min-width: 768px) {
  .v2-produit-chart {
    padding: 18px;
  }
}
.v2-produit-chart__legend {
  display: none;
  gap: 14px;
  flex-wrap: wrap;
}
@media (min-width: 768px) {
  .v2-produit-chart__legend {
    display: flex;
  }
}
.v2-produit-chart__legend-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--text-secondary);
}
.v2-produit-chart__legend-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  flex: none;
}
.v2-produit-chart__legend-dot--scans { background: var(--chart-scans); }
.v2-produit-chart__legend-dot--wines { background: var(--chart-wines); }
.v2-produit-chart__legend-dot--recos { background: var(--chart-recos); }
.v2-produit-chart__legend-item b {
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.v2-produit-chart__tracks {
  display: flex;
  flex-direction: column;
  gap: 8px;
  height: 110px;
}
.v2-produit-chart__track {
  display: none;
  align-items: flex-end;
  gap: 3px;
  height: 110px;
}
.v2-produit-chart__track.is-active-mobile {
  display: flex;
}
.v2-produit-chart__track--scans { --bar-color: var(--chart-scans); }
.v2-produit-chart__track--wines { --bar-color: var(--chart-wines); }
.v2-produit-chart__track--recos { --bar-color: var(--chart-recos); }
@media (min-width: 768px) {
  .v2-produit-chart__tracks {
    height: 204px;
  }
  .v2-produit-chart__track {
    display: flex !important;
    gap: 4px;
    height: auto;
    flex: 1 1 0;
  }
}
.v2-produit-chart__bar {
  flex: 1 1 0;
  min-width: 2px;
  height: 0%;
  background: var(--bar-color);
  border-radius: 2px 2px 0 0;
  cursor: pointer;
}
.v2-produit-chart__bar--missing {
  height: 100% !important;
  opacity: .5;
  background: repeating-linear-gradient(0deg, var(--skeleton) 0 2px, transparent 2px 4px);
}

.v2-produit-chart__dates {
  display: flex;
  justify-content: space-between;
  margin-top: 8px;
  font-size: 10px;
  color: var(--text-tertiary);
}

.v2-produit-chart__segment {
  margin-top: 12px;
}
@media (min-width: 768px) {
  .v2-produit-chart__segment {
    display: none;
  }
}
.v2-produit-chart__segment .v2-segment__option[data-series="scansCompleted"].is-active {
  color: var(--chart-scans);
}
.v2-produit-chart__segment .v2-segment__option[data-series="winesAdded"].is-active {
  color: var(--chart-wines);
}
.v2-produit-chart__segment .v2-segment__option[data-series="pairingsRequested"].is-active {
  color: var(--chart-recos);
}

.v2-produit-chart__tooltip {
  position: absolute;
  transform: translate(-50%, calc(-100% - 8px));
  background: var(--color-bordeaux);
  color: var(--text-inverse);
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
  padding: 5px 8px;
  border-radius: var(--radius-xs);
  box-shadow: var(--shadow-card-selected);
  pointer-events: none;
  z-index: 5;
}

/* --- Funnel d'activation : 4 étapes, largeurs posées en JS (% de la 1re
   étape, jamais du max local), couleur par étape via modificateur
   .v2-produit-funnel__fill--step-N (tokens de marque). Mobile : libellé +
   valeur + taux au-dessus de la piste (une ligne) ; desktop : grille
   64px/1fr/84px, valeur en surimpression sur la piste (même grid-area). --- */
.v2-produit-funnel__row {
  display: grid;
  grid-template-columns: 1fr auto auto;
  grid-template-areas:
    "label value rate"
    "track track track";
  row-gap: 6px;
  column-gap: 8px;
  align-items: center;
  margin-bottom: 14px;
}
.v2-produit-funnel__row:last-child {
  margin-bottom: 0;
}
.v2-produit-funnel__label {
  grid-area: label;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
}
.v2-produit-funnel__value {
  grid-area: value;
  font-size: 13px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}
.v2-produit-funnel__rate {
  grid-area: rate;
  font-size: 12px;
  color: var(--text-tertiary);
  white-space: nowrap;
}
.v2-produit-funnel__track {
  grid-area: track;
  position: relative;
  height: 22px;
  border-radius: 6px;
  background: var(--surface-muted);
  overflow: hidden;
}
.v2-produit-funnel__fill {
  height: 100%;
  min-width: 4px;
  border-radius: 6px 0 0 6px;
  transition: width .3s ease;
}
.v2-produit-funnel__fill--step-1 { background: var(--color-bordeaux); }
.v2-produit-funnel__fill--step-2 { background: var(--color-ruby-mid); }
.v2-produit-funnel__fill--step-3 { background: var(--color-ruby); }
.v2-produit-funnel__fill--step-4 { background: var(--color-ruby-light); }
@media (min-width: 768px) {
  .v2-produit-funnel__row {
    grid-template-columns: 64px 1fr 84px;
    grid-template-areas: "label track rate";
    row-gap: 0;
    column-gap: 12px;
    height: 22px;
  }
  .v2-produit-funnel__value {
    grid-area: track;
    justify-self: start;
    align-self: center;
    margin-left: 10px;
    position: relative;
    z-index: 1;
    text-shadow: 0 0 4px var(--surface-card), 0 0 4px var(--surface-card);
  }
  .v2-produit-funnel__rate {
    text-align: right;
  }
}

/* --- Rétention : 2 cartes D7/D30, réutilise .v2-card/.v2-kpi. ------------- */
.v2-produit-retention {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}
@media (min-width: 768px) {
  .v2-produit-retention {
    gap: 14px;
  }
}
.v2-produit-retention__cohort {
  margin-top: 6px;
  font-size: 12px;
  color: var(--text-tertiary);
}

/* ============================================================================
   STORY 18.5 — IDÉES : mobile Liste/Colonnes (segment en en-tête, pills,
   recherche/filtres), kanban desktop 6 colonnes, détail plein écran/modale 2
   volets. Handoff §5.1 (écrans 2a/3e), §3.5/3.6/3.8/3.9/3.12. Remplace tout le
   CSS legacy `.ideas-*` / `#idea-detail-modal .modal-*` (retiré plus haut).
   ============================================================================ */

/* --- En-tête mobile : segment Liste/Colonnes (remplace « à jour »/↻, comme le
   segment Administration) — jamais affiché en desktop (le kanban n'a pas de
   bascule de vue). --- */
.v2-idees-view-segment {
  display: none;
}
.v2-idees-view-segment.is-visible {
  display: flex;
}
@media (min-width: 768px) {
  .v2-idees-view-segment {
    display: none !important;
  }
}

/* --- En-tête desktop : recherche + Version ▾ + Plateforme ▾ (avant « à jour
   »/↻ générique) — jamais affiché en mobile (ces filtres vivent alors dans le
   contenu de page, voir .v2-ideas-mobile-toolbar). --- */
.v2-idees-header-filters {
  display: none;
}
@media (min-width: 768px) {
  .v2-idees-header-filters.is-visible {
    display: flex;
    align-items: center;
    gap: 8px;
  }
}
.v2-idees-search-input {
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--text);
  background: var(--surface-card);
  border: 1px solid var(--border-bordeaux-15);
  border-radius: var(--radius-sm);
  padding: 7px 12px;
}
.v2-idees-header-filters .v2-idees-search-input {
  width: 220px;
}
.v2-idees-filter-select {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  /* --section-title (pas --color-bordeaux, fixe) : a un pendant sombre —
     sinon ce texte devient quasi illisible en thème sombre (cf. story 18.2). */
  color: var(--section-title);
  background: var(--surface-card);
  border: 1px solid var(--border-bordeaux-15);
  border-radius: var(--radius-sm);
  padding: 7px 10px;
}

/* --- Mobile uniquement : pills de statut + recherche/filtres (contenu de page) --- */
.v2-ideas-mobile-toolbar {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 12px;
}
.v2-ideas-mobile-filters {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.v2-ideas-mobile-filters .v2-idees-search-input {
  flex: 1;
  min-width: 140px;
}
.v2-ideas-mobile-filters .v2-idees-filter-select {
  flex: none;
}

.v2-ideas-board-status {
  margin-bottom: 12px;
}

/* --- Mobile — vue Liste --- */
.v2-ideas-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.v2-ideas-empty {
  color: var(--text-tertiary);
  font-size: 13px;
  text-align: center;
  padding: 24px 0;
}

/* --- Mobile — vue Colonnes (scroll-snap 335px, handoff §5.1/3e) --- */
.v2-ideas-columns__dots {
  display: flex;
  justify-content: center;
  gap: 6px;
  padding: 0 0 12px;
}
.v2-ideas-columns__dot {
  width: 6px;
  height: 6px;
  border-radius: 3px;
  border: none;
  background: var(--border-bordeaux-25);
  padding: 0;
  cursor: pointer;
}
.v2-ideas-columns__dot.is-active {
  width: 18px;
  background: var(--color-ruby);
}
.v2-ideas-columns__track {
  display: flex;
  gap: 12px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding-bottom: 4px;
}
.v2-ideas-columns__track::-webkit-scrollbar {
  display: none;
}
.v2-ideas-columns__col {
  flex: 0 0 335px;
  width: 335px;
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.v2-ideas-columns__col-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 0 4px;
}
.v2-ideas-columns__col-title {
  font-size: 15px;
  font-weight: 600;
  color: var(--section-title);
}
.v2-ideas-columns__col-count {
  color: var(--text-tertiary);
  font-weight: 400;
}
.v2-ideas-columns__col-hint {
  font-size: 12px;
  color: var(--text-tertiary);
  white-space: nowrap;
}

/* --- Desktop — kanban 6 colonnes --- */
.v2-ideas-kanban {
  display: none;
}
.v2-ideas-kanban__col {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}
.v2-ideas-kanban__col-header {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  font-weight: 600;
  color: var(--section-title);
  padding: 0 4px;
}
.v2-ideas-kanban__col-count {
  color: var(--text-tertiary);
  font-weight: 400;
}
.v2-ideas-kanban__more {
  border: none;
  background: none;
  font-size: 11px;
  color: var(--text-tertiary);
  text-align: center;
  padding: 6px;
  cursor: pointer;
  font-family: var(--font-sans);
}

@media (min-width: 768px) {
  .v2-ideas-mobile-toolbar,
  .v2-ideas-list,
  .v2-ideas-columns {
    display: none !important;
  }
  .v2-ideas-kanban {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    gap: 12px;
  }
}

/* --- Carte idée (§5.1) : #N + titre + point « nouvelle réponse », puces,
   pied questions/date. .v2-card--kanban (desktop) réutilise cette structure
   en plus compact via les descendants ci-dessous. --- */
.v2-idea-card {
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.v2-idea-card.is-open {
  outline: 2px solid var(--color-ruby);
}
.v2-idea-card__title-row {
  display: flex;
  align-items: flex-start;
  gap: 8px;
}
.v2-idea-card__shortid {
  font-size: 13px;
  font-weight: 700;
  color: var(--color-ruby);
  font-variant-numeric: tabular-nums;
  flex: none;
}
.v2-idea-card__title {
  flex: 1;
  font-size: 15px;
  font-weight: 600;
  line-height: 1.3;
}
.v2-idea-card__dot {
  width: 9px;
  height: 9px;
  border-radius: 5px;
  background: var(--color-ruby);
  margin-top: 4px;
  flex: none;
}
.v2-idea-card__chip--error {
  background: var(--danger-bg);
  color: var(--danger-text);
}
.v2-idea-card__footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  font-size: 12px;
}
.v2-idea-card__questions {
  color: var(--amber-text-strong);
  font-weight: 600;
}
.v2-idea-card__untriaged {
  color: var(--text-tertiary);
}
.v2-idea-card__date {
  color: var(--text-tertiary);
  flex: none;
  margin-left: auto;
}

/* Variante compacte (kanban desktop, .v2-card--kanban) : texte réduit, puces 10px. */
.v2-card--kanban.v2-idea-card {
  gap: 6px;
}
.v2-card--kanban .v2-idea-card__title {
  font-size: 12px;
}
.v2-card--kanban .v2-idea-card__shortid {
  font-size: 11px;
}
.v2-card--kanban .v2-idea-card__badges .v2-chip,
.v2-card--kanban .v2-idea-card__footer {
  font-size: 10px;
}

/* ============================================================================
   Détail d'une idée (plein écran mobile / modale 820×600 desktop 2 volets).
   .v2-idea-detail est en flex-column sur mobile (topbar/segment/scroll/
   composer empilés) ; sur desktop il devient une grille 2 colonnes (le volet
   gauche .v2-idea-detail__main regroupe topbar+segment+scroll+composer
   mobile, masqués par ailleurs ; le volet droit .v2-idea-detail__thread
   n'existe qu'en desktop).
   ============================================================================ */
.v2-idea-detail {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  height: 100%;
}
.v2-idea-detail__main {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}
.v2-idea-detail__topbar {
  flex: none;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 16px 12px;
  border-bottom: 1px solid var(--divider);
  background: var(--surface-card);
  position: relative;
}
.v2-idea-detail__back {
  border: none;
  background: none;
  color: var(--section-title);
  font-size: 20px;
  width: 32px;
  flex: none;
  cursor: pointer;
}
.v2-idea-detail__shortid {
  font-size: 13px;
  font-weight: 700;
  color: var(--color-ruby);
  flex: none;
}
.v2-idea-detail__title {
  flex: 1;
  min-width: 0;
  font-size: 15px;
  font-weight: 600;
  line-height: 1.25;
  color: var(--text);
}
.v2-idea-detail__close-desktop {
  display: none;
  border: none;
  background: none;
  font-size: 20px;
  color: var(--text-tertiary);
  cursor: pointer;
  flex: none;
}
.v2-idea-detail__menu-wrap {
  position: relative;
  flex: none;
}
.v2-idea-detail__menu-btn {
  border: none;
  background: none;
  color: var(--text-tertiary);
  font-size: 18px;
  cursor: pointer;
  min-width: 44px;
  min-height: 44px;
}
.v2-idea-detail__menu {
  position: absolute;
  right: 0;
  top: 100%;
  background: var(--surface-card);
  border: 1px solid var(--divider);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-modal);
  z-index: 10;
  min-width: 140px;
  overflow: hidden;
}
.v2-idea-detail__menu-item {
  display: block;
  width: 100%;
  border: none;
  background: none;
  text-align: left;
  padding: 10px 14px;
  font-size: 13px;
  font-family: var(--font-sans);
  cursor: pointer;
  color: var(--text);
}
.v2-idea-detail__menu-item--danger {
  color: var(--danger);
}
.v2-idea-detail__status-row {
  flex: none;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 16px;
}
.v2-idea-detail__status-segment {
  flex: 1;
}
.v2-idea-detail__delete-link {
  display: none;
  flex: none;
}
.v2-idea-detail__scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 0 16px 16px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.v2-idea-detail__body-text {
  white-space: pre-wrap;
  line-height: 1.45;
  color: var(--text);
  font-size: 14px;
}
.v2-idea-detail__section-label {
  font-weight: 600;
  color: var(--section-title);
  font-size: 13px;
}
.v2-idea-detail__triage {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 13px;
}
.v2-idea-detail__triage-header {
  display: flex;
  justify-content: space-between;
  gap: 8px;
}
.v2-idea-detail__triage-date {
  font-size: 11px;
  color: var(--text-tertiary);
  flex: none;
}
.v2-idea-detail__triage-empty {
  color: var(--text-tertiary);
  font-size: 13px;
  margin: 0;
}
.v2-idea-detail__triage-questions-label {
  color: var(--amber-text-strong);
  font-weight: 600;
  margin-top: 4px;
}
.v2-idea-detail__triage-questions ul {
  margin: 4px 0 0 18px;
}
.v2-idea-detail__release-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  font-size: 13px;
}
.v2-idea-detail__release-row select,
.v2-idea-detail__release-row input[type="text"] {
  font-family: var(--font-sans);
  font-size: 13px;
  padding: 6px 10px;
  border: 1px solid var(--border-bordeaux-20);
  border-radius: var(--radius-xs);
  background: var(--surface-card);
  color: var(--text);
}
.v2-idea-detail__release-row input[type="text"] {
  width: 90px;
}
.v2-idea-detail__release-status {
  font-size: 12px;
  color: var(--text-tertiary);
  flex-basis: 100%;
}
.v2-idea-detail__release-status.error {
  color: var(--danger-text);
}
.v2-idea-detail__thread-label {
  font-size: 12px;
  color: var(--text-tertiary);
  text-align: center;
}
.v2-idea-detail__composer {
  flex: none;
  display: flex;
  gap: 8px;
  align-items: center;
  padding: 10px 12px calc(14px + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--divider);
  background: var(--bg);
}
.v2-idea-detail__composer-input {
  flex: 1;
  min-width: 0;
  resize: none;
  font-family: var(--font-sans);
  font-size: 15px;
  color: var(--text);
  background: var(--surface-card);
  border: 1px solid var(--border-bordeaux-15);
  border-radius: var(--radius-card);
  padding: 11px 14px;
  max-height: 90px;
}
.v2-idea-detail__thread {
  display: none;
}

/* Segment de statut : 4 visibles + « … » sur mobile, les 6 sur desktop. */
@media (max-width: 767px) {
  .v2-idea-detail__status-segment [data-status="done"],
  .v2-idea-detail__status-segment [data-status="rejected"] {
    display: none;
  }
  .v2-idea-detail__status-segment.is-more-open [data-status="done"],
  .v2-idea-detail__status-segment.is-more-open [data-status="rejected"] {
    display: flex;
  }
  .v2-idea-detail__status-segment.is-more-open .v2-idea-detail__status-more {
    display: none;
  }
}
@media (min-width: 768px) {
  .v2-idea-detail__status-more {
    display: none;
  }
}

@media (min-width: 768px) {
  .v2-idea-detail {
    display: grid;
    grid-template-columns: 1fr 360px;
    /* Correctif 2.0.2 : sans ligne contrainte, la ligne implicite `auto` grandit
       avec le contenu et la modale (overflow:hidden, 600px) coupe le surplus au
       lieu de laisser chaque volet défiler. */
    grid-template-rows: minmax(0, 1fr);
  }
  .v2-idea-detail__main {
    min-height: 0;
  }
  .v2-idea-detail__back,
  .v2-idea-detail__menu-wrap,
  .v2-idea-detail__thread-mobile-block,
  .v2-idea-detail__composer--mobile {
    display: none;
  }
  .v2-idea-detail__close-desktop {
    display: inline-flex;
  }
  .v2-idea-detail__title {
    font-size: 18px;
  }
  .v2-idea-detail__delete-link {
    display: inline-flex;
    margin-left: auto;
  }
  .v2-idea-detail__thread {
    display: flex;
    flex-direction: column;
    min-height: 0;
    background: var(--surface-card);
    border-left: 1px solid var(--divider);
  }
  .v2-idea-detail__thread-header {
    flex: none;
    display: flex;
    justify-content: space-between;
    padding: 16px 18px;
    border-bottom: 1px solid var(--divider);
    font-size: 13px;
    font-weight: 600;
    color: var(--section-title);
  }
  .v2-idea-detail__thread-header span:last-child {
    font-weight: 400;
    color: var(--text-tertiary);
  }
  #idea-detail-thread-desktop {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding: 14px 16px;
  }
}

/* --- Fil de conversation (§3.12) : bulles IA/fondateur, markdown rendu via
   marked.parse() + sanitizePreviewHtml() (voir renderIdeaBubblesInto()). --- */
.v2-idea-thread {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.v2-idea-thread__empty {
  color: var(--text-tertiary);
  font-size: 13px;
  font-style: italic;
  text-align: center;
}
.v2-idea-bubble {
  max-width: 88%;
  padding: 10px 12px;
  font-size: 13px;
  line-height: 1.4;
}
.v2-idea-bubble--claude {
  align-self: flex-start;
  background: var(--surface-card);
  border: 1px solid var(--divider);
  border-radius: 14px 14px 14px 4px;
}
.v2-idea-bubble--theau {
  align-self: flex-end;
  background: var(--color-bordeaux);
  color: var(--text-inverse);
  border-radius: 14px 14px 4px 14px;
}
.v2-idea-bubble__meta {
  font-size: 11px;
  font-weight: 700;
  margin-bottom: 3px;
}
.v2-idea-bubble--claude .v2-idea-bubble__meta {
  color: var(--color-ruby);
}
.v2-idea-bubble--theau .v2-idea-bubble__meta {
  color: var(--text-inverse-subtle);
}
.v2-idea-bubble__text {
  word-break: break-word;
}
.v2-idea-bubble__text p {
  margin: 0 0 6px;
}
.v2-idea-bubble__text p:last-child {
  margin-bottom: 0;
}
/* ============================================================================
   STORY 18.7 — PAGE ARTICLES (handoff §6.1, inventaire §7)
   Remplace l'ancien markup Articles (weeks-band/candidate-card/ai-step/
   markdown-toolbar/image-upload-container/source-row… CSS pré-v2, retirée en
   fin de section). Zone strictement Articles : #articles-content,
   #article-modal, #ai-generator-modal, #article-preview-modal,
   #schedule-date-modal. Seule exception (additive, pas de renommage) : le
   slot générique .v2-page-header__actions dans #v2-page-header-right,
   consommé ici pour la recherche + les 2 CTA d'en-tête (§6.1).
   ============================================================================ */
/* --- En-tête de page : recherche + « Générer avec l'IA » + « Nouvel article »
   (slot générique, réutilisable par d'autres pages comme route.headerActions
   — voir renderPageHeaderChrome() dans admin.js). Sur mobile, seul le bouton
   blanc « + Nouvel » reste (recherche/IA masqués, §2.2) et remplace la
   mention « à jour »/↻ générique (classe .has-custom-slot déjà prévue). */
.v2-page-header__actions {
  display: flex;
  align-items: center;
  gap: 8px;
}
.v2-page-header__search {
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--text);
  background: var(--surface-card);
  border: 1px solid var(--border-bordeaux-15);
  border-radius: var(--radius-sm);
  padding: 7px 12px;
  width: 200px;
}
.v2-page-header__search::placeholder { color: var(--placeholder); }
@media (max-width: 767px) {
  .v2-page-header__search,
  .v2-page-header__ai-btn {
    display: none;
  }
  .v2-page-header__new-btn {
    /* Bouton "blanc sur bordeaux" du handoff §2.2 : blanc fixe (--text-inverse
       n'est jamais redéfini en sombre) + texte bordeaux fixe — pas
       --surface-card, qui foncirait en thème sombre et casserait le contraste
       avec le texte bordeaux (fixe lui aussi). */
    background: var(--text-inverse);
    color: var(--color-bordeaux);
  }
}
/* --- Bandeau des semaines (6 cartes) --- */
.v2-weeks-band {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-bottom: 2px;
  margin-bottom: 16px;
}
.v2-weeks-band::-webkit-scrollbar { display: none; }
@media (min-width: 768px) {
  .v2-weeks-band {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    overflow: visible;
  }
}
.v2-week-chip {
  flex: none;
  scroll-snap-align: start;
  min-width: 148px;
  text-align: left;
  background: var(--surface-card);
  border: 1px solid var(--border-bordeaux-15);
  border-radius: var(--radius-md);
  padding: 10px 12px;
  font-size: 12px;
  cursor: pointer;
  font-family: var(--font-sans);
}
@media (min-width: 768px) {
  .v2-week-chip { min-width: 0; }
}
.v2-week-chip.is-selected {
  border: 2px solid var(--color-ruby);
  box-shadow: var(--shadow-card-selected);
}
.v2-week-chip__label {
  display: block;
  font-weight: 600;
  color: var(--section-title);
}
.v2-week-chip__status {
  display: block;
  margin-top: 2px;
  color: var(--text-tertiary);
}
.v2-week-chip__status.is-pending {
  color: var(--amber-text-strong);
  font-weight: 600;
}
/* --- Titre de section générique (Discord / À venir / Publiés) --- */
.v2-articles-section {
  margin-bottom: 20px;
}
.v2-articles-section__header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 10px;
}
.v2-articles-section__title {
  margin: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--section-title);
  text-transform: uppercase;
  letter-spacing: .04em;
}
.v2-articles-section__title-suffix,
.v2-articles-section__count {
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  color: var(--text-tertiary);
  margin-left: 4px;
}
.v2-articles-section__hint {
  font-size: 12px;
  color: var(--text-tertiary);
  margin-bottom: 10px;
}
.v2-empty-note {
  color: var(--text-tertiary);
  font-style: italic;
  font-size: 13px;
  margin: 4px 0;
}

/* --- Lien d'action texte (Ignorer / Aperçu / ♡ / Valider / Date… / Voir…) ---
   Cible ≥ 44px sur mobile via le padding vertical, même quand le texte est
   petit (§3.7 « cibles tactiles »). */
.v2-link-action {
  background: none;
  border: none;
  padding: 10px 2px;
  margin: -10px 0;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  color: var(--section-title);
  cursor: pointer;
}
@media (min-width: 768px) {
  .v2-link-action { padding: 4px 2px; margin: 0; font-size: 13px; }
}
.v2-link-action--danger { color: var(--color-ruby); }
.v2-link-action:disabled { opacity: .45; cursor: not-allowed; }
.v2-link-action-row {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  align-items: center;
}

/* --- Grille de cartes candidates (Discord + Brouillons IA) --- */
.v2-candidates-grid {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.v2-candidate-card__top {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  align-items: flex-start;
}
.v2-candidate-card__title {
  font-size: 15px;
  font-weight: 600;
  line-height: 1.3;
}
.v2-candidate-card__meta {
  font-size: 12px;
  color: var(--text-tertiary);
  margin-top: 2px;
}
.v2-candidate-card__like {
  background: none;
  border: none;
  padding: 4px;
  font-size: 18px;
  color: var(--color-ruby);
  cursor: pointer;
  flex: none;
}
.v2-candidate-card__note {
  font-size: 12px;
  color: var(--danger-text);
  margin: 0;
}
.v2-candidate-card--featured .v2-candidate-card__actions {
  display: flex;
  gap: 8px;
  margin-top: 4px;
  flex-wrap: wrap;
}
.v2-candidate-card--featured .v2-candidate-card__cta {
  flex: 1;
  min-width: 160px;
}
.v2-candidate-card__links {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  margin-top: 4px;
}

/* --- Bloc pick résolu (semaine ignorée / publiée auto / etc.) --- */
.v2-resolved-pick__label {
  margin: 0 0 8px;
  font-size: 13px;
}
.v2-resolved-pick__actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

/* --- « À venir » : liste de cartes (desktop ET mobile, champ date inline
   visible seulement à partir de 768px — sur mobile la date reste lisible en
   puce, « Enregistrer » n'a pas de sens sans le champ). --- */
.v2-upcoming-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.v2-upcoming-card__title {
  font-size: 15px;
  font-weight: 600;
  line-height: 1.3;
  margin-bottom: 6px;
}
.v2-upcoming-card__schedule {
  display: none;
  align-items: center;
  gap: 10px;
  margin: 8px 0;
}
@media (min-width: 768px) {
  .v2-upcoming-card__schedule { display: flex; }
}
.v2-articles-date-field {
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--text);
  background: var(--surface-card);
  border: 1px solid var(--border-bordeaux-20);
  border-radius: var(--radius-xs);
  padding: 6px 10px;
}
.v2-upcoming-card__actions {
  margin-top: 8px;
}
@media (min-width: 768px) {
  .v2-upcoming-card__actions .v2-link-action--danger {
    margin-left: auto;
  }
}

/* --- « Publiés » : résumé + table desktop (réutilise .v2-table-wrap, déjà
   masquée < 768px) + bloc mobile (ligne repliée -> liste de cartes). --- */
.v2-published-summary {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
  font-size: 12px;
  color: var(--text-tertiary);
  margin-bottom: 10px;
}
.v2-published-summary b {
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.v2-published-mobile {
  display: block;
}
@media (min-width: 768px) {
  .v2-published-mobile { display: none; }
}
.v2-published-mobile__toggle {
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: var(--surface-card);
  border: none;
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: 12px 14px;
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  color: var(--section-title);
  cursor: pointer;
}
.v2-published-mobile__toggle-sub {
  font-weight: 400;
  color: var(--text-tertiary);
}
.v2-published-mobile__list {
  margin-top: 10px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.v2-published-card__top {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  align-items: flex-start;
}
.v2-published-card__title {
  font-size: 14px;
  font-weight: 600;
  line-height: 1.3;
}
.v2-published-card__meta {
  font-size: 12px;
  color: var(--text-tertiary);
  margin-top: 4px;
}

/* Correctif 2.0.1 : l'attribut HTML `hidden` doit toujours l'emporter sur un
   `display` posé par une classe (flex/grid/inline-block). Sans cette règle, tout
   composant à `display:flex` reste visible malgré `el.hidden = true` — c'est ce qui
   affichait tous les menus « ⋯ » de la table Publiés en même temps. */
[hidden] { display: none !important; }

/* Correctif 2.0.2 : tant qu'une modale v2 est visible, la page derrière ne doit
   pas défiler (classe posée par installModalScrollLock() dans admin.js) et la
   molette au-dessus de l'overlay ne doit pas se propager au document. */
body.v2-modal-open { overflow: hidden; }
.v2-modal-overlay { overscroll-behavior: contain; }
.v2-modal__body, .v2-idea-detail__scroll, #idea-detail-thread-desktop { overscroll-behavior: contain; }

/* --- Menu contextuel « ⋯ » (table Publiés desktop + cartes Publiés mobile) --- */
.v2-row-menu {
  position: relative;
  display: inline-block;
}
.v2-row-menu__toggle {
  width: 30px;
  height: 30px;
  border-radius: var(--radius-xs);
  border: 1px solid var(--border-bordeaux-20);
  background: var(--surface-card);
  color: var(--text);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
}
@media (max-width: 767px) {
  .v2-row-menu__toggle { width: 36px; height: 36px; }
}
.v2-row-menu__panel {
  position: absolute;
  right: 0;
  top: calc(100% + 4px);
  z-index: 60;
  min-width: 200px;
  background: var(--surface-card);
  border: 1px solid var(--divider);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-modal);
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.v2-row-menu__info {
  padding: 6px 10px;
  font-size: 12px;
  color: var(--text-tertiary);
}
.v2-row-menu__info b { color: var(--text); }
.v2-row-menu__divider {
  height: 1px;
  background: var(--divider);
  margin: 4px 2px;
}
.v2-row-menu__item {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  border-radius: var(--radius-xs);
  padding: 8px 10px;
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  color: var(--section-title);
  cursor: pointer;
}
.v2-row-menu__item:hover { background: var(--surface-muted); }
.v2-row-menu__item.is-danger { color: var(--color-ruby); }

/* --- Sections repliables (Brouillons manuels / Brouillons IA) --- */
.v2-collapsible {
  border-top: 1px solid var(--divider);
  padding-top: 14px;
  margin-top: 6px;
}
.v2-collapsible > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 13px;
  font-weight: 600;
  color: var(--section-title);
  text-transform: uppercase;
  letter-spacing: .04em;
}
.v2-collapsible > summary::-webkit-details-marker { display: none; }
.v2-collapsible > summary::after {
  content: '▸';
  text-transform: none;
  font-weight: 400;
  color: var(--text-tertiary);
  transition: transform .15s ease;
}
.v2-collapsible[open] > summary::after {
  transform: rotate(90deg);
}
.v2-collapsible__body {
  margin-top: 12px;
}
.v2-collapsible__toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 10px;
  font-size: 12px;
  color: var(--text-tertiary);
}

/* ----------------------------------------------------------------------------
   Éditeur d'article (modale plein écran mobile / scindée ≥1100px, handoff §6.1)
   Structure : .v2-modal (header Annuler/Titre/Enregistrer + onglets) →
   .v2-editor-body → { .v2-editor-pane--content, .v2-editor-pane--preview,
   .v2-editor-pane--publication }. Un seul panneau visible à la fois entre
   768 et 1099px (onglets) ; ≥1100px, la media query ci-dessous affiche les 3
   panneaux simultanément (content+preview côte à côte, publication dessous)
   quel que soit l'onglet actif en JS — aucune classe de largeur nécessaire.
   ---------------------------------------------------------------------------- */
.v2-editor-tabs {
  display: flex;
  border-top: 1px solid var(--divider);
}
.v2-editor-tabs__tab {
  flex: 1;
  text-align: center;
  padding: 10px 0;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  color: var(--text-tertiary);
  cursor: pointer;
}
.v2-editor-tabs__tab.is-active {
  color: var(--color-ruby);
  border-bottom-color: var(--color-ruby);
}
@media (min-width: 1100px) {
  .v2-editor-tabs { display: none; }
}
.v2-editor-body {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 14px 16px;
}
.v2-editor-pane { display: none; flex-direction: column; gap: 12px; }
.v2-editor-pane.is-active { display: flex; }
@media (min-width: 1100px) {
  .v2-editor-body {
    display: grid;
    grid-template-columns: 1.2fr 1fr;
    align-items: start;
  }
  .v2-editor-pane--content,
  .v2-editor-pane--preview {
    display: flex; /* toujours visibles, indépendamment de .is-active (onglets mobiles/tablette) */
  }
  .v2-editor-pane--publication {
    grid-column: 1 / -1;
  }
}
.v2-editor-field {
  width: 100%;
  box-sizing: border-box;
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--text);
  background: var(--surface-card);
  border: 1px solid var(--border-bordeaux-15);
  border-radius: var(--radius-md);
  padding: 11px 14px;
}
.v2-editor-field--title { font-size: 16px; font-weight: 600; }
.v2-editor-dropzone {
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--surface-card);
  border: 1px dashed var(--border-bordeaux-35);
  border-radius: var(--radius-md);
  padding: 14px;
}
.v2-editor-dropzone__thumb {
  width: 64px;
  height: 48px;
  border-radius: var(--radius-xs);
  background: repeating-linear-gradient(135deg, var(--divider) 0 6px, var(--surface-muted) 6px 12px);
  flex: none;
  object-fit: cover;
}
.v2-editor-dropzone__meta {
  flex: 1;
  min-width: 0;
  font-size: 12px;
  color: var(--text-tertiary);
  font-family: var(--font-mono);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.v2-editor-dropzone__action {
  color: var(--color-ruby);
  font-weight: 600;
  font-size: 13px;
  background: none;
  border: none;
  cursor: pointer;
  flex: none;
}
.v2-editor-markdown-toolbar {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  white-space: nowrap;
}
.v2-editor-markdown-toolbar::-webkit-scrollbar { display: none; }
.v2-editor-md-btn {
  flex: none;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  background: var(--surface-card);
  border: 1px solid var(--border-bordeaux-15);
  border-radius: var(--radius-sm);
  padding: 9px 12px;
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  color: var(--section-title);
  cursor: pointer;
}
@media (min-width: 768px) {
  .v2-editor-md-btn { min-height: 0; }
}
.v2-editor-textarea {
  flex: 1;
  min-height: 240px;
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1.55;
  color: var(--text);
  background: var(--surface-card);
  border: 1px solid var(--border-bordeaux-15);
  border-radius: var(--radius-md);
  padding: 12px 14px;
  resize: vertical;
}
.v2-editor-counter {
  font-size: 12px;
  color: var(--text-tertiary);
  text-align: right;
}
.v2-editor-preview {
  flex: 1;
  min-height: 240px;
  overflow-y: auto;
  background: var(--surface-card);
  border: 1px solid var(--border-bordeaux-15);
  border-radius: var(--radius-md);
  padding: 12px 14px;
  font-size: 14px;
  line-height: 1.5;
}
.v2-editor-preview :first-child { margin-top: 0; }
.v2-editor-field-label {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-secondary);
  margin-bottom: 4px;
  display: block;
}
.v2-editor-source-row {
  display: flex;
  gap: 6px;
  align-items: center;
  margin-bottom: 8px;
}
.v2-editor-source-row select,
.v2-editor-source-row input {
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--text);
  background: var(--surface-card);
  border: 1px solid var(--border-bordeaux-15);
  border-radius: var(--radius-xs);
  padding: 8px 10px;
}
.v2-editor-source-row select { flex: none; }
.v2-editor-source-row input:first-of-type { flex: 1; min-width: 0; }
.v2-editor-source-row input[type="url"] { flex: 1.4; min-width: 0; }
.v2-editor-source-row input[type="url"].optional { opacity: .7; }
.v2-editor-source-row input[type="url"].optional::placeholder { font-style: italic; }
.v2-editor-source-remove {
  background: none;
  border: none;
  font-size: 18px;
  color: var(--text-tertiary);
  cursor: pointer;
  flex: none;
}
.v2-editor-checkbox-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
}
.v2-editor-help {
  font-size: 12px;
  color: var(--text-tertiary);
}
.v2-editor-upload-progress {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--text-tertiary);
}
.v2-editor-upload-progress__bar {
  flex: 1;
  height: 6px;
  border-radius: 3px;
  background: var(--surface-muted-2);
  overflow: hidden;
}
.v2-editor-upload-progress__fill {
  height: 100%;
  background: var(--color-ruby);
}

/* --- Modale IA (3 étapes : sélection / chargement / aperçu) --- */
.v2-ai-section { margin-bottom: 16px; }
.v2-ai-section__title {
  font-size: 13px;
  font-weight: 600;
  color: var(--section-title);
  margin-bottom: 8px;
  display: flex;
  align-items: center;
  gap: 6px;
}
.v2-ai-divider {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--text-tertiary);
  font-size: 12px;
  margin: 14px 0;
}
.v2-ai-divider::before,
.v2-ai-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--divider);
}
.v2-ai-draft-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.v2-ai-draft-card {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  background: var(--surface-card);
  border: 1px solid var(--border-bordeaux-15);
  border-radius: var(--radius-md);
  padding: 10px 12px;
}
.v2-ai-draft-card__title { font-size: 14px; font-weight: 600; }
.v2-ai-draft-card__topic { font-size: 12px; color: var(--text-tertiary); margin-top: 2px; }
.v2-ai-suggestions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.v2-ai-suggestion-chip {
  background: var(--chip-bg);
  border: none;
  border-radius: var(--radius-pill);
  padding: 6px 12px;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  color: var(--text);
  cursor: pointer;
}
.v2-ai-loading {
  text-align: center;
  padding: 40px 0;
}
.v2-ai-spinner {
  width: 32px;
  height: 32px;
  margin: 0 auto 16px;
  border-radius: 50%;
  border: 3px solid var(--border-bordeaux-15);
  border-top-color: var(--color-ruby);
  animation: v2-spin 0.8s linear infinite;
}
@keyframes v2-spin { to { transform: rotate(360deg); } }
.v2-ai-loading__hint {
  font-size: 12px;
  color: var(--text-tertiary);
  margin-top: 6px;
}
.v2-ai-preview-content {
  background: var(--surface-muted);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  max-height: 200px;
  overflow-y: auto;
  font-size: 13px;
  line-height: 1.5;
  white-space: pre-wrap;
}
.v2-ai-prompts {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.v2-ai-prompt-card {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  background: var(--surface-muted);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
}
.v2-ai-prompt-card__text {
  font-size: 12px;
  color: var(--text);
}

/* --- Modale Aperçu --- */
.v2-preview-title { font-size: 17px; font-weight: 600; color: var(--section-title); margin-bottom: 4px; }
.v2-preview-subtitle { font-size: 13px; color: var(--text-tertiary); font-style: italic; margin-bottom: 8px; }
.v2-preview-meta { font-size: 12px; color: var(--text-tertiary); margin-bottom: 12px; }
.v2-preview-content :first-child { margin-top: 0; }
.v2-preview-field { margin-top: 12px; }

/* --- Modale « Choisir une date » --- */
.v2-schedule-field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
/* ============================================================================
   STORY 18.8 — PAGE EMAILS : campagnes & groupes (handoff §6.2)
   Réécrit le contenu de #emails-content (18.2 avait posé la coquille : segment
   Campagnes/Groupes + routes). Introduit ici, réutilisables par les stories
   suivantes : .v2-input/.v2-select/.v2-textarea (champs de formulaire —
   aucune page v2 n'en avait encore besoin), .v2-tabs (onglets), .v2-stepper
   (jalons). Tout le reste (.v2-campaign-*, .v2-group-*, .v2-emails-*) est
   propre à cette page.
   ============================================================================ */

/* --- Champs de formulaire génériques : le conteneur `.v2-field` est défini
   une seule fois plus haut (§ Administration, « Champs de formulaire »,
   ~l.2102 — story 18.10, réutilisé tel quel ici) ; on n'ajoute ici que les
   variantes propres à cette page (label brut au lieu de .v2-field__label,
   contrôles .v2-input/.v2-select/.v2-textarea au lieu de .v2-field__control). */
.v2-field label {
  font-size: 11px;
  color: var(--text-tertiary);
}
.v2-input,
.v2-select,
.v2-textarea {
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--text);
  background: var(--surface-card);
  border: 1px solid var(--border-bordeaux-15);
  border-radius: var(--radius-sm);
  padding: 8px 11px;
  width: 100%;
}
.v2-textarea {
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.55;
  resize: vertical;
  min-height: 150px;
}
.v2-input:disabled,
.v2-select:disabled,
.v2-textarea:disabled {
  opacity: .6;
  cursor: not-allowed;
}
.v2-field__help {
  margin: 4px 0 0;
  font-size: 11px;
  color: var(--text-tertiary);
}
.v2-field__error {
  margin: 4px 0 0;
  font-size: 12px;
  color: var(--danger-text);
  background: var(--danger-bg);
  border-radius: var(--radius-xs);
  padding: 6px 10px;
}

/* --- Onglets (.v2-tabs), introduits ici (Contenu HTML/Aperçu) -------------- */
.v2-tabs {
  display: flex;
  gap: 12px;
}
.v2-tab {
  border: none;
  background: none;
  padding: 0 0 3px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-tertiary);
  cursor: pointer;
  border-bottom: 2px solid transparent;
}
.v2-tab.is-active {
  color: var(--color-bordeaux);
  border-bottom-color: var(--color-ruby);
}

/* --- Jalons (.v2-stepper), introduit ici (modale campagne) ----------------- */
.v2-stepper {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  flex-wrap: wrap;
}
.v2-stepper__sep { color: var(--text-tertiary); }
.v2-stepper__step { color: var(--text-tertiary); }
.v2-stepper__step[data-state="done"] { color: var(--success); }
.v2-stepper__step[data-state="current"] { color: var(--color-ruby); }

/* --- Barre d'en-tête de page Emails (segment + action contextuelle) ------- */
.v2-emails-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
}
.v2-emails-toolbar .v2-btn { margin-left: auto; }
.v2-emails-status {
  font-size: 13px;
  color: var(--text-tertiary);
  padding: 4px 0 12px;
}
.v2-emails-status--error { color: var(--danger-text); }
.v2-emails-empty {
  text-align: center;
  color: var(--text-tertiary);
  font-size: 13px;
  padding: 24px 0;
}

/* --- Liste mobile (cartes) — le tableau .v2-table-wrap se masque déjà seul
   sous 768px (§3.11) ; .v2-card-list est le pendant mobile, affiché seulement
   sous ce seuil. --- */
.v2-card-list { display: none; flex-direction: column; gap: 10px; }
@media (max-width: 767px) {
  .v2-card-list { display: flex; }
}
.v2-emails-card { cursor: pointer; }
.v2-emails-card__top {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 8px;
}
.v2-emails-card__name { font-weight: 600; font-size: 14px; color: var(--text); }
.v2-emails-card__subject { font-size: 13px; color: var(--text-secondary); margin-top: 2px; }
.v2-emails-card__desc { font-size: 13px; color: var(--text-secondary); margin-top: 2px; }
.v2-emails-card__badges { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 8px; }
.v2-emails-card__stats { margin-top: 8px; font-size: 12px; color: var(--text-tertiary); }
.v2-emails-card__stats b { color: var(--text); font-variant-numeric: tabular-nums; }
.v2-emails-stats-errors { color: var(--danger); }

/* Lignes de tableau cliquables (clic -> modale, §6.2) ; l'état actif réutilise
   .is-selected (déjà stylé par le composant tableau générique, §3.11). */
#email-campaigns-tbody tr,
#email-groups-tbody tr { cursor: pointer; }

/* --- Modale campagne : volet gauche (formulaire) / volet droit (récap, test,
   programmation, envoi) 1fr/300px desktop, empilés sur mobile (§3.9). --- */
.v2-campaign-modal__header { flex-wrap: wrap; row-gap: 6px; }
.v2-campaign-modal__body {
  display: grid;
  grid-template-columns: 1fr 300px;
  padding: 0;
  overflow: hidden;
}
.v2-campaign-modal__main {
  padding: 20px 24px;
  border-right: 1px solid var(--divider);
  overflow-y: auto;
}
.v2-campaign-modal__side {
  padding: 20px 22px;
  background: var(--surface-card);
  display: flex;
  flex-direction: column;
  gap: 12px;
  overflow-y: auto;
}
.v2-campaign-modal__row { display: flex; gap: 10px; }
.v2-campaign-modal__row .v2-field { flex: 1; margin-bottom: 0; }
.v2-campaign-modal__content-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 11px;
  color: var(--text-tertiary);
  margin-bottom: 4px;
}
.v2-campaign-preview {
  border: 1px solid var(--border-bordeaux-15);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  min-height: 150px;
  max-height: 260px;
  overflow: auto;
  background: var(--surface-card);
  font-size: 13px;
  color: var(--text);
}
.v2-campaign-test-banner--pending,
.v2-campaign-test-banner--ok {
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  font-weight: 600;
  font-size: 13px;
}
.v2-campaign-test-banner--pending { background: var(--amber-bg); color: var(--amber-text); }
.v2-campaign-test-banner--ok { background: var(--success-bg); color: var(--success); }
.v2-campaign-recap {
  border: 1px solid var(--divider);
  border-radius: var(--radius-sm);
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.v2-campaign-recap__row {
  display: flex;
  justify-content: space-between;
  font-size: 13px;
  color: var(--text-tertiary);
}
.v2-campaign-recap__row b { color: var(--text); font-variant-numeric: tabular-nums; }
.v2-campaign-schedule-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--text);
  cursor: pointer;
}
.v2-campaign-schedule-toggle input {
  width: 18px;
  height: 18px;
  accent-color: var(--color-ruby);
}
.v2-campaign-irreversible {
  font-size: 12px;
  color: var(--text-tertiary);
  line-height: 1.4;
  margin: 0;
}
.v2-campaign-modal__actions {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-top: 8px;
}
.v2-campaign-modal__actions-row { display: flex; gap: 8px; }
.v2-campaign-modal__actions-row .v2-btn { flex: 1; }
@media (max-width: 767px) {
  /* Empilé (§3.9 "mobile : sous le formulaire") : un seul scroll pour
     l'ensemble main+side plutôt que deux mini-scrolls indépendants (chacun
     ne recevant qu'une fraction de la hauteur en grille à 2 lignes) — sinon
     les boutons "Renvoyer un test"/"Enregistrer" du bas du volet droit
     restent inaccessibles (repérage Playwright : rendu tronqué, contenu
     bien présent en DOM mais coupé par le overflow interne du volet). */
  .v2-campaign-modal__body {
    grid-template-columns: 1fr;
    overflow-y: auto;
  }
  .v2-campaign-modal__main,
  .v2-campaign-modal__side {
    overflow: visible;
  }
  .v2-campaign-modal__main { border-right: none; border-bottom: 1px solid var(--divider); }
  .v2-campaign-modal__actions { margin-top: 16px; }
}

/* --- Modale groupe : segment Tous/Dans le groupe + recherche + liste à cocher
   (remplace la double liste avec flèches). --- */
.v2-group-checklist {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 320px;
  overflow-y: auto;
  margin-top: 4px;
}
.v2-group-check-item {
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--surface-card);
  border-radius: var(--radius-card);
  padding: 12px 14px;
  box-shadow: var(--shadow-card);
  cursor: pointer;
}
.v2-group-checkbox {
  width: 22px;
  height: 22px;
  border-radius: var(--radius-xs);
  border: 1.5px solid var(--border-bordeaux-35);
  flex: none;
  display: grid;
  place-items: center;
  font-size: 13px;
  color: var(--text-inverse);
}
.v2-group-check-item.is-checked .v2-group-checkbox {
  background: var(--color-ruby);
  border-color: var(--color-ruby);
}
.v2-group-check-item__info { flex: 1; min-width: 0; }
.v2-group-check-item__email {
  font-weight: 600;
  font-size: 14px;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.v2-group-check-item__meta { font-size: 12px; color: var(--text-tertiary); margin-top: 2px; }
.v2-group-more {
  font-size: 12px;
  color: var(--text-tertiary);
  text-align: center;
  padding: 8px 0 0;
  margin: 0;
}
/* ============================================================================
   STORY 18.6 — FEEDBACK (handoff §5.2, inventaire §9). Segment d'en-tête
   Avis/Bugs (rendu par updateFeedbackHeaderChrome(), admin.js — même mécanique
   paresseuse que le segment Liste/Colonnes d'Idées, 18.5), stats en tuiles
   compactes, filtres en puces, cartes mobile (commentaire intégral) / tableau
   desktop. Page strictement lecture seule (aucun statut de traitement).
   Remplace tout le CSS legacy `.feedback-subtabs`/`.subtab`/cartes CSS-only
   `#reviews-table`/`#bugs-table` (retiré plus haut).
   ============================================================================ */

/* --- Segment d'en-tête Avis N / Bugs N — visible mobile ET desktop
   (contrairement au segment Idées, les 2 sous-pages ont chacune leur propre
   contenu sur les deux largeurs). Sur mobile il remplace la zone « à jour »/↻
   générique (handoff §2.2 : « Certaines pages remplacent la zone droite par un
   segment (…, Avis/Bugs) »— même trade-off que 18.5, ↻ restant disponible en
   desktop et le rechargement auto >5 min couvrant le mobile). --- */
.v2-feedback-view-segment {
  display: none;
}
.v2-feedback-view-segment.is-visible {
  display: flex;
}
@media (min-width: 768px) {
  /* Re-habillage clair : la variante --on-bordeaux (fond blanc translucide)
     n'a de sens que sur l'en-tête mobile bordeaux ; en desktop l'en-tête est
     clair/transparent (§2.4). */
  .v2-feedback-view-segment.v2-segment--on-bordeaux {
    background: var(--surface-muted);
    color: var(--text-secondary);
  }
  .v2-feedback-view-segment.v2-segment--on-bordeaux .v2-segment__option.is-active {
    background: var(--surface-card);
    color: var(--color-bordeaux);
  }
}

/* --- Stats : 4 tuiles compactes mobile (grid 4 col, valeur 20px/libellé 10px),
   cartes KPI normales en desktop (§3.2, réutilise .v2-card.v2-kpi). --- */
.v2-feedback-stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  margin-bottom: 12px;
}
.v2-feedback-stats .v2-card {
  padding: 8px 6px;
  text-align: center;
}
.v2-feedback-stats .v2-kpi__value {
  font-size: 20px;
  margin-top: 2px;
}
.v2-feedback-stats .v2-kpi__label {
  font-size: 10px;
}
@media (min-width: 768px) {
  .v2-feedback-stats {
    gap: 12px;
  }
  .v2-feedback-stats .v2-card {
    padding: 16px 18px;
    text-align: left;
  }
  .v2-feedback-stats .v2-kpi__value {
    font-size: 30px;
    margin-top: 4px;
  }
  .v2-feedback-stats .v2-kpi__label {
    font-size: 12px;
  }
}

/* --- Filtres en puces (§3.6) — « Note ▾ » et « iOS · Android » sont des
   <select> natifs stylés en puce (comportement natif conservé, look pill). --- */
.v2-feedback-filters {
  margin-bottom: 10px;
}
select.v2-pill {
  appearance: auto;
  font-family: var(--font-sans);
}
.v2-feedback-search {
  display: block;
  width: 100%;
  box-sizing: border-box;
  margin-bottom: 12px;
}

/* --- Bandeau d'erreur inline (partagé Feedback/Tests, tokens uniquement —
   remplace .error-text legacy, couleurs en dur, non repris ici). --- */
.v2-error-banner {
  background: var(--danger-bg);
  color: var(--danger-text);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  font-size: 13px;
  margin-bottom: 12px;
}

/* --- Mobile — liste de cartes (masquée desktop, le tableau prend le relais
   via .v2-table-wrap qui s'auto-masque déjà <768px). --- */
.v2-feedback-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 12px;
}
@media (min-width: 768px) {
  .v2-feedback-list {
    display: none;
  }
}
.v2-feedback-card__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 6px;
}
.v2-feedback-stars {
  letter-spacing: 1px;
  font-size: 14px;
  color: var(--star-empty);
}
.v2-feedback-star.is-filled {
  color: var(--star-filled);
}
.v2-feedback-card__meta {
  flex: none;
  font-size: 11px;
  color: var(--text-tertiary);
  white-space: nowrap;
}
.v2-feedback-card__comment {
  font-size: 14px;
  line-height: 1.45;
  color: var(--text);
  margin: 0 0 8px;
  white-space: pre-wrap;
  word-break: break-word;
}
.v2-feedback-card__comment--empty {
  font-style: italic;
  color: var(--text-tertiary);
}
.v2-feedback-card__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.v2-feedback-card__user {
  font-size: 12px;
  color: var(--text-secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.v2-feedback-card__user-name {
  color: var(--text-tertiary);
}
.v2-feedback-empty {
  color: var(--text-tertiary);
  font-size: 13px;
  font-style: italic;
  text-align: center;
  padding: 16px 0;
}

/* --- Desktop — tableau (.v2-table réutilisé tel quel, §3.11) : commentaire
   intégral, jamais tronqué (pas d'info-bulle, tout le texte est visible). --- */
.v2-feedback-comment-cell {
  max-width: 420px;
  white-space: pre-wrap;
  word-break: break-word;
  line-height: 1.45;
}

.v2-feedback-readonly-note {
  font-size: 11px;
  color: var(--text-tertiary);
  margin: 8px 0;
}
.v2-feedback-filter-summary {
  font-size: 12px;
  color: var(--text-secondary);
  margin: 0 0 8px;
}
.v2-feedback-filter-summary__reset {
  border: none;
  background: none;
  color: var(--color-ruby);
  font-weight: 600;
  font-size: 12px;
  cursor: pointer;
  padding: 0;
  margin-left: 4px;
}
.v2-feedback-pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin-top: 8px;
}
.v2-feedback-pagination__info {
  font-size: 12px;
  color: var(--text-tertiary);
}

/* ============================================================================
   STORY 18.6 — TESTS DE RECETTE (handoff §5.3, inventaire §13). Carte de
   progression globale (nouveau, calculé côté client), puces de filtre par
   statut, board de cartes, détail plein écran mobile / modale 640px desktop
   (pas de volet fil, donc plus simple qu'un plein écran systématique).
   Captures/lightbox : logique JS Epic 17 lot 2 inchangée, restylée ici.
   Remplace tout le CSS legacy `.tests-*`/`#scenario-lightbox-*` (retiré plus haut).
   ============================================================================ */

.v2-tests-intro {
  color: var(--text-tertiary);
  font-size: 13px;
  margin: 0 0 10px;
}
.v2-tests-count {
  display: block;
  font-size: 12px;
  color: var(--text-tertiary);
  margin: 10px 0 8px;
}

/* --- Carte de progression globale (nouveau) --- */
.v2-tests-progress__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 8px;
}
.v2-tests-progress__title {
  font-size: 15px;
  font-weight: 600;
  color: var(--section-title);
}
.v2-tests-progress__count {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-secondary);
  white-space: nowrap;
}
.v2-tests-progress__bar {
  height: 10px;
  border-radius: 5px;
  background: var(--surface-muted-2);
  display: flex;
  overflow: hidden;
}
.v2-tests-progress__bar--sm {
  height: 6px;
  border-radius: 3px;
  flex: 1;
}
.v2-tests-progress__seg--ok { background: var(--success); }
.v2-tests-progress__seg--ko { background: var(--danger); }
.v2-tests-progress__legend {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 8px;
  font-size: 11px;
  color: var(--text-tertiary);
}
.v2-tests-progress__legend-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  margin-right: 4px;
  vertical-align: middle;
}
.v2-tests-progress__legend-dot--ok { background: var(--success); }
.v2-tests-progress__legend-dot--ko { background: var(--danger); }
.v2-tests-progress__legend-dot--pending { background: var(--surface-muted-2); border: 1px solid var(--border-bordeaux-20); }

/* --- Puces de filtre par statut --- */
.v2-tests-status-pills {
  margin: 12px 0;
}

/* --- Board de cartes (une seule vue, pas de colonnes par statut, §13.1) --- */
.v2-tests-board {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.v2-tests-card {
  flex: 1 1 260px;
  max-width: 360px;
  cursor: pointer;
}
@media (max-width: 767px) {
  .v2-tests-card {
    flex-basis: 100%;
    max-width: none;
  }
}
.v2-tests-card__title-row {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin-bottom: 8px;
}
.v2-tests-card__shortid {
  font-size: 12px;
  font-weight: 700;
  color: var(--text-tertiary);
  flex: none;
}
.v2-tests-card__title {
  font-weight: 600;
  color: var(--text);
  flex: 1;
  min-width: 0;
}
.v2-tests-card__badge {
  flex: none;
}
.v2-tests-card__counter {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-secondary);
  margin: 8px 0 4px;
}
.v2-tests-card__tickets {
  margin-top: 4px;
}

/* --- Chips tickets liés (réutilise .v2-chip, cliquables → #/idees/N) --- */
.v2-chip--clickable {
  cursor: pointer;
}
.v2-chip--clickable:hover {
  background: var(--border-bordeaux-20);
}

/* --- Détail : modale 640px desktop (pas de volet fil → plus simple qu'un
   plein écran systématique, cohérent avec les modales « content-based »
   type campagne 760/aperçu 700) / plein écran mobile (générique .v2-modal). --- */
.v2-modal--tests-detail {
  max-width: 640px;
}
.v2-tests-detail__header {
  gap: 8px;
}
.v2-tests-detail__back {
  font-size: 20px;
  width: 32px;
  flex: none;
  border: none;
  background: none;
  color: var(--section-title);
  cursor: pointer;
}
.v2-tests-detail__close-desktop {
  display: none;
}
.v2-tests-detail__title-block {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: baseline;
  gap: 8px;
}
.v2-tests-detail__shortid {
  font-size: 13px;
  font-weight: 700;
  color: var(--color-ruby);
  flex: none;
}
.v2-tests-detail__title {
  font-size: 15px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.v2-tests-detail__save-status {
  flex: none;
  font-size: 11px;
  color: var(--text-tertiary);
  white-space: nowrap;
}
.v2-tests-detail__save-status.error {
  color: var(--danger-text);
}
@media (min-width: 768px) {
  .v2-tests-detail__back {
    display: none;
  }
  .v2-tests-detail__close-desktop {
    display: inline-flex;
  }
}
.v2-tests-detail__description {
  white-space: pre-wrap;
  color: var(--text);
  font-size: 13px;
  margin: 0 0 12px;
}
.v2-tests-detail__status-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
  flex-wrap: wrap;
}
.v2-tests-status-select {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  color: var(--section-title);
  background: var(--surface-card);
  border: 1px solid var(--border-bordeaux-15);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
  min-height: 44px;
  flex: none;
}
.v2-tests-detail__progress {
  flex: 1;
  min-width: 120px;
  display: flex;
  align-items: center;
  gap: 8px;
}
.v2-tests-detail__progress-text {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-secondary);
  white-space: nowrap;
}
.v2-tests-tickets {
  margin-bottom: 12px;
}

/* --- Items --- */
.v2-tests-items {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.v2-tests-item {
  background: var(--surface-card);
  border: 1px solid var(--divider);
  border-radius: var(--radius-md);
  padding: 12px;
}
.v2-tests-item.is-ko {
  outline: 2px solid var(--danger);
}
.v2-tests-item.is-ok {
  opacity: .7;
}
.v2-tests-item__index {
  font-size: 12px;
  color: var(--text-tertiary);
  margin-bottom: 2px;
}
.v2-tests-item-label {
  font-weight: 600;
  color: var(--text);
  font-size: 14px;
  margin-bottom: 8px;
  white-space: pre-wrap;
}
.v2-tests-segmented {
  margin-bottom: 8px;
}
.v2-tests-item-comment {
  width: 100%;
  box-sizing: border-box;
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--text);
  background: var(--surface-card);
  border: 1px solid var(--border-bordeaux-15);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
  resize: vertical;
  margin-bottom: 8px;
}

/* --- Captures : miniatures 72×72 + bouton d'ajout pointillé + compteur
   toujours visible (nouveau) --- */
.v2-tests-attachments {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
.v2-tests-attachment-thumb {
  position: relative;
  flex: none;
  width: 72px;
  height: 72px;
  border: 1px solid var(--border-bordeaux-15);
  border-radius: 10px;
  overflow: hidden;
  background: var(--surface-muted-2);
  cursor: pointer;
  padding: 0;
}
.v2-tests-attachment-thumb img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.v2-tests-attachment-thumb:hover {
  border-color: var(--color-ruby);
}
.v2-tests-attachment-remove {
  position: absolute;
  top: 3px;
  right: 3px;
  width: 22px;
  height: 22px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: var(--thumb-overlay);
  color: var(--text-inverse);
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.v2-tests-attachment-remove:hover {
  background: var(--color-ruby);
}
.v2-tests-attachment-add {
  position: relative;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 72px;
  height: 72px;
  border: 1.5px dashed var(--border-bordeaux-35);
  border-radius: 10px;
  background: transparent;
  color: var(--text-tertiary);
  font-size: 20px;
  cursor: pointer;
  overflow: hidden;
}
.v2-tests-attachment-add:hover {
  border-color: var(--color-ruby);
  color: var(--color-ruby);
}
.v2-tests-attachment-add input[type="file"] {
  position: absolute;
  inset: 0;
  opacity: 0;
  font-size: 0;
  cursor: pointer;
}
.v2-tests-attachment-count {
  flex-basis: 100%;
  font-size: 11px;
  color: var(--text-tertiary);
}
.v2-tests-attachment-progress {
  flex-basis: 100%;
  min-height: 14px;
  font-size: 11px;
  color: var(--text-secondary);
  margin-top: 4px;
}

/* --- Lightbox plein écran — au-dessus de la modale (z-index modale = 3000). --- */
.v2-tests-lightbox {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 3100;
  background: var(--lightbox-overlay);
  align-items: center;
  justify-content: center;
  padding: 24px;
  box-sizing: border-box;
}
.v2-tests-lightbox.active {
  display: flex;
}
.v2-tests-lightbox__img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-modal);
}
.v2-tests-lightbox__close {
  position: fixed;
  top: 16px;
  right: 16px;
  width: 44px;
  height: 44px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: var(--lightbox-btn-bg);
  color: var(--text-inverse);
  font-size: 26px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.v2-tests-lightbox__close:hover {
  background: var(--lightbox-btn-bg-hover);
}
/* ============================================================================
   STORY 18.9 — UTILISATEURS & ABONNEMENTS (handoff §7, écrans 2d/3e).
   Remplace tout le CSS legacy exclusif à #users-content (`.bulk-actions-container`,
   `.selected-count` — 1re occurrence, `.bulk-action-btn`, `.user-select-cb`,
   `#select-all-users`, `.subscription-badge*`, retirés plus haut). Les classes
   `.users-toolbar` / `.users-search-input` / `.users-count` / `.users-loading` /
   `.users-pagination` / `.users-page-info` / `.action-btn` / `.admin-table` /
   `.table-scroll` restent en place : partagées avec Articles/Emails/Feedback/
   Légal, non migrées par cette story.
   ============================================================================ */

/* --- Case à cocher (carte mobile, ligne desktop, en-tête « sélectionner tout »,
   état indéterminé). Un seul composant pour les 3 contextes. --- */
.v2-user-checkbox {
  appearance: none;
  -webkit-appearance: none;
  width: 20px;
  height: 20px;
  border-radius: 5px;
  border: 1.5px solid var(--border-bordeaux-35);
  background: var(--surface-card);
  margin: 0;
  flex: none;
  cursor: pointer;
  display: inline-grid;
  place-items: center;
}
.v2-user-checkbox:checked,
.v2-user-checkbox:indeterminate {
  background: var(--color-ruby);
  border-color: var(--color-ruby);
}
.v2-user-checkbox:checked::after { content: '✓'; color: var(--text-inverse); font-size: 12px; line-height: 1; }
.v2-user-checkbox:indeterminate::after { content: '–'; color: var(--text-inverse); font-size: 12px; line-height: 1; }

/* --- En-tête desktop : recherche + Abonnement ▾ (avant « à jour »/↻), injectés
   par ensureUsersDesktopFilters() — jamais affichés en mobile (ces contrôles
   vivent alors dans le contenu de page, .v2-users-mobile-toolbar). --- */
.v2-users-header-filters {
  display: none;
}
@media (min-width: 768px) {
  .v2-users-header-filters.is-visible {
    display: flex;
    align-items: center;
    gap: 8px;
  }
  .v2-users-header-filters .v2-users-search-input { width: 220px; }
}
.v2-users-search-input {
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--text);
  background: var(--surface-card);
  border: 1px solid var(--border-bordeaux-15);
  border-radius: var(--radius-sm);
  padding: 9px 12px;
}
.v2-users-filter-select {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  color: var(--section-title);
  background: var(--surface-card);
  border: 1px solid var(--border-bordeaux-20);
  border-radius: var(--radius-pill);
  padding: 6px 12px;
  flex: none;
}

/* --- Bouton « Sélect. » (en-tête mobile bordeaux, injecté par
   ensureUsersMobileSelectToggle()) — remplace « à jour »/↻ sur cette page,
   comme les segments Idées/Administration (§2.2). --- */
.v2-users-select-toggle {
  display: none;
  border: 1px solid var(--border-inverse-25);
  background: transparent;
  color: var(--text-inverse);
  font-size: 13px;
  font-weight: 600;
  border-radius: var(--radius-md);
  padding: 8px 12px;
  cursor: pointer;
  font-family: var(--font-sans);
}
.v2-users-select-toggle.is-visible { display: inline-flex; }
@media (min-width: 768px) {
  .v2-users-select-toggle { display: none !important; }
}

/* --- Mobile uniquement : recherche + puces Tri/Abonnement/Actifs (contenu de
   page). Tri/Abonnement rendus en <select> stylés en pilule (contrôle natif
   accessible) plutôt qu'un popover maison. --- */
.v2-users-mobile-toolbar {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 12px;
}
@media (min-width: 768px) {
  .v2-users-mobile-toolbar { display: none; }
}
.v2-users-mobile-controls {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

/* --- Barre de sélection (§7) : mobile fixe en bas (remplace la barre flottante
   d'idée via setFloatingBarHidden(), story 18.3), desktop inline au-dessus du
   tableau. Masquée tant que la sélection est vide. --- */
.v2-users-selection-bar {
  display: none;
  align-items: center;
  gap: 10px;
  background: var(--color-bordeaux);
  color: var(--text-inverse);
  border-radius: var(--radius-lg);
  padding: 8px 8px 8px 16px;
  font-size: 13px;
}
.v2-users-selection-bar.is-visible { display: flex; }
.v2-users-selection-bar__count { font-weight: 600; flex: none; }
.v2-users-selection-bar__meta { opacity: .75; flex: 1; min-width: 0; }
.v2-users-selection-bar__clear {
  border: none;
  background: none;
  color: inherit;
  text-decoration: underline;
  font: inherit;
  cursor: pointer;
  padding: 0;
}
.v2-users-selection-bar__cta {
  border: none;
  background: var(--surface-inverse);
  color: var(--color-bordeaux);
  border-radius: var(--radius-sm);
  padding: 8px 12px;
  font-weight: 600;
  font-size: 13px;
  cursor: pointer;
  flex: none;
  font-family: var(--font-sans);
  white-space: nowrap;
}
@media (max-width: 767px) {
  .v2-users-selection-bar.is-visible {
    position: fixed;
    left: 12px;
    right: 12px;
    bottom: calc(14px + env(safe-area-inset-bottom, 0px));
    z-index: 30;
    box-shadow: var(--shadow-float);
  }
}
@media (min-width: 768px) {
  .v2-users-selection-bar.is-visible {
    margin-bottom: 14px;
    border-radius: var(--radius-md);
    padding: 10px 16px;
  }
}

/* --- Mobile — liste de cartes (remplace le tableau, masquée dès 768px : le
   `.v2-table-wrap` desktop se masque déjà lui-même sous 768px, voir §3.11). --- */
.v2-users-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
@media (min-width: 768px) {
  .v2-users-list { display: none; }
}
.v2-users-empty {
  color: var(--text-tertiary);
  font-size: 13px;
  text-align: center;
  padding: 24px 0;
}
.v2-user-card {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  cursor: pointer;
}
.v2-user-card__body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.v2-user-card__top {
  display: flex;
  justify-content: space-between;
  gap: 8px;
}
.v2-user-card__id { min-width: 0; }
.v2-user-card__email {
  font-weight: 600;
  font-size: 14px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.v2-user-card__meta {
  font-size: 12px;
  color: var(--text-tertiary);
  margin-top: 2px;
}

/* --- Desktop — tableau 9 colonnes (.v2-table, composant §3.11) --- */
.v2-table--users td.numeric-cell,
.v2-table--users th.numeric-cell { text-align: right; }
.v2-table td.numeric-cell { font-variant-numeric: tabular-nums; }
.v2-table__checkbox-cell { width: 40px; text-align: center; }
.v2-users-row-action {
  border: none;
  background: none;
  /* --section-title (pas --color-bordeaux, fixe) : a un pendant sombre — sinon
     l'icône devient quasi illisible sur fond sombre (même correctif que le
     titre de page/les puces de filtre Idées, story 18.2/18.5). */
  color: var(--section-title);
  font-weight: 600;
  font-size: 16px;
  cursor: pointer;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-xs);
}
.v2-users-row-action:hover { background: var(--surface-muted); }

/* --- Modale individuelle « Gestion abonnement » (§6.3) : contenu fonctionnel
   inchangé, restylé sur .v2-modal / .v2-users-*. --- */
.v2-users-modal-subtext {
  color: var(--text-tertiary);
  font-size: 13px;
  margin-top: 4px;
}
.v2-users-manual-status {
  background: var(--success-bg);
  color: var(--success);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  font-size: 13px;
  margin-top: 14px;
}
.v2-users-manual-status strong { color: inherit; }
.v2-users-form { display: flex; flex-direction: column; }
.v2-users-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 12px;
}
.v2-users-field label,
.v2-users-field__label {
  font-size: 12px;
  color: var(--text-tertiary);
}
.v2-users-field input,
.v2-users-field select,
.v2-users-field textarea {
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--text);
  background: var(--surface-card);
  border: 1px solid var(--border-bordeaux-15);
  border-radius: var(--radius-sm);
  padding: 9px 12px;
}
.v2-users-field textarea { resize: vertical; }
.v2-users-field__aux { margin-top: 8px; }
.v2-users-modal-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 16px;
}
.v2-users-modal-error { color: var(--danger-text); font-size: 13px; margin-top: 10px; }
.v2-users-modal-success { color: var(--success); font-size: 13px; margin-top: 10px; }
.v2-users-history {
  margin-top: 14px;
  font-size: 13px;
}
.v2-users-history summary {
  cursor: pointer;
  font-weight: 600;
  /* --section-title (pas --color-bordeaux, fixe) : la modale a un fond
     --bg (pas --surface-card) en sombre — même correctif que ci-dessus. */
  color: var(--section-title);
}
.v2-users-history-row {
  padding: 8px 0;
  border-bottom: 1px solid var(--divider);
  color: var(--text);
}
.v2-users-history-row:last-child { border-bottom: none; }
.v2-users-history-row__reason {
  color: var(--text-tertiary);
  font-size: 12px;
  margin-top: 2px;
}

/* --- Modale « Passer Premium (lot) » (§6.4, .v2-modal--bulk 520px) : 2 vues
   (formulaire / résultat) togglées en JS, mêmes classes .v2-users-field/-form
   ci-dessus pour le formulaire. --- */
.v2-users-modal-note {
  font-size: 12px;
  color: var(--text-tertiary);
  margin-top: 12px;
  line-height: 1.45;
}
.v2-users-result-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 12px;
}
.v2-users-result-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  background: var(--surface-card);
  border-radius: var(--radius-md);
  padding: 10px 14px;
  box-shadow: var(--shadow-card);
  font-size: 13px;
}
.v2-users-result-row--error { outline: 1px solid var(--danger-bg); }
.v2-users-result-row__icon { flex: none; font-weight: 600; }
.v2-users-result-row--success .v2-users-result-row__icon { color: var(--success); }
.v2-users-result-row--amber .v2-users-result-row__icon { color: var(--amber-text); }
.v2-users-result-row--error .v2-users-result-row__icon { color: var(--danger); }
.v2-users-result-row__text { flex: 1; min-width: 0; }
.v2-users-result-row__primary {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.v2-users-result-row__secondary {
  font-size: 11px;
  color: var(--text-tertiary);
  margin-top: 2px;
}
.v2-users-result-row--error .v2-users-result-row__secondary { color: var(--danger-text); }
