/* ============================================================================
 * components.css — i componenti condivisi di Traffic Law
 * ============================================================================
 *
 * ── La regola che governa tutto questo file ─────────────────────────────────
 *
 *   OGNI selettore comincia con una classe .tl-.
 *
 * Non c'è una sola regola su un elemento nudo (body, table, input, a…), e non
 * c'è un solo `!important`. È la ragione per cui questo foglio può essere
 * caricato su tutte le 292 pagine esistenti senza cambiarne nemmeno una: se una
 * pagina non usa le classi .tl-, questo file per lei non esiste.
 *
 * La conseguenza pratica è che il rilascio è a rischio zero e non richiede di
 * verificare le pagine una per una. L'aspetto nuovo si vede su una pagina nel
 * momento in cui la si converte, non prima.
 *
 * ── Cosa c'è dentro ─────────────────────────────────────────────────────────
 *
 *   1  Struttura di pagina    .tl-shell .tl-sidebar .tl-main .tl-topbar
 *   2  Pulsanti               .tl-btn
 *   3  Campo di modulo        .tl-field
 *   4  Barra dei filtri       .tl-filters
 *   5  Tabella dati           .tl-table
 *   6  Pastiglia di stato     .tl-badge
 *   7  Azioni di riga         .tl-actions
 *   8  Messaggi               .tl-alert .tl-toast .tl-dialog
 *   9  Minuterie              .tl-card .tl-empty .tl-spinner …
 *
 * I sei componenti dal 3 all'8 sono quelli previsti dalla fase 2 della proposta
 * di rimodernizzazione: coprono, da soli, quasi tutto ciò che l'applicazione
 * disegna. Le sole coppie BoxRowLabel/BoxRowCaption che il .tl-field sostituisce
 * ricorrono 8.221 volte nel codice attuale.
 *
 * ── Scelte tecniche, e perché ───────────────────────────────────────────────
 *
 *   Niente @layer.  Sembrerebbe adatto, ma fa il contrario di quello che serve:
 *   il CSS dentro un layer perde SEMPRE contro il CSS fuori da ogni layer, a
 *   prescindere dalla specificità. Bootstrap qui non è in un layer, quindi il
 *   suo `table { }` batterebbe il nostro `.tl-table td { }`. Si resta sulla
 *   specificità normale, che è prevedibile e sufficiente.
 *
 *   Niente nesting CSS.  Selettori piatti: si leggono senza sapere una sintassi
 *   in più, e si possono cercare con grep. In un progetto che verrà toccato da
 *   più persone nei prossimi anni vale più della concisione.
 *
 *   Niente valori scritti a mano.  Ogni numero e ogni colore viene da
 *   tokens.css. Se qui compare un esadecimale, è un errore da correggere.
 * ========================================================================= */


/* ═══════════════════════════════════════════════════════════════════════════
 * 1 · STRUTTURA DI PAGINA
 * ═══════════════════════════════════════════════════════════════════════════
 * Il guscio: barra laterale ferma a sinistra, contenuto a destra.
 * Si attiva mettendo la classe .tl-shell sul <body> — cosa che fa menu_side.php.
 * Una pagina che non ce l'ha non è toccata da niente di quanto segue.
 *
 * ── Perché lo spazio per la barra è un padding sul <body> ────────────────────
 *
 * La scelta ovvia sarebbe un <main> che avvolge il contenuto. Qui non si può, e
 * la ragione è istruttiva: le pagine non chiudono i propri contenitori. È
 * footer.php a emettere </body>, e page/title_top.php contiene un </div> in più
 * di quanti ne apra. Un involucro aperto dal menu non avrebbe nessuno che lo
 * chiude, e il primo </div> spaiato lo chiuderebbe al posto sbagliato.
 *
 * Con la barra a posizione fissa e un padding sul <body> non si apre nessun tag:
 * il guscio funziona su tutte le 292 pagine così come sono, senza modificarne
 * nemmeno una e senza dipendere da come chiudono il proprio HTML.
 *
 * .tl-main resta disponibile per le pagine convertite, che il proprio markup lo
 * chiudono davvero.
 */

/* box-sizing dichiarato invece che dato per scontato.
 *
 * Sulle 292 pagine esistenti lo imposta già Bootstrap con `* { }`, quindi qui è
 * un doppione innocuo. Ma un componente deve funzionare anche dove Bootstrap non
 * c'è — la pagina demo, e domani una pagina convertita che non lo carica più.
 * Senza questa riga larghezza al 100% più padding fa sforare il contenitore, e
 * la pagina scorre in orizzontale. */
.tl-shell,
.tl-shell *,
.tl-shell *::before,
.tl-shell *::after { box-sizing: border-box; }

.tl-shell {
  margin: 0;
  min-height: 100vh;
  padding-inline-start: var(--tl-sidebar-width);
  background: var(--tl-surface-sunken);
  color: var(--tl-text);
  font-family: var(--tl-font-sans);
  font-size: var(--tl-text-base);
  line-height: var(--tl-leading-normal);
  -webkit-font-smoothing: antialiased;
  transition: padding var(--tl-duration) var(--tl-ease);
}

/* ── La barra laterale ──────────────────────────────────────────────────── */

.tl-sidebar {
  position: fixed;
  inset-block: 0;
  inset-inline-start: 0;
  z-index: var(--tl-z-sidebar);
  width: var(--tl-sidebar-width);
  display: flex;
  flex-direction: column;
  background: var(--tl-nav-bg);
  color: var(--tl-nav-text);
  border-inline-end: 1px solid var(--tl-nav-border);
  transition: width var(--tl-duration) var(--tl-ease),
              transform var(--tl-duration) var(--tl-ease);
  overscroll-behavior: contain;
}

/* Intestazione: stemma dell'ente, nome del comune, anno di esercizio.
 * Sono le tre informazioni che dicono su quali dati si sta lavorando, ed è
 * giusto che stiano sempre sotto agli occhi invece che in fondo a una barra. */
.tl-sidebar__head {
  display: flex;
  align-items: center;
  gap: var(--tl-space-3);
  padding: var(--tl-space-4) var(--tl-space-3);
  border-block-end: 1px solid var(--tl-nav-border);
  min-height: var(--tl-topbar-height);
}

.tl-sidebar__blazon {
  flex: 0 0 auto;
  width: 32px;
  height: 40px;
  object-fit: contain;
}

