/* Il titolo di pagina riceve il focus via codice dopo la navigazione, perché gli screen reader
   annuncino la pagina nuova: non essendo raggiungibile da tastiera non deve mostrare l'anello di
   focus, ma l'outline trasparente resta necessario per renderlo visibile in alto contrasto. */
h1[tabindex="-1"]:focus {
	box-shadow: none !important;
	outline: 2px solid transparent;
	outline-offset: 2px;
}

/* Blazor marca i campi di un EditForm con classi proprie che il design system non conosce:
   l'errore si segnala con il bordo previsto da Bootstrap Italia per .form-control.is-invalid.
   Lo stato valido non si segnala: Bootstrap Italia non colora i campi corretti. */
.form-control.invalid {
	border-color: var(--bs-danger);
}

.validation-message {
	color: var(--bs-danger);
}

/* Il box del check nasce trasparente (border-color e basta): dentro una fascia tinta — la barra dei
   filtri di un elenco è .bg-200 — assume il grigio del fondo e smette di leggersi come un controllo,
   mentre il .form-control accanto resta bianco. Il fondo del corpo lo riallinea agli altri campi
   ovunque stia. Bootstrap Italia lo fa già per la casella disabilitata: qui vale per tutti gli stati
   non selezionati, e per il radio anche da selezionato, dove il bianco è l'anello fra il bordo e il
   pallino. La casella spuntata resta esclusa: il suo fondo è la tinta piena che porta il segno. */
.form-check [type="checkbox"]:not(:checked) + label::after,
.form-check [type="radio"] + label::before {
	background-color: var(--bs-body-bg, #fff);
}

#blazor-error-ui {
	display: none;
	position: fixed;
	bottom: 0;
	width: 100%;
	z-index: 1000;
	padding: 0.7rem 1.25rem;
	color: #721c24;
	background-color: #f8d7da;
	border-top: 1px solid #f5c6cb;
	text-align: center;
}

	#blazor-error-ui .dismiss {
		cursor: pointer;
		position: absolute;
		right: 0.75rem;
		top: 0.5rem;
	}

/* Aggiustamenti estetici per pulsanti barra in alto  */
@media (min-width: 992px){
	.it-user-wrapper.dropdown > button {
		min-width: 10rem;
	}
}

/* Il tema slim dimensiona i pulsanti della barra con un selettore ancorato alla right zone
   (`.it-header-slim-wrapper .it-header-slim-wrapper-content .it-header-slim-right-zone button`,
   padding verticale 7.5px). Il pulsante di installazione della PWA sta a sinistra, quindi quella
   regola non lo raggiunge: sotto i 992px ereditava i 16px di `.btn-full` e risultava alto 57px
   contro i 40px degli altri, facendo crescere la barra.
   Il padding è dichiarato senza !important di proposito: da 992px in su `.btn-full` impone
   `padding: 12px 24px !important` e deve continuare a vincere, perché a quella larghezza i pulsanti
   della barra si estendono per tutta la sua altezza. */
.pwa-install-zone .btn {
	padding-top: 7.5px;
	padding-bottom: 7.5px;
}

.it-header-slim-wrapper form button.dropdown-item.list-item {
	background-color: white;
}
	.it-header-slim-wrapper form button.dropdown-item.list-item:hover {
		background-color: white;
		text-decoration: underline;
	}

/* Menu Impersonazione — colori distinti per evidenziare funzionamento */
@media (min-width: 576px) {
	.impersonation-menu .dropdown-menu {
		min-width: 17rem;
	}
}

/* Il tema slim dell'header colora tutti i button della right zone con un selettore a 4 classi,
   che vince su .btn-danger: le utility bg-* sono l'unico modo per riportare lo stato in evidenza. */
.impersonation-menu--active .btn {
	animation: impersonation-pulse 2s ease-in-out infinite;
}

	.impersonation-menu--active .btn:hover {
		background-color: hsl(350, 60%, 32%) !important;
	}

	.impersonation-menu--active .avatar {
		background: #fff;
	}

		.impersonation-menu--active .avatar p {
			color: hsl(350, 60%, 40%) !important;
		}

	/* Il nome dell'utente impersonato arriva da SPID e può essere lungo: senza un tetto il pulsante
	   allarga la right zone dell'header e con essa il dropdown, che ne eredita la larghezza. */
	.impersonation-menu--active .btn > .text-truncate {
		max-width: 12rem;
	}

/* Il blocco di stato non è una voce cliccabile ma deve allinearsi a quelle sotto:
   .link-list-wrapper usa 24px di padding orizzontale, le utility px-* userebbero 16px. */
.impersonation-status {
	padding: 0.5rem 24px 0.75rem;
}

	.impersonation-status__label {
		font-size: 0.777rem;
		font-weight: 600;
		letter-spacing: 0.5px;
		text-transform: uppercase;
		/* `text-secondary` non è questo colore: vale rgb(48,71,95) mentre `--bs-secondary` è rgb(93,112,131). */
		color: var(--bs-secondary);
	}

	.impersonation-status__name {
		font-size: 1rem;
		font-weight: 700;
		line-height: 1.3;
		overflow-wrap: anywhere;
	}

