/**
 * BI Perspect - App Stylesheet
 * Single source of truth for all UI styles
 *
 * SECTIONS:
 * 1. Design Tokens (CSS Variables)
 * 2. Base & Reset
 * 3. Typography
 * 4. Layout & Sidebar
 * 5. Cards & Widgets
 * 6. Buttons
 * 7. Forms & Inputs
 * 8. Tables
 * 9. Badges, Pills, Chips
 * 10. Progress Bars
 * 11. Tabs
 * 12. Charts
 * 13. Alerts
 * 14. Utilities
 */

/* =============================================================================
   1. DESIGN TOKENS
   ============================================================================= */

:root {
    /* ═══ Palette definitiva — la fonte e' DESIGN_SYSTEM.md §Palette ═══
       Nessun conteggio qui: diceva «11» mentre ne erano 14, e un numero
       scritto in tre file si aggiorna in uno solo. */
    --snow:         #FAFAFA;   /* Bright Snow — sfondo body e widget */
    --platinum:     #F2F2F2;   /* Platinum — sfondo hover, header tabelle */
    --alabaster:    #E4E4E4;   /* Alabaster Grey — bordi, divisori */
    --silver:       #9C9D9E;   /* Silver — bordi, campioni, e testo SOLO su fondo scuro:
                                  5,64:1 su --jet, ma 2,43:1 su --platinum. Su fondo
                                  chiaro non e' un testo, e' una decorazione. */
    --stone:        #686868;   /* Stone — testo muted leggibile: 5,57:1 su bianco · 5,34 su
                                  --snow · 4,98 su --platinum (misurato il 23/09/2026), sopra
                                  il 4,5 di WCAG AA su tutti i fondi chiari di casa. */
    --iron:         #4D4D4C;   /* Iron Grey — testo corpo su bianco */
    --jet:          #252525;   /* Jet — titoli, primario, sidebar (--dark-gray del sito) */

    /* Marca Starmatik (tema del sito). Vive solo nella zona scura: logo e voce attiva
       della sidebar. Nei contenuti il rosso vuol dire errore (--cherry), e i due rossi
       hanno contrasto reciproco 1,02: all'occhio sono lo stesso colore. */
    --marca:          #BB2830;
    --marca-logo:     #85160E;
    --burgundy:      #93102D;   /* Burgundy — dark red */
    --cherry:        #C21B3F;   /* Intense Cherry — bright red */
    --dark-emerald:  #1B6B52;   /* Dark Emerald — dark green */
    --seaweed:       #1AAD7E;   /* Seaweed — bright green */
    --princeton:     #FB850F;   /* Princeton Orange */

    /* Ambra scura — la versione di --princeton che si puo' usare come TESTO.
       Princeton nudo su fondo chiaro da' 2,25-2,48:1: sotto il 4,5:1 di WCAG per
       un testo, quindi su fondo chiaro serve solo come fondo o come tratto di un
       grafico. Questa da' 5,68:1 sul fondo del badge --warn e 6,17-6,79 sui fondi
       di casa. Sta fra i colori pieni e non fra le tinte trasparenti perche' e'
       un colore, non una derivazione in alpha. Scelta ambrata e non ruggine per
       non avvicinarsi a --cherry: warn e danger convivono nella stessa colonna. */
    --princeton-dark: #8A4B08;

    /* Il colore dell'INFORMAZIONE (`ui-alert--info`), riportato da Spark il
       26/08/2026. Neutro non vuol dire invisibile: questi avvisi dicono da dove
       vengono i dati che si stanno leggendo, e in grigio si leggevano come una
       nota di servizio. Non si e' riusato nessuno dei tre colori vivi perche'
       hanno gia' un significato — verde riuscito, ambra c'e' un'azione, rosso
       guasto — e un avviso verde che dice «il dato arriva dal gestionale» manda
       un segnale contrario al proprio testo.
       ⚠️ Un colore si dichiara in DUE posti e sono due cose diverse: questa
       variabile serve al CSS di casa, il token nel `tailwind.config` di
       `views/partials/header.php` e' cio' che GENERA `text-sapphire`. Senza il
       secondo la classe rende l'iron ereditato, e la sonda del contrasto la
       ASSOLVE, perche' un colore ereditato un contrasto ce l'ha. */
    --sapphire:       #1B5A7A;

    /* Tinte trasparenti (derivate dai colori della palette) */
    --seaweed-light: rgba(27, 107, 82, 0.12);
    --cherry-light: rgba(194, 27, 63, 0.12);
    --princeton-light: rgba(251, 133, 15, 0.12);

    /* Base */
    --white: #ffffff;

    /* Elevazione — due sole misure, e si riusano.
       L'elevazione e' spenta globalmente piu' sotto (`box-shadow: none`); queste
       servono ai pannelli che galleggiano su contenuto chiaro, dove il solo
       bordo da 1px misura 1,21:1 contro il fondo e non dice niente. */
    --elevazione-pannello: 0 8px 20px rgba(37, 37, 37, 0.12);
    --elevazione-tooltip:  0 4px 12px rgba(37, 37, 37, 0.10);

    /* Anello di focus — un colore solo per tutta l'applicazione.
       Jet da' 13,8:1 sui fondi chiari, contro il 3:1 che WCAG 2.2 chiede a un
       indicatore di focus. Si dichiara come `outline`, MAI come `box-shadow`:
       la regola globale piu' sotto spegne ogni ombra e un anello dichiarato
       cosi' non viene disegnato mai. */
    --focus-ring: var(--jet);

    /* Text */
    --text-heading: var(--jet);       /* Jet Black — titoli, testo su grigio/sidebar */
    --text-body: var(--iron);         /* Iron Grey — testo corpo su sfondo bianco */
    --text-muted: var(--iron);        /* Iron — testo secondario/muted (helpers, label uppercase) */
    --text-light: var(--stone);       /* Stone — testo terziario, placeholder, icone decorative */

    /* Primary = jet, hover = iron: l'arancio al passaggio prometteva un avviso */
    --primary-dark: var(--jet);
    --primary: var(--jet);
    --primary-mid: var(--jet);
    --primary-soft: var(--silver);
    --primary-hover: var(--iron);
    --primary-light: rgba(37, 37, 37, 0.06);

    /* Accent = princeton orange (accento UI, non finanziario) */
    /* ⚠️ Valeva `var(--princeton)`, cioe' la stessa tinta piena: un nome che
       promette la variante leggibile e ne consegna una da 2,25:1 su fondo
       chiaro. Dal 24/08/2026 e' davvero la scura. */
    --accent-dark: var(--princeton-dark);
    --accent: var(--princeton);
    --accent-mid: var(--princeton);
    --accent-hover: var(--princeton);
    --accent-light: rgba(251, 133, 15, 0.10);

    /* Borders and surfaces */
    --border: var(--alabaster);
    --border-hover: var(--alabaster);
    --border-focus: var(--iron);
    --bg-page: var(--platinum);       /* Platinum — sfondo pagina/body */
    --bg-card: var(--snow);           /* Bright Snow — sfondo card/widget */
    --bg-hover: rgba(37, 37, 37, 0.04);
    --bg-subtle: var(--platinum);
    --bg-muted: var(--platinum);

    /* Table */
    --table-bg: var(--white);
    --table-row-alt: var(--platinum);
    --table-header-bg: var(--platinum);
    --table-row-hover: rgba(37, 37, 37, 0.04);
    --table-font-size: 14px;
    --table-link-color: var(--jet);
    --table-link-hover: var(--iron);
    --table-focus-border: var(--iron);
    --table-focus-ring: rgba(37, 37, 37, 0.15);
    --table-action-hover-bg: rgba(37, 37, 37, 0.04);
    --table-action-hover-text: var(--jet);
    --table-page-active-bg: var(--iron);
    --table-page-active-text: var(--white);
    /* ⚠️ Era `--silver`: 1,95:1 sul fondo platinum dell'intestazione, cioe'
       sotto AA su OGNI tabella dell'applicazione. `--iron` da' 6,89:1, ed e'
       gia' il colore che gran parte del markup forzava a mano con
       `text-ui-body`. */
    --table-header-text: var(--iron);

    /* Component accents */
    --loader-overlay-color: rgba(37, 37, 37, 0.15);
    --ai-card-bg-start: rgba(37, 37, 37, 0.04);
    --ai-card-bg-end: rgba(37, 37, 37, 0.02);
    --ai-card-border: rgba(37, 37, 37, 0.15);
    --ai-card-hover-shadow: rgba(37, 37, 37, 0.10);
    --ai-card-hover-border: rgba(37, 37, 37, 0.25);
    --ai-card-accent-start: rgba(37, 37, 37, 0.06);
    --ai-card-accent-end: rgba(37, 37, 37, 0.03);
    --ai-card-title-accent: var(--jet);
    --ai-card-chip-bg: rgba(37, 37, 37, 0.08);
    --ai-card-chip-text: var(--jet);

    /* Internal navigation */
    --internal-menu-bg: var(--platinum);
    --internal-menu-border: var(--border);
    --internal-menu-item-text: var(--stone);
    --internal-menu-item-hover-bg: rgba(37, 37, 37, 0.04);
    --internal-menu-item-hover-text: var(--jet);
    --internal-menu-active-bg: var(--jet);
    --internal-menu-active-text: var(--white);
    --internal-menu-active-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
    --internal-menu-focus-ring: rgba(37, 37, 37, 0.20);

    /* Sidebar — dark (corporate monochrome) */
    --sidebar-bg: var(--jet);
    --sidebar-border: rgba(255, 255, 255, 0.08);
    --sidebar-text: rgba(255, 255, 255, 0.85);
    --sidebar-text-muted: rgba(255, 255, 255, 0.55);   /* 45% dava 4,27:1 sul jet, sotto soglia */
    --sidebar-hover-bg: rgba(255, 255, 255, 0.06);
    --sidebar-rapidi-bg: rgba(255, 255, 255, 0.04);
    --sidebar-tree-line: rgba(255, 255, 255, 0.12);
    /* Sidebar nav active card */
    --sidebar-nav-active-bg: rgba(255, 255, 255, 0.10);
    --sidebar-nav-active-text: #ffffff;
    /* Sidebar del backoffice (§20): velo del cassetto, bordo al passaggio, barra di scorrimento.
       Niente token d'ombra: `body * { box-shadow: none !important }` (sezione elevazione) toglie
       l'elevazione a tutta la UI, il cassetto si stacca dalla pagina col velo. */
    --sidebar-velo: color-mix(in srgb, var(--jet) 45%, transparent);
    --sidebar-border-hover: rgba(255, 255, 255, 0.25);
    --sidebar-scrollbar: rgba(255, 255, 255, 0.15);

    /* Semantic — green/red ONLY for financial meaning */
    --income: var(--seaweed);      /* #1AAD7E */
    --expense: var(--cherry);  /* #C21B3F */
    --success: var(--seaweed);        /* #1AAD7E */
    --danger: var(--cherry);         /* #C21B3F */
    --warning: var(--princeton);     /* #FB850F */
    --info: var(--silver);
    /* ⚠️ Le varianti SCURE, non le tinte piene: questi tre token si usano solo
       come `color` e `border-color` — mai come fondo — e seaweed/princeton su
       chiaro danno 2,60 e 2,25, cioe' sotto il 4,5 del testo e sotto il 3 che
       WCAG chiede a un bordo di stato. Cambiarli qui sistema in un colpo i
       feedback dei form, le icone dentro i campi e i bordi is-valid/is-warning. */
    --ui-success: var(--dark-emerald);
    --ui-danger: var(--cherry);
    --ui-warning: var(--princeton-dark);

    /* Accent color for native controls */
    accent-color: var(--iron);

    /* Unified radius — Corporate: tighter */
    --sidebar-active-radius: 4px;
    --interactive-radius: 4px;
    --badge-radius: 4px;

    /* Typography */
    --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    --font-mono: 'Roboto Mono', monospace;

    /* Spacing - Premium (more generous) */
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 20px;   /* ← Up from 16px */
    --space-5: 28px;   /* ← Up from 20px */
    --space-6: 36px;   /* ← Up from 24px */
    --space-8: 48px;   /* ← Up from 32px */

    /* Radius - Corporate: tight, minimal */
    --radius-xs: 2px;
    --radius-sm: 3px;
    --radius-md: 4px;
    --radius-lg: 6px;
    --radius-xl: 8px;
    --radius-2xl: 12px;

    /* Lo stacco fra un avviso e il contenuto che segue. In una variabile perche'
       lo dichiarano DUE regole — l'avviso fratello del contenuto e lo slot che
       ne contiene uno solo — e due letterali divergono alla prima modifica. */
    --spazio-avviso: 16px;

    /* Il respiro verticale di una cella. In una variabile perche' `th` e `td`
       devono muoversi INSIEME: se divergono, l'intestazione e le righe si
       disallineano in altezza e la tabella sembra storta senza che nulla lo
       dica — oggi sono allineate a 0,75rem e la variabile serve a tenerle tali.
       ⚠️ DIVERGENZA VOLUTA da Spark, che il 25/08/2026 l'ha alzata a 0,9rem
       perche' la sua scheda anagrafica si leggeva compressa. Qui le tabelle sono
       finanziarie e larghe — dodici colonne e oltre — e 0,9rem porterebbe la
       riga da 43 a ~48 px (misurato in Chrome 151 su `?page=template`), cioe'
       meno righe a schermo su ogni vista del CdG. E' una scelta di densita' da
       fare con Nicola, non un travaso: qui si porta la variabile, non il valore. */
    --cella-y: 0.75rem;

    /* Elevations - Corporate: subtle, nearly flat */
    --elevation-1: 0 1px 2px rgba(0, 0, 0, 0.04);
    --elevation-2: 0 2px 4px rgba(0, 0, 0, 0.06);
    --elevation-3: 0 4px 8px rgba(0, 0, 0, 0.08);
    --elevation-4: 0 8px 16px rgba(0, 0, 0, 0.10);

    /* Transizione di base: la curva di casa (--ease-out, qui sotto), non una sua copia. */
    --transition: 200ms var(--ease-out);

    /* Curve di casa. `ease` e' un in-out debole: parte lento proprio nell'istante
       in cui si guarda. Chi entra o esce usa --ease-out, chi si muove a schermo
       --ease-in-out. */
    --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
    --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);

    /* Tracking per gradino di dimensione. Il testo grande legge troppo largo
       se non lo si stringe, il minuscolo troppo stretto se non lo si allarga:
       un solo letter-spacing per tutta la pagina e' sbagliato da qualche parte.
       In prova nella palestra (?page=template#tipografia): i 35 valori
       esistenti non sono ancora ricondotti qui. */
    --tracking-display: -0.02em;   /* >= 24px: titoli, valori KPI */
    --tracking-body: -0.01em;      /* 13-16px: corpo, celle — e' cio' che `body` applica */
    --tracking-label: 0.08em;      /* <= 11px maiuscoletto: etichette, badge */

    /* Sidebar Width — 260 misurati su BI il 21/09/2026 (LB-4); ridotta a icone: 72 */
    --sidebar-width: 260px;
    --sidebar-collapsed: 72px;
    /* Allargarsi e stringersi della sidebar: resta a schermo, quindi ease-in-out (LB-6). */
    --sidebar-durata-riduzione: 200ms;
}

/* =============================================================================
   2. BASE & RESET
   ============================================================================= */

*, *::before, *::after {
    box-sizing: border-box;
}

body {
    font-family: var(--font-sans);
    font-size: 14px;
    line-height: 1.5;
    color: var(--text-body);
    background: var(--bg-page);
    letter-spacing: -0.01em;   /* ← Minimal: Slightly less tight */
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* Prevent content overflow in widgets and tables */
.widget-card,
.overflow-x-auto,
.ui-tabs,
.ui-tab-panels,
.ui-tab-panel {
    min-width: 0;
}

/* Text overflow utilities */

/* =============================================================================
   3. TYPOGRAPHY
   ============================================================================= */

/* In `@layer base` come i link qui sotto: fuori dai layer batteva ogni utility di Tailwind v4, e il
   `text-ui-muted` degli `h4` di sezione usciva jet (revisione design system, 22/09/2026). */
@layer base {
    h1, h2, h3, h4, h5, h6 {
        color: var(--text-heading);
        font-weight: 600;
        line-height: 1.3;
    }
}

/* In `@layer base` e solo sui link senza classi: fuori da un layer questa regola batteva
   ogni utility di Tailwind v4 (che sta in `@layer utilities`, e un layer perde sempre
   contro il CSS senza layer), e il marchio `text-ui-heading`, le icone d'azione delle
   tabelle e i bottoni `a.ui-btn` uscivano arancioni e sottolineati. Un link con classi
   e' un componente: il suo aspetto lo decidono le classi. */
@layer base {
    a:not([class]) {
        color: var(--jet);
        text-decoration: underline;
    }

    a:not([class]):hover {
        color: var(--jet);
        text-decoration: none;
    }
}

code {
    font-family: var(--font-mono);
    font-size: 0.9em;
    color: var(--iron);
    /* Un nome tecnico non ha spazi dove andare a capo: `.error-state` in mezzo a una
       frase spingeva la riga oltre il bordo, e `main.main-content` ha
       `overflow-x: hidden`, quindi il pezzo finale spariva invece di dare una barra.
       Misurato il 14/09/2026 su `?page=template`: 52 px a 1440 e 464 a 768.
       ⚠️ Dentro un `<pre>` questa riga non fa niente, ed e' voluto: con
       `white-space: pre` il testo non va a capo comunque, e quei blocchi hanno gia'
       il proprio `overflow-x: auto` — un blocco di codice si scorre, non si spezza. */
    overflow-wrap: anywhere;
}

/* =============================================================================
   5. CARDS & WIDGETS
   ============================================================================= */

/* Widget Variants */
.widget {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    border-left: 3px solid var(--iron);
    padding: var(--space-5);
}

/* Widget Card - Corporate Flat */
.widget-card {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 20px;
    overflow: visible;
    box-shadow: none;
    transition: border-color var(--transition);
}

.widget-card:hover {
    border-color: var(--border-hover);
}

/* Widget Header - Metabolis Style: Flat, no glassmorphism */
.widget-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    margin: 0 0 12px 0;               /* ← Metabolis: More space below */
    padding: 0;                       /* allineato al widget-body (padding 0) — no rientro titolo */
    background: transparent;          /* ← Metabolis: No background */
    /* No blur, no border-bottom */
}

.widget-header .widget-title {
    margin-bottom: 0;
}

/* ⚠️ L'info-circle sta SEMPRE all'estrema destra, come ULTIMO elemento
   dell'intestazione (regola di Nicola, 26/08/2026). Il `justify-content:
   space-between` distribuisce i figli, quindi in un'intestazione a tre — titolo,
   tooltip, filtro — il tooltip finiva **al centro**, dove non si legge come la
   spiegazione del widget ma come un elemento a se'. Il titolo si prende lo
   spazio libero con `margin-right: auto` e il resto si impacchetta a destra;
   `order` mette la «i» in coda a qualunque comando ci sia. Vale per tutte le
   intestazioni insieme: scritto vista per vista, il primo widget nuovo lo
   sbaglia di nuovo. */
.widget-header > .widget-title {
    margin-right: auto;
}

.widget-header > .ui-tooltip {
    order: 99;
}
.widget-header-actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

/* Widget Body - Corporate Flat */
.widget-body {
    background: transparent;
    border-radius: 0;
    padding: 0;
    box-shadow: none;
    /* Container per `.ui-campi`: le sue due colonne devono rispondere alla
       larghezza REALE del corpo, non a quella del viewport — la stessa card
       misura 546 px o 338 px dentro uno split 1/2+1/2 e 828 px sotto la sua
       rottura a `lg`, e un `@media` guarda solo il viewport.
       ⚠️ `inline-size` e non `size`: il containment e' su layout, stile e
       larghezza, NON su `paint`, quindi un discendente `position: fixed`
       continua ad ancorarsi al viewport — e il BI ci conta, i menu della
       sidebar si posizionano con `getBoundingClientRect()` proprio per uscire
       dal contenitore. Misurato prima e dopo su `?page=template`: 26 canvas
       grandi, gli stessi 20 widget alle stesse dimensioni, altezza documento
       21604 px, otto elementi `fixed`, nessuno scorrimento orizzontale. */
    container-type: inline-size;
}

