/* ===== Spin (icona in corso, es. purge Cloudflare) ===== */
@keyframes spin{to{transform:rotate(360deg)}}
.spin{display:inline-block;animation:spin .8s linear infinite}

/* ===== Buttons ===== */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:.4rem;font-family:inherit;font-size:var(--fs);font-weight:600;line-height:1.2;padding:.55rem 1.1rem;border:1px solid transparent;border-radius:var(--radius);cursor:pointer;text-decoration:none;transition:background-color .15s,border-color .15s,color .15s,transform .1s;white-space:nowrap}
.btn:hover{text-decoration:none}
.btn:active{transform:scale(.98)}
.btn:disabled,.btn.disabled{opacity:.55;cursor:not-allowed}
.btn-sm{font-size:var(--fs-sm);padding:.35rem .7rem;border-radius:var(--radius-sm)}
/* Il testo resta bianco: e' il fondo a scendere di un passo. Il bianco sull'ambra
   #d97706 da' 3.19:1, sotto AA; su --accent-solid ne da' 5.02:1, ed e' lo stesso
   ambra, solo piu' profondo. */
.btn-primary{background:var(--accent-solid);color:var(--text-on-accent)}
.btn-primary:hover{background:var(--accent-solid-hover);color:var(--text-on-accent)}
.btn-secondary{background:var(--surface-2);color:var(--text);border-color:var(--border)}
.btn-secondary:hover{background:var(--border);color:var(--text)}
.btn-outline-secondary{background:transparent;color:var(--muted);border-color:var(--border)}
.btn-outline-secondary:hover,.btn-outline-secondary.active{background:var(--surface-2);color:var(--text)}
.btn-outline-light{background:transparent;color:var(--muted);border-color:var(--border)}
.btn-outline-light:hover{background:var(--surface-2);color:var(--text)}
.btn-danger{background:var(--danger-solid);color:#fff}
.btn-danger:hover{filter:brightness(1.12);color:#fff}
.btn-outline-danger{background:transparent;color:var(--danger-ink);border-color:var(--danger)}
.btn-outline-danger:hover{background:var(--danger-bg);color:var(--danger-ink)}
.btn-outline-info{background:transparent;color:var(--info-ink);border-color:var(--info)}
.btn-outline-info:hover{background:var(--info-bg);color:var(--info-ink)}
.btn-link{background:transparent;border:none;color:var(--accent-text);padding:.25rem}
.btn-link:hover{color:var(--accent-text-hover);text-decoration:underline}
.btn-close{appearance:none;-webkit-appearance:none;border:0;background:transparent;color:var(--muted);font-size:1.25rem;line-height:1;cursor:pointer;padding:.15rem .35rem;border-radius:var(--radius-sm)}
.btn-close::before{content:"\00D7"}
.btn-close:hover{color:var(--text);background:var(--surface-2)}

/* ===== Card =====
   La card e' il livello piu' pesante dei tre, e va usata solo quando serve
   davvero contenere materiale eterogeneo o far galleggiare qualcosa sopra il
   resto. Per un gruppo di campi basta .section; per un controllo solo, .setting.
   Una cornice attorno a un interruttore pesa piu' di cio' che contiene. */
.card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg);box-shadow:var(--shadow-sm)}
.card-header{padding:.85rem 1.15rem;border-bottom:1px solid var(--border);font-weight:600;display:flex;align-items:center}
.card-body{padding:1.15rem}

/* ===== Section — un gruppo di campi correlati, senza cornice =====
   La gerarchia la fanno il titolo e lo spazio, non un filetto: con il filetto,
   un titolo seguito dall'intestazione di una tabella si legge come la prima riga
   di quella tabella. Piu' aria sopra il titolo che sotto: il distacco appartiene
   alla sezione che comincia, non a quella finita. */
.section{margin-bottom:2.5rem}
.section:last-child{margin-bottom:0}
/* Piu' sezioni dentro un form solo (credenziali + archivio, salvate insieme):
   l'ultima azzera il proprio margine per la regola qui sopra, ma il form non e'
   l'ultimo elemento della pagina — e cio' che segue gli finiva addosso. Il
   margine appartiene a chi finisce, non a chi comincia. */
form > .section:last-child{margin-bottom:2.5rem}
form:last-child > .section:last-child{margin-bottom:0}
.section-head{display:flex;align-items:baseline;justify-content:space-between;gap:1rem;
  margin-bottom:.9rem}
.section-head h2{font-size:var(--fs-lg);margin:0;font-weight:600}
.section-head .section-hint{font-size:var(--fs-sm);color:var(--muted)}

/* ===== Setting — un controllo e cio' che serve per capirlo =====
   Etichetta e spiegazione a sinistra, controllo a destra. Nessun filetto: sono
   poche righe e le separa il loro stesso respiro. */
.setting{display:flex;align-items:flex-start;justify-content:space-between;gap:1.5rem;
  padding:.75rem 0}
.setting:first-child{padding-top:0}
.setting-text{min-width:0}
.setting-text strong{display:block;font-weight:600;font-size:var(--fs)}
.setting-text span{display:block;color:var(--muted);font-size:var(--fs-sm);margin-top:.15rem}
.setting-control{flex:0 0 auto;padding-top:.15rem}
.setting-control .form-check{margin:0}

/* ===== Credenziali =====
   Una chiave configurata non viene rimandata al browser: il campo e' vuoto e il
   segnaposto ne mostra la coda. L'occhio sta dentro il campo, come quello delle
   password, perche' fa la stessa promessa — mostrami cosa c'e' qui. */
.credential{position:relative}
.credential .form-control{padding-right:2.75rem}
.credential-eye{position:absolute;top:50%;right:.35rem;transform:translateY(-50%);
  display:flex;align-items:center;justify-content:center;width:2rem;height:2rem;
  color:var(--muted);background:none;border:0;border-radius:var(--radius-sm);cursor:pointer}
.credential-eye:hover{color:var(--text);background:var(--surface-2)}

.credential-dialog{max-width:min(32rem,calc(100vw - 2rem))}

