/* ============================================================
   site.css - CSS globale Backoffice AIR Enterprise
   REGOLA: nessun colore hardcoded. Solo var(--ae-*).
   Il blocco :root qui sotto sono SOLO fallback: a runtime vengono
   sovrascritti da AppConfigService.GetThemeCssVariables() (_Layout.cshtml).
   Per cambiare i colori: editare wwwroot/config/app-config.json.
   Nuove variabili si aggiungono PRIMA in GetThemeCssVariables(), poi qui.

   PALETTE: bianco + il solo colore di marca #C00000. Le tinte tenui (superfici,
   bordi, hover, ombre) sono OPACITA' del primary, non colori nuovi: una
   variabile che introduce un'altra tinta va discussa, non aggiunta.
   Tre eccezioni, tutte motivate:
   - i colori del testo (--ae-secondary, --ae-text, --ae-muted) sono neutri per
     contrasto, in rosso il corpo del testo non sarebbe leggibile;
   - --ae-sidebar-text e' neutro come il resto del testo (la sidebar e' bianca: il
     rosso pieno su tutta la colonna era troppo);
   - le --ae-success/warning/info-* tengono tinte proprie (verde, ambra, teal):
     sui badge di stato il colore porta informazione, e in monocromia "Attivo",
     "In approvazione" e "Cessato" diventerebbero indistinguibili;
   - le --ae-sell-* e --ae-cost-* (viola, blu) sono il tipo di listino: prima usava
     success e info come gli stati, e nella stessa riga si confondeva con
     pubblicazione e validita';
   - --ae-skin e' la pelle dell'omino del velo di caricamento: in rosso o in bianco
     la faccia non si leggeva come una faccia;
   - le --ae-pastel-* (rosa dal primary, poi salvia, azzurro, lavanda, burro, pesca)
     colorano le card KPI (statistiche del cliente, riepilogo dei borderò): numeri
     diversi, ognuno riconoscibile dal suo colore. Scelta del 30/10/2026, solo per
     quelle card.
   ============================================================ */
:root {
    --ae-primary: #C00000;
    --ae-secondary: #5A5A5A;
    --ae-accent: rgba(192, 0, 0, .08);
    --ae-primary-hover: #A70000;
    --ae-primary-text: #ffffff;
    --ae-success: #2E7D4F;
    --ae-success-bg: #E5EFE9;
    --ae-success-text: #225D3B;
    --ae-warning: #B45309;
    --ae-warning-bg: #F6EAE1;
    --ae-warning-text: #873E06;
    --ae-info: #0E7490;
    --ae-info-bg: #E2EEF1;
    --ae-info-text: #0A576C;
    --ae-sell: #7C3AED;
    --ae-sell-bg: #EFE7FC;
    --ae-sell-text: #5D2BB1;
    --ae-cost: #2563EB;
    --ae-cost-bg: #E4ECFC;
    --ae-cost-text: #1B4AB0;
    --ae-scrollbar-thumb: rgba(192, 0, 0, .22);
    --ae-scrollbar-thumb-hover: rgba(192, 0, 0, .4);
    --ae-step-done-bg: rgba(46, 125, 79, .18);
    --ae-login-bg: rgba(192, 0, 0, .06);
    --ae-sidebar-bg: #ffffff;
    --ae-sidebar-text: #5A5A5A;
    --ae-sidebar-active: #C00000;
    --ae-surface: #ffffff;
    --ae-bg: #ffffff;
    --ae-tint: rgba(192, 0, 0, .04);
    --ae-border: rgba(192, 0, 0, .16);
    --ae-text: #262626;
    --ae-muted: #6B6B6B;
    --ae-danger-bg: rgba(192, 0, 0, .12);
    --ae-danger-text: #A70000;
    --ae-shadow-card: 0 1px 3px rgba(192, 0, 0, .1);
    --ae-shadow-login: 0 18px 50px rgba(192, 0, 0, .16);
    --ae-login-deco: rgba(255, 255, 255, .13);
    --ae-skin: #F0C6A4;
    --ae-pastel-rose-bg: #FAEBEB;
    --ae-pastel-rose-border: #F2CCCC;
    --ae-pastel-rose-text: #A70000;
    --ae-pastel-sage-bg: #EAF2EC;
    --ae-pastel-sage-border: #C9DFCF;
    --ae-pastel-sage-text: #2E6046;
    --ae-pastel-sky-bg: #E8F0F7;
    --ae-pastel-sky-border: #C8DAEA;
    --ae-pastel-sky-text: #285880;
    --ae-pastel-lavender-bg: #EFECF8;
    --ae-pastel-lavender-border: #D5CEEC;
    --ae-pastel-lavender-text: #4E3C8A;
    --ae-pastel-butter-bg: #FAF3DE;
    --ae-pastel-butter-border: #EDDFAF;
    --ae-pastel-butter-text: #7A5B0B;
    --ae-pastel-peach-bg: #FCEDE3;
    --ae-pastel-peach-border: #F2D3BE;
    --ae-pastel-peach-text: #9A4A12;
    --ae-overlay: rgba(38, 38, 38, .62);
    --ae-art-ink: #262626;
    --ae-art-paper: #ffffff;
    --ae-logo-wide: url("/img/LOGO_LUNGO.svg");
    --ae-logo-mark: url("/img/LOGO_CORTO.svg");
}

/* Tema scuro (30/10/2026): attivo con data-theme="dark" su <html>, scelto dall'interruttore in fondo al menu e
   salvato nel cookie ae-theme. Anche questi sono SOLO fallback: a runtime li riscrive
   AppConfigService.GetDarkThemeCssVariables(), calcolandoli dal primary dell'app-config (qui i valori per #C00000).
   Stesse variabili del chiaro, quindi nessuna regola del file cambia. Il primary e' schiarito del 30%: il rosso
   pieno su fondo quasi nero si leggeva male. --ae-overlay (il velo dei caricamenti) e --ae-art-* (l'illustrazione
   del furgoncino) esistono perche' derivarli da testo e superficie, che in scuro si invertono, rovinava il disegno. */
:root[data-theme="dark"] {
    color-scheme: dark;
    --ae-primary: #D24C4C;
    --ae-secondary: #B8B8B8;
    --ae-accent: rgba(210, 76, 76, .16);
    --ae-primary-hover: #DC7272;
    --ae-primary-text: #ffffff;
    --ae-success: #77AA8C;
    --ae-success-bg: rgba(46, 125, 79, .22);
    --ae-success-text: #A0C4AF;
    --ae-warning: #CE8F5F;
    --ae-warning-bg: rgba(180, 83, 9, .22);
    --ae-warning-text: #DDB190;
    --ae-info: #62A4B6;
    --ae-info-bg: rgba(14, 116, 144, .22);
    --ae-info-text: #92C0CD;
    --ae-sell: #A97EF3;
    --ae-sell-bg: rgba(124, 58, 237, .22);
    --ae-sell-text: #C4A6F6;
    --ae-cost: #7199F2;
    --ae-cost-bg: rgba(37, 99, 235, .22);
    --ae-cost-text: #9CB8F6;
    --ae-scrollbar-thumb: rgba(210, 76, 76, .35);
    --ae-scrollbar-thumb-hover: rgba(210, 76, 76, .55);
    --ae-step-done-bg: rgba(46, 125, 79, .28);
    --ae-login-bg: rgba(210, 76, 76, .1);
    --ae-sidebar-bg: #1C1C1F;
    --ae-sidebar-text: #C8C8C8;
    --ae-sidebar-active: #C00000;
    --ae-surface: #1C1C1F;
    --ae-bg: #141416;
    --ae-tint: rgba(210, 76, 76, .08);
    --ae-border: rgba(210, 76, 76, .3);
    --ae-text: #E6E6E6;
    --ae-muted: #9C9C9C;
    --ae-danger-bg: rgba(210, 76, 76, .2);
    --ae-danger-text: #DF7F7F;
    --ae-shadow-card: 0 1px 3px rgba(0, 0, 0, .45);
    --ae-shadow-login: 0 18px 50px rgba(0, 0, 0, .55);
    --ae-login-deco: rgba(255, 255, 255, .06);
    --ae-pastel-rose-bg: rgba(210, 76, 76, .14);
    --ae-pastel-rose-border: rgba(210, 76, 76, .38);
    --ae-pastel-rose-text: #E28C8C;
    --ae-pastel-sage-bg: #1F2B24;
    --ae-pastel-sage-border: #2F4A3A;
    --ae-pastel-sage-text: #9FD3B4;
    --ae-pastel-sky-bg: #1D2732;
    --ae-pastel-sky-border: #2E4560;
    --ae-pastel-sky-text: #9CC3E8;
    --ae-pastel-lavender-bg: #25223A;
    --ae-pastel-lavender-border: #3E3765;
    --ae-pastel-lavender-text: #C3B8F0;
    --ae-pastel-butter-bg: #2D2818;
    --ae-pastel-butter-border: #4D4220;
    --ae-pastel-butter-text: #E9CF7F;
    --ae-pastel-peach-bg: #2F2219;
    --ae-pastel-peach-border: #553621;
    --ae-pastel-peach-text: #F0B48A;
    --ae-overlay: rgba(0, 0, 0, .65);
}

/* Dimensioni condivise: NON sono colori, non arrivano dall'app-config. */
:root {
    /* Corpo del testo di TUTTA l'app, una manopola sola: le misure di questo file e quelle del
       tema DevExpress Fluent (--dxds-font-size-*, --dxds-spacing-*) sono tutte in rem e nessuno
       dei due dichiara un font-size su html, quindi da qui si scalano insieme e restano in
       proporzione: nostro testo, editor, griglie, popup.
       106.25% = 17px col default del browser a 16px (richiesta del cliente, 28/09/2026: testi
       leggermente piu' grandi). In percentuale e non in px di proposito: cosi' resta relativa
       alla taglia scelta dall'utente nel browser, che un valore fisso azzererebbe.
       Alzandola ancora vanno riguardate le poche misure in px che contengono testo: le MinWidth
       delle colonne di griglia e le Width dei popup, che stanno nei .razor. */
    font-size: 106.25%;
    /* La topbar resta in px apposta: dentro ci stanno logo (24px), avatar e il bottone Esci, che
       anche a 17px arrivano a ~36px. La cornice non deve crescere insieme al testo. */
    --ae-topbar-h: 57px;
    /* Raggio di TUTTI i campi di input, nostri e DevExpress. Era ripetuto a mano in piu' punti:
       da qui resta un valore unico, altrimenti i campi divergono appena se ne aggiunge uno. */
    --ae-radius-input: 10px;
    /* Altezza dei campi scritti a mano, allineata a quella che DevExpress calcola per SizeMode
       Medium (impostato in Program.cs): padding-y 0.3125rem x2 + line-height 1.25rem + bordi 1px
       x2 = 32px. Serve perche' i nostri campi stanno in riga con gli editor DX (toolbar delle
       liste) e con il line-height naturale di Sora venivano ~2px piu' alti.
       In rem e non piu' i 32px di prima: DevExpress la calcola in rem, quindi la sua cresce col
       corpo del testo qui sopra e un valore fisso avrebbe disallineato la riga (a 17px la stessa
       formula da' ~33.9px). Col browser a 16px il risultato e' identico a prima.
       Se si cambia SizeMode va ricalcolata. */
    --ae-input-h: calc(1.875rem + 2px);
}

/* Font applicazione: Sora e' caricato in _Layout.cshtml ma va APPLICATO qui, altrimenti vince il
   font del tema DevExpress. margin 0: senza, attorno alle pagine a sfondo pieno (login) resta una
   cornice chiara (qui non c'e' Bootstrap a fare il reset). */
body {
    margin: 0;
    background: var(--ae-bg);
    color: var(--ae-text);
    font-family: 'Sora', system-ui, -apple-system, 'Segoe UI', sans-serif;
}

/* FocusOnNavigate (App.razor) porta il focus sull'h1: nascondiamo solo l'outline. */
h1:focus,
h1:focus-visible { outline: none; }

/* ============================================================
   Logo. I due SVG in wwwroot/img sono monocromatici e vengono usati come MASCHERA, non
   come <img>: il colore lo dipinge background-color con currentColor, quindi lo stesso file
   serve sia il rosso su fondo bianco sia il bianco sul pannello login. Con <img src> non
   funzionerebbe: l'SVG e' un documento isolato e il suo currentColor ricadrebbe sul nero.
   Percorsi in --ae-logo-* (app-config.json), non qui: il file resta senza riferimenti fissi.
   L'altezza la impone il chiamante; la larghezza la calcola aspect-ratio dal viewBox.
   L'elemento e' un div vuoto: chi lo usa deve mettere role="img" e aria-label, che
   sostituiscono l'alt dell'immagine.
   ============================================================ */
.ae-logo {
    background-color: currentColor;
    -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
    -webkit-mask-position: center;  mask-position: center;
    -webkit-mask-size: contain;     mask-size: contain;
    flex: 0 0 auto;
}
/* Lockup orizzontale: unica versione leggibile sotto i 50px. */
.ae-logo--wide {
    aspect-ratio: 960 / 108;
    -webkit-mask-image: var(--ae-logo-wide); mask-image: var(--ae-logo-wide);
}
/* Marchio quadrato: porta dentro il wordmark in micro-testo, quindi solo a taglie grandi. */
.ae-logo--mark {
    aspect-ratio: 1 / 1;
    -webkit-mask-image: var(--ae-logo-mark); mask-image: var(--ae-logo-mark);
}
/* Marchio a colori propri (logo.colored in app-config, BrandLogo.razor): un <img> vero, niente maschera e niente
   tinta. L'altezza la impone il chiamante come per .ae-logo; la larghezza segue le proporzioni del file. */
.ae-logo-img { display: block; flex: 0 0 auto; width: auto; object-fit: contain; }

/* --- Shell / topbar --- */
/* L'header e' fuori dal DxDrawer (MainLayout), quindi e' larga quanto la finestra e la sidebar
   comincia sotto. Il drawer non si dimensiona da solo: l'altezza gliela da' .ae-shell-body. */
.ae-shell { height: 100vh; }
.ae-shell-body { height: calc(100vh - var(--ae-topbar-h)); }
/* Due selettori: non e' garantito se la CssClass finisca sull'elemento radice del drawer o su un
   wrapper. Senza altezza il drawer collassa, non avendo piu' quella della finestra. */
.ae-shell-body .ae-drawer,
.ae-shell-body > .dxbl-drawer { height: 100%; }
.ae-topbar {
    display: flex; align-items: center; gap: .9rem;
    height: var(--ae-topbar-h);
    padding: 0 1.25rem;
    background-color: var(--ae-surface);
    color: var(--ae-text);
    border-bottom: 1px solid var(--ae-border);
}
/* Il logo occupa la colonna della sidebar (280px meno il padding sinistro della topbar), cosi'
   cade esattamente sopra il menu invece di galleggiare a sinistra. */
.ae-topbar-brand {
    display: flex; align-items: center;
    flex: 0 0 auto;
    width: calc(280px - 1.25rem);
    color: var(--ae-primary);
}
.ae-topbar-brand .ae-logo { height: 24px; }
/* Un lockup raster con payoff (es. 362x94) a 24px sarebbe illeggibile: 38px stanno nei 57px della topbar. */
.ae-topbar-brand .ae-logo-img { height: 38px; }
/* Tema scuro: logo bianco (30/10/2026). Il logo a maschera (AIR) prende il colore del contenitore, quindi basta il
   bianco. Il logo a colori e' un <img> e il PNG di Maganetti non ha trasparenza (sfondo bianco pieno): un filtro che
   imbianca tutto darebbe un rettangolo bianco. Qui grigio + inversione portano lo sfondo a nero e il marchio (rosso
   e nero) a chiaro, la luminosita' lo spinge fino al bianco e la fusione "screen" con la topbar fa sparire il nero:
   resta il marchio bianco sul fondo della topbar. Con un file dedicato bianco e trasparente non servirebbe. */
:root[data-theme="dark"] .ae-topbar-brand { color: var(--ae-primary-text); }
:root[data-theme="dark"] .ae-topbar-brand .ae-logo-img { filter: grayscale(1) invert(1) brightness(1.9); mix-blend-mode: screen; }
/* Il wrapper DevExpress .dxbl-drawer-content e' overflow:hidden e non scrolla: .ae-content e' il
   contenitore scrollabile (altezza piena meno la topbar). */
.ae-content {
    padding: 1.25rem 1.75rem 2rem;
    background: var(--ae-bg);
    height: calc(100vh - var(--ae-topbar-h));
    overflow-y: auto;
    box-sizing: border-box;
}
.ae-content-note { color: var(--ae-muted); }
/* Anteprima portale in scheda separata (PreviewLayout): senza topbar il contenuto prende tutta l'altezza */
.ae-content--standalone { height: 100vh; }

/* ============================================================
   Barre di scorrimento dei NOSTRI contenitori (menu laterale e area contenuto): quella di
   sistema e' grigia e larga, e stonava col resto.

   Due meccanismi perche' coprono browser diversi: scrollbar-width/scrollbar-color e' lo
   standard (Firefox da sempre, Chrome dalla 121), i ::-webkit-scrollbar-* servono ai WebKit
   piu' vecchi e sono l'unico modo per avere il thumb arrotondato. Dove valgono entrambi
   vincono i pseudo-elementi, quindi l'aspetto e' quello.

   Il bordo trasparente + background-clip: content-box e' il modo di dare aria al thumb:
   con width 10px e bordo 2px la parte colorata resta di 6px, staccata dal bordo.

   NON si applica su * o su body: sovrascrivere le scrollbar di tutta la pagina toccherebbe
   anche gli scroll viewer interni di DevExpress (la griglia ne ha uno), che hanno una loro
   resa. Se serve anche la' si aggiunge il selettore, consapevolmente.
   ============================================================ */