.widget-body canvas,
.widget-body .ui-chart-container {
    overflow: hidden;
}

/* Text list block (component: text-blocks -> text-list) */

/* Widget Title - Corporate Monochrome */
.widget-title {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--iron);
    margin-bottom: 10px;
    line-height: 1;
    /* Flex item dentro .widget-header: senza questo non si restringe sotto il proprio
       contenuto e l'ellipsis dichiarata sullo span non scatta mai. */
    min-width: 0;
}

/* Titolo di card sulla riga delle azioni: `mb-0` di Tailwind sta in un layer e perdeva contro
   `.widget-title` (qui e nella media query mobile, entrambe fuori layer). 0,2,0 le batte entrambe e
   lascia i 4px dell'header dei menu (`.ui-quickfilter-menu .ui-dropdown-header.widget-title`). */
.widget-title.mb-0 {
    margin-bottom: 0;
}

.widget-title i {
    font-size: 12px;
    color: var(--iron);
    flex-shrink: 0;
}

.widget-title i.bi-info-circle {
    color: var(--iron);
}

.widget-title span {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    /* Idem: l'ellipsis su un flex item richiede min-width 0, altrimenti il titolo sfora
       la card invece di troncarsi. Si vedeva a 1024 sui widget in colonna da 1/3. */
    min-width: 0;
}

/* Widget compact - No body wrapper needed */

/* Widget flat - No border/shadow */

/* =============================================================================
   KPI WIDGET VARIANTS

   .kpi-widget  - Standard KPI (header+body, left-aligned, 3-4 per row)
   .kpi-mini    - Compact KPI (inline, no header/body, 5+ per row)
   ============================================================================= */

/* KPI Widget - Standard left-aligned with header+body */

/* KPI Widget with highlight - Corporate subtle */

/* Le varianti che REGGONO come testo: `seaweed` su bianco misura 2,87:1 e
   `princeton` 2,48 — sotto anche la soglia di 3:1 del testo grande — mentre
   `dark-emerald` fa 6,42 e `princeton-dark` 6,79.
   Fino all'11/09/2026 questa cascata ammetteva solo seaweed e cherry, quindi
   `text-dark-emerald` su un KPI veniva scavalcato da `color: var(--jet)` e il
   valore tornava nero senza errori (misurato su `#kpi-prezzo`).
   ⚠️ Le due righe gemelle per `.text-seaweed` sono uscite il 14/09/2026 INSIEME
   al markup che le nominava: `text-seaweed` e `text-princeton` non esistono piu'
   come colore di testo in nessuna vista (137 punti su 29 file). Rimetterle qui
   senza rimettere il markup e' codice morto; rimettere il markup e' tornare a
   2,87:1. */

/* KPI Mini - Compact inline structure */
.widget-card.kpi-mini {
    padding: var(--space-4);
    background: var(--white);
}

.kpi-mini .widget-title {
    margin-bottom: var(--space-2);
}

.kpi-mini .kpi-value {
    font-size: 1.75rem;        /* 28px */
    font-family: var(--font-mono);
    font-weight: 500;
    color: var(--jet);
    line-height: 1.05;
    letter-spacing: -0.02em;
    margin-top: 8px;
}

.kpi-mini .kpi-value.text-dark-emerald  { color: var(--dark-emerald); }
.kpi-mini .kpi-value.text-princeton-dark { color: var(--princeton-dark); }

.kpi-mini .kpi-label {
    font-size: 11px;
    color: var(--iron);
    margin-top: 4px;
}

/* Bilancio: sezione Performance (6 KPI) */

/* Section Title - Titoli sezione pagina */

/* =============================================================================
   6. TABLES
   ============================================================================= */

/* Sortable Tables */
.sortable-table th[data-sort] {
    cursor: pointer;
    user-select: none;
    position: relative;
    padding-right: 20px;
}
.sortable-table th[data-sort].text-right,
.sortable-table th[data-sort].text-center {
    padding-right: 24px;
}
/* Preserve sticky positioning when both sticky and sortable */

.sortable-table th[data-sort]:hover {
    color: var(--text-heading);
}
.sortable-table th[data-sort]::after {
    content: '⇅';
    position: absolute;
    right: 6px;
    top: 50%;
    transform: translateY(-50%);
    font-family: var(--font-sans);
    font-size: 10px;
    opacity: 0.3;
}
.sortable-table th[aria-sort="ascending"]::after {
    content: '↑';
    opacity: 0.8;
    color: var(--accent);
}
.sortable-table th[aria-sort="descending"]::after {
    content: '↓';
    opacity: 0.8;
    color: var(--accent);
}

/* Ordinamento di questo progetto (LB-7, `BiUI.initOrdinamento`): intestazioni
   `th[data-ordina]`, il cui contenuto ui.js avvolge in un bottone con l'icona
   Bootstrap. Le frecce `::after` di BI qui sopra scattano su ogni `aria-sort`:
   su queste intestazioni si spengono, o l'icona comparirebbe due volte. */
.sortable-table th[data-ordina]::after {
    content: none;
}
.ui-ordina {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 0;
    border: 0;
    background: transparent;
    font: inherit;
    color: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    text-align: inherit;
    cursor: pointer;
    transition: color 150ms var(--ease-out);
}
.ui-ordina:hover {
    color: var(--text-heading);
}
.ui-ordina:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}
.ui-ordina-icona {
    font-size: 10px;
    opacity: 0.45;
}
.sortable-table th[aria-sort="ascending"] .ui-ordina-icona,
.sortable-table th[aria-sort="descending"] .ui-ordina-icona {
    opacity: 1;
    color: var(--text-heading);
}

/* Sticky table header — intestazione fissa allo scroll. Richiede un wrapper
   con overflow-auto + max-height; bg opaco per coprire le righe sottostanti.
   Il secondo selettore batte la specificità di `.sortable-table th[data-sort]`
   (che imporrebbe position:relative sui th ordinabili). */

/* Zebra rows - global table alignment */
table tbody tr:nth-child(odd) td:not([class*="bg-"]) {
    background-color: var(--table-bg);
}

table tbody tr:nth-child(even) td:not([class*="bg-"]) {
    background-color: var(--table-row-alt);
}

/* Tabella filtrabile: le righe nascoste restano nel DOM e spezzerebbero `nth-child`. ui.js segna le righe
   visibili pari con `data-zebra` e la zebra segue quel segno. */
table[data-filtrabile] tbody tr td:not([class*="bg-"]) {
    background-color: var(--table-bg);
}

table[data-filtrabile] tbody tr[data-zebra] td:not([class*="bg-"]) {
    background-color: var(--table-row-alt);
}

/* Table Row - Metabolis Style */
table tbody tr {
    transition: background-color 0.15s ease;
}

table td,
table th {
    padding: 10px 12px;                               /* ← Minimal: Compact vertical padding */
    font-size: 12px;
    color: var(--iron);
}

/* Table Row Hover */
.sortable-table tbody tr:hover td,
table tbody tr:hover td {
    background-color: var(--table-row-hover) !important;
}

.sortable-table tbody tr:hover,
table tbody tr:hover {
    /* No transform, no shadow - just background change */
}

/* Ensure sticky columns also get the hover effect */

.sortable-table tbody tr:hover td[class*="sticky"],
table tbody tr:hover td[class*="sticky"] {
    background-color: var(--table-row-hover) !important;
}

/* Table Headers - Clean Minimal */
table th,
thead th {
    background: var(--table-header-bg);
    border-bottom: 1px solid var(--border);
    font-weight: 600;
    font-size: 12px;
    letter-spacing: 0.05em;
    color: var(--table-header-text);
}

/* Ensure table utility classes don't shrink headers/cells */
.sortable-table th.text-xs,
table th.text-xs,
.sortable-table td.text-xs,
table td.text-xs {
    font-size: 12px;
}

/* Table utilities */

.ui-table-link {
    color: var(--table-link-color);
    text-decoration: none;
}

.ui-table-link:hover {
    color: var(--table-link-hover);
    text-decoration: none;
}

/* La gemella distruttiva. Il markup la dichiarava da sempre (`movimenti-cassa.js`,
   pulsante Elimina di una regola) e il foglio non la definiva: il bottone restava
   senza colore e senza hover, cioe' indistinguibile da Modifica proprio nel momento
   in cui l'utente decide se premerlo. Il rosso e' per cio' che non si annulla. */

/* =============================================================================
   9. CHIPS
   ============================================================================= */

/* Chips - Interactive pills */

/* Badge — famiglia unica dal 26/08/2026 */
/* ⚠️ Le misure (56x19 px, maiuscolo) sono quelle della vecchia famiglia `.badge`,
   e restano di proposito: `ui-badge` fu allineata a lei PRIMA di sostituirla, in
   modo che le 254 occorrenze si potessero convertire una per una senza che una
   tabella mostrasse mai due badge di altezza diversa. La migrazione e' finita —
   zero classi `badge-*` nel codice servito, verificato il 27/08/2026 — quindi
   qui non c'e' piu' niente da far convivere: cambiare queste misure oggi e' una
   scelta di design, non un vincolo di transizione. */
.ui-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 3px 8px;
    border-radius: 3px;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    /* Trasparente e non assente: le varianti grigie ci scrivono un colore, e
       senza la dichiarazione qui la loro riga sarebbe 2 px piu' alta delle
       altre nella stessa colonna. */
    border: 1px solid transparent;
    font-size: 11px;
    font-weight: 600;
    line-height: 1.2;
    white-space: nowrap;
    vertical-align: middle;
    background: rgba(37, 37, 37, 0.08);
    color: var(--jet);
    transition: background var(--transition), color var(--transition);
}

/* Solo il badge che e' davvero un comando: l'hover che scurisce promette
   un'azione, e in BI i badge cliccabili sono ZERO (misurato il 24/08/2026:
   nessun `.ui-badge` porta `role="button"` o `onclick`). Su un badge che
   classifica e basta, il colore che cambia sotto il puntatore fa cercare un
   clic che non fa niente. */
.ui-badge[role="button"]:hover {
    background: var(--iron);
    color: #ffffff;
}

.ui-badge--warn {
    background: var(--princeton-light);
    color: var(--princeton-dark);
}

/* ⚠️ Le varianti col fondo `--platinum` portano un BORDO, e non e' una
   rifinitura: `--platinum` e' anche il fondo delle righe pari di ogni tabella,
   quindi una riga si' e una no il badge sparisce — resta il testo, che si legge
   come una cella di testo normale invece che come una classificazione.
   Misurato il 24/08/2026: fondo del badge e fondo della riga identici,
   contrasto **1,00**. Il rimedio non e' cambiare il fondo (sarebbe quello di
   un'altra variante, e due varianti con lo stesso aspetto non sono due
   varianti): e' definire il chip col contorno, come per le icone di riga.
   Il bordo contro il fondo della riga misura 1,21 — poco, ma e' un contorno,
   non un testo: serve a delimitare, e il significato lo porta la parola. */
.ui-badge--info {
    background: var(--platinum);
    color: var(--jet);
    border-color: var(--alabaster);
}

.ui-chip {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
    padding: 5px 12px;
    min-width: 32px;
    font-size: 11px;
    font-weight: 500;
    border-radius: var(--radius-sm);
    background: var(--bg-muted);
    color: var(--text-body);
    cursor: pointer;
    transition: background-color var(--transition), color var(--transition);
    text-decoration: none;
    white-space: nowrap;
    flex-shrink: 0;
}

.ui-chip:hover {
    background: var(--platinum);
    color: var(--jet);
    text-decoration: none;
}

/* Dato in pillola (catalogo «Dato in pillola», 24/09/2026, dai dettagli di BI): icona, etichetta in
   grassetto e valore su fondo platinum. Non è cliccabile (per quello c'è .ui-chip) e va a capo dentro
   la pillola se il valore è lungo, per esempio un nome di file. */
.ui-meta {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 6px;
    max-width: 100%;
    padding: 4px 10px;
    border-radius: var(--radius-md);
    background: var(--platinum);
    font-size: 13px;
    color: var(--iron);
}

.ui-meta > .bi {
    color: var(--stone);
}

.ui-meta-etichetta {
    font-weight: 600;
    color: var(--jet);
}

/* Card categoria cliccabile — Lead Gen pre-screening (vista per categoria) */

/* Card scelta metodo di ricerca — Lead Gen, step iniziale modale campagna */

/* =============================================================================
   10. PROGRESS BARS
   ============================================================================= */

/* Il riempimento va qualificato dal contenitore: piu' in basso (sezione "Progress
   Bar Animation") `.ui-progress-bar` e' ridefinita come contenitore di
   `.ui-progress-fill`, e vincendo per ordine spegneva il colore di ogni barra
   `.ui-progress > .ui-progress-bar` che non passasse un background inline
   (24/07/2026: la barra di avanzamento della Riconciliazione e quella di HubSpot
   nel Direzionale risultavano grigie). I due sistemi convivono, questa regola
   e' piu' specifica e vince senza toccare l'altro. */

/* Legend dots */

/* =============================================================================
   11. TABS
   ============================================================================= */

/* Container */
.ui-tabs {
    display: flex;
    flex-direction: column;
    gap: 0;
    min-width: 0;
}

/* Tab buttons row */
.ui-tabs .ui-tablist {
    display: flex;
    gap: 2px;
    border: 1px solid var(--internal-menu-border);
    border-radius: var(--radius-sm);
    background: var(--internal-menu-bg);
    padding: 3px;
    border-bottom: none;
    margin-bottom: 0;
}

.ui-tab {
    padding: 8px 14px;
    font-size: 12px;
    font-weight: 500;
    color: var(--iron);
    cursor: pointer;
    transition: color var(--transition), background-color var(--transition), border-color var(--transition), box-shadow var(--transition), opacity var(--transition);
    background: rgba(37, 37, 37, 0.05);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    margin-bottom: 0;
    box-shadow: inset 0 -1px 0 rgba(37, 37, 37, 0.10);
}

.ui-tab:hover:not(:disabled):not([aria-disabled="true"]) {
    color: var(--jet);
    background: rgba(37, 37, 37, 0.08);
}

.ui-tab.active {
    color: var(--white);
    background: var(--jet);
    border-color: var(--iron);
    box-shadow: 0 1px 2px rgba(37, 37, 37, 0.12);
}

.ui-tab:disabled,
.ui-tab[aria-disabled="true"] {
    opacity: 0.4;
    cursor: not-allowed;
}

.ui-tab:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
}

/* Bilancio Panoramica: row allineate con altezze controllate per breakpoint */

/* Bilancio tab Performance: 4 mini KPI con sparkline */

/* Bilancio tab Performance: CCC + Dettaglio row */

/* Bilancio tab Performance: Charts row */

/* Panels container */
.ui-tab-panels {
    min-width: 0;
    padding-top: var(--space-4);
}

/* Pannelli dentro una card, col menu delle tab fuori (Lingue e categorie): il respiro lo dà la card. */
.widget-card > .ui-tab-panels {
    padding-top: 0;
}

.ui-tab-panel {
    display: none;
    min-width: 0;
}

.ui-tab-panel.active {
    display: block;
}

/* Variant: in-widget (inside widget-card) */
.widget-body .ui-tabs .ui-tablist {
    background: var(--internal-menu-bg);
}

.widget-body .ui-tabs .ui-tab {
    padding: 6px 12px;
}

/* Variante compatta: lingue di un singolo campo, anche dentro un'altra tab (modale versione, 02/10/2026).
   La riga delle tab non occupa tutta la larghezza: è un dettaglio del campo, non una sezione. */
.ui-tabs--compatte .ui-tablist {
    display: inline-flex;
    align-self: flex-start;
    padding: 2px;
}

.ui-tabs--compatte .ui-tab {
    padding: 3px 8px;
    font-size: 11px;
}

.ui-tabs--compatte .ui-tab-panels {
    padding-top: var(--space-2);
}

.widget-body .ui-tabs .ui-tab.active {
    background: var(--jet);
    color: var(--white);
    border-color: var(--iron);
    box-shadow: 0 1px 2px rgba(37, 37, 37, 0.10);
}

.widget-body .ui-tabs .ui-tab-panels {
    padding-top: var(--space-3);
}

/* Internal segmented menu (tabs-like) */
.ui-internal-menu {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 6px;
    border: 1px solid var(--internal-menu-border);
    border-radius: 16px;
    background: var(--internal-menu-bg);
}

.ui-internal-menu-item {
    flex: 1 1 0;
    min-width: 0;
    height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: 12px;
    background: transparent;
    color: var(--internal-menu-item-text);
    font-size: 14px;
    font-weight: 500;
    letter-spacing: 0.01em;
    text-decoration: none;
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
}

.ui-internal-menu-item:hover:not(.active) {
    background: var(--internal-menu-item-hover-bg);
    color: var(--internal-menu-item-hover-text);
}

.ui-internal-menu-item.active {
    background: var(--internal-menu-active-bg);
    color: var(--internal-menu-active-text);
    box-shadow: var(--internal-menu-active-shadow);
}

.ui-internal-menu-item:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px var(--internal-menu-focus-ring);
}

.ui-internal-menu--compact {
    padding: 4px;
    gap: 4px;
}

.ui-internal-menu--compact .ui-internal-menu-item {
    height: 36px;
    border-radius: 10px;
    font-size: 12px;
}

.ui-internal-menu--inline .ui-internal-menu-item {
    flex: 0 0 auto;
    min-width: auto;
    padding: 0 12px;
}

.ui-internal-menu--compact.ui-internal-menu--inline {
    padding: 1px;
    gap: 2px;
    border-radius: 10px;
    background: var(--bg-card);
    border-color: var(--border);
}

.ui-internal-menu--compact.ui-internal-menu--inline .ui-internal-menu-item {
    height: 32px;
    border-radius: 8px;
    padding: 0 10px;
    font-size: 11px;
    color: var(--text-body);
}

.ui-internal-menu--compact.ui-internal-menu--inline .ui-internal-menu-item:hover:not(.active) {
    background: rgba(37, 37, 37, 0.06);
    color: var(--primary);
}

.ui-internal-menu--compact.ui-internal-menu--inline .ui-internal-menu-item.active {
    background: var(--primary);
    color: #fff;
    box-shadow: none;
}

/* Tab vere (`.ui-tab[data-tab]`, pannelli in pagina) disegnate col menu segmentato: via il fondo e
   l'ombra interna di `.ui-tab`, che resterebbero sotto le voci non attive. */
.ui-internal-menu .ui-tab.ui-internal-menu-item:not(.active) {
    background: transparent;
    box-shadow: none;
    border-color: transparent;
}

/* =============================================================================
   12. CHARTS
   ============================================================================= */

.ui-chart-container {
    position: relative;
    width: 100%;
}

.ui-chart-container canvas {
    width: 100% !important;
    height: 100% !important;
}

/* Chart height: fixed by default */
.ui-chart-sm { height: 150px; }
.ui-chart-md { height: 250px; }

/* Capped-height widget: prevents widgets from growing too tall.
   - Chart widgets: chart container shrinks to fit via flex.
   - List widgets (.ui-widget-scrollable): content area scrolls. */

/* =============================================================================
   13. UTILITIES
   ============================================================================= */

/* Text Colors */

/* Le due utility passano per i token semantici invece di ripetere la tinta:
   `--ui-success` e' la variante scura, e cosi' non restano due verita' sullo
   stesso nome. ⚠️ A schermo vince comunque il `tailwind.config` inline di
   `views/partials/header.php`, che il CDN inietta DOPO questo foglio: le due
   dichiarazioni vanno tenute d'accordo, o si corregge qui e si continua a
   misurare l'altro valore. */