/* Cadenza dei task: cinque campi stretti in fila, che vanno a capo da soli
   quando non ci stanno. I preset sopra, perche' nove volte su dieci la
   risposta e' una di quelli. */
.cron-dialog{max-width:min(40rem,calc(100vw - 2rem))}
.cron-presets{display:flex;flex-wrap:wrap;gap:.5rem;margin:0 0 1rem}
.cron-campi{display:flex;flex-wrap:wrap;gap:.75rem;margin-bottom:1rem}
.cron-campi > div{flex:1 1 6rem;min-width:5.5rem}
.cron-campi input{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;text-align:center}
.credential-dialog-title{font-size:1.075rem;margin:0 0 .5rem}
.credential-ask{margin-bottom:1rem}
.credential-error{margin:.5rem 0 0;font-size:var(--fs-sm);color:var(--danger-ink)}
/* Il valore sta qui, non nel campo: nel campo tornerebbe al server al primo
   submit e finirebbe nell'old input dei flash. Va a capo perche' una chiave
   lunga non deve allargare il dialogo. */
.credential-shown{display:flex;align-items:center;gap:.75rem;margin-bottom:1rem;
  padding:.6rem .75rem;background:var(--surface-2);border-radius:var(--radius)}
.credential-shown code{flex:1 1 auto;background:none;padding:0;color:var(--text);
  font-size:var(--fs-sm);word-break:break-all}

/* ===== Filtri di tabella =====
   Un filtro non e' contenuto: e' il comando dell'elenco che sta sotto. Sta sopra la
   tabella, su una riga sola, senza titolo (ripeterebbe la parola del bottone che ha
   accanto) e senza cornice. Prima stavano su una griglia a 12 colonne, che con cinque
   campi produceva quattro righe.
   Ogni campo e' una colonna che cresce dal proprio minimo: e' il wrap del flex a
   mandare a capo, non una media query, quindi il punto in cui la barra si spezza
   dipende dallo spazio disponibile e non da una soglia decisa a tavolino — e la stessa
   barra funziona a tre filtri come a sei.
   Il basis e' in rem: se una lingua allunga le etichette, i campi si stringono da soli
   invece di sfondare la riga. */
.filters{--filter-gap:1rem;--filter-min:10.5rem;
  display:flex;flex-wrap:wrap;align-items:flex-end;gap:.75rem var(--filter-gap);margin-bottom:1.5rem}
.filter{display:flex;flex-direction:column;flex:1 1 10rem;min-width:8rem}
.filter .form-label{margin-bottom:.35rem}
/* Chi porta i valori piu' lunghi (le select di un elenco) prende doppia quota. */
.filter-grow{flex-grow:2}
/* Chi ha un'interfaccia nativa parte da piu' in alto e non scende sotto il suo
   pavimento: un date-input, sotto una certa misura, il browser lo taglia. Cresce
   come gli altri — impedirglielo lo lasciava a meta' riga quando resta da solo,
   sugli schermi stretti. Il vincolo e' il minimo, non il massimo. */
.filter-min{flex:1 1 var(--filter-min);min-width:9.5rem}
/* Due campi che si leggono solo insieme — gli estremi di un intervallo — sono un
   elemento solo della barra: vanno a capo insieme, perche' un "Al" separato dal suo
   "Dal" sembra una condizione a se'. Si spezzano al proprio interno soltanto quando
   nemmeno loro due ci stanno: allora e' il wrap interno a dividerli, e non c'e' una
   soglia da scegliere. */
.filter-pair{display:flex;flex-wrap:wrap;gap:.75rem var(--filter-gap);
  /* La coppia e' larga quanto i suoi due campi piu' il vuoto in mezzo, e va detto:
     con basis `auto` il browser la misura sul contenuto intrinseco dei due input,
     che e' piu' stretto del loro flex-basis — e i due andrebbero a capo dentro la
     coppia anche con spazio in abbondanza fuori. */
  flex:1 1 calc(var(--filter-min) * 2 + var(--filter-gap))}
/* I bottoni restano della loro misura e finiscono in coda ai campi: sono la fine
   della frase, non un'altra riga. */
.filter-actions{display:flex;gap:.5rem;flex:0 0 auto}

/* ===== Autocomplete — suggerimenti su un campo di testo =====
   Il JS avvolge SOLO il campo (non l'intera colonna .filter, che porta anche
   l'etichetta) in questo contenitore, cosi' la lista si ancora al campo e non
   alla colonna. Larghezza del campo, non di piu': un suggerimento e' un
   completamento del testo digitato, non un pannello a se'. */
.ac{position:relative}
.ac-list{position:absolute;z-index:var(--z-dropdown,50);top:calc(100% + .25rem);left:0;right:0;
  max-height:16rem;overflow-y:auto;margin:0;padding:.35rem;list-style:none;
  background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);
  box-shadow:0 16px 48px rgba(0,0,0,.28)}
.ac-list[hidden]{display:none}
.ac-list li{padding:.5rem .65rem;font-size:var(--fs-sm);color:var(--text);
  border-radius:var(--radius-sm);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ac-list li.is-active,.ac-list li:hover{background:var(--accent-bg);color:var(--accent-text)}
/* Il contenitore e' largo quanto il campo, che nella barra dei filtri puo'
   essere stretto: sotto i 768px il campo cresce (.filter-grow va a piena
   riga), e la lista lo segue senza mai uscire dalla finestra — right:0
   insieme a left:0 la tiene dentro qualunque sia la larghezza del campo. */

/* ===== Selettore di periodo =====
   Un periodo e' una cosa sola, e chiederne gli estremi in due caselle separate
   obbliga a pensarli separati. Qui e' un controllo unico: i periodi frequenti
   a sinistra, il calendario a destra, e in fondo cio' che si sta scegliendo.
   Prende il posto di un `.filter-pair` di due `<input type="date">`, che restano
   nel markup servito e valgono ancora senza JavaScript. */
.daterange{position:relative;flex:1 1 14rem}
/* Il trigger si legge come un campo, perche' e' un campo: stessa cornice, stesso
   fondo, stessa altezza degli altri filtri della barra. */
.daterange-trigger{display:flex;align-items:center;gap:.5rem;width:100%;
  padding:.6rem .85rem;font-family:inherit;font-size:var(--fs);text-align:left;
  color:var(--muted);background:var(--surface);border:1px solid var(--border);
  border-radius:var(--radius);cursor:pointer;transition:border-color .15s,box-shadow .15s}
.daterange-trigger:hover{border-color:var(--accent)}
.daterange-trigger[aria-expanded="true"]{border-color:var(--accent);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 22%,transparent)}
/* A periodo scelto l'inchiostro diventa quello del testo: un filtro attivo non
   si legge come un segnaposto. */