.tl-sidebar__ident {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.tl-sidebar__city {
  font-size: var(--tl-text-sm);
  font-weight: var(--tl-weight-semibold);
  color: var(--tl-nav-text-strong);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tl-sidebar__meta {
  font-size: var(--tl-text-xs);
  color: var(--tl-nav-text);
  opacity: 0.8;
  white-space: nowrap;
}

/* Il corpo scorre, l'intestazione e il piede restano fermi. */
.tl-sidebar__nav {
  flex: 1 1 auto;
  overflow-y: auto;
  overflow-x: hidden;
  padding: var(--tl-space-2) 0;
  scrollbar-width: thin;
  scrollbar-color: var(--tl-nav-scrollbar) transparent;
}

.tl-sidebar__foot {
  flex: 0 0 auto;
  padding: var(--tl-space-2);
  border-block-start: 1px solid var(--tl-nav-border);
  display: flex;
  align-items: center;
  gap: var(--tl-space-2);
}

/* ── Gruppi del menu (primo livello: TitleMenu) ──────────────────────────────
 * Sono <details>/<summary>: la fisarmonica funziona senza una riga di
 * JavaScript, è navigabile da tastiera e la sa leggere un lettore di schermo.
 * Il menu attuale ottiene lo stesso effetto con checkbox nascoste e label, che
 * per la tastiera e per lo screen reader non è la stessa cosa.                */

.tl-nav-group {
  border: 0;
}

.tl-nav-group__label {
  display: flex;
  align-items: center;
  gap: var(--tl-space-2);
  width: 100%;
  padding: var(--tl-space-2) var(--tl-space-3);
  min-height: var(--tl-target-comfort);
  font-size: var(--tl-text-sm);
  font-weight: var(--tl-weight-semibold);
  color: var(--tl-nav-text-strong);
  cursor: pointer;
  user-select: none;
  list-style: none;
  transition: background var(--tl-duration-fast) var(--tl-ease);
}

/* Toglie il triangolino predefinito: ne disegniamo uno nostro che ruota. */
.tl-nav-group__label::-webkit-details-marker { display: none; }
.tl-nav-group__label::marker { content: ""; }

.tl-nav-group__label:hover { background: var(--tl-nav-bg-hover); }

.tl-nav-group__label:focus-visible {
  outline: var(--tl-focus-ring);
  outline-offset: calc(var(--tl-focus-offset) * -1);
}

.tl-nav-group__icon {
  flex: 0 0 auto;
  width: 16px;
  text-align: center;
  opacity: 0.9;
}

.tl-nav-group__text {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tl-nav-group__chevron {
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  border-inline-end: 1.5px solid currentColor;
  border-block-end: 1.5px solid currentColor;
  transform: rotate(-45deg);
  opacity: 0.6;
  transition: transform var(--tl-duration) var(--tl-ease);
}

.tl-nav-group[open] > .tl-nav-group__label .tl-nav-group__chevron {
  transform: rotate(45deg);
}

/* ── Sottogruppi annidati ──────────────────────────────────────────────────
   Un contenitore del menu è un <details> dentro un <li> dell'elenco: si annida
   da solo, ma senza rientranza si presenta allineato al gruppo di primo
   livello e chi legge non capisce che sta un gradino più in basso.

   L'etichetta si allinea alle voci sorelle (`--tl-space-6`, la stessa di
   `.tl-nav-link`), e l'elenco dei figli rientra di un passo in più. La
   rientranza si accumula da sé a ogni livello, perché gli elenchi sono
   annidati l'uno dentro l'altro: vale a qualunque profondità senza cablarla. */

.tl-nav-group--annidato > .tl-nav-group__label {
  padding-inline-start: var(--tl-space-6);
}

.tl-nav-group--annidato .tl-nav-list {
  padding-block-end: 0;
  padding-inline-start: var(--tl-space-3);
}

/* ── Voci del menu (secondo livello: TitleSubMenu) ───────────────────────── */

.tl-nav-list {
  margin: 0;
  padding: 0 0 var(--tl-space-2) 0;
  list-style: none;
}

.tl-nav-link {
  display: flex;
  align-items: center;
  gap: var(--tl-space-2);
  padding: var(--tl-space-2) var(--tl-space-3) var(--tl-space-2) var(--tl-space-6);
  min-height: var(--tl-target-min);
  font-size: var(--tl-text-sm);
  color: var(--tl-nav-text);
  text-decoration: none;
  border-inline-start: 3px solid transparent;
  transition: background var(--tl-duration-fast) var(--tl-ease),
              color var(--tl-duration-fast) var(--tl-ease);
}

.tl-nav-link:hover {
  background: var(--tl-nav-bg-hover);
  color: var(--tl-nav-text-strong);
  text-decoration: none;
}

.tl-nav-link:focus-visible {
  outline: var(--tl-focus-ring);
  outline-offset: calc(var(--tl-focus-offset) * -1);
}

/* La pagina in cui ci si trova. Oggi non è segnalata in nessun modo: si capisce
 * dove si è solo leggendo il titolo. */
.tl-nav-link.is-current {
  background: var(--tl-nav-bg-active);
  color: var(--tl-nav-text-strong);
  font-weight: var(--tl-weight-medium);
  border-inline-start-color: var(--tl-white);
}

.tl-nav-link__text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── Barra ridotta a sole icone ──────────────────────────────────────────── */

.tl-shell.is-sidebar-collapsed { padding-inline-start: var(--tl-sidebar-width-collapsed); }
.tl-shell.is-sidebar-collapsed .tl-sidebar { width: var(--tl-sidebar-width-collapsed); }

.tl-shell.is-sidebar-collapsed .tl-sidebar__ident,
.tl-shell.is-sidebar-collapsed .tl-nav-group__text,
.tl-shell.is-sidebar-collapsed .tl-nav-group__chevron,
.tl-shell.is-sidebar-collapsed .tl-nav-list,
.tl-shell.is-sidebar-collapsed .tl-sidebar__label {
  display: none;
}

.tl-shell.is-sidebar-collapsed .tl-nav-group__label { justify-content: center; padding-inline: 0; }
.tl-shell.is-sidebar-collapsed .tl-sidebar__head    { justify-content: center; padding-inline: 0; }

/* ── Il contenuto ───────────────────────────────────────────────────────── */

/* Involucro del contenuto, per le pagine convertite. Non serve al guscio: lo
 * spazio per la barra lo fa già il padding su .tl-shell. */
.tl-main {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.tl-topbar {
  position: sticky;
  top: 0;
  z-index: var(--tl-z-sticky);
  display: flex;
  align-items: center;
  gap: var(--tl-space-3);
  min-height: var(--tl-topbar-height);
  padding: var(--tl-space-2) var(--tl-space-4);
  background: var(--tl-surface);
  border-block-end: 1px solid var(--tl-border);
}

.tl-topbar__title {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  font-size: var(--tl-text-lg);
  font-weight: var(--tl-weight-semibold);
  color: var(--tl-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* «Pag. n\tot» della procedura, in alto a destra (task C8).
 *
 * Il documento di specifica lo chiede su ogni pagina dedicata a una procedura,
 * e dice che «la situazione più comune sarà Pag. 1\1». Cioè: comparirà quasi
 * sempre, e quasi sempre dirà la stessa cosa. Da qui il tono — è un riferimento
 * che si consulta quando serve, non un'informazione che deve chiamare
 * l'attenzione ogni volta che si apre una pagina.
 *
 * Cifre tabellari perché il numero cambia sotto i piedi quando si passa da una
 * scheda all'altra: senza, «Pag. 1\11» e «Pag. 2\11» hanno larghezze diverse e
 * l'etichetta balla.
 *
 * ⚠ Serve anche alle pagine legacy, che non hanno la `.tl-topbar`: lì sta nella
 * riga del titolo con un `float:right` in linea (vedi `inc/page/title_top.php`).
 * Quindi qui niente `position` e niente margini che presuppongano il flex. */
.tl-pagina {
  flex: 0 0 auto;
  white-space: nowrap;
  font-size: var(--tl-text-sm);
  font-variant-numeric: tabular-nums;
  color: var(--tl-text-muted);
}

/* ── L'avanzamento delle elaborazioni lunghe ─────────────  .tl-avanzamento
 *
 * Il documento di specifica chiede, per importazioni, estrazioni, elaborazioni
 * e stampe lunghe, «prima il numero totale delle registrazioni e poi la
 * progressione» (task C7). Qui c'è solo il vestito: i due numeri li mette
 * `ui/tl.js`, che li chiede a `ajax/avanzamento.php`.
 *
 * Compare sotto al modulo che ha lanciato l'elaborazione, e solo dopo che è
 * stata lanciata: `hidden` finché non serve. Non è una finestra modale — chi ha
 * appena lanciato un'elaborazione da novanta secondi deve poter continuare a
 * guardare i parametri con cui l'ha lanciata.
 *
 * ⚠ La barra «indefinita» non è un ornamento: copre la PRIMA fase, quella in
 * cui si stanno contando le registrazioni e un totale ancora non c'è. Senza,
 * quei secondi mostrerebbero una barra ferma a zero, che si legge come «non sta
 * succedendo niente». */
.tl-avanzamento {
  margin-block-start: var(--tl-space-3);
  padding: var(--tl-space-3) var(--tl-space-4);
  background: var(--tl-surface);
  border: 1px solid var(--tl-border);
  border-radius: var(--tl-radius-lg);
}

.tl-avanzamento__testa {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--tl-space-3);
  margin-block-end: var(--tl-space-2);
}

.tl-avanzamento__fase {
  font-size: var(--tl-text-sm);
  font-weight: var(--tl-weight-medium);
  color: var(--tl-text);
}

.tl-avanzamento__conto {
  font-size: var(--tl-text-sm);
  font-variant-numeric: tabular-nums;
  color: var(--tl-text-muted);
}

.tl-avanzamento__pista {
  overflow: hidden;
  height: 8px;
  background: var(--tl-surface-muted);
  border-radius: var(--tl-radius-full);
}

.tl-avanzamento__barra {
  width: 0;
  height: 100%;
  background: var(--tl-accent);
  border-radius: var(--tl-radius-full);
  transition: width 150ms linear;
}

/* La fase del conteggio: non si sa ancora quanto manchi, e lo si dice. */
.tl-avanzamento__barra--indefinita {
  width: 35%;
  animation: tl-avanzamento-scorre 1.2s ease-in-out infinite;
}

@keyframes tl-avanzamento-scorre {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(300%); }
}

/* Chi ha chiesto di non vedere animazioni non deve vedere nemmeno questa: la
 * barra resta ferma a un terzo, che è comunque diverso da «zero». */
@media (prefers-reduced-motion: reduce) {
  .tl-avanzamento__barra--indefinita { animation: none; }
}

.tl-avanzamento--errore { border-color: var(--tl-danger); }
.tl-avanzamento--errore .tl-avanzamento__fase { color: var(--tl-danger-text); }
.tl-avanzamento--errore .tl-avanzamento__barra { background: var(--tl-danger); }

/* ── La barra dei comandi, in fondo e sempre in vista ─────────────────────
 *
 * Il gemello in basso di `.tl-topbar`: stesso meccanismo, stesso `z-index`,
 * bordo dalla parte opposta. Serve alle schede lunghe — quella del verbale ha
 * tredici sezioni — dove i comandi in coda si raggiungono solo scorrendo fino
 * in fondo, e chi arriva dall'elenco non sa nemmeno che ci sono.
 *
 * `sticky` e non `fixed`, che pure darebbe lo stesso effetto a schermo, per tre
 * ragioni concrete in questo impianto:
 *
 *   · `fixed` si àncora alla finestra, non al contenuto: andrebbe scostato a
 *     mano di `--tl-sidebar-width` e ri-scostato quando la barra laterale si
 *     richiude (`.tl-shell.is-sidebar-collapsed`), cioè due regole in più che
 *     rincorrono un valore che il flusso normale conosce già.
 *   · `.tl-content` è `flex: 1 1 auto` dentro un `.tl-main` alto `100vh`:
 *     il blocco contenitore arriva comunque in fondo allo schermo, quindi anche
 *     a pagina corta la barra si appoggia al bordo inferiore. L'effetto voluto
 *     c'è, senza uscire dal flusso.
 *   · un elemento `fixed` copre l'ultima riga del contenuto e va compensato con
 *     un `padding-block-end` sul contenitore, che è la classica misura che poi
 *     nessuno aggiorna quando la barra cambia altezza.
 *
 * I margini negativi in linea annullano il `padding` di `.tl-content`
 * (`--tl-space-4`): la barra arriva ai bordi della colonna come fa la fascia in
 * alto, invece di galleggiare rientrata. */
.tl-barra-comandi {
  position: sticky;
  inset-block-end: 0;
  z-index: var(--tl-z-sticky);
  display: flex;
  align-items: center;
  gap: var(--tl-space-3);
  flex-wrap: wrap;
  margin-block-start: var(--tl-space-5);
  margin-inline: calc(var(--tl-space-4) * -1);
  margin-block-end: calc(var(--tl-space-4) * -1);
  padding: var(--tl-space-3) var(--tl-space-4);
  background: var(--tl-surface);
  border-block-start: 1px solid var(--tl-border);
  box-shadow: var(--tl-shadow-barra-comandi);
}

/* Su schermo stretto lo spaziatore va tolto, non ridotto: con lui i comandi
   vanno a capo **tenendo il vuoto in mezzo**, e restano staccati come se
   appartenessero a due gruppi diversi. Senza, si dispongono di fila e vanno a
   capo dove serve. (Misurato a 390px: il secondo comando passa da x241 a x167,
   accanto al primo; le righe restano due, perché tre comandi su 390px non ci
   stanno comunque — quello che cambia è che non c'è più il buco.) */
@media (max-width: 700px) {
  .tl-barra-comandi .tl-spacer { display: none; }
}

/* ── La ricerca dentro il selettore dell'ente ─────────────────────────────
 *
 * Il campo non sta nel markup: lo inserisce tl.js. La fascia è disegnata in
 * DUE file — ui/menu_side.php per le pagine legacy e
 * partials/tl-cornice-topbar.blade.php per Blade e Filament — e scriverlo a
 * mano vorrebbe dire ripeterlo in tutti e due e tenerli allineati. Qui resta
 * il solo aspetto.
 *
 * `sticky` è il motivo per cui queste regole esistono invece di due stili in
 * linea messi da JavaScript: il pannello dell'ente scorre — ha `max-height` e
 * `overflow-y` — e un campo dentro un contenitore che scorre se ne andrebbe
 * fuori vista alla prima rotella, cioè proprio mentre si guarda l'elenco che
 * si sta filtrando. */
.tl-picker-search {
  position: sticky;
  inset-block-start: 0;
  z-index: 1;
  padding: var(--tl-space-2);
  background: var(--tl-surface);
  border-block-end: 1px solid var(--tl-border);
}

.tl-picker-search .tl-field__control { width: 100%; }

/* ── Il campo che propone mentre si scrive ───────────────────────────────────
 *
 * Sostituisce `.tl-select-search`, che era una casella di filtro sopra a una
 * tendina: si scriveva, e poi bisognava comunque aprire la tendina. Qui le
 * proposte compaiono sotto al campo ({@see initSelectRicerca} in tl.js).
 *
 * `position: relative` sul contenitore e `absolute` sull'elenco: così l'elenco
 * **esce dal flusso** e si sovrappone a ciò che ha sotto invece di spingerlo in
 * giù. Su `select_customer.php` sotto c'è «Scegliere gestione», e un elenco che
 * spinge farebbe saltare le righe a ogni lettera digitata. */
.tl-combo { position: relative; }
.tl-combo__campo { width: 100%; height: auto; font-size: inherit; }

.tl-combo__elenco {
  position: absolute;
  inset-inline: 0;
  top: calc(100% + 2px);
  z-index: var(--tl-z-dropdown);
  margin: 0;
  padding: var(--tl-space-1);
  list-style: none;
  max-height: 320px;
  overflow-y: auto;
  background: var(--tl-surface-raised);
  border-radius: var(--tl-radius);
  box-shadow: 0 0 0 1px var(--tl-border), var(--tl-shadow-lg);
}

.tl-combo__voce {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--tl-space-3);
  padding: var(--tl-space-2) var(--tl-space-3);
  min-height: var(--tl-target-comfort);
  border-radius: var(--tl-radius-sm);
  font-size: var(--tl-text-base);
  line-height: var(--tl-leading-tight);
  color: var(--tl-text);
  cursor: pointer;
}

/* Una classe sola per «col puntatore sopra» e «raggiunta con le frecce»: sono
 * la stessa cosa per chi guarda, e tenerle distinte porta a scoprire un giorno
 * che con la tastiera non si vede dove si è. */
.tl-combo__voce:hover,
.tl-combo__voce.is-attiva {
  background: var(--tl-accent-subtle);
  color: var(--tl-accent-text);
}

.tl-combo__nome { overflow-wrap: anywhere; }

.tl-combo__codice {
  flex: 0 0 auto;
  font-family: var(--tl-font-mono);
  font-size: var(--tl-text-xs);
  color: var(--tl-text-subtle);
}

.tl-combo__vuoto {
  padding: var(--tl-space-2) var(--tl-space-3);
  font-size: var(--tl-text-sm);
  color: var(--tl-text-muted);
}

/* ── Una <select> vestita come il selettore dell'ente ─────────────────────
 *
 * Il pannello che `initSelectPicker` costruisce al posto di una tendina. Non
 * è il campo qui sopra: là la tendina resta e il campo le sta accanto; qui la
 * tendina si nasconde e questo pulsante prende il suo posto.
 *
 * .tl-card, .tl-nav-list, .tl-picker-search e .tl-picker-empty sono le stesse
 * classi della fascia in alto, di proposito: ciò che si corregge lì si vede
 * anche qui. Restano da dire solo le cose che nella fascia fanno gli stili in
 * linea del markup, che qui non ci sono. */
.tl-select-picker { position: relative; display: block; }

/* Il pulsante somiglia al campo che sostituisce e prende la misura dalla
   pagina invece di dettarla: la stessa schermata è grande il triplo con
   l'aspetto classico e normale con quello nuovo, che i suoi 3rem li annulla
   (ui/reskin.css). Da qui `font: inherit` e le misure in `em`, che seguono
   qualunque delle due sia in vigore. */
.tl-select-picker__toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--tl-space-2);
  min-height: var(--tl-target-min);
  padding: 0 .5em;
  background: var(--tl-surface);
  color: var(--tl-text);
  border: 1px solid var(--tl-border);
  border-radius: var(--tl-radius);
  font: inherit;
  text-align: start;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
  list-style: none;
}

/* Vedi il commento in tl.js: la scritta sta in uno <span> perché i puntini
   funzionino. `min-width: 0` perché un elemento flex, di suo, si rifiuta di
   stringersi sotto la larghezza del proprio contenuto. */
.tl-select-picker__label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Il triangolino di <summary>: `list-style` lo toglie quasi ovunque, Safari
   vuole anche questo. */
.tl-select-picker__toggle::-webkit-details-marker { display: none; }

/* Come un campo, non come un pulsante: al passaggio del mouse cambia il bordo
   e non il fondo. Una <select> fa così, e questo deve passare per una. */
.tl-select-picker__toggle:hover { border-color: var(--tl-border-strong); }
.tl-select-picker__toggle:focus-visible {
  outline: var(--tl-focus-ring);
  outline-offset: var(--tl-focus-offset);
}

/* Finché non si è scelto il pulsante porta il segnaposto, e deve dire di non
   essere una scelta — come il grigio del `placeholder` di un campo. */
.tl-select-picker__toggle.is-empty { color: var(--tl-text-muted); }

/* La punta a destra: la tendina nativa ne aveva una, e senza, il pulsante non
   dichiara di potersi aprire. Disegnata coi bordi e misurata in `em`, così
   cresce col testo invece di restare un francobollo su un controllo enorme. */
.tl-select-picker__toggle::after {
  content: '';
  flex: none;
  width: .4em;
  height: .4em;
  margin-inline-start: var(--tl-space-2);
  /* Nera, e non «del colore del testo»: quando non si è ancora scelto il testo
     è il grigio del segnaposto, e la freccia sbiadirebbe con lui. Una <select>
     nativa la disegna scura comunque. */
  border-inline-end: .1em solid var(--tl-text);
  border-block-end: .1em solid var(--tl-text);
  transform: translateY(-.15em) rotate(45deg);
}

.tl-select-picker[open] > .tl-select-picker__toggle::after {
  transform: translateY(.1em) rotate(-135deg);
}

/* Il pannello pende dal pulsante e non sposta la pagina. Dentro torna alla
   misura normale del testo: la erediterebbe dal pulsante, e dove la pagina lo
   fa grande — select_customer.php con l'aspetto classico — un elenco di
   ottanta enti non starebbe in nessuno schermo.

   ⚠ L'altezza era `min(48rem, 60vh)`, e valeva due cose diverse nei due posti
   in cui questo pannello vive: 480px sulle pagine legacy, dove bootstrap.css
   porta la radice a 10px, e 768px su quelle Laravel, che bootstrap.css non lo
   caricano affatto. In pixel significa la stessa cosa in entrambe — è la
   ragione per cui tokens.css vieta i rem — ed è un terzo più bassa, che su un
   elenco lungo è la differenza fra una tendina e un muro. */
.tl-select-picker__panel {
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: calc(100% + var(--tl-space-1));
  z-index: var(--tl-z-dropdown);
  min-width: 100%;
  max-height: min(320px, 40vh);
  overflow-y: auto;
  box-shadow: var(--tl-shadow-lg);
  font-size: 1rem;
  line-height: 1.5;
  text-align: start;
}

.tl-select-picker__list { padding-block: var(--tl-space-1); }

/* Le voci sono <button> e non link: la fascia naviga, questo imposta un
   valore. Si toglie loro l'aspetto di pulsante e si ripete ciò che nella
   fascia dicono gli stili in linea — il colore del testo della pagina invece
   di quello della barra laterale, e il rientro ridotto, perché qui non ci sono
   icone da allineare. */
.tl-select-picker__opt {
  width: 100%;
  padding-inline-start: var(--tl-space-3);
  border: 0;
  background: none;
  color: var(--tl-text);
  font-family: inherit;
  font-size: var(--tl-text-sm);
  text-align: start;
  cursor: pointer;
}

/* Le voci scartate dal filtro si nascondono con l'attributo `hidden`. Il
 * browser gli dà `display: none` di suo, ma è un valore predefinito: qualunque
 * regola di questo foglio sul `display` di un <li> avrebbe la meglio. Dirlo
 * qui rende il nascondere indipendente da come cambierà `.tl-nav-list`. */
.tl-nav-list li[hidden] { display: none; }

.tl-picker-empty {
  margin: 0;
  padding: var(--tl-space-3);
  font-size: var(--tl-text-sm);
  color: var(--tl-text-muted);
  text-align: center;
}

/* Briciole di pane: dicono da dove si è arrivati. Oggi il titolo di pagina è
 * "Menu / Sottomenu" dentro un unico <span>, che non è navigabile. */
.tl-breadcrumb {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--tl-space-1);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--tl-text-xs);
  color: var(--tl-text-muted);
}