.ae-nav,
.ae-content,
.ae-popup-form--tall,
.ae-popup-table-wrap {
    scrollbar-width: thin;
    scrollbar-color: var(--ae-scrollbar-thumb) transparent;
}
.ae-nav::-webkit-scrollbar,
.ae-content::-webkit-scrollbar,
.ae-popup-form--tall::-webkit-scrollbar,
.ae-popup-table-wrap::-webkit-scrollbar { width: 10px; height: 10px; }
.ae-nav::-webkit-scrollbar-track,
.ae-content::-webkit-scrollbar-track,
.ae-popup-form--tall::-webkit-scrollbar-track,
.ae-popup-table-wrap::-webkit-scrollbar-track { background: transparent; }
.ae-nav::-webkit-scrollbar-thumb,
.ae-content::-webkit-scrollbar-thumb,
.ae-popup-form--tall::-webkit-scrollbar-thumb,
.ae-popup-table-wrap::-webkit-scrollbar-thumb {
    background-color: var(--ae-scrollbar-thumb);
    border-radius: 999px;
    border: 2px solid transparent;
    background-clip: content-box;
}
.ae-nav::-webkit-scrollbar-thumb:hover,
.ae-content::-webkit-scrollbar-thumb:hover,
.ae-popup-form--tall::-webkit-scrollbar-thumb:hover,
.ae-popup-table-wrap::-webkit-scrollbar-thumb:hover { background-color: var(--ae-scrollbar-thumb-hover); }
/* L'angolo tra le due barre resta trasparente, altrimenti ricompare un quadratino grigio. */
.ae-nav::-webkit-scrollbar-corner,
.ae-content::-webkit-scrollbar-corner,
.ae-popup-form--tall::-webkit-scrollbar-corner,
.ae-popup-table-wrap::-webkit-scrollbar-corner { background: transparent; }

/* --- Blocco utente + logout in topbar (push a destra) --- */
.ae-topbar-right { margin-left: auto; display: flex; align-items: center; gap: .9rem; }
.ae-topbar-user {
    display: inline-flex; align-items: center; gap: .5rem;
    font-size: .88rem; color: var(--ae-text);
}
.ae-topbar-avatar {
    display: inline-flex; align-items: center; justify-content: center;
    width: 30px; height: 30px; border-radius: 50%;
    background: var(--ae-primary); color: var(--ae-primary-text); font-size: .95rem;
}
.ae-topbar-right form { margin: 0; }
.ae-topbar-logout {
    display: inline-flex; align-items: center; gap: .45rem;
    background: none; border: 1px solid var(--ae-border); border-radius: .55rem; cursor: pointer;
    padding: .4rem .75rem;
    font: inherit; font-size: .85rem; font-weight: 500; color: var(--ae-text);
}
.ae-topbar-logout:hover { background: var(--ae-accent); color: var(--ae-primary); border-color: var(--ae-primary); }

/* --- Sidebar chiara. NavMenu vive nel RenderFragment di DxDrawer: stili globali obbligatori. --- */
/* Il pannello del DxDrawer mette un proprio riquadro (sfondo/bordi/ombra del tema DX) attorno al
   NavMenu: lo neutralizziamo e lasciamo un UNICO bordo destro. */
.ae-drawer .dxbl-drawer-panel {
    background-color: var(--ae-sidebar-bg);
    border: 0;
    border-right: 1px solid var(--ae-border);
    box-shadow: none;
}
/* Niente padding verticale del tema sul corpo: si sommava a quello di .ae-nav e lasciava una fascia bianca
   sotto l'ultima voce a menu scorso fino in fondo. Quello orizzontale resta, e' il margine delle voci. */
.ae-drawer .dxbl-drawer-body {
    background-color: var(--ae-sidebar-bg);
    border: 0;
    box-shadow: none;
    padding-top: 0;
    padding-bottom: 0;
}
.ae-drawer .dxbl-drawer-content { background-color: var(--ae-bg); }

/* Il padding in cima sostituisce lo spazio che dava l'area brand, ora spostata nella topbar. */
.ae-nav {
    background-color: var(--ae-sidebar-bg);
    height: 100%;
    width: 100%;
    padding: .85rem 0 .35rem;
    box-sizing: border-box;
    overflow-y: auto;
}
.ae-nav-list { list-style: none; padding: 0 .75rem; margin: 0; }
.ae-nav-link {
    display: flex; align-items: center; gap: .6rem;
    padding: .6rem .85rem;
    margin-bottom: .25rem;
    border-radius: .55rem;
    color: var(--ae-sidebar-text); text-decoration: none;
    font-weight: 500; font-size: .92rem;
}
.ae-nav-link:hover {
    background-color: var(--ae-accent);
    color: var(--ae-primary);
}
.ae-nav-link.active {
    background-color: var(--ae-sidebar-active);
    color: var(--ae-primary-text);
}

/* --- Pagina: header + card --- */
.ae-page-header { margin-bottom: 1.25rem; }
.ae-page-header h1 { margin: 0; font-size: 1.5rem; font-weight: 700; color: var(--ae-text); }
.ae-page-header p { margin: .25rem 0 0; color: var(--ae-muted); font-size: .92rem; }
.ae-page-header h2 { margin: 0; font-size: 1.3rem; font-weight: 700; color: var(--ae-text); }

.ae-card {
    background: var(--ae-surface);
    border: 1px solid var(--ae-border);
    border-radius: 12px;
    box-shadow: var(--ae-shadow-card);
    padding: 1.25rem 1.5rem;
    max-width: 480px;
}
.ae-card-title { margin: 0; color: var(--ae-muted); font-size: .85rem; font-weight: 600; }
.ae-card-value { margin: .25rem 0 1.25rem; font-size: 1.15rem; font-weight: 600; color: var(--ae-text); }
.ae-card form { margin: 0; }

.ae-btn-primary {
    display: inline-flex; align-items: center; gap: .5rem;
    border: 0; border-radius: 10px; cursor: pointer;
    background: var(--ae-primary); color: var(--ae-primary-text);
    font: inherit; font-weight: 600; font-size: .9rem;
    padding: .6rem 1rem;
}
.ae-btn-primary:hover { background: var(--ae-primary-hover); }

/* ============================================================
   Login. Stili GLOBALI perche' il wrapper della pagina e' nel LoginLayout
   (parent del componente): il CSS scoped non lo coprirebbe.
   ============================================================ */
.ae-login-page {
    min-height: 100vh;
    display: flex; align-items: center; justify-content: center;
    background-color: var(--ae-login-bg);
    padding: 24px;
    box-sizing: border-box;
}
.ae-login-wrapper { width: 100%; max-width: 760px; }

/* Card split: pannello brand a sinistra, form a destra */
/* La pagina non e' piu' scura: la card bianca ha bisogno del bordo, altrimenti su un
   fondo bianco appena velato di rosso i suoi margini non si leggono. */
.ae-login-card {
    display: flex;
    background: var(--ae-surface);
    border: 1px solid var(--ae-border);
    border-radius: 16px;
    overflow: hidden;
    box-shadow: var(--ae-shadow-login);
    min-height: 440px;
}
/* Pannello brand in tinta piena: la profondita' la danno i due cerchi bianchi a bassa
   opacita' (::before/::after), non un gradiente verso una seconda tinta di rosso. */
.ae-login-brand {
    flex: 0 0 42%;
    position: relative; overflow: hidden;
    background-color: var(--ae-primary);
    color: var(--ae-primary-text);
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    padding: 2rem; text-align: center;
}
.ae-login-brand::before,
.ae-login-brand::after {
    content: ""; position: absolute; border-radius: 50%;
    background: var(--ae-login-deco);
}
.ae-login-brand::before { width: 260px; height: 260px; top: -90px; left: -70px; }
.ae-login-brand::after { width: 200px; height: 200px; bottom: -70px; right: -60px; }
/* Marchio quadrato, bianco sul rosso: position/z-index lo tengono sopra i cerchi decorativi.
   150px sta comodo nei 255px utili del pannello (42% di 760 meno il padding). */
.ae-login-logo {
    width: 150px; height: 150px;
    color: var(--ae-primary-text);
    position: relative; z-index: 1;
}
/* Pannello chiaro per il marchio a colori (BrandLogo con logo.colored): un'immagine bicolore senza trasparenza non
   regge il fondo in tinta piena, quindi superficie bianca, cerchi decorativi in tinta tenue e bordo di separazione.
   Il marchio e' il lockup orizzontale a 210px, come nella versione impilata. */
.ae-login-brand--light {
    background-color: var(--ae-surface);
    color: var(--ae-primary);
    border-right: 1px solid var(--ae-border);
}
.ae-login-brand--light::before,
.ae-login-brand--light::after { background: var(--ae-accent); }
.ae-login-logo.ae-logo-img { width: 210px; max-width: 100%; height: auto; }

.ae-login-form { flex: 1; display: flex; flex-direction: column; padding: 2.1rem 2.3rem 1.3rem; }
.ae-login-eyebrow {
    color: var(--ae-primary); font-weight: 700;
    font-size: .72rem; letter-spacing: .14em; text-transform: uppercase;
}
.ae-login-form h1 { font-size: 1.4rem; font-weight: 700; margin: .35rem 0 .25rem; color: var(--ae-text); }
.ae-login-sub { color: var(--ae-muted); font-size: .88rem; margin: 0 0 1.35rem; }

.ae-form-group { margin-bottom: 1.05rem; }
.ae-form-group > label { display: block; font-size: .85rem; font-weight: 600; margin-bottom: .4rem; }
.ae-login-field {
    display: flex; align-items: center; gap: .55rem;
    border: 1px solid var(--ae-border);
    background: var(--ae-tint);
    border-radius: var(--ae-radius-input);
    padding: .5rem .8rem;
    color: var(--ae-muted);
}
.ae-login-field:focus-within { border-color: var(--ae-primary); background: var(--ae-surface); }
.ae-login-field input {
    flex: 1; border: 0; outline: none; background: transparent;
    font: inherit; font-size: .92rem; color: var(--ae-text);
}
.ae-login-field input::placeholder { color: var(--ae-muted); }

.ae-form-check { display: flex; align-items: center; gap: .5rem; font-size: .88rem; margin: .1rem 0 1.35rem; }
.ae-form-check input { accent-color: var(--ae-primary); width: 16px; height: 16px; }

.ae-login-btn {
    width: 100%; border: 0; border-radius: 10px; cursor: pointer;
    background: var(--ae-primary); color: var(--ae-primary-text);
    font: inherit; font-weight: 700; font-size: .95rem;
    padding: .7rem;
    display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
}
.ae-login-btn:hover { background: var(--ae-primary-hover); }

/* In una palette a una sola tinta l'errore non si distingue piu' per colore dagli accenti di
   marca: lo qualifica la barra piena a sinistra. Vale anche per .ae-form-error. */
.ae-login-error {
    background: var(--ae-danger-bg); color: var(--ae-danger-text);
    border-left: 3px solid var(--ae-primary);
    border-radius: 8px; padding: .6rem .8rem;
    font-size: .85rem; margin-bottom: 1rem;
}

.ae-login-footer {
    margin-top: auto; padding-top: 1.5rem;
    text-align: center; color: var(--ae-muted); font-size: .78rem;
}

@media (max-width: 760px) {
    .ae-login-card { flex-direction: column; min-height: 0; }
    .ae-login-brand { flex: 0 0 auto; padding: 1.6rem; }
    .ae-login-form { padding: 1.8rem 1.6rem 1.2rem; }
    /* Impilata, la fascia brand e' bassa e larga: il marchio quadrato la farebbe crescere in
       altezza rubando spazio al form, quindi qui si passa al lockup orizzontale. Vince
       sull'ae-logo--mark per ordine di dichiarazione, a pari specificita'. */
    .ae-login-logo.ae-logo {
        width: 210px; height: auto; aspect-ratio: 960 / 108;
        -webkit-mask-image: var(--ae-logo-wide); mask-image: var(--ae-logo-wide);
    }
    .ae-login-brand--light { border-right: 0; border-bottom: 1px solid var(--ae-border); }
}

/* ============================================================
   Pagina Error (Razor Page senza Blazor, Layout = null)
   ============================================================ */
.ae-error-body {
    margin: 0;
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--ae-bg);
    color: var(--ae-text);
    font-family: "Segoe UI", system-ui, sans-serif;
}
.ae-error-card {
    background: var(--ae-surface);
    border: 1px solid var(--ae-border);
    border-radius: 12px;
    box-shadow: var(--ae-shadow-card);
    padding: 32px 40px;
    max-width: 520px;
}
.ae-error-card h1 { margin: 0 0 12px; font-size: 1.4rem; color: var(--ae-primary); }
.ae-error-ref { color: var(--ae-muted); font-size: .9rem; }
.ae-error-link { color: var(--ae-primary); font-weight: 600; text-decoration: none; }
.ae-error-link:hover { color: var(--ae-primary-hover); }

/* ============================================================
   Liste, dettaglio e form (modulo Clienti). Globali perche' attraversano i template
   DevExpress (DxGrid CellDisplayTemplate, DxFormLayout) e il RenderFragment di PPKPaginator.
   Nessun colore hardcoded: le tinte semantiche dei badge (success/warning/info) hanno le loro
   variabili --ae-{success,warning,info}(-bg,-text), configurabili da app-config.json.
   ============================================================ */

/* Header con azione a destra (variante di .ae-page-header) */
.ae-page-header--row {
    display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem;
    flex-wrap: wrap;
}

/* Contenitore a piena larghezza per griglie e form (a differenza di .ae-card, senza max-width) */
.ae-panel {
    background: var(--ae-surface);
    border: 1px solid var(--ae-border);
    border-radius: 12px;
    box-shadow: var(--ae-shadow-card);
    padding: 1rem 1.25rem;
}
/* Pannello senza cornice, quando dentro ci sono gia' i riquadri dei gruppi del form (dati anagrafici del
   cliente, nuova bolla manuale, nuova importazione): la cornice esterna attorno a tutto faceva un doppio bordo. Restano header e contenuto, allineati
   al bordo della pagina come gli altri blocchi. */
.ae-panel--bare { background: none; border: 0; box-shadow: none; padding: 0; }
/* Card staccata dal blocco sopra quando la pagina non ha una colonna .ae-stack (dispositivo nel dettaglio utente,
   sotto i gruppi senza cornice). */
.ae-panel--detached { margin-top: 1.5rem; }
/* Colonna di blocchi di pagina (card, banner, errori) staccati fra loro: lo spazio e' del contenitore,
   non un margine su .ae-panel, che toccherebbe tutte le pagine dove il pannello sta da solo.
   Stesso passo di .ae-tab-panel. */
.ae-stack { display: flex; flex-direction: column; gap: 1rem; }
/* L'errore porta un suo margin-bottom per le pagine senza colonna: qui lo spazio lo da' gia' il gap. */
.ae-stack > .ae-form-error { margin-bottom: 0; }
/* I DxFormLayoutGroup ("Persona", "Anagrafica", ...) sono i riquadri DENTRO il pannello:
   DevExpress li disegna con un raggio piccolo e un bordo neutro, e stonavano col contenitore,
   che e' tondo a 12px e con il bordo rosso tenue. Qui prendono gli stessi due valori.
   Le variabili le dichiara il tema su .dxbl-fl, cioe' (0,1,0): questo selettore sta a (0,2,0) e
   vince, e passando dal .ae-panel non dipende da dove finisca la CssClass del componente. */
.ae-panel .dxbl-fl,
.ae-popup-form .dxbl-fl {
    --dxbl-fl-group-border-radius: 12px;
    --dxbl-fl-group-border-color: var(--ae-border);
}

/* ============================================================
   Editor DevExpress (DxTextBox, DxComboBox, DxDateEdit, DxMemo...) allineati ai campi scritti
   a mano (.ae-login-field, .ae-list-search): stesso raggio, stesso bordo tenue, rosso al focus.
   Senza questo i form avevano campi con angoli e bordi diversi dai nostri, nella stessa pagina.

   Il tema dichiara queste variabili su .dxbl-text-edit, cioe' (0,1,0): i selettori qui sono a
   (0,2,0) e vincono per specificita', non per ordine. Serve, perche' site.css e' caricato PRIMA
   del tema (i suoi <link> li emette DxResourceManager in App.razor, quindi nel <body>).

   Si sovrascrive --dxbl-text-edit-border-radius e non le tre varianti di taglia (-s/-m/-l):
   quella base e' l'unica consumata e di suo punta a -radius-m.

   Fuori da qui restano checkbox e radio, che sono un'altra famiglia di variabili
   (--dxbl-checkbox-*): se servono anche quelli si allineano a parte.
   ============================================================ */
.ae-panel .dxbl-text-edit,
.ae-popup-form .dxbl-text-edit,
.ae-list-toolbar .dxbl-text-edit {
    --dxbl-text-edit-bg: var(--ae-surface);
    --dxbl-text-edit-color: var(--ae-text);
    --dxbl-text-edit-border-color: var(--ae-border);
    --dxbl-text-edit-border-hovered-color: var(--ae-primary);
    --dxbl-text-edit-border-active-color: var(--ae-primary);
    --dxbl-text-edit-border-radius: var(--ae-radius-input);

    /* La riga scura sotto il campo e' l'underline di Fluent: un bordo inferiore a se', che il tema
       colora di un neutro scuro (.dxbl-text-edit fa border-bottom-color: var(--...-underline-color)).
       Non si mette a transparent, che lascerebbe il riquadro aperto in basso: prende lo stesso
       colore degli altri tre lati, cosi' il campo torna un bordo uniforme come i nostri.
       L'altezza a fuoco la riportiamo allo spessore del bordo: Fluent la raddoppia per far
       "crescere" l'underline, effetto che i nostri campi non hanno. */
    --dxbl-text-edit-underline-color: var(--ae-border);
    --dxbl-text-edit-underline-hovered-color: var(--ae-primary);
    --dxbl-text-edit-underline-focused-color: var(--ae-primary);
    --dxbl-text-edit-underline-active-color: var(--ae-primary);
    --dxbl-text-edit-underline-focused-height: var(--dxbl-text-edit-border-width);
}

/* ============================================================
   Bottoni DevExpress Secondary (Annulla, Torna alla lista, Aggiungi sede...): sono i bottoni a
   fondo bianco, e il tema li borda di grigio neutro. Ora prendono il bordo rosso tenue e
   l'hover degli altri nostri bottoni bianchi (.ae-topbar-logout, .ae-paginator-btn), che quel
   trattamento lo avevano gia'.

   Qui l'override e' GLOBALE e non scoped a un contenitore, al contrario di griglie ed editor:
   i bottoni dei DxPopup sono renderizzati fuori da .ae-content, quindi un selettore per
   contenitore ne mancherebbe una parte (Annulla e Salva di ogni popup).
   Il tema li dichiara su .dxbl-btn, cioe' (0,1,0): serve il :root davanti per arrivare a
   (0,2,0), perche' site.css e' caricato prima dei <link> del tema e a pari specificita'
   perderebbe.

   Qui non c'entrano i RenderStyleMode.Outline (Disabilita/Riabilita in UserDetailPage): sono
   famiglie a se'. "Riabilita" era Success e il verde stava fuori palette: dal 28/09/2026 e'
   Primary, che segue --dxbl-accent-color (= --ae-primary) e quindi esce rosso senza CSS. In tutto
   il Backoffice non resta un solo bottone Success. Restano i Danger delle azioni distruttive
   (Elimina, Disabilita): rossi anche loro, ma del rosso semantico del tema, non di --ae-primary,
   perche' l'accent le scale semantiche non le tocca. Da decidere a parte se allinearli.
   ============================================================ */
