﻿/*
 * ============================================================
 *  APPLICATION LIGHT THEME
 *  DevExpress XAF Blazor v26.1 — Fluent mode
 *  (l'en-tête annonçait v25.x jusqu'au 2026-09-10 ; plusieurs règles de la § 13 avaient été
 *   posées contre des sélecteurs DevExpress qui n'existent plus en 26.1.)
 *
 *  File structure
 *  ──────────────
 *  1-2. (colours, and the DevExpress theme's public tokens: charte/ds-pim.css, since 27/10/2026)
 *  3.  Base typography
 *  4.  Buttons — native (detail-view tabs fix only)
 *  5.  Primary button — charter (pressed / disabled states, Bootstrap / DevExtreme bridge)
 *  6-9. Secondary toolbar actions — charter; outline, danger, icon-only — native
 *  10. XAF toolbar — notes only
 *  11. Sidebar navigation
 *  12. Tabs / detail-view nav
 *  13. Grid (DataGrid)
 *  14. Inputs / editors
 *  15. Page title / top bar
 *  16. Focus ring
 *  17. Modal / popup
 *  18. Detail View (object forms)
 *  19. Pager
 *  20. Nav-ribbon (DxTabs)
 *  21. Icon overrides (DevExpress native icons)
 *
 *  Override strategy for DevExpress components
 *  ────────────────────────────────────────────
 *  DevExpress Fluent theme relies on a two-level CSS variable chain:
 *    component variable (--dxbl-*, INTERNAL)  →  dxds token (--dxds-*, PUBLIC)  →  value
 *  Adding !important on a property such as background-color has NO
 *  effect when the component reads its color from a CSS variable at
 *  render time.  The public way (dxdocs 405705, 26.1) is to set the
 *  --dxds-* token: globally in charte/ds-pim.css, or re-declared on a class of ours
 *  set on the component when only that component must differ
 *  (rules: .claude/rules/ui.md § 2).
 *
 *  Example (grid selected row):
 *    core.min.css sets --dxbl-grid-selection-bg: var(--dxds-color-surface-primary-deep-rest)
 *    → set --dxds-color-surface-primary-deep-rest, not --dxbl-grid-selection-bg.
 *
 *  ⚠ The --dxbl-* declarations still written in this file are DEBT:
 *  internal variables, which DevExpress may rename in any version (some
 *  rules of § 13 broke at the 26.1 upgrade). Do not add new ones.
 *
 *  When in doubt, search core.min.css for the component class and
 *  trace back the variable chain to its --dxds-* token.
 * ============================================================
 */

/* Manrope font: loaded once, by the <link> of Pages/_Host.cshtml (an @import here blocked the first paint). */


/* ============================================================
   3. BASE TYPOGRAPHY
   ============================================================ */
html,
body,
.dxbl-theme-fluent-mode-light {
    font-family: var(--ds-font-family-text);
    font-weight: 400;
    color: var(--dxds-color-content-neutral-default-rest);
}


/* ============================================================
   4. BUTTONS — native
   Buttons keep the theme's metrics and states (padding, font,
   shadow, focus ring, icon size, disabled look): nothing is reset
   here. What follows is the detail-view tabs fix only.
   ============================================================ */
dxbl-tab-list .dxbl-tabs-scroll-btn-prev[aria-hidden="true"],
dxbl-tab-list .dxbl-tabs-scroll-btn-next[aria-hidden="true"] {
    display: none !important;
}

/* Onglets de DetailView (DxFormLayoutTabPages) : flèches de défilement absentes
   quand le panneau est étroit (splitter du split view).

   Cause racine : dans la chaîne flex du form-layout, les conteneurs gardent
   min-width:auto et ne peuvent donc pas rétrécir sous la largeur totale des
   onglets. Le composant d'onglets grandit au-delà du panneau, un ancêtre le
   coupe, et son scroll-viewer ne détecte jamais de dépassement -> les flèches
   NavButtons (natives, actives par défaut sur desktop) restent masquées.

   Fix à la source : autoriser toute la chaîne flex à rétrécir (min-width:0).
   Le composant d'onglets est alors borné à la largeur réelle du panneau, le
   scroll-viewer mesure le dépassement et sort ses flèches de lui-même. */
.dxbl-fl,
.dxbl-fl .dxbl-fl-group,
.dxbl-fl .dxbl-fl-item,
.dxbl-fl .dxbl-fl-ctrl,
.dxbl-fl .dxbl-fl-ctrl-nc {
    min-width: 0 !important;
}

.dxbl-fl .dxbl-tabs {
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
}

/* Les boutons de défilement (dxbl-tabs-scroll-btn-prev/next) sont bien présents
   quand DevExpress détecte le dépassement, mais masqués : ils gardent
   aria-hidden="true" (mesuré le 26/10/2026, barre débordante). On leur redonne
   une taille ; leur chevron est l'icône native (un SVG de 13 px, présent en 26.1 —
   le chevron jadis dessiné en CSS, quand l'icône était vide, la doublait).

   On accroche l'affichage à la classe .dxbl-tabs-tablist-has-overflow que
   DevExpress pose sur la barre uniquement quand les onglets débordent : les
   flèches n'apparaissent donc que quand il y a réellement des onglets cachés,
   sans dépendre de l'attribut aria-hidden de chaque bouton. */
/* Spécificité (classe de la barre + classe du bouton + attribut, 0,3,0) volontaire : il
   faut battre la règle DevExpress .dxbl-tabs-scroll-btn-next[aria-hidden="true"]
   { display:none !important } (0,2,0) chargée après notre feuille. On affiche donc les
   flèches dès que la barre déborde, sans tenir compte de l'aria-hidden que
   DevExpress ne gère pas correctement dans ce contexte (onglets XAF). */
.dxbl-tabs-tablist-has-overflow .dxbl-tabs-scroll-btn-prev[aria-hidden],
.dxbl-tabs-tablist-has-overflow .dxbl-tabs-scroll-btn-next[aria-hidden] {
    flex: 0 0 24px !important;
    width: 24px !important;
    min-width: 24px !important;
    height: auto !important;
    align-self: stretch !important;
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
    padding: 0 !important;
    opacity: 1 !important;
    visibility: visible !important;
}


/* ============================================================
   5. PRIMARY BUTTON
   A DevExpress primary button takes its colours, text, icon and
   states from the theme's public variables
   (--dxds-color-surface-primary-*, set to the charter by charte/ds-pim.css);
   only its pressed and disabled text, which the theme takes from the
   rest state, is redeclared below. The creation and validation actions are primary through the
   API (Controllers/Shared/PrimaryActionsController), not by caption.
   The .dx-button rules below are the DevExtreme buttons (report
   designer), out of the theme's reach: they read the same public
   variables, so the two kinds stay alike. That is why the public way
   does not suffice for them and these !important remain.
   ============================================================ */

/* Pressed and disabled: the theme keeps the rest text colour on these states
   (--dxbl-btn-active-color is --dxbl-btn-color; a disabled button reads the
   neutral disabled tokens), while the charter paints them pale green — white
   text on it read 1.2:1. The tokens the text and background read are
   redeclared on the state itself (way 2, design/CHARTE.md § 4): the
   theme's --dxbl-btn-primary-color resolves on this same element.
   A disabled <button> never matches :active; the disabled rule comes
   after the .dxbl-active one, at the same specificity. */
.dxbl-btn-primary:active,
.dxbl-btn-primary.dxbl-active {
    --dxds-color-content-neutral-default-static-dark-rest: var(--ds-button-primary-pressed-content);
}

.dxbl-btn-primary.dxbl-disabled {
    --dxds-color-surface-neutral-default-disabled: var(--ds-button-primary-disabled-bg);
    --dxds-color-content-neutral-default-disabled: var(--ds-button-primary-disabled-content);
    /* XAF's disabled primary toolbar button reads the rest text token instead (its styles.css). */
    --dxds-color-content-neutral-default-static-dark-rest: var(--ds-button-primary-disabled-content);
}

/* XAF fades that button with a hard-coded opacity: .8 on top of the disabled
   colours, which already say "disabled" (way 3: same selector as XAF's,
   loaded after it, no !important). */
.dxbl-theme-fluent:not(.dxbl-theme-dark) .xaf-primary-toolbar-btn.dxbl-btn.dxbl-btn-primary.dxbl-disabled {
    opacity: 1;
}

.dx-button.dx-button-mode-contained.dx-button-default {
    background: var(--dxds-color-surface-primary-default-rest) !important;
    border: 1px solid var(--dxds-color-surface-primary-default-rest) !important;
    color: var(--dxds-color-content-neutral-default-static-dark-rest) !important;
}

    .dx-button.dx-button-mode-contained.dx-button-default:hover {
        background: var(--dxds-color-surface-primary-default-hovered) !important;
        border-color: var(--dxds-color-surface-primary-default-hovered) !important;
    }

    .dx-button.dx-button-mode-contained.dx-button-default.dx-state-active {
        background: var(--dxds-color-surface-primary-default-active) !important;
        border-color: var(--dxds-color-surface-primary-default-active) !important;
    }

    .dx-button.dx-button-mode-contained.dx-button-default.dx-state-disabled {
        background: var(--dxds-color-surface-primary-default-disabled) !important;
        border-color: var(--dxds-color-surface-primary-default-disabled) !important;
        cursor: not-allowed !important;
    }

/* The Bootstrap secondary class the window template's hamburger button carries: no stylesheet of ours
   or of the theme paints it (bootstrap-external only clears its shadow), so it reads the theme's neutral
   tokens, like a DevExpress secondary button — no !important needed, nothing competes. */
.btn-secondary {
    background: var(--dxds-color-surface-neutral-default-rest);
    border: 1px solid var(--dxds-color-border-neutral-default-rest);
    color: var(--dxds-color-content-neutral-default-rest);
}

    .btn-secondary:hover {
        background: var(--dxds-color-surface-neutral-default-hovered);
        border-color: var(--dxds-color-border-neutral-default-hovered);
    }


/* ============================================================
   6. to 9. SECONDARY, OUTLINE, DANGER, ICON-ONLY BUTTONS
   Secondary ACTIONS take the charter's secondary button (pale
   green, teal text; hover full green, white text —
   design/CHARTE.md § 5.1). They are every .dxbl-btn-secondary of a
   .dxbl-toolbar: the XAF toolbars of list and detail views and of
   popup windows (Annuler), split actions (both halves) and the "…"
   overflow button — DOM inventory of 23/10/2026. Measured with it:
   a disabled action loses .xaf-action, hence the toolbar scope. The
   other secondary renders stay the theme's: the grid's select-all
   split button, the funnel, editor buttons, inline row actions.
   Way 1 (design/CHARTE.md § 4): the theme resolves its
   --dxbl-btn-secondary-* on the button itself, so the public tokens
   are redeclared there; way 2 for the text of hover and pressed,
   which the theme takes from the rest state.
   Outline, danger and icon-only buttons: the theme's, untouched.
   ============================================================ */
.dxbl-toolbar .dxbl-btn-secondary {
    --dxds-color-surface-neutral-default-rest: var(--ds-button-secondary-enable-bg);
    --dxds-color-border-neutral-default-rest: var(--ds-button-secondary-enable-border);
    --dxds-color-content-neutral-default-rest: var(--ds-button-secondary-enable-content);
    /* the text of every toolbar action: the theme reads -subdued-rest inside .dxbl-btn-toolbar */
    --dxds-color-content-neutral-subdued-rest: var(--ds-button-secondary-enable-content);
    --dxds-color-surface-neutral-default-hovered: var(--ds-button-secondary-hover-bg);
    --dxds-color-border-neutral-default-hovered: var(--ds-button-secondary-hover-bg);
    /* pressed: -active, and -selected inside a toolbar */
    --dxds-color-surface-neutral-default-active: var(--ds-button-secondary-pressed-bg);
    --dxds-color-surface-neutral-default-selected: var(--ds-button-secondary-pressed-bg);
    --dxds-color-border-neutral-default-active: var(--ds-button-secondary-enable-border);
    --dxds-color-surface-neutral-default-disabled: var(--ds-button-secondary-disabled-bg);
    --dxds-color-content-neutral-default-disabled: var(--ds-button-secondary-disabled-content);
    --dxds-color-border-neutral-default-disabled: var(--ds-button-secondary-enable-border);
}

    /* A disabled button reads the disabled tokens, not these: no :not(disabled) needed. */
    .dxbl-toolbar .dxbl-btn-secondary:hover {
        --dxds-color-content-neutral-default-rest: var(--ds-button-secondary-hover-content);
        --dxds-color-content-neutral-subdued-rest: var(--ds-button-secondary-hover-content);
    }

    /* After :hover: a button held down is hovered too. */
    .dxbl-toolbar .dxbl-btn-secondary:active,
    .dxbl-toolbar .dxbl-btn-secondary.dxbl-active {
        --dxds-color-content-neutral-default-rest: var(--ds-button-secondary-pressed-content);
        --dxds-color-content-neutral-subdued-rest: var(--ds-button-secondary-pressed-content);
    }