.daterange.is-set .daterange-trigger{color:var(--text)}
.daterange-trigger .bi-calendar3{color:var(--accent-text);flex:0 0 auto}
.daterange-value{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.daterange-caret{flex:0 0 auto;font-size:.7rem;opacity:.6}

/* Si apre verso SINISTRA: allineato al bordo destro del campo. Aprendo a destra,
   un pannello da 667px attaccato a un filtro che sta a meta' barra usciva dalla
   finestra — misurato a 900px: 203px fuori, piu' scorrimento orizzontale della
   pagina. Quando nemmeno a sinistra ci sta, e' il JS ad agganciarlo al bordo. */
.daterange-popup{position:absolute;z-index:var(--z-dropdown,50);top:calc(100% + .35rem);right:0;
  display:flex;flex-direction:column;
  background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg);
  box-shadow:0 16px 48px rgba(0,0,0,.28)}
/* Il pannello non si comprime sotto la sua misura — un calendario stretto non si
   legge — ma solo dove c'e' spazio per non farlo: sotto la soglia diventa un foglio
   a tutta larghezza, e li' un minimo rigido sfonderebbe lo schermo. */
@media (min-width:768px){ .daterange-popup{min-width:max-content} }
.daterange-popup[hidden]{display:none}
.daterange-body{display:flex}

/* I periodi frequenti sono la prima cosa: la maggior parte delle volte la
   risposta e' li' e il calendario non serve aprirlo nemmeno. */
.daterange-presets{display:flex;flex-direction:column;gap:.15rem;flex:0 0 auto;
  padding:.75rem;border-right:1px solid var(--border)}
.daterange-presets button{padding:.4rem .7rem;font-family:inherit;font-size:var(--fs-sm);
  text-align:left;white-space:nowrap;color:var(--text);background:none;border:0;
  border-radius:var(--radius-sm);cursor:pointer}
.daterange-presets button:hover{background:var(--surface-2)}
.daterange-presets button.is-active{background:var(--accent-bg);color:var(--accent-text);font-weight:600}

.daterange-cal{position:relative;padding:.75rem}
/* Le frecce stanno sopra i titoli dei mesi, agli estremi: con due mesi affiancati
   una freccia per mese direbbe che scorrono separatamente, e non e' cosi'. */
.daterange-nav{position:absolute;top:.75rem;left:.75rem;right:.75rem;
  display:flex;justify-content:space-between;pointer-events:none}
.daterange-step{pointer-events:auto;display:flex;align-items:center;justify-content:center;
  width:1.75rem;height:1.75rem;color:var(--muted);background:none;border:0;
  border-radius:var(--radius-sm);cursor:pointer}
.daterange-step:hover{color:var(--text);background:var(--surface-2)}
.daterange-months{display:flex;gap:1.25rem}
.daterange-month{flex:0 0 auto}
.daterange-title{height:1.75rem;display:flex;align-items:center;justify-content:center;
  font-size:var(--fs-sm);font-weight:600;text-transform:capitalize}
.daterange-grid{display:grid;grid-template-columns:repeat(7,2rem);gap:2px;margin-top:.35rem}
.daterange-dow{display:flex;align-items:center;justify-content:center;height:1.6rem;
  font-size:.7rem;font-weight:600;color:var(--muted);text-transform:uppercase}
.daterange-day{height:2rem;font-family:inherit;font-size:var(--fs-sm);color:var(--text);
  background:none;border:0;border-radius:var(--radius-sm);cursor:pointer}
/* Il passaggio del mouse non deve cancellare la selezione: `:hover` ha specificita'
   maggiore di `.is-edge`, e su una cella scelta rimetteva il fondo grigio sotto il
   testo bianco — 1.03:1, cioe' illeggibile. Dentro l'intervallo la banda resta e si
   scurisce, invece di aprirsi un buco grigio in mezzo. */
.daterange-day:hover:not(:disabled):not(.is-edge){background:var(--surface-2)}
.daterange-day.is-inside:hover{background:color-mix(in srgb,var(--accent) 20%,transparent)}
.daterange-day:disabled{color:var(--muted);opacity:.45;cursor:default}
/* Oggi si segnala con un filetto sotto, non con un fondo: il fondo e' il
   linguaggio della selezione, e due significati sullo stesso segno si confondono. */
.daterange-day.is-today{box-shadow:inset 0 -2px 0 var(--accent)}
.daterange-day.is-inside{background:var(--accent-bg);border-radius:0}
.daterange-day.is-edge{background:var(--accent-solid);color:var(--text-on-accent);font-weight:600}

.daterange-footer{display:flex;align-items:center;justify-content:space-between;gap:1rem;
  padding:.6rem .75rem;border-top:1px solid var(--border)}
.daterange-sel{font-size:var(--fs-sm);color:var(--muted)}
.daterange-buttons{display:flex;gap:.5rem;flex:0 0 auto}

/* Velo del foglio mobile: isola la scelta dal resto della pagina. Uno solo per
   documento, condiviso da tutti i selettori, creato dal JS al primo bisogno. */
.daterange-backdrop{position:fixed;inset:0;z-index:var(--z-backdrop);background:rgba(0,0,0,.45)}

/* Sotto la soglia il pannello smette di pendere dal campo: sale dal bordo inferiore
   dello schermo, a tutta larghezza, dove arriva il pollice. Un pannello sospeso
   sotto un campo, con la pagina che scorre dietro, su un telefono si usa male.
   Il calendario e' un mese solo (lo decide il JS) e i periodi frequenti passano
   sopra in orizzontale: affiancati toglierebbero al mese lo spazio per leggersi. */
