body {
    margin: 0;
    overflow: hidden;
}

.mud-main-content {
    padding-top: 0 !important;
    height: 100vh;
    display: flex;
    flex-direction: column;
}

.app-root {
    display: grid;
    grid-template: "main" 1fr / 1fr;
    height: 100vh;
    width: 100vw;
}

.app-loading-root {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100vh;
    width: 100vw;
    background: var(--mud-palette-background);
}

.app-root > * {
    grid-area: main;
}

/* Bandeau d'impersonation : fixe en haut, au-dessus du drawer (lui aussi fixed, d'où le décalage
   explicite des deux zones plein écran plutôt qu'un simple élément dans le flux). */
.impersonation-banner {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 44px;
    z-index: 1400;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 0 16px;
    background: var(--mud-palette-warning);
    color: var(--mud-palette-warning-text);
}

.impersonation-expired {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100vh;
    width: 100vw;
    background: var(--mud-palette-background);
}

.impersonation-banner-text {
    flex-grow: 1;
    font-size: 0.875rem;
    font-weight: 500;
}

.app-root.impersonating .mud-drawer {
    top: 44px !important;
    height: calc(100vh - 44px) !important;
}

.app-root.impersonating .mud-main-content {
    margin-top: 44px;
    height: calc(100vh - 44px);
}

.global-progress-wrapper {
    pointer-events: none;
    z-index: 1300;
    align-self: start;
}

.global-progress-wrapper.hidden {
    opacity: 0;
}

ul {
    list-style: none;
}

.mud-tabs-relations {
    background-color: var(--mud-palette-background-gray);
}

.mud-tabs-relations .mud-tab {
    cursor: pointer;
}

.mud-tabs-relations .mud-tabs-panels {
    padding: 18px;
}

.custom-mud-data-grid {
    border: 1px solid var(--mud-palette-background-gray);
    border-radius: 4px;
}

/* Scroller = .mud-table-container : il ne contient QUE la table, pas le footer PagerContent
   (bouton Nouveau + pagination), qui est son frère dans la racine → le footer reste HORS de
   la zone de scroll. En mode resize MudBlazor impose au conteneur, en inline, width: max-content
   + overflow: clip pour SA mécanique ; on les override en !important pour en faire un vrai
   scroller borné des deux axes. La largeur du menu est celle du layout (le menu se redimensionne) ;
   18 = padding content-container ; 169 = header. */
.custom-mud-data-grid-object .mud-table-container {
    width: calc(100vw - var(--mud-drawer-width-left) - 18px) !important;
    height: calc(100vh - 169px);
    overflow: auto !important;
}

/* La table se dimensionne à son contenu → elle déborde le conteneur borné → ascenseur
   horizontal. min-width: 100 % la fait remplir quand il y a peu de colonnes (pas de trou). */
.custom-mud-data-grid-object .mud-table-root {
    width: max-content;
    min-width: 100%;
}

.content-container .mud-grid,
.content-container .mud-grid-item {
    min-width: 0;
}