/* Border Colors */

/* Font Sizes - Standard scale */
  /* labels, badges, tags */
.text-xs { font-size: 11px; }   /* small text, metadata */
.text-sm { font-size: 12px; }   /* body small, descriptions */
 /* body text */
   /* body medium */
   /* headings small */
   /* headings */
.text-2xl { font-size: 20px; }  /* headings large */
.text-3xl { font-size: 24px; }  /* hero */

/* Fixed dimensions */

/* Font Weights */

.font-medium { font-weight: 500; }
.font-semibold { font-weight: 600; }

/* Spacing (for use without Tailwind) */
.mt-1 { margin-top: var(--space-1); }
.mt-2 { margin-top: var(--space-2); }
.mt-3 { margin-top: var(--space-3); }
.mt-4 { margin-top: var(--space-4); }
.mb-1 { margin-bottom: var(--space-1); }
.mb-2 { margin-bottom: var(--space-2); }
.mb-3 { margin-bottom: var(--space-3); }
.mb-4 { margin-bottom: var(--space-4); }
.p-4 { padding: var(--space-4); }
.p-5 { padding: var(--space-5); }
.p-6 { padding: var(--space-6); }

/* Responsive utilities */
.overflow-x-auto {
    overflow-x: auto;
    min-width: 0;
}

/* Scrollbar styling */
::-webkit-scrollbar {
    width: 6px;
    height: 6px;
}

::-webkit-scrollbar-track {
    background: transparent;
}

::-webkit-scrollbar-thumb {
    background: var(--border);
    border-radius: 3px;
}

::-webkit-scrollbar-thumb:hover {
    background: var(--text-light);
}

/* =============================================================================
   15. UI COMPONENTS (from template.php)
   ============================================================================= */

/* Metrics */

/* ⚠️ Le classi di stato vanno dichiarate qui o NON colorano: il selettore sopra ha
   specificita' 0,3,1 e batte una utility come `.text-cherry` (0,1,0). Misurato il
   14/09/2026 in Chrome 152: «Obsolete · da ignorare» nel Direzionale si dipingeva da
   sempre con `text-seaweed`/`text-cherry` da JS e rendeva **iron** in entrambi i casi —
   (`text-seaweed` e' poi uscito dal markup il 14/09/2026, vedi sotto) —
   cioe' il colore che doveva distinguere zero da non-zero non e' mai arrivato a schermo,
   senza errori. Stessa forma delle eccezioni di `.kpi-mini .kpi-value`. */

/* Text Block */

/* Form Inputs */
.ui-input {
    display: block;
    height: 38px;
    padding: 0 12px;
    font-size: 14px;
    color: var(--text-body);
    background: #fff;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

/* Campo con icona a sinistra (ricerca). Non `pl-9`: app.css è caricato dopo
   tailwind.css e fuori dai layer, quindi il `padding` di `.ui-input` vince sulla
   utility e il testo finisce sotto l'icona. */
.ui-input--icona,
.ui-select.ui-input--icona {
    padding-left: 36px;
}

/* Separatore con testo («oppure»): due strade diverse nello stesso riquadro (modale accessi). */
.ui-separatore-testo {
    display: flex;
    align-items: center;
    gap: 12px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.ui-separatore-testo::before,
.ui-separatore-testo::after {
    content: '';
    flex: 1;
    border-top: 1px solid var(--border);
}

/* Occhio per vedere la password (helper occhio_password, JS in ui.js): dentro il campo, a destra. */
.ui-input--occhio {
    padding-right: 40px;
}

.ui-input-occhio {
    position: absolute;
    top: 50%;
    right: 4px;
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border: 0;
    border-radius: var(--interactive-radius);
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
    transition: color 150ms var(--ease-out), background-color 150ms var(--ease-out);
}

.ui-input-occhio:hover {
    color: var(--jet);
    background: rgba(37, 37, 37, 0.06);
}

.ui-input-occhio:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 1px;
}

textarea.ui-input {
    height: auto;
    min-height: 80px;
    padding: 8px 12px;
    line-height: 1.5;
    resize: vertical;
}

.ui-input:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-light);
}

.ui-input::placeholder {
    color: var(--text-light);
}

.ui-input:disabled {
    background: var(--bg-subtle);
    cursor: not-allowed;
    opacity: 0.7;
}

/* Select dropdown */
.ui-select {
    display: block;
    height: 38px;
    padding: 0 32px 0 12px;
    font-size: 14px;
    color: var(--text-body);
    background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%235a5a5a' d='M3 4.5L6 8l3-3.5H3z'/%3E%3C/svg%3E") no-repeat right 10px center;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    cursor: pointer;
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.ui-select:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-light);
}

.ui-select:disabled {
    background-color: var(--bg-subtle);
    cursor: not-allowed;
    opacity: 0.7;
}

/* Date Input Styling */
input[type="date"],
input[type="datetime-local"],
input[type="time"],
.ui-input[type="date"],
.ui-input[type="datetime-local"],
.ui-input[type="time"] {
    padding-right: 8px;
    accent-color: var(--primary);
    color-scheme: light;
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    font-feature-settings: 'tnum' on, 'lnum' on;
}

.ui-input[type="date"]::-webkit-calendar-picker-indicator,
.ui-input[type="datetime-local"]::-webkit-calendar-picker-indicator,
.ui-input[type="time"]::-webkit-calendar-picker-indicator {
    width: 18px;
    height: 18px;
    padding: 2px;
    margin-left: 4px;
    cursor: pointer;
    border-radius: var(--radius-sm);
    opacity: 0.6;
    transition: opacity 0.15s ease, background-color 0.15s ease;
}

.ui-input[type="date"]::-webkit-calendar-picker-indicator:hover,
.ui-input[type="datetime-local"]::-webkit-calendar-picker-indicator:hover,
.ui-input[type="time"]::-webkit-calendar-picker-indicator:hover {
    opacity: 1;
    background-color: var(--primary-light);
}

/* Date picker popup styling (Chrome/Edge) */
::-webkit-datetime-edit {
    padding: 0;
    font-family: inherit;
}

::-webkit-datetime-edit-fields-wrapper {
    padding: 0;
    font-family: inherit;
}

::-webkit-datetime-edit-text {
    color: var(--text-light);
    padding: 0 2px;
    font-family: inherit;
}

::-webkit-datetime-edit-month-field,
::-webkit-datetime-edit-day-field,
::-webkit-datetime-edit-year-field {
    color: var(--text-body);
    font-family: inherit;
}

::-webkit-datetime-edit-month-field:focus,
::-webkit-datetime-edit-day-field:focus,
::-webkit-datetime-edit-year-field:focus {
    background-color: var(--primary-light);
    color: var(--primary);
    border-radius: var(--radius-sm);
    outline: none;
}

/* ═══════════════════════════════════════════════════════════════════════════
   FLATPICKR CUSTOM THEME (Violet)
   ═══════════════════════════════════════════════════════════════════════════ */

/* Datepicker input styling */

.flatpickr-calendar {
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    border-radius: var(--radius-md);
    border: 1px solid var(--border);
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12);
    background: #fff;
}

/* Month navigation */

/* Weekdays header */

/* Days grid */

/* Time picker */

/* Buttons - Clean Minimal */
.ui-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 36px;
    padding: 0 14px;
    font-size: 14px;
    font-weight: 500;
    border-radius: var(--radius-md);
    border: 1px solid transparent;
    cursor: pointer;
    transition: background-color 140ms var(--ease-out),
                border-color 140ms var(--ease-out),
                color 140ms var(--ease-out),
                box-shadow 140ms var(--ease-out),
                transform 140ms var(--ease-out);
    white-space: nowrap;
    position: relative;
}

/* Il bottone deve dire di aver sentito il click prima che la risposta arrivi.
   Al posto del ripple, che cresceva width/height per 600 ms partendo dal centro
   invece che dal puntatore: layout + paint, e fuori budget di dieci volte. */
.ui-btn:active {
    transform: scale(0.97);
}

.ui-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.ui-btn:disabled:active {
    transform: none;
}

/* Primary - Colore principale */
.ui-btn-primary {
    background: var(--primary);
    color: #fff;
    border-color: var(--primary);
}

.ui-btn-primary:hover:not(:disabled) {
    background: var(--primary-hover);
    border-color: var(--primary-hover);
}

/* Accent (Green) */
/* ⚠️ Testo JET, non bianco: bianco su princeton misura 2,48:1 e jet 6,13:1.
   L'arancio resta quello del marchio — a cambiare e' cio' che ci sta sopra. */

/* Secondary - Sfondo subtle, hover viola pieno */
.ui-btn-secondary {
    background: var(--bg-subtle);
    color: var(--text-body);
    border-color: var(--border);
}

.ui-btn-secondary:hover:not(:disabled) {
    background: var(--primary);
    border-color: var(--primary);
    color: #fff;
}

/* Outline - Trasparente, hover solo bordo e testo viola */

/* Ghost */
.ui-btn-ghost {
    background: transparent;
    color: var(--text-muted);
    border-color: transparent;
}

.ui-btn-ghost:hover:not(:disabled) {
    background: var(--bg-subtle);
    color: var(--text-body);
}

/* Danger */
.ui-btn-danger {
    background: var(--cherry);
    color: #fff;
    border-color: var(--cherry);
}

.ui-btn-danger:hover:not(:disabled) {
    background: var(--danger);
    border-color: var(--danger);
}

/* Sizes */
.ui-btn-sm {
    height: 28px;
    padding: 0 10px;
    font-size: 11px;
}

/* Icon only */

/* Loading (da BI): bottone che ha avviato una richiesta in corso. Disattivato ma meno sbiadito di un bottone non
   disponibile, con l'icona che gira (BiUI.bottoneInAttesa). */
.ui-btn.ui-btn-loading,
.ui-btn.ui-btn-loading:disabled {
    pointer-events: none;
    cursor: progress;
    opacity: 0.8;
}

/* Button Group */

/* Accordion */

/* Lo stacco dal `summary` vale per QUALUNQUE contenuto, non per i soli paragrafi:
   fino al 01/09/2026 la regola era `.ui-accordion p`, e un accordion con dentro
   una tabella la attaccava al titolo — 0 px contro gli 8 di un `<p>`. Non si
   vede, perche' il padding della prima cella riempie lo spazio che manca e a
   schermo somiglia a una scelta. Riportato da Spark (commit 8890885), dove il
   difetto e' emerso su un accordion vero. La tipografia resta sui soli paragrafi:
   una tabella ha la sua. */

/* Accordion variant: in-widget (flat style) */

/* Breadcrumb */

/* Breadcrumb variant: in-widget */

/* Dropdown */
.ui-dropdown {
    position: relative;
    display: inline-block;
}

.ui-dropdown-trigger {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    height: 36px;
    padding: 0 12px;
    font-size: 12px;
    font-weight: 500;
    color: var(--text-body);
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background-color 150ms var(--ease-out), border-color 150ms var(--ease-out);
}

.ui-dropdown-trigger:hover {
    background: var(--bg-subtle);
    border-color: var(--primary);
}

.ui-dropdown-trigger--icon {
    padding: 0;
    width: 36px;
}

/* Compact variant for widget/toolbar */
.ui-dropdown-trigger--sm {
    height: 32px;
}

.ui-dropdown-trigger--icon.ui-dropdown-trigger--sm {
    width: 32px;
    font-size: 11px;
}

.ui-dropdown-menu {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    min-width: 160px;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    box-shadow: 0 8px 18px rgba(37, 37, 37, 0.08);
    z-index: 50;
    opacity: 0;
    visibility: hidden;
    /* Il menu nasce dal suo trigger, non dal proprio centro: l'origine sta
       sull'angolo da cui il pannello e' ancorato. */
    transform-origin: top left;
    transform: translateY(-4px) scale(0.97);
    transition: opacity 150ms var(--ease-out),
                transform 150ms var(--ease-out),
                visibility 150ms var(--ease-out);
}

/* Le due varianti in uso sono allineate a destra (right: 0): l'origine segue. */
.ui-quickfilter-menu {
    transform-origin: top right;
}

.ui-dropdown:hover .ui-dropdown-menu,
.ui-dropdown:focus-within .ui-dropdown-menu {
    opacity: 1;
    visibility: visible;
    transform: translateY(0) scale(1);
}

.ui-dropdown--click:hover .ui-dropdown-menu,
.ui-dropdown--click:focus-within .ui-dropdown-menu {
    opacity: 0;
    visibility: hidden;
    transform: translateY(-4px) scale(0.97);
}

.ui-dropdown.is-open .ui-dropdown-menu {
    opacity: 1;
    visibility: visible;
    transform: translateY(0) scale(1);
}

/* Dentro un contenitore che scorre (tabella in `.overflow-x-auto`) un menu
   absolute viene tagliato: questa variante lo toglie dal flusso, e
   `BiUI.initDropdownToggles` calcola top/left dal trigger all'apertura. */
.ui-dropdown--fixed .ui-dropdown-menu {
    position: fixed;
    top: 0;
    left: 0;
    right: auto;
}

.ui-dropdown-item {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 8px 12px;
    font-size: 12px;
    color: var(--text-body);
    background: transparent;
    border: none;
    cursor: pointer;
    transition: background 0.15s ease;
    text-align: left;
}

.ui-dropdown-item:hover {
    background: var(--bg-subtle);
}

.ui-dropdown-item--danger {
    color: var(--danger);
}

.ui-dropdown-item--danger:hover {
    background: rgba(194, 27, 63, 0.10);
}

.ui-quickfilter-menu {
    left: auto;
    right: 0;
    min-width: 220px;
    padding: 6px 0;
    overflow: hidden;
    background: #fff;
}

.ui-quickfilter-menu .ui-dropdown-header {
    padding: 7px 12px 9px;
    margin: 0 0 4px;
    border-bottom: 1px solid var(--border);
}

.ui-quickfilter-menu .ui-dropdown-header.widget-title {
    margin-bottom: 4px;
}

.ui-quickfilter-menu .ui-dropdown-item {
    padding: 8px 12px;
    font-size: 12px;
}

.ui-quickfilter-menu .ui-dropdown-item.active {
    background: rgba(37, 37, 37, 0.06);
    color: var(--primary);
    font-weight: 600;
}

.ui-quickfilter-menu .ui-dropdown-item.active i {
    color: var(--primary);
}

/* Dropdown variant: in-widget */
.widget-body .ui-dropdown-trigger {
    background: var(--bg-subtle);
    border-color: transparent;
}

.widget-body .ui-dropdown-trigger:hover {
    background: var(--bg-muted);
    border-color: transparent;
}

/* AI Components */

/* Status Cards */

/* Contrast Cards — filled backgrounds */

/* Il badge dentro una card COLORATA o SCURA vuole il fondo bianco.
   Il fondo delle varianti e' un velo al 12% (`--seaweed-light`, `--cherry-light`),
   pensato per schiarire il bianco: sopra un fondo pieno non schiarisce niente e il
   testo scuro resta quasi sul proprio stesso colore. Misurato in Chrome 151 sui due
   KPI di movimenti-cassa: «SCOPERTO» 1,50:1 e «POSITIVO» 2,05:1 — contro 8,93 e 6,42
   che gli stessi due colori danno sul bianco. Il colore semantico resta al TESTO, che
   e' dove si legge.
   ⚠️ `--dark` fa eccezione perche' il suo testo e' bianco: su fondo bianco sparirebbe,
   quindi qui prende il jet. */

.ui-kpi-dark .ui-badge {
    background: #ffffff;
    border-color: transparent;
}

/* Dark KPI Cards */
.ui-kpi-dark {
    background: var(--jet);
    color: var(--platinum);
    border-radius: var(--radius-md);
    padding: 18px;
    display: grid;
    gap: 8px;
    border: 1px solid var(--iron);
    box-shadow: none;
}

.ui-kpi-dark canvas { max-width: 100%; display: block; }

.ui-kpi-dark-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: rgba(255, 255, 255, 0.70);
}

/* Titolo vero (h2) nell'intestazione scura: senza, prende il colore dei titoli e sparisce sul jet. */
.ui-kpi-dark-header h2 { color: inherit; font: inherit; letter-spacing: inherit; margin: 0; }

.ui-kpi-dark-value { font-size: 2.25rem; font-family: var(--font-mono); font-weight: 500; letter-spacing: -0.02em; line-height: 1; margin-bottom: 1px; }
.ui-kpi-dark-meta { font-size: 12px; color: rgba(255, 255, 255, 0.55); line-height: 1.2; }

/* KPI cliccabile (home): la card intera è il link; al passaggio il bordo si scurisce, come le card. */
.ui-kpi-link {
    display: block;
    color: inherit;
    text-decoration: none;
}

.widget-card.ui-kpi-link:hover {
    border-color: var(--iron);
}

.ui-kpi-link:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
}

.ui-kpi-dark-value + .ui-kpi-dark-meta { margin-top: -3px; }

/* Semantica su superficie scura.
   seaweed #1AAD7E e cherry #C21B3F sono tarati sul bianco: su --jet #252525 il rosso
   scende a ~2,2:1, sotto il 3:1 richiesto per un indicatore non testuale. Queste due
   tinte schiarite valgono SOLO dentro le card scure; altrove si usano i colori pieni. */
:root {
    --seaweed-on-dark: #4ADE80;
    --cherry-on-dark: #FF7A93;
}

/* L'icona del tooltip eredita text-ui-light (stone), illeggibile su jet. */
.ui-kpi-dark .bi-info-circle { color: rgba(255, 255, 255, 0.55); }

/* Timeline */
/* In BI la linea stava sul contenitore a 5 px dal bordo e i pallini, dopo il
   padding-left, a 17: 12 px di scarto. Qui un tratto per voce, dal centro del
   suo pallino al centro del successivo (gap + margine + mezzo pallino), così
   sopra il primo e sotto l'ultimo non sporge. */
.ui-timeline {
    --timeline-gap: 14px;
    --timeline-pallino: 10px;
    --timeline-pallino-top: 4px;
    display: grid;
    gap: var(--timeline-gap);
}

.ui-timeline-item {
    position: relative;
    display: flex;
    gap: 12px;
    align-items: flex-start;
}

.ui-timeline-item:not(:last-child)::before {
    content: '';
    position: absolute;
    left: calc(var(--timeline-pallino) / 2 - 1px);
    top: calc(var(--timeline-pallino-top) + var(--timeline-pallino) / 2);
    bottom: calc(-1 * (var(--timeline-gap) + var(--timeline-pallino-top) + var(--timeline-pallino) / 2));
    width: 2px;
    background: var(--border);
}

.ui-timeline-dot {
    position: relative;
    flex-shrink: 0;
    width: var(--timeline-pallino);
    height: var(--timeline-pallino);
    border-radius: 50%;
    background: var(--primary);
    margin-top: var(--timeline-pallino-top);
    box-shadow: 0 0 0 4px rgba(37, 37, 37, 0.06);
}

/* Badges extras */
/* Le varianti SCURE sul proprio velo. Misurato il 24/08/2026 componendo il velo
   sui due fondi di riga: seaweed sul suo velo dentro una riga --platinum da'
   2,20 e cherry 4,46 — sotto il 4,5. Dark-emerald da' 4,93 e burgundy 6,67. */
.ui-badge--success { background: var(--seaweed-light); color: var(--dark-emerald); }
.ui-badge--danger { background: var(--cherry-light); color: var(--burgundy); }

.ui-badge--secondary { background: rgba(37, 37, 37, 0.08); color: var(--jet); }

/* Alerts */
.ui-alert {
    display: flex;
    gap: 8px;
    align-items: center;
    padding: 10px 12px;
    border-radius: var(--radius-md);
    font-size: 12px;
    border: 1px solid transparent;
}