/* Stessa gabbia del blocco di stato: il form vive nello stesso dropdown e deve condividerne i
   margini. Il padding superiore è più ampio perché l'etichetta flottante di Bootstrap Italia,
   quando sale, esce dal box del .form-group. */
.impersonation-form {
	padding: 2rem 24px 1.5rem;
}

	.impersonation-form .form-group {
		margin-bottom: 1.25rem;
	}

	/* .form-feedback nasce per stare fuori dal .form-group e compensa con un margine sinistro:
	   qui sta dentro, e va allineato al testo dell'input come l'indicazione di formato. Il corpo
	   sale a quello dell'indicazione, perché un errore non deve leggersi peggio di un suggerimento. */
	.impersonation-form .form-feedback {
		margin-left: 0;
		padding: 0 0.5rem;
		font-size: 0.875rem;
	}

/* Il tema slim ridefinisce sfondo e padding di ogni <button> della right zone, compresi quelli
   che finiscono dentro i dropdown: il pulsante di conferma va riportato all'aspetto di un .btn. */
.it-header-slim-wrapper .dropdown-menu form button[type="submit"] {
	padding: 12px 24px;
	background-color: var(--bs-btn-bg);
}

	.it-header-slim-wrapper .dropdown-menu form button[type="submit"]:hover {
		background-color: var(--bs-btn-hover-bg);
	}

/* L'alone segue il colore del pulsante: durante l'impersonazione è danger, non warning. */
@keyframes impersonation-pulse {
	0%, 100% {
		box-shadow: 0 0 0 0 rgba(var(--bs-danger-rgb), 0.5);
	}

	50% {
		box-shadow: 0 0 0 4px rgba(var(--bs-danger-rgb), 0);
	}
}

/* BottomNav — spazio extra in basso per non sovrapporre il contenuto su mobile.
   `:has(.bottom-nav)` lega lo spazio alla presenza della barra: la login non la renderizza
   (v. BottomNav.razor) e senza la condizione resterebbero 5rem di vuoto sotto il footer.
   `:has()` è già impiegato più sotto per le tabelle e segue da solo login/logout. */
@media (max-width: 991.98px) {
	body:has(.bottom-nav) {
		padding-bottom: 5rem;
	}

	/* WCAG 2.4.11 Focus Not Obscured — scroll-padding evita che elementi focusati finiscano sotto header sticky o BottomNav fissa */
	html {
		scroll-padding-top: 4rem;
	}

	html:has(.bottom-nav) {
		scroll-padding-bottom: 5rem;
	}
}

/* Deroga al vincolo "bootstrap-italia vanilla": la libreria dichiara `.bottom-nav` position:fixed
   SENZA z-index. Un elemento fixed crea sempre uno stacking context, quindi lo z-index:20 che la
   libreria mette sulla <ul> bianca vale solo dentro la barra: nel contesto radice la barra resta
   z-index:auto e le label flottanti dei form (`.form-group label`, z-index:2) si dipingono sopra
   durante lo scroll. 1030 è il valore che Bootstrap riserva alle barre fisse: sopra al contenuto
   (sticky-top 1020), sotto al menu mobile offcanvas (navbar-collapsable 1055) e al suo backdrop
   (1040), che devono continuare a coprirla. Il componente non espone classi utility per questo:
   la correzione non è ottenibile con il solo markup. */
.bottom-nav {
	z-index: 1030;
	/* Il nav è alto 96px ma solo i 64px della <ul> sono visibili: i 32px sopra ospitano l'ombra
	   (:before) e restano trasparenti. Senza pointer-events:none quella fascia fantasma intercetta
	   i click: un link o un input che vi scorre sotto è visibile ma non cliccabile. La <ul>
	   riabilita gli eventi sulla parte che risponde davvero. */
	pointer-events: none;
}

	.bottom-nav ul {
		pointer-events: auto;
	}

/* L'autofill del browser non emette input/change né mutazioni DOM: la label flottante di
   bootstrap-italia non se ne accorge e resta stampata sopra il valore. Chromium e WebKit però
   avviano le animazioni dichiarate su un campo nel momento in cui lo compilano: agganciare una
   @keyframes a :-webkit-autofill produce un animationstart che bootstrap-italia.js ascolta per
   riallineare le label. Regola inerte ovunque l'autofill non avvenga (Firefox compreso, dove il
   caso è coperto dai listener delegati sul document). Nessuna utility della libreria offre il
   gancio: la keyframes qui è il pezzo che il JS non può generare da solo. */
@keyframes autofill-start {
	from { }
}

input[data-bs-input]:-webkit-autofill {
	animation-name: autofill-start;
}

/* Spinner in linea nei pulsanti. Bootstrap Italia lo disegna come blocco da 48px con un colore
   fisso: dentro un pulsante serve alto quanto il testo e del colore del pulsante, altrimenti sul
   primario il grigio-azzurro di serie sparisce sul fondo blu (contrasto 1.1:1).
   Il selettore ricalca quello del design system perché il suo, per lo stato "active", è più
   specifico del nostro: è una regola sola, non una catena di sovrascritture. */