.custom-mud-data-grid-object .mud-table-body .mud-table-cell {
    max-width: 500px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.custom-mud-data-grid-object .mud-table-child-content {
    max-width: 1px;
    padding: 0;
}

/* La cellule de la ligne dépliée ne doit pas rogner : un overflow hidden (celui des cellules de données,
   ci-dessus) en ferait le conteneur de défilement de l'explorer collant, qui ne suivrait plus le scroll
   horizontal de la grille. L'explorer rogne lui-même son contenu. */
.custom-mud-data-grid-object .mud-table-body .mud-table-cell.mud-table-child-content {
    overflow: visible;
}

/* Redimensionnement de colonnes : MudBlazor étire la poignée (.mud-resizer) du header sur toute la
   hauteur de la grille (height inline dynamique) → la ligne bleue traverse toutes les lignes. On rogne
   la cellule d'en-tête (sticky = bloc conteneur de la poignée absolue) : poignée + bordure visibles
   uniquement dans le header. Le resize reste actionnable via le header.
   min-width: max-content → plancher de colonne = largeur du header sur une seule ligne (dérivé du
   contenu, rien en dur). min-width bat le width:Npx inline du resize : impossible d'écraser le
   header sur 2 lignes. Les cellules de données restent libres de s'ellipser. */
.custom-mud-data-grid-object .mud-table-head .mud-table-cell {
    overflow: hidden;
    min-width: max-content;
    white-space: nowrap;
}

.custom-mud-data-grid-relation .mud-table-container {
    height: auto;
    width: auto;
}

.custom-mud-table-footer {
    padding: 8px;
    height: 50px;
}

.custom-mud-empty-data-grid {
    height: calc(100vh - 18px);
}

.content-container {
    display: flex;
    padding: 8px;
    flex: 1;
    min-height: 0;
    box-sizing: border-box;
    position: relative;
    overflow: hidden;
}

.menu-container {
    display: flex;
    flex-direction: column;
    height: 100%;
}

.menu-container .mud-drawer-header {
    flex-shrink: 0;
}

.menu-nav-content {
    flex: 1;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    padding: 0 8px;
}

.menu-workspace-section {
    flex-shrink: 0;
    padding-bottom: 8px;
}

.menu-favorites-section {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    max-height: 30%;
    padding-bottom: 8px;
}

.favorite-header {
    flex-shrink: 0;
    padding-bottom: 8px;
}

.favorite-list {
    flex: 1;
    display: flex;
    flex-direction: column;
    overflow-y: auto;
    min-height: 0;
}

.favorite-item {
    flex-shrink: 0;
    min-width: 0;
}

/* The row is a text button so the whole title takes the click; its label keeps the type icon and the
   ellipsis of the menu items. */
.favorite-item-button {
    justify-content: flex-start;
    text-transform: none;
    padding: 4px 8px;
    min-width: 0;
}

.favorite-item-label {
    min-width: 0;
    overflow: hidden;
}

.favorite-item-name {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Hidden until the row is hovered: the star that lets a favorite go. */
.favorite-item .favorite-item-remove {
    opacity: 0;
    transition: opacity 0.15s;
}

.favorite-item:hover .favorite-item-remove,
.favorite-item:focus-within .favorite-item-remove {
    opacity: 1;
}

.menu-types-section {
    flex: 0 1 auto;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    min-height: 0;
    padding-bottom: 8px;
}

.menu-features-section {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    max-height: 50%;
    padding-bottom: 8px;
}

.feature-header {
    flex-shrink: 0;
    padding-bottom: 8px;
}

.feature-list {
    flex: 1;
    display: flex;
    flex-direction: column;
    overflow-y: auto;
    min-height: 0;
}

.feature-item {
    padding: 4px 8px;
    border-radius: 8px;
    box-sizing: border-box;
    flex-shrink: 0;
}

.feature-item-content {
    min-width: 0;
    overflow: hidden;
}

.feature-item-label {
    min-width: 0;
    overflow: hidden;
}

.feature-item-name {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.type-header {
    flex-shrink: 0;
    padding-bottom: 8px;
}

.type-list {
    flex: 1;
    overflow-y: auto;
    min-height: 0;
}

.drawer-empty-hint {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    padding: 12px;
    text-align: center;
}

.drawer-empty-hint-text {
    color: var(--mud-palette-text-secondary);
}

/* Floating "keep going" badge shown while the guided tour is paused (user is filling a drawer). Purely
   informational and non-blocking so it never interferes with the form / pickers underneath. */
.radio-tour-waiting {
    position: fixed;
    left: 50%;
    bottom: 28px;
    transform: translateX(-50%);
    z-index: 2000000000;
    pointer-events: none;
    display: flex;
    align-items: center;
    max-width: 90vw;
    padding: 10px 18px;
    border-radius: 999px;
    background: var(--mud-palette-primary);
    color: var(--mud-palette-primary-text, #fff);
    font-size: 0.9rem;
    font-weight: 500;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
    animation: radio-tour-waiting-in 0.25s ease;
}

@keyframes radio-tour-waiting-in {
    from { opacity: 0; transform: translate(-50%, 8px); }
    to { opacity: 1; transform: translate(-50%, 0); }
}

/* Theme the Driver.js popovers to match MudBlazor (and follow light/dark via the palette variables).
   driver.css loads after app.css, so these need !important to win. */
.driver-popover {
    background-color: var(--mud-palette-surface) !important;
    color: var(--mud-palette-text-primary) !important;
    border-radius: 12px !important;
    padding: 16px 18px !important;
    max-width: 340px !important;
    box-shadow: var(--mud-elevation-8, 0 8px 24px rgba(0, 0, 0, 0.22)) !important;
    border: 1px solid var(--mud-palette-lines-default) !important;
}

.driver-popover * {
    font-family: 'Inter', ui-sans-serif, system-ui, "Segoe UI", sans-serif !important;
}

.driver-popover-title {
    color: var(--mud-palette-text-primary) !important;
    font-size: 1.05rem !important;
    font-weight: 600 !important;
    line-height: 1.35 !important;
}

.driver-popover-description {
    color: var(--mud-palette-text-secondary) !important;
    font-size: 0.875rem !important;
    line-height: 1.5 !important;
}

.driver-popover-progress-text {
    color: var(--mud-palette-text-secondary) !important;
    font-size: 0.75rem !important;
}

.driver-popover-close-btn {
    color: var(--mud-palette-text-secondary) !important;
}
.driver-popover-close-btn:hover,
.driver-popover-close-btn:focus {
    color: var(--mud-palette-text-primary) !important;
}

/* Buttons: primary "Next/Done" filled, "Previous" as a subtle text button. */
.driver-popover-footer button {
    border: none !important;
    border-radius: 8px !important;
    padding: 6px 14px !important;
    font-size: 0.8rem !important;
    font-weight: 500 !important;
    text-shadow: none !important;
    transition: background-color 0.15s ease, opacity 0.15s ease !important;
}

.driver-popover-next-btn {
    background-color: var(--mud-palette-primary) !important;
    color: var(--mud-palette-primary-text, #fff) !important;
}
.driver-popover-next-btn:hover,
.driver-popover-next-btn:focus {
    background-color: var(--mud-palette-primary-darken, var(--mud-palette-primary)) !important;
    opacity: 0.92 !important;
}

.driver-popover-prev-btn {
    background-color: transparent !important;
    color: var(--mud-palette-text-secondary) !important;
}
.driver-popover-prev-btn:hover,
.driver-popover-prev-btn:focus {
    background-color: var(--mud-palette-action-default-hover, rgba(0, 0, 0, 0.06)) !important;
    color: var(--mud-palette-text-primary) !important;
}

/* Arrow: recolour the visible side to the popover background so it stays a triangle. */
.driver-popover-arrow-side-left { border-left-color: var(--mud-palette-surface) !important; }
.driver-popover-arrow-side-right { border-right-color: var(--mud-palette-surface) !important; }
.driver-popover-arrow-side-top { border-top-color: var(--mud-palette-surface) !important; }
.driver-popover-arrow-side-bottom { border-bottom-color: var(--mud-palette-surface) !important; }

.tutorial-invite {
    position: relative;
    display: flex;
    align-items: center;
    gap: 20px;
    /* The quote above has a 3rem bottom margin (scoped CSS); pull the banner up to tighten the gap
       without changing the no-banner spacing. */
    margin: -1.75rem 0 16px;
    padding: 20px 24px;
    border-radius: 14px;
    border: 1px solid color-mix(in srgb, var(--mud-palette-primary) 35%, transparent);
    background: linear-gradient(
        135deg,
        color-mix(in srgb, var(--mud-palette-primary) 12%, transparent),
        color-mix(in srgb, var(--mud-palette-primary) 4%, transparent));
}

.tutorial-invite-close {
    position: absolute;
    top: 6px;
    right: 6px;
    color: var(--mud-palette-text-secondary);
}

.tutorial-invite-emoji {
    font-size: 2.25rem;
    line-height: 1;
    flex-shrink: 0;
    animation: tutorial-invite-bounce 2.4s ease-in-out infinite;
}

@keyframes tutorial-invite-bounce {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-4px); }
}

.tutorial-invite-body {
    flex-grow: 1;
    min-width: 0;
}

.tutorial-invite-title {
    font-weight: 600;
    line-height: 1.3;
    margin: 0 0 6px;
}

.tutorial-invite-text {
    color: var(--mud-palette-text-secondary);
    font-size: 0.9rem;
    line-height: 1.5;
    margin: 0;
}

.tutorial-invite-actions {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-shrink: 0;
    padding-left: 8px;
}

@media (max-width: 700px) {
    .tutorial-invite {
        flex-direction: column;
        align-items: flex-start;
        gap: 14px;
    }

    .tutorial-invite-actions {
        align-self: stretch;
        justify-content: flex-end;
        padding-left: 0;
    }
}

.detail-container {
    padding: 8px;
}

.element-hover {
    opacity: 0;
}

.parent-hover:hover .element-hover {
    opacity: 1;
    transition: fill 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
}

.mud-collapse-container {
    transition: grid-template-rows 100ms ease-in-out !important;
}

.mud-dialog-width-sm {
    max-width: 700px !important;
}

/* Popups de confirmation (DialogMessageView) : la boîte s'étend selon le texte (fit-content) au lieu de
   remplir la MaxWidth, avec un plafond. Elle grandit donc avec un titre long et reste compacte sinon. */
.mud-dialog.mud-message-dialog-fit {
    width: fit-content !important;
    min-width: 320px;
    max-width: min(90vw, 640px) !important;
}

.mud-tab-badge {
    &.mud-badge-root {
        position: static;
        margin-left: 8px;

        .mud-badge-wrapper {
            position: static;
            width: auto;
            pointer-events: auto;
        }

        .mud-badge {
            position: static;
            display: inline-flex;
        }
    }
}

/**:focus {*/
/*    background-color: red !important;*/
/*}*/

/*.mud-switch .mud-icon-button:focus-within, .mud-icon-button:active {*/
/*    background-color: var(--mud-palette-primary) !important;*/
/*}*/

.custom-mud-treeview-item .mud-treeview-item-disabled {
    color: inherit !important;
    pointer-events: auto !important;
}

/* Header Card */
.header-card {
    background: var(--mud-palette-background-gray);
    border-radius: 12px;
    padding: 20px 24px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 8px;
    position: relative;
    overflow: hidden;
}

.header-card-left {
    display: flex;
    align-items: center;
    gap: 16px;
}

.header-card-icon {
    width: 48px;
    height: 48px;
    border-radius: 10px;
    background: var(--mud-palette-action-default-hover);
    display: flex;
    align-items: center;
    justify-content: center;
}

.header-card-info {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.header-card-title {
    font-size: 1.25rem;
    font-weight: 600;
    display: flex;
    align-items: center;
    gap: 8px;
}

.header-card-description {
    color: var(--mud-palette-text-secondary);
    font-size: 0.85rem;
}

.header-card-chip {
    background: var(--header-accent-color, var(--mud-palette-primary));
    color: #fff;
    padding: 2px 6px;
    border-radius: 12px;
    font-size: 0.75rem;
    font-weight: 600;
}

.mud-chip-xs {
    height: 18px !important;
    padding: 2px 10px !important;
    font-size: 0.75rem !important;
    font-weight: 600 !important;
}

.mud-chip-xs .mud-chip-content {
    padding: 0 !important;
}

/* Header Search */
.header-search {
    min-width: 100px;
    width: 500px;
}

/* Dialog Header */
.dialog-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 16px 12px;
    margin-top: 12px;
    /* Ligne séparatrice pleine largeur sous le header, symétrique du border-top du footer. */
    border-bottom: 1px solid var(--mud-palette-divider);
}

.dialog-header-left {
    display: flex;
    align-items: center;
    gap: 12px;
}

.dialog-header-icon {
    width: 40px;
    height: 40px;
    border-radius: 10px;
    background: var(--mud-palette-action-default-hover);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--mud-palette-primary);
}

.dialog-header-text {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    min-width: 0;
}

.dialog-header-title {
    font-size: 1.1rem;
    font-weight: 600;
}

.dialog-header-subtitle {
    font-size: 0.8rem;
    color: var(--mud-palette-text-secondary);
}

.dialog-header-close {
    width: 40px !important;
    height: 40px !important;
    border-radius: 50%;
    background: transparent;
    opacity: 0.7;
    transition: all 0.2s ease;
}

.dialog-header-close:hover {
    opacity: 1;
    background: var(--mud-palette-action-default-hover);
}

/* Header de l'Historique : il a DEUX lignes (icône + titre, puis l'instance en dessous), donc le
   align-items: center de .dialog-header centrait la croix sur toute la hauteur. On la cale en haut :
   comme .dialog-header-close et .dialog-header-icon font tous deux 40×40, elle tombe pile sur la
   ligne du titre, sans décalage à ajuster. Scopé pour ne pas toucher les autres headers (une seule
   ligne titre/sous-titre), où le centrage reste correct. */
.dialog-header:has(.history-header) .dialog-header-close {
    align-self: flex-start;
}

/* Dialog Footer */
.dialog-footer {
    padding: 16px 20px;
    border-top: 1px solid var(--mud-palette-divider);
    width: 100%;
}

/* CommandView content : default = scrollable mono-colonne ; with-stepper = layout horizontal rail/form */
.command-view-content {
    flex: 1;
    overflow-y: auto;
    padding: 16px;
}

.command-view-content.with-stepper {
    display: flex;
    flex-direction: row;
    padding: 0;
    overflow: hidden;
}

.command-view-content.with-stepper .command-view-form {
    flex: 1;
    overflow-y: auto;
    padding: 16px 24px;
}

/* Step de sélection d'objet (liste paginée) : le form devient flex column borné pour que SEULE la
   liste scrolle et que la pagination reste collée en bas (façon MudDataGrid), au lieu que tout le
   form scrolle. Ciblé via :has(.relation-select-field) — présent uniquement dans cette vue — pour
   ne PAS affecter les autres steps (ObjectAdd, onboarding…) qui gardent leur flux block scrollable. */
.command-view-content.with-stepper .command-view-form:has(.relation-select-field) {
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* Propage la hauteur à travers les wrappers Mud (CustomMudForm → .mud-form, MudFocusTrap →
   .mud-focus-trap) jusqu'à .relation-step-container, sinon la chaîne flex se casse et la liste ne
   peut pas scroller en interne. Ciblé via :has(.relation-select-field) → uniquement la step de sélection. */
.command-view-form:has(.relation-select-field) > .mud-form,
.command-view-form:has(.relation-select-field) .mud-form > .mud-focus-trap {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
}

.header-search-field .mud-input-outlined-border {
    border-color: var(--mud-palette-lines-inputs) !important;
    border-radius: 8px !important;
}

/* User Section (Bottom Sidebar) */
.user-section {
    display: flex;
    align-items: center;
    gap: 12px;
}

.user-section-info {
    flex: 1;
    min-width: 0;
}

.user-section-name {
    font-weight: 500;
    font-size: 14px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.user-section-role {
    font-size: 12px;
    color: var(--mud-palette-text-secondary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.user-section-actions {
    display: flex;
    gap: 4px;
    flex-shrink: 0;
}

.user-section-btn {
    color: var(--mud-palette-text-secondary) !important;
    transition: all 0.2s ease !important;
}

.user-section-btn:hover {
    color: var(--mud-palette-text-primary) !important;
    background: var(--mud-palette-action-default-hover) !important;
}

.user-section-btn.logout:hover {
    color: var(--mud-palette-error) !important;
    background: rgba(244, 67, 54, 0.12) !important;
}

/* Menu profil (popover du bouton en bas du drawer) */
/* La largeur est bornée à celle de l'ancre via RelativeWidth="DropdownWidth.Relative" sur le
   MudMenu (max-width = largeur de la ligne profil) : le popover ne déborde plus du drawer. */
/* Écart entre le popover et le bloc user. MudBlazor place le popover en position: absolute avec
   un `top` calculé en JS (bottom: auto) → un margin-bottom n'a aucun effet. On le remonte donc
   avec un transform, que MudBlazor n'utilise pas pour le placement (aucun risque d'écrasement). */
.profile-menu {
    transform: translateY(-24px);
}

/* Le wrapper d'activateur de MudMenu (.mud-menu-activator) se dimensionne à son contenu et les
   flex items ont min-width: auto → l'email long imposait sa largeur, rien ne s'ellipsait et le
   drawer débordait (ascenseur horizontal). On borne la chaîne pour laisser jouer les ellipses
   déjà présentes dans InstanceDisplay (.instance-title / .instance-details). */
.profile-menu-activator,
.profile-menu-activator .mud-menu-activator,
.profile-menu-activator .user-section {
    /* display: block indispensable : en niveau inline le wrapper se réduit autour de son contenu,
       et MudBlazor positionne le popover par rapport à CE wrapper → le menu s'ancrait sur le
       chevron. En bloc pleine largeur, l'ancre devient toute la ligne profil. */
    display: block;
    width: 100%;
    min-width: 0;
}

/* .user-section doit rester en flex (avatar + infos + chevron) malgré la règle ci-dessus. */
.profile-menu-activator .user-section {
    display: flex;
    align-items: center;
    gap: 12px;
}

/* Le chevron ne doit jamais être comprimé par le texte. */
.profile-menu-activator .user-section .mud-icon-button {
    flex-shrink: 0;
}

/* MudBlazor force `.mud-menu * { cursor: pointer }` sur TOUS ses descendants : le nom et l'email
   paraissaient donc cliquables alors que seul le chevron ouvre le menu. On remet le curseur par
   défaut sur le contenu de l'activateur… */
.profile-menu-activator .user-section,
.profile-menu-activator .user-section * {
    cursor: default;
}

/* …sauf sur le chevron, lui bien cliquable. */
.profile-menu-activator .user-section .mud-icon-button,
.profile-menu-activator .user-section .mud-icon-button * {
    cursor: pointer;
}

.profile-menu-header {
    padding: 6px 8px 10px;
}

/* Le form ne doit pas casser la mise en page du menu ; le bouton EST l'item de menu. */
.profile-menu-logout-form {
    display: contents;
}

.profile-menu-logout {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 8px 12px;
    border: none;
    background: transparent;
    border-radius: 8px;
    color: var(--mud-palette-error);
    font: inherit;
    cursor: pointer;
    text-align: left;
    transition: background-color 0.15s ease;
}

.profile-menu-logout:hover {
    background: rgba(166, 68, 92, 0.12);
}

/* Type List Items */
.type-item-content {
    min-width: 0;
    overflow: hidden;
}

.type-item-label {
    min-width: 0;
    overflow: hidden;
}

.type-item-name {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Les lignes de relation se déplient à leur tour : le chevron garde sa colonne à tous les niveaux, même
   absent, pour que les icônes des lignes voisines restent alignées ; et un peu d'air entre lui et l'icône. */
.type-list .mud-treeview-item-arrow {
    margin: 0 8px 0 0;
}

/* Un filet à gauche de chaque niveau imbriqué : on lit d'un coup d'œil sous quoi une ligne se range. */
.type-list .mud-treeview-item .mud-treeview-group {
    margin-left: 22px;
    padding-left: 6px;
    border-left: 1px solid var(--mud-palette-lines-default);
}

.type-list .mud-treeview-item-content {
    border-radius: 8px;
}

/* MudBlazor marque l'item sur lequel on a cliqué, pas ceux qui lui sont égaux : une même ligne de relation
   figure sous chacun des types qu'elle touche, et toutes désignent le même type. Ce fond-là les allume
   ensemble, en reprenant celui de la sélection Mud. */
.type-list .type-item-selected > .mud-treeview-item-content {
    background-color: var(--mud-palette-primary-hover);
}

.type-list .type-item-selected > .mud-treeview-item-content .type-item-name {
    color: var(--mud-palette-primary);
}

.mud-input-control-boolean-input {
    width: fit-content;
}

/* ========== Feature Rule Card ========== */

.frule-card {
    background: var(--mud-palette-surface);
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 8px;
    padding: 14px 16px;
    transition: border-color 0.15s;
}

.frule-card:hover {
    border-color: var(--mud-palette-lines-inputs);
}

.frule-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    margin-bottom: 8px;
}

.frule-left {
    display: flex;
    align-items: center;
    gap: 10px;
}

.frule-icon {
    width: 30px;
    height: 30px;
    border-radius: 6px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

.frule-icon.blocker {
    background: var(--mud-palette-error-hover);
    color: var(--mud-palette-error);
}

.frule-icon.action {
    background: var(--mud-palette-info-hover);
    color: var(--mud-palette-info);
}

.frule-name {
    font-size: 13px;
    font-weight: 500;
    margin-bottom: 1px;
}

.frule-desc {
    font-size: 11px;
    color: var(--mud-palette-text-secondary);
    font-weight: 300;
}

.frule-right {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
}

.frule-badge {
    font-size: 9px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.8px;
    padding: 2px 8px;
    border-radius: 4px;
}

.frule-badge.blocker {
    color: var(--mud-palette-error);
    background: var(--mud-palette-error-hover);
}

.frule-badge.action {
    color: var(--mud-palette-info);
    background: var(--mud-palette-info-hover);
}

.frule-pseudo {
    margin-top: 8px;
    padding: 8px 12px;
    background: var(--mud-palette-background-gray);
    border-radius: 6px;
    border: 1px solid var(--mud-palette-lines-default);
    font-family: 'JetBrains Mono', monospace;
    font-size: 10px;
    line-height: 1.6;
}

.frule-meta {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px solid var(--mud-palette-lines-default);
    font-size: 10px;
    color: var(--mud-palette-text-secondary);
}

.frule-meta-item {
    display: flex;
    align-items: center;
    gap: 4px;
}

.frule-card.disabled .frule-left { opacity: 0.55; }
.frule-card.disabled .frule-pseudo { opacity: 0.55; }
.frule-card.disabled .frule-meta { opacity: 0.55; }
.frule-card.disabled .frule-badge { opacity: 0.55; }

.frule-card.removed {
    opacity: 0.5;
    border-style: dashed;
}

/* ========== Condition Builder ========== */

.cb {
    background: var(--mud-palette-background-gray);
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 8px;
    overflow: hidden;
}

.cb-block {
    border-bottom: 1px solid var(--mud-palette-lines-default);
    padding: 10px 14px;
}

.cb-block:last-of-type {
    border-bottom: none;
}

.cb-row {
    display: grid;
    grid-template-columns: 48px 1fr auto 1fr 24px;
    gap: 0 8px;
    grid-template-rows: auto auto;
    align-items: end;
}

/* The path source: what is read first, always whole; the way there after it, cut when it runs out of room. */
/* Up to four sources (Objet, Champ, Système, Parcours) in the width of the field and the operator: each column takes
   the room its label needs, rather than the equal share MudToggleGroup sets inline, which cuts the longest. */
.cb-source-toggle {
    grid-template-columns: none !important;
    grid-auto-flow: column;
    grid-auto-columns: auto;
}

.rule-path-chip {
    min-width: 0;
    max-width: 100%;
    min-height: 40px;
    justify-content: flex-start;
    text-transform: none;
}

.rule-path-chip .mud-button-label {
    min-width: 0;
    overflow: hidden;
    justify-content: flex-start;
}

.rule-path-chip-field {
    flex-shrink: 0;
    font-weight: 600;
}

.rule-path-chip-path {
    min-width: 0;
    margin-left: 6px;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    opacity: 0.75;
}

.cb-toggle-wrapper {
    grid-column: 4;
    grid-row: 1;
    padding-bottom: 6px;
}

.cb-left-toggle-wrapper {
    grid-column: 2;
    grid-row: 1;
    padding-bottom: 6px;
}

.cb-toggle {
    display: flex;
    gap: 0;
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 4px;
    overflow: hidden;
    width: fit-content;
}

.cb-toggle-btn {
    padding: 2px 10px;
    font-size: 10px;
    font-weight: 600;
    border: none;
    background: var(--mud-palette-surface);
    color: var(--mud-palette-text-disabled);
    cursor: pointer;
    font-family: inherit;
    display: flex;
    align-items: center;
    gap: 4px;
}

.cb-toggle-btn.active {
    background: rgba(68, 92, 166, 0.15);
    color: var(--mud-palette-secondary);
}

.cb-toggle-btn + .cb-toggle-btn {
    border-left: 1px solid var(--mud-palette-lines-default);
}

.cb-logic {
    grid-column: 1;
    grid-row: 2;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    text-align: center;
    color: var(--mud-palette-secondary);
}

.cb-logic-toggle {
    grid-column: 1;
    grid-row: 2;
    padding: 4px 0;
    font-size: 10px;
    font-weight: 700;
    border-radius: 4px;
    border: 1px solid rgba(95, 118, 190, 0.3);
    background: var(--mud-palette-surface);
    color: var(--mud-palette-secondary);
    cursor: pointer;
    font-family: inherit;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    width: 48px;
    text-align: center;
    align-self: center;
}

.cb-logic-toggle.or {
    color: var(--mud-palette-warning);
    border-color: var(--mud-palette-warning-hover);
    background: rgba(255, 183, 77, 0.12);
}

.cb-current-ref {
    grid-column: 4;
    grid-row: 2;
    padding: 8px 12px;
    border-radius: 6px;
    background: var(--mud-palette-surface);
    border: 1px solid var(--mud-palette-lines-default);
    font-size: 11px;
    font-weight: 600;
    color: var(--mud-palette-text-disabled);
    font-family: 'JetBrains Mono', monospace;
    width: 100%;
}

.cb-current-badge {
    padding: 4px 8px;
    border-radius: 4px;
    background: var(--mud-palette-surface);
    border: 1px solid var(--mud-palette-lines-default);
    font-size: 10px;
    font-weight: 600;
    color: var(--mud-palette-text-disabled);
    white-space: nowrap;
    flex-shrink: 0;
    font-family: 'JetBrains Mono', monospace;
}

.cb-val-duration {
    grid-column: 4;
    grid-row: 2;
    display: flex;
    align-items: center;
    gap: 6px;
}

.cb-val-current-field {
    grid-column: 4;
    grid-row: 2;
    display: flex;
    align-items: center;
    gap: 4px;
}

.cb-add {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 8px;
    border-top: 1px solid var(--mud-palette-lines-default);
    cursor: pointer;
    color: var(--mud-palette-text-disabled);
    font-size: 12px;
    gap: 4px;
}

.cb-add:hover {
    background: var(--mud-palette-action-default-hover);
    color: var(--mud-palette-text-secondary);
}

.rules-header {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    margin-bottom: 10px;
}
.onboarding-root {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    width: 100vw;
    background: var(--mud-palette-background);
}

.onboarding-card {
    width: 100%;
    max-width: 960px;
}

.onboarding-logout {
    position: absolute;
    top: 16px;
    right: 24px;
    z-index: 10;
    display: flex;
    align-items: center;
    gap: 4px;
}

.error-root {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    width: 100vw;
    background: var(--mud-palette-background);
    padding: 24px;
    box-sizing: border-box;
}

.error-content {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    max-width: 720px;
}

/* ---- Pont survol↔colonne de l'éditeur de cardinalité (RelationCardinalityEditor) ----
   Pur CSS (:has() + :hover), sans aucun événement Blazor : survoler un nom de type dans la phrase
   atténue la colonne DE/VERS opposée et illumine l'en-tête de sa propre colonne. Règles GLOBALES
   (non isolées) pour pouvoir traverser la frontière vue ↔ composant éditeur. */
.rce-col,
.rce-col-head {
    transition: opacity .16s, filter .16s, color .16s;
}

.rce-bridge-scope:has(.rce-label.bridge[data-side="from"]:hover) .rce-col[data-side="to"],
.rce-bridge-scope:has(.rce-label.bridge[data-side="to"]:hover) .rce-col[data-side="from"] {
    opacity: .42;
    filter: saturate(.7);
}

.rce-bridge-scope:has(.rce-label.bridge[data-side="from"]:hover) .rce-col[data-side="from"] .rce-col-head,
.rce-bridge-scope:has(.rce-label.bridge[data-side="to"]:hover) .rce-col[data-side="to"] .rce-col-head {
    color: var(--mud-palette-text-primary);
}

/* la couleur du type sur son icône ; sélectionnée, la chip prend la couleur en fond et l'icône suit le texte */
.rce-chip .mud-chip-icon {
    color: var(--rce-chip-color, inherit);
}

.rce-chip.rce-chip-selected .mud-chip-icon {
    color: inherit;
}

/* ... et contour sur les chips sélectionnées de la colonne survolée */
.rce-chip-selected {
    transition: box-shadow .16s, transform .16s;
}

.rce-bridge-scope:has(.rce-label.bridge[data-side="from"]:hover) .rce-col[data-side="from"] .rce-chip-selected,
.rce-bridge-scope:has(.rce-label.bridge[data-side="to"]:hover) .rce-col[data-side="to"] .rce-chip-selected {
    /* anneau = couleur de la chip éclaircie (teinte + saturation conservées) ; fallback clair si pas de couleur */
    box-shadow: 0 0 0 2px oklch(from var(--rce-chip-color, #ECE9E2) 0.85 c h), 0 6px 18px -6px rgba(0, 0, 0, .6);
    transform: translateY(-1px);
}

/* ========== Relation Explorer (détail de ligne : arbre + détail) ========== */
/* Trois colonnes (arbre | détail | actions), hauteur FIXE : l'arbre est virtualisé et a besoin d'un
   conteneur de scroll borné. Le td.mud-table-child-content a padding:0, on gère ici nos marges. */
.relation-explorer {
    display: grid;
    /* Arbre : largeur FIXE → identique d'une ligne dépliée à l'autre (colonnes alignées) et plafonnée
       (au-delà, ellipsis). Détail : 1fr (s'étend). Actions : 58px = largeur réelle rendue de la dernière
       colonne de la datagrid (40px déclarés + padding de cellule MudDataGrid). */
    grid-template-columns: 320px minmax(260px, 1fr) 58px;
    align-items: stretch;
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 6px;
    /* Hauteur ADAPTATIVE : la grille épouse son contenu. Ce sont les COLONNES (arbre, détail) qui sont
       plafonnées (max-height) et scrollent en interne — un max-height sur la grille déborderait/clipperait
       la rangée. La virtualisation a juste besoin d'un viewport borné : le max-height de la colonne arbre. */
    overflow: hidden;
    background: var(--mud-palette-surface);
    /* Contexte d'empilement propre. Sans ça, le z-index de l'en-tête de groupe épinglé (10) se compare à
       celui de l'en-tête collant de la datagrid (2 chez MudBlazor) : au scroll, l'explorer passait
       par-dessus les titres de colonnes. Enfermé ici, il ne peut plus rivaliser qu'avec ses propres
       descendants, et c'est cette rangée entière qui passe sous l'en-tête de la grille. */
    z-index: 0;
    /* La ligne dépliée s'étend sur toute la largeur de la grille, souvent plus large que l'écran : le détail
       (et sa colonne « relation » à droite) partait hors champ. L'explorer se cale sur la zone VISIBLE de la
       grille — largeur du scroller (unité de conteneur, cf. règle suivante) — et reste collé à gauche quand
       on défile à l'horizontale. Collant = positionné : le contexte d'empilement ci-dessus tient toujours. */
    position: sticky;
    left: 12px;
    margin: 12px 0;
    width: calc(100cqi - 24px);
    box-sizing: border-box;
}

/* Le scroller de la grille devient le conteneur de référence de l'explorer (100cqi = sa largeur visible). Sa
   largeur est posée explicitement (cf. .custom-mud-data-grid-object .mud-table-container) : la containment
   d'inline-size ne peut pas l'écraser à zéro comme elle le ferait d'un max-content. */
.custom-mud-data-grid-object .mud-table-container:has(.relation-explorer) {
    container-type: inline-size;
}

/* Lignes de l'arbre virtualisé : hauteur uniforme, doit correspondre à l'ItemSize de <Virtualize> (36). */
/* Hauteur unique, et elle doit rester égale à l'ItemSize de <Virtualize> (RelationExplorerView) : une
   ligne plus haute que déclarée fait dériver le scroll et mentir l'ascenseur. */
.relation-tree-row {
    height: 38px;
    box-sizing: border-box;
}

/* En-tête de groupe RACINE épinglé. position: sticky borné à sa section → il reste en haut pendant le
   scroll de ses enfants, puis est « poussé » quand la section suivante arrive (effet natif, sans JS).
   Un SEUL scroll (.relation-explorer-tree) : la section est juste un bloc empilé, sans overflow propre. */
.relation-tree-sticky-header {
    position: sticky;
    top: 0;
    /* Au-dessus de tout le contenu virtualisé qui défile dessous (les composants Mud des nœuds peuvent
       créer leur propre empilement). */
    z-index: 10;
    /* Opaque (= fond de l'arbre) pour masquer les nœuds qui défilent dessous. */
    background: var(--mud-palette-background-gray);
}

/* Sentinelle de scroll infini en bas de l'arbre : zone observée + spinner pendant le chargement de page. */
.relation-tree-truncated {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 8px 0;
    color: var(--mud-palette-text-disabled);
}

/* Chargement initial de l'explorer (expand de la ligne datagrid) : spinner centré. */
.relation-explorer-loading {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 120px;
    padding: 32px;
}

/* Spinner à la place du chevron pendant le chargement des enfants d'un nœud. Même gabarit 28×28 que le
   chevron (.relation-tree-chevron.mud-button-root) et le spacer → la ligne ne bouge pas à l'affichage. */
.relation-tree-chevron-loading {
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.relation-tree-chevron-loading .mud-progress-circular {
    width: 18px;
    height: 18px;
}

.relation-explorer-tree {
    /* Viewport de scroll de la virtualisation : plafonné (adaptatif en deçà), scroll au-delà.
       PAS de padding vertical : l'en-tête sticky doit être flush en haut (sinon du contenu peut
       apparaître au-dessus dans l'espace du padding). */
    max-height: 600px;
    overflow-y: auto;
    /* Titres longs : plutôt que de les rogner, la colonne défile. Les sections en max-content (ci-dessous)
       donnent au contenu une largeur supérieure au viewport, sinon l'ascenseur ne se déclenche jamais. */
    overflow-x: auto;
    min-height: 0;
    background: var(--mud-palette-background-gray);
    border-right: 1px solid var(--mud-palette-lines-default);
}

/* La section prend la largeur de sa ligne la plus longue (min 100 % du viewport). C'est elle qui porte la
   largeur, pas chaque ligne : sinon les lignes courtes garderaient un fond tronqué au bord du viewport
   pendant le défilement horizontal, et le survol s'arrêterait au milieu de rien. */
.relation-tree-section {
    width: max-content;
    min-width: 100%;
}

.relation-explorer-detail {
    /* Nuance distincte de la Surface (lignes de la grille) et du background-gray (arbre) : le
       détail se lit comme une carte de focus. color-mix conserve ce décalage dans les deux
       thèmes — il éclaircit en sombre, assombrit légèrement en clair — là où le #2E2E2C codé
       en dur ne valait que pour le thème sombre. */
    background: color-mix(in srgb, var(--mud-palette-surface) 94%, var(--mud-palette-text-primary));
    min-width: 0;
    /* Plafonné et scrollable indépendamment (champs longs) — même borne que l'arbre. */
    max-height: 600px;
    overflow-y: auto;
    min-height: 0;
}

/* Rien de sélectionné : le panneau se fond dans le fond (pas de carte), sans message. */
.relation-explorer-detail.empty {
    background: var(--mud-palette-surface);
}

.relation-explorer-empty {
    padding: 24px;
    color: var(--mud-palette-text-secondary);
    text-align: center;
}

/* --- En-têtes de groupe (RelationType) --- */
.relation-group-header {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 8px;
    font-size: 11px;
    letter-spacing: 0.01em;
    color: var(--mud-palette-text-secondary);
}

/* Icônes de l'en-tête racine réduites pour coller au libellé minuscule (10px). Le chevron de repli est
   re-dimensionné par la règle dédiée plus bas (.relation-tree-row .relation-tree-chevron), de spécificité
   supérieure → il échappe à cette réduction. */
.relation-group-header .mud-icon-root {
    font-size: 12px;
}

.relation-group-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Conteneur flex direct de l'en-tête → l'auto-marge pousse le « + » tout à droite de la colonne
   (le MudIconButton est imbriqué dans un MudTooltip, donc l'auto-marge ne marche pas sur le bouton). */
.relation-add {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    /* Même raison que le menu « ⋮ » des nœuds : l'auto-marge pousse le bouton au bord droit de la SECTION,
       qui peut être plus large que la colonne. Sans ça, il disparaît dès qu'on défile vers un titre long.
       Fond opaque simple ici — un en-tête de groupe n'a ni survol ni sélection à reproduire. */
    position: sticky;
    right: 0;
    /* Mêmes gouttières que le menu des nœuds, marge négative comprise (l'en-tête a son propre padding
       de 8 px à droite, qui doublerait sans elle). */
    padding-left: 8px;
    padding-right: 8px;
    margin-right: -8px;
    background: var(--mud-palette-background-gray);
}

/* Bouton « + » plein, forcé en cercle (MudIconButton Filled rend un carré arrondi par défaut). Bleu posé
   par la maquette, plus vif que le primaire du thème. */
.relation-add-button.mud-button-root {
    border-radius: 50%;
    background-color: #4c59d8;
}


/* --- Nœuds objet --- */
.relation-tree-node {
    display: flex;
    align-items: center;
    gap: 4px;
    padding-right: 8px;
    border-left: 2px solid transparent;
    font-size: 13px;
    color: var(--mud-palette-text-primary);
}

.relation-tree-node:hover {
    background: var(--mud-palette-action-default-hover);
}

.relation-tree-node.selected {
    background: rgba(68, 92, 166, 0.15);
    border-left-color: var(--mud-palette-primary);
    font-weight: 500;
}

/* Chevron = MudIconButton compact (les events sur HTML brut ne se câblent pas ici → composants Mud). */
.relation-tree-chevron.mud-button-root {
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
    padding: 2px;
    color: var(--mud-palette-text-secondary);
}

/* Pas de fond propre au bouton (hover/focus/active) : seul le surlignage de ligne doit apparaître,
   sinon on a l'impression d'une « double sélection ». */
.relation-tree-chevron.mud-button-root:hover,
.relation-tree-chevron.mud-button-root:focus,
.relation-tree-chevron.mud-button-root:focus-visible,
.relation-tree-chevron.mud-button-root:active {
    background-color: transparent;
}

/* Chevron dimensionné explicitement → identique partout (repli d'en-tête racine ET dépli de nœud). Spécificité
   (0,3,0) volontairement supérieure aux règles de contexte `.relation-group-header .mud-icon-root` (12px) et
   `.relation-tree-node .mud-icon-root` (16px) — toutes deux (0,2,0) — pour gagner quel que soit l'ordre. */
.relation-tree-row .relation-tree-chevron .mud-icon-root {
    font-size: 20px;
}

/* Réservé quand le nœud est une feuille (aucun enfant visible) : garde l'alignement des libellés. */
.relation-tree-chevron-spacer {
    flex: 0 0 auto;
    width: 20px;
}

/* Menu d'actions (3 points) de la relation, à droite de la ligne ; affiché en permanence.
   Collé au bord droit du viewport pendant le défilement horizontal : la ligne peut être bien plus large
   que la colonne, et le menu sortirait du champ dès qu'on va lire la fin d'un titre long. */
.relation-tree-menu {
    flex: 0 0 auto;
    position: sticky;
    right: 0;
    /* Les DEUX gouttières appartiennent à la zone collante, pas à la ligne. À gauche, sans quoi le titre
       vient buter contre les trois points — le padding droit de .relation-tree-node vit au bout de la
       section, donc hors champ dès qu'on défile. À droite, parce que `right: 0` plaque l'élément contre le
       bord du viewport et lui fait perdre ce même padding au moment précis où le collage s'active.
       La marge négative annule le padding droit du parent : l'icône ne bouge pas, mais son fond opaque va
       maintenant jusqu'au bord, sinon le texte réapparaîtrait dans les 8 px laissés à découvert. */
    padding-left: 8px;
    padding-right: 8px;
    margin-right: -8px;
    /* Opaque, sinon le titre défile en transparence sous les trois points. Le fond de l'arbre est la
       couche du dessous ; le survol et la sélection sont des voiles translucides posés PAR-DESSUS par
       .relation-tree-node — d'où le même empilement ici (dégradé plat sur fond opaque), qui redonne
       exactement la teinte de la ligne au lieu d'y coller un rectangle d'une autre couleur. */
    background: var(--mud-palette-background-gray);
}

.relation-tree-node:hover .relation-tree-menu {
    background:
        linear-gradient(var(--mud-palette-action-default-hover), var(--mud-palette-action-default-hover)),
        var(--mud-palette-background-gray);
}

.relation-tree-node.selected .relation-tree-menu {
    background:
        linear-gradient(rgba(68, 92, 166, 0.15), rgba(68, 92, 166, 0.15)),
        var(--mud-palette-background-gray);
}

/* Zone cliquable de sélection = MudButton déguisé en ligne d'arbre. */
.relation-tree-node-main.mud-button-root {
    /* Grandit pour pousser le menu « ⋮ » à droite, mais ne rétrécit JAMAIS sous son libellé : c'est ce
       min-width qui fait déborder la ligne et donne sa largeur à la section. */
    flex: 1 0 auto;
    min-width: max-content;
    justify-content: flex-start;
    text-transform: none;
    font-weight: inherit;
    font-size: 13px;
    line-height: 1.3;
    color: var(--mud-palette-text-primary);
    /* padding-left réduit (2 au lieu de 8) : aligne l'icône du nœud sur celle des en-têtes de relation
       (chevron 28 + gap) → l'escalier d'indentation reste régulier de 16 px entre chaque niveau. */
    /* Vertical resserré : la ligne à deux étages doit tenir dans les 38 px. */
    padding: 3px 8px 3px 2px;
}

/* Idem : pas de fond propre au bouton de ligne ; le survol/sélection est porté par .relation-tree-node. */
.relation-tree-node-main.mud-button-root:hover,
.relation-tree-node-main.mud-button-root:focus,
.relation-tree-node-main.mud-button-root:focus-visible,
.relation-tree-node-main.mud-button-root:active {
    background-color: transparent;
}

/* Ligne à deux étages : le type de relation coiffe le nom de l'objet. Un seul étage quand le micro-libellé
   est masqué (premier niveau) — la colonne reste alors centrée verticalement. */
.relation-tree-node-content {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: flex-start;
    gap: 0;
    min-width: 0;
    width: 100%;
}

/* Étage 1 : le type de relation, en petit et en majuscules — même vocabulaire visuel que les en-têtes de
   groupe du premier niveau, dont il prend la place en profondeur. */
.relation-tree-node-relation {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 10.5px;
    line-height: 1.2;
    letter-spacing: 0.01em;
    color: var(--mud-palette-text-secondary);
}

/* Spécificité (0,3,0) volontaire, même raison que le chevron plus haut : `.relation-tree-node .mud-icon-root`
   (16px) pèse pareil que cette règle en (0,2,0) et vient APRÈS dans le fichier — elle gagnerait, et l'icône
   du type de relation sortirait à la taille de celle de l'objet. */
.relation-tree-node .relation-tree-node-relation .mud-icon-root {
    font-size: 10px;
}

/* Étage 2 : icône + nom de l'objet, inchangés. */
.relation-tree-node-title {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    line-height: 1.2;
    min-width: 0;
}

/* Image de l'objet à la place de l'icône du type : même encombrement, ne rétrécit pas. */
.relation-tree-node-image {
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
}

.relation-tree-node .mud-icon-root {
    font-size: 16px;
}

/* Plus d'ellipsis : le libellé s'affiche en entier et pousse la largeur de la section, que l'ascenseur
   horizontal de la colonne permet d'atteindre. */
.relation-tree-node-label {
    flex: 0 0 auto;
    white-space: nowrap;
    text-align: left;
}

/* Réserve en permanence la largeur du libellé en gras (copie invisible), pour que passer la ligne
   sélectionnée en bold ne recalcule pas la colonne max-content (plus de décalage). */
.relation-tree-node-label::after {
    content: attr(data-text);
    display: block;
    height: 0;
    overflow: hidden;
    visibility: hidden;
    font-weight: 500;
    white-space: nowrap;
}

.relation-tree-more {
    padding: 2px 8px 4px 0;
    font-size: 11px;
    font-style: italic;
    color: var(--mud-palette-text-disabled);
}

/* --- Détail (colonne droite) --- */
.relation-detail {
    padding: 16px 20px;
}

.relation-detail-header {
    padding-bottom: 12px;
    margin-bottom: 12px;
    border-bottom: 1px solid var(--mud-palette-lines-default);
}

/* Réserve la hauteur d'une fiche à deux lignes (titre + description) : hauteur d'en-tête stable d'une
   sélection à l'autre, et — sans description — le titre seul reste centré face à l'avatar (align-items
   center sur .instance-display). */
.relation-detail-header .instance-display {
    min-height: 50px;
}

/* Colonne d'action de l'objet lié, en bout de détail : largeur fixe (= dernière colonne de la datagrid),
   fond plus sombre, pleine hauteur. */
.relation-explorer-actions {
    overflow: hidden; /* la colonne fait 40px (piste de grille) ; on borne le bouton à cette largeur */
    padding-top: 16px; /* aligne le haut de la zone sur le contenu du détail (.relation-detail padding) */
    background: var(--mud-palette-background-gray);
    border-left: 1px solid var(--mud-palette-lines-default);
}

/* Boîte de hauteur = en-tête (InstanceDisplay) : le 3-points y est centré → aligné sur le centre
   de l'avatar/nom, peu importe la hauteur totale du détail. */
.relation-explorer-actions-top {
    height: 50px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.relation-detail-section-title {
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-weight: 600;
    color: var(--mud-palette-text-secondary);
    margin-bottom: 6px;
}

/* Informations (instance) et « Via {RelationType} » côte à côte ; repli en colonne si trop étroit. */
.relation-detail-sections {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 32px;
}

.relation-detail-section {
    flex: 1 1 260px;
    min-width: 0;
}

/* Le nom de la relation se lit comme un nom, pas comme un libellé de section : il échappe aux capitales
   que .relation-detail-section-title pose sur « Informations » et consorts. */
.relation-detail-via {
    display: flex;
    align-items: center;
    gap: 6px;
    text-transform: none;
    letter-spacing: 0.01em;
    font-size: 11px;
}

.relation-detail-via .mud-icon-root {
    font-size: 12px;
}

.relation-detail-fields {
    display: flex;
    flex-direction: column;
}

.relation-detail-field {
    display: grid;
    /* minmax(0, 1fr) et non 1fr : une piste de grille prend par défaut la largeur de son contenu, si bien
       qu'un texte long s'étirait sur une seule ligne au lieu d'être borné par la colonne. */
    grid-template-columns: 140px minmax(0, 1fr);
    gap: 12px;
    padding: 6px 0;
    border-bottom: 1px solid var(--mud-palette-lines-default);
}

.relation-detail-field:last-child {
    border-bottom: none;
}

.relation-detail-field-label {
    font-size: 12px;
    color: var(--mud-palette-text-secondary);
}

.relation-detail-field-value {
    font-size: 13px;
    min-width: 0;
}

/* Une seule ligne, comme une cellule de datagrid : les lignes du panneau gardent toutes la même hauteur,
   ce qui se balaie mieux qu'une fiche aux blocs inégaux. La valeur entière se lit dans l'édition de l'objet
   — le panneau donne un aperçu, pas le texte.
   La règle vise aussi l'enfant direct : FieldValueDisplay rend un <p> (MudText), et un parent en overflow
   hidden ne pose pas d'ellipse sur le débordement d'un enfant bloc — il le coupe net. */
.relation-detail-field-value,
.relation-detail-field-value > * {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* --- Valeurs typées (FieldValueDisplay) --- */
.field-value-boolean {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 13px;
}

.field-value-boolean .mud-icon-root {
    font-size: 16px;
}

.field-value-boolean-yes {
    color: var(--mud-palette-success);
}

.field-value-boolean-no {
    color: var(--mud-palette-text-secondary);
}

.field-value-empty {
    color: var(--mud-palette-text-disabled);
    font-style: italic;
}



/* ============================================================
   Lifecycle editor (JointJS). The graph is styled here, not in JS: cells carry these classes, and the
   --ru-* tokens are the only place the editor touches the theme (they follow MudBlazor's palette, so
   dark mode comes for free). Only the grid and the arrow heads are set from JS, SVG cannot inherit them.
   ============================================================ */
:root {
    --ru-canvas: var(--mud-palette-background-gray);
    --ru-surface: var(--mud-palette-surface);
    --ru-text: var(--mud-palette-text-primary);
    --ru-text-soft: var(--mud-palette-text-secondary);
    --ru-line: var(--mud-palette-lines-default);
    --ru-grid: var(--mud-palette-divider);
    --ru-accent: var(--mud-palette-primary);
    --ru-terminal: var(--mud-palette-error);
    --ru-accent-wash: color-mix(in srgb, var(--ru-accent) 12%, var(--ru-surface));
    --ru-terminal-wash: color-mix(in srgb, var(--ru-terminal) 12%, var(--ru-surface));
}

.lifecycle-editor {
    position: relative;
    height: 60vh;
    min-height: 360px;
    overflow: auto;
    background: var(--ru-canvas);
    border: 1px solid var(--ru-line);
    border-radius: 4px;
}

.lifecycle-editor-paper {
    outline: none;
}

.lifecycle-editor-paper svg {
    display: block;
}

.lifecycle-editor-hint {
    position: sticky;
    left: 14px;
    bottom: 14px;
    float: left;
    pointer-events: none;
    font-size: 12px;
    color: var(--ru-text-soft);
    background: var(--ru-surface);
    border: 1px solid var(--ru-line);
    border-radius: 4px;
    padding: 6px 10px;
}

.ru-body { fill: var(--ru-surface); stroke: var(--ru-line); stroke-width: 1.5px; }
.ru-body--initial { fill: var(--ru-accent-wash); stroke: var(--ru-accent); }
.ru-body--final { fill: var(--ru-terminal-wash); stroke: var(--ru-terminal); }
.ru-body.is-selected { stroke: var(--ru-accent); stroke-width: 3px; }

.ru-label { fill: var(--ru-text); }
.ru-port { fill: var(--ru-surface); stroke: var(--ru-accent); stroke-width: 1.5px; opacity: 0; transition: opacity .12s; }
.joint-element:hover .ru-port,
.ru-port.available-magnet { opacity: 1; }

.ru-line { stroke: var(--ru-text-soft); stroke-width: 1.5px; }
.ru-line.is-selected { stroke: var(--ru-accent); stroke-width: 2.5px; }
.ru-marker { stroke: none; }

.ru-edge-label { fill: var(--ru-text-soft); cursor: move; }
.ru-edge-bg { fill: var(--ru-canvas); stroke: var(--ru-line); stroke-width: 1px; cursor: move; }

.lifecycle-editor .joint-element { cursor: move; }
.lifecycle-editor .joint-highlight-stroke { stroke: var(--ru-accent); }

.ru-tool-bg { fill: var(--ru-surface); stroke: var(--ru-line); stroke-width: 1.5px; transition: stroke .12s, fill .12s; }
.ru-tool-icon { fill: none; stroke: var(--ru-text-soft); stroke-width: 1.8px; stroke-linecap: round; }
.joint-tool:hover .ru-tool-bg { stroke: var(--ru-terminal); fill: var(--ru-terminal-wash); }
.joint-tool:hover .ru-tool-icon { stroke: var(--ru-terminal); }

/* The cross of a path lives in its label: hidden until the path is selected. Text glyph, so fill not stroke. */
.ru-label-remove { display: none; }
.ru-label-remove.is-visible { display: block; }
text.ru-tool-icon { fill: var(--ru-text-soft); stroke: none; }
.ru-tool-bg.ru-label-remove:hover { stroke: var(--ru-terminal); fill: var(--ru-terminal-wash); }

.lifecycle-card {
    padding: 12px 16px;
    margin-bottom: 8px;
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 8px;
    background-color: transparent !important;
}

.lifecycle-card.is-removed { opacity: .5; text-decoration: line-through; }


/* ============================================================
   Lifecycle in an instance: the state chip (list, explorer card, drawer), the filter chips above the grid,
   and the drawer panel (current state + path buttons, the walk when expanded).
   ============================================================ */
/* MudChip, read-only: no margin (it sits in grid cells and flex rows), a small dot as icon, the current state in
   bold on the primary wash, the ones ahead toned down. */
.lifecycle-state-chip.mud-chip {
    margin: 0;
    cursor: default;
    max-width: 100%;
}

.mud-chip.lifecycle-state-chip .mud-chip-icon,
.mud-chip.mud-chip-size-small.lifecycle-state-chip .mud-chip-icon {
    font-size: 10px;
    width: 10px;
    height: 10px;
    margin-inline-end: 6px;
}

/* Outlined everywhere, so the pill reads on a dark surface too; the current one adds the primary wash. */
.lifecycle-state-chip.is-current {
    font-weight: 600;
    background: var(--ru-accent-wash);
}

.lifecycle-state-chip.is-pending {
    border-style: dashed;
    font-weight: 600;
}

.lifecycle-state-chip.is-upcoming {
    color: var(--mud-palette-text-secondary);
}

.lifecycle-state-chip.is-upcoming .mud-chip-icon {
    color: var(--mud-palette-lines-default);
}

/* A state with a colour of its own: the dot, the border of the current/pending one and its wash take that colour;
   the text stays the theme's, a chosen colour may be too light to read. */
.lifecycle-state-chip.has-color .mud-chip-icon {
    color: var(--state-color);
}

.lifecycle-state-chip.has-color.is-current,
.lifecycle-state-chip.has-color.is-pending {
    border-color: var(--state-color);
    color: var(--mud-palette-text-primary);
}

.lifecycle-state-chip.has-color.is-current {
    background: color-mix(in srgb, var(--state-color) 18%, var(--ru-surface));
}

.lifecycle-state-chip.has-color.is-upcoming .mud-chip-icon {
    color: color-mix(in srgb, var(--state-color) 45%, var(--ru-surface));
}

/* A path button towards a coloured state. */
.lifecycle-path-button.has-color.mud-button-outlined {
    color: var(--state-color);
    border-color: var(--state-color);
}

.content-filters-dot {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--state-color);
    margin-right: 6px;
    vertical-align: middle;
}

/* « Filtres » in the header: the button next to the search, its popover (a list of checkboxes, grouped by
   lifecycle), and the active filters as chips right under the header. */
.header-card-right {
    display: flex;
    align-items: stretch;
    gap: 12px;
}

/* The button stands as tall as the search field next to it. */
/* No height on the badge root itself: a set height (even 100% of an auto parent) would defeat the flex stretch. */
.content-filters-badge {
    display: flex;
}

.content-filters-button,
.content-filters-button .mud-menu-activator,
.content-filters-button .mud-button-root {
    height: 100%;
}

.content-filters-button .mud-button-root {
    padding: 0 20px;
    border-radius: 8px;
}

.content-filters-badge .mud-badge {
    inset-inline-end: 6px;
    top: 6px;
}

.content-filters-popover {
    min-width: 280px;
    padding: 4px 0 8px;
}

.content-filters-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 8px 8px 0 16px;
}

.content-filters-section-title {
    font-size: 12px;
    color: var(--mud-palette-text-secondary);
    padding: 4px 16px 0;
}

.content-filters-group {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-weight: 600;
    color: var(--mud-palette-text-secondary);
    padding: 10px 16px 2px;
}

.content-filters-option {
    display: flex;
    padding: 0 8px;
}

.content-filters-option.is-unbound .mud-switch-label,
.content-filters-option.is-unbound .mud-typography {
    color: var(--mud-palette-text-secondary);
}

.content-filters-active {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
    padding: 0 10px 8px;
}

.content-filters-active .mud-chip {
    margin: 0;
}

.content-filters-active-label {
    font-size: 12px;
    color: var(--mud-palette-text-secondary);
    margin-right: 4px;
}

/* The explorer card: the state stands alone on its row, above the information fields. */
.relation-detail-section-state {
    flex: 1 1 100%;
}

/* The lifecycle followed, named after the « État » section title in its own case (the title is uppercase). */
.lifecycle-section-name {
    text-transform: none;
    letter-spacing: normal;
    font-weight: 400;
    font-size: 11px;
    margin-left: 2px;
}

/* Drawer panel. */
.lifecycle-panel {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 12px 14px;
    border: 1px solid var(--ru-line);
    border-radius: 8px;
    background: var(--ru-surface);
}

.lifecycle-panel-line {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
}

.lifecycle-panel-spacer {
    flex-grow: 1;
}

.lifecycle-panel-toggle {
    margin-left: 4px;
}

.lifecycle-bind-banner-body {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px 12px;
}

.lifecycle-bind-select {
    min-width: 220px;
}

/* The walk: a grid, one column per lane and a rail row between two content rows, so the lanes of a fork stay
   aligned whatever the chip widths (chips stretch to their lane). */
.lifecycle-walk {
    display: grid;
    grid-auto-columns: max-content;
    grid-auto-rows: auto;
    column-gap: 12px;
    justify-content: start;
    border-top: 1px solid var(--ru-line);
    padding-top: 12px;
}

/* A lane cell spans its column so the fork lines land on the next lane; the chip inside stays content-sized. */
.lifecycle-walk-cell {
    display: flex;
    align-items: center;
    min-height: 30px;
    min-width: 42px;
    position: relative;
}

.lifecycle-walk-rail {
    display: block;
    width: 2px;
    height: 14px;
    margin-left: 20px;
    background: var(--ru-line);
}

.lifecycle-walk-fork-space {
    display: block;
    height: 18px;
}

/* Fork: the cells of the head row hang from a bar. The first lane carries the trunk down from the row above;
   each cell but the last draws the bar to its neighbour, with the elbow that drops onto it. */
.lifecycle-walk .is-fork-trunk::before {
    content: "";
    position: absolute;
    left: 20px;
    top: -18px;
    width: 2px;
    height: 18px;
    background: var(--ru-line);
}

.lifecycle-walk .is-fork-bar::after {
    content: "";
    position: absolute;
    left: 20px;
    top: -9px;
    width: calc(100% + 14px);
    height: 9px;
    border-top: 2px solid var(--ru-line);
    border-right: 2px solid var(--ru-line);
    border-top-right-radius: 8px;
    pointer-events: none;
}

.lifecycle-walk-separator {
    display: block;
    height: 1px;
    background: var(--ru-line);
    margin: 14px 0 12px;
}

/* ===== Field modes by lifecycle state (Trello #73) ===== */
/* Drawer: a field brought in or kept by the move being prepared, and the line saying what the move does to it. */
.field-mode-appears {
    border-left: 3px solid var(--mud-palette-primary);
    padding-left: 10px;
}

.field-mode-leaving {
    border-left: 3px solid var(--mud-palette-warning);
    padding-left: 10px;
}

.field-mode-note {
    font-size: 0.75rem;
    line-height: 1.4;
    color: var(--mud-palette-text-secondary);
    margin: -2px 0 10px;
}

/* List: a field the row's state hides — an empty, greyed cell, distinct from « no value ». */
.mud-table-cell.field-cell-not-applicable {
    background-color: var(--mud-palette-background-gray);
}

/* State settings dialog: the fields of the state and their mode. */
.lifecycle-field-modes {
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 8px;
    overflow: hidden;
}

.lifecycle-field-mode-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 6px 8px 6px 14px;
    border-bottom: 1px solid var(--mud-palette-lines-default);
}

.lifecycle-field-mode-row:last-child {
    border-bottom: none;
}

/* The name takes the room left, so that ↺ and the toggles line up on the right, row after row. */
.lifecycle-field-mode-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

.lifecycle-field-mode-name.is-hidden {
    color: var(--mud-palette-text-disabled);
    text-decoration: line-through;
}

/* Four square-ish cells, a neutral outline with delimiters, the icon centred; the tooltip fills the cell so the
   whole of it answers the hover. */
.lifecycle-field-mode-toggle.mud-toggle-group {
    flex-shrink: 0;
    border-radius: 6px;
}

.lifecycle-field-mode-toggle.mud-toggle-group-outlined,
.lifecycle-field-mode-toggle.mud-toggle-group-outlined .mud-toggle-item {
    border-color: var(--mud-palette-lines-inputs);
}

.lifecycle-field-mode-toggle .mud-toggle-item {
    width: 44px;
    height: 34px;
    padding: 0;
}

.lifecycle-field-mode-toggle .mud-toggle-item > .mud-button-label,
.lifecycle-field-mode-toggle .mud-toggle-item .mud-tooltip-root {
    width: 100%;
    height: 100%;
    min-height: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}

.lifecycle-field-mode-toggle .mud-toggle-item .mud-icon-root {
    font-size: 1.25rem;
}

/* Only the chosen mode takes the primary colour; the others stay neutral. */
.lifecycle-field-mode-toggle .mud-toggle-item:not(.mud-toggle-item-selected) {
    color: var(--mud-palette-text-secondary);
}

/* What comes from above (the lifecycle for a state, the field's own setting for the lifecycle): the same cell, in
   a light tint rather than filled — what is chosen at this level stands out. */
.lifecycle-field-mode-toggle.is-inherited .mud-toggle-item.mud-toggle-item-selected {
    background-color: color-mix(in srgb, var(--mud-palette-primary) 16%, var(--mud-palette-surface));
    color: var(--mud-palette-primary);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--mud-palette-primary) 45%, transparent);
}

/* ↺ back to the setting above, or the room it would take, so the toggles stay aligned. */
.lifecycle-field-mode-reset,
.lifecycle-field-mode-reset-placeholder {
    flex-shrink: 0;
}

.lifecycle-field-mode-reset-placeholder {
    display: inline-block;
    width: 34px;
}