#blazor-error-ui {
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 0.75rem;
    top: 0.5rem;
}

:root {
    font-size: var(--rz-root-font-size);
}

body {
    font-family: var(--rz-text-font-family);
    color: var(--rz-text-color);
    font-size: var(--rz-body-font-size);
    line-height: var(--rz-body-line-height);
    background-color: var(--rz-body-background-color);
}

.rz-body {
    --rz-body-padding: 0;
}

a {
    color: var(--rz-link-color);
}

a:hover,
a:focus {
    color: var(--rz-link-hover-color);
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

.blazor-error-boundary::after {
    content: "An error has occurred."
}

.loading-progress {
    position: relative;
    display: block;
    width: 8rem;
    height: 8rem;
    margin: 20vh auto 1rem auto;
}

.loading-progress circle {
    fill: none;
    stroke: #e0e0e0;
    stroke-width: 0.6rem;
    transform-origin: 50% 50%;
    transform: rotate(-90deg);
}

.loading-progress circle:last-child {
    stroke: #1b6ec2;
    stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
    transition: stroke-dasharray 0.05s ease-in-out;
}

.loading-progress-text {
    position: absolute;
    text-align: center;
    font-weight: bold;
    inset: calc(20vh + 3.25rem) 0 auto 0.2rem;
}

.loading-progress-text:after {
    content: var(--blazor-load-percentage-text, "Loading");
}

/* Les libelles d'onglets Radzen (theme material) sont en majuscules par defaut.
   On les repasse en casse normale (comme ecrit dans le code) dans toute l'application.
   Fichier statique direct (pas de bundling CSS isole) pour eviter tout probleme de cache
   ou de regeneration du bundle Fusion.styles.css. */
:root {
    --rz-tabs-tab-text-transform: none;
}

.rz-tabview-nav button {
    text-transform: none !important;
}

/* Meme mecanisme que ci-dessus, applique aux boutons d'action des en-tetes de fiche
   (Enregistrer / Annuler / Modifier / Retour sur EditClient, EditFamille, EditArticle, etc.).
   Le theme Material met le texte des RadzenButton en majuscules via .rz-button-text.
   Regle placee ici (feuille globale, pas de CSS isolation) car la regle equivalente posee
   dans les .razor.css scopes de chaque page, meme avec ::deep et !important, n'avait pas
   d'effet : les elements internes generes par RadzenButton (dont .rz-button-text) ne portent
   pas de facon fiable l'attribut de scope b-xxxx de la page appelante, donc le selecteur scope
   ne les matchait jamais. */
.fiche-action-btn .rz-button-text {
    text-transform: none !important;
}

/* Meme mecanisme, pour le bouton "Fermer tous les onglets" (Components/Layout/TabWorkspace.razor,
   classe .tab-workspace-close-all). */
.tab-workspace-close-all .rz-button-text {
    text-transform: none !important;
}

/* Meme mecanisme, pour les boutons "Export" (RadzenSplitButton) et "Ajouter" (RadzenButton) des
   pages de liste (Familles.razor, Clients.razor, Articles.razor), classe .list-action-btn. */
.list-action-btn .rz-button-text {
    text-transform: none !important;
}

/* Meme mecanisme, pour les boutons d'action sous la grille "Lignes" de PieceDetail.razor
   ("Ajouter article", "Modifier", "Supprimer", ...), classe .ligne-action-btn. */
.ligne-action-btn .rz-button-text {
    text-transform: none !important;
}

/* Meme mecanisme, pour les boutons Ok/Annuler des boites de dialogue DialogService.Confirm
   (ex. "Modifications non enregistrées" - EditClient.razor.cs, EditTva.razor.cs, EditGlossaire.
   razor.cs, etc.). Pas de classe CSS assignable directement sur ces boutons (generes en interne
   par Radzen.Blazor, ConfirmOptions n'expose pas de CssClass par bouton) : on cible a la place
   ".rz-dialog-confirm-buttons", le conteneur fixe que Radzen.Blazor place systematiquement autour
   d'eux (DialogService.Confirm), stable quel que soit le texte des boutons. */
.rz-dialog-confirm-buttons .rz-button-text {
    text-transform: none !important;
}

/* Bordure fine sur toutes les boites de dialogue (ArticlePicker, ClientPicker, TvaPicker, etc.,
   mais aussi DialogService.Confirm) - a la demande explicite de l'utilisateur : en theme sombre,
   la boite de dialogue se distingue mal du fond (l'ombre portee seule ne suffit pas a marquer ses
   bords sur un arriere-plan deja sombre, contrairement au theme clair). ".rz-dialog" est la boite
   elle-meme generee par Radzen.Blazor (DialogService) : meme raison qu'au-dessus pour la regle
   globale plutot que scopee (balisage interne de Radzen.Blazor, hors de portee d'un CSS isole).
   var(--rz-dialog-border-color, ...) : utilise la variable de theme si Radzen.Blazor la definit,
   sinon un gris neutre semi-transparent qui reste visible aussi bien en theme clair qu'en theme
   sombre sans avoir a distinguer les deux ici.
   display: flex/flex-direction: column ci-dessous (avec ".rz-dialog-content" en flex: 1 1 auto) :
   le contenu d'une boite de dialogue (".rz-dialog-content", sous le bandeau de titre ".rz-dialog-
   titlebar") ne s'etire pas en hauteur avec la boite elle-meme par defaut - un RadzenStack racine
   en "height: 100%" dans le composant de la boite de dialogue (ex. Components/Dialogs/
   DesignationEditorDialog.razor) n'a alors aucun effet, faute de hauteur definie sur ce parent.
   Sans ces regles, les boutons "Annuler"/"Valider" (poses en bas du RadzenStack racine, flex: 0 0
   auto) restent a la hauteur du contenu d'origine au lieu de suivre le bas de la fenetre quand
   elle est agrandie manuellement (Resizable="true") - a la demande explicite de l'utilisateur, en
   particulier pour DesignationEditorDialog.razor (zone de saisie HTML qui doit grandir avec la
   fenetre, boutons toujours colles en bas a droite). Colonne flex (bandeau de titre en hauteur
   naturelle, contenu en flex: 1 1 auto) : ".rz-dialog-content" recoit et repercute la hauteur
   disponible a son propre enfant (le RadzenStack racine du composant, deja en height: 100%).
   min-height: 0 sur ".rz-dialog-content" : necessaire pour qu'un enfant flex puisse retrecir sous
   sa taille de contenu naturelle plutot que de deborder (meme raison que le wrapper de
   RadzenHtmlEditor dans DesignationEditorDialog.razor). Sans effet indesirable sur les boites de
   dialogue non redimensionnables/a contenu court (ArticlePicker, TvaPicker, formulaires
   AdminUsers, DialogService.Confirm...) : leur contenu ne demandant pas de hauteur particuliere,
   il garde sa taille naturelle au sein de la colonne flex. */
.rz-dialog {
    border: 1px solid var(--rz-dialog-border-color, rgba(128, 128, 128, 0.4));
    display: flex;
    flex-direction: column;
}

.rz-dialog-content {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

/* Le titre et les boutons Export/Ajouter des pages de liste (Clients.razor, Articles.razor,
   Familles.razor, classe .list-sticky-header) restent visibles en haut au defilement. Regle
   placee ici (feuille globale) et non dans un .razor.css scope : la <div> racine generee par
   RadzenRow appartient au balisage interne de Radzen.Blazor (RadzenRow.razor), elle ne porte donc
   jamais l'attribut de scope b-xxxx de la page appelante - un CSS isole ne peut pas l'atteindre,
   meme sans ::deep. Verifie en direct (DevTools) : avec une regle scopee, position calculee =
   "static" (aucun effet) faute de cet attribut. */
.list-sticky-header {
    position: sticky;
    top: 0;
    z-index: 10;
    background-color: var(--rz-body-background-color);
    padding-top: 0.5rem;
    padding-bottom: 0.5rem;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
}

/* Entete des colonnes des grilles de donnees des pages de liste (Clients.razor, Articles.razor,
   Familles.razor, Paiements.razor) : reste egalement visible au defilement.
   IMPORTANT - pourquoi "top: 0" fonctionne ici alors que .list-sticky-header a besoin de mesurer
   sa hauteur en JS : ce ne sont PAS le meme contexte de defilement. RadzenDataGrid enveloppe sa
   <table> dans une <div class="rz-data-grid-data"> qui porte overflow-x:auto (necessaire au
   defilement horizontal des colonnes Frozen) - or en CSS, des qu'un axe a un overflow different de
   "visible", l'autre axe (ici overflow-y) est recalcule a "auto" par la specification, meme s'il
   n'est jamais explicitement pose. Cette <div> devient donc le "conteneur de defilement le plus
   proche" au sens de position:sticky pour tout ce qu'elle contient - PAS la page. Tant que cette
   <div> n'a pas de hauteur bornee, ce n'est pas un vrai conteneur a defilement interne (son
   contenu ne deborde jamais d'elle, elle grandit avec lui) : un sticky "top" y reste fige a un
   decalage constant DANS la div (comme du position:relative), d'ou le grand espace vide observe
   au-dessus de l'entete et les lignes qui passaient dessous. Fix : donner une hauteur bornee au
   RadzenDataGrid (Style="height: calc(100vh - 300px)" sur chaque page de liste) pour que cette div
   devienne un vrai conteneur a defilement interne - "top: 0" y est alors correct et suffisant, et
   c'est le mecanisme que Radzen.Blazor prevoit lui-meme pour un entete fixe (cf. issue GitHub
   radzenhq/radzen-blazor #175 : definir une hauteur active le defilement interne avec entete fixe).
   Regle placee dans cette feuille globale (pas de CSS isole scope) : le <thead>/<th> genere par
   RadzenDataGrid ne porte pas l'attribut de scope b-xxxx de la page appelante.

   border-left/border-right : bordure verticale sur CHAQUE entete de colonne (donc a la fois a son
   debut et a sa fin), de toutes les grilles de donnees de l'application - uniquement l'entete
   (thead), pas les cellules de donnees (tbody) - pour voir clairement ou commence et ou se termine
   chaque colonne, a la demande explicite de l'utilisateur. Meme variable/repli que la bordure des
   boites de dialogue en theme sombre plus haut dans ce fichier, pour une teinte coherente avec le
   reste de l'application. */
.rz-data-grid-data thead th {
    position: sticky !important;
    top: 0 !important;
    z-index: 8;
    background-color: var(--rz-body-background-color) !important;
    border-left: 1px solid var(--rz-dialog-border-color, rgba(128, 128, 128, 0.4));
    border-right: 1px solid var(--rz-dialog-border-color, rgba(128, 128, 128, 0.4));
}

/* Meme mecanisme, pour l'entete des fiches de modification (EditClient.razor, EditFamille.razor,
   EditArticle.razor) : titre "Fiche ..." et boutons Enregistrer/Annuler/Modifier/Retour, classe
   .fiche-sticky-header sur la RadzenStack d'entete. */
.fiche-sticky-header {
    position: sticky;
    top: 0;
    z-index: 10;
    background-color: var(--rz-body-background-color);
    padding-top: 0.5rem;
    padding-bottom: 0.5rem;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
}

/* Page de connexion (Components/Layout/LoginLayout.razor) - a la demande explicite de
   l'utilisateur : meme habillage visuel que la page d'accueil (Components/Pages/Index.razor/
   Index.razor.css, classes ".home-hero*") - meme degrade, meme carte arrondie, meme texte
   ("Fusion" / "Modules facturation, stock, inventaire, ..." / "Votre succès est notre priorité.").
   Classes distinctes (".login-hero*" plutot que reutiliser ".home-hero*") : Index.razor.css est
   un fichier CSS isole (scoped) par Blazor, ses regles ne s'appliqueraient donc pas aux elements
   de LoginLayout.razor - meme raison que ".login-split*" ci-avant (deja dans ce fichier global).
   Compose sur le meme principe que ".home-hero" mais mirroir : le logo (a droite sur la page
   d'accueil) passe a gauche, aux cotes du texte (conserve tel quel) ; la carte blanche de droite
   accueille desormais le formulaire de connexion (@Body : Login.razor) a la place du logo. */
.login-hero-page {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 2rem 1.5rem;
    background: var(--rz-body-background-color);
}

.login-hero {
    display: flex;
    align-items: center;
    gap: 2.5rem;
    width: 100%;
    max-width: 900px;
    border-radius: var(--rz-border-radius, 8px);
    overflow: hidden;
    background: linear-gradient(135deg, #0f2f2d 0%, #1b6e64 55%, #3aa393 100%);
    padding: 3rem 2.75rem;
    box-shadow: 0 12px 32px rgba(15, 47, 45, 0.28);
}

.login-hero-text {
    flex: 1 1 55%;
    min-width: 260px;
}

/* Logo "de droite" de la page d'accueil (.home-hero-logo), repris ici a gauche - meme habillage
   (carte blanche arrondie, ombre) que sur la page d'accueil, juste au-dessus du texte a la place
   du petit badge (logoAzurPetit.png) : evite de dupliquer deux fois le meme logo sur ce cote. */
.login-hero-logo {
    display: inline-flex;
    margin-bottom: 1.25rem;
}

.login-hero-logo img {
    max-width: 130px;
    width: 100%;
    height: auto;
    display: block;
    background: #ffffff;
    border-radius: 14px;
    padding: 0.85rem;
    box-shadow: 0 10px 28px rgba(0, 0, 0, 0.3);
}

/* Carte blanche de droite - a la place du logo sur la page d'accueil, accueille le formulaire de
   connexion (@Body, cf. LoginLayout.razor). Largeur fixe (plutot que flex: 1 comme
   ".login-split-content" avant elle) pour ne pas ecraser le texte de gauche sur les tres grands
   ecrans, comme ".home-hero-logo" qui a aussi une largeur bornee. */
.login-hero-card {
    flex: 0 0 auto;
    width: 340px;
    max-width: 100%;
    background: #ffffff;
    border-radius: 14px;
    padding: 2rem;
    box-shadow: 0 10px 28px rgba(0, 0, 0, 0.3);
}

/* Remise en surbrillance de la ligne d'une grille de liste (Clients/Articles/Familles/Tva/
   ModeReglement/Paiements) au retour sur la liste apres fermeture d'une fiche - voir RowRender
   dans les .razor.cs correspondants (ex. Clients.razor.cs). On ne peut pas se contenter du
   binding SelectionMode/Value pour cela : Radzen ne resynchronise pas de facon fiable son style
   de ligne selectionnee (classe "rz-state-highlight") quand Value est modifie par programmation
   apres le premier rendu de la grille. RowRender pose donc a la place un attribut
   data-reselected="true" (choisi plutot qu'une classe pour ne jamais interferer avec la classe
   calculee par Radzen lui-meme sur le <tr>, cf. RadzenDataGridRow.razor : "class" est pose AVANT
   "@attributes", donc poser "class" via RowRender ecraserait entierement les classes de Radzen).
   Les regles ci-dessous reproduisent le rendu de ".rz-state-highlight" (memes variables CSS que
   Radzen : --rz-grid-selected-background-color/--rz-grid-selected-color) directement sur le fond
   de chaque cellule, colonnes figees (Frozen="true") comprises : un premier essai avait imite le
   mecanisme interne de Radzen pour les colonnes figees (pseudo-elements ::before/::after) mais ces
   pseudo-elements portent aussi l'ombre de defilement des colonnes figees, ce qui donnait une
   teinte differente de celle des colonnes normales - poser le fond directement sur chaque <td>
   (y compris figee) avec !important donne une teinte identique partout et est suffisant, le
   <td> figee n'ayant pas lui-meme de regle !important a contourner. */
tr[data-reselected="true"] > td {
    background-color: var(--rz-grid-selected-background-color) !important;
}

tr[data-reselected="true"] .rz-cell-data {
    color: var(--rz-grid-selected-color) !important;
}

/* Rubrique "Désignation" de la grille "Lignes" (PieceDetail.razor) : affichage (.designation-display,
   cf. Template de la colonne) et edition (.rz-html-editor-content, classe interne du contenu editable
   de RadzenHtmlEditor - seul usage de RadzenHtmlEditor dans le projet, avec DesignationEditorDialog.razor
   ou cette regle est egalement sans consequence puisqu'il n'y a pas de ligne de grille selectionnee
   autour). Sans cette regle, le texte "brut" (sans couleur explicite) de la designation herite de la
   couleur de surbrillance de la ligne selectionnee de la grille (.rz-cell-data { color:
   var(--rz-grid-selected-color); }, regle du theme Radzen lui-meme, cf. themes/components/blazor/
   _grid.scss) au lieu de la couleur de texte normale de l'application - or la ligne en cours d'edition
   est TOUJOURS selectionnee (cf. AjouterArticleClick/ModifierLigneClick dans PieceDetail.razor.cs),
   donc ce texte semblait changer de couleur des qu'on modifiait la ligne. On fixe donc explicitement
   la couleur de texte par defaut au demarrage de ce sous-arbre HTML, ce qui suffit a interrompre
   l'heritage de la couleur de surbrillance sans toucher au texte auquel l'utilisateur a lui-meme
   choisi une couleur dans l'editeur : un style inline explicite l'emporte deja naturellement sur une
   couleur heritee, avec ou sans cette regle. */
.designation-display,
.rz-html-editor-content {
    color: var(--rz-text-color) !important;
}

/* Titre "Connexion" de la carte de la page de connexion (Components/Pages/Login.razor,
   class="login-card-title"). Cause reelle identifiee via les DevTools (onglet Computed, avec
   l'utilisateur) : ce n'est PAS un probleme de regle CSS qui perd - .login-card-title * (ci-
   dessous) gagne deja bien, !important, sur .rz-text-h4 - le vrai probleme est que
   var(--rz-text-color) lui-meme vaut rgb(224, 224, 224) (gris tres clair) dans le theme de
   l'application (Radzen "Material Dark" ou equivalent, cf. material-dark-*.css) : une couleur de
   texte clair prevue pour un fond sombre, illisible sur fond blanc. .login-hero-card (site.css)
   a un fond blanc fixe, independant du theme choisi par l'application - le texte a l'interieur a
   donc besoin d'une couleur sombre fixe elle aussi, PAS de la variable de theme (qui suit le
   theme sombre global). Couleur en dur plutot que var(--rz-text-color). */
.login-card-title,
.login-card-title * {
    color: #1a1a1a !important;
}

@media (max-width: 768px) {
    .login-hero {
        flex-direction: column;
        text-align: center;
        padding: 2.25rem 1.5rem;
    }

    .login-hero-logo {
        display: flex;
        justify-content: center;
    }

    .login-hero-card {
        width: 100%;
    }
}