.btn .progress-spinner.progress-spinner-active:not(.progress-spinner-double) {
	display: inline-block;
	vertical-align: middle;
	margin-right: 0.5rem;
	width: 1.25rem;
	height: 1.25rem;
	border-width: 2px;
	border-color: currentColor;
	border-bottom-color: transparent;
}
/* Il bordo esterno dell'elenco lo mette il contenitore (vedi DataTable), ma le celle portano tutte un
   filetto inferiore, ultima riga compresa: Bootstrap Italia non ha alcuna regola `:last-child` sulle
   tabelle. Sommato al bordo del contenitore darebbe 2,5px di spessore contro 1px, cioè un filetto
   doppio proprio sul lato più visibile. Nessuna utility lo raggiunge: andrebbe messa su ogni cella
   dell'ultima riga con una condizione dentro il ciclo.
   Il secondo selettore serve alle tabelle in cui ogni elemento occupa due righe: lì l'ultima riga è il
   dettaglio, che è già senza bordo e a collasso chiuso ha altezza zero, quindi a chiudere l'elenco è la
   penultima. `:has(+ tr:last-child > .border-0)` dice esattamente questo — «la riga che precede
   un'ultima riga priva di bordo proprio» — e non è un aggancio arbitrario al markup: è la condizione
   stessa. Verificato che non seleziona nulla in una tabella normale, in quella con la sola riga di
   caricamento o di elenco vuoto, e nemmeno con una riga finale di riepilogo, che un bordo lo vuole. */
.table-responsive.border > .table > tbody > tr:last-child > *,
.table-responsive.border > .table > tbody > tr:has(+ tr:last-child > .border-0) > * {
	border-bottom-width: 0;
}

/* Quale colonna si può ordinare deve vedersi **prima** di provare a cliccarla: la freccia compare solo
   sulla colonna già ordinata, quindi a elenco appena aperto nulla distingue un titolo ordinabile da uno
   che non lo è. La sottolineatura è punteggiata e non piena perché i link dell'app sono sottolineati
   pieni (misurato): un titolo sottolineato pieno prometterebbe una navigazione che non avviene.
   L'aggancio è `aria-sort`, che il componente scrive **solo** sulle colonne ordinabili: è la condizione
   stessa, non un suo indizio, e non dipende dall'etichetta del comando, che è localizzata.
   Sta qui e non nel markup del componente perché `text-decoration-style` non ha una utility, e `dotted`
   è proprio ciò che distingue il segnale da un link; lo scostamento tiene la linea sotto le discendenti.
   Un'unica regola per ogni tabella della soluzione, presente e futura. */
th[aria-sort] button > span {
	text-decoration: underline dotted;
	text-underline-offset: 0.25em;
}

/* Tabelle in cui ogni elemento occupa due righe: la riga dei dati e la riga di dettaglio espandibile.
   Bootstrap Italia una classe per le righe alternate ce l'ha — `.table-striped` — ma alterna **una
   riga per volta** (`tbody > tr:nth-of-type(odd)`): dove un elemento ne occupa due, colora tutte le
   righe di dati e lascia bianchi tutti i dettagli, cioè smette di alternare. Una variante a gruppi
   nella libreria non esiste (verificato su 2.18.1: zero occorrenze di `nth-child(4n`).
   Questa regola non riscrive lo striping: riusa i token della libreria — `--bs-table-striped-bg` e
   `--bs-table-accent-bg`, che `.table > :not(caption) > * > *` consuma in un `box-shadow` interno —
   e ne cambia il solo selettore, che è strutturale e nessuna utility può esprimere.
   La classe la scrive `DataTable` quando riceve `Striped="TableStriping.PerCoppia"`: nessuna pagina
   deve ricordarsene, ed è ciò che tiene identiche fra loro tutte le tabelle della soluzione. */
.table-striped-coppie > tbody > tr:nth-child(4n+1) > *,
.table-striped-coppie > tbody > tr:nth-child(4n+2) > * {
	--bs-table-accent-bg: var(--bs-table-striped-bg);
	color: var(--bs-table-striped-color);
}

/* Lo striping a coppie ha la stessa specificità della regola di hover di Bootstrap e, arrivando dopo,
   la sovrascriverebbe: qui l'hover viene ripristinato con una specificità superiore. */
.table-striped-coppie.table-hover > tbody > tr:hover > * {
	--bs-table-accent-bg: var(--bs-table-hover-bg);
	color: var(--bs-table-hover-color);
}

/* La freccia del pulsante di dettaglio segue lo stato del Collapse di Bootstrap Italia.
   Non è sostituibile con due icone diverse nel markup: il Collapse è nativo, lo stato vive in
   `aria-expanded` scritto da Bootstrap e Blazor non lo conosce, quindi il solo modo di rifletterlo
   senza interop è un selettore su quell'attributo. */
.toggle-ruoli .icon {
	transition: transform 0.2s ease-in-out;
}

.toggle-ruoli[aria-expanded="true"] .icon {
	transform: rotate(180deg);
}

