/* ══════════════════════════════════════════════════════════════
   Design tokens — ClickUp-inspired visual style
   ══════════════════════════════════════════════════════════════ */
:root {
    --sidebar-bg: #2D2B55;
    --sidebar-text: #C4C1E0;
    --sidebar-section: #9F9BCB; /* section labels: solid, dimmer than items, never opacity */
    --sidebar-active: #7B68EE;
    --sidebar-hover: rgba(255, 255, 255, 0.08);
    --header-bg: #FFFFFF;
    --content-bg: #F7F8FA;
    --row-hover: #F0F0F3;
    --text-primary: #1E1F21;
    --text-secondary: #6B6F76;
    --text-light: #9CA0A9;
    --accent-primary: #7B68EE;
    --accent-success: #6BC950;
    /* Il verde dei pulsanti pieni (`.btn-success`, e quindi «Nuovo …», NewButton.razor).
       `--accent-success` con il testo bianco ha un contrasto di 2,1:1 — sotto il 4,5:1 che il
       testo normale chiede (WCAG AA) — e «Nuovo ruolo» su quel verde non si leggeva. Questo e'
       lo stesso verde scurito: 5,1:1 a riposo, 6,7:1 al passaggio del mouse (04-10-2026).
       `--accent-success` resta per i filetti, i pallini e le bande, dove non c'e' testo sopra. */
    --accent-success-strong: #2F7D1E;
    --accent-success-strong-hover: #276A19;
    --accent-warning: #FFB800;
    --accent-danger: #FF4D4F;
    --border-subtle: #E8E8ED;
    --radius-sm: 4px;
    --radius-md: 8px;
    --radius-lg: 12px;
    /* Typography scale.
       The original scale started at 13px body / 11px meta, which read as cramped on a
       tablet held at arm's length — the primary target device. Every step moved up by
       roughly 1px. Sizes live here rather than inline so the scale can be retuned in one
       place; before this they were hardcoded at ~50 sites.
       Note: form controls stay pinned at 16px on mobile further down — anything smaller
       makes the WebView zoom on focus, and that override must not be tokenised away. */
    --fs-micro: 11px;
    --fs-meta: 12px;
    --fs-small: 13px;
    --fs-body: 14px;
    --fs-md: 15px;
    --fs-lg: 16px;
    --fs-title: 18px;
    --fs-page: 20px;
}

/* ══════════════════════════════════════════════════════════════
   Global typography
   ══════════════════════════════════════════════════════════════ */
html, body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    font-size: var(--fs-body);
    font-weight: 400;
    color: var(--text-secondary);
    line-height: 1.5;
    background-color: var(--content-bg);
    /* The app is embedded in a MAUI WebView: nothing may ever scroll sideways.
       `clip`, not `hidden`: hidden turns body into a scroll container and kills
       every `position: sticky` inside it — the sidebar scrolled away with the page. */
    overflow-x: clip;
    -webkit-text-size-adjust: 100%;
    -webkit-tap-highlight-color: rgba(123, 104, 238, 0.15);
}

/* Long words / URLs must never widen the layout on a narrow screen. */
body {
    overflow-wrap: break-word;
}

/* Off-canvas menu open — freeze the page underneath. */
body.is-scroll-locked {
    overflow: hidden;
}

h1, h2, h3, h4, h5, h6 {
    color: var(--text-primary);
    font-weight: 600;
}

h1 { font-size: var(--fs-page); }
h2 { font-size: var(--fs-title); }
h3 { font-size: var(--fs-lg); }
h4 { font-size: var(--fs-md); }

h1:focus {
    outline: none;
}

/* `.small` is included, and the size is absolute rather than Bootstrap's `0.875em`, because
   em compounds: a `<small class="text-muted">` or a `.text-muted.small` inherited 12 px and
   then shrank it again to 10.5 px, below the legibility floor. */
small, .small, .text-muted, .form-text {
    font-size: var(--fs-meta);
    color: var(--text-light);
}

/* ══════════════════════════════════════════════════════════════
   Links
   ══════════════════════════════════════════════════════════════ */
a, .btn-link {
    color: var(--accent-primary);
}

a:hover, .btn-link:hover {
    color: #6352d4;
}

/* ══════════════════════════════════════════════════════════════
   Buttons
   ══════════════════════════════════════════════════════════════ */
/* **Due misure, e basta** (27-09-2026, richiesta dell'utente: «dimensione standard dei
   pulsanti in tutta l'app»). Le pagine mescolavano `.btn` pieni, `.btn-sm` con il padding di
   Bootstrap e pulsanti ritoccati a mano, e due azioni affiancate uscivano alte 31 e 38 px.
   Adesso l'altezza la decide una variabile per misura, e nient'altro:
   - `.btn`      → **azione di pagina** (salva, nuovo, applica): 38 px;
   - `.btn-sm`   → **azione di riga** (modifica, elimina, vai): 31 px;
   - `.btn-lg`   → solo la pagina d'ingresso pubblica.
   Sotto i 768 px vince il minimo di 44 px al tocco (regole 46 e 48), più sotto. Chi scrive
   una pagina **non ritocca** altezza e padding di un pulsante: sceglie la misura. */
:root {
    --tm-btn-h: 38px;
    --tm-btn-h-sm: 31px;
}

.btn {
    font-size: var(--fs-body);
    font-weight: 500;
    border-radius: 6px;
    padding: 0.375rem 0.875rem;
    min-height: var(--tm-btn-h);
}

.btn-sm {
    font-size: var(--fs-small);
    padding: 0.25rem 0.625rem;
    min-height: var(--tm-btn-h-sm);
}

.btn-link {
    min-height: 0;
}

.btn-primary {
    color: #fff;
    background-color: var(--accent-primary);
    border-color: var(--accent-primary);
}

.btn-primary:hover, .btn-primary:focus {
    background-color: #6352d4;
    border-color: #6352d4;
}

.btn-outline-primary {
    color: var(--accent-primary);
    border-color: var(--accent-primary);
}

.btn-outline-primary:hover {
    background-color: var(--accent-primary);
    color: #fff;
}

.btn-outline-secondary {
    color: var(--text-secondary);
    border-color: var(--border-subtle);
}

.btn-outline-secondary:hover {
    background-color: var(--row-hover);
    border-color: var(--border-subtle);
    color: var(--text-primary);
}

.btn-danger {
    background-color: var(--accent-danger);
    border-color: var(--accent-danger);
}

.btn-success {
    color: #fff;
    background-color: var(--accent-success-strong);
    border-color: var(--accent-success-strong);
}

.btn-success:hover, .btn-success:focus, .btn-success:active {
    color: #fff;
    background-color: var(--accent-success-strong-hover);
    border-color: var(--accent-success-strong-hover);
}

/* «Nuovo …» (NewButton.razor): sotto i 576 px il testo corto, se la pagina ne ha dato uno. */
.tm-new .tm-new-short {
    display: none;
}

@media (max-width: 575.98px) {
    .tm-new:has(.tm-new-short) .tm-new-long {
        display: none;
    }

    .tm-new .tm-new-short {
        display: inline;
    }
}

.btn-warning {
    background-color: var(--accent-warning);
    border-color: var(--accent-warning);
    color: var(--text-primary);
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
    box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem rgba(123, 104, 238, 0.4);
}

/* Icon + text alignment in buttons */
.btn .bi {
    vertical-align: -0.125em;
}

/* Bottoni di sola icona.

   Il `vertical-align` qui sopra è una correzione pensata per l'icona **accanto al
   testo**: è il testo a fissare la riga di base, e l'icona si allinea a quella. Senza
   testo quella riga non c'è, e a decidere dove sta il glifo sono le sue metriche —
   che cambiano da un'icona all'altra. Due bottoni affiancati, la pausa e la croce,
   uscivano così di larghezze diverse e con le icone a quote diverse.

   `aspect-ratio: 1` li rende quadrati sull'altezza che hanno già: restano della
   dimensione decisa da `btn-sm`, quindi non si toccano i minimi tappabili. */
.btn-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    aspect-ratio: 1;
    line-height: 1;
}

.btn-icon > .bi {
    vertical-align: 0;
    font-size: 1rem;
}

/* ══════════════════════════════════════════════════════════════
   Forms
   ══════════════════════════════════════════════════════════════ */
.form-control, .form-select {
    font-size: var(--fs-body);
    border-color: var(--border-subtle);
    border-radius: var(--radius-sm);
    color: var(--text-primary);
}

.form-control:focus, .form-select:focus {
    border-color: var(--accent-primary);
    box-shadow: 0 0 0 0.2rem rgba(123, 104, 238, 0.15);
}

.form-label {
    font-size: var(--fs-body);
    font-weight: 500;
    color: var(--text-primary);
    margin-bottom: 0.25rem;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}

/* ══════════════════════════════════════════════════════════════
   Validation
   ══════════════════════════════════════════════════════════════ */
.valid.modified:not([type=checkbox]) {
    outline: 1px solid var(--accent-success);
}

.invalid {
    outline: 1px solid var(--accent-danger);
}

.validation-message {
    color: var(--accent-danger);
    font-size: var(--fs-meta);
}

/* ══════════════════════════════════════════════════════════════
   Content area
   ══════════════════════════════════════════════════════════════ */
.content {
    padding-top: 1.1rem;
}

/* ══════════════════════════════════════════════════════════════
   Tables — compact, no gridlines, hover rows
   ══════════════════════════════════════════════════════════════ */
.table {
    font-size: var(--fs-body);
    color: var(--text-secondary);
    border-color: var(--border-subtle);
    --bs-table-hover-bg: var(--row-hover);
}

.table > thead > tr > th {
    font-size: var(--fs-meta);
    font-weight: 500;
    text-transform: uppercase;
    color: var(--text-light);
    border-bottom-width: 1px;
    border-bottom-color: var(--border-subtle);
    padding: 0.5rem 0.75rem;
    letter-spacing: 0.03em;
}

.table > tbody > tr > td {
    padding: 0.5rem 0.75rem;
    vertical-align: middle;
    border-bottom: 1px solid var(--border-subtle);
    line-height: 1.3;
}

.table > tbody > tr:last-child > td {
    border-bottom: none;
}

.table-striped > tbody > tr:nth-of-type(odd) {
    --bs-table-striped-bg: rgba(0, 0, 0, 0.015);
}

/* ══════════════════════════════════════════════════════════════
   Cards
   ══════════════════════════════════════════════════════════════ */
.card {
    background: #fff;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
}

.card-header {
    background: #fff;
    border-bottom: 1px solid var(--border-subtle);
    font-size: var(--fs-md);
    font-weight: 600;
    color: var(--text-primary);
    padding: 0.75rem 1rem;
}

.card-body {
    padding: 0.75rem 1rem;
}

/* ══════════════════════════════════════════════════════════════
   Badges & pills — status tags
   ══════════════════════════════════════════════════════════════ */
.badge {
    font-size: var(--fs-meta);
    font-weight: 500;
    padding: 0.25em 0.6em;
    border-radius: 10px;
    letter-spacing: 0.02em;
}

.badge.bg-primary, .badge.text-bg-primary {
    background-color: var(--accent-primary) !important;
}

.badge.bg-success, .badge.text-bg-success {
    background-color: var(--accent-success) !important;
}

.badge.bg-warning, .badge.text-bg-warning {
    background-color: var(--accent-warning) !important;
    color: var(--text-primary) !important;
}

.badge.bg-danger, .badge.text-bg-danger {
    background-color: var(--accent-danger) !important;
}

/* ══════════════════════════════════════════════════════════════
   Icon + text utility
   ══════════════════════════════════════════════════════════════ */
.icon-text {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}

/* Flex children default to min-width:auto and refuse to shrink below their
   content — this lets long names truncate instead of widening the row. */
.min-w-0 {
    min-width: 0;
}

/* ══════════════════════════════════════════════════════════════
   Avatars
   ══════════════════════════════════════════════════════════════ */
.avatar {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: var(--fs-meta);
    font-weight: 600;
    color: #fff;
    background-color: var(--accent-primary);
    flex-shrink: 0;
}

/* ══════════════════════════════════════════════════════════════
   Modals
   ══════════════════════════════════════════════════════════════ */
.modal-content {
    border-radius: var(--radius-md);
    border: none;
    box-shadow: 0 4px 24px rgba(0, 0, 0, 0.12);
}

.modal-header {
    border-bottom: 1px solid var(--border-subtle);
    padding: 0.75rem 1rem;
}

.modal-title {
    font-size: var(--fs-title);
    font-weight: 600;
    color: var(--text-primary);
}

.modal-body {
    padding: 1rem;
}

.modal-footer {
    border-top: 1px solid var(--border-subtle);
    padding: 0.75rem 1rem;
}

/* Mobile: una modale prende tutta la **larghezza**, non tutta l'altezza (09-09-2026).

   Fino a quel giorno `.modal-content` aveva `min-height: 100dvh`, e su una conferma di due
   righe — «Annulla la rinuncia» — il risultato era una schermata quasi vuota: la domanda in
   cima, mille pixel di bianco in mezzo, e i due pulsanti parcheggiati in fondo allo schermo,
   così lontani dal testo da non sembrare più la risposta a quella domanda. Non era una
   finestra: era una pagina, e per giunta vuota.

   Con `max-height` la scheda cresce quanto le serve e si ferma allo schermo: una domanda
   corta resta una scheda centrata sopra la pagina — che è ciò che dice «rispondi e torni
   dov'eri» — e un modulo lungo riempie lo schermo come prima, scorrendo nel corpo.
   Perché la scheda sia centrata serve che `.modal-dialog` resti alto quanto lo schermo
   (`min-height` qui sotto) e che chi la usa metta `modal-dialog-centered`.

   100dvh (non 100vh) perché non finisca sotto la barra del browser o della WebView. */
@media (max-width: 767.98px) {
    .modal-dialog {
        margin: 0;
        max-width: none;
        min-height: 100dvh;
    }

    .modal-content {
        border-radius: 0;
        max-height: 100dvh;
        padding-bottom: env(safe-area-inset-bottom);
    }

    .modal-body {
        overflow-y: auto;
    }
}

/* ══════════════════════════════════════════════════════════════
   Empty states
   ══════════════════════════════════════════════════════════════ */
.empty-state {
    text-align: center;
    padding: 3rem 1.5rem;
    color: var(--text-light);
}

.empty-state .bi {
    font-size: 2.5rem;
    margin-bottom: 0.75rem;
    display: block;
}

.empty-state p {
    margin-bottom: 1rem;
    font-size: var(--fs-body);
}

/* ══════════════════════════════════════════════════════════════
   Pagination
   ══════════════════════════════════════════════════════════════ */
.page-link {
    color: var(--accent-primary);
    font-size: var(--fs-body);
    border-color: var(--border-subtle);
}

.page-item.active .page-link {
    background-color: var(--accent-primary);
    border-color: var(--accent-primary);
}

/* ══════════════════════════════════════════════════════════════
   Alerts
   ══════════════════════════════════════════════════════════════ */
.alert {
    font-size: var(--fs-body);
    border-radius: var(--radius-md);
    padding: 0.625rem 1rem;
}

/* ══════════════════════════════════════════════════════════════
   Checkbox styling
   ══════════════════════════════════════════════════════════════ */
.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

/* ══════════════════════════════════════════════════════════════
   MobileSheet — il pannello di destra che su mobile diventa un foglio
   (Components/Shared/MobileSheet.razor)

   Su desktop qui non c'e' niente: tre div senza stile, e la pagina a due
   colonne resta identica. Il foglio esiste **solo** sotto i 768 px, dove
   «la colonna di destra» sarebbe «due schermate piu' giu'».
   ══════════════════════════════════════════════════════════════ */
.tm-sheet-backdrop,
.tm-sheet-bar {
    display: none;
}

@media (max-width: 767.98px) {
    /* Chiuso non e' «vuoto»: e' **niente**. Il segnaposto «scegli qualcosa a sinistra»
       ha senso accanto all'elenco, non sotto — li' e' una scheda vuota che si scorre
       senza capire a che serva. */
    .tm-sheet:not(.is-open) {
        display: none;
    }

    /* Tranne dove il contenuto, a foglio chiuso, e' ancora l'unico modo di fare una cosa:
       il modulo che *aggiunge* quando non si sta correggendo niente. Vedi
       MobileSheet.InlineWhenClosed. */
    .tm-sheet.is-inline:not(.is-open) {
        display: block;
    }

    .tm-sheet.is-open .tm-sheet-backdrop {
        display: block;
        position: fixed;
        inset: 0;
        background: rgba(0, 0, 0, 0.4);
        /* Sopra la barra delle schede (1035) e la testata (1020), sotto il cassetto del
           menu (1040/1050) e gli avvisi (1200). */
        z-index: 1060;
    }

    .tm-sheet.is-open .tm-sheet-panel {
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        /* Non a tutto schermo: la striscia che resta scoperta in cima dice che sotto c'e'
           ancora l'elenco da cui si e' partiti, e che questo si chiude. */
        top: 8vh;
        z-index: 1061;
        display: flex;
        flex-direction: column;
        background: #fff;
        border-radius: var(--radius-lg) var(--radius-lg) 0 0;
        box-shadow: 0 -6px 24px rgba(0, 0, 0, 0.22);
        animation: tm-sheet-up 0.22s ease-out;
    }

    .tm-sheet.is-open .tm-sheet-bar {
        display: flex;
        align-items: center;
        gap: 8px;
        flex: 0 0 auto;
        padding: 10px 12px;
        border-bottom: 1px solid var(--border-subtle);
        border-radius: var(--radius-lg) var(--radius-lg) 0 0;
        background: var(--header-bg);
    }

    .tm-sheet.is-open .tm-sheet-title {
        flex: 1 1 auto;
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        font-size: var(--fs-md);
        font-weight: 600;
        color: var(--text-primary);
    }

    .tm-sheet.is-open .tm-sheet-close {
        flex: 0 0 auto;
        width: 40px;
        height: 40px;
        display: flex;
        align-items: center;
        justify-content: center;
        background: none;
        border: none;
        border-radius: 50%;
        color: var(--text-secondary);
        font-size: var(--fs-lg);
        -webkit-tap-highlight-color: transparent;
    }

    .tm-sheet.is-open .tm-sheet-body {
        flex: 1 1 auto;
        min-height: 0;
        overflow-y: auto;
        overscroll-behavior: contain;
        -webkit-overflow-scrolling: touch;
        padding: 12px 12px calc(16px + env(safe-area-inset-bottom));
    }

    /* Dentro il foglio la scheda non serve piu': il foglio **e'** la scheda, e un bordo
       dentro un bordo mangia 24 px di larghezza su uno schermo che ne ha 390. */
    .tm-sheet.is-open .tm-sheet-body > .card {
        border: none !important;
        box-shadow: none !important;
        margin-bottom: 0;
    }

    .tm-sheet.is-open .tm-sheet-body > .card > .card-body {
        padding: 0;
    }

    /* La testata della scheda la ripete la barra del foglio. `!important` perche' quelle
       testate portano spesso `d-flex`, che in Bootstrap e' gia' `!important`: senza,
       il titolo compariva due volte, una nella barra e una sotto. */
    .tm-sheet.is-open .tm-sheet-body > .card > .card-header {
        display: none !important;
    }

    /* Stessa cosa per un AccordionSection dentro il foglio: la sua testata e' un
       interruttore che qui non serve — il foglio e' gia' aperto, e chiuderla lascerebbe un
       foglio vuoto senza niente da ritoccare. Chi apre il foglio apre anche il contenitore
       (`_openForm = true` in tutte e quattro le schede di /admin/common).

       **`:only-child`, dal 05-09-2026** (segnalato dall'utente: «nella pagina di assegnazione
       ruoli ai volontari manca la parte in cui dai un nuovo ruolo»). Vale quando il
       contenitore **e'** tutto il foglio. Dove invece e' una sezione accanto ad altre —
       `/org/volunteer-roles`: la tabella dei ruoli attribuiti, e sotto «Attribuisci un
       ruolo», che arriva **chiusa** — nascondere la testata nascondeva l'unico modo di
       aprirla: la sezione restava alta zero pixel, e dal telefono attribuire un ruolo era
       impossibile. Misurato: `.acc` alto 0, `.acc-head` con `display: none`. */
    .tm-sheet.is-open .tm-sheet-body > .acc:only-child > .acc-head {
        display: none;
    }
}