@media (max-width:767.98px){
  .daterange-popup{position:fixed;top:auto;right:0;bottom:0;left:0;
    max-height:85vh;overflow-y:auto;
    border-width:1px 0 0;border-radius:var(--radius-lg) var(--radius-lg) 0 0;
    box-shadow:0 -8px 32px rgba(0,0,0,.35)}
  .daterange-body{flex-direction:column}
  .daterange-presets{flex-direction:row;flex-wrap:wrap;
    border-right:0;border-bottom:1px solid var(--border)}
  .daterange-months{gap:0}
  .daterange-month{width:100%}
  /* Le colonne si dividono la larghezza dello schermo invece di stare a 2rem fisse,
     e le celle crescono a 2.75rem: sotto i 44px un bersaglio si sbaglia col dito. */
  .daterange-grid{grid-template-columns:repeat(7,1fr)}
  .daterange-day{height:2.75rem}
  .daterange-dow{height:2rem}
  /* Il footer resta a vista mentre il foglio scorre: Applica non deve andarsi a
     cercare in fondo. */
  .daterange-footer{position:sticky;bottom:0;background:var(--surface)}
  .daterange-buttons{flex:1 1 auto}
  .daterange-buttons .btn{flex:1 1 auto}
}

/* Percorso di file come didascalia sotto il nome: monospaziato perche' e' un
   percorso, senza lo sfondo di <code> che in tabella disegna una barra per riga. */
.file-path{display:block;font-family:var(--font-mono);font-size:.72rem;color:var(--muted);
  word-break:break-all;margin-top:.15rem}

/* ===== Confirm dialog — la conferma nel linguaggio del pannello =====
   Il confirm() del browser e' l'unica finestra del pannello che non appartiene al
   pannello: font di sistema, colori di sistema, il nome del dominio in cima.
   <dialog> nativo perche' focus, Esc e inertizzazione del resto della pagina sono
   gia' del browser: qui si aggiunge solo la pelle. */
.confirm-dialog{max-width:min(28rem,calc(100vw - 2rem));padding:0;color:var(--text);
  background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg);
  box-shadow:0 16px 48px rgba(0,0,0,.28)}
.confirm-dialog::backdrop{background:rgba(9,9,11,.55)}
.confirm-dialog form{padding:1.5rem;margin:0}
.confirm-dialog-text{margin:0 0 1.35rem;font-size:var(--fs-lg);line-height:1.45;text-wrap:pretty}
.confirm-dialog-actions{display:flex;justify-content:flex-end;gap:.6rem;flex-wrap:wrap}
/* La conferma di un'azione distruttiva e' rossa: il bottone che l'ha chiesta lo
   dichiara, e il dialogo gli va dietro invece di dire sempre la stessa cosa. */
