/*
 * Componenti del design system di Spark.
 *
 * `docs/design.md` dichiara che questo progetto adotta 1:1 il design system di
 * Spark (`/Users/nicola/Documents/vhosts/spark`). Fino al 2026-08-24 l'adozione
 * era parziale: c'erano palette, tipografia, badge, bottoni, widget e modali,
 * ma le ~250 classi restanti — tabelle, tab, paginatore, avvisi, stati vuoti,
 * menu a tendina, campi di form, timeline, stepper, scheletri, metriche — no.
 * Il catalogo le descriveva a parole senza poterle mostrare.
 *
 * Questo file le porta. Le regole sono copiate da `assets/css/app.css` di Spark
 * SENZA riscriverle: una copia riscritta diverge alla prima modifica di la',
 * e nessuno se ne accorge finche' un componente non si vede storto.
 *
 * REGOLA DI SINCRONIZZAZIONE. Qui dentro non si scrive a mano. Se un componente
 * va cambiato, si cambia in Spark e si riporta; se serve un componente che Spark
 * non ha, sta in `styles.css` — che e' anche il posto delle parti proprie di
 * Trading Portfolio (grafici a candele, marcatori di trading, semantica
 * positivo/negativo sui dati di mercato).
 *
 * COSA NON C'E'. Le regole che qui erano gia' state portate — `.widget-*`,
 * `.kpi-*`, `.ui-badge*`, `.ui-btn-*`, `.ui-modal-card*`, `.page-*`, la sidebar,
 * le tabelle ordinabili — restano in `styles.css` e non sono duplicate: due
 * copie della stessa regola in due file si contraddicono appena una invecchia,
 * e vince quella caricata dopo, che non e' una scelta ma un caso.
 *
 * COSA E' STATO LASCIATO INDIETRO, e perche'. La sidebar e il menu a scomparsa
 * (`sidebar-brand*`, `sidebar-user*`, `nav-flyout*`): qui la navigazione e'
 * propria e gia' vestita in `styles.css`, e due regole per lo stesso nodo si
 * contraddicono. `nav-section-label` e' l'eccezione che conferma la regola: la
 * struttura di Spark e' stata adottata il 2026-08-25, ma i suoi valori stanno
 * in `styles.css` accanto al resto della navigazione, non qui. Il banner di Spark (`bi-banner-riga*`): il canale
 * unico degli avvisi qui e' `mostraAvviso()`. Del datepicker sono arrivati il
 * tema di `flatpickr` e i due campi (`ui-datepicker`, `ui-daterange-picker`) il
 * 2026-08-25, insieme alla libreria; NON sono arrivati la barra dei preset
 * (`ui-date-range`, `ui-date-range-presets`, `ui-date-preset`) ne' la macchina
 * che li accende, perche' qui non c'e' ancora un filtro per intervallo che li
 * chiami, e stile senza markup e' peso morto che sembra un componente
 * disponibile.
 *
 * Caricato DOPO `styles.css` (vedi `views/layouts/head-comune.php`).
 */

:root {
    /* Le variabili che le regole qui sotto citano e che `styles.css` non
       dichiara. Valori presi da Spark, non riscritti. */

    /* Palette, con i nomi di Spark. `styles.css` porta gli stessi dodici colori
       sotto altri nomi (--primary, --bg-card, ...): questi non li sostituiscono,
       li affiancano perche' le regole copiate li citano per nome. */
    --jet: #1C2826;
    --iron: #4C5655;
    --stone: #686F6E;
    --snow: #FAFBFC;
    --platinum: #F3F4F5;
    --alabaster: #DBE0E1;
    --cherry: #C21B3F;
    --seaweed: #1AAD7E;
    --princeton: #FB850F;
    --white: #ffffff;

    /* Ruoli */
    --text-heading: var(--jet);
    --primary-light: rgba(28, 40, 38, 0.06);
    /* In Spark `--primary` e `--primary-dark` sono lo stesso Jet: il secondo
       esiste per i punti in cui una tinta piena deve stare su un fondo gia'
       chiaro-primario. Lo cita il tema di flatpickr. */
    --primary-dark: var(--jet);
    --accent-dark: var(--princeton-dark);
    --accent-hover: var(--princeton);
    --bg-subtle: var(--platinum);
    --bg-muted: var(--platinum);

    /* Le tre tinte con cui un componente dichiara un esito. Sono piu' scure
       delle omonime della palette: su fondo chiaro --seaweed non arriva a 4,5:1,
       --dark-emerald si'. */
    --ui-success: var(--dark-emerald);
    --ui-danger: var(--cherry);
    --ui-warning: var(--princeton-dark);

    /* Scala di spaziatura */
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 20px;
    --space-5: 28px;
    --space-6: 36px;
    --transition: 200ms var(--ease-out);

    /* Tabelle */
    --table-bg: var(--white);
    --table-row-alt: var(--platinum);
    --table-row-hover: rgba(28, 40, 38, 0.04);
    /* 12px e non i 14 di Spark: qui le tabelle sono a 12 e `.sortable-table th`
       lo impone comunque alle celle, con specificita' maggiore. Lasciare 14
       avrebbe dichiarato una misura che nessuna cella rende — un valore che
       mente e' peggio di un valore diverso. */
    --table-font-size: 12px;
    --table-link-color: var(--jet);
    --table-link-hover: var(--iron);
    --table-focus-border: var(--iron);
    --table-action-hover-bg: rgba(28, 40, 38, 0.04);
    --table-action-hover-text: var(--jet);
    --table-page-active-bg: var(--iron);
    --table-page-active-text: var(--white);

    /* Card degli insight AI */
    --ai-card-bg-start: rgba(28, 40, 38, 0.04);
    --ai-card-bg-end: rgba(28, 40, 38, 0.02);
    --ai-card-border: rgba(28, 40, 38, 0.15);
    --ai-card-hover-border: rgba(28, 40, 38, 0.25);
    --ai-card-accent-start: rgba(28, 40, 38, 0.06);
    --ai-card-accent-end: rgba(28, 40, 38, 0.03);
    --ai-card-title-accent: var(--jet);
    --ai-card-chip-bg: rgba(28, 40, 38, 0.08);
    --ai-card-chip-text: var(--jet);

    /* Menu interno a una vista (non la sidebar) */
    --internal-menu-bg: var(--platinum);
    --internal-menu-border: var(--border);
    --internal-menu-item-text: var(--stone);
    --internal-menu-item-hover-bg: rgba(28, 40, 38, 0.04);
    --internal-menu-item-hover-text: var(--jet);
    --internal-menu-active-bg: var(--jet);
    --internal-menu-active-text: var(--white);
    --internal-menu-focus-ring: var(--focus-ring);
    --internal-menu-focus-ring-on-active: var(--platinum);

    /* Larghezze della scheda a campi (`.ui-campo`) */
    --colonna-dato: 320px;
    --etichetta-dato: 168px;
}

/* ============================================================================
   ANIMAZIONI
   ============================================================================ */

@keyframes ui-fade-up {
    from { opacity: 0; transform: translateY(8px); }
    to { opacity: 1; transform: translateY(0); }
}

@keyframes ui-spin {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}

@keyframes ui-progress-slide {
    0% { transform: translateX(-100%); }
    100% { transform: translateX(400%); }
}

@keyframes ui-fade-in {
    from { opacity: 0; }
    to { opacity: 1; }
}

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

@keyframes uiSparkle {
    0%, 100% { transform: scale(1); opacity: 0.7; }
    50% { transform: scale(1.15); opacity: 1; }
}

/* ============================================================================
   COMPONENTI
   ============================================================================ */

/* 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 */
.truncate {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Cella di tabella con un testo lungo: due righe, poi ellissi. Serve dove una
   colonna testuale allargherebbe la tabella oltre il suo contenitore — con
   `table-layout: auto` la colonna cresce fino al contenuto e nessuna larghezza
   dichiarata sul `td` la ferma.
   ⚠️ La clamp sta sullo SPAN, non sul `td`: `display: -webkit-box` su una cella
   le toglie il `display: table-cell` e la tabella si scompone. Il `max-width`
   sullo span basta a limitare la colonna, perche' la larghezza di colonna la
   decide il contenuto piu' largo. */
/* ── Selezione a spunta dentro una lista ─────────────────────────────────
   La colonna della spunta NON e' una colonna del registro: non ha etichetta,
   non si ordina e non finisce nel CSV. Si dichiara nel markup con
   `data-colonna-controllo`, e `verifica-elenchi.php` la salta confrontando
   `<thead>` e registro — un'eccezione dichiarata, non dedotta.
   La larghezza e' fissa perche' una colonna di sole caselle non deve
   contendere spazio ai dati: 36 px sono la casella (16) piu' il respiro. */
.ui-col-check {
    width: 36px;
    padding-right: 0;
}

.ui-col-check input[type="checkbox"] {
    display: block;
    margin: 0;
    cursor: pointer;
    accent-color: var(--jet);
}

/* 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. */
.ui-selezione-barra {
    display: flex;
    align-items: center;
    gap: 12px 20px;
    flex-wrap: wrap;
    padding: 12px 16px;
    border-radius: var(--radius-md);
    background: var(--bg-muted);
    border: 1px solid var(--border);
    margin-bottom: 16px;
}

.ui-selezione-conta {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-heading);
}

.ui-selezione-azioni {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-left: auto;
}

/* Il filetto che stacca l'uscita dalle azioni. Senza, «annulla» sta in fila
   con «arricchisci» e si legge come una terza cosa da fare. */
.ui-selezione-divisore {
    width: 1px;
    height: 20px;
    background: var(--border);
    margin: 0 4px;
    flex-shrink: 0;
}

.ui-cell-clamp {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    max-width: 260px;
}

/* Il RITMO VERTICALE dentro una card.

   Due `.widget-body` consecutivi sono due blocchi diversi — una banda di
   metriche, due grafici, una tabella — e fino al 21/08/2026 stavano attaccati:
   misurate **nove** coppie consecutive in tutta l'applicazione, tutte a
   `margin-top: 0` e `padding-top: 0`. Sul Rapporto commerciale i grafici
   toccavano l'intestazione della tabella, e le etichette dell'asse X si
   leggevano come una riga di quella.

   36 px (`--space-6`) perché dev'essere nettamente più del respiro INTERNO di
   un blocco, che vale 8 (didascalia → grafico): la gerarchia si legge dal
   rapporto fra le due distanze, non dal valore assoluto.

   ⚠️ `:not(.hidden)` sul PRIMO dei due: un corpo nascosto non occupa spazio, ma
   il combinatore `+` lo vede lo stesso, e il blocco dopo prenderebbe 36 px da
   un fratello che a schermo non c'è. Sulla scheda anagrafica quel fratello
   esiste (`#vuotoRapporto`), quindi non è un caso teorico.
   ⚠️ Il paginatore è escluso: non è un blocco, è la coda della sua tabella, e
   `.ui-paginator` porta già un filo e 12 px di suo. */
.widget-card > .widget-body:not(.hidden) + .widget-body:not(.hidden):not(.ui-paginator-mount):not(.ui-coda-blocco) {
    margin-top: var(--space-6);
}

/* Il contenitore in cui `BiUI.paginate` monta la barra. Era `widget-body pt-0`
   scritto a mano in nove viste: un nome invece di un'utility, così il ritmo fra
   blocchi sa di doverlo saltare. Non prende nemmeno i 12 px della coda:
   `.ui-paginator` porta già un filo e 12 px di padding suo. */
.ui-paginator-mount {
    padding-top: 0;
}

/* La CODA di un blocco: il comando che agisce su ciò che sta sopra — il modulo
   per aggiungere una riga alla lista che precede. Non è un blocco a sé, quindi
   non prende i 36 px del ritmo; ma non è nemmeno attaccato, perché è un'altra
   cosa dalla lista. ⚠️ Senza, il modulo «Aggiungi figura chiave» stava 36 px
   sotto la tabella e in mezzo restava una fascia vuota che lo staccava da ciò
   su cui agisce: a schermo si leggeva come un comando della card, non della
   lista. `.ui-paginator-mount` è il caso particolare a zero. */
.ui-coda-blocco {
    margin-top: var(--space-3);
    padding-top: 0;
}

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

/* Text list block (component: text-blocks -> text-list) */
.ui-text-list {
    font-size: 12px;
    line-height: 1.4;
}

/* KPI Widget with highlight - Corporate subtle */
.widget-card.kpi-widget.highlight {
    box-shadow: none;
    border-color: var(--alabaster);
}

/* Section Title - Titoli sezione pagina */
.section-title {
    font-size: 11px !important;
    font-weight: 600 !important;
    text-transform: uppercase !important;
    letter-spacing: 0.08em !important;
    color: var(--iron) !important;
    padding-bottom: 0 !important;
    margin-bottom: 16px !important;
    margin-top: 40px !important;
    border-bottom: none !important;
}