.tl-breadcrumb li { display: flex; align-items: center; gap: var(--tl-space-1); }
.tl-breadcrumb li + li::before { content: "›"; opacity: 0.5; }
.tl-breadcrumb a { color: inherit; text-decoration: none; }
.tl-breadcrumb a:hover { color: var(--tl-text-link); text-decoration: underline; }

.tl-content {
  flex: 1 1 auto;
  padding: var(--tl-space-4);
  max-width: var(--tl-content-max);
  width: 100%;
}

/* ── Il pulsante che apre e chiude la barra ──────────────────────────────── */

.tl-sidebar-toggle {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tl-target-comfort);
  height: var(--tl-target-comfort);
  padding: 0;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--tl-radius);
  color: var(--tl-text-muted);
  cursor: pointer;
}

.tl-sidebar-toggle:hover { background: var(--tl-surface-muted); color: var(--tl-text); }
.tl-sidebar-toggle:focus-visible { outline: var(--tl-focus-ring); outline-offset: var(--tl-focus-offset); }

/* Le tre lineette, disegnate senza dipendere da un font di icone: i tre set di
 * icone del progetto non sono tutti presenti su tutte le pagine. */
.tl-sidebar-toggle__bars,
.tl-sidebar-toggle__bars::before,
.tl-sidebar-toggle__bars::after {
  display: block;
  width: 16px;
  height: 2px;
  background: currentColor;
  border-radius: 1px;
}

.tl-sidebar-toggle__bars { position: relative; }
.tl-sidebar-toggle__bars::before { content: ""; position: absolute; top: -5px; }
.tl-sidebar-toggle__bars::after  { content: ""; position: absolute; top:  5px; }

/* ── Su schermo stretto la barra diventa un pannello che scorre da sinistra ── */

.tl-sidebar-scrim {
  position: fixed;
  inset: 0;
  z-index: calc(var(--tl-z-sidebar) - 1);
  background: rgb(13 16 19 / 0.45);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--tl-duration) var(--tl-ease);
}

@media (max-width: 900px) {
  .tl-shell   { padding-inline-start: 0; }
  .tl-sidebar { transform: translateX(-100%); box-shadow: var(--tl-shadow-lg); }

  .tl-shell.is-sidebar-open .tl-sidebar       { transform: translateX(0); }
  .tl-shell.is-sidebar-open .tl-sidebar-scrim { opacity: 1; pointer-events: auto; }

  /* Sotto i 900px "ridotta a icone" non ha senso: o c'è o non c'è. */
  .tl-shell.is-sidebar-collapsed { padding-inline-start: 0; }
  .tl-shell.is-sidebar-collapsed .tl-sidebar { width: var(--tl-sidebar-width); }
  .tl-shell.is-sidebar-collapsed .tl-sidebar__ident,
  .tl-shell.is-sidebar-collapsed .tl-nav-group__text,
  .tl-shell.is-sidebar-collapsed .tl-nav-group__chevron,
  .tl-shell.is-sidebar-collapsed .tl-nav-list { display: revert; }

  .tl-content { padding: var(--tl-space-3); }
}

/* Chi stampa non vuole la barra di navigazione sul foglio. */
@media print {
  .tl-sidebar, .tl-sidebar-scrim, .tl-topbar, .tl-actions, .tl-filters { display: none !important; }
  .tl-main { margin: 0; }
}


/* ═══════════════════════════════════════════════════════════════════════════
 * 2 · PULSANTI
 * ═══════════════════════════════════════════════════════════════════════════
 * Serve prima degli altri componenti perché filtri, azioni e messaggi lo usano.
 * Va su <button> e su <a> indifferentemente.
 */

.tl-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--tl-space-2);
  min-height: var(--tl-control-height);
  padding: 0 var(--tl-space-4);
  border: 1px solid transparent;
  border-radius: var(--tl-radius);
  background: var(--tl-surface);
  color: var(--tl-text);
  font-family: inherit;
  font-size: var(--tl-text-base);
  font-weight: var(--tl-weight-medium);
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  box-shadow: var(--tl-shadow-btn);
  transition: background var(--tl-duration-fast) var(--tl-ease),
              border-color var(--tl-duration-fast) var(--tl-ease),
              box-shadow var(--tl-duration-fast) var(--tl-ease),
              transform var(--tl-duration-fast) var(--tl-ease),
              color var(--tl-duration-fast) var(--tl-ease);
}

/* ── Il rilievo ───────────────────────────────────────────────────────────────
 * Si solleva sotto il puntatore e si abbassa quando lo si preme. L'abbassamento
 * è un pixel di `transform` più un'ombra interna: costa niente e dà il riscontro
 * del clic **prima** che il server risponda — che su una stampa da trenta
 * secondi è ciò che evita il secondo clic.
 *
 * `:active` e non `:focus`: il fuoco resta sul pulsante dopo il rilascio, e un
 * pulsante che rimane premuto sembra rotto. */
.tl-btn:hover { box-shadow: var(--tl-shadow-btn-hover); }
.tl-btn:active { transform: translateY(1px); box-shadow: var(--tl-shadow-btn-active); }

.tl-btn:focus-visible { outline: var(--tl-focus-ring); outline-offset: var(--tl-focus-offset); }
.tl-btn:disabled, .tl-btn.is-disabled { opacity: 0.5; cursor: not-allowed; pointer-events: none; box-shadow: none; }

/* Predefinito: contorno grigio. È il pulsante che non fa niente di irreversibile. */
.tl-btn { border-color: var(--tl-border-strong); }
.tl-btn:hover { background: var(--tl-surface-muted); border-color: var(--tl-gray-400); }

/* Primario: uno per schermata. Se ce ne sono tre, non ce n'è nessuno.
 * Il filo di luce in cima (`--tl-highlight-top`) si vede solo sui fondi scuri,
 * ed è quello che fa leggere il pulsante come un oggetto invece che come un
 * rettangolo colorato. */
.tl-btn--primary { background: var(--tl-accent); border-color: var(--tl-accent); color: var(--tl-on-accent); box-shadow: var(--tl-highlight-top), var(--tl-shadow-btn); }
.tl-btn--primary:hover { background: var(--tl-accent-hover); border-color: var(--tl-accent-hover); box-shadow: var(--tl-highlight-top), var(--tl-shadow-btn-hover); }
.tl-btn--primary:active { background: var(--tl-accent-active); box-shadow: var(--tl-shadow-btn-active); }

/* Distruttivo: cancellazioni e operazioni non annullabili. */
.tl-btn--danger { background: var(--tl-danger); border-color: var(--tl-danger); color: var(--tl-text-inverse); box-shadow: var(--tl-highlight-top), var(--tl-shadow-btn); }
.tl-btn--danger:hover { background: color-mix(in srgb, var(--tl-danger) 85%, black); box-shadow: var(--tl-highlight-top), var(--tl-shadow-btn-hover); }
.tl-btn--danger:active { box-shadow: var(--tl-shadow-btn-active); }

/* Silenzioso: azioni secondarie, non disegna niente finché non lo si tocca.
 * Niente ombra, in nessuno stato: se ce l'avesse smetterebbe di essere
 * silenzioso, e in una fila di comandi si vedrebbero tre rilievi dove ce n'è
 * uno solo che conta. */
.tl-btn--ghost { background: transparent; border-color: transparent; color: var(--tl-text-muted); box-shadow: none; }
.tl-btn--ghost:hover { background: var(--tl-surface-muted); color: var(--tl-text); border-color: transparent; box-shadow: none; }
.tl-btn--ghost:active { box-shadow: none; }

/* Che sembra un collegamento, ma è un pulsante (e quindi la tastiera lo attiva). */
.tl-btn--link {
  background: transparent; border-color: transparent;
  color: var(--tl-text-link); padding-inline: var(--tl-space-1);
  text-decoration: underline; text-underline-offset: 2px;
  box-shadow: none;
}
.tl-btn--link:hover { background: transparent; border-color: transparent; color: var(--tl-text-link-hover); box-shadow: none; }
.tl-btn--link:active { transform: none; box-shadow: none; }

.tl-btn--sm { min-height: var(--tl-control-height-sm); padding-inline: var(--tl-space-3); font-size: var(--tl-text-sm); }
.tl-btn--lg { min-height: var(--tl-control-height-lg); padding-inline: var(--tl-space-5); font-size: var(--tl-text-lg); }

/* Solo icona: qui il bersaglio minimo va imposto, perché non c'è il testo a
 * tenerlo grande. È il difetto più diffuso nelle azioni di riga di oggi. */
.tl-btn--icon {
  padding: 0;
  width: var(--tl-target-comfort);
  min-width: var(--tl-target-comfort);
  height: var(--tl-target-comfort);
}
.tl-btn--icon.tl-btn--sm { width: var(--tl-target-min); min-width: var(--tl-target-min); height: var(--tl-target-min); }

.tl-btn-group { display: inline-flex; align-items: center; gap: var(--tl-space-2); flex-wrap: wrap; }


/* ═══════════════════════════════════════════════════════════════════════════
 * 3 · CAMPO DI MODULO                                        .tl-field
 * ═══════════════════════════════════════════════════════════════════════════
 * Il componente che conta di più: sostituisce le 8.221 coppie
 * BoxRowLabel / BoxRowCaption sparse nelle pagine.
 *
 * Risolve in un colpo solo quattro cose che oggi vanno sistemate a mano ogni
 * volta, e che quindi non sono sistemate quasi mai:
 *   · <label for> collegata davvero al controllo (WCAG 1.3.1 / 3.3.2);
 *   · lo stato di errore visibile e annunciato, non solo colorato di rosso;
 *   · il testo di aiuto legato al campo con aria-describedby;
 *   · la spaziatura, uguale ovunque senza .clean_row e .HSpace4.
 */

.tl-field { display: flex; flex-direction: column; gap: var(--tl-space-1); margin-block-end: var(--tl-space-3); min-width: 0; }

/* Un campo nascosto sparisce davvero. Il commento di x-tl.campo promette che
   `hidden` sul contenitore fa sparire etichetta e controllo insieme, ma il
   `display: flex` qui sopra è d'autore e batte il [hidden] del browser, che ha
   specificità zero: senza questa riga il campo resta lì, nascosto per i lettori
   di schermo e visibile a tutti gli altri. Lo usa «Creazione ente» per
   scambiare la tendina dei comuni col campo del codice libero. */
.tl-field[hidden] { display: none; }

.tl-field__label {
  display: flex;
  align-items: center;
  gap: var(--tl-space-1);
  font-size: var(--tl-text-sm);
  font-weight: var(--tl-weight-medium);
  color: var(--tl-text-muted);
}

/* L'asterisco dell'obbligatorio non basta da solo: accanto ci va la parola,
 * visibile solo ai lettori di schermo (.tl-sr-only). Il componente lo fa da sé. */
.tl-field__required { color: var(--tl-danger); font-weight: var(--tl-weight-bold); }

.tl-field__control {
  width: 100%;
  min-height: var(--tl-control-height);
  padding: 0 var(--tl-control-padding-x);
  border: 1px solid var(--tl-border-strong);
  border-radius: var(--tl-radius);
  background: var(--tl-surface);
  color: var(--tl-text);
  font-family: inherit;
  font-size: var(--tl-text-base);
  line-height: var(--tl-leading-normal);
  transition: border-color var(--tl-duration-fast) var(--tl-ease),
              box-shadow var(--tl-duration-fast) var(--tl-ease);
}

.tl-field__control:hover { border-color: var(--tl-gray-400); }

.tl-field__control:focus {
  outline: none;
  border-color: var(--tl-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--tl-accent) 18%, transparent);
}

.tl-field__control:disabled,
.tl-field__control[readonly] { background: var(--tl-surface-muted); color: var(--tl-text-subtle); cursor: not-allowed; }