@keyframes tm-sheet-up {
    from {
        opacity: 0;
        transform: translateY(24px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Un punto della pagina a cui un pulsante porta (timeMgr.scrollToId). La testata e'
   `sticky`: senza questo margine `scrollIntoView` incolla l'ancora al bordo alto della
   finestra e la testata le finisce sopra, cioe' proprio la riga che si voleva far vedere. */
.tm-scroll-anchor {
    scroll-margin-top: 16px;
}

@media (max-width: 767.98px) {
    .tm-scroll-anchor {
        scroll-margin-top: calc(60px + env(safe-area-inset-top));
    }
}

/* ══════════════════════════════════════════════════════════════
   Ricerca sopra un elenco (Components/Shared/ListSearch.razor)
   ══════════════════════════════════════════════════════════════ */
.list-search-box {
    max-width: 320px;
    flex: 1 1 220px;
}

@media (max-width: 767.98px) {
    /* Tutta la larghezza: un campo di ricerca stretto meta' schermo, con il conteggio
       accanto, lascia visibili tre lettere di quello che si sta cercando. */
    .list-search-box {
        flex: 1 1 100%;
        max-width: none;
    }
}

/* ══════════════════════════════════════════════════════════════
   Il numero sulla campanella (Components/Layout/NotificationBell.razor)

   Misure proprie e non ereditate: dentro un `btn-sm` il `.badge` di Bootstrap
   vale 0.75em di 0.875rem, cioe' 10,5 px — sotto la soglia degli 11 px che
   /dev/ui-audit segnala. E sta **dentro** l'ingombro del pulsante: con
   `start-100 translate-middle` meta' pillola usciva a destra e su mobile finiva
   sotto l'avatar dell'utente, che le sta accanto a 8 px.
   ══════════════════════════════════════════════════════════════ */
.notif-badge {
    position: absolute;
    top: -5px;
    right: -5px;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 999px;
    background-color: var(--accent-danger);
    color: #fff;
    font-size: 11px;
    font-weight: 600;
    line-height: 1;
    /* Un filo del colore della testata: sul bordo del pulsante il rosso su grigio
       si legge come una macchia, e il numero dentro perde il contorno. */
    box-shadow: 0 0 0 2px var(--header-bg);
    pointer-events: none;
}

@media (max-width: 767.98px) {
    .notif-badge {
        min-width: 19px;
        height: 19px;
        font-size: 11px;
    }
}

/* ══════════════════════════════════════════════════════════════
   Toast notifications — top-right, toastr-style
   ══════════════════════════════════════════════════════════════ */
.toast-container-top-right {
    position: fixed;
    top: 16px;
    right: 16px;
    z-index: 1200;
    display: flex;
    flex-direction: column;
    gap: 8px;
    max-width: 380px;
    width: 100%;
    pointer-events: none;
}

.toast-item {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 12px 16px;
    border-radius: var(--radius-md);
    color: #fff;
    font-size: var(--fs-body);
    line-height: 1.4;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    pointer-events: auto;
    animation: toast-slide-in 0.3s ease-out;
}

.toast-item .toast-body {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: 1;
}

.toast-item .toast-body .bi {
    font-size: var(--fs-lg);
    flex-shrink: 0;
}

.toast-close {
    background: none;
    border: none;
    color: rgba(255, 255, 255, 0.7);
    cursor: pointer;
    padding: 0;
    font-size: var(--fs-page);
    line-height: 1;
    flex-shrink: 0;
    margin-left: 4px;
}

.toast-close:hover {
    color: #fff;
}

.toast-success {
    background-color: var(--accent-success);
}

.toast-warning {
    background-color: var(--accent-warning);
    color: var(--text-primary);
}

.toast-warning .toast-close {
    color: rgba(0, 0, 0, 0.5);
}

.toast-warning .toast-close:hover {
    color: var(--text-primary);
}

.toast-error {
    background-color: var(--accent-danger);
}

@keyframes toast-slide-in {
    from {
        opacity: 0;
        transform: translateX(100%);
    }
    to {
        opacity: 1;
        transform: translateX(0);
    }
}

@keyframes toast-slide-up {
    from {
        opacity: 0;
        transform: translateY(60%);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Su mobile gli avvisi stanno **in basso**, non in cima (03-09-2026, richiesta dell'utente:
   «quando faccio una modifica o viene un alert copre completamente la parte alta, quindi
   anche il menu»). La striscia in cima e' larga quanto lo schermo meno 16 px, e un avviso di
   due righe copriva tutta la testata: nome dell'applicazione, selettore dell'associazione,
   campanella e hamburger sparivano per quattro secondi — e su un **errore**, che non se ne va
   da solo, finche' non lo si chiudeva a mano. In basso non copre niente che serva: sta sopra
   la barra delle schede, che resta tappabile. */
@media (max-width: 767.98px) {
    .toast-container-top-right {
        top: auto;
        bottom: calc(64px + 12px + env(safe-area-inset-bottom));
        right: 8px;
        left: 8px;
        max-width: none;
        width: auto;
        /* La pila cresce verso l'alto: l'ultimo arrivato resta il piu' vicino al pollice
           senza spingere gli altri fuori dallo schermo. */
        flex-direction: column-reverse;
    }

    /* Entra dal basso: da destra, con la striscia larga quanto lo schermo, era un salto
       laterale che finiva a meta' strada. */
    .toast-item {
        animation-name: toast-slide-up;
    }

    /* Close button large enough to tap (warning/error toasts stay open). */
    .toast-close {
        min-width: 32px;
        min-height: 32px;
        display: flex;
        align-items: center;
        justify-content: center;
    }
}

/* ══════════════════════════════════════════════════════════════
   Blazor error boundary
   ══════════════════════════════════════════════════════════════ */
.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "Si è verificato un errore."
    }

/* ══════════════════════════════════════════════════════════════
   Tables → stacked card list on mobile

   Add `table-mobile` to any <table> and `data-label="…"` to each <td>.
   Below 768 px the table collapses into one card per row with
   label–value pairs, so nothing ever scrolls sideways.
   Cells with no data-label (action buttons) span the full card width.
   ══════════════════════════════════════════════════════════════ */
@media (max-width: 767.98px) {
    table.table-mobile,
    table.table-mobile > tbody,
    table.table-mobile > tfoot {
        display: block;
        width: 100%;
    }

    table.table-mobile > thead {
        display: none;
    }

    table.table-mobile > tbody > tr,
    table.table-mobile > tfoot > tr {
        display: block;
        background: #fff;
        border: 1px solid var(--border-subtle);
        border-radius: var(--radius-md);
        padding: 6px 12px;
        margin-bottom: 8px;
    }

    table.table-mobile > tfoot > tr {
        background: #fafafc;
        font-weight: 600;
    }

    /* **Il colore della riga sopravvive alla scheda** (03-09-2026, richiesta dell'utente).
       Bootstrap tinge `tr.table-danger` dipingendo le *celle* (`--bs-table-bg` applicato a
       ogni `td`), e la regola qui sotto azzera lo sfondo delle celle con un `!important`
       per neutralizzare le zebrature: il risultato era che su mobile la riga di un posto
       scoperto tornava bianca come tutte le altre, e la distinzione fra coperto e non
       coperto restava affidata alla sola parola «Scoperto». Qui il colore si rimette dove
       su mobile vive davvero: sulla scheda. Il filetto a sinistra e' quello che si vede per
       primo scorrendo l'elenco col pollice. */
    table.table-mobile > tbody > tr.table-success {
        background: var(--bs-success-bg-subtle, #d1e7dd);
        border-color: var(--bs-success-border-subtle, #a3cfbb);
        border-left: 4px solid var(--accent-success);
    }

    table.table-mobile > tbody > tr.table-warning {
        background: var(--bs-warning-bg-subtle, #fff3cd);
        border-color: var(--bs-warning-border-subtle, #ffe69c);
        border-left: 4px solid var(--accent-warning);
    }

    table.table-mobile > tbody > tr.table-danger {
        background-color: rgba(220, 53, 69, 0.07);
        border-color: var(--bs-danger-border-subtle, #f1aeb5);
        border-left: 4px solid var(--accent-danger);
    }

    table.table-mobile > tbody > tr:last-child,
    table.table-mobile > tfoot > tr:last-child {
        margin-bottom: 0;
    }

    /* Neutralise Bootstrap striping/hover — each row is already a card. */
    table.table-mobile > tbody > tr > td,
    table.table-mobile > tfoot > tr > td {
        display: flex;
        align-items: flex-start;
        justify-content: space-between;
        /* Il valore va a capo sotto l'etichetta quando non ci sta: senza, un contenuto
           largo — le cinque stelle da 44 px della valutazione — sbordava a destra e la
           pagina scorreva in orizzontale, che e' proprio cio' che questa tabella evita. */
        flex-wrap: wrap;
        gap: 12px;
        padding: 5px 0;
        border: none !important;
        box-shadow: none !important;
        background-color: transparent !important;
        text-align: right;
        line-height: 1.4;
        min-width: 0;
    }

    table.table-mobile > tbody > tr > td::before,
    table.table-mobile > tfoot > tr > td::before {
        content: attr(data-label);
        flex: 0 0 auto;
        max-width: 45%;
        text-align: left;
        font-size: var(--fs-meta);
        font-weight: 500;
        line-height: 1.6;
        letter-spacing: 0.03em;
        text-transform: uppercase;
        color: var(--text-light);
    }

    /* Unlabelled cells (action buttons, checkboxes) take the whole width. */
    table.table-mobile > tbody > tr > td:not([data-label]),
    table.table-mobile > tfoot > tr > td:not([data-label]),
    table.table-mobile > tbody > tr > td[data-label=""] {
        justify-content: flex-end;
        flex-wrap: wrap;
        text-align: left;
    }

    table.table-mobile > tbody > tr > td:not([data-label])::before,
    table.table-mobile > tfoot > tr > td:not([data-label])::before,
    table.table-mobile > tbody > tr > td[data-label=""]::before {
        content: none;
    }

    /* Action cells: buttons stretch and wrap instead of overflowing. */
    table.table-mobile td.tm-actions {
        justify-content: flex-start !important;
        gap: 8px;
        padding-top: 10px;
        margin-top: 4px;
        border-top: 1px solid var(--border-subtle) !important;
    }

    table.table-mobile td.tm-actions .btn {
        flex: 1 1 auto;
    }

    /* Form controls inside a card cell must not be squeezed. */
    table.table-mobile td > .form-control,
    table.table-mobile td > .form-select,
    table.table-mobile td > .input-group {
        max-width: 60%;
    }

    /* Multi-control cells (qualification picker, volunteer search) take the free width. */
    table.table-mobile td > .stq-quals,
    table.table-mobile td > .vss-wrapper {
        flex: 1 1 auto;
        min-width: 0;
        text-align: left;
    }

    /* Empty-state row ("Nessun dato") spans the card. */
    table.table-mobile > tbody > tr > td[colspan] {
        justify-content: center;
        text-align: center;
    }

    table.table-mobile > tbody > tr > td[colspan]::before {
        content: none;
    }
}

/* Un valore che ha una spiegazione dietro (07-09-2026): il tratteggio dice che passandoci
   sopra si legge da dove viene. Senza, il `title` c'è e nessuno lo cerca — è la stessa
   ragione per cui una nota nascosta e una nota assente si equivalgono. */
.tm-help {
    text-decoration: underline dotted;
    text-underline-offset: 2px;
    cursor: help;
}

/* ══════════════════════════════════════════════════════════════
   Correzione in riga (07-09-2026)
   Il modulo che corregge un record si apre **sotto il record**, dentro la tabella:
   `<tr class="tm-inline-edit"><td colspan="N">…`. Prima stava in cima alla pagina e chi
   premeva Modifica sulla riga trenta non vedeva succedere niente.
   Il filetto viola a sinistra lega visivamente il modulo alla riga da cui e' uscito.
   ══════════════════════════════════════════════════════════════ */
tr.tm-inline-edit > td {
    background: var(--content-bg);
    border-left: 3px solid var(--accent-primary);
    padding: 12px 16px;
}

@media (max-width: 767.98px) {
    /* La riga è già una scheda a sé: qui serve solo disfare l'allineamento centrato che
       `td[colspan]` riceve per gli stati vuoti ("Nessun dato"), che qui sarebbe sbagliato. */
    table.table-mobile > tbody > tr.tm-inline-edit > td[colspan] {
        display: block;
        justify-content: flex-start;
        text-align: left;
        background: transparent;
        border-left: none;
        padding: 0;
    }

    table.table-mobile > tbody > tr.tm-inline-edit {
        border-left: 3px solid var(--accent-primary);
        background: var(--content-bg);
    }
}

/* ══════════════════════════════════════════════════════════════
   Mobile responsiveness
   ══════════════════════════════════════════════════════════════ */
@media (max-width: 767.98px) {
    /* 44 px touch targets. inline-flex (not the inline-block default) so the
       label stays vertically centred inside the taller box. */
    .btn:not(.btn-close) {
        min-height: 44px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        gap: 6px;
    }

    /* Stacked icon-over-label tiles opt out of the row direction. */
    .btn.flex-column {
        gap: 0;
    }

    .btn-link {
        min-height: auto;
    }

    /* Checkboxes are the smallest thing anyone has to hit. Bootstrap's 1em box lands at
       ~18 px; enlarge the box and give the whole row a 44 px tap area via the label. */
    .form-check-input {
        width: 1.5em;
        height: 1.5em;
        margin-top: 0.1em;
    }

    /* **Non inline-flex** (corretto il 03-09-2026, segnalato dall'utente: «la scritta non
       viene bellina»). Una label ricca — un <strong> col nome e uno <span> con la
       spiegazione — dentro un inline-flex diventa due *elementi flex in riga*, e il
       `flex-wrap` predefinito e' `nowrap`: i due pezzi si schiacciavano uno accanto
       all'altro invece di scorrere come testo, e la spiegazione usciva a filo di una riga
       sola. Qui basta il testo normale: a centrarla verticalmente ci pensa gia'
       `align-items` del `.form-check` che la contiene. */
    .form-check-label {
        padding-top: 2px;
        min-height: 28px;
        display: inline-block;
        line-height: 1.35;
    }

    /* A bare .form-check (no label) still needs a comfortable row. */
    .form-check {
        min-height: 32px;
        display: flex;
        /* In cima e non al centro: su una spunta con la spiegazione sotto, il quadratino
           centrato verticalmente finiva accanto alla seconda riga di testo. */
        align-items: flex-start;
        gap: 4px;
    }

    /* Una spunta con la spiegazione: il nome sulla sua riga, il perche' sotto. Su 390 px
       «Assegnazione automatica — va da sola a chi ha tutte le certificazioni richieste»
       su una riga sola non ci sta, e spezzato a meta' parola non si legge. */
    .form-check-label .check-hint {
        display: block;
        margin-top: 1px;
    }

    .form-check-label .check-hint .check-dash {
        display: none;
    }

    /* 16 px avoids the automatic zoom-on-focus some WebViews apply.
       `.input-group-sm > .form-control` is listed explicitly: Bootstrap sets it with a
       two-class selector that outranks the single-class rules below, which left the
       inputs inside input groups at 12.25 px and zooming on focus. */
    .form-control,
    .form-select,
    .input-group-sm > .form-control,
    .input-group-sm > .form-select,
    .input-group-sm > .input-group-text,
    input[type="text"],
    input[type="email"],
    input[type="tel"],
    input[type="date"],
    input[type="time"],
    input[type="number"],
    input[type="search"],
    input[type="password"],
    textarea,
    select {
        font-size: 16px;
    }

    /* Text-entry controls are tap targets too — Bootstrap's default height is ~37 px. */
    .form-control,
    .form-select,
    .input-group-sm > .form-control,
    .input-group-sm > .form-select {
        min-height: 44px;
    }

    textarea.form-control {
        min-height: 66px;
    }

    .form-control-sm,
    .form-select-sm {
        font-size: 16px;
        min-height: 44px;
    }

    /* The dismiss "x" on chips/badges renders at 12 px — unusable with a thumb. Growing the
       button itself to 44 px would inflate every badge it sits in, so keep the glyph small
       and extend only the *hit area* with a pseudo-element: it is positioned, so it enlarges
       what the finger can hit without taking part in layout. */
    .btn-close-sm,
    .btn-close {
        position: relative;
        min-width: 24px;
        min-height: 24px;
        background-size: 0.65em;
    }

    .btn-close-sm::after,
    .btn-close::after {
        content: "";
        position: absolute;
        top: 50%;
        left: 50%;
        width: 44px;
        height: 44px;
        transform: translate(-50%, -50%);
    }

    .content {
        padding-left: 12px !important;
        padding-right: 12px !important;
        /* Clear of the gesture bar / home indicator. */
        padding-bottom: calc(1.5rem + env(safe-area-inset-bottom));
    }

    /* Page titles + their action buttons wrap instead of colliding. */
    h1, h2, h3 {
        min-width: 0;
        overflow-wrap: break-word;
    }

    h3 {
        font-size: var(--fs-title);
    }

    .card-body {
        padding: 0.75rem;
    }

    .card-header {
        padding: 0.625rem 0.75rem;
    }

    /* Bootstrap rows have no vertical gutter by default — stacked columns
       would otherwise touch each other once they wrap on mobile. */
    .row > [class*="col-"] + [class*="col-"] {
        margin-top: var(--bs-gutter-y, 0);
    }

    /* Any remaining wide block scrolls inside itself, never the page. */
    .table-responsive {
        -webkit-overflow-scrolling: touch;
    }

    pre, code {
        overflow-x: auto;
        max-width: 100%;
    }
}

/* **Le azioni di riga compaiono al passaggio del mouse** (27-09-2026, richiesta
   dell'utente). Una tabella di trenta righe con tre icone ciascuna erano novanta pulsanti
   accesi, e la colonna delle azioni si leggeva più dei dati. Una regola sola, e scritta su
   `(hover: hover)` e non come «nascosto, poi mostrato su touch»: dove il mouse non c'è la
   regola **non esiste**, quindi sul telefono e sul tablet le azioni restano visibili senza
   una seconda regola da tenere allineata. `:focus-within` le riaccende a chi arriva con la
   tastiera, altrimenti un Tab finirebbe su un pulsante invisibile.
   **Restano sempre accesi i pulsanti pieni** (`btn-primary`, `btn-success`, `btn-warning`):
   sono l'azione per cui si apre la pagina — «Accetta» in una coda, «Approva» in sala
   d'attesa — e nasconderli vorrebbe dire far cercare la cosa principale. `.tm-keep` è la
   via d'uscita per gli altri casi. `.invisible` resta quello che era: «qui non si applica». */
@media (hover: hover) {
    td.tm-actions .btn {
        transition: opacity .12s ease-in-out;
    }

    tr:not(:hover):not(:focus-within) > td.tm-actions .btn:not(.btn-primary):not(.btn-success):not(.btn-warning):not(.tm-keep) {
        opacity: 0;
    }
}

/* ══════════════════════════════════════════════════════════════
   Touch devices — hover-only affordances must stay reachable
   ══════════════════════════════════════════════════════════════ */
@media (hover: none) {
    .cal-add-btn,
    .cal-event-edit,
    .cal-week-event-edit {
        opacity: 1;
    }

    .cal-hour-plus {
        opacity: 0.35;
    }
}

/* ══════════════════════════════════════════════════════════════
   Utility: header row that wraps on narrow screens
   ══════════════════════════════════════════════════════════════ */
.page-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 1rem;
}

.page-header > * {
    min-width: 0;
}

.page-header .page-header-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

@media (max-width: 575.98px) {
    .page-header .page-header-actions {
        width: 100%;
    }

    .page-header .page-header-actions > .btn,
    .page-header .page-header-actions > a.btn {
        flex: 1 1 auto;
    }
}

/* ══════════════════════════════════════════════════════════════
   Calendar — Google Calendar style (month + week grids)
   ══════════════════════════════════════════════════════════════ */
.cal-toolbar {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 16px;
}

.cal-toolbar .cal-nav {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}

.cal-toolbar .cal-title {
    font-size: var(--fs-title);
    font-weight: 600;
    color: var(--text-primary);
    text-transform: capitalize;
    margin: 0 8px;
    min-width: 180px;
}

/* Il titolo mangia lo spazio che avanza, cosi' «Nuovo turno» finisce **contro il bordo
   destro** (06-09-2026, richiesta dell'utente). Prima era `margin-left:auto` sulle viste,
   che le spingeva a destra e ci si portava dietro il pulsante: con le viste passate in
   testa, quel margine sarebbe finito nel posto sbagliato. Spingere invece che tirare vuol
   dire anche che il pulsante resta a destra qualunque cosa ci sia in mezzo. */
.cal-toolbar .cal-title {
    flex: 1 1 auto;
}

.cal-toolbar .cal-views {
    display: inline-flex;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    overflow: hidden;
    background: #fff;
}

.cal-toolbar .cal-views .btn {
    border-radius: 0;
    border: none;
    background: #fff;
    color: var(--text-secondary);
    padding: 6px 14px;
}

.cal-toolbar .cal-views .btn.active {
    background: var(--accent-primary);
    color: #fff;
}

/* Sits outside .cal-views so it keeps the primary button styling. */
.cal-toolbar .cal-new-btn {
    flex-shrink: 0;
}

/* ── Month grid ─────────────────────────────────────────────── */
.cal-month {
    background: #fff;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    overflow: hidden;
}

.cal-month-head,
.cal-month-row {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
}

.cal-month-head {
    background: #fafafc;
    border-bottom: 1px solid var(--border-subtle);
}

.cal-month-head .cal-dow {
    padding: 8px 10px;
    font-size: var(--fs-meta);
    font-weight: 500;
    color: var(--text-light);
    text-transform: uppercase;
    text-align: center;
}

.cal-month-row {
    border-bottom: 1px solid var(--border-subtle);
    min-height: 110px;
}

.cal-month-row:last-child {
    border-bottom: none;
}

.cal-day {
    border-right: 1px solid var(--border-subtle);
    padding: 6px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-height: 110px;
    overflow: hidden;
}

.cal-day:last-child {
    border-right: none;
}

.cal-day.is-other-month {
    background: #fafafc;
}

.cal-day.is-other-month .cal-day-num {
    color: var(--text-light);
}

.cal-day-num {
    font-size: var(--fs-small);
    font-weight: 500;
    color: var(--text-secondary);
    align-self: flex-end;
    width: 22px;
    height: 22px;
    line-height: 22px;
    text-align: center;
    border-radius: 50%;
}

.cal-day.is-today .cal-day-num {
    background: var(--accent-primary);
    color: #fff;
}

/* Day-cell header (day number + add button) */
.cal-day-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 4px;
    min-height: 22px;
}

/* La riga del giorno **apre quel giorno** (06-09-2026, richiesta dell'utente). Era il
   gesto «nuovo turno» dal 04-09-2026, che era il ripiego per il telefono: ora la creazione
   passa dalla griglia oraria della vista Giorno, dove il tocco dice anche a che ora. Vedi
   il commento nel mese di Calendar.razor. */
.cal-day-head.is-openable {
    cursor: pointer;
    border-radius: var(--radius-sm);
}

.cal-day-head.is-openable:hover {
    background: rgba(123, 104, 238, 0.08);
}

.cal-add-btn {
    appearance: none;
    background: transparent;
    border: none;
    color: var(--text-light);
    width: 20px;
    height: 20px;
    border-radius: 50%;
    padding: 0;
    font-size: var(--fs-small);
    line-height: 1;
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.15s, background-color 0.15s, color 0.15s;
}

.cal-day:hover .cal-add-btn {
    opacity: 1;
}

.cal-add-btn:hover {
    background: var(--accent-primary);
    color: #fff;
}

/* Event row in month view: event + inline edit icon.
   Lo stacco fra due chip (06-09-2026): erano appoggiati l'uno all'altro e con due tipi di
   turno di colore simile facevano una barra sola. */
.cal-event-row {
    position: relative;
    display: flex;
    align-items: center;
    gap: 2px;
    margin-bottom: 2px;
}

.cal-event-row .cal-event {
    flex: 1 1 auto;
    min-width: 0;
}

.cal-event-edit {
    appearance: none;
    background: transparent;
    border: none;
    color: var(--text-light);
    width: 18px;
    height: 18px;
    padding: 0;
    font-size: var(--fs-meta);
    line-height: 1;
    border-radius: var(--radius-sm);
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.15s, color 0.15s, background-color 0.15s;
    flex-shrink: 0;
}

.cal-event-row:hover .cal-event-edit {
    opacity: 1;
}

.cal-event-edit:hover {
    color: var(--accent-primary);
    background: rgba(123, 104, 238, 0.1);
}

.cal-event {
    display: block;
    background: var(--accent-primary);
    color: #fff;
    text-decoration: none;
    border-radius: var(--radius-sm);
    padding: 2px 6px;
    font-size: var(--fs-meta);
    line-height: 1.3;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    border: none;
}

.cal-event:hover {
    color: #fff;
    filter: brightness(0.92);
}

.cal-event-time {
    font-weight: 500;
    margin-right: 4px;
    opacity: 0.9;
}

.cal-event.is-understaffed::after {
    content: " ●";
    color: var(--accent-warning);
}

/* ── Vista del socio: perché un turno è lì ───────────────────────
   Erano due fili da 3 px, uno viola e uno verde, disegnati *dentro* un chip
   che il tipo di turno colora già di suo: sopra un fondo blu la differenza
   fra i due non si vedeva, e un turno proprio sembrava uguale a uno in cui
   ci si era solo dichiarati disponibili (28-08-2026).

   Adesso la differenza non è una tinta ma la **forma**: un turno tuo è
   **pieno**, con l'anello continuo; una disponibilità è **vuota**, con il
   bordo tratteggiato. Si legge dall'altra parte della stanza, si legge in
   bianco e nero, e si legge da chi i colori non li distingue. Il colore del
   tipo di turno resta in tutti e due — pieno nel primo, come tinta chiara e
   barra a sinistra nel secondo. */
.cal-event.is-mine,
.cal-week-event.is-mine {
    box-shadow: inset 6px 0 0 var(--accent-primary), 0 0 0 2px var(--accent-primary);
    font-weight: 600;
}

/* Il fondo chiaro arriva dallo stile inline (`EventStyle`), che aggiunge la
   trasparenza al colore del tipo: qui si sistema tutto il resto, perché il
   testo del chip nasce bianco e su un fondo chiaro sparirebbe. */
.cal-event.is-available,
.cal-week-event.is-available {
    box-shadow: inset 6px 0 0 var(--accent-success);
    outline: 2px dashed var(--accent-success);
    outline-offset: -2px;
    color: var(--text-primary);
}

.cal-event.is-available:hover,
.cal-week-event.is-available:hover {
    color: var(--text-primary);
    filter: none;
    background-image: linear-gradient(rgba(0, 0, 0, 0.05), rgba(0, 0, 0, 0.05));
}

.cal-week-event.is-available .cal-slot-row {
    color: var(--text-secondary);
}

/* Il contrassegno: due parole nel chip della settimana, un'icona sola nel
   mese, dove una cella e' larga ~50 px. Il testo e' la ragione per cui la
   differenza si capisce senza aver letto la legenda. */
/* Niente maiuscoletto e niente spaziatura fra le lettere: in una colonna della
   settimana larga ~75 px ogni pixel serve, e in maiuscolo le due parole si
   tagliavano a meta'. Con l'ellissi il contrassegno degrada invece di sparire. */
.cal-flag {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    max-width: 100%;
    margin-bottom: 2px;
    padding: 0 6px;
    border-radius: 999px;
    font-size: var(--fs-micro);
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Viola pieno, non bianco: il fondo del chip e' il colore del **tipo** di turno,
   e un contrassegno bianco su un tipo giallo chiaro non si leggerebbe. */
.cal-flag.is-mine {
    background: var(--accent-primary);
    color: #fff;
}

.cal-flag.is-available {
    background: var(--accent-success);
    color: #fff;
}

.cal-event-mark {
    margin-right: 3px;
    flex-shrink: 0;
}

/* Ne' mio ne' in una mia fascia: un turno scoperto a cui potrei candidarmi.
   Resta — e' la strada per proporsi — ma smorzato, perche' il calendario del
   socio deve dire prima di tutto quando devo esserci io (28-08-2026). Solo
   nella vista del socio: per chi gestisce la classe non viene mai applicata. */
.cal-event.is-other,
.cal-week-event.is-other {
    opacity: 0.5;
    filter: saturate(0.55);
}

.cal-event.is-other:hover,
.cal-week-event.is-other:hover {
    opacity: 1;
    filter: none;
}

.cal-legend-chip.is-other {
    background: var(--bg-subtle, rgba(0, 0, 0, 0.05));
    color: var(--text-light);
    opacity: 0.75;
}

/* ── Disponibilita' trascinata sulla griglia (28-08-2026) ────────────
   `user-select: none` e' obbligatorio: senza, trascinare seleziona il testo
   delle ore e il gesto si vede come un'evidenziazione blu. */
.cal-hour-slot.is-draggable {
    cursor: cell;
    user-select: none;
}

.cal-hour-slot.is-draggable:hover .cal-hour-plus {
    opacity: 0.6;
}

/* ── I due esiti del trascinamento (05-09-2026) ──────────────────────
   Le due voci della domanda «disponibilita' o turno?». La riga di spiegazione
   sta sotto il titolo e rientra quanto l'icona, cosi' si leggono come due voci
   e non come quattro righe. Trasparenza e non `text-muted`: dentro un pulsante
   `outline` il testo diventa bianco al passaggio del mouse, e un grigio fisso
   ci resterebbe sopra illeggibile. */
.btn.cal-drag-choice {
    /* `display: block` e il selettore doppio non sono un vezzo: sotto i 768 px
       `.btn:not(.btn-close)` rende ogni pulsante `inline-flex` centrato, e le tre
       parti di questa voce — icona, titolo, spiegazione — diventavano tre colonne
       affiancate invece di due righe. Stessa trappola della `.form-check-label`
       del 03-09-2026. Serve la stessa specificita' di quella regola per scavalcarla. */
    display: block;
    text-align: left;
    white-space: normal;
    line-height: 1.35;
}

.cal-drag-choice > span {
    margin-left: 1.65rem;
    opacity: 0.75;
}

/* La selezione trascinata porta il colore di **quello che sta per diventare**
   (09-09-2026). Era verde in ogni caso — il verde delle disponibilità — anche quando il
   gesto stava creando un turno: chi trascinava guardava la griglia, non la riga sopra, e
   l'unica cosa che aveva sotto gli occhi diceva la cosa sbagliata. I due colori sono gli
   stessi del selettore «Crei» e della riga del gesto: viola = riguarda l'associazione,
   verde = riguarda solo te. Sono tre posti, e vanno cambiati insieme. */
.cal-week-body.is-making-availability .cal-hour-slot.is-drag-selected {
    background: rgba(107, 201, 80, 0.22);
    box-shadow: inset 0 0 0 1px rgba(107, 201, 80, 0.5);
}

.cal-week-body.is-making-shift .cal-hour-slot.is-drag-selected {
    background: rgba(123, 104, 238, 0.20);
    box-shadow: inset 0 0 0 1px rgba(123, 104, 238, 0.55);
}

/* Il ripiego per chi non passa dalla griglia con la classe (nessuno oggi, ma una
   selezione invisibile sarebbe un trascinamento che non si vede accadere). */
.cal-hour-slot.is-drag-selected {
    background: rgba(107, 201, 80, 0.22);
    box-shadow: inset 0 0 0 1px rgba(107, 201, 80, 0.5);
}

/* ── Il selettore «Crei» ────────────────────────────────────────────────────────────
   Due assi e due controlli: «Vedi» sceglie che cosa guardi, «Crei» che cosa produce il
   trascinamento. Stavano sullo stesso selettore fino al 09-09-2026, e sul livello
   *Insieme* — quello d'ingresso — il gesto non era deciso affatto: si trascinava, e solo
   dopo arrivava una domanda. La voce accesa si tinge di quello che creerà, cosi' la
   risposta e' leggibile senza leggere. */
.cal-create-mode {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
    margin: 0 0 10px;
}

.cal-create-mode-label {
    font-size: var(--fs-meta);
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: .5px;
    font-weight: 600;
    margin-right: 2px;
}

.cal-create-mode .btn {
    /* `display: block` con la stessa specificita' della regola del telefono: sotto i
       768 px `.btn:not(.btn-close)` rende ogni pulsante `inline-flex` centrato, e le due
       righe — titolo e «chi riguarda» — diventerebbero due colonne affiancate. Stessa
       trappola della `.form-check-label` del 03-09-2026 e della `.cal-drag-choice`. */
    display: block;
    text-align: left;
    line-height: 1.25;
    border: 1px solid var(--border-subtle);
    background: var(--bg-card, #fff);
    color: var(--text-secondary);
}

.cal-create-mode-who {
    display: block;
    font-size: var(--fs-meta);
    opacity: .75;
}

/* Sotto i 768 px le due voci vanno in **due colonne uguali**, non a capo una sotto
   l'altra (09-09-2026, misurato a 375 px): «Un turno» e «La mia disponibilità» hanno
   lunghezze molto diverse, e con `flex-wrap` finivano su due righe di larghezza diversa,
   con l'etichetta CREI appesa alla sola prima. Due colonne uguali dicono a colpo d'occhio
   che sono **due facce della stessa scelta**, e costano una riga invece di due — su una
   schermata dove la griglia comincia dopo la barra delle viste, il selettore delle date e
   il selettore «Vedi», ogni riga risparmiata e' calendario guadagnato. */
@media (max-width: 767.98px) {
    .cal-create-mode {
        display: grid;
        grid-template-columns: 1fr 1fr;
        column-gap: 6px;
        align-items: stretch;
    }

    /* L'etichetta prende tutta la riga sopra le due voci: in colonna accanto a loro si
       porterebbe via un terzo della larghezza per quattro lettere. */
    .cal-create-mode-label {
        grid-column: 1 / -1;
        margin: 0 0 2px;
    }

    .cal-create-mode .btn {
        padding: 6px 8px;
    }
}

/* Acceso: il colore lo decide **quale** voce e' attiva, non il contenitore, o le due
   voci spente si tingerebbero anche loro. */
.cal-create-mode .btn:first-of-type.active {
    background: rgba(123, 104, 238, .12);
    border-color: rgba(123, 104, 238, .55);
    color: #4b3fb0;
    font-weight: 600;
}

.cal-create-mode .btn:last-of-type.active {
    background: rgba(25, 135, 84, .12);
    border-color: rgba(25, 135, 84, .5);
    color: #14653b;
    font-weight: 600;
}

.cal-scope-hint {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin-bottom: 12px;
    padding: 8px 12px;
    background: #fff;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    font-size: var(--fs-meta);
    color: var(--text-secondary);
    line-height: 1.5;
}

.cal-scope-hint .bi {
    color: var(--text-light);
    flex-shrink: 0;
    line-height: 1.5;
}

.cal-legend-chip {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 999px;
    font-size: var(--fs-meta);
    white-space: nowrap;
}

/* Le stesse due forme dei chip del calendario, in piccolo: assegnato =
   **pieno**, disponibile = **vuoto col bordo tratteggiato**. La legenda deve
   assomigliare a quel che spiega, altrimenti spiega qualcos'altro. Valgono
   anche per le righe della vista Lista, che e' quella predefinita su mobile. */
.cal-legend-chip.is-mine {
    background: var(--accent-primary);
    color: #fff;
    font-weight: 600;
}

.cal-legend-chip.is-available {
    background: rgba(107, 201, 80, 0.12);
    color: #2f6d20;
    font-weight: 600;
    border: 1px dashed var(--accent-success);
    padding: 0 7px;
}

.cal-legend-chip.is-understaffed {
    background: rgba(255, 184, 0, 0.18);
    color: #8a6100;
}

.cal-day-more {
    font-size: var(--fs-meta);
    color: var(--text-light);
    padding: 0 6px;
}

/* ── Week grid ──────────────────────────────────────────────── */
/* La griglia oraria: sette colonne per la settimana, tre per la vista "3 giorni".
   Il numero arriva dal componente come `--cal-days`, cosi la CSS resta una sola. */
.cal-week {
    --cal-days: 7;
    background: #fff;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    overflow: hidden;
}

.cal-week-head {
    display: grid;
    grid-template-columns: 56px repeat(var(--cal-days), 1fr);
    border-bottom: 1px solid var(--border-subtle);
    background: #fafafc;
}

.cal-week-head .cal-dow {
    padding: 8px 6px;
    font-size: var(--fs-meta);
    font-weight: 500;
    color: var(--text-light);
    text-transform: uppercase;
    text-align: center;
    border-left: 1px solid var(--border-subtle);
}

.cal-week-head .cal-dow:first-child {
    border-left: none;
}

.cal-week-head .cal-dow strong {
    display: block;
    font-size: var(--fs-lg);
    font-weight: 600;
    color: var(--text-primary);
    margin-top: 2px;
}

.cal-week-head .cal-dow.is-today strong {
    color: var(--accent-primary);
}

/* La testata di una colonna apre quel giorno (06-09-2026). Solo dove le colonne sono piu'
   di una: nella vista Giorno la classe non c'e', e senza classe non c'e' nemmeno l'invito. */
.cal-week-head .cal-dow.is-openable {
    cursor: pointer;
}

.cal-week-head .cal-dow.is-openable:hover {
    background: rgba(123, 104, 238, 0.08);
}

.cal-week-body {
    position: relative;
    display: grid;
    grid-template-columns: 56px repeat(var(--cal-days), 1fr);
    overflow-x: auto;
}

.cal-hour-col {
    border-right: 1px solid var(--border-subtle);
}

.cal-hour-cell {
    height: 40px;
    font-size: var(--fs-meta);
    color: var(--text-light);
    padding: 2px 6px;
    text-align: right;
    border-bottom: 1px solid var(--border-subtle);
}

.cal-day-col {
    position: relative;
    border-right: 1px solid var(--border-subtle);
}

.cal-day-col:last-child {
    border-right: none;
}

.cal-day-col .cal-hour-slot {
    height: 40px;
    border-bottom: 1px solid var(--border-subtle);
}

/* Dal 05-09-2026 la cella oraria ha un gesto solo — il trascinamento — e ce l'hanno
   tutti: `is-clickable`, che era il click con cui chi gestisce creava un turno, non
   esiste piu' qui. Che cosa ne esce lo decide la pagina al rilascio. */
.cal-day-col .cal-hour-slot.is-draggable {
    position: relative;
    transition: background-color 0.15s;
}

.cal-day-col .cal-hour-slot.is-draggable:hover {
    background: rgba(123, 104, 238, 0.08);
}

.cal-hour-plus {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    color: var(--accent-primary);
    font-size: var(--fs-md);
    line-height: 1;
    opacity: 0;
    transition: opacity 0.15s;
    pointer-events: none;
}

.cal-day-col .cal-hour-slot.is-draggable:hover .cal-hour-plus {
    opacity: 0.7;
}

.cal-week-event-edit {
    appearance: none;
    position: absolute;
    top: 3px;
    right: 3px;
    background: rgba(255, 255, 255, 0.25);
    border: none;
    color: #fff;
    width: 20px;
    height: 20px;
    border-radius: var(--radius-sm);
    padding: 0;
    font-size: var(--fs-meta);
    line-height: 1;
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.15s, background-color 0.15s;
}

.cal-week-event:hover .cal-week-event-edit {
    opacity: 1;
}

.cal-week-event-edit:hover {
    background: rgba(255, 255, 255, 0.45);
}

.cal-week-event {
    position: absolute;
    left: 4px;
    right: 4px;
    background: var(--accent-primary);
    color: #fff;
    text-decoration: none;
    border-radius: var(--radius-sm);
    padding: 4px 6px;
    font-size: var(--fs-meta);
    line-height: 1.3;
    overflow: hidden;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    z-index: 1;
}

.cal-week-event:hover {
    color: #fff;
    filter: brightness(0.92);
}

/* Titolo e ora **sulla stessa riga** (06-09-2026): erano due righe da 13 px, e su un turno
   di un'ora — 40 px di griglia meno il padding — non restava spazio per i nomi, che finivano
   tagliati da `overflow:hidden`. L'ora si stringe per ultima perche' il titolo e' cio' che
   distingue due turni vicini. */
.cal-week-event-head {
    display: flex;
    align-items: baseline;
    gap: 4px;
    min-width: 0;
}

.cal-week-event strong {
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}

.cal-week-event small {
    flex: 0 0 auto;
    margin-left: auto;
    font-size: var(--fs-micro);
    color: rgba(255, 255, 255, 0.85);
}

/* Slot/role list inside week-view events.

   **Una griglia, non una colonna di righe flex** (06-09-2026, richiesta dell'utente: «metti
   il ruolo non grassetto e nome in grassetto, con i nomi allineati»). Con tre flex per riga
   ogni nome partiva dove finiva il *suo* ruolo, e su quattro posti i nomi facevano una
   scaletta. Qui la colonna di mezzo e' larga quanto il ruolo piu' lungo **di quel turno**,
   quindi i nomi cominciano tutti alla stessa ascissa; `minmax(0, …)` perche' su un chip
   stretto il ruolo deve poter cedere invece di sfondare il riquadro. */
.cal-slot-list {
    margin-top: 2px;
    display: grid;
    grid-template-columns: 12px minmax(0, max-content) minmax(0, 1fr);
    column-gap: 4px;
    row-gap: 1px;
    align-items: baseline;
    font-size: var(--fs-micro);
    line-height: 1.3;
    color: rgba(255, 255, 255, 0.9);
}

/* La riga sparisce come scatola e lascia i suoi tre figli alla griglia del padre: e' cio'
   che incolonna i nomi. Lo stato «vuoto» passa quindi ai figli, non alla riga. */
.cal-slot-row {
    display: contents;
}

.cal-slot-row.is-empty > * {
    opacity: 0.6;
}

/* Il simbolo del ruolo, nella griglia oraria. Larghezza fissa: i posti di un turno stanno
   incolonnati, e se ogni simbolo e' largo quanto il suo disegno le etichette accanto
   partono da ascisse diverse e la colonna non si scorre piu' con l'occhio. */
.cal-slot-icon {
    flex: 0 0 auto;
    width: 12px;
    text-align: center;
    font-size: var(--fs-micro);
    opacity: 0.85;
}

/* Il ruolo e' la didascalia, il nome e' la risposta: il grassetto sta sul nome
   (06-09-2026, richiesta dell'utente). Prima erano tutti e due a 500, cioe' nessuno dei
   due, e in un chip di quattro righe non c'era niente da cui cominciare a leggere. */
.cal-slot-role {
    font-weight: 400;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    opacity: .9;
}

.cal-slot-role::after {
    content: ":";
}

.cal-slot-vol {
    font-weight: 700;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.cal-slot-vacant {
    font-style: italic;
    font-weight: 400;
    opacity: 0.7;
}

/* **Il testo cresce con il riquadro** (10-10-2026, richiesta dell'utente: «scrivi le cose
   all'interno più grandi se c'è posto»). Le misure qui sopra sono tarate sul caso peggiore —
   un turno di un'ora nella Settimana, 40 px per un settimo di schermo — e valevano anche per
   un turno di otto ore nella vista Giorno, cioè un riquadro largo mezza pagina con dentro
   scritte da 11 px. Il riquadro è un **contenitore** (`container-type: size`): può esserlo
   perché la sua altezza e la sua larghezza le decide sempre lo stile in linea (top/height,
   lane), mai il contenuto — su un elemento ad altezza automatica `size` lo schiaccerebbe a
   zero.
   **Una misura che segue lo spazio, non due gradini** (riscritto lo stesso giorno, seconda
   segnalazione: «quando apro un turno con il giorno zoommato rimane ancora moltissimo spazio
   libero»). I gradini fermavano il testo a 16/14 px anche in un riquadro di 1000 × 400, cioè
   metà vuoto. Adesso la misura è **lo spazio per riga**: l'altezza del riquadro divisa per le
   righe che deve contenere (`--cal-rows`, scritto in linea da Calendar.razor: i posti, più
   titolo e bollino), e mai più di una frazione della larghezza — un riquadro alto e stretto
   (otto ore nella Settimana) non ha spazio per scritte larghe. `clamp` tiene le due estremità:
   **il minimo sono le misure di prima**, quindi un chip piccolo resta identico, e un tetto,
   perché un turno di dodici ore non diventi un manifesto.
   Il timore dei gradini — «due turni affiancati avrebbero scritte di misure diverse» — vale
   ancora a metà: due turni della stessa durata e con gli stessi posti hanno la stessa misura;
   due turni di durata diversa no, ed è giusto, perché hanno spazio diverso.
   `--cal-fit` resta una proprietà non registrata: si sostituisce **dove si usa**, quindi
   `cqh`/`cqw` si misurano sul riquadro (il contenitore dei figli), non su chi lo contiene. */
.cal-week-event {
    container-type: size;
    container-name: cal-event;
    --cal-rows: 4;
    --cal-fit: min(calc(100cqh / (var(--cal-rows) * 1.9)), 3.4cqw);
    /* Colonna: l'elenco dei posti prende l'altezza che avanza (sotto). */
    display: flex;
    flex-direction: column;
}

.cal-week-event .cal-week-event-head {
    flex: 0 0 auto;
    font-size: clamp(var(--fs-meta), calc(var(--cal-fit) * 1.25), calc(var(--fs-page) * 2));
}

/* Il bollino resta largo quanto il suo testo, non quanto il riquadro. */
.cal-week-event > .cal-flag {
    align-self: flex-start;
    flex: 0 0 auto;
}

.cal-week-event .cal-week-event-head small {
    font-size: clamp(var(--fs-micro), calc(var(--cal-fit) * 0.85), var(--fs-title));
}

.cal-week-event .cal-flag {
    font-size: clamp(var(--fs-micro), calc(var(--cal-fit) * 0.8), var(--fs-md));
}

/* Colonne e spazi in `em`: crescono con le scritte, o i simboli resterebbero da 12 px accanto a
   nomi da 24 e la colonna dei nomi non sarebbe più allineata a niente. */
/* Quando il testo ha raggiunto il tetto e il riquadro è ancora alto, lo spazio che avanza si
   distribuisce **fra le righe** (`space-evenly`) invece di restare tutto sotto l'ultima: un
   turno di otto ore nella vista Giorno si legge come una tabella che riempie il riquadro, non
   come quattro righe in cima e mezzo riquadro vuoto (seconda segnalazione del 10-10-2026). Su un
   chip piccolo lo spazio in più non c'è, e non cambia niente. **Niente `min-height: 0`**:
   l'elenco non deve mai diventare più basso del suo contenuto, o lo spazio libero diventa
   negativo e `space-evenly` ripiega su un allineamento al centro — su un chip di un'ora
   sparirebbero sopra il bordo proprio i primi posti. Così l'eccedenza resta sotto, tagliata dal
   riquadro, come prima. */
.cal-week-event .cal-slot-list {
    flex: 1 1 auto;
    font-size: clamp(var(--fs-micro), var(--cal-fit), calc(var(--fs-page) * 1.6));
    grid-template-columns: 1.15em minmax(0, max-content) minmax(0, 1fr);
    column-gap: .4em;
    row-gap: .15em;
    margin-top: .2em;
}

.cal-week-event .cal-slot-icon {
    width: 1.15em;
    font-size: 1em;
}

/* Solo nei riquadri larghi (Giorno, 3 giorni): in una colonna stretta della Settimana le scritte
   restano piccole, e quattro righe da 11 px sparse su trecento pixel si leggono peggio che
   tutte in cima. */
@container cal-event (min-width: 240px) {
    .cal-week-event .cal-slot-list {
        align-content: space-evenly;
    }
}

/* Slot/role display in list view — stessa griglia della settimana, stessa ragione:
   i nomi si incolonnano perche' la colonna del ruolo e' larga quanto il piu' lungo. */
.cal-list-slots {
    min-width: 0;
    display: grid;
    grid-template-columns: 16px minmax(0, max-content) minmax(0, 1fr);
    column-gap: 6px;
    align-items: baseline;
    font-size: var(--fs-small);
    line-height: 1.5;
}

.cal-list-slot {
    display: contents;
}

@media (max-width: 767.98px) {
    /* Dentro una scheda impilata l'elenco si appoggia alla colonna di destra: la griglia
       si stringe al contenuto invece di stendersi per tutta la cella. */
    .cal-list-slots {
        justify-content: end;
    }
}

/* Il simbolo del ruolo nella vista Lista: qui c'e' lo spazio, ed e' il punto in cui i
   ruoli di un turno si leggono davvero. Larghezza fissa per la stessa ragione di
   .cal-slot-icon: incolonnati, si scorrono; a larghezza variabile, no. */
.cal-list-slot-icon {
    flex: 0 0 auto;
    width: 16px;
    text-align: center;
    color: var(--text-light);
}

.cal-list-slot.is-empty .cal-list-slot-role {
    color: var(--text-light);
}

.cal-list-slot-role {
    font-weight: 400;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    color: var(--text-secondary);
}

.cal-list-slot-role::after {
    content: ":";
}

/* Il nome in grassetto anche qui: le due viste devono leggersi uguale. */
.cal-list-slot-vol {
    font-weight: 600;
    white-space: nowrap;
}

@media (max-width: 767.98px) {
    /* Toolbar: nav row, then title, then the view switcher — no overflow. */
    .cal-toolbar {
        gap: 8px;
        margin-bottom: 12px;
    }

    .cal-toolbar .cal-title {
        order: -1;
        flex: 1 1 100%;
        min-width: 0;
        margin: 0;
        font-size: var(--fs-lg);
    }

    /* **Cinque viste vanno a capo** (06-09-2026, con l'arrivo di «Giorno»). In una riga
       sola, a 375 px, ciascuna aveva ~62 px: «Settimana» si troncava a «Setti…» e i
       bersagli scendevano sotto il minimo. Andare a capo tiene i nomi interi e ogni voce
       alta 40 px; il bordo del contenitore cuce le due righe in un blocco solo. */
    .cal-toolbar .cal-views {
        margin-left: 0;
        flex: 1 1 100%;
        flex-wrap: wrap;
    }

    .cal-toolbar .cal-views .btn {
        padding: 6px 8px;
        font-size: var(--fs-small);
        flex: 1 1 30%;
        min-height: 40px;
        border-top: 1px solid transparent;
    }

    /* Le voci della seconda riga: un filetto le stacca da quelle sopra, altrimenti le
       due file sembrano una griglia senza ordine. */
    .cal-toolbar .cal-views .btn:nth-child(n + 4) {
        border-top-color: var(--border-subtle);
    }

    .cal-toolbar .cal-new-btn {
        flex: 0 0 auto;
    }

    .cal-day {
        min-height: 64px;
        padding: 3px;
    }

    .cal-month-row {
        min-height: 64px;
    }

    .cal-day-num {
        font-size: var(--fs-meta);
        width: 20px;
        height: 20px;
        line-height: 20px;
    }

    /* A month cell is ~50 px wide — only one of time or title fits. Keep the TIME:
       shift titles are long and nearly identical ("Turno ordinario", "Turno Protezione
       Civile"), so they all truncated to the same useless "Turno…", while the start time
       is four characters and actually tells the shifts apart. The type stays readable
       through the chip colour, and the full title is still in the tooltip and in the
       Lista view, which is the default on mobile. */
    /* 24 px, non 16 (04-09-2026, misurato sul telefono: erano 42x16). Le 44 della guida
       qui non sono raggiungibili e non sarebbe onesto fingerlo — tre chip da 44 piu' la
       riga del giorno fanno 158 px per riga, cioe' un mese alto quasi mille pixel — ma 16
       era sotto la meta' del minimo, con i chip di due giorni diversi a 4 px l'uno
       dall'altro. Chi vuole l'elenco leggibile ha la vista Lista, che sul telefono e'
       gia' quella di partenza (vedi OnAfterRenderAsync in Calendar.razor). */
    .cal-event {
        display: flex;
        align-items: center;
        justify-content: center;
        min-height: 24px;
        font-size: var(--fs-micro);
        padding: 1px 3px;
        line-height: 1.25;
        text-align: center;
    }

    .cal-event-title {
        display: none;
    }

    .cal-event-time {
        margin-right: 0;
        opacity: 1;
    }

    /* Il «+» mangerebbe la cella — e' larga ~46 px e il numero ne prende gia' 20 — quindi
       resta il tocco sul giorno, che dal 06-09-2026 **apre quel giorno**: di li' la griglia
       oraria fa creare il turno all'ora giusta, che dal mese non si poteva dire. Qui gli si
       da' l'altezza per essere un bersaglio. */
    .cal-add-btn {
        display: none;
    }

    .cal-day-head.is-openable {
        min-height: 26px;
    }

    .cal-event-edit {
        display: none;
    }

    /* Week view keeps its real column widths and scrolls inside its card. */
    .cal-week {
        overflow-x: auto;
    }

    /* La larghezza segue il **numero di colonne**, non un numero fisso (04-09-2026).
       Con 620 px buoni per tutte e due le viste, «3 giorni» — che esiste apposta per lo
       schermo stretto — scorreva in orizzontale come la settimana e ne mostrava uno e
       mezzo: 562 px di griglia dentro 345 di scheda, misurati. Con 56 px di colonna oraria
       piu' 96 per giorno, tre giorni fanno 344 e ci stanno per intero; sette restano 728 e
       scorrono, perche' sette colonne leggibili su un telefono non esistono — ma ora sono
       larghe 96 e non 81, e «06:00 – 14:00» non va piu' a capo dentro un numero. */
    .cal-week-head,
    .cal-week-body {
        min-width: calc(56px + var(--cal-days) * 96px);
    }

    .cal-week-body {
        font-size: var(--fs-micro);
    }
}

/* ══════════════════════════════════════════════════════════════
   Chat
   ══════════════════════════════════════════════════════════════ */
.chat-scroll {
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    height: 55vh;
    min-height: 220px;
}

.chat-bubble {
    display: inline-block;
    max-width: 80%;
    padding: 8px 10px;
    border-radius: var(--radius-md);
    text-align: left;
    overflow-wrap: anywhere;
}

.chat-composer {
    display: flex;
    gap: 8px;
    align-items: center;
}

.chat-composer .form-control {
    flex: 1 1 auto;
    min-width: 0;
}

@media (max-width: 767.98px) {
    .chat-scroll {
        height: 58dvh;
    }

    .chat-bubble {
        max-width: 88%;
    }

    /* Keep the composer on one line: labels collapse to icons. */
    .chat-composer .btn {
        flex: 0 0 auto;
        padding-left: 0.75rem;
        padding-right: 0.75rem;
    }

    .chat-composer .btn-label {
        display: none;
    }
}

/* ── Searchable volunteer select ────────────────────────────────── */
.vss-wrapper {
    position: relative;
}

.vss-input-wrap {
    position: relative;
}

.vss-input {
    padding-right: 28px;
}

.vss-clear {
    position: absolute;
    top: 50%;
    right: 6px;
    transform: translateY(-50%);
    appearance: none;
    background: transparent;
    border: none;
    color: var(--text-light);
    font-size: var(--fs-md);
    line-height: 1;
    cursor: pointer;
    padding: 2px;
    border-radius: 50%;
}

.vss-clear:hover {
    color: var(--accent-danger);
    background: rgba(255, 77, 79, 0.1);
}

/* La rotella di `SearchPicker` mentre la scelta si sta scrivendo: sta dove sta la crocetta
   del cugino, che li' non c'e' — quel campo non tiene nessuna selezione. */
.vss-busy {
    position: absolute;
    top: 50%;
    right: 8px;
    transform: translateY(-50%);
    color: var(--text-light);
    line-height: 1;
}

.vss-dropdown {
    list-style: none;
    margin: 2px 0 0;
    padding: 4px 0;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 1000;
    background: #fff;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
    max-height: 240px;
    overflow-y: auto;
}

.vss-item {
    padding: 6px 10px;
    font-size: var(--fs-body);
    color: var(--text-primary);
    cursor: pointer;
    display: flex;
    flex-direction: column;
    line-height: 1.3;
}

.vss-item:hover {
    background: var(--row-hover);
}

.vss-item.is-selected {
    background: rgba(123, 104, 238, 0.1);
    color: var(--accent-primary);
    font-weight: 500;
}

.vss-item .vss-sub {
    color: var(--text-light);
    font-size: var(--fs-meta);
}

.vss-empty {
    padding: 8px 10px;
    font-size: var(--fs-small);
    color: var(--text-light);
    font-style: italic;
}

/* ── Permissions checkbox list (multi-select) ────────────────────── */
.permission-checkbox-list {
    background: #fff;
    max-height: 360px;
    overflow-y: auto;
}

.permission-checkbox-list .perm-group:last-child {
    margin-bottom: 0 !important;
}

.permission-checkbox-list .form-check {
    padding: 2px 0 2px 1.5em;
}

.permission-checkbox-list .form-check-label {
    color: var(--text-primary);
    cursor: pointer;
}

/* ── Mini calendar (ShiftEdit sidebar) ───────────────────────── */
.mini-cal {
    width: 100%;
    font-size: var(--fs-small);
    user-select: none;
}

.mini-cal-head {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    margin-bottom: 2px;
}

.mini-cal-dow {
    text-align: center;
    font-size: var(--fs-micro);
    font-weight: 500;
    color: var(--text-light);
    text-transform: uppercase;
    padding: 2px 0 4px;
}

.mini-cal-row {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
}

.mini-cal-cell {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 3px 1px;
    border-radius: var(--radius-sm);
    min-height: 34px;
    gap: 2px;
}

.mini-cal-num {
    font-size: var(--fs-small);
    line-height: 1;
    color: var(--text-primary);
    font-weight: 400;
}

.mini-cal-cell.is-other .mini-cal-num {
    color: var(--text-light);
}

.mini-cal-cell.is-today {
    background: rgba(123, 104, 238, 0.12);
}

.mini-cal-cell.is-today .mini-cal-num {
    color: var(--accent-primary);
    font-weight: 600;
}

.mini-cal-cell.is-selected {
    background: var(--accent-primary);
    border-radius: var(--radius-sm);
}

.mini-cal-cell.is-selected .mini-cal-num {
    color: #fff;
    font-weight: 600;
}

.mini-cal-cell.has-events {
    background: rgba(107, 201, 80, 0.08);
}

.mini-cal-dots {
    display: flex;
    gap: 2px;
    flex-wrap: nowrap;
    justify-content: center;
}

.mini-cal-dot {
    width: 5px;
    height: 5px;
    border-radius: 50%;
    flex-shrink: 0;
}

.mini-cal-cell.is-clickable {
    cursor: pointer;
    transition: background 0.15s;
}

.mini-cal-cell.is-clickable:hover {
    background: rgba(123, 104, 238, 0.08);
}

/* ── Day detail timeline (ShiftEdit sidebar) ─────────────────── */
.day-tl-hour {
    position: absolute;
    left: 0;
    width: 100%;
    border-top: 1px solid var(--border-subtle);
    pointer-events: none;
}

.day-tl-hour span {
    position: absolute;
    top: -7px;
    left: 0;
    font-size: 10px;
    color: var(--text-light);
    background: #fff;
    padding-right: 4px;
}

.day-tl-event {
    position: absolute;
    left: 36px;
    right: 4px;
    background: #fff;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    padding: 3px 6px;
    overflow: hidden;
    z-index: 1;
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.day-tl-event-head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 4px;
}

.day-tl-slots {
    display: flex;
    flex-direction: column;
    gap: 0;
}

.day-tl-slot {
    display: flex;
    justify-content: space-between;
    gap: 4px;
    line-height: 1.3;
}

.day-tl-coverage {
    height: 3px;
    background: #eee;
    border-radius: 2px;
    margin-top: 2px;
    overflow: hidden;
}

.day-tl-coverage-bar {
    height: 100%;
    border-radius: 2px;
    transition: width 0.2s;
}

.day-tl-coverage-bar.is-full {
    background: var(--accent-success);
}

.day-tl-coverage-bar.is-partial {
    background: var(--accent-warning);
}

.day-tl-gap {
    position: absolute;
    left: 36px;
    right: 4px;
    background: repeating-linear-gradient(
        -45deg,
        transparent, transparent 4px,
        rgba(255, 77, 79, 0.06) 4px,
        rgba(255, 77, 79, 0.06) 8px
    );
    border: 1px dashed rgba(255, 77, 79, 0.25);
    border-radius: var(--radius-sm);
    z-index: 0;
}

/* ══════════════════════════════════════════════════════════════
   Mobile app shell — bottom tab bar, install banner, offline pill
   Only active below 768px; on wider viewports the classic
   sidebar + header layout is untouched.

   The off-canvas drawer is NOT here: it belongs to MobileNav.razor,
   the interactive island in the header. The "Menu" tab clicks that
   island's hamburger through app-shell.js, so there is one drawer
   and not two implementations drifting apart.
   ══════════════════════════════════════════════════════════════ */
.mobile-tabbar {
    display: none;
}

.mobile-tab {
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    min-height: 56px;
    padding: 6px 2px 4px;
    background: none;
    border: none;
    color: var(--text-light);
    /* --fs-micro, not 10px: anything smaller is flagged by /dev/ui-audit. */
    font-size: var(--fs-micro);
    font-weight: 500;
    line-height: 1.1;
    text-decoration: none;
    -webkit-tap-highlight-color: transparent;
}

.mobile-tab:hover,
.mobile-tab:focus {
    color: var(--text-secondary);
    text-decoration: none;
}

.mobile-tab .bi {
    font-size: 19px;
    line-height: 1;
}

/* NavLink adds .active on the matching route. */
.mobile-tab.active {
    color: var(--accent-primary);
}

.mobile-tab-label {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* "Aggiungi a schermata Home" banner, injected by pwa.js */
.pwa-install-banner {
    position: fixed;
    left: 12px;
    right: 12px;
    bottom: calc(64px + env(safe-area-inset-bottom));
    z-index: 1150;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    background: #fff;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.14);
    font-size: var(--fs-small);
    color: var(--text-primary);
}

.pwa-install-banner .bi {
    font-size: 20px;
    color: var(--accent-primary);
    flex-shrink: 0;
}

.pwa-install-banner .pwa-install-text {
    flex: 1;
    line-height: 1.3;
}

.pwa-install-banner .pwa-install-text small {
    display: block;
    color: var(--text-light);
}

/* Connection-lost pill shown by app-shell.js */
.offline-pill {
    position: fixed;
    left: 50%;
    transform: translateX(-50%);
    bottom: calc(72px + env(safe-area-inset-bottom));
    z-index: 1160;
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 7px 14px;
    border-radius: 999px;
    background: var(--text-primary);
    color: #fff;
    font-size: var(--fs-meta);
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25);
}

@media (max-width: 767.98px) {
    .mobile-tabbar {
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 1035;
        display: flex;
        align-items: stretch;
        background: var(--header-bg);
        border-top: 1px solid var(--border-subtle);
        padding-bottom: env(safe-area-inset-bottom);
        box-shadow: 0 -1px 6px rgba(0, 0, 0, 0.05);
    }

    /* The tab bar is fixed: the content has to end above it. !important beats
       the scoped rule in MainLayout.razor.css, which carries an attribute
       selector and therefore wins on specificity. */
    article.content {
        padding-bottom: calc(64px + env(safe-area-inset-bottom)) !important;
    }

    /* Gli avvisi stanno sopra la barra delle schede — mai sulla testata: vedi la nota
       accanto a .toast-container-top-right. */
    .toast-container-top-right {
        bottom: calc(64px + 12px + env(safe-area-inset-bottom));
    }
}

/* ══════════════════════════════════════════════════════════════
   StarRating — valutazione 0-5 (Components/Shared/StarRating.razor)
   Usata per la volonta del volontario sulle fasce e per la
   valutazione dell'associazione sulle qualifiche.
   ══════════════════════════════════════════════════════════════ */
.star-rating {
    display: inline-flex;
    align-items: center;
    gap: 2px;
}

.star-rating-star {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* 44 px: e il minimo tappabile della guida di stile, non un valore di gusto. */
    min-width: 44px;
    min-height: 44px;
    padding: 0;
    background: none;
    border: none;
    color: var(--accent-warning);
    font-size: 18px;
    line-height: 1;
    -webkit-tap-highlight-color: transparent;
}

.star-rating.is-readonly .star-rating-star {
    min-width: 0;
    min-height: 0;
    padding: 0 1px;
}

.star-rating-star:hover {
    color: var(--accent-primary);
}

.star-rating-value {
    margin-left: 6px;
    color: var(--text-light);
    font-size: var(--fs-meta);
    white-space: nowrap;
}

.star-rating-clear {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 32px;
    min-height: 32px;
    margin-left: 4px;
    padding: 0;
    background: none;
    border: none;
    color: var(--text-light);
    font-size: 12px;
}

.star-rating-clear:hover {
    color: var(--accent-danger);
}

/* ── Mezzi passi: le stelle disegnano, lo slider sceglie (01-09-2026) ──────
   Con `AllowHalf` le stelle non sono piu' pulsanti — non saprebbero esprimere
   il mezzo punto — quindi si stringono come nella variante di sola lettura, e
   il comando diventa il cursore. `min-height: 44px` sul range non e' estetica:
   e' il tap target della guida, ed e' esattamente la ragione per cui la mezza
   stella cliccabile era stata scartata. */
/* Larghezza **esatta**, non minima: la mappatura del range si regge sul fatto che la
   striscia sia 11 zone da 15 px (una per lo zero, due per stella). Con `min-width` un
   glifo piu' largo avrebbe spostato tutto e i valori non sarebbero piu' caduti sulle
   stelle. Vedi il commento in cima a StarRating.razor. */
.star-rating.has-slider .star-rating-star {
    width: 30px;
    /* Anche `min-width`, non solo `width`: la regola generale qui sopra ne impone 44 px
       (il tap target dei pulsanti-stella), e `min-width` vince su `width` — le stelle
       restavano larghe 44 px e la striscia non era piu' fatta di zone da 15 px. Misurato
       in browser il 02-09-2026, mentre si correggeva la mappatura. */
    min-width: 30px;
    flex: 0 0 30px;
    min-height: 44px;
    padding: 0;
    font-size: 20px;
}

/* Lo zero: «non voglio». Una zona vera in testa alla striscia, larga quanto una mezza
   stella — prima viveva in una scheggia di un pixel e mezzo sul bordo sinistro. */
.star-rating-zero {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 15px;
    flex: 0 0 15px;
    min-height: 44px;
    font-size: 12px;
    color: var(--border-subtle);
}

.star-rating-zero.is-on {
    color: var(--accent-danger);
}

/* Steso sopra le stelle (02-09-2026, richiesta dell'utente: «lo slider sulle
   stelline, tipo nascosto»): il range e' a opacita' zero e copre l'intera
   striscia, quindi si tocca la stella che si vuole e si trascina per scorrere.
   `left: -4%` e il cursore da 1 px non sono un capriccio: un range mappa il
   dito in modo lineare tolta la larghezza del cursore, e senza questo scarto la
   meta' sinistra di una stella cadeva a cavallo di due scatti e il bordo
   sinistro non arrivava mai a zero. Vedi il commento in StarRating.razor. */
.star-rating-track {
    position: relative;
    display: inline-flex;
    align-items: center;
    border-radius: var(--radius-sm);
}

.star-rating-track:focus-within {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}

.star-rating-slider {
    position: absolute;
    top: 0;
    bottom: 0;
    /* Nessuno scarto: il range copre **esattamente** la striscia. Lo scarto `-4%` di
       stamattina compensava un cursore da 1 px e non tornava agli estremi — la quinta
       stella piena era quasi irraggiungibile e cliccare fuori a sinistra scriveva 0.
       Adesso il conto lo fa la larghezza del cursore, qui sotto. */
    left: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    opacity: 0;
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
    background: transparent;
    touch-action: pan-y;
}

/* Il cursore e' largo **quanto una zona** (15 px), ed e' questo che fa cadere ogni valore
   al centro della sua mezza stella: un range mappa il dito su `larghezza - cursore`
   partendo da meta' cursore, quindi con 165 px di striscia e 15 px di cursore il valore
   vale 5 * (x - bordo - 7,5) / 150. E' invisibile (opacita' zero sull'input): la sua
   larghezza serve solo al conto. */
.star-rating-slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 15px;
    height: 44px;
}

.star-rating-slider::-moz-range-thumb {
    width: 15px;
    height: 44px;
    border: 0;
    border-radius: 0;
}

/* ── Sul telefono le zone si allargano, tutte e tre insieme ────────────────────
   15 px sono una zona da puntatore: col dito, che ne copre una quarantina, la mezza
   stella si prende per caso (misurato il 04-09-2026 a 375 px — la striscia restava
   larga 165 dove ce n'erano 300 disponibili). Qui la zona passa a 22 px: striscia
   242 = 11 x 22, che dentro una scheda di `table-mobile` va a capo sotto l'etichetta
   e ci sta.

   **I tre valori si muovono insieme o la mappatura salta**: la stella e' due zone,
   lo zero una, e il cursore dev'essere largo **quanto una zona** — e' l'unica forma
   in cui il centro di ogni zona cade sul suo valore. E `min-width` va cambiato con
   `width`, perche' la regola generale delle stelle ne impone 44 e vincerebbe.
   Stessa trappola gia' pagata il 02-09-2026 stringendole. */
@media (max-width: 767.98px) {
    .star-rating.has-slider .star-rating-star {
        width: 44px;
        min-width: 44px;
        flex: 0 0 44px;
        font-size: 24px;
    }

    .star-rating-zero {
        width: 22px;
        flex: 0 0 22px;
        font-size: 14px;
    }

    .star-rating-slider::-webkit-slider-thumb {
        width: 22px;
    }

    .star-rating-slider::-moz-range-thumb {
        width: 22px;
    }
}

/* ── Strisce da cantiere (solo in Development) ─────────────────────────────
   Devono gridare "impalcatura", non "prodotto": spigoli vivi mentre tutto il
   resto dell'app e' arrotondato, banda a strisce sul bordo sinistro, etichetta
   monospace maiuscola, magenta acceso. Chi guarda non deve poterle scambiare
   per una parte del disegno.

   Spente = display:none, niente margini residui: la pagina torna esattamente
   come sara' in produzione, che e' il motivo per cui l'interruttore esiste. */
:root {
    --dev-note-bg: #2B0B22;
    --dev-note-accent: #FF00A0;
    --dev-note-accent-2: #C6FF00;
    --dev-note-text: #FFE9F7;
}

.dev-note {
    background: var(--dev-note-bg);
    color: var(--dev-note-text);
    border-radius: 0;
    border-left: 10px solid transparent;
    border-image: repeating-linear-gradient(
        45deg,
        var(--dev-note-accent) 0 6px,
        var(--dev-note-accent-2) 6px 12px
    ) 1;
    margin: 0 0 1rem;
    box-shadow: 0 2px 0 rgba(255, 0, 160, 0.35);
}

.dev-notes-off .dev-note,
.dev-notes-off .dev-notes-toggle-label {
    display: none !important;
}

.dev-note-head {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    background: none;
    border: 0;
    color: inherit;
    text-align: left;
    padding: 6px 12px;
    min-height: 36px;
    cursor: pointer;
}

.dev-note-tag {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: var(--fs-micro);
    font-weight: 700;
    letter-spacing: .08em;
    color: var(--dev-note-bg);
    background: var(--dev-note-accent-2);
    padding: 1px 6px;
    white-space: nowrap;
    flex-shrink: 0;
}

.dev-note-title {
    font-size: var(--fs-small);
    font-weight: 600;
    flex-grow: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.dev-note-chevron {
    font-size: var(--fs-meta);
    opacity: .7;
    flex-shrink: 0;
}

.dev-note-body {
    padding: 0 12px 10px 12px;
    font-size: var(--fs-small);
}

.dev-note-what {
    margin: 0 0 .35rem;
}

.dev-note-details {
    margin: 0;
    padding-left: 1.1rem;
    color: #F0B9DE;
    font-size: var(--fs-meta);
}

.dev-note-details li {
    margin-bottom: .15rem;
}

/* L'interruttore. In basso a sinistra: a destra ci sono i toast. */
.dev-notes-toggle {
    position: fixed;
    left: 12px;
    bottom: 12px;
    z-index: 1080;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: 36px;
    padding: 6px 12px;
    border: 2px solid var(--dev-note-accent);
    border-radius: 0;
    background: var(--dev-note-bg);
    color: var(--dev-note-text);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: var(--fs-meta);
    cursor: pointer;
    opacity: .85;
}

.dev-notes-toggle:hover {
    opacity: 1;
}

/* Sotto i 768 px c'e' la tab bar: l'interruttore le sta sopra, non sotto. */
@media (max-width: 767.98px) {
    .dev-notes-toggle {
        bottom: calc(var(--mobile-tabbar-height, 56px) + 12px);
    }

    .dev-notes-toggle span {
        display: none;
    }
}

/* ══════════════════════════════════════════════════════════════
   Turni su cui bisogna intervenire (28-08-2026)
   Un posto **obbligatorio** vuoto, o meno persone del minimo: cosi' com'e'
   il turno non parte. Prima si vedeva solo leggendo riga per riga la
   colonna dei ruoli, dove un trattino rosso valeva sia per un posto
   obbligatorio sia per uno facoltativo.

   La tinta si mette sui <td> e non sul <tr>: Bootstrap dipinge il fondo
   delle celle, e un colore messo sulla riga finirebbe sotto.
   ══════════════════════════════════════════════════════════════ */
table tr.tm-needs-action > td {
    background-color: rgba(220, 53, 69, 0.07);
}

table tr.tm-needs-action > td:first-child {
    box-shadow: inset 4px 0 0 0 #dc3545;
}

/* Sotto i 768 px la tabella e' una scheda per riga, e le celle hanno
   `background-color: transparent !important`: qui il colore va sulla
   scheda, cioe' sul <tr>. */
@media (max-width: 767.98px) {
    table.table-mobile > tbody > tr.tm-needs-action {
        background: rgba(220, 53, 69, 0.07);
        border-color: rgba(220, 53, 69, 0.5);
        border-left-width: 4px;
        border-left-color: #dc3545;
    }
}

/* ══════════════════════════════════════════════════════════════
   Il pulsante Salva (01-09-2026) — Components/Shared/SaveButton.razor

   Tre promesse, e tutte e tre stanno qui dentro:

   1. UNO SOLO. Il raggio lo da' gia' `.btn` (6 px, come dice la guida di stile):
      qui non si scrive ne' `rounded-pill` ne' `btn-sm`. Erano quelli a rendere
      diversi fra loro i quattordici Salva scritti a mano.

   2. SPENTO E' GRIGIO, NON SBIADITO. Bootstrap disabilita con `opacity: .65`, che
      lascia il viola sotto una velina: da lontano — e su un tablet al sole — somiglia
      troppo all'acceso, e il pulsante smette di dire alcunche'. Qui il fondo cambia
      davvero e l'opacita' torna piena.

   3. ACCESO SI VEDE. L'anello viola e' l'unica cosa che si muove alla prima modifica:
      il colore da solo si nota se si stava guardando li'.

   Il minimo tappabile: 40 px, che sono la soglia di /dev/ui-audit, e 44 sotto i
   768 px come vuole la guida per il tocco.
   ══════════════════════════════════════════════════════════════ */
.tm-save {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    min-height: 40px;
    transition: background-color .15s ease, border-color .15s ease, box-shadow .15s ease;
}

.tm-save.is-dirty {
    box-shadow: 0 0 0 3px rgba(123, 104, 238, 0.22);
}

/* Spento e' `.is-clean`, NON `:disabled`.

   Il pulsante resta cliccabile apposta: in Blazor Server `Dirty` e' vecchio di un giro
   di rete, e un `disabled` calcolato su quel valore mangia il primo click dopo che si e'
   scritto in un campo (400-1000 ms misurati su /org/settings). Chi non deve salvare lo
   dice `aria-disabled`, e a non far succedere niente ci pensa il server. La spiegazione
   per esteso sta in cima a SaveButton.razor.

   `.btn.tm-save` e non `.tm-save`: `.btn-primary` di Bootstrap porta due classi, e con
   una sola qui il fondo viola resterebbe. */
.btn.tm-save.is-clean,
.btn.tm-save.is-clean:hover,
.btn.tm-save.is-clean:focus {
    background-color: var(--border-subtle);
    border-color: var(--border-subtle);
    color: var(--text-light);
    opacity: 1;
    box-shadow: none;
    cursor: default;
}

/* Salvataggio in corso: qui `disabled` e' legittimo, perche' e' uno stato che il server
   conosce gia' quando disegna. */
.btn.tm-save:disabled {
    opacity: .65;
    cursor: default;
}

/* La riga delle azioni: il Salva chiude a destra, sempre. Quello che gli sta accanto
   (Annulla, Ripristina) va a sinistra, e su schermo stretto la riga va a capo invece
   di sbordare. */
.tm-save-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: 0.5rem;
}

/* Il secondario che deve stare all'estrema sinistra della riga (le note, un link). */
.tm-save-row > .tm-save-aside {
    margin-right: auto;
}

/* ── La riga appoggiata al fondo, sui moduli lunghi ──────────────────────────
   `sticky`, non `fixed`, e per due ragioni distinte:

   - le regole comuni la danno per tutte le barre in basso (iOS);
   - `sticky` NON stacca l'elemento dal documento, quindi il pulsante resta dentro
     il suo <EditForm> e `type="submit"` continua a funzionare. Portato fuori in una
     barra `fixed` perderebbe il submit implicito, e il click non farebbe niente:
     nessun errore, nessuna traccia.

   `z-index: 5` e non di piu': qui si compete solo con i fratelli in flusso della
   stessa pagina. Sopra ci sono gia' cinque livelli negoziati altrove — tab bar 1035,
   interruttore delle note 1080, banner PWA 1150, pillola offline 1160, toast 1200 —
   e nessuno di questi va scavalcato: un Salva che copre il toast del salvataggio
   appena fatto e' esattamente il contrario di quello che serve.

   Funziona perche' `html, body` usano `overflow-x: clip` e non `hidden`: con
   `hidden` il body diventa un contenitore di scroll e ogni `sticky` dei discendenti
   smette di funzionare (trappola gia' pagata sulla sidebar, 25-08-2026). */
.tm-save-row.is-sticky {
    position: sticky;
    bottom: 0;
    z-index: 5;
    margin-top: 1rem;
    padding: 0.6rem 0;
    /* Le righe appiccicate stanno dentro una scheda bianca; la sfumatura verso il
       trasparente evita il taglio netto sul contenuto che le scorre sotto. */
    background: linear-gradient(to top, #fff 62%, rgba(255, 255, 255, 0));
}

@media (max-width: 767.98px) {
    .tm-save {
        min-height: 44px;
    }

    /* La tab bar e' `fixed` in fondo: `bottom: 0` incollerebbe la riga sotto di lei.
       Stesso calcolo che fanno gia' il banner PWA e la pillola offline. */
    .tm-save-row.is-sticky {
        bottom: calc(64px + env(safe-area-inset-bottom));
    }

    /* Su schermo stretto il Salva prende tutta la riga: e' l'azione principale, e in
       fondo alla pagina il pollice ci arriva senza mirare. */
    .tm-save-row > .tm-save {
        flex: 1 1 100%;
        order: 2;
    }

    /* ── L'ultimo campo non deve restare sotto la riga appiccicata ─────────────────
       Misurato il 04-09-2026 su /org/shifts/nuovo a 375 px: la barra Salva/Annulla
       copriva 45 dei 66 px del campo Note, e **non c'era modo di liberarlo** — la pagina
       era gia' in fondo. Una riga `sticky` galleggia sul contenuto finche' c'e' pagina
       sotto di lei: se sotto non ce n'e', quello che copre resta coperto.

       Quindi due cose insieme, e servono tutte e due.

       1. Spazio in fondo **solo dove la barra c'e' davvero**: `:has()` evita di
          regalare 112 px di vuoto a ogni pagina senza Salva appiccicato. Sta fuori dalla
          scheda, sull'articolo, cosi' non si vede come buco dentro il modulo — e' solo
          strada per scorrere, dove comunque c'e' la barra delle schede. Dove `:has()` non
          e' supportato non cambia niente rispetto a prima: nessuna regressione.
       2. `scroll-margin-bottom`, perche' il browser che porta in vista il campo toccato
          (o che fa spazio alla tastiera) sappia di quanto deve stare piu' su.

       112 px sono la riga con i due pulsanti impilati; i 64 sono la barra delle schede. */
    article.content:has(.tm-save-row.is-sticky) {
        padding-bottom: calc(64px + 112px + env(safe-area-inset-bottom)) !important;
    }

    form .form-control,
    form .form-select,
    form textarea {
        scroll-margin-bottom: 112px;
    }
}

/* ── Schede (tab) ────────────────────────────────────────────────────────────
   Un elenco e il modulo che ci aggiunge una voce, nella stessa pagina: le schede
   evitano che il modulo stia sempre aperto in cima e spinga in basso quello per cui
   si e' tornati. Lo stato lo tiene il componente (C# + @onclick): il bundle JS di
   Bootstrap non e' caricato, quindi `data-bs-toggle="tab"` non farebbe niente.

   Il `min-height` non e' cosmetico: la nav-link di Bootstrap sta sotto i 40 px e
   /dev/ui-audit la segna come tap target troppo piccolo. */
.tm-tabs .nav-link {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    color: var(--text-secondary, #6B6F76);
    font-weight: 500;
}

.tm-tabs .nav-link.active {
    color: var(--accent-primary, #7B68EE);
    font-weight: 600;
}

/* ── Una linguetta per voce (TabStrip, 01-09-2026) ─────────────────────────
   Su una riga sola, e scorre. Con quaranta volontari una striscia che va a capo
   diventa un muro alto mezza pagina e la pagina non si legge piu': meglio come
   le schede di un browser, dove si scorre di lato per arrivare a quella che
   serve. La barra di scorrimento e' sottile perche' la striscia e' alta 44 px e
   una barra di sistema se ne mangerebbe un quarto. */
.tm-tabs-scroll {
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: thin;
}

.tm-tabs-scroll .nav-item {
    flex: 0 0 auto;
}

/* Un nome lungo non deve allargare la linguetta oltre lo schermo: si tronca, e
   il nome intero resta nel `title`. */
.tm-tabs-scroll .tm-tab-label {
    display: inline-block;
    max-width: 14rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: bottom;
}

/* La linguetta che aggiunge: staccata dalle altre, perche' non e' una di loro. */
.tm-tabs .tm-tab-add {
    color: var(--accent-primary, #7B68EE);
}

/* ── Linguette in colonna (TabStrip Vertical, 02-09-2026) ──────────────────
   Richiesta dell'utente per tipi di turno, ruoli e qualifiche: i nomi si leggono
   tutti insieme a sinistra e la voce scelta sta a destra. La colonna e' `sticky`
   — non `fixed`, che la staccherebbe dal documento (regola comune sulle barre) —
   cosi' con un contenuto lungo l'elenco resta a portata di mano. Sotto i 768 px
   la colonna non ci sta accanto al contenuto e torna una riga che scorre. */
.tm-vtabs {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
}

.tm-vtabs-body {
    flex: 1 1 auto;
    min-width: 0;
}

.tm-vtabs-nav {
    flex: 0 0 15rem;
    position: sticky;
    top: 4rem;
    padding: 4px;
    background: var(--header-bg, #FFFFFF);
    border: 1px solid var(--border-subtle, #E8E8ED);
    border-radius: var(--radius-md, 8px);
}

.tm-vtabs-nav .nav-link {
    width: 100%;
    text-align: left;
    border-radius: var(--radius-sm, 4px);
}

.tm-vtabs-nav .nav-link .tm-tab-label {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tm-vtabs-nav .nav-link:hover {
    background: var(--row-hover, #F0F0F3);
}

.tm-vtabs-nav .nav-link.active {
    background: rgba(123, 104, 238, 0.12);
}

/* Chi aggiunge sta in fondo, oltre una riga: non e' una delle voci. */
.tm-vtabs-nav .tm-tab-add-item {
    margin-top: 4px;
    padding-top: 4px;
    border-top: 1px solid var(--border-subtle, #E8E8ED);
}

/* ── Sotto i 768 px: una fisarmonica, non una striscia che scorre ──────────
   05-09-2026, richiesta dell'utente. La colonna diventava una **riga** con
   `overflow-x: auto`: su 375 px si leggevano tre nomi su venti, troncati a nove
   lettere, e per trovarne uno bisognava trascinare di lato al buio. Ora le voci
   restano in colonna — si leggono tutte — e quella scelta si apre **sotto se
   stessa** (`.tm-vtabs-panel`, che TabStrip infila nel `li` giusto). In fondo
   all'elenco sarebbe stata fuori campo, che e' lo stesso difetto di prima. */
@media (max-width: 767.98px) {
    .tm-vtabs {
        flex-direction: column;
        gap: .75rem;
    }

    .tm-vtabs-body {
        width: 100%;
    }

    .tm-vtabs-nav {
        position: static;
        flex: 0 0 auto;
        width: 100%;
        padding: 0;
        border: 0;
        background: transparent;
        gap: 6px;
    }

    .tm-vtabs-nav .nav-item {
        flex: 0 0 auto;
    }

    /* Ogni voce e' una riga a se': fondo bianco e bordo, cosi' si conta a colpo
       d'occhio quante sono. Senza, venti righe di testo su fondo grigio sono un
       muro in cui la voce aperta non si distingue. */
    .tm-vtabs-nav .nav-link {
        width: 100%;
        min-height: 44px;
        display: flex;
        align-items: center;
        gap: 8px;
        background: var(--header-bg, #FFFFFF);
        border: 1px solid var(--border-subtle, #E8E8ED);
        border-radius: var(--radius-sm, 4px);
    }

    .tm-vtabs-nav .nav-link.active {
        border-color: var(--accent-primary, #7B68EE);
        font-weight: 600;
        /* La voce aperta e' attaccata al suo pannello: sono la stessa cosa. */
        border-bottom-left-radius: 0;
        border-bottom-right-radius: 0;
    }

    /* Il nome per intero: e' il punto della colonna. */
    .tm-vtabs-nav .nav-link .tm-tab-label {
        white-space: normal;
        overflow: visible;
        text-overflow: clip;
    }

    /* Il pannello della voce aperta, appeso sotto di lei. */
    .tm-vtabs-panel {
        border: 1px solid var(--accent-primary, #7B68EE);
        border-top: 0;
        border-radius: 0 0 var(--radius-sm, 4px) var(--radius-sm, 4px);
        background: var(--header-bg, #FFFFFF);
        padding: 12px;
        margin-bottom: 6px;
    }

    /* Dentro il pannello la scheda non serve piu': il pannello **e'** la scheda, e
       un bordo dentro un bordo mangia larghezza su uno schermo che ne ha 375.
       Stessa scelta del foglio (`.tm-sheet-body > .card`). */
    .tm-vtabs-panel > .card {
        border: none !important;
        box-shadow: none !important;
        margin-bottom: 0;
    }

    .tm-vtabs-panel > .card > .card-body {
        padding: 0;
    }

    /* Chi aggiunge sta in **cima** (lo mette li' TabStrip, nel DOM): in fondo a venti
       voci sarebbe da cercare scorrendo. Il filetto va quindi sotto, non sopra. */
    .tm-vtabs-nav .tm-tab-add-item {
        margin: 0 0 4px;
        padding: 0 0 4px;
        border-top: 0;
        border-bottom: 1px solid var(--border-subtle, #E8E8ED);
    }
}

/* ── Righe che si aprono (/org/volunteer-qualifications, 02-09-2026) ───────
   Una tabella di persone; si tocca una riga e sotto si aprono le sue azioni.
   La riga e' un pulsante a tutti gli effetti — cursore, focus, altezza tappabile. */
.tm-row-select {
    cursor: pointer;
}

.tm-row-select:focus-visible {
    outline: 2px solid var(--accent-primary, #7B68EE);
    outline-offset: -2px;
}

.tm-row-select > td {
    min-height: 44px;
}

.tm-row-select.is-open > td {
    background: rgba(123, 104, 238, 0.08);
    font-weight: 500;
}

.tm-row-detail > td {
    background: var(--content-bg, #F7F8FA);
    padding: 1rem;
    border-bottom: 2px solid var(--border-subtle, #E8E8ED);
}

.tm-row-chevron {
    color: var(--text-light, #9CA0A9);
    transition: transform .15s ease;
}

.tm-row-select.is-open .tm-row-chevron {
    transform: rotate(90deg);
}

@media (max-width: 767.98px) {
    /* Con table-mobile ogni riga e' una scheda: la riga aperta e quella del dettaglio
       si attaccano, cosi' si capisce che sono la stessa persona. */
    table.table-mobile > tbody > tr.tm-row-select.is-open {
        margin-bottom: 0;
        border-bottom-left-radius: 0;
        border-bottom-right-radius: 0;
    }

    table.table-mobile > tbody > tr.tm-row-detail {
        border-top-left-radius: 0;
        border-top-right-radius: 0;
    }

    /* La cella del dettaglio non e' una coppia etichetta-valore: torna un blocco
       normale, allineato a sinistra, con dentro la scheda intera. */
    table.table-mobile > tbody > tr.tm-row-detail > td {
        display: block;
        text-align: left;
        padding: .75rem;
    }
}

@media (max-width: 767.98px) {
    .tm-tabs-scroll .tm-tab-label {
        max-width: 9rem;
    }
}

/* ══════════════════════════════════════════════════════════════
   Il quadro d'insieme della graduatoria (una riga per volontario,
   una colonna per ruolo). Con sei ruoli la tabella scorre di lato
   dentro .table-responsive, e il nome della persona deve restare
   visibile: senza, si scorre fino alla colonna giusta e non si sa
   più di chi sia quel punteggio.
   ══════════════════════════════════════════════════════════════ */
.shift-score-matrix th:first-child,
.shift-score-matrix td:first-child {
    position: sticky;
    left: 0;
    z-index: 1;
    background-color: var(--bs-body-bg, #fff);
}

.shift-score-matrix tr.table-light td:first-child {
    background-color: var(--bs-table-bg, #f8f9fa);
}

.shift-score-matrix th:first-child,
.shift-score-matrix td:first-child {
    min-width: 12rem;
}

/* Il punteggio e il pulsante che assegna sono una cosa sola: se vanno a capo
   la riga raddoppia di altezza e la tabella non si legge piu' a colpo d'occhio. */
.shift-score-matrix tbody td {
    white-space: nowrap;
}

@media (max-width: 767.98px) {
    /* Su un telefono 12 rem di nome lasciano mezza colonna di ruolo: il nome si
       stringe (resta fermo mentre si scorre di lato, quindi si legge comunque) e
       le celle si stringono con lui. */
    .shift-score-matrix th:first-child,
    .shift-score-matrix td:first-child {
        min-width: 7.5rem;
    }

    .shift-score-matrix th,
    .shift-score-matrix td {
        padding: .35rem .4rem;
    }
}

/* Il nome invece va a capo, e non e' un dettaglio: con `nowrap` un indirizzo di
   posta lungo (chi non ha ancora messo nome e cognome) allargava la colonna oltre
   il suo minimo e mangiava lo spazio delle colonne dei ruoli. */
.shift-score-matrix tbody td:first-child {
    white-space: normal;
    overflow-wrap: anywhere;
    max-width: 14rem;
}

/* ══════════════════════════════════════════════════════════════
   Le fasce di disponibilita' del socio, nella griglia oraria del
   calendario (02-09-2026). Il calendario le faceva dichiarare
   trascinando e poi non le disegnava: si trascinava, usciva il
   toast, e la pagina restava identica.

   Stanno DIETRO ai turni (z-index 0 contro 1) e non prendono i
   click: sono lo sfondo su cui i turni si leggono, e il gesto che
   conta li' sopra e' ancora il trascinamento che ne dichiara una
   nuova. Colore diverso da quello dei turni "disponibile" (viola
   tenue) per non far credere che siano turni.
   ══════════════════════════════════════════════════════════════ */
.cal-availability-band {
    position: absolute;
    left: 2px;
    right: 2px;
    z-index: 0;
    pointer-events: none;
    border-radius: .35rem;
    background: repeating-linear-gradient(
        45deg,
        rgba(25, 135, 84, .10),
        rgba(25, 135, 84, .10) 8px,
        rgba(25, 135, 84, .17) 8px,
        rgba(25, 135, 84, .17) 16px);
    border: 1px dashed rgba(25, 135, 84, .45);
    overflow: hidden;
}

.cal-availability-label {
    display: block;
    padding: 1px 4px;
    font-size: var(--fs-micro, .65rem);
    line-height: 1.2;
    color: #14653b;
    white-space: nowrap;
}

/* I turni stanno sopra le fasce: senza, una fascia disegnata dopo
   nel DOM coprirebbe il turno che la riempie. */
.cal-week-event {
    z-index: 1;
}

/* Il pollice nella cella del mese: la fascia non ci sta, il fatto
   che ci sia si'. */
.cal-day-availability {
    color: #198754;
    font-size: .7rem;
    margin-left: auto;
    padding-right: .15rem;
}

/* ══════════════════════════════════════════════════════════════
   Bersagli da dito (collaudo mobile del 04-09-2026, seconda passata)
   Misurato a 375 px con emulazione Android: quattro cose sotto i 44 px
   che il primo audit non vedeva perche' guardava solo l'altezza.
   ══════════════════════════════════════════════════════════════ */
@media (max-width: 767.98px) {
    /* La linguetta «Nuovo …» delle schede in colonna era `sticky` sul bordo destro di una
       striscia larga fino a 1.800 px (04-09-2026). Dal 05-09-2026 quella striscia non
       esiste: le voci stanno in colonna e chi aggiunge e' la **prima**, quindi non c'e'
       piu' niente da tenere fermo. */

    /* Un pulsante-icona senza padding e' largo quanto l'icona: 16 px (togli dal posto nel
       modulo del turno, copia il link di iscrizione). L'altezza era gia' 44 per la riga;
       la larghezza no. */
    .btn-link.btn-sm.p-0 {
        min-width: 44px;
        min-height: 44px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

    /* Invia e Aggiorna della chat, ridotti a icona: 37 px. */
    .chat-composer .btn {
        min-width: 44px;
    }

    /* Le righe-pulsante degli elenchi di persone (ruoli dei volontari): 36 px. */
    .list-group-item-action {
        min-height: 44px;
    }
}

/* ══════════════════════════════════════════════════════════════
   Simboli disegnati qui (06-09-2026)

   Bootstrap Icons 1.11.3 — la versione vendorizzata — non ha nessun
   volante, e il salvagente, che ne ha la forma, dice un'altra cosa.
   Il volante era chiesto per i ruoli di guida, quindi si disegna.

   Il prefisso `bi-` non e' un travestimento: serve a far scattare
   `[class^=bi-]::before` di bootstrap-icons.css, che e' la regola che
   da' a tutte le icone `display:inline-block` e l'allineamento
   verticale. Qui si sovrascrivono contenuto e resa — glifo di un font
   la', maschera SVG qui — e il resto resta in comune, cosi' il volante
   si comporta come le sorelle dovunque compaia.

   `background-color: currentColor` + `mask`: l'icona prende il colore
   del testo come un glifo, che e' quello che serve dentro un badge o
   una riga colorata. Con un `background-image` sarebbe rimasta nera.

   L'elenco delle voci ammesse sta in Models/RoleIcons.cs: una classe
   `bi-tm-*` senza la sua regola qui si comporta come un refuso — cella
   vuota, nessun errore — che e' esattamente il difetto che quel listino
   chiuso esiste per impedire.
   ══════════════════════════════════════════════════════════════ */
.bi-tm-steering-wheel::before {
    content: "";
    display: inline-block;
    width: 1em;
    height: 1em;
    background-color: currentColor;
    -webkit-mask: var(--tm-icon-steering-wheel) center / contain no-repeat;
    mask: var(--tm-icon-steering-wheel) center / contain no-repeat;
}

:root {
    --tm-icon-steering-wheel: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M8 .5a7.5 7.5 0 1 0 0 15 7.5 7.5 0 0 0 0-15Zm0 1.5a6 6 0 1 1 0 12A6 6 0 0 1 8 2Z'/%3E%3Ccircle cx='8' cy='8' r='2.25'/%3E%3Cpath d='M2.3 7.25h3.55v1.5H2.3zM10.15 7.25h3.55v1.5h-3.55zM7.25 10.15h1.5v3.55h-1.5z'/%3E%3C/svg%3E");
}

/* Le qualifiche richieste da un posto, nel modulo del turno: **una per riga**
   (06-09-2026, richiesta dell'utente). Erano una stringa sola con « o » in mezzo, e su
   tre qualifiche andava a capo dove capitava — con la « o », che e' la cosa da capire,
   in fondo a una riga. Elenco vero, con il pallino dentro il margine perche' la cella
   della tabella e' gia' stretta. */
.tm-req-quals {
    list-style: disc;
    margin: 0;
    padding-left: 1.1em;
}

.tm-req-quals li {
    line-height: 1.35;
}

/* ══════════════════════════════════════════════════════════════
   I tre calendari, e che cosa fa il gesto (06-09-2026)

   Fabbisogno / Disponibilità / Insieme. Il selettore ha lo stesso
   aspetto segmentato delle viste — sono due scelte parallele: *quanto
   largo* si guarda e *che cosa* si guarda — ma sta su una riga sua,
   perché il livello decide anche che cosa produce il trascinamento
   sulla griglia, ed è una cosa che si legge, non una linguetta in
   mezzo alle altre.
   ══════════════════════════════════════════════════════════════ */
.cal-layers {
    display: flex;
    align-items: center;
    gap: 0;
    flex-wrap: wrap;
    margin: -8px 0 12px;
}

.cal-layers-label {
    font-size: var(--fs-meta);
    color: var(--text-light);
    text-transform: uppercase;
    letter-spacing: .04em;
    margin-right: 8px;
}

.cal-layers .btn {
    border-radius: 0;
    border: 1px solid var(--border-subtle);
    border-left-width: 0;
    background: #fff;
    color: var(--text-secondary);
    padding: 5px 12px;
}

.cal-layers .btn:first-of-type {
    border-left-width: 1px;
    border-top-left-radius: var(--radius-md);
    border-bottom-left-radius: var(--radius-md);
}

.cal-layers .btn:last-of-type {
    border-top-right-radius: var(--radius-md);
    border-bottom-right-radius: var(--radius-md);
}

.cal-layers .btn.active {
    background: var(--accent-primary);
    border-color: var(--accent-primary);
    color: #fff;
}

/* La riga che dice che cosa produce il gesto **prima** di farlo. Tre stati e tre colori,
   gli stessi delle due voci della domanda al rilascio: verde = riguarda solo te,
   viola/blu = riguarda l'associazione, grigio = te lo chiedo. Cambiare uno dei due posti
   senza l'altro rimetterebbe il difetto che questa riga esiste per togliere. */
.cal-gesture-hint {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0 0 12px;
    padding: 6px 10px;
    border-radius: var(--radius-md);
    font-size: var(--fs-meta);
    border: 1px solid transparent;
}

.cal-gesture-hint.is-personal {
    background: rgba(25, 135, 84, .08);
    border-color: rgba(25, 135, 84, .35);
    color: #14653b;
}

.cal-gesture-hint.is-management {
    background: rgba(123, 104, 238, .08);
    border-color: rgba(123, 104, 238, .35);
    color: #4b3fb0;
}

.cal-gesture-hint.is-ask {
    background: var(--bg-subtle, #f6f6f9);
    border-color: var(--border-subtle);
    color: var(--text-secondary);
}

/* La banda di **copertura**, nel calendario delle disponibilità: un tratto di tempo con
   quante persone lo coprono. Piena invece che tratteggiata, perché lì le bande non sono più
   lo sfondo su cui si leggono i turni — sono il contenuto. Il numero ha bisogno di un fondo
   su cui staccarsi.

   Il tratteggio della banda propria dice «sfondo»; il pieno dice «oggetto». Le due specie non
   compaiono mai insieme (una è il livello personale, l'altra quello dell'associazione), ma
   distinguerle serve lo stesso: chi gestisce passa dall'uno all'altro con un click, e due
   disegni identici farebbero sembrare che non sia cambiato niente. */
.cal-availability-band.is-coverage {
    background: rgba(25, 135, 84, .16);
    border-style: solid;
    border-color: rgba(25, 135, 84, .5);
}

.cal-availability-band.is-coverage .cal-availability-label {
    overflow: hidden;
    text-overflow: ellipsis;
    font-weight: 500;
}

/* Il numero nella cella del mese, quando le fasce sono di tutti: «3» accanto al pollice. */
.cal-day-availability-count {
    font-size: var(--fs-micro);
    font-weight: 600;
    margin-left: 1px;
}

/* ── Il selettore «Vedi» sul telefono ────────────────────────────────────────────────
   **Una griglia, non una riga flex** (09-09-2026, segnalato dall'utente: «da mobile il tasto
   per vedere i turni scoperti del calendario viene più grande rispetto agli altri»).

   Con `flex: 1 1 0` i pulsanti *sembrano* uguali e non lo sono: un figlio flex nasce con
   `min-width: auto`, quindi non si stringe sotto la larghezza minima del suo contenuto — e
   la voce con la parola più lunga si prende più dello spazio che le tocca, spingendo le
   altre. È la stessa trappola di `main` nella regola 47, qui su un asse diverso.

   Le colonne di una griglia `1fr` sono uguali **per costruzione**, quale che sia il testo
   dentro. Due per riga: a 375 px con quattro voci — il socio ne ha quattro dal 09-09-2026 —
   quattro colonne farebbero celle da 87 px, dove «Disponibilità» va a capo in mezzo alla
   parola. E siccome le celle non si toccano più, ogni pulsante torna a essere un pulsante
   suo: il bordo comune del segmented control qui non ha più niente da unire. */
@media (max-width: 767.98px) {
    .cal-layers {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 6px;
        margin-top: 0;
    }

    .cal-layers-label {
        grid-column: 1 / -1;
        margin: 0 0 2px;
    }

    /* I tre selettori insieme, e non solo il primo: `:first-of-type` e `:last-of-type`
       pesano di più, e senza nominarli qui i due pulsanti agli estremi resterebbero
       squadrati da un lato — cioè segmented a metà, che si legge come un difetto. */
    .cal-layers .btn,
    .cal-layers .btn:first-of-type,
    .cal-layers .btn:last-of-type {
        min-height: 44px;
        min-width: 0;
        padding: 5px 6px;
        font-size: var(--fs-small);
        line-height: 1.15;
        white-space: normal;
        border-radius: var(--radius-md);
        border-left-width: 1px;
    }

    /* Con un numero **dispari** di pulsanti l'ultimo resterebbe da solo a metà riga, e a
       colpo d'occhio sembra una voce di un altro genere. Il conteggio parte dall'etichetta
       «Vedi», che è il primo figlio e occupa una riga sua: quindi «ultimo figlio in
       posizione pari» vuol dire «numero dispari di pulsanti». */
    .cal-layers .btn:last-child:nth-child(even) {
        grid-column: 1 / -1;
    }
}

/* ── Calendario di gestione: quanti per ruolo, ora per ora (27-09-2026, C3) ──
   Una riga per ora, dietro ai turni come le bande (che su quel livello non ci sono), con
   il simbolo del ruolo e il numero. Il simbolo e non il nome: a 375 px una colonna di tre
   giorni è larga ~96 px. Lo zero è smorzato ma c'è — «0 autisti» è la risposta che conta. */
.cal-role-hour {
    position: absolute;
    left: 2px;
    right: 2px;
    height: 38px;
    display: flex;
    flex-wrap: wrap;
    align-content: center;
    gap: 2px 6px;
    padding: 2px 4px;
    font-size: var(--fs-meta);
    line-height: 1.1;
    background: rgba(25, 135, 84, 0.07);
    border-left: 2px solid rgba(25, 135, 84, 0.45);
    border-radius: 4px;
    overflow: hidden;
    z-index: 0;
}

.cal-role-count {
    white-space: nowrap;
    font-weight: 600;
    color: var(--text-primary);
}

.cal-role-count .bi {
    margin-right: 2px;
    color: #198754;
}

.cal-role-count.is-zero {
    color: var(--accent-danger);
    font-weight: 500;
}

.cal-role-count.is-zero .bi {
    color: var(--accent-danger);
}

/* Sul telefono una colonna di tre giorni è larga ~91 px: con cinque ruoli i conteggi vanno su
   tre righe, e con il carattere del desktop la terza usciva dall'ora (misurato: 43 px in 38). */
@media (max-width: 767.98px) {
    .cal-role-hour {
        font-size: var(--fs-micro);
        line-height: 1;
        gap: 1px 4px;
        padding: 1px 3px;
    }

    .cal-role-count .bi {
        margin-right: 1px;
    }
}

/* ══════════════════════════════════════════════════════════════
   «Di chi»: le mie cose / l'associazione (04-10-2026)
   Stessa forma segmentata dei livelli — è un selettore gemello — ma
   su una riga sua, perché risponde a un'altra domanda: il livello
   dice *che cosa* si guarda, questo *di chi*. Sei pulsanti in fila
   sembrerebbero sei alternative della stessa scelta.
   Il margine negativo in alto lo incolla alla striscia dei livelli:
   le due righe si leggono come un blocco solo.
   ══════════════════════════════════════════════════════════════ */
.cal-scope {
    margin-top: -4px;
}

/* ══════════════════════════════════════════════════════════════
   Il filtro «quali tipi di turno vedere» (04-10-2026)
   Un <details> e non un <select multiple>: la scelta è multipla, e
   un select multiplo col dito è il controllo peggiore che il browser
   abbia. Il pannello è `absolute` per non allargare la barra quando
   si apre — altrimenti ogni apertura sposterebbe il calendario sotto.
   ══════════════════════════════════════════════════════════════ */
.cal-typefilter {
    position: relative;
}

.cal-typefilter > summary {
    list-style: none;
    cursor: pointer;
    user-select: none;
}

.cal-typefilter > summary::-webkit-details-marker {
    display: none;
}

.cal-typefilter-panel {
    position: absolute;
    z-index: 20;
    top: calc(100% + 4px);
    left: 0;
    min-width: 220px;
    max-height: 60vh;
    overflow-y: auto;
    padding: 10px 12px;
    background: #fff;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    box-shadow: 0 8px 24px rgb(0 0 0 / 12%);
}

.cal-typefilter-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 3px 0;
    margin: 0;
    cursor: pointer;
}

/* Il minimo di 44 px vale anche qui: una riga di casella si tocca col
   dito come qualunque altra cosa (regola 48). */
@media (max-width: 767.98px) {
    .cal-typefilter-item {
        min-height: 44px;
    }

    /* A destra invece che a sinistra: su 375 px un pannello da 220
       ancorato a sinistra di un pulsante che sta a metà barra esce
       dallo schermo dal lato opposto. */
    .cal-typefilter-panel {
        left: auto;
        right: 0;
    }
}

/* ══════════════════════════════════════════════════════════════
   Il modello «lista → pop-up» (04-10-2026)

   Lista in sola lettura → «Nuovo» in alto a destra → clic sulla riga → pop-up di
   dettaglio → Modifica / Elimina / Chiudi. I pezzi qui sotto sono quelli che ogni pagina
   usa: la barra in cima (`.tm-bar`), la tabella (`.tm-table`), il pop-up (`.tm-zoom`,
   ZoomDialog.razor), la «i» (`.tm-info`, InfoTip.razor), l'elenco etichetta–valore del
   dettaglio (`.tm-dl`) e le spunte dei permessi (`.tm-perm-*`).
   Riferimento: /.agents/esempi/ruoli_prove_ux.html, variante «3 · Finestra».
   ══════════════════════════════════════════════════════════════ */

/* ── La barra in cima alla pagina: titolo, «i», ricerca, Nuovo ─────────────── */
.tm-bar {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 12px;
}

.tm-bar-title {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin: 0;
    font-size: var(--fs-page);
    font-weight: 600;
    color: var(--text-primary);
}

.tm-bar-grow {
    flex: 1 1 auto;
}

.tm-bar-search {
    position: relative;
    min-width: 220px;
}

.tm-bar-search > .bi {
    position: absolute;
    left: 10px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-light);
    pointer-events: none;
}

.tm-bar-search > input {
    height: var(--tm-btn-h);
    padding-left: 30px;
}

.tm-bar-actions {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

@media (max-width: 767.98px) {
    /* La ricerca va a capo sotto il titolo, a tutta larghezza; «Nuovo» resta in alto a
       destra accanto al titolo, dove lo si cerca. */
    .tm-bar-search {
        order: 3;
        flex: 1 1 100%;
        min-width: 0;
    }

    .tm-bar-search > input {
        height: 44px;
    }
}

/* ── La tabella: righe da 34 px, linee sottili, niente zebra ──────────────── */
/* **Tabelle compatte** (04-10-2026, richiesta dell'utente: «tabelle più compatte»). Le righe
   erano da 40 px e l'intestazione da 36: su un elenco di trenta voci un terzo dello schermo
   era aria. Le misure stanno qui, in variabili, e nessuna pagina le ritocca:
   - `--tm-row-h`: la riga, sul desktop. Il pulsante di riga (`.btn-sm` dentro la tabella)
     scende con lei a `--tm-btn-h-row`, altrimenti i suoi 31 px più il padding la
     rialzerebbero da soli — e una riga con il pulsante sarebbe più alta di una senza;
   - `--tm-head-h`: l'intestazione;
   - `--tm-card-*`: la scheda di una riga sotto i 768 px, dove i bersagli restano da 44 px
     (la scheda intera è un bersaglio, ed è per questo che ha `min-height: 44px`). */
:root {
    --tm-row-h: 34px;
    --tm-cell-py: 2px;
    --tm-cell-px: 10px;
    --tm-head-h: 30px;
    --tm-btn-h-row: 26px;
    --tm-card-py: 6px;
    --tm-card-px: 10px;
    --tm-card-gap: 6px;
}

.tm-table {
    width: 100%;
    margin-bottom: 0;
    background: #fff;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    border-collapse: separate;
    border-spacing: 0;
}

.tm-table > thead > tr > th {
    position: sticky;
    /* Sotto la testata dell'applicazione (48 px, `.top-row` sticky in MainLayout). */
    top: 48px;
    z-index: 2;
    height: var(--tm-head-h);
    padding: 0 var(--tm-cell-px);
    background: #fff;
    border-bottom: 1px solid var(--border-subtle);
    white-space: nowrap;
    vertical-align: middle;
}

.tm-table > thead > tr > th:first-child { border-top-left-radius: var(--radius-md); }
.tm-table > thead > tr > th:last-child { border-top-right-radius: var(--radius-md); }

.tm-table > tbody > tr > td {
    height: var(--tm-row-h);
    padding: var(--tm-cell-py) var(--tm-cell-px);
    line-height: 1.3;
    vertical-align: middle;
    border-bottom: 1px solid var(--border-subtle);
    background: transparent;
}

.tm-table > tbody > tr:last-child > td {
    border-bottom: 0;
}

.tm-table .tm-num,
.tm-table th.tm-num {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.tm-table td.tm-name {
    color: var(--text-primary);
    font-weight: 500;
}

/* La riga intera apre il dettaglio. */
.tm-table > tbody > tr.tm-row {
    cursor: pointer;
}

.tm-table > tbody > tr.tm-row:hover > td,
.tm-table > tbody > tr.tm-row:focus-within > td {
    background: var(--row-hover);
}

.tm-table > tbody > tr.tm-row:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: -2px;
}

/* La colonna delle azioni di riga: stretta, a destra. Le azioni compaiono al passaggio
   del mouse e al focus da tastiera (la regola sta qui sopra, `td.tm-actions`); su touch
   restano visibili. */
.tm-table td.tm-actions {
    width: 1%;
    white-space: nowrap;
    text-align: right;
}

/* Il pulsante di riga segue la riga compatta (sul desktop; sotto i 768 px vince il minimo
   di 44 px al tocco, scritto più giù). È l'unico punto in cui un `.btn-sm` cambia misura,
   e lo fa la tabella, non una pagina. */
@media (min-width: 768px) {
    .tm-table > tbody > tr > td .btn-sm {
        min-height: var(--tm-btn-h-row);
        padding-top: 0;
        padding-bottom: 0;
    }
}

/* L'intestazione che ordina. */
.tm-sort {
    all: unset;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font: inherit;
    color: inherit;
    text-transform: inherit;
    letter-spacing: inherit;
}

.tm-sort:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
    border-radius: 2px;
}

.tm-sort.is-on {
    color: var(--text-secondary);
}

.tm-sort .tm-sort-idle {
    opacity: 0.45;
}

/* Icone di stato sulla riga: solo icone, ciascuna con il suo nome (aria-label + title). */
.tm-states {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    color: var(--text-light);
}

.tm-states .bi {
    font-size: 15px;
}

/* Il simbolo accanto al nome (ruoli). */
.tm-sym {
    width: 24px;
    height: 24px;
    flex: 0 0 24px;
    border-radius: 5px;
    background: #EFEDFF;
    color: var(--accent-primary);
    display: inline-grid;
    place-items: center;
    font-size: 13px;
}

.tm-namecell {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
}

.tm-pill {
    display: inline-block;
    padding: 1px 9px;
    border-radius: 999px;
    font-size: var(--fs-meta);
    font-weight: 500;
    white-space: nowrap;
    background: var(--content-bg);
    color: var(--text-secondary);
    border: 1px solid var(--border-subtle);
}

.tm-pill.is-green { background: #EAF7E5; color: #3F8F27; border-color: transparent; }
.tm-pill.is-amber { background: #FFF3D1; color: #9A6B00; border-color: transparent; }
.tm-pill.is-violet { background: #EFEDFF; color: #5B4BD4; border-color: transparent; }
.tm-pill.is-red { background: #FFE8E8; color: #C8282A; border-color: transparent; }
.tm-pill.is-grey { background: #F0F0F3; color: var(--text-secondary); border-color: transparent; }

.tm-empty {
    padding: 36px 16px;
    text-align: center;
    background: #fff;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    color: var(--text-secondary);
}

.tm-empty > .bi {
    display: block;
    font-size: 28px;
    color: var(--text-light);
    margin-bottom: 6px;
}

.tm-rowcap {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    padding: 8px 4px 0;
    font-size: var(--fs-small);
    color: var(--text-secondary);
}

@media (max-width: 767.98px) {
    /* Sul telefono la tabella diventa una scheda per riga (`table-mobile`): niente bordo
       esterno, le schede ce l'hanno gia'. La testata fissa non serve: non c'e' testata. */
    table.tm-table.table-mobile {
        border: 0;
        background: transparent;
    }

    table.tm-table.table-mobile > tbody > tr > td {
        height: auto;
    }
}

/* ── La «i» (InfoTip) ─────────────────────────────────────────────────────── */
.tm-info {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    padding: 0;
    margin: 0 2px;
    border: 0;
    background: none;
    color: var(--text-light);
    font-size: 14px;
    line-height: 1;
    vertical-align: middle;
    cursor: help;
    text-transform: none;
    flex: 0 0 auto;
}

.tm-info:hover,
.tm-info:focus-visible,
.tm-info[aria-expanded="true"] {
    color: var(--accent-primary);
    outline: 0;
}

.tm-info:focus-visible {
    box-shadow: 0 0 0 2px rgba(123, 104, 238, 0.35);
    border-radius: 50%;
}

@media (max-width: 767.98px), (hover: none) {
    /* 44 px al tocco senza far crescere la riga: l'area toccabile e' lo pseudo-elemento. */
    .tm-info::after {
        content: '';
        position: absolute;
        inset: -13px;
    }
}

#tm-tip {
    position: fixed;
    z-index: 1300;
    display: none;
    max-width: 300px;
    padding: 8px 10px;
    border-radius: 6px;
    background: #1E1F21;
    color: #fff;
    font-size: var(--fs-small);
    font-weight: 400;
    line-height: 1.45;
    text-transform: none;
    letter-spacing: normal;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
}

#tm-tip ul {
    margin: 4px 0 0;
    padding-left: 18px;
}

#tm-tip a {
    color: #C9C1FF;
}

/* ── Il pop-up (ZoomDialog) ───────────────────────────────────────────────── */
body.tm-overlay-open {
    overflow: hidden;
}

/* Il contenitore copre lo schermo e centra la finestra con flex, **non** con un
   `transform`: un antenato trasformato diventa il riferimento di ogni `position: fixed`
   che sta dentro, e una tendina, una conferma o un secondo pop-up disegnati nel corpo
   finivano spostati e tagliati (visto dall'area 3 sul dossier, 04-10-2026). Cosi' un
   pop-up dentro un pop-up si impila da se': stesso livello, e vince chi viene dopo. */
.tm-zoom {
    position: fixed;
    inset: 0;
    z-index: 1060;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px 16px;
}

.tm-zoom-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(30, 31, 33, 0.35);
}

.tm-zoom-panel {
    position: relative;
    width: min(560px, calc(100vw - 32px));
    max-height: 100%;
    display: flex;
    flex-direction: column;
    background: #fff;
    border-radius: var(--radius-lg);
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.2);
    outline: 0;
}

.tm-zoom-panel.tm-zoom-sm { width: min(420px, calc(100vw - 32px)); }
.tm-zoom-panel.tm-zoom-lg { width: min(760px, calc(100vw - 32px)); }
.tm-zoom-panel.tm-zoom-xl { width: min(1080px, calc(100vw - 32px)); }

.tm-zoom-head {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 14px 16px 12px 20px;
    border-bottom: 1px solid var(--border-subtle);
    flex: 0 0 auto;
}

.tm-zoom-titles {
    flex: 1 1 auto;
    min-width: 0;
}

.tm-zoom-title {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0;
    font-size: var(--fs-title);
    font-weight: 600;
    color: var(--text-primary);
    line-height: 1.3;
    overflow-wrap: anywhere;
}

.tm-zoom-sub {
    margin-top: 2px;
    font-size: var(--fs-small);
    color: var(--text-secondary);
}

.tm-zoom-x {
    flex: 0 0 auto;
}

/* Quando ci sono le schede, la testata non ha il filetto: lo porta la riga delle schede. */
.tm-zoom-head:has(+ .tm-zoom-tabs) {
    border-bottom: 0;
    padding-bottom: 4px;
}

.tm-zoom-tabs {
    display: flex;
    gap: 4px;
    padding: 0 20px;
    border-bottom: 1px solid var(--border-subtle);
    overflow-x: auto;
    scrollbar-width: none;
    flex: 0 0 auto;
}

.tm-zoom-tab {
    border: 0;
    background: none;
    padding: 9px 10px;
    margin-bottom: -1px;
    border-bottom: 2px solid transparent;
    color: var(--text-secondary);
    font-size: var(--fs-body);
    white-space: nowrap;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.tm-zoom-tab.is-active {
    color: var(--accent-primary);
    border-bottom-color: var(--accent-primary);
    font-weight: 500;
}

.tm-zoom-tab-count {
    font-size: var(--fs-meta);
    color: var(--text-light);
    font-variant-numeric: tabular-nums;
}

.tm-zoom-tab.is-active .tm-zoom-tab-count {
    color: var(--accent-primary);
}

.tm-zoom-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 16px 20px;
}

.tm-zoom-foot {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    padding: 12px 20px;
    border-top: 1px solid var(--border-subtle);
    background: #fff;
    border-radius: 0 0 var(--radius-lg) var(--radius-lg);
}

.tm-zoom-grow {
    flex: 1 1 auto;
}

@media (max-width: 767.98px) {
    /* Sul telefono e' un foglio dal basso, alto quasi tutto lo schermo: la striscia che resta
       in cima dice che sotto c'e' ancora la pagina da cui si e' partiti. */
    .tm-zoom {
        align-items: flex-end;
        padding: 0;
    }

    .tm-zoom-panel,
    .tm-zoom-panel.tm-zoom-sm,
    .tm-zoom-panel.tm-zoom-lg,
    .tm-zoom-panel.tm-zoom-xl {
        width: 100%;
        max-height: 92dvh;
        border-radius: var(--radius-lg) var(--radius-lg) 0 0;
        animation: tm-sheet-up 0.22s ease-out;
    }

    .tm-zoom-head {
        padding: 10px 12px 10px 16px;
    }

    .tm-zoom-tabs {
        padding: 0 12px;
    }

    .tm-zoom-tab {
        min-height: 44px;
    }

    .tm-zoom-body {
        padding: 14px 16px;
    }

    .tm-zoom-foot {
        padding: 10px 12px calc(10px + env(safe-area-inset-bottom));
        border-radius: 0;
    }
}

/* La conferma sta sopra il pop-up. */
.modal.tm-confirm {
    z-index: 1070;
}

/* ── Il dettaglio: etichetta–valore ───────────────────────────────────────── */
.tm-dl {
    display: grid;
    grid-template-columns: 150px 1fr;
    gap: 10px 14px;
    margin: 0;
    align-items: start;
}

.tm-dl > dt {
    display: flex;
    align-items: center;
    gap: 4px;
    padding-top: 2px;
    font-size: var(--fs-meta);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--text-light);
}

.tm-dl > dd {
    margin: 0;
    color: var(--text-primary);
    min-width: 0;
    overflow-wrap: anywhere;
}

.tm-dl .tm-none {
    color: var(--text-light);
}

.tm-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.tm-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 1px 10px;
    border-radius: 999px;
    background: var(--content-bg);
    border: 1px solid var(--border-subtle);
    font-size: var(--fs-small);
    color: var(--text-primary);
}

@media (max-width: 767.98px) {
    .tm-dl {
        grid-template-columns: 1fr;
        gap: 2px;
    }

    .tm-dl > dd {
        margin-bottom: 10px;
    }
}

/* ── I campi dentro il pop-up ─────────────────────────────────────────────── */
.tm-field {
    margin-bottom: 14px;
}

.tm-field > .tm-label,
.tm-label {
    display: flex;
    align-items: center;
    gap: 4px;
    margin-bottom: 4px;
    font-size: var(--fs-meta);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--text-light);
}

.tm-check {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    padding: 4px 0;
    color: var(--text-primary);
    cursor: pointer;
}

.tm-check > input {
    flex: 0 0 auto;
    margin-top: 3px;
}

.tm-check-hint {
    display: block;
    color: var(--text-secondary);
    font-size: var(--fs-small);
}

.tm-note {
    margin: 0 0 12px;
    color: var(--text-secondary);
    font-size: var(--fs-small);
}

/* ── Le spunte dei permessi (PermissionChecklist) ─────────────────────────── */
.tm-perm-list.is-columns .tm-perm-group-items {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: 2px 16px;
}

.tm-perm-group {
    margin-bottom: 14px;
}

.tm-perm-group:last-child {
    margin-bottom: 0;
}

.tm-perm-group-head {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 4px;
    font-size: var(--fs-meta);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--text-light);
}

.tm-perm-group-count {
    font-variant-numeric: tabular-nums;
}

.tm-perm-group-count.is-on {
    color: var(--accent-primary);
}

.tm-perm-item {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 4px 8px;
    min-height: 32px;
    padding: 2px 0;
    color: var(--text-secondary);
    cursor: pointer;
}

.tm-perm-item > input {
    flex: 0 0 auto;
    margin: 0;
}

.tm-perm-item.is-on {
    color: var(--text-primary);
}

/* Spuntato perche' arriva da un'altra parte: grigio, non viola (regola 54). */
.tm-perm-item.is-inherited > input:checked {
    background-color: var(--text-light);
    border-color: var(--text-light);
}

.tm-perm-from {
    font-size: var(--fs-meta);
    color: var(--text-light);
}

@media (max-width: 767.98px) {
    .tm-perm-item {
        min-height: 44px;
    }
}

@media (max-width: 767.98px) {
    /* Sul telefono la scheda di una riga tiene il nome, lo stato e l'azione: il resto si
       legge nel dettaglio, a un tocco. Le colonne da togliere portano `tm-hide-mobile`. */
    table.table-mobile > tbody > tr > td.tm-hide-mobile {
        display: none !important;
    }

    table.tm-table.table-mobile > tbody > tr > td.tm-name {
        justify-content: flex-start;
        font-size: var(--fs-md);
    }

    table.tm-table.table-mobile > tbody > tr > td.tm-actions {
        border-top: 0 !important;
        margin-top: 0;
        padding-top: 4px;
        justify-content: flex-end !important;
    }

    table.tm-table.table-mobile > tbody > tr > td.tm-actions .btn {
        flex: 0 0 auto;
    }

    table.tm-table.table-mobile > tbody > tr.tm-row {
        cursor: pointer;
    }
}

/* Icona + testo in un pulsante: `<i class="bi"></i><span>Testo</span>`. Blazor toglie gli
   spazi fra un elemento e l'altro, quindi lo stacco va scritto; dove il testo e' un nodo
   nudo dopo l'icona (`me-1`) la regola non scatta, perche' l'icona e' l'unico elemento. */
.btn > .bi:first-child:not(:only-child) {
    margin-right: 6px;
}

@media (max-width: 767.98px) {
    /* La scheda di una riga, compatta: a sinistra il nome e sotto le righe che restano, a
       destra l'azione, alta quanto la scheda. Prima la cella delle azioni portava il suo
       `width: 1%` del desktop e il pulsante usciva dalla scheda a sinistra. */
    table.tm-table.table-mobile > tbody > tr {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        column-gap: 10px;
        row-gap: 2px;
        align-items: center;
        /* Compatta (04-10-2026), ma mai sotto i 44 px: la scheda intera apre il dettaglio. */
        padding: var(--tm-card-py) var(--tm-card-px);
        margin-bottom: var(--tm-card-gap);
        min-height: 44px;
    }

    table.tm-table.table-mobile > tbody > tr:last-child {
        margin-bottom: 0;
    }

    table.tm-table.table-mobile > tbody > tr > td {
        grid-column: 1;
        padding: 0;
    }

    table.tm-table.table-mobile > tbody > tr > td.tm-actions {
        grid-column: 2;
        grid-row: 1 / span 6;
        width: auto;
        align-self: center;
    }

    /* Una riga senza etichetta sotto il nome (le icone di stato): a sinistra, non a destra. */
    table.tm-table.table-mobile > tbody > tr > td.tm-sub {
        justify-content: flex-start;
    }

    /* Nella barra, sotto i 576 px, i pulsanti secondari restano solo icona (con il loro
       aria-label): titolo e «Nuovo» stanno cosi' sulla stessa riga. */
}

@media (max-width: 575.98px) {
    .tm-bar .tm-hide-narrow {
        display: none;
    }

    .tm-bar .btn > .bi:first-child:has(+ .tm-hide-narrow) {
        margin-right: 0;
    }
}

@media (max-width: 575.98px) {
    /* Il pie' del pop-up sta su una riga sola: Elimina resta il cestino (con il suo nome
       nell'aria-label e nel title), Salva e Annulla tengono il testo. */
    .tm-zoom-del-text {
        display: none;
    }

    .tm-zoom-del > .bi:first-child {
        margin-right: 0;
    }

    .tm-zoom-foot {
        flex-wrap: nowrap;
    }
}

@media (max-width: 767.98px) {
    /* Una riga di stato senza icone non lascia un buco nella scheda. */
    table.tm-table.table-mobile > tbody > tr > td.tm-sub:has(> .tm-states:empty) {
        display: none;
    }
}

/* ── Titolo di una sezione dentro una pagina (04-10-2026, proposto dall'area 5) ── */
.tm-section {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin: 24px 0 8px;
}

.tm-section:first-of-type {
    margin-top: 4px;
}

.tm-section-title {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin: 0;
    font-size: var(--fs-md);
    font-weight: 600;
    color: var(--text-primary);
}

/* La seconda riga nella cella del nome: un estratto, un messaggio, il tipo. */
.tm-table td.tm-name .tm-name-sub {
    display: block;
    max-width: 640px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--fs-small);
    font-weight: 400;
    color: var(--text-secondary);
}

@media (max-width: 767.98px) {
    .tm-table td.tm-name .tm-name-sub {
        max-width: none;
        white-space: normal;
    }
}

/* Dentro il pop-up non c'e' la testata dell'applicazione: l'intestazione fissa sta in cima. */
.tm-zoom-body .tm-table > thead > tr > th {
    top: 0;
}

/* Il pallino del colore di un tipo di turno (04-10-2026, proposto dall'area 2b). */
.tm-dot {
    display: inline-block;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    flex: 0 0 auto;
}

/* Le tendine di ricerca (SearchPicker, VolunteerSearchSelect) dentro un pop-up stanno nel
   flusso e non galleggiano: il corpo del pop-up scorre, e una tendina assoluta in fondo al
   corpo restava tagliata dal bordo (visto in /org/shift-types, scheda Posti, 04-10-2026).
   Quando compare, ui-kit.js porta il corpo a mostrarla. */
.tm-zoom-body .vss-dropdown {
    position: static;
    margin-top: 4px;
}

/* Un pie' su misura con tre o quattro azioni (il turno, il dettaglio di una richiesta) le
   raccoglie in `.tm-zoom-actions`, che va a capo sul telefono (04-10-2026, area 4). */
.tm-zoom-foot > .tm-zoom-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    flex: 1 1 auto;
    min-width: 0;
}

/* «Da sistemare» resta rosso anche dentro `.tm-table`, che le celle le vuole trasparenti:
   a parita' di specificita' vinceva la regola della tabella, scritta dopo (area 4). */
.tm-table > tbody > tr.tm-needs-action > td {
    background-color: rgba(220, 53, 69, 0.07);
}

.tm-table > tbody > tr.tm-needs-action > td:first-child {
    box-shadow: inset 4px 0 0 0 #dc3545;
}

/* Un turno nel calendario apre il suo riepilogo nel pop-up (04-10-2026): non e' piu' un link,
   e il cursore lo deve dire lo stesso. */
.cal-event,
.cal-week-event {
    cursor: pointer;
}

/* ── Lista a sinistra, dettaglio a destra (04-10-2026, solo /org/roles) ───────────────
   Richiesta dell'utente per la pagina dei ruoli: il clic sulla riga mostra il ruolo in una
   colonna di destra, in sola lettura, con «Modifica» in alto che apre il pop-up. È
   un'eccezione voluta al modello «il dettaglio si apre in un pop-up» (regola 106), e vale
   **solo dai 992 px**: sotto, una colonna di destra finirebbe due schermate più giù
   (regola 45), quindi non si disegna e il clic apre il pop-up (lo decide OrgRoles.razor).
   `align-items: start` serve alla colonna `sticky`: in una griglia che stira i figli, il
   figlio è alto quanto la riga e `sticky` non ha spazio in cui muoversi. */
:root {
    --tm-split-side-w: 380px;
}

.tm-split-side {
    display: none;
}

@media (min-width: 992px) {
    .tm-split {
        display: grid;
        grid-template-columns: minmax(0, 1fr) var(--tm-split-side-w);
        gap: 16px;
        align-items: start;
    }

    .tm-split-main {
        min-width: 0;
    }

    .tm-split-side {
        display: block;
        position: sticky;
        /* Sotto la testata dell'applicazione (48 px) con un po' d'aria. */
        top: 60px;
        max-height: calc(100vh - 72px);
        overflow-y: auto;
        background: #fff;
        border: 1px solid var(--border-subtle);
        border-radius: var(--radius-md);
    }

    /* La riga scelta: la stessa tinta del passaggio del mouse, più il filetto viola, così
       resta riconoscibile anche quando il mouse è altrove. */
    .tm-table > tbody > tr.tm-row.is-selected > td {
        background: #EFEDFF;
    }

    .tm-table > tbody > tr.tm-row.is-selected > td:first-child {
        box-shadow: inset 3px 0 0 0 var(--accent-primary);
    }
}

.tm-split-empty {
    padding: 36px 16px;
    text-align: center;
    color: var(--text-secondary);
}

.tm-split-empty > .bi {
    display: block;
    font-size: 28px;
    color: var(--text-light);
    margin-bottom: 6px;
}

.tm-split-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 14px;
    border-bottom: 1px solid var(--border-subtle);
}

.tm-split-titles {
    flex: 1 1 auto;
    min-width: 0;
}

.tm-split-title {
    margin: 0;
    font-size: var(--fs-title);
    font-weight: 600;
    color: var(--text-primary);
    overflow-wrap: anywhere;
}

.tm-split-sub {
    font-size: var(--fs-meta);
    color: var(--text-light);
}

.tm-split-body {
    padding: 14px;
}

.tm-split-body .tm-dl {
    grid-template-columns: 110px 1fr;
}

/* Con la colonna di destra la tabella ha meno posto: le colonne che il dettaglio ripete
   escono finché non ci stanno (misurato: a 1024 px la tabella intera chiedeva 747 px e la
   colonna sinistra ne aveva 339, e passava sotto il pannello). `tm-split-opt` sono i
   conteggi, che tornano dai 1400 px; `tm-split-opt2` è il tipo, che torna dai 1200. */
@media (min-width: 992px) and (max-width: 1399.98px) {
    .tm-split .tm-split-opt {
        display: none;
    }
}

@media (min-width: 992px) and (max-width: 1199.98px) {
    .tm-split .tm-split-opt2 {
        display: none;
    }

    .tm-split {
        --tm-split-side-w: 340px;
    }
}