.ui-alert--warn { background: rgba(251, 133, 15, 0.08); color: var(--princeton-dark); border-color: rgba(251, 133, 15, 0.30); }
.ui-alert--danger { background: rgba(194, 27, 63, 0.10); color: var(--danger); border-color: rgba(194, 27, 63, 0.30); }
.ui-alert--success { background: rgba(26, 173, 126, 0.10); color: var(--dark-emerald); border-color: rgba(26, 173, 126, 0.30); }
.ui-alert--info { background: rgba(27, 90, 122, 0.08); color: var(--sapphire); border-color: rgba(27, 90, 122, 0.20); }

/* ⚠️ Un avviso che porta PIU' di una riga: l'icona va alla prima riga, non a
   meta' del blocco. `.ui-alert` centra — giusto per l'avviso di una riga sola,
   che sono quasi tutti — e su tre righe l'icona finiva 22 px sotto la prima
   (misurato in Chrome 151 sull'avviso della preview di import): un glifo a
   meta' testo si legge come un elenco puntato mal riuscito, non come l'icona
   dell'avviso. I 2 px di margine sono ottici: allineano il glifo alla linea di
   base del testo, non al suo riquadro. */
.ui-alert--multiriga { align-items: flex-start; }
.ui-alert--multiriga > i { margin-top: 2px; }

/* Lo stacco dell'avviso dal contenuto che segue, che nel BI mancava: misurati
   in Chrome 151 il 26/08/2026 su `?page=template`, **0 px** fra i dieci avvisi
   e cio' che sta loro sotto. Sta sull'avviso e non sul contenitore perche' la
   distanza e' una proprieta' dell'avviso. Un avviso che e' l'ultimo figlio non
   cambia, e non serve.

   ⚠️ Non si somma a `space-y-*`: in flow normale i margini adiacenti
   COLLASSANO. Il collasso pero' NON avviene in un contenitore flex o grid: li'
   i due margini si sommerebbero. Contati il 26/08/2026 su tutto il markup: i
   cinque `space-y-*` che nominano flex/grid sono tutti `flex-1`, cioe' ITEM di
   un flex e non contenitori, e nessuno di loro contiene un avviso. Chi ne
   introduce uno rimisuri questa distanza.

   ⚠️ Dove il markup dichiara gia' `space-y-*` questa regola NON vince e non
   deve: `.space-y-6 > :not([hidden]) ~ :not([hidden])` e' 0,3,0 contro 0,2,0 e
   azzera il `margin-bottom` mettendo la distanza sul `margin-top` del fratello.
   Misurato su `?page=template`: nove avvisi su dieci stanno in un `space-y-*`
   e restano a 0 px di margine inferiore, con la distanza gia' data dal
   contenitore. Questa regola e' il pavimento per l'avviso che quel contenitore
   non ce l'ha, non un secondo sistema di spaziatura.

   ⚠️ Le righe di `#bi-banner` sono escluse: `banner.js` le crea con `mb-2` e
   quella distanza breve e' voluta — la pila del banner e' un blocco solo. Senza
   l'esclusione questa regola le batterebbe (0,2,0 contro 0,1,0 di `.mb-2`) e le
   porterebbe a 16 px, misurato con tre avvisi a schermo. */
.ui-alert:not(:last-child):not(.bi-banner-riga) {
    margin-bottom: var(--spazio-avviso);
}

/* ⚠️ Un avviso dentro uno SLOT che il JS riempie sfugge alla regola qui sopra,
   e in silenzio: dentro il proprio wrapper l'avviso e' `:last-child`, quindi
   `:not(:last-child)` non lo raggiunge mai — anche quando il wrapper e'
   seguito da una tabella. In Spark e' stato misurato a 0 px, cioe' esattamente
   il difetto che la regola sopra esiste per impedire, ricomparso perche' il
   markup ha guadagnato un livello.

   Lo stacco lo prende il WRAPPER, non l'avviso: e' l'unico dei due che sa di
   essere seguito da qualcosa. Sopra E sotto, perche' uno slot puo' stare sia
   prima sia dopo cio' che chiosa. `:not(:empty)` perche' uno slot vuoto non
   deve lasciare un buco quando non c'e' niente da dire. */

.ui-alert-close {
    margin-left: auto;
    background: transparent;
    border: 0;
    color: inherit;
    font-size: 14px;
    line-height: 1;
    cursor: pointer;
    opacity: 0.6;
    transition: opacity 0.15s ease;
}

.ui-alert-close:hover { opacity: 1; }

/* Tags */

/* Animation */

/* =============================================================================
   16. SIDEBAR NAVIGATION (App-specific)
   ============================================================================= */
/* Sidebar Container */

/* Mobile */

/* Desktop sidebar */
@media (min-width: 1024px) {
    body { padding-top: 0; }

    /* Let body scroll, not main-content - required for sticky sidebar */
    
}

/* =============================================================================
   16b. SIDEBAR NAVIGATION — NEW STRUCTURE (mobile-shell, brand-shell, tertiary, flyout groups)
   ============================================================================= */

/* Mobile header */

/* Brand shell */
/* Nav section labels */
.nav-section-label {
    padding: 8px 10px 6px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--sidebar-text-muted);
}
/* =============================================================================
   17. RESPONSIVE
   ============================================================================= */

@media (max-width: 640px) {
    /* Widget responsiveness */
    .widget-card {
        min-height: auto;
    }
    .widget-card canvas { max-height: 180px; }
    /* Il contenitore scende col canvas: a 250 px restava un vuoto di 70 sotto ogni grafico (misurato sulla home, 25/09/2026). */
    .ui-chart-md { height: 180px; }

    .widget-title {
        font-size: 9px;
        gap: 4px;
        margin-bottom: 8px;
    }
    .widget-title i { font-size: 9px; }

    

    
    
    

    
    

    /* Tabs on mobile */
    .ui-tabs .ui-tablist { flex-wrap: wrap; gap: 2px; }
    .ui-tab { padding: 6px 10px; font-size: 11px; }
    .widget-body .ui-tabs .ui-tab { padding: 5px 10px; font-size: 10px; }

    /* Pills and badges */
    
    .ui-chip { padding: 4px 10px; font-size: 10px; }

    
    
    
    
    
    
    /* Chart containers */
    .ui-chart-container { min-height: 120px; }
}

/* =============================================================================
   18. ADVANCED COMPONENTS
   ============================================================================= */

/* -----------------------------------------------------------------------------
   18.1 Variance / Delta Indicators
   ----------------------------------------------------------------------------- */

/* -----------------------------------------------------------------------------
   18.2 Gauge / Speedometer
   ----------------------------------------------------------------------------- */
/* -----------------------------------------------------------------------------
   18.3 Skeleton Loaders
   ----------------------------------------------------------------------------- */

/* -----------------------------------------------------------------------------
   18.4 Date Range Picker
   ----------------------------------------------------------------------------- */

/* -----------------------------------------------------------------------------
   18.5 Step Wizard / Stepper
   ----------------------------------------------------------------------------- */

/* Stepper verticale */

/* -----------------------------------------------------------------------------
   18.6 Export Buttons
   ----------------------------------------------------------------------------- */

/* =============================================================================
   MODALS
   ============================================================================= */

/**
 * L'entrata e l'uscita sono guidate dalla `.hidden` che c'era gia', non da una
 * classe nuova: i modali si aprono in 88 punti diversi, 53 via BiUI.openModal()
 * e 35 con classList.remove('hidden') diretto. Una classe in piu' avrebbe
 * voluto dire toccarli tutti, e dimenticarne qualcuno.
 *
 * `transition-behavior: allow-discrete` fa transitare anche `display`, e
 * @starting-style da' i valori di partenza al primo frame in cui l'elemento
 * viene reso. Senza, da display:none non parte niente.
 */
.ui-modal {
    position: fixed;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(37, 37, 37, 0.45);
    z-index: 100;
    padding: 16px;
    opacity: 1;
    transition: opacity 200ms var(--ease-out),
                display 200ms allow-discrete;
}

.ui-modal.hidden {
    display: none;
    opacity: 0;
}

@starting-style {
    .ui-modal:not(.hidden) {
        opacity: 0;
    }
}

.ui-modal-card {
    background: #ffffff;
    border-radius: var(--radius-lg);
    box-shadow: 0 20px 40px rgba(37, 37, 37, 0.2);
    width: 100%;
    max-width: 420px;
    padding: 28px 24px;
    max-height: 90vh;
    overflow-y: auto;
    /* La card resta ancorata al centro: il modale e' l'eccezione fra i
       pannelli, non nasce da un trigger e non ha un angolo da cui crescere. */
    transition: transform 200ms var(--ease-out),
                opacity 200ms var(--ease-out);
}

.ui-modal.hidden .ui-modal-card {
    transform: scale(0.97);
    opacity: 0;
}

@starting-style {
    .ui-modal:not(.hidden) .ui-modal-card {
        transform: scale(0.97);
        opacity: 0;
    }
}
.ui-modal-card--md { max-width: 520px; }
.ui-modal-card--lg { max-width: 600px; }
.ui-modal-card--xl { max-width: 920px; }

/* Per cio' che si GUARDA e non si compila — una tabella larga, un confronto.
   Le 19 modali scrivevano la misura inline con nove valori diversi: sei erano
   arrotondamenti delle quattro qui sopra, questo no. */

/* =============================================================================
   MICRO ANIMATIONS
   Animazioni sottili per rendere l'UI più reattiva e accattivante
   ============================================================================= */

/* -----------------------------------------------------------------------------
   Keyframes
   ----------------------------------------------------------------------------- */

/* Fade in from bottom */

/* Fade in from top */
/* Scale in */
/* Subtle bounce */
/* Pulse glow */
/* Shake (for errors) */
/* Spin */
@keyframes ui-spin {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}

/* Lead workflow inline loader (riga campagna, accanto azioni) */

/* Variante bottone: click = ferma il job. L'icona stop rimpiazza il loader su hover. */

/* Slide in from right */
/* Highlight flash */
/* Ripple effect */
/* Progress slide (indeterminate) */

/* Shimmer effect - Premium */
/* -----------------------------------------------------------------------------
   Animation Utility Classes
   ----------------------------------------------------------------------------- */

.bi-spin { animation: ui-spin 1s linear infinite; display: inline-block; }
/* -----------------------------------------------------------------------------
   Skeleton Loaders - Premium Shimmer Effect
   ----------------------------------------------------------------------------- */

.ui-skeleton {
    background: linear-gradient(
        90deg,
        var(--platinum) 0%,
        var(--alabaster) 20%,
        var(--platinum) 40%,
        var(--platinum) 100%
    );
    background-size: 200% 100%;
    animation: ui-skeleton-pulse 1.5s ease-in-out infinite;
    border-radius: var(--radius-sm);
    position: relative;
    overflow: hidden;
}

@keyframes ui-skeleton-pulse {
    0%, 100% { background-position: 200% 0; }
    50% { background-position: -200% 0; }
}

/* Shimmer overlay - Premium */
/* Delayed animations */
/* -----------------------------------------------------------------------------
   Hover Micro-interactions
   ----------------------------------------------------------------------------- */

/* Lift on hover (for cards, widgets) - Premium */
/* Subtle scale on hover (for buttons, icons) */
/* Glow on hover */
/* Icon rotate on hover */
/* Icon bounce on hover */
/* Arrow slide on hover */
/* -----------------------------------------------------------------------------
   Active/Click States
   ----------------------------------------------------------------------------- */

/* Press down effect */
/* Ripple container (requires JS to add ripple span) */
/* -----------------------------------------------------------------------------
   Focus States
   ----------------------------------------------------------------------------- */

/* -----------------------------------------------------------------------------
   Staggered List Animations
   ----------------------------------------------------------------------------- */

/* -----------------------------------------------------------------------------
   Progress Bar Animation
   ----------------------------------------------------------------------------- */

/* Indeterminate progress */

/* -----------------------------------------------------------------------------
   Sidebar Notification Bell + Dropdown
   ----------------------------------------------------------------------------- */

/* Notification dropdown wrapper positioned relative to bell */

/* Notification dropdown menu — fixed position to escape sidebar overflow:hidden */

/* Notification item */

/* Urgency border colors */

/* Icon wrapper */

/* Text */

/* Empty state */

/* -----------------------------------------------------------------------------
   Count Up Animation (visual only, JS handles actual counting)
   ----------------------------------------------------------------------------- */

/* =============================================================================
   EMPTY STATES
   Placeholder UI per assenza di dati
   ============================================================================= */

.ui-empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 48px 24px;
    text-align: center;
}

.ui-empty-state-icon {
    width: 64px;
    height: 64px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bg-subtle);
    border-radius: 50%;
    margin-bottom: 16px;
    color: var(--text-light);
    font-size: 24px;
}

.ui-empty-state-title {
    font-size: 15px;
    font-weight: 600;
    color: var(--text-heading);
    margin-bottom: 8px;
}

.ui-empty-state-text {
    font-size: 13px;
    color: var(--text-muted);
    max-width: 320px;
    margin-bottom: 20px;
}

.ui-empty-state-compact {
    padding: 32px 16px;
}

.ui-empty-state-compact .ui-empty-state-icon {
    width: 48px;
    height: 48px;
    font-size: 18px;
    margin-bottom: 12px;
}

.ui-empty-state-compact .ui-empty-state-title {
    font-size: 13px;
}

.ui-empty-state-compact .ui-empty-state-text {
    font-size: 12px;
    margin-bottom: 16px;
}

/* =============================================================================
   DATO CHE QUESTA FONTE NON PORTA
   Il widget resta a schermo con la sua forma, e dice da dove arriverebbe il dato.
   ============================================================================= */

/* ⚠️ Le barre NON pulsano, e non e' una dimenticanza: `.ui-skeleton` anima perche'
   significa «sto caricando», e qui non sta caricando niente — un'attesa che non
   finisce mai e' peggio di un vuoto dichiarato. Restano le stesse barre di platino,
   ferme, perche' servono solo a non far collassare l'altezza del widget quando il
   dato manca: senza, una riga di due card si sbilancia e il vicino si allunga. */

/* =============================================================================
   ERROR STATES
   UI per errori e problemi
   ============================================================================= */

.ui-error-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 48px 24px;
    text-align: center;
}

.ui-error-state-icon {
    width: 64px;
    height: 64px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(194, 27, 63, 0.1);
    border-radius: 50%;
    margin-bottom: 16px;
    color: var(--ui-danger);
    font-size: 24px;
}

.ui-error-state-title {
    font-size: 15px;
    font-weight: 600;
    color: var(--text-heading);
    margin-bottom: 8px;
}

.ui-error-state-text {
    font-size: 13px;
    color: var(--text-muted);
    max-width: 320px;
    margin-bottom: 20px;
}

/* =============================================================================
   FORM VALIDATION STATES
   Stati di validazione per input
   ============================================================================= */

.ui-input-feedback {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 6px;
    font-size: 12px;
}

.ui-input-feedback i {
    font-size: 11px;
}

/* Success state */
.ui-input-feedback.is-valid {
    color: var(--dark-emerald);
}

/* Error state */
.ui-input.is-invalid,
.ui-select.is-invalid {
    border-color: var(--ui-danger);
}

.ui-input.is-invalid:focus,
.ui-select.is-invalid:focus {
    border-color: var(--ui-danger);
    box-shadow: 0 0 0 3px rgba(194, 27, 63, 0.1);
}

.ui-input-feedback.is-invalid {
    color: var(--ui-danger);
}

/* Misura della nuova password (partials/misura-password.php, misura-password.js): la barra si riempie
   di 1/minlength a carattere, grigia finché è corta; arrivata al minimo, verde se il server la accetterà
   e rossa se è fra le comuni. Il colore non basta (WCAG 1.4.1): sotto c'è sempre il testo. */
.ui-misura-password {
    margin-top: 8px;
}

.ui-misura-password-barra {
    height: 4px;
    border-radius: 2px;
    background: var(--alabaster);
    overflow: hidden;
}

.ui-misura-password-riempimento {
    height: 100%;
    background: var(--stone);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 150ms var(--ease-out), background-color 150ms var(--ease-out);
}

.ui-misura-password.is-valida .ui-misura-password-riempimento {
    background: var(--seaweed);
}

.ui-misura-password.is-errata .ui-misura-password-riempimento {
    background: var(--cherry);
}

.ui-misura-password-testo {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 6px;
    font-size: 12px;
    color: var(--stone);
}

.ui-misura-password-testo i {
    font-size: 11px;
}

/* `display: flex` qui sopra batterebbe l'attributo `hidden` che il JS usa per svuotarli. */
.ui-misura-password-testo[hidden],
.ui-input-feedback[hidden] {
    display: none;
}

.ui-misura-password.is-valida [data-misura-esito] {
    color: var(--dark-emerald);
}

.ui-misura-password.is-errata [data-misura-esito] {
    color: var(--burgundy);
}

/* Warning state */

/* Input with icon */

/* =============================================================================
   DROPDOWN MENU
   Menu contestuali e azioni
   ============================================================================= */

/* =============================================================================
   CONFIRMATION DIALOG
   Modal di conferma azioni
   ============================================================================= */

.ui-confirm-icon {
    width: 56px;
    height: 56px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    margin: 0 auto 20px;
    font-size: 24px;
}

.ui-confirm-icon.warning {
    background: rgba(251, 133, 15, 0.1);
    color: var(--ui-warning);
}

.ui-confirm-icon.danger {
    background: rgba(194, 27, 63, 0.1);
    color: var(--ui-danger);
}

.ui-confirm-icon.info {
    background: rgba(37, 37, 37, 0.05);
    color: var(--iron);
}

.ui-confirm-title {
    font-size: 16px;
    font-weight: 600;
    color: var(--text-heading);
    text-align: center;
    margin-bottom: 8px;
}

.ui-confirm-text {
    font-size: 13px;
    color: var(--text-muted);
    text-align: center;
    margin-bottom: 24px;
    /* Un elenco nel messaggio (caricamento rapido: una commessa per riga) va a capo dove lo dice il testo. */
    white-space: pre-line;
    overflow-wrap: anywhere;
}

.ui-confirm-actions {
    display: flex;
    gap: 12px;
    justify-content: center;
}

/* =============================================================================
   DRAG & DROP UPLOAD
   Area upload file con drag & drop
   ============================================================================= */

.ui-dropzone {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 32px 24px;
    border: 2px dashed var(--border);
    border-radius: var(--radius-md);
    background: var(--bg-subtle);
    cursor: pointer;
    transition: border-color 200ms var(--ease-out), background-color 200ms var(--ease-out);
}

.ui-dropzone:hover {
    border-color: var(--primary);
    background: rgba(37, 37, 37, 0.02);
}

.ui-dropzone.dragover {
    border-color: var(--primary);
    background: rgba(37, 37, 37, 0.05);
    transform: scale(1.01);
}

.ui-dropzone-icon {
    width: 48px;
    height: 48px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #fff;
    border-radius: 50%;
    margin-bottom: 16px;
    color: var(--primary);
    font-size: 20px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}

.ui-dropzone-text {
    font-size: 14px;
    font-weight: 500;
    color: var(--text-heading);
    margin-bottom: 4px;
}

.ui-dropzone-hint {
    font-size: 12px;
    color: var(--text-muted);
}

.ui-dropzone-hint a {
    color: var(--iron);
    font-weight: 500;
}

.ui-dropzone input[type="file"] {
    position: absolute;
    inset: 0;
    opacity: 0;
    cursor: pointer;
}

/* File list */

/* Dentro un `widget-body` il corpo ha gia' il suo stacco: il margine in piu'
   faceva scendere la lista di 16 px senza motivo. */

/* =============================================================================
   EDIT MODAL PATTERNS
   Pattern per modal di modifica
   ============================================================================= */

.ui-modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-bottom: 16px;
    margin-bottom: 20px;
    border-bottom: 1px solid var(--border);
}

.ui-modal-title {
    font-size: 16px;
    font-weight: 600;
    color: var(--text-heading);
}