.tl-field__control::placeholder { color: var(--tl-text-subtle); }

/* textarea e select hanno bisogno di due aggiustamenti loro. */
textarea.tl-field__control { min-height: calc(var(--tl-control-height) * 2.5); padding-block: var(--tl-space-2); resize: vertical; }
select.tl-field__control  { padding-inline-end: var(--tl-space-6); cursor: pointer; }

/* Un importo. Allineato a destra e a cifre incolonnate, come in tabella
 * (`.tl-num`): una colonna di importi si confronta a occhio solo se le unità
 * stanno una sotto l'altra. Il controllo resta `type="text"`, perché
 * `type="number"` rifiuta la virgola decimale italiana — che è come il
 * gestionale scrive e rilegge tutti i suoi importi. */
.tl-field__control--num { text-align: end; font-variant-numeric: tabular-nums; }

.tl-field__hint { font-size: var(--tl-text-xs); color: var(--tl-text-muted); }

/* L'errore ha un'icona oltre al colore: il colore da solo non è un mezzo di
 * comunicazione accettabile (WCAG 1.4.1) e non lo vede chi non distingue il rosso. */
.tl-field__error {
  display: flex;
  align-items: flex-start;
  gap: var(--tl-space-1);
  font-size: var(--tl-text-xs);
  font-weight: var(--tl-weight-medium);
  color: var(--tl-danger-text);
}

.tl-field__error::before { content: "⚠"; flex: 0 0 auto; line-height: 1.35; }

.tl-field.is-invalid .tl-field__control { border-color: var(--tl-danger); }
.tl-field.is-invalid .tl-field__control:focus { box-shadow: 0 0 0 3px color-mix(in srgb, var(--tl-danger) 18%, transparent); }

.tl-field.is-valid .tl-field__control { border-color: var(--tl-success); }

/* Modificatori di larghezza, per non tornare a scrivere col-sm-* a mano. */
.tl-field--sm  { max-width: 160px; }
.tl-field--md  { max-width: 256px; }
.tl-field--lg  { max-width: 448px; }
.tl-field--full { max-width: none; }

/* Campi affiancati: da 1 a 4 colonne che si impilano da sole su schermo stretto. */
.tl-field-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(224px, 100%), 1fr));
  gap: 0 var(--tl-space-4);
  align-items: start;
}

/* Casella di spunta e radio: bersaglio grande, etichetta cliccabile. */
.tl-check { display: flex; align-items: center; gap: var(--tl-space-2); min-height: var(--tl-target-min); cursor: pointer; }
.tl-check__input { width: 16px; height: 16px; margin: 0; accent-color: var(--tl-accent); cursor: pointer; flex: 0 0 auto; }
.tl-check__label { font-size: var(--tl-text-base); color: var(--tl-text); cursor: pointer; }
.tl-check__input:focus-visible { outline: var(--tl-focus-ring); outline-offset: var(--tl-focus-offset); }


/* ═══════════════════════════════════════════════════════════════════════════
 * 4 · BARRA DEI FILTRI                                       .tl-filters
 * ═══════════════════════════════════════════════════════════════════════════
 * L'elenco dei verbali ha 26 filtri tutti visibili insieme. Qui i frequenti
 * restano in linea, gli altri stanno in un pannello che si apre, e i filtri
 * attivi diventano pastiglie sopra ai risultati — così si sa cosa si sta
 * guardando senza rileggere ventisei tendine.
 */

.tl-filters {
  background: var(--tl-surface);
  border: 1px solid var(--tl-border);
  border-radius: var(--tl-radius-lg);
  margin-block-end: var(--tl-space-4);
}

.tl-filters__primary {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--tl-space-3);
  padding: var(--tl-space-3) var(--tl-space-4);
}

.tl-filters__primary .tl-field { margin-block-end: 0; flex: 1 1 192px; }

.tl-filters__actions { display: flex; align-items: center; gap: var(--tl-space-2); margin-inline-start: auto; }

/* Il pannello "altri filtri" è un <details>: si apre senza JavaScript. */
.tl-filters__more { border-block-start: 1px solid var(--tl-border-subtle); }

.tl-filters__more-toggle {
  display: flex;
  align-items: center;
  gap: var(--tl-space-2);
  padding: var(--tl-space-2) var(--tl-space-4);
  min-height: var(--tl-target-comfort);
  font-size: var(--tl-text-sm);
  font-weight: var(--tl-weight-medium);
  color: var(--tl-text-link);
  cursor: pointer;
  user-select: none;
  list-style: none;
}

.tl-filters__more-toggle::-webkit-details-marker { display: none; }
.tl-filters__more-toggle::marker { content: ""; }
.tl-filters__more-toggle:hover { background: var(--tl-surface-muted); }
.tl-filters__more-toggle:focus-visible { outline: var(--tl-focus-ring); outline-offset: calc(var(--tl-focus-offset) * -1); }

.tl-filters__more-panel {
  padding: var(--tl-space-3) var(--tl-space-4) var(--tl-space-4);
  border-block-start: 1px solid var(--tl-border-subtle);
  background: var(--tl-surface-muted);
  border-end-start-radius: var(--tl-radius-lg);
  border-end-end-radius: var(--tl-radius-lg);
}

/* ── Filtri attivi ─────────────────────────────────────────────────────────
 * Ogni pastiglia dice campo e valore, e ha la sua croce per toglierla. */

.tl-chips { display: flex; flex-wrap: wrap; align-items: center; gap: var(--tl-space-2); padding: var(--tl-space-2) var(--tl-space-4); border-block-start: 1px solid var(--tl-border-subtle); }
.tl-chips:empty { display: none; }
.tl-chips__label { font-size: var(--tl-text-xs); color: var(--tl-text-muted); text-transform: uppercase; letter-spacing: 0.04em; }

.tl-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--tl-space-2);
  padding: var(--tl-space-1) var(--tl-space-1) var(--tl-space-1) var(--tl-space-3);
  min-height: var(--tl-target-min);
  background: var(--tl-accent-subtle);
  color: var(--tl-accent-text);
  border: 1px solid color-mix(in srgb, var(--tl-accent) 25%, transparent);
  border-radius: var(--tl-radius-full);
  font-size: var(--tl-text-xs);
  white-space: nowrap;
}

.tl-chip__key { font-weight: var(--tl-weight-semibold); }

.tl-chip__remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  padding: 0;
  border: 0;
  border-radius: var(--tl-radius-full);
  background: transparent;
  color: inherit;
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
}

.tl-chip__remove:hover { background: color-mix(in srgb, var(--tl-accent) 20%, transparent); }
.tl-chip__remove:focus-visible { outline: var(--tl-focus-ring); outline-offset: 1px; }


/* ── 4b · La barra: due pulsanti, e tutti i filtri nella finestra ──────────
 *
 * È la forma che sostituisce ovunque i filtri tutti aperti. Il pannello che si
 * apre (`.tl-filters__more`, qui sopra) era il primo tentativo e ha un difetto:
 * apre in colonna dentro la pagina, quindi con dodici filtri spinge i risultati
 * sotto la piega e chi cerca perde di vista ciò che stava guardando. Qui i
 * filtri stanno in una finestra sopra la pagina, e sotto la barra restano le
 * pastiglie di quelli attivi — così la modale nasconde i comandi, mai lo stato.
 *
 *   ┌────────────────────────────────────────────────────────┐
 *   │                                 [Filtri ③] [Cerca]     │  .tl-filters__barra
 *   │ ATTIVI:  ⓧ Targa: AB123CD  ⓧ Stato: notificato        │  .tl-chips
 *   └────────────────────────────────────────────────────────┘
 *
 * Le pastiglie e il pallino col numero li compone tl.js leggendo i campi del
 * pannello: nessuna pagina deve dichiarare due volte quali filtri ha.
 *
 * Fino al 7 settembre 2026 i campi identificativi — targa, numero, nominativo —
 * stavano in barra, in un `.tl-filters__cerca` che cresceva a riempire la riga.
 * Il committente li ha voluti tutti nella finestra: quella regola è stata tolta
 * di qui perché non le corrispondeva più nessun HTML, ed era già successo con
 * `.tl-filters__grid` di restare a fare da classe fantasma. Se si torna
 * indietro va rifatta: `display:flex`, `flex: 1 1 320px`, `min-width: 0`, e i
 * `.tl-field` dentro a `flex: 1 1 192px`.
 */

.tl-filters__barra {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--tl-space-3);
  padding: var(--tl-space-3) var(--tl-space-4);
}

/* Il numero dei filtri attivi sul pulsante. Senza, «Filtri» non dice se sotto
   c'è qualcosa, ed è proprio l'informazione che la modale toglie di vista. */
.tl-filters__conteggio {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  margin-inline-start: var(--tl-space-2);
  padding: 0 5px;
  border-radius: var(--tl-radius-full);
  background: var(--tl-accent);
  color: var(--tl-on-accent);
  font-size: var(--tl-text-xs);
  font-weight: var(--tl-weight-semibold);
  line-height: 1;
}

/* `display: inline-flex` qui sopra batte il `display: none` che il browser dà
   a [hidden], che è dell'UA e ha specificità zero: senza questa riga, quando
   non c'è nessun filtro attivo il pallino resta lì, colorato e vuoto, e dice
   il contrario di quello che deve dire. tl.js l'attributo lo mette già. */
.tl-filters__conteggio[hidden] { display: none; }

/* La finestra dei filtri: la stessa `.tl-dialog` della conferma, più larga
   perché deve contenere una griglia di campi e non una domanda.
   ⚠ La doppia classe non è un vezzo. `.tl-dialog` porta `max-width: 448px` ed
   è scritta 330 righe più in basso: a parità di specificità vinceva lei, e la
   finestra restava stretta come quella di una domanda — una colonna sola, con
   i diciassette filtri di Elenco verbali incolonnati da scorrere. Si vedeva
   già prima; da quando dentro ci sono ANCHE i campi identificativi è diventato
   il difetto principale della pagina. `.tl-dialog.tl-filters__pannello` vince
   per specificità, e non dipende più da chi è scritto per ultimo. */
/* 1040px e non più 760: la griglia a binari qui sotto guadagna tre binari, cioè
 * una colonna e mezza di campi in più per riga. Su Elenco verbali — diciassette
 * filtri — sono due righe risparmiate, e la finestra sta ancora dentro un
 * portatile da 1280 senza toccare i bordi. */
.tl-dialog.tl-filters__pannello { max-width: min(1040px, calc(100vw - var(--tl-space-6))); }
.tl-filters__pannello .tl-dialog__body { color: var(--tl-text); font-size: var(--tl-text-base); }

.tl-filters__pannello-testa {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--tl-space-3);
}

.tl-filters__chiudi {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tl-target-min);
  height: var(--tl-target-min);
  padding: 0;
  border: 0;
  border-radius: var(--tl-radius-full);
  background: transparent;
  color: var(--tl-text-muted);
  font-size: var(--tl-text-lg);
  line-height: 1;
  cursor: pointer;
}

.tl-filters__chiudi:hover { background: var(--tl-surface-muted); color: var(--tl-text); }
.tl-filters__chiudi:focus-visible { outline: var(--tl-focus-ring); outline-offset: var(--tl-focus-offset); }

/* La griglia dei campi dentro il pannello.
 *
 * ⚠ `.tl-filters__grid` è qui perché 21 viste Blade la usavano **senza che
 * esistesse**: i loro filtri cadevano impilati uno sotto l'altro, e non si
 * vedeva che fosse un difetto perché una colonna di campi sembra una scelta.
 * È l'errore contro cui mette in guardia il commento di
 * `components/layout/gestionale.blade.php`: il nome della classe si copia, non
 * si sceglie. Le due classi sono sinonimi, e il nome nuovo è `__campi`. */
/* ── La griglia dei campi ─────────────────────────────────────────────────────
 *
 * Fino al 7 settembre 2026: `repeat(auto-fill, minmax(200px, 1fr))`. Colonne
 * tutte uguali, larghe almeno 200px. Il difetto è che «Cron» — cinque cifre —
 * occupa esattamente quanto «Trasgressore», e su un pannello da diciassette
 * filtri metà della finestra è spazio vuoto dentro campi corti.
 *
 * Adesso la griglia è fatta di **binari stretti** (120px) e ogni campo dichiara
 * quanti binari occupa. È la ricetta della contabilità — griglia a 12 colonne e
 * `columnSpan` per campo, tarato sul contenuto: `Indirizzo` 6, `Civico` 2,
 * `CAP` 3 — portata su CSS puro.
 *
 * Perché binari stretti e `auto-fill` invece di dodici colonne fisse: così la
 * disposizione si adatta da sé alla larghezza senza una sola media query, e
 * soprattutto **senza un contenitore in più**. Un elemento non può rispondere
 * alla propria `@container`, quindi dodici colonne fisse avrebbero richiesto di
 * avvolgere la griglia in un div solo per poterla interrogare — cioè toccare il
 * markup di quaranta pagine invece di questo file.
 *
 * L'unica media query è quella sotto: sotto i 700px i `span` si azzerano tutti.
 * Senza, un campo `--lg` (4 binari) in una griglia che ne ha due creerebbe due
 * colonne implicite e la finestra scorrerebbe in orizzontale.
 *
 * ── Perché il binario è 112px ────────────────────────────────────────────────
 * Non è un numero tondo scelto a occhio: è quello che fa tornare i conti nella
 * finestra dei filtri, che è il posto dove questa griglia si vede di più.
 *
 *   finestra 1040 − 2×16 di margine interno   = 1008 di spazio utile
 *   8 binari da 112 + 7 spazi da 16           = 1008   ← esatto
 *
 * Quindi: otto binari, quattro campi normali per riga (2 binari l'uno) oppure
 * otto campi corti. Prima erano tre per riga in ogni caso. Un binario da 112px
 * è anche la misura minima sotto cui una data `gg/mm/aaaa` comincia a scorrere
 * dentro il proprio campo, ed è la ragione per cui `--sm` è un binario e non
 * mezzo. */