.confirm-dialog[data-tone="danger"] [data-confirm-ok]{background:var(--danger-solid);color:#fff}
.confirm-dialog[data-tone="danger"] [data-confirm-ok]:hover{filter:brightness(1.12)}
/* Entrata breve: il dialogo interrompe un'azione, non e' un momento da guardare.
   L'uscita non si anima — chi ha deciso vuole vedere subito il risultato. */
@keyframes confirm-in{from{opacity:0;transform:translateY(-.5rem) scale(.98)}to{opacity:1;transform:none}}
.confirm-dialog[open]{animation:confirm-in .16s cubic-bezier(.22,1,.36,1)}
.confirm-dialog[open]::backdrop{animation:confirm-in .16s ease-out}
@media (prefers-reduced-motion:reduce){
  .confirm-dialog[open],.confirm-dialog[open]::backdrop{animation:none}
}

/* ===== Metriche — numeri in linea, non quattro scatole =====
   Quattro conteggi in quattro card identiche sono il template hero-metric: su uno
   schermo grande diventavano riquadri di 874x79px per contenere un numero e una
   parola. Qui stanno su una riga sola, separati da un filo verticale. */
.metrics{display:flex;flex-wrap:wrap;gap:0;background:var(--table-bg);
  border:1px solid var(--table-border);border-radius:var(--radius-lg);overflow:hidden}
.metric{flex:1 1 12rem;padding:1.15rem 1.35rem;border-left:1px solid var(--table-border)}
.metric:first-child{border-left:0}
.metric-value{font-size:1.75rem;font-weight:700;line-height:1.1;font-variant-numeric:tabular-nums}
.metric-label{display:flex;align-items:center;gap:.4rem;color:var(--muted);font-size:var(--fs-sm);margin-top:.25rem}
@media (max-width:575.98px){
  /* In colonna il filo verticale non separa piu' niente: passa sopra. */
  .metric{flex-basis:100%;border-left:0;border-top:1px solid var(--table-border)}
  .metric:first-child{border-top:0}
}

/* ===== Record list — un elenco di schede che resta UN blocco =====
   Le sessioni attive erano una card per riga: N scatole staccate, cioe' la griglia
   di card identiche che il sistema evita. Qui l'elenco e' un solo blocco, con la
   stessa superficie delle tabelle, e le voci si separano per banda come le righe. */
.record-list{background:var(--table-bg);border:1px solid var(--table-border);
  border-radius:var(--radius-lg);overflow:hidden}
.record{display:flex;justify-content:space-between;align-items:flex-start;gap:1rem;padding:1rem 1.15rem}
.record:nth-child(even){background:var(--row-alt)}
.record-main{display:flex;gap:1rem;min-width:0}
.record-title{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;margin-bottom:.25rem;font-weight:600}
.record-meta{display:flex;flex-wrap:wrap;gap:.25rem 1rem;color:var(--muted);font-size:var(--fs-sm)}
.record-meta i{margin-right:.25rem}
/* La voce corrente si segnala con l'accento sul filo sinistro dell'icona, non con
   una cornice colorata attorno a tutto il blocco. */
.record-icon{width:42px;height:42px;min-width:42px;border-radius:50%;display:flex;
  align-items:center;justify-content:center;background:var(--surface-2);color:var(--muted);font-size:1.1rem}
.record-current .record-icon{background:var(--accent-solid);color:var(--text-on-accent)}

/* La coda di un registro: righe lunghe che non devono andare a capo, o si
   perde l'allineamento fra data e messaggio. Scorre per conto suo. */
.log-tail{background:var(--surface-2);border:1px solid var(--border);border-radius:var(--radius);
  padding:.9rem 1rem;margin:0;overflow:auto;max-height:32rem;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.78rem;line-height:1.55;
  white-space:pre;color:var(--text)}

/* ===== Elenco apribile (task periodici, versioni del changelog) =====
   Stessa superficie dell'elenco di record, ma ogni voce si apre. Serve dove le
   cose che si fanno hanno frequenze molto diverse: lo stato si scorre a ogni
   visita, la riga da copiare e la cadenza si toccano una volta e poi mai piu'.
   Tenerle tutte a vista dava lo stesso peso a tutto, e con cinque task la
   pagina diventava un muro.

   E' `<details>` nativo: niente JavaScript, funziona da tastiera, e senza CSS
   resta comunque usabile. */
.task-list{background:var(--table-bg);border:1px solid var(--table-border);
  border-radius:var(--radius-lg);overflow:hidden}
.task{border-top:1px solid var(--table-border)}
.task:first-child{border-top:0}
.task > summary{display:flex;align-items:center;gap:.75rem;flex-wrap:wrap;
  padding:1rem 1.15rem;cursor:pointer;list-style:none}
.task > summary::-webkit-details-marker{display:none}
/* La freccia la disegniamo noi, per poterla ruotare all'apertura: quella di
   sistema cambia forma da un browser all'altro. */
.task > summary::before{content:"";width:.5rem;height:.5rem;flex:0 0 auto;
  border-right:2px solid var(--muted);border-bottom:2px solid var(--muted);
  transform:rotate(-45deg);transition:transform .15s ease}
.task[open] > summary::before{transform:rotate(45deg)}
.task > summary:hover{background:var(--accent-bg)}
.task-name{font-weight:600}
.task-when{color:var(--muted);font-size:var(--fs-sm);margin-left:auto;text-align:right}
.task-body{padding:0 1.15rem 1.15rem;border-top:1px solid var(--table-border)}
.task-body > *:first-child{margin-top:.9rem}
.task-actions{display:flex;flex-wrap:wrap;gap:.5rem;align-items:center;margin-top:.9rem}
@media (max-width:575.98px){
  .task-when{margin-left:0;text-align:left;flex-basis:100%}
}

/* ===== State — lo stato quando e' il soggetto, non l'etichetta di una riga ===== */
.state{display:flex;align-items:flex-start;gap:.6rem;font-weight:600}
.state-led{width:.6rem;height:.6rem;border-radius:50%;margin-top:.45rem;flex:0 0 auto;background:var(--muted)}
.state-on .state-led{background:var(--success);box-shadow:0 0 0 3px var(--success-bg)}
.state-warn .state-led{background:var(--warning);box-shadow:0 0 0 3px var(--warning-bg)}
.state small{display:block;font-weight:400;color:var(--muted);font-size:var(--fs-sm);margin-top:.1rem}

/* ===== Forms ===== */
.form-label{display:block;color:var(--text);font-size:var(--fs-sm);font-weight:500;margin-bottom:.4rem}
.form-text{display:block;color:var(--muted);font-size:var(--fs-sm);margin-top:.35rem}
.form-control,.form-select{display:block;width:100%;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:.6rem .85rem;color:var(--text);font-family:inherit;font-size:var(--fs);transition:border-color .15s,box-shadow .15s}
.form-control::placeholder{color:var(--muted);opacity:.7}
.form-control:focus,.form-select:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 22%,transparent)}
.form-control:disabled,.form-control[readonly]{background:var(--surface-2);opacity:.8}
.input-group{display:flex;align-items:stretch;border:1px solid var(--border);border-radius:var(--radius);background:var(--surface);overflow:hidden;transition:border-color .15s,box-shadow .15s}
.input-group:focus-within{border-color:var(--accent);box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 22%,transparent)}
/* Le icone dei campi restano alla misura del testo. A 15px un glifo a contorno con
   tratti sottili (`bi-lock`) perde l'arco e il corpo si legge piu' largo che alto —
   sembra schiacciato, ma non lo schiaccia niente: a 96px il glifo e' perfetto. Portarle
   a 1.05rem non serve (il corpo cresce di **un pixel**); la forma torna solo a 1.25rem,
   provato e scartato perche' l'icona diventa piu' presente del campo che accompagna.
   Se il caso si ripresenta, la strada e' la variante piena, non la misura. */
.input-group-text{display:flex;align-items:center;padding:0 .75rem;color:var(--muted);background:transparent}
.input-group .form-control{border:none;background:transparent;border-radius:0}
.input-group .form-control:focus{box-shadow:none}
.form-check{display:flex;align-items:center;gap:.5rem;min-height:1.5rem}
.form-check-input{appearance:none;-webkit-appearance:none;width:1.15rem;height:1.15rem;border:1px solid var(--border);border-radius:.35rem;background:var(--surface);cursor:pointer;flex-shrink:0;position:relative;transition:background .15s,border-color .15s}
.form-check-input:checked{background:var(--accent);border-color:var(--accent)}
.form-check-input:checked::after{content:"\2713";position:absolute;top:-2px;left:2px;font-size:.85rem;color:var(--text-on-accent)}
.form-check-label{cursor:pointer}
.form-switch .form-check-input{width:2.25rem;height:1.2rem;border-radius:1rem}
/* La pallina si centra sull'asse verticale invece di stare a 1px dal bordo: cosi' resta a posto qualunque sia l'altezza dell'interruttore. Lo stato acceso deve ripetere il -50% perche' transform e' una proprieta' sola: scrivere solo la traslazione orizzontale cancellerebbe il centraggio. */
.form-switch .form-check-input::after{content:"";position:absolute;top:50%;left:1px;width:.95rem;height:.95rem;border-radius:50%;background:var(--muted);transform:translateY(-50%);transition:transform .15s,background .15s}
.form-switch .form-check-input:checked::after{content:"";transform:translate(1rem,-50%);background:var(--text-on-accent)}