:root .dxbl-btn {
    --dxbl-btn-secondary-bg: var(--ae-surface);
    --dxbl-btn-secondary-color: var(--ae-text);
    --dxbl-btn-secondary-border-color: var(--ae-border);
    --dxbl-btn-secondary-hover-bg: var(--ae-accent);
    --dxbl-btn-secondary-hover-border-color: var(--ae-primary);
    --dxbl-btn-secondary-active-bg: var(--ae-accent);
    --dxbl-btn-secondary-active-border-color: var(--ae-primary);
}

/* ============================================================
   Bottone Primary in modalita' Outline ("Riabilita" in UserDetailPage, oggi l'unico).

   Il colore della famiglia NON basta. Nel tema Fluent tutte le varianti outline
   (.dxbl-btn.dxbl-btn-outline-*) mappano --dxbl-btn-color e --dxbl-btn-border-color sulle
   --dxbl-btn-outline-*, che sono NEUTRE e uguali per tutti: a riposo un outline e' grigio con
   testo scuro, qualunque sia la famiglia, e il colore compare solo su hover e active
   (--dxbl-btn-outline-primary-hover-color / -active-color, le uniche due dichiarate).
   Misurato a schermo: outline-primary e outline-success a riposo danno gli stessi valori.
   Quindi un outline-primary "in palette" bisogna dipingerlo qui.

   Il trattamento e' quello degli altri nostri bottoni bianchi (Secondary qui sopra,
   .ae-topbar-logout, .ae-paginator-btn): bordo rosso tenue a riposo, hover su --ae-accent col
   bordo pieno. In piu' il testo e' rosso gia' a riposo: e' un bottone d'azione, non un Annulla,
   e senza quello di "rosso" non avrebbe niente finche' non ci passi sopra.

   Si scrivono le --dxbl-btn-* finali e non le --dxbl-btn-outline-*: il tema mappa le prime sulle
   seconde in .dxbl-btn.dxbl-btn-outline-primary, cioe' (0,2,0); questo selettore sta a (0,3,0),
   quindi vince a prescindere dall'ordine dei <link>. Se un domani nascono altri outline-primary,
   ereditano da qui.
   ============================================================ */
:root .dxbl-btn.dxbl-btn-outline-primary {
    --dxbl-btn-color: var(--ae-primary);
    --dxbl-btn-border-color: var(--ae-border);
    --dxbl-btn-hover-color: var(--ae-primary);
    --dxbl-btn-hover-bg: var(--ae-accent);
    --dxbl-btn-hover-border-color: var(--ae-primary);
    --dxbl-btn-active-color: var(--ae-primary-hover);
    --dxbl-btn-active-bg: var(--ae-accent);
    --dxbl-btn-active-border-color: var(--ae-primary);
}

/* --- Toolbar lista: ricerca nativa + filtri DevExpress --- */
.ae-list-toolbar {
    display: flex; align-items: center; gap: .75rem; flex-wrap: wrap;
    margin-bottom: 1rem;
}
/* Altezza fissa e non da padding: sta in riga con i filtri DevExpress, che hanno un'altezza
   calcolata dal tema. Con il solo padding il line-height di Sora la faceva crescere di ~2px. */
.ae-list-search {
    flex: 1 1 280px;
    display: flex; align-items: center; gap: .55rem;
    border: 1px solid var(--ae-border);
    background: var(--ae-surface);
    border-radius: var(--ae-radius-input);
    height: var(--ae-input-h);
    padding: 0 .8rem;
    box-sizing: border-box;
    color: var(--ae-muted);
}
.ae-list-search:focus-within { border-color: var(--ae-primary); }
.ae-list-search input {
    flex: 1; border: 0; outline: none; background: transparent;
    font: inherit; font-size: .875rem; color: var(--ae-text);
}
.ae-list-search input::placeholder { color: var(--ae-muted); }
.ae-list-filter { flex: 0 0 200px; }

/* ============================================================
   Varianti strette, per le toolbar affollate. Oggi le usa solo Spedizioni, che di filtri ne ha
   cinque o sei e li vuole su una riga sola (01/10/2026).

   Modificatori e non valori nuovi sui selettori base: .ae-list-search e .ae-list-filter stanno in
   tutte le liste, e stringerli la' dove i filtri sono due o tre li farebbe sembrare schiacciati
   senza che nessuno lo abbia chiesto.

   Le basi sono tarate perche' la SOMMA dei sei controlli piu' i gap stia nei ~1046px che restano
   su uno schermo da 1366 (meno la sidebar e il padding del contenuto): 977 + 64 di gap. Non e'
   pignoleria, e' l'unico modo di ottenere la riga sola. In un contenitore flex-wrap le righe si
   formano sulle BASI dichiarate e solo dopo, dentro la riga, agisce flex-shrink: un campo che su
   carta "potrebbe stringersi" va a capo lo stesso. Misurato: con basi piu' larghe i sei filtri
   andavano a capo fino a 1600px di schermo.

   Il flex-wrap resta come rete: sotto quella larghezza (finestra a meta' schermo, portatili
   piccoli) la riga si spezza invece di sbordare. E' il ripiego, non il funzionamento normale.

   La riga occupa SEMPRE tutta la larghezza: lo spazio che avanza se lo prendono la ricerca e il
   selettore cliente, gli unici due che da larghi ci guadagnano davvero (testo libero, nomi lunghi).
   Tendine e date restano alla loro misura: una data e' gg/mm/aaaa a qualunque risoluzione, allargarla
   e' solo spazio sprecato. Niente max-width sui due elastici, altrimenti oltre una certa larghezza
   smettono di crescere e in coda alla riga resta un buco.
   ============================================================ */
.ae-list-search--compact { flex: 1 1 10.5rem; min-width: 0; }
/* Una tendina di stati o di mandanti: l'etichetta piu' lunga ("Tutti i mandanti") sta in meno di
   9.75rem, i 200px della base erano tarati su liste con tre filtri in croce. */
.ae-list-filter--narrow { flex: 0 1 9.75rem; }
/* Una data sono dieci caratteri piu' calendario e cancelletto: 200px erano lo spazio di una
   tendina, non di un gg/mm/aaaa. */
.ae-list-filter--date { flex: 0 1 8.25rem; }
/* Stati a selezione multipla: i tag vanno a capo dentro il campo, la base resta quella di una tendina
   un po' piu' larga per far stare almeno un tag col suo cancelletto. */
.ae-list-filter--tags { flex: 0 1 12rem; min-width: 0; }
/* Il flag "Mostra annullate" vuole la sua etichetta su una riga, non una base da tendina.
   Dal 30/10/2026 ha la forma degli altri filtri (era un checkbox nudo in mezzo a campi riquadrati): stessa
   altezza della riga (stretch), bordo tenue e raggio dei campi, e da acceso il rosso tenue come i bottoni attivi
   dell'header. */
.ae-list-filter--check {
    flex: 0 0 auto; align-self: stretch; white-space: nowrap;
    display: inline-flex; align-items: center;
    padding: 0 .8rem;
    background: var(--ae-surface);
    border: 1px solid var(--ae-border); border-radius: var(--ae-radius-input);
    cursor: pointer;
}
.ae-list-filter--check:hover { border-color: var(--ae-primary); }
.ae-list-filter--check:has(input:checked) { background: var(--ae-accent); border-color: var(--ae-primary); color: var(--ae-primary); }
/* Il selettore cliente nella toolbar: la sua 1 1 320px e' tarata sulla riga della nuova bolla,
   dove sta da solo. Qui divide con altri cinque. Selettore per contenitore, cosi' la bolla non
   viene toccata. */
.ae-list-toolbar .ae-customer-picker { flex: 1 1 11rem; min-width: 0; }

/* Toolbar a griglia di quattro colonne (listini di vendita, 02/11/2026): due righe di quattro filtri incolonnati.
   La prima colonna (ricerca per nome esatto sopra, cliente sotto) e' larga due volte e mezza le altre: sono i due
   campi di testo libero, con nomi lunghi; le altre tre (tendine, date, Esporta) restano uguali fra loro.
   Esporta occupa la sua cella ma resta della sua misura, a destra. Sotto i 1100px le colonne diventano due. */