.tl-filters__campi,
.tl-filters__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(112px, 100%), 1fr));
  gap: var(--tl-space-3) var(--tl-space-4);
  align-items: end;
}

.tl-filters__campi .tl-field,
.tl-filters__grid .tl-field { margin-block-end: 0; }

/* Quanti binari occupa ogni misura. Il default è 2 (≈256px): la larghezza di un
 * nome o di una tendina. I modificatori li dichiara la pagina con
 * `larghezza="…"`, oppure li deduce `<x-tl.campo>` dal tipo del campo — una
 * data non ha mai avuto bisogno di 256px. */
.tl-filters__campi > .tl-field,
.tl-filters__grid  > .tl-field       { grid-column: span 2; }
.tl-filters__campi > .tl-field--sm,
.tl-filters__grid  > .tl-field--sm   { grid-column: span 1; }
.tl-filters__campi > .tl-field--md,
.tl-filters__grid  > .tl-field--md   { grid-column: span 2; }
.tl-filters__campi > .tl-field--lg,
.tl-filters__grid  > .tl-field--lg   { grid-column: span 4; }
.tl-filters__campi > .tl-field--full,
.tl-filters__grid  > .tl-field--full { grid-column: 1 / -1; }

/* Dentro la griglia il tetto di larghezza del controllo non serve più — ce l'ha
 * già la cella — e anzi farebbe danno: lascerebbe il controllo più stretto
 * della propria colonna, cioè il difetto di prima in piccolo. */
.tl-filters__campi > .tl-field,
.tl-filters__grid  > .tl-field { max-width: none; }

/* Le caselle di spunta stanno su una riga sola: una accanto all'altra, non una
 * per colonna. `span 2` perché l'etichetta è di fianco e non sopra. */
.tl-filters__campi > .tl-check,
.tl-filters__grid  > .tl-check { grid-column: span 2; }

@media (max-width: 700px) {
  .tl-filters__campi > .tl-field,
  .tl-filters__grid  > .tl-field,
  .tl-filters__campi > .tl-check,
  .tl-filters__grid  > .tl-check { grid-column: 1 / -1; }
}

/* Una casella di spunta non ha etichetta sopra, quindi in una riga di campi con
   etichetta resta in alto e sembra scollegata: la si allinea al loro controllo. */
.tl-filters__campi .tl-check { min-height: var(--tl-target-comfort); align-items: center; }


/* ═══════════════════════════════════════════════════════════════════════════
 * 5 · TABELLA DATI                                           .tl-table
 * ═══════════════════════════════════════════════════════════════════════════
 * Una <table> vera, con <thead> e <th scope="col">.
 *
 * Oggi gli elenchi sono griglie di <div class="col-sm-*">. Passare a una tabella
 * sblocca insieme quattro cose che con i div non si possono avere: l'ordinamento
 * per colonna, la copia verso un foglio di calcolo, la navigazione per righe e
 * colonne del lettore di schermo, e le intestazioni che restano ferme mentre si
 * scorre. È l'intervento che cambia di più la giornata di chi usa gli elenchi.
 */

.tl-table-wrap {
  background: var(--tl-surface);
  border: 1px solid var(--tl-border);
  border-radius: var(--tl-radius-lg);
  overflow: auto;           /* la tabella larga scorre dentro il suo riquadro,
                               non fa scorrere la pagina in orizzontale        */
  max-width: 100%;
}

.tl-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: var(--tl-text-sm);
  color: var(--tl-text);
}

.tl-table caption { text-align: start; padding: var(--tl-space-3) var(--tl-space-4); font-size: var(--tl-text-sm); color: var(--tl-text-muted); }

.tl-table th,
.tl-table td { padding: var(--tl-space-2) var(--tl-space-3); text-align: start; vertical-align: middle; border-block-end: 1px solid var(--tl-border-subtle); }

/* Righe più alte del minimo: si vedono meno righe per schermata e si legge
 * molto più in fretta. Nel lavoro d'ufficio è quasi sempre il baratto giusto. */
.tl-table tbody tr { min-height: var(--tl-target-comfort); }

.tl-table thead th {
  position: sticky;
  top: 0;
  z-index: var(--tl-z-sticky);
  background: var(--tl-surface-muted);
  border-block-end: 1px solid var(--tl-border-strong);
  font-size: var(--tl-text-xs);
  font-weight: var(--tl-weight-semibold);
  color: var(--tl-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

.tl-table tbody tr:last-child td { border-block-end: 0; }
.tl-table tbody tr:hover td { background: var(--tl-accent-subtle); }
.tl-table--striped tbody tr:nth-child(even) td { background: var(--tl-surface-muted); }
.tl-table--striped tbody tr:hover td { background: var(--tl-accent-subtle); }

.tl-table--compact th,
.tl-table--compact td { padding: var(--tl-space-1) var(--tl-space-2); }

/* La colonna dei numeri va allineata a destra: le cifre si confrontano a colpo
 * d'occhio solo se le unità sono incolonnate.
 *
 * I selettori sono scritti per esteso (th.x, td.x) e non come semplice .x
 * perché `.tl-table th` ha specificità (0,1,1) e batterebbe un `.tl-table__num`
 * da (0,1,0): la regola verrebbe scritta, sembrerebbe giusta, e non si
 * applicherebbe. */
.tl-table th.tl-table__num,
.tl-table td.tl-table__num,
.tl-table th.tl-num,
.tl-table td.tl-num { text-align: end; font-variant-numeric: tabular-nums; }

.tl-table th.tl-table__mono,
.tl-table td.tl-table__mono { font-family: var(--tl-font-mono); font-size: var(--tl-text-xs); }

.tl-table th.tl-table__nowrap,
.tl-table td.tl-table__nowrap { white-space: nowrap; }

/* La colonna dei pulsanti in fondo alla riga: si stringe sul contenuto.
 *
 * `width: 1%` in una tabella non significa «l'uno per cento»: è il modo con cui
 * si dice al motore di dare a questa colonna il minimo che le serve e tutto il
 * resto alle altre. Senza, una tabella da due colonne le fa larghe uguali e i
 * pulsanti restano sospesi in mezzo al vuoto, lontanissimi dalla riga a cui si
 * riferiscono — che su un elenco lungo vuol dire sbagliare riga.
 *
 * Aggiunta il 13 agosto 2026 per le anagrafiche migrate. È additiva: nessuna
 * pagina del gestionale usa questa classe, quindi non cambia niente di ciò che
 * esiste. */
.tl-table th.tl-table__azioni,
.tl-table td.tl-table__azioni { width: 1%; white-space: nowrap; text-align: end; }

/* La colonna di una casella di spunta: stretta sul contenuto e centrata.
 *
 * Nasce per la matrice dei permessi di «Gestione utenti», dove le colonne sono
 * dieci e ognuna contiene una sola casella. Allineate all'inizio della cella,
 * le caselle si accostano tutte al bordo sinistro della propria colonna e
 * finiscono lontane dall'intestazione che le nomina: su dieci colonne vicine
 * vuol dire spuntare quella sbagliata.
 *
 * `width: 1%` è lo stesso espediente di `.tl-table__azioni` qui sopra: in una
 * tabella non significa «l'uno per cento», è il modo di dire al motore di dare
 * a questa colonna il minimo che le serve e tutto il resto alle altre. Così le
 * nove colonne strette si stringono e il nome della voce prende lo spazio che
 * avanza.
 *
 * I selettori sono scritti per esteso (th.x, td.x) per la ragione annotata
 * sopra: `.tl-table th` batterebbe una classe scritta da sola.
 *
 * È additiva: nessuna pagina del gestionale usa questa classe, quindi non
 * cambia niente di ciò che esiste. */
.tl-table th.tl-table__spunta,
.tl-table td.tl-table__spunta { width: 1%; white-space: nowrap; text-align: center; }

/* ── Intestazione che ordina ─────────────────────────────────────────────────
 * È un <button> dentro il <th>, non un onclick sul <th>: così si attiva con la
 * tastiera e il lettore di schermo lo annuncia come comando. */

.tl-table__sort {
  display: inline-flex;
  align-items: center;
  gap: var(--tl-space-1);
  width: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  text-transform: inherit;
  letter-spacing: inherit;
  text-align: start;
  cursor: pointer;
}

.tl-table__sort:hover { color: var(--tl-text); }
.tl-table__sort:focus-visible { outline: var(--tl-focus-ring); outline-offset: var(--tl-focus-offset); }

.tl-table__sort::after { content: "↕"; opacity: 0.35; font-size: 0.9em; }
.tl-table th[aria-sort="ascending"]  .tl-table__sort::after { content: "↑"; opacity: 1; color: var(--tl-accent); }
.tl-table th[aria-sort="descending"] .tl-table__sort::after { content: "↓"; opacity: 1; color: var(--tl-accent); }

/* La stessa intestazione, quando è un COLLEGAMENTO invece di un pulsante.
 *
 * Serve dove l'ordinamento si fa **nella query** e non nel browser: su un elenco
 * impaginato, riordinare le righe presenti in pagina riordinerebbe solo quelle
 * che si stanno guardando. Lì l'intestazione è un `<a>` che porta un indirizzo,
 * e la prima pagina a farlo è l'elenco di «Gestione utenti».
 *
 * Le due righe qui sotto dicono l'unica cosa che la regola sopra dà per scontata
 * senza scriverla: un `<button>` non è colorato né sottolineato di suo, un `<a>`
 * sì. Senza, le intestazioni ordinabili diventano azzurre e sottolineate e non
 * somigliano più a quelle di tutte le altre tabelle del gestionale.
 *
 * ⚠ Regola **affiancata**, non modificata: la versione a pulsante è quella che
 *   usa `initSortableTables` di `ui/tl.js` da prima, e toccarla si vedrebbe
 *   ovunque quella funzione sia attiva. `a.` la restringe ai soli collegamenti.
 *
 * La freccia non va aggiunta al testo: la disegna il `::after` qui sopra
 * leggendo `aria-sort` sul `<th>`, che chi scrive la pagina deve comunque
 * impostare per i lettori di schermo. Una sola fonte, e non due che possono
 * discordare. */
a.tl-table__sort,
a.tl-table__sort:hover {
  color: inherit;
  text-decoration: none;
}

/* ── Elenco vuoto ─────────────────────────────────────────────────────────
 * Oggi una ricerca senza risultati produce una tabella vuota e basta: non si
 * capisce se non c'è niente o se qualcosa non ha funzionato. */

.tl-empty { padding: var(--tl-space-7) var(--tl-space-4); text-align: center; color: var(--tl-text-muted); }
.tl-empty__title { font-size: var(--tl-text-lg); font-weight: var(--tl-weight-medium); color: var(--tl-text); margin-block-end: var(--tl-space-2); }
.tl-empty__text { font-size: var(--tl-text-sm); margin-block-end: var(--tl-space-4); }

/* ── Paginazione ────────────────────────────────────────────────────────── */

.tl-pager { display: flex; align-items: center; flex-wrap: wrap; gap: var(--tl-space-2); padding: var(--tl-space-3) var(--tl-space-4); border-block-start: 1px solid var(--tl-border-subtle); font-size: var(--tl-text-sm); color: var(--tl-text-muted); }
.tl-pager__info { margin-inline-end: auto; font-variant-numeric: tabular-nums; }


/* ═══════════════════════════════════════════════════════════════════════════
 * 6 · PASTIGLIA DI STATO                                     .tl-badge
 * ═══════════════════════════════════════════════════════════════════════════
 * Lo stato della pratica va reso con una pastiglia che contiene la parola.
 * Oggi la colonna "Stato pratica" è una fila di otto glifi grigi: per leggere
 * una riga bisogna passare il mouse su ciascuno, uno alla volta. Non è un
 * problema di accessibilità soltanto — è più lento per chiunque.
 *
 * Ogni variante porta colore E parola. Il colore da solo non è mai sufficiente.
 */

.tl-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--tl-space-1);
  padding: 2px var(--tl-space-2);
  min-height: 20px;
  border: 1px solid transparent;
  border-radius: var(--tl-radius-full);
  font-size: var(--tl-text-xs);
  font-weight: var(--tl-weight-medium);
  line-height: 1.4;
  white-space: nowrap;
  background: var(--tl-neutral-subtle);
  color: var(--tl-neutral-text);
  border-color: color-mix(in srgb, var(--tl-neutral) 25%, transparent);
}

/* Il pallino aggiunge una seconda differenza oltre al colore del fondo. */
.tl-badge::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: 0 0 auto; }
.tl-badge--nodot::before { display: none; }

.tl-badge--success { background: var(--tl-success-subtle); color: var(--tl-success-text); border-color: color-mix(in srgb, var(--tl-success) 30%, transparent); }
.tl-badge--warning { background: var(--tl-warning-subtle); color: var(--tl-warning-text); border-color: color-mix(in srgb, var(--tl-warning) 30%, transparent); }
.tl-badge--danger  { background: var(--tl-danger-subtle);  color: var(--tl-danger-text);  border-color: color-mix(in srgb, var(--tl-danger) 30%, transparent); }
.tl-badge--info    { background: var(--tl-info-subtle);    color: var(--tl-info-text);    border-color: color-mix(in srgb, var(--tl-info) 30%, transparent); }
.tl-badge--special { background: var(--tl-special-subtle); color: var(--tl-special-text); border-color: color-mix(in srgb, var(--tl-special) 30%, transparent); }
.tl-badge--accent  { background: var(--tl-accent-subtle);  color: var(--tl-accent-text);  border-color: color-mix(in srgb, var(--tl-accent) 30%, transparent); }