/* ===== Alerts ===== */
.alert{position:relative;border:1px solid transparent;border-radius:var(--radius);padding:.8rem 1rem;font-size:var(--fs);margin-bottom:1rem}
.alert>i:first-child{margin-right:.4rem}
.alert-success{background:var(--success-bg);color:var(--success-ink);border-color:color-mix(in srgb,var(--success) 30%,transparent)}
.alert-danger{background:var(--danger-bg);color:var(--danger-ink);border-color:color-mix(in srgb,var(--danger) 30%,transparent)}
.alert-warning{background:var(--warning-bg);color:var(--warning-ink);border-color:color-mix(in srgb,var(--warning) 30%,transparent)}
.alert-info{background:var(--info-bg);color:var(--info-ink);border-color:color-mix(in srgb,var(--info) 30%,transparent)}
.alert-dismissible{padding-right:2.6rem}
.alert-dismissible .btn-close{position:absolute;top:.45rem;right:.5rem}

/* ===== Toast (successi e info) ===== */
/* Successi e info: toast flottante con JS presente. Senza JS (o se initToast
   non trova l'elemento) restano alert inline: e' il fallback, non un difetto. */
/* Sfondo pieno invece del colore semitrasparente di .alert-success/.alert-info:
   quel 10-16% di opacita' basta sopra lo sfondo fisso della pagina, ma qui la
   card fluttua sopra contenuto qualsiasi e diventerebbe illeggibile. */