@media (prefers-reduced-motion: reduce) {
	.toggle-ruoli .icon {
		transition: none;
	}

	/* L'alone dell'impersonazione è decorativo: lo stato resta leggibile dal colore e dal testo. */
	.impersonation-menu--active .btn {
		animation: none;
	}
}

/* Con table-layout auto il browser distribuisce la larghezza anche in base al contenuto della cella
   con colspan: aprendo un dettaglio le colonne si riassestano sotto gli occhi di chi legge, e cambiano
   pure passando da una pagina all'altra. Il layout fisso rende le colonne indipendenti dal contenuto. */
.table-utenti {
	table-layout: fixed;
	/* Sotto questa soglia il badge di ruolo più lungo non entra nella sua colonna: si lascia
	   scorrere orizzontalmente `.table-responsive` invece di far debordare il contenuto. */
	min-width: 20rem;
}

/* Con table-layout: fixed le colonne nascoste da `d-none` continuano a consumare la percentuale
   dichiarata, quindi ogni breakpoint ridichiara l'intero set azzerando le colonne non visibili.
   Le percentuali tengono conto del fatto che l'intestazione non è più solo testo: ogni colonna
   ordinabile e filtrabile porta anche la freccia di ordinamento e l'imbuto del filtro, e in una
   colonna troppo stretta il titolo si spezzerebbe su quattro righe.
   Le colonne sono cinque: indirizzo email e stato di conferma stanno insieme. */
.table-utenti > thead > tr > th:nth-child(1) { width: 36%; }
.table-utenti > thead > tr > th:nth-child(2) { width: 0; }
.table-utenti > thead > tr > th:nth-child(3) { width: 0; }
.table-utenti > thead > tr > th:nth-child(4) { width: 40%; }
.table-utenti > thead > tr > th:nth-child(5) { width: 24%; }

@media (min-width: 768px) {
	.table-utenti > thead > tr > th:nth-child(1) { width: 22%; }
	.table-utenti > thead > tr > th:nth-child(2) { width: 33%; }
	.table-utenti > thead > tr > th:nth-child(4) { width: 30%; }
	.table-utenti > thead > tr > th:nth-child(5) { width: 15%; }
}

@media (min-width: 992px) {
	.table-utenti > thead > tr > th:nth-child(1) { width: 17%; }
	.table-utenti > thead > tr > th:nth-child(2) { width: 30%; }
	.table-utenti > thead > tr > th:nth-child(3) { width: 21%; }
	.table-utenti > thead > tr > th:nth-child(4) { width: 20%; }
	.table-utenti > thead > tr > th:nth-child(5) { width: 12%; }
}

/* Username, email e Codice Fiscale sono stringhe senza spazi: senza questo mandano in overflow la colonna. */
.table-utenti > thead > tr > th,
.table-utenti > tbody > tr > td {
	overflow-wrap: anywhere;
}

/* I badge hanno una misura sola in tutta l'applicazione.
   `--bs-badge-font-size` di Bootstrap Italia è `0.875em`, cioè **relativa al contesto**: lo stesso
   badge misurava 12,25px nel riepilogo del dettaglio giorno (contesto 14px), 15,75px nel menu utente
   (18px) e 24,5px accanto al titolo di pagina (28px) — il doppio, nella stessa schermata. In `rem`
   vale 14px ovunque, che è la misura di servizio del design system.
   Nessuna utility imposta una variabile CSS, e `fs-*` non ha un gradino da 14px: `fs-6` è 16px. */
.badge {
	--bs-badge-font-size: 0.875rem;
}

/* ── Badge dei ruoli ──
   Tre pesi visivi distinti: piattaforma, ruoli che conferiscono poteri operativi, ruoli di sola utenza.
   I ruoli di utenza sono in versione leggera perché sono la condizione ordinaria di quasi tutti gli account:
   riempirli tutti renderebbe indistinguibili i pochi che contano davvero in un elenco amministrativo.
   Restano qui solo i due pesi che il design system non ha: piattaforma e operativi sono `text-bg-primary`
   e `text-bg-secondary`, misurati identici al pixel, e il grassetto 600 è già il default di `.badge`.
   Il contorno è un'ombra interna e non un `border`: un bordo vero porta il badge da 21px a 23px di
   altezza e lo disallinea dagli altri della stessa riga. */
.badge-ruolo-utenza {
	color: var(--bs-dark);
	background-color: transparent;
	box-shadow: inset 0 0 0 1px var(--bs-secondary);
}

/* Contatore dei ruoli che il riepilogo di riga non mostra: neutro, per non competere con i ruoli veri. */
.badge-ruolo-altri {
	color: var(--bs-gray-700);
	background-color: transparent;
	box-shadow: inset 0 0 0 1px var(--bs-border-color);
}

/* ── Dettaglio dei ruoli per Ente ──
   Un blocco per Ente; dentro, un ruolo per riga con i propri scope incolonnati sotto: una lista
   in linea diventa illeggibile appena l'utente ha più sedi con nomi lunghi.
   Restano le due sole cose che le utility non esprimono: il `gap` a due valori e il filetto verticale
   che raccoglie gli scope sotto il proprio ruolo. Il filetto tiene con sé anche la propria spaziatura,
   perché separare il bordo dal padding che lo distanzia dal testo significa poter cambiare l'uno
   dimenticando l'altro. */