/* Action icons follow their text, in every state (design/CHARTE.md § 5.1). An action icon is an
   <img> served by XAF's image service: it cannot read the text colour, and the set mixes the
   charter's line icons with DevExpress's multicolour Fluent ones (black pencil, red and green
   dashboards, orange refresh — measured 26/10/2026). Each state paints the icon's shape in the
   token of its text through an SVG filter declared in Pages/_Host.cshtml (way 3). Two icons
   keep one tint in every state, their text's: a grid row's inline action (.xaf-inline-action,
   a text button with no background, on the row's colour) and a layout tab's icon (the tab's
   text is Teal in every state). Toolbar states: XAF's own classes and the button's native
   states (a disabled action carries the disabled attribute); every rule weighs (0,2,1) or
   (0,3,1), and the order settles the rest — disabled after hover, the primary button after
   the secondary one. */
.ds-icon-tints {
    position: absolute;
}

.xaf-toolbar-item-icon {
    filter: url(#ds-icon-secondary);
}

.xaf-inline-action img {
    filter: url(#ds-icon-grid-line);
}

.xaf-layout-tab-icon {
    filter: url(#ds-icon-tab);
}

button:hover > .xaf-toolbar-item-icon {
    filter: url(#ds-icon-secondary-hover);
}

button:active > .xaf-toolbar-item-icon,
button.dxbl-active > .xaf-toolbar-item-icon {
    filter: url(#ds-icon-secondary-pressed);
}

button:disabled > .xaf-toolbar-item-icon {
    filter: url(#ds-icon-secondary-disabled);
}

button.xaf-primary-toolbar-btn > .xaf-toolbar-item-icon {
    filter: url(#ds-icon-primary);
}

button.xaf-primary-toolbar-btn:active > .xaf-toolbar-item-icon {
    filter: url(#ds-icon-primary-pressed);
}

button.xaf-primary-toolbar-btn:disabled > .xaf-toolbar-item-icon {
    filter: url(#ds-icon-primary-disabled);
}

/* An open split action and an open "…": the theme resolves the pressed colours of the dropdown
   half and of the "…" on their container (--dxbl-btn-split-contained-secondary-active-*,
   --dxbl-btn-group-contained-secondary-active-*), not on the button — measured grey-violet
   (#dfdce5) on the "…" before this rule. The "…" group only: a wider toolbar group also holds
   the toolbar's editors, whose border would inherit the charter's. */
.dxbl-toolbar .dxbl-btn-split,
.dxbl-toolbar .dxbl-toolbar-btn-ellipsis {
    --dxds-color-surface-secondary-default-selected: var(--ds-button-secondary-pressed-bg);
    --dxds-color-content-secondary-default-rest: var(--ds-button-secondary-pressed-content);
    --dxds-color-border-neutral-default-rest: var(--ds-button-secondary-enable-border);
}


/* ============================================================
   10. XAF TOOLBAR — native
   XAF renders its toolbar buttons as .dxbl-btn-secondary, except
   the primary actions (.dxbl-btn-primary.xaf-primary-toolbar-btn):
   XAF's own (Logon, DialogOK, Save) and ours
   (Controllers/Shared/PrimaryActionsController: New, the piece
   lines' New - CustomNewAction - and Valider). Their metrics are
   the theme's; their colours, the charter's (sections 5 and 6).
   ============================================================ */

/* The gap between two actions: the charter's (design/CHARTE.md § 5.1). XAF puts each action in
   its own toolbar group, spaced by --dxbl-toolbar-group-spacing, which no public token feeds
   (the theme sets it per size on .dxbl-toolbar.dxbl-sm: same weight, this sheet loads after).
   XAF's toolbars only (view, nested view, popup window and its footer): the report viewer and
   the rich-text ribbon keep their own layout. */
:is(.main-toolbar, .nested-toolbar, .xafPopup) .dxbl-toolbar {
    --dxbl-toolbar-group-spacing: var(--ds-toolbar-item-gap);
}


/* ============================================================
   11. SIDEBAR NAVIGATION — VOLONTAIREMENT VIDE
   La sidebar est habillée exclusivement par sidebar.css (classe NATIVE
   `.sidebar`), chargé AVANT ce fichier (_Host.cshtml) : ses règles gagnent
   par leur spécificité (`.sidebar .X`), pas par l'ordre. Ne pas réintroduire
   de règle `.sidebar` ici : chargée après, elle l'emporterait à spécificité
   égale sur sidebar.css.
   ============================================================ */


/* ============================================================
   12. TABS / DETAIL-VIEW NAVIGATION
   The detail view's tabs are DevExpress tabs (DxFormLayoutTabPages):
   section 20. The Bootstrap .nav-tabs once styled here are no longer
   rendered (none in the DOM, 26/10/2026).
   ============================================================ */


/* ============================================================
   13. GRID (DataGrid)

   IMPORTANT — CSS variable override pattern
   ─────────────────────────────────────────
   DevExpress reads row colours from CSS variables, not from
   hard-coded background-color declarations. Surcharger la VARIABLE
   sur un ancêtre scopé (.dxbl-grid) est donc la voie à préférer :
   une seule déclaration éteint ou reteinte toutes les règles du
   composant qui la lisent, y compris celles des versions à venir.

   ⚠️ Nuance, la formulation précédente était fausse : « !important on
   background-color is silently ignored » ne l'est pas — le fichier s'en
   sert des centaines de fois et cela fonctionne. Ce qu'un !important ne
   fait pas, c'est se propager aux AUTRES règles alimentées par la même
   variable : il gagne là où il est posé, et laisse passer les états
   qu'on n'a pas pensé à réécrire. C'est un argument de maintenance,
   pas de cascade.

   Variable chain traced in core.min.css (relue en 26.1.3 le 2026-09-10) :
     .dxbl-grid-table .dxbl-grid-selected-row     ← sur le <tr>, PAS sur les td
       → reads --dxbl-grid-selection-bg
       → default: var(--dxds-color-surface-primary-deep-rest)
       → which maps to primary-100 (full orange) by default

   ⚠️ Cette chaîne était écrite « .dxbl-grid-selected-row td » et servait à justifier que les
   états de ligne se peignent sur les td. C'était une observation de la v25.x : en 26.1 le
   sélecteur DevExpress n'a plus de td, tous les états de ligne sont peints sur le <tr>, et
   les cellules figées en héritent. Voir le CONTRAT DE PEINTURE DES LIGNES plus bas — c'est
   lui qui fait autorité, cette § ne parle que du mécanisme des variables.

   Checkbox checked variable chain:
     dxbl-check.dxbl-checkbox-checked
       → --dxbl-checkbox-check-element-bg
       → --dxbl-checkbox-primary-checked-check-element-bg
       → --dxds-color-content-primary-compound-rest  (charte/ds-pim.css : l'accent de la charte)
     Les cases à cocher sont réglées par leurs jetons publics, plus bas
     (« Cases à cocher »), jamais par leurs variables de composant.
   ============================================================ */

/* ── Les couleurs de la grille : la charte (CHARTE.md § 5.3) ─────────────────────────────
   Hauteurs, marges, police : celles du thème, à la densité de l'utilisateur (SizeMode) — rien
   ici. Les couleurs, sur .dxbl-grid, la racine de la famille.

   Voie 1, les jetons publics que le thème lit pour la grille ; dans une grille, ils ne
   nourrissent que la grille (relevé dans core.min.css 26.1.3, le 26/10/2026) :
     surface-primary-deep-rest      → ligne sélectionnée (et survol de l'entonnoir d'en-tête)
     surface-primary-deep-hovered   → ligne sélectionnée survolée
     surface-primary-deep-selected  → ligne en édition
     content-neutral-default-*      → texte des lignes, de l'en-tête, du pied et des cellules en
                                      édition (Teal pour toute la grille, comme la maquette)

   Écrits en --dxbl-grid-* : aucun jeton public ne les porte sans repeindre aussi les boutons,
   le pager et les listes logés dans la grille, ou toute l'application :
     hover-bg, focus-bg       — surface-neutral-default-hovered (35 composants) et
                                surface-primary-default-rest (l'accent global)
     selection-focus-dark-*   — ligne sélectionnée ET focalisée : l'accent global, texte blanc
     header-bg, footer-bg     — surface-transparent-rest (tout fond transparent). Le pied doit
                                de plus être opaque : une cellule de pied figée est collante et
                                peinte au-dessus des cellules qui défilent dessous.
     border-color: transparent — pas de filets : la charte sépare les lignes par le zébrage.

   Survol et ligne courante au clavier portent le MÊME repère, gris (« je suis ici ») ; seule la
   sélection, qui porte une décision, est verte. La maquette met le survol au vert de la ligne
   sélectionnée (Jungle_Green/20) : deux surbrillances de même teinte, aucune lisible — défaut
   observé à l'usage le 03/08/2026, et pratique Fluent. Arbitrage : CHARTE.md § 5.3. */
.dxbl-grid {
    --dxds-color-surface-primary-deep-rest: var(--ds-grid-table-line-active-bg);
    --dxds-color-surface-primary-deep-hovered: var(--ds-grid-table-line-active-hover-bg);
    --dxds-color-surface-primary-deep-selected: var(--ds-grid-table-line-edit-bg);
    --dxds-color-content-neutral-default-rest: var(--ds-grid-line-item-enable-content);
    --dxds-color-content-neutral-default-hovered: var(--ds-grid-line-item-hover-content);
    --dxds-color-content-neutral-default-selected: var(--ds-grid-table-line-active-content);
    --dxbl-grid-hover-bg: var(--ds-grid-table-line-cursor-bg);
    --dxbl-grid-focus-bg: var(--ds-grid-table-line-cursor-bg);
    --dxbl-grid-focus-color: var(--ds-grid-line-item-enable-content);
    --dxbl-grid-selection-focus-dark-bg: var(--ds-grid-table-line-active-hover-bg);
    --dxbl-grid-selection-focus-dark-color: var(--ds-grid-table-line-active-content);
    /* Malgré son nom, le thème la lit pour toute ligne focalisée SURVOLÉE, sélectionnée ou non
       (même valeur que focus-bg par défaut) : le repère de position, comme le focus. */
    --dxbl-grid-selection-focus-dark-hover-bg: var(--ds-grid-table-line-cursor-bg);
    --dxbl-grid-header-bg: var(--ds-grid-filter-item-enable-bg);
    --dxbl-grid-footer-bg: var(--ds-grid-footer-item-bg);
    --dxbl-grid-border-color: transparent;
}

/* Zébrage : la classe est posée sur une ligne sur deux par CustomListViewController
   (CustomizeElement, VisibleIndex impair) — un :nth-child changerait de parité à chaque pas du
   défilement virtuel, qui recycle les lignes. Recette de DevExpress (dxdocs, « Alternate Row
   Style ») : la ligne redéclare la couleur dont le thème peint toute ligne, si bien que survol,
   sélection et colonnes figées (qui héritent du <tr>) restent ceux du thème.
   Même recette pour le fond d'une règle de style « toute la ligne » (27/10/2026) : sa classe
   style-bg-<couleur> pose --style-rule-bg (css/domains/styling.css), qui l'emporte ici sur le zébrage. */
.dxbl-grid :is(.grid-row-alternate, [class*="style-bg-"]) {
    --dxbl-grid-row-bg: var(--style-rule-bg, var(--ds-grid-table-line-enable-impair-bg));
}

/* ── CONTRAT DE PEINTURE DES LIGNES — À NE PAS CONTOURNER ────
   DevExpress 26.1 peint TOUS les états de ligne (survol, sélection, édition, focus) sur le
   <tr>, jamais sur les <td>, et donne aux cellules figées `background-color: inherit` :
     .dxbl-grid .dxbl-grid-table > tbody > tr        { background-color: var(--dxbl-grid-row-bg) }
     .dxbl-grid .dxbl-grid-table > tbody > tr > td.dxbl-grid-fixed-cell { background-color: inherit }
   (core.min.css 26.1.3, relu le 2026-09-10.)

   La ligne est donc la SOURCE DE VÉRITÉ UNIQUE de sa couleur. Une colonne figée est
   `position: sticky` et se peint AU-DESSUS des cellules qui défilent dessous : si son <tr>
   n'a pas de fond opaque, on voit le contenu défiler à travers la colonne gelée.

   ⚠️ RÈGLE PRATIQUE : un état de LIGNE (toute la ligne change de couleur) se peint sur le
   <tr>. Un état de CELLULE (une colonne se signale) se peint sur le <td>. Peindre un état de
   ligne sur les td casse les colonnes figées — c'est le défaut qu'on a payé deux fois :
   d'abord sur la cellule de sélection (rustine de la ligne focalisée de site.css, retirée le
   26/10/2026), puis sur toute la grille.

   Ce que la règle ci-dessous fait exactement — et ce qu'elle ne fait pas :
   elle ne change RIEN au rendu actuel (un td sans fond laisse déjà voir son <tr>, et DevExpress
   pose déjà `inherit` sur les cellules figées avec une spécificité supérieure). C'est un
   GARDE-FOU, pas le mécanisme : elle intercepte, à (0,2,3), toute future règle de fond sur td
   moins spécifique et sans !important.

   Volontairement SANS !important, et c'est ce qui la rend sûre. Tout ce qui doit repeindre
   par-dessus le fait :
     · les états de cellule du projet (.pim-coef-below-min, .pim-general-tariff-cell) portent
       !important ;
     · XAF écrit les fonds qu'il calcule à l'exécution en STYLE INLINE !important — la § des
       cellules « valeur modifiée » plus bas le documente et s'en sert. Ce serait vrai aussi d'un
       [Appearance] BackColor ; il n'y en a plus depuis le 27/10/2026 (la dernière, la ligne d'avoir
       épuisée, est devenue une classe : CreditNoteLineExhaustedRowStyleController), et les
       compteurs de dette C# empêchent d'en remettre.
   Un !important ici aurait au contraire écrasé ces états.
   Seule exception, sans !important elle non plus : la cellule d'une règle de style « champs précis »
   (style-bg-<couleur>, css/domains/styling.css, 27/10/2026), un état de CELLULE qui porte son propre fond. */
.dxbl-grid .dxbl-grid-table > tbody > tr > td:not([class*="style-bg-"]) {
    background-color: inherit;
}

/* ── Ligne courante au clavier ───────────────────────────────
   Sans ce style, le seul repère visuel d'une grille est le survol, qui suit la SOURIS : en
   naviguant au clavier on ne voit rien bouger, et la bande grise reste là où le pointeur a été
   laissé. C'est le défaut signalé sur la liste produits.

   :focus-within et non .dxbl-grid-focused-row : cette dernière classe n'est posée que si
   FocusedRowEnabled est activé sur la grille, ce qui n'est le cas que sur trois vues
   (raccourcis F4 des lignes de pièce et des tarifs). :focus-within, lui, ne dépend d'aucun
   réglage du composant — il désigne la ligne qui contient l'élément ayant le focus DOM, donc
   celle où le Tab vient d'arriver.
   ⚠️ LIMITE ASSUMÉE : cela couvre la navigation au Tab, PAS les flèches haut/bas. Les flèches
   ne déplacent aucun focus DOM tant que FocusedRowEnabled est désactivé — aucun CSS ne peut
   cibler un état que le composant n'écrit pas.

   LE FOCUS REPREND EXACTEMENT LE FOND DU SURVOL (--ds-grid-table-line-cursor-bg, posé sur
   --dxbl-grid-hover-bg et --dxbl-grid-focus-bg plus haut). Souris et clavier désignent la même
   chose — « je suis ici » ; seule la SÉLECTION porte une décision et garde son vert. Une
   teinte verte ici se confond avec la sélection (observé le 2026-08-03).

   Le fond est posé sur le tr, jamais sur les td — cf. le CONTRAT DE PEINTURE DES LIGNES plus
   haut : sur les td, une colonne gelée devenait transparente sur la ligne focalisée. Une ligne
   sélectionnée qui a le focus reste verte : la règle de sélection du thème est plus spécifique. */
.dxbl-grid .dxbl-grid-table > tbody > tr:focus-within {
    background-color: var(--ds-grid-table-line-cursor-bg);
}

/* La case à cocher d'une ligne focalisée (FocusedRowEnabled, les trois grilles du raccourci F4) : le
   thème, qui peint cette ligne à l'accent plein, y dessine la case en « contenu sur fond sombre »
   (blanc). Sur notre repère gris clair, la case vide disparaissait — la rustine de site.css le
   compensait jusqu'au 26/10/2026. Voie 2 : ces jetons publics, sur l'état, reprennent ceux de la case. */
.dxbl-grid .dxbl-grid-table .dxbl-grid-focused-row {
    --dxds-color-content-neutral-default-static-dark-rest: var(--ds-checkbox-enable-border);
    --dxds-color-content-neutral-default-static-dark-hovered: var(--ds-checkbox-hover-border);
    --dxds-color-content-neutral-default-on-surface-rest: var(--ds-checkbox-active-on-light-bg);
}

    /* Cochée sur une ligne focalisée ET sélectionnée, le thème la dessine en creux — fond transparent,
       coche blanche — : invisible sur fond clair. Elle reprend son aplat d'accent. */
    .dxbl-grid .dxbl-grid-table .dxbl-grid-focused-row .dxbl-checkbox-checked {
        --dxds-color-surface-transparent-rest: var(--ds-checkbox-active-on-light-bg);
    }

/* UN SEUL REPÈRE À L'ÉCRAN. Dès qu'une ligne porte le focus clavier, le survol des AUTRES
   lignes se tait : sans ça la souris laisse sa surbrillance là où le pointeur a été abandonné,
   et l'on voit deux lignes marquées sans savoir laquelle suit les flèches.

   On neutralise la VARIABLE du survol, sur chaque ligne, plutôt que de surcharger les règles
   du thème qui la lisent (ligne simple, sélectionnée, focalisée). Effet de bord assumé : tant
   que le focus reste dans la grille, le survol des autres lignes est éteint.

   ⚠️ ÉTEINDRE, C'EST REPEINDRE À LA COULEUR DE LA LIGNE — PAS `transparent`, sur lequel une
   colonne figée héritait de rgba(0,0,0,0) (mesuré le 2026-09-10). Déclaré sur chaque <tr> et
   non sur la grille : --dxbl-grid-row-bg y est résolu ligne par ligne, et une ligne zébrée
   garde son gris au lieu de passer au blanc. */
.dxbl-grid:has(.dxbl-grid-table > tbody > tr:focus-within) .dxbl-grid-table > tbody > tr:not(:focus-within) {
    --dxbl-grid-hover-bg: var(--dxbl-grid-row-bg);
}

/* ── La barre de défilement horizontale ne doit pas recouvrir la dernière ligne ──
   Le thème Fluent rend la barre horizontale EN SURIMPRESSION, pas dans le flux :
     .dxbl-scroll-viewer-hor-scroll-bar { position: absolute; bottom: 0; left: 0; ... }
   (core.min.css, hauteur --dxbl-scroll-viewer-hor-scroll-bar-hover-height: 0.5rem).
   Quand la grille est défilée en bas, la barre se pose donc SUR le texte de la
   dernière ligne — c'est ce qu'on voyait sur la grille des lignes de pièce.

   On réserve sous le contenu défilant la hauteur de la barre (8px) plus une marge de
   respiration, soit 16px : la dernière ligne se détache nettement au lieu de frôler la
   barre. C'est du padding sur le CONTENEUR de défilement, pas sur les lignes : la
   hauteur de ligne — seule grandeur dont dépend le moteur de défilement virtuel (cf.
   l'avertissement plus bas) — n'est pas touchée, seule l'étendue défilante s'allonge. */
.dxbl-grid > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content {
    padding-bottom: 1rem !important;
}

/* ════════════════════════════════════════════════════════════════════════════════════
   HAUTEUR DE LA GRILLE — PRÉREQUIS DU DÉFILEMENT VIRTUEL. NE PAS RETIRER.

   Documentation DevExpress (Scrolling in Blazor Grid, « Grid Height ») :
   « Sans contrainte CSS de hauteur, la grille ajuste automatiquement sa hauteur
     (jusqu'à un maximum de 10 000 pixels) pour contenir toutes les lignes. »

   Le moteur de défilement virtuel déduit de cette hauteur combien de lignes il doit
   afficher : hauteur du conteneur ÷ hauteur d'une ligne. Au-delà d'un maximum interne,
   il lève UnableToRequestItemsException, la mise à jour de la vue n'aboutit jamais, et
   la classe `dxbl-skeleton-animate` n'est jamais retirée : les lignes restent affichées
   en placeholders gris, indéfiniment, alors que les données sont bien arrivées.

   C'est ce qui s'est produit le 29/07/2026, sur toutes les vues liste :
       lignes ~40px (défaut DevExpress) -> 10000/40 = 250 lignes   -> OK
       lignes ~16px (compactage ci-dessous) -> 10000/16 = 625 lignes -> dépassement
   Le compactage n'était pas fautif en soi : il a fait franchir un seuil que rien ne
   bornait. La contrainte de hauteur ci-dessous supprime la cause, et permet de garder
   les lignes compactes.

   La borne est exprimée en unités de fenêtre pour ne dépendre d'aucun ancêtre : la
   chaîne flex de XAF ne garantit pas toujours une hauteur définie jusqu'à la grille.
   `min-height: 0` est indispensable sur un enfant flex, sinon il refuse de descendre
   sous la taille de son contenu et aucune borne ne s'applique.
   ════════════════════════════════════════════════════════════════════════════════════ */
/* Le conteneur de page porte `overflow-auto` (CustomApplicationWindowTemplateComponent.razor,
   ligne 130) : indispensable pour les vues détail, dont les formulaires dépassent l'écran.
   Mais quand il contient une grille en défilement virtuel, c'est LUI qui prend le défilement,
   la grille n'est jamais contrainte en hauteur et n'établit jamais son étendue virtuelle
   (mesuré le 29/07/2026 : zone de défilement annonçant 241px de contenu pour 11 365 lignes,
   et lignes rendues positionnées à -546px, hors écran).
   On neutralise donc le défilement du conteneur uniquement dans ce cas, et on laisse la
   grille remplir la place : c'est elle qui défile. `:has()` limite la règle aux pages qui
   contiennent effectivement une grille virtualisée — les vues détail gardent leur défilement. */
.main-content:has(> .grid-content.grid-virtual-scrolling-mode) {
    overflow: hidden !important;
    min-height: 0 !important;
}

.main-content > .grid-content.grid-virtual-scrolling-mode {
    height: 100% !important;
    min-height: 0 !important;
}

    /* La grille remplit son conteneur désormais borné. `min-height: 0` est indispensable sur
       un enfant flex, sinon il refuse de descendre sous la taille de son contenu. */
    .main-content > .grid-content.grid-virtual-scrolling-mode .dxbl-grid {
        height: 100% !important;
        min-height: 0 !important;
        max-height: none !important;
    }

    .main-content > .grid-content.grid-virtual-scrolling-mode .dxbl-scroll-viewer {
        min-height: 0 !important;
    }

/* Grille imbriquée dans une fiche qui prend la hauteur de la fenêtre (classe posée par
   GridFillWindowController, qui liste ses vues ; aujourd'hui les lignes d'une mercuriale vue sans
   détail, PIM-466) : hors du cas ci-dessus, XAF plafonne une grille imbriquée en défilement virtuel à
   300 px — un petit cadre, alors que la liste défile sans pages. Elle prend la hauteur de la fenêtre
   moins l'en-tête de la fiche. La borne reste en unités de fenêtre : une hauteur non bornée casserait
   le défilement virtuel (voir l'avertissement ci-dessus). La vue avec détails de la mercuriale
   (PriceList_PriceListItems_ListView) n'est plus visée ici : elle est un écran liste + détail, bornée par son
   panneau (règle suivante). La même hauteur de fenêtre lui avait été donnée le 28/10/2026 (défaut n° 63) : plus
   haute que son panneau de 300 px, la grille passait sous le panneau de la ligne (29/10/2026, PIM-530).
   Spécificité suffisante : la seule autre hauteur forcée vise les listes racines (.main-content). */
.grid-content.grid-virtual-scrolling-mode:has(.grid-fill-window) {
    height: auto !important;
    max-height: none !important;
}

.dxbl-grid.grid-fill-window {
    height: calc(100vh - 300px) !important;
    min-height: 320px !important;
    max-height: none !important;
}

    .dxbl-grid.grid-fill-window > .dxbl-scroll-viewer {
        flex: 1 1 0 !important;
        min-height: 0 !important;
        max-height: none !important;
    }

/* Écrans liste + détail (MasterDetailMode = ListViewAndDetailView, côte à côte ou l'un sous l'autre) : la grille
   prend la hauteur de son panneau, jamais plus. Le panneau a la taille que lui donne le séparateur (300 px par
   défaut sous XAF, ou la position où l'utilisateur l'a tiré) et laisse déborder son contenu : une grille plus haute
   passait sous le panneau de détail (mercuriale « Liste avec détails », 29/10/2026). Ici la grille suit le panneau,
   quelle que soit la taille de la fenêtre ou la position du séparateur — et reste bornée, ce que le défilement
   virtuel exige (défaut n° 63 : une grille non bornée demandait toutes ses lignes d'un coup).
   Le minimum du panneau n'est pas touché : sous un séparateur enregistré à 50 px, c'est lui qui garde 300 px de
   liste (filtres enregistrés, règles d'édition exclusive). Mesuré sur huit écrans le 29/10/2026 : aucun débordement,
   aucun panneau changé de taille. xaf-contextmenu-click-helper est l'enveloppe que XAF pose entre le conteneur et la
   grille : sans elle dans la chaîne de flex, la grille ne voit pas la hauteur du panneau.
   Aucun !important : les règles natives de XAF que celles-ci complètent n'en portent pas (styles.css,
   « .xaf-masterdetail-listview »), et la grille garde son max-height: 100 % natif, relatif à un parent borné
   (mesuré sans !important le 29/10/2026, mercuriale et commandes, de 800×600 à 2560×1440 : aucun débordement). */
.xaf-masterdetail-container > .xaf-masterdetail-listview {
    display: flex;
    flex-direction: column;
}

    .xaf-masterdetail-container > .xaf-masterdetail-listview > .grid-content {
        flex: 1 1 auto;
        display: flex;
        flex-direction: column;
    }

        .xaf-masterdetail-container > .xaf-masterdetail-listview > .grid-content > xaf-contextmenu-click-helper {
            display: flex;
            flex-direction: column;
            flex: 1 1 auto;
            min-height: 0;
        }

    /* L'élément dxbl-grid, comme la règle native qu'elle complète (« .xaf-masterdetail-listview > .grid-content
       dxbl-grid », styles.css de XAF 26.1) : pas de classe interne .dxbl-*. */
    .xaf-masterdetail-container > .xaf-masterdetail-listview dxbl-grid {
        flex: 1 1 auto;
        min-height: 0;
    }

/* Colonnes d'action (crayon, import…) : sans largeur contrainte, l'icône flotte au
   centre d'une colonne large -> gros écarts horizontaux. Même astuce que la colonne
   de sélection (width:1px) : la colonne se réduit à la largeur de son contenu.
   Ciblé th + td pour que l'en-tête ne rouvre pas la colonne. */
.dxbl-grid .dxbl-grid-table td.xaf-action,
.dxbl-grid .dxbl-grid-table th.xaf-action,
.dxbl-grid .dxbl-grid-table td.dxbl-grid-command-cell,
.dxbl-grid .dxbl-grid-table th.dxbl-grid-command-cell {
    width: 1px !important;
    white-space: nowrap !important;
}

/* ⚠️ NE REMETS PAS DE `height` NI DE `min-height: 0` SUR LES LIGNES OU LES CELLULES.
   Cette règle imposait `height: 16px !important` sur les <tr>/<td>. Elle a été retirée le
   29/07/2026 : le défilement virtuel de DxGrid calcule l'étendue de défilement et la position
   de chaque ligne à partir de SA PROPRE notion de hauteur de ligne, celle que lui donnent ses
   variables CSS internes — pas celle réellement rendue. Forcer la hauteur ici changeait donc
   l'affichage sans que le moteur le sache. Mesuré : zone de défilement annonçant 241px de
   contenu pour 11 365 lignes, lignes rendues avec leurs données mais positionnées à top:-546px
   (hors écran), classe `dxbl-skeleton-animate` jamais retirée → placeholders gris permanents
   sur toutes les vues liste.

   La densité vient de l'API du composant : le SizeMode de l'utilisateur (Compact par défaut,
   Startup, GlobalOptions ; le thème Fluent définit alors --dxbl-grid-text-height ≈ 16px et
   les marges des cellules) + TextWrapEnabled = false (hauteur de ligne uniforme, requise par
   le moteur ; Controllers/Shared/CustomListViewController.cs). Pour ajuster la densité, c'est
   le SizeMode — pas ici : plus aucune hauteur, marge, police ni taille d'icône n'est forcée
   dans une grille depuis le 26/10/2026 (lot 3 de la charte). */

/* Cellules « valeur modifiée » (mode batch-edit) : XAF pose en inline
   `background-color: var(--fluent-primary-transparent-bg, var(--bs-primary-transparent-bg)) !important`.
   Le défaut Bootstrap = color-mix(15% primary, white) -> teal pâle perçu bleu.
   Impossible de battre un !important inline par une règle classique : on
   redéfinit donc la variable SUR la cellule -> le var() inline résout en la couleur de sa
   ligne (blanche ou zébrée ; pas transparente : une cellule figée laisserait voir le contenu
   qui défile dessous). Scopé à .xaf-grid-modified-value : n'affecte aucun autre usage. */
.dxbl-grid td.xaf-grid-modified-value,
dxbl-tree-list.dxbl-grid td.xaf-grid-modified-value {
    --fluent-primary-transparent-bg: var(--dxbl-grid-row-bg);
    --bs-primary-transparent-bg: var(--dxbl-grid-row-bg);
}

/* Calendriers de tournées (classe posée par RoundCalendarGrid.LockToDataSourceOrder) :
   lignes fixes Lundi→Dimanche, non ouvrables. On masque la colonne de commande inline
   « Ouvrir » (action ShowObject, affichée désactivée) : contenu caché + colonne réduite
   à 0 pour ne pas laisser de gouttière. La 1re <col> = colonne de commande. width:0 !important
   bat le width inline 61px.
   ATTENTION : le même marqueur est posé sur des grilles qui, elles, ont une colonne de
   sélection (multi-select activé, ex. « Clients desservis » / CustomerRoundDay). Là, la 1re
   <col> est la colonne de sélection : la réduire à 0 écrase la case « tout sélectionner » de
   l'en-tête, qui déborde sur la colonne suivante. On exclut donc ce cas via
   :not(:has(.dxbl-grid-selection-cell)) — on ne collapse la 1re colonne que sans sélection. */
.round-calendar-grid .dxbl-grid-table:not(:has(.dxbl-grid-selection-cell)) > colgroup > col:first-child {
    width: 0 !important;
}

.round-calendar-grid .dxbl-grid-command-cell {
    padding: 0 !important;
    border: 0 !important;
}

    .round-calendar-grid .dxbl-grid-command-cell > * {
        display: none !important;
    }

/* ── Cases à cocher — la charte (CHARTE.md § 5.9), voie 1 ────────────────────────────────
   Taille, épaisseur, rayon : ceux du thème, à la densité de l'utilisateur (16 px en Compact
   comme en Standard) — une seule taille, en grille comme sur un formulaire. La case cochée
   (accent plein, coche blanche) vient du pont de la charte (charte/ds-pim.css) : content-primary-compound-* et
   border-primary-compound-*, que seule la case lit.
   La case VIDE prend sa bordure dans le gris « accessible » commun à d'autres repères
   (border-neutral-accessible-*) : on le redéclare ici, sur la racine de la famille, pour la
   seule case — ni l'interrupteur ni le bouton radio, qui ont leurs propres jetons (lot 4).
   Ce bloc remplace 26 règles à !important qui repeignaient une case cochée blanche sur blanc
   (le § 2b mettait l'aplat en blanc) et forçaient trois tailles (20, 16 et 12 px). */
.dxbl-checkbox:not(.dxbl-checkbox-switch):not(.dxbl-checkbox-radio) {
    --dxds-color-border-neutral-accessible-rest: var(--ds-checkbox-enable-border);
    --dxds-color-border-neutral-accessible-hovered: var(--ds-checkbox-hover-border);
}

/* Survol de ligne : celui du composant (HighlightRowOnHover, posé par CustomListViewController),
   qui lit --dxbl-grid-hover-bg, le selected-hover et le focus-dark-hover réglés plus haut. Il
   remplace une règle maison à !important (0,9,2) qui ne couvrait ni la ligne sélectionnée ni la
   ligne focalisée survolées. */

/* Ligne de filtre (« Grid filter research » de la charte) : un champ par colonne, blanc, bordure
   grise, gris plus soutenu au survol et au focus. Mêmes jetons publics que les champs du § 18,
   redéclarés sur les champs de cette seule ligne. Texte et police : ceux de la grille. */
.dxbl-grid .dxbl-grid-filter-row .dxbl-text-edit {
    --dxds-color-border-neutral-default-rest: var(--ds-grid-filter-research-enable-border);
    --dxds-color-border-neutral-default-hovered: var(--ds-grid-filter-research-hover-border);
    --dxds-color-border-neutral-default-active: var(--ds-grid-filter-research-active-border);
    --dxds-color-content-neutral-subdued-rest: var(--ds-grid-filter-research-enable-content);
}

    .dxbl-grid .dxbl-grid-filter-row .dxbl-text-edit:not(:focus-within):hover {
        --dxds-color-surface-neutral-default-rest: var(--ds-grid-filter-research-hover-bg);
    }

    .dxbl-grid .dxbl-grid-filter-row .dxbl-text-edit:focus-within {
        --dxds-color-surface-neutral-default-rest: var(--ds-grid-filter-research-active-bg);
    }

/* ============================================================
   13b. TREE LIST — la grille en arbre : toute la largeur
   ============================================================ */

dxbl-tree-list.dxbl-grid {
    width: 100% !important;
    min-width: 100% !important;
}

    dxbl-tree-list.dxbl-grid .dxbl-scroll-viewer,
    dxbl-tree-list.dxbl-grid .dxbl-scroll-viewer-content,
    dxbl-tree-list.dxbl-grid .dxbl-grid-table {
        width: 100% !important;
        min-width: 100% !important;
    }

    /* Le reste de l'arbre est celui de la grille (§ 13 : couleurs, zébrage, densité du
       SizeMode ; survol et zébrage posés par TreeListRowStyleController). Les « lignes-pilules »
       arrondies, sur fond gris, hauteur, marges et police forcées, venaient d'une maquette
       antérieure à la charte (nœud 2308-47972, jamais archivé) : retirées le 26/10/2026 (lot 3).
       Elles imposaient aussi la seule exception au CONTRAT DE PEINTURE DES LIGNES. */

/* ============================================================
   14. INPUTS / SEARCH / EDITORS — native
   Text editors, search boxes and their states (hover, focus,
   invalid, disabled, read-only) are the theme's: font through
   --dxds-font-family-sans-serif (charte/ds-pim.css), colours through the
   public tokens. What follows are functional exceptions only.
   ============================================================ */

/* Client code listing memo (Export Catalogue popup): a fixed 280 px box whose textarea fills it
   and scrolls, instead of growing with thousands of codes. CssClass "pim-scroll-memo" is set in
   MemoAutoResizeController. */
.detail-view-content .pim-scroll-memo.dxbl-memo-edit,
.dxbl-modal-content .pim-scroll-memo.dxbl-memo-edit,
.pim-scroll-memo.dxbl-memo-edit,
.pim-scroll-memo .dxbl-memo-edit {
    height: 280px !important;
    min-height: 280px !important;
    max-height: 280px !important;
    overflow: hidden !important;
}

.pim-scroll-memo textarea {
    height: 100% !important;
    min-height: 100% !important;
    max-height: 100% !important;
    overflow-y: auto !important;
    resize: none !important;
}


/* ============================================================
   15. PAGE TITLE / TOP BAR
   The charter's page header ("Fil d'ariane", design/CHARTE.md § 5.11):
   a bare back chevron, the module in grey, the page in green,
   text/16/semibold. XAF renders it as the back action and its
   ViewCaptionComponent; the two-line breadcrumb of the mock-up is a
   custom component, a ticket of its own (plan § 6). No global
   h1-h6 or header font: the body's Manrope is inherited.
   ============================================================ */

/* Page band (back action + view caption), rendered in the content area by
   Templates/CustomApplicationWindowTemplateComponent.razor in place of XAF's hidden dark header.
   Its horizontal padding is XAF's own for .main-toolbar (its styles.css, same breakpoint), so
   the title lines up with the first action below: the template's Bootstrap utilities (px-2
   px-sm-3) are inert, no Bootstrap sheet is loaded with the Fluent theme (measured 26/10/2026 —
   the title sat against the sidebar). */
.main-pagebar {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: var(--ds-fil-d-ariane-content-gap-default);
    padding: 16px .5rem 2px;
    min-height: 44px;
    flex: 0 0 auto;
}

@media (min-width: 576px) {
    .main-pagebar {
        padding-inline: 1rem;
    }
}

/* Back action: the charter's bare chevron, no box — the theme's own text button
   (.dxbl-btn-text-none), only its colour from the charter. The arrow is a mask painted with
   currentColor. Weighs (0,3,0), over the theme's (0,2,0) loaded before. */
.main-pagebar .navigate-back-icon-container.dxbl-btn {
    color: var(--ds-header-page-icon);
}
/* View caption (ViewCaptionComponent), XAF's DOM: .view-caption-root > [.xaf-caption-icon-container,
   .xaf-caption-container > (.xaf-view-caption-sm|-lg, the view, + .xaf-object-caption, the object)];
   -sm in a detail view (an object caption follows), -lg in a list view. The charter's header:
   - no icon;
   - the view always whole, never truncated; only a long object name ellipsises;
   - detail view: the view in grey (--ds-header-page-text), « / », the object in green
     (--ds-header-page-title) — the module / page of the mock-up's breadcrumb (4086-8698);
   - list view: the view alone, as the page, in green;
   - all of it text/16/semibold, the view of a detail view regular.
   XAF sizes these captions per size mode with (0,3,0) rules (.dxbl-theme-fluent.xaf-size-s
   .xaf-view-caption-lg); ours weigh as much from .main-pagebar and load after: no !important. */
.main-pagebar-title {
    display: flex;
    align-items: center;
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    margin: 0;
}

/* XAF's header toolbar (notifications bell and count, quick access, diagnostic), moved here from the
   hidden dark header on 29/10/2026: the bell could not be reached at all. Right of the title, at its
   content's width. */
.main-pagebar-tools {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
}

.main-pagebar .main-pagebar-title .view-caption-root,
.main-pagebar .main-pagebar-title .xaf-caption-container {
    display: flex;
    flex: 1 1 auto;
    align-items: baseline;
    min-width: 0;
    max-width: 100%;
    overflow: hidden;
}

.main-pagebar .main-pagebar-title .xaf-caption-icon-container {
    display: none;
}

.main-pagebar .main-pagebar-title .xaf-view-caption-sm,
.main-pagebar .main-pagebar-title .xaf-view-caption-lg,
.main-pagebar .main-pagebar-title .xaf-object-caption {
    font-size: var(--ds-font-size-16);
    line-height: 1.25;
    font-weight: var(--ds-font-weight-semibold);
    color: var(--ds-header-page-title);
    padding: 0;
    margin: 0;
}

.main-pagebar .main-pagebar-title .xaf-view-caption-sm,
.main-pagebar .main-pagebar-title .xaf-view-caption-lg {
    flex: 0 0 auto;
    white-space: nowrap;
}

.main-pagebar .main-pagebar-title .xaf-view-caption-sm {
    font-weight: var(--ds-font-weight-regular);
    color: var(--ds-header-page-text);
}

.main-pagebar .main-pagebar-title .xaf-object-caption {
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The « / » of the breadcrumb, in a detail view only: a dashboard renders an EMPTY view caption
   before its object caption (26.1.3, seen on the home dashboard as « / Dashboard »), and keeps
   no separator. */
.main-pagebar .main-pagebar-title .xaf-view-caption-sm:not(:empty) + .xaf-object-caption::before {
    content: "\00A0/\00A0";
    font-weight: var(--ds-font-weight-regular);
    color: var(--ds-header-page-text);
}


/* ============================================================
   16. FOCUS RING
   Each DevExpress component draws its own focus (outline or Fluent
   underline) from the focus tokens of charte/ds-pim.css, at the charter's
   accent; anything else keeps the browser's focus ring. The global
   :focus-visible outline in !important that stood here, over every
   component's own, is gone (design/CHARTE.md § 2.4, 26/10/2026).
   A window's box (role="dialog") draws none: DevExpress focuses it on
   Escape, just before closing it, and the browser's ring flashed round
   the whole window while it disappeared (PIM-471 acceptance, 22/10/2026).
   Focus then returns to what held it before, which keeps its own ring.
   ============================================================ */
[role="dialog"]:focus-visible {
    outline: none;
}

/* Le grid DevExpress place le focus PAR PROGRAMME sur sa zone scrollable /
   ligne / cellule au chargement de la vue liste. Le navigateur fait alors
   matcher :focus-visible -> contour parasite (arrondi, débordant sous le
   grid) en bas à gauche. On neutralise le focus ring sur ces éléments
   structurels ; il reste actif sur les contrôles interactifs (boutons, champs,
   checkbox) pour la navigation clavier.

   ⚠️ La CELLULE de données est volontairement EXCLUE de cette neutralisation
   (retrait de `.dxbl-grid-data-row > td:focus-visible`, ajout de `:not(td)` sur
   la clause [tabindex]). Diagnostic du 09/09/2026, mesuré dans le navigateur sur
   les lignes de commande vente : en édition Batch (= GridEditMode.EditCell),
   Entrée valide la cellule et descend le focus d'une ligne — le focus DOM se
   déplace réellement, document.activeElement passant au <td> du dessous — mais
   aucun éditeur ne s'ouvre à l'arrivée (EditNextCellOnEnter est injoignable en
   26.1, voir GridKeyboardEntryController). Sans cadre sur la cellule focalisée,
   la saisie au clavier était donc AVEUGLE : rien n'indiquait où l'on se trouvait,
   et Entrée semblait ne rien faire. La règle ci-dessus, écrite pour un contour
   parasite au CHARGEMENT, emportait aussi ce cadre-là.

   Le cadre restitué est celui du thème (core.min.css : `.dxbl-grid
   .dxbl-grid-table>tbody>tr>td:focus-visible`, contour de .15rem à
   --dxbl-grid-focus-frame-color, l'accent de la charte par charte/ds-pim.css).
   Ne réélargis pas ce sélecteur aux `td` sans remettre un indicateur de
   cellule focalisée à la place. */
.dxbl-grid:focus-visible,
.dxbl-grid-table:focus-visible,
.dxbl-grid [tabindex]:focus-visible:not(.dxbl-btn):not(input):not(.dxbl-checkbox):not(td) {
    outline: none !important;
}


/* ============================================================
   17. MODAL / POPUP
   XAF Blazor uses DevExpress popup components that render as
   .dxbl-modal-* elements.  Layout: backdrop → content →
   header / body / footer.
   ============================================================ */

/* !important in this section, measured 26/10/2026 on three windows (Transformer's parameters, a
   detail view, a yes / no confirmation), each one stripped in turn: those left are the ones the
   theme's own rules — more specific, or !important themselves — would otherwise win. The others
   were removed, and the declarations that changed nothing at all with them. */

/* Backdrop: XAF renders it with the Bootstrap class, which no Fluent rule paints from the theme's backdrop token;
   that token is the charter's Teal at 40 % (charte/ds-pim.css, the bridge). */
.modal-backdrop {
    background-color: var(--dxds-color-surface-backdrop-default-rest);
    backdrop-filter: blur(2px);
}

/* Sizing — NOT here: a pop-up window takes one of the three sizes of the charter's Modal
   (s / m / l) through the native way, Controllers/Shared/PopupWindowSizeController (26/10/2026,
   design/CHARTE.md § 5.10); until 26/10/2026, this section stretched every pop-up over the whole
   content area. Centering: the theme's. */
.dxbl-modal,
.dxbl-window-root {
    align-items: center;
    justify-content: center;
}

/* The pop-up's look: the charter's Modal surface, overflow hidden, so that header and footer stay
   put while the body (.dxbl-modal-body) scrolls (the theme lays it out as a flex column). Its size
   and place: see above. min-height: 0 lets it shrink to the window's height cap (below), the body
   scrolling instead. !important: the theme paints the content from more specific rules. */
.dxbl-modal-content {
    background-color: var(--ds-modal-bg) !important;
    border: 1px solid var(--ds-modal-border) !important;
    border-radius: var(--ds-card-radius) !important;
    overflow: hidden !important;
    /* fills a window of fixed height (a list: its grid needs the bounded height), shrinks to a capped one */
    flex: 1 1 auto;
    min-height: 0;
}

/* The direct PARENT of .dxbl-modal-content (its class depends on the kind of XAF pop-up) carries the
   window's size (PopupWindowSizeController: width, height, max-height): a flex column, so that the
   content keeps within the height cap of a tall form and its buttons stay on screen. It also carries
   the theme's shadow, which the charter's Modal does not have (!important: the theme sets it from a
   more specific rule). Scoped by :has(> .dxbl-modal-content): drop-downs and tooltips are not touched. */
*:has(> .dxbl-modal-content) {
    display: flex;
    flex-direction: column;
    box-shadow: none !important;
}

/* Message dialogs (error, information, yes / no confirmation) show no view, only a text and one or
   two buttons: a fixed narrow width, whatever the length of the message, and a height from the
   content, capped (the body scrolls, the buttons stay visible). Two families:
   - .dxbl-message-box > .dxbl-modal-content: DxMessageBox;
   - .dxbl-modal-content with no XAF view inside: XAF's IExceptionDialogTemplate /
     IConfirmationDialogTemplate carry no class of their own; they are told by the absence of any
     editable element of a view (grid, tabs, form layout, input, textarea, text editor).
   Not the piece confirmations (.confirm-panel): a view, sized Small by PopupWindowSizeController, whose
   content fills its window. Specificity (0,2,0) > the .dxbl-modal-content rule (0,1,0). */
.dxbl-message-box > .dxbl-modal-content,
.dxbl-modal-content:not(:has(.dxbl-grid, .dxbl-form-layout, .dxbl-tabs, input, textarea, .dxbl-text-edit, .confirm-panel)) {
    width: min(480px, 90vw) !important;
    height: auto;
    max-height: 70vh !important;
    /* Centred in its window, which may be wider (the content's width is set here, the window's elsewhere). */
    align-self: center;
}

/* La fenêtre de CONFLIT D'ENREGISTREMENT (PIM-330) n'a pas de champ de saisie : la règle ci-dessus la
   réduisait à 480 px, son texte (qui a modifié quoi, les deux valeurs) devenait illisible et ses
   boutons se repliaient en icônes. Elle est reconnue à la classe que portent ses actions
   (CustomCSSClassName="pim-conflict-dialog" sur IgnoreCollisionAction, ReloadCollisionAction,
   MergeAction et RefreshAction dans Model.xafml), comme le popup des notifications plus bas. Les
   attributs data-action-id / data-action-name visés jusqu'au 13/10/2026 ne sont pas posés par XAF
   dans ce dialogue : la règle ne s'appliquait jamais (vu par l'E2E ConcurrencyConflictTests).
   Classe doublée : spécificité (0,3,0) > (0,2,0) de la règle des messages, sans dépendre de l'ordre. */
.dxbl-modal-content.dxbl-modal-content:has(.pim-conflict-dialog) {
    width: min(760px, 92vw) !important;
    max-height: 80vh !important;
    /* Centred even when wider than its window (the theme's default 500 px), flex lets it overflow both sides. */
    align-self: center;
}

/* EXCEPTION aux tailles de la charte : le popup des NOTIFICATIONS. Il contient une grille, donc il
   prendrait la grande taille (liste) pour trois lignes — une immense boîte vide. On le veut petit,
   posé en haut à droite de l'écran (d'où sa position fixe, la seule du § 17).
   Il est reconnu à la classe portée par son action « Ouvrir l'entité » (NavigateToLinkedEntity,
   CustomCSSClassName dans Model.xafml) : aucun autre popup ne la contient, donc aucun autre
   popup n'est touché par cette règle.
   Hauteur FIXE, surtout pas auto : la vue liste tourne en défilement virtuel, et une grille dont
   la hauteur n'est pas bornée lève UnableToRequestItemsException. 420px = ~8 notifications
   visibles, au-delà la grille défile.
   Spécificité (0,2,0), comme l'exception message-box ci-dessus -> l'emporte sur .dxbl-modal-content. */
.dxbl-modal-content:has(.pim-notifications-popup) {
    position: fixed !important;
    top: 16px !important;
    bottom: auto !important;
    left: auto !important;
    right: 16px !important;
    transform: none !important;

    width: min(420px, 92vw) !important;
    height: min(320px, 60vh) !important;
    max-height: 60vh !important;
}

/* NB : rendre ce popup NON BLOQUANT n'est pas faisable ici. Testé : neutraliser l'overlay
   (pointer-events + voile) laisse passer les clics visuellement, mais XAF garde la Frame du popup
   active et aucune action n'aboutit derrière. La fenêtre de notifications XAF est modale par
   conception. Pour du non-bloquant il faut sortir du popup XAF (cloche + volet latéral en
   composant Blazor autonome) — ne pas retenter la piste CSS. */

/* Header. !important: the theme's header padding and border come from more specific rules. */
.dxbl-modal-header,
dxbl-popup-header.dxbl-modal-header {
    background-color: var(--ds-modal-bg) !important;
    border-bottom: 1px solid var(--ds-modal-border) !important;
    padding: 15px 20px !important;
    min-height: 56px;
}

.dxbl-modal-header-default {
    gap: 8px;
    min-height: 20px;
}

/* Title: the view's caption (a list or a non-persistent view, .xaf-view-caption-lg) or the object's.
   !important on size and line height: XAF sizes a message's caption from a more specific rule. */
.dxbl-modal-header .xaf-view-caption-lg,
.dxbl-modal-header .xaf-object-caption,
dxbl-popup-header .xaf-object-caption {
    /* text/14/semibold: the title of the charter's Modal (6288-23319, WMS window 4090-57511) */
    font-size: var(--ds-font-size-14) !important;
    font-weight: var(--ds-font-weight-semibold);
    color: var(--ds-modal-content);
    line-height: 1.4 !important;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Close button: the charter's Teal cross, in a 28 px square (the theme's layout and transparency). */
button.dxbl-popup-header-button {
    border: none;
    color: var(--ds-modal-content);
    min-width: 28px;
    min-height: 28px;
    flex-shrink: 0;
}

/* Body. !important on the padding: the theme's comes from a more specific rule. */
.dxbl-modal-body {
    padding: 15px 20px !important;
    background-color: var(--ds-modal-body-bg);
    overflow: auto;
    flex: 1 1 auto;
}

    /* A message's text at 14 px, keeping its line breaks. !important: XAF's message text (size,
       white-space) comes from more specific rules. Not the labels: a field's caption keeps the size of
       the user's density, as on a page (CHARTE.md § 5.1, 12 px in Compact) - a « label » selector here
       had fixed them at 14 px, found by the catalogue references on 2026-10-28. */
    .dxbl-modal-body .xaf-static-text {
        font-size: 14px !important;
        font-weight: 400;
        color: var(--ds-modal-content);
        line-height: 1.5 !important;
        white-space: pre-line !important;
    }

/* Footer. !important: the theme's footer padding, border and surface come from more specific rules. */
.dxbl-modal-footer {
    padding: 16px 24px !important;
    min-height: 68px;
    border-top: 1px solid var(--ds-modal-border) !important;
    background-color: var(--ds-modal-bg) !important;
}

    /* Toolbar root inside footer: the full width, for the buttons' alignment (!important: the
       theme's toolbar sets its own min-width). */
    .dxbl-modal-footer .dxbl-toolbar,
    .dxbl-modal-footer dxbl-toolbar {
        min-width: 100% !important;
        min-height: 36px;
    }



/* ============================================================
   18. DETAIL VIEW — Object forms (XAF FormLayout)
   Scoped to .main.main-object-model and .detail-view-content
   to avoid touching grid cell editors and modal editors.

   Verified DOM structure (provided HTML):
     .dxbl-group
       .dxbl-group-header > .dxbl-text
       .dxbl-group-body.dxbl-fl-group-body
         .dxbl-group-body-content
           .dxbl-fl-item
             .dxbl-fl-cpt          ← field label
             .dxbl-fl-ctrl
               .dxbl-text-edit     ← editor root
                 .dxbl-text-edit-input   ← actual <input>
                 .dxbl-btn-group-right   ← attached buttons (clear/dropdown)

   The editors themselves are the theme's (one exception below:
   a disabled field stays readable). What this section paints
   is the form's frame — cards, their header, the labels.
   ============================================================ */

/* Card — the charter's "card" (design/CHARTE.md § 5.7, capture ds-pim/6288-23390): pale green
   header, thin green border, white body, no shadow; its text and the field labels Teal; header and
   body padded by the card's content-wrapper tokens, compact or standard with the user's density
   (XAF's .xaf-size-s / -m on the body); the body stays the theme's transparent, the page shows
   through. The theme draws a group borderless and transparent from
   tokens it shares with every transparent surface and border (surface-transparent-rest,
   border-transparent-rest): redeclaring them would paint the body and the header's buttons too,
   hence properties on our scope (way 3) — (0,2,0) to (0,3,0), over the theme's .dxbl-group rules
   loaded before. The text token is redeclared (way 1) on the form itself: the form layout resolves
   its caption and group header colours on .dxbl-fl (core.min.css), and the editors and nested
   grids inside redeclare their own. */
.main.main-object-model .dxbl-fl,
.detail-view-content .dxbl-fl {
    --dxds-color-content-neutral-default-rest: var(--ds-card-content);
}

.main.main-object-model .dxbl-group,
.detail-view-content .dxbl-group {
    border: 1px solid var(--ds-card-border);
    border-radius: var(--ds-card-radius);
    overflow: hidden;
}

    .main.main-object-model .dxbl-group > .dxbl-group-header,
    .detail-view-content .dxbl-group > .dxbl-group-header {
        background-color: var(--ds-card-bg);
        border-bottom: 1px solid var(--ds-card-border);
        border-radius: 0;
        padding: var(--ds-card-content-wrapper-padding-y-compact) var(--ds-card-content-wrapper-padding-x-compact);
    }

/* The body: the theme pads it from the form root with a (0,6,0) rule; the same path behind our
   scope weighs (0,7,0). */
:is(.main.main-object-model, .detail-view-content) .dxbl-fl .dxbl-fl-group > .dxbl-group > .dxbl-group-body > .dxbl-group-body-content {
    padding: var(--ds-card-content-wrapper-padding-y-compact) var(--ds-card-content-wrapper-padding-x-compact);
}

:is(.xaf-size-m, .xaf-size-l) :is(.main.main-object-model, .detail-view-content) .dxbl-group > .dxbl-group-header {
    padding: var(--ds-card-content-wrapper-padding-y-standard) var(--ds-card-content-wrapper-padding-x-standard);
}

:is(.xaf-size-m, .xaf-size-l) :is(.main.main-object-model, .detail-view-content) .dxbl-fl .dxbl-fl-group > .dxbl-group > .dxbl-group-body > .dxbl-group-body-content {
    padding: var(--ds-card-content-wrapper-padding-y-standard) var(--ds-card-content-wrapper-padding-x-standard);
}

/* Bootstrap row gutters inside groups */
.main.main-object-model .dxbl-group-body .dxbl-row,
.detail-view-content .dxbl-group-body .dxbl-row {
    --bs-gutter-x: 16px;
    --bs-gutter-y: 10px;
}

/* Field item wrapper */
.main.main-object-model .dxbl-fl-item,
.detail-view-content .dxbl-fl-item {
    margin-bottom: 8px !important;
}

/* Field labels: the card's Teal text (token redeclared on the group, above) at the size of the
   user's density; only their gap to a field below them is ours. */
.main.main-object-model .dxbl-fl-cpt,
.detail-view-content .dxbl-fl-cpt {
    margin-bottom: 6px !important;
}

/* En layout horizontal le label est à côté de l'input — annuler le margin-bottom et centrer verticalement */
.main.main-object-model .dxbl-fl-item.dxbl-fl-item-horizontal > .dxbl-fl-cpt,
.detail-view-content .dxbl-fl-item.dxbl-fl-item-horizontal > .dxbl-fl-cpt {
    margin-bottom: 0 !important;
    align-self: center !important;
}

/* Control wrapper — full width */
.main.main-object-model .dxbl-fl-ctrl,
.detail-view-content .dxbl-fl-ctrl {
    width: 100% !important;
}

/* Text editors — the charter's field (design/CHARTE.md § 5.2): light grey surface, grey border, teal
   text, the theme's underline kept and tinted; states differentiated by the theme, coloured by the
   charter. Every text editor resolves its --dxbl-text-edit-* on .dxbl-text-edit itself (core.min.css
   26.1.3), so the public tokens are redeclared there (way 1), and on the state for the hover surface,
   which the theme does not change (way 2). Left out: grid cell editors — a cell locked by an appearance
   rule (PIM-456) must keep looking locked, the grid's states are lot 3 — and the sidebar's search
   field, which has its own (sidebar.css).
   A locked field stays readable: the read mode of the pieces (PIM-470) disables every field of the
   form, and the theme's disabled text (a light grey) made a consulted order hard to read; it takes the
   charter's read-only text on the charter's disabled surface — readable text on a muted surface, so
   that it does not look editable either. */
.dxbl-text-edit:not(.dxbl-grid *):not(.sidebar *) {
    --dxds-color-surface-neutral-default-rest: var(--ds-input-enable-filled-bg);
    --dxds-color-border-neutral-default-rest: var(--ds-input-enable-filled-border);
    --dxds-color-content-neutral-default-rest: var(--ds-input-enable-filled-content);
    --dxds-color-border-neutral-default-hovered: var(--ds-input-hover-filled-border);
    --dxds-color-border-neutral-default-active: var(--ds-input-focus-filled-border);
    /* the underline, and the field's chevron and clear button */
    --dxds-color-content-neutral-subdued-rest: var(--ds-input-underline);
    --dxds-color-content-neutral-subdued-hovered: var(--ds-input-underline-hover);
    /* the focused underline: the charter's accent */
    --dxds-color-content-primary-default-rest: var(--ds-input-focus-filled-border);
    /* the disabled text, readable (surfaces: on the states below — redeclared here, surface-transparent-rest
       would also paint the clear button, which the theme draws on it) */
    --dxds-color-content-neutral-default-disabled: var(--ds-input-read-only-filled-content);
    --dxds-color-border-neutral-default-disabled: var(--ds-input-read-only-filled-border);
}

    /* A read-only or disabled field paints surface-transparent-rest, not this token: no guard needed. */
    .dxbl-text-edit:not(.dxbl-grid *):not(.sidebar *):not(:focus-within):hover {
        --dxds-color-surface-neutral-default-rest: var(--ds-input-hover-filled-bg);
    }

    /* Focused: the border too takes the accent, as in the charter; the theme only colours the underline. */
    .dxbl-text-edit:not(.dxbl-grid *):not(.sidebar *):focus-within {
        --dxds-color-border-neutral-default-rest: var(--ds-input-focus-filled-border);
    }

    .dxbl-text-edit.dxbl-readonly:not(.dxbl-grid *):not(.sidebar *) {
        --dxds-color-surface-transparent-rest: var(--ds-input-read-only-filled-bg);
        --dxds-color-content-neutral-default-rest: var(--ds-input-read-only-filled-content);
    }

    .dxbl-text-edit.dxbl-disabled:not(.dxbl-grid *):not(.sidebar *) {
        --dxds-color-surface-transparent-rest: var(--ds-input-disabled-filled-bg);
    }

/* An open list (combo box, lookup): the theme resolves its item colours on the list's container,
   rendered outside the field (a popup). Selected item: the charter's pale green with teal text — the
   theme puts it on the full accent. */
.dxbl-list-box-render-container {
    --dxds-color-surface-neutral-default-rest: var(--ds-input-item-enable-bg);
    --dxds-color-content-neutral-default-rest: var(--ds-input-item-enable-content);
    --dxds-color-surface-neutral-default-hovered: var(--ds-input-item-hover-bg);
    --dxds-color-content-neutral-default-hovered: var(--ds-input-item-hover-content);
    --dxds-color-surface-primary-default-rest: var(--ds-input-item-active-bg);
    --dxds-color-content-neutral-default-static-dark-active: var(--ds-input-item-active-content);
    --dxds-color-surface-primary-default-hovered: var(--ds-input-item-active-hover-bg);
    --dxds-color-content-neutral-default-static-dark-rest: var(--ds-input-item-active-content);
}

/* Checkbox vertical alignment, beside a field of the form. Not in a grid nested in the form (lines of
   a piece): the 36 px would set the height of every row. */
.main.main-object-model .dxbl-checkbox:not(.dxbl-grid *),
.detail-view-content .dxbl-checkbox:not(.dxbl-grid *) {
    min-height: 36px !important;
    display: inline-flex !important;
    align-items: center !important;
}


/* Detail area background — transparent so the page bg shows through */
.main.main-object-model .main-content,
.detail-view-content {
    background: transparent !important;
}

/* Hide spin buttons on numeric editors (design decision) */
.dxbl-spin-btns,
.dxbl-text-edit > .dxbl-spin-btns,
.dxbl-text-edit > .dxbl-btn-group-vertical.dxbl-spin-btns,
.dxbl-input-editor > .dxbl-spin-btns,
.dxbl-input-editor > .dxbl-btn-group-vertical.dxbl-spin-btns {
    display: none !important;
}

/* Detail pane of a split list (« Vue splitté »): DxFormLayout picks its columns from the WINDOW width
   (dxbl-col-md-* = 50 % from 768 px), not from the pane's. On a wide screen the ~300 px pane kept two
   groups of ~130 px side by side; the caption took 110 px and the editor was left 20 px — value loaded,
   unreadable (sprint 398 recette, 09/10/2026). Stacking the groups alone is not enough: the caption
   column keeps the width of the longest caption (« Désignation commerciale ») and the editor got 46 px.
   The pane is made a size container; when it is narrow, its form columns stack one per row and each
   caption goes above its editor. A wide pane, or a detail pane under the list, is unchanged. */
.xaf-masterdetail-detailview {
    container-type: inline-size;
}

@container (max-width: 640px) {
    .xaf-masterdetail-detailview .dxbl-fl [class*="dxbl-col-md-"],
    .xaf-masterdetail-detailview .dxbl-fl [class*="dxbl-col-lg-"] {
        flex: 0 0 100% !important;
        width: 100% !important;
        max-width: 100% !important;
    }

    .xaf-masterdetail-detailview .dxbl-fl .dxbl-fl-item-horizontal {
        flex-direction: column !important;
        align-items: stretch !important;
    }

        .xaf-masterdetail-detailview .dxbl-fl .dxbl-fl-item-horizontal > .dxbl-fl-cpt {
            width: auto !important;
            max-width: none !important;
            justify-content: flex-start !important;
            text-align: left !important;
            padding-bottom: 4px;
        }

        .xaf-masterdetail-detailview .dxbl-fl .dxbl-fl-item-horizontal > .dxbl-fl-ctrl {
            width: 100% !important;
        }
}


/* ============================================================
   19. PAGER
   The theme's pager (sizes, states), coloured by the charter's page
   number (design/CHARTE.md § 5.5): the active page already takes the
   accent with white text (charte/ds-pim.css); the rest, way 1 on the family
   root where the theme resolves its --dxbl-pager-*: Teal text, pale
   green hover and press. No pager is rendered today — every list view
   scrolls virtually (VirtualScrollingEnabled, Model.xafml) — so this
   only keeps one that a view would turn on in the charter.
   ============================================================ */
.dxbl-pager {
    --dxds-color-content-neutral-default-rest: var(--ds-grid-line-item-enable-content);
    --dxds-color-content-neutral-default-hovered: var(--ds-grid-line-item-enable-content);
    --dxds-color-surface-neutral-default-hovered: var(--ds-page-number-hover);
}


/* ============================================================
   20. NAV-RIBBON (DxTabs / navigation principale)
   Figma nodes 115-715 (nav-ribbon), 115-277 (ribbon-item),
   115-317 (ribbon-bar).

   HTML vérifié :
     dxbl-tab-item.dxbl-tabs-item          ← onglet
     dxbl-tab-item.dxbl-tabs-item.dxbl-active ← onglet actif
     .dxbl-tabs-text-container > .dxbl-text > .dxbl-tabs-text-overflow

   États Figma :
     active   : SemiBold 600, underline primary-100 (3px pill)
     enable   : Regular 400, pas d'underline
     hover    : bg primary-10, radius 4px, Regular
     pressed  : bg primary-20, radius 4px, Regular
   ============================================================ */

/* Tabs: the theme's (layout, size, the active tab's underline, states), coloured by the charter's
   nav-ribbon (design/CHARTE.md § 5.4) — way 1 on the family root, where the theme resolves its
   --dxbl-tabs-tab-* (core.min.css .dxbl-tabs): text Teal in every state, the active tab's
   underline at the accent. The theme's "hover background" is in fact the underline of a hovered
   tab (a grey line, from a disabled-text token): a green one notch above the strip's pale line
   instead. A disabled tab's text reads the same token: pale green, which reads as disabled.
   The root also wraps the tabs' content panel: the content gets the tokens it had outside the
   tabs back (a grid's empty text, a disabled field, a link), kept beforehand on the tabs' parent —
   on the root itself they would already read the strip's values. */
:has(> .dxbl-tabs) {
    --ds-tabs-outer-subdued-rest: var(--dxds-color-content-neutral-subdued-rest);
    --ds-tabs-outer-subdued-hovered: var(--dxds-color-content-neutral-subdued-hovered);
    --ds-tabs-outer-subdued-active: var(--dxds-color-content-neutral-subdued-active);
    --ds-tabs-outer-default-rest: var(--dxds-color-content-neutral-default-rest);
    --ds-tabs-outer-primary-rest: var(--dxds-color-content-primary-default-rest);
    --ds-tabs-outer-subdued-disabled: var(--dxds-color-content-neutral-subdued-disabled);
}

.dxbl-tabs {
    --dxds-color-content-neutral-subdued-rest: var(--ds-nav-ribbon-enable-content);
    --dxds-color-content-neutral-subdued-hovered: var(--ds-nav-ribbon-hover-content);
    --dxds-color-content-neutral-subdued-active: var(--ds-nav-ribbon-active-content);
    --dxds-color-content-neutral-default-rest: var(--ds-nav-ribbon-active-content);
    --dxds-color-content-primary-default-rest: var(--ds-nav-ribbon-active-border);
    --dxds-color-content-neutral-subdued-disabled: var(--ds-nav-ribbon-hover-border);
}

    .dxbl-tabs-content-panel {
        --dxds-color-content-neutral-subdued-rest: var(--ds-tabs-outer-subdued-rest);
        --dxds-color-content-neutral-subdued-hovered: var(--ds-tabs-outer-subdued-hovered);
        --dxds-color-content-neutral-subdued-active: var(--ds-tabs-outer-subdued-active);
        --dxds-color-content-neutral-default-rest: var(--ds-tabs-outer-default-rest);
        --dxds-color-content-primary-default-rest: var(--ds-tabs-outer-primary-rest);
        --dxds-color-content-neutral-subdued-disabled: var(--ds-tabs-outer-subdued-disabled);
    }

/* The charter's pale line under the whole tab strip, which Fluent does not draw: our own
   decoration, on the line of the theme's active underline — the list keeps the focus outline's
   room below its tabs — and under it (its own stacking context, the line at -1). */
dxbl-scroll-viewer ul[role="tablist"] {
    position: relative;
    isolation: isolate;
}

    dxbl-scroll-viewer ul[role="tablist"]::after {
        content: "";
        position: absolute;
        z-index: -1;
        bottom: calc(var(--dxbl-tabs-tab-focus-outline-size) + var(--dxbl-tabs-tab-focus-outline-offset));
        left: 0;
        right: 0;
        height: var(--dxbl-tabs-tab-border-thickness);
        background: var(--ds-nav-ribbon-enable-border);
        pointer-events: none;
    }

    /* ── Focus : cadre remplacé par un aplat ────────────────────── */
    /* DevExpress encadre l'onglet focalisé d'un liseré rectangulaire :
         .dxbl-tabs-tablist:not([data-dx-focus-hidden]) .dxbl-tabs-item:focus-visible {
             outline: var(--dxbl-tabs-tab-focus-outline-color) solid ... }
       Ce cadre reste affiché après un simple clic à la souris — c'est la boîte observée
       autour de « Généralités » — et il jure avec le repère de l'onglet actif, qui est la
       barre verte sous le libellé. Deux indicateurs concurrents pour le même onglet.

       L'outline est retiré, PAS le repère de focus : il est remplacé par le même aplat que
       le survol. Supprimer l'outline sans rien mettre à la place rendrait la navigation au
       clavier aveugle dans le ruban — l'inverse de ce qu'on cherche sur les grilles.
       Le fond suffit à situer le focus, et il est cohérent avec le reste du ruban. */
    dxbl-tab-item.dxbl-tabs-item:focus-visible,
    dxbl-tab-item.dxbl-tabs-item:focus {
        outline: none !important;
        box-shadow: none !important;
    }

    dxbl-tab-item.dxbl-tabs-item:not(.dxbl-active):focus-visible {
        background: var(--ds-nav-ribbon-hover-bg) !important;
    }


/* ============================================================
   21. ICON OVERRIDES
   Stratégie : data-qa-selector identifie le composant DevExpress
   de façon stable. On masque .dxbl-image (icône native) et on
   injecte le SVG via ::before + CSS mask (couleur contrôlable
   via background-color, compatible tous navigateurs modernes).
   ============================================================ */

/* ── Popup — bouton de fermeture ─────────────────────────────── */
button[data-qa-selector="dx-popup-close-button"] .dxbl-image {
    display: none !important;
}

button[data-qa-selector="dx-popup-close-button"] {
    width: 28px !important;
    height: 28px !important;
    padding: 0 !important;
    background: transparent !important;
}

    button[data-qa-selector="dx-popup-close-button"]::before {
        content: "";
        display: block;
        width: 14px;
        height: 14px;
        margin: auto;
        background-color: var(--ds-modal-content);
        -webkit-mask: url("/images/Close.svg") center / contain no-repeat;
        mask: url("/images/Close.svg") center / contain no-repeat;
    }

/* TreeList expand / collapse: the component's own chevron, in the grid's text colour (section 13). The
   images of the charter's family filter (family_filter_button*.svg), once swapped in by the button's
   aria-label at 30 px, belong to that custom component (design/CHARTE.md § 5.12), not to a tree list. */

/* ── Popup « Éditeur de critère » (PopupCriteriaPropertyEditor) : hauteur ────────
   Le FilterEditor DevExpress se dispose en positionnement ABSOLU + height:100%
   (.dx-filtereditor.dxrd-height-100 ; case « Advanced Mode » en bottom:0, contenu en
   top:0). Dans le popup, aucun ancêtre ne lui donne de hauteur -> le height:100%
   s'effondre sur 0 et tout se tasse en une fine ligne. On donne une hauteur au wrapper
   popup et on fait cascader 100% jusqu'à l'éditeur ; DevExpress gère ensuite lui-même
   l'agencement (arbre visuel « ajouter une condition » / zone texte ACE avec suggestions
   selon la case), sans chevauchement. */
/* ⚠️ CETTE RÈGLE VISAIT .popup-filter-wrapper, UNE CLASSE QUI N'EXISTE PLUS. Aucun composant,
   aucun .razor, aucun .xafml ne la pose — vérifié le 2026-08-03 sur tout le dépôt. Elle ne
   s'appliquait donc à rien et le défaut était réapparu : case « Advanced Mode » superposée à la
   zone de saisie, champs tassés les uns sur les autres dans le popup « View ».
   Elle est rebranchée sur .dxbl-popup, la classe réelle du popup DevExpress.

   min-height plutôt que height : c'est le minimum qui résout le problème. Le FilterEditor
   dispose ses enfants en absolu contre un height:100% ; il suffit que LUI ait une étendue pour
   que tout se replace, sans lui imposer une hauteur fixe qui brimerait un critère long.
   Portée limitée au popup : les éditeurs de critères inline gardent leur dimensionnement. */
.dxbl-popup .dx-filtereditor,
.dxbl-popup .filter-editor,
.dxbl-popup .dx-filtereditor-plain {
    min-height: 380px !important;
}

/* ============================================================
   MENU CONTEXTUEL — L'ITEM COURANT D'UNE ACTION « À MODES »
   ============================================================

   Une SingleChoiceAction en ItemType = ItemIsMode designe un ETAT et non une operation : l'un de
   ses items est le mode COURANT. XAF pose bien la classe `active` sur cet item (mesure du
   2026-09-10), mais DevExpress 26.1 ne la style pas dans un menu contextuel : meme fond, meme
   couleur, meme graisse que les autres. Le marquage existait donc sans se voir.

   Premier usage : « Figer jusqu'a… », ou l'item courant est la frontiere de gel. Sans ce reperage,
   on ne peut pas deviner que choisir une colonne situee AVANT la frontiere libere celles qui
   suivent — et le degel progressif reste inaccessible alors que la mecanique est la.

   POURQUOI LA REGLE EST GENERALE et non limitee a notre action : marquer le mode courant est le
   comportement correct de TOUTE action a modes, ou qu'elle soit. Verifie au passage que `active` ne
   sert qu'a ca dans ce menu — le survol ne l'ajoute pas, il a ses propres classes.

   Fond volontairement inchange : le survol s'en sert, et le lui disputer ferait clignoter l'item
   sous le pointeur. Le repere passe donc par la graisse et un file a gauche, qui ne se battent
   avec rien. */
.dxbl-context-menu-item.active {
    font-weight: 600;
    box-shadow: inset 3px 0 0 0 var(--dxds-color-content-primary-default-rest);
}

/* ============================================================
   HELP NOTE — one line of help inside a form (a StaticText item)
   Set through the layout item's CustomCSSClassName="app-help-note" in the
   model: a pale primary band, an "i" badge, small text. Read as help, not
   as data: never put a value or an input in it.
   ============================================================ */
.app-help-note {
    display: flex !important;
    align-items: center;
    gap: 8px;
    margin-bottom: 8px;
    padding: 6px 10px 6px 8px;
    background: var(--ds-help-bg);
    border-radius: var(--ds-radius-md);
    color: var(--ds-help-content);
    font-size: 12px;
    line-height: 16px;
}

.app-help-note::before {
    content: "i";
    flex: none;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--ds-help-icon-bg);
    color: var(--ds-help-icon-content);
    font: 700 11px/16px var(--ds-font-family-text);
    text-align: center;
}

.app-help-note label {
    margin: 0;
    font-size: inherit;
    line-height: inherit;
    color: inherit;
}

/* ============================================================
   END OF APPLICATION LIGHT THEME
   ============================================================
*/

/* ──────────────────────────────────────────────────────────────────────────────
   Les boîtes de CONFIRMATION d'une pièce (marqueur .confirm-panel, posé par
   ConfirmationMessagePropertyEditor) : leur taille est la petite du Modal de la charte,
   posée par PopupWindowSizeController (26/10/2026) — plus par du CSS. Leur corps ne
   défile pas : la phrase tient.
   ────────────────────────────────────────────────────────────────────────────── */
.dxbl-modal-content:has(.confirm-panel) .dxbl-modal-body {
    overflow: visible !important;
}

/* ============================================================
   22. RELATED OBJECT ZOOM — PIM-471
   La flèche qui ouvre la fiche d'un élément lié (RelatedObjectZoomController,
   RelatedObjectZoomLink.razor) et la loupe qui cherche de quoi remplir un champ
   (RelatedObjectSearchController). En fiche, ce sont des boutons d'éditeur DevExpress :
   permanent, il hérite du style des boutons d'éditeur (--ds-input-underline,
   plus foncé au survol). En grille, la cellule porte un lien à nous, visible
   seulement au survol de la ligne ou au focus clavier (décision du 21/10/2026 :
   au survol en liste, pour garder les listes denses), et peint aux mêmes
   couleurs : hériter de la cellule le rendait invisible au survol dans les
   listes, dont le texte est déjà teal (recette du 22/10/2026). Opacité et non
   display : le lien reste atteignable au clavier et par un test. Aucune classe
   interne DevExpress : une ligne de table est un <tr> quel que soit le composant.
   ============================================================ */
.related-zoom__cell {
    display: inline-flex;
    align-items: center;
    gap: var(--dxds-spacing-30);
    max-width: 100%;
}

.related-zoom__text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.related-zoom__btn {
    display: inline-flex;
    align-items: center;
    flex: none;
    color: var(--ds-input-underline);
    text-decoration: none;
    line-height: 1;
    opacity: 0;
}

tr:hover .related-zoom__btn,
tr:focus-within .related-zoom__btn,
.related-zoom__btn:focus-visible {
    opacity: 1;
}

.related-zoom__btn:hover {
    color: var(--ds-input-underline-hover);
}

/* Centrée dans son champ ou sa cellule. Le bouton d'éditeur est dans le groupe de
   droite, haut comme le champ : en cellule, le thème le ramène à 12 px et il restait
   collé en haut, à moitié coupé (recette du 22/10/2026). Le glyphe, lui, est une
   lettre de police posée en haut de sa boîte : centré, il quitte ses 1,7 px de trop
   en hauteur. */
.related-zoom__editor-btn {
    align-self: center;
}

.related-zoom__editor-btn .dx-icon,
.related-zoom__btn .dx-icon {
    display: flex;
    align-items: center;
    justify-content: center;
}

/* En cellule, la croix d'effacement est un SVG dessiné à 8 px dans sa boîte de 12 ; la
   loupe, lettre de police de 14 px, débordait sa boîte, paraissait plus grande et touchait
   la croix (recette du 22/10/2026). Elle prend la taille de texte du bouton (dessin de
   ~10 px), et son groupe un écart de chaque côté : elle ne touche plus ni la croix ni le
   bord de la cellule. Le groupe est désigné par la loupe qu'il contient, sans classe
   interne DevExpress. */
td .related-zoom__editor-btn .dx-icon {
    font-size: inherit;
}
td :has(> .related-zoom__editor-btn) {
    margin-inline: var(--dxds-spacing-30);
}

/* Lookup en lecture (fiche ouverte en lecture, PIM-470) : XAF le rend DÉSACTIVÉ, ce qui
   désactive aussi sa loupe ; RelatedObjectZoomController le passe en lecture seule, l'état
   natif qui refuse la saisie et garde ses boutons. Il ne doit pas se distinguer des champs
   désactivés voisins : le texte en lecture seule et le texte désactivé lisent déjà le même
   jeton (section « Text editors »), seul le fond diffère — la surface de lecture seule prend
   ici celle des champs désactivés, redéclarée sur notre classe. */
.related-zoom__readonly {
    --ds-input-read-only-filled-bg: var(--ds-input-disabled-filled-bg);
}


/* ============================================================
   23. LIST FILTER PANEL — what its .razor.css cannot reach (PIM-350)
   The panel's own rules are in Editors/ListFilterPanel/ListFilterPanel.razor.css.
   These target elements outside the component, which carry none of its scope
   attributes: two drop-down frames and the range calendar (popups), and the host
   grid the panel is placed in (ListFilterPanelPlacementController). Moved here
   unchanged from the <style> of ListFilterPanel.razor (27/10/2026).
   ============================================================ */
/* The range calendar, rendered in a popup outside the panel (hence its own class). No week numbers and
   no days of the neighbouring months: both only confused (sprint 9 feedback) — "40" read as a day, and
   1 to 11 October shown under September. Hidden, not removed, so the days keep their columns. The
   picker offers no parameter for either (DxDateRangePicker members, 26.1). The days of the picked
   period are readable natively since the charter's pale "deep" accent (charte/ds-pim.css). */
.lfp-period-dropdown tr > td:first-child:not(.dxbl-calendar-cell) {
    display: none;
}

.lfp-period-dropdown .dxbl-calendar-not-current-view {
    visibility: hidden;
    pointer-events: none;
}

/* The "Customize" and "My filters" windows: the native drop-down gives the frame and the positioning,
   the panel draws the whole content edge to edge — no body padding (no public token for it). */
.lfp-fields-dropdown {
    --dxbl-dropdown-body-padding-x: 0;
    --dxbl-dropdown-body-padding-y: 0;
}

/* Rendered in the grid's toolbar area: full width, and the toolbar must let the calendar popup overflow
   onto the rows below. */
.dxbl-grid-toolbar-container:has(.lfp) {
    display: block;
    overflow: visible;
    padding: 0 0 8px 0;
}

/* XAF caps a nested virtual-scrolling grid at 300 px (its styles.css). The panel lives in that grid's
   toolbar: capped as a whole, the grid gave the panel's height away from the rows — expanded, one order
   was left visible. The cap goes to the rows alone: they keep their 300 px, the grid grows by the panel,
   the page scrolls a little further. The rows area stays bounded, as virtual scrolling requires.
   !important: XAF writes that height inline. */
.grid-content.grid-virtual-scrolling-mode:has(.lfp) {
    height: auto !important;
}

.grid-content.grid-virtual-scrolling-mode:has(.lfp) > * > dxbl-grid,
.grid-content.grid-virtual-scrolling-mode:has(.lfp) dxbl-grid:has(.lfp) {
    max-height: none;
    height: auto;
}

dxbl-grid:has(> .dxbl-grid-toolbar-container .lfp) > .dxbl-scroll-viewer {
    flex: 0 1 auto;
    max-height: 300px;
}

/* A list pane resized with the splitter (dashboard-splitter.js marks it data-splitter-sized="vertical" and
   writes its height as the custom property --dashboard-pane-height, read by css/domains/shared.css) is bounded:
   the grid then fills it instead of stopping at 300 px over a blank. Only then — an unbounded height would
   break virtual scrolling. */
dxbl-form-layout-item[data-splitter-sized="vertical"]:has(.lfp) > .dxbl-fl-ctrl,
dxbl-form-layout-item[data-splitter-sized="vertical"]:has(.lfp) .nested-frame,
dxbl-form-layout-item[data-splitter-sized="vertical"]:has(.lfp) .nested-content,
dxbl-form-layout-item[data-splitter-sized="vertical"]:has(.lfp) .grid-content > * {
    display: flex;
    flex-direction: column;
}

dxbl-form-layout-item[data-splitter-sized="vertical"]:has(.lfp) > .dxbl-fl-ctrl {
    height: 100%;
}

dxbl-form-layout-item[data-splitter-sized="vertical"]:has(.lfp) .nested-frame,
dxbl-form-layout-item[data-splitter-sized="vertical"]:has(.lfp) .nested-content,
dxbl-form-layout-item[data-splitter-sized="vertical"]:has(.lfp) .grid-content > * {
    flex: 1 1 0;
    min-height: 0;
}

dxbl-form-layout-item[data-splitter-sized="vertical"]:has(.lfp) .grid-content.grid-virtual-scrolling-mode {
    flex: 1 1 0;
    min-height: 0;
    height: auto !important;
    display: flex;
    flex-direction: column;
}

dxbl-form-layout-item[data-splitter-sized="vertical"]:has(.lfp) dxbl-grid:has(.lfp) {
    flex: 1 1 0;
    min-height: 0;
    height: auto;
    max-height: none;
}

dxbl-form-layout-item[data-splitter-sized="vertical"]:has(.lfp) dxbl-grid:has(> .dxbl-grid-toolbar-container .lfp) > .dxbl-scroll-viewer {
    flex: 1 1 0;
    min-height: 0;
    max-height: none;
}