.ae-list-toolbar--grid4 { display: grid; grid-template-columns: minmax(0, 2.5fr) repeat(3, minmax(0, 1fr)); }
.ae-list-toolbar--grid4 > * { min-width: 0; width: 100%; }
.ae-list-toolbar--grid4 > .ae-export { width: auto; justify-self: end; }
@media (max-width: 1100px) {
    .ae-list-toolbar--grid4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* Toolbar su una riga sola (listini dei corrieri, 02/11/2026): sei filtri piu' Esporta in fondo. Senza a capo, con
   le basi strette delle varianti --narrow e --date e tutto restringibile: con le basi normali (200px a tendina)
   la riga non ci stava e andava a capo. Esporta resta della sua misura e si spinge in fondo. */
.ae-list-toolbar--one-row { flex-wrap: nowrap; }
.ae-list-toolbar--one-row > .ae-list-search { flex: 1 1 10.5rem; min-width: 0; }
.ae-list-toolbar--one-row > .ae-list-filter { flex: 0 1 9.75rem; min-width: 0; }
.ae-list-toolbar--one-row > .ae-list-filter--date { flex: 0 1 8.25rem; }
.ae-list-toolbar--one-row > .ae-export { flex: 0 0 auto; margin-left: auto; }

/* Colonna chiave della griglia = link al dettaglio */
.ae-list-link {
    color: var(--ae-primary); font-weight: 600; text-decoration: none;
    display: inline-flex; align-items: center; gap: .35rem;
}
.ae-list-link:hover { color: var(--ae-primary-hover); text-decoration: underline; }
/* Variante neutra: il codice in lista e' un identificativo, non un titolo di riga, quindi peso
   normale e colore del testo invece del rosso. Il peso 600 e il rosso restano sulle altre
   celle-link (nomi, denominazioni) e sui link "Torna a...".
   Deve stare DOPO .ae-list-link: pari specificita' (0,1,0), vince l'ultima dichiarata.
   L'hover invece resta rosso e sottolineato, perche' .ae-list-link:hover e' a (0,1,1) e batte
   questa regola: cosi' la cella non sembra un testo morto e si capisce che e' cliccabile. */
.ae-list-link--plain { font-weight: 400; color: var(--ae-text); }
/* Variante a peso normale ma rossa: resta riconoscibile come link, senza grassetto. Lista Destinatari
   (mittente e destinatario, richiesta del 30/10/2026): due colonne di nomi affiancate in grassetto pesavano. */
.ae-list-link--regular { font-weight: 400; }
/* Colonna centrata (classe messa da CustomizeElement): cella e titolo. Il testo del titolo sta in uno <span> che il
   tema allarga a tutta la cella (.dxbl-grid-header-content span:first-child { flex-grow: 1 }), quindi si centra il
   testo dentro lo span: un justify-content sul contenitore non sposterebbe niente. */
.ae-grid .dxbl-grid-table > tbody > tr > td.ae-cell-center { text-align: center; }
.ae-grid .dxbl-grid-table > thead > tr > th.ae-cell-center > .dxbl-grid-header-content > span { text-align: center; }
/* Cella di griglia su piu' righe: testo sopra, badge sotto, allineati a sinistra (badge non stirati). */
.ae-cell-stack { display: flex; flex-direction: column; align-items: flex-start; gap: .25rem; }

.ae-empty-state {
    padding: 2rem 1rem; text-align: center;
    color: var(--ae-muted); font-size: .92rem;
}

/* --- Badge di stato (StatusBadge): suffisso = DevExpressColor del ValueObject --- */
.ae-badge {
    display: inline-flex; align-items: center;
    padding: .18rem .6rem;
    border-radius: 999px;
    font-size: .75rem; font-weight: 600; line-height: 1.3;
    white-space: nowrap;
}
.ae-badge-primary {
    background: color-mix(in srgb, var(--ae-primary) 14%, var(--ae-surface));
    color: var(--ae-primary);
}
/* Attivo = success, Sospeso = warning, In approvazione = info, Cessato = secondary, Rifiutato = danger */
.ae-badge-success {
    background: var(--ae-success-bg);
    color: var(--ae-success-text);
}
.ae-badge-warning {
    background: var(--ae-warning-bg);
    color: var(--ae-warning-text);
}
.ae-badge-info {
    background: var(--ae-info-bg);
    color: var(--ae-info-text);
}
/* Il bordo pieno e' l'unico segnale che resta per separare "danger" da "info": in monocromia
   i due riempimenti (.12 e .08 del primary) sono quasi indistinguibili. */
.ae-badge-danger {
    background: var(--ae-danger-bg);
    color: var(--ae-danger-text);
    border: 1px solid var(--ae-primary);
}
.ae-badge-secondary {
    background: color-mix(in srgb, var(--ae-secondary) 12%, var(--ae-surface));
    color: var(--ae-secondary);
}
/* Tipo di listino (RateCardRow.TypeBadgeColor): tinte che nessuno stato usa, e un bordo pieno che le
   fa leggere come un'etichetta di categoria e non come un'altra pillola di stato accanto a
   "Pubblicato" e "In vigore". */
.ae-badge-sell {
    background: var(--ae-sell-bg);
    color: var(--ae-sell-text);
    border: 1px solid color-mix(in srgb, var(--ae-sell) 45%, var(--ae-surface));
}
.ae-badge-cost {
    background: var(--ae-cost-bg);
    color: var(--ae-cost-text);
    border: 1px solid color-mix(in srgb, var(--ae-cost) 45%, var(--ae-surface));
}

/* --- Paginatore (PPKPaginator) --- */
.ae-paginator {
    display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
    margin-top: 1rem;
    font-size: .88rem; color: var(--ae-text);
}
.ae-paginator-nav { display: flex; align-items: center; gap: .3rem; flex-wrap: wrap; }
/* Quadrati alti come gli input, non 32px fissi: stanno nella stessa barra di .ae-paginator-select,
   che prende --ae-input-h. Ora che quella e' in rem, un valore fisso qui avrebbe fatto divergere i
   due lati del paginatore al primo ritocco del corpo del testo. */
.ae-paginator-btn {
    min-width: var(--ae-input-h); height: var(--ae-input-h); padding: 0 .5rem;
    display: inline-flex; align-items: center; justify-content: center;
    border: 1px solid var(--ae-border); border-radius: 8px;
    background: var(--ae-surface); color: var(--ae-text);
    font: inherit; font-size: .85rem; cursor: pointer;
}
.ae-paginator-btn:hover:not(:disabled) { background: var(--ae-accent); border-color: var(--ae-primary); color: var(--ae-primary); }
.ae-paginator-btn:disabled { cursor: not-allowed; opacity: .45; }
.ae-paginator-btn--arrow { padding: 0 .4rem; }
/* Lo stato attivo e' informativo, non disabilitato: annulla l'opacita' del :disabled */
.ae-paginator-btn--active,
.ae-paginator-btn--active:disabled {
    background: var(--ae-primary); color: var(--ae-primary-text); border-color: var(--ae-primary);
    cursor: default; opacity: 1;
}
.ae-paginator-ellipsis { min-width: 20px; text-align: center; color: var(--ae-muted); user-select: none; padding: 0 .2rem; }
.ae-paginator-size { display: flex; align-items: center; gap: .5rem; }
.ae-paginator-size-label { color: var(--ae-muted); font-size: .85rem; margin: 0; }
.ae-paginator-select {
    height: var(--ae-input-h); padding: 0 .6rem;
    border: 1px solid var(--ae-border); border-radius: var(--ae-radius-input);
    background: var(--ae-surface); color: var(--ae-text);
    font: inherit; font-size: .85rem;
}
.ae-paginator-select:focus { outline: none; border-color: var(--ae-primary); }
.ae-paginator-info { color: var(--ae-muted); font-size: .85rem; }

/* --- Form --- */
.ae-form-actions {
    display: flex; align-items: center; gap: .6rem;
    margin-top: 1.25rem; padding-top: 1rem;
    border-top: 1px solid var(--ae-border);
}
.ae-form-error {
    display: flex; align-items: center; gap: .5rem;
    background: var(--ae-danger-bg); color: var(--ae-danger-text);
    border-left: 3px solid var(--ae-primary);
    border-radius: 8px; padding: .65rem .9rem;
    font-size: .88rem; margin-bottom: 1rem;
}
.ae-form-hint { color: var(--ae-muted); font-size: .82rem; }

/* --- Dettaglio in sola lettura --- */
.ae-detail-title { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; }
.ae-detail-code { font-family: ui-monospace, 'Cascadia Mono', Consolas, monospace; font-size: .9em; }
.ae-detail-value { display: block; padding: .3rem 0; font-size: .92rem; color: var(--ae-text); min-height: 1.4rem; }
.ae-detail-notes { white-space: pre-wrap; }
/* Tabelle HTML del dettaglio (Organizzazione, tab del cliente, portale, popup): stesso
   trattamento delle liste DxGrid con .ae-grid, cosi' tutte le tabelle dell'app si leggono uguali.
   Nessuna linea tra le righe: le separa lo spazio, e le intestazioni sono in rosso con una riga
   di 2px sotto. I valori (padding, corpo, spaziatura) sono gli stessi delle variabili
   --dxbl-grid-* dichiarate su .ae-grid: se si cambiano la', vanno cambiati anche qui. */
.ae-detail-table { width: 100%; border-collapse: collapse; font-size: .9rem; }
.ae-detail-table th {
    text-align: left;
    /* Stessi valori delle intestazioni del DxGrid: le due famiglie di tabelle devono restare
       indistinguibili, quindi se si cambia la' va cambiato anche qui. */
    font-size: .7rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
    color: var(--ae-primary);
    padding: .68rem .8rem;
    border-bottom: 2px solid var(--ae-border);
}
.ae-detail-table td { padding: .68rem .8rem; color: var(--ae-text); }
.ae-detail-table tbody tr:hover td { background: var(--ae-tint); }
.ae-detail-table-row--inactive td { color: var(--ae-muted); text-decoration: line-through; }

/* --- Header di pagina con piu' azioni a destra (Modifica/Salva + Torna) --- */
.ae-page-header-actions { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }

/* --- Sezioni del dettaglio con azione (Sedi, Recapiti): titolo a sinistra, bottone a destra --- */
.ae-section-header {
    display: flex; align-items: center; justify-content: space-between; gap: 1rem;
    margin-bottom: .6rem;
}
.ae-section-title { font-weight: 600; font-size: .95rem; color: var(--ae-text); }

/* Azioni per riga nelle tabelle di dettaglio: bottoni testuali, stile del link al dettaglio.
   Vale anche per gli <a> (anteprima portale in scheda nuova): stesso aspetto del <button> */
.ae-row-actions { text-align: right; white-space: nowrap; }
/* Dal 21/10/2026 piu' grandi (richiesta del cliente: "Modifica", "Aggiungi" e simili erano troppo piccoli): testo
   come il corpo e un bersaglio di click vero, con lo sfondo tenue all'hover al posto della sola sottolineatura. */
.ae-row-action {
    display: inline-flex; align-items: center; gap: .3rem; text-decoration: none;
    background: none; border: 0; border-radius: .45rem; cursor: pointer; padding: .3rem .55rem;
    font: inherit; font-size: .95rem; font-weight: 600; line-height: 1.2;
    color: var(--ae-primary);
}
.ae-row-action:hover { color: var(--ae-primary-hover); background: var(--ae-accent); text-decoration: none; }
.ae-row-action:disabled { cursor: not-allowed; opacity: .45; text-decoration: none; background: none; }
.ae-row-action--danger { color: var(--ae-danger-text); }
.ae-row-action--danger:hover { color: var(--ae-danger-text); background: var(--ae-danger-bg); }
/* Azione di riga con la sola icona: senza testo serve un bersaglio un po' piu' grande, e chi la usa
   deve mettere title e aria-label, che diventano l'unico modo per sapere cosa fa. */
.ae-row-action--icon {
    padding: .35rem .45rem;
    font-size: 1.1rem;
    line-height: 1;
}
.ae-row-action--icon:hover { text-decoration: none; }
/* La riga disattivata barra il testo delle celle: le azioni restano leggibili */
.ae-detail-table-row--inactive td.ae-row-actions { text-decoration: none; }

/* --- Popup (DxPopup) con form o conferma --- */
.ae-popup-form { padding: .25rem .25rem 0; }
/* Gruppi del form affiancati (due per riga) alla stessa altezza: la riga del FormLayout e' gia' flex e stira le
   colonne, ma il tema mette .dxbl-fl-group a display:block e il riquadro (.dxbl-group) resta alto quanto il suo
   contenuto. Qui la colonna diventa flex e il riquadro la riempie. (0,3,0): il tema sta a (0,2,0) ed e' caricato
   dopo site.css. */
.ae-form-pairs .dxbl-fl .dxbl-fl-group { display: flex; flex-direction: column; }
.ae-form-pairs .dxbl-fl .dxbl-fl-group > .dxbl-group { flex: 1 1 auto; }
/* Valori in mezza colonna (l'email nel dettaglio utente): vanno a capo invece di uscire dal riquadro. */
.ae-form-pairs .ae-detail-value { overflow-wrap: anywhere; }

/* Modale alta (dettaglio listino): il corpo puo' contenere quattro sezioni di tabelle e superare
   l'altezza dello schermo. Il DxPopup non limita il proprio corpo, quindi il contenuto usciva
   dalla finestra: qui l'altezza e' legata a quella del viewport e lo scorrimento resta DENTRO la
   modale, con l'intestazione sempre visibile.
   E' un modificatore e non una regola su .ae-popup-form: le altre popup dell'app sono form corti e
   non devono guadagnare una barra di scorrimento senza motivo. */
.ae-popup-form--tall {
    max-height: calc(100vh - 13rem);
    overflow-y: auto;
    padding-right: .6rem;
}

/* Sezioni della modale: un filo e piu' aria le staccano, invece di susseguirsi appiccicate.
   Il blocco dei dati in testa non e' un .ae-section-title, quindi la prima riga cade dove serve,
   fra i dati e la prima sezione.
   Vale anche per .ae-detail-sections, il pannello del dettaglio listino: quelle sezioni erano
   dentro questa modale fino al 23/09/2026 e devono continuare a staccarsi allo stesso modo. */
.ae-popup-form .ae-section-title,
.ae-detail-sections .ae-section-title {
    margin-top: 1.5rem;
    padding-top: 1.1rem;
    border-top: 1px solid var(--ae-border);
}
/* Due editor affiancati in un solo item del form (chiave AS400 filiale + cliente) */
.ae-inline-pair { display: flex; gap: .5rem; }
.ae-inline-pair > * { flex: 1 1 0; min-width: 0; }
/* Stesso trattamento nel pannello del dettaglio listino: i paragrafi (titoli di sezione e note)
   li distanzia la regola qui sopra, non il margine di default del browser. */
.ae-popup-form p,
.ae-detail-sections p { margin: 0; color: var(--ae-text); font-size: .92rem; }
/* Dettaglio listino e dettaglio aree commerciali (02/11/2026): pannello senza cornice esterna (.ae-panel--bare) e
   piu' aria fra gli elementi, che con i paragrafi a margine zero stavano attaccati: sezioni piu' staccate fra loro,
   spazio fra il titolo di una sezione e la sua nota, e fra la nota e quello che segue. Selettori a due classi per
   vincere su .ae-detail-sections p qui sopra. */
.ae-detail-sections .ae-section-title { margin-top: 2rem; padding-top: 1.5rem; margin-bottom: .75rem; }
.ae-detail-sections p.ae-form-hint { margin-bottom: 1rem; line-height: 1.55; }
.ae-popup-actions { display: flex; align-items: center; justify-content: flex-end; gap: .6rem; width: 100%; }

/* ============================================================
   Griglia (DxGrid con CssClass="ae-grid"): clienti, utenti, rubrica.
   Senza reticolo, intestazioni rosse, righe separate dallo spazio.

   Le variabili DX si dichiarano SULL'ELEMENTO, non su :root: i <link> del tema li emette
   DxResourceManager in App.razor, quindi core.min.css arriva nel <body> DOPO questo file e
   un nostro :root perderebbe a pari specificita' (e' lo stesso inciampo dei bottoni Primary).
   core.min.css le dichiara su .dxbl-grid, cioe' (0,1,0): i selettori qui sotto stanno a (0,2,0)
   e vincono.

   DUE selettori e non uno perche' non e' garantito DOVE DevExpress applichi la CssClass: se la
   mette sullo stesso elemento che porta .dxbl-grid vale il primo, se la mette su un wrapper che
   lo contiene vale il secondo. Con il solo .ae-grid non basterebbe: le variabili si erediterebbe-
   ro, ma la dichiarazione di core su .dxbl-grid e' piu' vicina all'elemento e vincerebbe lei.

   Il reticolo NON si toglie da qui: lo fa GridlinesVisible="Gridlines.None" sul componente, che
   e' l'API supportata e non dipende da questi selettori. Le liste che non la impostano tengono
   le linee: e' un attributo per pagina.
   ============================================================ */
.ae-grid.dxbl-grid,
.ae-grid .dxbl-grid {
    border: 0;

    /* Il bianco lo mette il .ae-panel che la contiene: la griglia resta trasparente. */
    --dxbl-grid-bg: transparent;
    --dxbl-grid-row-bg: transparent;

    /* Intestazioni in rosso, senza fondo grigio. */
    --dxbl-grid-header-bg: transparent;
    --dxbl-grid-header-color: var(--ae-primary);
    --dxbl-grid-header-font-weight: 700;

    /* Resta come ripiego per le superfici DX che non dipingiamo noi (es. header ordinabile):
       l'hover di riga lo fa la regola sul td piu' sotto, allineata a --ae-tint. */
    --dxbl-grid-hover-bg: var(--ae-tint);
    --dxbl-grid-hover-color: var(--ae-text);

    /* Senza reticolo e' lo spazio che separa le righe: senza questo respiro si impastano. */
    --dxbl-grid-text-cell-padding-y: .68rem;
    --dxbl-grid-text-cell-padding-x: .8rem;
}
/* Riga cliccabile (classe messa da CustomizeElement nel code-behind della lista): a DevExpress
   manca solo il cursore, l'evidenziazione la fa la regola di hover qui sotto. */
.ae-grid .ae-grid-row-link { cursor: pointer; }

/* Riga che e' un link (oggi i listini: il dettaglio e' la pagina /listini/{id} e si apre in una
   scheda nuova). L'ancora sta nella prima cella ma si stende su tutta la riga, cosi' si clicca dove
   capita, come quando il click lo gestiva RowClick.
   La RIGA e' il blocco contenitore dell'ancora: le celle non devono essere posizionate, altrimenti
   il link si fermerebbe dentro la propria cella. */
.ae-grid .ae-grid-row-link { position: relative; }
.ae-grid-row-stretch { position: absolute; inset: 0; z-index: 1; }

/* I link veri dentro la riga (il cliente) vanno tenuti sopra a quello steso, che altrimenti li
   copre: senza position lo z-index non conta, quindi servono tutti e due. */
.ae-grid-link-above { position: relative; z-index: 2; }

/* Colonna si'/no di una lista (oggi "Predefinito" nei listini): il segno positivo prende il rosso
   di marca, il negativo resta neutro e smorzato, cosi' la colonna si legge a colpo d'occhio come
   una sequenza di segni accesi e spenti invece che come due icone di pari peso.
   NON si usano verde e rosso semantici: qui non c'e' un esito buono o cattivo, solo un attributo. */
.ae-flag { font-size: .95rem; line-height: 1; }
.ae-flag--on { color: var(--ae-primary); }
.ae-flag--off { color: var(--ae-muted); opacity: .5; }

/* Hover di riga dipinto DA NOI e non lasciato a --dxbl-grid-hover-bg. Due motivi:
   1. la variabile arriva a destinazione solo se la CssClass "ae-grid" finisce sull'elemento che
      porta .dxbl-grid, cosa non garantita: se cade su un wrapper, l'hover resta quello DX;
   2. cosi' e' identico a quello di Organizzazione, che e' una tabella HTML e usa la stessa forma
      (.ae-detail-table tbody tr:hover td).
   Si dipinge il td, non il tr: la cella copre lo sfondo della riga, quindi vince sulla regola del
   tema senza dover rincorrere la sua specificita'. Vale per tutte le liste con .ae-grid.
   Il token e' --ae-tint, lo stesso di Organizzazione: le tre liste devono evidenziare allo stesso
   modo, e Organizzazione e' il riferimento scelto. */
.ae-grid .dxbl-grid-table > tbody > tr:hover > td { background-color: var(--ae-tint); }

/* Qui stanno solo i tratti tipografici che le variabili DX non espongono. Colore e peso NON si
   mettono piu' qui: li vince core.min.css con
   .dxbl-grid .dxbl-grid-table>thead .dxbl-grid-header-row>th:not(.dxbl-grid-empty-cell), che a
   (0,4,2) batte questo selettore a (0,2,0). Arrivano infatti da --dxbl-grid-header-color e
   --dxbl-grid-header-font-weight qui sopra: quelle funzionano, una dichiarazione diretta no.

   La linea che separa intestazioni e dati va sul <th> e non sulla riga: la tabella del grid ha
   border-collapse: separate, e con separate un bordo su <tr> non viene disegnato. Le celle sono
   adiacenti (border-spacing: 0), quindi i bordi delle singole celle formano una linea continua.
   Nessuna regola del tema tocca i bordi dei th del grid, percio' qui non c'e' contesa.

   Il selettore e' STRUTTURALE (thead > tr > th) e non per classe: .dxbl-grid-header nel CSS del
   tema compare sul group panel e accanto a .dxbl-grid-header-sortable, ma le regole sulle celle
   di intestazione della tabella targettano th diretto. Non essendo garantito che quella classe
   stia sul th, un selettore per classe rischia di non matchare nulla. La tabella sta dentro uno
   scroll viewer, quindi .ae-grid resta un discendente e non un genitore diretto. */
/* Titoli piu' piccoli e meno spaziati di prima (.74rem / .06em): nelle liste larghe, come i listini
   con nove colonne, il maiuscoletto spaziato occupava piu' del dato sotto e le intestazioni si
   stringevano fra loro. La spaziatura conta quanto il corpo: su una parola come "Pubblicazione"
   sono quasi due caratteri di larghezza recuperati. */
.ae-grid .dxbl-grid-table > thead > tr > th {
    border-bottom: 2px solid var(--ae-border);
    font-size: .7rem;
    text-transform: uppercase;
    letter-spacing: .04em;
}

/* Titoli per intero, senza puntini di troncamento. Il tema mette il testo dell'intestazione in uno
   <span> con overflow:hidden e text-overflow:ellipsis, quindi un titolo piu' largo della colonna
   veniva TAGLIATO invece di andare a capo: rimpicciolirlo aiutava, ma non risolveva.
   Qui il testo va a capo e la riga di intestazione cresce quanto serve.
   Il :root davanti non e' ornamentale: il selettore del tema sta a (0,3,4), identico a questo senza
   prefisso, e a pari specificita' vincerebbe lui, perche' site.css e' caricato prima del tema. */
:root .ae-grid .dxbl-grid-table > thead > tr > th > .dxbl-grid-header-content > span {
    overflow: visible;
    text-overflow: clip;
    white-space: normal;
}

/* Variante leggera (lista Spedizioni, richiesta del 30/10/2026): molte colonne fitte, quindi titoli un po' piu'
   piccoli e niente grassetto nelle celle; i link restano rossi. Sta dopo le regole di .ae-grid: a pari
   specificita' vince l'ultima. */
.ae-grid--light .dxbl-grid-table > thead > tr > th { font-size: .64rem; }
.ae-grid--light .dxbl-grid-table > tbody .ae-list-link { font-weight: 400; }

/* Variante "una riga per testo" (liste Spedizioni, Giacenze, Notizie, Collegamento ai corrieri e Ticket, 30/10/2026):
   niente a capo nelle celle. Le colonne hanno un MinWidth scelto perche' il testo ci stia: su uno schermo largo si
   allargano e riempiono il pannello, su uno stretto la tabella scorre in orizzontale e lo Stato (o le Azioni), a
   larghezza fissa, resta fermo a destra (FixedPosition sulla colonna). Le righe a capo VOLUTE (codice + LdV, nome + citta') sono div separati.
   Se un testo eccezionalmente lungo non ci sta finisce coi puntini, con il testo intero nel title: il link e'
   inline-block con max-width, altrimenti, inline, i puntini non compaiono e il testo viene solo tagliato. */
.ae-grid--nowrap .dxbl-grid-table > tbody > tr > td,
.ae-grid--nowrap .dxbl-grid-table > tbody > tr > td div {
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ae-grid--nowrap .dxbl-grid-table > tbody .ae-list-link { display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; vertical-align: bottom; }
/* Anche i titoli su una riga ("Data bolla" andava a capo): annulla il white-space: normal messo ai titoli di
   tutte le .ae-grid piu' sopra, con lo stesso selettore e quindi la stessa specificita' (vince perche' viene dopo). */
:root .ae-grid--nowrap .dxbl-grid-table > thead > tr > th > .dxbl-grid-header-content > span { white-space: nowrap; }
/* La colonna ferma (Stato) ha il contenuto centrato. TextAlignment sulla colonna lo chiede gia' a DevExpress;
   questa regola copre le celle con template, dove l'allineamento del tema potrebbe non arrivare. */
.ae-grid--nowrap .dxbl-grid-table > tbody > tr > td.dxbl-grid-fixed-cell { text-align: center; }
/* Le azioni di riga (.ae-row-actions, di solito a destra) in una colonna ferma stanno al centro come il resto. */
.ae-grid--nowrap .dxbl-grid-table > tbody > tr > td.dxbl-grid-fixed-cell .ae-row-actions { justify-content: center; }
/* Il titolo: il testo sta in uno <span> allargato a tutta la cella dal tema (flex-grow: 1), quindi si centra il
   testo dentro lo span (vedi .ae-cell-center). */
.ae-grid--nowrap .dxbl-grid-table > thead > tr > th.dxbl-grid-fixed-cell > .dxbl-grid-header-content > span { text-align: center; }

/* Tutto centrato tranne la prima colonna, che e' il nome della riga (Collegamento ai corrieri, 30/10/2026): celle e
   titoli. Strutturale (:first-child) invece che per classe: vale anche dove la lista non ha un CustomizeElement. */
.ae-grid--center-rest .dxbl-grid-table > tbody > tr > td:not(:first-child) { text-align: center; }
.ae-grid--center-rest .dxbl-grid-table > tbody > tr > td:not(:first-child) .ae-row-actions { justify-content: center; }
.ae-grid--center-rest .dxbl-grid-table > thead > tr > th:not(:first-child) > .dxbl-grid-header-content > span { text-align: center; }

/* Colonne fisse (FixedPosition): il tema le fa sticky con lo sfondo ereditato dalla riga, che in .ae-grid e'
   trasparente, quindi le colonne che scorrono sotto si vedrebbero attraverso. Qui prendono il bianco della
   superficie, con la riga di hover sovrapposta come sulle altre celle.
   La linea a sinistra che separa la colonna ferma da quelle che scorrono (stesso spessore e colore della linea
   sotto i titoli, 2px --ae-border) compare solo quando la tabella scorre davvero: con lo schermo largo le colonne
   riempiono il pannello e la colonna fissa e' una colonna come le altre (02/11/2026). data-ae-scrolls lo mette
   wwwroot/js/grid-scroll.js.
   :root e :not() servono a superare la specificita' del tema, che e' caricato dopo site.css. Vale per tutte le
   liste .ae-grid. */
/* Il bordo azzerato: DevExpress da' alla colonna fissata un suo bordo verticale nel colore scuro delle linee della
   griglia (la riga nera che si vedeva prima dello Stato); la linea la disegna solo il box-shadow. */
:root .ae-grid .dxbl-grid-table > tbody > tr > td.dxbl-grid-fixed-cell { background-color: var(--ae-surface); border-inline-width: 0; }
:root .ae-grid .dxbl-grid-table > tbody > tr:hover > td.dxbl-grid-fixed-cell { background: linear-gradient(var(--ae-tint), var(--ae-tint)) var(--ae-surface); }
:root .ae-grid .dxbl-grid-table > thead > tr > th.dxbl-grid-fixed-cell:not(.dxbl-grid-empty-cell) { background: var(--ae-surface); border-inline-width: 0; }
:root .ae-grid[data-ae-scrolls="true"] .dxbl-grid-table > tbody > tr > td.dxbl-grid-fixed-cell,
:root .ae-grid[data-ae-scrolls="true"] .dxbl-grid-table > thead > tr > th.dxbl-grid-fixed-cell:not(.dxbl-grid-empty-cell) { box-shadow: inset 2px 0 0 var(--ae-border); }

/* Popup doppioni e rubrica: tabelle dentro DxPopup/DxGrid */
.ae-popup-table-wrap { overflow-x: auto; margin-top: .75rem; }
/* Azioni di riga nelle griglie allineate come il titolo della colonna (02/11/2026): a sinistra, come "Azioni";
   centrate dove la colonna e' centrata (colonna ferma a destra, .ae-grid--center-rest: regole piu' sotto). */
.ae-grid .ae-row-actions { display: flex; gap: .5rem; justify-content: flex-start; }

/* ============================================================
   Allineamento al mockup UX (10/09/2026): menu a sezioni con voci disabilitate,
   schede del dettaglio cliente, banner di stato, riquadri e percorso di onboarding.
   Nessun colore hardcoded: solo var(--ae-*).
   ============================================================ */

/* --- Menu: intestazione di sezione e voce disabilitata (title = "Disponibile in una fase successiva") --- */
/* Prendono --ae-sidebar-text come le altre voci del menu, smorzato: la sidebar e' bianca e il
   testo neutro (il rosso pieno su tutta la colonna era stato provato e scartato). */
.ae-nav-section {
    padding: 1rem .9rem .3rem; margin: 0;
    font-size: .7rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
    color: var(--ae-sidebar-text); opacity: .7;
}
.ae-nav-link--disabled,
.ae-nav-link--disabled:hover {
    color: var(--ae-sidebar-text); opacity: .45; cursor: not-allowed;
    background: transparent;
}
.ae-nav-note { padding: .5rem .9rem .25rem; font-size: .75rem; color: var(--ae-sidebar-text); opacity: .7; }
/* Levetta chiaro/scuro, ultima voce del menu (30/10/2026): stessa misura e stesso hover delle voci (.ae-nav-link),
   etichetta "Tema:" a sinistra e levetta a destra. Il pallino si sposta in base a data-theme e non ad aria-checked:
   theme.js gira data-theme al click, quindi la levetta scatta subito e il ricaricamento della pagina arriva dopo. */
/* Tutto su una riga: etichetta e levetta non vanno mai a capo, nemmeno con la colonna stretta (nowrap e nessun
   elemento che si restringe sotto la sua misura). */
.ae-theme-switch {
    display: flex; flex-wrap: nowrap; align-items: center; justify-content: space-between; gap: .6rem;
    white-space: nowrap;
    width: 100%;
    padding: .6rem .85rem; margin-bottom: .25rem;
    background: none; border: 0; border-radius: .55rem; cursor: pointer;
    font: inherit; font-weight: 500; font-size: .92rem; color: var(--ae-sidebar-text);
}
.ae-theme-switch:hover { background-color: var(--ae-accent); color: var(--ae-primary); }
.ae-theme-switch:focus-visible { outline: 2px solid var(--ae-primary); outline-offset: 2px; }
.ae-theme-switch-label { display: inline-flex; flex: 0 0 auto; align-items: center; gap: .6rem; }
.ae-theme-switch-track {
    position: relative; flex: 0 0 auto;
    width: 2.75rem; height: 1.5rem;
    border-radius: 999px;
    background: var(--ae-border);
    transition: background-color .2s ease;
}
.ae-theme-switch-knob {
    position: absolute; top: .1875rem; left: .1875rem;
    display: flex; align-items: center; justify-content: center;
    width: 1.125rem; height: 1.125rem;
    border-radius: 50%;
    background: var(--ae-surface);
    color: var(--ae-primary);
    font-size: .7rem; line-height: 1;
    box-shadow: var(--ae-shadow-card);
    transition: transform .2s ease;
}
.ae-theme-switch-icon--dark { display: none; }
:root[data-theme="dark"] .ae-theme-switch-track { background: var(--ae-primary); }
:root[data-theme="dark"] .ae-theme-switch-knob { transform: translateX(1.25rem); background: var(--ae-primary-text); }
:root[data-theme="dark"] .ae-theme-switch-icon--light { display: none; }
:root[data-theme="dark"] .ae-theme-switch-icon--dark { display: inline; }
@media (prefers-reduced-motion: reduce) {
    .ae-theme-switch-track, .ae-theme-switch-knob { transition: none; }
}

/* --- Schede (dettaglio cliente): barra + pannello --- */
.ae-tabs {
    display: flex; gap: .25rem; flex-wrap: wrap;
    border-bottom: 1px solid var(--ae-border);
    margin-bottom: 1rem;
}
.ae-tab {
    background: transparent; border: 0; border-bottom: 2px solid transparent;
    padding: .6rem .9rem; margin-bottom: -1px;
    font: inherit; font-size: .9rem; font-weight: 500; color: var(--ae-secondary);
    cursor: pointer; white-space: nowrap;
}
.ae-tab:hover { color: var(--ae-primary); }
.ae-tab--active { color: var(--ae-primary); font-weight: 600; border-bottom-color: var(--ae-primary); }
.ae-tab--disabled,
.ae-tab--disabled:hover { color: var(--ae-muted); opacity: .55; cursor: not-allowed; }
.ae-tab-panel { display: flex; flex-direction: column; gap: 1rem; }
/* Voce di menu a tendina prevista ma non ancora attiva (ruoli della US #17430): si legge, non si sceglie */
.ae-option--unavailable { color: var(--ae-muted); opacity: .55; cursor: not-allowed; }

/* --- Card con header (titolo a sinistra, azioni a destra) dentro .ae-panel --- */
.ae-card-header {
    display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
    margin-bottom: .9rem;
}
.ae-card-header h2 { margin: 0; font-size: 1.05rem; font-weight: 600; color: var(--ae-text); }
.ae-card-header p { margin: .2rem 0 0; font-size: .85rem; color: var(--ae-muted); }
.ae-card-header-actions { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
/* Lato destro dell'intestazione quando sopra i bottoni serve una riga di avviso: tutto allineato a destra. */
/* flex 1 1 0 e min-width 0: la colonna prende lo spazio rimasto accanto al titolo invece di misurarsi sul testo
   dell'avviso steso su una riga, che la farebbe andare a capo sotto il titolo. Cosi' l'avviso sta alla sua altezza. */
.ae-card-header-side {
    display: flex; flex-direction: column; align-items: flex-end; gap: .45rem;
    flex: 1 1 0; min-width: 0; margin-left: auto;
}
.ae-card-header-side .ae-card-header-actions { justify-content: flex-end; }
/* Avviso piccolo dentro una card (non un banner): testo e icona nei colori warning, fondo tenue. */
.ae-inline-alert {
    display: flex; gap: .4rem; align-items: baseline;
    margin: 0; padding: .35rem .65rem;
    border-radius: 8px;
    background: var(--ae-warning-bg); color: var(--ae-warning-text);
    font-size: .8rem; line-height: 1.4; text-align: right;
}

/* --- Banner di stato (Panoramica) --- */
.ae-banner {
    display: flex; gap: .8rem; align-items: flex-start;
    border-radius: 12px; padding: .9rem 1.1rem;
    border: 1px solid var(--ae-border);
}
.ae-banner i { font-size: 1.15rem; line-height: 1.3; }
.ae-banner--warning { background: var(--ae-warning-bg); color: var(--ae-warning-text); border-color: color-mix(in srgb, var(--ae-warning) 30%, var(--ae-surface)); }
.ae-banner--info { background: var(--ae-info-bg); color: var(--ae-info-text); border-color: color-mix(in srgb, var(--ae-info) 30%, var(--ae-surface)); }
.ae-banner-title { margin: 0; font-weight: 600; font-size: .95rem; }
.ae-banner-text { margin: .25rem 0 0; font-size: .88rem; }
/* Variante su una riga (esito dell'import): titolo e azioni centrati in verticale, azioni spinte a
   destra. Su schermo stretto le azioni vanno a capo sotto il titolo invece di schiacciarsi. */
.ae-banner--inline { align-items: center; flex-wrap: wrap; }
.ae-banner--inline i { line-height: 1; }
.ae-banner-actions { margin-left: auto; display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
/* Gli outline stanno su un fondo colorato: senza un fondo proprio il banner ci passerebbe attraverso
   e sembrerebbero disattivati. Si scrive la variabile, come nell'override outline-primary sopra, e a
   (0,4,0) per vincerlo: l'hover resta suo (--dxbl-btn-hover-bg). */
:root .ae-banner-actions .dxbl-btn.dxbl-btn-outline-primary { --dxbl-btn-bg: var(--ae-surface); }

/* --- Riquadri riepilogo (Contratto / Listino / Corriere / Data attivazione) --- */
.ae-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: .9rem; }
.ae-tile {
    background: var(--ae-surface); border: 1px solid var(--ae-border); border-radius: 12px;
    box-shadow: var(--ae-shadow-card); padding: .9rem 1.1rem;
}
.ae-tile-label { margin: 0; font-size: .72rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--ae-muted); }
.ae-tile-value { margin: .35rem 0 0; font-size: 1.15rem; font-weight: 600; color: var(--ae-text); }
.ae-tile-value--muted { color: var(--ae-muted); font-weight: 500; }
.ae-tile-note { margin: .2rem 0 0; font-size: .75rem; color: var(--ae-muted); }

/* Riquadri KPI (lista borderò): il numero e' il dato, quindi grande; l'etichetta in tono normale e non
   maiuscoletto, perche' qui e' una frase ("Corrieri in uscita oggi") e non il nome di un campo.
   --alert evidenzia il riquadro che chiede di intervenire (foto mancanti), solo quando il numero e' > 0. */
.ae-tiles--kpi { margin-bottom: 1rem; }
.ae-tiles--kpi .ae-tile { padding: 1.1rem 1.25rem; }
.ae-tiles--kpi .ae-tile-label { font-size: .85rem; font-weight: 500; letter-spacing: 0; text-transform: none; color: var(--ae-text); }
.ae-tiles--kpi .ae-tile-value { margin-top: .4rem; font-size: 1.9rem; font-weight: 700; line-height: 1.1; }
.ae-tiles--kpi .ae-tile-note { margin-top: .35rem; font-size: .8rem; }
.ae-tiles--kpi .ae-tile--alert { --ae-tile-ink: var(--ae-danger-text); background: var(--ae-danger-bg); border: 2px solid var(--ae-danger-text); }
.ae-tiles--kpi .ae-tile--alert .ae-tile-label { font-weight: 700; }
.ae-tiles--kpi .ae-tile--alert .ae-tile-label,
.ae-tiles--kpi .ae-tile--alert .ae-tile-value,
.ae-tiles--kpi .ae-tile--alert .ae-tile-note { color: var(--ae-danger-text); }

/* --- Percorso di onboarding (stepper) --- */
.ae-stepper { display: flex; align-items: flex-start; gap: 0; flex-wrap: wrap; }
.ae-step { display: flex; align-items: center; gap: .6rem; flex: 1 1 160px; min-width: 160px; padding: .4rem 0; color: var(--ae-muted); }
.ae-step-circle {
    display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
    width: 30px; height: 30px; border-radius: 50%;
    border: 2px solid var(--ae-border); background: var(--ae-surface);
    font-size: .8rem; font-weight: 700; color: var(--ae-muted);
}
.ae-step-label { font-size: .88rem; font-weight: 500; }
/* Pallino completato: verde OPACIZZATO (18% di --ae-success), non verde pieno. Tiene il
   significato di "fatto" che il verde porta, ma smette di urlare accanto al rosso del tema.
   Il check dentro va in --ae-success-text (il verde scurito) e NON in --ae-primary-text: su un
   fondo al 18% il bianco non si leggerebbe.
   Resta ben distinto dallo step corrente, che e' bianco con l'anello rosso pieno: qui il disco
   e' pieno e morbido, e anche la tinta e' diversa. */
.ae-step--done .ae-step-circle {
    background: var(--ae-step-done-bg);
    border-color: var(--ae-step-done-bg);
    color: var(--ae-success-text);
}
.ae-step--done .ae-step-label { color: var(--ae-text); }
.ae-step--current .ae-step-circle { border-color: var(--ae-primary); color: var(--ae-primary); }
.ae-step--current .ae-step-label { color: var(--ae-primary); font-weight: 600; }

/* --- Anteprima portale cliente: barra in testa alla pagina (accesso come <utente>) --- */
.ae-preview-bar {
    display: flex; align-items: center; gap: .8rem; flex-wrap: wrap;
    background: var(--ae-primary); color: var(--ae-primary-text);
    border-radius: 12px; padding: .7rem 1.1rem; margin-bottom: 1rem;
    font-size: .9rem;
}
.ae-preview-bar b { font-weight: 600; }
.ae-preview-bar-note { margin-left: auto; opacity: .85; font-size: .85rem; }

/* Tabella modificabile nel dettaglio (editor regole di ricarico): editor compatti, niente hover di riga. */
.ae-editor-table td { padding: .35rem .3rem; vertical-align: middle; }
.ae-editor-table tbody tr:hover td { background: transparent; }

/* Larghezze: il minimo era 6.5rem per TUTTI gli editor, e con dieci colonne faceva ~1040px, cioe'
   quasi quanto l'intera modale. Ora la base e' stretta e solo le tendine, che hanno etichette vere
   da mostrare, si allargano; i numerici (valore, pesi, arrotondamento, priorita') stanno in poco.
   Le posizioni seguono l'ordine delle colonne dichiarate nel <thead>: aggiungendone una vanno riviste. */
.ae-editor-table .dxbl-text-edit,
.ae-editor-table .dxbl-spin-edit,
.ae-editor-table .dxbl-combobox { min-width: 4.25rem; }

.ae-editor-table td:nth-child(1) .dxbl-combobox { min-width: 8rem; }   /* Tipo: "Importo fisso a spedizione" */
.ae-editor-table td:nth-child(3) .dxbl-combobox { min-width: 6.5rem; } /* Servizio */
.ae-editor-table td:nth-child(4) .dxbl-combobox { min-width: 6.5rem; } /* Direzione */
.ae-editor-table td:nth-child(5) .dxbl-text-edit { min-width: 5.5rem; } /* Zona: testo libero */

/* Le intestazioni lunghe ("Arrotondamento", "Peso da (kg)") allargavano la colonna quanto la parola:
   mandandole a capo la larghezza la decide l'editor sotto, non il titolo. */
.ae-editor-table th { white-space: normal; }
/* La colonna delle azioni tiene solo un'icona: non deve prendersi spazio contrattabile. */
.ae-editor-table td:last-child { width: 1%; }
.ae-editor-errors { margin: .2rem 0 0 1rem; padding: 0; }
.ae-editor-saved { color: var(--ae-success); }
.ae-editor-actions { margin-top: .8rem; }

/* ============================================================
   Ultime notizie (NewsWidget): bottone nell'header, subito prima del nome utente, con l'elenco a
   tendina che si apre verso il basso allineato al bordo destro del bottone.

   Il bottone ha la stessa forma di "Esci" (.ae-topbar-logout), cosi' l'header resta una fila di
   comandi omogenei. La tendina e' position:absolute rispetto a .ae-news: esce dall'header e si posa
   sul contenuto senza spostare niente.

   z-index 900: sopra il contenuto e sopra la sidebar, ma SOTTO i popup DevExpress (1055 e oltre
   nel tema). Una modale aperta deve coprirla, altrimenti resterebbe a galleggiare sul velo.

   Palette: bianco piu' opacita' del primary, come il resto. L'unico pieno e' il contatore delle
   novita' da leggere, che senza fondo non si distinguerebbe dal testo.
   ============================================================ */
.ae-news { position: relative; }

.ae-news-tab {
    display: inline-flex; align-items: center; gap: .45rem;
    background: none; border: 1px solid var(--ae-border); border-radius: .55rem; cursor: pointer;
    padding: .4rem .75rem;
    font: inherit; font-size: .85rem; font-weight: 500; color: var(--ae-text);
    white-space: nowrap;
}
.ae-news-tab:hover,
.ae-news--open .ae-news-tab { background: var(--ae-accent); color: var(--ae-primary); border-color: var(--ae-primary); }
.ae-news-tab-icon { color: var(--ae-primary); font-size: .95rem; line-height: 1; }
/* La freccia dice in che verso va il prossimo click: giu' per aprire, su per richiudere. */
.ae-news-chevron {
    font-size: .75rem; line-height: 1; color: var(--ae-muted);
    transition: transform .28s ease;
}
.ae-news--open .ae-news-chevron { transform: rotate(180deg); }
/* Sotto i 768px l'header ha anche il bottone del menu: resta il megafono, la scritta sta nel title. */
@media (max-width: 768px) {
    .ae-news-tab-label { display: none; }
}

/* La tendina: una scheda vera, non un fumetto. A 37rem il corpo di una notizia sta in due righe; il
   min() e' il freno sulle finestre strette, dove deve restare dentro lo schermo (meno i margini). */
.ae-news-body {
    position: absolute;
    top: calc(100% + .5rem);
    right: 0;
    z-index: 900;
    width: min(37rem, calc(100vw - 2rem));
    background: var(--ae-surface);
    border: 1px solid var(--ae-border);
    border-radius: 14px;
    box-shadow: var(--ae-shadow-login);
    overflow: hidden;
}
.ae-news-count {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 1.4rem; height: 1.4rem; padding: 0 .4rem;
    border-radius: 999px;
    background: var(--ae-primary); color: var(--ae-primary-text);
    font-size: .75rem; font-weight: 700; line-height: 1;
}

/* ============================================================
   Lampeggio: parte quando arriva una notizia da leggere e si ferma al primo click. La classe la
   mette il componente (ShouldBlink = non ancora cliccato E chiuso E con non lette): qui non c'e'
   logica.

   Il bottone si accende del colore primario e torna bianco: un'alternanza netta che si vede con la
   coda dell'occhio, ma su un ciclo di 1.6s e non piu' veloce, per non diventare un allarme.
   ============================================================ */
@keyframes ae-news-blink {
    0%, 100% { background: var(--ae-surface); color: var(--ae-text); border-color: var(--ae-border); }
    50%      { background: var(--ae-primary); color: var(--ae-primary-text); border-color: var(--ae-primary); }
}
.ae-news--blink .ae-news-tab { animation: ae-news-blink 1.6s ease-in-out infinite; }
/* Acceso, icona e freccia prendono il colore del testo del bottone: in primario sparirebbero sul fondo
   primario. Il contatore si inverte per la stessa ragione. */
.ae-news--blink .ae-news-tab-icon,
.ae-news--blink .ae-news-chevron { color: inherit; }
.ae-news--blink .ae-news-count { background: var(--ae-surface); color: var(--ae-primary); }
/* Fermo il lampeggio mentre il mouse e' sopra: si sta gia' per cliccare. */
.ae-news--blink .ae-news-tab:hover { animation-play-state: paused; }

/* Apertura: griglia a una riga che passa da 0fr a 1fr. E' l'unico modo di animare fino
   all'altezza vera del contenuto senza fissare un max-height finto (che, se piu' alto del
   contenuto, alla chiusura fa partire l'animazione in ritardo).
   Dove non fosse supportato l'elenco appare e sparisce di colpo: si perde l'animazione, non la
   funzione. Il figlio vuole min-height:0, altrimenti la riga non scende sotto il suo contenuto.
   visibility a chiusa: senza, a 0fr resterebbe a schermo il solo bordo della tendina, una riga
   sotto il bottone. Ritardata della durata della chiusura, cosi' sparisce a animazione finita. */
.ae-news-body {
    display: grid;
    grid-template-rows: 0fr;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: grid-template-rows .28s ease, opacity .2s ease, visibility 0s linear .28s;
}
.ae-news--open .ae-news-body {
    grid-template-rows: 1fr;
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition: grid-template-rows .28s ease, opacity .2s ease, visibility 0s;
}
.ae-news-body-inner { min-height: 0; overflow: hidden; }

/* L'elenco scrolla per conto suo: con molte voci la card non deve arrivare a mangiarsi lo
   schermo, e in basso le resta comunque la linguetta. La barra e' quella in tinta piu' sotto.
   I due limiti lavorano in coppia: 40rem e' quanto si vuole al massimo, 68vh e' il freno sulle
   finestre basse, dove 40rem coprirebbero lo schermo intero. Vince sempre il piu' piccolo. */
.ae-news-list {
    list-style: none; margin: 0; padding: 0;
    max-height: min(40rem, 68vh);
    overflow-y: auto;
    scrollbar-width: thin;
    scrollbar-color: var(--ae-scrollbar-thumb) transparent;
}
.ae-news-list::-webkit-scrollbar { width: 10px; }
.ae-news-list::-webkit-scrollbar-track { background: transparent; }
.ae-news-list::-webkit-scrollbar-thumb {
    background-color: var(--ae-scrollbar-thumb);
    border-radius: 999px;
    border: 2px solid transparent;
    background-clip: content-box;
}
.ae-news-list::-webkit-scrollbar-thumb:hover { background-color: var(--ae-scrollbar-thumb-hover); }

.ae-news-item { padding: .95rem 1.1rem; border-bottom: 1px solid var(--ae-border); }
/* L'ultima riga la fa gia' il bordo della tendina: due bordi attaccati si vedono. */
.ae-news-item:last-child { border-bottom: 0; }
.ae-news-item-head { display: flex; align-items: center; gap: .5rem; margin-bottom: .25rem; }
.ae-news-date { color: var(--ae-muted); font-size: .8rem; font-weight: 600; }
.ae-news-title { margin: 0; font-size: .95rem; font-weight: 600; color: var(--ae-text); }
.ae-news-text { margin: .2rem 0 0; font-size: .88rem; line-height: 1.45; color: var(--ae-muted); }
.ae-news-empty { margin: 0; padding: 1.75rem 1.1rem; text-align: center; color: var(--ae-muted); font-size: .95rem; }

/* Chi ha chiesto meno movimento non deve vedersi crescere una tendina addosso ne' lampeggiare niente
   nell'header: il lampeggio diventa il bottone acceso fisso, che dice lo stesso "c'e' del nuovo"
   senza moto perpetuo, ed e' esattamente cio' che questa impostazione chiede di non fare. */
@media (prefers-reduced-motion: reduce) {
    .ae-news-body,
    .ae-news-chevron { transition: none; }
    .ae-news--blink .ae-news-tab {
        animation: none;
        background: var(--ae-primary); color: var(--ae-primary-text); border-color: var(--ae-primary);
    }
}

/* ============================================================
   Scelta file (Importa trasmissioni): il bottone nativo dell'input file ha l'aspetto del browser e
   la scritta "Nessun file selezionato" nella sua lingua. Qui si vede un DxButton Secondary vero,
   identico agli altri, e l'input gli sta steso sopra trasparente: e' lui a prendere il click (e il
   fuoco da tastiera), quindi il selettore del sistema si apre senza una riga di JavaScript.

   Proprio perche' il mouse sta sull'input, l'hover del tema sul bottone non scatta: lo si riporta
   dal contenitore con gli stessi valori del Secondary (vedi l'override :root .dxbl-btn sopra).
   Il nome del file scelto lo dice gia' la riga di aiuto sotto.
   ============================================================ */
.ae-file-pick { position: relative; display: inline-flex; }
.ae-file-pick input[type="file"] {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    opacity: 0;
    cursor: pointer;
}
/* La faccia e' inert nel markup (niente Tab, niente screen reader) e non prende il puntatore. */
.ae-file-pick-face { display: inline-flex; pointer-events: none; }
.ae-file-pick:not(.ae-file-pick--disabled):hover .dxbl-btn {
    --dxbl-btn-secondary-bg: var(--ae-accent);
    --dxbl-btn-secondary-border-color: var(--ae-primary);
}
/* Fuoco da tastiera sull'input invisibile: l'anello si disegna sul bottone che si vede. :focus-visible
   e non :focus-within, che lo accenderebbe anche dopo un click col mouse. */
.ae-file-pick:has(input:focus-visible) .dxbl-btn {
    outline: 2px solid var(--ae-primary);
    outline-offset: 2px;
}
.ae-file-pick--disabled input[type="file"] { cursor: not-allowed; }

/* ============================================================
   Velo di aggiornamento (RefreshVeil): contenuto gia' a schermo che si sta ricaricando.

   Il velo copre il contenuto (inset 0) senza cambiarne l'altezza, quindi lo scroll non si muove.
   Compare con 150ms di ritardo: quasi tutte le ricariche durano meno e un lampo grigio a ogni
   salvataggio sarebbe peggio del salto che si vuole togliere. I click pero' sono bloccati da subito
   (il velo c'e', e' solo trasparente), perche' i dati sotto sono gia' vecchi.

   La scritta e' sticky in cima: su una scheda lunga scrollata a meta' resta visibile in alto
   invece di finire fuori schermo all'inizio del contenuto.
   ============================================================ */
.ae-refresh { position: relative; }
/* Dentro le schede il velo si mette fra .ae-tab-panel e i suoi blocchi: deve riportare la stessa colonna
   con lo stesso gap, altrimenti banner, card e bottoni si attaccherebbero. Il velo, assoluto, non conta. */
.ae-tab-panel .ae-refresh { display: flex; flex-direction: column; gap: 1rem; }
.ae-refresh-veil {
    position: absolute; inset: 0;
    z-index: 5;
    display: flex; justify-content: center; align-items: flex-start;
    cursor: progress;
    background: color-mix(in srgb, var(--ae-surface) 55%, transparent);
    opacity: 0;
    animation: ae-refresh-in .2s ease .15s forwards;
}
/* Barra indeterminata in cima al contenuto: il segnale di "sta lavorando" anche quando la scritta
   e' lontana dall'occhio. */
.ae-refresh-veil::before {
    content: "";
    position: absolute; top: 0; left: 0; right: 0;
    height: 3px;
    background: linear-gradient(90deg, transparent, var(--ae-primary), transparent);
    background-size: 40% 100%;
    background-repeat: no-repeat;
    animation: ae-refresh-bar 1.1s linear infinite;
}
.ae-refresh-pill {
    position: sticky; top: .75rem;
    margin-top: .75rem;
    display: inline-flex; align-items: center; gap: .5rem;
    padding: .4rem .85rem;
    background: var(--ae-surface);
    border: 1px solid var(--ae-border);
    border-radius: 999px;
    box-shadow: var(--ae-shadow-card);
    font-size: .85rem; font-weight: 600; color: var(--ae-text);
}
.ae-refresh-spinner {
    width: .9rem; height: .9rem;
    border: 2px solid var(--ae-border);
    border-top-color: var(--ae-primary);
    border-radius: 50%;
    animation: ae-refresh-spin .7s linear infinite;
}
@keyframes ae-refresh-in { to { opacity: 1; } }
@keyframes ae-refresh-bar {
    /* Con un'immagine larga il 40% le percentuali valgono sul 60% che avanza: -70% e 170% la
       portano appena fuori dai due bordi, cosi' entra ed esce senza comparire a meta'. */
    from { background-position: -70% 0; }
    to   { background-position: 170% 0; }
}
@keyframes ae-refresh-spin { to { transform: rotate(360deg); } }
/* Meno movimento: restano velo e scritta, spariscono barra che scorre e rotella che gira. */
@media (prefers-reduced-motion: reduce) {
    .ae-refresh-veil { animation-duration: 0s; }
    .ae-refresh-veil::before,
    .ae-refresh-spinner { animation: none; }
}

/* ============================================================
   Velo di caricamento (LoadingVeil + LoadingVeilHost): primo caricamento, niente ancora da mostrare.

   Copre tutta la finestra (fixed) con il velo scuro e al centro il furgoncino. z-index 1000: sopra
   sidebar e tendina notizie (900), sotto le modali DevExpress (1055), come il resto dei veli.
   Compare con 150ms di ritardo come RefreshVeil: un caricamento rapido non deve lampeggiare.

   Un giro dura 10s (--ae-lv-cycle) e tutte le fasi sono percentuali dello stesso giro:
     0-14%  l'omino entra da sinistra col pacco     14-24% sale la rampa ed entra dietro la fiancata
     25%    lascia il pacco, si gira                27-48% scende ed esce di scena a sinistra
     45-51% la rampa si richiude                    52-68% il furgone parte ed esce a destra
     72-90% torna in retromarcia (luce che lampeggia) 91-97% la rampa si riabbassa
   Colori: solo --ae-* (furgone --ae-art-paper e primary, --ae-art-ink per gomme e pantaloni) piu' --ae-skin.
   Inchiostro e carta del disegno sono fissi nei due temi: presi da testo e superficie, in scuro si invertivano
   (gomme chiare, furgone nero). Il velo e' --ae-overlay per lo stesso motivo.
   ============================================================ */
.ae-loading-veil {
    --ae-lv-cycle: 10s;
    position: fixed; inset: 0;
    z-index: 1000;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .4rem;
    cursor: progress;
    background: var(--ae-overlay);
    opacity: 0;
    animation: ae-refresh-in .2s ease .15s forwards;
}
.ae-loading-art { width: min(340px, 80vw); height: auto; overflow: hidden; }
.ae-loading-text { color: var(--ae-primary-text); font-size: .95rem; font-weight: 500; letter-spacing: .01em; }
.ae-loading-text::after { content: ""; display: inline-block; width: 1.2em; text-align: left; animation: ae-lv-dots 1.2s steps(4) infinite; }

.ae-lv-ground { stroke: var(--ae-primary-text); stroke-opacity: .45; stroke-width: 2; stroke-linecap: round; }
.ae-lv-ramp-board { stroke: var(--ae-muted); stroke-width: 4; stroke-linecap: round; fill: none; }
.ae-lv-ramp-grip { stroke: var(--ae-art-paper); stroke-opacity: .7; stroke-width: 1; stroke-dasharray: 2 4; fill: none; }
.ae-lv-door,
.ae-lv-chassis,
.ae-lv-tyre,
.ae-lv-belt,
.ae-lv-eye { fill: var(--ae-art-ink); }
.ae-lv-pants { fill: color-mix(in srgb, var(--ae-art-ink) 85%, var(--ae-art-paper)); }
.ae-lv-shoe { fill: var(--ae-art-ink); }
.ae-lv-body { fill: var(--ae-art-paper); stroke: var(--ae-primary); stroke-width: 2.5; }
.ae-lv-stripe,
.ae-lv-cab,
.ae-lv-shirt,
.ae-lv-sleeve { fill: var(--ae-primary); }
.ae-lv-sleeve { stroke: var(--ae-primary-hover); stroke-width: .8; }
.ae-lv-cap,
.ae-lv-sleeve-back { fill: var(--ae-primary-hover); }
.ae-lv-arm-hold { stroke: var(--ae-primary-hover); stroke-width: 4; stroke-linecap: round; fill: none; }
.ae-lv-seam { stroke: var(--ae-border); stroke-width: 1.5; }
/* Logo sulla fiancata (LoadingVeilHost): centrato nello spazio sopra la fascia, rosso come la fascia. */
.ae-lv-livery { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; color: var(--ae-primary); }
.ae-lv-livery .ae-logo { width: 100%; height: auto; }
.ae-lv-livery .ae-logo-img { max-width: 100%; max-height: 100%; }
.ae-lv-window { fill: var(--ae-art-paper); fill-opacity: .9; }
.ae-lv-handle { stroke: var(--ae-art-paper); stroke-width: 2; stroke-linecap: round; }
.ae-lv-headlight,
.ae-lv-reverse,
.ae-lv-hub { fill: var(--ae-art-paper); }
.ae-lv-hub { fill-opacity: .85; }
.ae-lv-skin { fill: var(--ae-skin); }
.ae-lv-box { fill: color-mix(in srgb, var(--ae-primary) 15%, var(--ae-art-paper)); stroke: var(--ae-primary); stroke-width: 1.5; }
.ae-lv-tape { stroke: var(--ae-primary); stroke-opacity: .5; stroke-width: 1.5; }

/* Omino: percorso e verso sul giro lungo, passo e braccia su un ciclo breve sempre acceso. */
.ae-lv-man { animation: ae-lv-man var(--ae-lv-cycle) linear infinite; }
.ae-lv-leg,
.ae-lv-arm { transform-box: fill-box; transform-origin: 50% 0; animation: ae-lv-step .35s ease-in-out infinite alternate; }
.ae-lv-leg--back,
.ae-lv-arm { animation-direction: alternate-reverse; }
.ae-lv-arm--front { animation-direction: alternate; }
.ae-lv-bob { animation: ae-lv-bob .175s ease-in-out infinite alternate; }
.ae-lv-carry { animation: ae-lv-carry var(--ae-lv-cycle) steps(1) infinite; }
.ae-lv-empty { animation: ae-lv-empty var(--ae-lv-cycle) steps(1) infinite; }

/* Furgone: la rampa ruota attorno al bordo del pianale (angolo in alto a destra del suo riquadro) fino a
   posarsi verticale contro il portellone. */
.ae-lv-truck { animation: ae-lv-truck var(--ae-lv-cycle) infinite; }
.ae-lv-ramp { transform-box: fill-box; transform-origin: 100% 0; animation: ae-lv-ramp var(--ae-lv-cycle) ease-in-out infinite; }
.ae-lv-wheel { transform-box: fill-box; transform-origin: 50% 50%; animation: ae-lv-wheel var(--ae-lv-cycle) infinite; }
.ae-lv-reverse { animation: ae-lv-reverse var(--ae-lv-cycle) steps(1) infinite; }

@keyframes ae-lv-man {
    0%          { transform: translate(-30px, 0) scaleX(1); }
    14%         { transform: translate(70px, 0) scaleX(1); }
    20%         { transform: translate(108px, -14px) scaleX(1); }
    24%, 26%    { transform: translate(130px, -14px) scaleX(1); }
    26.1%       { transform: translate(130px, -14px) scaleX(-1); }
    30%         { transform: translate(108px, -14px) scaleX(-1); }
    36%         { transform: translate(70px, 0) scaleX(-1); }
    48%, 99.9%  { transform: translate(-30px, 0) scaleX(-1); }
    100%        { transform: translate(-30px, 0) scaleX(1); }
}
@keyframes ae-lv-step { from { transform: rotate(-22deg); } to { transform: rotate(22deg); } }
@keyframes ae-lv-bob { to { transform: translateY(-1px); } }
@keyframes ae-lv-carry { 0% { opacity: 1; } 25%, 100% { opacity: 0; } }
@keyframes ae-lv-empty { 0% { opacity: 0; } 25%, 100% { opacity: 1; } }
@keyframes ae-lv-truck {
    0%, 52%   { transform: translateX(0); animation-timing-function: ease-in; }
    68%, 72%  { transform: translateX(230px); animation-timing-function: ease-out; }
    90%, 100% { transform: translateX(0); }
}
@keyframes ae-lv-wheel {
    0%, 52%   { transform: rotate(0); animation-timing-function: ease-in; }
    68%, 72%  { transform: rotate(900deg); animation-timing-function: ease-out; }
    90%, 100% { transform: rotate(0); }
}
@keyframes ae-lv-ramp {
    0%, 45%   { transform: rotate(0); }
    51%, 91%  { transform: rotate(113deg); }
    97%, 100% { transform: rotate(0); }
}
@keyframes ae-lv-reverse {
    0% { opacity: 0; }
    72%, 76%, 80%, 84%, 88% { opacity: 1; }
    74%, 78%, 82%, 86% { opacity: .2; }
    90% { opacity: 0; }
}
@keyframes ae-lv-dots {
    0% { content: ""; } 25% { content: "."; } 50% { content: ".."; } 75% { content: "..."; }
}
/* Meno movimento: il furgone resta fermo con la rampa giu' e l'omino col pacco davanti alla rampa. */
@media (prefers-reduced-motion: reduce) {
    .ae-loading-veil { animation-duration: 0s; }
    .ae-loading-veil * ,
    .ae-loading-text::after { animation: none !important; }
    .ae-lv-man { transform: translate(60px, 0); }
    .ae-lv-empty { opacity: 0; }
}

/* ============================================================
   Spedizioni (29/09/2026): selettore cliente con risultati sotto il campo, tabella colli della bolla.
   ============================================================ */
.ae-customer-picker { flex: 1 1 320px; position: relative; }
.ae-picker-results {
    list-style: none; margin: .25rem 0 0; padding: .25rem 0;
    border: 1px solid var(--ae-border); border-radius: var(--ae-radius-input);
    background: var(--ae-surface); max-height: 320px; overflow-y: auto;
    position: absolute; left: 0; right: 0; z-index: 30;
    box-shadow: 0 6px 18px rgba(0,0,0,.08);
}
.ae-picker-results li { margin: 0; }
.ae-picker-results .ae-row-action { display: block; width: 100%; text-align: left; padding: .45rem .8rem; font-weight: 500; color: var(--ae-text); }
.ae-picker-results .ae-row-action:hover { background: var(--ae-bg); text-decoration: none; }
.ae-packages-table td { vertical-align: middle; }
.ae-packages-table .dxbl-text-edit, .ae-packages-table .dxbl-spin-edit { min-width: 90px; }
.ae-volume-total { font-weight: 600; margin-top: .5rem; }

/* ============================================================
   Dettaglio spedizione a card affiancate (01/10/2026).

   Prima i sei .ae-panel erano uno sotto l'altro e ATTACCATI: .ae-panel non ha margini propri,
   quindi i riquadri si toccavano e la pagina era una colonna lunghissima da scorrere. Qui li
   governa una griglia a due colonne con il suo gap, cosi' lo spazio fra le card e' una proprieta'
   del contenitore e non un margine appiccicato a ogni riquadro (che avrebbe toccato anche tutte le
   altre pagine, dove .ae-panel sta da solo).

   minmax(0, 1fr) e non 1fr: la colonna di default e' larga quanto il suo contenuto piu' largo
   (auto), e una tabella dentro la card la farebbe sfondare invece di mandarla a capo.

   Le card restano alte uguali dentro la riga (stretch, il default): e' quello che le fa leggere
   come una coppia invece che come due foglietti scollati.

   Sotto i 1200px si torna a una colonna: con la sidebar da 280px, meta' di quel che resta non e'
   piu' una card, e' una fessura.
   ============================================================ */
.ae-detail-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1.25rem;
}
/* Le card che tengono una tabella larga (colli, storico): riga intera. */
.ae-detail-grid > .ae-panel--wide { grid-column: 1 / -1; }
@media (max-width: 1200px) {
    .ae-detail-grid { grid-template-columns: minmax(0, 1fr); }
}

/* Due colonne di coppie etichetta/valore dentro una card larga (il riepilogo colli e pesi).
   Sono due tabelle separate e non una sola spezzata: una <table> non si divide in colonne.
   Il gap orizzontale e' largo apposta, 2.5rem, perche' le due colonne non si leggano come
   quattro colonne di un'unica tabella. */
.ae-detail-split {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0 2.5rem;
    /* start e non lo stretch di default: le due tabelle non hanno lo stesso numero di righe, e
       stirando la piu' corta all'altezza dell'altra le sue righe si allargavano, cadendo a quote
       diverse da quelle di sinistra. Cosi' ogni tabella tiene la sua altezza naturale e le righe
       delle due colonne si leggono in parallelo. */
    align-items: start;
}
/* Stessa proporzione etichetta/valore nelle due tabelle: con la larghezza automatica ognuna si
   tarava sul proprio testo piu' lungo e le due colonne venivano disallineate. */
.ae-detail-split .ae-detail-table th { width: 45%; }
@media (max-width: 1200px) {
    .ae-detail-split { grid-template-columns: minmax(0, 1fr); }
}

/* Tabella di dettaglio in coda a un riepilogo (l'elenco dei colli sotto i totali): staccata,
   altrimenti sembra la continuazione delle righe di sopra. */
.ae-detail-subtable { margin-top: 1.75rem; }

/* Riquadro in linea dentro una modale: un sotto-form che non merita una seconda finestra
   (es. "Nuova voce Custom" nel supplemento del contratto). */
.ae-form-hint--action { margin-top: .35rem; }
.ae-inline-panel { margin-top: .6rem; padding: .75rem .9rem; border: 1px solid var(--ae-border); border-radius: var(--ae-radius-input); background: var(--ae-bg); }
.ae-inline-panel__title { font-weight: 600; margin-bottom: .4rem; }
.ae-inline-panel__actions { display: flex; gap: .5rem; justify-content: flex-end; margin-top: .6rem; }
.ae-inline-panel__fields { display: grid; grid-template-columns: 7fr 5fr; gap: .6rem; }
.ae-inline-panel__field label { display: block; font-size: .82rem; color: var(--ae-muted); margin-bottom: .2rem; }
@media (max-width: 640px) { .ae-inline-panel__fields { grid-template-columns: 1fr; } }

/* ============================================================
   Righe etichetta/valore nelle card del dettaglio spedizione (05/10/2026).

   Le tabelle di dettaglio nascono come le liste: etichetta in rosso maiuscolo con la riga piena da
   2px sotto, uguale alle intestazioni del DxGrid. Su una tabella a colonne quella riga separa il
   titolo dai dati ed e' giusta; su una coppia etichetta/valore no: sta sotto la sola etichetta,
   quindi TAGLIA la riga a meta' invece di legarne i due lati, e ripetuta su sette righe diventa una
   scaletta di trattini rossi.

   Al suo posto un tratteggio che PARTE dove finisce l'etichetta e arriva al valore, come i puntini
   di un indice: CLIENTE _ _ _ _ _ Rossi Trasporti. Non sottolinea niente, collega, e l'occhio su una
   card larga non perde la riga per strada. Neutro e non rosso, perche' il rosso di marca deve
   restare sulle cose che contano (azioni, stati, intestazioni vere), non disegnare il reticolo di
   un modulo. Il colore si ricava da --ae-secondary con color-mix invece di essere una tinta nuova:
   resta agganciato alla palette e segue il tema se cambia.

   COME. Il tratteggio e' uno ::after dell'etichetta, non un bordo: un bordo sta sotto TUTTA la cella
   e ripartirebbe da sinistra, sotto il testo, mentre questo deve cominciare dove il testo finisce, e
   quanto sia lungo il testo lo sa solo il browser. L'::after e' un inline-block vuoto largo quanto
   tutta la cella: eccede sempre lo spazio rimasto, e a tagliarlo al bordo giusto ci pensa
   l'overflow: hidden della cella. Niente caratteri veri (trattini o puntini) nel content: sarebbero
   letti a voce dai lettori di schermo, un "trattino trattino trattino" a ogni riga.

   vertical-align: baseline su un inline-block vuoto allinea il suo bordo inferiore alla linea di
   base del testo: e' quello che fa cadere il tratteggio esattamente dove cadrebbe una sottolineatura
   dell'etichetta, invece che a mezza altezza.

   white-space: nowrap non e' un dettaglio, senza il tratteggio NON STA IN RIGA: largo quanto la
   cella, dopo il testo non ci entra e il browser lo manda a capo, disegnando una riga tratteggiata
   sotto l'etichetta invece che di fianco. Col nowrap resta attaccato al testo e sborda a destra,
   dove l'overflow lo taglia. L'etichetta non rischia di essere tagliata a sua volta: senza a capo la
   sua larghezza minima entra nel calcolo della colonna, che semmai si allarga.

   Provata anche la via "canonica" dei puntini d'indice, cioe' la cella a display: flex con uno
   spaziatore che cresce: inapplicabile qui. Una cella che cambia display smette di essere una cella,
   il browser le costruisce intorno una cella anonima e la percentuale di larghezza non ha piu' un
   riferimento: a schermo le etichette andavano a capo parola per parola e il tratteggio spariva.

   La larghezza in percentuale della colonna etichette e' necessaria, non estetica: con la larghezza
   automatica la colonna si stringe sul testo piu' lungo e di spazio per il tratteggio non ne resta.

   Selettore su tbody th: le due tabelle a colonne della pagina (elenco colli, storico stati) hanno
   le intestazioni nel thead e restano come sono, insieme a tutte le altre .ae-detail-table del
   Backoffice, che questa regola non raggiunge.
   ============================================================ */
.ae-detail-grid .ae-detail-table tbody th {
    width: 34%;
    border-bottom: 0;
    overflow: hidden;
    white-space: nowrap;
}
.ae-detail-grid .ae-detail-table tbody th::after {
    content: "";
    display: inline-block;
    width: 100%;
    height: 0;
    margin-left: .5rem;
    vertical-align: baseline;
    /* 25% del neutro: il tratteggio deve accompagnare l'occhio, non disegnare un reticolo. Sotto il
       20% su certi monitor sparisce del tutto e il collegamento non si vede piu'. */
    border-bottom: 1px dashed color-mix(in srgb, var(--ae-secondary) 25%, transparent);
}
/* Nelle due colonne dei colli la cella etichetta sta in meta' card: le serve una quota maggiore,
   altrimenti "Peso tassato dal corriere" non ci sta e al tratteggio non resta spazio. */
.ae-detail-grid .ae-detail-split .ae-detail-table tbody th { width: 46%; }

/* Lo stesso tratteggio etichetta -> valore nelle card fatte col form DevExpress (.ae-detail-form: scheda cliente,
   zona, utente, organizzazione, contratto...), richiesta del 21/10/2026 "come nel dettaglio spedizione".
   La didascalia DevExpress (.dxbl-fl-cpt) e' gia' come la cella etichetta qui sopra: una riga sola (nowrap),
   overflow nascosto e larghezza allineata fra le voci del gruppo. Quindi vale lo stesso trucco: un ::after largo
   quanto la didascalia, attaccato al testo, di cui l'overflow taglia la parte che sborda.
   Solo in lettura, cioe' quando il valore e' un .ae-detail-value: accanto a una casella di testo in modifica il
   tratteggio collegherebbe l'etichetta a un bordo, e sarebbe solo rumore. Solo didascalie a sinistra
   (.dxbl-fl-item-horizontal) e solo da 992px: e' la soglia da cui il tema le tiene su una riga (nowrap) e allineate
   a una larghezza comune; sotto vanno a capo e il tratteggio finirebbe sotto l'etichetta.
   text-overflow: clip perche' il tema mette i puntini di sospensione: col tratteggio che sborda apposta,
   "Stato del cliente…" comparirebbe sempre. Un'etichetta piu' lunga della colonna resta tagliata, come prima. */
@media (min-width: 992px) {
    .ae-detail-form .dxbl-fl-item.dxbl-fl-item-horizontal:has(.ae-detail-value) > .dxbl-fl-cpt {
        overflow: hidden;
        text-overflow: clip;
    }
    .ae-detail-form .dxbl-fl-item.dxbl-fl-item-horizontal:has(.ae-detail-value) > .dxbl-fl-cpt::after {
        content: "";
        display: inline-block;
        width: 100%;
        height: 0;
        margin-left: .5rem;
        vertical-align: baseline;
        border-bottom: 1px dashed color-mix(in srgb, var(--ae-secondary) 25%, transparent);
    }
}

/* Nota di import sotto il badge di stato nella lista spedizioni (anomalie delle "Importata"). */
.ae-import-note { color: var(--ae-warning-text); }
.ae-import-note i { margin-right: .25rem; }


/* Ritocchi call cliente 06/10/2026 */
.ae-grid .ae-ratecard-row--cost > td:first-child { box-shadow: inset 4px 0 0 var(--ae-info); }
.ae-grid .ae-ratecard-row--sell > td:first-child { box-shadow: inset 4px 0 0 var(--ae-success); }
/* Motivo di "Attiva contratto" spento: sopra i bottoni, a destra, e mandato a capo entro mezza card
   cosi' non spinge il titolo della card su una riga a se'. */
.ae-activate-blocked { max-width: min(36rem, 100%); }
.ae-last-failure { display: flex; gap: .5rem; align-items: flex-start; }
.ae-attempts-history > summary { cursor: pointer; }
.ae-unit-field { display: flex; align-items: center; gap: .35rem; }
.ae-unit-suffix { color: var(--ae-muted); font-size: .82rem; white-space: nowrap; }
.ae-matrix-row > td { background: var(--ae-bg); }
.ae-news-list + .ae-news-empty { padding: .6rem 1.1rem; font-size: .85rem; border-top: 1px solid var(--ae-border); }
/* Matrice prezzi del dettaglio listino: fino a 140 scaglioni, la tabella scorre dentro un riquadro invece di allungare la pagina. */
.ae-matrix-scroll { max-height: 420px; overflow: auto; border: 1px solid var(--ae-border); border-radius: var(--ae-radius-input); }
.ae-matrix-scroll thead th { position: sticky; top: 0; background: var(--ae-surface); z-index: 1; }
.ae-matrix-scroll td, .ae-matrix-scroll th { white-space: nowrap; }
/* Listino di vendita a ricarico: costo di partenza sotto il prezzo di vendita (US #18468) */
.ae-matrix-cost { font-size: .78rem; }
/* Controllo corrieri: barra di avanzamento della zona e miniature delle foto POD. */
.ae-couriers-progress { display: flex; height: 8px; width: 120px; margin-top: 4px; border-radius: 999px; overflow: hidden; background: var(--ae-tint); border: 1px solid var(--ae-border); }
.ae-couriers-photo img { width: 96px; height: 96px; object-fit: cover; border-radius: 8px; display: block; }
/* Righe non concluse del borderò (esito senza foto ricevuta, anomalia): testo rosso e barra a sinistra, come le
   righe di listino; la riga selezionata tiene il suo sfondo inline. */
.ae-grid .ae-couriers-line--open > td { color: var(--ae-danger-text); }
.ae-grid .ae-couriers-line--open > td:first-child { box-shadow: inset 4px 0 0 var(--ae-danger-text); }
.ae-couriers-photo-missing, .ae-couriers-cash-due { color: var(--ae-danger-text); font-weight: 600; }
/* Tracciato eventi (dettaglio spedizione e controllo corrieri): eventi critici in rosso. */
.ae-tracking-row--critical > td, .ae-tracking-row--critical > td .ae-form-hint { color: var(--ae-danger-text); }
.ae-tracking-row--critical > td:first-child { box-shadow: inset 4px 0 0 var(--ae-danger-text); }
.ae-tracking-current { display: inline-flex; align-items: center; gap: .4rem; flex-wrap: wrap; font-size: .9rem; }

/* Aree commerciali (B5, 15/10/2026) */
/* Editor di un gruppo di aree (/aree-commerciali/{id}): un blocco per area, con la testata (codice, descrizione,
   Resto d'Italia, cestino) in riga e sotto la tabella dei criteri. Stessi editor compatti di .ae-editor-table. */
.ae-areas-area {
    border: 1px solid var(--ae-border);
    border-radius: var(--ae-radius-input);
    padding: 1rem 1.1rem .85rem;
    margin-top: 1.25rem;
    background: var(--ae-surface);
}
/* Il Resto d'Italia si distingue a colpo d'occhio: e' l'area che prende tutto cio' che le altre non coprono. */
.ae-areas-area--rest { background: var(--ae-tint); border-style: dashed; }
.ae-areas-area-head { display: flex; align-items: flex-end; gap: .75rem; flex-wrap: wrap; }
.ae-areas-field { display: flex; flex-direction: column; gap: .2rem; min-width: 0; }
.ae-areas-field > label { font-size: .7rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--ae-primary); }
.ae-areas-field--code { flex: 0 1 11rem; }
.ae-areas-field--desc { flex: 1 1 16rem; }
.ae-areas-field--rest { flex: 0 0 auto; align-self: center; padding-top: 1rem; white-space: nowrap; }
.ae-areas-area-head .ae-row-actions { align-self: center; padding-top: 1rem; }
/* I codici (del gruppo e delle aree) viaggiano in maiuscolo verso il motore: si mostrano cosi' gia' mentre si scrivono. */
.ae-areas-code input { text-transform: uppercase; }
.ae-areas-rest-hint { margin: .5rem 0 0; }
.ae-areas-rest-hint i { margin-right: .25rem; }
/* Tabella dei criteri: il tipo e la provincia hanno etichette lunghe, i CAP stanno in poco. Le larghezze per
   posizione valgono per QUESTA tabella e non toccano .ae-editor-table dei ricarichi. */