/* Pieno: per il conteggio in un menu o un contatore di notifiche. */
.tl-badge--solid { background: var(--tl-neutral); color: var(--tl-text-inverse); border-color: transparent; }
.tl-badge--solid.tl-badge--danger { background: var(--tl-danger); color: var(--tl-text-inverse); }

.tl-badge-group { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--tl-space-1); }


/* ═══════════════════════════════════════════════════════════════════════════
 * 7 · AZIONI DI RIGA                                         .tl-actions
 * ═══════════════════════════════════════════════════════════════════════════
 * Bersagli da 24px come minimo, un'etichetta accessibile su ognuno, e lo stesso
 * ordine su tutte le righe di tutti gli elenchi. Oggi le icone d'azione stanno
 * sotto ai 16px e cambiano ordine da un elenco all'altro.
 */

.tl-actions { display: inline-flex; align-items: center; gap: var(--tl-space-1); }

.tl-actions .tl-btn--icon { color: var(--tl-text-muted); }
.tl-actions .tl-btn--icon:hover { color: var(--tl-accent); background: var(--tl-accent-subtle); }
.tl-actions .tl-btn--icon.tl-btn--danger-ghost:hover { color: var(--tl-danger); background: var(--tl-danger-subtle); }

/* Un separatore prima dell'azione distruttiva: la distanza è ciò che impedisce
 * di premere "elimina" mirando a "modifica". */
.tl-actions__sep { width: 1px; height: 16px; background: var(--tl-border); margin-inline: var(--tl-space-1); flex: 0 0 auto; }


/* ═══════════════════════════════════════════════════════════════════════════
 * 8 · MESSAGGI                                    .tl-alert .tl-toast .tl-dialog
 * ═══════════════════════════════════════════════════════════════════════════
 * Sostituiscono i 320 alert() e i 134 confirm() nativi. Quelle finestre bloccano
 * la pagina, non si possono stilare, non dicono da dove arrivano e su alcuni
 * browser l'utente le può disattivare — e i confirm() sono proprio le conferme
 * prima delle operazioni irreversibili, cioè il punto in cui serve più chiarezza.
 */

/* ── In pagina, permanente ──────────────────────────────────────────────── */

/* Stessa storia del pallino dei filtri qui sopra: il `display: flex` della
   riga seguente batte il `display: none` che il browser dà a [hidden], che è
   dell'UA e ha specificità zero. Senza questa riga un avviso preparato vuoto
   nel markup e mostrato dal JavaScript quando serve resta visibile SEMPRE —
   una striscia grigia senza testo sotto il modulo. Trovato l'11/09/2026 sul
   banco di validazione foto; vale per chiunque usi `.tl-alert` con `hidden`. */
.tl-alert[hidden] { display: none; }

.tl-alert {
  display: flex;
  align-items: flex-start;
  gap: var(--tl-space-3);
  padding: var(--tl-space-3) var(--tl-space-4);
  border: 1px solid transparent;
  border-inline-start-width: 3px;
  border-radius: var(--tl-radius);
  font-size: var(--tl-text-base);
  margin-block-end: var(--tl-space-3);
  background: var(--tl-neutral-subtle);
  color: var(--tl-neutral-text);
  border-color: var(--tl-border);
  border-inline-start-color: var(--tl-neutral);
}

.tl-alert__icon { flex: 0 0 auto; font-size: 1.1em; line-height: 1.35; }
.tl-alert__body { flex: 1 1 auto; min-width: 0; }
.tl-alert__title { font-weight: var(--tl-weight-semibold); margin-block-end: var(--tl-space-1); }
.tl-alert__text { font-size: var(--tl-text-sm); }

.tl-alert__close {
  flex: 0 0 auto;
  width: var(--tl-target-min); height: var(--tl-target-min);
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0; border: 0; border-radius: var(--tl-radius);
  background: transparent; color: inherit; font-size: 16px; line-height: 1; cursor: pointer; opacity: 0.6;
}
.tl-alert__close:hover { opacity: 1; background: rgb(13 16 19 / 0.06); }
.tl-alert__close:focus-visible { outline: var(--tl-focus-ring); outline-offset: 1px; }

.tl-alert--success { background: var(--tl-success-subtle); color: var(--tl-success-text); border-inline-start-color: var(--tl-success); }
.tl-alert--warning { background: var(--tl-warning-subtle); color: var(--tl-warning-text); border-inline-start-color: var(--tl-warning); }
.tl-alert--danger  { background: var(--tl-danger-subtle);  color: var(--tl-danger-text);  border-inline-start-color: var(--tl-danger); }
.tl-alert--info    { background: var(--tl-info-subtle);    color: var(--tl-info-text);    border-inline-start-color: var(--tl-info); }

/* ── Passeggero, in un angolo ───────────────────────────────────────────── */

.tl-toasts {
  position: fixed;
  inset-block-end: var(--tl-space-4);
  inset-inline-end: var(--tl-space-4);
  z-index: var(--tl-z-toast);
  display: flex;
  flex-direction: column;
  gap: var(--tl-space-2);
  max-width: min(384px, calc(100vw - var(--tl-space-6)));
  pointer-events: none;
}

.tl-toast {
  pointer-events: auto;
  box-shadow: var(--tl-shadow-lg);
  background: var(--tl-surface);
  margin: 0;
  animation: tl-toast-in var(--tl-duration) var(--tl-ease-out);
}

.tl-toast.is-leaving { animation: tl-toast-out var(--tl-duration) var(--tl-ease) forwards; }

@keyframes tl-toast-in  { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@keyframes tl-toast-out { to { opacity: 0; transform: translateY(8px); } }

/* ── Finestra di conferma ─────────────────────────────────────────────────
 * Costruita su <dialog>: la gestione della messa a fuoco, la chiusura con Esc e
 * il blocco del resto della pagina li fa il browser. Riscriverli a mano è il
 * modo classico per ottenere una finestra da cui la tastiera non esce più. */

.tl-dialog {
  /* ⚠ `margin: auto` non è una spaziatura: è ciò che CENTRA la finestra.
     Un <dialog> modale è `position: fixed` con `inset: 0`, e lo user-agent lo
     centra dando `margin: auto` alle quattro parti. Il gestionale però carica
     `css/style.css`, che comincia con un reset `* { margin: 0 }`: quella riga
     colpisce anche i <dialog> e li incolla in alto a sinistra.
     Il difetto era di TUTTE le finestre — conferme, doppia conferma, filtri —
     ed è emerso l'11 settembre 2026 costruendo quella dei colori. */
  margin: auto;
  padding: 0;
  border: 0;
  border-radius: var(--tl-radius-xl);
  box-shadow: var(--tl-shadow-lg);
  max-width: min(448px, calc(100vw - var(--tl-space-6)));
  width: 100%;
  background: var(--tl-surface);
  color: var(--tl-text);
  font-family: var(--tl-font-sans);
  font-size: var(--tl-text-base);
}

.tl-dialog::backdrop { background: rgb(13 16 19 / 0.5); }

.tl-dialog__head  { padding: var(--tl-space-4) var(--tl-space-4) var(--tl-space-2); font-size: var(--tl-text-lg); font-weight: var(--tl-weight-semibold); }
.tl-dialog__body  { padding: 0 var(--tl-space-4) var(--tl-space-4); color: var(--tl-text-muted); font-size: var(--tl-text-sm); }
.tl-dialog__foot  { display: flex; justify-content: flex-end; gap: var(--tl-space-2); padding: var(--tl-space-3) var(--tl-space-4); background: var(--tl-surface-muted); border-block-start: 1px solid var(--tl-border-subtle); border-end-start-radius: var(--tl-radius-xl); border-end-end-radius: var(--tl-radius-xl); }

/* ── La finestra dei colori ───────────────────────────────────────────────────
 *
 * Si apre dal pulsante in fondo alla barra, fra il nome dell'operatore e
 * «Esci», ed è la seconda metà di A11: la gamma predefinita entro cui ognuno
 * può scegliere. La gamma sta in `ui/temi.css`, il comportamento in `tl.js`.
 *
 * ⚠ **I colori qui dentro non seguono il tema, e non è una svista.** Le
 * pastiglie usano i campioni `--tl-tema-<chiave>-*`, che `temi.css` dichiara
 * fuori dai blocchi dei temi proprio perché restino fermi: una pastiglia deve
 * mostrare il tema che rappresenta anche mentre ne è acceso un altro. È
 * l'unico posto del progetto in cui un colore non deve cambiare col tema.
 *
 * La finestra è un filo più larga della conferma (448px), perché tre pastiglie
 * per riga sotto i 520px diventano due righe da due più una spaiata. */
.tl-dialog.tl-temi { max-width: min(520px, calc(100vw - var(--tl-space-6))); }

.tl-temi__gamma {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--tl-space-2);
  margin-block-start: var(--tl-space-3);
}
/* Sotto i 420px le tre colonne stringono le pastiglie fino a rendere il
   ritratto illeggibile: meglio due, più alte. */
@media (max-width: 420px) { .tl-temi__gamma { grid-template-columns: repeat(2, 1fr); } }

.tl-tema {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--tl-space-1);
  padding: var(--tl-space-2);
  border: 1px solid var(--tl-border);
  border-radius: var(--tl-radius);
  background: var(--tl-surface);
  cursor: pointer;
  transition: border-color var(--tl-duration-fast) var(--tl-ease),
              box-shadow var(--tl-duration-fast) var(--tl-ease);
}
.tl-tema:hover { border-color: var(--tl-border-strong); }

/* Il pallino del radio sparisce alla vista ma NON dalla tastiera né dal lettore
   di schermo: la scelta resta un gruppo di radio vero, con le frecce che
   funzionano. È il bordo della pastiglia a dire quale è scelta. */
.tl-tema input {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden; clip-path: inset(50%);
  white-space: nowrap;
}
/* ⚠ La pastiglia scelta si marca con una CLASSE, messa da `tl.js`, e non con
   `.tl-tema:has(input:checked)`, che sarebbe la forma naturale. Il motivo è il
   vincolo di browser che `tokens.css` dichiara — Firefox 113 — e `:has()` in
   Firefox arriva solo con la 121. Il selettore fratello qui sotto, invece,
   funziona dappertutto. */
.tl-tema.is-scelto {
  border-color: var(--tl-accent);
  box-shadow: 0 0 0 1px var(--tl-accent);
}
/* L'anello di messa a fuoco va sul ritratto, non sul radio nascosto:
   altrimenti navigando con le frecce non si vedrebbe dove si è (WCAG 2.4.7). */
.tl-tema input:focus-visible ~ .tl-tema__quadro {
  outline: var(--tl-focus-ring);
  outline-offset: var(--tl-focus-offset);
}

/* Il ritratto in piccolo della pagina: il fondo neutro, due righe di testo
   finto e il pallino dell'accento. Serve a far vedere la temperatura del
   grigio, che a parole — «freddo», «caldo» — non si distingue. */
.tl-tema__quadro {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  border: 1px solid;
  border-radius: var(--tl-radius-sm);
  overflow: hidden;
}
.tl-tema__riga {
  position: absolute;
  inset-inline: 18%;
  height: 3px;
  border-radius: var(--tl-radius-full);
  background: currentColor;
  opacity: 0.28;
}
.tl-tema__riga           { inset-block-start: 26%; }
.tl-tema__riga--corta    { inset-block-start: 44%; inset-inline-end: 42%; }
.tl-tema__pallino {
  position: absolute;
  inset-block-end: 16%;
  inset-inline-start: 18%;
  width: 34%;
  height: 22%;
  border-radius: var(--tl-radius-sm);
}

.tl-tema__nome { font-size: var(--tl-text-sm); color: var(--tl-text); text-align: center; line-height: var(--tl-leading-tight); }
.tl-tema__nota { font-size: var(--tl-text-xs); color: var(--tl-text-subtle); }


/* ═══════════════════════════════════════════════════════════════════════════
 * 9 · MINUTERIE
 * ═══════════════════════════════════════════════════════════════════════════ */

/* Riquadro. Il contenitore normale di una schermata. */
/* La scheda: fondo bianco, bordo tenue, ombra sottile e raggio da 5px — la
 * STESSA resa di `.fi-section` di Filament (vedi ui/filament-tema.css), così una
 * scheda su una pagina Blade e una su una pagina Filament sono indistinguibili.
 * Prima la scheda era piatta (nessuna ombra) e con un raggio da 8px, mentre il
 * commento di filament-tema.css la dava già a 5px come riferimento: l'ombra e il
 * 5px la portano dove il disegno la voleva. */
/* ── Dove comincia e dove finisce una sezione ─────────────────────────────────
 *
 * Segnalato dal committente l'8 settembre 2026: «alcune sezioni non si capisce
 * bene dove finiscono». Il motivo era che la scheda si affidava a un bordo da
 * un pixel del colore più tenue e a un'ombra quasi invisibile: su una pagina
 * bianca, un rettangolo bianco con un bordo grigio chiarissimo non ha confini
 * che l'occhio trovi da lontano.
 *
 * Le tre cose che glieli danno, in ordine di quanto pesano:
 *
 *   1. **l'intestazione con un fondo suo.** È quella che fa più lavoro: dice
 *      dove la sezione COMINCIA, e con il piede già tinto la scheda risulta
 *      chiusa in cima e in fondo;
 *   2. **un'ombra vera** al posto di quella appena accennata, così il piano
 *      della scheda si stacca da quello della pagina;
 *   3. **il raggio grande** (12px), che è quello di Filament e della
 *      contabilità: un angolo più marcato si legge come un oggetto a sé.
 *
 * Il bordo resta a `--tl-border` e non si scurisce: scurirlo avrebbe dato un
 * reticolo di riquadri, che è il difetto da cui questo lavoro è partito. */