.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].sticky {
    position: sticky;
}

/* 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). */
.ui-table-sticky-head thead th,
.ui-table-sticky-head.sortable-table thead th[data-sort] {
    position: sticky;
    top: 0;
    z-index: 10;
    background: var(--platinum);
}

table tbody tr:nth-child(odd) td.sticky {
    background-color: var(--table-bg);
}

table tbody tr:nth-child(even) td.sticky {
    background-color: var(--table-row-alt);
}

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

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

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

.ui-table-control:focus {
    outline: none;
    border-color: var(--table-focus-border);
}

.ui-table-action-edit:hover {
    color: var(--table-action-hover-text);
    background-color: var(--table-action-hover-bg);
}

.ui-table-page-active {
    background: var(--table-page-active-bg);
    color: var(--table-page-active-text);
}

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

/* Chips - Interactive pills */
.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-md);
    background: var(--bg-muted);
    color: var(--text-body);
    cursor: pointer;
    transition: background-color var(--transition);
    white-space: nowrap;
    flex-shrink: 0;
    text-decoration: none;
}

.chip:hover {
    background: var(--border);
    text-decoration: none;
}

.ui-pill {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 3px 10px;
    min-width: 24px;
    font-size: 11px;
    font-weight: 500;
    border-radius: var(--radius-md);
    background: rgba(28, 40, 38, 0.08);
    color: var(--jet);
    white-space: nowrap;
    flex-shrink: 0;
}

.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;
    white-space: nowrap;          /* stessa ragione di .ui-badge */
    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;
}

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

.ui-progress {
    height: 6px;
    background: var(--bg-muted);
    border-radius: 9999px;
    overflow: hidden;
}

/* 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. */
.ui-progress > .ui-progress-bar {
    height: 100%;
    background: var(--accent);
    border-radius: 9999px;
}

/* =============================================================================
   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(28, 40, 38, 0.05);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    margin-bottom: 0;
}

.ui-tab:hover:not(:disabled) {
    color: var(--jet);
    background: rgba(28, 40, 38, 0.08);
}

.ui-tab.active {
    color: var(--white);
    background: var(--jet);
    border-color: var(--iron);
}

.ui-tab:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

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

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

/* Il pannello che entra si dissolve, quello che esce no: animare l'uscita
   vorrebbe dire tenere a schermo per 120 ms un contenuto che l'utente ha gia'
   sostituito, cioe' far sembrare lento un comando che si preme spesso.
   120 ms e' al limite del percettibile, ed e' voluto: si cambia scheda decine
   di volte al giorno, non due. */
.ui-tab-panel.active {
    display: block;
    animation: ui-fade-in 120ms var(--ease-out);
}

/* 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;
}

.widget-body .ui-tabs .ui-tab.active {
    background: var(--jet);
    color: var(--white);
    border-color: var(--iron);
}

.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);
}

/* L'anello di focus e' un outline e non una box-shadow: la regola globale
   in fondo al file (`body *`) spegne ogni box-shadow con !important, quindi
   l'anello dichiarato come ombra non veniva mai disegnato e il focus da
   tastiera era invisibile. outline quella regola non lo tocca.
   L'offset e' NEGATIVO perche' la variante --compact --inline ha gap 2px e
   2px di margine interno: un anello esterno da 4px sforerebbe il contenitore
   e coprirebbe l'item vicino. Dentro l'elemento la geometria non dipende
   dal gap, quindi vale per tutte e tre le varianti. */
.ui-internal-menu-item:focus-visible {
    outline: 2px solid var(--internal-menu-focus-ring);
    outline-offset: -2px;
}

/* L'item attivo ha il fondo dello stesso colore dell'anello: senza invertire
   il colore l'anello ci sparisce dentro. */
.ui-internal-menu-item.active:focus-visible {
    outline-color: var(--internal-menu-focus-ring-on-active);
}

.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(28, 40, 38, 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;
}

.ui-internal-menu-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 16px;
    padding: 0 5px;
    margin-left: 6px;
    border-radius: 999px;
    font-size: 10px;
    font-weight: 700;
    line-height: 1;
    background: var(--primary);
    color: #fff;
    border: 1px solid rgba(255, 255, 255, 0.15);
}

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

/* =============================================================================
   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; }

.ui-chart-lg { height: 350px; }

.ui-chart-xl { height: 450px; }

/* 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. */
@media (min-width: 1024px) {
    .ui-widget-capped {
        max-height: 450px;
        display: flex;
        flex-direction: column;
    }
    .ui-widget-capped .ui-chart-container {
        flex: 1 1 0%;
        height: auto;
        min-height: 150px;
    }
    .ui-widget-scrollable {
        flex: 1 1 0%;
        overflow-y: auto;
        min-height: 0;
        padding-right: 8px;
        margin-right: -4px;
        scrollbar-width: thin;
        scrollbar-color: var(--border) transparent;
    }
    .ui-widget-scrollable::-webkit-scrollbar {
        width: 4px;
    }
    .ui-widget-scrollable::-webkit-scrollbar-track {
        background: transparent;
    }
    .ui-widget-scrollable::-webkit-scrollbar-thumb {
        background: var(--border);
        border-radius: 2px;
    }
}

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

/* Text Colors */
.text-accent { color: var(--accent); }

/* ⚠️ Le utility di testo semantico portano la variante SCURA: seaweed nudo su
   fondo chiaro da' 2,59:1 e cherry regge (5,96). Il verde chiaro resta un
   colore da fondo e da tratto di grafico, non da testo. */
/* ⚠️ `.text-ui-success`, `.text-ui-warning` e `.text-ui-danger` NON stanno qui:
   le genera Tailwind dagli alias `ui.*` del config in `views/partials/header.php`,
   e quel foglio viene iniettato DOPO questo — le copie scritte qui rendevano un
   colore e a schermo se ne misurava un altro. Il valore si cambia la', in un
   posto solo. */

/* Font Sizes — ⚠️ questa scala RIDEFINISCE le utility native di Tailwind, che
   arriva dal CDN e viene iniettato prima di questo foglio: `text-sm` qui vale
   12px contro i 14 di Tailwind, `text-xl` 18 contro 20, `text-2xl` 20 contro
   24. Chi copia una misura da una guida di Tailwind ne ottiene un'altra, e non
   ha modo di accorgersene. Le due voci INERTI sono uscite il 24/08/2026 —
   `.text-base` (14px contro 16) e `.text-3xl`: nessun markup le scriveva,
   quindi erano una trappola armata e non una convenzione. Le altre restano
   perche' sono la scala in uso; la loro divergenza e' dichiarata qui e in
   `DESIGN_SYSTEM.md § Tipografia`. */
.text-2xs { font-size: 10px; }

/* headings large */

/* Fixed dimensions */
.h-card-sm { height: 52px; }

/* Font Weights */
.font-normal { font-weight: 400; }

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

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

.font-bold { font-weight: 700; }

/* 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); }

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

/* Metrics */
.ui-metrics {
    display: grid;
    gap: 10px;
}

.ui-metric {
    display: flex;
    justify-content: space-between;
    font-size: 12px;
    color: var(--iron);
    padding-bottom: 8px;
    border-bottom: 1px solid var(--border);
}

.ui-metric strong {
    color: var(--text-heading);
}

.ui-metrics .ui-metric:last-child {
    border-bottom: none;
    padding-bottom: 0;
}

/* 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. */
.ui-metrics--riga {
    display: flex;
    flex-wrap: wrap;
    column-gap: 48px;
    row-gap: 16px;
    align-items: flex-start;
}

.ui-metrics--riga .ui-metric {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    padding-bottom: 0;
    border-bottom: none;
    min-width: 0;
}

/* 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. */
.ui-metrics--riga .ui-metric > span {
    font-size: 10px;
    font-weight: 500;
    line-height: 1.3;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--text-muted);
}

/* 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. */
.ui-metrics--riga .ui-metric strong {
    font-size: 20px;
    font-weight: 600;
    line-height: 1.15;
    letter-spacing: -0.01em;
    color: var(--text-heading);
}

/* Text Block */
.ui-text-block {
    display: grid;
    gap: 10px;
    font-size: 14px;
    line-height: 1.6;
    color: var(--text-body);
}

.ui-text-block h3 {
    font-size: 13px;
    color: var(--text-heading);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.025em;
}

.ui-text-block--mid { font-weight: 500; }

.ui-text-block blockquote {
    border-left: 3px solid rgba(28, 40, 38, 0.20);
    padding-left: 10px;
    color: var(--text-body);
    font-style: italic;
}

.ui-text-accent {
    color: var(--iron);
    font-weight: 700;
}

/* 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;
}

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

/* Variante compatta, gemella di `.ui-select-sm`. Serve dove il campo sta
   NELL'INTESTAZIONE di un widget e non in un form: li' i 38px e i 14px di
   `.ui-input` pesano piu' del titolo che dovrebbero accompagnare. */
.ui-input-sm {
    height: 32px;
    padding: 0 10px;
    font-size: 13px;
}

.ui-input:focus {
    outline: none;
    border-color: var(--primary);
}

.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);
}

.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);
}

/* Buttons - Clean Minimal */
.ui-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    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;
}

/* Accent (arancio). ⚠️ Il testo e' `--jet`, non bianco: su `--princeton` pieno
   il bianco da' 2,48:1 e il jet 6,13. La tinta del bottone non cambia — cambia
   cio' che ci si scrive sopra, che e' l'unica parte che si deve leggere. */
.ui-btn-accent {
    background: var(--accent);
    color: var(--jet);
    border-color: var(--accent);
}

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

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

.ui-btn-lg {
    height: 44px;
    padding: 0 20px;
    font-size: 14px;
}

.ui-btn-sm.ui-btn-icon {
    width: 28px;
}

/* ⚠️ Due comandi icona adiacenti si toccano: concatenati nel JS senza nulla in
   mezzo la distanza misura **0 px** (Chrome 150), e i due quadrati col bordo si
   leggono come un controllo solo diviso a metà. Il rimedio non è uno spazio nel
   markup — `lead.js` lo faceva e dava **3 px**, cioè la larghezza accidentale
   di uno spazio a quel font, che cambia col font.
   `0.5rem` è lo stesso `gap` che `.ui-btn` usa fra icona e testo: una misura
   sola per «due cose che appartengono allo stesso comando ma restano
   distinte». */
.ui-row-actions {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
}

/* ⚠️ `ghost` NON toglie il bordo a un'icona di riga, e fino al 21/08/2026 lo
   toglieva: l'idea era che undici cornici in colonna disegnassero una griglia
   in competizione coi dati. Guardato su una tabella vera — la coda delle
   conferme, righe a zebra — vince l'obiezione opposta, che quel commento
   scriveva da sé: senza cornice la spunta e la croce si leggono come due glifi
   appoggiati, non come comandi. Il bordo a riposo è `--border`, lo stesso peso
   dei filetti di riga, e a distinguerlo basta la forma: un quadrato arrotondato
   di 28 px non si confonde con un filetto orizzontale largo quanto la tabella.
   ⚠️ Un bordo più CHIARO dei filetti non è la via di mezzo che sembra: sui
   fondi a zebra (`--platinum`) misura 1,08:1 contro 1,21 del bordo pieno, cioè
   sparisce una riga sì e una no — la visibilità a intermittenza si legge come
   due comandi diversi. Per questo `ghost` e `icon` insieme non dichiarano più
   niente: valgono le regole di `.ui-btn-icon` qui sopra. */

.ui-btn-lg.ui-btn-icon {
    width: 44px;
}

/* Loading */
.ui-btn-loading {
    pointer-events: none;
    opacity: 0.8;
}

/* Button Group */
.ui-btn-group {
    display: inline-flex;
}

.ui-btn-group .ui-btn {
    border-radius: 0;
    margin-left: -1px;
}

.ui-btn-group .ui-btn:first-child {
    border-radius: var(--radius-sm) 0 0 var(--radius-sm);
    margin-left: 0;
}