.ae-areas-entries { margin-top: .5rem; }
.ae-areas-entries td:nth-child(1) .dxbl-combobox { min-width: 9rem; }
.ae-areas-entries td:nth-child(2) .dxbl-combobox { min-width: 16rem; }
.ae-areas-entries td:nth-child(2) .dxbl-text-edit,
.ae-areas-entries td:nth-child(3) .dxbl-text-edit { min-width: 8rem; max-width: 11rem; }
/* "Escludi": casella centrata sotto un titolo centrato (02/11/2026, prima il titolo restava a sinistra). */
.ae-areas-entries td.ae-areas-exclude { width: 1%; text-align: center; }
.ae-areas-entries th.ae-areas-exclude { text-align: center; }
/* Provincia e prefissi sulla stessa riga (02/11/2026): con i prefissi sotto la riga si alzava e il criterio accanto
   restava disallineato. Tutte le celle al centro in verticale, cosi' tendine e campi delle due colonne combaciano. */
.ae-areas-entries td { vertical-align: middle; }
.ae-areas-province { display: flex; align-items: center; gap: .75rem; }
.ae-areas-province > .dxbl-combobox { flex: 0 1 16rem; }
.ae-areas-prefixes { flex: 0 0 auto; white-space: nowrap; }
.ae-areas-prefixes i { margin-left: .35rem; color: var(--ae-warning-text); }
.ae-areas-entry-actions { display: flex; gap: .9rem; flex-wrap: wrap; margin-top: .35rem; padding-left: .3rem; }
.ae-areas-entry-actions .ae-row-action { padding: 0; }