.tl-card {
  background: var(--tl-surface);
  border: 1px solid var(--tl-border);
  border-radius: var(--tl-radius-lg);
  box-shadow: var(--tl-shadow);
  margin-block-end: var(--tl-space-4);
}
.tl-card__head {
  display: flex;
  align-items: center;
  gap: var(--tl-space-3);
  padding: var(--tl-space-3) var(--tl-space-4);
  background: var(--tl-surface-muted);
  border-block-end: 1px solid var(--tl-border);
  border-start-start-radius: var(--tl-radius-lg);
  border-start-end-radius: var(--tl-radius-lg);
}
.tl-card__title { flex: 1 1 auto; margin: 0; font-size: var(--tl-text-base); font-weight: var(--tl-weight-semibold); }
.tl-card__body { padding: var(--tl-space-4); }
.tl-card__foot { padding: var(--tl-space-3) var(--tl-space-4); border-block-start: 1px solid var(--tl-border); background: var(--tl-surface-muted); border-end-start-radius: var(--tl-radius-lg); border-end-end-radius: var(--tl-radius-lg); }

/* Indicatore numerico della home. Il numero è un collegamento all'elenco che lo
 * produce: oggi sapere che ci sono tre verbali non porta a vederli. */
.tl-stat { display: flex; flex-direction: column; gap: var(--tl-space-1); padding: var(--tl-space-3) var(--tl-space-4); background: var(--tl-surface); border: 1px solid var(--tl-border); border-radius: var(--tl-radius); box-shadow: var(--tl-shadow-sm); text-decoration: none; color: inherit; transition: border-color var(--tl-duration-fast) var(--tl-ease), box-shadow var(--tl-duration-fast) var(--tl-ease); }
a.tl-stat:hover { border-color: var(--tl-accent); box-shadow: var(--tl-shadow); text-decoration: none; }
a.tl-stat:focus-visible { outline: var(--tl-focus-ring); outline-offset: var(--tl-focus-offset); }
.tl-stat__label { font-size: var(--tl-text-xs); color: var(--tl-text-muted); text-transform: uppercase; letter-spacing: 0.04em; }
.tl-stat__value { font-size: var(--tl-text-2xl); font-weight: var(--tl-weight-semibold); font-variant-numeric: tabular-nums; line-height: var(--tl-leading-tight); overflow-wrap: anywhere; }
.tl-stat-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(176px, 1fr)); gap: var(--tl-space-3); margin-block-end: var(--tl-space-4); }
/* Variante per le griglie con POCHI elementi.
 *
 * `auto-fill`, qui sopra, crea comunque tutte le tracce che ci stanno — anche
 * quelle vuote: tre indicatori su una scheda larga restano a 176px, ammucchiati
 * a sinistra con mezza riga vuota accanto. `auto-fit` fa collassare le tracce
 * avanzate e li allarga a riempire lo spazio. Dove gli elementi riempiono già
 * la riga, le due parole danno lo stesso risultato.
 *
 * È una VARIANTE e non la regola perché `.tl-stat-grid` compare in 113 punti di
 * 50 viste — `verbale-scheda` da sola ne ha 16, `preinserimento-vista` 15 — e
 * cambiarla per tutti sposterebbe l'aspetto di una cinquantina di pagine che
 * nessuno ha guardato. Passare tutti ad `auto-fit` resta un miglioramento vero,
 * ma merita un intervento a sé, con la verifica prima/dopo che il progetto usa
 * per il reskin. Aggiunta il 9 settembre 2026 per «I miei dati», che ha tre
 * indicatori. */
.tl-stat-grid--largo { grid-template-columns: repeat(auto-fit, minmax(176px, 1fr)); }

/* Variante per le schede in cui il valore di un indicatore è una FRASE.
 *
 * `.tl-stat` è nato per la home, dove ogni riquadro contiene un numero: da lì
 * il valore a `--tl-text-2xl` in semigrassetto, che è la misura giusta per «14»
 * e quella sbagliata per «99 · sopra tutte, compresa quella del 51». Con una
 * frase dentro, il riquadro sembra stretto due volte — perché la traccia è di
 * 176px e perché il testo è enorme.
 *
 * Le due classi si usano insieme e risolvono le due cause separatamente:
 * `--ampio` allarga le tracce e le fa collassare quando avanzano (`auto-fit`,
 * come `--largo`), `.tl-stat--testo` riporta il valore alla misura del testo
 * normale sui soli riquadri che contengono prosa, lasciando grandi quelli che
 * contengono davvero una cifra.
 *
 * Sono VARIANTI e non la regola per lo stesso motivo di `--largo`:
 * `.tl-stat-grid` compare in 113 punti di 50 viste, e `.tl-stat__value` in
 * altrettanti. Aggiunte il 10 settembre 2026 per la scheda utente di «Gestione
 * utenti», che ha otto riquadri di cui cinque descrittivi. */
.tl-stat-grid--ampio { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.tl-stat--testo .tl-stat__value { font-size: var(--tl-text-base); font-weight: var(--tl-weight-normal); line-height: var(--tl-leading-normal); }

/* ── Due modificatori per una barra di filtri con un selettore di ricerca ──
 *
 * `.tl-filters__campi` allinea i campi con `align-items: end`, cioè pareggia i
 * FONDI. Va bene finché i campi hanno tutti lo stesso testo d'aiuto sotto, o
 * nessuno: appena uno va a capo una riga in più, il suo controllo si alza
 * rispetto agli altri. Con `--campi-in-cima` si pareggiano le cime, e la
 * lunghezza dell'aiuto smette di contare — l'aiuto pende sotto, dove non
 * disturba nessuno.
 *
 * `.tl-select-picker__panel` pende dal pulsante in posizione assoluta, che è
 * giusto in una pagina e sbagliato dentro una `<dialog>`: un elemento
 * `<dialog>` nasce con `overflow: auto` per foglio di stile del browser, quindi
 * **il pannello viene ritagliato dal bordo della finestra**. Con
 * `--picker-in-linea` il pannello torna nel flusso: si apre in linea sotto il
 * pulsante, allunga la finestra invece di sfondarla, e siccome non galleggia
 * più prende un bordo al posto dell'ombra.
 *
 * Sono modificatori e non regole per lo stesso motivo di `--largo` e
 * `--ampio`: `.tl-filters__campi` è in cinquantasei pagine e
 * `.tl-select-picker__panel` serve anche alla fascia in alto e alla schermata
 * d'ingresso, dove il comportamento attuale è quello corretto. Aggiunti il 10
 * settembre 2026 per i filtri di «Gestione utenti», la prima barra di filtri
 * che contiene un selettore con ricerca. */
.tl-filters--campi-in-cima .tl-filters__campi { align-items: start; }
.tl-filters--picker-in-linea .tl-select-picker__panel {
  position: static;
  margin-block-start: var(--tl-space-1);
  max-height: 240px;
  box-shadow: none;
  border: 1px solid var(--tl-border);
}

/* La terza via, alternativa alla precedente: invece di riportare il pannello
 * nel flusso, si toglie il ritaglio alla finestra. Il pannello resta dov'era —
 * pende dal pulsante, in posizione assoluta — ed esce dal bordo senza
 * allungare la finestra di un pixel.
 *
 * ⚠ Ha un prezzo, e va saputo: così **la finestra dei filtri non scorre più**.
 *   Qui non serve, perché i filtri sono quattro e ci stanno; su una pagina con
 *   quindici filtri questo modificatore sarebbe un difetto, non una
 *   correzione. E su uno schermo molto basso l'elenco degli enti può finire
 *   sotto il bordo del viewport senza modo di raggiungerlo.
 *
 * Le due si escludono: la vista usa `--picker-in-linea` **oppure**
 * `--senza-ritaglio`, e passare dall'una all'altra è una parola sola nel
 * markup. Sono tenute entrambe di proposito, così tornare indietro non
 * richiede di riscrivere il CSS. Se il limite qui sopra diventasse un
 * problema, la strada seguente è un pannello `position: fixed` con le
 * coordinate calcolate dal JavaScript — che non viene ritagliato da nessun
 * antenato — ma va scritto con un nome proprio accanto a `initSelectPicker`,
 * che serve alla fascia in alto e alla schermata d'ingresso. */
.tl-filters--senza-ritaglio .tl-filters__pannello { overflow: visible; }

/* Il pannello, in una barra di filtri, va tenuto corto: `min(48rem, 60vh)` è
 * la misura giusta in una schermata d'ingresso, dove il selettore dell'ente è
 * il contenuto della pagina; qui è uno di quattro campi, e mezzo schermo di
 * comuni copre gli altri tre. Circa otto voci, poi scorre. */
.tl-filters--senza-ritaglio .tl-select-picker__panel { max-height: 260px; }

/* Il pulsante del selettore deve passare per una tendina come le altre.
 *
 * `.tl-select-picker__toggle` prende di suo le misure dalla pagina — `font:
 * inherit` e imbottitura in `em` — perché in `select_customer.php` l'aspetto
 * classico lo fa tre volte più grande. In una barra di filtri quella prudenza
 * si vede come un difetto: accanto a tre `<select>` il pulsante è più basso,
 * col bordo più tenue e il testo di un'altra misura. Qui si allinea al
 * controllo vero, dichiarando gli stessi cinque valori di
 * `.tl-field__control` — compresi il passaggio del mouse e l'aspetto ad
 * aperto, che imita quello al fuoco di una tendina.
 *
 * ⚠ **`border-radius: 8px` NON è ridondante: togliendolo l'aspetto cambia.**
 *   Sembra ripetere la regola base di `.tl-select-picker__toggle`, che
 *   dichiara `--tl-radius` — cioè proprio 8px — ma non è quella a vincere.
 *   `reskin.css:346` mette questo pulsante nello stesso gruppo che veste i
 *   campi **legacy** (`.form-control`, gli `input` di `.BoxRowCaption`) e gli
 *   dà `--tl-radius-sm`, 4px; quel selettore comincia per `body`, quindi è più
 *   specifico della regola base e la scavalca. Provato il 10 settembre 2026
 *   togliendo la riga: gli angoli diventano più spigolosi, non uguali.
 *
 *   È anche il motivo per cui qui si ridichiarano altezza minima, imbottitura
 *   e dimensione del testo: in `reskin.css` valgono 28px e misure da campo
 *   legacy, che sono giuste in mezzo a campi legacy e sbagliate accanto a tre
 *   `.tl-field__control`.
 *
 * Angoli **più** tondi dei campi sono stati provati a video lo stesso giorno —
 * 12px, poi 10, poi 9 — e ogni volta risultavano troppi: il pulsante deve
 * passare per una tendina, non distinguersi da quelle accanto. */
.tl-filters--picker-come-campo .tl-select-picker__toggle {
  min-height: var(--tl-control-height);
  padding: 0 var(--tl-control-padding-x);
  border-color: var(--tl-border-strong);
  border-radius: 8px;
  font-size: var(--tl-text-base);
  line-height: var(--tl-leading-normal);
}
.tl-filters--picker-come-campo .tl-select-picker__toggle:hover { border-color: var(--tl-gray-400); }
.tl-filters--picker-come-campo .tl-select-picker[open] > .tl-select-picker__toggle {
  border-color: var(--tl-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--tl-accent) 18%, transparent);
}

/* Lo stesso, per un selettore che sta in una SCHEDA invece che in una barra di
 * filtri: la scheda anagrafica di «Gestione utenti», dove l'ente è un campo in
 * mezzo agli altri dentro una `.tl-card`. Il motivo per cui serve è identico —
 * `reskin.css:346` veste il pulsante come un campo legacy, e accanto a campi
 * nuovi risulta più basso e col bordo più tenue.
 *
 * ⚠ **Le sei dichiarazioni sono duplicate da `.tl-filters--picker-come-campo`
 *   qui sopra, di proposito.** La scelta, presa il 10 settembre 2026, è stata
 *   di non toccare la regola già in uso: allargarne il selettore sarebbe stato
 *   più asciutto, ma è una modifica a codice che funziona, e la prudenza è
 *   stata preferita.
 *
 *   Il prezzo va saputo: **chi cambia un valore deve cambiarlo in due posti.**
 *   Se le due regole divergono, lo stesso pulsante avrà due aspetti diversi
 *   nella stessa pagina — nei filtri e nella scheda — e niente lo segnalerà.
 *   Il giorno in cui servisse un terzo contesto, conviene fermarsi e unire i
 *   selettori invece di aggiungere una terza copia.
 *
 *   Sul `border-radius: 8px` che sembra ridondante vale la nota del blocco
 *   qui sopra: non lo è, lo scavalca `reskin.css`. */
.tl-picker-in-scheda .tl-select-picker__toggle {
  min-height: var(--tl-control-height);
  padding: 0 var(--tl-control-padding-x);
  border-color: var(--tl-border-strong);
  border-radius: 8px;
  font-size: var(--tl-text-base);
  line-height: var(--tl-leading-normal);
}
.tl-picker-in-scheda .tl-select-picker__toggle:hover { border-color: var(--tl-gray-400); }
.tl-picker-in-scheda .tl-select-picker[open] > .tl-select-picker__toggle {
  border-color: var(--tl-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--tl-accent) 18%, transparent);
}

/* Attesa. Rispetta prefers-reduced-motion tramite il token di durata. */
.tl-spinner { display: inline-block; width: 16px; height: 16px; border: 2px solid color-mix(in srgb, currentColor 25%, transparent); border-top-color: currentColor; border-radius: 50%; animation: tl-spin 700ms linear infinite; }
@keyframes tl-spin { to { transform: rotate(360deg); } }

/* Solo per i lettori di schermo. Serve alle etichette delle azioni di riga e
 * alla parola "obbligatorio" accanto all'asterisco. */
.tl-sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }

/* Salta al contenuto: compare solo con la tastiera. Primo elemento del <body>,
 * è il criterio WCAG 2.4.1 e oggi non c'è. */
