

/* 3.44.10 — VIA I RIPIEGHI DAI var(). Nel file c'erano 119 scritture del tipo
   var(--token, valore-di-scorta) e diverse davano allo STESSO token colori diversi:
   --accent ripiegava su #6366f1 in un punto, #4f46e5 in un altro, #374b5f altrove (il
   valore vero e #374b5f); --danger su #dc2626 oppure #ef4444; --success su #16a34a oppure
   #22c55e; --radius-sm (12px) su 14px o 8px. Erano copie disallineate che non entravano
   mai in gioco, perche i token esistono tutti qui sotto. Peggio: nascondevano gli errori
   di battitura, come ha dimostrato --border (scritto male e senza scorta, quindi invisibile
   fino a rompersi a schermo). Ora si scrive var(--token) e basta: se il token manca, si
   vede subito. Restano con ripiego solo i token NON definiti qui, cioe quelli impostati a
   run-time su singoli elementi (--chat-canvas, --chat-in-bg, --fade-dist), dove la scorta
   e un comportamento voluto. */
:root {

    --bg-primary: #e8edf6;
    --bg-secondary: #eef2f9;
    --bg-tertiary: rgba(110,125,160,0.10);
    --bg-card: rgba(255,255,255,0.60);
    --bg-hover: rgba(110,125,160,0.13);
    --bg-input: rgba(255,255,255,0.58);
    --border-color: rgba(20,28,50,0.10);
    --border-light: rgba(20,28,50,0.16);
    --text-primary: #1b1f30;
    --text-secondary: #535976;
    --text-muted: #5f6478; /* 3.43.48: scurito da #868ca6 (~3,3:1) per superare il contrasto AA (~4,6:1) sul testo piccolo */
    --accent: #374b5f;
    --accent-hover: #2b3a49;
    --accent-soft: rgba(55,75,95,0.14);
    --accent-light: #81c8fe;
    --accent-glow: rgba(129,200,254,0.45);
    --success: #16a34a;
    --success-soft: rgba(34,197,94,0.14);
    --warning: #d97706;
    --warning-soft: rgba(245,158,11,0.16);
    --danger: #dc2626;
    --danger-soft: rgba(239,68,68,0.14);
    --info: #0891b2;
    --info-soft: rgba(6,182,212,0.14);
    --pink: #db2777;
    --pink-soft: rgba(236,72,153,0.14);
    --sidebar-width: 260px;
    --header-height: 64px;

    /* ======================================================================
       3.44.11 — SISTEMA DI DESIGN
       Prima di questa versione non esisteva una scala: 24 corpi di carattere
       diversi (fino a 6px), 35 spaziature, 16 raggi, 40 ombre, 17 livelli di
       sovrapposizione e 10 soglie responsive, tutti scritti a mano volta per
       volta. Elementi con lo stesso ruolo finivano con misure diverse a
       seconda di chi li aveva scritti e quando.
       Ora c'e' una sola sorgente, allineata alle linee guida Apple per iPhone
       (Human Interface Guidelines): stessa scala tipografica, griglia da 8pt,
       bersagli tattili da 44pt, angoli e ombre coerenti.
       ====================================================================== */

    /* --- Tipografia: gli 11 stili di testo iOS ---------------------------
       Nomi e valori sono quelli Apple alla dimensione predefinita ("Large").
       Ogni corpo ha la sua interlinea accanto. Le interlinee FISSE sono applicate
       ai titoli, dove contano davvero perche' il testo e' grande e su poche righe.
       Per il testo corrente si usa invece un rapporto (1,5 su body): su schermo
       largo un'interlinea assoluta stringerebbe troppo i paragrafi lunghi, che a
       differenza di iOS qui possono occupare tutta la riga. Le cinque --lh- del
       testo restano definite come riferimento della scala, anche dove non servono. */
    --fs-large-title: 34px;  --lh-large-title: 41px;
    --fs-title-1:     28px;  --lh-title-1:     34px;
    --fs-title-2:     22px;  --lh-title-2:     28px;
    --fs-title-3:     20px;  --lh-title-3:     25px;
    --fs-headline:    17px;  --lh-headline:    22px;   /* = corpo, ma semibold */
    --fs-body:        17px;  --lh-body:        22px;
    --fs-callout:     16px;  --lh-callout:     21px;
    --fs-subhead:     15px;  --lh-subhead:     20px;
    --fs-footnote:    13px;  --lh-footnote:    18px;
    --fs-caption-1:   12px;  --lh-caption-1:   16px;
    --fs-caption-2:   11px;  --lh-caption-2:   13px;   /* minimo leggibile Apple */
    /* Pesi: Apple usa Regular per il testo, Semibold per i titoli di riga. */
    --fw-regular: 400;
    --fw-medium: 500;
    --fw-semibold: 600;
    --fw-bold: 700;
    /* Glifi puramente decorativi (pallini di stato, icone grandi degli stati
       vuoti). Non sono testo: il minimo di 11px non li riguarda, ma vanno
       comunque presi da qui e non inventati ogni volta. */
    --fs-dot: 8px;
    /* 3.44.12: --icon-lg mancava del tutto pur essendo usato da .cloud-item-icon sotto i
       768px. La dichiarazione era invalida e l'icona ereditava la misura del testo (17px)
       invece della sua: nell'elenco Cloud su telefono si vedeva un'icona minuscola. */
    --icon-lg: 32px;
    --icon-xl: 40px;
    --icon-2xl: 48px;

    /* --- Spaziature: griglia da 8pt, con mezzi passi da 4 ---------------- */
    --sp-05: 2px;
    --sp-1:  4px;
    --sp-2:  8px;
    --sp-3:  12px;
    --sp-4:  16px;   /* margine standard del contenuto su iPhone */
    --sp-5:  20px;
    --sp-6:  24px;
    --sp-8:  32px;
    --sp-10: 40px;
    --sp-12: 48px;
    --sp-16: 64px;

    /* --- Angoli --------------------------------------------------------- */
    --radius: 16px;      /* schede */
    --radius-sm: 12px;   /* campi, pulsanti */
    --radius-xs: 8px;    /* controlli piccoli */
    --radius-lg: 20px;   /* finestre e pannelli */
    --radius-full: 999px;/* pillole ed etichette */

    /* --- Controlli: 44pt e' il bersaglio tattile minimo delle HIG -------- */
    --control-h: 44px;
    --control-h-sm: 36px;
    --tap-min: 44px;
    /* 3.44.10: --font-mono era invocato ma mai definito: il monospazio cadeva sul ripiego
       generico del sistema. JetBrains Mono e gia scaricato da index.php e finora inutilizzato. */
    --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
    /* 3.44.10: inchiostri semantici per il TESTO. I colori pieni (--success, --warning, ...)
       restano per barre, punti e icone dove bastano 3:1, ma su fondo chiaro o -soft non
       arrivano a 4,5:1 (il piu debole, --warning, si ferma a 2,68:1). Queste varianti scure
       servono solo quando il colore semantico fa da testo.
       Valori scelti misurando il contrasto sul fondo reale (il -soft steso sopra --bg-card
       sopra --bg-primary), non sul bianco puro: sul bianco sarebbero passati anche toni piu
       chiari. Rapporti ottenuti: verde 5,10:1 - ambra 4,82:1 - ciano 4,98:1 - rosa 4,79:1 -
       rosso 5,08:1, tutti sopra il 4,5:1 richiesto dal testo piccolo. */
    --success-ink: #14713a;
    --warning-ink: #955706;
    --info-ink: #0f6c85;
    --pink-ink: #be185d;
    --danger-ink: #b91c1c;
    /* 3.44.15: due inchiostri che mancavano, ed e' il motivo per cui alcune pastiglie erano
       rimaste col colore pieno come testo — quello che non si legge. Il verde acqua (etichetta
       "servizio") e il grigio dello stato "Disdetto" hanno ora anche loro la variante scura.
       Misurati sullo stesso fondo reale degli altri: verde acqua 4,71:1, grigio 6,16:1. */
    --teal-ink: #0f766e;
    --slate-ink: #475569;

    --glass-bg: rgba(255,255,255,0.55);
    --glass-bg-strong: rgba(255,255,255,0.74);
    /* 3.44.10: --glass-border era usato ma mai definito, quindi ogni bordo "vetro" cadeva
       sulla scorciatoia letterale. NON lo si puo sostituire con --border-color: quello e un
       filo scuro rgba(20,28,50,0.10), questo e un riflesso bianco sopra il vetro. Sono due
       cose diverse, quindi il token va definito, non rimpiazzato. */
    --glass-border: rgba(255,255,255,0.45);
    --glass-blur: saturate(180%) blur(22px);
    --glass-highlight: inset 0 1px 0 rgba(255,255,255,0.65);
    /* --- Ombre: cinque livelli, una sola tinta ---------------------------
       C'erano 40 ricette diverse mescolando quattro tinte (rgba 40/52/110,
       30/40/90, 15/23/42, nero puro): ombre affiancate avevano temperature
       diverse. Ora la tinta e' una sola e cambia solo la distanza. */
    --shadow-xs: 0 1px 3px rgba(40,52,110,0.08);
    --shadow-sm: 0 3px 16px rgba(40,52,110,0.10);
    --shadow: 0 10px 40px rgba(40,52,110,0.16);
    --shadow-lg: 0 16px 48px rgba(40,52,110,0.22);
    --shadow-xl: 0 24px 70px rgba(40,52,110,0.28);

    /* --- Sovrapposizioni: una scala al posto di 17 numeri sparsi --------- */
    --z-sticky: 100;
    --z-dropdown: 300;
    --z-drawer: 800;
    --z-modal: 1000;
    --z-popover: 1200;
    --z-toast: 1400;
    --z-urgent: 1600;

    /* --- Larghezze delle finestre ----------------------------------------
       Erano 12 misure diverse scritte a mano nel markup. */
    --modal-sm: 460px;
    --modal-md: 560px;
    --modal-lg: 720px;
    --modal-xl: 900px;
    /* 3.44.7 — SISTEMA UNICO DEL MOVIMENTO.
       Prima l'app usava 17 durate diverse (da 0,1s a 0,6s) e piu di meta delle transizioni
       non dichiarava alcuna curva: ogni schermata si muoveva con un ritmo suo. Ora ogni
       transizione passa da questi tre tempi e da un'unica curva. */
    --dur-fast: 120ms;   /* micro-stati: colore, sfondo, bordo, opacita */
    --dur-base: 200ms;   /* predefinito: passaggio del mouse, focus, comparse */
    --dur-slow: 300ms;   /* elementi grandi: finestre, pannelli, barre di avanzamento */
    --ease: cubic-bezier(0.4, 0, 0.2, 1);
    --ease-out: cubic-bezier(0.16, 1, 0.3, 1);   /* comparsa: parte decisa e si posa */
    --ease-in: cubic-bezier(0.4, 0, 1, 1);       /* scomparsa: accelera e va via */
    /* Elenco esplicito al posto di "all": "all" anima anche larghezza, altezza e spaziature,
       che al passaggio del mouse producono scatti e ricalcoli di impaginazione. */
    --dur-spin: 800ms;   /* giro completo di ogni indicatore di attesa */
    --transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), opacity var(--dur-fast) var(--ease), box-shadow var(--dur-base) var(--ease), transform var(--dur-base) var(--ease), filter var(--dur-base) var(--ease);

    /* Skeleton loader (liquid glass): base = sagoma traslucida, hi = riflesso dello shimmer */
    --sk-base: rgba(110,125,160,0.16);
    --sk-hi: rgba(255,255,255,0.70);
}

/* 3.44.7: definizione unica della girandola, usata da .spin, .btn-loading e .prot-spin. */
@keyframes mfSpin { to { transform: rotate(360deg); } }

* { margin:0; padding:0; box-sizing: border-box; }

body {
    font-family: 'DM Sans', -apple-system, sans-serif;
    background:
        radial-gradient(circle at 12% 18%, rgba(55,75,95,0.18), transparent 42%),
        radial-gradient(circle at 88% 12%, rgba(236,72,153,0.14), transparent 42%),
        radial-gradient(circle at 78% 88%, rgba(6,182,212,0.14), transparent 45%),
        radial-gradient(circle at 22% 92%, rgba(34,197,94,0.10), transparent 45%),
        linear-gradient(135deg, #e6ecf7 0%, #f2f5fb 45%, #e7eef8 100%);
    background-attachment: fixed;
    color: var(--text-primary);
    overflow-x: hidden;
    /* 3.44.11: il testo predefinito passa a Body (17px), la misura che Apple indica come
       corpo del testo su iPhone. Prima era 14px: sotto Subhead, e con elenchi e tabelle che
       scendevano fino a 9-10px. Chi ha bisogno di una misura minore la chiede esplicitamente
       con Subhead, Footnote o Caption; sotto Caption 2 (11px) non si scende piu'. */
    font-size: var(--fs-body);
    line-height: 1.5;
    min-height: 100vh;
    min-height: 100dvh; /* iOS: dvh esclude la barra URL dinamica */
}

::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border-color); border-radius:var(--radius-xs); }
::-webkit-scrollbar-thumb:hover { background: var(--border-light); }

.login-page {
    min-height: 100vh;
    min-height: 100dvh;
    padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bg-primary);
    position: relative;
    overflow: hidden;
}

.login-page::before {
    content: '';
    position: absolute;
    width: 600px; height: 600px;
    border-radius:50%;
    background: radial-gradient(circle, rgba(55,75,95,0.12) 0%, transparent 70%);
    top: -200px; right: -200px;
}

.login-page::after {
    content: '';
    position: absolute;
    width: 500px; height: 500px;
    border-radius:50%;
    background: radial-gradient(circle, rgba(236,72,153,0.08) 0%, transparent 70%);
    bottom: -150px; left: -150px;
}

.login-card {
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius:var(--radius-lg);
    padding:var(--sp-12);
    width: 100%;
    max-width: 420px;
    position: relative;
    z-index: 1;
    box-shadow: var(--shadow);
}

.login-logo {
    display: flex;
    align-items: center;
    gap:var(--sp-3);
    margin-bottom:var(--sp-8);
    justify-content: center;
}

.login-logo .logo-icon {
    width: 48px; height: 48px;
    background: linear-gradient(135deg, var(--accent), var(--pink));
    border-radius:var(--radius);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size:var(--fs-title-2); line-height: var(--lh-title-2);
    color: white;
}

.login-logo h1 {
    font-size:var(--fs-title-2); line-height: var(--lh-title-2);
    font-weight: var(--fw-bold);
    background: linear-gradient(135deg, var(--text-primary), var(--accent));
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
}

.login-card h2 {
    text-align: center;
    margin-bottom:var(--sp-2);
    font-size:var(--fs-title-3); line-height: var(--lh-title-3);
    font-weight: var(--fw-semibold);
}

.login-card .subtitle {
    text-align: center;
    color: var(--text-secondary);
    margin-bottom:var(--sp-8);
    font-size:var(--fs-subhead);
}

.form-group { margin-bottom:var(--sp-5); }

.form-group label {
    display: block;
    font-weight: var(--fw-medium);
    margin-bottom:var(--sp-2);
    font-size:var(--fs-footnote);
    color: var(--text-secondary);
}

.form-control, .form-select {
    width: 100%;
    padding:var(--sp-3) var(--sp-4);
    background: var(--bg-input);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    color: var(--text-primary);
    font-family: inherit;
    font-size:var(--fs-subhead);
    transition: var(--transition);
    outline: none;
}

/* 3.44.15: bordo di messa a fuoco leggibile — vedi la nota sulla regola gemella piu' sotto. */
.form-control:focus, .form-select:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-glow);
}

.form-control::placeholder { color: var(--text-muted); }

textarea.form-control { resize: vertical; min-height: 80px; }

/* 3.44.2: icona Passkey (immagine personalizzata) usata inline nei pulsanti e negli elenchi */
.pk-ico { width: 1.2em; height: 1.2em; border-radius:var(--radius-xs); vertical-align: -0.25em; margin-right:var(--sp-2); flex-shrink: 0; }

/* ==================== 3.44.4: schermata 2FA animata (stile riferimento video) ==================== */
.tfa-stage { position: relative; }
.tfa-row { position: relative; display: flex; gap:var(--sp-3); justify-content: center; padding:var(--sp-2) 0; cursor: text; }
.tfa-tile {
    width: 46px; height: 56px; border-radius:var(--radius-sm);
    background: var(--bg-input); border: 1.5px solid var(--border-color);
    display: flex; align-items: center; justify-content: center;
    font-size:var(--fs-title-2); line-height: var(--lh-title-2); font-weight: var(--fw-bold); color: var(--text-primary);
    transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
    position: relative; will-change: transform;
}
.tfa-tile.active { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-glow); }
.tfa-tile.filled { border-color: var(--accent-light); }
.tfa-tile.active:empty::after {
    content: ''; width: 2px; height: 24px; background: var(--accent);
    animation: tfa-caret 1s steps(1) infinite;
}
@keyframes tfa-caret { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }
/* input reale trasparente sopra la riga: cattura tastiera e autofill del codice */
.tfa-ghost-input {
    position: absolute; inset: 0; width: 100%; height: 100%;
    opacity: 0; border: none; background: transparent; color: transparent;
    caret-color: transparent; outline: none; font-size:var(--fs-callout); /* >=16px: niente zoom iOS */
}
/* fase verifica (3.44.6): moto COORDINATO guidato da requestAnimationFrame come nel riferimento —
   le tessere si raccolgono in un anello e orbitano insieme attorno al centro (girandola con raggio
   che respira), poi al successo si impilano al centro. Qui solo lo stato visivo delle tessere. */
.tfa-tile.orbit { border-color: var(--accent); z-index: 2; transition: none; }
.tfa-tile.digit-fade { color: transparent !important; transition: color var(--dur-slow) var(--ease); }
.tfa-tile.stacked { opacity: 0; transition: opacity var(--dur-slow) var(--ease); }
/* errore: la riga trema, le tessere si tingono di rosso */
.tfa-row.shake { animation: tfa-shake .45s ease; }
.tfa-row.shake .tfa-tile { border-color: var(--danger); }
@keyframes tfa-shake {
    0%, 100% { transform: translateX(0); }
    20% { transform: translateX(-9px); } 40% { transform: translateX(8px); }
    60% { transform: translateX(-6px); } 80% { transform: translateX(4px); }
}
/* successo: check in tessera verde + cerchio che si disegna + bagliore */
.tfa-check-wrap { position: relative; width: 120px; height: 120px; margin:0 auto; }
.tfa-circle { position: absolute; inset: 0; transform: rotate(-90deg); }
.tfa-circle circle { stroke-dasharray: 339.3; stroke-dashoffset: 339.3; animation: tfa-draw .8s ease forwards .2s; opacity: .8; }
@keyframes tfa-draw { to { stroke-dashoffset: 0; } }
.tfa-check {
    position: absolute; top: 50%; left: 50%; width: 52px; height: 52px;
    border-radius:var(--radius); background: var(--bg-input); border: 2px solid var(--success);
    color: var(--success-ink); font-size:var(--fs-title-1); line-height: var(--lh-title-1);
    display: flex; align-items: center; justify-content: center;
    transform: translate(-50%, -50%) scale(0); animation: tfa-pop .45s cubic-bezier(.3, 1.6, .5, 1) forwards .1s;
}
@keyframes tfa-pop { to { transform: translate(-50%, -50%) scale(1); } }
.login-card.tfa-ok { box-shadow: 0 0 90px -18px rgba(34, 197, 94, .55), var(--shadow); transition: box-shadow var(--dur-slow) var(--ease); }  /* 3.44.10: era .6s a mano, unica durata fuori dai token */

/* 3.43.57: input file — il controllo nativo dentro .form-control risultava disallineato
   (pulsante di sistema attaccato al bordo, testo fuori baseline). Pulsante ristilizzato
   coerente con l'app e allineamento verticale corretto. */
input[type="file"].form-control {
    padding:var(--sp-2) var(--sp-3);
    line-height: 1.5;
    cursor: pointer;
    display: flex;
    align-items: center;
}
input[type="file"].form-control::file-selector-button {
    border: 1px solid var(--border-color);
    background: var(--bg-secondary);
    color: var(--text-primary);
    font-family: inherit;
    font-size:var(--fs-footnote);
    font-weight: var(--fw-semibold);
    padding:var(--sp-1) var(--sp-4); /* 3.43.58: il pulsante (29px) superava lo spazio interno del campo (26px) e traboccava in alto */
    border-radius:var(--radius-xs);
    margin-right:var(--sp-3);
    cursor: pointer;
    transition: var(--transition);
}
input[type="file"].form-control::file-selector-button:hover { background: var(--bg-tertiary); border-color: var(--accent-light); }
input[type="file"].form-control::-webkit-file-upload-button {
    border: 1px solid var(--border-color);
    background: var(--bg-secondary);
    color: var(--text-primary);
    font-family: inherit;
    font-size:var(--fs-footnote);
    font-weight: var(--fw-semibold);
    padding:var(--sp-1) var(--sp-4);
    border-radius:var(--radius-xs);
    margin-right:var(--sp-3);
    cursor: pointer;
    transition: var(--transition);
}

/* Gli input data su iOS ignorano width e appaiono più larghi: li normalizziamo */
input[type="date"].form-control {
    -webkit-appearance: none;
    appearance: none;
    box-sizing: border-box;
    min-width: 0;
}

.form-select {
    appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='%239399ad' viewBox='0 0 16 16'%3E%3Cpath d='M8 11L3 6h10l-5 5z'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 12px center;
    padding-right:var(--sp-10);
}

.form-select option { background: var(--bg-secondary); color: var(--text-primary); }

/* 3.44.10 — ALTEZZA UNICA DEI PULSANTI. .btn non dichiarava un'altezza: la ricavava dal
   padding. Ma .btn-primary e .btn-secondary aggiungono un bordo di 1px e .btn-danger,
   .btn-success e .btn-ghost no, quindi con box-sizing:border-box due pulsanti affiancati
   misuravano 38,8px e 36,8px: nella stessa fila non erano allineati. Ora l'altezza e'
   fissata qui e il bordo e' sempre presente, trasparente dove non serve, cosi' le varianti
   non possono piu' cambiare l'ingombro. 40px e' la stessa altezza gia' usata dai campi del
   modulo, quindi finalmente pulsanti e campi si allineano tra loro.
   Il padding verticale va a 0: con altezza fissa e align-items:center il testo si centra da
   solo, mentre un padding verticale residuo entrerebbe in conflitto con l'altezza. Restano
   i 18px orizzontali, che danno il respiro laterale. */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap:var(--sp-2);
    /* 3.44.11: 44px e' il bersaglio tattile minimo delle HIG Apple; il testo sale a Callout
       (16px) per stare in proporzione. Prima 40px con testo a 14px. */
    height: var(--control-h);
    padding:0 var(--sp-5);
    border-radius: var(--radius-sm);
    font-family: inherit;
    font-size: var(--fs-callout);
    font-weight: var(--fw-medium);
    border: 1px solid transparent;
    cursor: pointer;
    transition: var(--transition);
    text-decoration: none;
    line-height: 1.2;
    white-space: nowrap;
}

.btn-primary {
    background: linear-gradient(135deg, rgba(55,75,95,0.82), rgba(55,75,95,0.66));
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border: 1px solid rgba(255,255,255,0.22);
    color: #fff;
    box-shadow: var(--shadow-sm), inset 0 1px 0 rgba(255,255,255,0.20);
}
.btn-primary:hover {
    background: linear-gradient(135deg, rgba(55,75,95,0.94), rgba(55,75,95,0.80));
    transform: translateY(-1px);
}

.btn-primary:active { transform: translateY(0); }

.btn-secondary {
    background: rgba(255,255,255,0.6);
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
    color: var(--text-primary);
    border: 1px solid var(--border-color);
}
.btn-secondary:hover { background: rgba(255,255,255,0.85); border-color: var(--border-light); }

.btn-danger { background: var(--danger-soft); color: var(--danger-ink); }
.btn-danger:hover { background: var(--danger); color: white; }

.btn-success { background: var(--success-soft); color: var(--success-ink); }

.btn-ghost {
    background: transparent;
    color: var(--text-secondary);
    /* 3.44.10: solo padding orizzontale, l'altezza la impone .btn */
    padding:0 var(--sp-3);
}
.btn-ghost:hover { background: var(--bg-tertiary); color: var(--text-primary); }

/* 3.44.10: unita all'altezza fissa invece di crearne una seconda regola: .btn-sm aveva
   gia' padding e corpo suoi, qui si aggiunge solo l'altezza corrispondente. */
.btn-sm { height: var(--control-h-sm); padding:0 var(--sp-3); font-size:var(--fs-footnote); }
.btn-lg { padding:var(--sp-3) var(--sp-6); font-size:var(--fs-subhead); }
.btn-block { width: 100%; justify-content: center; }
.btn-icon { padding:var(--sp-2); border-radius: var(--radius-xs); }

.app-wrapper { display: flex; min-height: 100vh; min-height: 100dvh; }

.sidebar {
    width: var(--sidebar-width);
    background: var(--bg-secondary);
    border-right: 1px solid var(--border-color);
    display: flex;
    flex-direction: column;
    position: fixed;
    top: 0; left: 0; bottom: 0;
    z-index: var(--z-sticky);
    transition: transform var(--dur-slow) var(--ease);
}

.sidebar-header {
    padding:var(--sp-5);
    border-bottom: 1px solid var(--border-color);
    display: flex;
    align-items: center;
    gap:var(--sp-3);
    min-height: 64px;
}

.sidebar-header .logo-icon {
    width: 38px; height: 38px;
    background: linear-gradient(135deg, var(--accent), var(--pink));
    border-radius:var(--radius-sm);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size:var(--fs-title-3); line-height: var(--lh-title-3);
    color: white;
    flex-shrink: 0;
}

.sidebar-header h1 {
    font-size:var(--fs-body);
    font-weight: var(--fw-bold);
    background: linear-gradient(135deg, var(--text-primary), var(--accent));
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
}

.sidebar-nav { flex: 1; padding:var(--sp-3); overflow-y: auto; }

.nav-section {
    margin-bottom:var(--sp-6);
}

.nav-section-title {
    font-size:var(--fs-caption-2);
    font-weight: var(--fw-semibold);
    text-transform: uppercase;
    letter-spacing: 0.8px;
    color: var(--text-muted);
    padding:0 var(--sp-3);
    margin-bottom:var(--sp-2);
}

.nav-item {
    display: flex;
    align-items: center;
    gap:var(--sp-3);
    padding:var(--sp-2) var(--sp-3);
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    cursor: pointer;
    transition: var(--transition);
    font-size:var(--fs-subhead);
    font-weight: var(--fw-regular);
    position: relative;
    margin-top:var(--sp-05);
}

.nav-item:hover { background: var(--bg-hover); color: var(--text-primary); }

.nav-item.active {
    background: var(--accent-soft);
    color: var(--accent);
    font-weight: var(--fw-medium);
}

.nav-item .nav-icon { width: 20px; text-align: center; font-size:var(--fs-callout); }

.nav-item .badge-count {
    margin-left:auto;
    background: var(--bg-tertiary);
    color: var(--text-muted);
    font-size:var(--fs-caption-2);
    padding:var(--sp-05) var(--sp-2);
    border-radius:var(--radius-sm);
    font-weight: var(--fw-medium);
}

.nav-item.active .badge-count { background: var(--accent); color: white; }
/* 3.44.12: il contatore dei messaggi non letti era rosso #ef4444 qui e --accent nell'elenco
   conversazioni: stesso numero, stessa pagina, due colori. #ef4444 per giunta non e' nemmeno il
   rosso dell'applicazione (--danger e' #dc2626): era una terza tinta arrivata da fuori. Ora
   entrambi i contatori usano --danger, cosi' "da leggere" ha un colore solo. */
.nav-item .chat-nav-badge {
    background: var(--danger); color: #fff; font-weight: var(--fw-bold);
    min-width: 20px; height: 20px; padding:0 var(--sp-1);
    border-radius:var(--radius-full);
    display: inline-flex; align-items: center; justify-content: center;
    box-sizing: border-box; line-height: 1; font-size:var(--fs-caption-2);
}
.nav-item.active .chat-nav-badge { background: var(--danger); color: #fff; }

.nav-project-dot {
    width: 8px; height: 8px;
    border-radius:50%;
    flex-shrink: 0;
}

.sidebar-footer {
    padding:var(--sp-4);
    border-top: 1px solid var(--border-color);
}

.user-profile {
    display: flex;
    align-items: center;
    gap:var(--sp-3);
    padding:var(--sp-2);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: var(--transition);
}

.user-profile:hover { background: var(--bg-hover); }

.avatar {
    width: 34px; height: 34px;
    border-radius:var(--radius-sm);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size:var(--fs-footnote);
    font-weight: var(--fw-semibold);
    color: white;
    /* 3.44.10: le iniziali sono bianche su una tavolozza di sfondi generata altrove, e su 8
       colori su 11 il bianco non regge il contrasto (il giallo #f59e0b si ferma a 2,15:1).
       La tavolozza vive in index.php e da qui non si puo' toccare: l'ombra stacca le lettere
       da qualunque fondo capiti sotto. Non si tocca color: il bianco resta corretto sui
       colori scuri della tavolozza. font-weight e' gia' 600, quindi resta com'e'. */
    text-shadow: 0 1px 2px rgba(0,0,0,0.45);
    flex-shrink: 0;
}

.avatar-sm { width: 28px; height: 28px; font-size:var(--fs-caption-2); border-radius:var(--radius-xs); }
.avatar-lg { width: 42px; height: 42px; font-size:var(--fs-callout); border-radius:var(--radius-sm); }
.avatar-xl { width: 56px; height: 56px; font-size:var(--fs-title-3); line-height: var(--lh-title-3); border-radius:var(--radius); }

.user-profile .user-info { flex: 1; min-width: 0; }
.user-profile .user-name { font-weight: var(--fw-medium); font-size:var(--fs-footnote); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.user-profile .user-role { font-size:var(--fs-caption-2); color: var(--text-muted); }

.main-content {
    margin-left: var(--sidebar-width);
    padding-top: calc(var(--header-height) + env(safe-area-inset-top));
    flex: 1;
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
}

.content-header {
    height: calc(var(--header-height) + env(safe-area-inset-top));
    border-bottom: 1px solid var(--border-color);
    padding: env(safe-area-inset-top) max(28px, env(safe-area-inset-right)) 0 max(28px, env(safe-area-inset-left));
    display: flex;
    align-items: center;
    gap:var(--sp-4);
    background: var(--bg-primary);
    position: sticky;
    top: 0;
    z-index: var(--z-sticky);
    flex-shrink: 0;
}

.content-header h2 {
    font-size:var(--fs-title-3); line-height: var(--lh-title-3);
    font-weight: var(--fw-semibold);
    width: 25%;
    min-width: 0;
    flex-shrink: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.content-header .header-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap:var(--sp-3);
    width: 75%;
    min-width: 0;
}

.search-box {
    position: relative;
    flex: 1;
    /* 3.44.11-d: soglia minima. Senza, il campo era l'unico elastico della barra filtri e cedeva
       tutto lo spazio agli altri: con le etichette dei pulsanti portate a Subhead si riduceva a
       zero (agenti) o addirittura andava in negativo con l'input che ne usciva (scadenzario).
       La ricerca a scomparsa dell'intestazione ha una regola propria piu' specifica e resta a 44px. */
    min-width: 160px;
}

.search-box input {
    width: 100%;
    padding:var(--sp-2) var(--sp-4) var(--sp-2) var(--sp-10);
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius:var(--radius-lg);
    color: var(--text-primary);
    font-size:var(--fs-footnote);
    font-family: inherit;
    outline: none;
    transition: var(--transition);
}

.search-box input::placeholder {
    color: var(--text-muted);
    font-weight: var(--fw-regular);
}

.search-box input:focus {
    border-color: var(--accent-light);
    background: var(--bg-card);
    box-shadow: 0 0 0 3px var(--accent-glow);
}

.search-box > i {
    position: absolute;
    left: 14px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-muted);
    font-size:var(--fs-subhead);
    z-index: 1;
    /* 3.44.11-d: la lente e' decorativa ma sta SOPRA al campo: dove la ricerca e' stretta
       (barra filtri di Agenti e Scadenzario su schermo piccolo) si prendeva il tocco destinato
       all'input, e in un caso arrivava a coprire il primo centimetro del campo accanto.
       Vale per tutte le ricerche, non solo per quella della barra superiore. */
    pointer-events: none;
    transition: color var(--dur-base) var(--ease);
}

.search-box input:focus + .global-search-results + i,
.search-box:focus-within > i {
    color: var(--accent);
}

.content-body {
    flex: 1;
    padding:var(--sp-8);
    overflow-y: auto;
}

.mobile-toggle {
    display: none;
    background: none;
    border: none;
    color: var(--text-primary);
    font-size:var(--fs-title-3); line-height: var(--lh-title-3);
    cursor: pointer;
    padding:var(--sp-1);
}

.card {
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    overflow: hidden;
    transition: var(--transition);
}

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

.card-header {
    padding:var(--sp-5) var(--sp-5);
    border-bottom: 1px solid var(--border-color);
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.card-header h3 { font-size:var(--fs-subhead); font-weight: var(--fw-semibold); }
.card-body { padding:var(--sp-5); }

.stats-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap:var(--sp-4);
    margin-bottom:var(--sp-8);
}

.stat-card {
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    padding:var(--sp-6);
    transition: var(--transition);
    position: relative;
    overflow: hidden;
}

.stat-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-sm); }

.stat-card::before {
    content: '';
    position: absolute;
    top: 0; left: 0; right: 0;
    height: 3px;
}

.stat-card.accent::before { background: var(--accent); }
.stat-card.success::before { background: var(--success); }
.stat-card.warning::before { background: var(--warning); }
.stat-card.danger::before { background: var(--danger); }
.stat-card.info::before { background: var(--info); }
.stat-card.pink::before { background: var(--pink); }

.stat-icon {
    width: 42px; height: 42px;
    border-radius:var(--radius-sm);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size:var(--fs-title-3); line-height: var(--lh-title-3);
    margin-bottom:var(--sp-4);
}

.stat-card.accent .stat-icon { background: var(--accent-soft); color: var(--accent); }
/* 3.44.10: il testo semantico passa ai token -ink (piu scuri). I colori pieni --success,
   --warning, --info, --pink, --danger restano su sfondi, bordi e barre, dove bastano 3:1,
   ma come TESTO su fondo chiaro o -soft non raggiungevano il 4,5:1 richiesto: il piu debole,
   --warning su --warning-soft, si fermava a 2,68:1. Vedi i valori in :root. */
.stat-card.success .stat-icon { background: var(--success-soft); color: var(--success-ink); }
.stat-card.warning .stat-icon { background: var(--warning-soft); color: var(--warning-ink); }
.stat-card.danger .stat-icon { background: var(--danger-soft); color: var(--danger-ink); }
.stat-card.info .stat-icon { background: var(--info-soft); color: var(--info-ink); }
.stat-card.pink .stat-icon { background: var(--pink-soft); color: var(--pink-ink); }

.stat-value { font-size:var(--fs-title-1); line-height: var(--lh-title-1); font-weight: var(--fw-bold); margin-bottom:var(--sp-1); }
.stat-label { color: var(--text-secondary); font-size:var(--fs-footnote); }

.dashboard-grid {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    gap:var(--sp-5);
}
/* 3.44.11-b: 1279.98 e non 1280. Portando la vecchia soglia da 1200 a 1280 il layout a due
   colonne arrivava a coprire ANCHE 1280px, che e' la larghezza di moltissimi portatili: il terzo
   pannello finiva sotto la piega. Con il limite appena sotto, a 1280 tornano tre colonne.
   (E' la convenzione usata dai framework CSS per far combaciare max-width e min-width.) */
@media (max-width: 1279.98px) {
    .dashboard-grid { grid-template-columns: 1fr 1fr; }
}

.dashboard-grid-full {
    display: grid;
    grid-template-columns: 1fr;
    gap:var(--sp-5);
    margin-top:var(--sp-5);
}

.kanban-board {
    display: flex;
    gap:var(--sp-4);
    overflow-x: auto;
    padding-bottom:var(--sp-4);
    min-height: calc(100vh - 200px);
    min-height: calc(100dvh - 200px);
}

.kanban-column {
    min-width: 300px;
    max-width: 340px;
    flex: 1;
    background: var(--bg-secondary);
    border-radius: var(--radius);
    border: 1px solid var(--border-color);
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - 200px);
    max-height: calc(100dvh - 200px);
}

.kanban-column-header {
    padding:var(--sp-4);
    border-bottom: 1px solid var(--border-color);
    display: flex;
    align-items: center;
    gap:var(--sp-3);
    flex-shrink: 0;
}

.kanban-column-header .col-dot {
    width: 10px; height: 10px;
    border-radius:50%;
}

.kanban-column-header h4 { font-size:var(--fs-subhead); font-weight: var(--fw-semibold); flex: 1; }

.kanban-column-header .count {
    background: var(--bg-tertiary);
    padding:var(--sp-05) var(--sp-3);
    border-radius:var(--radius-sm);
    font-size:var(--fs-caption-1);
    color: var(--text-muted);
    font-weight: var(--fw-medium);
}

.kanban-cards {
    flex: 1;
    padding:var(--sp-3);
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap:var(--sp-3);
}

.kanban-card {
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    padding:var(--sp-4);
    cursor: pointer;
    transition: var(--transition);
}

.kanban-card:hover { border-color: var(--accent); transform: translateY(-1px); box-shadow: var(--shadow-sm); }

.kanban-card .task-tags {
    display: flex;
    flex-wrap: wrap;
    gap:var(--sp-1);
    margin-bottom:var(--sp-2);
}

.tag {
    padding:var(--sp-05) var(--sp-2);
    border-radius:var(--radius-xs);
    font-size:var(--fs-caption-2);
    font-weight: var(--fw-medium);
    background: var(--bg-tertiary);
    color: var(--text-secondary);
}

.tag-design { background: var(--accent-soft); color: var(--accent); }
.tag-frontend { background: var(--info-soft); color: var(--info-ink); }
.tag-backend { background: var(--success-soft); color: var(--success-ink); }
.tag-mobile { background: var(--pink-soft); color: var(--pink-ink); }
.tag-bug { background: var(--danger-soft); color: var(--danger-ink); }

.kanban-card .task-title {
    font-weight: var(--fw-medium);
    font-size:var(--fs-subhead);
    margin-bottom:var(--sp-2);
    line-height: 1.4;
}

.kanban-card .task-meta {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap:var(--sp-2);
}

.task-due {
    font-size:var(--fs-caption-1);
    color: var(--text-muted);
    display: flex;
    align-items: center;
    gap:var(--sp-1);
}

.task-due.overdue { color: var(--danger-ink); }

.priority-dot {
    width: 6px; height: 6px;
    border-radius:50%;
    flex-shrink: 0;
}

.priority-dot.high { background: var(--danger); }
.priority-dot.medium { background: var(--warning); }
.priority-dot.low { background: var(--success); }

.kanban-add {
    padding:var(--sp-3);
    display: flex;
    align-items: center;
    justify-content: center;
    gap:var(--sp-2);
    color: var(--text-muted);
    cursor: pointer;
    border-top: 1px solid var(--border-color);
    font-size:var(--fs-footnote);
    transition: var(--transition);
    flex-shrink: 0;
}

.kanban-add:hover { color: var(--accent); background: var(--accent-soft); }

.projects-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
    gap:var(--sp-4);
}

.project-card {
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    padding:var(--sp-6);
    transition: var(--transition);
    cursor: pointer;
    position: relative;
    overflow: hidden;
}

.project-card:hover { border-color: var(--border-light); transform: translateY(-2px); box-shadow: var(--shadow-sm); }

.project-card .project-top {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    margin-bottom:var(--sp-4);
}

.project-icon {
    width: 44px; height: 44px;
    border-radius:var(--radius-sm);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size:var(--fs-title-3); line-height: var(--lh-title-3);
    color: white;
}

.project-card h3 { font-size:var(--fs-callout); font-weight: var(--fw-semibold); margin-bottom:var(--sp-2); }
.project-card .project-desc { color: var(--text-secondary); font-size:var(--fs-footnote); margin-bottom:var(--sp-4); line-height: 1.5; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }

.progress-bar-wrap {
    background: var(--bg-tertiary);
    height: 6px;
    border-radius:var(--radius-xs);
    overflow: hidden;
    margin-bottom:var(--sp-3);
}

.progress-bar-fill {
    height: 100%;
    border-radius:var(--radius-xs);
    /* 3.44.10: era 0.5s scritto a mano. --dur-slow e' il tempo previsto proprio per le barre
       di avanzamento, come dice il commento accanto al token. */
    transition: width var(--dur-slow) var(--ease);
}

.project-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.project-members {
    display: flex;
}

.project-members .avatar {
    margin-left:-6px;
    border: 2px solid var(--bg-card);
}

.project-members .avatar:first-child { margin-left:0; }

.project-deadline {
    font-size:var(--fs-caption-1);
    color: var(--text-muted);
    display: flex;
    align-items: center;
    gap:var(--sp-1);
}

.priority-badge {
    padding:var(--sp-1) var(--sp-3);
    border-radius:var(--radius-xs);
    font-size:var(--fs-caption-2);
    font-weight: var(--fw-semibold);
    text-transform: uppercase;
    letter-spacing: 0.3px;
}

.priority-badge.high { background: var(--danger-soft); color: var(--danger-ink); }
.priority-badge.medium { background: var(--warning-soft); color: var(--warning-ink); }
.priority-badge.low { background: var(--success-soft); color: var(--success-ink); }

.team-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap:var(--sp-4);
}

.team-card {
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    padding:var(--sp-6);
    text-align: center;
    transition: var(--transition);
}

.team-card:hover { border-color: var(--border-light); transform: translateY(-2px); }

.team-card .avatar { margin:0 auto var(--sp-4); }

.team-card h4 { font-size:var(--fs-subhead); margin-bottom:var(--sp-1); }

.team-card .role-badge {
    display: inline-block;
    padding:var(--sp-1) var(--sp-3);
    border-radius:var(--radius-xs);
    font-size:var(--fs-caption-2);
    font-weight: var(--fw-semibold);
    text-transform: uppercase;
    letter-spacing: 0.3px;
    margin-bottom:var(--sp-4);
}

.role-badge.admin { background: var(--accent-soft); color: var(--accent); }
.role-badge.manager { background: var(--pink-soft); color: var(--pink-ink); }
.role-badge.member { background: var(--info-soft); color: var(--info-ink); }

.team-stats {
    display: flex;
    justify-content: center;
    gap:var(--sp-5);
    padding-top:var(--sp-4);
    border-top: 1px solid var(--border-color);
}

.team-stats .ts { text-align: center; }
.team-stats .ts-val { font-size:var(--fs-title-3); line-height: var(--lh-title-3); font-weight: var(--fw-bold); }
.team-stats .ts-lbl { font-size:var(--fs-caption-2); color: var(--text-muted); }

.activity-list { display: flex; flex-direction: column; }

.activity-item {
    display: flex;
    align-items: flex-start;
    gap:var(--sp-3);
    padding:var(--sp-3) 0;
    border-bottom: 1px solid var(--border-color);
}

.activity-item:last-child { border-bottom: none; }

.activity-item .activity-text {
    flex: 1;
    font-size:var(--fs-footnote);
    line-height: 1.5;
}

.activity-item .activity-text strong { color: var(--text-primary); }

.activity-item .activity-time {
    font-size:var(--fs-caption-2);
    color: var(--text-muted);
    white-space: nowrap;
}

.calendar-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap:var(--sp-05);
}

.calendar-header-cell {
    padding:var(--sp-3);
    text-align: center;
    font-weight: var(--fw-semibold);
    font-size:var(--fs-caption-1);
    color: var(--text-muted);
    text-transform: uppercase;
}

.calendar-cell {
    padding:var(--sp-2);
    background: var(--bg-secondary);
    border-radius: var(--radius-xs);
    font-size:var(--fs-footnote);
    position: relative;
    transition: var(--transition);
    min-height: 90px;
    height: auto;
    overflow: hidden;
}

.calendar-cell:hover { background: var(--bg-hover); }
.calendar-cell.today { border: 2px solid var(--accent); }
.calendar-cell.other-month { opacity: 0.3; }

.calendar-cell .day-num { font-weight: var(--fw-semibold); margin-bottom:var(--sp-1); }

.calendar-task {
    font-size:var(--fs-caption-2);
    padding:var(--sp-05) var(--sp-2);
    border-radius:var(--radius-xs);
    margin-bottom:var(--sp-05);
    white-space: normal;
    word-break: break-word;
    line-height: 1.3;
    cursor: pointer;
    display: flex;
    align-items: flex-start;
    gap:var(--sp-1);
}
.calendar-task i { font-size:var(--fs-caption-2); flex-shrink: 0; margin-top:1px; opacity: 0.85; }

.chat-app {
    display: grid;
    grid-template-columns: 320px 1fr;
    gap:1px;
    background: var(--border-color);
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    overflow: hidden;
    /* 3.44.12: mancava la tacca. .main-content riserva in alto
       calc(--header-height + env(safe-area-inset-top)), qui invece si sottraeva la sola
       --header-height: nell'app installata su un iPhone con la tacca la chat risultava alta
       ESATTAMENTE quanto l'incavo (47px) di troppo e, siccome .chat-app e' overflow:hidden e
       nessuno scorre, la barra di scrittura finiva sotto il bordo dello schermo (pulsante Invia
       a 849 su una finestra di 844) senza modo di raggiungerla. Ora l'incavo si sottrae qui
       come lo si aggiunge la': i due conti tornano. */
    height: calc(100vh - var(--header-height) - env(safe-area-inset-top) - 56px);
    height: calc(100dvh - var(--header-height) - env(safe-area-inset-top) - 56px);
    /* 3.44.12: il tetto serve perche' su schermo tattile l'altezza NON viene da qui. La funzione
       che riadatta la chat all'apertura della tastiera la riscrive nell'attributo style
       dell'elemento, e uno stile in linea batte qualunque regola: nell'app installata continuava
       quindi a mancare la tacca anche dopo la correzione qui sopra. max-height non e' scritto in
       linea, quindi il limite vale lo stesso; e siccome la tastiera RIMPICCIOLISCE la chat, il
       tetto non le va mai contro: interviene solo quando l'altezza calcolata e' troppo grande,
       che e' esattamente il difetto. Il tetto ripete sempre la stessa somma dell'altezza, riga
       per riga: se un giorno cambia una, va cambiata anche l'altra. */
    max-height: calc(100dvh - var(--header-height) - env(safe-area-inset-top) - 56px);
}

/* 3.44.12: min-width:0 su tutte e due le colonne. Un elemento di griglia ha per difetto un
   minimo automatico pari alla sua larghezza minima di contenuto: bastava un allegato in attesa
   con un nome di file lungo e la colonna della conversazione si allargava da 390 a 680px su un
   iPhone, spingendo il pulsante Invia a x=670 fuori da uno schermo di 390. Non si vedeva
   scorrere niente perche' .chat-app ritaglia (overflow:hidden): il contenuto veniva
   semplicemente tagliato via. Con min-width:0 la colonna torna a rispettare la griglia e sono i
   testi al suo interno a troncarsi, come e' giusto. */
.chat-sidebar {
    background: var(--bg-primary);
    display: flex;
    flex-direction: column;
    min-height: 0;
    min-width: 0;
}

.chat-sidebar-header {
    padding:var(--sp-3);
    border-bottom: 1px solid var(--border-color);
    flex-shrink: 0;
}

.chat-contacts {
    flex: 1;
    overflow-y: auto;
}

.chat-contact {
    display: flex;
    gap:var(--sp-3);
    align-items: center;
    padding:var(--sp-3) var(--sp-4);
    border-bottom: 1px solid var(--border-color);
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease);
}
.chat-contact:hover { background: var(--bg-secondary); }
.chat-contact.active { background: var(--accent-soft); }
.chat-contact.hidden { display: none; }

/* 3.44.12 (secondo giro) — INTERLINEE ACCOPPIATE NELLA CHAT.
   La scala Apple non e' fatta di soli corpi: ogni corpo ha la SUA interlinea, e le due cose
   vanno insieme. Qui i testi brevi (nomi, anteprime, orari, didascalie) ereditavano invece il
   rapporto 1,5 del <body>, pensato per i paragrafi lunghi: 15px diventavano 22,5 invece di 20,
   12px diventavano 18 invece di 16, 11px 16,5 invece di 13. Su una riga sola quell'aria in piu'
   non si legge come respiro, si legge come disallineamento — il nome e l'orario che gli sta
   accanto stavano su due griglie diverse. Da qui in avanti ogni --fs- della chat porta con se'
   il suo --lh-. Restano sul rapporto le due cose che davvero scorrono su piu' righe e possono
   occupare tutta la larghezza: il testo dei messaggi (gia' 17/22, cioe' Body pieno) e il campo
   di scrittura, che cambia corpo fra telefono e desktop e quindi non puo' avere una sola
   interlinea fissa. */
.chat-section-title {
    padding:var(--sp-3) var(--sp-4) var(--sp-2);
    font-size:var(--fs-caption-2); line-height: var(--lh-caption-2);
    font-weight: var(--fw-bold);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-muted);
    background: var(--bg-secondary);
}

.chat-team-search {
    border-top: 1px solid var(--border-color);
}

.chat-contact.new-chat:hover { background: var(--bg-secondary); }
.chat-contact.new-chat .avatar { opacity: 0.85; }

.chat-contact .avatar {
    width: 44px;
    height: 44px;
    font-size:var(--fs-subhead);
    flex-shrink: 0;
}

.chat-contact-info {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap:var(--sp-1);
}

.chat-contact-top {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap:var(--sp-2);
}

.chat-contact-name {
    font-weight: var(--fw-semibold);
    font-size:var(--fs-subhead); line-height: var(--lh-subhead);
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.chat-contact-time {
    font-size:var(--fs-caption-2); line-height: var(--lh-caption-2);
    color: var(--text-muted);
    flex-shrink: 0;
}

.chat-contact-bottom {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap:var(--sp-2);
}

.chat-contact-preview {
    font-size:var(--fs-caption-1); line-height: var(--lh-caption-1);
    color: var(--text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex: 1;
    min-width: 0;
}

/* 3.44.12: due correzioni in una. 1) Colore: questo contatore era --accent mentre lo stesso
   numero nella barra di navigazione era rosso; ora sono tutti e due --danger su testo bianco.
   2) Misura: erano dichiarati 18px qui e 20px piu' in basso (nel blocco iOS), quindi la pillola
   cambiava taglia a seconda di quale regola si guardava. Resta la sola misura di 20px, dichiarata
   qui: la riga in fondo al foglio e' stata tolta. */
.chat-unread-badge {
    background: var(--danger);
    color: #fff;
    font-size:var(--fs-caption-2);
    font-weight: var(--fw-semibold);
    min-width: 20px;
    height: 20px;
    padding:0 var(--sp-1);
    border-radius:var(--radius-full);
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    line-height: 1;
}

/* 3.44.12: vedi la nota su .chat-sidebar. Questa e' la colonna che sbordava davvero. */
.chat-main {
    background: var(--bg-primary);
    display: flex;
    flex-direction: column;
    min-height: 0;
    min-width: 0;
}

.chat-main-empty {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding:var(--sp-5);
}

.chat-conversation {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.chat-header {
    padding:var(--sp-4) var(--sp-5);
    border-bottom: 1px solid var(--border-color);
    display: flex;
    align-items: center;
    gap:var(--sp-3);
    flex-shrink: 0;
    background: var(--bg-secondary);
}

/* 3.44.12: l'avatar dell'intestazione era un rettangolo 40x44. I 44 arrivavano da una
   sostituzione automatica (--control-h) applicata a quello che era un 40 scritto a mano: e'
   l'altezza di un CONTROLLO, non di una pastiglia rotonda, e qui produceva un cerchio schiacciato.
   Anche il corpo del testo era sbagliato al contrario: 13px (--fs-footnote) su una tessera piu'
   GRANDE di quella dell'elenco, che di iniziali ne mostra 15px. Ora e' 40x40 con le iniziali
   alla stessa misura dell'elenco. */
.chat-header .avatar {
    width: 40px;
    height: 40px;
    font-size:var(--fs-subhead);
}

/* 3.44.12: mancava min-width:0, ed e' l'unico motivo per cui il nome della persona sbordava.
   Un elemento flessibile non scende sotto la larghezza minima del proprio contenuto finche' non
   glielo si concede: qui la colonna nome+stato restava larga quanto il nome per intero (misurati
   363px), quindi arrivava a x=479 su uno schermo di 390 e a maggior ragione su uno di 320. Il
   troncamento con i puntini dichiarato piu' in basso (blocco "MESSAGGI SU TELEFONO") non poteva
   quindi entrare in funzione: il testo NON eccedeva mai la sua casella, era la casella a uscire
   dallo schermo, e il nome finiva tagliato a meta' parola contro il bordo, senza nemmeno il
   segnale dei puntini. E' la stessa correzione gia' applicata a .chat-contact-info nell'elenco:
   qui era rimasta indietro. */
.chat-header-info { display: flex; flex-direction: column; gap:var(--sp-05); min-width: 0; }
.chat-header-name { font-weight: var(--fw-semibold); font-size:var(--fs-subhead); line-height: var(--lh-subhead); }
.chat-header-role { font-size:var(--fs-caption-1); line-height: var(--lh-caption-1); color: var(--text-muted); }

.chat-messages {
    flex: 1;
    overflow-y: auto;
    padding:var(--sp-5);
    display: flex;
    flex-direction: column;
    gap:var(--sp-2);
    background: var(--bg-tertiary);
}

/* 3.44.10: rimossi max-width, padding, border-radius e font-size:erano tutti e quattro
   riscritti dal blocco della bolla riprogettata piu' in basso, quindi non arrivavano mai
   a schermo. */
.chat-bubble {
    line-height: 1.4;
    word-break: break-word;
    display: flex;
    flex-direction: column;
    gap:var(--sp-05);
    position: relative;
}

/* 3.44.12: tolti border-bottom-*-radius:4px e il bordo della bolla in entrata. Erano tre
   scritture morte: il blocco delle bolle in stile iOS, piu' in basso, rifissa gli stessi angoli a
   19px e azzera il bordo (border:none). Restavano qui a raccontare un disegno che non esiste piu'. */
.chat-bubble.sent {
    align-self: flex-end;
    background: var(--accent);
    color: #fff;
}

.chat-bubble.received {
    align-self: flex-start;
    background: var(--bg-card);
    color: var(--text-primary);
}

.chat-bubble-text { white-space: pre-wrap; }

/* 3.44.10: qui font-size e opacity erano scritture morte, rimosse: le rifissa piu in basso
   il blocco della bolla riprogettata. Restano align-self e margin-top, che invece agiscono
   ancora perche' il contenitore .chat-bubble-meta e' un flex. Tolta anche la riga
   .chat-bubble.sent .chat-bubble-time, quella si' interamente sovrascritta. */
.chat-bubble-time {
    align-self: flex-end;
    margin-top:var(--sp-05);
}

.chat-bubble-del {
    background: none;
    border: none;
    cursor: pointer;
    font-size:var(--fs-caption-1);
    padding:0;
    opacity: 0;
    transition: opacity var(--dur-fast) var(--ease);
    color: inherit;
}
.chat-bubble:hover .chat-bubble-del { opacity: 0.7; }
.chat-bubble-del:hover { opacity: 1 !important; }
/* 3.44.12: #ef4444 non e' il rosso dell'applicazione (--danger e' #dc2626). Era una tinta
   arrivata da fuori e usata solo qui e sul contatore della barra: sostituita dal token. */
.chat-bubble.received .chat-bubble-del { color: var(--danger); }

.admin-msg-row {
    display: flex;
    align-items: center;
    gap:var(--sp-3);
    padding:var(--sp-1) var(--sp-2);
    border-radius:var(--radius-xs);
    margin-bottom:var(--sp-1);
    transition: background var(--dur-fast) var(--ease);
}
/* 3.44.12: era un nero puro al 4%, l'unico di tutto il foglio; lo sfondo di passaggio del mouse
   ha gia' il suo token e in questo sistema non e' nero ma un grigio-blu. */
.admin-msg-row:hover { background: var(--bg-hover); }
.admin-msg-row.selected { background: var(--accent-soft); }
/* 3.44.12: la casella di spunta era 16x16, sotto qualsiasi soglia toccabile: per selezionare un
   messaggio da eliminare bisognava centrare un quadratino di 16px. Passa a 20px (l'area sensibile
   piena da 44px arriva dalla regola per gli schermi tattili in fondo al foglio, che alza anche la
   riga: allargarla qui la renderebbe enorme col mouse, dove 20px bastano). */
.admin-msg-cb {
    width: 20px;
    height: 20px;
    flex-shrink: 0;
    cursor: pointer;
    accent-color: var(--accent);
}
/* 3.44.12: l'area sensibile della casella. Misurata sul telefono la casella restava 24x24, cioe'
   poco piu' di meta' del minimo Apple: la nota qui sopra dava per fatto che ci pensasse la riga,
   ma la riga NON commuta niente (nessun gestore sopra), quindi il bersaglio utile era davvero il
   quadratino. Ingrandire il quadratino non si puo': una casella di sistema da 44px sarebbe fuori
   scala rispetto a tutte le altre dell'applicazione. Si allarga invece l'etichetta che la
   avvolge, che per definizione commuta il controllo a cui e' legata: 44x44 di area buona, con
   dentro lo stesso quadratino di prima, che resta disegnato uguale. */
.admin-msg-cb-hit {
    width: var(--tap-min);
    height: var(--tap-min);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    cursor: pointer;
}
/* 3.44.12: la bolla accanto alla casella e' un elemento flessibile e come tale non scende sotto
   la larghezza minima del proprio contenuto finche' non glielo si concede. Con l'area sensibile
   portata a 44px il conto non tornava piu' e su uno schermo da 320px la bolla arrivava a x=323,
   cioe' fuori dallo schermo. E' la stessa medicina di .chat-contact-info e .chat-header-info. */
.admin-msg-row > .chat-bubble { min-width: 0; }

.chat-input-area {
    padding:var(--sp-4) var(--sp-4);
    border-top: 1px solid var(--border-color);
    display: flex;
    gap:var(--sp-3);
    background: var(--bg-primary);
    flex-shrink: 0;
}

/* 3.44.12: tolta ".chat-input-area input { flex: 1 }". Nella barra di scrittura l'unico <input>
   e' il selettore di file, che e' display:none: la regola non ha mai avuto niente da allargare.
   Il campo di testo e' una <textarea> e prende il suo flex dalla regola .chat-input. */

/* 3.44.12: fra tablet e desktop la griglia saltava da "320px 1fr" a niente. Su un iPad in
   verticale (820px) restavano 517px da spartire e l'elenco se ne prendeva 320: la conversazione
   scendeva sotto i 300px, larga quanto l'elenco che le sta accanto, con le bolle strette a 200px.
   Fra 769 e 1100px l'elenco cede il posto: 220px di minimo (avatar 44 + nome e anteprima
   troncati) e le righe si stringono di conseguenza. */
@media (min-width: 769px) and (max-width: 1100px) {
    .chat-app { grid-template-columns: minmax(220px, 30%) 1fr; }
    .chat-contact { padding:var(--sp-3); gap:var(--sp-2); }
    .chat-section-title { padding:var(--sp-3) var(--sp-3) var(--sp-2); }
    /* Con la colonna a 220px l'intestazione dell'elenco (ricerca + "Vista Admin") restava
       larga 196px contro i 198 che le servono, e l'etichetta veniva tagliata di netto, senza
       nemmeno i puntini di sospensione: due passi di imbottitura in meno bastano a farcela stare. */
    .chat-sidebar-header { padding:var(--sp-2); }
}

@media (max-width: 768px) {
    .chat-app { grid-template-columns: 1fr; }
    .chat-app.peer-selected .chat-sidebar { display: none; }
    .chat-app:not(.peer-selected) .chat-main { display: none; }
}

div.content-header {
    height: auto;
    min-height: var(--header-height);
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    margin-bottom:var(--sp-4);
    padding:var(--sp-4) var(--sp-5);
    position: static;
    flex-wrap: wrap;
    box-shadow: var(--shadow-sm);
}
.cloud-quota-bar {
    position: relative;
    width: 200px; height: 30px;
    background: var(--bg-tertiary);
    border: 1px solid var(--border-color);
    border-radius:var(--radius);
    overflow: hidden;
    display: flex; align-items: center;
}
.cloud-quota-fill {
    position: absolute; left: 0; top: 0; bottom: 0;
    background: var(--accent);
    transition: width var(--dur-slow) var(--ease), background var(--dur-slow) var(--ease);
    border-radius:var(--radius);
}
.cloud-quota-text {
    position: relative; z-index: 1;
    width: 100%; text-align: center;
    font-size:var(--fs-caption-2); font-weight: var(--fw-semibold);
    color: var(--text-primary);
    mix-blend-mode: normal;
}
.cloud-admin-bar {
    display: flex; align-items: center; gap:var(--sp-3); flex-wrap: wrap;
    background: var(--danger-soft);
    border: 1px solid rgba(239,68,68,0.3);
    border-radius: var(--radius-sm);
    padding:var(--sp-3) var(--sp-4); margin-top:var(--sp-4); margin-bottom:var(--sp-4);
    font-size:var(--fs-footnote); color: var(--text-secondary);
}
.cloud-admin-bar i { color: var(--danger-ink); }
.cloud-breadcrumb {
    display: flex; align-items: center; gap:var(--sp-2); flex-wrap: wrap;
    margin-bottom:var(--sp-4); font-size:var(--fs-subhead);
}
.cloud-nav { display: flex; align-items: center; gap:var(--sp-3); margin-bottom:var(--sp-4); flex-wrap: wrap; }
.cloud-nav .cloud-breadcrumb { margin-bottom:0; }
.cloud-up-btn:disabled { opacity: .45; cursor: default; }
.cloud-crumb {
    cursor: pointer; color: var(--text-secondary);
    padding:var(--sp-1) var(--sp-2); border-radius: var(--radius-xs);
    transition: var(--transition);
}
.cloud-crumb:hover { background: var(--bg-hover); color: var(--accent); }
.cloud-crumb-sep { color: var(--text-muted); font-size:var(--fs-caption-2); }
#cloudDropZone.cloud-dragover {
    outline: 2px dashed var(--accent);
    outline-offset: -6px;
    background: var(--accent-soft);
}
.cloud-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap:var(--sp-4);
    padding:var(--sp-5);
}
.cloud-item {
    position: relative;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    padding:var(--sp-4) var(--sp-3) var(--sp-3);
    cursor: pointer;
    transition: var(--transition);
    background: var(--bg-tertiary);
    text-align: center;
    display: flex; flex-direction: column; align-items: center; gap:var(--sp-2);
}
.cloud-item:hover {
    border-color: var(--accent);
    transform: translateY(-2px);
    box-shadow: var(--shadow-sm);
}
.cloud-item-icon { position: relative; font-size:var(--icon-xl); line-height: 1; }
.cloud-badge {
    position: absolute; bottom: -2px; right: -8px;
    background: var(--accent); color: #fff;
    width: 18px; height: 18px; border-radius:50%;
    display: flex; align-items: center; justify-content: center;
    font-size:var(--fs-caption-2); border: 2px solid var(--bg-card);
}
.cloud-badge-inherit { background: var(--text-muted); opacity: 0.85; }
/* Link di caricamento esterno: badge a sinistra (convive con il badge condivisione a destra) */
.cloud-badge-upreq { left: -8px; right: auto; background: var(--success); }
.cloud-badge-upreq-off { background: var(--text-muted); opacity: 0.85; }
/* Link di DOWNLOAD esterno (3.45.0): stesso lato del caricamento ma piu' in basso,
   cosi' una cartella che ha entrambi i link mostra due badge distinti e non sovrapposti. */
.cloud-badge-dlink { left: -8px; right: auto; top: auto; bottom: -6px; background: var(--primary, #40566e); }
.cloud-badge-dlink-off { background: var(--text-muted); opacity: 0.85; }
.cloud-item-name {
    font-size:var(--fs-footnote); font-weight: var(--fw-medium); color: var(--text-primary);
    width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cloud-item-meta {
    font-size:var(--fs-caption-2); color: var(--text-muted);
    display: flex; flex-direction: column; gap:var(--sp-05);
}
.cloud-owner {
    display: inline-block; font-size:var(--fs-caption-2);
    background: var(--bg-hover); color: var(--text-secondary);
    padding:1px var(--sp-2); border-radius:var(--radius-xs); margin-top:var(--sp-05);
}
.cloud-item-actions {
    display: flex; gap:var(--sp-1); justify-content: center;
    margin-top:var(--sp-1); opacity: 0; transition: opacity var(--dur-fast) var(--ease);
}
.cloud-item:hover .cloud-item-actions { opacity: 1; }
.cloud-act {
    background: var(--bg-card); border: 1px solid var(--border-color);
    color: var(--text-secondary); cursor: pointer;
    width: 26px; height: 26px; border-radius: var(--radius-xs);
    display: flex; align-items: center; justify-content: center;
    font-size:var(--fs-caption-1); transition: var(--transition);
}
.cloud-act:hover { background: var(--accent); color: #fff; border-color: var(--accent); }
.cloud-act-danger:hover { background: var(--danger); border-color: var(--danger); }

.cloud-share-all {
    display: flex; align-items: center; gap:var(--sp-3);
    padding:var(--sp-3) var(--sp-4); border: 1px solid var(--border-color);
    border-radius: var(--radius-sm); cursor: pointer; font-weight: var(--fw-medium);
}
.cloud-share-all input { width: 16px; height: 16px; accent-color: var(--accent); }
.cloud-share-divider {
    text-align: center; font-size:var(--fs-caption-2); color: var(--text-muted);
    text-transform: uppercase; letter-spacing: 0.5px; margin:var(--sp-4) 0 var(--sp-3);
}
.cloud-share-users { max-height: 260px; overflow-y: auto; display: flex; flex-direction: column; gap:var(--sp-1); }
.cloud-share-user {
    display: flex; align-items: center; gap:var(--sp-3);
    padding:var(--sp-2) var(--sp-3); border-radius: var(--radius-xs); cursor: pointer;
    transition: var(--transition);
}
.cloud-share-user:hover { background: var(--bg-hover); }
.cloud-share-cb { width: 16px; height: 16px; accent-color: var(--accent); }

/* ============================================================================
   3.44.11-e — CLOUD IN FORMA DI ELENCO SU TELEFONO E TABLET
   La griglia a schede metteva riquadri da circa 110px di larghezza, e dentro
   ciascuno la fila dei quattro pulsanti azione (scarica, condividi, rinomina,
   elimina). Finche' i pulsanti erano da 26px la fila ci stava a stento; portati
   a 44px — il bersaglio tattile minimo delle linee guida Apple — servivano 188px
   in 110 disponibili: la fila usciva dalla scheda da entrambi i lati e finiva
   sopra i pulsanti delle schede vicine, rendendo impossibile capire quale
   pulsante appartenesse a quale file (e toccarne uno a colpo sicuro).
   Allargare le schede non basta: nemmeno a due colonne i 188px ci starebbero.
   Su schermo stretto quindi il Cloud diventa un ELENCO, una riga per elemento —
   la forma che usano tutti i gestori di file su telefono: l'icona a sinistra, il
   nome per esteso al centro, i quattro pulsanti a destra con tutto lo spazio che
   serve. Sopra i 768px resta la griglia a schede di sempre.
   ============================================================================ */
@media (max-width: 768px) {
    .cloud-quota-bar { width: 140px; }

    .cloud-grid { grid-template-columns: 1fr; gap:var(--sp-2); padding:var(--sp-3); }

    .cloud-item {
        display: grid;
        grid-template-columns: auto minmax(0, 1fr);
        /* I quattro pulsanti da 44px occupano 176px: messi sulla stessa riga del nome gli
           lasciavano una colonna di appena 60px, e i nomi andavano a capo dopo due parole.
           Stanno quindi su una riga propria, allineati a destra: il nome si prende tutta la
           larghezza e i pulsanti restano comodi da toccare. */
        grid-template-areas:
            "icona nome"
            "icona dati"
            "azioni azioni";
        align-items: center;
        column-gap: var(--sp-3);
        row-gap: var(--sp-05);
        text-align: left;
        padding: var(--sp-3) var(--sp-3) var(--sp-2) var(--sp-10);
    }
    /* Al passaggio del dito la riga non si solleva: in un elenco fitto lo scatto
       verticale si legge come un salto della lista. */
    .cloud-item:hover { transform: none; }

    .cloud-item-icon { grid-area: icona; font-size: var(--icon-lg); }
    .cloud-item-name { grid-area: nome; white-space: normal; overflow-wrap: anywhere; }
    .cloud-item-meta { grid-area: dati; flex-direction: row; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
    .cloud-owner { margin-top: 0; }

    .cloud-item-actions {
        grid-area: azioni;
        opacity: 1;
        margin-top: var(--sp-1);
        gap: var(--sp-1);
        justify-content: flex-end;
        border-top: 1px solid var(--border-color);
        padding-top: var(--sp-1);
    }
    /* L'icona resta in alto, allineata al nome, non centrata sull'intera riga. */
    .cloud-item-icon { align-self: start; padding-top: var(--sp-05); }

    /* La casella di selezione occupa la corsia lasciata libera dal riempimento
       sinistro della riga, senza sovrapporsi all'icona come faceva nella scheda. */
    .cloud-sel-cb { top: 50%; left: var(--sp-2); transform: translateY(-50%); }
}

.vault-search {
    position: relative; display: flex; align-items: center;
}
.vault-search i {
    position: absolute; left: 10px; color: var(--text-muted); font-size:var(--fs-footnote); pointer-events: none;
}
.vault-search input {
    padding:var(--sp-2) var(--sp-3) var(--sp-2) var(--sp-8);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    background: var(--bg-tertiary);
    color: var(--text-primary);
    font-size:var(--fs-footnote); width: 180px;
}
.vault-search input:focus { outline: none; border-color: var(--accent); }
.vault-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(290px, 1fr));
    gap:var(--sp-4);
}
.vault-card {
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    background: var(--bg-card);
    padding:var(--sp-4);
    display: flex; flex-direction: column; gap:var(--sp-3);
    transition: var(--transition);
}
.vault-card:hover { border-color: var(--accent); box-shadow: var(--shadow-sm); }
.vault-card-head { display: flex; align-items: center; gap:var(--sp-3); }
.vault-avatar {
    width: 40px; height: var(--control-h); border-radius: var(--radius-sm);
    background: linear-gradient(135deg, var(--accent), var(--accent-hover));
    color: #fff; font-weight: var(--fw-bold); font-size:var(--fs-title-3); line-height: var(--lh-title-3);
    display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.vault-card-titles { min-width: 0; flex: 1; }
.vault-title {
    font-weight: var(--fw-semibold); font-size:var(--fs-subhead); color: var(--text-primary);
    display: flex; align-items: center; gap:var(--sp-2);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.vault-cat { font-size:var(--fs-caption-2); color: var(--text-muted); }
.vault-badge {
    background: var(--accent-soft); color: var(--accent);
    width: 18px; height: 18px; border-radius:50%;
    display: inline-flex; align-items: center; justify-content: center;
    font-size:var(--fs-caption-2); flex-shrink: 0;
}
.vault-row {
    display: flex; align-items: center; gap:var(--sp-2);
    background: var(--bg-tertiary);
    border-radius: var(--radius-xs);
    padding:var(--sp-2) var(--sp-2); font-size:var(--fs-footnote);
}
.vault-row-label {
    font-size:var(--fs-caption-2); text-transform: uppercase; letter-spacing: 0.4px;
    color: var(--text-muted); width: 60px; flex-shrink: 0;
}
/* 3.44.10: --font-mono ora e definito in :root (JetBrains Mono, gia caricato). */
.vault-row-val {
    flex: 1; color: var(--text-primary);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font-family: var(--font-mono);
}
.vault-pw-mask { letter-spacing: 1px; }
.vault-mini {
    background: transparent; border: none; color: var(--text-secondary);
    cursor: pointer; width: 24px; height: 24px; border-radius: var(--radius-xs);
    display: flex; align-items: center; justify-content: center;
    font-size:var(--fs-caption-1); transition: var(--transition); flex-shrink: 0;
}
.vault-mini:hover { background: var(--bg-hover); color: var(--accent); }
.vault-url-row { background: transparent; padding:0 var(--sp-2); }
.vault-url {
    color: var(--accent); font-size:var(--fs-caption-1); text-decoration: none;
    display: inline-flex; align-items: center; gap:var(--sp-1);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%;
}
.vault-url:hover { text-decoration: underline; }
.vault-note-flag { font-size:var(--fs-caption-2); color: var(--text-muted); display: flex; align-items: center; gap:var(--sp-1); }
.vault-card-actions { display: flex; align-items: center; gap:var(--sp-2); margin-top:var(--sp-05); }

.vault-pw-field { position: relative; display: flex; align-items: center; }
.vault-pw-field input { flex: 1; padding-right:var(--sp-16); }
.vault-pw-btn {
    position: absolute; background: transparent; border: none;
    color: var(--text-secondary); cursor: pointer; font-size:var(--fs-subhead);
    width: 30px; height: 100%; display: flex; align-items: center; justify-content: center;
    transition: var(--transition);
}
.vault-pw-btn:hover { color: var(--accent); }
.vault-pw-btn:nth-of-type(1) { right: 32px; }
.vault-pw-btn:nth-of-type(2) { right: 2px; }
.vault-share-cb { width: 16px; height: 16px; accent-color: var(--accent); }

@media (max-width: 768px) {
    .vault-grid { grid-template-columns: 1fr; }
    .vault-search input { width: 120px; }
}

.modal-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.6);
    -webkit-backdrop-filter: blur(4px); /* iOS: Safari richiede il prefisso */
    backdrop-filter: blur(4px);
    /* 3.44.7: prima il padding era dichiarato due volte e la seconda dichiarazione
       (20px secchi) annullava le zone sicure: su iPhone la finestra finiva sotto il notch.
       Ora un'unica regola tiene il margine minimo E rispetta le zone sicure. */
    padding: max(20px, env(safe-area-inset-top)) max(20px, env(safe-area-inset-right))
             max(20px, env(safe-area-inset-bottom)) max(20px, env(safe-area-inset-left));
    z-index: var(--z-modal);
    align-items: center;
    justify-content: center;
}

/* 3.44.7: apertura E chiusura animate. Prima le finestre comparivano in dissolvenza ma
   sparivano di colpo: il contrasto fra entrata morbida e uscita istantanea era la stonatura
   piu evidente dell'interfaccia. Ora la chiusura e' l'esatto contrario dell'apertura,
   piu breve perche' l'uscita deve togliersi di mezzo in fretta. */
.modal-overlay.active { display: flex; animation: mfOverlayIn var(--dur-base) var(--ease) both; }
.modal-overlay.active.is-closing {
    animation: mfOverlayOut var(--dur-fast) var(--ease-in) both;
    /* Mentre svanisce la finestra non deve piu' raccogliere clic: senza questo un doppio
       clic sul fondo scuro finiva sull'elemento che stava scomparendo invece che su
       quello sotto, e il secondo clic sembrava non fare niente. */
    pointer-events: none;
}
.modal-overlay.is-closing .modal { animation: mfModalOut var(--dur-fast) var(--ease-in) both; }

@keyframes mfOverlayIn  { from { opacity: 0; } to { opacity: 1; } }
@keyframes mfOverlayOut { from { opacity: 1; } to { opacity: 0; } }
@keyframes mfModalOut {
    from { opacity: 1; transform: scale(1) translateY(0); }
    to   { opacity: 0; transform: scale(0.97) translateY(6px); }
}

.modal {
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius:var(--radius);
    width: 100%;
    max-width: 560px;
    max-height: 85vh;
    overflow-y: auto;
    box-shadow: var(--shadow);
    animation: modalIn var(--dur-base) var(--ease-out) both;
}

@keyframes modalIn {
    from { opacity: 0; transform: scale(0.95) translateY(10px); }
    to { opacity: 1; transform: scale(1) translateY(0); }
}

.modal-header {
    padding:var(--sp-6) var(--sp-6);
    border-bottom: 1px solid var(--border-color);
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.modal-header h3 { font-size:var(--fs-body); font-weight: var(--fw-semibold); }

.modal-close {
    background: none;
    border: none;
    color: var(--text-muted);
    font-size:var(--fs-title-3); line-height: var(--lh-title-3);
    cursor: pointer;
    padding:var(--sp-1);
    border-radius:var(--radius-xs);
    transition: var(--transition);
}

.modal-close:hover { background: var(--bg-tertiary); color: var(--text-primary); }

.modal-body { padding:var(--sp-6); }
.modal-footer {
    padding:var(--sp-4) var(--sp-6);
    border-top: 1px solid var(--border-color);
    display: flex;
    justify-content: flex-end;
    gap:var(--sp-3);
}

.task-detail-grid {
    display: grid;
    grid-template-columns: 1fr 200px;
    gap:var(--sp-5);
}

.task-detail-main { }

.task-detail-sidebar .detail-item {
    margin-bottom:var(--sp-4);
}

.task-detail-sidebar .detail-label {
    font-size:var(--fs-caption-2);
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin-bottom:var(--sp-1);
}

.checklist-item {
    display: flex;
    align-items: center;
    gap:var(--sp-2);
    padding:var(--sp-2) 0;
    font-size:var(--fs-footnote);
}

.checklist-item input[type="checkbox"] {
    accent-color: var(--accent);
    width: 16px;
    height: 16px;
}

.checklist-item.done { text-decoration: line-through; color: var(--text-muted); }

.comment-item {
    display: flex;
    gap:var(--sp-3);
    padding:var(--sp-3) 0;
    border-bottom: 1px solid var(--border-color);
}

.comment-item:last-child { border-bottom: none; }

.comment-text { font-size:var(--fs-footnote); line-height: 1.5; }
.comment-meta { font-size:var(--fs-caption-2); color: var(--text-muted); margin-top:var(--sp-1); }

.chart-bar-group { margin-bottom:var(--sp-4); }

.chart-bar-label {
    display: flex;
    justify-content: space-between;
    font-size:var(--fs-footnote);
    margin-bottom:var(--sp-2);
}

.chart-bar {
    height: 8px;
    background: var(--bg-tertiary);
    border-radius:var(--radius-xs);
    overflow: hidden;
}

.chart-bar-fill {
    height: 100%;
    border-radius:var(--radius-xs);
    /* 3.44.10: era 0.6s scritto a mano, ora sul token delle barre. */
    transition: width var(--dur-slow) var(--ease);
}

.data-table {
    width: 100%;
    border-collapse: collapse;
}

.data-table th {
    text-align: left;
    padding:var(--sp-3) var(--sp-4);
    font-size:var(--fs-caption-1);
    font-weight: var(--fw-semibold);
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    border-bottom: 1px solid var(--border-color);
}

.data-table td {
    padding:var(--sp-3) var(--sp-4);
    border-bottom: 1px solid var(--border-color);
    font-size:var(--fs-subhead);
    transition: var(--transition);
}

.data-table tr:hover td { background: var(--bg-hover); }
.data-table tr:last-child td { border-bottom: none; }

.tabs {
    display: flex;
    gap:var(--sp-05);
    background: var(--bg-secondary);
    border-radius: var(--radius-sm);
    padding:var(--sp-1);
    margin-bottom:var(--sp-5);
    width: fit-content;
}

.tab-btn {
    padding:var(--sp-2) var(--sp-4);
    border: none;
    background: none;
    color: var(--text-secondary);
    font-family: inherit;
    font-size:var(--fs-footnote);
    font-weight: var(--fw-medium);
    cursor: pointer;
    border-radius: var(--radius-xs);
    transition: var(--transition);
}

.tab-btn:hover { color: var(--text-primary); }
.tab-btn.active { background: var(--accent); color: white; }

.empty-state {
    text-align: center;
    padding:var(--sp-16) var(--sp-5);
    color: var(--text-secondary);
}

.empty-state i { font-size:var(--fs-title-1); line-height: var(--lh-title-1); opacity: 0.35; } /* 3.43.48: allineato agli altri stati vuoti (erano 28-34px, questo 20px) */
.empty-state h3 { margin-bottom:var(--sp-2); color: var(--text-primary); }
.empty-state p { margin-bottom:var(--sp-5); font-size:var(--fs-subhead); }

@media (max-width: 1024px) {
    .dashboard-grid, .dashboard-grid-full { grid-template-columns: 1fr; }
    .task-detail-grid { grid-template-columns: 1fr; }
}

@media (max-width: 768px) {
    .sidebar { transform: translateX(-100%); }
    .sidebar.open { transform: translateX(0); }
    .main-content { margin-left:0; }
    .mobile-toggle { display: block; }
    /* 3.44.10: qui c'era .content-body { padding:var(--sp-4) }, scrittura morta: piu' avanti,
       nello stesso punto di rottura 768px, la stessa proprieta' viene rifissata a 14px. */
    .stats-grid { grid-template-columns: repeat(2, 1fr); }
    .kanban-column { min-width: 260px; }
    .projects-grid { grid-template-columns: 1fr; }
    .search-box input { width: 100%; }
    .search-box input:focus { width: 100%; }
}

.filter-bar {
    display: flex;
    align-items: center;
    gap:var(--sp-3);
    margin-bottom:var(--sp-5);
    flex-wrap: wrap;
    padding:var(--sp-4) var(--sp-4);
    border-radius: var(--radius);
}

.filter-bar .form-select {
    width: auto;
    padding:var(--sp-2) var(--sp-10) var(--sp-2) var(--sp-3);
    font-size:var(--fs-footnote);
}

.filter-bar .searchable-select {
    width: auto;
    min-width: 180px;
    flex-shrink: 0;
}
.filter-bar .searchable-select .form-control {
    padding:var(--sp-2) var(--sp-3);
    font-size:var(--fs-footnote);
    width: 100%;
}
.filter-bar .searchable-select-dropdown {
    z-index: var(--z-dropdown);
    min-width: 180px;
}

.color-options {
    display: flex;
    gap:var(--sp-2);
    flex-wrap: wrap;
}

.color-option {
    width: 32px; height: 32px;
    border-radius:var(--radius-xs);
    cursor: pointer;
    border: 3px solid transparent;
    transition: var(--transition);
}

.color-option:hover { transform: scale(1.1); }
.color-option.selected { border-color: var(--text-primary); }

/* 3.44.7: unica comparsa in dissolvenza. Erano tre curve identiche che differivano solo
   per quanto l'elemento saliva (16px, 8px, 4px): ora e' un solo fotogramma e la distanza
   la sceglie chi la usa, con --fade-dist. */
@keyframes mfFadeUp { from { opacity: 0; transform: translateY(var(--fade-dist, 8px)); } to { opacity: 1; transform: none; } }

/* 3.44.8: .fade-in e' definita in fondo al foglio, insieme agli altri tempi
   rivisti. Qui restava la versione lenta (1,2s con 0,3s di attesa) che veniva
   comunque sovrascritta: due valori diversi per la stessa cosa nello stesso
   file. Tolte anche .stagger-1..4, che assegnavano un ritardo a scaglioni ma
   non erano applicate a nessun elemento in tutto il progetto. */

.kanban-card.dragging { opacity: 0.5; transform: rotate(2deg); }
.kanban-column.drag-over { background: var(--bg-hover); border-color: var(--accent); }

    .theme-toggle {
        display:flex; align-items:center; justify-content:center;
        width:36px; height:36px; border:none; border-radius:var(--radius-xs);
        background:transparent; color:var(--text-muted); cursor:pointer;
        font-size:var(--fs-title-3); line-height: var(--lh-title-3); transition: var(--transition);
    }
    .theme-toggle:hover { color:var(--accent); background:var(--bg-hover); }

    /* 3.44.10: era 'Courier New', monospace; ora usa il token --font-mono come gli altri
       due punti monospazio del file (schede credenziali ed editor di testo). */
    .sync-log { min-height:250px; max-height:500px; overflow-y:auto; font-family:var(--font-mono); font-size:var(--fs-caption-1); background:var(--bg-primary); border:1px solid var(--border-color); border-radius:var(--radius-xs); padding:var(--sp-4); line-height:1.8; flex:1; }
    .sync-log .log-ok { color:var(--success-ink); }
    .sync-log .log-err { color:var(--danger-ink); }
    .sync-log .log-warn { color:var(--warning-ink); }
    .sync-log .log-info { color:var(--info-ink); }
    .sync-log .log-dim { color:var(--text-muted); }
    .sync-status-cards { display:grid; grid-template-columns:repeat(4,1fr); gap:var(--sp-3); margin-bottom:var(--sp-4); }
    .sync-status-card { text-align:center; padding:var(--sp-3) var(--sp-2); background:var(--bg-tertiary); border-radius:var(--radius-xs); }
    .sync-status-card .sc-val { font-size:var(--fs-title-2); line-height: var(--lh-title-2); font-weight: var(--fw-bold); }
    .sync-status-card .sc-lbl { font-size:var(--fs-caption-2); color:var(--text-muted); text-transform:uppercase; margin-top:var(--sp-05); }
    .sync-conn-form { display:grid; grid-template-columns:1fr 1fr; gap:var(--sp-3); margin-bottom:var(--sp-4); }
    .sync-conn-form .form-group { margin:0; }
    .sync-conn-form label { font-size:var(--fs-caption-2); font-weight: var(--fw-semibold); color:var(--text-muted); margin-bottom:var(--sp-1); }
    /* 3.44.7: unica girandola per tutta l'app (era ripetuta tre volte identica, a tre
       velocita diverse: 0,6s nei pulsanti, 0,9s nella protezione, 1s qui). */
    .spin { display:inline-block; animation: mfSpin var(--dur-spin) linear infinite; }
    .attachments-section { margin-top:var(--sp-4); }
    .attachments-list { display: flex; flex-direction: column; gap:var(--sp-2); }
    .attachment-item {
        display: flex; align-items: center; gap:var(--sp-3);
        padding:var(--sp-2) var(--sp-3); background: var(--bg-tertiary); border-radius: var(--radius-xs);
        font-size:var(--fs-footnote); transition: background var(--dur-fast) var(--ease);
    }
    .attachment-item:hover { background: var(--bg-hover); }
    .attachment-icon { font-size:var(--fs-title-3); line-height: var(--lh-title-3); color: var(--accent); flex-shrink: 0; }
    .attachment-info { flex: 1; min-width: 0; }
    .attachment-name {
        font-weight: var(--fw-medium); white-space: nowrap; overflow: hidden;
        text-overflow: ellipsis; color: var(--text-primary);
    }
    .attachment-meta { font-size:var(--fs-caption-2); color: var(--text-muted); }
    .attachment-actions { display: flex; gap:var(--sp-1); flex-shrink: 0; }
    .upload-zone {
        border: 2px dashed var(--border-color); border-radius: var(--radius-sm);
        padding:var(--sp-5); text-align: center; cursor: pointer;
        transition: var(--transition); color: var(--text-muted); font-size:var(--fs-footnote);
    }
    .upload-zone:hover, .upload-zone.dragover {
        border-color: var(--accent); background: var(--accent-soft); color: var(--accent);
    }
    .upload-zone i { font-size:var(--fs-title-1); line-height: var(--lh-title-1); display: block; margin-bottom:var(--sp-2); }
    .upload-progress { margin-top:var(--sp-2); }
    .upload-progress-bar {
        height: 4px; background: var(--bg-tertiary); border-radius:var(--radius-xs); overflow: hidden;
    }
    .upload-progress-fill {
        height: 100%; background: var(--accent); border-radius:var(--radius-xs);
        transition: width var(--dur-slow) var(--ease); width: 0%;
    }
    .pending-files { margin-top:var(--sp-3); }
    .pending-file {
        display: flex; align-items: center; gap:var(--sp-2);
        padding:var(--sp-2) var(--sp-3); background: var(--bg-tertiary); border-radius: var(--radius-xs);
        font-size:var(--fs-caption-1); margin-bottom:var(--sp-1);
    }
    .pending-file .remove-pending {
        margin-left:auto; cursor: pointer; color: var(--danger-ink);
        background: none; border: none; font-size:var(--fs-subhead); padding:0 var(--sp-1);
    }
    .project-attachments-count {
        display: inline-flex; align-items: center; gap:var(--sp-1);
        font-size:var(--fs-caption-2); color: var(--text-muted);
    }

    .clients-grid {
        display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap:var(--sp-4);
    }
    .client-card {
        background: var(--bg-card); border: 1px solid var(--border-color);
        border-radius:var(--radius-xs); padding:var(--sp-5); transition: var(--transition);
        cursor: pointer; position: relative;
    }
    .client-card:hover { border-color: var(--accent); box-shadow: var(--shadow-sm); transform: translateY(-1px); }
    .client-card-header { display: flex; align-items: flex-start; gap:var(--sp-3); margin-bottom:var(--sp-3); }
    .client-avatar {
        width: 44px; height: 44px; border-radius:var(--radius-sm); display: flex;
        align-items: center; justify-content: center; font-weight: var(--fw-bold);
        font-size:var(--fs-callout); color: #fff; flex-shrink: 0;
    }
    .client-card-info { flex: 1; min-width: 0; }
    .client-card-info h4 {
        font-size:var(--fs-subhead); font-weight: var(--fw-semibold); margin:0 0 var(--sp-05);
        white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    .client-card-info .client-contact {
        font-size:var(--fs-caption-1); color: var(--text-muted);
        white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    .client-card-actions {
        position: absolute; top: 12px; right: 12px;
        display: flex; gap:var(--sp-1); opacity: 0; transition: opacity var(--dur-fast) var(--ease);
    }
    .client-card:hover .client-card-actions { opacity: 1; }
    .client-meta-row {
        display: flex; align-items: center; gap:var(--sp-2);
        font-size:var(--fs-caption-1); color: var(--text-secondary); margin-bottom:var(--sp-1);
    }
    .client-meta-row i { font-size:var(--fs-footnote); color: var(--text-muted); width: 16px; text-align: center; }
    .btn-map-open { display:inline-flex; align-items:center; gap:var(--sp-1); flex-shrink:0; margin-left:var(--sp-2); padding:var(--sp-05) var(--sp-2); border:1px solid var(--accent); background:transparent; color:var(--accent); border-radius:var(--radius-full); font-size:var(--fs-caption-2); font-weight: var(--fw-semibold); line-height:1.7; cursor:pointer; white-space:nowrap; transition: var(--transition); }
    .btn-map-open:hover { background:var(--accent); color:#fff; }
    .btn-map-open i { width:auto; font-size:var(--fs-caption-2); color:inherit; }
    .client-stats {
        display: flex; gap:var(--sp-4); margin-top:var(--sp-4); padding-top:var(--sp-4);
        border-top: 1px solid var(--border-color);
    }
    .client-stats .cs { text-align: center; flex: 1; }
    .client-stats .cs-val { font-size:var(--fs-title-3); line-height: var(--lh-title-3); font-weight: var(--fw-bold); color: var(--text-primary); }
    .client-stats .cs-lbl { font-size:var(--fs-caption-2); color: var(--text-muted); }
    .client-status-badge {
        display: inline-flex; align-items: center; gap:var(--sp-1);
        padding:var(--sp-05) var(--sp-2); border-radius:var(--radius-sm); font-size:var(--fs-caption-2); font-weight: var(--fw-semibold);
    }
    .client-status-badge.active { background: var(--success-soft); color: var(--success-ink); }
    .client-status-badge.inactive { background: var(--bg-tertiary); color: var(--text-muted); }
    .client-status-badge.lead { background: var(--accent-soft); color: var(--accent); }
    .client-history-item {
        display: flex; gap:var(--sp-3); padding:var(--sp-3) 0;
        border-bottom: 1px solid var(--border-color); font-size:var(--fs-footnote);
    }
    .client-history-item:last-child { border-bottom: none; }
    .client-history-icon {
        width: 28px; height: 28px; border-radius:50%;
        display: flex; align-items: center; justify-content: center;
        font-size:var(--fs-caption-1); flex-shrink: 0;
    }
    .client-linked-item {
        display: flex; align-items: center; gap:var(--sp-3);
        padding:var(--sp-2) var(--sp-3); background: var(--bg-tertiary); border-radius: var(--radius-xs);
        font-size:var(--fs-footnote); margin-bottom:var(--sp-2); cursor: pointer; transition: background var(--dur-fast) var(--ease);
    }
    .client-linked-item:hover { background: var(--bg-hover); }

    .client-extra-tags { display:flex; flex-wrap:wrap; gap:var(--sp-1); margin-top:var(--sp-2); }
    .client-extra-tags .etag {
        display:inline-flex; align-items:center; gap:var(--sp-1);
        padding:var(--sp-05) var(--sp-2); border-radius:var(--radius-xs); font-size:var(--fs-caption-2); font-weight: var(--fw-semibold);
    }
    /* 3.44.15: quattro di queste cinque etichette avevano come testo il colore PIENO della loro
       tinta, sul proprio stesso fondo tenue: la scritta di 11px non si leggeva — si vedeva la
       forma della pastiglia, non la parola dentro (1,88:1 l'attivita', 2,09:1 il nodo, 2,14:1 il
       servizio, 2,94:1 il gruppo, contro il 4,5:1 che serve a un testo di questa misura). La
       quinta, la software house, era gia' fatta bene con l'accento: 7,22:1. Passano tutte agli
       inchiostri, e sono state misurate una per una sul fondo reale della scheda:
       gruppo 5,03:1 - servizio 4,71:1 - attivita' 5,04:1 - nodo 5,15:1. */
    .client-extra-tags .etag.sw  { background:rgba(55,75,95,0.1); color:var(--accent); }
    .client-extra-tags .etag.grp { background:rgba(236,72,153,0.1); color:var(--pink-ink); }
    .client-extra-tags .etag.srv { background:rgba(20,184,166,0.1); color:var(--teal-ink); }
    .client-extra-tags .etag.att { background:rgba(245,158,11,0.1); color:var(--warning-ink); }
    .client-extra-tags .etag.nodo { background:rgba(6,182,212,0.1); color:var(--info-ink); }

    .servizio-group { display:flex; flex-wrap:wrap; gap:var(--sp-2); }
    .servizio-group label {
        display:flex; align-items:center; gap:var(--sp-1);
        padding:var(--sp-1) var(--sp-3); background:var(--bg-tertiary); border-radius:var(--radius-xs);
        cursor:pointer; font-size:var(--fs-footnote);
    }

    .contract-badge {
        display:inline-flex; align-items:center; gap:var(--sp-1);
        padding:var(--sp-1) var(--sp-2); border-radius:var(--radius-xs); font-size:var(--fs-caption-2); font-weight: var(--fw-semibold);
    }
    /* 3.44.15: "In Scadenza" era l'unico stato del contratto che non si leggeva (1,85:1), accanto
       ad "Attivo" e "Scaduto" che si leggono benissimo: due righe su cinque non erano state
       portate agli inchiostri. Ora 4,96:1; "Disdetto" da 3,87:1 a 6,16:1. */
    .contract-badge.active { background:var(--success-soft); color:var(--success-ink); }
    .contract-badge.expiring { background:rgba(245,158,11,0.12); color:var(--warning-ink); }
    .contract-badge.expired { background:var(--danger-soft); color:var(--danger-ink); }
    .contract-badge.disdetto { background:rgba(100,116,139,0.12); color:var(--slate-ink); }
    .contract-badge.none { background:var(--bg-tertiary); color:var(--text-muted); }

    .paid-flag {
        display:inline-flex; align-items:center; gap:var(--sp-1);
        padding:var(--sp-1) var(--sp-2); border-radius:var(--radius-sm); font-size:var(--fs-caption-2); font-weight: var(--fw-bold);
    }
    /* 3.44.15: "Pagato" e "Non Pagato" avevano come testo i colori pieni --success e --danger, che
       la nota in cima al foglio riserva a barre, punti e icone: come TESTO non arrivano alla
       soglia (2,81:1 e 3,89:1). Con gli inchiostri: 5,18:1 e 5,21:1. */
    .paid-flag.paid { background:rgba(34,197,94,0.12); color:var(--success-ink); }
    .paid-flag.unpaid { background:rgba(239,68,68,0.12); color:var(--danger-ink); }
    .contract-upload-zone {
        border:2px dashed var(--border-color); border-radius:var(--radius-xs);
        padding:var(--sp-4); text-align:center; cursor:pointer;
        transition: var(--transition); color:var(--text-muted); font-size:var(--fs-caption-1);
    }
    .contract-upload-zone:hover, .contract-upload-zone.dragover {
        border-color:var(--accent); background:var(--accent-soft); color:var(--accent);
    }
    .contract-file-display {
        display:flex; align-items:center; gap:var(--sp-3);
        padding:var(--sp-3) var(--sp-3); background:var(--bg-tertiary); border-radius:var(--radius-xs);
        font-size:var(--fs-footnote);
    }
    .contract-file-display .cf-info { flex:1; min-width:0; }
    .contract-file-display .cf-name {
        font-weight: var(--fw-medium); max-width:250px;
        overflow-wrap:break-word; word-break:break-word; color:var(--text-primary);
    }
    .contract-file-display .cf-meta { font-size:var(--fs-caption-2); color:var(--text-muted); }
    .contract-file-display .cf-actions { display:flex; align-items:center; gap:var(--sp-2); flex-shrink:0; }
    @media (max-width: 768px) {
        .contract-file-display { flex-wrap:wrap; }
        .contract-file-display .cf-actions { flex-direction:column; align-items:stretch; width:100%; gap:var(--sp-2); }
        .contract-file-display .cf-actions .btn { width:100%; justify-content:center; }
    }

    .client-att-zone { border:2px dashed var(--border-color); border-radius:var(--radius-xs); padding:var(--sp-3); text-align:center; cursor:pointer; color:var(--text-muted); font-size:var(--fs-caption-1); transition: var(--transition); }
    .client-att-zone:hover, .client-att-zone.dragover { border-color:var(--accent); background:var(--accent-soft); color:var(--accent); }
    .client-att-list { display:flex; flex-direction:column; gap:var(--sp-1); margin-top:var(--sp-2); }
    .client-att-item { display:flex; align-items:center; gap:var(--sp-2); padding:var(--sp-2) var(--sp-3); background:var(--bg-tertiary); border-radius:var(--radius-xs); font-size:var(--fs-caption-1); }
    .client-att-item .att-icon { font-size:var(--fs-callout); color:var(--accent); flex-shrink:0; }
    .client-att-item .att-name { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-weight: var(--fw-medium); color:var(--text-primary); }
    .client-att-item .att-size { color:var(--text-muted); font-size:var(--fs-caption-2); white-space:nowrap; }
    .client-att-item .att-del { color:var(--danger-ink); cursor:pointer; padding:var(--sp-05) var(--sp-1); border:none; background:none; font-size:var(--fs-subhead); transition: var(--transition); }
    .client-att-item .att-del:hover { background:var(--danger-soft); border-radius:var(--radius-xs); }
    .client-att-item a { color:var(--accent); text-decoration:none; font-size:var(--fs-subhead); transition: var(--transition); }
    .client-att-item a:hover { color:var(--accent-hover); }

    .sh-list-item {
        display:flex; align-items:center; gap:var(--sp-2);
        padding:var(--sp-2) var(--sp-3); background:var(--bg-tertiary); border-radius:var(--radius-xs);
        margin-bottom:var(--sp-1); font-size:var(--fs-footnote); transition: background var(--dur-fast) var(--ease);
    }
    .sh-list-item:hover { background:var(--bg-hover); }
    .sh-list-item .sh-name { flex:1; min-width:0; }
    .sh-list-item .sh-name input {
        background:transparent; border:none; font-size:var(--fs-footnote); width:100%;
        color:var(--text-primary); outline:none; padding:0;
    }
    .sh-list-item .sh-name input:focus {
        border-bottom:1px solid var(--accent); padding-bottom:var(--sp-05);
    }
    .sh-list-item .sh-remove {
        cursor:pointer; color:var(--text-muted); background:none;
        border:none; font-size:var(--fs-subhead); padding:0 var(--sp-1); line-height:1;
        transition: color var(--dur-fast) var(--ease);
    }
    .sh-list-item .sh-remove:hover { color:var(--danger-ink); }

    .import-preview-table { width:100%; border-collapse:collapse; font-size:var(--fs-caption-1); }
    .import-preview-table th {
        position:sticky; top:0; background:var(--bg-card); padding:var(--sp-2) var(--sp-2);
        text-align:left; font-weight: var(--fw-semibold); border-bottom:2px solid var(--border-color);
        white-space:nowrap; font-size:var(--fs-caption-2); color:var(--text-muted);
    }
    .import-preview-table td {
        padding:var(--sp-1) var(--sp-2); border-bottom:1px solid var(--border-color);
        white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:180px;
        transition: var(--transition);
    }
    .import-preview-table tr:hover td { background:var(--bg-hover); }
    .import-preview-table tr.import-skip { opacity:0.4; }
    .import-preview-table tr.import-merge td { background:rgba(245,158,11,0.06); }
    .import-stats { display:flex; gap:var(--sp-4); margin-bottom:var(--sp-4); flex-wrap:wrap; }
    .import-stats .is { text-align:center; padding:var(--sp-3) var(--sp-4); background:var(--bg-tertiary); border-radius:var(--radius-xs); flex:1; min-width:80px; }
    .import-stats .is-val { font-size:var(--fs-title-3); line-height: var(--lh-title-3); font-weight: var(--fw-bold); }
    .import-stats .is-lbl { font-size:var(--fs-caption-2); color:var(--text-muted); }
    .import-file-zone {
        border:2px dashed var(--border-color); border-radius:var(--radius-sm);
        padding:var(--sp-8); text-align:center; cursor:pointer; transition: var(--transition);
        color:var(--text-muted);
    }
    .import-file-zone:hover, .import-file-zone.dragover {
        border-color:var(--accent); background:var(--accent-soft); color:var(--accent);
    }

    .import-mapping-table { width:100%; border-collapse:collapse; }
    .import-mapping-table th {
        padding:var(--sp-2) var(--sp-3); text-align:left; font-size:var(--fs-caption-2); font-weight: var(--fw-bold);
        color:var(--text-muted); text-transform:uppercase; border-bottom:2px solid var(--border-color);
    }
    .import-mapping-table td { padding:var(--sp-2) var(--sp-3); border-bottom:1px solid var(--border-color); font-size:var(--fs-footnote); }
    .import-mapping-table .col-excel { font-weight: var(--fw-semibold); color:var(--text-primary); }
    .import-mapping-table .col-sample { font-size:var(--fs-caption-2); color:var(--text-muted); max-width:180px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
    .import-mapping-table select { font-size:var(--fs-caption-1); padding:var(--sp-1) var(--sp-2); }
    .import-mapping-table .mapped { background:rgba(34,197,94,0.04); }
    .import-mapping-table .mapped .col-excel::after { content:' ✓'; color:var(--success-ink); font-size:var(--fs-caption-2); }

    .import-steps { display:flex; gap:var(--sp-1); margin-bottom:var(--sp-5); }
    .import-step-dot {
        flex:1; height:4px; border-radius:var(--radius-xs); background:var(--bg-tertiary); transition: background var(--dur-slow) var(--ease);
    }
    .import-step-dot.active { background:var(--accent); }
    .import-step-dot.done { background:var(--success); }

    .merge-options { display:flex; gap:var(--sp-3); margin-bottom:var(--sp-4); }
    .merge-option {
        flex:1; padding:var(--sp-3); border:2px solid var(--border-color); border-radius:var(--radius-xs);
        cursor:pointer; text-align:center; transition: var(--transition); font-size:var(--fs-footnote);
    }
    .merge-option:hover { border-color:var(--accent); }
    .merge-option.selected { border-color:var(--accent); background:var(--accent-soft); }
    .merge-option i { display:block; font-size:var(--fs-title-2); line-height: var(--lh-title-2); margin-bottom:var(--sp-2); color:var(--accent); }
    .merge-option .mo-title { font-weight: var(--fw-semibold); margin-bottom:var(--sp-05); }
    .merge-option .mo-desc { font-size:var(--fs-caption-2); color:var(--text-muted); }

    .bulk-bar {
        display:none; align-items:center; gap:var(--sp-3);
        padding:var(--sp-3) var(--sp-4); margin-bottom:var(--sp-3);
        background:rgba(55,75,95,0.92); color:#fff; border-radius:var(--radius-sm);
        -webkit-backdrop-filter:blur(10px); backdrop-filter:blur(10px);
        box-shadow:0 6px 20px rgba(55,75,95,0.30), inset 0 1px 0 rgba(255,255,255,0.25);
        font-size:var(--fs-footnote); font-weight: var(--fw-medium); animation: toastIn var(--dur-base) var(--ease-out) both;
    }
    .bulk-bar.visible { display:flex; }
    .bulk-bar .bulk-count { font-weight: var(--fw-bold); }
    .bulk-bar .btn { color:#fff; border-color:rgba(255,255,255,0.3); }
    .bulk-bar .btn:hover { background:rgba(255,255,255,0.15); }
    .bulk-bar .btn-danger { background:var(--danger); border-color:var(--danger); }
    /* 3.44.10: l'effetto al passaggio era identico allo stato a riposo (#dc2626 e' esattamente
       var(--danger)), quindi il pulsante non reagiva. Ora usa il rosso piu' scuro. */
    .bulk-bar .btn-danger:hover { background:var(--danger-ink); border-color:var(--danger-ink); }
    .bulk-status-btn { border-radius:var(--radius-xs) !important; padding:var(--sp-1) var(--sp-3) !important; }

    .export-menu {
        display:none; position:fixed;
        background:var(--bg-card); border:1px solid var(--border-color);
        border-radius:var(--radius-sm); box-shadow: var(--shadow);
        min-width:200px; padding:var(--sp-1); z-index: var(--z-toast);
    }
    .export-menu.open { display:block; }
    .export-menu button {
        display:flex; align-items:center; gap:var(--sp-2); width:100%; text-align:left;
        padding:var(--sp-2) var(--sp-3); border:none; background:none; cursor:pointer;
        border-radius:var(--radius-xs); font-size:var(--fs-footnote); transition: background var(--dur-fast) var(--ease);
        color:var(--text-primary);
    }
    .export-menu button:hover { background:var(--bg-tertiary); }

    .client-card-cb {
        position:absolute; top:12px; left:12px; z-index:2;
        width:18px; height:18px; cursor:pointer; accent-color:var(--accent);
    }
    .client-card { position:relative; }
    .client-card.selected { border-color:var(--accent); box-shadow: var(--shadow-xs); }

    .adv-filters-panel {
        background:var(--bg-card); border:1px solid var(--border-color);
        border-radius:var(--radius-xs); margin-bottom:var(--sp-4); overflow:hidden;
        transition: var(--transition);
    }
    .adv-filters-toggle {
        display:flex; align-items:center; justify-content:space-between;
        padding:var(--sp-3) var(--sp-4); cursor:pointer; user-select:none; transition: var(--transition);
    }
    .adv-filters-toggle:hover { background:var(--bg-tertiary); }
    .adv-filters-toggle h4 { font-size:var(--fs-footnote); font-weight: var(--fw-semibold); margin:0; display:flex; align-items:center; gap:var(--sp-2); }
    .adv-filters-toggle .af-badge {
        display:inline-flex; align-items:center; justify-content:center;
        min-width:20px; height:20px; padding:0 var(--sp-2); border-radius:var(--radius-sm);
        background:var(--accent); color:#fff; font-size:var(--fs-caption-2); font-weight: var(--fw-bold);
    }
    .adv-filters-toggle .af-chevron { transition: transform var(--dur-base) var(--ease); font-size:var(--fs-subhead); color:var(--text-muted); }
    .adv-filters-toggle.open .af-chevron { transform:rotate(180deg); }
    .adv-filters-body { padding:0 var(--sp-4) var(--sp-4); display:none; }
    .adv-filters-body.open { display:block; }
    .adv-filters-grid {
        display:grid; grid-template-columns:repeat(auto-fill, minmax(200px, 1fr)); gap:var(--sp-3);
    }
    .adv-filters-grid .form-group { margin-bottom:0; }
    .adv-filters-grid .form-group label { font-size:var(--fs-caption-2); font-weight: var(--fw-semibold); color:var(--text-muted); margin-bottom:var(--sp-1); }
    .adv-filters-grid .form-select, .adv-filters-grid .form-control { font-size:var(--fs-caption-1); padding:var(--sp-2) var(--sp-3); }
    .adv-filters-actions {
        display:flex; align-items:center; gap:var(--sp-2); margin-top:var(--sp-4);
        padding-top:var(--sp-3); border-top:1px solid var(--border-color);
    }
    .adv-filters-actions .af-count { font-size:var(--fs-caption-1); color:var(--text-muted); margin-left:auto; }

    .view-toggle { display:inline-flex; border:1px solid var(--border-color); border-radius:var(--radius-xs); overflow:hidden; }
    .view-toggle button {
        background:var(--bg-card); border:none; padding:var(--sp-2) var(--sp-3); cursor:pointer;
        color:var(--text-muted); font-size:var(--fs-subhead); transition: var(--transition); line-height:1;
    }
    .view-toggle button:not(:last-child) { border-right:1px solid var(--border-color); }
    .view-toggle button.active { background:var(--accent); color:#fff; }
    .view-toggle button:hover:not(.active) { background:var(--bg-tertiary); }

    .clients-list-wrap { display:none; }
    .clients-list-wrap.active { display:flex; flex-direction:column; flex:1; min-height:0; }
    .clients-grid.hidden { display:none; }
    .clients-list-wrap .card { display:flex; flex-direction:column; flex:1; min-height:0; overflow:hidden; }
    .clients-list-wrap .card-body {
        flex:1; min-height:0; overflow:auto; -webkit-overflow-scrolling:touch; padding-bottom:0;
    }
    .clients-list-wrap .card-body::-webkit-scrollbar { width:6px; height:8px; }
    .clients-list-wrap .card-body::-webkit-scrollbar-track { background:var(--bg-primary); border-radius:var(--radius-xs); }
    .clients-list-wrap .card-body::-webkit-scrollbar-thumb { background:var(--border-color); border-radius:var(--radius-xs); }
    .clients-list-wrap .card-body::-webkit-scrollbar-thumb:hover { background:var(--border-light); }
    .clients-list-wrap .card-body::-webkit-scrollbar-corner { background:transparent; }

    /* 3.44.15: era l'unica altezza del foglio senza la coppia dvh. Su iOS 100vh e' l'altezza con
       la barra degli indirizzi RITRATTA, cioe' 80-90px in piu' di quella visibile: il fondo
       dell'elenco clienti e la barra di paginazione cadevano sotto il bordo dello schermo dentro un
       contenitore che ritaglia, quindi non era lo scorrimento dell'elenco a riportarli su e la fine
       dell'elenco restava fuori corsa. Tutte le altre altezze hanno la coppia da versioni; la chat
       e' arrivata a sottrarre anche l'incavo dopo un difetto identico. */
    .clients-page-wrap {
        display:flex; flex-direction:column;
        height:calc(100vh - var(--header-height) - 56px);
        height:calc(100dvh - var(--header-height) - env(safe-area-inset-top) - 56px);
        overflow:hidden;
    }
    .clients-page-wrap .filter-bar { flex-shrink:0; }
    .clients-page-wrap .adv-filters-panel { flex-shrink:0; }
    .clients-page-wrap .bulk-bar { flex-shrink:0; }
    .clients-page-wrap .clients-grid { flex:1; overflow-y:auto; min-height:0; }
    .clients-page-wrap .clients-list-wrap.active { flex:1; min-height:0; }
    .clients-list { width:100%; border-collapse:collapse; table-layout:fixed; min-width:1500px; }
    .clients-list thead th {
        position:sticky; top:0; background:var(--bg-card); padding:var(--sp-2) var(--sp-2);
        text-align:left; font-size:var(--fs-caption-2); font-weight: var(--fw-bold); color:var(--text-muted);
        text-transform:uppercase; letter-spacing:0.3px;
        border-bottom:2px solid var(--border-color); 
    /* 3.44.11-e: le intestazioni possono andare a capo. Con nowrap le colonne a larghezza fissa
       (80px) non contenevano etichette come CONTRATTO o COD. FORNITORE, e il testo con la freccina
       di ordinamento sbordava sulla colonna accanto (gia' prima di 5px; con il corpo portato a
       11px si arrivava a 14). Su due righe ci stanno e non sporge nulla. */
    white-space:normal; line-height:1.25; overflow-wrap:anywhere;
        user-select:none; z-index:1;
    }
    .clients-list thead th.sortable { cursor:pointer; transition: var(--transition); }
    .clients-list thead th.sortable:hover { color:var(--accent); }
    .clients-list thead th .sort-icon { font-size:var(--fs-caption-2); margin-left:var(--sp-05); opacity:0.4; }
    .clients-list thead th.sort-asc .sort-icon,
    .clients-list thead th.sort-desc .sort-icon { opacity:1; color:var(--accent); }
    .clients-list tbody tr {
        cursor:pointer; transition: background var(--dur-fast) var(--ease);
        border-bottom:1px solid var(--border-color);
    }
    .clients-list tbody tr:hover { background:var(--bg-hover); }
    .clients-list tbody td {
        padding:var(--sp-2) var(--sp-2); font-size:var(--fs-caption-1);
        overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
    }

    .clients-list .col-cb { width:36px; min-width:36px; max-width:36px; }
    .clients-list .col-name { width:200px; min-width:160px; }
    .clients-list .col-sm { width:80px; min-width:70px; }
    .clients-list .col-md { width:110px; min-width:90px; }
    .clients-list .col-lg { width:140px; min-width:110px; }
    /* 3.44.11-e: 84px e non 70. La cella ha 8px di riempimento per lato, quindi ne restavano 54
   per due pulsanti che ne chiedono 64: sbordavano nel riempimento. Su schermo tattile i
   pulsanti salgono a 44px e la colonna si allarga di conseguenza (vedi blocco pointer:coarse). */
.clients-list .col-act { width:84px; min-width:84px; max-width:84px; }
    .clients-list .cl-name-cell { display:flex; align-items:center; gap:var(--sp-2); }
    .clients-list .cl-name-cell .client-avatar { width:28px; height:28px; font-size:var(--fs-caption-2); border-radius:var(--radius-xs); flex-shrink:0; }
    .clients-list .cl-name-cell .cl-name { font-weight: var(--fw-semibold); font-size:var(--fs-caption-1); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
    .clients-list .cl-name-cell .cl-contact { font-size:var(--fs-caption-2); color:var(--text-muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
    .clients-list .cl-actions { display:flex; gap:var(--sp-05); opacity:0; transition: opacity var(--dur-fast) var(--ease); }
    .clients-list tbody tr:hover .cl-actions { opacity:1; }

    .toast-container {
        position: fixed; top: calc(20px + env(safe-area-inset-top)); right: calc(20px + env(safe-area-inset-right)); z-index: var(--z-toast);
        display: flex; flex-direction: column; gap:var(--sp-2); pointer-events: none;
    }
    .toast {
        padding:var(--sp-3) var(--sp-5); border-radius: var(--radius-sm);
        font-size:var(--fs-footnote); font-weight: var(--fw-medium); pointer-events: auto;
        /* 3.44.7: stessa durata della barra di selezione multipla, che usa lo stesso
           fotogramma: prima entravano a due velocita diverse (0,2s contro 0,3s). */
        animation: toastIn var(--dur-base) var(--ease-out) both; box-shadow: var(--shadow-sm);
        display: flex; align-items: center; gap:var(--sp-2); max-width: 360px;
    }
    /* 3.44.10: qui c'erano .toast.success/.error/.info, scritture morte: gli stessi tre
       selettori sono ridichiarati piu' avanti, allo stesso peso ma dopo, quindi vincevano
       sempre quelli. Restano solo la' sotto, in un punto solo. */
    @keyframes toastIn { from { opacity:0; transform:translateX(40px); } to { opacity:1; transform:translateX(0); } }
    @keyframes toastOut { from { opacity:1; transform:translateX(0); } to { opacity:0; transform:translateX(40px); } }

    .btn-loading { position: relative; pointer-events: none; opacity: 0.7; }
    .btn-loading::after {
        content: ''; position: absolute; width: 14px; height: 14px;
        border: 2px solid transparent; border-top-color: currentColor;
        border-radius:50%; animation: mfSpin var(--dur-spin) linear infinite;
        top: 50%; left: 50%; margin:-7px 0 0 -7px;
    }

    .fc-page-header { display:flex; align-items:center; justify-content:space-between; margin-bottom:var(--sp-5); flex-wrap:wrap; gap:var(--sp-3); }
    .fc-page-header h3 { font-size:var(--fs-title-3); line-height: var(--lh-title-3); font-weight: var(--fw-semibold); display:flex; align-items:center; gap:var(--sp-2); }
    .fc-project-selector { display:flex; align-items:center; gap:var(--sp-2); }
    .fc-project-selector .form-select { width:auto; min-width:200px; }

    .permissions-table { width:100%; border-collapse:collapse; font-size:var(--fs-caption-1); min-width:900px; }
    .permissions-table th, .permissions-table td { padding:var(--sp-2) var(--sp-2); text-align:center; border-bottom:1px solid var(--border-color); }
    .permissions-table th { font-size:var(--fs-caption-2); font-weight: var(--fw-bold); color:var(--text-muted); text-transform:uppercase; letter-spacing:0.3px; white-space:nowrap; }
    .perm-col-header span { display:inline-block; max-width:70px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:var(--fs-caption-2); }
    .perm-toggle { position:relative; display:inline-block; width:32px; height:18px; cursor:pointer; }
    .perm-toggle input { opacity:0; width:0; height:0; }
    .perm-slider { position:absolute; top:0; left:0; right:0; bottom:0; background:var(--border-color); border-radius:var(--radius-xs); transition: var(--transition); }
    .perm-slider::before { content:''; position:absolute; width:14px; height:14px; left:2px; bottom:2px; background:#fff; border-radius:50%; transition: var(--transition); }
    .perm-toggle input:checked + .perm-slider { background:var(--success); }
    .perm-toggle input:checked + .perm-slider::before { transform:translateX(14px); }
    .permissions-table tbody tr { transition: var(--transition); }
    .permissions-table tbody tr:hover { background:var(--bg-hover); }
    .permissions-table tbody tr:hover td[style*="sticky"] { background:var(--bg-hover) !important; }

    .main-content {
        max-width: calc(100vw - var(--sidebar-width));
        overflow-x: hidden;
    }
    .content-header {
        min-width: 0;
        overflow: visible;
    }
    .content-header .header-actions {
        flex-shrink: 1;
        min-width: 0;
    }
    .content-body {
        overflow-x: auto;
        min-width: 0;
    }
    .filter-bar {
        min-width: 0;
        overflow-x: auto;
        flex-wrap: wrap;
    }

    @media (max-width: 1280px) {
        .adv-filters-grid { grid-template-columns:repeat(auto-fill, minmax(170px, 1fr)); }
        .sync-status-cards { grid-template-columns:repeat(3, 1fr) !important; }
    }

    @media (max-width: 1024px) {
        .content-body { padding:var(--sp-5); }
        .content-header { padding:0 var(--sp-4); }
        .content-header h2 { width:30%; }
        .content-header .header-actions { width:70%; }
        .clients-grid { grid-template-columns:repeat(auto-fill, minmax(280px, 1fr)); }
        .sync-conn-form { grid-template-columns:1fr 1fr !important; }
    }

    @media (max-width: 768px) {

        .main-content { max-width:100vw; margin-left:0; }
        .sidebar { transform:translateX(-100%); }
        .sidebar.open { transform:translateX(0); z-index: var(--z-drawer); }
        .content-body { padding:var(--sp-4); overflow-x:auto; }
        .content-header { padding:0 var(--sp-3); gap:var(--sp-2); }
        /* 3.44.11-b: il titolo NON si comprime. Con flex-shrink:1 perdeva la contesa contro la
           ricerca (flex:1 1 auto) e finiva troncato molto prima del limite: "Team" diventava
           "Te…". Ora cede spazio solo la ricerca, che ha del testo scorrevole al suo interno. */
        .content-header h2 { font-size:var(--fs-headline); line-height: var(--lh-headline); font-weight:var(--fw-semibold); min-width:0; width:auto; flex-shrink:0; max-width:60%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

        /* 3.44.12-b: l'intestazione DENTRO il contenuto (Cloud, Vault) porta una barra di
           strumenti, non tre icone: su iPhone non ci sta accanto al titolo e non deve provarci.
           Va a capo sulla propria riga e i pulsanti si dispongono su piu' righe. Il titolo qui
           puo' prendersi tutta la larghezza, perche' non ha piu' niente accanto. */
        .content-body .content-header { flex-wrap:wrap; row-gap:var(--sp-3); }
        .content-body .content-header h2 { max-width:100%; }
        .content-body .content-header .header-actions {
            width:auto; flex:1 1 auto; min-width:0; margin-left:auto;
            flex-wrap:wrap; justify-content:flex-end; gap:var(--sp-2);
        }
        /* 3.44.12-b (rifinitura): andare a capo funzionava, ma le misure erano casuali. Su Cloud
           a 440px veniva [140][135] / [157][108], quattro larghezze diverse appoggiate a destra:
           si legge come un incidente, non come una scelta. Ora le celle sono dichiarate. La base
           "meta' meno il divario" fa entrare esattamente due comandi per riga (tre farebbero
           150% e non ci stanno) e, a parita' di base e di crescita, le due celle risultano
           identiche; le righe partono dal bordo sinistro e arrivano al destro. I tetti di
           larghezza scritti nell'attributo style delle singole voci (200px sulla categoria del
           Vault) sono misure pensate per il monitor: qui impedirebbero alla cella di riempire la
           riga, quindi vanno tolti per questa fascia. Sotto i 480px si passa a una voce per riga
           (vedi piu' avanti): con celle da 121px le etichette lunghe — "Nuovo file di testo" ne
           chiede 157 — verrebbero tagliate, perche' i pulsanti non mandano a capo. */
        .content-body .content-header .header-actions > * {
            flex:1 1 calc(50% - var(--sp-3)); min-width:0; width:auto;
            max-width:100% !important;
        }
        .content-body .content-header .header-actions .vault-search { display:flex; }
        .content-body .content-header .header-actions .vault-search input { width:100%; }

        /* 3.44.12-b: la barra dei filtri come "striscia da scorrere" (nowrap + overflow-x:auto)
           e' il trattamento giusto per una fila di pastiglie tutte uguali: si capisce che
           continua e la si trascina. Qui pero' le barre non sono pastiglie, sono controlli di
           modulo piu' il comando principale della schermata, e la striscia li nasconde: su
           iPhone la barra di scorrimento non si vede a riposo, quindi non c'e' nessun segno che
           ci sia dell'altro; una tendina tagliata a meta' non si legge come "scorri", si legge
           come rotto. Misurato a 440px da amministratore: Compiti 1334px di contenuto dentro
           414 (quattro controlli su sei irraggiungibili), Progetti 1109, Kanban 781 con "Nuovo
           Compito" a x=621, Clienti 607. Il pulsante che crea la cosa che la schermata elenca
           non puo' stare fuori schermo. La regola di base della barra e' gia' flex-wrap:wrap:
           qui si smette di contraddirla. Con flex-grow:1 le voci che finiscono sulla stessa
           riga si allargano a riempirla, cosi' non resta il bordo destro frastagliato, e
           siccome la crescita puo' solo allargare non taglia nessuna etichetta.
           L'overflow-x:auto resta come rete di sicurezza: se un giorno una voce non riducibile
           supera da sola la riga, a scorrere dev'essere la barra, non la pagina. */
        .filter-bar { gap:var(--sp-2); row-gap:var(--sp-2); overflow-x:auto; -webkit-overflow-scrolling:touch; flex-wrap:wrap; padding-bottom:var(--sp-3); }
        .filter-bar > * { flex-grow:1; max-width:100%; }
        /* 3.44.12-b: il gruppo di navigazione del Calendario e' l'unico blocco della barra che
           non si riduce: due frecce da 44, "Oggi" da 59 e un titolo con min-width:190px scritto
           nell'attributo style fanno 361px fissi dentro i 270 disponibili a 320px. Finche' la
           barra scorreva la cosa restava nascosta li' dentro; smettendo di scorrere usciva nel
           contenuto e tornava a trascinare di lato l'intera pagina (misurato: .content-body
           scrollWidth 386 contro 320) — cioe' esattamente il difetto segnalato dall'utente, in
           un altro punto. I 190px del titolo servono a tenere fermi i due pulsanti mentre il
           nome del mese cambia lunghezza: su monitor ha senso, su iPhone e' il doppio dello
           spazio che resta. */
        .cal-toolbar > div { flex-wrap:wrap; justify-content:center; }
        #calTitle { min-width:0 !important; flex:1 1 auto; }
        /* NB: la ricerca resta fuori da questa riga. La soglia di 160px che ha nella regola di
           base e' una decisione della 3.44.11-d (senza, era l'unico elemento elastico della
           barra e si azzerava): togliendogliela tornava a 39px su Agenti. Qui devono poter
           cedere solo le tendine, che si dimensionano sull'opzione piu' lunga. */
        .filter-bar .form-select, .filter-bar .searchable-select { flex-shrink:1; min-width:0; }
        .filter-bar .btn { font-size:var(--fs-subhead); padding:var(--sp-2) var(--sp-3); white-space:nowrap; flex-shrink:0; }
        .filter-bar .btn .bi { margin-right:var(--sp-05); }
        .view-toggle { display:none; }
        #globalSearch { font-size:var(--fs-callout); }

        .sync-status-cards { grid-template-columns:repeat(2, 1fr) !important; }
        .sync-conn-form { grid-template-columns:1fr !important; }

        .clients-grid { grid-template-columns:1fr; gap:var(--sp-3); }
        .client-card { padding:var(--sp-4); }
        .client-card-header .client-avatar { width:36px; height:36px; font-size:var(--fs-footnote); }

        .clients-list-wrap.active { display:flex !important; }
        .clients-list-wrap .card-body { overflow:auto; -webkit-overflow-scrolling:touch; }
        .clients-list { min-width:1500px; }

        .kanban-column { min-width:240px; }

        .modal-overlay {
            /* 3.44.15: qui c'era "padding:var(--sp-3)", 12px secchi, che annullavano le zone
               sicure dichiarate nella regola di base: nell'app installata su iPhone il titolo
               della finestra e la X per chiuderla finivano sotto la barra di sistema — si
               vedeva il bordo della finestra e non si riusciva a chiuderla dall'alto. E' la
               STESSA regressione che il 3.44.7 aveva corretto nella regola di base (il suo
               commento la descrive parola per parola), ricomparsa in tre punti, tutti e tre
               dentro i blocchi per schermo stretto: cioe' esattamente dove morde. Il margine
               piu' stretto del telefono si tiene, ma senza mai scendere sotto l'incavo. */
            padding: max(var(--sp-3), env(safe-area-inset-top)) max(var(--sp-3), env(safe-area-inset-right))
                     max(var(--sp-3), env(safe-area-inset-bottom)) max(var(--sp-3), env(safe-area-inset-left));
        }
        .modal { max-width:100% !important; width:100%; max-height:90vh; border-radius:var(--radius); }
        .modal-header { padding:var(--sp-4) var(--sp-4); }
        .modal-header h3 { font-size:var(--fs-headline); line-height: var(--lh-headline); }
        .modal-body { padding:var(--sp-4) var(--sp-4); }
        .modal-footer { padding:var(--sp-3) var(--sp-4); flex-wrap:wrap; gap:var(--sp-2); }
        /* Sync DB: il corpo ha overflow:hidden inline -> su mobile va reso scrollabile.
           Serve un'altezza DEFINITA sul modale perche' il corpo (flex:1) possa scorrere. */
        #syncModal .modal { height:90vh !important; max-width:100% !important; }
        #syncModal .modal-body { overflow-y:auto !important; -webkit-overflow-scrolling:touch; }

        .modal [style*="grid-template-columns"]:not(.sync-status-cards):not(.sync-conn-form) { grid-template-columns:minmax(0, 1fr) !important; }

        /* 3.44.12-b: la stessa cosa serve FUORI dalle finestre. Report ha nell'attributo style
           una griglia "1fr 1fr 1fr" a colonne fisse: su iPhone diventa tre colonne strettissime
           — 58px a 320 (con 15px che escono dalla scheda e vengono tagliati, perche' il pulsante
           "Genera" non si stringe sotto la sua etichetta), 87 a 393, 110 a 440. Dentro ci sono
           due tendine che dicono "Agenti — attivita'" e "Questa settimana": a 58px non si legge
           niente. Le griglie scritte con repeat(auto-fit, minmax(...)) sanno gia' adattarsi da
           sole e restano fuori: il selettore prende solo le larghezze fisse. */
        .content-body [style*="grid-template-columns:1fr 1fr"] { grid-template-columns:minmax(0, 1fr) !important; }
        .content-body [style*="grid-template-columns:1fr 1fr"] > * { min-width: 0; }

        .adv-filters-grid { grid-template-columns:1fr 1fr; gap:var(--sp-2); }

        .sync-log { min-height:150px; max-height:300px; font-size:var(--fs-caption-2); }

        .team-card { padding:var(--sp-4); }
    }

    @media (max-width: 480px) {
        /* 3.44.10: tolto padding:var(--sp-3), mai applicato: sotto i 480px vince il var(--sp-3)
           dichiarato piu' avanti allo stesso punto di rottura. overflow-x resta, quello agisce. */
        .content-body { overflow-x:auto; }
        .content-header { padding:0 var(--sp-2); gap:var(--sp-1); }
        /* 3.44.11: il titolo della schermata era sceso a 13px sul telefono. Apple usa Headline
           (17 semibold) per il titolo della barra di navigazione; qui si tronca invece di
           rimpicciolire, lasciando spazio alla ricerca accanto. */
        /* 3.44.12 (secondo giro): il titolo torna a poter cedere spazio. Con flex-shrink:0 non
           cedeva mai, e siccome i comandi a destra non ci stavano nella fetta che il flex gli
           lasciava (tre bersagli da 44 piu' due spazi fanno 156px, la fetta ne dava 122) quei
           comandi traboccavano ALL'INDIETRO, cioe' sopra il titolo: misurato a 320px, la lente
           della ricerca era stampata sopra le ultime lettere in sei schermate su quattordici —
           30px su "Registro Attività", 25 su "Password Vault", 14 su "Kanban Board". Il titolo
           non se ne accorgeva nemmeno (scrollWidth = clientWidth: per lui ci stava tutto), quindi
           i puntini di sospensione non comparivano e la parola finiva semplicemente sotto
           un'icona. Ora i comandi non si stringono (le loro misure sono fisse e sono quelle
           minime toccabili) e a cedere e' il titolo, che ha gia' i puntini pronti: e' il verso
           giusto, ed e' anche quello che dice la nota qui sopra ("si tronca invece di
           rimpicciolire"). Il margine automatico li tiene appoggiati a destra come prima, ora che
           non sono piu' loro a riempire lo spazio che avanza. */
        .content-header h2 { font-size:var(--fs-headline); line-height: var(--lh-headline); font-weight:var(--fw-semibold); width:auto; flex-shrink:1; min-width:0; max-width:60%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
        /* Lo spazio FRA i comandi scende da 12 a 4px, lo stesso gia' usato dalla barra qui sopra.
           Non tocca i bersagli, che restano 44x44 attaccati: e' solo aria fra un'icona e l'altra,
           e i 16px che si recuperano vanno al titolo — abbastanza perche' a 320px "Impostazioni"
           torni a starci per intero invece di finire "Impostazion…". */
        /* 3.44.12-b: il selettore era `.content-header .header-actions`, che pero' vale per DUE
           barre diverse. Quella in cima alla pagina ha tre icone da 44px ed e' giusto che non
           si stringa. Ma Cloud e Vault hanno una SECONDA intestazione dentro il contenuto, con
           la barra degli strumenti (categoria, Preferite, ricerca, Nuova password): 592px di
           roba. Con flex:0 0 auto quel contenitore si dimensiona sul contenuto, quindi il
           flex-wrap:wrap non entra mai in funzione e la barra esce di 152px dallo schermo di
           un iPhone da 440px. E siccome .content-body e' overflow-x:auto, a scorrere non era
           la barra: era tutta la pagina, che si trascinava di lato lasciando fuori il bordo
           sinistro. Ora la regola vale solo per la barra in cima. */
        .main-content > .content-header .header-actions { width:auto; flex:0 0 auto; margin-left:auto; min-width:0; gap:var(--sp-1); }
        /* 3.44.12-b: sotto i 480px — cioe' su OGNI iPhone tenuto in verticale, dal 320 al 440 —
           la barra degli strumenti di Cloud e Vault mette una voce per riga. Due per riga qui
           non si possono: a 320px una cella misurerebbe 121px e "Nuovo file di testo" ne chiede
           157, e i pulsanti hanno white-space:nowrap, quindi l'etichetta verrebbe tagliata
           invece di andare a capo. Una per riga e' anche la forma che iOS usa per le liste di
           azioni: stessa larghezza, stessa altezza, nessun bordo frastagliato. */
        .content-body .content-header .header-actions > * { flex-basis:100%; }
        .search-box input { font-size:var(--fs-callout); padding:var(--sp-2) var(--sp-3) var(--sp-2) var(--sp-8); }

        /* 3.44.11-c — RICERCA A SCOMPARSA (schema della barra di navigazione iOS).
           Sotto i 480px tre cose vogliono la stessa riga: il titolo a 17px, la ricerca e il gruppo
           campanella+esci da 108px. Non ci stanno: o si tronca il titolo o la ricerca si riduce a
           un francobollo — a 320px restavano 46px di campo, cioe' 2px di area digitabile una volta
           tolta l'icona. Apple risolve cosi': la ricerca resta un'icona da 44px e si apre a tutta
           larghezza solo quando la si tocca. Nessun JavaScript: basta :focus-within. */
        .content-header .search-box { flex:0 0 var(--tap-min); min-width:var(--tap-min); }
        .content-header .search-box input {
            /* Da chiuso il campo NON ha riempimento. Con box-sizing:border-box la larghezza non
               puo' scendere sotto la somma di riempimento e bordi: 44px di riempimento sinistro
               piu' 2px di bordo facevano un campo di 46px dentro una scatola di 44, e sporgeva.
               Il testo comunque non si vede (segnaposto trasparente) e la lente, posizionata in
               assoluto a 14px, resta centrata nei 44px. */
            width: 100%; padding-left: 0; padding-right: 0;
            cursor: pointer; transition: none;
        }
        .content-header .search-box input::placeholder { opacity: 0; }
        .content-header .search-box:focus-within {
            position: absolute; left: var(--sp-2); right: var(--sp-2);
            top: 50%; transform: translateY(-50%);
            flex: none; min-width: 0; z-index: var(--z-dropdown);
        }
        .content-header .search-box:focus-within input {
            width: 100%; padding-left: var(--sp-10); padding-right: var(--sp-4); cursor: text;
            /* 3.44.12-r15: fondo PIENO. Ogni campo a fuoco dell'applicazione prende
               rgba(255,255,255,0.85), e va benissimo finche' dietro c'e' una scheda vuota. Qui
               no: aperto, questo campo si stende SOPRA il titolo della schermata e sopra
               campanella ed esci, e da quel 15% di trasparenza il titolo continuava a leggersi
               attraverso — "Cerca ovunque..." stampato sopra "Registro Attivita'", due testi
               sovrapposti nello stesso punto, con le due icone che spuntavano da dentro la
               pastiglia. Vale per tutte e quattordici le schermate sotto i 480px. Il tono e'
               quello che il campo ha gia' da chiuso, cioe' nessun cambio d'aspetto: cambia solo
               che adesso copre. */
            background: var(--bg-secondary);
        }
        .content-header .search-box:focus-within input::placeholder { opacity: 1; }

        /* 3.44.12-b: la riga del Registro Attivita' e' una fila flex che non va a capo, con
           dentro un orario a white-space:nowrap (90px) e un cestino portato a 44px dal minimo
           tattile. A 320px la riga chiede 314px dentro 254 disponibili: il cestino finiva a
           x=303 con il bordo della scheda a 287, e siccome la scheda e' overflow:hidden veniva
           tagliato via — su quella schermata l'amministratore non poteva cancellare niente.
           Diventa una griglia di due righe: avatar, testo e cestino sopra, orario sotto al
           testo. L'ordine del documento resta quello, cambia solo dove cade l'orario, e il
           testo guadagna larghezza a tutte le misure (a 320px passa da 108 a 158px). */
        .activity-item {
            display: grid;
            grid-template-columns: auto minmax(0, 1fr) auto;
            grid-template-areas: "act-av act-txt act-del" ". act-time .";
            align-items: start;
            column-gap: var(--sp-3); row-gap: var(--sp-1);
        }
        .activity-item > .avatar { grid-area: act-av; }
        .activity-item .activity-text { grid-area: act-txt; min-width: 0; }
        .activity-item .activity-time { grid-area: act-time; }
        .activity-item .activity-del { grid-area: act-del; margin-left: 0; }

        /* 3.44.12-b: nell'intestazione di scheda del Registro Attivita' i filtri stanno in un
           contenitore che si dimensiona sul contenuto. La tendina degli utenti si misura
           sull'opzione piu' lunga e arriva a 368px dentro una scheda larga 294: sporgeva di 74px
           a 320 e ancora di 4 a 430, e siccome la scheda e' overflow:hidden la freccia veniva
           tagliata. Non basta togliere il pavimento min-width scritto nell'attributo style: una
           <select> non si stringe sotto la sua opzione piu' lunga finche' non ha una larghezza
           DEFINITA da cui partire. Quindi il gruppo dei filtri prende una riga sua, larga quanto
           l'intestazione (flex-basis:100% si risolve sul contenitore, quindi e' definita), e da
           li' le tendine possono cedere. Titolo sopra, filtri sotto: e' la stessa forma gia'
           scelta per le schede della Dashboard. */
        .card-header { flex-wrap: wrap; row-gap: var(--sp-3); }
        .card-header > div:has(> .form-select), .card-header > div:has(> .form-control) {
            flex: 1 1 100%; min-width: 0;
        }
        .card-header .form-select, .card-header .form-control {
            min-width: 0 !important; max-width: 100%;
        }
        /* I comandi di pagina (4 pulsanti da 44px piu' "Pagina 1 / 2") chiedono 274px dentro i
           252 della barra a 320px: l'ultimo pulsante restava fuori. Vanno a capo. */
        .pg-ctrl { flex-wrap: wrap; justify-content: center; }

        /* 3.44.11: 11px di etichetta dentro un pulsante alto 44px erano sproporzionati e sotto il
           minimo leggibile: si scende a Subhead, non oltre. */
        .filter-bar .btn { padding:var(--sp-2) var(--sp-3); font-size:var(--fs-subhead); }

        .sync-status-cards { grid-template-columns:1fr 1fr !important; font-size:var(--fs-caption-2); }
        .sync-status-card .sc-val { font-size:var(--fs-title-3); line-height: var(--lh-title-3); }
        .sync-status-card .sc-lbl { font-size:var(--fs-caption-2); }

        .clients-grid { grid-template-columns:1fr; }
        .client-extra-tags { gap:var(--sp-1); }
        .etag { font-size:var(--fs-caption-2); padding:var(--sp-05) var(--sp-1); }

        .modal-overlay {
            /* 3.44.15: qui c'era "padding:var(--sp-3)", 12px secchi, che annullavano le zone
               sicure dichiarate nella regola di base: nell'app installata su iPhone il titolo
               della finestra e la X per chiuderla finivano sotto la barra di sistema — si
               vedeva il bordo della finestra e non si riusciva a chiuderla dall'alto. E' la
               STESSA regressione che il 3.44.7 aveva corretto nella regola di base (il suo
               commento la descrive parola per parola), ricomparsa in tre punti, tutti e tre
               dentro i blocchi per schermo stretto: cioe' esattamente dove morde. Il margine
               piu' stretto del telefono si tiene, ma senza mai scendere sotto l'incavo. */
            padding: max(var(--sp-3), env(safe-area-inset-top)) max(var(--sp-3), env(safe-area-inset-right))
                     max(var(--sp-3), env(safe-area-inset-bottom)) max(var(--sp-3), env(safe-area-inset-left));
        }
        .modal { border-radius:var(--radius); max-height:94vh; height:auto; }
        .modal-footer { flex-wrap:wrap; gap:var(--sp-2); }
        .modal-footer .btn { flex:1 1 auto; justify-content:center; }

        .adv-filters-grid { grid-template-columns:1fr; }

        .bulk-bar { font-size:var(--fs-footnote); padding:var(--sp-2) var(--sp-3); flex-wrap:wrap; gap:var(--sp-1); }
        .bulk-bar .btn { font-size:var(--fs-subhead); padding:var(--sp-1) var(--sp-3); }
    }

.task-has-comments {
    display:inline-flex; align-items:center; gap:var(--sp-1);
    font-size:var(--fs-caption-2); color:var(--warning-ink); font-weight: var(--fw-semibold);
    background:var(--warning-soft); padding:1px var(--sp-2); border-radius:var(--radius-xs);
}
.task-has-comments i { font-size:var(--fs-caption-2); }

.role-badge.supervisore { background:rgba(6,182,212,0.15); color:var(--info-ink); }

.att-preview {
    border:none; background:none; cursor:pointer; color:var(--accent);
    padding:var(--sp-05) var(--sp-1); border-radius:var(--radius-xs); font-size:var(--fs-subhead); transition: var(--transition);
}
.att-preview:hover { background:var(--accent-soft); }
/* 3.44.10: min-width:320px sommato ai 10px di margine della sovrapposizione chiedeva 340px
   dentro una finestra di 355px, e sotto i 340px la finestra sbordava. min(95vw,900px)
   tiene la stessa larghezza sugli schermi normali ma sui piu' stretti si adatta invece
   di rompersi; il tetto a 900px evita che su desktop diventi larghissima. */
#previewModal .modal { width: min(95vw, 900px); min-width: 0; }
#previewModal .modal-body img { background: repeating-conic-gradient(var(--bg-tertiary) 0% 25%, transparent 0% 50%) 50% / 20px 20px; }

.searchable-select { position:relative; }
.searchable-select-dropdown {
    display:none; position:absolute; top:calc(100% + 2px); left:0; right:0;
    background:var(--bg-card); border:1px solid var(--border-color);
    border-radius:var(--radius-sm); box-shadow: var(--shadow);
    max-height:200px; overflow-y:auto; z-index: var(--z-dropdown); padding:var(--sp-1);
}
.ss-option {
    padding:var(--sp-2) var(--sp-3); font-size:var(--fs-footnote); cursor:pointer; border-radius:var(--radius-xs);
    color:var(--text-primary); transition: background var(--dur-fast) var(--ease);
}
.ss-option:hover { background:var(--bg-hover); }
.ss-option.selected { background:var(--accent-soft); color:var(--accent); font-weight: var(--fw-semibold); }
/* 3.44.14: da oggi queste proposte si scorrono con le frecce (vedi app-init.js), ma senza un segno
   visibile la freccia muoveva qualcosa che non si vedeva: si premeva tre volte giu' e lo schermo
   restava identico, poi Invio sceglieva un nome a sorpresa. Serve percio' l'equivalente da tastiera
   di quello che il mouse ottiene passandoci sopra. Non basta ripetere lo sfondo del passaggio del
   mouse: la riga gia' scelta ha uno sfondo suo, e sovrapponendoli non si sarebbe piu' distinta la
   riga scelta da quella su cui si e' arrivati con le frecce. Si aggiunge quindi anche la barretta
   colorata a sinistra, che dice "sei qui" e non si confonde con nient'altro. La regola sta dopo
   quella della riga scelta apposta, per avere la meglio quando le due coincidono. */
.ss-option.ss-attiva { background:var(--bg-hover); box-shadow: inset 3px 0 0 var(--accent); }
.ss-option.hidden { display:none; }

.global-search-results {
    /* 3.44.15: mancava l'incavo. L'intestazione e' alta --header-height PIU' l'area sicura (vedi
       .content-header), ma questo pannello partiva da --header-height meno 4px: nell'app installata
       su iPhone l'elenco dei risultati veniva disegnato SOPRA il campo in cui si stava ancora
       scrivendo, coprendo il testo appena digitato. Le finestre a tutto schermo l'incavo lo sommano
       da sempre: era un'incoerenza interna, non una convenzione. */
    display:none; position:fixed; top:calc(var(--header-height) + env(safe-area-inset-top) - 4px);
    /* 3.43.48: su desktop parte dopo la sidebar (prima left:0 faceva sporgere il pannello
       risultati SOPRA la sidebar per 260px). Su mobile (sidebar off-canvas) torna a left:0. */
    left:var(--sidebar-width); right:0;
    max-height:calc(100dvh - var(--header-height) - env(safe-area-inset-top) + 4px); overflow-y:auto;
    background:var(--bg-card); border:1px solid var(--border-color);
    border-radius:0 0 var(--radius) var(--radius); box-shadow: var(--shadow-lg);
    z-index: var(--z-toast); padding:0;
}
.global-search-results::-webkit-scrollbar { width:4px; }
.global-search-results::-webkit-scrollbar-thumb { background:var(--border-color); border-radius:var(--radius-xs); }
.gsr-group { padding:0; }
.gsr-group + .gsr-group { border-top:1px solid var(--border-color); }
.gsr-grid {
    display:grid; grid-template-columns:repeat(auto-fill, minmax(280px, 1fr));
    gap:0;
}
.gsr-grid .gsr-group { border-right:1px solid var(--border-color); border-top:none; }
.gsr-grid .gsr-group:last-child { border-right:none; }
.gsr-group-title {
    font-size:var(--fs-caption-2); font-weight: var(--fw-bold); text-transform:uppercase; letter-spacing:0.5px;
    padding:var(--sp-3) var(--sp-4) var(--sp-1); display:flex; align-items:center; gap:var(--sp-2);
    position:sticky; top:0; background:var(--bg-card); z-index:1;
}
.gsr-group-title i { font-size:var(--fs-caption-1); flex-shrink:0; }
.gsr-count {
    background:var(--bg-tertiary); border-radius:var(--radius-xs); padding:1px var(--sp-2);
    font-size:var(--fs-caption-2); color:var(--text-muted); margin-left:auto; font-weight: var(--fw-semibold);
}
.gsr-item {
    padding:var(--sp-2) var(--sp-4); cursor:pointer; transition: background var(--dur-fast) var(--ease);
    display:flex; flex-direction:column; gap:1px;
}
.gsr-item:hover { background:var(--bg-hover); }
.gsr-item:active { background:var(--accent-soft); }
.gsr-item-label {
    font-size:var(--fs-footnote); font-weight: var(--fw-medium); color:var(--text-primary); line-height:1.4;
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.gsr-item-label mark {
    background:var(--warning); color:#000; border-radius:var(--radius-xs); padding:0 var(--sp-05);
    font-weight: var(--fw-bold);
}
.gsr-item-sub {
    font-size:var(--fs-caption-2); color:var(--text-muted); line-height:1.3;
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.gsr-more {
    font-size:var(--fs-caption-2); color:var(--accent); padding:var(--sp-1) var(--sp-4) var(--sp-2);
    cursor:default; opacity:0.7;
}
.gsr-empty {
    text-align:center; padding:var(--sp-8) var(--sp-4); color:var(--text-muted); font-size:var(--fs-footnote);
}
.gsr-empty i { display:block; font-size:var(--fs-title-1); line-height: var(--lh-title-1); margin-bottom:var(--sp-3); opacity:0.4; }
.gsr-footer {
    text-align:center; font-size:var(--fs-caption-2); color:var(--text-muted);
    border-top:1px solid var(--border-color); padding:var(--sp-2) 0;
    position:sticky; bottom:0; background:var(--bg-card);
}
@media (max-width: 768px) {
    .global-search-results { left:0; right:0; max-height:60vh; border-radius:0 0 var(--radius) var(--radius); }
    .gsr-grid { grid-template-columns:1fr; }
    .gsr-grid .gsr-group { border-right:none; border-bottom:1px solid var(--border-color); }
}

.login-page { background: transparent; }

.sidebar {
    background: rgba(255,255,255,0.45);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border-right: 1px solid var(--border-color);
    box-shadow: var(--glass-highlight);
}
.sidebar-header { border-bottom: 1px solid var(--border-color); }
.sidebar-footer { border-top: 1px solid var(--border-color); }
.sidebar-header h1 {
    background: linear-gradient(135deg, var(--text-primary), var(--accent));
    -webkit-background-clip: text; background-clip: text;
    -webkit-text-fill-color: transparent;
}
.nav-item { color: var(--text-secondary); }
.nav-item:hover { background: var(--bg-hover); color: var(--text-primary); }
.nav-item.active {
    background: rgba(129,200,254,0.20);
    color: var(--accent);
    box-shadow: inset 3px 0 0 0 var(--accent-light), inset 0 0 0 1px rgba(129,200,254,0.30);
}

header.content-header {
    background: rgba(255,255,255,0.55);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border-bottom: 1px solid var(--border-color);
    box-shadow: var(--glass-highlight);
    position: fixed;
    top: 0;
    left: var(--sidebar-width);
    right: 0;
    z-index: var(--z-sticky);
}

.card,
div.content-header,
.filter-bar,
.report-card,
.stat-card {
    background: var(--bg-card);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border: 1px solid var(--border-color);
    box-shadow: var(--shadow-sm), var(--glass-highlight);
}
.card-header { background: transparent; border-bottom: 1px solid var(--border-color); }
.card-body { background: transparent; }

.cloud-item, .vault-card {
    background: var(--bg-card);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    box-shadow: var(--glass-highlight);
}
.cloud-item:hover, .vault-card:hover { box-shadow: var(--shadow-sm), var(--glass-highlight); }

.modal-overlay {
    background: rgba(18,22,40,0.35);
    -webkit-backdrop-filter: blur(8px) saturate(160%);
    backdrop-filter: blur(8px) saturate(160%);
}
/* 3.44.15: tre ombre di questo blocco — la finestra, il pannello dei risultati e la scheda di
   accesso — erano scritte a mano con la tinta rgba(30,40,90), che la nota in cima al foglio elenca
   fra quelle eliminate ("la tinta e' una sola"). Due erano il token carattere per carattere con la
   tinta sbagliata; la terza aveva anche l'opacita' un filo diversa (0,22 invece di 0,28), quindi da
   ora la scheda di accesso ha la stessa profondita' delle finestre. Lo stesso caso era stato
   corretto dal 3.44.12 duecento righe piu' in basso: questi tre erano rimasti indietro. */
.modal {
    background: var(--glass-bg-strong);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border: 1px solid rgba(255,255,255,0.6);
    box-shadow: var(--shadow-xl), var(--glass-highlight);
}
.modal-header { border-bottom: 1px solid var(--border-color); background: transparent; }
.modal-footer { border-top: 1px solid var(--border-color); background: transparent; }

.global-search-results,
.searchable-select-dropdown,
.ss-dropdown,
.dropdown-menu {
    background: var(--glass-bg-strong);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border: 1px solid var(--border-color);
    box-shadow: var(--shadow-lg), var(--glass-highlight);
}
.gsr-group-title, .gsr-footer { background: transparent; }

.login-card {
    background: var(--glass-bg-strong);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border: 1px solid rgba(255,255,255,0.6);
    box-shadow: var(--shadow-xl), var(--glass-highlight);
}

.form-control, .form-select, .search-box input, .vault-search input {
    background: var(--bg-input);
    border: 1px solid var(--border-color);
    color: var(--text-primary);
}
/* 3.44.15: il segno di dove si e' arrivati col Tab era misurabilmente invisibile. Il bordo di
   messa a fuoco usava --accent-light, che sull'interno del campo fa 1,76:1, e l'alone attorno
   1,26:1, contro il 3:1 che serve a un contorno: chi lavora da tastiera vedeva accendersi
   qualcosa di azzurrissimo indistinguibile dal campo a riposo. Non e' in discussione
   'outline: none', che e' voluto: si sostituisce cio' su cui quella scelta si appoggia. Il modo
   giusto era gia' nel foglio, in due punti — la ricerca della Cassaforte e quella delle sessioni
   usano il bordo --accent, che fa 8,45:1 — e adesso vale per tutti i campi. L'alone resta, ma
   come effetto, non come unico indizio. */
.form-control:focus, .form-select:focus, .search-box input:focus {
    background: rgba(255,255,255,0.85);
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-glow);
}

/* 3.44.7: qui c'erano .btn-primary e .btn-primary:hover identici carattere per carattere a
   quelli dichiarati con gli altri pulsanti, piu .btn-secondary spezzato in due punti del
   foglio. Tutto accorpato la' sopra: ogni pulsante ha ora una sola definizione. */

/* 3.44.7: la stessa evidenziazione al passaggio del mouse era scritta due volte, una sulla
   riga e una sulla cella. Resta solo quella sulla cella (piu' in alto nel foglio, dove e'
   definita la tabella): la cella copre comunque lo sfondo della riga, quindi la versione
   sulla riga non si vedeva mai e faceva solo confusione. */
.data-table thead th { background: rgba(255,255,255,0.4); }

.kanban-column {
    background: var(--bg-card);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border: 1px solid var(--border-color);
    box-shadow: var(--glass-highlight);
}

::-webkit-scrollbar-thumb { background: rgba(20,28,50,0.18); }
::-webkit-scrollbar-thumb:hover { background: rgba(20,28,50,0.30); }

.global-search-results { background: rgba(255,255,255,0.95); }

.chat-input-area { align-items: flex-end; }
/* 3.44.12: il tetto di crescita del campo era scritto tre volte con tre misure diverse
   (120px qui, 110px nel blocco sotto i 768px, 116px nel blocco iOS): lo stesso campo smetteva di
   crescere a tre altezze a seconda della larghezza dello schermo, senza che nulla lo giustificasse.
   Resta questa, l'unica: le altre due sono state tolte. */
.chat-input {
    flex: 1;
    resize: none;
    max-height: 120px;
    min-height: var(--control-h);
    line-height: 1.4;
    overflow-y: auto;
    padding-top:var(--sp-2); padding-bottom:var(--sp-2);
}
/* 3.44.12: tolta l'altezza da qui. Le misure dei due pulsanti della barra si dichiarano ora in
   un punto solo (blocco "Invio tondo" in fondo al foglio); qui resta il solo flex-shrink. */
.chat-send-btn { flex-shrink: 0; }

.chat-date-sep { display: flex; justify-content: center; margin:var(--sp-3) 0 var(--sp-2); }
/* 3.44.12: il separatore di giornata stava a 11px, il minimo leggibile della scala, e con
   --text-muted si fermava a 5,23:1 contro lo sfondo della conversazione: praticamente scompariva
   fra un giorno e l'altro. Sale a Footnote (13px) con --text-secondary. Il fondo bianco al 70%
   scritto a mano era una quarta gradazione di vetro: ora e' --glass-bg-strong. */
.chat-date-sep span {
    font-size:var(--fs-footnote); line-height: var(--lh-footnote); font-weight: var(--fw-semibold); color: var(--text-secondary);
    background: var(--glass-bg-strong);
    -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
    padding:var(--sp-1) var(--sp-3); border-radius:var(--radius-full); border: 1px solid var(--border-color);
}
/* 3.44.12 (questo giro): la riga che dichiara la cronologia archiviata. Stessa pastiglia del
   separatore di giornata — e' la stessa cosa, una nota di servizio in mezzo ai messaggi — ma la
   frase e' lunga: su un telefono da 390px non ci sta su una riga sola, quindi qui la pastiglia
   puo' andare a capo (il separatore di giornata e' sempre corto e non ne ha mai bisogno) e il
   testo resta centrato. --radius-lg invece del tondo pieno: su due righe la pillola
   diventerebbe un ovale schiacciato. */
.chat-date-sep.chat-archived-sep span {
    max-width: 88%; text-align: center; border-radius: var(--radius-lg);
    font-weight: var(--fw-regular);
}
.chat-date-sep.chat-archived-sep span .bi { margin-right: var(--sp-1); opacity: 0.7; }
/* 3.44.12 (questo giro): la riga che dichiara che l'altra persona non ha piu' accesso a Messaggi.
   Stessa pastiglia della nota sull'archivio (a capo consentito, testo centrato), ma in inchiostro
   d'avviso: non e' una nota di servizio, e' il motivo per cui da li' in poi non arrivera' nessuna
   risposta. --warning-ink e' l'ambra studiata per il TESTO (4,82:1 sul fondo chiaro). */
.chat-date-sep.chat-nochat-sep span { color: var(--warning-ink); }

/* 3.44.12: gli 8px degli angoli raggruppati sono esattamente --radius-xs. */
.chat-bubble.grouped { margin-top:-4px; }
.chat-bubble.sent.grouped { border-top-right-radius: var(--radius-xs); }
.chat-bubble.received.grouped { border-top-left-radius: var(--radius-xs); }

.chat-bubble-meta { display: flex; justify-content: flex-end; align-items: center; gap:var(--sp-2); margin-top:var(--sp-05); }

.chat-receipt { font-size:var(--fs-footnote); line-height: 1; opacity: 0.85; }
.chat-bubble.sent .chat-receipt { color: rgba(255,255,255,0.9); }
/* 3.44.12: #7dd3fc era l'azzurro della doppia spunta "letto", scritto a mano; il token che gli
   corrisponde nel sistema e' --accent-light. */
.chat-bubble.sent .chat-receipt.read { color: var(--accent-light); opacity: 1; }
/* 3.44.12: #fecaca sostituito dal token rosso tenue. NB: questa dichiarazione non arriva a
   schermo — .chat-bubble.sent .chat-receipt (tre classi) la batte per specificita' e la spunta
   fallita resta bianca. La si allinea al sistema senza cambiarne la specificita': alzarla
   renderebbe l'icona invisibile, perche' un rosso al 14% sopra la bolla gia' rossa non si vede. */
.chat-receipt.failed { color: var(--danger-soft); }

.chat-bubble.pending { opacity: 0.65; }
.chat-bubble.failed { background: var(--danger); }
.chat-bubble.failed .chat-bubble-time { color: rgba(255,255,255,0.85); }

.chat-link { color: inherit; text-decoration: underline; word-break: break-all; }
.chat-bubble.received .chat-link { color: var(--accent); }

.chat-empty-msgs {
    margin:auto; text-align: center; color: var(--text-muted); font-size:var(--fs-footnote); line-height: var(--lh-footnote);
    display: flex; flex-direction: column; align-items: center; gap:var(--sp-3);
}
.chat-empty-msgs i { font-size:var(--fs-large-title); line-height: var(--lh-large-title); opacity: 0.4; }

.chat-typing {
    display: flex; align-items: center; gap:var(--sp-2);
    padding:var(--sp-1) var(--sp-5) var(--sp-2); font-size:var(--fs-caption-1); line-height: var(--lh-caption-1); color: var(--text-muted); flex-shrink: 0;
}
.chat-typing-dots { display: inline-flex; gap:var(--sp-1); }
/* 3.44.12: i pallini erano 6px qui e 7px nel blocco iOS, con in piu' un grigio #9aa2b5 che non
   esiste da nessun'altra parte. Sono glifi decorativi: la loro misura e' --fs-dot (8px) e il loro
   colore --text-muted, esattamente come i pallini di stato del resto dell'applicazione. */
.chat-typing-dots span {
    width: var(--fs-dot); height: var(--fs-dot); border-radius:50%; background: var(--text-muted);
    animation: chatTypingBounce 1.2s infinite ease-in-out;
}
.chat-typing-dots span:nth-child(2) { animation-delay: 0.15s; }
.chat-typing-dots span:nth-child(3) { animation-delay: 0.3s; }
@keyframes chatTypingBounce {
    0%, 60%, 100% { transform: translateY(0); opacity: 0.4; }
    30% { transform: translateY(-4px); opacity: 1; }
}

.chat-contact .avatar, .chat-header .avatar { position: relative; }
/* 3.44.12: due cose. 1) Il pallino "in linea" era #22c55e mentre la scritta "Online" qui sotto
   era #16a34a: due verdi diversi per lo stesso identico concetto, a due centimetri l'uno
   dall'altra. Ora tutti e due usano --success. 2) Posizione: l'avatar e' un quadrato con gli
   angoli stondati (border-radius 12px), non un cerchio; ancorato a -1px il pallino restava
   sospeso FUORI dall'angolo, staccato dalla tessera. Ancorandolo al bordo (0/0) e spostandolo
   di un quarto di se stesso si appoggia sulla curva, come sugli avatar di sistema. */
.chat-contact .avatar.is-online::after, .chat-header .avatar.is-online::after {
    content: ''; position: absolute; right: 0; bottom: 0;
    transform: translate(25%, 25%);
    width: 10px; height: 10px; border-radius:50%;
    background: var(--success); border: 2px solid rgba(255,255,255,0.95);
    box-shadow: var(--shadow-xs);
}

.chat-header-status { font-size:var(--fs-caption-1); line-height: var(--lh-caption-1); color: var(--text-muted); }
/* 3.44.12: vedi sopra — #16a34a e' il valore di --success, scritto a mano. */
.chat-header-status.online { color: var(--success); font-weight: var(--fw-semibold); }
.chat-header-status.typing { color: var(--accent); font-style: italic; }

.chat-conversation { position: relative; }
/* 3.44.12: la pastiglia "vai in fondo" era 38x38 (sotto i 44 minimi) e stava a 86px fissi dal
   basso, misurati su una barra di scrittura a una riga. Bastavano tre righe di testo perche' la
   barra crescesse a 133px e se la mangiasse: il pulsante per tornare in fondo finiva sotto al
   campo di scrittura, cioe' irraggiungibile proprio quando serve.
   Ora la distanza dal basso e' agganciata a --chat-composer-h, che il codice puo' aggiornare con
   l'altezza reale della barra. Finche' nessuno la imposta vale il ripiego, calcolato sulla barra
   al MASSIMO della sua crescita (140px = 120 di campo + 8 e 8 di imbottitura + bordo) piu'
   l'area sicura in fondo allo schermo: cosi' la pastiglia resta sopra la barra anche nel caso
   peggiore, invece di sparirci dietro. */
.chat-jump-pill {
    position: absolute; right: 18px;
    bottom: calc(var(--chat-composer-h, calc(140px + env(safe-area-inset-bottom))) + var(--sp-3));
    width: var(--tap-min); height: var(--tap-min); border-radius:50%;
    background: var(--glass-bg-strong);
    -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
    border: 1px solid var(--border-color);
    /* 3.44.12: questa tinta d'ombra (30,40,90) doveva essere sparita con la consolidazione delle
       ombre: e' --shadow-lg. */
    box-shadow: var(--shadow-lg), var(--glass-highlight);
    color: var(--accent); font-size:var(--fs-title-3); line-height: var(--lh-title-3); cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    opacity: 0; transform: translateY(8px) scale(0.9); pointer-events: none;
    transition: opacity var(--dur-base) var(--ease), transform var(--dur-base) var(--ease); z-index: 5;
}
.chat-jump-pill.visible { opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; }

.toast {
    background: rgba(28,33,52,0.92);
    -webkit-backdrop-filter: blur(12px) saturate(160%);
    backdrop-filter: blur(12px) saturate(160%);
    color: #fff;
    border: 1px solid rgba(255,255,255,0.12);
    box-shadow: var(--shadow);
}
.toast i { color: #fff; }
/* 3.44.10: questi quattro rgba NON sono colori a caso, sono esattamente i token semantici
   al 96% di opacita'. Si e' valutato di riscriverli con color-mix() per lasciare ai token il
   ruolo di fonte unica, ma color-mix non compare da nessuna altra parte in questo foglio e
   qui non ci sarebbe rete di sicurezza: su un browser che non lo capisce la dichiarazione
   cade e l'avviso perde il colore che ne comunica l'esito. Essendo l'avviso il canale con
   cui l'applicazione dice se un'operazione e' andata a buon fine, si e' preferito restare
   sull'rgba e annotare qui la corrispondenza, cosi' chi cambia un token sa cosa allineare:
     rgba(22,163,74)  = --success    rgba(220,38,38) = --danger
     rgba(217,119,6)  = --warning    rgba(55,75,95)  = --accent */
/* 3.44.15: due avvisi su quattro non si leggevano — il verde di esito positivo (3,15:1) e l'ambra
   (3,05:1), con scritta bianca di 13px che ne chiede 4,5:1. Gli altri due passavano (rosso 4,65:1,
   neutro 8,07:1), quindi non era una scelta di famiglia ma due righe rimaste indietro. L'avviso e'
   il canale con cui l'applicazione dice se un'operazione e' andata a buon fine: e' il posto dove
   una scritta illeggibile costa di piu'. I due fondi passano ai valori degli inchiostri, gli
   stessi che il foglio usa quando questi colori portano del testo: 5,61:1 e 5,33:1.
   La corrispondenza dei token, per chi ne cambia uno: rgba(20,113,58) = --success-ink
   rgba(149,87,6) = --warning-ink   rgba(220,38,38) = --danger   rgba(55,75,95) = --accent */
.toast.success { background: rgba(20,113,58,0.96); color: #fff; }
.toast.error   { background: rgba(220,38,38,0.96); color: #fff; }
.toast.warning { background: rgba(149,87,6,0.96); color: #fff; }
.toast.info    { background: rgba(55,75,95,0.96); color: #fff; }

textarea.chat-input { min-height: 42px; resize: none; }

.chat-notify-popup {
    position: fixed; right: calc(22px + env(safe-area-inset-right)); bottom: calc(22px + env(safe-area-inset-bottom)); z-index: var(--z-toast);
    display: flex; align-items: center; gap:var(--sp-3);
    padding:var(--sp-3) var(--sp-4); border-radius:var(--radius); max-width: 330px;
    background: var(--glass-bg-strong);
    -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
    border: 1px solid var(--border-color);
    /* 3.44.12: stessa tinta d'ombra fuori sistema della pastiglia: e' --shadow-xl. */
    box-shadow: var(--shadow-xl), var(--glass-highlight);
    cursor: pointer;
    opacity: 0; transform: translateY(16px) scale(0.96); pointer-events: none;
    transition: opacity var(--dur-base) var(--ease), transform var(--dur-base) var(--ease);
}
.chat-notify-popup.visible { opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; }
/* 3.44.12: la sfumatura partiva da #a5a5a5, un grigio che non ha nessuna sorgente nel sistema
   (arrivava a #374b5f, cioe' --accent scritto a mano). Ora e' la stessa ricetta usata altrove per
   le tessere piene: dal colore d'accento alla sua variante scura. */
.chat-notify-popup .cnp-icon {
    position: relative; width: 42px; height: 42px; border-radius:var(--radius-sm); flex-shrink: 0;
    background: linear-gradient(135deg, var(--accent), var(--accent-hover)); color: #fff;
    display: flex; align-items: center; justify-content: center; font-size:var(--fs-title-3); line-height: var(--lh-title-3);
}
/* 3.44.12: era l'unico contatore quadrato dell'applicazione (--radius-sm su una pastiglia da
   20px): tutti gli altri sono pillole, --radius-full. */
.chat-notify-popup .cnp-badge {
    position: absolute; top: -6px; right: -6px; min-width: 20px; height: 20px; padding:0 var(--sp-1);
    background: var(--danger); color: #fff; border-radius:var(--radius-full);
    font-size:var(--fs-caption-2); font-weight: var(--fw-bold); display: flex; align-items: center; justify-content: center;
    /* 3.44.12 (secondo giro): interlinea 1 come le altre due pastiglie del conteggio
       (.chat-unread-badge e .chat-nav-badge). Ereditava 25px dentro un cerchio da 20. */
    line-height: 1;
    border: 2px solid rgba(255,255,255,0.92);
}
.chat-notify-popup .cnp-body { flex: 1; min-width: 0; }
.chat-notify-popup .cnp-title { font-weight: var(--fw-bold); font-size:var(--fs-subhead); line-height: var(--lh-subhead); color: var(--text-primary); }
.chat-notify-popup .cnp-sub { font-size:var(--fs-caption-1); line-height: var(--lh-caption-1); color: var(--text-secondary); margin-top:1px; }
/* 3.44.12: la crocetta per chiudere misurava 19x21px. Sbagliarla non era neutro: il tocco finiva
   sul corpo dell'avviso, che apre la chat. Ora il bersaglio e' 44x44 come tutti gli altri, ma i
   margini negativi lo riportano a un ingombro di 20px: l'avviso non si allarga di un pixel, si
   allarga solo l'area sensibile. */
.chat-notify-popup .cnp-close {
    background: none; border: none; color: var(--text-muted); cursor: pointer;
    font-size:var(--fs-subhead); flex-shrink: 0;
    width: var(--tap-min); height: var(--tap-min); padding:0;
    margin: calc(var(--sp-5) / 2 - var(--tap-min) / 2);
    align-self: center;
    display: inline-flex; align-items: center; justify-content: center;
    transition: var(--transition);
}
.chat-notify-popup .cnp-close:hover { color: var(--text-primary); }

/* 3.44.12: con una conversazione aperta l'avviso atterrava SOPRA la barra di scrittura. Non era
   solo brutto: su iPhone elementFromPoint al centro del campo di testo restituiva DIV.cnp-sub,
   cioe' per sette secondi toccare "Messaggio" non apriva la tastiera ma cambiava pagina
   (sovrapposizione misurata: 49px). Con la conversazione aperta l'avviso si appoggia SOPRA la
   barra; nell'elenco, dove la barra non c'e', resta in basso come prima.
   L'altezza della barra si legge da --chat-composer-h, che il codice puo' aggiornare; il ripiego
   e' la barra a riposo, cioe' campo 44 + 20 fra imbottitura e bordo. */
body:has(.chat-app.peer-selected) .chat-notify-popup {
    bottom: calc(var(--chat-composer-h, calc(var(--control-h) + var(--sp-5) + env(safe-area-inset-bottom))) + var(--sp-3));
}
/* Sopra i 768px la chat non arriva al bordo dello schermo: sta dentro una scheda con il
   riempimento della pagina sotto. Quel riempimento va aggiunto, altrimenti l'avviso si appoggia
   sopra il fondo della finestra e non sopra la barra (misurati 53px di sovrapposizione su iPad,
   con il campo di testo di nuovo non toccabile). Qui l'area sicura la mette gia' .content-body,
   quindi non si somma due volte. */
@media (min-width: 769px) {
    body:has(.chat-app.peer-selected) .chat-notify-popup {
        bottom: calc(var(--chat-composer-h, calc(var(--control-h) + var(--sp-5))) + max(28px, env(safe-area-inset-bottom)) + var(--sp-3));
    }
}
@media (max-width: 768px) {
    .chat-notify-popup { right: calc(12px + env(safe-area-inset-right)); left: calc(12px + env(safe-area-inset-left)); bottom: calc(12px + env(safe-area-inset-bottom)); max-width: none; }
}
/* 3.44.12 (secondo giro): spostare l'avviso SOPRA la barra di scrittura (regola qui sopra) lo ha
   portato esattamente dove si apre il menu degli allegati, che parte anche lui dal bordo alto
   della barra. Misurato su iPhone 13 a 320px: l'avviso copriva per intero "Dal cloud" e meta' di
   "Dal dispositivo", e siccome sta piu' in alto nella pila (--z-toast contro --z-popover) il
   tocco su quelle voci apriva la chat invece di scegliere l'allegato. Fra i due comanda il menu:
   e' l'utente ad averlo aperto ed e' li' che sta guardando, mentre l'avviso e' una cortesia che
   passa da sola. Finche' il menu e' aperto l'avviso si toglie in dissolvenza — la transizione e'
   gia' dichiarata sopra — e torna appena il menu si chiude, senza sparire di colpo e senza
   perdere i secondi che gli restano. Il messaggio in arrivo resta comunque annunciato dalla
   pastiglia del menu, dal titolo della scheda e dalla zona viva per i lettori di schermo. */
body:has(.chat-attach-menu.open) .chat-notify-popup {
    opacity: 0;
    pointer-events: none;
}

.calendar-appt {
    font-size:var(--fs-caption-2); line-height: 1.3; padding:var(--sp-05) var(--sp-2); margin-top:var(--sp-05);
    border-radius:var(--radius-xs); cursor: pointer;
    background: rgba(55,75,95,0.14); color: var(--accent);
    border: 1px solid rgba(55,75,95,0.22);
    white-space: normal; word-break: break-word;
    display: flex; align-items: flex-start; gap:var(--sp-1);
    transition: var(--transition);
}
.calendar-appt:hover { background: rgba(55,75,95,0.22); }
.calendar-appt i { font-size:var(--fs-caption-2); flex-shrink: 0; margin-top:1px; }
.calendar-appt .cal-appt-body { min-width: 0; flex: 1; }
.cal-appt-client { display: block; font-size:var(--fs-caption-2); color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top:1px; }
.calendar-appt .cal-appt-client i { font-size:var(--fs-caption-2); margin-top:0; margin-right:var(--sp-05); }
.cal-agenda-client { font-size:var(--fs-caption-2); color: var(--accent); background: var(--bg-tertiary); padding:1px var(--sp-2); border-radius:var(--radius-sm); white-space: nowrap; display: inline-flex; align-items: center; gap:var(--sp-1); }
.cal-agenda-client i { font-size:var(--fs-caption-2); }

.appointment-item {
    display: flex; align-items: flex-start; gap:var(--sp-3);
    padding:var(--sp-2) var(--sp-3); margin-bottom:var(--sp-2);
    background: var(--bg-tertiary); border-radius: var(--radius-sm);
    border-left: 3px solid var(--accent);
}
.appointment-item .appt-ico { color: var(--accent); font-size:var(--fs-subhead); margin-top:var(--sp-05); flex-shrink: 0; }
.appointment-item .appt-body { flex: 1; min-width: 0; }
.appointment-item .appt-when { font-size:var(--fs-caption-1); font-weight: var(--fw-semibold); color: var(--text-primary); }
.appointment-item .appt-desc { font-size:var(--fs-footnote); color: var(--text-secondary); word-break: break-word; white-space: pre-wrap; }

.appt-add-form { display: flex; gap:var(--sp-2); margin-top:var(--sp-3); flex-wrap: wrap; }
.appt-add-form #newApptDate { flex: 0 0 auto; max-width: 200px; }
.appt-add-form #newApptDesc { flex: 1; min-width: 160px; }

.cal-filters {
    display: flex; align-items: center; gap:var(--sp-3); flex-wrap: wrap;
    margin-bottom:var(--sp-4); padding:0 var(--sp-05);
}
.cal-filters-label { font-size:var(--fs-footnote); color: var(--text-secondary); font-weight: var(--fw-semibold); }
.cal-filter-btn {
    display: inline-flex; align-items: center; gap:var(--sp-2);
    font-size:var(--fs-caption-1); font-weight: var(--fw-semibold); cursor: pointer;
    padding:var(--sp-2) var(--sp-3); border-radius:var(--radius-full);
    border: 1px solid var(--border-color);
    background: var(--glass-bg-strong);
    -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
    color: var(--text-primary);
    transition: var(--transition);
}
.cal-filter-btn i { font-size:var(--fs-footnote); }
.cal-filter-tasks i { color: var(--text-secondary); }
.cal-filter-appts i { color: var(--accent); }
.cal-filter-btn:hover { border-color: var(--accent); }
.cal-filter-btn.off { opacity: 0.5; text-decoration: line-through; }
.cal-filter-btn.off i { color: var(--text-muted) !important; }

.calendar-grid.hide-tasks .calendar-task,
.calendar-grid.hide-tasks .cal-more-tasks { display: none !important; }
.calendar-grid.hide-appts .calendar-appt,
.calendar-grid.hide-appts .cal-more-appts { display: none !important; }

.cal-user-filter { display: flex; align-items: center; gap:var(--sp-2); margin-left:auto; }
.cal-user-filter i { color: var(--accent); font-size:var(--fs-subhead); }
.cal-user-filter .form-select { font-size:var(--fs-caption-1); padding:var(--sp-2) var(--sp-8) var(--sp-2) var(--sp-3); min-width: 170px; width: auto; }
@media (max-width: 768px) {
    .cal-user-filter { margin-left:0; width: 100%; }
    .cal-user-filter .form-select { flex: 1; }
}

.settings-layout {
    display: flex;
    gap:var(--sp-5);
    align-items: flex-start;
    flex-wrap: wrap;
}
.settings-profile-col {
    flex: 0 0 425px;
    max-width: 425px;
    min-width: 0;
}
@media (max-width: 1024px) {
    .settings-profile-col { flex-basis: 100%; max-width: 100%; }
}

/* 3.44.12: qui stava il primo di QUATTRO blocchi che davano una misura ai due pulsanti della
   barra di scrittura (42px qui, 44 sotto i 768px, 38 nel blocco iOS, 40 in fondo al foglio): il
   risultato a schermo era 38x38 col mouse e 40x44 sul telefono, cioe' mai i 44 minimi delle linee
   guida, e nessuno dei quattro numeri era quello che si leggeva per primo. Il blocco e' stato
   tolto per intero: i 42px erano gia' sovrascritti, e tutto il resto (imbottitura, allineamento,
   transizione) e' ridichiarato dal blocco "Invio tondo" che vince davvero. La misura unica sta
   ora la', in un punto solo. Con il blocco se ne va anche .chat-urgent-btn: quel pulsante non
   esiste nel markup e il codice che lo citava e' stato appena rimosso. */
.chat-send-btn {
    background: var(--glass-bg-strong);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border: 1px solid var(--border-color);
    color: var(--text-secondary);
}
.chat-send-btn:hover { color: var(--accent); border-color: var(--accent); }
.chat-send-btn i { margin:0; }
.chat-attach-btn {
    background: var(--glass-bg-strong);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border: 1px solid var(--border-color);
    color: var(--text-secondary);
}
.chat-attach-btn:hover { color: var(--accent); border-color: var(--accent); }

/* 3.44.19: da UNA riga a un ELENCO di righe (fino a CHAT_ATTACH_MAX file in attesa). La barra
   diventa una colonna e ogni file e' una .chat-pending-row con la stessa disposizione di prima:
   con un file solo si vede esattamente quello che si vedeva, perche' una colonna di un elemento e'
   indistinguibile dalla riga che c'era.
   Il tetto di altezza e' obbligatorio: dieci righe da 44px si mangerebbero mezzo schermo e su un
   telefono spingerebbero il campo di scrittura sotto la tastiera. Oltre, si scorre. */
.chat-pending-attach {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap:var(--sp-2);
    margin:0 var(--sp-4) var(--sp-1);
    padding:var(--sp-3) var(--sp-4);
    background: var(--accent-soft);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    max-height: 34vh;
    overflow-y: auto;
    overscroll-behavior: contain;
}
.chat-pending-row { display: flex; align-items: center; gap:var(--sp-3); min-width: 0; }
/* L'intestazione compare solo da due file in su (vedi _renderChatPending): dice quanti sono e che
   partiranno separati. La crocetta qui li toglie tutti. */
.chat-pending-head {
    display: flex; align-items: center; gap:var(--sp-2); min-width: 0;
    font-size:var(--fs-caption-1); line-height: var(--lh-caption-1); color: var(--text-muted);
}
.chat-pending-head span { flex: 1; min-width: 0; }
/* 3.44.12: senza min-width:0 il nome del file non si troncava: un elemento flessibile non scende
   sotto la larghezza minima del proprio contenuto, e "text-overflow: ellipsis" non entrava mai in
   funzione. Il nome spingeva invece la barra, la barra spingeva la colonna e la colonna usciva
   dallo schermo (vedi la nota su .chat-main). Ora i puntini di sospensione fanno il loro lavoro. */
.chat-pending-name { flex: 1; min-width: 0; font-size:var(--fs-footnote); line-height: var(--lh-footnote); font-weight: var(--fw-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-primary); }
.chat-pending-size { font-size:var(--fs-caption-1); line-height: var(--lh-caption-1); color: var(--text-muted); flex-shrink: 0; }
/* 3.44.12: la crocetta per togliere l'allegato era 20x20. Come per la crocetta dell'avviso, il
   bersaglio sale a 44x44 e i margini negativi lo riportano a un ingombro di 20px, cosi' la barra
   dell'allegato non cresce di un pixel. */
.chat-pending-remove {
    background: none; border: none; cursor: pointer;
    font-size:var(--fs-title-3); line-height: 1; color: var(--text-muted); flex-shrink: 0;
    width: var(--tap-min); height: var(--tap-min); padding:0;
    margin: calc(var(--sp-5) / 2 - var(--tap-min) / 2);
    display: inline-flex; align-items: center; justify-content: center;
    transition: var(--transition);
}
.chat-pending-remove:hover { color: var(--danger-ink); }

.chat-bubble.has-attach { max-width: 340px; }
/* 3.44.19 — APRENDO UNA CONVERSAZIONE CON DELLE FOTO CI SI FERMAVA SULLE FOTO.
   La 3.44.12 aveva riservato il posto all'anteprima con aspect-ratio (qui sotto) e aveva ridotto
   il salto, ma non l'aveva tolto, perche' il vuoto non era nell'altezza: era nella LARGHEZZA.
   Le bolle sono elementi flessibili in colonna con align-self: la loro larghezza la decide il
   contenuto. Un <img> non ancora scaricato ha larghezza naturale ZERO, quindi contribuiva zero: la
   bolla nasceva stretta quanto l'orario, e l'anteprima alta il 75% di quel nulla. Appena la foto
   arrivava, la sua larghezza naturale (una foto di telefono: migliaia di pixel) portava la bolla al
   suo tetto di 340px e l'anteprima da poche decine di pixel a oltre duecento. Ogni foto spingeva
   giu' quelle sotto DOPO che lo scorrimento al fondo era gia' avvenuto, e la conversazione si
   apriva ferma su un'immagine invece che sull'ultimo messaggio. Con piu' foto il riaggancio del
   codice (onload, vedi _chatAttachmentHtml) non bastava: interviene solo entro 300px dal fondo, e
   la somma dei salti li superava.
   La larghezza ora e' dichiarata, quindi identica prima e dopo il caricamento: il posto riservato
   e' quello giusto fin dal primo disegno e non si sposta piu' niente. 'max-width:100%' tiene la
   bolla dentro lo schermo sui telefoni stretti (e deve stare DOPO .has-attach, che fissa 340px con
   la stessa specificita'). Vale solo per le bolle con un'anteprima vera: quelle con un allegato
   non-immagine, in caricamento o non partito restano larghe quanto il loro contenuto. */
.chat-bubble.has-img { width: 340px; max-width: 100%; }
/* 3.44.12: l'anteprima non dichiarava nessuna dimensione, quindi finche' l'immagine non era
   scaricata il suo ingombro era ZERO: aprendo una conversazione che ne contiene una, lo
   scorrimento al fondo si fermava 211px piu' in alto e l'ultimo messaggio restava fuori
   schermo. Anche object-fit:cover era una scrittura senza effetto, perche' non esisteva nessuna
   casella da riempire. Ora il posto e' riservato subito, con la proporzione 4:3 e lo stesso
   tetto di 260px di prima. Il riaggancio dopo il caricamento lo fa il codice. */
.chat-attach-img {
    width: 100%;
    height: auto;
    aspect-ratio: 4 / 3;
    max-width: 100%;
    max-height: 260px;
    border-radius:var(--radius-sm);
    cursor: pointer;
    display: block;
    object-fit: cover;
    margin-bottom:var(--sp-05);
}
.chat-attach {
    display: flex;
    align-items: center;
    gap:var(--sp-3);
    padding:var(--sp-2) var(--sp-3);
    border-radius:var(--radius-sm);
    cursor: pointer;
    margin-bottom:var(--sp-05);
    background: rgba(255, 255, 255, 0.2);
    max-width: 280px;
    transition: filter var(--dur-fast) var(--ease);
}
.chat-attach:hover { filter: brightness(0.96); }
.chat-attach.uploading { cursor: default; }
/* 3.44.12: nuovo stato. Il codice disegna ora l'allegato NON PARTITO come
   <div class="chat-attach failed"> con un triangolo di avviso: prima restava identico a uno in
   caricamento e la clessidra girava per sempre. Serviva un aspetto suo: rosso tenue, filo rosso,
   inchiostro rosso scuro (--danger-ink e' l'unico rosso che regge il 4,5:1 sul -soft) e nessuna
   manina, perche' non c'e' niente da scaricare. I selettori ripetono l'antenato perche'
   altrimenti fondo e colore verrebbero rifissati da .chat-bubble.sent/.received .chat-attach,
   scritte piu' avanti nel foglio. */
.chat-attach.failed,
.chat-bubble.received .chat-attach.failed {
    background: var(--danger-soft);
    color: var(--danger-ink);
    border: 1px solid var(--danger);
    cursor: default;
}
/* 3.44.12: il caso che capita davvero. Un invio fallito e' sempre un messaggio PROPRIO, e la
   bolla che lo contiene prende gia' la classe .failed, cioe' fondo --danger pieno. Una targhetta
   rossa tenue con inchiostro rosso scuro appoggiata su una bolla rossa non si legge (misurati
   2,6:1 il testo sul fondo composito) e nemmeno la versione a testo bianco basta (3,8:1, sotto
   il 4,5 richiesto a 11-13px). Qui la targhetta si ribalta: superficie chiara, stesso inchiostro
   rosso e stesso filo rosso di sopra. Misura 6,5:1 e resta riconoscibile come lo stesso oggetto. */
.chat-bubble.sent .chat-attach.failed,
.chat-bubble.failed .chat-attach.failed {
    background: #fff;
    color: var(--danger-ink);
    border: 1px solid var(--danger);
    cursor: default;
}
.chat-attach.failed:hover { filter: none; }
.chat-attach.failed > i:first-child,
.chat-bubble.sent .chat-attach.failed > i:first-child,
.chat-bubble.received .chat-attach.failed > i:first-child { color: var(--danger-ink); }
.chat-attach.failed .chat-attach-size { opacity: 1; }
.chat-attach > i:first-child { font-size:var(--fs-title-2); line-height: var(--lh-title-2); flex-shrink: 0; }
.chat-attach-info { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.chat-attach-name { font-size:var(--fs-footnote); line-height: var(--lh-footnote); font-weight: var(--fw-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-attach-size { font-size:var(--fs-caption-2); line-height: var(--lh-caption-2); opacity: 0.75; }
.chat-attach-dl { font-size:var(--fs-callout); flex-shrink: 0; opacity: 0.8; }
.chat-bubble.received .chat-attach { background: var(--bg-tertiary); color: var(--text-primary); }
.chat-bubble.received .chat-attach > i:first-child { color: var(--accent); }
.chat-bubble.sent .chat-attach { background: rgba(255, 255, 255, 0.2); color: #fff; }

.dash-project {
    display: flex; gap:var(--sp-3); align-items: flex-start;
    padding:var(--sp-3); border-radius: var(--radius-sm);
    cursor: pointer; transition: var(--transition);
    border: 1px solid transparent;
}
.dash-project:hover {
    background: var(--glass-bg-strong);
    border-color: var(--border-color);
    transform: translateX(2px);
}
.dash-project + .dash-project { margin-top:var(--sp-05); }
.dash-project-icon {
    width: 38px; height: 38px; border-radius:var(--radius-sm);
    display: flex; align-items: center; justify-content: center;
    color: #fff; font-size:var(--fs-body); flex-shrink: 0;
    box-shadow: var(--shadow-sm);
}
.dash-project-main { flex: 1; min-width: 0; }
.dash-project-top { display: flex; justify-content: space-between; align-items: center; gap:var(--sp-2); }
.dash-project-name {
    font-weight: var(--fw-semibold); font-size:var(--fs-subhead); color: var(--text-primary);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dash-project-pct { font-size:var(--fs-caption-1); font-weight: var(--fw-bold); flex-shrink: 0; }
.dash-project-sub {
    display: flex; flex-wrap: wrap; gap:var(--sp-3);
    margin:var(--sp-1) 0 var(--sp-2); font-size:var(--fs-caption-2); color: var(--text-muted);
}
.dash-project-sub span { display: inline-flex; align-items: center; }
.dash-project-sub i { font-size:var(--fs-caption-2); margin-right:var(--sp-1); opacity: 0.85; }
.dash-project-sub .overdue { color: var(--danger-ink); font-weight: var(--fw-semibold); }
.dash-progress { height: 6px; background: var(--bg-tertiary); border-radius:var(--radius-full); overflow: hidden; }
.dash-progress-fill { height: 100%; border-radius:var(--radius-full); transition: width var(--dur-slow) var(--ease); min-width: 2px; }
.dash-empty { text-align: center; color: var(--text-muted); padding:var(--sp-6) var(--sp-3); font-size:var(--fs-footnote); line-height: 1.7; }
.dash-empty i { font-size:var(--fs-title-1); line-height: var(--lh-title-1); display: block; margin-bottom:var(--sp-2); opacity: 0.45; }

.activity-item.clickable { cursor: pointer; border-radius: var(--radius-sm); transition: background var(--dur-fast) var(--ease); }
.activity-item.clickable:hover { background: var(--glass-bg-strong); }
.activity-item.clickable .activity-text strong:last-child { color: var(--accent); }

.cal-toolbar #calTitle { font-size:var(--fs-callout); font-weight: var(--fw-bold); }
.cal-toolbar #calTodayBtn:disabled { opacity: .45; cursor: default; }

/* 3.44.10: era var(--border), token inesistente e senza ripiego: il bordo veniva disegnato
   con currentColor, cioe con il colore del testo. Corretto nel token reale --border-color. */
.cal-view-toggle { display: inline-flex; background: var(--bg-tertiary); border: 1px solid var(--border-color); border-radius: var(--radius); padding:var(--sp-05); gap:var(--sp-05); }
.cal-view-btn { background: transparent; border: none; color: var(--text-secondary); font-size:var(--fs-footnote); font-weight: var(--fw-semibold); padding:var(--sp-1) var(--sp-3); border-radius: calc(var(--radius) - 3px); cursor: pointer; display: inline-flex; align-items: center; gap:var(--sp-2); transition: var(--transition); }
.cal-view-btn i { font-size:var(--fs-footnote); }
.cal-view-btn:hover { color: var(--text-primary); }
.cal-view-btn.active { background: var(--accent); color: #fff; }

.cal-summary { margin-left:auto; font-size:var(--fs-caption-1); color: var(--text-muted); white-space: nowrap; }
.cal-summary i { font-size:var(--fs-caption-1); }

.cal-legend { display: flex; flex-wrap: wrap; gap:var(--sp-4); margin-top:var(--sp-3); padding:0 var(--sp-1); font-size:var(--fs-caption-1); color: var(--text-muted); }
.cal-legend span { display: inline-flex; align-items: center; gap:var(--sp-2); }
.cal-legend-swatch { width: 11px; height: 11px; border-radius:var(--radius-xs); display: inline-block; }
.cal-legend-swatch.cal-legend-task { background: linear-gradient(135deg, #374b5f, #ec4899); }

.calendar-cell.cal-clickable { cursor: pointer; }
.calendar-cell.cal-weekend { background: var(--bg-tertiary); }
.calendar-header-cell.cal-weekend { color: var(--text-muted); }
.calendar-task.overdue { font-weight: var(--fw-semibold); box-shadow: inset 2px 0 0 var(--danger); }
.cal-more-tasks { font-size:var(--fs-caption-2); color: var(--text-muted); text-align: center; cursor: pointer; padding:1px 0; }
.cal-more-appts { font-size:var(--fs-caption-2); color: var(--accent); text-align: center; cursor: pointer; padding:1px 0; font-weight: var(--fw-semibold); }
.cal-more-tasks:hover, .cal-more-appts:hover { text-decoration: underline; }

.cal-agenda { display: flex; flex-direction: column; gap:var(--sp-2); }
/* 3.44.10: era var(--border), token inesistente e senza ripiego: il bordo veniva disegnato
   con currentColor, cioe con il colore del testo. Corretto nel token reale --border-color. */
.cal-agenda-day { border: 1px solid var(--border-color); border-radius: var(--radius); padding:var(--sp-3) var(--sp-3); background: var(--bg-card); }
.cal-agenda-day.today { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.cal-agenda-day.past { opacity: .62; }
.cal-agenda-date { font-weight: var(--fw-bold); font-size:var(--fs-footnote); margin-bottom:var(--sp-2); cursor: pointer; display: flex; align-items: center; gap:var(--sp-2); transition: var(--transition); }
.cal-agenda-date:hover { color: var(--accent); }
.cal-today-badge { font-size:var(--fs-caption-2); font-weight: var(--fw-bold); background: var(--accent); color: #fff; padding:1px var(--sp-2); border-radius:var(--radius-sm); }
.cal-agenda-items { display: flex; flex-direction: column; gap:var(--sp-1); }
.cal-agenda-item { display: flex; align-items: center; gap:var(--sp-2); font-size:var(--fs-footnote); padding:var(--sp-2) var(--sp-2); border-radius: var(--radius-sm); cursor: pointer; color: var(--text-primary); transition: var(--transition); }
.cal-agenda-item:hover { background: var(--bg-hover); }
.cal-agenda-item i { font-size:var(--fs-caption-1); color: var(--text-muted); flex-shrink: 0; }
.cal-agenda-item.appt i { color: var(--accent); }
.cal-agenda-item > span:nth-child(3) { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cal-agenda-time { font-variant-numeric: tabular-nums; font-weight: var(--fw-semibold); color: var(--text-secondary); min-width: 38px; }
.cal-agenda-dot { width: 9px; height: 9px; border-radius:50%; flex-shrink: 0; }
.cal-agenda-task { font-size:var(--fs-caption-2); color: var(--text-muted); background: var(--bg-tertiary); padding:1px var(--sp-2); border-radius:var(--radius-sm); white-space: nowrap; }
.cal-agenda-overdue { font-size:var(--fs-caption-2); font-weight: var(--fw-bold); color: var(--danger-ink); background: rgba(239,68,68,.12); padding:1px var(--sp-2); border-radius:var(--radius-sm); }
.cal-agenda-item.overdue { color: var(--danger-ink); }

.cal-day-section { margin-bottom:var(--sp-4); }
.cal-day-section-title { font-size:var(--fs-caption-1); font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: .4px; color: var(--text-muted); margin-bottom:var(--sp-2); display: flex; align-items: center; gap:var(--sp-2); }
/* 3.44.10: era var(--border), token inesistente e senza ripiego: il bordo veniva disegnato
   con currentColor, cioe con il colore del testo. Corretto nel token reale --border-color. */
.cal-day-row { display: flex; align-items: center; gap:var(--sp-3); padding:var(--sp-2) var(--sp-3); border: 1px solid var(--border-color); border-radius: var(--radius); margin-bottom:var(--sp-2); cursor: pointer; transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease); }
.cal-day-row:hover { background: var(--bg-hover); border-color: var(--accent); }
.cal-day-row.overdue { border-left: 3px solid var(--danger); }
.cal-day-row > i.bi-chevron-right { margin-left:auto; color: var(--text-muted); font-size:var(--fs-caption-1); }
.cal-day-time { font-variant-numeric: tabular-nums; font-weight: var(--fw-bold); color: var(--accent); min-width: 42px; }
.cal-day-dot { width: 10px; height: 10px; border-radius:50%; flex-shrink: 0; }
.cal-day-row-main { flex: 1; min-width: 0; font-size:var(--fs-footnote); }
.cal-day-row-sub { font-size:var(--fs-caption-2); color: var(--text-muted); margin-top:var(--sp-05); }

@media (max-width: 768px) {
    .cal-summary { width: 100%; margin-left:0; }
    .calendar-cell { min-height: 64px; }
    .calendar-task span, .calendar-appt { font-size:var(--fs-caption-2); }
}

.docx-preview {
    background: #fff;
    color: #1b1f30;
    max-width: 820px;
    margin:0 auto;
    padding:var(--sp-8) var(--sp-10);
    max-height: 80vh;
    overflow: auto;
    font-size:var(--fs-subhead);
    line-height: 1.65;
    border-radius:var(--radius-xs);
}
.docx-preview > *:first-child { margin-top:0; }
.docx-preview p { margin:0 0 0.7em; }
.docx-preview h1, .docx-preview h2, .docx-preview h3, .docx-preview h4 { margin:1em 0 0.4em; line-height: 1.25; color: #111827; }
.docx-preview h1 { font-size:1.6em; }
.docx-preview h2 { font-size:1.35em; }
.docx-preview h3 { font-size:1.15em; }
.docx-preview ul, .docx-preview ol { margin:0 0 0.7em 1.4em; }
.docx-preview li { margin:0.2em 0; }
.docx-preview img { max-width: 100%; height: auto; }
.docx-preview a { color: var(--accent); }
.docx-preview table { border-collapse: collapse; width: 100%; margin:0.8em 0; font-size:0.95em; }
.docx-preview th, .docx-preview td { border: 1px solid #d8dde6; padding:var(--sp-2) var(--sp-2); text-align: left; vertical-align: top; }
.docx-preview th { background: #f1f4f9; font-weight: var(--fw-semibold); }

.dash-fill-card { display: flex; flex-direction: column; max-height: 65vh; }
.dash-fill-card > .card-body.dash-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; overflow-x: hidden; }
.dash-scroll { overflow-y: auto; }
.dash-scroll::-webkit-scrollbar { width: 8px; }
.dash-scroll::-webkit-scrollbar-track { background: transparent; }
.dash-scroll::-webkit-scrollbar-thumb { background: rgba(120,130,150,0.35); border-radius:var(--radius-xs); }
.dash-scroll:hover::-webkit-scrollbar-thumb { background: rgba(120,130,150,0.55); }

/* 3.44.12: tolte le tre regole di .chat-urgent-btn. Quel pulsante non esiste nel markup della
   barra di scrittura (l'invio urgente si fa tenendo premuto Invia per tre secondi) e il codice
   che lo nominava e' stato appena rimosso: erano istruzioni per un elemento che non c'e'. */

.urgent-overlay {
    position: fixed; inset: 0; z-index: var(--z-urgent);
    display: flex; align-items: center; justify-content: center;
    padding: calc(24px + env(safe-area-inset-top)) calc(24px + env(safe-area-inset-right)) calc(24px + env(safe-area-inset-bottom)) calc(24px + env(safe-area-inset-left));
    background: rgba(20,28,40,0.72); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
    opacity: 0; pointer-events: none; transition: opacity var(--dur-base) var(--ease);
}
.urgent-overlay.visible { opacity: 1; pointer-events: auto; }
/* 3.44.12: era l'unico pannello sovrapposto dell'applicazione senza il trattamento a vetro.
   Tutte le finestre usano --glass-bg-strong con --glass-blur; questa usava --bg-card (0,60) e
   nessuna sfocatura, e sopra la tenda scura veniva fuori un rettangolo grigio piatto: la
   superficie meno importante del prodotto, per l'interruzione piu' importante che l'applicazione
   sappia fare. Ora e' vetro come le altre, e il bordo prende il token del riflesso invece di un
   bianco al 50% scritto a mano. */
.urgent-card {
    width: 100%; max-width: 560px;
    background: var(--glass-bg-strong);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border-radius:var(--radius); padding:var(--sp-8) var(--sp-8) var(--sp-6);
    box-shadow: var(--shadow-xl); border: 1px solid var(--glass-border);
    transform: scale(.95); transition: transform var(--dur-base) var(--ease);
}
.urgent-overlay.visible .urgent-card { transform: scale(1); }
.urgent-flag { display: inline-flex; align-items: center; gap:var(--sp-2); font-weight: var(--fw-bold); color: var(--danger-ink); font-size:var(--fs-footnote); letter-spacing: .04em; text-transform: uppercase; margin-bottom:var(--sp-4); }
.urgent-flag i { font-size:var(--fs-title-3); line-height: var(--lh-title-3); }
.urgent-from { display: flex; align-items: center; gap:var(--sp-3); margin-bottom:var(--sp-4); }
.urgent-from .avatar { width: 44px; height: 44px; border-radius:50%; display: flex; align-items: center; justify-content: center; color: #fff; font-weight: var(--fw-bold); flex-shrink: 0; }
.urgent-from-name { font-weight: var(--fw-bold); color: var(--text-primary); }
.urgent-from-time { font-size:var(--fs-caption-1); color: var(--text-muted); }
/* 3.44.12: un messaggio urgente lungo veniva tagliato a meta' frase senza un solo segnale che
   sotto ci fosse dell'altro: niente sfumatura, niente barra di scorrimento, niente indicatore
   (su un iPhone, 621px di testo dentro 422px di finestra: 199px nascosti). Ora il bordo inferiore
   sfuma: e' il modo consueto di dire "continua". L'imbottitura in basso passa a --sp-8 e la
   sfumatura e' lunga esattamente quanto quella: quando si e' scorsi fino in fondo la sfumatura
   copre solo l'imbottitura, quindi l'ultima riga si legge intera. Il fondo grigio scritto a mano
   e' --bg-tertiary. */
.urgent-text {
    font-size:var(--fs-callout); line-height: 1.6; color: var(--text-primary);
    white-space: pre-wrap; word-break: break-word;
    max-height: 50vh; overflow: auto;
    padding:var(--sp-4) var(--sp-4) var(--sp-8);
    background: var(--bg-tertiary); border-radius:var(--radius-sm); margin-bottom:var(--sp-6);
    -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - var(--sp-8)), transparent 100%);
    mask-image: linear-gradient(to bottom, #000 calc(100% - var(--sp-8)), transparent 100%);
}
/* 3.44.12: white-space:nowrap sull'azione principale. Sotto i 380px "Leggi ora" andava a capo e
   il pulsante primario diventava alto due righe accanto a un "Chiudi" di una riga sola. */
.urgent-read-btn { width: 100%; padding:var(--sp-4); border: none; border-radius:var(--radius-sm); background: var(--danger); color: #fff; font-size:var(--fs-callout); font-weight: var(--fw-bold); white-space: nowrap; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; gap:var(--sp-2); transition: var(--transition); }
.urgent-read-btn:hover { filter: brightness(1.06); }
.urgent-read-btn:disabled { opacity: .7; cursor: default; }

.team-card .role-badge { margin-bottom:var(--sp-2); }
.team-last-access { font-size:var(--fs-caption-1); color: var(--text-muted); margin-bottom:var(--sp-2); display: flex; justify-content: center; align-items: center; gap:var(--sp-1); }
.team-last-access i { font-size:var(--fs-caption-1); }

.chat-urgent-mark { color: var(--danger-ink); font-size:var(--fs-caption-1); }
/* 3.44.12: il filo che segna la bolla urgente era rgba(239,68,68,0.45), cioe' nemmeno il rosso
   dell'applicazione (--danger e' 220,38,38) e per giunta annacquato al 45%. Ora e' il token
   pieno: il segno e' piu' netto, che per un messaggio urgente e' esattamente lo scopo. */
.chat-bubble.urgent { box-shadow: inset 0 0 0 1px var(--danger); }

.bi-send.bi::before { vertical-align: -.250em; }
.bi::before,
[class^="bi-"]::before,
[class*=" bi-"]::before {
    display: inline-block;
    font-family: bootstrap-icons !important;
    font-style: normal;
    font-weight: normal !important;
    font-variant: normal;
    text-transform: none;
    line-height: 1;
    vertical-align: -.125em;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

.urgent-actions { display: flex; gap:var(--sp-3); }
.urgent-actions > button { flex: 1; width: auto; }
.urgent-close-btn { padding:var(--sp-4); border: 1px solid var(--border-color); border-radius:var(--radius-sm); background: var(--glass-bg-strong); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur); color: var(--text-secondary); font-size:var(--fs-callout); font-weight: var(--fw-bold); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; gap:var(--sp-2); transition: var(--transition); }
.urgent-close-btn:hover { color: var(--text-primary); border-color: var(--text-secondary); }
/* 3.44.12: su uno schermo da 320px i due pulsanti affiancati non ci stanno: "Leggi ora" andava a
   capo e restava alto il doppio del "Chiudi" che gli sta accanto, con due larghezze quasi uguali
   e nessuna gerarchia leggibile. Sotto i 380px si impilano a tutta larghezza. column-reverse
   perche' nel markup "Chiudi" viene prima: rovesciando l'ordine l'azione principale sale in
   cima alla pila, che e' dove la mette anche .app-dialog-actions ("azione primaria in alto") in
   tutte le altre finestre dell'applicazione. NB: la prima stesura di questa nota diceva "resta
   in basso, piu' vicina al pollice" — il rovesciamento fa l'esatto contrario, e la resa a schermo
   e' sempre stata quella descritta adesso. Corretta la nota, non la regola: cambiare la regola
   avrebbe messo questa finestra al contrario di tutte le altre. */
@media (max-width: 380px) {
    .urgent-actions { flex-direction: column-reverse; }
    .urgent-actions > button { width: 100%; flex: 0 0 auto; }
}

/* 3.44.12: stesso grigio scritto a mano di .urgent-text, stesso token. */
.urgent-att { display: inline-flex; align-items: center; gap:var(--sp-2); font-size:var(--fs-subhead); font-weight: var(--fw-semibold); color: var(--text-primary); padding:var(--sp-3) var(--sp-4); background: var(--bg-tertiary); border: 1px solid var(--border-color); border-radius:var(--radius-sm); margin-bottom:var(--sp-6); word-break: break-word; }
.urgent-att i { color: var(--accent); }

.cloud-item { position: relative; }
.cloud-item[draggable="true"] { cursor: grab; }
.cloud-item[draggable="true"]:active { cursor: grabbing; }
.cloud-item.dragging { opacity: .45; }
.cloud-item.drag-over { box-shadow: inset 0 0 0 2px var(--accent); background: var(--accent-soft); }
.cloud-item.selected { box-shadow: inset 0 0 0 2px var(--accent); background: var(--accent-soft); }
.cloud-sel-cb { position: absolute; top: 8px; left: 8px; width: 17px; height: 17px; accent-color: var(--accent); cursor: pointer; opacity: 0; transition: opacity var(--dur-fast) var(--ease); z-index: 2; }
.cloud-item:hover .cloud-sel-cb, .cloud-sel-cb:checked, .cloud-item.selected .cloud-sel-cb { opacity: 1; }
.cloud-crumb.drag-over { background: var(--accent-soft); box-shadow: inset 0 0 0 2px var(--accent); border-radius:var(--radius-xs); }
.cloud-selbar { display: flex; align-items: center; gap:var(--sp-3); flex-wrap: wrap; margin:0 0 var(--sp-3); padding:var(--sp-2) var(--sp-4); background: var(--accent-soft); border: 1px solid var(--border-color); border-radius: var(--radius-sm); }
.cloud-sel-count { font-size:var(--fs-footnote); font-weight: var(--fw-semibold); color: var(--text-primary); margin-right:auto; display: inline-flex; align-items: center; gap:var(--sp-2); }
.cloud-picker-tree { max-height: 50vh; overflow: auto; border: 1px solid var(--border-color); border-radius: var(--radius-sm); padding:var(--sp-2); }
/* 3.44.7: la riga era definita qui e piu avanti (picker "Allega dal Cloud"). Il passaggio
   del mouse dichiarato piu avanti non si vedeva mai, perche' :hover:not(.disabled) di questo
   blocco ha specificita maggiore e vinceva pur venendo prima. Le due definizioni sono ora
   unite nell'unico blocco piu avanti; qui restano solo gli stati. */
.cloud-pick-row.active { background: var(--accent-soft); box-shadow: inset 0 0 0 1px var(--accent); font-weight: var(--fw-semibold); }
.cloud-pick-row.disabled { opacity: .5; cursor: not-allowed; }

@media (max-width: 768px) {
    header.content-header { left: 0; }
}

.scad-summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap:var(--sp-4); margin-bottom:var(--sp-4); }
/* 3.44.10: era var(--card-bg,#fff), token inesistente: le schede KPI restavano bianche
   opache mentre tutte le altre schede sono vetro traslucido. Ora usano --bg-card. */
.scad-kpi { position: relative; background: var(--bg-card); border: 1px solid var(--border-color); border-radius:var(--radius-sm); padding:var(--sp-4) var(--sp-4); overflow: hidden; }
.scad-kpi::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 4px; background: var(--accent); }
.scad-kpi-due::before { background: var(--accent); }
.scad-kpi-over::before { background: var(--danger); }
.scad-kpi-paid::before { background: var(--success); }
.scad-kpi-label { font-size:var(--fs-caption-2); text-transform: uppercase; letter-spacing: .04em; color: var(--text-muted); font-weight: var(--fw-semibold); }
/* 3.44.10: era var(--text-color,#111827), token inesistente -> --text-primary. */
.scad-kpi-value { font-size:var(--fs-title-2); line-height: var(--lh-title-2); font-weight: var(--fw-bold); margin-top:var(--sp-1); font-variant-numeric: tabular-nums; color: var(--text-primary); }
.scad-kpi-over .scad-kpi-value { color: var(--danger-ink); }
.scad-kpi-paid .scad-kpi-value { color: var(--success-ink); }
.scad-kpi-sub { font-size:var(--fs-caption-2); color: var(--text-muted); margin-top:var(--sp-05); }

/* Tabella scadenze: scroller SOLO orizzontale. overflow-y:hidden evita che iOS
   lo tratti come scroller verticale (overflow-x:auto altrimenti forza overflow-y:auto),
   e touch-action:pan-x fa sì che lo swipe verticale iniziato sulla tabella scorra la
   PAGINA invece di restare intrappolato qui (bug "scroll scadenzario"). */
.scad-table-wrap { overflow-x: auto; overflow-y: hidden; touch-action: pan-x; -webkit-overflow-scrolling: touch; }
.scad-table th, .scad-table td { vertical-align: middle; }
.scad-date { font-variant-numeric: tabular-nums; font-weight: var(--fw-semibold); white-space: nowrap; }
.scad-paid-on { font-size:var(--fs-caption-2); color: var(--success-ink); font-weight: var(--fw-medium); margin-top:var(--sp-05); }
.scad-client { display: inline-flex; align-items: center; gap:var(--sp-2); }
.scad-client-dot { width: 9px; height: 9px; border-radius:50%; flex: 0 0 auto; box-shadow: var(--shadow-xs); }

.scad-tipo { display: inline-block; font-size:var(--fs-caption-2); font-weight: var(--fw-semibold); padding:var(--sp-05) var(--sp-2); border-radius:var(--radius-full); background: var(--bg-secondary); color: var(--text-muted); border: 1px solid var(--border-color); }
.scad-tipo-fattura { background: var(--accent-soft); color: var(--accent); border-color: transparent; }
.scad-tipo-canone { background: var(--info-soft); color: var(--info-ink); border-color: transparent; }
.scad-tipo-rinnovo { background: #f3e8ff; color: #7e22ce; border-color: transparent; }
.scad-tipo-rata { background: #fef3c7; color: #b45309; border-color: transparent; }

.scad-badge { display: inline-block; font-size:var(--fs-caption-2); font-weight: var(--fw-bold); padding:var(--sp-1) var(--sp-3); border-radius:var(--radius-full); letter-spacing: .02em; }
.scad-badge-attesa { background: var(--info-soft); color: var(--info-ink); }
.scad-badge-oggi { background: #fef3c7; color: #b45309; }
.scad-badge-scaduto { background: var(--danger-soft); color: var(--danger-ink); }
.scad-badge-pagato { background: var(--success-soft); color: var(--success-ink); }

.scad-act { background: transparent; border: 1px solid var(--border-color); color: var(--text-muted); width: 30px; height: 30px; border-radius:var(--radius-xs); cursor: pointer; font-size:var(--fs-subhead); line-height: 1; margin-left:var(--sp-1); transition: var(--transition); }
.scad-act:hover { background: var(--bg-hover); color: var(--accent); border-color: var(--accent); }
.scad-act-danger:hover { background: var(--danger-soft); color: var(--danger-ink); border-color: var(--danger); }

/* 3.44.11-b: le schede riepilogo si dispongono da sole in base allo spazio, senza soglie.
   Prima erano legate a due breakpoint (900 e 520) che, normalizzati a 1024 e 480, spostavano il
   punto di riordino: quattro schede diventavano due gia' a 1024, e a 500px ne comparivano due
   dove l'autore ne voleva una. Con auto-fit il problema non si pone piu'. */

.activity-del {
    flex: 0 0 auto;
    background: transparent;
    border: 1px solid transparent;
    color: var(--text-muted);
    width: 28px; height: 28px;
    border-radius:var(--radius-xs);
    cursor: pointer;
    font-size:var(--fs-footnote); line-height: 1;
    margin-left:var(--sp-2);
    opacity: .4;
    transition: var(--transition);
}
.activity-item:hover .activity-del { opacity: 1; }
.activity-del:hover {
    background: var(--danger-soft);
    color: var(--danger-ink);
    border-color: var(--danger);
}

.scad-num { font-variant-numeric: tabular-nums; white-space: nowrap; font-size:var(--fs-caption-1); }
.scad-pag { font-size:var(--fs-caption-2); color: var(--text-muted); margin-top:var(--sp-1); }
.scad-comm { font-size:var(--fs-caption-2); color: var(--text-muted); margin-top:var(--sp-05); }
.scad-num-chip { display: inline-block; font-size:var(--fs-caption-2); font-weight: var(--fw-semibold); font-variant-numeric: tabular-nums; padding:1px var(--sp-2); border-radius:var(--radius-xs); background: var(--bg-secondary); border: 1px solid var(--border-color); color: var(--text-secondary); }
.client-scad-list { display: flex; flex-direction: column; }
.client-scad-row { display: flex; align-items: center; gap:var(--sp-3); padding:var(--sp-2) 0; border-bottom: 1px solid var(--border-color); font-size:var(--fs-footnote); }
.client-scad-row:last-child { border-bottom: none; }

.scad-badge-parziale { background: #f3e8ff; color: #7e22ce; }

.pq-head { display: flex; align-items: center; justify-content: space-between; gap:var(--sp-3); margin-bottom:var(--sp-2); }
.pq-title { font-size:var(--fs-footnote); font-weight: var(--fw-semibold); color: var(--text-primary); }
.pq-bulk { display: flex; gap:var(--sp-2); }
.pq-subtitle { font-size:var(--fs-caption-2); text-transform: uppercase; letter-spacing: .04em; font-weight: var(--fw-semibold); color: var(--text-muted); margin:var(--sp-3) 0 var(--sp-2); }
.pq-grid { display: grid; grid-template-columns: 1fr 1fr; gap:var(--sp-1) var(--sp-4); }
.pq-row { display: flex; align-items: center; justify-content: space-between; gap:var(--sp-3); padding:var(--sp-2) var(--sp-2); border-radius:var(--radius-xs); cursor: pointer; user-select: none; transition: background var(--dur-fast) var(--ease); }
.pq-row:hover { background: var(--bg-hover); }
.pq-label { font-size:var(--fs-footnote); color: var(--text-primary); }
.pq-switch { position: relative; width: 34px; height: 19px; border-radius:var(--radius-full); background: var(--border-color); flex: 0 0 auto; transition: background var(--dur-fast) var(--ease); }
.pq-switch.on { background: var(--accent); }
.pq-knob { position: absolute; top: 2px; left: 2px; width: 15px; height: 15px; border-radius:50%; background: #fff; box-shadow: var(--shadow-xs); transition: left var(--dur-fast) var(--ease); }
.pq-switch.on .pq-knob { left: 17px; }
.pq-loading { font-size:var(--fs-footnote); color: var(--text-muted); padding:var(--sp-2) 0; }
.pq-usage-bar { height: 8px; border-radius:var(--radius-full); background: var(--bg-secondary); overflow: hidden; margin-bottom:var(--sp-1); }
.pq-usage-fill { height: 100%; border-radius:var(--radius-full); background: var(--accent); transition: width var(--dur-base) var(--ease); }
.pq-usage-txt { font-size:var(--fs-caption-2); color: var(--text-muted); margin-bottom:var(--sp-2); }
.pq-quota-row { display: flex; align-items: center; gap:var(--sp-2); }
.pq-mb { font-size:var(--fs-caption-1); color: var(--text-muted); }
@media (max-width: 480px) { .pq-grid { grid-template-columns: 1fr; } }

.settings-main-col { flex: 1 1 420px; min-width: 0; }
.enc-badge { display: inline-block; font-size:var(--fs-caption-2); font-weight: var(--fw-bold); padding:var(--sp-1) var(--sp-3); border-radius:var(--radius-full); white-space: nowrap; }
.enc-ok { background: var(--success-soft); color: var(--success-ink); }
.enc-warn { background: var(--danger-soft); color: var(--danger-ink); }
.enc-empty { background: var(--bg-secondary); color: var(--text-muted); }

/* 3.44.7: qui c'era una seconda .view-toggle rimasta da una versione precedente. Imponeva
   display:flex e uno spazio di 6px al selettore griglia/lista dei clienti, rompendo il
   gruppo di pulsanti unito definito piu sopra; la regola figlia (.btn.active) non trovava
   nessun elemento, perche' i pulsanti di quel selettore non hanno la classe .btn. */
.team-table td { vertical-align: middle; }
.team-row-id { display: inline-flex; align-items: center; gap:var(--sp-3); }
.team-row-nm { display: inline-flex; flex-direction: column; min-width: 0; }
.team-row-name { font-weight: var(--fw-semibold); }
.team-row-mail { font-size:var(--fs-caption-2); color: var(--text-muted); }

.pq-locked { cursor: default; opacity: .85; }
.pq-locked:hover { background: transparent; }
.pq-note { font-size:var(--fs-caption-1); color: var(--text-muted); background: var(--bg-secondary); border-radius:var(--radius-xs); padding:var(--sp-2) var(--sp-3); margin-bottom:var(--sp-3); }

.team-table .role-badge { display: inline-block; padding:var(--sp-1) var(--sp-3); border-radius:var(--radius-full); }

.pq-ico { width: 18px; display: inline-block; text-align: center; margin-right:var(--sp-2); color: var(--accent); font-size:var(--fs-footnote); flex: 0 0 auto; }
.pq-label { display: inline-flex; align-items: center; min-width: 0; }

.pg-hidden { display: none !important; }
/* 3.44.12-b: i comandi di pagina finivano DENTRO lo scorrimento orizzontale della tabella.
   pgInit inserisce la barra subito dopo la tabella, che su Scadenzario sta in .scad-table-wrap
   e su Compiti in un .card-body con overflow-x:auto: la barra e' quindi larga quanto la parte
   visibile, ma parte da sinistra e scorre insieme al resto. Misurato a 440px su Scadenzario:
   portando la tabella a fondo corsa (scrollLeft 567 di 567) la barra passava da x=13 a x=-554,
   cioe' fuori dallo schermo per intero — ed e' esattamente cio' che mostra la schermata mandata
   dall'utente. Il guaio e' che scorrere non e' facoltativo: la colonna "Azioni" sta in fondo,
   quindi si finisce sempre nella posizione in cui la paginazione non c'e' piu'. Ancorandola al
   bordo sinistro dello scorrimento resta ferma qualunque sia la posizione della tabella, senza
   spostarla dal punto in cui il codice la mette. Verificato: a fondo corsa resta a x=13. */
.pg-bar {
    display: flex; align-items: center; justify-content: flex-end; gap:var(--sp-4); flex-wrap: wrap;
    position: sticky; left: 0;
    margin-top:var(--sp-4); padding:var(--sp-3) var(--sp-4);
    font-size:var(--fs-footnote); color: var(--text-secondary);
    background: var(--glass-bg);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    box-shadow: 0 4px 16px rgba(15, 23, 42, 0.06), inset 0 1px 0 rgba(255, 255, 255, 0.35);
}
.pg-bar:empty { display: none; }
.pg-info { color: var(--text-muted); }
.pg-ctrl { display: inline-flex; align-items: center; gap:var(--sp-1); }
.pg-btn { border: 1px solid var(--glass-border); background: rgba(255, 255, 255, 0.55); color: var(--text-primary); border-radius:var(--radius-sm); width: 30px; height: 30px; display: inline-flex; align-items: center; justify-content: center; cursor: pointer; font-size:var(--fs-caption-1); transition: background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease); box-shadow: var(--shadow-xs); }
.pg-btn:active:not(:disabled) { transform: scale(0.94); }
.pg-btn:hover:not(:disabled) { background: var(--bg-tertiary); }
.pg-btn:disabled { opacity: .4; cursor: default; }
.pg-page { margin:0 var(--sp-1); white-space: nowrap; }
.pg-size { width: auto; max-width: 160px; padding:var(--sp-2) var(--sp-8) var(--sp-2) var(--sp-3); font-size:var(--fs-caption-1); border-radius:var(--radius-sm); background-color: rgba(255, 255, 255, 0.55); border: 1px solid var(--glass-border); }

.chat-back-btn { display: none; position: absolute; top: 14px; left: 12px; z-index: 5; width: 40px; height: var(--control-h); align-items: center; justify-content: center; border: 1px solid var(--border-color); background: var(--bg-card); color: var(--text-primary); border-radius:var(--radius-sm); font-size:var(--fs-callout); cursor: pointer; box-shadow: var(--shadow-xs); }

@media (max-width: 768px) {
    input.form-control, select.form-select, textarea.form-control, .chat-input, .vault-search input { font-size:var(--fs-callout); }
    /* 3.44.12: allineata alla regola base — si sottrae anche la tacca. NB: sotto i 768px vince
       comunque la dichiarazione del blocco "CHAT MOBILE" piu' in basso; questa resta coerente
       perche' non si contraddicano fra loro. */
    .chat-app { height: calc(100dvh - var(--header-height) - env(safe-area-inset-top)); max-height: calc(100dvh - var(--header-height) - env(safe-area-inset-top)); }
    .chat-conversation { position: relative; }
    .chat-back-btn { display: inline-flex; }
    .chat-header { padding-left:var(--sp-16); }
    .card-body:has(> .data-table) { overflow-x: auto; -webkit-overflow-scrolling: touch; }
    .modal-overlay {
            /* 3.44.15: qui c'era "padding:var(--sp-3)", 12px secchi, che annullavano le zone
               sicure dichiarate nella regola di base: nell'app installata su iPhone il titolo
               della finestra e la X per chiuderla finivano sotto la barra di sistema — si
               vedeva il bordo della finestra e non si riusciva a chiuderla dall'alto. E' la
               STESSA regressione che il 3.44.7 aveva corretto nella regola di base (il suo
               commento la descrive parola per parola), ricomparsa in tre punti, tutti e tre
               dentro i blocchi per schermo stretto: cioe' esattamente dove morde. Il margine
               piu' stretto del telefono si tiene, ma senza mai scendere sotto l'incavo. */
            padding: max(var(--sp-3), env(safe-area-inset-top)) max(var(--sp-3), env(safe-area-inset-right))
                     max(var(--sp-3), env(safe-area-inset-bottom)) max(var(--sp-3), env(safe-area-inset-left));
        }
    .modal { max-height: 92dvh; }
    .modal-body { overflow-wrap: anywhere; word-break: break-word; }
    .modal-body img, .modal-body video, .modal-body canvas, .modal-body svg, .modal-body table { max-width: 100%; }
    .modal [style*="grid-template-columns"] > * { min-width: 0; }
    .modal-body .client-meta-row, .modal-body .client-meta-row > * { min-width: 0; }
    .modal-footer { flex-wrap: wrap; gap:var(--sp-2); }
    .modal-footer .btn { flex: 1 1 auto; justify-content: center; }
    /* 3.44.12-b: qualche finestra tiene i pulsanti nel CORPO invece che nel piede, dentro una
       riga flex scritta nell'attributo style e quindi senza andare a capo. Su "Template email
       dei solleciti" a 320px la coppia Annulla + Salva template chiede 273px dentro i 262
       disponibili e il pulsante che salva sporgeva 7px oltre il bordo della finestra. Stesso
       trattamento gia' dato al piede: si va a capo. Andare a capo scatta solo quando la roba
       non ci sta, quindi dove ci stava non cambia niente. */
    .modal-body div[style*="display:flex"] { flex-wrap: wrap; }
    .filter-bar { padding:var(--sp-3) var(--sp-3); gap:var(--sp-2); }
    .filter-bar .form-select, .filter-bar .searchable-select { max-width: 100%; }
    .filter-bar .btn, .content-header .btn { padding:var(--sp-2) var(--sp-3); }
    .pg-bar { justify-content: center; gap:var(--sp-3); padding:var(--sp-2) var(--sp-3); }
    .calendar-grid { gap:1px; }
    .calendar-header-cell { padding:var(--sp-2) var(--sp-05); font-size:var(--fs-caption-2); }
    .calendar-cell { padding:var(--sp-1) var(--sp-1); min-height: 56px; font-size:var(--fs-caption-2); }
    .calendar-cell .day-num { font-size:var(--fs-caption-2); margin-bottom:var(--sp-05); }
    .calendar-task, .calendar-appt { font-size:0; height: 6px; padding:0; margin:var(--sp-05) 0; border-radius:var(--radius-xs); overflow: hidden; line-height: 0; }
    .cal-more-tasks, .cal-more-appts { font-size:var(--fs-caption-2); padding:0 var(--sp-05); }
    .cal-filters { flex-wrap: wrap; gap:var(--sp-2); }
    .cal-legend { flex-wrap: wrap; gap:var(--sp-2); }
    .scad-kpi { padding:var(--sp-3); }
    .chat-input-area { padding: 10px 10px calc(10px + env(safe-area-inset-bottom)); gap:var(--sp-2); align-items: flex-end; }
    /* 3.44.12: tolto max-height:110px, secondo dei tre tetti di crescita contraddittori: ne resta
       uno solo, nella regola base. */
    .chat-input { min-height: var(--control-h); padding:var(--sp-3) var(--sp-3); }
    /* 3.44.12: tolta la riga che ridimensionava i due pulsanti (e il .chat-urgent-btn
       inesistente): la misura e' ora dichiarata in un punto solo, nel blocco "Invio tondo". */
}

@media (max-width: 480px) {
    .stats-grid { grid-template-columns: 1fr 1fr; gap:var(--sp-3); }
    .content-body { padding:var(--sp-3); }
    .pq-bulk { flex-wrap: wrap; }
    .team-stats { gap:var(--sp-2); }
}

.dash-head-tools { display: flex; align-items: center; gap:var(--sp-2); }
.dash-sort { width: auto; max-width: 165px; padding:var(--sp-1) var(--sp-6) var(--sp-1) var(--sp-3); font-size:var(--fs-caption-1); border-radius:var(--radius-sm); }
.dash-appt-date { display: flex; flex-direction: column; align-items: center; justify-content: center; width: 44px; height: 44px; border-radius:var(--radius-sm); background: var(--bg-secondary); border: 1px solid var(--border-color); flex: 0 0 auto; line-height: 1; }
.dash-appt-date.today { background: var(--warning-soft); border-color: var(--warning); }
.dash-appt-day { font-size:var(--fs-callout); font-weight: var(--fw-bold); color: var(--text-primary); }
.dash-appt-mon { font-size:var(--fs-caption-2); text-transform: uppercase; color: var(--text-muted); margin-top:var(--sp-05); }

.dash-appt-past { opacity: .62; }
.dash-appt-past:hover { opacity: 1; }
.dash-appt-date.past { background: var(--danger-soft); border-color: var(--danger); }

.changelog-body { max-height: 380px; overflow-y: auto; }
.changelog-row { display: flex; align-items: flex-start; gap:var(--sp-3); padding:var(--sp-2) 0; border-bottom: 1px solid var(--border-color); font-size:var(--fs-footnote); }
.changelog-row:last-child { border-bottom: none; }
.changelog-v { flex: 0 0 auto; min-width: 86px; text-align: center; font-size:var(--fs-caption-2); font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; padding:var(--sp-1) var(--sp-2); border-radius:var(--radius-full); background: var(--bg-secondary); border: 1px solid var(--border-color); color: var(--text-secondary); }
.changelog-v.current { background: var(--accent); border-color: var(--accent); color: #fff; }
.changelog-d { color: var(--text-secondary); line-height: 1.45; }

.th-sort { cursor: pointer; user-select: none; white-space: nowrap; transition: var(--transition); }
.th-sort:hover { color: var(--accent); }
.th-sort-ico { font-size:var(--fs-caption-2); opacity: .55; margin-left:var(--sp-1); }
.th-sort-ico.active { opacity: 1; color: var(--accent); }

/* 3.44.15: la stella che distingue una credenziale preferita era #f59e0b su fondo chiaro: 2,02:1,
   contro il 3:1 che serve a un'icona che porta uno stato — si vedeva solo sapendo che c'era.
   Passa all'inchiostro ambra (5,41:1), che nel foglio non aveva ancora un uso qui. Il testo del
   pulsante grande stava gia' bene (6,37:1) e non cambia. */
.vault-fav-ico { color: var(--warning-ink); font-size:var(--fs-footnote); }
.vault-fav-btn.on { color: var(--warning-ink); }
#vaultFavBtn.on { background: #fef3c7; border-color: var(--warning-ink); color: #92400e; }

.ss-option.ss-create { color: var(--accent); font-weight: var(--fw-semibold); border-top: 1px solid var(--border-color); }

.pptx-render-box { width: 100%; max-height: 80vh; overflow: auto; padding:var(--sp-4); background: #f1f5f9; border-radius:var(--radius-sm); }
.pptx-render-box .slide { margin:0 auto var(--sp-4) auto !important; box-shadow: var(--shadow-sm); border-radius:var(--radius-xs); }

.scad-canone { font-size:var(--fs-caption-2); color: var(--danger-ink); font-weight: var(--fw-semibold); margin-top:var(--sp-05); white-space: nowrap; }

/* Scheda cliente: testo scuro per leggibilità (3.36.1) */
/* 3.44.10: qui c'erano cinque regole che riscrivevano i colori a mano, due delle quali
   agganciate al TESTO dell'attributo style ([style*="var(--text-muted)"]). Era il punto piu
   fragile del foglio: bastava scrivere lo stesso token con una spaziatura diversa
   (var( --text-muted )) e la regola smetteva di agganciarsi, in silenzio. Al posto di
   inseguire le scritture si ridefiniscono i token dentro al pannello: tutto cio' che gia'
   usa var(--text-muted) o var(--text-secondary) si scurisce da solo, senza !important e
   senza dipendere da come e' scritto l'attributo. */
#clientDetailBody {
    --text-muted: var(--text-secondary);
    --text-secondary: var(--text-primary);
}
/* Conservate: non sono aggiustamenti di contrasto ma scelte di impaginazione del pannello. */
#clientDetailBody .client-meta-row { font-size:var(--fs-footnote); }
#clientDetailBody .client-meta-row i { color: var(--accent); }

/* Anteprima fogli Excel negli allegati (3.36.4) */
.xlsx-preview { padding:var(--sp-4); width: 100%; max-height: 80vh; overflow: auto; }
/* 3.44.10: era var(--text-color,#111827), token inesistente -> --text-primary. */
.xlsx-sheet-name { font-size:var(--fs-caption-1); font-weight: var(--fw-bold); color: var(--text-primary); margin:var(--sp-3) 0 var(--sp-2); }
.xlsx-sheet-name:first-child { margin-top:0; }
.xlsx-preview table { border-collapse: collapse; font-size:var(--fs-caption-1); background: #fff; margin-bottom:var(--sp-2); }
.xlsx-preview td { border: 1px solid var(--border-color); padding:var(--sp-1) var(--sp-2); color: #111827; white-space: nowrap; max-width: 340px; overflow: hidden; text-overflow: ellipsis; }
.xlsx-preview tr:first-child td { background: #f1f5f9; font-weight: var(--fw-semibold); }

/* ============ Altezza uniforme dei controlli form (3.36.8) ============ */
.form-control, .form-select, .search-box input, .vault-search input {
    height: var(--control-h);
    padding-top:0;
    padding-bottom:0;
    box-sizing: border-box;
    line-height: normal;
}
textarea.form-control:not(.chat-input) {
    height: auto;
    min-height: 90px;
    line-height: 1.5;
    padding-top:var(--sp-3);
    padding-bottom:var(--sp-3);
}
/* 3.44.12: tolta la riga "textarea.form-control.chat-input { min-height:42px; height:42px;
   line-height: var(--control-h) }". L'interlinea da 44px dentro un campo alto 44 e' esattamente
   il difetto che il commento della 3.44.9, poco piu' avanti, dichiara risolto: era ancora qui, e
   se non si vedeva era solo perche' la regola scritta dopo, a parita' di specificita', vince per
   ordine. Bastava spostare quel blocco piu' in su e la scritta "Messaggio" tornava fuori asse.
   Anche le altre due dichiarazioni erano gia' battute (la stessa regola successiva impone
   height:auto e min-height:--control-h): il campo non parte piu' da 42px ma da 44, come i due
   pulsanti che gli stanno accanto. */
.filter-bar .form-select, .filter-bar .form-control,
.content-header .form-select, .content-header .form-control { height: var(--control-h); }

/* Dashboard: titolo card sopra, filtri sotto (3.36.17) */
.dashboard-grid .card-header, .dashboard-grid-full .card-header {
    flex-direction: column;
    align-items: flex-start;
    gap:var(--sp-3);
}
.dashboard-grid .card-header .dash-head-tools,
.dashboard-grid-full .card-header .dash-head-tools {
    display: flex;
    flex-wrap: wrap;
    gap:var(--sp-2);
    width: 100%;
}

/* 3.44.10: rimossa la pezza "#syncBtnRow .btn { height:40px }" (3.36.26): ora i 40px e la
   centratura sono nel .btn di base, quindi valgono ovunque e non solo in questa barra. */

/* Allega dal Cloud: picker (3.37.0) */
.cloud-pick-row { display: flex; align-items: center; gap:var(--sp-3); padding:var(--sp-3) var(--sp-3); font-size:var(--fs-footnote); border: 1px solid var(--border-color); border-radius:var(--radius-sm); margin-bottom:var(--sp-2); cursor: pointer; transition: var(--transition); }
.cloud-pick-row:hover:not(.disabled) { background: var(--accent-soft); border-color: var(--accent); }
.cloud-pick-row > i { font-size:var(--fs-title-3); line-height: var(--lh-title-3); color: var(--accent); flex-shrink: 0; }
.cloud-pick-info { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.cloud-pick-name { font-size:var(--fs-footnote); color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cloud-pick-path { font-size:var(--fs-caption-2); color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cloud-pick-size { font-size:var(--fs-caption-2); color: var(--text-muted); white-space: nowrap; flex-shrink: 0; }

/* Allega dal Cloud: breadcrumb navigazione (3.37.1) */
.cloud-pick-crumbs { display: flex; flex-wrap: wrap; align-items: center; gap:var(--sp-1); font-size:var(--fs-footnote); }
.cloud-pick-crumbs > i { color: var(--text-muted); font-size:var(--fs-caption-1); }
.cloud-pick-crumb { color: var(--accent); cursor: pointer; padding:var(--sp-05) var(--sp-2); border-radius:var(--radius-xs); transition: var(--transition); }
.cloud-pick-crumb:hover { background: rgba(99,102,241,.1); }
.cloud-pick-crumb.active { color: var(--text-primary); cursor: default; font-weight: var(--fw-semibold); }

/* Sessioni in corso (Impostazioni) */
.session-row { display: flex; align-items: center; gap:var(--sp-4); padding:var(--sp-3) var(--sp-4); border: 1px solid var(--border-color); border-radius: var(--radius-sm); margin-bottom:var(--sp-3); background: var(--bg-card); }
.session-row:last-child { margin-bottom:0; }
.session-row.current { border-color: var(--accent); }
.session-icon { width: 40px; height: var(--control-h); flex: 0 0 40px; border-radius:var(--radius-sm); display: flex; align-items: center; justify-content: center; background: var(--accent-soft); color: var(--accent); font-size:var(--fs-title-3); line-height: var(--lh-title-3); }
.session-info { flex: 1; min-width: 0; }
.session-title { font-weight: var(--fw-semibold); font-size:var(--fs-subhead); color: var(--text-primary); display: flex; align-items: center; gap:var(--sp-2); flex-wrap: wrap; }
.session-current { font-size:var(--fs-caption-2); font-weight: var(--fw-semibold); color: var(--success-ink); background: var(--success-soft); padding:var(--sp-05) var(--sp-2); border-radius:var(--radius-lg); }
.session-meta { font-size:var(--fs-caption-1); color: var(--text-muted); margin-top:var(--sp-1); word-break: break-word; }
.session-meta i { margin-right:var(--sp-1); }
.session-revoke { color: var(--danger-ink) !important; flex: 0 0 auto; }
.session-revoke:hover { background: var(--danger-soft) !important; }

/* Sessioni: vista admin raggruppata per utente */
.session-user { border: 1px solid var(--border-color); border-radius: var(--radius-sm); margin-bottom:var(--sp-4); overflow: hidden; }
.session-user:last-child { margin-bottom:0; }
.session-user.blocked { border-color: var(--danger); }
.session-user-head { display: flex; align-items: center; gap:var(--sp-3); padding:var(--sp-3) var(--sp-4); background: var(--bg-tertiary); border-bottom: 1px solid var(--border-color); }
.su-avatar { width: 36px; height: 36px; flex: 0 0 36px; border-radius:50%; color: #fff; font-weight: var(--fw-semibold); font-size:var(--fs-footnote); display: flex; align-items: center; justify-content: center; }
.su-meta { flex: 1; min-width: 0; }
.su-name { font-weight: var(--fw-semibold); font-size:var(--fs-subhead); color: var(--text-primary); display: flex; align-items: center; gap:var(--sp-2); flex-wrap: wrap; }
.su-role { font-size:var(--fs-caption-1); color: var(--text-muted); text-transform: capitalize; }
.su-blocked { font-size:var(--fs-caption-2); font-weight: var(--fw-semibold); color: var(--danger-ink); background: var(--danger-soft); padding:var(--sp-05) var(--sp-2); border-radius:var(--radius-lg); }
.su-toggle { flex: 0 0 auto; }
.su-block { color: var(--danger-ink) !important; }
.su-block:hover { background: var(--danger-soft) !important; }
.su-actions { display: flex; align-items: center; gap:var(--sp-2); flex: 0 0 auto; flex-wrap: wrap; justify-content: flex-end; }
.su-disc-dups { white-space: nowrap; }
.su-sessions { padding:var(--sp-3) var(--sp-4); }
.su-sessions .session-row { background: var(--bg-card); }
.su-empty { font-size:var(--fs-caption-1); color: var(--text-muted); padding:var(--sp-05); }

/* Sessioni: lista scrollabile come il registro versioni + ricerca utente (admin) */
.sessions-body { max-height: 380px; overflow-y: auto; }
.session-search { position: relative; margin-bottom:var(--sp-3); }
.session-search i { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--text-muted); font-size:var(--fs-subhead); pointer-events: none; }
.session-search input { width: 100%; padding:var(--sp-2) var(--sp-3) var(--sp-2) var(--sp-8); border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-input); color: var(--text-primary); font-size:var(--fs-footnote); }
.session-search input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-glow); }

/* ===== Protezione file — design moderno (colonna stretta) ===== */
.prot-spin { display: inline-block; animation: mfSpin var(--dur-spin) linear infinite; }

.prot-hero { display: flex; align-items: center; gap:var(--sp-4); padding:var(--sp-4); border-radius: var(--radius-sm); background: linear-gradient(135deg, var(--accent-soft), transparent 80%); border: 1px solid var(--border-color); margin-bottom:var(--sp-4); }
.prot-shield { position: relative; width: 46px; height: 46px; flex: 0 0 46px; border-radius:50%; display: flex; align-items: center; justify-content: center; background: var(--accent); color: #fff; font-size:var(--fs-title-2); line-height: var(--lh-title-2); box-shadow: 0 6px 18px -6px var(--accent-glow); }
.prot-shield::after { content: ''; position: absolute; inset: -4px; border-radius:50%; border: 2px solid var(--accent); opacity: 0; animation: protPulse 2.6s ease-out infinite; }
@keyframes protPulse { 0% { transform: scale(.8); opacity: .5; } 70% { transform: scale(1.4); opacity: 0; } 100% { opacity: 0; } }
.prot-hero-title { font-weight: var(--fw-semibold); font-size:var(--fs-subhead); color: var(--text-primary); }
.prot-hero-sub { font-size:var(--fs-caption-1); color: var(--text-muted); margin-top:var(--sp-1); line-height: 1.45; }

.prot-scan-btn { width: 100%; justify-content: center; margin-bottom:var(--sp-3); }
.prot-hint { font-size:var(--fs-caption-1); color: var(--text-muted); margin-top:var(--sp-2); line-height: 1.45; }
.prot-hint i { margin-right:var(--sp-1); }

#encScanReport { --fade-dist: 8px; animation: mfFadeUp var(--dur-slow) var(--ease-out) both; }

.prot-summary { display: flex; align-items: center; gap:var(--sp-4); padding:var(--sp-4); border-radius: var(--radius-sm); border: 1px solid var(--border-color); margin-bottom:var(--sp-4); }
.prot-summary.ok { background: var(--success-soft); }
.prot-summary.warn { background: var(--danger-soft); }
.prot-ring { --pct: 100; position: relative; width: 56px; height: 56px; flex: 0 0 56px; border-radius:50%; background: conic-gradient(var(--accent) calc(var(--pct) * 1%), var(--bg-tertiary) 0); display: flex; align-items: center; justify-content: center; }
.prot-summary.ok .prot-ring { background: conic-gradient(var(--success) calc(var(--pct) * 1%), var(--bg-tertiary) 0); }
.prot-summary.warn .prot-ring { background: conic-gradient(var(--danger) calc(var(--pct) * 1%), var(--bg-tertiary) 0); }
.prot-ring::before { content: ''; position: absolute; inset: 5px; border-radius:50%; background: var(--bg-card); }
.prot-ring span { position: relative; font-size:var(--fs-footnote); font-weight: var(--fw-bold); color: var(--text-primary); }
.prot-summary-info { min-width: 0; }
.prot-summary-title { font-weight: var(--fw-bold); font-size:var(--fs-subhead); color: var(--text-primary); }
.prot-summary-sub { font-size:var(--fs-caption-1); color: var(--text-muted); margin-top:var(--sp-1); }

.prot-cats { display: flex; flex-direction: column; gap:var(--sp-3); }
.prot-cat { padding:var(--sp-3) var(--sp-3); border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-card); }
.prot-cat-top { display: flex; align-items: center; justify-content: space-between; gap:var(--sp-2); }
.prot-cat-label { font-size:var(--fs-footnote); font-weight: var(--fw-semibold); color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.prot-cat-badge { font-size:var(--fs-caption-2); font-weight: var(--fw-semibold); padding:var(--sp-05) var(--sp-2); border-radius:var(--radius-full); white-space: nowrap; flex: 0 0 auto; }
.prot-cat.ok .prot-cat-badge { color: var(--success-ink); background: var(--success-soft); }
.prot-cat.warn .prot-cat-badge { color: var(--danger-ink); background: var(--danger-soft); }
.prot-cat.empty .prot-cat-badge { color: var(--text-muted); background: var(--bg-tertiary); }
.prot-cat-meta { font-size:var(--fs-caption-2); color: var(--text-muted); margin:var(--sp-1) 0 var(--sp-2); }
.prot-bar { height: 6px; border-radius:var(--radius-full); background: var(--bg-tertiary); overflow: hidden; }
.prot-bar-fill { height: 100%; border-radius:var(--radius-full); background: var(--success); transform-origin: left; animation: protBar .65s cubic-bezier(.22,1,.36,1); }
.prot-cat.warn .prot-bar-fill { background: var(--danger); }
.prot-cat.empty .prot-bar-fill { background: var(--text-muted); }
@keyframes protBar { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.prot-allok { display: flex; align-items: center; gap:var(--sp-2); font-size:var(--fs-footnote); font-weight: var(--fw-semibold); color: var(--success-ink); margin-top:var(--sp-2); }
.prot-allok i { font-size:var(--fs-callout); }

/* 3.43.40: la dissolvenza di ingresso del contenuto è stata RIMOSSA per allinearsi allo
   skeleton loader. Durante il caricamento pagina si vedono le sagome (skeleton); appena la
   nuova pagina è pronta il contenuto reale compare SUBITO, senza un fade da 1,2s che si
   sovrapporrebbe alla transizione. Le singole card non animano una per una. Il fade-in resta
   solo su elementi esterni al contenuto (es. la card di login). */
.content-body { animation: none; }
.content-body .fade-in { animation: none; }

/* Anteprima Word ad alta fedeltà (docx-preview): vista a pagine come in Word */
#previewModalBody .docx-view { width: 100%; max-height: 82vh; overflow: auto; -webkit-overflow-scrolling: touch; background: #eef0f3; border-radius:var(--radius-xs); }
#previewModalBody .docx-view .docx-wrapper { background: transparent; padding:var(--sp-6) var(--sp-4); }
#previewModalBody .docx-view .docx-wrapper > section.docx { box-shadow: var(--shadow-sm); margin:0 auto var(--sp-5); }
@media (max-width: 768px) {
    #previewModalBody .docx-view .docx-wrapper { padding:var(--sp-3) var(--sp-2); }
}

/* Editor di testo semplice (.txt) */
/* 3.44.10: pila monospazio propria sostituita dal token --font-mono, unica fonte. */
.text-editor-area { width: 100%; height: 100%; border: none; outline: none; resize: none; padding:var(--sp-5) var(--sp-5); font-family: var(--font-mono); font-size:var(--fs-subhead); line-height: 1.6; color: var(--text-primary); background: var(--bg-primary); border-radius:0 0 var(--radius-sm) var(--radius-sm); box-sizing: border-box; }
/* 3.44.15: l'area di scrittura del documento non aveva NESSUNA regola di messa a fuoco (bordo e
   contorno erano entrambi spenti): arrivandoci col Tab non cambiava niente a schermo. Un filo
   interno --accent, che non sposta il testo di un pixel perche' non e' un bordo ma un'ombra. */
.text-editor-area:focus { box-shadow: inset 0 0 0 2px var(--accent); }

/* ============ Card "Estensioni permesse" (3.41.0) ============ */
.ext-grid { display: flex; flex-wrap: wrap; gap:var(--sp-3); }
.ext-chip {
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap:var(--sp-1);
    width: 74px; padding:var(--sp-3) var(--sp-2);
    border: 1px solid var(--border-color); border-radius: var(--radius-xs);
    background: var(--bg-secondary); color: var(--text-muted);
    opacity: 0.4; user-select: none;
    transition: opacity var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.ext-chip i { font-size:var(--fs-title-1); line-height: 1; }
.ext-chip-label { font-size:var(--fs-caption-2); font-weight: var(--fw-semibold); letter-spacing: .3px; }
.ext-chip.active { opacity: 1; color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
.ext-chip[role="button"] { cursor: pointer; }
.ext-chip[role="button"]:hover { transform: translateY(-1px); border-color: var(--accent); }
.ext-chip[role="button"]:active { transform: translateY(0); }
.ext-chip[role="button"]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (max-width: 480px) {
    .ext-grid { gap:var(--sp-2); justify-content: space-between; }
    .ext-chip { width: calc(25% - 6px); padding:var(--sp-3) var(--sp-1); }
}

/* Sezioni a tendina per categoria (card Estensioni permesse) */
details.ext-cat { border: 1px solid var(--border-color); border-radius: var(--radius-xs); margin-bottom:var(--sp-3); overflow: hidden; }
details.ext-cat:last-child { margin-bottom:0; }
details.ext-cat > summary {
    list-style: none; cursor: pointer; user-select: none;
    display: flex; align-items: center; justify-content: space-between; gap:var(--sp-3);
    padding:var(--sp-3) var(--sp-4); font-weight: var(--fw-semibold); font-size:var(--fs-subhead);
    background: var(--bg-secondary); color: var(--text-primary);
    transition: var(--transition);
}
details.ext-cat > summary::-webkit-details-marker { display: none; }
details.ext-cat > summary::marker { content: ""; }
details.ext-cat > summary:hover { background: var(--bg-tertiary); }
/* 3.44.12: sotto i 768px .modal-body dichiara overflow-wrap:anywhere per non far mai sbordare
   niente da una finestra. "anywhere" pero' azzera anche la larghezza minima di contenuto, quindi
   un elemento flessibile puo' essere schiacciato fin dentro le parole: nel pannello di
   amministrazione della chat, su uno schermo da 320px, il titolo "Impostazioni chat" si leggeva
   "Impostazio / ni chat". "break-word" fa lo stesso lavoro dove serve — spezza una parola che da
   sola non ci sta — ma lascia alla riga la sua misura minima, cioe' la parola piu' lunga: il
   titolo va a capo fra le due parole, come e' giusto, e non si taglia mai a meta'. */
.ext-cat-name { display: inline-flex; align-items: center; overflow-wrap: break-word; word-break: normal; }
.ext-cat-chevron { transition: transform var(--dur-fast) var(--ease); margin-right:var(--sp-2); color: var(--text-muted); font-size:var(--fs-footnote); }
details.ext-cat[open] > summary .ext-cat-chevron { transform: rotate(90deg); }
.ext-cat-count { font-size:var(--fs-caption-1); color: var(--text-muted); font-weight: var(--fw-medium); white-space: nowrap; }
details.ext-cat > .ext-grid { padding:var(--sp-4); border-top: 1px solid var(--border-color); }

/* ============================ CENTRO NOTIFICHE ============================= */
.notif-wrap { position: relative; }
.notif-badge {
    position: absolute; top: 0; right: 0;
    min-width: 17px; height: 17px; padding:0 var(--sp-1);
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--danger); color: #fff;
    font-size:var(--fs-caption-2); font-weight: var(--fw-bold); line-height: 1;
    border-radius:var(--radius-xs); border: 2px solid var(--bg-secondary);
    pointer-events: none;
}
.notif-panel {
    position: absolute; top: calc(100% + 8px); right: 0;
    width: 370px; max-width: 92vw;
    background: rgb(255 255 255 / 90%);
    -webkit-backdrop-filter: blur(18px) saturate(150%);
    backdrop-filter: blur(18px) saturate(150%);
    border: 1px solid var(--border-color);
    border-radius:var(--radius); box-shadow: var(--shadow);
    z-index: var(--z-popover); overflow: hidden;
    opacity: 0; transform: translateY(-6px); pointer-events: none;
    transition: opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.notif-panel.open { opacity: 1; transform: translateY(0); pointer-events: auto; }
.notif-panel-head {
    display: flex; align-items: center; justify-content: space-between;
    padding:var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--border-color);
}
.notif-panel-title { font-size:var(--fs-subhead); font-weight: var(--fw-bold); color: var(--text-primary); }
.notif-panel-title i { color: var(--accent); margin-right:var(--sp-2); }
.notif-markall {
    background: none; border: none; cursor: pointer;
    font-size:var(--fs-caption-1); color: var(--accent); padding:var(--sp-05) var(--sp-1); border-radius:var(--radius-xs);
    transition: var(--transition);
}
.notif-markall:hover { background: var(--accent-soft); }
.notif-list { max-height: min(62vh, 460px); overflow-y: auto; }
.notif-item {
    display: flex; gap:var(--sp-3); align-items: flex-start;
    padding:var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--border-color);
    cursor: pointer; transition: background var(--dur-fast) var(--ease);
}
.notif-item:last-child { border-bottom: none; }
.notif-item:hover { background: var(--bg-hover); }
.notif-item.unread { background: var(--accent-soft); }
.notif-item.unread:hover { background: var(--accent-soft); }
.notif-item-icon {
    flex: 0 0 32px; width: 32px; height: 32px; border-radius:var(--radius-xs);
    display: flex; align-items: center; justify-content: center;
    background: var(--bg-secondary); color: var(--accent); font-size:var(--fs-subhead);
}
.notif-item.unread .notif-item-icon { background: #fff; }
.notif-item-main { flex: 1 1 auto; min-width: 0; }
.notif-item-title {
    font-size:var(--fs-footnote); font-weight: var(--fw-semibold); color: var(--text-primary);
    display: flex; align-items: center; gap:var(--sp-2);
}
.notif-item-title .notif-dot {
    flex: 0 0 7px; width: 7px; height: 7px; border-radius:50%; background: var(--danger);
}
.notif-item-body {
    font-size:var(--fs-caption-1); color: var(--text-secondary); margin-top:var(--sp-05);
    overflow: hidden; text-overflow: ellipsis; display: -webkit-box;
    -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.notif-item-time { font-size:var(--fs-caption-2); color: var(--text-muted); margin-top:var(--sp-1); }
.notif-empty { padding:var(--sp-8) var(--sp-4); text-align: center; color: var(--text-muted); font-size:var(--fs-footnote); }
.notif-panel-foot {
    display: flex; align-items: center; justify-content: space-between;
    padding:var(--sp-2) var(--sp-4); border-top: 1px solid var(--border-color);
}
.notif-foot-link {
    background: none; border: none; cursor: pointer;
    font-size:var(--fs-caption-1); color: var(--text-secondary); padding:var(--sp-1) var(--sp-2); border-radius:var(--radius-xs);
    transition: var(--transition);
}
.notif-foot-link:hover { background: var(--bg-hover); color: var(--text-primary); }
.notif-foot-link.danger:hover { background: var(--danger-soft); color: var(--danger-ink); }
@media (max-width: 480px) {
    .notif-panel { width: 94vw; right: -6px; }
}

/* ===================== CHAT: allega unico + invio long-press ============== */
.chat-attach-wrap { position: relative; display: flex; }
/* 3.44.12: il menu era largo almeno 190px col mouse e 210 sul telefono. Due misure per lo stesso
   menu, con le stesse due voci: resta la piu' capiente, dichiarata una volta sola. */
.chat-attach-menu {
    position: absolute; bottom: calc(100% + 8px); left: 0;
    min-width: 210px; padding:var(--sp-2);
    /* 3.44.20: era --bg-card, cioe' bianco al 60%. Quel token e' fatto per le SCHEDE, che
       stanno appoggiate sullo sfondo della pagina: qui sotto passano invece i messaggi, con
       testo e fotografie, e le due voci del menu si leggevano attraverso. Il pannello delle
       notifiche - l'altro riquadro che galleggia sopra il contenuto - sta al 90%: questo si
       allinea a quello. La sfocatura resta, ma smette di essere l'unica cosa che rende
       leggibile il menu. */
    background: rgb(255 255 255 / 92%);
    /* 3.44.12: blur(18px) saturate(150%) era una seconda ricetta di sfocatura, scritta a mano,
       per un pannello che e' vetro esattamente come l'avviso dei messaggi e il pannello delle
       azioni: quelli usano --glass-blur, questo no. Ora la ricetta e' una sola. */
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border: 1px solid var(--border-color); border-radius:var(--radius-sm);
    box-shadow: var(--shadow); z-index: var(--z-popover);
    display: none; flex-direction: column; gap:var(--sp-05);
}
.chat-attach-menu.open { display: flex; }
.chat-attach-menu button {
    display: flex; align-items: center; gap:var(--sp-2);
    background: none; border: none; text-align: left;
    padding:var(--sp-3) var(--sp-3); border-radius:var(--radius-xs);
    font-size:var(--fs-footnote); line-height: var(--lh-footnote); color: var(--text-primary); cursor: pointer; width: 100%;
    transition: var(--transition);
}
.chat-attach-menu button:hover { background: var(--bg-hover); }
.chat-attach-menu button i { color: var(--accent); font-size:var(--fs-callout); }

/* Invio: tap = normale, tieni premuto 3s = urgente (riempimento progressivo) */
.chat-send-btn { position: relative; overflow: hidden; -webkit-touch-callout: none; user-select: none; -webkit-user-select: none; touch-action: manipulation; }
.chat-send-btn > i { position: relative; z-index: 1; transition: color var(--dur-fast) var(--ease); }
.chat-send-btn::before {
    content: ''; position: absolute; inset: 0;
    background: var(--danger); opacity: 0.9;
    transform: scaleY(0); transform-origin: bottom;
    pointer-events: none; z-index: 0;
}
.chat-send-btn.charging::before { animation: chatSendCharge 3s linear forwards; }
.chat-send-btn.charging > i { color: #fff; }
.chat-send-btn.urgent-fired { border-color: var(--danger); }
/* 3.44.10: qui si resta sul --danger pieno, NON su --danger-ink: il pulsante di invio ha
   riempimento --accent scuro, quindi l'icona sta su fondo scuro e scurirla peggiorerebbe. */
.chat-send-btn.urgent-fired > i { color: var(--danger); }
@keyframes chatSendCharge { from { transform: scaleY(0); } to { transform: scaleY(1); } }

/* ===================== CHAT MOBILE: scroll e ingombri ===================== */
@media (max-width: 768px) {
    /* In chat: nessun padding/scroll del contenitore esterno (scrolla solo l'elenco messaggi) */
    .content-body:has(.chat-app) { padding:0; overflow: hidden; }
    /* 3.44.12: qui si sottraeva la sola --header-height mentre .main-content riserva in alto
       header + env(safe-area-inset-top). Nell'app installata su un telefono con la tacca la chat
       usciva sotto lo schermo esattamente di quell'incavo (47px) e non c'era modo di scorrere.
       Vedi la nota estesa sulla regola base di .chat-app. */
    .chat-app {
        height: calc(100dvh - var(--header-height) - env(safe-area-inset-top));
        max-height: calc(100dvh - var(--header-height) - env(safe-area-inset-top));
        border: none; border-radius:0; grid-template-rows: minmax(0, 1fr);
    }
    .chat-messages { overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }
    /* 3.44.12: tolto il secondo min-width del menu allegati: e' ora unico nella regola base. */
    .chat-attach-menu button { padding:var(--sp-3) var(--sp-4); font-size:var(--fs-subhead); line-height: var(--lh-subhead); }
    .chat-attach-menu button i { font-size:var(--fs-title-3); line-height: var(--lh-title-3); }
}

/* ===== Centro notifiche su mobile: pannello a tutta larghezza, mai tagliato ===== */
@media (max-width: 768px) {
    .notif-panel {
        position: fixed;
        /* 3.44.15: mancava l'incavo. L'intestazione e' alta --header-height PIU' l'area sicura, ma
           il pannello partiva da --header-height + 8px e sta piu' in alto nella pila: nell'app
           installata su iPhone copriva la campanella che lo aveva aperto e il titolo della
           schermata. Ora parte sotto l'intestazione vera. */
        top: calc(var(--header-height) + env(safe-area-inset-top) + 8px);
        left: 10px;
        right: 10px;
        width: auto;
        max-width: none;
    }
    .notif-list { max-height: calc(100dvh - var(--header-height) - env(safe-area-inset-top) - 150px); }
}

/* ===================== IMPOSTAZIONI: colonna unica + card richiudibili ===== */
.settings-layout { flex-direction: column; align-items: stretch; }
.settings-profile-col, .settings-main-col { flex: 1 1 auto !important; max-width: 100% !important; width: 100%; min-width: 0; }

.settings-layout .card.set-collapse > .card-header {
    cursor: pointer; user-select: none; -webkit-user-select: none;
    position: relative; padding-right:var(--sp-12);
}
.settings-layout .card.set-collapse > .card-body { display: none; }
.settings-layout .card.set-collapse.open > .card-body { display: block; }
/* 3.43.55: niente flash all'apertura — le card con intestazione partono chiuse già al primo
   paint (pure CSS, prima che il JS aggiunga set-collapse al DOMContentLoaded). Sui browser
   senza :has() resta il comportamento precedente. */
.settings-layout .card:has(> .card-header):not(.open) > .card-body { display: none; }
.set-chevron {
    position: absolute; right: 18px; top: 50%;
    transform: translateY(-50%) rotate(0deg);
    transition: transform var(--dur-base) var(--ease);
    color: var(--text-muted); font-size:var(--fs-subhead); pointer-events: none;
}
.settings-layout .card.set-collapse.open > .card-header .set-chevron { transform: translateY(-50%) rotate(180deg); }

/* ===================== SESSIONI: due colonne, disconnetti sotto l'icona ===== */
.session-icon-col { display: flex; flex-direction: column; align-items: center; gap:var(--sp-2); flex: 0 0 auto; }
.session-revoke { color: var(--danger-ink) !important; flex: 0 0 auto; padding:var(--sp-1) var(--sp-2); min-width: 0; }
.session-revoke i { margin:0; font-size:var(--fs-callout); }
.session-revoke:hover { background: var(--danger-soft) !important; }

/* ===== Admin: dispositivi con notifiche push attive (Impostazioni) ===== */
.pushadm-user {
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    padding:var(--sp-3) var(--sp-3);
    margin-bottom:var(--sp-3);
    background: var(--bg-card);
}
.pushadm-user:last-child { margin-bottom:0; }
.pushadm-uhead {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap:var(--sp-3);
    margin-bottom:var(--sp-1);
}
.pushadm-uname {
    font-size:var(--fs-footnote);
    font-weight: var(--fw-semibold);
    color: var(--text-primary);
    display: flex;
    align-items: center;
    gap:var(--sp-2);
    flex-wrap: wrap;
    min-width: 0;
}
.pushadm-revall {
    flex: 0 0 auto;
    font-size:var(--fs-caption-1);
    color: var(--danger-ink) !important;
    white-space: nowrap;
}
.pushadm-revall:hover { background: var(--danger-soft) !important; }
.pushadm-dev {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap:var(--sp-3);
    padding:var(--sp-1) 0;
    border-top: 1px dashed var(--border-color);
}
.pushadm-devinfo {
    font-size:var(--fs-caption-1);
    color: var(--text-secondary);
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}
.pushadm-devinfo i { margin-right:var(--sp-1); color: var(--accent); }
.pushadm-rev {
    flex: 0 0 auto;
    padding:var(--sp-1) var(--sp-2);
    color: var(--danger-ink) !important;
}
.pushadm-rev:hover { background: var(--danger-soft) !important; }

/* ============================================================================
   SKELETON LOADER "LIQUID GLASS" — mostrato durante il caricamento delle pagine
   (sostituisce lo spinner). Le sagome riproducono il layout con shimmer.
   Guidato da js/app-core.js -> showPageSkeleton(). Rispetta prefers-reduced-motion.
   ============================================================================ */
.sk-wrap { --fade-dist: 4px; animation: mfFadeUp var(--dur-base) var(--ease-out) both; }

.sk-row { display: flex; gap:var(--sp-4); flex-wrap: wrap; margin-bottom:var(--sp-4); }
.sk-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap:var(--sp-4); }
.sk-cols { display: flex; gap:var(--sp-4); overflow: hidden; align-items: flex-start; }
.sk-col { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap:var(--sp-3); }
.sk-list { display: flex; flex-direction: column; gap:var(--sp-3); }

/* Contenitore vetro traslucido, come le card dell'app */
.sk-card {
    position: relative;
    background: var(--glass-bg);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm), var(--glass-highlight);
    padding:var(--sp-5);
    overflow: hidden;
}
.sk-tile { flex: 1 1 150px; }
.sk-card.sk-mini { padding:var(--sp-4); border-radius: var(--radius-sm); }
.sk-head { display: flex; align-items: center; gap:var(--sp-3); margin-bottom:var(--sp-4); }
.sk-head-txt { flex: 1; display: flex; flex-direction: column; gap:var(--sp-2); }
.sk-stack { display: flex; flex-direction: column; gap:var(--sp-3); }
.sk-foot { display: flex; gap:var(--sp-3); margin-top:var(--sp-4); }

/* La singola sagoma: gradiente che "scorre" (shimmer) — robusto su qualsiasi tema */
.sk-shape {
    background: linear-gradient(100deg, var(--sk-base) 30%, var(--sk-hi) 48%, var(--sk-base) 66%);
    background-size: 220% 100%;
    border-radius: var(--radius-xs);
    animation: skShimmer 1.4s ease-in-out infinite;
}
@keyframes skShimmer { from { background-position: 180% 0; } to { background-position: -60% 0; } }

.sk-circle { border-radius:50%; flex: none; }
.sk-line { height: 12px; }
.sk-line.lg { height: 16px; }
.sk-line.sm { height: 10px; }
.sk-w40 { width: 40%; } .sk-w55 { width: 55%; } .sk-w70 { width: 70%; }
.sk-w85 { width: 85%; } .sk-w100 { width: 100%; }
.sk-block { height: 96px; }
.sk-block.tall { height: 150px; }
.sk-block.short { height: 60px; }
.sk-btn { height: 34px; width: 96px; border-radius: var(--radius-sm); }

@media (max-width: 768px) {
    .sk-grid { grid-template-columns: 1fr 1fr; }
    .sk-cols { flex-wrap: nowrap; }
}
@media (max-width: 480px) {
    .sk-grid { grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: reduce) {
    /* 3.44.7: le due righe "animation: none" sono ora nella regola generale in fondo al
       foglio. Qui resta solo il riposizionamento del riflesso, che altrimenti si
       fermerebbe fuori dalla sagoma lasciandola di un colore piatto e sbagliato. */
    .sk-shape { background-position: 50% 0; }
}

/* Skeleton 3.43.42: stat-grid (dashboard) e tabella (clienti/scadenzario/team/agenti),
   per far combaciare le sagome con la struttura reale della pagina. */
.sk-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap:var(--sp-4); margin-bottom:var(--sp-4); }
.sk-card.sk-table { padding:0; overflow: hidden; }
.sk-tr { display: grid; grid-template-columns: 2fr 1.3fr 1.3fr 1fr 0.8fr; gap:var(--sp-4); align-items: center; padding:var(--sp-4) var(--sp-5); border-bottom: 1px solid var(--border-color); }
.sk-tr:last-child { border-bottom: none; }
.sk-thead { background: var(--bg-tertiary); }
@media (max-width: 768px) {
    .sk-tr { grid-template-columns: 1.6fr 1fr 0.9fr; }
    .sk-tr .sk-shape:nth-child(n+4) { display: none; }
}

/* ============================================================================
   iOS / Safari — ottimizzazioni globali (3.43.43)
   Safe-area (notch/home indicator), niente flash/hover appiccicoso al tocco,
   niente zoom-al-focus sugli input, momentum scroll. env() vale 0 dove non serve,
   quindi queste regole sono innocue su desktop/Android. Poste a fine file per
   vincere per ordine di sorgente dove serve sovrascrivere.
   ============================================================================ */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
/* 3.43.49: pinch-to-zoom disattivato nella PWA. pan-x pan-y consente lo scorrimento normale ma
   esclude i gesti di zoom (pizzico e doppio tap) sul documento. Insieme al viewport
   maximum-scale=1/user-scalable=no e al blocco JS di gesturestart (per Safari iOS, che nel
   browser ignora user-scalable) copre tutte le piattaforme. Gli scroller interni non cambiano. */
html, body { touch-action: pan-x pan-y; }
* { -webkit-tap-highlight-color: transparent; }                 /* niente flash grigio al tocco */
body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; overscroll-behavior-y: none; }

/* Niente lente/menu contestuale al long-press sui controlli (il testo resta selezionabile) */
.btn, .nav-item, .mobile-toggle, .btn-icon, .btn-ghost, [onclick], summary,
.cal-filter-btn, .cal-view-btn, .theme-toggle, .modal-close, .pg-btn,
/* 3.44.12: tolto .chat-urgent-btn anche da questo elenco: l'elemento non esiste. */
.chat-attach-btn, .chat-send-btn, .ss-option, .ext-chip {
    -webkit-touch-callout: none;
}
/* Rimuove il ritardo di 300ms e il doppio-tap-zoom sugli elementi interattivi */
.btn, a, .nav-item, .mobile-toggle, .btn-icon, .btn-ghost, [onclick], summary,
.cal-filter-btn, .cal-view-btn, .theme-toggle, .modal-close, .pg-btn,
.chat-contact, .gsr-item, .notif-item, .ss-option, .cloud-item, .vault-card,
.ext-chip, .color-option, .kanban-card, .project-card, .client-card, .stat-card {
    touch-action: manipulation;
}
/* Scroll con inerzia (iOS ≤12) + niente rimbalzo che propaga alla pagina intera.
   NB: .content-body è ESCLUSO di proposito. Non è uno scroller reale: la pagina
   scorre a livello di documento (body/html) e .content-body cresce col contenuto
   (scrollHeight == clientHeight). Su iOS, dargli -webkit-overflow-scrolling:touch
   + overscroll-behavior:contain lo faceva diventare un layer di scroll che
   "catturava" il gesto senza avere nulla da scorrere e, con contain, non passava
   lo scroll al documento: la pagina non scorreva (evidente nello scadenzario, la
   pagina più lunga). Il rimbalzo pull-to-refresh è già bloccato da
   body{overscroll-behavior-y:none}. Qui restano solo gli scroller REALI. */
.modal, .notif-list, .chat-contacts, .chat-messages, .sidebar-nav,
.global-search-results, .searchable-select-dropdown, .sessions-body, .changelog-body,
.cloud-picker-tree, .clients-grid {
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
}

/* Safe-area sui bordi schermo (home indicator; notch laterale in landscape) */
/* 3.44.15: il commento diceva "notch laterale in landscape", ma le due righe coprivano solo il
   bordo INFERIORE. Due conseguenze visibili nell'app installata: aprendo il menu a scomparsa il
   logo e il nome dell'applicazione, che stanno nei primi 58px del pannello, finivano sotto la
   barra di stato — con un incavo da 47px ne restava coperto oltre il 70%; e girando il telefono in
   orizzontale la prima colonna del pannello e del contenuto finiva sotto la tacca laterale.
   L'intestazione l'incavo lo somma da sempre (vedi .content-header): qui era una dimenticanza.
   Il manifest dichiara orientamento libero, quindi il caso orizzontale e' reale. */
.sidebar {
    padding-top: env(safe-area-inset-top);
    padding-bottom: env(safe-area-inset-bottom);
    padding-left: env(safe-area-inset-left);
}
.content-body { padding-bottom: max(28px, env(safe-area-inset-bottom)); }
/* 3.44.15: la tacca laterale si fa sentire solo col telefono in orizzontale, ed e' l'unico caso in
   cui vale la pena toccare i margini laterali del contenuto: qui il pannello e' a scomparsa, quindi
   il contenuto arriva fino al bordo dello schermo. Si usano gli stessi valori dei rispettivi
   scaglioni (--sp-4 sotto i 768px, --sp-3 sotto i 480px), cosi' in verticale non cambia niente. */
@media (max-width: 768px) and (orientation: landscape) {
    .content-body {
        padding-left: max(var(--sp-4), env(safe-area-inset-left));
        padding-right: max(var(--sp-4), env(safe-area-inset-right));
    }
}
@media (max-width: 480px) and (orientation: landscape) {
    .content-body {
        padding-left: max(var(--sp-3), env(safe-area-inset-left));
        padding-right: max(var(--sp-3), env(safe-area-inset-right));
    }
}

/* iOS fa "restare" :hover dopo il tocco (hover appiccicoso). Su dispositivi senza vero
   hover annulla gli stati che si "incollano"; :active resta per il feedback al tocco.

   3.44.7 — RIALLINEAMENTO. I due elenchi erano cresciuti separatamente e non
   corrispondevano piu' a quello che gli elementi fanno davvero al passaggio del mouse:
   c'erano voci che annullavano uno spostamento inesistente e altre che si spostavano o
   accendevano lo sfondo senza che nessuno lo spegnesse. Ora i gruppi sono tre, uno per
   ogni tipo di stato che resta acceso, e ogni elemento compare nei gruppi che gli
   servono davvero. Gli elementi con uno stato proprio (selezionato, attivo, da leggere)
   sono esclusi: il tocco non deve cancellare l'evidenziazione che indica quello stato. */
@media (hover: none) {
    /* 1. Spostamenti: tutto cio' che si alza o si ingrandisce sotto il puntatore. */
    .btn-primary:hover,
    .stat-card:hover, .kanban-card:not(.dragging):hover, .project-card:hover,
    .team-card:hover, .client-card:hover, .color-option:hover, .dash-project:hover,
    .ext-chip[role="button"]:hover, .cloud-item:hover {
        transform: none;
    }
    /* 2. Bordo e ombra delle schede: tornano al bordo neutro e senza rilievo. */
    .kanban-card:hover, .project-card:hover, .client-card:not(.selected):hover,
    .cloud-item:not(.selected):not(.drag-over):hover, .vault-card:hover,
    .team-card:hover, .stat-card:hover, .btn-secondary:hover,
    .ext-chip[role="button"]:not(.active):hover {
        border-color: var(--border-color);
        box-shadow: none;
    }
    /* 3. Sfondo di righe ed elenchi: non deve restare acceso dopo il tocco. I pulsanti
          non sono qui dentro: il loro sfondo e' un colore proprio, non va ereditato dal
          contenitore. */
    .gsr-item:hover, .chat-contact:not(.active):hover, .notif-item:not(.unread):hover,
    .client-card:not(.selected):hover, .cloud-item:not(.selected):not(.drag-over):hover,
    .clients-list tbody tr:hover, .data-table tr:hover td {
        background: inherit;
    }
    /* 4. A riposo queste due hanno il bordo trasparente, non quello neutro. */
    .dash-project:hover { background: inherit; border-color: transparent; }
    .att-preview:hover { background: none; }
    .nav-item:not(.active):hover { background: transparent; }
}

/* ============================================================================
   3.44.10 — ADATTAMENTI PER LO SCHERMO TATTILE.
   Tre difetti diversi con la stessa causa: il foglio dava per scontato un mouse.
   Su un dispositivo senza puntatore fine il "passaggio sopra" non esiste e i
   bersagli piccoli non si centrano col dito. Si interviene solo qui dentro: su
   desktop tutto resta compatto come prima.
   ========================================================================== */
@media (hover: none) and (pointer: coarse) {

    /* 1. BERSAGLI DA 44px (linea guida Apple, WCAG 2.5.5 Target Size).
       Nel foglio convivevano sette misure diverse di pulsante-icona, da 26x26 a
       38x38: nessuna raggiungeva i 44px sotto i quali il dito sbaglia mira. Si
       impone una misura minima senza toccare le classi, quindi il disegno resta
       quello di prima e cambia solo l'area sensibile.
       ESCLUSA .vault-pw-btn: sta in posizione assoluta DENTRO al campo password,
       larga 30px, e le due copie sono ancorate a right:32px e right:2px. A 44px
       si sovrapporrebbero fra loro e finirebbero sopra al testo della password
       (il campo riserva 70px di spazio a destra, non 88px). Allargarla richiede
       di rifare il campo, non di cambiare un minimo. */
    .cloud-act, .scad-act, .pg-btn, .modal-close, .mobile-toggle,
    .chat-send-btn, .chat-attach-btn, .btn-icon, .activity-del, .att-del {
        min-width: var(--tap-min); min-height: var(--tap-min);
    }

    /* 3.44.11-b: seconda tornata. Erano rimasti fuori i pulsanti piccoli, le
       intestazioni di colonna ordinabili e le caselle di spunta — tutti tocchi
       frequenti e tutti sotto misura (i .btn-sm a 36px, le caselle a 15px). */
    .btn-sm, .vault-mini, .cal-view-btn, .cal-filter-btn, .cloud-crumb, .vault-url {
        min-height: var(--tap-min);
    }
    /* Sulle celle di tabella min-height non ha effetto: le specifiche lasciano il comportamento
       indefinito e i browser lo ignorano. Serve height, che sulle celle vale come minimo. */
    th.sortable, th.th-sort { height: var(--tap-min); }
    .btn-sm, .vault-mini, .cal-view-btn, .cal-filter-btn { min-width: var(--tap-min); }
    /* Le caselle di spunta restano piccole a vedersi ma guadagnano area sensibile
       tramite il riempimento, cosi' la riga non si allarga. */
    input[type="checkbox"].bulk-cb, input[type="checkbox"].cloud-sel-cb,
    .bulk-select-all-list, td.col-cb input[type="checkbox"] {
        width: 24px; height: 24px;
    }
    /* 3.44.15: la casella della spunta di un compito era rimasta fuori da questo elenco, e a
       differenza delle caselle di condivisione il testo accanto non e' un'etichetta: per spuntare
       una voce bisognava centrare un quadratino di 16px e non c'era nessun bersaglio di riserva.
       Ora la casella e' 24px come le altre e la riga che la contiene si alza a 44, cosi' il
       bersaglio utile diventa la riga intera senza cambiare il disegno. */
    .checklist-item input[type="checkbox"] { width: 24px; height: 24px; }
    .checklist-item { min-height: var(--tap-min); }
    /* 3.44.15: "Segna tutte come lette" e' l'unico comando per svuotare il pannello delle
       notifiche ed era alto una ventina di pixel — 12px di scritta e 2px di riempimento per lato.
       Le pastiglie del colore (avatar e progetti) erano 32px con 8px di spazio fra l'una e l'altra:
       due caselle vicine piu' piccole del dito. Entrambe passano al minimo tattile. */
    .notif-markall { min-height: var(--tap-min); padding: var(--sp-1) var(--sp-2); }
    .color-option { width: var(--tap-min); height: var(--tap-min); }
    /* 3.44.12: la stessa cura per il pannello di amministrazione della chat, rimasto fuori.
       La casella di spunta di ogni messaggio passa a 24px come le altre e la riga che la contiene
       si alza a 44: il bersaglio utile e' la riga, non il quadratino. Il cestino della singola
       bolla misurava 13x15px — il comando piu' distruttivo della schermata era anche il piu'
       piccolo. Qui prende un'area da 44x44 senza cambiare l'icona (il riempimento e' scritto nel
       markup con uno style in linea, che si batte solo cosi'). */
    .admin-msg-cb { width: 24px; height: 24px; }
    .admin-msg-row { min-height: var(--tap-min); }
    .admin-msg-row .chat-bubble button {
        min-width: var(--tap-min); min-height: var(--tap-min);
        padding:var(--sp-2) !important;
        display: inline-flex; align-items: center; justify-content: center;
    }
    /* .clients-list .col-cb fissa width/min-width/max-width a 36px con specificita' maggiore:
       va ripetuto qui, e il tetto va tolto, altrimenti la cella resta a 36px. */
    .clients-list .col-cb, td.col-cb, th.col-cb {
        width: var(--tap-min); min-width: var(--tap-min); max-width: none;
    }
    /* Stessa cosa per la colonna delle azioni: e' fissata a 70px, misura giusta per due
       pulsanti da 29px ma non per due da 44px, che sbordavano di 20px sulla colonna accanto.
       La tabella scorre gia' in orizzontale, quindi allargarla non toglie niente al resto. */
    .clients-list .col-act, td.col-act, th.col-act {
        width: 110px; min-width: 110px; max-width: none;
    }

    /* Su tablet (sopra i 768px la vista a elenco non si applica) le schede del Cloud
       restano a griglia, ma devono essere abbastanza larghe da contenere la fila dei
       quattro pulsanti da 44px: 188px piu' il riempimento della scheda. Con il minimo
       di 150px la fila usciva dalla scheda e si sovrapponeva a quella accanto, come
       accadeva su telefono prima della vista a elenco. */
    .cloud-grid { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }

    /* 2. La casella di selezione del cloud era opacity:0 e si accendeva solo al
       passaggio del mouse: su telefono non si poteva ne' vedere ne' toccare, e
       siccome Sposta e Copia pretendono una selezione, quelle due funzioni erano
       semplicemente irraggiungibili. Qui e' sempre visibile e passa da 17px a
       24px, che al dito e' la differenza fra centrarla e mancarla. */
    .cloud-sel-cb { opacity: 1; width: 24px; height: 24px; }

    /* 3. Stesso difetto per le azioni di scheda ed elenco dei clienti: nascoste
       dietro a un :hover che su tocco non arriva mai. */
    .client-card-actions,
    .clients-list .cl-actions { opacity: 1; }
}


/* iOS ingrandisce (zoom) al focus quando un campo ha font-size < 16px. Forziamo ≥16px su
   TUTTI i controlli sotto 768px: alcune select (filtri, avanzati, calendario, import,
   paginazione…) avevano 12-13px e sfuggivano al fix precedente per specificità. */
@media (max-width: 768px) {
    input, select, textarea,
    .form-control, .form-select, .search-box input, .vault-search input, .chat-input,
    .filter-bar .form-select, .filter-bar .searchable-select .form-control,
    .adv-filters-grid .form-select, .adv-filters-grid .form-control,
    .cal-user-filter .form-select, .import-mapping-table select,
    .pg-size, .dash-sort, .session-search input, .fc-project-selector .form-select,
    #globalSearch {
        font-size:var(--fs-callout) !important; /* 3.43.48: !important per vincere sugli style="font-size:var(--fs-caption-1)" inline (sync DB, link esterno) che facevano ancora zoomare iOS */
    }
    /* 3.43.48: su mobile la sidebar è off-canvas → il pannello risultati torna a tutta larghezza */
    .global-search-results { left: 0; }
}

/* 3.43.48 (a11y): indicatore di focus da tastiera visibile su tutti gli elementi interattivi.
   Prima solo i campi form avevano un box-shadow al focus; pulsanti, voci di menu, link e icone
   non mostravano nulla (navigazione da tastiera non tracciabile, WCAG 2.4.7). :focus-visible
   NON compare al clic del mouse, quindi non introduce aloni indesiderati al tocco/click. */
.btn:focus-visible, .nav-item:focus-visible, a:focus-visible, .modal-close:focus-visible,
.btn-icon:focus-visible, .btn-ghost:focus-visible, .cloud-act:focus-visible, .scad-act:focus-visible,
[onclick]:focus-visible, summary:focus-visible, .theme-toggle:focus-visible, .mobile-toggle:focus-visible,
/* 3.44.14: le intestazioni richiudibili delle Impostazioni da oggi si raggiungono col Tab (vedi
   app-core.js), ma non rientravano in nessuna delle voci qui sopra — il loro comando e' agganciato
   dal codice, non scritto nell'attributo onclick — e restavano l'unico punto in cui il fuoco della
   tastiera diventava invisibile: si premeva Tab e non si capiva piu' dove si era finiti. */
.settings-layout .card.set-collapse > .card-header:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: var(--radius-xs);
}
/* 3.44.14: i due riquadri di caricamento grandi (allegati di progetto e importazione clienti) hanno
   gli angoli piu' arrotondati del resto. La regola qui sopra impone a tutti gli elementi con un
   comando attaccato lo stesso raggio piccolo: da quando questi riquadri si raggiungono col Tab,
   arrivandoci sopra gli angoli si squadravano di colpo — un salto visibile, per giunta proprio
   sull'elemento che si sta cercando di mettere a fuoco. Qui si ridanno i loro. */
.upload-zone:focus-visible, .import-file-zone:focus-visible { border-radius: var(--radius-sm); }
/* 3.44.15: lo stesso salto lo facevano altri cinque componenti, e la deroga di sopra era stata
   scritta solo per i due riquadri di caricamento. Arrivando col Tab su una voce del menu laterale
   o su un riquadro dei numeri della Bacheca gli angoli si squadravano di colpo, proprio
   sull'elemento che si sta cercando di individuare. Invece di allungare l'elenco a ogni versione,
   ognuno riprende il TOKEN che usa nella propria regola: --radius-sm (12px) per le voci di menu,
   i cartellini della Lavagna, l'icona di progetto e i progetti in Bacheca, --radius (16px) per i
   riquadri dei numeri. Le pastiglie del colore e le schede cliente non compaiono qui perche'
   hanno gia' il raggio piccolo e non saltano. */
.nav-item:focus-visible, .kanban-card:focus-visible,
.project-icon:focus-visible, .dash-project:focus-visible { border-radius: var(--radius-sm); }
.stat-card:focus-visible { border-radius: var(--radius); }

/* ===========================================================================
   3.44.7 — FINESTRA DI DIALOGO UNICA DELL'APP (mfConfirm / mfPrompt)
   Sostituisce i dialoghi nativi del browser: stessa grafica delle altre finestre,
   variante rossa per le azioni distruttive, animazione condivisa con i modali.
   =========================================================================== */
.app-dialog { max-width: 460px; border-top: 4px solid var(--accent); }
.app-dialog.is-danger { border-top-color: var(--danger); }
.app-dialog .modal-header h3 { display: flex; align-items: center; gap:var(--sp-2); }
.app-dialog .modal-header h3 i { font-size:var(--fs-body); color: var(--accent); }
.app-dialog.is-danger .modal-header h3,
.app-dialog.is-danger .modal-header h3 i { color: var(--danger-ink); }
.app-dialog-msg {
    font-size:var(--fs-subhead); line-height: 1.6; margin:0 0 var(--sp-5);
    color: var(--text-primary); word-break: break-word;
}
.app-dialog.is-danger .app-dialog-msg strong { color: var(--danger-ink); }
.app-dialog-field { margin:0 0 var(--sp-5); }
.app-dialog-actions {
    display: flex; justify-content: flex-end; gap:var(--sp-3); flex-wrap: wrap;
}
/* Su schermi stretti i due pulsanti vanno a tutta larghezza, azione primaria in alto */
@media (max-width: 480px) {
    .app-dialog-actions { flex-direction: column-reverse; }
    .app-dialog-actions .btn { width: 100%; justify-content: center; }
}

/* ============================================================================
   3.44.8 — MESSAGGI IN STILE iOS
   La sezione Messaggi e' stata riportata alle convenzioni che chi usa un iPhone
   conosce a memoria: bolle con la codina, messaggi vicini raggruppati per chi
   parla, barra di scrittura translucida appoggiata sopra la tastiera, pulsante
   di invio tondo che si accende solo quando c'e' qualcosa da mandare.
   Le tre superfici (sfondo della conversazione, bolla in entrata, bolla in
   uscita) sono dichiarate una volta sola qui sotto: le codine leggono gli stessi
   valori, quindi cambiando un colore non si scollano piu' dalle bolle.
   ============================================================================ */
.chat-messages {
    /* Stesso identico colore di prima (bg-tertiary sopra bg-primary), ma pieno:
       la codina di ogni bolla viene ritagliata con un tassello di questo colore
       e con uno sfondo semitrasparente si sarebbe vista la bolla in trasparenza. */
    --chat-canvas: #dce2ed;
    --chat-in-bg: #ffffff;
    background: var(--chat-canvas);
    padding:var(--sp-4) var(--sp-4) var(--sp-5);
    gap:var(--sp-05);
    scroll-behavior: smooth;
    overflow-anchor: none; /* il salto al fondo lo decidiamo noi, non il browser */
    /* Il tassello che ritaglia la codina sporge oltre il bordo della bolla e
       creava 9px di scorrimento orizzontale (la conversazione si spostava di
       lato al tocco). Va nascosto: e' dello stesso colore dello sfondo, quindi
       tagliarlo non cambia nulla di quello che si vede. */
    overflow-x: hidden;
}

/* Distanza fra un discorso e l'altro: i messaggi dello stesso mittente restano
   attaccati (2px dal gap), chi cambia turno stacca. */
.chat-bubble { margin-top:var(--sp-3); }
.chat-bubble.grouped { margin-top:0; }

/* 3.44.12: le conversazioni corte restavano appese in alto, con mezzo schermo vuoto sotto
   (misurati ~450px di tela vuota per due sole bolle). Con margin-top:auto sul primo elemento il
   gruppo si appoggia sopra la barra di scrittura, come in tutte le applicazioni di messaggistica.
   NON si usa justify-content:flex-end, che ottiene lo stesso effetto ma in Chrome e Safari rende
   IRRAGGIUNGIBILE la parte alta quando la conversazione e' piu' lunga della finestra: il margine
   automatico invece si annulla da solo appena il contenuto trabocca. La regola sta dopo quelle
   del margine superiore delle bolle apposta, per vincere anche su .chat-bubble.grouped. */
.chat-messages > :first-child { margin-top: auto; }

/* 3.44.12: il testo dei messaggi era 15px col mouse e 16 sul telefono, mentre il corpo del testo
   dell'applicazione e' 17px (--fs-body) e tutto il resto della riga — nome, orario, anteprima —
   misura 17. Il contenuto principale della sezione era il testo piu' piccolo dell'applicazione, e
   per giunta RIMPICCIOLIVA al crescere dello schermo. Ora e' Body con la sua interlinea, alla
   stessa misura a qualunque larghezza; l'aggiunta di 16px nel blocco per telefono e' stata tolta. */
.chat-bubble {
    --chat-out-bg: var(--accent);
    max-width: 76%;
    padding:var(--sp-2) var(--sp-3) var(--sp-2);
    border-radius:var(--radius-lg);
    font-size:var(--fs-body);
    line-height: var(--lh-body);
    gap:0;
    box-shadow: var(--shadow-xs);
    animation: chatBubbleIn var(--dur-base) var(--ease-out) both;
}
/* Il contenuto sta sopra la codina: le pseudo-caselle posizionate coprirebbero
   il testo, che invece scorre nel flusso normale. */
.chat-bubble > * { position: relative; z-index: 2; }

.chat-bubble.sent {
    background: var(--chat-out-bg);
    border-bottom-right-radius: 19px;
}
.chat-bubble.received {
    background: var(--chat-in-bg);
    border: none;
    border-bottom-left-radius: 19px;
}
.chat-bubble.failed { --chat-out-bg: var(--danger); background: var(--chat-out-bg); }

@keyframes chatBubbleIn {
    from { opacity: 0; transform: translateY(6px) scale(0.97); }
    to   { opacity: 1; transform: none; }
}

/* ---- Codine: solo sull'ultimo messaggio di ogni gruppo, come su iOS -------
   .tail viene messa da renderChatMessages() sull'ultima bolla di ogni turno.
   Ogni codina e' fatta di due tasselli: il primo disegna la curva del colore
   della bolla, il secondo la rifila con il colore dello sfondo. */
.chat-bubble.tail::before,
.chat-bubble.tail::after {
    content: '';
    position: absolute;
    bottom: 0;
    height: 19px;
    width: 19px;
    pointer-events: none;
}
.chat-bubble.sent.tail::before {
    right: -7px;
    background: var(--chat-out-bg);
    border-bottom-left-radius: 16px 14px;
}
.chat-bubble.sent.tail::after {
    right: -19px;
    background: var(--chat-canvas, transparent);
    border-bottom-left-radius: 10px;
}
.chat-bubble.received.tail::before {
    left: -7px;
    background: var(--chat-in-bg);
    border-bottom-right-radius: 16px 14px;
}
.chat-bubble.received.tail::after {
    left: -19px;
    background: var(--chat-canvas, transparent);
    border-bottom-right-radius: 10px;
}

/* ---- Ora, ricevute e azioni ----------------------------------------------
   L'ora stava su una riga tutta sua sotto al testo: ogni messaggio, anche un
   "Ok", occupava due righe e la conversazione risultava lunga il doppio del
   necessario. Nelle applicazioni di messaggistica l'ora sta in coda al testo,
   sulla stessa riga, e va a capo da sola solo quando non ci sta.

   Si ottiene senza toccare il codice che costruisce i messaggi: la bolla smette
   di essere una colonna (era display:flex) e torna un normale blocco di testo,
   il testo diventa in linea e il gruppo ora+ricevuta si accosta a destra. Messo
   in coda al testo, "accostato a destra" vuol dire: in fondo all'ultima riga se
   c'e' spazio, altrimenti su una riga nuova comunque a destra. */
.chat-bubble {
    /* flow-root al posto di block: serve perche' la bolla contenga in altezza
       l'ora accostata, altrimenti sborderebbe sotto lo sfondo colorato. */
    display: flow-root;
}
.chat-bubble-text { display: inline; }
.chat-bubble-meta {
    float: right;
    margin:var(--sp-1) 0 0 var(--sp-3);   /* 5px sopra = l'ora si appoggia in basso, allineata
                               alla base della riga di testo invece che in cima */
    gap:var(--sp-1);
    min-height: 13px;
}
/* Con l'allegato l'ora resta sotto, staccata: appiccicarla al bordo di
   un'anteprima la renderebbe illeggibile. */
.chat-bubble.has-attach .chat-bubble-meta { float: none; margin:var(--sp-1) 0 0; }
/* 3.44.10: 10,5px arrotondato a 11px (mezzi pixel di corpo non sono resi in modo
   prevedibile). Soprattutto: sulla bolla inviata l'ora univa opacity 0,55 a un bianco gia'
   all'80%, per un'opacita' effettiva di 0,44 sul fondo --accent, cioe' 3,14:1. La bolla
   ricevuta era gia' stata sistemata con opacity:1, questa era rimasta indietro. Ora
   l'opacita' e' esplicita e il contrasto sale a ~6,9:1. */
.chat-bubble-time { font-size:var(--fs-caption-2); line-height: var(--lh-caption-2); opacity: 0.55; }
.chat-bubble.sent .chat-bubble-time { color: rgba(255, 255, 255, 0.85); opacity: 1; }
.chat-bubble.received .chat-bubble-time { color: var(--text-muted); opacity: 1; }

/* I pulsanti che comparivano al passaggio del mouse erano irraggiungibili su
   schermo tattile: senza mouse non esiste il "passaggio sopra" e cancellare un
   messaggio dal telefono era impossibile. Ora su desktop restano dove erano, su
   telefono si tiene premuta la bolla e si apre l'elenco delle azioni. */
.chat-bubble-del {
    font-size:var(--fs-caption-1);
    padding:var(--sp-05);
    border-radius:50%;
    line-height: 1;
    transition: opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.chat-bubble-del:active { transform: scale(0.88); }
@media (hover: none) {
    .chat-bubble-del { display: none; }
    .chat-bubble { -webkit-touch-callout: none; }
}
.chat-bubble.pressed { transform: scale(0.965); transition: transform var(--dur-fast) var(--ease); }

/* ---- Separatore di giornata ---------------------------------------------- */
/* 3.44.12: qui si riscrivevano corpo e colore riportandoli a 11px e --text-muted, cioe' proprio
   la coppia che rendeva il separatore quasi invisibile (5,23:1). Le due dichiarazioni sono state
   tolte: valgono quelle della regola base, 13px in --text-secondary. Il bianco al 55% e'
   esattamente --glass-bg. */
.chat-date-sep { margin:var(--sp-4) 0 var(--sp-2); }
.chat-date-sep span {
    background: var(--glass-bg);
    border: none;
    letter-spacing: 0.2px;
    padding:var(--sp-1) var(--sp-3);
}

/* ---- "Sta scrivendo": bolla vera, non piu' una riga di testo -------------- */
/* 3.44.12: i 16px ai lati venivano da quando questa riga stava FUORI dall'elenco messaggi ed era
   lei a doversi allineare al margine. Ora il codice la disegna DENTRO #chatMessages, che quei
   16px li mette gia' di suo: i due riempimenti si sommavano e i pallini partivano da 32px mentre
   le bolle in entrata partono da 16, cioe' l'indicatore risultava rientrato di mezzo centimetro
   rispetto ai messaggi della stessa persona. Resta il solo riempimento verticale. */
.chat-typing {
    padding:var(--sp-05) 0 var(--sp-3);
    background: var(--chat-canvas, transparent);
    gap:var(--sp-3);
}
.chat-typing-dots {
    background: var(--chat-in-bg, #fff);
    padding:var(--sp-2) var(--sp-3);
    border-radius:var(--radius-lg);
    border-bottom-left-radius: 6px;
    gap:var(--sp-1);
    box-shadow: var(--shadow-xs);
}
/* 3.44.12: tolta la riga dei pallini. Ridava 7px (contro i 6 della regola base) e un grigio
   #9aa2b5 inesistente altrove: misura e colore sono ora dichiarati una volta sola nella regola
   base, con --fs-dot e --text-muted. */
.chat-typing-label { font-size:var(--fs-caption-2); line-height: var(--lh-caption-2); }

/* ---- Barra di scrittura --------------------------------------------------- */
/* 3.44.12: il bianco al 72% era una gradazione di vetro in piu' rispetto alle due del sistema
   (--glass-bg 0,55 e --glass-bg-strong 0,74) e il filo superiore un rgba(20,28,50,0.08) che e'
   --border-color con due centesimi di differenza. Tutti e due presi dai token. */
.chat-input-area {
    padding:var(--sp-2) var(--sp-3);
    gap:var(--sp-2);
    align-items: flex-end;
    background: var(--glass-bg-strong);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border-top: 1px solid var(--border-color);
}
/* 3.44.9 — LA SCRITTA NEL CAMPO NON ERA IN MEZZO.
   L'altezza del campo era il risultato di tre regole scritte in momenti diversi
   che si contraddicevano: una imponeva una riga alta 40px (scritta quando il
   campo era alto 40px e non aveva imbottitura), un'altra 9px di imbottitura
   sopra e sotto, una terza un'interlinea di 1,35. Vinceva la prima perche' piu'
   specifica: la riga di testo era alta 40px dentro uno spazio di 20, quindi la
   scritta "Messaggio" si appoggiava sotto al centro invece di stare in mezzo.

   Ora l'altezza e' una somma esatta e non un caso: 20 di riga + 9 e 9 di
   imbottitura + 1 e 1 di bordo = 40, la stessa misura dei due pulsanti tondi
   accanto. Il testo e' centrato per costruzione, non per correzione.
   Il selettore ripete .form-control apposta: serve a battere la vecchia regola,
   che senza questo continuerebbe a vincere. */
textarea.chat-input,
textarea.form-control.chat-input,
.chat-input {
    border-radius:var(--radius-lg);
    padding:var(--sp-2) var(--sp-4);
    height: auto;
    min-height: var(--control-h);
    line-height: 20px;
    background: #fff;
    /* 3.44.12: rgba(20,28,50,0.12) e' --border-color a due centesimi di distanza. */
    border: 1px solid var(--border-color);
    transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
/* 3.44.15: il campo del messaggio aveva come unico segno di messa a fuoco l'alone, che fa 1,26:1:
   da tastiera non si capiva se si stava scrivendo nel campo o no. Si aggiunge il bordo --accent,
   lo stesso degli altri campi. */
textarea.chat-input:focus,
.chat-input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-glow); }

/* Invio tondo: pieno quando c'e' testo, spento quando non c'e' niente da
   mandare. Il pulsante resta cliccabile (l'avviso "scrivi qualcosa" e' piu'
   chiaro di un pulsante morto che non reagisce). */
/* 3.44.12 — UNA MISURA SOLA PER I DUE PULSANTI DELLA BARRA.
   Erano dichiarati quattro volte, con quattro numeri diversi (42, 44, 38, 40), sparsi in quattro
   punti del foglio: a schermo uscivano 38x38 col mouse e 40x44 sul telefono, cioe' sempre sotto i
   44pt che le linee guida Apple e il criterio WCAG 2.5.5 indicano come minimo. Le altre tre
   scritture sono state tolte; questa e' l'unica, e prende il token del bersaglio tattile.
   A 390px il campo di testo resta largo 266px (390 - 20 di imbottitura - 16 di spazi - 88 dei due
   pulsanti), quindi non si stringe nulla: lo spazio arriva dai vuoti, non dal campo. */
.chat-send-btn,
.chat-attach-btn {
    width: var(--tap-min);
    height: var(--tap-min);
    min-width: var(--tap-min);
    flex-shrink: 0;
}

.chat-send-btn {
    padding:0;
    border-radius:50%;
    background: var(--accent);
    border: 1px solid var(--accent);
    color: #fff;
    display: inline-flex; align-items: center; justify-content: center;
    transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease),
                opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.chat-send-btn > i { font-size:var(--fs-body); line-height: 1; }
.chat-send-btn:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: #fff; }
.chat-send-btn:active { transform: scale(0.9); }
/* 3.44.12: a campo vuoto il pulsante era bianco su #c2c9d6, cioe' 1,66:1 — sotto il 3:1 che il
   criterio WCAG 1.4.11 chiede ai comandi: la freccia semplicemente non si vedeva e il pulsante
   sembrava disattivato, mentre invece SI PUO' premere (il messaggio "scrivi qualcosa" e' piu'
   chiaro di un pulsante morto). I due grigi #c2c9d6 e #b4bcca, per giunta, non comparivano da
   nessun'altra parte nel foglio. Ora e' la coppia di token gia' usata per i controlli spenti:
   fondo --bg-tertiary, filo --border-light, freccia --text-muted (che sale a ~5:1), e al
   passaggio del mouse --bg-hover. Lo stato "spento" resta distinguibile da quello acceso perche'
   cambia tutto: fondo chiaro contro fondo --accent scuro. */
.chat-send-btn.is-empty {
    background: var(--bg-tertiary);
    border-color: var(--border-light);
    color: var(--text-muted);
}
.chat-send-btn.is-empty:hover {
    background: var(--bg-hover);
    border-color: var(--border-light);
    color: var(--text-primary);
}
.chat-send-btn.charging > i { color: #fff; }

.chat-attach-btn {
    padding:0;
    border-radius:50%;
    display: inline-flex; align-items: center; justify-content: center;
    transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease),
                border-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.chat-attach-btn > i { font-size:var(--fs-body); line-height: 1; }
.chat-attach-btn:active { transform: scale(0.9); }

/* 3.44.12: terzo bianco al 72% del blocco — e' --glass-bg-strong. */
.chat-pending-attach { background: var(--glass-bg-strong); }

/* ---- Intestazione della conversazione ------------------------------------- */
/* 3.44.12: quarto bianco al 72% — stesso token. */
.chat-header {
    background: var(--glass-bg-strong);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
}

/* ---- Elenco conversazioni -------------------------------------------------- */
.chat-contact { transition: background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease); }
.chat-contact:active { transform: scale(0.985); }
/* 3.44.12: tolta la seconda misura del contatore (era 18px nella regola base e 20 qui): resta
   quella base, portata a 20. Il corpo era gia' identico nelle due regole. */

/* 3.44.12: nella riga SELEZIONATA anteprima e orario scendevano a 4,02:1 — sotto il 4,5:1 che il
   testo piccolo richiede (le righe non selezionate stanno a 4,99:1). Il colpevole e' il fondo:
   --accent-soft steso sopra la lista schiarisce il contrasto proprio dove l'utente sta leggendo.
   Si alzano di un gradino i due soli testi interessati, e solo nella riga attiva: altrove
   --text-muted resta al suo posto. */
.chat-contact.active .chat-contact-preview,
.chat-contact.active .chat-contact-time { color: var(--text-secondary); }

/* ============================================================================
   3.44.8 — ELENCO AZIONI DEL MESSAGGIO (tocco prolungato)
   Su telefono le azioni sulla bolla non erano raggiungibili. Questo pannello
   sale dal basso come i menu di sistema di iOS: gruppo di voci arrotondato,
   "Annulla" staccato sotto, area sicura del bordo inferiore rispettata.
   ============================================================================ */
.chat-sheet {
    position: fixed; inset: 0; z-index: var(--z-urgent);
    display: flex; flex-direction: column; justify-content: flex-end;
    opacity: 0; pointer-events: none;
    transition: opacity var(--dur-base) var(--ease);
}
.chat-sheet.visible { opacity: 1; pointer-events: auto; }
.chat-sheet-backdrop {
    position: absolute; inset: 0;
    background: rgba(12, 17, 30, 0.32);
    -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
}
.chat-sheet-panel {
    position: relative;
    padding: 0 8px calc(8px + env(safe-area-inset-bottom));
    display: flex; flex-direction: column; gap:var(--sp-2);
    transform: translateY(14px);
    transition: transform var(--dur-base) var(--ease-out);
}
.chat-sheet.visible .chat-sheet-panel { transform: none; }
/* 3.44.12: il bianco all'86% era una QUARTA gradazione di vetro, dopo 0,55 - 0,72 - 0,74: il
   pannello delle azioni era percio' l'unica superficie translucida piu' opaca di tutte le altre,
   senza motivo. Portata su --glass-bg-strong come le altre. */
.chat-sheet-group {
    background: var(--glass-bg-strong);
    -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
    border-radius:var(--radius);
    overflow: hidden;
    box-shadow: var(--shadow);
}
/* 3.44.12: rgba(20,28,50,0.10) e' esattamente --border-color, scritto a mano. */
.chat-sheet-title {
    padding:var(--sp-3) var(--sp-4);
    font-size:var(--fs-caption-1); line-height: var(--lh-caption-1); color: var(--text-muted); text-align: center;
    border-bottom: 1px solid var(--border-color);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chat-sheet-btn {
    display: flex; align-items: center; justify-content: space-between; gap:var(--sp-3);
    width: 100%; min-height: 54px; padding:0 var(--sp-5);
    background: none; border: none; cursor: pointer;
    font: inherit; font-size:var(--fs-callout); line-height: var(--lh-callout); color: var(--text-primary); text-align: left;
    transition: background var(--dur-fast) var(--ease);
}
/* 3.44.12: stesso rgba(20,28,50,0.10) = --border-color. */
.chat-sheet-btn + .chat-sheet-btn { border-top: 1px solid var(--border-color); }
.chat-sheet-btn:hover { background: rgba(20, 28, 50, 0.05); }
.chat-sheet-btn:active { background: rgba(20, 28, 50, 0.10); }
.chat-sheet-btn i { font-size:var(--fs-title-3); line-height: var(--lh-title-3); opacity: 0.8; }
.chat-sheet-btn.danger { color: var(--danger-ink); }
.chat-sheet-btn.cancel { font-weight: var(--fw-bold); justify-content: center; color: var(--accent); }

/* ============================================================================
   3.44.8 — MESSAGGI SU TELEFONO (e iPad in verticale)
   ============================================================================ */
@media (max-width: 768px) {
    /* 14px ai lati, non 10: la codina dell'ultimo messaggio sporge di 7px oltre
       il bordo della bolla, quindi con 10px sfiorava il bordo dello schermo e
       sembrava tagliata. */
    .chat-messages { padding:var(--sp-3) var(--sp-4) var(--sp-4); }
    /* 3.44.12: tolto font-size:var(--fs-callout). Il testo dei messaggi e' Body ovunque: era
       l'unica riga per cui la stessa frase rimpiccioliva passando dal telefono al desktop.
       Resta la larghezza massima, che su schermo stretto ha senso allargare. */
    .chat-bubble { max-width: 82%; }
    /* min-height fissa l'altezza dell'intestazione: senza, il pulsante "indietro"
       (posizionato in assoluto) non avrebbe un riferimento stabile su cui
       centrarsi. 66px e' l'altezza naturale: avatar 40px + 10px sopra e sotto,
       piu' i due righi di testo. */
    .chat-header { padding:var(--sp-3) var(--sp-4) var(--sp-3) var(--sp-16); min-height: 66px; }
    /* Nome e stato su una riga sola: un nome lungo faceva crescere
       l'intestazione e scendere il pulsante. */
    .chat-header-name, .chat-header-status {
        white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    /* 44x44 e' la misura minima di un bersaglio toccabile secondo le linee guida
       Apple. top: 11px = (66 - 44) / 2, cioe' centrato nell'intestazione.
       NB: il riferimento e' .chat-conversation (l'unico antenato posizionato),
       non l'intestazione: con top: 50% il pulsante finiva a mezza altezza della
       conversazione, sospeso in mezzo ai messaggi. */
    .chat-back-btn { width: 44px; height: 44px; top: 11px; }
    .chat-input-area { padding: 8px 10px calc(8px + env(safe-area-inset-bottom)); }
    /* 3.44.9 — LA FASCIA VUOTA SOTTO LA BARRA DI SCRITTURA.
       Sugli iPhone senza tasto centrale il sistema chiede 34px in fondo allo
       schermo per la sua barra: li' sotto non si puo' mettere niente. Ma quando
       la tastiera e' aperta quei 34px stanno *dietro* la tastiera, e riservarli
       lo stesso lasciava una striscia vuota fra il campo di scrittura e la
       tastiera. Con la tastiera su lo spazio non serve e si toglie: la barra di
       scrittura appoggia sulla tastiera, come nelle applicazioni di sistema.
       La classe la mette e la toglie _chatViewportFit(). */
    body.kb-open .chat-input-area { padding-bottom:var(--sp-2); }
    /* 3.44.12: tolte le ultime due misure fuori posto. I 40x44 dei pulsanti (quarta e ultima
       delle dichiarazioni contraddittorie, e quella che vinceva sul telefono) sono ora 44x44
       dichiarati una volta sola; il terzo tetto di crescita del campo (116px, dopo 120 e 110)
       e' sparito e resta quello unico della regola base. */
    /* La conversazione scorre dietro l'elenco quando si torna indietro. */
    .chat-app.peer-selected .chat-main { animation: chatSlideIn var(--dur-base) var(--ease-out) both; }
}
@keyframes chatSlideIn {
    from { opacity: 0.4; transform: translateX(16px); }
    to   { opacity: 1; transform: none; }
}

/* Gesto "indietro": la conversazione segue il dito partendo dal bordo sinistro. */
.chat-conversation.swiping { transition: none; }
.chat-conversation.swipe-reset { transition: transform var(--dur-base) var(--ease-out); }

/* ============================================================================
   3.44.8 — RISPOSTA AL TOCCO, STATI SPENTI E RITMO DELLE COMPARSE
   ============================================================================
   Tre difetti che si sentivano in tutta l'applicazione, non solo in una pagina.

   1) NIENTE RISPONDEVA AL DITO. Su telefono e tablet il passaggio del mouse non
      esiste, quindi tutti gli effetti :hover non si vedono mai: toccando un
      pulsante, una scheda o una riga di elenco non succedeva assolutamente
      nulla finche' la schermata non cambiava. Nel dubbio si tocca di nuovo, e
      cosi' nascono i doppi invii. Ora ogni cosa toccabile si abbassa appena
      sotto il dito: e' lo stesso gesto delle applicazioni di sistema.

   2) I PULSANTI SPENTI SEMBRAVANO ACCESI. .btn non aveva alcuno stato per
      "disabilitato": un pulsante bloccato restava a colori pieni, col cursore a
      manina, e al clic non faceva niente. Dove lo stato c'era era scritto a mano
      con quattro trasparenze diverse (.4, .45, .45, .7). Ora la regola e' una
      sola e vale per tutti.

   3) LA SCHERMATA DI ACCESSO ENTRAVA AL RALLENTATORE. .fade-in durava 1,2
      secondi con 0,3 di attesa prima di partire: un secondo e mezzo. Dentro le
      pagine non si notava, perche' dal 3.43.40 la regola .content-body .fade-in
      la spegne del tutto (il contenuto deve sostituire le sagome di caricamento
      all'istante, senza dissolvenza sopra); restava pero' accesa sulla card di
      accesso, cioe' proprio sulla prima cosa che si vede aprendo l'applicazione.
      Ora dura 300ms come le altre due comparse che condividono lo stesso
      fotogramma (#encScanReport e .sk-wrap).
   ============================================================================ */

/* --- 1. Pressione ---------------------------------------------------------- */

/* La transizione sta sull'elemento a riposo, non dentro :active: cosi' anche il
   ritorno su e' morbido. Durata corta: la risposta al tocco deve essere
   immediata, non un'animazione da guardare. */
.btn:not(:disabled):active,
.btn-icon:not(:disabled):active,
.btn-ghost:not(:disabled):active,
.modal-close:active,
.theme-toggle:active,
.mobile-toggle:active,
.cloud-act:active,
.scad-act:active,
.color-option:active {
    transform: scale(0.96);
    transition-duration: var(--dur-fast);
}

/* Il primario si abbassa invece di rimpicciolirsi: ha un rilievo proprio
   (ombra + bordo chiaro) e schiacciarlo verso il piano e' piu' coerente. */
.btn-primary:not(:disabled):active {
    transform: translateY(1px) scale(0.985);
    box-shadow: none;
}

/* Schede e riquadri: si "premono" appena. Un valore piu' contenuto dei pulsanti
   perche' sono grandi, e su una superficie larga anche pochi punti di scala si
   notano molto. */
.stat-card:active, .project-card:active, .client-card:active, .team-card:active,
.vault-card:active, .kanban-card:not(.dragging):active, .dash-project:active,
.cloud-item:active, .ext-chip[role="button"]:active {
    transform: scale(0.99);
    transition-duration: var(--dur-fast);
}

/* Righe di elenco: non cambiano forma (si vedrebbe il testo "ballare"), si
   accendono di sfondo per un istante. */
.nav-item:active, .notif-item:active, .gsr-item:active,
.data-table tbody tr:active, .clients-list tbody tr:active,
.searchable-select-dropdown div:active, .chat-attach-menu button:active {
    background: var(--accent-soft);
    transition-duration: var(--dur-fast);
}

/* Su touch il ritorno elastico dopo il rilascio non serve: il dito se n'e' gia'
   andato e l'animazione arriva in ritardo rispetto al gesto. */
@media (hover: none) {
    .btn, .btn-icon, .stat-card, .project-card, .client-card, .team-card,
    .vault-card, .kanban-card, .dash-project, .cloud-item, .nav-item {
        -webkit-tap-highlight-color: transparent;
    }
}

/* --- 2. Stato spento ------------------------------------------------------- */

/* Una sola definizione di "non si puo' toccare": mezzo tono, cursore normale,
   nessuna reazione al passaggio o alla pressione. :disabled copre i pulsanti
   veri; [aria-disabled] copre quelli finti (div e link usati come pulsanti). */
.btn:disabled, .btn[disabled], .btn[aria-disabled="true"],
.btn-icon:disabled, .cloud-up-btn:disabled, .pg-btn:disabled,
.cal-toolbar #calTodayBtn:disabled, .urgent-read-btn:disabled,
input:disabled, select:disabled, textarea:disabled, .form-control:disabled {
    opacity: 0.45;
    cursor: not-allowed;
    box-shadow: none;
}
.btn:disabled:hover, .btn[disabled]:hover, .btn[aria-disabled="true"]:hover,
.btn:disabled:active, .btn[disabled]:active, .btn[aria-disabled="true"]:active {
    transform: none;
    filter: none;
}

/* Un pulsante che sta lavorando NON e' un pulsante spento, anche se il codice
   usa lo stesso interruttore per bloccarlo. _btnBusy() mette disabled e salva il
   contenuto originale in data-old-html: e' quell'attributo a distinguere i due
   casi. Sbiadire al 45% un pulsante che sta salvando renderebbe illeggibile
   proprio la scritta che serve ("Salvataggio…") e la rotella che gira. Resta
   quindi quasi a piena intensita', col cursore di attesa. */
.btn[data-old-html] {
    opacity: 0.8;
    cursor: progress;
}

/* --- 3. Ritmo delle comparse ----------------------------------------------- */

/* 300ms come le altre due comparse che condividono mfFadeUp (#encScanReport,
   .sk-wrap): stessa curva, stessa famiglia, tempi coerenti. L'attesa iniziale
   scende da 300ms a 40ms — quel tanto che basta perche' il movimento parta dopo
   che il testo e' stato disegnato, non un ritardo percepibile. */
.fade-in {
    --fade-dist: 12px;
    animation: mfFadeUp var(--dur-slow) var(--ease-out) 40ms both;
}

/* --- 4. Rifiniture mancanti ------------------------------------------------ */

/* .btn-success aveva il colore ma nessuna reazione al passaggio, unico fra le
   quattro varianti colorate. Ora si comporta come .btn-danger.
   3.44.15: la simmetria con .btn-danger valeva per il gesto, non per il contrasto. Su --success la
   scritta bianca fa 3,30:1 e non si legge; su --danger fa 4,83:1 e passa. Al passaggio del mouse
   la parola sul pulsante verde si smorzava — solo su desktop, perche' sul telefono la regola qui
   sotto lo riporta ai colori tenui. Si usa l'inchiostro verde come fondo: 5,61:1. */
.btn-success:hover { background: var(--success-ink); color: #fff; }

/* .btn-ghost e .btn-secondary non si spostavano mai: il gruppo di pulsanti in
   fondo alle finestre reagiva solo a meta' (Salva si alzava, Annulla no). */
.btn-secondary:hover { transform: translateY(-1px); }
.btn-ghost:hover { transform: translateY(-1px); }
@media (hover: none) {
    .btn-secondary:hover, .btn-ghost:hover, .btn-success:hover, .btn-danger:hover {
        transform: none;
    }
    .btn-success:hover { background: var(--success-soft); color: var(--success-ink); }
    .btn-danger:hover  { background: var(--danger-soft);  color: var(--danger-ink); }
}

/* Le icone dentro i pulsanti ereditavano la transizione del pulsante e a volte
   partivano leggermente dopo lo sfondo. */
.btn > i, .btn-icon > i { transition: none; }

/* ============================================================================
   3.44.7 — MOVIMENTO RIDOTTO (una regola sola per tutta l'applicazione).
   Chi ha attivato "riduci le animazioni" nelle impostazioni del proprio sistema
   chiede di non vedere cose che si muovono: capogiri e nausea da movimento sono
   il motivo per cui quell'interruttore esiste. Prima la richiesta veniva
   rispettata solo in tre punti sparsi (il codice di verifica, la comparsa delle
   pagine e le sagome di caricamento): tutto il resto - finestre, avvisi, barre di
   avanzamento, rotelline di attesa - continuava a muoversi lo stesso.
   Questa regola vale per ogni elemento: le durate non vengono azzerate del tutto
   ma ridotte a un istante, cosi' il codice che aspetta la fine di un'animazione
   riceve comunque il suo avviso e non resta bloccato a meta'. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 1ms !important;
        animation-delay: 0ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 1ms !important;
        transition-delay: 0ms !important;
        scroll-behavior: auto !important;
    }
}

/* ============================================================================
   3.44.11 — ETICHETTE: UN SOLO COMPONENTE
   Nel file convivevano dodici classi che disegnano la stessa cosa — una piccola
   etichetta colorata — ognuna con la sua geometria: sei raggi di arrotondamento
   diversi (4, 6, 8, 10, 999 px), sette combinazioni di spaziatura interna, tre
   pesi del carattere e display misti fra inline-block e inline-flex. Affiancate
   nella stessa riga non si allineavano.
   Qui la geometria e' una sola, a pillola come nelle HIG Apple; i colori restano
   nelle singole classi, che e' l'unica cosa che le distingue davvero.
   (Restano fuori .cloud-badge, .vault-badge e .notif-badge: sono contatori
   circolari, un componente diverso.)
   ============================================================================ */
.tag, .priority-badge, .scad-num-chip, .contract-badge, .task-has-comments,
.client-status-badge, .scad-tipo, .prot-cat-badge, .scad-badge, .enc-badge,
.changelog-v, .etag {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-1);
    padding: var(--sp-05) var(--sp-2);
    border-radius: var(--radius-full);
    font-size: var(--fs-caption-2);
    line-height: var(--lh-caption-2);
    font-weight: var(--fw-semibold);
    white-space: nowrap;
    vertical-align: middle;
}

/* ============================================================================
   3.44.11 — LARGHEZZE DELLE FINESTRE
   Erano dodici misure diverse (460, 480, 520, 560, 600, 640, 680, 700, 800,
   880, 900, 950) scritte a mano dentro l'attributo style di ogni finestra, quindi
   invisibili al foglio di stile e impossibili da adeguare in un punto solo.
   ============================================================================ */
.modal-sm { max-width: var(--modal-sm); }
.modal-md { max-width: var(--modal-md); }
.modal-lg { max-width: var(--modal-lg); }
.modal-xl { max-width: var(--modal-xl); }
/* Finestra alta: contenuto scorrevole con intestazione e piede fissi. */
.modal-tall { height: 85vh; display: flex; flex-direction: column; }
.modal-tall .modal-body { flex: 1 1 auto; overflow-y: auto; }

/* ============================================================================
   3.44.12 (secondo giro) — TESTO SOLO PER I LETTORI DI SCHERMO
   Serve quando l'informazione a schermo e' data dalla forma e non dalle parole:
   la pastiglia rossa accanto a "Messaggi" dice "3", e chi non vede lo schermo
   sentiva "Messaggi 3" senza sapere 3 di cosa. Accanto al numero si mette allora
   la frase per intero, tolta dal disegno ma non dall'albero di accessibilita'.
   Niente display:none e niente visibility:hidden: quelli tolgono l'elemento
   anche ai lettori di schermo, che e' l'opposto di quello che serve qui.
   ============================================================================ */
.sr-only {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* ============================================================================
   3.44.12-b — GESTO PROLUNGATO: UN SOLO MENU, NON DUE
   Tenendo premuta una scheda della bacheca salivano DUE menu sovrapposti: quello
   dell'applicazione ("Sposta in «Da Fare»"…) e, sopra, quello di sistema di iOS
   ("Copia | Cerca info | Traduci"). Segnalato con fotografia da iPhone 16 Pro Max.

   Perche' succedeva. Piu' su, in questo stesso foglio, c'e' gia' un elenco che
   assegna -webkit-touch-callout:none ai comandi, e la scheda ci rientra (ha un
   onclick in linea). Ma -webkit-touch-callout governa solo il foglietto dei
   COLLEGAMENTI e delle IMMAGINI ("Apri in una nuova scheda", "Aggiungi a Foto").
   "Copia | Cerca info | Traduci" e' un'altra cosa: e' il menu della SELEZIONE DI
   TESTO, e quello lo governa -webkit-user-select. L'applicazione non lo
   dichiarava per nessuno di questi elementi. Su Chrome non si vedeva perche' il
   foglio predefinito del browser contiene [draggable=true]{user-select:none} e
   la scheda e' trascinabile: la protezione veniva dal BROWSER, non da qui — e
   infatti spariva sulle schede di chi non ha il permesso di spostare i compiti,
   che l'attributo draggable non ce l'hanno.

   Perche' solo su schermo tattile (@media (hover:none)). E' la stessa condizione
   con cui il codice arma il gesto (_kbIsTouch in js/app-core.js): dove il tocco
   prolungato non apre nessun pannello non c'e' nessuna sovrapposizione da
   togliere, e col mouse la selezione del testo resta intatta.

   Cosa NON viene reso non selezionabile, di proposito: le password del Vault, il
   codice TOTP, gli indirizzi di posta, le note, i campi di testo e le bolle del
   pannello di amministrazione della chat (.admin-msg-row, che il tocco prolungato
   non usa). Li' il testo e' contenuto da copiare, non un comando.
   ============================================================================ */
@media (hover: none) {
    /* Scheda della bacheca: il tocco prolungato apre l'elenco delle colonne, il
       tocco singolo apre il dettaglio, il trascinamento la sposta. E' un comando:
       il titolo, le etichette e la scadenza che mostra sono tutti ripetuti — e
       selezionabili — dentro la scheda di dettaglio che il tocco apre.
       NON si restringe a [draggable=true]: e' proprio l'aver lasciato la
       protezione appesa a quell'attributo (via foglio predefinito del browser) ad
       aver prodotto il difetto. La scheda e' un comando per tutti, permesso di
       spostamento o meno. */
    .kanban-card {
        -webkit-touch-callout: none;
        -webkit-user-select: none;
        user-select: none;
    }
    /* Bolla della chat: il tocco prolungato apre le azioni sul messaggio, che
       includono "Copia testo" — il testo resta quindi copiabile, con un comando
       esplicito invece che con una selezione che oggi non si riesce comunque a
       usare (il pannello con il suo fondo scuro copre le maniglie di selezione).
       Solo dentro #chatMessages: nel pannello di amministrazione le stesse bolle
       non hanno nessun tocco prolungato e restano contenuto selezionabile. */
    #chatMessages .chat-bubble {
        -webkit-touch-callout: none;
        -webkit-user-select: none;
        user-select: none;
    }
    /* I collegamenti dentro la bolla tornano a essere collegamenti: il foglietto
       di sistema al tocco prolungato ("Copia link") li' e' giusto che ci sia, e il
       gesto sulla bolla gia' li esclude (vedi il controllo su 'a' in app-chat.js). */
    #chatMessages .chat-bubble a {
        -webkit-touch-callout: default;
        -webkit-user-select: auto;
        user-select: auto;
    }
}

/* ============================================================================
   3.44.12-b — DUE CORREZIONI DI touch-action (stessa famiglia: il gesto va a
   finire dove non deve). Aggiunte in coda, non modifiche alle regole di sopra.
   ============================================================================ */

/* 1. SCADENZARIO — lo scorrimento verticale della pagina era MORTO col dito
   appoggiato sulla tabella. La regola piu' su (.scad-table-wrap) dichiara
   touch-action:pan-x e il commento accanto dice che serve a far scorrere la
   PAGINA quando lo swipe verticale parte dalla tabella: e' l'intenzione giusta
   ma il valore fa l'opposto. pan-x significa "di questo tocco e' permesso solo
   il trascinamento orizzontale": il verticale non e' concesso ne' qui ne' a
   nessun antenato, quindi la pagina non si muove affatto.
   Misurato a 440x956 con 49 scadenze: la tabella e' alta 2893px su una pagina
   di 3603, cioe' l'80% della pagina e' zona morta. Swipe verticale che parte
   dalla tabella -> window.scrollY resta 0; lo stesso swipe che parte 40px piu'
   in alto -> la pagina scorre. Presente identico anche in 3.44.11: NON e' una
   regressione di questo giro.
   pan-x pan-y concede entrambe le direzioni: l'orizzontale trova questo
   contenitore (l'unico che scorre in orizzontale) e la tabella scorre come
   prima; il verticale non trova nulla da scorrere qui — overflow-y e' hidden —
   e sale alla pagina, che e' esattamente quello che il commento voleva.
   NB: pan-x pan-y esclude comunque il pizzico, quindi il divieto di zoom
   volutamente attivo su tre livelli resta intatto. */
.scad-table-wrap { touch-action: pan-x pan-y; }

/* 2. FONDO DEI PANNELLI A SCOMPARSA — col pannello aperto, trascinando sul
   fondo scurito la pagina DIETRO scorreva (misurato: window.scrollY da 0 a 114
   con il pannello della bacheca aperto e ben visibile). Sull'iPhone si somma
   l'inerzia e il rimbalzo, cosi' il contenuto sfila via sotto un pannello che
   dovrebbe averlo bloccato.
   Il fondo e' un rettangolo a tutto schermo che serve solo a raccogliere il
   tocco che chiude: non ha niente da scorrere, quindi touch-action:none e' il
   modo piu' diretto per fermare li' il gesto. Non si usa body{overflow:hidden}
   come fanno le finestre modali perche' su Safari iOS quel blocco e' notoriamente
   inaffidabile, mentre touch-action e' rispettato.
   touch-action non tocca la generazione del clic: il tocco che chiude il
   pannello continua a funzionare (verificato). Vale per il pannello dei messaggi
   della chat e per quello di spostamento della bacheca, che riusa la stessa
   classe. */
.chat-sheet-backdrop { touch-action: none; }