/* Un <button type="button">, non un <div> come in BI: col div la X non si raggiungeva col Tab. */
.ui-modal-close {
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--text-light);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background-color 150ms var(--ease-out), color 150ms var(--ease-out);
}

.ui-modal-close:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 1px;
}

.ui-modal-close:hover {
    background: var(--bg-subtle);
    color: var(--text-body);
}

.ui-modal-body {
    margin-bottom: 24px;
}

.ui-modal-footer {
    display: flex;
    justify-content: flex-end;
    gap: 12px;
    padding-top: 16px;
    border-top: 1px solid var(--border);
}

/* Un secondo oggetto dentro la stessa modale (la fattura sotto la trattativa): il fondo
   dice «questo e' un'altra cosa» meglio di un filetto, che si confondeva con i campi.
   I campi restano bianchi sul platinum, quindi si leggono ancora come campi. */

/* Form layout in modal */
.ui-form-row {
    margin-bottom: 16px;
}

.ui-form-row:last-child {
    margin-bottom: 0;
}

.ui-form-label {
    display: block;
    font-size: 12px;
    font-weight: 500;
    color: var(--text-body);
    margin-bottom: 6px;
}

.ui-form-label.required::after {
    content: ' *';
    color: var(--ui-danger);
}

.ui-form-hint {
    font-size: 11px;
    color: var(--text-light);
    margin-top: 6px;
}

.ui-form-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 16px;
}

/* Tre campi per riga: per form larghi (ui-modal-card--lg e oltre), dove due
   colonne lascerebbero mezza riga vuota. Sotto i 640px scende a due, poi a una:
   tre select affiancate su un tablet diventano illeggibili. */

@media (max-width: 480px) {
    .ui-form-grid {
        grid-template-columns: 1fr;
    }
}

/* Checkbox list (multi-select replacement) */

/* =============================================================================
   CUSTOM SCROLLBAR - PREMIUM
   ============================================================================= */

/* Webkit (Chrome, Safari, Edge) */
::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

::-webkit-scrollbar-track {
    background: transparent;
    border-radius: 10px;
}

::-webkit-scrollbar-thumb {
    background: linear-gradient(
        180deg,
        rgba(37, 37, 37, 0.15) 0%,
        rgba(37, 37, 37, 0.10) 100%
    );
    border-radius: 10px;
    border: 2px solid transparent;
    background-clip: padding-box;
    transition: background 0.3s ease;
}

::-webkit-scrollbar-thumb:hover {
    background: linear-gradient(
        180deg,
        rgba(37, 37, 37, 0.30) 0%,
        rgba(37, 37, 37, 0.20) 100%
    );
    background-clip: padding-box;
}

::-webkit-scrollbar-thumb:active {
    background: linear-gradient(
        180deg,
        rgba(37, 37, 37, 0.50) 0%,
        rgba(37, 37, 37, 0.40) 100%
    );
    background-clip: padding-box;
}

/* Firefox */
* {
    scrollbar-width: thin;
    scrollbar-color: rgba(37, 37, 37, 0.15) transparent;
}

/* =============================================================================
   PAGE HEADER PATTERN
   ============================================================================= */

.page-header {
    margin-bottom: 24px;
}

.page-breadcrumb {
    font-size: 11px;
    color: var(--text-muted);
    margin-bottom: 8px;
    display: flex;
    align-items: center;
    gap: 4px;
}

.page-breadcrumb a {
    color: var(--text-muted);
    text-decoration: none;
}

.page-breadcrumb a:hover {
    color: var(--primary);
}

.page-header-row {
    display: flex;
    /* I comandi scendono sotto il titolo invece di uscire dalla pagina: senza
       `wrap` finivano fuori e `main.main-content` ha `overflow-x: hidden`, quindi
       sparivano senza nemmeno una barra di scorrimento. Misurato il 14/09/2026 a
       400 px sulla demo di `?page=template`: 234 px fuori. A larghezza piena non
       cambia niente — i due blocchi stanno su una riga e il wrap non scatta. */
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
}

/* Dettaglio azienda: tab nav with 7 items — allow flex-wrap on mobile */

.page-header-divider .ui-internal-menu--compact.ui-internal-menu--inline .ui-internal-menu-item,
.page-header-divider .ui-dropdown-trigger {
    color: var(--iron);
}

.page-header-divider .ui-internal-menu--compact.ui-internal-menu--inline .ui-internal-menu-item.active {
    color: #fff;
}

.page-header-divider .ui-dropdown-trigger i {
    color: var(--iron);
}

.page-header-divider .ui-dropdown-trigger:hover,
.page-header-divider .ui-dropdown.is-open > .ui-dropdown-trigger {
    border-color: var(--accent);
    background: rgba(37, 37, 37, 0.04);
}

.page-header-divider .ui-quickfilter-menu .ui-dropdown-item:hover {
    background: rgba(37, 37, 37, 0.04);
}

.page-header-divider .ui-quickfilter-menu .ui-dropdown-item.active {
    background: rgba(37, 37, 37, 0.06);
    color: var(--princeton-dark);
}

.page-header-divider .ui-quickfilter-menu .ui-dropdown-item.active i {
    color: var(--accent);
}

.page-title {
    font-size: 22px;
    font-weight: 800;
    color: var(--text-heading);
    letter-spacing: -0.02em;
    line-height: 1.2;
    /* Un codice commessa senza spazi allargava la pagina a 525 px su telefono. */
    overflow-wrap: anywhere;
}

.page-subtitle {
    font-size: 14px;
    color: var(--iron);
    margin-top: 4px;
}

/* Testata di pagina di questo progetto (LB-2, `partials/testata-pagina.php`): le
   classi di BI qui sopra piu' il divisore che BI mette con le utility
   (`py-3 border-b mb-6`, misurato su ?page=users il 21/09/2026). Catalogo:
   «Testata di pagina». */
.ui-testata {
    padding: 12px 0;
    border-bottom: 1px solid var(--border);
}
.ui-testata-briciole {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px;
    margin: 0;
    padding: 0;
    list-style: none;
}
.ui-testata-briciole li + li::before {
    content: '/';
    margin-right: 4px;
}
.ui-testata-briciole span[aria-current] {
    color: var(--text-heading);
}
/* Azioni a destra del titolo; sotto `sm` la riga (flex-wrap) le manda a capo. */
.ui-testata-azioni {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    min-width: 0;
}

/* Page section header pattern (title + breadcrumb + internal menu + time filter) */

/* Premium vertical rhythm for template page */

/* Numeric typography: force monospace for numeric UI values */

.ui-badge {
    font-family: var(--font-mono);
    letter-spacing: 0;
    font-variant-numeric: tabular-nums;
}

/* Tables: numeric columns (td only — th headers keep sans-serif) */
table td.text-right,
.sortable-table td.text-right {
    font-family: var(--font-mono);
    letter-spacing: 0;
    font-variant-numeric: tabular-nums;
}

/* =============================================================================
   INTERACTIVE NORMALIZATION
   ============================================================================= */

/* Uniform border radius for interactive UI elements (excluding widget containers) */
.ui-btn,
.ui-btn-sm,
.ui-chip,
.ui-tab,
.ui-tabs .ui-tablist,
.widget-body .ui-tabs .ui-tablist,
.ui-internal-menu,
.ui-internal-menu-item,
.ui-dropdown-trigger,
.ui-dropdown-menu,
.ui-dropdown-item,
.ui-input,
.ui-select {
    border-radius: var(--interactive-radius) !important;
}

.ui-badge {
    border-radius: var(--badge-radius) !important;
    font-size: 11px !important;
}

/* Remove elevation from UI; keep only main content shadow */
body *,
body *::before,
body *::after {
    box-shadow: none !important;
}

/* Tooltip boxes: subtle elevation for readability */

/* ═══════════════════════════════════════════════════════════════════════════
   CHAT WIDGET — Floating AI chatbot
   ═══════════════════════════════════════════════════════════════════════════ */

/* FAB (Floating Action Button) */

/* Panel */

/* Panel Header */

/* Messages area */

/* Message bubbles */

/* Typing dots */

/* Form */

/* Mobile responsive */

/* ── Global text size bump (+1px) ─────────────────────────────────────────── */

/* Table cells: 13px → 14px */
td[class*="text-[13px]"],
.text-\[13px\] { font-size: 14px !important; }

/* Meta/labels: 11px → 12px */
td[class*="text-[11px]"],
th[class*="text-[11px]"],
.text-\[11px\] { font-size: 12px !important; }

/* Hints: 10px → 11px */
.text-\[10px\] { font-size: 11px !important; }

/* ── Scheda Azienda compilabile — Task 7 ───────────────────────────────── */

/* Il colore dell'anello dice il LIVELLO di completezza, quindi e' semantica e
   ha un nome. Era `border-color` inline, cioe' la stessa scelta scritta a mano
   in ogni punto che disegna un anello. */

/* Table header th with text-xs: 12px (Tailwind default) */
thead th { font-size: 12px; }

/* KPI labels: bump +1px */

.kpi-mini .kpi-label { font-size: 12px; }

/* Widget titles: 11px → 12px (base already updated above) */

/* Dropdown menu items */
.ui-dropdown-item { font-size: 13px; }
.ui-quickfilter-menu .ui-dropdown-item { font-size: 13px; }

/* ========================================================
   Budget previsionale dal bilancio
   ======================================================== */

/* ============================================================
   GUIDA ONLINE (Manuale Funzionale + altri documenti)
   ============================================================ */

.guida-wrap {
    width: 100%;
}

/* Doc switcher (visibile solo se >1 documento in catalogo) */
.guida-doc-switcher {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 14px;
    background: #fff;
    border: 1px solid var(--alabaster);
    border-radius: 8px;
    margin-bottom: 14px;
    flex-wrap: wrap;
}
.guida-doc-switcher-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--stone);
    display: inline-flex;
    align-items: center;
    gap: 5px;
    flex-shrink: 0;
}

.guida-searchbar {
    position: sticky;
    top: 0;
    z-index: 30;
    background: var(--platinum);
    padding: 6px 0 8px;
    margin-bottom: 12px;
    border-bottom: 1px solid var(--alabaster);
}
.guida-search-input-wrap {
    position: relative;
    display: flex;
    align-items: center;
}
.guida-search-icon {
    position: absolute;
    left: 14px;
    color: var(--stone);
    font-size: 16px;
    pointer-events: none;
}
.guida-search-input {
    width: 100%;
    padding: 9px 44px 9px 42px;
    border: 1px solid var(--silver);
    border-radius: 8px;
    background: #fff;
    font-size: 13.5px;
    color: var(--jet);
    outline: none;
    transition: border-color .15s, box-shadow .15s;
}
.guida-search-input:focus {
    border-color: var(--jet);
    box-shadow: 0 0 0 3px rgba(37, 37, 37,0.08);
}
.guida-search-input::placeholder { color: var(--stone); }

.guida-search-meta {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 4px 4px 0;
}

/* Search results dropdown */

.guida-searchbar { position: sticky; }
.guida-search-input-wrap { position: relative; }

/* Highlight pulse on jump */

/* Screenshot della guida (`![didascalia](/assets/img/guida/…)`): bordo come le card, didascalia sotto. */
.guida-figura {
    margin: 16px 0;
}

.guida-figura img {
    display: block;
    max-width: 100%;
    height: auto;
    border: 1px solid var(--alabaster);
    border-radius: var(--radius-md);
}

.guida-figura figcaption {
    margin-top: 6px;
    font-size: 12px;
    color: var(--stone);
}

.guida-layout {
    display: grid;
    grid-template-columns: 270px minmax(0, 1fr);
    gap: 24px;
    align-items: start;
}
@media (max-width: 960px) {
    .guida-layout { grid-template-columns: 1fr; }
    .guida-toc { position: static !important; max-height: none !important; }
}

/* TOC */
.guida-toc {
    position: sticky;
    top: 64px;
    max-height: calc(100vh - 88px);
    overflow-y: auto;
    overscroll-behavior: contain;
    padding-right: 8px;
    font-size: 13.5px;
    align-self: start;
}
.guida-toc-header {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--stone);
    font-weight: 700;
    padding: 6px 10px 10px;
    border-bottom: 1px solid var(--alabaster);
    margin-bottom: 10px;
}
.guida-toc-list { display: flex; flex-direction: column; gap: 0; }
.guida-toc-link {
    display: block;
    padding: 9px 12px;
    border-radius: 6px;
    color: var(--iron);
    font-weight: 600;
    line-height: 1.4;
    text-decoration: none;
    margin-top: 12px;
    transition: background-color .12s, color .12s;
}
.guida-toc-link:first-child { margin-top: 0; }
.guida-toc-link:hover { background: var(--alabaster); color: var(--jet); }
/* Come la sottovoce attiva: solo fondo, niente bordo (il bordo arrotondato sembrava una scheda). */
.guida-toc-link--active {
    background: var(--alabaster);
    color: var(--jet);
}

.guida-toc-sub {
    display: flex;
    flex-direction: column;
    gap: 0;
    margin: 4px 0 0 14px;
    padding-left: 10px;
    border-left: 1px solid var(--alabaster);
}
.guida-toc-sublink {
    display: block;
    padding: 7px 10px;
    border-radius: 5px;
    color: var(--stone);
    font-size: 13px;
    line-height: 1.4;
    font-weight: 500;
    text-decoration: none;
    transition: background-color .12s, color .12s;
}
.guida-toc-sublink:hover { background: var(--alabaster); color: var(--iron); }
.guida-toc-sublink.guida-toc-link--active {
    background: var(--alabaster);
    color: var(--jet);
    font-weight: 600;
}

/* Content */
.guida-content {
    min-width: 0;
    background: #fff;
    border: 1px solid var(--alabaster);
    border-radius: 10px;
    padding: 8px 24px 24px;
}
.guida-section {
    padding: 18px 0;
    border-bottom: 1px solid var(--alabaster);
}
.guida-section:last-of-type { border-bottom: 0; }
/* Spazio sotto l'ultima sezione: senza, un clic dell'indice sulle ultime voci non le porta in cima. */
.guida-layout { padding-bottom: 60vh; }
/* La barra di ricerca resta fissa in alto: il titolo si ferma sotto di lei (guida.js legge lo stesso valore). */
.guida-section-title {
    font-size: 22px;
    font-weight: 600;
    color: var(--jet);
    margin: 0 0 12px;
    scroll-margin-top: 110px;
}
.guida-section-body h3 {
    font-size: 16px;
    font-weight: 600;
    color: var(--jet);
    margin: 22px 0 10px;
    scroll-margin-top: 110px;
}
.guida-section-body h4 {
    font-size: 14px;
    font-weight: 600;
    color: var(--iron);
    margin: 18px 0 8px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.guida-section-body p {
    font-size: 14px;
    line-height: 1.65;
    color: var(--iron);
    margin: 0 0 12px;
}
.guida-section-body ul,
.guida-section-body ol {
    font-size: 14px;
    line-height: 1.6;
    color: var(--iron);
    margin: 0 0 12px;
    padding-left: 22px;
}
.guida-section-body ul { list-style: disc; }
.guida-section-body ol { list-style: decimal; }
.guida-section-body li { margin: 3px 0; }
.guida-section-body li > ul,
.guida-section-body li > ol { margin: 4px 0 4px 0; }
.guida-section-body strong { color: var(--jet); font-weight: 600; }
.guida-section-body em { font-style: italic; }
.guida-section-body a { color: var(--jet); text-decoration: underline; text-underline-offset: 2px; }
.guida-section-body a:hover { color: var(--seaweed); }
.guida-section-body hr {
    border: 0;
    border-top: 1px solid var(--alabaster);
    margin: 18px 0;
}
.guida-section-body code {
    background: var(--platinum);
    border: 1px solid var(--alabaster);
    padding: 1px 6px;
    border-radius: 4px;
    font-family: 'Roboto Mono', ui-monospace, monospace;
    font-size: 12.5px;
    color: var(--jet);
}
.guida-section-body pre {
    background: var(--jet);
    color: var(--platinum);
    padding: 14px 16px;
    border-radius: 8px;
    overflow-x: auto;
    font-size: 12.5px;
    line-height: 1.55;
    margin: 12px 0 14px;
}
.guida-section-body pre code {
    background: transparent;
    border: 0;
    padding: 0;
    color: inherit;
    font-size: inherit;
}
.guida-section-body blockquote {
    border-left: 3px solid var(--silver);
    padding: 4px 14px;
    color: var(--iron);
    margin: 12px 0;
    background: var(--platinum);
    border-radius: 0 6px 6px 0;
}
.guida-section-body blockquote p:last-child { margin-bottom: 0; }

/* Tables */
.guida-table-wrap {
    overflow-x: auto;
    margin: 12px 0 16px;
    border: 1px solid var(--alabaster);
    border-radius: 8px;
}
.guida-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
}
.guida-table th,
.guida-table td {
    padding: 8px 12px;
    text-align: left;
    border-bottom: 1px solid var(--alabaster);
    vertical-align: top;
}
.guida-table th {
    background: var(--platinum);
    color: var(--jet);
    font-weight: 600;
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    border-bottom: 2px solid var(--silver);
}
.guida-table tbody tr:last-child td { border-bottom: 0; }
.guida-table tbody tr:hover { background: var(--platinum); }

/* Search highlight */

/* Empty state */
.guida-empty {
    padding: 60px 20px;
    text-align: center;
}

/* ==========================================================================
   Card modulo della home (?page=home)
   ==========================================================================
   La home disegna una card per modulo con header, descrizione, KPI e un piede
   di collegamenti separato da una riga. Il piede deve toccare i bordi, quindi
   il padding non sta sulla card ma sulle sue parti — e per ottenerlo `home.php`
   portava **sei attributi `style=` inline**, che CLAUDE.md vieta e che nessuno
   avrebbe trovato cercando in app.css.
   Le misure sono quelle che c'erano, riportate qui senza cambiarle. */

/* =============================================================================
   MOVIMENTO RIDOTTO
   Chi ha chiesto meno movimento non ha chiesto nessun feedback: restano
   opacita' e colore, che spiegano cosa sta cambiando (BI DESIGN_SYSTEM «Movimento
   ridotto: meno, non zero»). Si restringe l'elenco delle proprieta', non la durata:
   cio' che si sposta scatta, cio' che cambia aspetto restando fermo transita.
   Via l'oscillazione continua.
   ============================================================================= */

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-property: opacity, color, background-color, border-color, fill, stroke !important;
        transition-duration: 150ms !important;
        scroll-behavior: auto !important;
    }

    /* Eccezione: lo spinner. Un indicatore di attesa fermo si legge come
       "bloccato", che e' peggio del movimento che eviterebbe. Resta, piu' lento. */
    .bi-spin {
        animation-duration: 2.4s !important;
        animation-iteration-count: infinite !important;
    }

    /* Le pulsazioni si fermano da accese, non da spente: congelate a opacita'
       0,55 sembrerebbero disabilitate. */
    

    /* La barra indeterminata avanza per traslazione: ferma resterebbe fuori
       campo, cioe' invisibile. Diventa piena. */
    
}

/* Lo scrim del menu mobile: la sidebar scivola per 300 ms, il velo compariva
   e spariva di colpo. Stessa tecnica del modale, guidata dalla `.hidden` che
   initMobileSidebar() gia' commuta. */
#sidebarOverlay {
    opacity: 1;
    transition: opacity 300ms var(--ease-out),
                display 300ms allow-discrete;
}

#sidebarOverlay.hidden {
    display: none;
    opacity: 0;
}

@starting-style {
    #sidebarOverlay:not(.hidden) {
        opacity: 0;
    }
}

/* Righe del banner (#bi-banner): entrano e escono dallo stesso bordo, quello
   di sopra. Un percorso asimmetrico si legge come due elementi diversi.
   Transizione e non keyframes: le righe si accodano a raffica e un keyframe
   ripartirebbe da zero invece di ri-mirare.
   Percentuali e non pixel: l'altezza della riga dipende dal testo. */