.dettaglio-ruoli {
	gap: 0.75rem 2.5rem;
}

.dettaglio-elenco-scope > li {
	padding-left: 1rem;
	margin-left: 0.375rem;
	border-left: 2px solid var(--bs-border-color);
	line-height: 1.5;
}

/* ── Calendario prenotazioni (Tutore/Iscritto) ───────────────────────────── */


/* ── Toolbar ──
   Tre disposizioni per fascia di larghezza. L'ordine del DOM è periodo → navigazione → viste, cioè
   l'ordine di lettura; in tutte e tre le disposizioni l'ordine visivo da sinistra a destra coincide
   con quello di tabulazione, perché l'h2 non è focalizzabile (WCAG 1.3.2, 2.4.3). */
.calendario-toolbar {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: 0.75rem 1rem;
}

/* I gruppi vanno a capo al proprio interno: sotto i ~360px i tre pulsanti delle viste non stanno su
   una riga, e senza wrap sfonderebbero il viewport con uno scroll orizzontale (WCAG 1.4.10). */

/* Sotto md tutto è impilato e centrato: il periodo su una riga, poi i due gruppi, ciascuno sul
   proprio rigo se non ci stanno insieme. */
.calendario-toolbar__periodo {
	flex: 1 1 100%;
	text-align: center;
}

/* Da md il periodo resta su una riga propria e i due gruppi si spingono ai bordi opposti di quella
   sotto: è la disposizione in cui i tre pulsanti delle viste non stanno più accanto alla navigazione. */
@media (min-width: 768px) {
	.calendario-toolbar {
		justify-content: space-between;
	}
}

/* Da lg tutto sta su una riga sola. Serve la griglia, non `space-between`: con due gruppi di
   larghezza diversa il flex centrerebbe il periodo fra di loro, non nel contenitore. Le tre colonne
   1fr auto 1fr lo centrano davvero. */
@media (min-width: 992px) {
	.calendario-toolbar {
		display: grid;
		grid-template-columns: 1fr auto 1fr;
		align-items: center;
	}

	.calendario-toolbar__periodo {
		flex: none;
		grid-column: 2;
		grid-row: 1;
	}

	.calendario-toolbar__gruppo--nav {
		grid-column: 1;
		grid-row: 1;
		justify-self: start;
	}

	.calendario-toolbar__gruppo--viste {
		grid-column: 3;
		grid-row: 1;
		justify-self: end;
	}
}

.calendario-griglia {
	display: flex;
	flex-direction: column;
	border: 1px solid var(--bs-border-color);
	border-radius: var(--bs-border-radius, 4px);
	overflow: hidden;
}

.calendario-griglia__intestazione,
.calendario-griglia__riga {
	display: grid;
	grid-template-columns: repeat(7, 1fr);
}

.calendario-griglia__dow {
	padding: 0.5rem 0.25rem;
	text-align: center;
	font-weight: 600;
	background-color: var(--bs-100);
	border-bottom: 1px solid var(--bs-border-color);
}

.calendario-griglia__cella {
	border-bottom: 1px solid var(--bs-border-color);
	border-right: 1px solid var(--bs-border-color);
	min-height: 6.5rem;
	/* Le tracce di una grid hanno min-width: auto: senza questo azzeramento la colonna si allarga
	   fino a contenere il chip più largo, le sette colonne perdono la stessa misura e la griglia
	   sfonda il contenitore invece di troncare il contenuto. */
	min-width: 0;
}

.calendario-griglia__cella:nth-child(7n) {
	border-right: none;
}

.calendario-griglia__riga:last-child .calendario-griglia__cella {
	border-bottom: none;
}

.calendario-cella {
	display: flex;
	flex-direction: column;
	align-items: stretch;
	gap: 0.25rem;
	width: 100%;
	height: 100%;
	/* La cella non si allarga mai (`min-width: 0` blocca la traccia della grid) ma deve poter
	   crescere in altezza quando gli eventi del giorno sono molti: niente `overflow: hidden`, che
	   li taglierebbe, e `min-height` come minimo e non come misura fissa. */
	min-width: 0;
	min-height: 6.5rem;
	padding: 0.35rem;
	text-align: left;
	background: none;
	border: 0;
}

.calendario-cella--interattiva {
	cursor: pointer;
}

.calendario-cella--interattiva:hover {
	background-color: rgba(var(--bs-primary-rgb), 0.08);
}