.ui-btn-group .ui-btn:last-child {
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

.ui-btn-group .ui-btn:hover,
.ui-btn-group .ui-btn.active {
    z-index: 1;
}

/* Accordion */
.ui-accordion {
    border: 1px solid var(--internal-menu-border);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    background: var(--internal-menu-bg);
}

.ui-accordion summary {
    font-size: 12px;
    font-weight: 600;
    color: var(--internal-menu-item-hover-text);
    cursor: pointer;
    list-style: none;
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.ui-accordion summary::after {
    content: '';
    width: 7px;
    height: 7px;
    margin-left: 10px;
    border-right: 1.8px solid var(--internal-menu-item-text);
    border-bottom: 1.8px solid var(--internal-menu-item-text);
    transform: rotate(45deg) translateY(-1px);
    transition: transform 0.2s ease, border-color 0.2s ease;
    flex-shrink: 0;
}

.ui-accordion[open] summary::after {
    transform: rotate(-135deg) translateY(1px);
    border-color: var(--internal-menu-active-bg);
}

.ui-accordion summary::-webkit-details-marker {
    display: none;
}

.ui-accordion p {
    font-size: 12px;
    color: var(--text-muted);
    margin-top: 8px;
}

/* Accordion variant: in-widget (flat style) */
.widget-body .ui-accordion {
    border: 1px solid var(--internal-menu-border);
    background: var(--internal-menu-bg);
    border-radius: var(--radius-sm);
}

.widget-body .ui-accordion[open] {
    background: var(--internal-menu-item-hover-bg);
    border-color: var(--internal-menu-active-bg);
}

.widget-body .ui-accordion summary {
    font-size: 12px;
}

/* Breadcrumb */
.ui-breadcrumb {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
}

/* Breadcrumb variant: in-widget */
.widget-body .ui-breadcrumb {
    font-size: 12px;
    gap: 6px;
}

/* 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;
}

/* Il pallino che dice «questo comando ha qualcosa di attivo».
   UNO per tutti i comandi d'intestazione: filtri, calendario, e quelli che
   verranno. Si chiamava `.ui-calendar-notify-dot`, cioe' del calendario e di
   nessun altro, e ad accenderlo non c'era NIENTE — `data-calendar-dot` non lo
   leggeva nessun JS, quindi era markup che sembrava una convenzione.
   Arancione e non --primary: il jet su un'icona jet non si distingue dal
   glifo, e il punto e' farsi notare. */
.ui-trigger-dot {
    position: absolute;
    top: 6px;
    right: 6px;
    width: 8px;
    height: 8px;
    border-radius: 999px;
    background: var(--princeton);
}

/* 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: 180px;
    /* Il pannello ha un suo margine interno: senza, le voci toccano il bordo
       e l'evidenziazione dell'hover finisce a filo della cornice. Prima
       questo padding stava solo su una variante, cioe' il menu nudo non era
       usabile da solo. */
    padding: 4px;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    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);
}

/* Allineamento a destra: serve a ogni menu ancorato a un trigger che sta
   sul bordo destro dell'intestazione, altrimenti il pannello esce dallo
   schermo. Era scritto `style="right:0;left:auto"` a mano voce per voce. */
.ui-dropdown-menu--right {
    left: auto;
    right: 0;
}

/* L'origine segue l'ancoraggio: il menu nasce dall'angolo da cui pende. */
.ui-dropdown-menu--right,
.ui-timefilter-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);
}

/* Intestazione di gruppo dentro il menu.
   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);
}

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

/* Larghezza fissa all'icona: senza, ogni glifo ha la sua e le etichette
   partono da ascisse diverse. Il markup lo faceva con `w-4` a mano, voce
   per voce — cioe' finche' qualcuno se lo ricordava. */
.ui-dropdown-item > i {
    flex: none;
    width: 16px;
    text-align: center;
    font-size: 13px;
    color: var(--text-light);
}

.ui-dropdown-item:hover > i {
    color: inherit;
}

/* Una voce pericolosa e' rossa anche nell'icona: spegnerla la farebbe
   sembrare disabilitata proprio dove il colore serve ad avvertire. */
.ui-dropdown-item--danger > i {
    color: inherit;
}

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

/* Il click deve dirsi sentito prima che la voce faccia effetto. Non `scale`
   come sul bottone: una riga a tutta larghezza che si stringe sembra un
   difetto di disegno, non una pressione. Il fondo scende subito (0 ms) e
   risale in 140: la risposta e' istantanea, il ritorno no. */
.ui-dropdown-item:active {
    background: rgba(28, 40, 38, 0.12);
    transition-duration: 0ms;
}

/* La voce scelta, in un menu che rappresenta uno stato (filtro attivo,
   ordinamento corrente). Lo stile esisteva solo dentro .ui-quickfilter-menu,
   una variante che nessuno usava: tolta quella, una voce marcata .active
   sarebbe rimasta identica alle altre in ogni dropdown dell'app. */
.ui-dropdown-item.active {
    background: rgba(28, 40, 38, 0.06);
    color: var(--primary);
    font-weight: 600;
}

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

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

.ui-dropdown-item--danger:hover {
    background: rgba(248, 113, 113, 0.10);
}

/* La freccetta del trigger. Non aveva NESSUNA regola di base: l'unica che la
   nominava era dentro il blocco `.ui-dropdown-demo`, quindi fuori dalla
   palestra era un'icona ferma. Ruota quando il menu e' aperto — e la
   rotazione e' l'unico segnale che il click ha fatto qualcosa quando il
   pannello si apre fuori dal campo visivo. */
.ui-dropdown-caret {
    font-size: 10px;
    color: var(--text-light);
    transition: transform 150ms var(--ease-out);
}

.ui-dropdown.is-open .ui-dropdown-caret {
    transform: rotate(180deg);
}

/* Il separatore taglia tutto il pannello, quindi rientra del padding del
   menu: un filo che si ferma prima del bordo sembra un errore di disegno. */
.ui-dropdown-divider {
    margin: 4px -4px;
    border: none;
    border-top: 1px solid var(--border);
}

/* 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 */
.ui-ai-card {
    border-radius: var(--radius-sm);
    padding: 18px;
    background: linear-gradient(135deg, var(--ai-card-bg-start), var(--ai-card-bg-end));
    border: 1px dashed var(--ai-card-border);
    display: grid;
    gap: 8px;
    font-size: 12px;
    color: var(--text-body);
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}



@media (hover: hover) and (pointer: fine) {
    .ui-ai-card:hover {
        transform: translateY(-2px);
        border-color: var(--ai-card-hover-border);
    }
}

.ui-ai-card--accent {
    background: linear-gradient(135deg, var(--ai-card-accent-start), var(--ai-card-accent-end));
}

.ui-ai-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ai-card-title-accent);
    font-weight: 600;
}

.ui-ai-chip {
    background: var(--ai-card-chip-bg);
    color: var(--ai-card-chip-text);
    padding: 2px 6px;
    border-radius: var(--radius-sm);
    font-size: 9px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.ui-ai-title {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    font-weight: 700;
    color: var(--text-heading);
}

.ui-ai-sparkle {
    color: var(--ai-card-title-accent);
    animation: uiSparkle 2.2s ease-in-out infinite;
}

.ui-ai-footer {
    font-size: 11px;
    color: var(--text-muted);
}

.ui-ai-accordion {
    border-style: dashed;
    background: rgba(255, 255, 255, 0.85);
}

.ui-ai-card p,
.ui-ai-card li,
.ui-ai-card .text-iron {
    color: var(--text-heading);
}

.ui-ai-kpis {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 8px;
}

.ui-ai-kpi {
    background: rgba(255, 255, 255, 0.7);
    border-radius: var(--radius-md);
    padding: 8px;
    text-align: center;
    font-size: 11px;
    color: var(--text-muted);
}

.ui-ai-kpi strong {
    display: block;
    font-size: 16px;
    color: var(--text-heading);
}

.ui-table-action {
    padding: 6px 10px;
    border-radius: var(--radius-md);
    border: 1px solid var(--border);
    font-size: 11px;
    font-weight: 600;
    color: var(--text-body);
    background: var(--bg-card);
}

.ui-table-action:hover {
    border-color: rgba(28, 40, 38, 0.15);
    color: var(--iron);
}

/* Contrast Cards — filled backgrounds */
.ui-contrast-card {
    border-radius: var(--radius-md);
    padding: var(--widget-padding);
    color: #fff;
    display: grid;
    gap: 8px;
    border: none;
}

.ui-contrast-card--positive { background: var(--dark-emerald); }

/* seaweed pieno col bianco sopra: 2,87:1 */
.ui-contrast-card--negative { background: var(--danger); }

.ui-contrast-card--neutral { background: var(--primary); }

.ui-contrast-card .ui-contrast-header { opacity: 0.85; }

.ui-contrast-card .ui-contrast-value { color: #fff; }

.ui-contrast-card .ui-contrast-meta { color: rgba(255,255,255,0.8); }

.ui-contrast-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    opacity: 0.75;
}

.ui-contrast-value { font-size: 28px; font-family: var(--font-mono); font-weight: 700; letter-spacing: 0; }

.ui-contrast-meta { font-size: 12px; opacity: 0.8; }

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

.ui-kpi-dark-soft {
    background: var(--iron);
    color: var(--platinum);
    border-radius: var(--radius-md);
    padding: var(--widget-padding);
    display: grid;
    gap: 8px;
    border: 1px solid var(--iron);
    box-shadow: none;
}

.ui-kpi-dark-subtle {
    background: var(--iron);
    color: var(--platinum);
    border-radius: var(--radius-md);
    padding: var(--widget-padding);
    display: grid;
    gap: 8px;
    border: 1px solid var(--iron);
    box-shadow: none;
}

.ui-kpi-dark-soft canvas,
.ui-kpi-dark canvas,
.ui-kpi-dark-subtle 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);
}

.ui-kpi-dark-soft .ui-kpi-dark-header { color: rgba(255, 255, 255, 0.70); }

.ui-kpi-dark-soft .ui-kpi-dark-meta { color: rgba(255, 255, 255, 0.55); }

.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; }

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

.ui-kpi-dark .h-card-sm,
.ui-kpi-dark-soft .h-card-sm,
.ui-kpi-dark-subtle .h-card-sm { height: 96px; }

.ui-kpi-dark .ui-dot--verde,
.ui-kpi-dark-soft .ui-dot--verde,
.ui-kpi-dark-subtle .ui-dot--verde { background: var(--seaweed-on-dark); }

.ui-kpi-dark .ui-dot--rosso,
.ui-kpi-dark-soft .ui-dot--rosso,
.ui-kpi-dark-subtle .ui-dot--rosso { background: var(--cherry-on-dark); }

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

/* Un ui-badge dentro un contenitore a fondo pieno.
   Le varianti .ui-badge--* hanno fondo del colore semantico al 12% e testo
   dello stesso colore pieno: leggibili su fondo chiaro, illeggibili appena
   il contenitore porta quel colore. Misurato prima di scrivere questa
   regola: "Alert" (--danger) dentro .ui-contrast-card--negative dava
   1,00:1, cioe' testo cherry su fondo cherry — invisibile — e "OK"
   (--success) dentro --positive 1,09:1. Entrambi sono il markup che il
   design system prescrive, e stanno a schermo nella palestra.
   Qui il fondo torna chiaro e il colore passa al testo, nella sua versione
   scura: --dark-emerald 5,52:1, --cherry 5,13:1, --iron 6,52:1.
   --warn usa --princeton-dark, il token ambrato nato proprio perche' in
   palette nessun arancio arrivava a 4,5:1 come testo. --iron resta il
   ripiego per le varianti senza una versione scura propria. */
.ui-contrast-card .ui-badge,
.ui-kpi-dark .ui-badge,
.ui-kpi-dark-soft .ui-badge,
.ui-kpi-dark-subtle .ui-badge {
    background: rgba(255, 255, 255, 0.92);
    color: var(--iron);
}

.ui-contrast-card .ui-badge--success,
.ui-kpi-dark .ui-badge--success,
.ui-kpi-dark-soft .ui-badge--success,
.ui-kpi-dark-subtle .ui-badge--success { color: var(--dark-emerald); }

.ui-contrast-card .ui-badge--danger,
.ui-contrast-card .ui-badge--error,
.ui-kpi-dark .ui-badge--danger,
.ui-kpi-dark .ui-badge--error,
.ui-kpi-dark-soft .ui-badge--danger,
.ui-kpi-dark-soft .ui-badge--error,
.ui-kpi-dark-subtle .ui-badge--danger,
.ui-kpi-dark-subtle .ui-badge--error { color: var(--cherry); }

.ui-contrast-card .ui-badge--warn,
.ui-kpi-dark .ui-badge--warn,
.ui-kpi-dark-soft .ui-badge--warn,
.ui-kpi-dark-subtle .ui-badge--warn { color: var(--princeton-dark); }

/* Pallino di stato.
   Era definito DUE VOLTE, a ~600 righe di distanza: una versione quadrata
   (border-radius 2px, «legend dots») e questa tonda. Vinceva questa perche'
   veniva dopo, quindi la prima non ha mai disegnato niente — ma chi leggeva
   il file dall'alto trovava per prima quella morta. Ne resta una.
   flex-shrink: 0 viene dalla versione tolta: senza, dentro una riga flex il
   pallino si schiaccia a ellisse quando l'etichetta e' lunga. */
.ui-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--primary);
    flex-shrink: 0;
}

/* Timeline */
.ui-timeline {
    display: grid;
    gap: 14px;
    position: relative;
    padding-left: 12px;
}