.bi-banner-riga {
    opacity: 1;
    transform: translateY(0);
    transition: opacity 250ms var(--ease-out),
                transform 250ms var(--ease-out);
}

@starting-style {
    .bi-banner-riga {
        opacity: 0;
        transform: translateY(-100%);
    }
}

/* L'uscita e' piu' svelta dell'entrata: chi arriva si annuncia, chi se ne va
   non deve far aspettare. */
.bi-banner-riga--esce {
    opacity: 0;
    transform: translateY(-100%);
    transition-duration: 200ms;
}

/* Blocco in attesa di dati (BiUI.setBusy). Non nasconde il contenuto vecchio:
   lo smorza. Chi guardava un numero continua a vederlo mentre arriva quello
   nuovo, e intanto sa che non e' piu' quello buono. */
.ui-cella-vuota {
    color: var(--text-muted);
    cursor: help;
}

.is-busy {
    opacity: 0.45;
    pointer-events: none;
    transition: opacity 120ms var(--ease-out);
}

/* =============================================================================
   19. COMPONENTI RIPORTATI DA SPARK — 24/08/2026
   -----------------------------------------------------------------------------
   Spark (`~/Documents/vhosts/spark`) e' un fork puro del BI al commit dfd24323
   e ha maturato questi componenti dopo l'import. Qui stanno solo quelli
   GENERICI: cio' che dipende dalle viste di Spark (scheda anagrafica, recapiti,
   figure chiave, contatore di spesa) resta la'.

   ⚠️ Le prime otto classi NON sono componenti nuovi: erano gia' scritte nel
   markup del BI e in questo foglio non esistevano, quindi non applicavano
   niente. Contate il 24/08/2026 sul markup, come token esatto:
   `ui-dropdown-header` 23 usi · `ui-table` 11 · `ui-checkbox` 10 ·
   `ui-btn-xs` 2 · `ui-select-sm` 2 · `ui-dropdown-caret` 2 · `ui-avatar` 1 ·
   `ui-code-block` 1. Definirle chiude un difetto: chi le ha scritte si
   aspettava uno stile e non l'ha avuto.
   ============================================================================= */

/* ── Dropdown: intestazione di gruppo e freccetta ─────────────────────────── */

/* Reggeva solo se il markup ci aggiungeva anche `widget-title`: da sola era
   testo a 14px, indistinguibile da una voce. Ora e' un maiuscoletto come ogni
   altro titolo del sistema, e il markup non deve ricordarsi niente. */
.ui-dropdown-header {
    padding: 7px 8px 5px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--text-muted);
}

/* La freccetta del trigger. Non aveva NESSUNA regola di base: l'unica che la
   nominava era dentro `.ui-dropdown-demo`, cioe' la palestra, quindi fuori di
   li' era un'icona ferma. Ruota quando il menu e' aperto — e la rotazione e'
   l'unico segnale che il clic ha fatto qualcosa quando il pannello si apre
   fuori dal campo visivo. */

/* ── Barra di paginazione ─────────────────────────────────────────────────── */
/* Rimessa il 26/08/2026 col suo JS. Era arrivata da Spark il 24/08 SENZA
   `BiUI.paginate()` — sei regole che promettevano un componente inesistente, e
   nessun file le nominava — ed era stata tolta per questo. Ora la barra la
   disegna l'helper, quindi le classi hanno di nuovo un consumatore. */
.ui-paginator {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    padding: 0.75rem 0 0.25rem;
    border-top: 1px solid var(--border);
}

.ui-paginator-info {
    font-size: 12px;
    color: var(--text-muted);
}

.ui-paginator-pages {
    display: flex;
    align-items: center;
    gap: 0.125rem;
}

/* Bottoni della paginazione (catalogo «Tabella filtrabile»): 28x28 come i bottoni a icona, la
   pagina corrente con `aria-current="page"` sul fondo del token della tabella. */
.ui-pagina,
.ui-pagina-salto {
    width: 28px;
    height: 28px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 11px;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
}

.ui-pagina {
    border: 0;
    border-radius: var(--interactive-radius);
    background: transparent;
    color: var(--text-body);
    cursor: pointer;
    transition: background-color 150ms var(--ease-out), color 150ms var(--ease-out);
}

.ui-pagina:hover:not(:disabled):not([aria-current="page"]) {
    background: color-mix(in srgb, var(--iron) 10%, transparent);
}

.ui-pagina[aria-current="page"] {
    background: var(--table-page-active-bg);
    color: var(--table-page-active-text);
}

.ui-pagina:disabled {
    color: var(--text-light);
    cursor: not-allowed;
}

.ui-pagina:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 1px;
}

.ui-pagina .bi {
    font-size: 10px;
}

.ui-pagina-salto {
    color: var(--text-light);
}

/* Il contenitore in cui `BiUI.paginate()` disegna la barra. La CODA di un
   blocco non prende il ritmo verticale dei blocchi (36 px): la barra descrive la
   tabella che la precede, e staccarla la fa leggere come un elemento a se'. */
.ui-paginator-mount {
    padding-top: 0;
}

/* ── Controlli piccoli ────────────────────────────────────────────────────── */

/* L'icona di riga porta il bordo gia' a riposo: e' il modo in cui si distingue
   da un glifo decorativo. */

/* ── Spunta ───────────────────────────────────────────────────────────────── */

.ui-checkbox {
    width: 15px;
    height: 15px;
    accent-color: var(--jet);
    cursor: pointer;
    vertical-align: middle;
}

.ui-checkbox:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
}

/* La colonna che ospita la spunta: larghezza fissa, o la prima colonna di dati
   si stringe di quel tanto a ogni tabella diversa. */

/* ── Avatar ───────────────────────────────────────────────────────────────── */

/* ── Blocco di codice ─────────────────────────────────────────────────────── */

/* Un link dentro una frase: si distingue per la sottolineatura, non per il
   colore — il colore da solo non basta a chi non lo distingue. */
.ui-link-inline {
    color: var(--text-heading);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.ui-link-inline:hover { color: var(--iron); }

/* Cella che tronca a due righe invece di allargare la colonna. */

/* =============================================================================
   TABELLA BASE — .ui-table
   -----------------------------------------------------------------------------
   `class="ui-table"` compare in 11 punti del BI e non applica NIENTE, mentre lo
   stile sta spalmato su ogni cella (px-4 py-3 text-right text-xs font-bold
   text-ui-body uppercase tracking-wide — otto e piu' classi per <th>). Nessuno
   ricopia dodici classi identiche due volte: e' da li' che le tabelle divergono
   fra loro.

   Due modificatori, perche' sono decisioni e non ripetizioni:
     .num     cella numerica: destra, cifre tabellari, mai a capo
     .fissa   colonna che resta ferma allo scroll orizzontale
   Si combina con .sortable-table e .ui-table-sticky-head.

   ⚠️ DIVERGENZA VOLUTA da Spark: la' `.ui-table tbody td` porta
   `text-transform: uppercase` e `font-size: 11px`, perche' le anagrafiche di
   Axerta arrivano dai gestionali gia' in maiuscolo e due tabelle vicine si
   leggevano come di natura diversa. Qui il dato non e' quello e le tabelle
   esistenti non sono maiuscole: importarlo cambierebbe undici punti in una
   direzione che nessuno ha chiesto. Se un giorno servira', si decide con la
   sua vetrina nel catalogo.
   ============================================================================= */
.ui-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--table-font-size, 13px);
}

.ui-table thead {
    background: var(--platinum);
    border-bottom: 1px solid var(--border);
}

.ui-table th {
    padding: var(--cella-y) 1rem;
    text-align: left;
    font-size: 11px;
    font-weight: 700;
    color: var(--text-body);
    text-transform: uppercase;
    letter-spacing: 0.03em;
    white-space: nowrap;
}

.ui-table td {
    padding: var(--cella-y) 1rem;
    vertical-align: middle;
}
/* ⚠️ Il colore sta in un `:where()`, e non e' stile: `.ui-table td` ha specificita'
   0,1,1 e batteva QUALUNQUE utility di colore su una cella (0,1,0). Misurato il
   14/09/2026 in Chrome 152: un `<td class="… text-dark-emerald">` di `?page=conti`
   rendeva **iron**, mentre uno `<span>` nudo con la stessa classe rendeva verde —
   cioe' il colore non arrivava a schermo e nessun errore lo diceva. Vale per le 72
   celle che nel markup portano un `text-*` di palette. `:where()` porta il selettore
   a 0,0,1, quindi la riga resta il default e l'utility torna a vincere. */
:where(.ui-table) td { color: var(--text-body); }

.ui-table tbody tr {
    border-bottom: 1px solid var(--border);
    transition: background-color 120ms var(--ease-out);
}

.ui-table tbody tr:last-child { border-bottom: 0; }

.ui-table tbody tr:hover { background: var(--platinum); }

/* Nome di una controparte di fattura, sempre in maiuscolo.
   ⚠️ È una scelta di PRESENTAZIONE su un dato che non lo è: il gestionale scrive
   `fatture.cliente_nome` come testo libero, e sui 737 documenti del tenant 1 ce ne
   sono 504 righe in maiuscolo e 233 miste (misurato il 14/09/2026). Decisa da
   Nicola quel giorno, sapendo che copre **2 gruppi su 17**: gli altri 15 non sono
   maiuscole diverse, sono NOMI diversi della stessa azienda — «Holzner &
   Bertagnolli», «Holzner & Bertagnolli Engineering» e «… Engineering SRL» convivono
   in archivio. Quel difetto resta, e sta in `tasks/todo.md`.
   ⚠️ Si fa in CSS e non nella stringa, e non è un dettaglio: ordinamento e ricerca
   leggono `textContent`, che `text-transform` NON cambia. Alzando le lettere in
   JavaScript si ordinerebbe e si cercherebbe sul testo alterato.
   Un testo che NON è un nome — «Non indicata su HubSpot» — porta `normal-case`. */

/* Riga dei totali. Sta in un `<tfoot>`, quindi si stila la STRUTTURA e non serve
   una classe: fino al 14/09/2026 ogni tabella col totale se lo disegnava a mano
   (l'aging con `bg-jet/5 font-semibold text-jet`, i report di movimenti-cassa con
   un'altra grafia ancora), e due totali diversi nella stessa app si leggono come
   due cose diverse. Il bordo sopra e' piu' marcato perche' separa il corpo dal
   riepilogo, non una riga dall'altra. */
.ui-table tfoot td {
    border-top: 2px solid var(--border);
    background: var(--platinum);
    font-weight: 600;
    color: var(--text-heading);
}

/* Prima colonna: e' il nome della cosa, quindi porta il peso. */
.ui-table tbody td:first-child {
    font-weight: 600;
}
/* Stessa ragione della riga sopra. ⚠️ Il `:where()` deve avvolgere ANCHE
   `td:first-child`: `:first-child` e' una pseudo-CLASSE e conta nella colonna
   delle classi, quindi `:where(.ui-table tbody) td:first-child` vale ancora 0,1,1
   e continuava a battere `.text-ui-body` (0,1,0) — misurato su cinque celle di
   `?page=import`, che rendevano jet invece di iron. Cosi' il selettore vale 0,0,0. */
:where(.ui-table tbody td:first-child) { color: var(--text-heading); }

/* Numeri: a destra, cifre di larghezza uguale perche' le colonne si leggano
   incolonnate, e mai a capo — un importo spezzato fra due righe si legge come
   due numeri. */