.toast-floating{position:fixed;top:1rem;right:1rem;z-index:var(--z-toast,1080);max-width:380px;background:var(--surface);box-shadow:var(--shadow);animation:toast-in .2s ease-out}
.toast-floating.toast-hide{animation:toast-out .2s ease-in forwards}
@keyframes toast-in{from{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}
@keyframes toast-out{from{opacity:1}to{opacity:0;transform:translateY(-8px)}}

/* ===== Badges ===== */
.badge{display:inline-flex;align-items:center;gap:.25rem;font-size:.72rem;font-weight:600;padding:.3em .65em;border-radius:var(--radius-pill);line-height:1;white-space:nowrap}
.bg-primary{background:var(--accent-solid);color:var(--text-on-accent)}
.bg-secondary{background:var(--surface-2);color:var(--muted);border:1px solid var(--border)}
.bg-success{background:var(--success-bg);color:var(--success-ink)}
.bg-danger{background:var(--danger-bg);color:var(--danger-ink)}
.bg-warning{background:var(--warning-bg);color:var(--warning-ink)}
.bg-info{background:var(--info-bg);color:var(--info-ink)}
/* Avatar per ruolo: fondo pieno, colori della palette del tema
   (super_admin teal, admin ambra/accento, user grigio). Il rosso e riservato
   agli stati di errore: su un avatar leggerebbe come allarme. */
.avatar-super-admin{background:var(--info);color:var(--text-on-info)}
.avatar-admin{background:var(--accent-solid);color:var(--text-on-accent)}
.avatar-user{background:var(--surface-2);color:var(--muted);border:1px solid var(--border)}
.badge.text-dark{color:var(--text)!important}

/* ===== Tables ===== */
/* La tabella si distingue per SUPERFICIE, non per bordo.
   Un filetto sotto ogni riga aggiungeva sei linee orizzontali a una pagina che ne
   ha gia' per la testata, per ogni sezione e per ogni impostazione — tutte dello
   stesso peso, e l'occhio smetteva di distinguere l'elenco dalla struttura.
   Le righe alternate fanno lo stesso lavoro TOGLIENDO linee invece di aggiungerne,
   e su sette colonne aiutano a seguire la riga fino in fondo. L'intestazione si
   stacca con lo stesso fondo, senza bisogno del suo filetto. */
.table{width:100%;font-size:var(--fs);background:var(--table-bg);
  border:1px solid var(--table-border);border-radius:var(--radius-lg);overflow:hidden;
  border-collapse:separate;border-spacing:0}
.table th{text-align:left;font-weight:600;color:var(--thead-ink);font-size:.72rem;text-transform:uppercase;letter-spacing:.5px;padding:.7rem 1rem;background:var(--thead-bg)}
.table td{padding:.7rem 1rem;vertical-align:middle}
/* Le cifre proporzionali fanno ballare le colonne numeriche riga per riga:
   in una tabella di dati le misure devono incolonnarsi da sole. */
.table td.text-end,.table td.num,.table .tabular{font-variant-numeric:tabular-nums}
/* Le bande partono dalla quinta riga in giu'. Su tre righe l'alternanza non si
   legge come alternanza: colora la riga di mezzo e sembra un'evidenziazione.
   Il selettore chiede alla prima riga se ha almeno quattro sorelle dopo di se';
   in caso contrario nessuna banda, e le poche righe si distinguono per posizione. */
.table tbody tr:first-child:nth-last-child(n+5) ~ tr:nth-child(even){background:var(--row-alt)}
/* L'hover non puo' essere --surface-2: sulle righe pari sarebbe invisibile.
   L'ambra tenue e' il colore dell'interazione nel resto del pannello e si stacca
   da entrambe le bande. */
.table-hover tbody tr:hover{background:var(--accent-bg)}
/* La riga che si sta modificando nel riquadro sotto la tabella. Il fondo sta
   sulle celle e non sulla riga: le bande alternate hanno specificita' maggiore
   di qualunque classe messa sul `tr`, e rincorrerla con un selettore piu' lungo
   sarebbe una gara che il prossimo caso riapre. */
.table tbody tr.is-editing > td{background:var(--accent-bg)}
/* L'ultima riga arriva a filo della card: in hover il suo sfondo squadrato
   sporgerebbe oltre la curvatura. Le celle d'angolo seguono il raggio della card,
   ma solo quando la tabella chiude davvero la card (nessun footer dopo). */
.card > .table-responsive:last-child .table-hover tbody tr:last-child:hover td:first-child,
.card > .table-hover:last-child tbody tr:last-child:hover td:first-child{border-bottom-left-radius:var(--radius-lg)}
.card > .table-responsive:last-child .table-hover tbody tr:last-child:hover td:last-child,
.card > .table-hover:last-child tbody tr:last-child:hover td:last-child{border-bottom-right-radius:var(--radius-lg)}
.table-responsive{overflow-x:auto}
.card .table{margin-bottom:0}

/* Intestazione di tabella ordinabile: un link vero dentro il <th>, non un
   bottone JS — funziona senza JavaScript e Ordinamento::query() fa il resto.
   Il colore si eredita dal thead (nessuna tinta di link) e la sottolineatura
   sparisce: qui l'indizio che si puo' cliccare e' la freccia, non il blu di un
   link qualunque, che stonerebbe nella riga uppercase dell'intestazione. Il
   focus da tastiera e' quello di sistema (base.css), non uno suo: due regole
   per lo stesso anello divergono alla prima modifica dell'una. */
.th-sort{display:inline-flex;align-items:center;gap:.3rem;color:inherit;text-decoration:none}
.th-sort:hover{text-decoration:underline}
/* La freccia delle colonne non attive e' un doppio verso attenuato: dice
   "si puo' ordinare qui" senza pretendere di dire gia' come. Su quella attiva
   l'inchiostro dell'intestazione basta, la direzione la dice la forma. */
.th-sort .bi-arrow-down-up{color:var(--th-sort-dim)}

/* ===== Editor delle lingue: schede e filtro =====
   Le pillole vanno a capo da sole: le schede sono ventuno, e una barra che
   scorre in orizzontale nasconde meta' delle destinazioni. */
.lang-tabs{display:flex;flex-wrap:wrap;gap:.3rem;margin:0 0 1rem}
.lang-tab{display:inline-flex;align-items:center;gap:.35rem;padding:.35rem .7rem;
  border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--surface);
  color:var(--muted);text-decoration:none;font-size:.8rem;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
.lang-tab:hover{background:var(--accent-bg);border-color:var(--accent);color:var(--text)}
.lang-tab.is-current{background:var(--accent-solid);border-color:var(--accent-solid);color:var(--text-on-accent)}
.lang-tab.is-current .badge{background:transparent;color:inherit;border:1px solid currentColor}
.lang-filter{display:inline-flex;align-items:center;gap:.45rem;margin-bottom:1rem;
  color:var(--muted);font-size:.85rem}
.lang-group{margin:1.5rem 0 .6rem;font-size:.75rem;text-transform:uppercase;letter-spacing:.04em;
  color:var(--muted);font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
.lang-panel > :first-child{margin-top:0}

/* ===== Markdown reso (changelog) =====
   Il testo di una versione e' prosa, non una tabella di dati: qui serve solo
   che i titoli si distinguano e che il codice si legga. Niente di piu', o la
   pagina diventa un secondo design system da tenere allineato. */
.md{font-size:var(--fs-sm);line-height:1.65}
.md h1,.md h2,.md h3{margin:1.2rem 0 .5rem;line-height:1.3}
.md h1{font-size:1.05rem}
.md h2{font-size:1rem}
.md h3{font-size:.92rem;color:var(--muted);text-transform:uppercase;letter-spacing:.04em}
/* Un nome di classe dentro un titolo non va maiuscolo: `Support\Pagina` non si
   scrive cosi', e un identificatore storpiato non si riconosce piu'. */
.md h3 code{text-transform:none;letter-spacing:0}
.md > :first-child{margin-top:0}
.md p{margin:0 0 .7rem}
.md ul,.md ol{margin:0 0 .7rem;padding-left:1.2rem}
.md li{margin-bottom:.3rem}
.md li.md-sub{margin-left:1.1rem;list-style-type:circle}
.md code{background:var(--surface-2);border:1px solid var(--border);border-radius:.3rem;
  padding:.05rem .3rem;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.85em}
.md pre{background:var(--surface-2);border:1px solid var(--border);border-radius:var(--radius);
  padding:.8rem 1rem;overflow:auto;margin:0 0 .7rem}
.md pre code{background:none;border:0;padding:0;font-size:.78rem;line-height:1.5}
.md table{width:100%;border-collapse:collapse;margin:0 0 .7rem;font-size:.9em}
.md th,.md td{border:1px solid var(--border);padding:.35rem .5rem;text-align:left}
.md hr{border:0;border-top:1px solid var(--border);margin:1rem 0}
.md strong{font-weight:600}

/* Nella riga chiusa di una versione: il titolo prende lo spazio che resta fra
   il numero e la data. */
.cl-titolo{color:var(--text);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ===== Pager — la barra sotto un elenco lungo =====
   Numeri e non solo avanti/indietro: con quaranta pagine, tornare al primo
   giorno costerebbe trentanove clic. Il bottone ai capi resta al suo posto
   spento, cosi' la barra non balla quando si arriva in fondo. */
.pager{display:flex;align-items:center;justify-content:space-between;gap:1rem;
  flex-wrap:wrap;margin-top:1.25rem}
.pager-count{margin:0;color:var(--muted);font-size:.85rem}
.pager-list{display:flex;align-items:center;gap:.25rem;list-style:none;margin:0;padding:0;flex-wrap:wrap}
.pager-link{display:inline-flex;align-items:center;gap:.3rem;min-width:2.25rem;height:2.25rem;
  padding:0 .6rem;justify-content:center;border:1px solid var(--border);border-radius:var(--radius-sm);
  color:var(--text);text-decoration:none;font-size:.85rem;background:var(--surface)}
.pager-link:hover{background:var(--accent-bg);border-color:var(--accent)}
.pager-link.is-current{background:var(--accent-solid);border-color:var(--accent-solid);
  color:var(--text-on-accent);font-weight:600}
.pager-link.is-current:hover{background:var(--accent-solid)}
.pager-link.is-off{opacity:.45;pointer-events:none}
.pager-gap{display:inline-flex;justify-content:center;min-width:1.5rem;color:var(--muted)}

/* La misura della pagina: un controllo piccolo, che non deve competere con i
   numeri. Le frecce sono simboli e non parole — il verso lo dice la forma. */
.pager-size{display:flex;align-items:center;gap:.4rem}
.pager-size-label{color:var(--muted);font-size:.85rem;margin:0;white-space:nowrap}
.pager-size .form-select{width:auto;min-width:4.5rem;height:2.25rem;padding:0 1.8rem 0 .6rem;font-size:.85rem}

@media (max-width: 575px){
  .pager{justify-content:center}
  .pager-link{padding:0 .45rem}
}

/* ===== .table-stack — la tabella si impila su schermo stretto =====
   Due tipi di tabella, due rimedi diversi. Un ELENCO (utenti, audit: molte righe,
   poche colonne) va lasciato scorrere in orizzontale. Una SCHEDA (i file GeoLite:
   poche righe, molte colonne) no: comprimendola, la colonna piu' larga si riduce a
   una gola di 50px e la riga cresce in altezza per venti capi. Qui ogni riga
   diventa un blocco e ogni cella dichiara il proprio nome, che il thead non puo'
   piu' dire perche' non e' piu' sopra di lei.
   Il thead resta nel documento — spostato fuori vista, non display:none — cosi'
   chi usa uno screen reader continua a sentire le intestazioni. */
@media (max-width:767.98px){
  .table-stack thead{position:absolute;width:1px;height:1px;padding:0;margin:-1px;
    overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
  .table-stack,.table-stack tbody,.table-stack tr,.table-stack td{display:block;width:100%}
  /* Le celle impilate perdono il padding orizzontale della tabella: senza,
     il testo tocca i bordi della superficie. Il respiro laterale e' loro. */
  .table-stack tr{padding:.9rem 0}
  .table-stack td{display:flex;align-items:baseline;justify-content:space-between;gap:1rem;
    padding:.25rem 1rem;border:0;text-align:left}
  /* Il nome della riga fa da titolo del blocco: niente etichetta, tutta la larghezza. */
  .table-stack td.stack-title{display:block;margin-bottom:.5rem}
  .table-stack td:not(.stack-title)::before{content:attr(data-label);color:var(--muted);
    font-size:var(--fs-sm);font-weight:500;flex:0 0 auto}
  /* Una cella vuota impilata sarebbe una riga fantasma con la sola etichetta. */
  .table-stack td:empty{display:none}
  .table-stack td.text-end,.table-stack td.text-center{text-align:left}
  /* La cella delle azioni non ha etichetta: niente ::before, che anche vuoto e'
     un elemento flex e si prende il gap. E i bottoni vanno a capo — a 390px
     quattro azioni non stanno in riga, e senza wrap l'ultima (di solito
     «Elimina») usciva dalla cella, tagliata dall'overflow della tabella. */
  .table-stack td:not([data-label]):not(.stack-title){flex-wrap:wrap;justify-content:flex-start;gap:.5rem}
  .table-stack td:not([data-label])::before{content:none}
}

/* ===== Nav pills ===== */
.nav{display:flex;flex-wrap:wrap;gap:.25rem;list-style:none;margin:0;padding:0}
.nav-pills .nav-link{display:inline-flex;align-items:center;gap:.4rem;padding:.45rem .9rem;border-radius:var(--radius);color:var(--muted);text-decoration:none;font-size:var(--fs);font-weight:500}
.nav-pills .nav-link:hover{background:var(--surface-2);color:var(--text)}
.nav-pills .nav-link.active{background:var(--accent-solid);color:var(--text-on-accent)}

/* Un badge dentro la pillola attiva cambia regola, e non e' un caso particolare:
   i badge tinti (`bg-info`, `bg-warning`, ...) hanno un fondo al 10-16% di
   opacita' e un inchiostro calcolato contro il **fondo pagina**. Messi sopra
   l'accento pieno succedono due cose insieme: la tinta sparisce dentro l'ambra,
   e l'inchiostro — teal, marrone — non ha piu' il contrasto per cui era stato
   scelto. `bg-primary` e' il caso limite: accento su accento, invisibile.
   Qui il numero smette di avere una tinta propria e diventa un contorno con
   l'inchiostro della pillola: si legge esattamente come la parola che ha
   accanto, che e' il contrasto gia' verificato (5.02:1 nel tema chiaro). */
.nav-pills .nav-link.active .badge{
  background:transparent;
  color:inherit;
  border:1px solid currentColor;
}

/* ===== Dropdown ===== */
.dropdown{position:relative}
.dropdown-menu{position:absolute;z-index:var(--z-dropdown);min-width:11rem;margin-top:.4rem;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow);padding:.35rem;list-style:none;display:none}
.dropdown-menu.show{display:block}
.dropdown-menu-end{right:0}
/* Solo i menu allineati a destra (azioni riga nelle tabelle): non devono superare
   la loro larghezza base, altrimenti su schermi stretti si allargano oltre il dovuto. */
ul.dropdown-menu.dropdown-menu-end.show{max-width:176px}
.dropdown-item{display:flex;align-items:center;gap:.5rem;width:100%;padding:.5rem .7rem;border-radius:var(--radius-sm);color:var(--text);text-decoration:none;background:transparent;border:none;font-size:var(--fs);text-align:left;cursor:pointer}
.dropdown-item:hover{background:var(--surface-2);color:var(--text)}
.dropdown-item.active{background:var(--accent-solid);color:var(--text-on-accent)}
.dropdown-item.text-danger{color:var(--danger)}
.dropdown-item.text-danger:hover{background:var(--danger-bg)}
.dropdown-divider{height:1px;margin:.35rem 0;background:var(--border);border:none;list-style:none}

/* ===== Collapse ===== */
.collapse{display:none}
.collapse.show{display:block}

/* ===== Requirements checklist (es. requisiti password live) ===== */
.req-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.6rem}
.req-list li{display:flex;align-items:center;gap:.55rem;font-size:.85rem;color:var(--muted);transition:color .15s}
.req-list li i{font-size:1rem;line-height:1;color:var(--muted);flex:0 0 auto;transition:color .15s}
.req-list li.is-ok{color:var(--text)}
.req-list li.is-ok i{color:var(--success)}
.req-note{margin:1rem 0 0;padding-top:.85rem;border-top:1px solid var(--border);display:flex;gap:.55rem;align-items:flex-start;font-size:.8rem;color:var(--muted)}
.req-note i{flex:0 0 auto;margin-top:.1rem;color:var(--accent-text)}