.ui-timeline::before {
    content: '';
    position: absolute;
    left: 4px;
    top: 4px;
    bottom: 4px;
    width: 2px;
    background: var(--border);
}

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

.ui-timeline-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--primary);
    margin-top: 4px;
}

/* 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;
}

/* ⚠️ Un avviso SEGUITO da qualcosa gli sta attaccato: misurato **0 px** fra
   l'avviso «il Registro non copre questa voce» e lo stato vuoto sotto, che li
   fa leggere come un blocco solo — la frase del vuoto sembra la coda
   dell'avviso. Un avviso che è l'ultimo figlio non cambia, e non serve.

   ⚠️ Non si somma a `space-y-*`: in flow normale i margini adiacenti
   COLLASSANO, e dentro `space-y-4` la distanza resta 16 px, non 28 (misurato in
   Chrome 150). Il collasso però NON avviene in un contenitore flex o grid: lì i
   due margini si sommerebbero. Contati il 24/08/2026 su tutto il markup —
   **zero** contenitori flex/grid che dichiarano anche `space-y`. Chi ne
   introduce uno con dentro un avviso rimisuri questa distanza. */
.ui-alert:not(:last-child) {
    margin-bottom: 12px;
}

/* ⚠️ Il testo dell'avviso deve poter STRINGERSI: un figlio flex ha
   `min-width: auto`, quindi non scende sotto il proprio contenuto e sotto i
   ~520 px le parole escono dal bordo invece di andare a capo. Non si vede,
   perche' `.main-content` ha `overflow-x: hidden` e le taglia. L'icona invece
   non si deve stringere. Best practice 5. */
.ui-alert > * { min-width: 0; }

.ui-alert > i { flex-shrink: 0; }

/* ⚠️ Il TESTO di queste due varianti non e' la tinta piena: princeton nudo sul
   proprio fondo velato da' 2,08:1 e seaweed 2,37:1, cioe' meta' della soglia.
   Le versioni scure della stessa tinta danno 5,69 e 5,31 — misurato il
   20/08/2026 componendo la velatura sui tre fondi di casa. Il bordo resta la
   tinta piena: quello non e' testo. */
.ui-alert--warn { background: rgba(255, 107, 0, 0.08); color: var(--princeton-dark); border-color: rgba(255, 107, 0, 0.30); }

.ui-alert--danger { background: rgba(248, 113, 113, 0.10); color: var(--danger); border-color: rgba(248, 113, 113, 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(28, 40, 38, 0.06); color: var(--iron); border-color: rgba(28, 40, 38, 0.15); }

/* 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);
    }
}

.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; }

/* ── Contatore di spesa in sidebar ──────────────────────────────────────────
   Sta sopra il pannello utente perche' l'offerta promette un contatore SEMPRE
   VISIBILE e Spark non ha una topbar. Numeri in Roboto Mono come ovunque:
   due importi incolonnati che cambiano ogni mese devono restare allineati. */
/* ⚠️ Il contatore non appartiene ne' al menu sopra ne' alla zona utente sotto:
   e' una terza cosa, e lo spazio e' l'unica dichiarazione che lo dice. Con
   `margin-bottom: 4px` — com'era fino al 21/08/2026 — si appoggiava al filetto
   del pannello utente e si leggeva come la prima riga di quel blocco, mentre
   sopra aveva 0 px dal menu: incollato da entrambe le parti, e quindi
   raggruppato con tutti e due.
   Le due misure verticali non sono uguali apposta: sopra c'e' un'area che
   SCORRE, e lo spazio li' esiste solo quando il menu e' corto; sotto c'e' un
   confine fisso, ed e' quello che va staccato.
   ⚠️ E il margine DESTRO e' 22, non 12: `.sidebar-container > .px-3` da'
   `padding-right: 22px` alla nav e al pannello utente, quindi ogni voce di
   menu, ogni etichetta di gruppo e il bottone utente finiscono a 238 px. Con
   12 il contatore finiva a 248 — l'unico elemento della sidebar fuori dalla
   colonna, e 10 px di sporgenza si leggono come «pesa piu' degli altri» prima
   ancora che come un disallineamento. Misurato il 21/08/2026. */
.ui-spesa-badge {
    margin: 12px 22px 16px 12px;
    padding: 10px 12px;
    border-radius: var(--radius-md);
    background: var(--sidebar-hover-bg);
}

.ui-spesa-badge-riga {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    min-width: 0;
}

.ui-spesa-badge-riga + .ui-spesa-badge-riga { margin-top: 2px; }

.ui-spesa-badge-etichetta {
    color: var(--sidebar-text-muted);
    font-size: 11px;
    white-space: nowrap;
}

/* Il residuo a zero non si dice colorando il numero: sulla sidebar scura cherry
   da' 2,13:1, cioe' il segnale sparisce proprio quando serve. Si dice col chip
   pieno, dove il bianco su cherry misura 5,96. */
.ui-spesa-badge-valore--esaurito {
    background: var(--cherry);
    color: #fff;
    padding: 0 5px;
    border-radius: var(--radius-sm);
}

.ui-spesa-badge-valore {
    color: var(--sidebar-text);
    font-family: var(--font-mono);
    font-size: 12px;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.ui-spesa-badge .v--empty { font-size: 11px; }

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

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

.ui-variance {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    border-radius: 4px;
    font-size: 11px;
    font-weight: 700;
}

/* ⚠️ `--dark-emerald` e non `--success`: seaweed sul proprio velo da' 2,37:1.
   `.ui-badge--success` era gia' corretto e questo no — due punti che dicono la
   stessa cosa con due colori diversi. */
.ui-variance-positive {
    background: rgba(26, 173, 126, 0.1);
    color: var(--dark-emerald);
}

.ui-variance-negative {
    background: rgba(248, 113, 113, 0.1);
    color: var(--danger);
}

.ui-variance i {
    font-size: 9px;
}

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


.ui-skeleton-kpi {
    height: 80px;
    border-radius: 6px;
}

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

.ui-stepper {
    display: flex;
    align-items: flex-start;
    gap: 0;
}

.ui-step {
    display: flex;
    flex-direction: column;
    align-items: center;
    flex: 1;
    position: relative;
}

.ui-step:not(:last-child)::after {
    content: '';
    position: absolute;
    top: 14px;
    left: calc(50% + 18px);
    right: calc(-50% + 18px);
    height: 2px;
    background: var(--alabaster);
}

.ui-step.completed:not(:last-child)::after {
    background: var(--primary);
}

.ui-step-indicator {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    font-weight: 600;
    background: var(--platinum);
    color: var(--stone);
    border: 2px solid var(--alabaster);
    position: relative;
    z-index: 1;
    transition: background-color 200ms var(--ease-out), color 200ms var(--ease-out), border-color 200ms var(--ease-out);
}

.ui-step.active .ui-step-indicator {
    background: var(--princeton);
    color: var(--jet);
    border-color: var(--princeton);
}

.ui-step.completed .ui-step-indicator {
    background: var(--primary);
    color: white;
    border-color: var(--primary);
}

.ui-step-label {
    margin-top: 8px;
    font-size: 11px;
    font-weight: 500;
    color: var(--stone);
    text-align: center;
}

.ui-step.active .ui-step-label {
    color: var(--accent-dark);
    font-weight: 600;
}

.ui-step.completed .ui-step-label {
    color: var(--iron);
}

/* Stepper verticale */
.ui-stepper-vertical {
    flex-direction: column;
    gap: 0;
}

.ui-stepper-vertical .ui-step {
    flex-direction: row;
    align-items: flex-start;
    padding-left: 0;
}

.ui-stepper-vertical .ui-step:not(:last-child)::after {
    top: 28px;
    left: 13px;
    right: auto;
    width: 2px;
    height: calc(100% - 8px);
    background: var(--alabaster);
}

.ui-stepper-vertical .ui-step.completed:not(:last-child)::after {
    background: var(--primary);
}

.ui-stepper-vertical .ui-step-content {
    margin-left: 12px;
    padding-bottom: 24px;
}

.ui-stepper-vertical .ui-step-label {
    margin-top: 0;
    text-align: left;
}

/* -----------------------------------------------------------------------------
   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(15, 23, 42, 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.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-animate-fade-up { animation: ui-fade-up 300ms var(--ease-out) forwards; }

.ui-animate-fade-in { animation: ui-fade-in 180ms var(--ease-out); }

.ui-animate-spin { animation: ui-spin 1s linear infinite; }

.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;
}

/* 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
   ----------------------------------------------------------------------------- */

.ui-progress-bar {
    height: 6px;
    background: var(--bg-subtle);
    border-radius: 3px;
    overflow: hidden;
}

.ui-progress-fill {
    height: 100%;
    background: var(--primary);
    border-radius: 3px;
    /* 500 ms erano fuori budget e per una barra di avanzamento non e' un
       dettaglio di stile: mezzo secondo di inseguimento vuol dire che il
       riempimento riporta il passato. */
    transition: width 250ms var(--ease-out);
}

/* Indeterminate progress */
.ui-progress-fill.indeterminate {
    width: 30%;
    animation: ui-progress-slide 1.5s ease-in-out infinite;
}

/* -----------------------------------------------------------------------------
   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;
}

/* 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;
}

.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;
}

/* =============================================================================
   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(213, 73, 82, 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;
}

.ui-error-state-code {
    font-family: 'Roboto Mono', monospace;
    font-size: 11px;
    color: var(--text-light);
    background: var(--bg-subtle);
    padding: 8px 12px;
    border-radius: var(--radius-sm);
    margin-bottom: 20px;
}

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

.ui-input-group {
    position: relative;
}

.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.is-valid,
.ui-select.is-valid {
    border-color: var(--ui-success);
}

/* I tre stati di validazione avevano un focus CIECO, e non si vedeva leggendo:
   il bordo a riposo e quello di focus sono la STESSA variabile, quindi l'unica
   differenza era la box-shadow — che la regola globale spegne. Un campo in
   errore restava identico a se stesso quando lo si metteva a fuoco.
   Il colore del bordo qui non puo' diventare l'indicatore, perche' e' gia'
   occupato dallo stato: serve un canale in piu', e l'outline e' l'unico che
   la globale non tocca. */
.ui-input.is-valid:focus,
.ui-select.is-valid:focus {
    border-color: var(--ui-success);
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
}

.ui-input-feedback.is-valid {
    color: var(--ui-success);
}

/* 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);
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
}

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

/* Warning state */
.ui-input.is-warning,
.ui-select.is-warning {
    border-color: var(--ui-warning);
}

.ui-input.is-warning:focus,
.ui-select.is-warning:focus {
    border-color: var(--ui-warning);
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
}

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

/* Input with icon */
.ui-input-icon-wrapper {
    position: relative;
}

.ui-input-icon-wrapper .ui-input {
    padding-right: 36px;
}

.ui-input-icon {
    position: absolute;
    right: 12px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 14px;
    pointer-events: none;
}

.ui-confirm-icon.warning {
    background: rgba(255, 107, 0, 0.1);
    color: var(--princeton-dark);
}

.ui-confirm-icon.danger {
    background: rgba(213, 73, 82, 0.1);
    color: var(--ui-danger);
}

.ui-confirm-icon.info {
    background: rgba(28, 40, 38, 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;
}

.ui-confirm-input {
    margin-bottom: 20px;
}

.ui-confirm-input label {
    display: block;
    font-size: 12px;
    color: var(--text-muted);
    margin-bottom: 8px;
    text-align: center;
}

.ui-confirm-input code {
    font-family: 'Roboto Mono', monospace;
    background: var(--bg-subtle);
    padding: 2px 6px;
    border-radius: var(--radius-sm);
    color: var(--ui-danger);
}

.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(28, 40, 38, 0.02);
}

.ui-dropzone.dragover {
    border-color: var(--primary);
    background: rgba(28, 40, 38, 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;
}

.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 */
.ui-file-list {
    margin-top: 16px;
}

.ui-file-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    margin-bottom: 8px;
}

.ui-file-item:last-child {
    margin-bottom: 0;
}

.ui-file-icon {
    width: 36px;
    height: 36px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bg-subtle);
    border-radius: var(--radius-sm);
    color: var(--text-muted);
    font-size: 16px;
}

.ui-file-icon.pdf { color: var(--cherry); }

.ui-file-icon.doc { color: var(--iron); }

.ui-file-icon.xls { color: var(--success); }

.ui-file-icon.img { color: var(--iron); }

.ui-file-info {
    flex: 1;
    min-width: 0;
}

.ui-file-name {
    font-size: 13px;
    font-weight: 500;
    color: var(--text-heading);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ui-file-size {
    font-size: 11px;
    color: var(--text-light);
}

.ui-file-progress {
    width: 100%;
    height: 4px;
    background: var(--bg-subtle);
    border-radius: 2px;
    margin-top: 6px;
    overflow: hidden;
}

.ui-file-progress-bar {
    height: 100%;
    background: var(--primary);
    border-radius: 2px;
    transition: width 0.3s ease;
}

.ui-file-actions {
    display: flex;
    gap: 4px;
}

.ui-file-action {
    width: 28px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-light);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background-color 150ms var(--ease-out), color 150ms var(--ease-out);
}

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

.ui-file-action.danger:hover {
    background: rgba(213, 73, 82, 0.1);
    color: var(--ui-danger);
}

.ui-file-status {
    font-size: 11px;
    font-weight: 500;
}

.ui-file-status.success {
    color: var(--ui-success);
}

.ui-file-status.error {
    color: var(--ui-danger);
}

/* =============================================================================
   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);
}

.ui-modal-close {
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    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: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);
}

/* 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. */
.ui-form-grid--3 {
    grid-template-columns: repeat(3, 1fr);
}

/* Matrice permessi: moduli in riga, livelli in colonna.
   Le tre etichette stanno in un'intestazione, una volta sola — ripetute su ogni
   riga (dodici parole per quattro moduli) il testo pesava piu' delle caselle e
   le colonne non si leggevano come colonne. Le colonne dei livelli sono a
   larghezza FISSA perche' devono restare allineate fra le righe: con `1fr` si
   ridimensionerebbero sul nome del modulo piu' lungo. */
.ui-permessi-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) repeat(3, 76px);
    gap: 10px 8px;
    align-items: center;
    margin-top: 8px;
}