.ui-table th.num,
.ui-table td.num {
    text-align: right;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

/* Colonna ferma allo scroll orizzontale.
   ⚠️ Qui c'era `background: inherit`, e sarebbe stato il difetto del primo che
   la usava: le righe di `.ui-table` non hanno un fondo proprio, quindi `inherit`
   vale **trasparente** e le colonne le sarebbero passate sotto. E vinceva:
   `.ui-table td.fissa` (0,2,1) sta sopra alla regola che il fondo lo mette
   davvero. Nessuno se n'era accorto perche' la classe non la usava nessuno —
   era una promessa del catalogo, non un componente. Tolta il 09/09/2026.

   Il fondo arriva dalle regole di riga piu' in alto in questo foglio —
   `table tbody tr:nth-child(even) td:not([class*="bg-"])` per la zebratura e
   `table tbody tr:hover td` per l'over — che valgono per OGNI cella, `fissa`
   compresa, **finche' la cella non porta una classe `bg-`**. Verificato a
   schermo il 09/09/2026: fondo opaco su tutte e cinque le righe, uguale a quello
   delle celle vicine. Quindi qui non serve ripeterlo: due copie dello stesso
   fondo divergerebbero, e chi legge non saprebbe quale delle due comanda.
   ⚠️ Il corollario e' che `.fissa` **non si combina con una classe `bg-`**: chi
   gliela mette spegne la regola di riga e si ritrova la cella trasparente. */
.ui-table th.fissa,
.ui-table td.fissa {
    position: sticky;
    left: 0;
    z-index: 1;
}

.ui-table thead th.fissa { background: var(--platinum); z-index: 2; }

/* `.ui-table th` (0,1,1) mette `text-align: left` e batte le utility di allineamento: senza queste
   l'intestazione «Azioni» restava a sinistra delle icone centrate (fino a 98 px, misurato il 23/09/2026).
   Stesso difetto in BI. */
.ui-table th.text-center { text-align: center; }
.ui-table th.text-right { text-align: right; }

/* Modificatori del progetto. `.ui-table th` tiene l'intestazione su una riga (0,1,1: un'utility non la batte):
   un'intestazione lunga in una tabella che deve stare in 390 px la manda a capo. Il registro allinea in alto le
   righe di altezza diversa (dettagli su più righe). */
.ui-table th.ui-th-a-capo { white-space: normal; }
.ui-table--cima td { vertical-align: top; }

/* ── Stati di una tabella: vuota e in guasto ──────────────────────────────── */

/* Una tabella senza righe deve dire PERCHE', non restare bianca. */
.ui-table-vuota td,
.ui-table .ui-table-vuota td {
    padding: 2.5rem 1rem;
    text-align: center;
    color: var(--text-muted);
    font-weight: 400;
}

/* Il caricamento non e' riuscito, e la riga ospita un `.ui-error-state` intero:
   la cella non mette padding proprio, quello lo porta gia' lo stato, e sommati
   farebbero 48+40 px. */
/* L'attesa: stessa geometria del vuoto ma piu' bassa — sta a schermo pochi
   secondi, e 2,5rem di respiro su una riga che sparisce fanno saltare la
   tabella in altezza quando arrivano i dati. */
.ui-table-attesa td,
.ui-table .ui-table-attesa td {
    padding: 1.5rem 1rem;
    text-align: center;
    color: var(--text-muted);
    font-weight: 400;
}

.ui-table-errore td,
.ui-table .ui-table-errore td {
    padding: 0;
    text-align: center;
}

/* La fascia dei fatti misurabili in cima a una scheda: fondo tenue e
   nient'altro — nessun bordo, nessuna ombra. L'evidenza e' **leggera apposta**:
   distingue le misure dalle righe di testo che seguono senza diventare una card
   dentro la card, che a schermo si legge come un widget annidato. */

/* ⚠️ Il margine sotto lo prende solo se QUALCOSA SEGUE dentro lo stesso blocco:
   dove la fascia chiude il suo `widget-body` il distacco lo da' gia' il ritmo
   verticale fra blocchi (36 px), e i due si sommavano a 56. Una spaziatura che
   dipende da un vicino va condizionata al vicino. */

/* ⚠️ 16 px, non i 20 di `ui-metrics--riga`, e l'override sta qui e non nel
   componente: la' la banda e' il contenuto principale di una card, qui
   introduce una lista di righe da 13-14 px — quattro valori da 20 px sopra
   quelle righe si leggono come il titolo della card, e non lo sono. Il fondo
   tenue basta gia' a distinguerli. */

/* ── Componenti nuovi: nessuno di questi era nel BI ────────────────────────────
   Barra di selezione · paginatore · tooltip di spiegazione · campi di anagrafica
   · card selezionabile · banda di metriche · varianti chiare del badge. Il
   markup da cui si copia sta in `?page=template`; i consumatori JS
   (`BiUI.paginate`, `BiUI.tabellaVuota`, `BiUI.erroreTabella`) arrivano col
   lotto su `helpers.js`. Finche' non ci sono, queste regole non rendono niente
   e non tolgono niente: e' la condizione per portarle prima. */

/* La barra che compare quando qualcosa e' selezionato. Sta SOPRA la tabella e
   non nell'intestazione del widget: e' uno stato dell'insieme, non un comando
   permanente, e sparisce quando la selezione si svuota. */

/* Il filetto che stacca l'uscita dalle azioni. Senza, «annulla» sta in fila
   con «arricchisci» e si legge come una terza cosa da fare. */

/* Due o tre metriche AFFIANCATE, per quando stanno sopra una tabella o due
   grafici larghi. È una BANDA DI FATTI, non un elenco: etichetta sopra, valore
   sotto, i gruppi impacchettati a sinistra.

   ⚠️ Non è la stessa cosa di prima, ed è una correzione del 21/08/2026. La
   variante affiancava tre `.ui-metric`, che restavano `space-between` dentro un
   terzo di riga: a 1440 px l'etichetta e il suo valore finivano a **225 px**
   l'uno dall'altro (a 2100 px, ~450), e i sei elementi si leggevano come sei
   colonne invece di tre coppie — la stessa famiglia del difetto che questa
   variante era nata per chiudere, solo un livello più in basso. Oggi la
   distanza è la sola interlinea e i bordi sinistri distinti sono **tre**.

   Perché a pacchetto e non in griglia: distribuire su `1fr` ciascuna lega la
   distanza fra i gruppi alla larghezza della card, quindi il difetto ritorna da
   solo appena la finestra cresce. Con `flex` la banda ha la larghezza del suo
   contenuto e la prossimità dice il raggruppamento — 2 px dentro la coppia
   contro 48 fra una coppia e l'altra.

   `flex-wrap` invece della rottura a 1024: i gruppi vanno a capo quando non ci
   stanno, senza una soglia da indovinare. */

/* IDENTICA alle didascalie dei grafici che stanno sotto (`text-2xs
   font-medium uppercase tracking-wider` = 10 px, 0,05em): l'etichetta di una
   metrica e la didascalia di un grafico dicono la stessa cosa — che dato è
   questo — quindi hanno lo stesso rango. A 11 px erano *quasi* uguali, che a
   40 px di distanza si legge come una disattenzione invece che come una
   gerarchia. Nella banda l'unica cosa che cambia rango è il valore. */

/* 20 px: il valore è ciò che si cita a voce, e a 12 px pesava come la sua
   etichetta. Tracking negativo perché il testo cresce — vedi §Tono e numeri. */

/* L'avviso che COMPARE a caricamento avvenuto, non quello scritto nel markup:
   e' un modificatore e non il default apposta, altrimenti ogni avviso gia' in
   pagina si dissolverebbe all'apertura e la palestra, che ne ha una decina,
   diventerebbe una cascata che nessuno ha chiesto.
   Stessa tecnica del modale: `allow-discrete` fa transitare anche `display`,
   @starting-style da' i valori del primo frame.
   ⚠️ La dissolvenza NON annulla lo spostamento del contenuto sotto: lo rende
   leggibile. Chi non puo' permetterselo riserva lo spazio, non anima. */
.ui-alert--reveal {
    opacity: 1;
    transform: translateY(0);
    transition: opacity 200ms var(--ease-out),
                transform 200ms var(--ease-out),
                display 200ms allow-discrete;
}

.ui-alert--reveal.hidden {
    display: none;
    opacity: 0;
    transform: translateY(-4px);
}

@starting-style {
    .ui-alert--reveal:not(.hidden) {
        opacity: 0;
        transform: translateY(-4px);
    }
}

/* Il vuoto di una SEZIONE dentro una card, non della card intera: una riga
   sola, allineata a sinistra, senza icona.

   ⚠️ Serve perché due `ui-empty-state` centrati impilati nella stessa card
   producono ~600 px di vuoto — misurati sulla scheda anagrafica, dove
   l'anagrafica non rilevata e i recapiti assenti convivono su 2.728 voci su
   2.729. Un rettangolo vuoto grande quanto una card piena si legge come
   contenuto perduto; qui il titolo della sezione resta e sotto una riga dice
   perché. La card intera continua a usare la forma centrata: lì il vuoto È il
   contenuto, e centrarlo lo dichiara. */
.ui-empty-state--inline {
    display: block;
    padding: 0;
    text-align: left;
}

.ui-empty-state--inline .ui-empty-state-icon { display: none; }

.ui-empty-state--inline .ui-empty-state-title {
    display: inline;
    font-size: 12px;
    font-weight: 500;
}

/* ⚠️ `:not(:last-child)`: il trattino SEPARA il titolo dal testo, e senza
   testo restava sospeso in fondo alla riga — «Nessun dato dal Registro Imprese
   —», che si legge come una frase troncata. Il testo è facoltativo per
   costruzione (dove l'avviso sopra ha già spiegato il perché), quindi il
   separatore deve dipendere dalla sua presenza. */
.ui-empty-state--inline .ui-empty-state-title:not(:last-child)::after { content: ' — '; }

.ui-empty-state--inline .ui-empty-state-text {
    display: inline;
    font-size: 12px;
    max-width: none;
}

/* Campi di anagrafica — DUE colonne, e l'etichetta SOPRA il valore.
   In una riga `space-between` a piena larghezza l'etichetta e il suo valore
   finiscono a 900 px di distanza: l'occhio deve attraversare la card per
   ricomporre una coppia, e su dodici righe non lo fa più. Impilati, la coppia
   sta in 200 px e le colonne raddoppiano la densità a parità di spazio. */
.ui-campi {
    display: grid;
    grid-template-columns: 1fr;
    gap: 14px 32px;
}

/* 640 px misurati sul corpo del widget: sotto, due colonne scendono a 260 px
   l'una — la soglia oltre cui l'occhio smette di ricomporre la coppia
   etichetta/valore (misurato 20/08/2026: la card a 546 px dentro lo split
   1/2+1/2 dava 240 px a colonna). Sopra, il corpo è largo almeno quanto la
   pagina intera sotto `lg` (828 px a 900 px di viewport) e due colonne
   restano leggibili. */
@container (min-width: 640px) {
    .ui-campi { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    /* Un valore lungo — una ragione sociale per esteso, un indirizzo — prende
       la riga intera invece di stringere anche la colonna accanto. */
    .ui-campi .ui-campo--largo { grid-column: 1 / -1; }
}

.ui-campo { min-width: 0; }

.ui-campo-etichetta {
    display: block;
    font-size: 11px;
    letter-spacing: 0.03em;
    color: var(--stone);
    margin-bottom: 2px;
}

/* Il valore è TESTO, quindi sans: il monospace serve a incolonnare cifre, e su
   una ragione sociale allarga le parole senza dare niente in cambio. I numeri e
   gli identificatori lo tengono, con `.ui-campo-valore--mono`. */
.ui-campo-valore {
    display: block;
    font-size: 14px;
    font-weight: 500;
    color: var(--jet);
    overflow-wrap: anywhere;
}

.ui-campo-valore--mono { font-family: 'Roboto Mono', monospace; font-size: 13px; }

.ui-campo-valore--vuoto { color: var(--stone); font-style: italic; font-weight: 400; }

/* ============================================================
 * Tooltip di spiegazione — `.ui-tooltip` + `.ui-tooltip-panel`
 * ============================================================
 * Il pannello che si apre dalla «i» accanto a un titolo. Era una collana di
 * utility ricopiata in **81 punti** con **cinque** firme diverse — `w-48`,
 * `w-56`, `w-64`, `w-72`, e una con `rounded-sm` — cioè la larghezza di un
 * tooltip la decideva chi lo scriveva.
 *
 * ⚠️ La larghezza ora è UNA, ed è misurata invece che scelta: la riga di testo
 * si legge fra 45 e 75 caratteri, e a 12px Inter un carattere vale ~6,2 px.
 * `w-64` (256 px) dà **41 caratteri per riga** — sotto il pavimento — ed è il
 * motivo per cui un testo lungo si leggeva come un blocco unico. 320 px danno
 * ~52 caratteri, dentro la finestra.
 *
 * ⚠️ E la STRUTTURA è del pannello, non di chi scrive: `p + p` stacca i
 * capoversi, `ul` fa l'elenco. Senza, l'unico modo di andare a capo era un
 * `<br>` — e un `<br>` non è un capoverso, è una riga in più dentro lo stesso
 * blocco. Il testo lungo va scritto in capoversi: la regola sta in
 * `DESIGN_SYSTEM.md § Tono dei testi`.
 *
 * ⚠️ `box-shadow` non esiste in questa applicazione (`body * { box-shadow:
 * none !important }`), quindi il `shadow-lg` che quelle 81 copie portavano non
 * disegnava niente: a separare il pannello è il BORDO, e per questo è più
 * scuro del bordo di una card — bianco su bianco misura 1,10:1 e non separa.
 */
.ui-tooltip {
    position: relative;
    display: inline-flex;
    align-items: center;
}

/* La «i» è un comando, quindi si raggiunge da tastiera: `:focus-within` apre il
   pannello come l'hover. Senza, l'unica spiegazione di un widget è dietro un
   gesto che con la tastiera non esiste. */
.ui-tooltip > .bi {
    font-size: 11px;
    color: var(--text-light);
    cursor: help;
}

.ui-tooltip > .bi:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
    border-radius: 2px;
}

.ui-tooltip-panel {
    display: none;
    position: absolute;
    right: 0;
    top: 22px;
    z-index: 50;
    width: 320px;
    max-width: min(360px, calc(100vw - 32px));
    padding: 12px 14px;
    background: var(--white);
    border: 1px solid var(--silver);
    border-radius: var(--radius-sm);
    font-size: 12px;
    line-height: 1.55;
    color: var(--text-body);
    text-transform: none;
    letter-spacing: normal;
    font-weight: 400;
    text-align: left;
    white-space: normal;
}

.ui-tooltip:hover > .ui-tooltip-panel,
.ui-tooltip:focus-within > .ui-tooltip-panel {
    display: block;
}

/* ⚠️ Sotto i 640 px il pannello si aggancia alla FINESTRA, non alla «i».
   Ancorato alla «i» con `right: 0` si apre verso sinistra, ed è giusto finché
   la «i» sta a destra della card — ma quando l'intestazione va a capo la «i»
   scende a sinistra, e i 320 px del pannello finivano a **-181 px**, cioè fuori
   dalla finestra dal lato opposto (misurato a 500 px sulla scheda anagrafica).

   Agganciarlo alla card non basta: dentro un KPI da 189 px il pannello
   scendeva a **155**, cioè 25 caratteri per riga — meno della metà del
   pavimento di leggibilità, quindi il rimedio del bordo avrebbe riprodotto il
   difetto del muro. `position: fixed` con `left`/`right` in pixel lo lega alla
   finestra: non può uscire da nessun lato e non può stringersi sotto la
   larghezza della finestra meno i margini, qualunque sia il contenitore.
   `top: auto` lascia la posizione STATICA, cioè quella che il pannello avrebbe
   avuto nel flusso: resta sotto la sua «i». */
@media (max-width: 640px) {
    .ui-tooltip-panel {
        position: fixed;
        left: 16px;
        right: 16px;
        top: auto;
        width: auto;
        max-width: none;
    }
}

.ui-tooltip-panel p + p { margin-top: 8px; }

.ui-tooltip-panel strong { color: var(--text-heading); font-weight: 600; }

.ui-tooltip-panel em { font-style: italic; }

.ui-tooltip-panel ul {
    margin-top: 6px;
    display: grid;
    gap: 4px;
}

.ui-tooltip-panel li {
    position: relative;
    padding-left: 12px;
}

.ui-tooltip-panel li::before {
    content: '\2013';
    position: absolute;
    left: 0;
    color: var(--stone);
}

/* Stato di guasto: il caricamento non e' riuscito, e la riga ospita un
   `.ui-error-state` intero. La cella non mette padding proprio — quello lo
   porta gia' lo stato, e sommati farebbero 48+40 px.
   ⚠️ `text-transform` si spegne: `.ui-table tbody td` mette maiuscolo perche'
   le anagrafiche arrivano cosi' dai gestionali, ma qui dentro non c'e' un dato,
   c'e' una frase — e «IL SERVER NON HA RISPOSTO» urla invece di spiegare.

/* Le due varianti che la famiglia .badge aveva e .ui-badge no, copiate
   identiche quando fu scritta l'unificazione. La famiglia vecchia non
   esiste piu' (07/08/2026); queste restano perche' sono in uso. */
.ui-badge--neutral { background: var(--alabaster); color: var(--iron); }

.ui-badge--light   { background: var(--platinum);  color: var(--iron); border-color: var(--alabaster); }

/* Outline (23/09/2026): stesso colore della variante, fondo trasparente e bordo tenue. Distingue un
   ATTRIBUTO di un dato (la gravità di una CVE) da uno STATO (Installata, Successiva, Attivo), che
   resta pieno. Si combina con una variante di colore: `ui-badge ui-badge--neutral ui-badge--outline`.
   Dopo le varianti, così a pari specificità vince lui. */
.ui-badge--outline {
    background: transparent;
    border-color: color-mix(in srgb, currentColor 45%, transparent);
}

/* A capo (01/10/2026): il badge in una colonna stretta, dove il testo intero è più largo della colonna
   («Aggiornamento disponibile» a 390 px nella tabella software del cliente). Fuori layer come .ui-badge:
   `whitespace-normal` di Tailwind qui non vince. */
.ui-badge--a-capo {
    white-space: normal;
    text-align: left;
}

/* ============================================================
 * CARD SELEZIONABILE — .ui-choice-card
 *
 * Accorpa .ui-cat-card e .ui-method-card, che facevano la stessa
 * cosa (una card cliccabile con stato attivo) con due nomi di
 * stato diversi: .active e .is-active. Due nomi per lo stesso
 * stato non si vedono leggendo il CSS, si scoprono a schermo
 * quando la selezione non si accende.
 *
 * Vince .is-active: e' il nome usato dai due soli file che nel BI
 * adoperano davvero una di queste card (lead.php, lead.js).
 *
 * Lo stato attivo NON usa box-shadow, e non e' una preferenza:
 * piu' in basso in questo file c'e'
 *     body *, body *::before, body *::after { box-shadow: none !important; }
 * cioe' l'elevazione e' spenta ovunque, con poche eccezioni riammesse
 * a forza di !important. .ui-method-card.is-active dichiara
 * `box-shadow: 0 0 0 1px var(--jet)` e non l'ha MAI disegnato:
 * misurato a schermo, computed = none. Una regola che c'e' e non fa
 * niente e' peggio di una che manca, perche' chi la legge crede che
 * il caso sia coperto.
 *
 * Quindi: bordo jet piu' fondo platinum. Due grigi, nessuna ombra,
 * niente da contrattare col !important globale. Il fondo seaweed di
 * .ui-cat-card resta fuori: la palette riserva il colore alla
 * semantica finanziaria e agli avvisi, e «questa e' la voce che hai
 * scelto» non e' ne' l'uno ne' l'altro — un fondo verde su una scelta
 * neutra la fa leggere come positiva.
 *
 * L'hover si ferma a silver: se anche lui arrivasse a jet, passare
 * col puntatore sembrerebbe aver gia' selezionato.
 * ============================================================ */

/* Variante per la riga orizzontale scorrevole (era .ui-cat-card):
   stessa card, meno respiro, perche' ne stanno otto in fila. */

/* Il colore di una barra di avanzamento dice il VERSO di cio' che misura, quindi
   ha un nome e non un `background` inline: verde per cio' che sale ed e' un bene
   (incassato), rosso per cio' che sale ed e' un guaio (scaduto). Il valore, che
   e' un dato, entra da `data-progress` e lo applica `initProgressBars()`. */
/* ⚠️ Qualificate dal contenitore, come la regola grigia qui sopra:
   `.ui-progress > .ui-progress-bar` vale 0,2,0 e batterebbe una classe sola,
   spegnendo il colore senza dare nessun errore — e' gia' successo il
   24/07/2026 con il `background` inline. */

/* La testata di una `kpi-mini`: niente stacco, niente filo, titolo attaccato al
   valore. Era uno `style=` che spegneva tre proprieta' del `widget-header`. */

/* Una cella che deve andare a capo dentro una tabella a colonne fisse: la
   descrizione di un movimento e il beneficiario sono testo libero e lungo, e
   senza questo la colonna si allarga a spingere fuori le altre. */

/* Colonne a larghezza dichiarata dai `<col>`: senza questo il browser le
   ridistribuisce sul contenuto e le percentuali non vengono rispettate. */

/* Il puntino di stato di una riga fitta: 6px, la meta' del `ui-dot` normale.
   Piu' piccolo di cosi' non si distingue piu' un colore dall'altro. */

/* --- i campioni della palette (catalogo, sezione Colori) ---
   I dodici colori erano dipinti con un `background:#RRGGBB` inline, ripetuto in
   22 punti: la vetrina della palette poteva divergere DALLA palette senza che
   niente lo segnalasse. Ora ogni campione prende il proprio token. Da Spark. */

/* I due piu' chiari sparirebbero sul bianco: il bordo li rende visibili. */

/* L'etichetta scritta SOPRA il campione: `--jet` e non `--iron`, che su
   seaweed da' 2,64:1. Qui il fondo e' il colore in mostra, quindi il testo
   e' l'unica parte che si puo' scegliere. */

/* Un menu a tendina allineato a DESTRA del suo grillo. Era `style="right:0;
   left:auto"` in sei punti: quando un menu largo nasce vicino al bordo destro
   della finestra, ancorarlo a sinistra lo fa uscire dallo schermo. */

/* --- dimostrazione dei gradini di tracking (catalogo, sezione Tipografia) ---
   Le quattro classi esistono perche' la vetrina mostrava i gradini con uno
   `style=` inline: la palestra e' il posto da cui si COPIA, e cio' che si copia
   li' e' il modo sbagliato di scrivere. Riportate da Spark il 24/08/2026. */

/* Il termine di paragone: il gradino spento. */

/* =============================================================================
   20. SIDEBAR DEL BACKOFFICE (Starmatik)
   -----------------------------------------------------------------------------
   Staff e admin: colonna fissa a sinistra da `lg` (1024px), riducibile a icone;
   cassetto sotto `lg` aperto dalla barra in alto. Il cliente non la vede: tiene
   l'header di `partials/header-area.php`. Struttura e toni dalla sidebar di BI
   (`--sidebar-*`): gruppi con `nav-section-label`, pannello utente col suo menu,
   riduzione (LB-4..LB-6); niente badge né sottomenu. Markup:
   `partials/sidebar-area.php`, comportamento: `assets/js/sidebar.js` e
   `assets/js/sidebar-stato.js`. Catalogo: sezione «Sidebar del backoffice».
   ============================================================================= */

.ui-shell-topbar {
    position: sticky;
    top: 0;
    z-index: 30;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 6px 8px 6px 16px;
    background: var(--white);
    border-bottom: 1px solid var(--border);
}

.ui-shell-topbar-brand {
    min-width: 0;
    display: inline-flex;
    align-items: center;
    gap: 10px;
    font-weight: 700;
    color: var(--text-heading);
    text-decoration: none;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ui-shell-topbar-brand:hover {
    color: var(--text-heading);
}

/* 44px: il minimo di un bersaglio da dito. */
.ui-shell-topbar-toggle {
    width: 44px;
    height: 44px;
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: var(--radius-md);
    background: transparent;
    color: var(--text-heading);
    font-size: 22px;
    cursor: pointer;
    transition: background-color 150ms var(--ease-out);
}

.ui-shell-topbar-toggle:hover {
    background: var(--platinum);
}

.ui-shell-topbar-toggle:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
}

.ui-sidebar {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    z-index: 70;
    width: var(--sidebar-width);
    max-width: 85vw;
    display: flex;
    flex-direction: column;
    overscroll-behavior: contain;
    background: var(--sidebar-bg);
    color: var(--sidebar-text);
    /* Chiusa: fuori campo e non raggiungibile col Tab. `visibility` scatta alla
       fine della corsa, non all'inizio, o l'uscita non si vedrebbe. */
    transform: translateX(-100%);
    visibility: hidden;
    transition: transform 220ms var(--ease-out),
                visibility 0s linear 220ms;
}

/* Stella in filigrana ancorata in basso a destra della pagina, come nel footer del sito Starmatik:
   fondo fisso del contenitore (non un pseudo-elemento con z-index, che vorrebbe un contesto di
   impilamento e chiuderebbe modali e tendine sotto la sidebar). SVG gia' tinto: jet al 5%. Le card
   sono piene, la stella si vede solo negli spazi della pagina. */
.ui-shell-contenuto {
    min-height: 100vh;
    min-height: 100dvh;
    background: url("../img/stella-filigrana.svg") right -70px bottom -70px / 280px 280px no-repeat fixed;
}

@media (min-width: 1024px) {
    .ui-shell-contenuto { background-position: right -100px bottom -100px; background-size: 400px 400px; }
}

.ui-sidebar.is-open {
    transform: translateX(0);
    visibility: visible;
    transition: transform 220ms var(--ease-out),
                visibility 0s linear 0s;
}

/* Contenuto a tutta larghezza (LB-1, BI `main-content`), per tutti i ruoli: niente colonna centrata. */
.ui-shell-main {
    padding: 16px;
}

@media (min-width: 1024px) {
    .ui-sidebar,
    .ui-sidebar.is-open {
        transform: none;
        visibility: visible;
        transition: width var(--sidebar-durata-riduzione) var(--ease-in-out);
    }

    .ui-shell-contenuto {
        padding-left: var(--sidebar-width);
        transition: padding-left var(--sidebar-durata-riduzione) var(--ease-in-out);
    }

    .ui-shell-main {
        padding: 32px 36px;
    }

    /* Qui e non con `lg:hidden` nel markup: app.css si carica dopo tailwind.css, e il
       `display: flex` della barra vincerebbe sull'utility. */
    .ui-shell-topbar {
        display: none;
    }
}

.ui-sidebar-velo {
    position: fixed;
    inset: 0;
    z-index: 60;
    background: var(--sidebar-velo);
    opacity: 1;
    transition: opacity 220ms var(--ease-out),
                display 220ms allow-discrete;
}

.ui-sidebar-velo[hidden] {
    display: none;
    opacity: 0;
}

@starting-style {
    .ui-sidebar-velo:not([hidden]) {
        opacity: 0;
    }
}

.ui-sidebar-brand {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    min-height: 56px;
    padding: 8px 8px 8px 20px;
    border-bottom: 1px solid var(--sidebar-border);
}

.ui-sidebar-brand-testo {
    min-width: 0;
    display: inline-flex;
    align-items: center;
    gap: 12px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 16px;
    font-weight: 700;
    line-height: 1.3;
    color: var(--sidebar-text);
    text-decoration: none;
}

.ui-sidebar-brand-testo:hover {
    color: var(--sidebar-nav-active-text);
}

/* Riquadro del logo (da Spark `sidebar-brand-logo`): il fondo chiaro non è una cornice, è ciò che rende
   visibile la stella rosso scuro sul jet. */
.ui-sidebar-brand-logo {
    width: 30px;
    height: 30px;
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 8px;
    background: var(--white);
}

.ui-sidebar-brand-logo img {
    display: block;
}

.ui-sidebar-brand-nome {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ui-sidebar-chiudi {
    width: 44px;
    height: 44px;
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: var(--radius-md);
    background: transparent;
    color: var(--sidebar-text-muted);
    font-size: 16px;
    cursor: pointer;
    transition: background-color 150ms var(--ease-out), color 150ms var(--ease-out);
}

.ui-sidebar-chiudi:hover {
    background: var(--sidebar-hover-bg);
    color: var(--sidebar-text);
}

@media (min-width: 1024px) {
    .ui-sidebar-chiudi {
        display: none;
    }
}

/* Riduzione a icone: esiste solo da `lg`, sotto il bottone non c'e'. */
.ui-sidebar-riduci {
    display: none;
    width: 36px;
    height: 36px;
    flex-shrink: 0;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: var(--radius-md);
    background: transparent;
    color: var(--sidebar-text-muted);
    font-size: 15px;
    cursor: pointer;
    transition: background-color 150ms var(--ease-out), color 150ms var(--ease-out);
}

.ui-sidebar-riduci:hover {
    background: var(--sidebar-hover-bg);
    color: var(--sidebar-text);
}

@media (min-width: 1024px) {
    .ui-sidebar-riduci {
        display: inline-flex;
    }
}

.ui-sidebar-nav {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    /* Arrivato in fondo alle voci, il trascinamento non passa alla pagina sotto. */
    overscroll-behavior: contain;
    overflow-x: hidden;
    padding: 12px;
    scrollbar-width: thin;
    scrollbar-color: var(--sidebar-scrollbar) transparent;
}

.ui-sidebar-nav ul,
.ui-sidebar-rapidi ul {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.ui-sidebar-gruppo + .ui-sidebar-gruppo {
    margin-top: 8px;
}

/* Guida: ultima voce del menu, staccata dalle sezioni con una linea come il pannello utente. */
.ui-sidebar-gruppo.ui-sidebar-gruppo--guida {
    margin-top: 12px;
    padding-top: 12px;
    border-top: 1px solid var(--sidebar-border);
}

.ui-sidebar-voce {
    display: flex;
    white-space: nowrap;
    overflow: hidden;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    border-radius: var(--sidebar-active-radius);
    font-size: 14px;
    font-weight: 500;
    color: var(--sidebar-text);
    text-decoration: none;
    transition: background-color 150ms var(--ease-out), color 150ms var(--ease-out);
}

.ui-sidebar-voce:hover {
    background: var(--sidebar-hover-bg);
    color: var(--sidebar-nav-active-text);
}

.ui-sidebar-voce.is-active {
    position: relative;
    background: var(--sidebar-nav-active-bg);
    color: var(--sidebar-nav-active-text);
    font-weight: 600;
}

/* Unico punto di marca nella UI: barretta a sinistra della voce attiva. Il rosso pieno sul velo
   dà 1,85:1 ed è voluto (decisione del 23/09/2026): è un accento, lo stato attivo lo dicono
   già fondo e grassetto. */
.ui-sidebar-voce.is-active::before {
    content: '';
    position: absolute;
    left: 0;
    top: 6px;
    bottom: 6px;
    width: 3px;
    border-radius: 0 3px 3px 0;
    background: var(--marca);
}

.ui-sidebar-icona {
    width: 22px;
    flex-shrink: 0;
    text-align: center;
    font-size: 16px;
    line-height: 1;
}

.ui-sidebar-voce-testo {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Albero delle commesse (catalogo «Sidebar», spec viste per ruolo): il nodo è una voce con la
   freccia a destra, i figli rientrano sotto la cartella lungo una linea tratteggiata (BI). */
.ui-sidebar-albero-nodo {
    width: 100%;
    border: 0;
    background: transparent;
    font-family: inherit;
    line-height: inherit;
    text-align: left;
    cursor: pointer;
}

.ui-sidebar-albero-nodo[aria-expanded="true"] {
    background: var(--sidebar-hover-bg);
    color: var(--sidebar-nav-active-text);
}

.ui-sidebar-albero-freccia {
    margin-left: auto;
    font-size: 11px;
    transition: transform 150ms var(--ease-in-out);
}

.ui-sidebar-albero-nodo[aria-expanded="true"] .ui-sidebar-albero-freccia {
    transform: rotate(180deg);
}

.ui-sidebar-nav .ui-sidebar-albero-figli {
    margin: 2px 0 4px 22px;
    padding-left: 10px;
    border-left: 1px dashed var(--sidebar-tree-line);
}

.ui-sidebar-albero-descrizione {
    display: block;
    margin-top: 2px;
    font-weight: 400;
    letter-spacing: 0;
    text-transform: none;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* `.ui-sidebar-nav ul` è flex: senza questa riga l'attributo `hidden` non chiuderebbe niente. */
.ui-sidebar-nav .ui-sidebar-albero-figli[hidden],
.ui-sidebar-albero-titolo {
    display: none;
}

.ui-sidebar-albero-figlio {
    padding-top: 8px;
    padding-bottom: 8px;
    font-size: 13px;
}

/* Sottomenu delle categorie sotto «Documentazione» (vista cliente, 24/09/2026): testo allineato a
   quello della voce madre, senza icona, più piccolo e attenuato finché non è attivo. */
.ui-sidebar-nav .ui-sidebar-categorie {
    margin: 2px 0 4px 32px;
}

.ui-sidebar-categoria {
    padding-top: 6px;
    padding-bottom: 6px;
    font-size: 12.5px;
    font-weight: 400;
    color: var(--sidebar-text-muted);
}

/* Link rapidi dello staff (Recenti) in fondo, sopra il pannello utente: un fondo più chiaro li
   separa dalle sezioni, come i collegamenti personali. Link diretti, niente albero; oltre `40vh` scorrono. */
.ui-sidebar-rapidi {
    flex-shrink: 0;
    max-height: 40vh;
    overflow-y: auto;
    overscroll-behavior: contain;
    overflow-x: hidden;
    padding: 8px 12px;
    border-top: 1px solid var(--sidebar-border);
    background: var(--sidebar-rapidi-bg);
    scrollbar-width: thin;
    scrollbar-color: var(--sidebar-scrollbar) transparent;
}

/* Pannello utente (LB-5, BI `sidebar-user-panel`): icona, nome, email, tre puntini;
   il menu si apre sopra il bottone. */
.ui-sidebar-utente-pannello {
    position: relative;
    padding: 16px 12px;
    border-top: 1px solid var(--sidebar-border);
}

.ui-sidebar-utente-btn {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 8px 12px;
    border: 0;
    border-radius: var(--sidebar-active-radius);
    background: transparent;
    color: var(--sidebar-text);
    text-align: left;
    white-space: nowrap;
    overflow: hidden;
    cursor: pointer;
    transition: background-color 150ms var(--ease-out);
}

/* Come BI: fondo solo al passaggio del mouse. La pagina aperta (Profilo) si vede nella voce del menu. */
.ui-sidebar-utente-btn:hover {
    background: var(--sidebar-hover-bg);
}

.ui-sidebar-utente-info {
    display: flex;
    flex: 1;
    flex-direction: column;
    min-width: 0;
}

.ui-sidebar-utente-nome,
.ui-sidebar-utente-email {
    overflow: hidden;
    text-overflow: ellipsis;
}

.ui-sidebar-utente-nome {
    font-size: 13px;
    font-weight: 600;
    color: var(--sidebar-text);
}

.ui-sidebar-utente-email {
    font-size: 12px;
    color: var(--sidebar-text-muted);
}

.ui-sidebar-utente-altro {
    flex-shrink: 0;
    font-size: 14px;
    color: var(--sidebar-text-muted);
}

/* Il menu e' chiaro sul fondo jet, come in BI (`sidebar-user-dropdown`): dentro,
   bottoni e colori sono quelli normali della pagina. Nessuna ombra (la regola
   globale le toglie): lo stacca il bordo. */
.ui-sidebar-menu-utente {
    position: absolute;
    left: 12px;
    right: 12px;
    bottom: calc(100% + 8px);
    z-index: 10;
    padding: 4px 0;
    background: var(--white);
    border: 1px solid var(--alabaster);
    border-radius: var(--radius-md);
    color: var(--text-body);
    opacity: 1;
    transform: translateY(0);
    transition: opacity 150ms var(--ease-out),
                transform 150ms var(--ease-out),
                display 150ms allow-discrete;
}

.ui-sidebar-menu-utente[hidden] {
    display: none;
    opacity: 0;
    transform: translateY(4px);
}

@starting-style {
    .ui-sidebar-menu-utente:not([hidden]) {
        opacity: 0;
        transform: translateY(4px);
    }
}

/* Voci come quelle di BI (`px-4 py-2.5 text-[13px]`, icona grigia). */
.ui-sidebar-menu-utente .ui-dropdown-item {
    gap: 12px;
    padding: 10px 16px;
    font-size: 13px;
}

.ui-sidebar-menu-utente .ui-dropdown-item:not(.ui-dropdown-item--danger) > .bi {
    color: var(--text-muted);
}

/* Pagina aperta dal menu utente (Profilo): come le voci del menu utente di BI. */
.ui-sidebar-menu-utente .ui-dropdown-item.is-active {
    background: var(--platinum);
    color: var(--text-heading);
    font-weight: 600;
}

/* Lingua come voce del menu: stessa colonna d'icona e stessi margini delle voci, tendina a tutta riga. */
.ui-sidebar-menu-utente-lingua {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 4px 16px 6px;
}

.ui-sidebar-menu-utente-lingua > .bi {
    flex-shrink: 0;
    color: var(--text-muted);
    text-align: center;
}

.ui-sidebar-menu-utente-lingua .ui-lingua {
    flex: 1;
    min-width: 0;
}

.ui-sidebar-menu-utente-lingua .ui-select {
    flex: 1;
    min-width: 0;
}

.ui-sidebar-menu-utente-divisore {
    margin: 4px 0;
    border-top: 1px solid var(--alabaster);
}

/* Selettore lingua a tendina (partials/selettore-lingua.php): compatto nell'header del cliente e nelle
   pagine pubbliche, a tutta larghezza nel menu utente della sidebar. */
.ui-lingua {
    display: flex;
    align-items: center;
    gap: 6px;
}

.ui-lingua .ui-select {
    height: 32px;
    font-size: 13px;
}

/* L'anello di fuoco globale e' jet: sul fondo della sidebar sparirebbe. Nel menu
   utente, che e' chiaro, torna jet. */
.ui-sidebar :focus-visible {
    outline: 2px solid var(--sidebar-nav-active-text);
    outline-offset: 2px;
}

.ui-sidebar .ui-sidebar-menu-utente :focus-visible {
    outline-color: var(--focus-ring);
}

/* Sidebar ridotta a icone (classe su <html> da sidebar-stato.js), solo da `lg`.
   Le etichette dei gruppi diventano separatori; il nome delle voci passa in
   `title`/`aria-label` (sidebar.js); nel pannello utente nome ed email restano
   per i lettori di schermo ma escono dallo schermo. */
@media (min-width: 1024px) {
    .ui-sidebar-ridotta .ui-sidebar {
        width: var(--sidebar-collapsed);
    }

    .ui-sidebar-ridotta .ui-shell-contenuto {
        padding-left: var(--sidebar-collapsed);
    }

    .ui-sidebar-ridotta .ui-sidebar-brand {
        flex-direction: column;
        justify-content: center;
        gap: 4px;
        padding: 8px;
    }

    .ui-sidebar-ridotta .ui-sidebar-brand-testo {
        justify-content: center;
        gap: 0;
    }

    .ui-sidebar-ridotta .ui-sidebar-brand-nome {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
    }

    .ui-sidebar-ridotta .ui-sidebar-voce-testo,
    .ui-sidebar-ridotta .ui-sidebar-utente-altro {
        display: none;
    }

    .ui-sidebar-ridotta .ui-sidebar-gruppo-etichetta {
        height: 1px;
        margin: 8px 6px;
        padding: 0;
        overflow: hidden;
        font-size: 0;
        background: var(--sidebar-border);
    }

    .ui-sidebar-ridotta .ui-sidebar-gruppo:first-child .ui-sidebar-gruppo-etichetta {
        height: 0;
        margin: 0;
    }

    .ui-sidebar-ridotta .ui-sidebar-gruppo + .ui-sidebar-gruppo {
        margin-top: 0;
    }

    .ui-sidebar-ridotta .ui-sidebar-voce,
    .ui-sidebar-ridotta .ui-sidebar-utente-btn {
        justify-content: center;
        gap: 0;
        padding-left: 0;
        padding-right: 0;
    }

    .ui-sidebar-ridotta .ui-sidebar-utente-info {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
    }

    /* Il menu non entra in 72px: esce a destra della sidebar, larghezza piena. */
    .ui-sidebar-ridotta .ui-sidebar-menu-utente {
        right: auto;
        width: calc(var(--sidebar-width) - 24px);
    }

    /* Albero da ridotta: resta la cartella; il clic apre i figli in un pannello a destra della
       sidebar (sidebar.js mette `is-pannello` e la quota in `--albero-top`). Fixed perché la lista
       delle voci scorre e taglierebbe un pannello assoluto. */
    .ui-sidebar-ridotta .ui-sidebar-albero-freccia,
    .ui-sidebar-ridotta .ui-sidebar-nav .ui-sidebar-albero-figli {
        display: none;
    }

    .ui-sidebar-ridotta .ui-sidebar-albero.is-pannello .ui-sidebar-albero-figli {
        display: flex;
        position: fixed;
        z-index: 75;
        top: var(--albero-top, 0);
        left: calc(var(--sidebar-collapsed) + 4px);
        width: 220px;
        margin: 0;
        padding: 6px;
        border: 1px solid var(--sidebar-border-hover);
        border-radius: var(--radius-md);
        background: var(--sidebar-bg);
    }

    .ui-sidebar-ridotta .ui-sidebar-albero.is-pannello .ui-sidebar-albero-titolo {
        display: block;
        padding: 6px 12px 4px;
        font-size: 11px;
        font-weight: 600;
        letter-spacing: 0.05em;
        color: var(--sidebar-text-muted);
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .ui-sidebar-ridotta .ui-sidebar-albero.is-pannello .ui-sidebar-categoria,
    .ui-sidebar-ridotta .ui-sidebar-albero.is-pannello .ui-sidebar-albero-figlio {
        justify-content: flex-start;
        gap: 10px;
        padding: 8px 12px;
    }

    .ui-sidebar-ridotta .ui-sidebar-albero.is-pannello .ui-sidebar-albero-figli .ui-sidebar-voce-testo {
        display: inline;
    }
}

/* Blocco dello scorrimento mentre il cassetto e' aperto (sidebar.js): su <html> e su
   <body>, perche' Safari iOS scorre il documento anche col solo body bloccato. */
.ui-scroll-bloccato {
    overflow: hidden;
}

/* Movimento ridotto: la regola globale («meno, non zero») fa scattare cassetto, riduzione e freccia
   (transform e larghezze) e lascia transitare colori e velo. */

/* Bottone d'azione a icona delle tabelle e dei blocchi (catalogo: «Bottone a icona»): 28x28, grigio,
   sfondo al passaggio. Prima era una stringa di utility copiata in sei file JS, con la curva di Tailwind.
   `disabled` quando basta spegnerlo; `aria-disabled` + `title` quando il motivo va detto (resta nel Tab). */
.ui-btn-icona {
    width: 28px;
    height: 28px;
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: var(--interactive-radius);
    background: transparent;
    color: var(--text-light);
    text-decoration: none;
    cursor: pointer;
    transition: color 140ms var(--ease-out), background-color 140ms var(--ease-out);
}

@media (hover: hover) {
    .ui-btn-icona:hover {
        color: var(--iron);
        background: color-mix(in srgb, var(--iron) 10%, transparent);
    }

    .ui-btn-icona--elimina:hover {
        color: var(--cherry);
        background: color-mix(in srgb, var(--cherry) 10%, transparent);
    }

    .ui-btn-icona[aria-disabled="true"]:hover {
        color: var(--text-light);
        background: transparent;
    }
}

.ui-btn-icona:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 1px;
}

.ui-btn-icona:disabled {
    opacity: 0.4;
    pointer-events: none;
}

.ui-btn-icona[aria-disabled="true"] {
    opacity: 0.4;
    cursor: not-allowed;
}

/* =============================================================================
   21. PAGINE DI ACCESSO (Starmatik, 23/09/2026)
   Accesso, password dimenticata, imposta password. Da 1024 px a tutto schermo, divisa come il
   riferimento «Nucleus» scelto dall'operatore: a sinistra, in un terzo, la foto del sito in verticale
   con velatura, marchio in negativo e crocini, a destra il modulo su bianco. Sotto, solo il modulo
   su bianco con il marchio scuro sopra. Partial accesso-sfondo.
   ============================================================================= */
.ui-accesso {
    display: flex;
    align-items: center;
    justify-content: center;
    /* dvh, non vh: sul telefono 100vh è l'altezza a barre del browser ritratte e la pagina scorre. */
    min-height: 100vh;
    min-height: 100dvh;
    padding: 40px 24px;
    background: var(--white);
}

.ui-accesso-scheda { width: 100%; max-width: 440px; }

.ui-accesso-sfondo { display: none; }

.ui-accesso-marchio-scuro {
    display: block;
    width: 180px;
    height: auto;
    margin-bottom: 40px;
}

.ui-accesso-box { width: 100%; }

/* Respiro del modulo, come il riferimento: campi e bottone da 46 px invece di 38. */
.ui-accesso-modulo .ui-input,
.ui-accesso-modulo .ui-select,
.ui-accesso-modulo .ui-btn-primary { min-height: 46px; }

@media (min-width: 1024px) {
    .ui-accesso { padding: 0; }

    .ui-accesso-scheda {
        display: grid;
        /* Foto a un terzo, come il riferimento (442 px su 1330): a metà era troppo. */
        grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
        max-width: none;
        min-height: 100vh;
        min-height: 100dvh;
    }

    .ui-accesso-marchio-scuro { display: none; }

    .ui-accesso-sfondo {
        position: relative;
        display: block;
        overflow: hidden;
        background: url("../img/accesso.webp") 68% center / cover no-repeat var(--jet);
    }

    /* Velatura della home del sito (#front_page .bg::after): grigio in moltiplica al 55%. */
    .ui-accesso-sfondo::after {
        content: '';
        position: absolute;
        inset: 0;
        opacity: 0.55;
        mix-blend-mode: multiply;
        background-color: var(--iron);
        pointer-events: none;
    }

    .ui-accesso-marchio {
        position: absolute;
        top: 36px;
        left: 32px;
        z-index: 2;
        width: 190px;
        height: auto;
    }

    .ui-accesso-box {
        align-self: center;
        justify-self: center;
        max-width: 440px;
        padding: 48px 32px;
    }

    /* Crocini con le regole del sito (tema, main.css:646 .grid--points): righe a space-between,
       croce 10x10 a tratti di 2 px, bianco al 50%; nel riquadro 3 x 3 invece di 3 x 5. Quelli
       che cadono sul marchio non si disegnano (ui.js, initPuntiAccesso). */
    .ui-accesso-punti {
        position: absolute;
        inset: 0;
        z-index: 1;
        display: flex;
        flex-direction: column;
        justify-content: space-between;
        padding: 40px 15px;
        pointer-events: none;
    }

    .ui-accesso-punti-riga {
        display: flex;
        justify-content: space-between;
    }

    .ui-accesso-punti-riga > i {
        position: relative;
        display: block;
        width: 10px;
        height: 10px;
    }

    .ui-accesso-punti-riga > i::before,
    .ui-accesso-punti-riga > i::after {
        content: '';
        position: absolute;
        top: 4px;
        left: 0;
        width: 10px;
        height: 2px;
        background: rgba(255, 255, 255, 0.5);
    }

    .ui-accesso-punti-riga > i::after { transform: rotate(90deg); }
}

/* Un figlio `absolute` (es. `.sr-only`) senza antenato posizionato esce dal wrapper che scorre e
   allarga la pagina su telefono: il wrapper delle tabelle fa da riferimento. In un layer, cosi'
   un'utility di posizione (`sticky`, `absolute`) sullo stesso elemento vince. */
@layer components {
    .overflow-x-auto { position: relative; }
}