.tl-skip-link { position: absolute; inset-block-start: -100%; inset-inline-start: var(--tl-space-2); z-index: var(--tl-z-toast); padding: var(--tl-space-2) var(--tl-space-4); background: var(--tl-accent); color: var(--tl-on-accent); border-radius: 0 0 var(--tl-radius) var(--tl-radius); text-decoration: none; }
/* ⚠ Il colore va ripetuto qui, e non è una ridondanza.
 *
 * `.tl-skip-link` dichiara `color: var(--tl-on-accent)` con specificità (0,1,0).
 * Il reskin ha `body a:hover, body a:focus { color: var(--tl-text-link-hover) }`,
 * che è (0,1,2) — quindi **vince**. Risultato: mettendo il fuoco sul
 * collegamento di salto il testo diventava blu scuro sul fondo grigio scuro
 * dell'accento, **1,13:1**, cioè invisibile. Su 46 pagine.
 *
 * Il collegamento di salto esiste per chi naviga da tastiera, e si vede **solo**
 * quando ha il fuoco: era rotto esattamente nell'unico stato in cui esiste.
 * Trovato da `verifica-ui/hover.mjs` il 9 settembre 2026 — è il primo difetto
 * vero che quello strumento ha pescato da solo.
 *
 * `.tl-skip-link:focus` è (0,2,0) e batte `body a:focus`. */
.tl-skip-link:hover,
.tl-skip-link:focus { color: var(--tl-on-accent); }
.tl-skip-link:focus { inset-block-start: 0; }


/* ═══════════════════════════════════════════════════════════════════════════
 * 10 · SCHEDE                                                       .tl-tabs
 * ═══════════════════════════════════════════════════════════════════════════
 *
 * Una pagina lunga divisa in schede, con i pannelli sempre nel documento.
 *
 * ── Perché serviva un componente e non l'ennesima copia ─────────────────────
 * Fino all'8 settembre 2026 nel progetto convivevano TRE modi di fare la stessa
 * cosa: `data-toggle="tab"` di Bootstrap (che vive di jQuery, e quale jQuery
 * arrivi dipende dalla pagina), la navigazione lato server `?tab=N` dei
 * Parametri (una richiesta al server per cambiare scheda, e i dati non ancora
 * salvati se ne vanno), e le classi locali `ver-schede` della statistica dei
 * verbali. Nessuno dei tre è riusabile e tutti e tre si comportano diversamente
 * con la tastiera.
 *
 * ── I pannelli restano nel documento ────────────────────────────────────────
 * Si nascondono con l'attributo `hidden`, non si tolgono. È la differenza che
 * conta su una pagina di modifica: i campi di una scheda non guardata **vengono
 * comunque inviati** col modulo, quindi un salvataggio scrive tutto e non solo
 * quello che si stava guardando. Ed è anche la ragione per cui il pulsante
 * «Salva» può stare nella barra delle schede e non dentro un pannello.
 *
 * ── Marcatura ───────────────────────────────────────────────────────────────
 *     <div class="tl-tabs" data-tl-tabs>
 *       <div class="tl-tabs__list" role="tablist">
 *         <button type="button" class="tl-tabs__tab" role="tab"
 *                 id="scheda-x" aria-controls="pannello-x">Titolo</button>
 *       </div>
 *       <div class="tl-tabs__actions">…</div>          facoltativa
 *     </div>
 *     <div class="tl-tabs__panel" id="pannello-x" role="tabpanel"
 *          aria-labelledby="scheda-x" data-tl-tabs-di="…">…</div>
 *
 * `aria-selected`, `tabindex` e `hidden` li scrive tl.js: la pagina non deve
 * dire quale scheda è attiva, e senza JavaScript si vedono tutti i pannelli —
 * che è il verso giusto in cui rompersi.
 *
 * `.tl-tabs__panel` non ha regole qui e non è una dimenticanza: un pannello è
 * un pezzo di pagina come un altro, e a nasconderlo basta `hidden`. La classe
 * serve a tl.js per ritrovare il pannello di un campo non valido. */

.tl-tabs {
  display: flex;
  align-items: flex-end;
  gap: var(--tl-space-3);
  margin-block-end: var(--tl-space-4);
  border-block-end: 1px solid var(--tl-border);
}

/* Le schede scorrono in orizzontale quando non ci stanno, invece di andare a
 * capo: una fila di schede su due righe non si legge più come una fila. */
.tl-tabs__list {
  display: flex;
  flex: 1 1 auto;
  gap: var(--tl-space-1);
  overflow-x: auto;
  scrollbar-width: thin;
}

.tl-tabs__tab {
  flex: 0 0 auto;
  min-height: var(--tl-target-comfort);
  padding: var(--tl-space-2) var(--tl-space-3);
  border: 0;
  border-block-end: 2px solid transparent;
  background: transparent;
  color: var(--tl-text-muted);
  font: inherit;
  font-weight: var(--tl-weight-medium);
  white-space: nowrap;
  cursor: pointer;
  transition: color var(--tl-duration-fast) var(--tl-ease),
              border-color var(--tl-duration-fast) var(--tl-ease);
}

.tl-tabs__tab:hover { color: var(--tl-text); }
.tl-tabs__tab:focus-visible { outline: var(--tl-focus-ring); outline-offset: calc(var(--tl-focus-offset) * -1); }

.tl-tabs__tab[aria-selected="true"] {
  color: var(--tl-text);
  border-block-end-color: var(--tl-accent);
}

/* Il pallino col numero: quante udienze, quanti documenti. Sta nella scheda
 * perché è l'informazione che decide se vale la pena aprirla. */
.tl-tabs__count {
  display: inline-block;
  margin-inline-start: var(--tl-space-1);
  padding: 0 var(--tl-space-1);
  min-width: 1.4em;
  border-radius: var(--tl-radius-full);
  background: var(--tl-surface-muted);
  color: var(--tl-text-muted);
  font-size: var(--tl-text-xs);
  font-variant-numeric: tabular-nums;
  text-align: center;
}

.tl-tabs__tab[aria-selected="true"] .tl-tabs__count { background: var(--tl-accent-subtle); color: var(--tl-accent-text); }

/* Le azioni della pagina — «Salva» — accanto alle schede e non in fondo: in una
 * pagina a schede il fondo è quello del pannello aperto, e cambia di scheda in
 * scheda. */
.tl-tabs__actions {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  gap: var(--tl-space-2);
  padding-block-end: var(--tl-space-2);
}

@media (max-width: 700px) {
  .tl-tabs { flex-wrap: wrap; }
  .tl-tabs__actions { padding-block-start: var(--tl-space-2); }
}


/* ── Organizza menu: lo spostamento di una voce ────────────────────────────
   Due classi sole, usate da configurazione/organizza-menu.blade.php. Stanno
   qui e non in un <style> dentro la vista perché il gestionale ha un foglio
   condiviso e uno solo: la prima eccezione diventa la regola per la pagina dopo.

   ⚠ La specificità è calcolata, non abbondante per prudenza: pareggia quella
   delle righe zebrate (riga 795) e vince per posizione. Spostare questo blocco
   più in alto nel file lo farebbe smettere di funzionare su una riga su due.

   Il fondo della riga in movimento è quello d'avviso e non l'accento, perché
   l'accento è già il colore del passaggio del mouse sulle righe: sarebbe lo
   stesso colore per «questa riga è sotto il puntatore» e «questa riga sta
   cambiando posto». */

.tl-table tbody tr.tl-riga-in-movimento > td {
  background: var(--tl-warning-subtle);
}

/* Il punto d'inserimento non è un dato, è un posto libero: fondo neutro e
   nessun bordo di riga, così non si conta fra le voci. La cornice tratteggiata
   la porta il pulsante che ci sta dentro. */
.tl-table tbody tr.tl-riga-inserimento > td {
  padding-block: 0;
  background: var(--tl-surface);
  border-block-end: 0;
}

.tl-riga-inserimento .tl-btn {
  border: 1px dashed var(--tl-accent);
  color: var(--tl-accent-text);
}

/* L'intestazione di queste tabelle **non** resta appiccicata: scorre via come il
   resto della pagina, passando sotto la barra in alto e sotto il cartello dello
   spostamento.

   Una classe nuova e non una correzione di `.tl-table thead th`, che è
   appiccicata su ogni tabella del gestionale: quella regola corrisponde già a
   tutte le tabelle esistenti, e toccarla si vedrebbe su tutte le pagine
   migrate. Questa invece non incontra nessun elemento finché qualcuno non le
   mette addosso la classe, e oggi ce l'hanno le due tabelle di
   configurazione/organizza-menu.blade.php.

   Diventando `static`, lo `z-index` che la regola generale porta con sé smette
   di avere effetto — vale solo per gli elementi posizionati — e il cartello,
   che è appiccicato, passa davanti senza bisogno d'altro. */
.tl-table--intestazione-scorre thead th {
  position: static;
}

/* Il cartello dello spostamento resta appeso in alto: la tabella è lunga, e un
   «Annulla» che si raggiunge solo risalendo non è una via d'uscita.

   Si ferma **sotto la barra in alto**, non al bordo della finestra: `.tl-topbar`
   è a sua volta appiccicata a `top: 0` e ha uno `z-index` più alto, quindi un
   cartello a `--tl-space-2` le scorrerebbe sotto e resterebbe coperto per
   quaranta pixel. La misura viene dalla variabile della barra e non da un
   numero, così se la barra cambia altezza il cartello la segue. */
.tl-alert--in-movimento {
  position: sticky;
  inset-block-start: calc(var(--tl-topbar-height) + var(--tl-space-2));
  z-index: 2;
}


/* Titoli di sezione. */
.tl-h1 { font-size: var(--tl-text-2xl); font-weight: var(--tl-weight-semibold); line-height: var(--tl-leading-tight); margin: 0 0 var(--tl-space-3); }
.tl-h2 { font-size: var(--tl-text-xl); font-weight: var(--tl-weight-semibold); line-height: var(--tl-leading-tight); margin: var(--tl-space-5) 0 var(--tl-space-2); }
.tl-h3 { font-size: var(--tl-text-lg); font-weight: var(--tl-weight-semibold); margin: var(--tl-space-4) 0 var(--tl-space-2); }

/* Le poche utilità che servono davvero. Non è una libreria di utility class:
 * se l'elenco cresce, vuol dire che manca un componente. */
.tl-stack   { display: flex; flex-direction: column; gap: var(--tl-space-3); }
.tl-row     { display: flex; align-items: center; gap: var(--tl-space-3); flex-wrap: wrap; }
.tl-row--end { justify-content: flex-end; }

/* Una riga di comandi staccata da ciò che segue.
 *
 * `.tl-row` non ha margini, e una riga appoggiata sopra un `.tl-table-wrap` gli
 * finisce contro: il pulsante «Nuovo …» tocca il bordo della tabella. La
 * struttura si ripete identica in cinque pagine — noleggi, preavvisi,
 * preinserimenti, ricorsi e gestione utenti — ma l'11 settembre 2026 si è
 * deciso di **staccare solo «Gestione utenti»** e di affrontare le altre su
 * segnalazione degli operatori. Questo modificatore esiste per quello: è
 * additivo, e chi lo userà altrove lo farà di proposito. */
.tl-row--staccata { margin-block-end: var(--tl-space-4); }
.tl-spacer  { flex: 1 1 auto; }
.tl-muted   { color: var(--tl-text-muted); }
.tl-nowrap  { white-space: nowrap; }
.tl-num     { font-variant-numeric: tabular-nums; text-align: end; }

/* ── L'avviso di CKEditor, ancorato dove si vede ────────────────────────────
 *
 * `inc/header.php` carica CKEditor 4.11.1 da una CDN su **tutte** le pagine, e
 * la libreria si disegna da sola un riquadro rosso — «This CKEditor 4.11.1
 * version is not secure» — che appartiene a lei, non a noi.
 *
 * Il riquadro non va nascosto, perché quello che dice è vero: quella versione è
 * del 2018 e ha vulnerabilità note. Va però **ancorato**, perché così com'è non
 * si legge e sembra un guasto: CKEditor gli scrive `top` nell'attributo style
 * ricalcolandolo a ogni scorrimento, e a pagina non scorsa il valore è
 * negativo. Misurato: `top: -279px` sull'elenco verbali (invisibile) e
 * `top: -63px` sul cruscotto, dove restano dieci pixel di striscia rossa in
 * cima allo schermo — che è esattamente il modo in cui un avviso vero si fa
 * scambiare per un errore di disegno.
 *
 * Il bottone di chiusura ce l'ha già; `tl.js` si ricorda che è stato premuto,
 * altrimenti l'avviso tornerebbe su ognuna delle 291 pagine.
 *
 * deroga: CKEditor scrive `position`, `top` e `left` nell'attributo style e li
 * riscrive a ogni evento di scorrimento. Nessuna specificità batte un attributo
 * style, e un valore posato da JavaScript verrebbe sovrascritto al primo
 * scorrimento — provato. Resta il foglio di stile, che vince e non va rincorso.
 *
 * La correzione vera non è questa: è non caricare CKEditor sulle 287 pagine che
 * non aprono nessun editor. È il registro degli asset di `boot.php`, oggi
 * vuoto. */
.cke_notifications_area {
  position: fixed !important;
  inset-block-start: auto !important;
  inset-block-end: var(--tl-space-3) !important;
  top: auto !important;
  inset-inline-start: auto !important;
  inset-inline-end: var(--tl-space-3) !important;
  left: auto !important;
  z-index: var(--tl-z-toast) !important;
}

/* La X è stata premuta: la classe la mette `tl.js` sulla radice, e sta lì e non
 * sull'avviso perché l'avviso, quando la pagina si carica, non esiste ancora. */
.tl-cke-muto .cke_notifications_area { display: none; }