.ui-permessi-intestazione {
    font-size: 11px;
    font-weight: 500;
    color: var(--text-light);
    text-align: center;
}

.ui-permessi-modulo {
    font-size: 13px;
    color: var(--text-body);
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* La casella si centra sotto la sua intestazione: allineata a sinistra, la
   colonna si legge solo grazie all'etichetta, che qui compare una volta sola. */
.ui-permessi-cella {
    display: flex;
    justify-content: center;
}

/* La vetrina del catalogo: la matrice fuori da una modale non ha un contenitore
   che la limiti, e stesa su tutta la card non si legge come si legge davvero. */
.ui-permessi-grid--vetrina { max-width: 420px; }



@media (max-width: 640px) {
    .ui-form-grid--3 {
        grid-template-columns: repeat(2, 1fr);
    }
}



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

/* =============================================================================
   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;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    /* A schermo stretto i comandi scendono sotto il titolo invece di spingere
       fuori la riga. Senza, a 500px l'intestazione chiedeva 520px in uno slot
       da 394 e a scorrere era LA PAGINA — su ogni vista, perche' il colpevole
       e' questo partial condiviso, non una vista. */
    flex-wrap: wrap;
}

/* Un flex item non scende sotto il proprio min-content finche' `min-width` resta
   `auto`: il blocco del titolo restava a 216px e mandava fuori riga i comandi
   anche quando avrebbe potuto stringersi a 93. */
.page-header-row > :first-child {
    min-width: 0;
}

/* I comandi non si stringono (`flex-shrink-0` nel markup, ed e' giusto: un'icona
   tagliata a meta' non e' un comando), e su schermi stretti superano la riga
   anche da soli — il blocco col menu interno chiede 400px di min-content contro
   i 394 disponibili. Quindi vanno a capo fra loro.
   ⚠️ NON `overflow-x: auto`: era il primo tentativo e rompeva ogni dropdown
   dell'intestazione. Un overflow diverso da `visible` su un asse rende `auto`
   anche l'altro, quindi il blocco diventa un contesto di RITAGLIO — e il menu
   del filtro temporale, alto 364px, si apriva dentro un contenitore alto 36:
   372px tagliati. Misurato. Andare a capo non ritaglia niente. */
.page-header-row > :last-child:not(:first-child) {
    max-width: 100%;
    flex-wrap: wrap;
    justify-content: flex-end;
}

.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(28, 40, 38, 0.04);
}

.ui-timefilter-dropdown {
    flex-shrink: 0;
    width: auto;
    max-width: 100%;
}

.ui-timefilter-menu {
    box-sizing: border-box;
    top: calc(100% + 12px) !important;
    margin-top: 0 !important;
    left: auto !important;
    right: 0 !important;
    width: min(390px, calc(100vw - 24px)) !important;
    min-width: 0 !important;
    max-width: calc(100vw - 24px) !important;
    padding: 14px;
    overflow: hidden;
    z-index: 140;
    background: #fff;
    border-color: var(--border);
    display: grid;
    gap: 12px;
}

.ui-timefilter-menu .ui-dropdown-header {
    padding: 0;
    margin: 0;
    color: var(--text-light);
    letter-spacing: 0.07em;
}

.ui-timefilter-menu--narrow {
    width: min(280px, calc(100vw - 24px)) !important;
    max-width: calc(100vw - 24px) !important;
    gap: 8px;
    padding: 10px;
}

.ui-timefilter-menu--narrow .ui-dropdown-header.widget-title {
    margin-bottom: 0;
    color: var(--iron);
    letter-spacing: 0.08em;
}

/* Premium vertical rhythm for template page */
.template-library .space-y-4 > :not([hidden]) ~ :not([hidden]) {
    margin-top: 1.5rem;
}

.template-library .space-y-6 > :not([hidden]) ~ :not([hidden]) {
    margin-top: 2rem;
}

.template-library .grid {
    row-gap: 1.5rem;
}




@media (max-width: 960px) {

    .ui-timefilter-dropdown {
        width: auto;
        max-width: 100%;
    }

    .ui-timefilter-menu {
        left: auto !important;
        right: 0 !important;
        width: min(390px, calc(100vw - 20px)) !important;
        max-width: calc(100vw - 20px) !important;
    }
}



@media (max-width: 640px) {
    .ui-timefilter-dropdown {
        width: 100%;
    }

    .ui-timefilter-menu {
        left: 0 !important;
        right: 0 !important;
        width: 100% !important;
        max-width: none !important;
    }
}

/* 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;
}

.ui-btn-group .ui-btn:first-child {
    border-radius: var(--interactive-radius) 0 0 var(--interactive-radius) !important;
}

.ui-btn-group .ui-btn:last-child {
    border-radius: 0 var(--interactive-radius) var(--interactive-radius) 0 !important;
}

/* Tooltip boxes: subtle elevation for readability */
.group .group-hover\:block.absolute.z-50.shadow-lg,
.group .group-hover\:block.absolute.z-50.shadow {
    box-shadow: var(--elevazione-tooltip) !important;
}

/* ── 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 ───────────────────────────────── */
.ui-block-title {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .5px;
    color: var(--stone);
    font-weight: 600;
}

.ui-dot--verde { background: var(--seaweed); }

.ui-dot--arancio { background: var(--princeton); }

.ui-dot--rosso { background: var(--cherry); }

.ui-mini-ring {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 50%;
}

.ui-mini-ring > span {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: #fff;
    font-size: 9px;
    font-weight: 600;
    color: var(--text-heading);
}

.ui-field-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    padding: 6px 0;
    border-bottom: 1px solid var(--platinum);
}

.ui-field-row:last-child { border-bottom: none; }

.ui-field-row .k { color: var(--stone); font-size: 13px; }

.ui-field-row .v { font-family: 'Roboto Mono', monospace; font-weight: 500; color: var(--jet); }

.ui-field-row .v--empty { color: var(--stone); font-style: italic; font-family: inherit; font-weight: 400; }

.ui-quality-sub { font-size: 11px; color: var(--stone); margin-top: 2px; }

/* Testata di una scheda — chi è il soggetto, in una fascia sola.
   Prima erano cinque `ui-field-row` dentro una card «Identità» larga un terzo:
   nome, categoria, tipo, P.IVA e provenienza sono l'INTESTAZIONE della pagina,
   non una sezione fra le altre, e come card competevano con l'Anagrafica invece
   di introdurla. */
.ui-scheda-testata {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}

/* Il nome: è l'unica cosa che si legge da lontano, quindi è l'unico elemento
   grande della fascia. Monospace perché sono ragioni sociali — stringhe da
   confrontare a colpo d'occhio con un file o una visura, non prosa. */
.ui-scheda-nome {
    font-family: 'Roboto Mono', monospace;
    font-size: 17px;
    font-weight: 600;
    line-height: 1.3;
    color: var(--jet);
    min-width: 0;
    overflow-wrap: anywhere;
}

.ui-scheda-chip-riga { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }

/* Un metadato: etichetta e valore attaccati. NON è `ui-chip`, che ha
   `cursor: pointer` ed è un comando: questo è un dato, e promettere un clic che
   non succede è peggio che non prometterlo.
   ⚠️ **Senza fondo, e senza icona**, ed è la correzione del 21/08/2026: quattro
   pill grigie in fila con quattro glifi decorativi mettevano quattro scatole
   uguali sopra la scheda, tutte allo stesso peso, e la riga si leggeva come
   rumore invece che come l'identità della voce. Qui la scatola è diventata
   informativa: **ce l'ha solo ciò che si può toccare** (`--controllo`, la
   categoria), quindi il fondo dice «questo si corregge» invece di ripetersi
   quattro volte. I metadati di sola lettura li separa un filo verticale. */
.ui-dato-chip {
    display: inline-flex;
    align-items: baseline;
    gap: 5px;
    padding: 2px 0;
    font-size: 11px;
    color: var(--stone);
    white-space: nowrap;
    max-width: 100%;
}

/* Il filo fra un metadato e il successivo: la separazione è un tratto, non una
   cornice per ciascuno. Sul primo non c'è, e non serve `:first-child` — il
   contenitore è un flex con `gap`, e il tratto sta nel padding sinistro. */
.ui-scheda-chip-riga > .ui-dato-chip + .ui-dato-chip {
    padding-left: 14px;
    border-left: 1px solid var(--platinum);
}

/* Il metadato che si CORREGGE tiene il fondo: è l'unica cosa interattiva della
   riga, e senza cornice un `<select>` spogliato non si distingue dal testo. */
.ui-dato-chip--controllo {
    padding: 4px 10px;
    border-radius: var(--radius-sm);
    background: var(--bg-muted);
}

.ui-scheda-chip-riga > .ui-dato-chip--controllo + .ui-dato-chip {
    padding-left: 8px;
    border-left: none;
}

.ui-dato-chip strong {
    font-weight: 600;
    color: var(--text-heading);
    overflow: hidden;
    text-overflow: ellipsis;
}

.ui-dato-chip i { font-size: 11px; color: var(--stone); }

.ui-dato-chip em { font-style: italic; color: var(--stone); }

/* Un campo modificabile DENTRO un chip: il controllo si spoglia della sua
   cornice e prende la tipografia del chip, o dentro un blocchetto da 11px
   entrerebbe una scatola bianca alta il doppio che sembra incollata lì. Resta
   un `<select>` vero — bordo al focus compreso, che `app.css` disegna con
   `outline` e non con l'ombra. */
.ui-dato-chip .ui-select {
    border: none;
    /* ⚠️ `background-color`, NON `background`: la scorciatoia cancella anche
       l'immagine di sfondo, che su `.ui-select` è la **freccia** — e un select
       senza freccia si legge come un'etichetta, quindi nessuno prova a
       cambiarlo. La categoria è l'unico campo derivato che una persona può
       correggere: deve sembrare correggibile. */
    background-color: transparent;
    background-position: right 0 center;
    padding: 0 14px 0 0;
    height: auto;
    min-height: 0;
    font-size: 11px;
    font-weight: 600;
    color: var(--text-heading);
}

/* 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;
}

/* Due colonne quando il CORPO della card è largo abbastanza, non quando lo è la
   finestra: `container-type: inline-size` su `.widget-body` sta in styles.css. */
