    [x-cloak] { display: none !important; }

    /* ════════════════════════════════════════════════════════════════════════
       DESIGN TOKENS — système sémantique complet
       Une seule source de vérité pour couleurs, surfaces, ombres, rayons
       ════════════════════════════════════════════════════════════════════════ */
    :root {
      /* ── Surfaces ──────────────────────────────────────────────────── */
      --bg-app:           linear-gradient(135deg, #f7fafc 0%, #eef4f8 52%, #f8f2f0 100%);
      --bg-section:       linear-gradient(135deg, rgba(255,255,255,.82) 0%, rgba(239,247,250,.74) 56%, rgba(255,247,242,.76) 100%);
      --bg-elevated:      rgba(255, 255, 255, 0.74);
      --bg-elevated-strong: rgba(255, 255, 255, 0.92);
      --bg-input:         rgba(255, 255, 255, 0.75);
      --bg-overlay:       rgba(99, 102, 241, 0.04);
      --bg-hover:         rgba(99, 102, 241, 0.08);
      /* Clair, comme les infobulles natives (title="") que le navigateur
         dessine lui-même et que la page ne peut pas restyler : une bulle
         sombre était la seule à détonner sur un fond d'app clair. */
      --bg-tooltip:       rgba(255, 255, 255, 0.97);

      /* ── Bordures ─────────────────────────────────────────────────── */
      --border-default:   rgba(255, 255, 255, 0.65);
      --border-subtle:    rgba(99, 102, 241, 0.14);
      --border-strong:    rgba(99, 102, 241, 0.28);

      /* ── Texte ────────────────────────────────────────────────────── */
      --text-primary:     #1a1f3a;
      --text-secondary:   #5a6184;
      --text-muted:       #8b92b3;
      --text-disabled:    #c0c4d8;
      --text-inverse:     #ffffff;

      /* ── Couleurs sémantiques ────────────────────────────────────── */
      --semantic-good:        #0ea968;
      --semantic-good-soft:   rgba(14, 169, 104, 0.12);
      --semantic-good-line:   rgba(14, 169, 104, 0.35);
      --semantic-warn:        #d97706;
      --semantic-warn-soft:   rgba(217, 119, 6, 0.12);
      --semantic-warn-line:   rgba(217, 119, 6, 0.35);
      /* Encre warn — réservée au texte ambre posé sur un fond DÉJÀ ambré (badges
         en bg-amber-500/15-/20). Là, --semantic-warn tombe à 2,5:1 parce que le
         fond porte la même teinte que le texte ; il faut donc descendre. Sur les
         surfaces claires ordinaires, c'est --semantic-warn qui s'applique. */
      --semantic-warn-ink:    #92400e;
      --semantic-danger:      #dc2626;
      --semantic-danger-soft: rgba(220, 38, 38, 0.12);
      --semantic-danger-line: rgba(220, 38, 38, 0.35);
      --semantic-cool:        #2563eb;
      --semantic-cool-soft:   rgba(37, 99, 235, 0.12);
      --semantic-cool-line:   rgba(37, 99, 235, 0.35);
      --semantic-accent:      #5547d8;
      --semantic-accent-soft: rgba(85, 71, 216, 0.10);
      --semantic-accent-line: rgba(85, 71, 216, 0.28);
      --semantic-info:        #0891b2;
      --semantic-info-soft:   rgba(8, 145, 178, 0.12);
      /* Encres pastel — même rôle que --semantic-warn-ink, pour les familles
         cyan / rose / emerald / violet / pink. Les nuances -200 à -500 de
         Tailwind sont prévues pour un fond sombre ; sur nos surfaces claires
         elles tombent sous 2:1. Ces valeurs ne sont pas nouvelles : ce sont
         celles que .anom-badge, .journal-badge et .wb-tab répétaient déjà en
         littéral. Les promouvoir en tokens permet de les appliquer une fois
         pour toute l'interface au lieu d'un correctif par écran.
         Contrastes sur carte blanche : cyan 7,3:1 · rose 8,0:1 · emerald 7,7:1. */
      --semantic-info-ink:    #155e75;
      --semantic-danger-ink:  #9f1239;
      --semantic-good-ink:    #065f46;
      --semantic-accent-ink:  #5b21b6;
      --semantic-pink-ink:    #9d174d;
      --ink-strong:           #111827;
      --ink-panel:            #172033;
      --signal-cyan:          #06b6d4;
      --signal-blue:          #2563eb;
      --signal-amber:         #f59e0b;
      --signal-rose:          #f43f5e;

      /* ── Ombres ──────────────────────────────────────────────────── */
      --shadow-sm:  0 4px 12px rgba(99, 102, 241, 0.08), 0 1px 2px rgba(99, 102, 241, 0.04);
      --shadow-md:  0 8px 24px rgba(99, 102, 241, 0.12), 0 2px 4px rgba(99, 102, 241, 0.06);
      --shadow-lg:  0 14px 36px rgba(99, 102, 241, 0.18), 0 2px 4px rgba(99, 102, 241, 0.06);
      --shadow-glow: 0 0 0 2px rgba(99, 102, 241, 0.25);

      /* ── Glass effect ────────────────────────────────────────────── */
      --glass-blur:       blur(24px) saturate(160%);
      --glass-blur-soft:  blur(12px) saturate(140%);

      /* ── Rayons ──────────────────────────────────────────────────── */
      --radius-xs: 6px;
      --radius-sm: 10px;
      --radius-md: 14px;
      --radius-lg: 20px;
      --radius-xl: 24px;
      --radius-pill: 999px;

      /* ── Gradients KPI ──────────────────────────────────────────── */
      --kpi-grad-neutral: linear-gradient(135deg, #1f2937 0%, #4f46e5 100%);
      --kpi-grad-danger:  linear-gradient(135deg, #dc2626 0%, #ec4899 100%);
      --kpi-grad-cool:    linear-gradient(135deg, #2563eb 0%, #06b6d4 100%);
      --kpi-grad-warn:    linear-gradient(135deg, #d97706 0%, #f59e0b 100%);
      --kpi-grad-good:    linear-gradient(135deg, #0ea968 0%, #10b981 100%);
    }

    /* ════════════════════════════════════════════════════════════════════════
       BASE — fond app + typographie
       ════════════════════════════════════════════════════════════════════════ */
    body {
      font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
      background: var(--bg-app);
      background-attachment: fixed;
      color: var(--text-primary);
      min-height: 100vh;
    }
    body::before {
      content: '';
      position: fixed;
      inset: 0;
      z-index: 0;
      pointer-events: none;
      background:
        radial-gradient(900px 340px at 92% 7%, rgba(6, 182, 212, .10), transparent 62%),
        radial-gradient(760px 420px at 12% 88%, rgba(245, 158, 11, .08), transparent 62%),
        linear-gradient(90deg, rgba(17, 24, 39, .025) 1px, transparent 1px),
        linear-gradient(180deg, rgba(17, 24, 39, .025) 1px, transparent 1px);
      background-size: auto, auto, 44px 44px, 44px 44px;
    }
    body > * { position: relative; z-index: 1; }
    pre { white-space: pre-wrap; word-break: break-word; font-family: ui-monospace, SFMono-Regular, monospace; }

    /* ════════════════════════════════════════════════════════════════════════
       COMPOSANTS — cartes, boutons, tabs, inputs
       ════════════════════════════════════════════════════════════════════════ */
    .card {
      background: var(--bg-elevated);
      backdrop-filter: var(--glass-blur);
      -webkit-backdrop-filter: var(--glass-blur);
      border: 1px solid var(--border-default);
      border-radius: var(--radius-md);
      box-shadow: var(--shadow-sm);
    }
    .csuite-card {
      background: var(--bg-elevated);
      backdrop-filter: var(--glass-blur);
      -webkit-backdrop-filter: var(--glass-blur);
      border: 1px solid var(--border-default);
      border-radius: var(--radius-lg);
      box-shadow: var(--shadow-sm);
      transition: transform .25s cubic-bezier(.2,.8,.2,1), box-shadow .25s ease;
    }
    .csuite-card:hover { box-shadow: var(--shadow-md); }
    .stat-card {
      background: linear-gradient(135deg, rgba(255,255,255,.7) 0%, rgba(255,255,255,.5) 100%);
      backdrop-filter: var(--glass-blur);
      -webkit-backdrop-filter: var(--glass-blur);
      border: 1px solid var(--border-default);
      border-radius: var(--radius-md);
    }

    /* Badges sémantiques */
    .badge-critique  { background: var(--semantic-danger-soft); color: var(--semantic-danger); border: 1px solid var(--semantic-danger-line); }
    .badge-attention { background: var(--semantic-warn-soft);   color: var(--semantic-warn);   border: 1px solid var(--semantic-warn-line); }
    .badge-info      { background: var(--semantic-info-soft);   color: var(--semantic-info);   border: 1px solid rgba(8,145,178,.35); }
    .badge-ok        { background: var(--semantic-good-soft);   color: var(--semantic-good);   border: 1px solid var(--semantic-good-line); }

    /* Tabs */
    .tab-active   { background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%); color: var(--text-inverse); box-shadow: var(--shadow-sm); }
    .tab-inactive { background: var(--bg-elevated); backdrop-filter: var(--glass-blur-soft); -webkit-backdrop-filter: var(--glass-blur-soft); color: var(--text-secondary); border: 1px solid var(--border-default); }
    .tab-inactive:hover { background: var(--bg-elevated-strong); color: var(--text-primary); }

    /* Inputs (on garde le nom .input-dark pour compat) */
    .input-dark {
      background: var(--bg-input);
      backdrop-filter: var(--glass-blur-soft);
      -webkit-backdrop-filter: var(--glass-blur-soft);
      border: 1px solid var(--border-strong);
      color: var(--text-primary);
      border-radius: var(--radius-sm);
      padding: 8px 12px;
      transition: border-color .2s, box-shadow .2s;
    }
    .input-dark::placeholder { color: var(--text-muted); }
    .input-dark:focus {
      outline: none;
      border-color: var(--semantic-accent);
      box-shadow: var(--shadow-glow);
    }

    /* Boutons */
    /* Curseur main sur TOUT ce qui se clique. Les classes .btn-* le posaient déjà,
       mais des dizaines de <button> sans classe (onglets, bascules, puces de
       filtre, lignes cliquables) gardaient la flèche par défaut : rien ne
       signalait qu'ils étaient actionnables. Un bouton désactivé, lui, doit dire
       qu'il ne l'est pas. */
    button:not(:disabled),
    [role="button"]:not(:disabled),
    summary,
    label:has(> input[type="checkbox"]),
    .cursor-pointer {
      cursor: pointer;
    }
    button:disabled,
    input:disabled,
    [role="button"][disabled] {
      cursor: not-allowed;
    }
    /* Poignées de curseur : on les saisit, on ne clique pas dessus. */
    input[type="range"]:not(:disabled) { cursor: grab; }
    input[type="range"]:active { cursor: grabbing; }

    .btn-primary {
      background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%);
      color: var(--text-inverse);
      padding: 9px 20px;
      border-radius: var(--radius-sm);
      font-weight: 600;
      box-shadow: var(--shadow-sm);
      transition: transform .15s, box-shadow .2s, opacity .2s;
      cursor: pointer;
      border: none;
    }
    .btn-primary:hover:not(:disabled) { transform: translateY(-1px); box-shadow: var(--shadow-md); }
    .btn-primary:active { transform: translateY(0); }
    .btn-secondary {
      background: var(--bg-elevated);
      backdrop-filter: var(--glass-blur-soft);
      -webkit-backdrop-filter: var(--glass-blur-soft);
      color: var(--text-primary);
      padding: 9px 20px;
      border-radius: var(--radius-sm);
      font-weight: 600;
      border: 1px solid var(--border-strong);
      box-shadow: var(--shadow-sm);
      transition: transform .15s, box-shadow .2s, background .2s;
      cursor: pointer;
    }
    .btn-secondary:hover:not(:disabled) {
      background: var(--bg-elevated-strong);
      transform: translateY(-1px);
      box-shadow: var(--shadow-md);
    }
    .btn-danger {
      background: linear-gradient(135deg, #dc2626 0%, #ec4899 100%);
      color: var(--text-inverse);
      padding: 7px 16px;
      border-radius: var(--radius-xs);
      font-weight: 600;
      box-shadow: var(--shadow-sm);
      transition: transform .15s, box-shadow .2s;
      cursor: pointer;
      border: none;
    }
    .btn-danger:hover:not(:disabled) { transform: translateY(-1px); box-shadow: var(--shadow-md); }

    /* ── Colonne « Actions » d'À approuver : verdicts + boutons icône ──────────
       Les deux verdicts partagent un seul gabarit (même largeur, même hauteur,
       icône + libellé) pour que la colonne ne danse pas d'une ligne à l'autre.
       Survol : SEULE la boîte s'agrandit — fond et bordure vivent sur un enfant
       absolu `.verdict-box` qui s'étend de 2px au-delà des bords. Le libellé et
       l'icône ne bougent pas d'un pixel (aucun transform sur le bouton, aucun
       changement de layout) et la teinte reste identique au repos comme au survol. */
    .btn-verdict {
      position: relative;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 5px;
      min-width: 94px;
      height: 26px;
      padding: 0 10px;
      border-radius: var(--radius-xs);
      font-size: 11px;
      font-weight: 700;
      line-height: 1;
      white-space: nowrap;
      cursor: pointer;
      background: transparent;
      border: none;
    }
    .verdict-box {
      position: absolute;
      top: 0; right: 0; bottom: 0; left: 0;
      z-index: 0;
      border-radius: inherit;
      border: 1px solid transparent;
      background: transparent;
      pointer-events: none;
      transform-origin: center;
      transition: transform .15s, box-shadow .2s;
    }
    /* Le contenu passe au-dessus de la boîte et reste fixe. */
    .btn-verdict > span:not(.verdict-box) { position: relative; z-index: 1; }
    /* Survol : SEULE la boîte grandit (scale) + une ombre douce. Le libellé et
       l'icône sont hors de la boîte, donc rien ne bouge dans le texte, et aucune
       couleur ne change. Au clic, retour à la taille normale. */
    .btn-verdict:hover:not(:disabled) > .verdict-box {
      transform: scale(1.07);
      box-shadow: var(--shadow-sm);
    }
    .btn-verdict:active:not(:disabled) > .verdict-box {
      transform: scale(1);
      box-shadow: none;
    }
    .btn-verdict:focus-visible { outline: none; }
    .btn-verdict:focus-visible > .verdict-box { box-shadow: var(--shadow-glow); }
    .btn-verdict .verdict-icon { font-size: 12px; line-height: 1; }

    /* Les teintes ne bougent PAS au survol : chaque variante ne fournit que sa
       couleur de texte et le fond doux de sa boîte. */
    .btn-verdict.is-approve { color: var(--semantic-good); }
    .btn-verdict.is-approve > .verdict-box {
      background: var(--semantic-good-soft);
      border-color: var(--semantic-good-line);
    }
    .btn-verdict.is-reject { color: var(--semantic-danger); }
    .btn-verdict.is-reject > .verdict-box {
      background: var(--semantic-danger-soft);
      border-color: var(--semantic-danger-line);
    }

    /* Badge de verdict déjà rendu : même gabarit que .btn-verdict pour que la
       colonne garde exactement la même géométrie une fois la ligne décidée. */
    .verdict-tag {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 5px;
      min-width: 94px;
      height: 26px;
      padding: 0 10px;
      border-radius: var(--radius-xs);
      font-size: 11px;
      font-weight: 700;
      line-height: 1;
      white-space: nowrap;
      border: 1px solid transparent;
    }

    /* Actions secondaires (audit, remise en attente) : carré, même hauteur. */
    .btn-icon-action {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: 26px;
      height: 26px;
      border-radius: var(--radius-xs);
      font-size: 12px;
      line-height: 1;
      cursor: pointer;
      background: var(--bg-elevated);
      color: var(--text-secondary);
      border: 1px solid var(--border-strong);
      transition: background .15s, border-color .15s, color .15s, transform .15s;
    }
    .btn-icon-action:hover {
      color: var(--semantic-accent);
      border-color: var(--semantic-accent-line);
      background: var(--semantic-accent-soft);
      transform: translateY(-1px);
    }
    .btn-icon-action:focus-visible { outline: none; box-shadow: var(--shadow-glow); }

    /* Tableau row hover */
    .table-row:hover { background: var(--bg-hover); }

    /* ── En-têtes triables (directive x-sortable) ──────────────────────────────
       La flèche est toujours présente mais transparente au repos, et sa boîte a
       une largeur fixe : la largeur de la colonne ne change donc ni au survol ni
       au tri (pas de saut de mise en page dans des tableaux à 17 colonnes).
       Les libellés gardent le droit de se replier sur deux lignes : forcer
       `nowrap` élargissait chaque en-tête multi-mots (« Ventes 2025 », « Demande
       modèle », « WOS actuel »…) et faisait déborder les tableaux larges de la
       fenêtre — ~300 px de trop sur l'onglet « À approuver ». */
    .th-sortable {
      cursor: pointer;
      user-select: none;
      transition: color .15s;
    }
    .th-sortable:hover { color: var(--semantic-accent); }
    .th-sortable:focus-visible {
      outline: none;
      box-shadow: var(--shadow-glow);
      border-radius: var(--radius-xs);
    }
    .th-sortable::after {
      content: '↕';
      display: inline-block;
      /* Largeur fixe : ↕ ↑ ↓ occupent la même place, donc trier ne peut pas
         provoquer un repli de ligne supplémentaire dans l'en-tête. */
      width: 1em;
      text-align: center;
      margin-left: 2px;
      font-size: .9em;
      opacity: 0;
      transition: opacity .15s;
    }
    .th-sortable:hover::after      { opacity: .55; }
    .th-sortable[data-sort-dir="asc"]::after,
    .th-sortable[data-sort-dir="desc"]::after { opacity: 1; }
    .th-sortable[data-sort-dir="asc"]::after  { content: '↑'; }
    .th-sortable[data-sort-dir="desc"]::after { content: '↓'; }
    /* Colonne active : on la distingue du reste de l'en-tête. */
    .th-sortable[data-sort-dir="asc"],
    .th-sortable[data-sort-dir="desc"] { color: var(--semantic-accent); font-weight: 700; }

    /* ── Tableau qui tient dans la largeur (« À approuver ») ───────────────────
       `table-layout: fixed` + un <colgroup> en pourcentages : la somme des
       colonnes vaut TOUJOURS 100 % du conteneur, donc le tableau ne peut plus
       déborder et l'onglet ne demande jamais de défilement horizontal (à 20
       colonnes, il partait ~400 px hors écran). En contrepartie le texte long
       doit être coupé : les cellules texte portent `.cell-ellipsis` (une ligne +
       points de suspension) et gardent la valeur complète en `title`. */
    .table-fit { width: 100%; table-layout: fixed; }
    /* Gouttières resserrées : à largeur fixe, chaque pixel de padding est pris
       sur le contenu (les classes px-2/px-3 de Tailwind sont écrasées ici). */
    .table-fit th, .table-fit td { padding-left: .25rem; padding-right: .25rem; }
    /* En-têtes : un cran plus petit et coupables au milieu d'un mot en dernier
       recours, sinon « Allouable » débordait d'une colonne de 45 px. Les
       libellés trop longs pour leur colonne sont abrégés dans le gabarit (avec
       le libellé complet en `title`) — la coupure en plein mot reste un
       filet de sécurité, pas la norme. */
    .table-fit thead th { position: relative; font-size: 11px; overflow-wrap: break-word; }
    /* Fenêtre étroite : un cran de plus, sinon « Ventes » ne tient pas dans une
       colonne de 32 px et se coupe en plein mot. */
    @media (max-width: 1500px) {
      .table-fit thead th { font-size: 10px; }
    }
    /* Ailleurs la flèche de tri réserve sa place dans le flux (pour que trier ne
       déplace rien) ; ici la largeur des colonnes est FIXE, donc rien ne peut
       bouger : on sort la flèche du flux pour rendre ses ~10 px au libellé. */
    .table-fit .th-sortable::after {
      position: absolute;
      top: 3px; right: 2px;
      width: auto;
      margin: 0;
    }
    .cell-ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    /* Verdicts compacts : plus de largeur plancher de 94 px, et la cellule
       Actions a le droit de replier ses boutons sur deux lignes — c'est la
       hauteur de la ligne qui absorbe l'étroitesse, jamais la largeur du
       tableau. */
    .table-fit .btn-verdict,
    .table-fit .verdict-tag { min-width: 0; padding: 0 5px; gap: 3px; }

    /* Chat bubbles */
    .chat-bubble-user { background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%); color: var(--text-inverse); margin-left: auto; max-width: 80%; }
    .chat-bubble-ai   { background: var(--bg-elevated); backdrop-filter: var(--glass-blur-soft); -webkit-backdrop-filter: var(--glass-blur-soft); border: 1px solid var(--border-default); max-width: 90%; }
    /* Rendered assistant markdown — theme-agnostic (inherits the bubble's text colour) */
    .chat-md { line-height: 1.55; word-break: break-word; }
    .chat-md > :first-child { margin-top: 0; }
    .chat-md > :last-child { margin-bottom: 0; }
    .chat-md p { margin: .45em 0; }
    .chat-md h1, .chat-md h2, .chat-md h3, .chat-md h4 { font-weight: 700; line-height: 1.25; margin: .7em 0 .35em; }
    .chat-md h1 { font-size: 1.25em; } .chat-md h2 { font-size: 1.15em; } .chat-md h3 { font-size: 1.05em; }
    .chat-md ul, .chat-md ol { margin: .45em 0; padding-left: 1.4em; }
    .chat-md ul { list-style: disc; } .chat-md ol { list-style: decimal; }
    .chat-md li { margin: .15em 0; }
    .chat-md a { color: #3b82f6; text-decoration: underline; }
    .chat-md strong { font-weight: 700; } .chat-md em { font-style: italic; }
    .chat-md code { font-family: 'SF Mono', Menlo, Monaco, Consolas, monospace; font-size: .9em; padding: .1em .35em; border-radius: 4px; background: rgba(127,127,127,.18); }
    .chat-md pre { margin: .5em 0; padding: .7em .9em; border-radius: 8px; background: rgba(127,127,127,.15); overflow-x: auto; }
    .chat-md pre code { padding: 0; background: none; }
    .chat-md blockquote { border-left: 3px solid rgba(127,127,127,.4); padding-left: .8em; margin: .5em 0; opacity: .9; }
    .chat-md table { border-collapse: collapse; margin: .5em 0; font-size: .92em; display: block; overflow-x: auto; }
    .chat-md th, .chat-md td { border: 1px solid rgba(127,127,127,.35); padding: .3em .6em; text-align: left; }
    .chat-md th { font-weight: 700; background: rgba(127,127,127,.12); }
    .chat-md hr { border: 0; border-top: 1px solid rgba(127,127,127,.3); margin: .8em 0; }
    .floating-ai-assistant { position: fixed; right: 22px; bottom: 22px; z-index: 70; display: flex; flex-direction: column; align-items: flex-end; gap: 12px; pointer-events: none; }
    .floating-ai-panel { width: min(430px, calc(100vw - 32px)); height: min(620px, calc(100vh - 110px)); background: rgba(248, 250, 252, .96); border: 1px solid rgba(148, 163, 184, .35); box-shadow: 0 24px 70px rgba(15, 23, 42, .24); backdrop-filter: blur(22px) saturate(160%); -webkit-backdrop-filter: blur(22px) saturate(160%); border-radius: 16px; overflow: hidden; pointer-events: auto; }
    .floating-ai-button { pointer-events: auto; border: 0; color: white; background: linear-gradient(135deg, #0f766e, #2563eb); box-shadow: 0 16px 38px rgba(37, 99, 235, .36); transition: transform .18s ease, box-shadow .18s ease; }
    .floating-ai-button:hover { transform: translateY(-2px); box-shadow: 0 20px 48px rgba(37, 99, 235, .42); }
    .floating-ai-button:disabled { opacity: .6; cursor: wait; }

    /* Scrollbars */
    .scrollbar-thin::-webkit-scrollbar { width: 6px; height: 6px; }
    .scrollbar-thin::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 999px; }
    .scrollbar-thin::-webkit-scrollbar-thumb:hover { background: var(--semantic-accent); }

    /* WOS variants (on garde la palette mais ajustée pour fond clair) */
    .wos-rouge   { background: var(--semantic-danger-soft); color: var(--semantic-danger); }
    .wos-orange  { background: var(--semantic-warn-soft);   color: var(--semantic-warn);   }
    .wos-vert    { background: var(--semantic-good-soft);   color: var(--semantic-good);   }
    .wos-violet  { background: rgba(167, 139, 250, .15);    color: #7c3aed;                }

    /* KPI dollar */
    .kpi-dollar  { font-size: 2.2rem; font-weight: 800; letter-spacing: -0.02em; }

    /* Action cards (border accent + glass) */
    .action-card-critique  { border-left: 4px solid var(--semantic-danger); background: var(--bg-elevated); backdrop-filter: var(--glass-blur-soft); -webkit-backdrop-filter: var(--glass-blur-soft); border-top: 1px solid var(--border-default); border-right: 1px solid var(--border-default); border-bottom: 1px solid var(--border-default); box-shadow: var(--shadow-sm); }
    .action-card-attention { border-left: 4px solid var(--semantic-warn);   background: var(--bg-elevated); backdrop-filter: var(--glass-blur-soft); -webkit-backdrop-filter: var(--glass-blur-soft); border-top: 1px solid var(--border-default); border-right: 1px solid var(--border-default); border-bottom: 1px solid var(--border-default); box-shadow: var(--shadow-sm); }
    .action-card-info      { border-left: 4px solid var(--semantic-cool);   background: var(--bg-elevated); backdrop-filter: var(--glass-blur-soft); -webkit-backdrop-filter: var(--glass-blur-soft); border-top: 1px solid var(--border-default); border-right: 1px solid var(--border-default); border-bottom: 1px solid var(--border-default); box-shadow: var(--shadow-sm); }

    /* Tooltips — le contenu (.tooltip-box) reste caché en place ; au survol,
       app.js le recopie dans #tooltip-portal (position:fixed sur <body>) pour
       échapper aux overflow:hidden / stacking contexts des cartes KPI, avec
       repli auto sous l'élément et bornage aux bords de la fenêtre (refs #14).
       Les classes .tooltip-below / .tooltip-right-edge sont devenues inertes. */
    .tooltip-wrap { position: relative; }
    .tooltip-box { display: none; }
    #tooltip-portal {
      position: fixed;
      z-index: 9999;
      background: var(--bg-tooltip);
      color: var(--text-primary);
      /* Sur fond clair, une bulle claire ne se détache que par son contour :
         l'ombre seule laissait les bords se fondre dans la page. */
      border: 1px solid var(--border-strong);
      border-radius: var(--radius-xs);
      padding: 8px 12px;
      font-size: 0.72rem;
      line-height: 1.5;
      width: max-content;
      max-width: min(320px, calc(100vw - 24px));
      white-space: normal;
      text-align: left;
      /* Certaines infobulles posent une addition (le calcul de la confiance) :
         les chiffres doivent s'aligner d'une ligne à l'autre. */
      font-variant-numeric: tabular-nums;
      pointer-events: none;
      box-shadow: var(--shadow-lg);
    }

    .sidebar-group-label { font-size: 0.65rem; font-weight: 700; letter-spacing: .08em; color: var(--text-muted); text-transform: uppercase; padding: 8px 12px 4px; }
    .timestamp           { font-size: 0.65rem; color: var(--text-muted); margin-top: 2px; }

    /* Alertes — compteurs des puces de filtre (refs #54). Un compte « partiel »
       est restreint par l'AUTRE rangée de filtres (type ↔ importance) : il passe
       en couleur warn pour ne pas être lu comme un total. Sur une puce active le
       fond est un bleu plein, d'où la teinte claire pour garder le contraste. */
    .chip-count                       { margin-left: 3px; }
    .chip-count-partial               { color: var(--semantic-warn); font-weight: 700; }
    .chip-active .chip-count-partial  { color: #fde68a; }

    /* Alertes — signe des écarts d'approbation. Teintes assombries par rapport
       aux tokens warn/cool : ce texte est petit (0.75rem mono) sur une carte
       quasi blanche, il faut ≥ 4.5:1. */
    .ecart-hausse { color: #b45309; }   /* hausse vs recommandation */
    .ecart-baisse { color: #1d4ed8; }   /* baisse vs recommandation */

    /* ════════════════════════════════════════════════════════════════════════
       OVERRIDES TAILWIND DARK → LIGHT
       Tailwind CDN injecte ses styles APRÈS le <style> donc on force avec !important
       ════════════════════════════════════════════════════════════════════════ */
    .text-white, .text-slate-100, .text-slate-200, .text-slate-50 { color: var(--text-primary) !important; }
    /* Carve-out: the user chat bubble sits on a dark purple gradient, so keep its
       text white despite the light-theme .text-white remap on the line above. */
    .chat-bubble-user, .chat-bubble-user pre { color: #fff !important; }
    .text-slate-300, .text-slate-400 { color: var(--text-secondary) !important; }
    .text-slate-500, .text-slate-600 { color: var(--text-muted) !important; }

    /* Couleurs sémantiques Tailwind → tokens */
    .text-blue-300, .text-blue-400, .text-blue-500  { color: var(--semantic-cool) !important; }
    .text-red-300, .text-red-400, .text-red-500    { color: var(--semantic-danger) !important; }
    .text-green-300, .text-green-400, .text-green-500 { color: var(--semantic-good) !important; }
    /* Teintes warn (ambre / jaune / orange) — les nuances -200 à -500 de Tailwind
       sont des pastels prévus pour un fond sombre ; sur nos surfaces claires elles
       tombent entre 1,1:1 et 2,2:1, donc illisibles. Le jaune et l'orange étaient
       déjà ramenés sur --semantic-warn ; l'ambre, lui, avait été oublié — d'où des
       colonnes de tableau quasi invisibles sur une dizaine d'écrans (Qté décidée /
       suggérée, messages d'anomalie, seuils, écarts, ⚠ en ligne).

       L'ambre rejoint donc la même couleur que le reste : celle de la puce
       « 4-8 sem. = Faible » de Couverture WOS (.wos-orange), soit --semantic-warn.
       Une seule teinte warn dans toute l'interface, plus de pastel orphelin.

       Seul le TEXTE est visé : aucun aplat ni bordure ne change. Les nuances -700
       et plus foncées sont déjà lisibles et ne sont pas touchées. Exception plus
       bas pour les badges dont le FOND est lui-même ambré : voir
       --semantic-warn-ink. */
    .text-amber-200,  .text-amber-300,  .text-amber-400,  .text-amber-500,
    .text-yellow-300, .text-yellow-400, .text-yellow-500,
    .text-orange-200, .text-orange-300, .text-orange-400, .text-orange-500 { color: var(--semantic-warn) !important; }
    .text-purple-300, .text-purple-400, .text-purple-500 { color: var(--semantic-accent) !important; }

    /* Teintes pastel restantes (cyan / sky / rose / emerald / violet / pink) —
       le dernier lot de pastels Tailwind laissés sans remap. Même défaut que
       l'ambre traité au-dessus, même correctif : ces nuances -200 à -500 sont
       faites pour un fond sombre et tombent entre 1,2:1 et 2,6:1 sur nos cartes
       quasi blanches. ~158 occurrences.

       Le déclencheur : la colonne « Recommandé » de Performance MRP et du Journal
       des écarts est en text-cyan-300, soit ~1,4:1 — le chiffre que le client
       compare à ce qui a été traité était illisible sur les deux écrans.
       Voisines et touchées par le même lot : Écart / Refus (rose), les valeurs
       favorables (emerald).

       Chaque famille rejoint l'encre que .anom-badge / .journal-badge / .wb-tab
       employaient déjà pour elle — aucune couleur inventée ici, et les carve-outs
       plus bas deviennent de simples redites (conservées : leur portée est plus
       spécifique, la valeur est identique).

       Seul le TEXTE change : aucun aplat, aucune bordure. Les nuances -600 et plus
       foncées sont déjà lisibles et ne sont pas touchées. Aucun de ces pastels
       n'est posé sur un aplat sombre dans les gabarits (vérifié) — les deux seuls
       contextes sombres, .chat-bubble-user et .chip-active, ont déjà leur
       carve-out. */
    .text-cyan-200,    .text-cyan-300,    .text-cyan-400,    .text-cyan-500    { color: var(--semantic-info-ink) !important; }
    .text-rose-200,    .text-rose-300,    .text-rose-400,    .text-rose-500    { color: var(--semantic-danger-ink) !important; }
    .text-emerald-200, .text-emerald-300, .text-emerald-400, .text-emerald-500 { color: var(--semantic-good-ink) !important; }
    .text-violet-300,  .text-violet-400,  .text-violet-500                     { color: var(--semantic-accent-ink) !important; }
    .text-pink-300,    .text-pink-400,    .text-pink-500                       { color: var(--semantic-pink-ink) !important; }
    /* Sky est un bleu : il rejoint le token bleu déjà employé par text-blue-*,
       pas une encre propre — sinon deux bleus voisins cohabiteraient. */
    .text-sky-300, .text-sky-400, .text-sky-500 { color: var(--semantic-cool) !important; }

    /* Historique à écarter — badges Niveau / Type / Action : texte assombri pour rester lisible
       sur les fonds teintés /20. On ne touche PAS au fond. */
    .anom-badge.text-rose-300    { color: var(--semantic-danger-ink) !important; } /* niveau fort */
    .anom-badge.text-yellow-300  { color: var(--semantic-warn-ink) !important; } /* niveau moyen */
    .anom-badge.text-blue-300    { color: #1e40af !important; } /* type rupture */
    .anom-badge.text-violet-300  { color: var(--semantic-accent-ink) !important; } /* type donnée */
    .anom-badge.text-emerald-300,
    .anom-badge.text-emerald-200 { color: var(--semantic-good-ink) !important; } /* action écarter de l'historique */
    /* Fond bg-amber-500/20 : le texte porte la même teinte que son fond, donc la
       couleur warn ordinaire n'y suffit pas (2,5:1). Encre foncée obligatoire. */
    .anom-badge.text-amber-300,
    .anom-badge.text-amber-400   { color: var(--semantic-warn-ink) !important; } /* action réintégrer pondéré */

    /* Projection — badge Modèle : même principe, texte assombri, fond inchangé. */
    .model-badge.text-emerald-300 { color: var(--semantic-good-ink) !important; }

    /* Journal des écarts — Type badge: same principle, darkened text on the
       tinted /15-/20 backgrounds. Amber included: the badge background carries
       the same hue as its text, so the plain warn colour is not enough there.
       Backgrounds unchanged. */
    .journal-badge.text-cyan-300   { color: var(--semantic-info-ink) !important; } /* decision */
    .journal-badge.text-slate-300  { color: var(--text-secondary) !important; } /* prolongation */
    .journal-badge.text-amber-300  { color: var(--semantic-warn-ink) !important; } /* TTL release */
    .journal-badge.text-violet-300 { color: var(--semantic-accent-ink) !important; } /* reset */

    /* Tableau blanc / Log — same principle, but scoped to the whole tab rather
       than one badge class: the pastel -300/-400 tints (kind toggles, note and
       ticket badges, counters, inline warnings) all sit on light /5-/20
       backgrounds here and need darkening to stay readable. Backgrounds
       unchanged; the generic semantic remaps above are not enough for a tint that
       shares its hue with the text. */
    .wb-tab .text-cyan-300,   .wb-tab .text-cyan-400    { color: var(--semantic-info-ink) !important; }
    .wb-tab .text-amber-300,  .wb-tab .text-amber-400   { color: var(--semantic-warn-ink) !important; }
    .wb-tab .text-emerald-300,.wb-tab .text-emerald-400 { color: var(--semantic-good-ink) !important; }
    .wb-tab .text-rose-300,   .wb-tab .text-rose-400    { color: var(--semantic-danger-ink) !important; }
    .wb-tab .text-pink-300                              { color: var(--semantic-pink-ink) !important; }
    .wb-tab .text-violet-300                            { color: var(--semantic-accent-ink) !important; }
    /* Secondary copy (ticket descriptions, timestamps, inactive toggles): one
       step darker than the global --text-muted so it stays comfortably readable
       on the white cards. */
    .wb-tab .text-slate-500, .wb-tab .text-slate-600    { color: var(--text-secondary) !important; }
    .wb-tab .metric-sub                                 { color: var(--text-secondary); }

    /* Surfaces sombres → glass clair */
    .bg-slate-700, .bg-slate-800, .bg-slate-900, .bg-slate-950 {
      background: var(--bg-elevated) !important;
      backdrop-filter: var(--glass-blur-soft);
      -webkit-backdrop-filter: var(--glass-blur-soft);
    }
    .bg-blue-500, .bg-blue-600 { background: linear-gradient(135deg, #4f46e5 0%, #2563eb 100%) !important; color: var(--text-inverse) !important; }
    .bg-green-500, .bg-green-600 { background: var(--semantic-good) !important; color: var(--text-inverse) !important; }
    .bg-red-500, .bg-red-600 { background: var(--semantic-danger) !important; color: var(--text-inverse) !important; }

    .border-slate-700, .border-slate-800, .border-slate-600 { border-color: var(--border-strong) !important; }
    .border-white { border-color: var(--text-inverse) !important; }

    /* Variantes /opacity slate (bg-slate-XXX/NN) : non couvertes par l'override de fond ci-dessus.
       Le fond reste gris foncé : on force le texte en blanc pour rester lisible.
       Concerne entre autres : encadrés résumé et badges « faible » niveau/priorité.
       Les en-têtes de tableau n'utilisent plus ces classes (fond blanc + texte sombre). */
    .bg-slate-600\/30,
    .bg-slate-700\/40,
    .bg-slate-800\/30,
    .bg-slate-800\/40,
    .bg-slate-800\/50,
    .bg-slate-800\/60,
    .bg-slate-800\/80,
    .bg-slate-900\/35,
    .bg-slate-900\/40,
    .bg-slate-900\/50,
    .bg-slate-900\/60 { color: #fff !important; }

    /* Spinners — indigo visible sur fond clair (Tailwind border-white invisible sur glass blanc) */
    .animate-spin.border-white { border-color: var(--semantic-accent) !important; border-top-color: transparent !important; }
    .animate-spin.border-blue-400 { border-color: var(--semantic-cool) !important; border-top-color: transparent !important; }

    /* Headings — dark navy sur fond clair */
    h1, h2, h3, h4, h5, h6 { color: var(--text-primary) !important; font-weight: 700; letter-spacing: -0.01em; }
    h1 { font-weight: 800; letter-spacing: -0.02em; }

    /* Texte de drawer / panneau d'info forcé en dark navy */
    .glass-card .glass-kpi-sub, .csuite-card p:not([class*="text-"]) { color: var(--text-secondary); }
    @media print {
      @page { size: A4 landscape; margin: 12mm; }
      nav, header, aside, .no-print, .sidebar, #sidebar, button, .tooltip-wrap, .tooltip-box, #tooltip-portal { display: none !important; }
      main, .main, body, html { overflow: visible !important; height: auto !important; width: 100% !important; padding: 0 !important; margin: 0 !important; }
      body { background: white !important; color: #000 !important; font-size: 11pt; }
      h1, h2, h3, h4, p, span, td, th, div { color: #000 !important; }
      .card, .csuite-card { background: white !important; border: 1px solid #bbb !important; break-inside: avoid; page-break-inside: avoid; box-shadow: none !important; margin-bottom: 8px !important; }
      table { page-break-inside: avoid; width: 100% !important; }
      tr, thead, tbody { page-break-inside: avoid; }
      #chartWaterfall, #chartStoreScatter, #chartHeatmap, #chartRuptureProjection, #chartAllocMag, #chartTaille, #chartStoreTrend, #chartBacktest, #chartHistorique, #chartSeasonal, #chartSTR, [id^="chart"] { display: block !important; page-break-inside: avoid; max-width: 100% !important; height: 320px !important; }
      svg { max-width: 100% !important; height: auto !important; }
      .text-white, .text-slate-400, .text-slate-300, .text-slate-500, .text-blue-400, .text-green-400, .text-yellow-400, .text-red-400 { color: #000 !important; }
      .bg-slate-700, .bg-slate-800, .bg-slate-900, .input-dark { background: #f5f5f5 !important; border-color: #ccc !important; }
      .print-break-inside-avoid { break-inside: avoid; page-break-inside: avoid; }
      .print-always-visible { display: block !important; }
      pre { background: #f5f5f5 !important; color: #000 !important; border: 1px solid #ccc !important; white-space: pre-wrap !important; }
    }

    /* ════════════════════════════════════════════════════════════════════════
       PREVIEW DESIGN — Glass Light (Option A)
       Section pastel + cartes verre dépoli + KPI dégradés
       Isolé via .preview-light-section pour ne pas affecter le reste de l'app
       ════════════════════════════════════════════════════════════════════════ */
    .preview-light-section {
      position: relative;
      background:
        radial-gradient(1200px 400px at 100% 0%, rgba(236, 72, 153, .12), transparent 60%),
        radial-gradient(900px 500px at 0% 100%, rgba(99, 102, 241, .15), transparent 60%),
        linear-gradient(135deg, #eef2ff 0%, #fdf4ff 50%, #fef3f2 100%);
      border-radius: 24px;
      padding: 36px 28px 28px;
      margin: 12px 0;
      box-shadow: 0 20px 60px -25px rgba(99, 102, 241, .25);
      border: 1px solid rgba(255, 255, 255, .8);
      overflow: hidden;
    }
    .preview-light-section::after {
      content: '';
      position: absolute;
      top: -50%; left: -10%;
      width: 60%; height: 200%;
      background: radial-gradient(ellipse at center, rgba(255,255,255,.4) 0%, transparent 60%);
      pointer-events: none;
      transform: rotate(-20deg);
    }
    .glass-card {
      position: relative;
      background: rgba(255, 255, 255, .55);
      backdrop-filter: blur(24px) saturate(160%);
      -webkit-backdrop-filter: blur(24px) saturate(160%);
      border: 1px solid rgba(255, 255, 255, .65);
      border-radius: 20px;
      padding: 24px 22px;
      box-shadow:
        0 4px 16px rgba(99, 102, 241, .08),
        0 1px 2px rgba(99, 102, 241, .05),
        inset 0 1px 0 rgba(255, 255, 255, .8);
      transition: transform .28s cubic-bezier(.2,.8,.2,1), box-shadow .28s ease;
      cursor: pointer;
      overflow: hidden;
    }
    .glass-card::before {
      content: '';
      position: absolute;
      top: 0; left: 16px; right: 16px;
      height: 2px;
      border-radius: 0 0 4px 4px;
    }
    .glass-card:hover {
      transform: translateY(-3px);
      box-shadow:
        0 14px 36px rgba(99, 102, 241, .18),
        0 2px 4px rgba(99, 102, 241, .06),
        inset 0 1px 0 rgba(255, 255, 255, .9);
    }
    .glass-card.accent-neutral::before { background: linear-gradient(90deg, transparent, #4f46e5, transparent); }
    .glass-card.accent-danger::before  { background: linear-gradient(90deg, transparent, #dc2626, transparent); }
    .glass-card.accent-cool::before    { background: linear-gradient(90deg, transparent, #2563eb, transparent); }
    .glass-card.accent-warn::before    { background: linear-gradient(90deg, transparent, #f59e0b, transparent); }
    .glass-kpi-label {
      color: #6b7280;
      font-size: 11px;
      font-weight: 700;
      text-transform: uppercase;
      letter-spacing: .1em;
      display: flex; align-items: center; gap: 6px;
    }
    .glass-kpi-label .info-dot {
      width: 14px; height: 14px;
      border-radius: 50%;
      background: rgba(99, 102, 241, .15);
      color: #6366f1;
      font-size: 9px;
      display: inline-flex; align-items: center; justify-content: center;
      font-weight: 800;
      transition: background .2s;
    }
    .glass-card:hover .glass-kpi-label .info-dot {
      background: rgba(99, 102, 241, .3);
    }
    .glass-kpi-value {
      font-size: 2.4rem;
      font-weight: 800;
      letter-spacing: -.025em;
      line-height: 1.05;
      margin-top: 10px;
      background-clip: text;
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
    }
    .kpi-grad-neutral { background-image: linear-gradient(135deg, #1f2937 0%, #4f46e5 100%); }
    .kpi-grad-danger  { background-image: linear-gradient(135deg, #dc2626 0%, #ec4899 100%); }
    .kpi-grad-cool    { background-image: linear-gradient(135deg, #2563eb 0%, #06b6d4 100%); }
    .kpi-grad-warn    { background-image: linear-gradient(135deg, #d97706 0%, #f59e0b 100%); }
    .glass-kpi-sub {
      color: #6b7280;
      font-size: 12px;
      margin-top: 6px;
    }

    /* ════════════════════════════════════════════════════════════════════════
       CONTROL TOWER — couche premium pour les écrans décisionnels
       ════════════════════════════════════════════════════════════════════════ */
    .metric-card {
      position: relative;
      min-height: 116px;
      padding: 18px;
      border: 1px solid rgba(85, 71, 216, .13);
      border-radius: 18px;
      background: linear-gradient(145deg, rgba(255,255,255,.88), rgba(248,250,252,.68));
      box-shadow: 0 14px 34px -26px rgba(17, 24, 39, .55), inset 0 1px 0 rgba(255,255,255,.9);
      overflow: hidden;
    }
    .metric-card::after {
      content: '';
      position: absolute;
      right: -28px;
      top: -34px;
      width: 96px;
      height: 96px;
      border-radius: 50%;
      background: color-mix(in srgb, var(--metric-color, #5547d8) 16%, transparent);
    }
    .metric-label {
      color: var(--text-secondary);
      font-size: 11px;
      font-weight: 800;
      letter-spacing: .08em;
      text-transform: uppercase;
    }
    .metric-value {
      color: var(--ink-strong);
      font-size: 2.2rem;
      line-height: 1;
      font-weight: 850;
      letter-spacing: 0;
      font-variant-numeric: tabular-nums;
    }
    .metric-sub {
      color: var(--text-muted);
      font-size: 12px;
      margin-top: 8px;
    }
    /* Quantité accolée à un montant. Les tuiles de tête portent désormais des $ —
       un montant se compare d'un run à l'autre et se discute en comité, un volume
       non — mais l'unité reste le langage des équipes magasin : elle suit le
       montant sans lui disputer la place, plutôt que de disparaître de l'écran. */
    .metric-qty {
      color: var(--text-muted);
      font-size: 13px;
      font-weight: 700;
      font-variant-numeric: tabular-nums;
      margin-left: 3px;
      white-space: nowrap;
    }
    /* Curseur à deux poignées sur UNE piste. Deux <input type=range> superposés :
       la piste native de chacun est rendue transparente, une piste commune est
       dessinée en dessous, et seules les poignées restent cliquables — sans quoi
       le curseur du dessus intercepterait tous les clics destinés à l'autre. */
    .dual-range {
      position: relative;
      height: 34px;
      display: flex;
      align-items: center;
    }
    .dual-range .dr-piste,
    .dual-range .dr-plage {
      position: absolute;
      height: 4px;
      border-radius: 999px;
      pointer-events: none;
    }
    .dual-range .dr-piste { left: 0; right: 0; background: var(--border-strong); }
    .dual-range .dr-plage { background: var(--semantic-good); }
    .dual-range input[type="range"] {
      position: absolute;
      left: 0;
      width: 100%;
      margin: 0;
      background: none;
      appearance: none;
      -webkit-appearance: none;
      pointer-events: none;   /* la piste ne capte rien… */
      height: 34px;
    }
    .dual-range input[type="range"]::-webkit-slider-thumb {
      pointer-events: auto;   /* …seule la poignée est saisissable */
      -webkit-appearance: none;
      width: 18px;
      height: 18px;
      border-radius: 50%;
      background: var(--semantic-good);
      border: 2px solid #fff;
      box-shadow: 0 1px 4px rgba(17, 24, 39, .35);
      cursor: grab;
    }
    .dual-range input[type="range"]::-moz-range-thumb {
      pointer-events: auto;
      width: 18px;
      height: 18px;
      border-radius: 50%;
      background: var(--semantic-good);
      border: 2px solid #fff;
      box-shadow: 0 1px 4px rgba(17, 24, 39, .35);
      cursor: grab;
    }
    .dual-range input[type="range"]::-moz-range-track { background: none; }
    /* Bandeau « Périmètre actif » de Données chargées. Pas de bg-slate-XXX/NN ici :
       ces variantes gardent un aplat gris foncé et forcent le texte en blanc
       (cf. overrides Tailwind), ce qui posait un rectangle sombre au milieu d'une
       carte quasi blanche. Surface claire teintée accent, comme le reste. */
    .scope-band {
      background: var(--semantic-accent-soft);
      border: 1px solid var(--border-subtle);
      border-radius: var(--radius-sm);
    }
    .dataset-view-panel {
      border: 1px solid rgba(85, 71, 216, .16);
      border-radius: var(--radius-md);
      background: rgba(255,255,255,.78);
      box-shadow: 0 14px 34px -28px rgba(17, 24, 39, .52);
      padding: 18px;
    }
    .dataset-view-head {
      display: flex;
      justify-content: space-between;
      align-items: flex-start;
      gap: 16px;
      margin-bottom: 14px;
    }
    .dataset-view-head h3 {
      color: var(--ink-strong);
      font-size: 1.15rem;
      line-height: 1.2;
      font-weight: 850;
      margin-top: 2px;
    }
    .dataset-view-head p:not(.wow-kicker) {
      color: var(--text-secondary);
      font-size: 13px;
      margin-top: 5px;
      max-width: 740px;
    }
    .dataset-filter-chips {
      display: flex;
      flex-wrap: wrap;
      gap: 7px;
      margin-bottom: 13px;
    }
    .dataset-filter-chips button {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      min-height: 30px;
      border-radius: var(--radius-pill);
      border: 1px solid rgba(85, 71, 216, .2);
      background: rgba(255,255,255,.82);
      color: var(--text-primary);
      padding: 5px 10px;
      font-size: 12px;
      font-weight: 650;
    }
    .dataset-tile-grid {
      display: grid;
      grid-template-columns: repeat(7, minmax(0, 1fr));
      gap: 10px;
    }
    .dataset-dim-tile {
      --tile-line: #86afd3;
      --tile-bg: #e0edf8;
      text-align: left;
      min-height: 128px;
      border-radius: 8px;
      border: 1px solid color-mix(in srgb, var(--tile-line) 55%, transparent);
      border-top: 4px solid var(--tile-line);
      background: linear-gradient(180deg, color-mix(in srgb, var(--tile-bg) 72%, white), rgba(255,255,255,.86));
      padding: 12px;
      color: var(--text-primary);
      display: flex;
      flex-direction: column;
      gap: 5px;
      overflow: hidden;
    }
    .dataset-dim-tile.tone-fuchsia,
    .slicer-segment.tone-fuchsia { --tile-line: #c178b6; --tile-bg: #f2dcef; --tile-ink: #8a2f78; }
    .dataset-dim-tile.tone-orange,
    .slicer-segment.tone-orange { --tile-line: #f0a070; --tile-bg: #fbe7db; --tile-ink: #9a4b16; }
    .dataset-dim-tile.tone-green,
    .slicer-segment.tone-green { --tile-line: #82b873; --tile-bg: #e1f0dc; --tile-ink: #2f6f28; }
    .dataset-dim-tile.tone-blue,
    .slicer-segment.tone-blue { --tile-line: #86afd3; --tile-bg: #e0edf8; --tile-ink: #245f93; }
    .dataset-dim-tile .tile-label {
      color: var(--tile-ink);
      font-size: 11px;
      font-weight: 850;
      text-transform: uppercase;
      letter-spacing: .04em;
    }
    .dataset-dim-tile strong {
      color: var(--ink-strong);
      font-size: 2rem;
      line-height: 1;
      font-weight: 900;
      font-variant-numeric: tabular-nums;
    }
    .dataset-dim-tile small,
    .dataset-dim-tile em {
      color: var(--text-secondary);
      font-size: 12px;
      line-height: 1.25;
      font-style: normal;
      overflow-wrap: anywhere;
    }
    .dataset-dim-tile em {
      color: var(--text-muted);
      margin-top: auto;
    }
    .dataset-view-metrics {
      display: grid;
      grid-template-columns: repeat(6, minmax(0, 1fr));
      gap: 10px;
      margin-top: 12px;
    }
    .dataset-view-metrics div {
      min-height: 88px;
      border-radius: 8px;
      border: 1px solid rgba(85, 71, 216, .13);
      background: rgba(255,255,255,.72);
      padding: 11px;
    }
    .dataset-view-metrics span {
      color: var(--text-secondary);
      display: block;
      font-size: 11px;
      font-weight: 800;
      text-transform: uppercase;
      letter-spacing: .04em;
    }
    .dataset-view-metrics strong {
      color: var(--ink-strong);
      display: block;
      font-size: 1.45rem;
      line-height: 1.1;
      margin-top: 5px;
      font-weight: 900;
      font-variant-numeric: tabular-nums;
      overflow-wrap: anywhere;
    }
    .dataset-view-metrics small {
      color: var(--text-muted);
      display: block;
      font-size: 11px;
      line-height: 1.25;
      margin-top: 5px;
    }
    .dataset-warning {
      color: var(--semantic-warn-ink);
      background: rgba(245, 158, 11, .12);
      border: 1px solid rgba(245, 158, 11, .24);
      border-radius: var(--radius-sm);
      margin-top: 12px;
      padding: 9px 11px;
      font-size: 12px;
    }
    .app-workspace > main { min-width: 0; }
    .dataset-slicer-dialog {
      width: min(1420px, calc(100vw - 28px));
      max-height: min(92vh, 920px);
      padding: 0;
      border: 0;
      border-radius: var(--radius-md);
      background: transparent;
      color: var(--text-primary);
      box-shadow: 0 26px 90px rgba(20, 40, 58, .28);
      overflow: hidden;
    }
    .dataset-slicer-dialog::backdrop { background: rgba(20, 37, 58, .42); }
    .slicer-shell {
      max-height: min(92vh, 920px);
      display: flex;
      flex-direction: column;
      background: #fff;
    }
    .slicer-header,
    .slicer-footer {
      flex: 0 0 auto;
      display: flex;
      justify-content: space-between;
      align-items: flex-start;
      gap: 18px;
      padding: 18px 22px;
      border-bottom: 1px solid rgba(85, 71, 216, .16);
    }
    .slicer-header h2 {
      color: var(--ink-strong);
      font-size: 1.35rem;
      font-weight: 900;
      line-height: 1.15;
    }
    .slicer-header p {
      color: var(--text-secondary);
      max-width: 760px;
      font-size: 13px;
      margin-top: 5px;
    }
    .slicer-kicker {
      color: var(--semantic-accent-ink) !important;
      font-size: 11px !important;
      font-weight: 850;
      text-transform: uppercase;
      letter-spacing: .08em;
      margin: 0 !important;
    }
    .slicer-icon-button {
      width: 34px;
      height: 34px;
      border-radius: 8px;
      border: 1px solid rgba(85, 71, 216, .18);
      background: rgba(255,255,255,.78);
      color: var(--text-secondary);
      font-size: 22px;
      line-height: 1;
    }
    .slicer-config {
      flex: 0 0 auto;
      border-bottom: 1px solid rgba(85, 71, 216, .1);
      padding: 12px 22px;
      background: rgba(248,250,252,.74);
    }
    .slicer-config-grid {
      display: grid;
      grid-template-columns: 1.2fr .8fr;
      gap: 14px;
      align-items: end;
    }
    .slicer-field-label {
      color: var(--text-primary);
      display: block;
      font-size: 12px;
      font-weight: 750;
      margin-bottom: 5px;
    }
    .slicer-period-row {
      display: flex;
      align-items: center;
      gap: 6px;
      flex-wrap: wrap;
    }
    .slicer-muted {
      color: var(--text-muted);
      font-size: 12px;
      margin-top: 8px;
    }
    .slicer-status-row {
      flex: 0 0 auto;
      display: flex;
      gap: 12px;
      flex-wrap: wrap;
      padding: 10px 22px;
      color: var(--text-secondary);
      font-size: 12px;
      border-bottom: 1px solid rgba(85, 71, 216, .1);
    }
    .slicer-error { color: var(--semantic-danger); }
    .slicer-ok { color: var(--semantic-good-ink); }
    .slicer-segments {
      flex: 1 1 auto;
      min-height: 0;
      overflow: auto;
      display: grid;
      grid-template-columns: repeat(7, minmax(148px, 1fr));
      gap: 10px;
      padding: 14px;
    }
    .slicer-segment {
      --tile-line: #86afd3;
      --tile-bg: #e0edf8;
      --tile-ink: #245f93;
      min-width: 0;
      border-radius: 8px;
      border: 1px solid color-mix(in srgb, var(--tile-line) 58%, transparent);
      border-top: 4px solid var(--tile-line);
      background: linear-gradient(180deg, color-mix(in srgb, var(--tile-bg) 78%, white), rgba(255,255,255,.92));
      padding: 10px;
      display: flex;
      flex-direction: column;
      gap: 8px;
    }
    .slicer-segment-head {
      display: flex;
      justify-content: space-between;
      gap: 8px;
      min-height: 48px;
    }
    .slicer-segment h3 {
      color: var(--tile-ink);
      font-size: 13px;
      font-weight: 900;
      line-height: 1.15;
    }
    .slicer-segment-head p,
    .slicer-footnote,
    .slicer-empty {
      color: var(--text-muted);
      font-size: 11px;
      line-height: 1.25;
      margin-top: 4px;
    }
    .slicer-count {
      color: var(--ink-strong);
      font-size: 18px;
      font-weight: 900;
      font-variant-numeric: tabular-nums;
    }
    .slicer-unavailable {
      color: var(--semantic-warn-ink);
      background: rgba(245, 158, 11, .12);
      border: 1px solid rgba(245, 158, 11, .22);
      border-radius: 8px;
      padding: 8px;
      font-size: 11px;
      line-height: 1.3;
    }
    .slicer-unavailable strong,
    .slicer-unavailable span { display: block; }
    .slicer-search input {
      width: 100%;
      min-height: 36px;
      font-size: 12px;
      padding: 7px 9px;
      border-radius: 8px;
    }
    .slicer-search .sr-only {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip: rect(0, 0, 0, 0);
      white-space: nowrap;
      border: 0;
    }
    .slicer-actions,
    .slicer-mode {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 5px;
    }
    .slicer-actions button,
    .slicer-mode button {
      min-height: 30px;
      border-radius: 7px;
      border: 1px solid color-mix(in srgb, var(--tile-line) 42%, transparent);
      background: rgba(255,255,255,.72);
      color: var(--tile-ink);
      font-size: 10.5px;
      font-weight: 800;
      line-height: 1.15;
      padding: 5px;
    }
    .slicer-mode button[aria-pressed="true"] {
      background: var(--tile-bg);
      border-color: var(--tile-ink);
      box-shadow: inset 0 0 0 1px var(--tile-ink);
    }
    .slicer-options {
      min-height: 180px;
      max-height: 290px;
      overflow: auto;
      display: flex;
      flex-direction: column;
      gap: 5px;
      padding-right: 2px;
    }
    .slicer-option {
      width: 100%;
      min-height: 32px;
      border-radius: 7px;
      border: 1px solid color-mix(in srgb, var(--tile-line) 34%, transparent);
      background: rgba(255,255,255,.78);
      color: var(--text-primary);
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 6px;
      padding: 6px 8px;
      text-align: left;
      font-size: 12px;
      line-height: 1.2;
    }
    .slicer-option span {
      overflow-wrap: anywhere;
      min-width: 0;
    }
    .slicer-option small {
      color: var(--semantic-warn-ink);
      flex: 0 0 auto;
      font-size: 10px;
    }
    .slicer-option.is-selected {
      border-color: var(--tile-ink);
      background: var(--tile-bg);
      box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--tile-ink) 38%, transparent);
      font-weight: 800;
    }
    .slicer-option.is-unavailable:not(.is-selected) {
      color: var(--text-disabled);
      background: rgba(241,245,249,.8);
    }
    .slicer-footer {
      align-items: center;
      border-top: 1px solid rgba(85, 71, 216, .16);
      border-bottom: 0;
    }
    .slicer-footer-copy {
      color: var(--text-secondary);
      font-size: 12px;
      line-height: 1.35;
    }
    .slicer-footer-copy strong {
      color: var(--ink-strong);
      display: block;
    }
    .slicer-footer-actions {
      display: flex;
      justify-content: flex-end;
      gap: 8px;
      flex-wrap: wrap;
    }
    @media (prefers-reduced-motion: reduce) {
      .dataset-dim-tile,
      .slicer-option,
      .slicer-icon-button {
        transition: none !important;
      }
    }
    /* Décomposition d'une tuile KPI : d'où viennent les N lignes du total.
       Les quantités sont alignées à droite sur une colonne fixe pour que la
       somme reste vérifiable à l'œil sans changer de police. */
    .metric-breakdown {
      margin-top: 8px;
      padding-top: 7px;
      border-top: 1px solid color-mix(in srgb, var(--metric-color, #5547d8) 20%, transparent);
      display: grid;
      gap: 3px;
      font-size: 11.5px;
      line-height: 1.25;
      position: relative;   /* passe au-dessus du disque décoratif ::after */
      z-index: 1;
    }
    .metric-breakdown li {
      display: grid;
      grid-template-columns: minmax(42px, auto) 1fr;
      gap: 7px;
      align-items: baseline;
    }
    /* Une tuile qui porte une décomposition a besoin de deux colonnes de grille :
       sur 128px, chaque ligne « 14 458 · avec demande prévue » passe à la ligne
       et double la hauteur de TOUTE la rangée (les cartes sont étirées). */
    .metric-card.is-wide { grid-column: span 2; }
    .metric-breakdown .mb-qty {
      color: var(--ink-strong);
      font-weight: 750;
      font-variant-numeric: tabular-nums;
      text-align: right;
    }
    /* Pointillé = « survole-moi » : la définition d'un seau tient en une phrase,
       un (?) par ligne surchargerait une tuile de 3 lignes. */
    .metric-breakdown .mb-label {
      color: var(--text-muted);
      border-bottom: 1px dotted color-mix(in srgb, var(--text-muted) 55%, transparent);
      justify-self: start;
    }
    .metric-card.is-danger { --metric-color: var(--signal-rose); border-color: rgba(244,63,94,.24); }
    .metric-card.is-warning { --metric-color: var(--signal-amber); border-color: rgba(245,158,11,.26); }
    .metric-card.is-info { --metric-color: var(--signal-cyan); border-color: rgba(6,182,212,.24); }
    .metric-card.is-good { --metric-color: var(--semantic-good); border-color: rgba(14,169,104,.24); }
    .decision-card {
      position: relative;
      border-radius: 16px;
      border: 1px solid rgba(85,71,216,.14);
      background: rgba(255,255,255,.78);
      box-shadow: 0 10px 26px -22px rgba(17,24,39,.5);
    }
    .decision-card.is-critical { border-color: rgba(244,63,94,.34); background: linear-gradient(135deg, rgba(255,255,255,.86), rgba(255,241,242,.78)); }
    .decision-card.is-attention { border-color: rgba(245,158,11,.34); background: linear-gradient(135deg, rgba(255,255,255,.86), rgba(255,251,235,.78)); }
    .decision-card.is-info { border-color: rgba(6,182,212,.26); background: linear-gradient(135deg, rgba(255,255,255,.86), rgba(236,254,255,.62)); }
    .decision-pill {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      padding: 4px 9px;
      border-radius: 999px;
      border: 1px solid rgba(85,71,216,.14);
      background: rgba(255,255,255,.72);
      color: var(--text-secondary);
      font-size: 11px;
      font-weight: 700;
      white-space: nowrap;
    }
    .premium-table {
      width: 100%;
      border-collapse: separate;
      border-spacing: 0;
      font-variant-numeric: tabular-nums;
    }
    .premium-table thead {
      position: sticky;
      top: 0;
      z-index: 2;
      background: var(--bg-elevated-strong);
      color: var(--text-secondary);
      border-bottom: 1px solid var(--border-strong);
    }
    .premium-table th {
      padding: 10px 12px;
      font-size: 11px;
      font-weight: 800;
      letter-spacing: .04em;
      text-transform: uppercase;
    }
    .premium-table td {
      padding: 9px 12px;
      border-bottom: 1px solid rgba(85,71,216,.10);
    }
    .premium-table tbody tr:hover { background: rgba(6,182,212,.06); }
    .progress-rail {
      height: 8px;
      border-radius: 999px;
      background: rgba(85,71,216,.10);
      overflow: hidden;
    }
    .progress-fill {
      height: 100%;
      border-radius: 999px;
      background: linear-gradient(90deg, var(--signal-cyan), var(--semantic-accent));
    }
    .wow-hero {
      position: relative;
      overflow: hidden;
      border-radius: 26px;
      border: 1px solid rgba(85,71,216,.18);
      background:
        linear-gradient(115deg, rgba(255,255,255,.94) 0%, rgba(244,248,255,.86) 48%, rgba(225,249,252,.72) 100%);
      box-shadow: 0 24px 70px -46px rgba(17,24,39,.55), inset 0 1px 0 rgba(255,255,255,.95);
    }
    .wow-hero::before {
      content: '';
      position: absolute;
      inset: 0;
      pointer-events: none;
      background:
        radial-gradient(520px 220px at 84% 12%, rgba(6,182,212,.18), transparent 64%),
        linear-gradient(90deg, rgba(85,71,216,.12) 0 1px, transparent 1px),
        linear-gradient(180deg, rgba(85,71,216,.10) 0 1px, transparent 1px);
      background-size: auto, 34px 34px, 34px 34px;
      opacity: .72;
    }
    .wow-hero > * { position: relative; z-index: 1; }
    .wow-kicker {
      color: var(--semantic-info);
      font-size: 11px;
      font-weight: 900;
      letter-spacing: .18em;
      text-transform: uppercase;
    }
    .hero-stat {
      min-width: 220px;
      padding: 18px;
      border-radius: 18px;
      background: rgba(255,255,255,.72);
      border: 1px solid rgba(85,71,216,.16);
      backdrop-filter: var(--glass-blur-soft);
      -webkit-backdrop-filter: var(--glass-blur-soft);
      box-shadow: 0 18px 44px -34px rgba(17,24,39,.55);
    }
    .hero-stat-value {
      color: var(--ink-strong);
      font-size: 2.4rem;
      line-height: 1;
      font-weight: 900;
      letter-spacing: 0;
      font-variant-numeric: tabular-nums;
    }
    .signal-strip {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
      gap: 12px;
    }
    .signal-tile {
      padding: 14px 16px;
      border-radius: 16px;
      border: 1px solid rgba(85,71,216,.13);
      background: rgba(255,255,255,.72);
      box-shadow: inset 0 1px 0 rgba(255,255,255,.82);
    }
    .signal-tile strong {
      display: block;
      color: var(--ink-strong);
      font-size: 1.25rem;
      line-height: 1.1;
      font-weight: 900;
      font-variant-numeric: tabular-nums;
    }
    .kpi-command-grid {
      display: grid;
      grid-template-columns: minmax(280px, 1.35fr) repeat(5, minmax(128px, 1fr));
      gap: 12px;
      align-items: stretch;
    }
    .metric-card.is-featured {
      min-height: 150px;
      padding: 22px;
      background:
        radial-gradient(220px 160px at 100% 0%, rgba(6,182,212,.24), transparent 58%),
        linear-gradient(135deg, rgba(255,255,255,.94), rgba(239,246,255,.82));
      border-color: rgba(6,182,212,.32);
    }
    .metric-card.is-featured .metric-value { font-size: 3rem; }
    .lux-table-note {
      color: var(--text-secondary);
      font-size: 12px;
      margin-top: -6px;
      margin-bottom: 10px;
    }
    @media (max-width: 1180px) {
      .kpi-command-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
      .signal-strip { grid-template-columns: 1fr; }
      .dataset-tile-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
      .dataset-view-metrics { grid-template-columns: repeat(3, minmax(0, 1fr)); }
      .slicer-segments { grid-template-columns: repeat(3, minmax(170px, 1fr)); }
    }
    @media (max-width: 720px) {
      .kpi-command-grid { grid-template-columns: 1fr; }
      .metric-card.is-wide { grid-column: auto; }   /* une seule colonne : pas de span */
      .hero-stat { width: 100%; }
      .hero-stat-value { font-size: 2rem; }
      .dataset-view-head,
      .slicer-header,
      .slicer-footer {
        flex-direction: column;
        align-items: stretch;
      }
      .dataset-tile-grid,
      .dataset-view-metrics,
      .slicer-config-grid { grid-template-columns: 1fr; }
      .app-header { flex-wrap: wrap; gap: 8px; }
      .app-header > div { flex-wrap: wrap; max-width: 100%; }
      .app-workspace { flex-direction: column; }
      .app-workspace > nav { width: 100%; max-height: 150px; }
      .app-workspace > main { width: 100%; }
      .slicer-segments {
        grid-template-columns: 1fr;
        padding: 10px;
      }
      .dataset-slicer-dialog,
      .slicer-shell {
        max-height: 94vh;
      }
      .dataset-slicer-dialog {
        left: 14px;
        right: auto;
        margin-left: 0;
        margin-right: 0;
      }
      .slicer-shell { overflow-y: auto; }
      .slicer-segments { flex: 0 0 auto; overflow: visible; }
      .slicer-header { position: relative; padding-right: 54px; }
      .slicer-header .slicer-icon-button { position: absolute; right: 14px; top: 14px; }
      .slicer-options {
        min-height: 80px;
        max-height: 210px;
      }
      .slicer-footer-actions > * {
        flex: 1 1 150px;
      }
    }