/* Prezzi di vendita (B6, 15/10/2026) */
/* Editor dei prezzi propri per area e scaglione nel dettaglio listino (/listini/{id}). Classe propria: le larghezze
   di .ae-editor-table sono legate per nth-child alle colonne dei ricarichi e qui le colonne sono le aree, in numero
   variabile. La griglia scorre dentro .ae-matrix-scroll come la matrice in sola lettura. */
.ae-prices-head { display: flex; gap: 1.2rem; flex-wrap: wrap; align-items: flex-end; margin: .4rem 0 .8rem; }
.ae-prices-field { display: flex; flex-direction: column; gap: .25rem; min-width: 0; }
.ae-prices-field > label { font-size: .7rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--ae-primary); }
.ae-prices-field--areas { flex: 1 1 22rem; }
.ae-prices-field--areas .dxbl-combobox { min-width: 16rem; }
.ae-prices-field--basis .dxbl-combobox { min-width: 8rem; }
.ae-prices-field-row { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.ae-prices-grid td { padding: .35rem .3rem; vertical-align: middle; }
.ae-prices-grid tbody tr:hover td:not(:first-child) { background: transparent; }
.ae-prices-grid .dxbl-spin-edit { min-width: 6rem; max-width: 8rem; }
/* Prima colonna: "fino a [N] kg" con l'editor in linea; resta fissa mentre le aree scorrono. */
.ae-prices-grid th:first-child, .ae-prices-grid td:first-child { position: sticky; left: 0; background: var(--ae-surface); z-index: 1; }
.ae-prices-grid thead th:first-child { z-index: 2; }
.ae-prices-grid td:first-child .ae-unit-field .dxbl-spin-edit { min-width: 5.5rem; max-width: 7rem; }
.ae-prices-grid td:last-child { width: 1%; }
/* Riga dell'eccedenza: si distingue dagli scaglioni e porta i suoi tre campi in linea (passo, unita', limite). */
.ae-prices-overflow > td { background: var(--ae-tint); border-top: 2px solid var(--ae-border); }
.ae-prices-overflow > td:first-child { background: var(--ae-tint); }
.ae-prices-overflow-fields { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
.ae-prices-overflow-fields .dxbl-spin-edit { min-width: 4.5rem; max-width: 6rem; }
.ae-prices-rules { display: flex; gap: 1.2rem; flex-wrap: wrap; align-items: flex-end; margin-top: .9rem; }
.ae-prices-rules .dxbl-spin-edit { min-width: 7rem; max-width: 9rem; }
.ae-prices-rules .dxbl-combobox { min-width: 10rem; }
.ae-prices-notes { margin: .4rem 0 0 1rem; padding: 0; color: var(--ae-muted); font-size: .82rem; }
.ae-prices-copy { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-top: .6rem; }
.ae-prices-copy .dxbl-combobox { min-width: 20rem; }
.ae-prices-confirm { margin-top: .6rem; }
.ae-prices-confirm .ae-page-header-actions { margin-top: .5rem; }
.ae-prices-readonly-rules { display: flex; gap: 1.5rem; flex-wrap: wrap; margin-top: .5rem; font-size: .9rem; }
.ae-prices-readonly-rules span { color: var(--ae-muted); }

/* Calendario dei campi data (DxDateEdit): senza la colonna dei numeri della settimana, che DxDateEdit non
   permette di spegnere da parametro e che in ufficio non si usa. Va tolta anche la cella vuota che le fa da
   intestazione sopra i giorni, altrimenti "lun" scala di una colonna. */
.dxbl-calendar .dxbl-calendar-week-number,
.dxbl-calendar .dxbl-calendar-days-of-week > th:first-child:empty { display: none; }

/* Testa del popup del tracciato (controllo corriere): indirizzo e stato su due righe staccate, il badge
   distante dal link "Apri la spedizione", e aria prima della tabella degli eventi. */
.ae-couriers-trace-head { display: flex; flex-direction: column; gap: .6rem; margin-bottom: 1.1rem; }
.ae-couriers-trace-head p { margin: 0; }
.ae-couriers-trace-head p:last-child { display: flex; align-items: center; gap: .9rem; }

/* Uso da tastiera (js/keyboard-nav.js): chi si muove con le frecce deve vedere dove si trova. I campi e le
   griglie DevExpress hanno gia' il loro anello; servono i nostri bottoni testuali (azioni di riga, risultati
   della ricerca cliente) e la cella della griglia raggiunta con "giu'" dai filtri. Solo :focus-visible, cosi'
   il clic col mouse non lascia l'anello acceso. */
.ae-row-action:focus-visible,
.ae-picker-results button:focus-visible {
    outline: 2px solid var(--ae-primary);
    outline-offset: 2px;
    border-radius: 4px;
}
/* Riga raggiunta con le frecce: tutta la riga col colore dell'hover, al posto del riquadro sulla singola cella
   (quello di DevExpress, .dxbl-grid .dxbl-grid-table>tbody>tr>td:focus-visible). Il :not(#ae-none) alza la
   specificita' sopra la regola del tema senza dipendere dall'ordine dei fogli. :has(:focus-visible) prende
   anche il focus su un link o una casella dentro la riga, ma non il clic col mouse. */
.ae-grid .dxbl-grid-table > tbody > tr:has(:focus-visible) > td { background-color: var(--ae-tint); }
.ae-grid .dxbl-grid-table > tbody > tr > td:focus-visible:not(#ae-none) { outline: none; }

/* PEC e codice SDI nei "Dati fiscali e amministrativi" della scheda cliente: un valore per riga con Modifica e Rimuovi
   accanto, e "Aggiungi" sotto. Sono recapiti, ma letti come dati fiscali: niente tabella, solo il valore. */
.ae-fiscal-contacts { display: flex; flex-direction: column; align-items: flex-start; gap: .1rem; }
.ae-fiscal-contact { display: flex; align-items: center; gap: .35rem; flex-wrap: wrap; }
.ae-fiscal-contact .ae-detail-value { display: inline; overflow-wrap: anywhere; }

/* Statistiche nella Panoramica cliente: riga dei corrieri sotto i riquadri, testo e non grafico (poche voci). */
.ae-customer-stats .ae-tiles--kpi { margin-bottom: .75rem; }
/* Card pastello: fondo e bordo tenui della tinta, numero nel suo tono scuro, icona su un quadratino bianco in
   alto a destra. Ogni modificatore imposta solo le tre variabili locali. */
.ae-tile--pastel {
    --ae-tile-bg: var(--ae-surface); --ae-tile-border: var(--ae-border); --ae-tile-ink: var(--ae-text);
    background: var(--ae-tile-bg); border-color: var(--ae-tile-border); box-shadow: none;
}
.ae-tile--pastel .ae-tile-value { color: var(--ae-tile-ink); }
.ae-tile-head { display: flex; align-items: flex-start; justify-content: space-between; gap: .5rem; }
.ae-tile-icon {
    flex: none; display: inline-flex; align-items: center; justify-content: center;
    width: 1.9rem; height: 1.9rem; border-radius: .55rem;
    background: var(--ae-surface); color: var(--ae-tile-ink); font-size: 1rem;
}
.ae-tile--rose     { --ae-tile-bg: var(--ae-pastel-rose-bg);     --ae-tile-border: var(--ae-pastel-rose-border);     --ae-tile-ink: var(--ae-pastel-rose-text); }
.ae-tile--sage     { --ae-tile-bg: var(--ae-pastel-sage-bg);     --ae-tile-border: var(--ae-pastel-sage-border);     --ae-tile-ink: var(--ae-pastel-sage-text); }
.ae-tile--sky      { --ae-tile-bg: var(--ae-pastel-sky-bg);      --ae-tile-border: var(--ae-pastel-sky-border);      --ae-tile-ink: var(--ae-pastel-sky-text); }
.ae-tile--lavender { --ae-tile-bg: var(--ae-pastel-lavender-bg); --ae-tile-border: var(--ae-pastel-lavender-border); --ae-tile-ink: var(--ae-pastel-lavender-text); }
.ae-tile--butter   { --ae-tile-bg: var(--ae-pastel-butter-bg);   --ae-tile-border: var(--ae-pastel-butter-border);   --ae-tile-ink: var(--ae-pastel-butter-text); }
.ae-tile--peach    { --ae-tile-bg: var(--ae-pastel-peach-bg);    --ae-tile-border: var(--ae-pastel-peach-border);    --ae-tile-ink: var(--ae-pastel-peach-text); }
.ae-customer-stats-carriers { margin: 0 0 .35rem; font-size: .9rem; color: var(--ae-text); }
.ae-customer-stats-caption { color: var(--ae-muted); margin-right: .5rem; }
/* Elenco dei contratti nella scheda Contratto: la riga del contratto mostrato sotto resta evidenziata. */
.ae-grid .dxbl-grid-table > tbody > tr.ae-contracts-row--current > td { background-color: var(--ae-accent); font-weight: 600; }

/* Autocompletamento dell'indirizzo (AddressAutocomplete): la lista delle proposte sta nel flusso e non sopra il form,
   perche' tre form su cinque sono popup, dove una lista sovrapposta resterebbe tagliata dal bordo del corpo. */
.ae-address-picker { position: relative; }
.ae-address-picker .ae-picker-results { position: static; max-height: 260px; }
.ae-address-results .ae-row-action { display: flex; gap: .45rem; align-items: center; }
.ae-address-results .bi-geo-alt { color: var(--ae-muted); }
/* Editor dei CAP della zona: due azioni affiancate nell'intestazione e i tre campi dell'intervallo su una riga. */
.ae-section-header-actions { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.ae-route-range__fields { display: grid; grid-template-columns: 1fr 1fr 2fr; gap: .6rem; margin-bottom: .4rem; }
@media (max-width: 640px) { .ae-route-range__fields { grid-template-columns: 1fr; } }
.ae-route-range { margin-bottom: .75rem; }
.ae-route-range-info { display: block; margin-bottom: .6rem; color: var(--ae-success-text); }
/* Riquadro con piu' valori uno per riga (Listini di vendita della Panoramica cliente). */
.ae-tile-value--list { display: flex; flex-direction: column; gap: .15rem; font-size: 1rem; }

/* Prezzo della spedizione (US #18660), dettaglio e lista: margine negativo in rosso, sottoriga "di cui" sotto il
   totale, voci di vendita e di costo in un <details> a scomparsa, icona del tasto che gira durante il calcolo. */
.ae-price-negative { color: var(--ae-danger-text); font-weight: 600; }
.ae-price-sub { display: block; color: var(--ae-muted); font-size: .82rem; margin-top: .15rem; }
.ae-price-reason { margin: -.2rem 0 .6rem; }
.ae-price-outcome { margin-bottom: .75rem; }
.ae-price-breakdown { margin-top: 1rem; }
.ae-price-breakdown > summary { cursor: pointer; font-size: .9rem; font-weight: 600; color: var(--ae-text); }
.ae-price-breakdown .ae-detail-split { margin-top: .5rem; }
.ae-price-breakdown .ae-detail-split .ae-detail-table th { width: auto; }
.ae-price-amount { text-align: right; white-space: nowrap; }
/* Il titolo "Importo" a destra come gli importi (02/11/2026): .ae-detail-table th (0,1,1) lo teneva a sinistra. */
.ae-detail-table th.ae-price-amount { text-align: right; }
.ae-price-spin { display: inline-block; animation: ae-price-spin 1s linear infinite; }
@keyframes ae-price-spin { to { transform: rotate(360deg); } }

/* Giacenze (Feature #18699): lista e dettaglio dell'episodio. "2 di 3" in evidenza quando la spedizione ha gia' avuto
   altre giacenze; il ticket collegato sta su una riga con il suo badge; il banner "in carico a un collega" e l'avviso
   del ritorno al mittente riusano .ae-banner--warning e qui prendono solo i margini. Il riepilogo in sola lettura dei
   popup e' una dl a due colonne, cosi' resta leggibile anche nel corpo stretto del popup. */
.ae-storage-repeat { color: var(--ae-warning-text); font-weight: 600; }
.ae-storage-ticket { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.ae-storage-locked { margin-bottom: 1rem; }
.ae-storage-warning { margin-bottom: 1rem; }
.ae-storage-summary {
    display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: .25rem .9rem;
    margin: 0 0 1rem; font-size: .9rem;
}
.ae-storage-summary dt { color: var(--ae-muted); font-weight: 500; }
.ae-storage-summary dd { margin: 0; color: var(--ae-text); overflow-wrap: anywhere; }

/* ============================================================
   Esportazione Excel delle liste (US #18781): il pulsante sta nella barra della pagina o in coda alla
   toolbar dei filtri; il messaggio (tetto superato, errore) gli resta accanto, sulla stessa riga.
   ============================================================ */
.ae-export { display: inline-flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.ae-export-error { font-size: .85rem; color: var(--ae-danger-text); }
.ae-list-toolbar .ae-export { flex: 0 0 auto; margin-left: auto; }

/* Finestra rapida delle giacenze (US #18779, riordinata il 02/11/2026).
   - Riga di stato sotto il titolo: etichetta "Stato" e badge, come una riga di intestazione.
   - Riquadro Azioni con la forma dei gruppi del form (bordo tenue, raggio 12px, titolo in alto) e due righe di
     bottoni senza titoli, separate da una linea: presa in carico sopra, cosa fare della merce sotto. Sotto ogni
     riga, in piccolo, perche' un'azione e' spenta.
     Spazi generosi tra titolo, righe e bottoni: il riquadro raccoglie molti comandi e stretto si leggeva come un blocco.
     Le azioni che chiudono la giacenza (.ae-quick-actions-terminal) vanno in fondo alla riga, staccate dalle altre.
   - Piede: la navigazione nella lista (.ae-quick-nav) a sinistra, le aperture (ticket, dettaglio) a destra. */
.ae-quick-status { display: flex; align-items: center; flex-wrap: wrap; gap: .5rem; margin-bottom: 1rem; }
.ae-quick-status-label { font-size: .85rem; font-weight: 500; color: var(--ae-muted); }
.ae-quick-actions {
    margin-top: 1.25rem;
    padding: 1.1rem 1.35rem 1.25rem;
    border: 1px solid var(--ae-border); border-radius: 12px;
}
/* Due classi: .ae-popup-form p { margin: 0 } (0,1,1) azzerava il margine di una classe sola, e il titolo restava attaccato. */
.ae-quick-actions .ae-quick-actions-title { margin: 0 0 1.5rem; font-weight: 600; color: var(--ae-text); }
.ae-quick-actions-row + .ae-quick-actions-row { margin-top: 1.25rem; padding-top: 1.25rem; border-top: 1px solid var(--ae-border); }
.ae-quick-actions-buttons { display: flex; align-items: center; flex-wrap: wrap; gap: .75rem; }
.ae-quick-actions-terminal { margin-left: auto; }
.ae-quick-actions-row > .ae-form-hint { margin: .65rem 0 0; }
.ae-quick-actions-blocked { margin: .65rem 0 0; padding-left: 1.1rem; font-size: .82rem; color: var(--ae-muted); }
.ae-quick-actions-blocked li + li { margin-top: .2rem; }
.ae-quick-actions-blocked strong { font-weight: 600; color: var(--ae-text); }
.ae-quick-nav { display: flex; align-items: center; gap: .6rem; margin-right: auto; }

/* ============================================================
   Ritocchi di impaginazione dalla verifica a video del 04/11/2026, finestra da 1440px: tolto il menu (280px), il
   padding del contenuto e quello del pannello, a una lista restano circa 1050px. I MinWidth delle colonne delle
   liste sotto sono tarati perche' la loro somma ci stia: aggiungendo una colonna va rifatto il conto, altrimenti la
   tabella torna a scorrere e la colonna ferma a destra copre quelle accanto.
   ============================================================ */

/* Liste fitte (spedizioni, giacenze): meno respiro orizzontale nelle celle. Con undici colonne il padding standard
   (.8rem per lato) da solo valeva quasi 300px. Stessa specificita' di .ae-grid.dxbl-grid: vince perche' viene dopo.
   I titoli tornano ad andare a capo fra le parole: con colonne strette uno su una riga sola sbordava sul vicino. */
.ae-grid--dense.dxbl-grid,
.ae-grid--dense .dxbl-grid { --dxbl-grid-text-cell-padding-x: .45rem; }
:root .ae-grid--dense .dxbl-grid-table > thead > tr > th > .dxbl-grid-header-content > span { white-space: normal; }

/* Colonna che va a capo dentro una lista "una riga per testo" (.ae-grid--nowrap): la classe sulla cella la mette
   CustomizeElement della pagina. Nomi, indirizzi e importi su due righe invece di allargare la tabella. */
:root .ae-grid--nowrap .dxbl-grid-table > tbody > tr > td.ae-cell-wrap,
:root .ae-grid--nowrap .dxbl-grid-table > tbody > tr > td.ae-cell-wrap div {
    white-space: normal;
    overflow-wrap: anywhere;
}
:root .ae-grid--nowrap .dxbl-grid-table > tbody > tr > td.ae-cell-wrap .ae-list-link { display: inline; overflow: visible; }

/* Testo senza spazi (email, nomi di file) che in una cella stretta deve poter andare a capo dove capita. */
.ae-text-break { overflow-wrap: anywhere; }

/* Esiti del borderò lunghi ("Mancata consegna senza foto"): il badge va a capo invece di uscire dalla colonna. */
.ae-bordero-lines .ae-badge { white-space: normal; }

/* Lista vuota in una tabella piu' larga del pannello (coda di stampa): il messaggio si centrava su tutta la larghezza
   della tabella e finiva tagliato a destra. Cosi' resta sulla parte visibile, al massimo larga quanto la griglia (la
   misura la scrive grid-scroll.js in --ae-grid-visible-w); con la tabella che non scorre non cambia nulla. */
.ae-grid--nowrap .ae-empty-state {
    position: sticky; left: 0;
    box-sizing: border-box;
    max-width: var(--ae-grid-visible-w, none);
}

/* Segnaposto del primo caricamento (LoadingVeil): sotto il velo si vedeva attraverso, coperto a meta' dal furgone
   ("aricamento..."). Resta a occupare l'altezza del pannello, ma senza testo quando il velo c'e'. */
body:has(.ae-loading-veil) .ae-loading-placeholder { visibility: hidden; }

/* Paragrafi sciolti nelle modali: .ae-popup-form p azzera i margini, e la frase prima del form ("Spedizione ...,
   corriere attuale ...") restava attaccata alla didascalia sotto, come la nota dopo il form alla tabella che segue. */
.ae-popup-form > p:not(:last-child) { margin-bottom: .9rem; }
.ae-popup-form > * + p { margin-top: .9rem; }

/* Sezione nel suo riquadro dentro un pannello, con lo stesso bordo e raggio dei gruppi del form (nuova zona: CAP e
   comuni coperti sotto il gruppo Zona). */
.ae-boxed-section {
    margin-top: 1rem;
    padding: 1rem 1.1rem;
    border: 1px solid var(--ae-border);
    border-radius: 12px;
}