@container (min-width: 640px) {
    .ui-campi { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    /* Un valore lungo — il nome per esteso di un fondo, un benchmark — 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; }

/* ============================================================
 * Anagrafica in righe allineate — `.ui-metrics--aligned`
 * ============================================================
 * Etichetta a sinistra con la sua icona, valore a destra, un filo per riga,
 * due colonne. E' la forma delle schede del BI (`Dati azienda` in `lead.js`),
 * portata qui perche' Nicola l'ha indicata come termine di paragone.
 *
 * Perche' non `ui-campi` (etichetta SOPRA il valore), che e' quello che c'era:
 * a due colonne dentro mezza card, i valori di un'anagrafica sono corti —
 * «S.P.A.», «ATTIVA», «100», «2024» — e l'etichetta sopra raddoppia l'altezza
 * di ogni riga per incolonnare una parola. Con l'etichetta a sinistra la riga
 * e' una, e la coppia si legge perche' il filo la delimita.
 *
 * ⚠️ La distanza etichetta/valore qui NON e' il difetto chiuso il 21/08/2026 su
 * `ui-metrics--riga`: la' i gruppi si distribuivano su tutta la card e la
 * distanza cresceva con la finestra. Qui la riga vive in **mezza colonna di
 * mezza card** — misurata ~250 px a 1440 — e il filo sotto dice dove finisce la
 * coppia. La regola resta la stessa: e' il contenitore che deve essere
 * limitato, non la distanza a essere vietata.
 */
/* ⚠️ Le colonne hanno un TETTO e non si stirano — `minmax(280px, 480px)` con
   `justify-content: start` — ed è la lezione del 21/08/2026 applicata qui prima
   di ripetere l'errore. Con `repeat(2, 1fr)` su una card a piena larghezza
   (1554 px misurati) la distanza fra il testo dell'etichetta e il suo valore
   arrivava a **525-648 px** sui campi corti e **1258** su quelli larghi: la
   stessa famiglia del difetto chiuso su `ui-metrics--riga`, e non mitigabile
   dal filo. Col tetto le colonne restano ≤480 e la distanza sta sotto i ~320.
   `auto-fill` fa il resto: tre colonne su una card larga, due sotto i 1000 px,
   una sotto i 600 — nessuna soglia scritta a mano. */
.ui-metrics--aligned {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(var(--colonna-dato), 1fr));
    gap: 0 40px;
}

.ui-metrics--aligned .ui-metric.ui-metric--largo {
    grid-column: 1 / -1;
    /* Stessa colonna d'etichetta delle righe corte: il valore comincia
       sull'UNICO asse della card. Con `max-content` l'etichetta si adattava a
       sé stessa e ogni riga larga partiva da un'ascissa sua — misurate 373, 397
       e 505 px in una card sola, che si legge come un allineamento saltato. */
    grid-template-columns: var(--etichetta-dato) minmax(0, 1fr);
}

.ui-metrics--aligned .ui-metric.ui-metric--largo .ui-metric-etichetta span {
    overflow: visible;
    text-overflow: clip;
    white-space: normal;
}

/* ⚠️ L'etichetta ha una colonna FISSA e il valore comincia subito dopo,
   allineato a sinistra — non `1fr auto` col valore a destra, che è quello che
   c'era. Con il valore appeso al bordo destro la distanza dal suo testo la
   decideva la lunghezza dell'etichetta, quindi cambiava riga per riga: misurate
   244 · 348 · 307 · 367 · 346 px sulla stessa card, cioè cinque distanze diverse
   dove l'occhio deve ogni volta ritrovare la coppia. Col la colonna fissa la
   distanza è costante per costruzione, e i valori si incolonnano fra loro —
   che è la seconda cosa che si legge scorrendo una colonna di dati. */
.ui-metrics--aligned .ui-metric {
    display: grid;
    grid-template-columns: minmax(0, var(--etichetta-dato)) minmax(0, 1fr);
    align-items: baseline;
    gap: 12px;
    min-width: 0;
    padding: 7px 0;
    border-bottom: 1px solid var(--platinum);
}

/* ⚠️ `:last-child` non basta: in griglia a due colonne l'ULTIMA di sinistra non
   e' l'ultima del DOM, e restava con il filo mentre quella accanto lo perdeva.
   Il filo si toglie sull'ultima riga di ciascuna colonna — `nth-last-child(-n+2)`
   sotto i due assieme — e sotto la rottura, dove la colonna e' una, sul solo
   ultimo elemento. */
/* ⚠️ Il filo NON si toglie all'ultima riga: con un numero di colonne che cambia
   con la larghezza, «l'ultima di ogni colonna» non è esprimibile in CSS senza
   indovinare quante sono — e una regola che indovina lascia il filo su una
   colonna e lo toglie sull'altra, che a schermo si legge come un errore di
   allineamento. Il filo su tutte è uniforme, e sotto l'ultima riga c'è il
   fondo della card, dove un filo in più non tocca niente. */

.ui-metrics--aligned .ui-metric-etichetta {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
    font-size: 12px;
    color: var(--text-muted);
}

/* L'icona e' un ancoraggio visivo, non un'informazione: dice «riga di
   anagrafica» e aiuta a ritrovare il campo scorrendo, quindi resta al livello
   del testo terziario. Un'icona colorata quanto il valore competerebbe con lui. */
.ui-metrics--aligned .ui-metric-icona {
    font-size: 12px;
    color: var(--stone);
    flex-shrink: 0;
}

.ui-metrics--aligned .ui-metric-etichetta span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ui-metrics--aligned .ui-metric strong {
    min-width: 0;
    font-size: 13px;
    font-weight: 500;
    color: var(--jet);
    text-align: left;
    overflow-wrap: anywhere;
}

/* La fascia dei numeri di bilancio, in cima ai dati verificati: fondo tenue e
   nient'altro — nessun bordo, nessuna ombra. L'evidenza è **leggera apposta**:
   distingue tre misure dalle righe di testo che seguono senza diventare una
   card dentro la card, che a schermo si legge come un widget annidato. */
.ui-fascia-dati {
    padding: 18px 20px;
    border-radius: var(--radius-md);
    background: var(--bg-muted);
}

/* ⚠️ Il margine sotto lo prende solo se QUALCOSA SEGUE dentro lo stesso blocco:
   dove la fascia chiude il suo `widget-body` — il Rapporto commerciale — il
   distacco lo dà già il ritmo verticale fra blocchi (36 px), e i due si
   sommavano a 56. È la stessa forma del separatore che restava sospeso senza
   testo: una spaziatura che dipende da un vicino va condizionata al vicino. */
.ui-fascia-dati:not(:last-child) { margin-bottom: 20px; }

/* ⚠️ **16 px, non i 20 di `ui-metrics--riga`**, e l'override sta qui e non nel
   componente: là la banda è il contenuto principale di una card (il Rapporto
   commerciale), 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 già a distinguerli; la dimensione in più era
   evidenza spesa due volte. */
.ui-fascia-dati .ui-metrics--riga .ui-metric strong { font-size: 16px; }

.ui-fascia-dati .ui-metrics--riga { column-gap: 40px; row-gap: 12px; }

/* ⚠️ I tre campi a riga intera sono i PRINCIPALI — ragione sociale, sede,
   ATECO — e il loro valore è un punto più grande: sono ciò che identifica la
   voce, mentre forma giuridica, fatturato e dipendenti la descrivono. La
   gerarchia non la fa il layout da solo: a riga intera stanno anche i vuoti
   raccolti in coda, che principali non sono, e infatti restano al corpo del
   testo perché portano `is-vuoto`. */
.ui-metrics--aligned .ui-metric.ui-metric--largo strong:not(.is-vuoto) {
    font-size: 14px;
}

.ui-metrics--aligned .ui-metric strong.is-mono {
    font-family: 'Roboto Mono', monospace;
    font-size: 12px;
}

/* Un vuoto che dice PERCHE' e' vuoto resta un testo, non un valore: corsivo,
   grigio terziario, peso normale — la stessa resa di `.ui-campo-valore--vuoto`,
   perche' e' la stessa cosa in un componente diverso. */
.ui-metrics--aligned .ui-metric strong.is-vuoto {
    color: var(--stone);
    font-style: italic;
    font-weight: 400;
}

/* ============================================================
 * Figure chiave in tabella — `.ui-persone`
 * ============================================================
 * Una persona per riga, le sue informazioni in colonna. Sostituisce il blocco
 * annidato (persona → gruppi per tipo → righe) che rendeva ogni figura alta
 * quattro righe per mostrare un telefono e un'email.
 *
 * Regge perche' i dati lo permettono, ed e' stato misurato prima di scriverlo:
 * **205 persone su 225** hanno esattamente due recapiti, uno per tipo; il
 * massimo per tipo e' **2**; e **217 soggetti su 218** hanno una persona sola.
 * Dove i valori dello stesso tipo sono piu' d'uno si impilano nella cella,
 * `.ui-persone-extra` — quattro persone in tutto l'archivio.
 */
/* ⚠️ L'unica eccezione al maiuscolo delle celle (`.ui-table tbody td`), e vale
   per gli IDENTIFICATORI DI MACCHINA: un'email o una URL in maiuscolo si legge
   come se andasse digitata così, ed è più difficile da leggere per davvero. La
   regola globale nasce dalle anagrafiche dei gestionali, che arrivano già
   maiuscole — un'email non è un'anagrafica. Il NOME della persona resta
   maiuscolo: quello è anagrafica, e uniformarlo è il motivo per cui la regola
   esiste. */
.ui-table tbody td .ui-recapito-valore,
.ui-table tbody td .ui-persone-extra,
.ui-table tbody td .ui-persone-fonte,
.ui-table tbody td .ui-recapito-fonte { text-transform: none; }

/* La cella del NOME porta due righe — il nome e la sua provenienza — mentre le
   altre ne portano una. Con l'allineamento al centro che `.ui-table td` applica,
   email e telefono galleggiavano a metà del nome invece di stare sulla sua prima
   riga, e la riga si leggeva sfalsata. */
.ui-persone tbody td { vertical-align: top; }

.ui-persone-nome { font-weight: 600; color: var(--jet); }

/* La provenienza sta sotto il nome e non in una colonna sua: e' un attributo
   della riga, non un dato da confrontare in verticale. */
.ui-persone-fonte {
    display: block;
    font-size: 10px;
    font-weight: 400;
    color: var(--stone);
    margin-top: 1px;
}

.ui-persone-extra {
    display: block;
    font-size: 11px;
    color: var(--text-muted);
    margin-top: 2px;
}

/* Recapiti — la TABELLA dei contatti dell'organizzazione, dal 21/08/2026.
   Prima era una lista di gruppi con un titolo per tipo, e prima ancora righe
   `ui-field-row`. Il passaggio a tabella non è cosmesi: nella stessa scheda la
   card Persone era già una tabella, e due liste con due impaginazioni diverse
   si leggono come due componenti presi da posti diversi. In tabella il tipo è
   una COLONNA — quindi si ordina, e non si ripete come intestazione di gruppo —
   e la provenienza smette di essere una chiosa del titolo per diventare un dato
   confrontabile in verticale, come «Affidabilità» fra le persone.
   Le classi del blocco annidato — `ui-org`, `ui-persona*`, `ui-recapito-gruppo`,
   `ui-recapito-tipo`, `ui-recapiti-coda`, `ui-fonte-blocco` — sono state
   rimosse con esso: erano CSS che nessuna vista usava più, cioè la forma in cui
   un catalogo comincia a insegnare la versione vecchia. */

/* La cella del recapito può portare due righe (il valore e, nel caso raro, la
   persona a cui appartiene) mentre le altre ne portano una: senza questo, tipo
   e provenienza galleggiano a metà del valore. È la stessa regola di
   `.ui-persone`. */
.ui-recapiti tbody td { vertical-align: top; }

.ui-recapito-valore {
    font-size: 13px;
    font-weight: 500;
    color: var(--jet);
    /* Un indirizzo lungo tronca invece di allargare la card: la larghezza della
       colonna e' la stessa per tutti, o l'incolonnamento salta di nuovo. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ui-recapito-valore--mono { font-family: 'Roboto Mono', monospace; }

.ui-recapito-fonte { font-size: 11px; color: var(--stone); white-space: nowrap; }

.ui-recapito-nota { font-size: 11px; color: var(--stone); }

/* Un modulo di scrittura che sta chiuso finché non serve.
   ⚠️ È l'ECCEZIONE: dove una card è vuota per costruzione, nascondere l'unico
   modo di riempirla la rende indistinguibile da una di sola lettura. Si usa
   dove la card ha già contenuto proprio e i moduli sono più d'uno. Il
   `<summary>` porta le classi di `ui-btn`: il marcatore nativo va tolto, o
   accanto al bottone resta un triangolo che non è del design system. */
/* ============================================================
 * Il corpo di un modulo in linea — `.ui-inline-form-corpo`
 * ============================================================
 * Aperto, il modulo è una SUPERFICIE DI LAVORO e non la continuazione della
 * lista sopra: il fondo rientrato lo dice senza aggiungere un bordo. Prima era
 * piatto — sommario, riga di stato, campi e nota tutti allineati alla stessa
 * colonna e allo stesso peso — e la sezione si leggeva come quattro righe di
 * testo invece che come un modulo.
 */
.ui-inline-form-corpo {
    display: grid;
    gap: 12px;
    padding: 14px 16px;
    background: var(--platinum);
    border-radius: var(--radius-sm);
}

/* I campi si raggruppano per SIGNIFICATO, e la distanza dice il gruppo: 8 px
   dentro (identità: nome, cognome, ruolo) e 20 fra un gruppo e l'altro
   (recapiti: telefono, email, LinkedIn). È la stessa regola della banda di
   metriche — a raggruppare è la prossimità, non una linea. */
.ui-form-campi {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px var(--space-4);
}

.ui-form-gruppo {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    min-width: 0;
}

.ui-form-gruppo--espandi { flex: 1 1 220px; }

/* I comandi in fondo, insieme: l'azione primaria e la sua via d'uscita si
   leggono come una coppia, non come due elementi capitati vicini. */
.ui-form-azioni {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-left: auto;
}

/* La riga che dichiara COSA si sta correggendo, dentro il modulo.
   ⚠️ Esiste perché il sommario del modulo NON deve cambiare testo: quello è il
   comando della card — «aggiungi una figura» — e vederlo diventare «correggi»
   mentre si clicca la matita di una riga si legge come se il comando fosse
   stato dirottato. Lo stato della correzione appartiene al modulo. */
.ui-form-corregge {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0;
    padding-bottom: 10px;
    border-bottom: 1px solid var(--border);
    font-size: 12px;
    color: var(--text-muted);
}

.ui-form-corregge strong { color: var(--text-heading); }

.ui-inline-form > summary { list-style: none; cursor: pointer; }

.ui-inline-form > summary::-webkit-details-marker { display: none; }

.ui-inline-form[open] > summary { margin-bottom: 10px; }

.ui-completeness-ring {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 3px solid var(--alabaster);
}

.ui-completeness-ring > span {
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 11px;
    font-weight: 600;
    color: var(--jet);
}

/* ============================================================
 * 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;
}

.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);
}

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


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

/* 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); }

/* 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. */
.is-busy {
    opacity: 0.45;
    pointer-events: none;
    transition: opacity 120ms var(--ease-out);
}

/* ============================================================
 * PAGINATORE — BiUI.paginate()
 * Il markup lo genera il wrapper: queste classi sono il suo
 * contratto. Cambiarle senza guardare helpers.js rompe ogni
 * tabella dell'applicazione in una volta.
 * ============================================================ */

.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-controls {
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

.ui-paginator-size {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 12px;
    color: var(--text-muted);
    white-space: nowrap;
}

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

.ui-select-sm {
    padding: 0.2rem 1.5rem 0.2rem 0.5rem;
    font-size: 12px;
    height: auto;
    min-height: 0;
}

/* ============================================================
 * TABELLA BASE — .ui-table
 *
 * Prima non esisteva: `class="ui-table"` compariva in 5 viste del BI
 * e non applicava NIENTE, mentre lo stile stava spalmato su ogni
 * cella (px-4 py-3 text-right text-xs font-bold text-ui-body
 * uppercase tracking-wide — 8/12 classi per <th>). Nessuno ricopia
 * dodici classi identiche due volte: e' da li' che le tabelle
 * divergevano fra loro.
 *
 * Qui il markup torna a <th>/<td> nudi. Restano due modificatori,
 * perche' sono decisioni e non ripetizioni:
 *   .num     cella numerica: destra, tabellare, non spezzabile
 *   .fissa   colonna che resta ferma allo scroll orizzontale
 *
 * Ce n'era un terzo, .ui-table--compatta, tolto l'08/08/2026: nessuna
 * vista lo nominava, non stava nel catalogo e non stava nei documenti,
 * quindi era una decisione che nessuno poteva prendere. Se serviranno
 * righe piu' basse per una tabella lunga, si rifa' con la sua vetrina.
 *
 * Si combina con .sortable-table (ordinamento) e
 * .ui-table-sticky-head (intestazione ferma allo scroll verticale).
 * ============================================================ */

.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: 0.75rem 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: 0.75rem 1rem;
    color: var(--text-body);
    vertical-align: middle;
}

.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); }