.calendario-cella--interattiva:focus-visible {
	outline: 2px solid var(--bs-primary, #06c);
	outline-offset: -2px;
}

/* Il grigio dice «qui non c'è più niente da fare»: il Servizio blocca le modifiche dei giorni
   passati con la stessa condizione (`data < oggi`, MotivoBlocco.GiornoPassato), e la cella la
   rispecchia — anche quando è vuota, perché un giorno senza pasti passati resta comunque chiuso.
   Prima il grigio marcava i giorni di un altro mese: è informazione di calendario, non di azione,
   e ora vive nell'etichetta della cella, che porta il mese abbreviato («31-lug»). */
.calendario-cella--passato {
	background-color: var(--bs-100);
}

.calendario-cella--fuori-mese {
	color: var(--bs-gray-secondary);
}

.calendario-cella--oggi {
	box-shadow: inset 0 0 0 2px var(--bs-primary, #06c);
}


/* Nelle celle i chip stanno in riga e vanno a capo: le icone restano leggibili e, quando i Servizi
   del giorno sono molti, è la riga della griglia a crescere in altezza — mai la colonna in
   larghezza, che deve restare uguale per tutti e sette i giorni. */
.calendario-cella__eventi {
	display: flex;
	flex-wrap: wrap;
	gap: 0.25rem;
	min-width: 0;
}

.calendario-chip {
	display: inline-flex;
	align-items: center;
	gap: 0.2rem;
	max-width: 100%;
	min-height: 1.9rem;
	padding: 0.15rem 0.4rem;
	border-radius: 999px;
	font-size: 0.8125rem;
	line-height: 1.3;
	border: 1px solid transparent;
}

/* Solo nelle celle il chip si lascia comprimere e taglia il proprio contenuto: è lì che si ferma
   l'eccedenza orizzontale, così la cella non ha bisogno di `overflow: hidden` e resta libera di
   crescere in altezza. Nell'agenda e nella legenda lo spazio non manca e il chip resta intero. */
.calendario-cella__eventi > .calendario-chip {
	min-width: 0;
	overflow: hidden;
}

.calendario-chip__testo {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* Numero d'ordine del Servizio e/o iniziali dell'Iscritto: nella griglia il chip non ha spazio per la
   descrizione, e questo è ciò che distingue due eventi nello stesso giorno. Il significato è dato
   dalla legenda. Il font monospaziato sui numeri tiene allineati i marcatori a parità di lunghezza. */
.calendario-chip__marcatore {
	font-weight: 700;
	font-variant-numeric: tabular-nums;
	line-height: 1;
}

/* Segno della scelta di menù diversa dalla preselezione, sul marcatore del chip e sul campione della
   legenda. Sostituisce l'asterisco in coda, che a 375px portava il chip a chiedere 43px in una cella
   che ne offre 37.7: veniva tagliato via, cioè proprio invisibile là dove serviva. La sottolineatura
   non costa un pixel di larghezza; di serie però nasce attaccata alla base delle maiuscole e a 13px
   si confonde col disegno del carattere, quindi la stacchiamo e la ingrossiamo. Sotto il testo il
   chip ha 8.9px liberi (misurati): la linea resta dentro il bordo. */
.calendario-chip__marcatore u,
.calendario-legenda__marcatore u {
	text-underline-offset: 2px;
	text-decoration-thickness: 2px;
}

/* Badge di stato del dettaglio giorno: Bootstrap Italia alza i badge di 1px, che qui li scollerebbe
   dalla riga del titolo del Servizio. */

/* I tre stati prendono i colori istituzionali dai token del design system: fondo tenue ricavato
   dalla stessa tinta del bordo, testo lasciato al colore del corpo perché nessuna delle tre
   tinte piene raggiunge 4.5:1 sul proprio fondo tenue. Il significato non è mai affidato al solo
   colore: l'icona è sempre presente (WCAG 1.4.1) e la legenda ne dà il testo. */
.calendario-stato--presente {
	background-color: rgba(var(--bs-success-rgb), 0.15);
	border-color: var(--bs-success);
}

.calendario-stato--assente {
	background-color: rgba(var(--bs-danger-rgb), 0.15);
	border-color: var(--bs-danger);
}

.calendario-stato--chiuso {
	background-color: rgba(var(--bs-secondary-rgb), 0.15);
	border-color: var(--bs-secondary);
}

/* ── Legenda ──
   Nelle viste a griglia il chip è ridotto alla sola icona: la legenda è il punto in cui icone e
   marcatori tornano a essere testo, requisito per non affidare lo stato al solo colore. */


.calendario-legenda__gruppo {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.375rem 0.75rem;
}


/* Bootstrap Italia dimensiona i testi impostando font-size e line-height direttamente su `ul`:
   senza ereditarietà esplicita le voci risulterebbero più grandi dell'etichetta del gruppo. */
.calendario-legenda__elenco {
	display: flex;
	flex-wrap: wrap;
	gap: 0.375rem 1rem;
	margin: 0;
	font-size: inherit;
	line-height: inherit;
}

.calendario-legenda__voce {
	display: flex;
	align-items: center;
	gap: 0.3rem;
}

.calendario-legenda__marcatore {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 1.4rem;
	height: 1.4rem;
	padding: 0 0.3rem;
	font-weight: 700;
	font-variant-numeric: tabular-nums;
	line-height: 1;
	background-color: var(--bs-body-bg, #fff);
	border: 1px solid var(--bs-border-color);
	border-radius: 999px;
}

/* Vista agenda (mobile-first) */

/* Le liste dell'agenda sono contenitori di layout, non elenchi di testo: Bootstrap Italia dimensiona
   `ul` a 1.125rem/1.75rem e senza questa ereditarietà le due viste della stessa pagina mostrerebbero
   lo stesso contenuto con corpi di testo diversi. */
.calendario-agenda,
.calendario-agenda__eventi {
	font-size: inherit;
	line-height: inherit;
}

.calendario-agenda__giorno {
	display: flex;
	gap: 1rem;
	padding: 0.75rem;
	border-bottom: 1px solid var(--bs-border-color);
}

/* Righe alternate come nelle tabelle: l'agenda è un elenco di giorni, e la banda tenue è ciò che
   tiene insieme gli eventi dello stesso giorno quando sono più d'uno. Lo striping è strutturale
   (`nth-child`) e nessuna utility può esprimerlo — stessa ragione già documentata per
   `.table-striped-coppie` — quindi qui c'è solo il selettore: il colore è il token della libreria,
   con il valore di `.table` come riserva perché il token è dichiarato in quello scope. */
.calendario-agenda__giorno:nth-child(odd) {
	background-color: var(--bs-table-striped-bg, rgba(0, 0, 0, 0.05));
}

/* Niente `text-transform` qui: la maiuscola iniziale del giorno della settimana la mette il C# con
   `Capitalizza`, come in ogni altra etichetta di data del calendario. Un `capitalize` sul blocco
   toccava anche il mese, che in italiano è **minuscolo** dentro una data — «6 ago», come la stessa
   etichetta del periodo un centimetro più sopra — e faceva divergere l'agenda dalla cella della
   griglia, che scrive «31-lug». */
.calendario-agenda__data {
	display: flex;
	flex-direction: column;
	align-items: center;
	min-width: 3.5rem;
}


.calendario-agenda__eventi {
	flex: 1;
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
}

.calendario-agenda__evento {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	width: 100%;
	padding: 0.5rem 0.75rem;
	min-height: 44px;
	text-align: left;
	background-color: var(--bs-body-bg, #fff);
	border: 1px solid var(--bs-border-color);
	border-radius: var(--bs-border-radius, 4px);
	cursor: pointer;
}

.calendario-agenda__evento:hover {
	background-color: rgba(var(--bs-primary-rgb), 0.08);
}

.calendario-agenda__data:focus-visible,
.calendario-agenda__evento:focus-visible {
	outline: 2px solid var(--bs-primary, #06c);
	outline-offset: 2px;
}

/* Nell'agenda il testo dell'evento è già completo: nella vista aggregata l'Iscritto va sopra al
   Servizio, come nel dettaglio del giorno. */
.calendario-agenda__evento-iscritto {
	display: block;
	font-size: 0.8125rem;
	color: var(--bs-gray-secondary);
}

.dettaglio-evento {
	padding: 1rem 0;
	border-bottom: 1px solid var(--bs-border-color);
}

.dettaglio-evento:first-of-type {
	padding-top: 0;
}

.dettaglio-evento:last-of-type {
	border-bottom: 0;
	padding-bottom: 0;
}

.dettaglio-evento__identita {
	min-width: 0;
	margin-bottom: 1rem;
}

/* Due soli livelli tipografici nel dettaglio: in evidenza l'identità (il titolo h6, cioè l'Iscritto
   quando la vista è aggregata e il Servizio altrimenti), sotto il Servizio a 1rem/600, attenuato il
   contorno. Più di due pesi renderebbero la lettura faticosa senza aggiungere gerarchia. */
.dettaglio-evento__titolo {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.375rem;
	margin: 0;
}

.dettaglio-evento__servizio {
	margin: 0.25rem 0 0;
	font-size: 1rem;
	line-height: 1.5;
	font-weight: 600;
}

.dettaglio-evento__contesto,
.dettaglio-evento__limite {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.25rem 1rem;
	margin: 0.25rem 0 0;
	font-size: 0.875rem;
	line-height: 1.5;
	color: var(--bs-gray-secondary);
}

/* Ogni informazione di contorno — Sede, Classe, limite di modifica — è una voce: icona ed etichetta
   tenute insieme a 4px. Il `gap` di 1rem del contenitore separa una voce dall'altra, quindi senza
   questo involucro l'icona starebbe a 16px dal proprio testo, e a schermo stretto la voce si
   spezzerebbe a metà invece di andare a capo intera. */

/* Il limite di modifica sta sopra i pulsanti che governa: sotto al menu a tendina finirebbe a
   ridosso della sezione dell'Iscritto successivo, e la nota sembrerebbe appartenere a quella.
   Lo spazio è uguale sopra e sotto — 16px, come il margine inferiore del blocco identità — perché
   la nota non appartiene né al blocco che la precede né a quello che la segue: sta in mezzo. Con
   8px sotto pendeva verso i pulsanti e, essendo grigia e piccola come la riga Sede/Classe, di
   riflesso sembrava l'ultima riga del contorno. */
.dettaglio-evento__limite {
	margin: 0 0 1rem;
}

/* Riepilogo di ciò che non è più modificabile: etichetta e valore sulla stessa riga, così le due
   coppie restano leggibili come una tabellina invece che come quattro righe impilate. */
.dettaglio-evento__riepilogo {
	display: grid;
	grid-template-columns: auto 1fr;
	align-items: center;
	gap: 0.375rem 0.75rem;
	margin: 0;
	font-size: 0.875rem;
	line-height: 1.5;
}




/* Bootstrap Italia dimensiona la X di chiusura su 1em di glifo e 0.25em di padding: il bersaglio
   risultante è di 24px, il minimo assoluto di WCAG 2.5.8. Qui viene portato ai 44px consigliati
   senza toccare il glifo, che resta centrato perché disegnato come background. */
.calendario-dettaglio .modal-header .btn-close {
	box-sizing: border-box;
	width: 44px;
	height: 44px;
	padding: 0;
}

/* Cella vuota: resta raggiungibile da tastiera (roving tabindex) ma non è un'azione */
.calendario-cella--vuota {
	cursor: default;
}

.calendario-cella:focus-visible {
	outline: 2px solid var(--bs-primary, #06c);
	outline-offset: -2px;
}

/* Ricarica del periodo: il contenuto resta visibile e viene solo attenuato, senza salti di layout */

.calendario-contenuto--occupato > .calendario-griglia,
.calendario-contenuto--occupato > .calendario-agenda,
.calendario-contenuto--occupato > .alert {
	opacity: 0.5;
	pointer-events: none;
}

.calendario-contenuto__spinner {
	position: absolute;
	inset: 0;
	z-index: 2;
	display: flex;
	align-items: flex-start;
	justify-content: center;
	padding-top: 4rem;
}

/* Bootstrap Italia dà alla finestra di dialogo `margin: 48px` ma lascia `--bs-modal-margin` al
   valore di Bootstrap (1.5rem), e su quella variabile sono calcolate le altezze di
   `.modal-dialog-scrollable` e `.modal-dialog-centered`: il riquadro risulta più alto dello spazio
   che ha e sborda di 32px sotto il bordo dello schermo. Allineare la variabile al margine vero
   rimette d'accordo i due valori senza toccare le regole della libreria. */
.calendario-dettaglio {
	--bs-modal-margin: 48px;
}

/* Su mobile il dettaglio del giorno si comporta da bottom-sheet: stesso markup, resa diversa (linee-guida-interfaccia §7) */
@media (max-width: 767.98px) {
	/* `max-width` va annullata insieme al margine: Bootstrap la fissa a 500px da 576px in su, e senza
	   margini automatici il foglio resterebbe largo 500px appoggiato al bordo sinistro, con una fascia
	   vuota a destra. Un bottom-sheet prende tutta la larghezza dello schermo. */
	.calendario-dettaglio .modal-dialog {
		align-items: flex-end;
		min-height: 100%;
		max-width: none;
		margin: 0;
	}

	.calendario-dettaglio .modal-content {
		max-height: 90vh;
		border-bottom-right-radius: 0;
		border-bottom-left-radius: 0;
	}

	.calendario-dettaglio .modal-content::before {
		width: 2.5rem;
		height: 0.25rem;
		margin: 0.5rem auto 0;
		content: "";
		background-color: var(--bs-border-color, #d9dadb);
		border-radius: 1rem;
	}
}

/* Riduzione del movimento: nessuna transizione sulle celle e sul dettaglio */
@media (prefers-reduced-motion: reduce) {
	.calendario-cella,
	.calendario-agenda__evento,
	.calendario-dettaglio .modal-dialog {
		transition: none;
	}
}


/* ── Compensazione delle barre ancorate in alto ──────────────────────────────── */
/*
 * Una barra `sticky-top` resta in alto mentre si scorre, e sposta il problema su ciò che finisce
 * sotto di essa: l'elemento che riceve il fuoco da tastiera e la sezione portata in vista quando
 * si apre una Classe. WCAG 2.2 SC 2.4.11 chiede che ciò che ha il fuoco non sia *interamente*
 * nascosto da contenuto dell'autore; la tecnica dedicata è C43, «Using CSS scroll-padding to
 * un-obscure content»: https://www.w3.org/WAI/WCAG22/Techniques/css/C43
 *
 * L'altezza non si scrive qui: la misura `osservaBarraAncorata` in browser-interop.js e la pubblica
 * nella custom property. Un valore fisso sarebbe sbagliato per costruzione, perché l'altezza cambia
 * con la larghezza della finestra (i comandi vanno a capo), con la lingua e con il numero di comandi
 * presenti — misurati 144px dove ne erano stati ipotizzati 120, cioè 24px di intestazione coperta.
 *
 * Il valore predefinito è `0px`: senza barra ancorata la regola non riserva nulla e non cambia il
 * comportamento delle altre pagine. `1rem` è il distacco dalla barra, perché l'elemento non risulti
 * appiccicato al suo bordo inferiore.
 */
html {
	scroll-padding-top: calc(var(--presenze-barra-altezza, 0px) + 1rem);
}