/* Testo delle celle SEMPRE maiuscolo. Le anagrafiche arrivano dai gestionali
   gia' in maiuscolo e le tassonomie scritte a mano no: due tabelle vicine
   finivano una in maiuscolo e una in minuscolo, e la differenza si legge come
   se le due colonne fossero di natura diversa. Uniformare qui costa una riga;
   normalizzare i dati a monte non si puo', sono anagrafiche di terzi.
   `text-transform` non tocca il valore: la ricerca, l'ordinamento e l'export
   continuano a vedere la stringa originale. */
.ui-table tbody td {
    text-transform: uppercase;
    /* Un punto in meno: il maiuscolo occupa piu' larghezza a parita' di
       caratteri, e senza questo la stessa colonna manda a capo cinque nomi in
       piu' e ne tronca cinque. Misurato sui 68 record di `portafoglio`:
       12px -> 5 troncati, 11px -> 0. */
    font-size: 11px;
}

/* Prima colonna: e' il nome della cosa, quindi porta il peso. */
.ui-table tbody td:first-child {
    font-weight: 600;
    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. `bg-inherit` sulla cella
   serve o il contenuto sottostante trasparirebbe. */
.ui-table th.fissa,
.ui-table td.fissa {
    position: sticky;
    left: 0;
    z-index: 1;
    background: inherit;
}

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

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

/* 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.

   ⚠️ Due selettori e non uno, perche' `BiUI.erroreTabella()` e' generica e
   funziona su qualunque `<table>`: senza il secondo, su una tabella priva di
   `.ui-table` restava il `padding: 10px 12px` di `table td` sommato ai 48 dello
   stato. E il primo non si puo' togliere: `.ui-table-errore td` da solo ha una
   classe e un tipo, mentre `.ui-table tbody td` ne ha uno e due, quindi
   perderebbe la cascata proprio dove il maiuscolo c'e'. */
.ui-table .ui-table-errore td,
.ui-table-errore td {
    padding: 0;
    text-transform: none;
}

/* Stesso trattamento quando il vuoto di una tabella ospita uno stato intero —
   icona, titolo e la frase che dice PERCHE' e' vuoto — invece del solo testo.
   ⚠️ Il selettore guarda il CONTENUTO (`:has`), non una classe nuova: le righe
   `ui-table-vuota` che portano il solo `—` reso dal server devono tenersi i
   loro 2,5rem, o si accartocciano. Chrome 150 lo supporta, ed e' gia' usato
   altrove in questo foglio. */
.ui-table .ui-table-vuota td:has(.ui-empty-state),
.ui-table-vuota td:has(.ui-empty-state) {
    padding: 0;
    text-transform: none;
    /* `.ui-table tbody td` porta peso 600 — le celle di una tabella sono dati —
       e la frase dello stato lo ereditava: stessa frase, grassetto qui e
       regolare nella card accanto. Il titolo tiene il suo 600, che lo dichiara
       da se'. */
    font-weight: 400;
}

/* ============================================================
 * BLOCCO DI CODICE — .ui-code-block
 * Usato nella palestra per mostrare il markup da copiare.
 * Era gia' nel markup di template.php ma senza alcuna regola:
 * il codice si vedeva con il font della pagina, indistinguibile
 * dal testo che lo commentava.
 * ============================================================ */

.ui-code-block {
    display: block;
    padding: 0.875rem 1rem;
    background: var(--jet);
    color: var(--snow);
    border-radius: 6px;
    font-family: 'Roboto Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
    line-height: 1.6;
    overflow-x: auto;
    white-space: pre;
    tab-size: 2;
}

.ui-code-block code {
    font: inherit;
    color: inherit;
    background: none;
    padding: 0;
}

/* ============================================================
 * TRE COMPONENTI CHE IL MARKUP USAVA SENZA CHE ESISTESSERO
 *
 * Misurato sulle viste del BI: comparivano nel markup e non
 * avevano alcuna regola CSS. Non davano errore — semplicemente
 * l'elemento restava quello di default del browser, che e' il
 * modo piu' silenzioso di avere un'interfaccia incoerente.
 *   .ui-checkbox  4 viste (servizi, lead, e i bulk-select in JS)
 *   .ui-btn-xs    lead.js, sui bottoni dentro le righe di tabella
 *   .ui-avatar    profile.php
 * ============================================================ */

/* Checkbox: il controllo nativo non si colora, e con accent-color
   si allinea alla palette senza reimplementarlo da zero — che
   significherebbe rifare anche focus, tastiera e stato indeterminato. */
.ui-checkbox {
    width: 16px;
    height: 16px;
    accent-color: var(--jet);
    cursor: pointer;
    vertical-align: middle;
    flex: none;
}

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

/* Iniziali dell'utente. Cerchio pieno, non immagine: non c'e' un
   caricamento di foto e un cerchio vuoto sarebbe peggio di niente. */
.ui-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: var(--jet);
    color: var(--snow);
    font-size: 14px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    flex: none;
}

.ui-avatar--sm { width: 28px; height: 28px; font-size: 11px; }

.ui-avatar--lg { width: 56px; height: 56px; font-size: 18px; }

/* ============================================================
 * 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.
 * ============================================================ */

.ui-choice-card {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 16px;
    border: 1px solid var(--alabaster);
    border-radius: 8px;
    background: #fff;
    text-align: left;
    transition: border-color .15s var(--ease-out), background-color .15s var(--ease-out);
}

.ui-choice-card:hover {
    border-color: var(--silver);
}

.ui-choice-card.is-active {
    border-color: var(--jet);
    background: var(--platinum);
}

/* Variante per la riga orizzontale scorrevole (era .ui-cat-card):
   stessa card, meno respiro, perche' ne stanno otto in fila. */
.ui-choice-card--compact {
    padding: 8px 12px;
    gap: 2px;
}

/* ============================================================
 * PALESTRA — classi che sostituiscono gli stili inline.
 *
 * Vivono qui e non dentro template.php perche' la regola vale
 * anche per la pagina che la insegna: se il catalogo mostra
 * `style="background:#1AAD7E"`, chi copia copia quello.
 *
 * I campioni prendono il colore dalla VARIABILE, non
 * dall'esadecimale ricopiato: prima erano scritti a mano in
 * 22 punti, quindi potevano divergere dalla palette vera senza
 * che niente lo segnalasse.
 * ============================================================ */

.ui-palette-grid { height: 280px; }

.ui-swatch--snow { background: var(--snow); }

.ui-swatch--platinum { background: var(--platinum); }

.ui-swatch--alabaster { background: var(--alabaster); }

.ui-swatch--silver { background: var(--silver); }

.ui-swatch--stone { background: var(--stone); }

.ui-swatch--iron { background: var(--iron); }

.ui-swatch--jet { background: var(--jet); }

.ui-swatch--burgundy { background: var(--burgundy); }

.ui-swatch--cherry { background: var(--cherry); }

.ui-swatch--dark-emerald { background: var(--dark-emerald); }

.ui-swatch--seaweed { background: var(--seaweed); }

.ui-swatch--princeton { background: var(--princeton); }

/* I due piu' chiari sparirebbero sul bianco: il bordo li rende visibili. */
.ui-swatch--snow,
.ui-swatch--platinum { border: 1px solid var(--alabaster); }

/* Etichetta scritta SOPRA un campione chiaro. */
/* 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. */
.ui-swatch-label { color: var(--jet); }

/* --- dimostrazione dei gradini di tracking (sezione Tipografia) --- */
.tpl-tracking-display {
    font-size: 2.25rem;
    font-weight: 600;
    line-height: 1.05;
    letter-spacing: var(--tracking-display);
}

.tpl-tracking-display-kpi {
    font-family: var(--font-mono);
    font-size: 2.75rem;
    font-weight: 500;
    line-height: 1;
    letter-spacing: var(--tracking-display);
}

.tpl-tracking-body { letter-spacing: var(--tracking-body); max-width: 62ch; }

.tpl-tracking-label { letter-spacing: var(--tracking-label); }

/* Il termine di paragone: il gradino spento. */
.tpl-tracking-zero { letter-spacing: 0; }

/* --- stati dell'anello di completezza ---
   Erano `style="border-color: var(--seaweed)"` scritto a mano. Il colore
   dipende dalla percentuale, ma le soglie sono tre e hanno un nome: uno
   stato con un nome e' un modificatore, non un valore. */
.ui-completeness-ring--ok { border-color: var(--seaweed); }

.ui-completeness-ring--warn { border-color: var(--princeton); }

.ui-completeness-ring--vuoto { border-color: var(--alabaster); }

.ui-mini-ring--ok { background: var(--seaweed); }

.ui-mini-ring--warn { background: var(--princeton); }

.ui-mini-ring--vuoto { background: var(--alabaster); }

/* =============================================================================
   MATRICE DI RITENZIONE — .ui-matrice
   Una tabella dove il colore della cella E' il dato. Non e' un grafico perche'
   i numeri vanno letti uno per uno: quanti clienti di quale coorte, in che
   anno. Un grafico li renderebbe confrontabili e illeggibili.

   ⚠️ LA CELLA PORTA UNA CLASSE SUA, e non e' un vezzo: la zebratura globale
   di riga 709 e' `table tbody tr:nth-child(odd) td:not([class*="bg-"])`, cioe'
   DUE selettori di classe e QUATTRO di tipo. `.ui-matrice td[data-intensita]`
   ne ha due di classe e uno di tipo: pareggia sulle classi e perde sui tipi,
   quindi la zebra vince e le celle restano bianche e grigie. A schermo si
   legge come una tabella normale — nessun errore, nessuna riga in console, e
   la matrice sembra semplicemente non colorata.
   Con `td.ui-matrice-cella[data-intensita]` le classi diventano tre e la
   regola vince senza dipendere dall'ordine nel file.
   ============================================================================= */

/* Cinque gradini piu' lo zero. L'intensita' arriva da `data-intensita`, mai da
   uno `style` calcolato in JS: una soglia scritta nel foglio si rilegge, una
   scritta in una stringa di stile no. Le soglie stanno in una costante sola —
   `INTENSITA_MATRICE` in assets/js/portafoglio.js — e sono tarate contando le
   celle vere, non su numeri tondi. */
.ui-matrice td.ui-matrice-cella {
    text-align: center;
    font-variant-numeric: tabular-nums;
    transition: background-color 150ms var(--ease-out);
}

.ui-matrice td.ui-matrice-cella[data-intensita="0"] { background-color: transparent; color: var(--text-light); }

.ui-matrice td.ui-matrice-cella[data-intensita="1"] { background-color: rgba(26, 173, 126, 0.10); }

.ui-matrice td.ui-matrice-cella[data-intensita="2"] { background-color: rgba(26, 173, 126, 0.22); }

.ui-matrice td.ui-matrice-cella[data-intensita="3"] { background-color: rgba(26, 173, 126, 0.38); }

/* Testo SCURO sui due gradini pieni, non bianco. Il bianco sembrava ovvio —
   fondo piu' carico, testo che si stacca — ed e' misurato a 1,83 e 2,30 di
   contrasto: sotto la meta' della soglia 4,5. Il verde di casa e' chiaro anche
   all'80% di opacita', e su un fondo chiaro il testo bianco sparisce.
   Con --jet: 6,9 e 5,6. */
.ui-matrice td.ui-matrice-cella[data-intensita="4"] { background-color: rgba(26, 173, 126, 0.56); color: var(--jet); }

.ui-matrice td.ui-matrice-cella[data-intensita="5"] { background-color: rgba(26, 173, 126, 0.78); color: var(--jet); }

/* La cella non ancora misurabile. NON e' uno zero, e la differenza e' tutta:
   uno zero li' direbbe «nessuno e' tornato» dove la verita' e' «l'anno non e'
   ancora passato». Tratteggiata, cosi' non si confonde ne' con lo zero ne' con
   una cella che nessuno ha colorato. */
.ui-matrice td.ui-matrice-cella.ui-matrice-futura {
    background-color: transparent;
    background-image: repeating-linear-gradient(
        45deg,
        transparent, transparent 3px,
        var(--alabaster) 3px, var(--alabaster) 4px
    );
}

/* =============================================================================
   MENU DEI FILTRI — .ui-dropdown-menu--filtri
   Un dropdown che contiene controlli, non solo voci: piu' largo, e con una riga
   che ne affianca due senza farli sembrare due voci di menu.
   ============================================================================= */

.ui-dropdown-menu--filtri {
    min-width: 280px;
    max-height: min(70vh, 560px);
    overflow-y: auto;
}

/* La riga dei due anni. `label` inline e non sopra: sono due parole («Dal»,
   «al») e sopra il campo occuperebbero due righe per dire quanto una. */
.ui-dropdown-riga {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 8px 8px;
}

.ui-dropdown-riga .ui-select { flex: 1 1 0; min-width: 0; }

.ui-dropdown-riga .ui-form-label { margin: 0; white-space: nowrap; }

/* Il conteggio accanto al titolo del gruppo: dentro un menu lungo le voci
   scelte possono restare fuori campo, e il titolo si vede sempre. */
.ui-dropdown-header .ui-dropdown-conta {
    margin-left: 6px;
    padding: 1px 5px;
    border-radius: var(--radius-sm);
    background: var(--princeton);
    color: var(--jet);
    font-size: 9px;
    font-weight: 700;
    letter-spacing: 0;
}

/* La spunta compare solo sulla voce scelta: sempre visibile sarebbe un elenco
   di caselle tutte uguali, e lo stato si leggerebbe solo dal fondo. */
.ui-dropdown-item[data-filtro] > i { opacity: 0; }

.ui-dropdown-item[data-filtro].active > i { opacity: 1; }

/* Un comando dentro una frase. Non e' un bottone: sta in mezzo al testo di un
   avviso, e un bottone li' spezzerebbe la riga. */
.ui-link-inline {
    background: none;
    border: 0;
    padding: 0;
    font: inherit;
    color: inherit;
    text-decoration: underline;
    text-underline-offset: 2px;
    cursor: pointer;
}

.ui-link-inline:hover { text-decoration-thickness: 2px; }


/* ============================================================================
   MOVIMENTO RIDOTTO

   Chi lo ha chiesto al sistema operativo lo ha chiesto per un motivo. Le
   animazioni non vengono azzerate del tutto: uno spinner fermo dice «bloccato»
   invece di «sto lavorando», e una barra indeterminata ferma resta fuori campo,
   cioè invisibile.
   ============================================================================ */

@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;
    }

    .bi-spin,
    .ui-animate-spin {
        animation-duration: 2.4s !important;
        animation-iteration-count: infinite !important;
    }

    .ui-progress-fill.indeterminate {
        width: 100% !important;
        transform: none !important;
    }
}


/* ── Calendario: il tema di flatpickr ───────────────────────────────────────
   Portato da Spark il 2026-08-25, riga per riga. Veste la libreria
   `flatpickr@4.6.13`, che `views/layouts/header.php` carica sulla sola pagina
   dell'applicazione — login e pannello admin non hanno campi data.

   ⚠️ L'ORDINE DEI FOGLI E' LA CONDIZIONE. `flatpickr.min.css` deve stare PRIMA
   di `styles.css` e di questo: a parita' di specificita' vince l'ultimo, e se
   la libreria si carica dopo il calendario torna viola e con gli angoli suoi.

   ⚠️ QUI IL CAMPO NON SCRIVE `d/m/Y`. Spark lascia che flatpickr riscriva il
   campo nel formato italiano; qui i quattro campi data stanno dentro moduli
   che il JS legge con `.value` e spedisce a `api/`, che vuole `Y-m-d`. Percio'
   l'inizializzazione usa `altInput`: la data italiana la mostra un campo
   gemello, l'originale resta in ISO e nessuna vista cambia. Vedi
   `initCampiData()` in `assets/js/app.js`.

   Le regole di `inRange`/`startRange`/`endRange` vestono la selezione a
   intervallo, che oggi qui non ha markup: restano perche' questo foglio e' la
   copia di Spark e si riporta intero, non a pezzi. Il componente
   `.ui-daterange-picker` e i suoi preset invece NON sono stati portati — quelli
   sono markup, e markup senza chiamanti e' un componente che sembra
   disponibile e non lo e'.
   ───────────────────────────────────────────────────────────────────────── */

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

/* Datepicker input styling */
.ui-datepicker,
.ui-daterange-picker {
    padding-left: 36px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='%23888888'%3E%3Cpath d='M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5zM1 4v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V4H1z'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: 12px center;
    cursor: pointer;
}

.ui-datepicker:focus,
.ui-daterange-picker:focus {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='%233D458D'%3E%3Cpath d='M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5zM1 4v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V4H1z'/%3E%3C/svg%3E");
}

.ui-datepicker.active,
.ui-daterange-picker.active {
    border-color: var(--primary);
}

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

.flatpickr-calendar.arrowTop:before,
.flatpickr-calendar.arrowTop:after {
    border-bottom-color: var(--border);
}

.flatpickr-calendar.arrowBottom:before,
.flatpickr-calendar.arrowBottom:after {
    border-top-color: var(--border);
}

/* Month navigation */
.flatpickr-months {
    padding: 8px 4px 4px;
}

.flatpickr-months .flatpickr-month {
    height: 36px;
}

.flatpickr-current-month {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-heading);
    padding-top: 6px;
}

.flatpickr-current-month .flatpickr-monthDropdown-months {
    font-weight: 600;
    color: var(--text-heading);
    background: transparent;
}

.flatpickr-current-month .flatpickr-monthDropdown-months:hover {
    background: var(--primary-light);
}

.flatpickr-current-month input.cur-year {
    font-weight: 600;
    color: var(--text-heading);
}

.flatpickr-months .flatpickr-prev-month,
.flatpickr-months .flatpickr-next-month {
    padding: 8px 10px;
    border-radius: var(--radius-sm);
    transition: background-color 0.15s ease;
}

.flatpickr-months .flatpickr-prev-month:hover,
.flatpickr-months .flatpickr-next-month:hover {
    background: var(--primary-light);
}

.flatpickr-months .flatpickr-prev-month svg,
.flatpickr-months .flatpickr-next-month svg {
    fill: var(--primary);
}

.flatpickr-months .flatpickr-prev-month:hover svg,
.flatpickr-months .flatpickr-next-month:hover svg {
    fill: var(--primary-dark);
}

/* Weekdays header */
.flatpickr-weekdays {
    background: transparent;
    padding: 0 8px;
}

.flatpickr-weekday {
    font-size: 10px;
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

/* Days grid */
.flatpickr-days {
    padding: 4px 8px 8px;
}

.dayContainer {
    min-width: 280px;
    max-width: 280px;
}

.flatpickr-day {
    font-size: 13px;
    font-weight: 500;
    color: var(--text-body);
    border-radius: var(--radius-sm);
    height: 36px;
    line-height: 36px;
    max-width: 36px;
    margin: 1px;
    border: none;
    transition: background-color 0.15s ease, color 0.15s ease;
}

.flatpickr-day:hover {
    background: var(--primary-light);
    border-color: transparent;
    color: var(--primary);
}

.flatpickr-day.today {
    border: 1px solid var(--primary);
    background: transparent;
    color: var(--primary);
    font-weight: 600;
}

.flatpickr-day.today:hover {
    background: var(--primary-light);
    color: var(--primary);
}

.flatpickr-day.selected,
.flatpickr-day.startRange,
.flatpickr-day.endRange,
.flatpickr-day.selected.inRange,
.flatpickr-day.startRange.inRange,
.flatpickr-day.endRange.inRange,
.flatpickr-day.selected:focus,
.flatpickr-day.startRange:focus,
.flatpickr-day.endRange:focus,
.flatpickr-day.selected:hover,
.flatpickr-day.startRange:hover,
.flatpickr-day.endRange:hover,
.flatpickr-day.selected.prevMonthDay,
.flatpickr-day.startRange.prevMonthDay,
.flatpickr-day.endRange.prevMonthDay,
.flatpickr-day.selected.nextMonthDay,
.flatpickr-day.startRange.nextMonthDay,
.flatpickr-day.endRange.nextMonthDay {
    background: var(--primary);
    border-color: var(--primary);
    color: #fff;
}

.flatpickr-day.inRange,
.flatpickr-day.prevMonthDay.inRange,
.flatpickr-day.nextMonthDay.inRange,
.flatpickr-day.today.inRange,
.flatpickr-day.prevMonthDay.today.inRange,
.flatpickr-day.nextMonthDay.today.inRange,
.flatpickr-day:hover,
.flatpickr-day.prevMonthDay:hover,
.flatpickr-day.nextMonthDay:hover,
.flatpickr-day:focus,
.flatpickr-day.prevMonthDay:focus,
.flatpickr-day.nextMonthDay:focus {
    border-color: transparent;
}

.flatpickr-day.inRange {
    background: rgba(28, 40, 38, 0.06);
    color: var(--primary-dark);
}

.flatpickr-day.prevMonthDay,
.flatpickr-day.nextMonthDay,
.flatpickr-day.notAllowed,
.flatpickr-day.notAllowed.prevMonthDay,
.flatpickr-day.notAllowed.nextMonthDay {
    color: var(--text-light);
}

.flatpickr-day.flatpickr-disabled,
.flatpickr-day.flatpickr-disabled:hover {
    color: var(--text-light);
    opacity: 0.5;
    background: transparent;
}

/* Time picker */
.flatpickr-time {
    border-top: 1px solid var(--border);
    padding: 8px;
}

.flatpickr-time input {
    font-size: 14px;
    font-weight: 500;
    color: var(--text-heading);
}

.flatpickr-time input:hover,
.flatpickr-time input:focus {
    background: var(--primary-light);
}

.flatpickr-time .flatpickr-time-separator,
.flatpickr-time .flatpickr-am-pm {
    color: var(--text-muted);
    font-weight: 500;
}

.flatpickr-time .flatpickr-am-pm:hover {
    background: var(--primary-light);
    color: var(--primary);
}

.numInputWrapper span {
    border-color: var(--border);
}

.numInputWrapper span:hover {
    background: var(--primary-light);
}

.numInputWrapper span:after {
    border-color: var(--text-muted) transparent;
}

.numInputWrapper span:hover:after {
    border-color: var(--primary) transparent;
