/*
 * SKATE@LAB — foglio di stile condiviso.
 *
 * Principi (vedi VISION.md §9 e la guida ui-ux-specialist):
 * - mobile-first: progettato prima per ~375px (telefono a bordo pista),
 *   poi tablet/desktop.
 * - nessun framework: variabili CSS + regole semplici, coerente con
 *   l'HTML nativo minimale già in uso nei template.
 * - doppio output: leggibile a schermo E stampabile bene (vedi sezione
 *   "Stampa" in fondo) — la scheda allenamento è il caso critico.
 * - touch target generosi (min 44px) per uso da bordo pista.
 */

/* --- Tipografia (v1.27) ---------------------------------------------------
   Barlow e Barlow Condensed, self-hostate: l'app è locale/offline-first
   (VISION), quindi niente `<link>` a un CDN — a bordo pista la rete non è
   garantita, e un font che non arriva è un font che non c'è.

   Quattro tagli e non di più, perché ognuno è un file che pesa: Barlow 400
   (corpo) e 600 (etichette, testo forte, bottoni); Barlow Condensed 600
   (titoli, occhielli, intestazioni) e 700 (titolo di schermata, cifre
   grandi). Barlow 500 è stato **escluso**: a 15 px le etichette si separano
   già dal corpo per dimensione, e il 500 costava 22 KB per una sfumatura.

   Nessun corsivo, deliberatamente. I quattro `<em>` dei template nominano un
   livello di valutazione («ancora sotto *Applicare*»): diventeranno
   `.level-chip`, che esiste già, dice la stessa cosa meglio e non richiede
   un quinto file.

   Barlow ha l'altezza della x più bassa del 17 % rispetto a Manrope
   (0,449 em contro 0,540): a parità di corpo dichiarato si legge più
   piccola. Fino alla v1.28 la scala compensava moltiplicando per 1,2 ogni
   gradino, così che la dimensione **percepita** restasse quella di prima
   del cambio di carattere. Dalla v1.29 quella compensazione non c'è più: i
   corpi sono quelli dei mockup — 16 px il corpo, 14 px il secondario — e la
   scelta è del committente, che ha visto le due misure a confronto e ha
   preferito la più stretta. Quel che si perde, e la soglia che ne esce,
   stanno scritti nel commento della scala più sotto: è lì che va riletto,
   non qui.

   `font-display: swap`: se un file manca, la pagina resta leggibile sullo
   stack di sistema invece di restare bianca. Lo stack di ripiego di
   `--font-cond` mette per primo un condensato di sistema, perché ripiegare
   su un tondo cambierebbe le larghezze e romperebbe proprio le etichette
   che la condensata serve a far stare. --- */
@font-face {
  font-family: "Barlow";
  src: url("fonts/Barlow-400-latin.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
    U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
    U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Barlow";
  src: url("fonts/Barlow-600-latin.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
    U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
    U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Barlow Condensed";
  src: url("fonts/BarlowCondensed-600-latin.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
    U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
    U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Barlow Condensed";
  src: url("fonts/BarlowCondensed-700-latin.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
    U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
    U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Secondo subset per entrambe le famiglie: serve solo ai caratteri fuori
   dal latino di base. Le accentate italiane, francesi e tedesche stanno
   già nel subset sopra — questo copre il resto (es. nomi propri stranieri
   nel catalogo importato) senza pesare sul caso normale, perché il
   browser lo scarica solo se un glifo lo richiede. */
@font-face {
  font-family: "Barlow";
  src: url("fonts/Barlow-400-latin-ext.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
    U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F,
    U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
    U+A720-A7FF;
}

@font-face {
  font-family: "Barlow";
  src: url("fonts/Barlow-600-latin-ext.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
    U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F,
    U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
    U+A720-A7FF;
}

@font-face {
  font-family: "Barlow Condensed";
  src: url("fonts/BarlowCondensed-600-latin-ext.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
    U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F,
    U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
    U+A720-A7FF;
}

@font-face {
  font-family: "Barlow Condensed";
  src: url("fonts/BarlowCondensed-700-latin-ext.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
    U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F,
    U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
    U+A720-A7FF;
}

:root {
  /* --- Neutri freddi (v1.12, ritoccati alla v1.29 sui mockup) -------------
     Registro quasi piatto: bianco puro sulle card, grigio chiarissimo sulla
     pagina, testo quasi-nero. Lo scarto rispetto alla v1.28 è di pochi
     punti su ogni canale — fondo un soffio più freddo, inchiostro un soffio
     più blu, grigio del testo secondario più scuro. Quest'ultimo è l'unico
     che cambia qualcosa di misurabile: #6b7280 su bianco dava 4,8:1,
     #5F6875 dà 5,6:1, e le didascalie sono appena scese a 14 px. --- */
  --color-bg: #F5F6F8;
  --color-surface: #FFFFFF;
  --color-text: #171A21;
  --color-text-muted: #5F6875;
  --color-border: #e3e5e9;

  /* --- Accento unico: petrolio/ghiaccio (Opzione B del mockup) ----------
     Nome delle variabili invariato dalla palette verde precedente: bottoni,
     link e nav vi fanno già riferimento ovunque, quindi il cambio di
     accento è questo blocco e nient'altro — nessun template toccato.

     (v1.29) Il primario passa a #0F766E, e le altre due tinte lo seguono
     **per forza**: erano su una tonalità più fredda (187° contro 175°) e
     accostate al nuovo verde-petrolio si leggevano come un secondo
     accento, non come lo stesso più scuro e lo stesso più chiaro. Le tre
     tinte stanno ora sulla stessa tonalità e cambiano solo di luminosità.
     Bianco su #0F766E dà 5,5:1; #115E59 su bianco dà 7,6:1, ed è quello che
     vestono i link. --- */
  --color-primary: #0F766E;
  --color-primary-dark: #115E59;
  --color-primary-contrast: #ffffff;
  /* Tinta chiarissima dello stesso accento: stati selezionati, pill di nav,
     righe evidenziate, riquadri di precisazione. */
  --color-primary-soft: #E6F4F1;
  --color-ok-bg: #e6f4ea;
  --color-ok-border: #34a853;
  --color-ok-text: #1e7a34;
  --color-warning-bg: #fff4e0;
  --color-warning-border: #e0a012;
  --color-warning-text: #7a4d00;
  --color-danger-bg: #fdecea;
  --color-danger-border: #d64541;
  --color-danger-text: #a12822;
  --color-badge-bg: #ede7f6;
  --color-badge-text: #4527a0;

  /* --- (v1.21) Filtri: stato "attivo" di chip, toggle e contatore. Alias
     dell'accento già in uso, nessun colore nuovo: un filtro attivo è un
     elemento selezionato, stesso vocabolario visivo delle pill di nav. --- */
  --color-chip-bg: var(--color-primary-soft);
  --color-chip-text: var(--color-primary-dark);
  --color-chip-border: var(--color-primary);

  /* --- (v1.23) Famiglie di temi. Il colore appartiene al tema e non al
     contesto in cui compare: la stessa "Condizione fisica" è verde nella
     timeline di stagione, sulla scheda e nei filtri del catalogo. Sette
     famiglie e non undici temi perché oltre le sette-otto tinte due
     qualsiasi diventano indistinguibili per chi ha un deficit di visione
     cromatica — il tema resta identificato dalla sua etichetta, che in
     questi grafici c'è sempre.

     L'ordine di queste righe è quello su cui la palette è stata verificata
     per coppie adiacenti (`ThemeFamily::ALL` in core): due famiglie vicine
     sono garantite distinguibili solo così.

     NON usare queste tinte dove il colore significa già "quanto" (le scale
     ordinali hanno la loro rampa a un tono, e due passi di quella rampa
     ripetono per hex due valori di qui). Una superficie usa l'una o
     l'altra, mai entrambe.

     Unica fonte di verità: `ThemeFamily::color_light`/`color_dark` in core.
     Se cambiano lì, cambiano qui. --- */
  --fam-tecnica: #2a78d6;
  --fam-costruzione: #eb6834;
  --fam-recupero: #1baf7a;
  --fam-transizione: #eda100;
  --fam-gioco: #e87ba4;
  --fam-atletica: #008300;
  --fam-portiere: #4a3aa7;

  /* --- (v1.39, issue #31) Le tinte della fase di gioco. Regola d'uso, non
     una preferenza: vivono **solo dentro il glifo semaforo**
     (`.fase-glifo`, `web::game_phase_glyph`) e non riempiono mai una card,
     una barra o uno sfondo. Il colore strutturale resta quello della
     famiglia del tema, qui sopra.

     Nel glifo il significato lo porta la *posizione* della luce accesa —
     alto, mezzo, basso — e la tinta la rinforza soltanto: la fase si legge
     su una fotocopia in bianco e nero e senza distinguere il rosso dal
     verde. Per questo il verde e l'arancio possono ripetere per hex
     `--fam-recupero` e `--fam-transizione` senza creare ambiguità: non
     competono mai sulla stessa superficie, e nessuna delle due
     informazioni dipende dalla tinta per essere letta.

     Il rosso resta riservato all'allarme *come riempimento*: dentro un
     glifo di 17 × 37 px è chiaramente un'altra cosa, perché lo dichiara la
     forma. --- */
  --fase-verde: #1baf7a;
  --fase-arancio: #eda100;
  --fase-rosso: #e34948;

  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 1rem;
  --space-4: 1.5rem;
  --space-5: 2.5rem;

  --radius-sm: 8px;
  --radius-md: 12px;
  --radius-lg: 16px;
  --radius-pill: 999px;
  /* (v1.23) Mancava: `--radius-nav` era usato da `.sidebar-link` e da
     `.team-switcher > summary` ma non definito da nessuna parte, quindi
     `border-radius: var(--radius-nav)` non produceva alcun arrotondamento.
     10px e non `--radius-pill`: la voce attiva porta una barra d'accento
     `inset` sul bordo sinistro, e con un raggio da pillola quella barra si
     incurverebbe agli estremi. */
  --radius-nav: 10px;

  /* Ombre molto tenui: il registro poggia sui bordi sottili, non
     sull'elevazione. */
  --shadow-sm: 0 1px 2px rgba(16, 24, 40, 0.04);
  --shadow-md: 0 4px 10px rgba(16, 24, 40, 0.06);

  --font-sans: "Barlow", system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  /* Il ripiego è un condensato, non un tondo: se Barlow Condensed non
     arriva, ciò che deve reggere è la **larghezza** — è per starci dentro
     che questa famiglia esiste. */
  --font-cond: "Barlow Condensed", "Barlow Semi Condensed", "Arial Narrow", var(--font-sans);

  /* --- Scala tipografica (v1.29, corpi dei mockup «Valutazioni») ----------
     Sette gradini, ognuno un multiplo esatto di 1/16 rem, così la scala
     continua a rispettare la dimensione di testo scelta nel browser invece
     di ignorarla come farebbero i px.

     Non è più una progressione geometrica, e vale la pena saperlo prima di
     aggiungere un componente. Fino alla v1.28 il rapporto era costante
     (×1,2) e serviva a compensare l'altezza della x di Barlow; questi sono
     invece i corpi disegnati nei mockup, adottati per decisione esplicita
     del committente. Il prezzo si vede in due punti: fra `--t-3` e `--t-4`
     ci sono due pixel — un gradino che da solo non fa gerarchia, e infatti
     i ruoli che li usano cambiano anche carattere e peso — e fra `--t-4` e
     `--t-5` ce ne sono sei. Si scelga sempre un **ruolo**, mai un gradino:
     i ruoli sanno già quali dei sette stanno bene accanto a quale.

     `--t-6` e `--t-7` si sono scambiati di posto rispetto alla v1.28: nei
     mockup il titolo di schermata è più grande della cifra isolata, mentre
     prima era il contrario. Il gradino più alto è quindi il titolo.

     **La soglia è cambiata, ed è una decisione, non una dimenticanza.**
     Fino alla v1.28 era 15 px in cassa bassa, misurata al campo il
     2026-09-06 in piedi, con il telefono a distanza di braccio. Adesso:

       · 14 px (`--t-2`) è il minimo per il testo in **cassa bassa** —
         etichette, didascalie, corpo secondario;
       · 12 px (`--t-1`) è ammesso **solo in maiuscoletto**, perché una
         parola tutta maiuscola si legge sull'altezza delle maiuscole
         (0,662 em in Barlow Condensed, cioè 7,9 px a un corpo di 12) e non
         su quella della x, che a 12 px sarebbe 5,4 px. Un occhiello in
         cassa bassa a `--t-1` resta un difetto, non una scelta.

     Il ragionamento della v1.27 non era sbagliato e non va cancellato: a
     bordo pista, in piedi, con il telefono a distanza di braccio, 14 px in
     cassa bassa si decifrano ma non si leggono a colpo d'occhio come 15 —
     ed è a colpo d'occhio che questa app viene letta durante una seduta.
     È un **rischio accettato**: il committente ha visto le due misure a
     confronto e ha scelto la più stretta perché la pagina ci guadagna in
     densità, che su una matrice di quattordici colonne è quel che serve. Se
     un giorno una seduta dimostrerà che sul campo non regge, il posto da
     cui ripartire è questo: sono sette righe.

     Come verificare che la regola regge, senza rileggere il foglio: in
     console, `[...document.querySelectorAll("*")]` filtrato su
     `getComputedStyle(el).fontSize` minore di 14px e `textTransform`
     diverso da `uppercase` deve dare zero elementi su ogni schermata.
     (L'unico corpo che sta fra due gradini, i 15 px di
     `--type-figure-cell`, passa il controllo: è sopra la soglia.) --- */
  --t-1: 0.75rem; /* 12 px — occhielli, intestazioni di tabella; solo in maiuscoletto */
  --t-2: 0.875rem; /* 14 px — etichette, didascalie, corpo secondario */
  --t-3: 1rem; /* 16 px — corpo */
  --t-4: 1.125rem; /* 18 px — nomi, titoli di terzo livello */
  --t-5: 1.5rem; /* 24 px — titoli di sezione */
  --t-6: 1.875rem; /* 30 px — cifra isolata */
  --t-7: 2.125rem; /* 34 px — titolo di schermata */

  /* --- I ruoli ------------------------------------------------------------
     Un componente richiama un ruolo, non una dimensione: `font: var(--type-…)`
     e, dove serve, la spaziatura gemella `var(--type-…-ls)`. La scorciatoia
     `font` porta peso, dimensione, interlinea e famiglia in una riga sola;
     la spaziatura non ne fa parte e viaggia accanto.

     Attenzione a un effetto della scorciatoia: `font` azzera
     `font-variant-numeric` — e lo azzera anche quando il valore era
     **ereditato**, non dichiarato lì. Una cella dentro una tabella che
     imposta `tabular-nums` perde le cifre tabulari nel momento in cui
     prende un ruolo. Perciò ogni uso di `--type-figure-cell` ridichiara
     `tabular-nums` subito dopo la scorciatoia: è il contratto del ruolo,
     non una ripetizione.

     Regola per decidere: **tabulari quando il numero va confrontato con un
     altro** — sopra e sotto in colonna, oppure con sé stesso un attimo
     prima, se si aggiorna restando dov'è. Proporzionali quando è solo.

     (v1.29) I pesi dei mockup non esistono tutti come file, e i pesi che
     non esistono il browser se li **inventa** ingrossando le aste con un
     algoritmo: su una grottesca la differenza fra un 700 disegnato e un 700
     finto si vede, e si vede peggio ai corpi piccoli. Imbarcati ci sono
     soltanto Barlow 400/600 e Barlow Condensed 600/700 (quattro file, vedi
     i `@font-face` in cima), quindi l'800 dei mockup sul condensato qui è
     700 e il 700 sul tondo qui è 600. Ogni arrotondamento è annotato sulla
     riga che lo subisce. Nessun file nuovo: aggiungerne uno per una
     sfumatura di peso costerebbe una ventina di KB su una app che deve
     partire anche senza rete. --- */
  --type-screen: 700 var(--t-7) / 1.05 var(--font-cond); /* mockup 800 → 700: un Barlow Condensed 800 non è imbarcato */
  --type-section: 700 var(--t-5) / 1.1 var(--font-cond);
  --type-title: 700 var(--t-4) / 1.15 var(--font-cond);
  --type-eyebrow: 700 var(--t-1) / 1.2 var(--font-cond);
  --type-eyebrow-ls: 0.09em;
  --type-thead: 700 var(--t-1) / 1.2 var(--font-cond);
  --type-thead-ls: 0.07em;
  /* Interlinea di nuovo 1,5. La v1.27 l'aveva stretta a 1,35 perché l'em
     era cresciuto di 1,2 al cambio di carattere, e il moltiplicatore si
     applica all'em: lasciando 1,5 le righe si sarebbero allontanate del
     17 % senza che nessuno l'avesse deciso. Adesso l'em è tornato a 16 px e
     quella ragione è scaduta insieme alla scala che l'aveva prodotta —
     tenere 1,35 su un corpo da 16 px stringerebbe le righe di una
     spiegazione fin quasi a toccarsi. */
  --type-body: 400 var(--t-3) / 1.5 var(--font-sans);
  --type-body-strong: 600 var(--t-3) / 1.5 var(--font-sans);
  --type-label: 600 var(--t-2) / 1.25 var(--font-sans); /* mockup 700 → 600: sul tondo il 700 sarebbe sintetico */
  --type-label-ls: 0.01em;
  --type-caption: 400 var(--t-2) / 1.35 var(--font-sans);
  --type-figure: 700 var(--t-6) / 1 var(--font-cond);
  --type-figure-sm: 700 var(--t-4) / 1.1 var(--font-cond);
  /* L'unico corpo fuori scala del foglio, e sta scritto qui perché non
     resti un mistero per chi lo trova: 15 px cade fra `--t-2` e `--t-3`. A
     14 px una cifra dentro una cella colorata perde peso contro il fondo —
     e queste cifre sono l'unico posto in cui il livello **non** vive nel
     colore; a 16 px la matrice si allarga di una colonna intera e su
     telefono si perde prima. Vive solo nelle celle e nei segmenti delle
     barre. */
  --type-figure-cell: 600 0.9375rem / 1.3 var(--font-sans);
  --type-button: 600 var(--t-3) / 1 var(--font-sans);
  --type-button-sm: 600 var(--t-2) / 1 var(--font-sans);

  /* --- La variante di stampa (v1.27) --------------------------------------
     Stessi ruoli, stesso rapporto ×1,2, unità diversa: la carta ha una
     misura fisica e i `pt` sono la sua. Sei gradini invece di sette perché
     su un foglio non c'è la cifra isolata dei riquadri di sintesi.

     Cambia la soglia, non la regola: su carta, a 30 cm, con inchiostro nero
     pieno e senza riflessi, il minimo scende — e la scheda G+S ha una
     griglia data da rispettare. Il gradino a 6 pt di `.gs-slot-tag` però
     sparisce comunque: erano 2,1 mm, e sotto quella misura la fotocopia in
     bianco e nero — che è come queste schede circolano davvero — chiude i
     contrografi.

     Undici dimensioni distinte diventano sei. Il peso 700 diventa 600
     ovunque: l'inchiostro su carta ingrossa già di suo, e il 700 di Barlow
     Condensed sotto gli 11 pt impasta le aste.

     **Da provare stampato, non in anteprima.** La resa dei pesi condensati
     ai corpi piccoli dipende dal driver, e questa è l'unica parte del
     sistema che uno schermo non sa verificare. --- */
  --tp-1: 7.5pt; /* solo maiuscoletto */
  --tp-2: 9pt; /* didascalie, note, meta */
  --tp-3: 10.5pt; /* corpo */
  --tp-4: 12.5pt; /* nome dell'esercizio */
  --tp-5: 15pt; /* sezione */
  --tp-6: 18pt; /* titolo del foglio */

  --typep-screen: 600 var(--tp-6) / 1.1 var(--font-cond);
  --typep-section: 600 var(--tp-5) / 1.15 var(--font-cond);
  --typep-title: 600 var(--tp-4) / 1.2 var(--font-cond);
  --typep-eyebrow: 600 var(--tp-1) / 1.25 var(--font-cond);
  --typep-eyebrow-ls: 0.08em;
  --typep-body: 400 var(--tp-3) / 1.15 var(--font-sans);
  --typep-body-strong: 600 var(--tp-3) / 1.15 var(--font-sans);
  --typep-caption: 400 var(--tp-2) / 1.15 var(--font-sans);

  /* Larghezza di lettura di default. Le pagine che hanno bisogno di più
     spazio la sovrascrivono con un modificatore su `<main>` (vedi
     `main_modifier` in base.html), non con una seconda base.

     (v1.29) La colonna larga scende da 76rem a 75rem: 1200 px è la misura
     su cui i mockup sono disegnati, ed è anche il punto in cui la heatmap a
     quattordici colonne smette di aver bisogno di scorrere. Sedici pixel in
     meno non si vedono; averla identica al disegno sì, quando si confronta
     una schermata con il suo mockup. */
  --content-max-width: 40rem;
  --content-max-width-wide: 75rem;
  /* Colonna laterale (filtri del catalogo). */
  --sidebar-width: 17.5rem;
  /* (v1.20) Sidebar di navigazione permanente della shell, da 1024px. */
  --app-sidebar-width: 15.5rem;
  /* (19.09.2026) La stessa sidebar ridotta alle sole icone. */
  --app-sidebar-width-collapsed: 4.5rem;
  /* (issue #97) La barra in basso del telefono, sotto 1024px: alta quanto
     un'icona e un'etichetta con il loro respiro. La shell e lo scorrimento
     le lasciano questo posto, più la linea dell'iPhone.

     (15.09.2026) Un filo più alta (da 3,5 a 3,75rem) per il segno della
     voce accesa sotto l'etichetta, e staccata dal fondo: galleggia sopra la
     pagina di `--bottom-nav-gap`, che sull'iPhone è la zona della linea
     (l'inset) e altrove un respiro di mezzo rem. È larga quanto lo schermo
     meno i margini, e mai più di `--bottom-nav-max-width`: su un tablet
     cinque voci su 700px sarebbero cinque isole. */
  --bottom-nav-height: 3.75rem;
  --bottom-nav-gap: max(var(--space-2), env(safe-area-inset-bottom, 0px));
  --bottom-nav-max-width: 34rem;
  --bottom-nav-width: min(
    var(--bottom-nav-max-width),
    100% - 2 * var(--space-2) - env(safe-area-inset-left, 0px) - env(safe-area-inset-right, 0px)
  );
  /* Il posto che la pagina lascia alla barra: lei, il suo stacco dal fondo
     e un respiro perché l'ultima riga non le stia attaccata. */
  --bottom-nav-space: calc(var(--bottom-nav-height) + var(--bottom-nav-gap) + var(--space-3));

  /* --- (15.09.2026) La shell del telefono ------------------------------
     L'intestazione è la fascia di marchio, in petrolio scuro: lo stesso
     `--color-primary-dark` dei link (bianco sopra dà 7,6:1), non una tinta
     nuova. La pagina le sale sopra di `--shell-sheet-radius`, con gli
     angoli arrotondati, come il foglio di un'app. Il petrolio resta un
     accento: una fascia di 60px in cima, e nient'altro di pieno. */
  --shell-header-bg: var(--color-primary-dark);
  --shell-sheet-radius: 1.25rem;

  /* --- v1.6 (Fase 3.5): serie ricorrenti + tag/badge di calendario --- */
  --color-camp-bg: #e0f2fe;
  --color-camp-text: #075985;

  /* --- (v1.24) Neutri della timeline di stagione.

     Esistono perché in quella vista il colore è **prenotato** dai temi: i
     blocchi diventano due grigi alternati, le tacche fuori piano un grigio
     medio, i confini una linea sottile. Variabili proprie e non i
     `--color-*` generici perché sono una scala coerente fra loro — due grigi
     che devono restare distinguibili l'uno dall'altro e dal fondo — e
     ritoccarne uno per un'altra pagina ne romperebbe la lettura qui.

     `--timeline-today` è inchiostro e **non** `--color-today-accent`, che è
     rosso: il rosso è riservato agli stati di allarme, e oggi non è un
     allarme (requisito §3.4). Nella vista precedente attirava l'occhio più
     dei dati. --- */
  --timeline-band-1: #f0f0ed;
  /* (23.09.2026) I toni dei blocchi: smorzati, lontani dai colori pieni
     delle famiglie (--fam-*). Fondo e inchiostro, a coppie. */
  --block-tone-1: #ece3d2; --block-tone-1-ink: #5f4d2e; /* sabbia */
  --block-tone-2: #dde4ec; --block-tone-2-ink: #36475a; /* ardesia */
  --block-tone-3: #dfe7da; --block-tone-3-ink: #475641; /* salvia */
  --block-tone-4: #e9dee6; --block-tone-4-ink: #5e4458; /* malva */
  --block-tone-5: #eedbd2; --block-tone-5-ink: #6e4636; /* argilla */
  --timeline-band-2: #e4e4e0;
  --timeline-lane: #ececea;
  /* (23.09.2026) La cella vuota di un blocco, sulla riga di un tema. */
  --timeline-cell: #f3f4f6;
  --timeline-line: #d8d8d3;
  --timeline-neutral-mark: #9a9c9f;
  --timeline-today: #16181a;

  /* --- v1.7 (Fase 3.5): vista stagione per mese --- */
  --color-today-accent: #d64541;
  --color-block-1: #0b6e4f;
  --color-block-2: #4527a0;
  --color-block-3: #e0a012;
  --color-block-4: #1e7a9c;
  --color-block-5: #075a3f;
}

/* --- Reset minimale --- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  font: var(--type-body);
  color: var(--color-text);
  background: var(--color-bg);
}

h1,
h2,
h3,
h4 {
  margin-top: 0;
}

h1 {
  font: var(--type-screen);
}

h2 {
  font: var(--type-section);
  margin-bottom: var(--space-2);
}

h3 {
  font: var(--type-title);
  margin-bottom: var(--space-1);
}

/* (v1.26) `h4` non aveva `font-size`: ereditava 1rem dal browser, cioè
   esattamente il corpo del testo che introduce. Nella scheda di allenamento
   «Svolgimento», «Punti chiave», «Errori comuni» e «Varianti» si
   distinguevano dal testo sotto per il solo grassetto — la gerarchia più
   debole di tutta l'app, sulla schermata che si tiene in mano durante la
   seduta. Sono occhielli: è quello il loro ruolo, e ora lo dicono.

   `.gs-exercise-title` è l'unico altro `<h4>` dei template e si riprende il
   suo stile per classe, che vince sul tipo. */
h4 {
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin-bottom: var(--space-1);
}

p {
  margin: 0 0 var(--space-3) 0;
}

a {
  color: var(--color-primary-dark);
}

ul,
ol {
  padding-left: 1.25rem;
}

/* (v1.27) Nessun taglio corsivo è imbarcato, e non è una dimenticanza: i
   quattro `<em>` dei template nominano un termine — un livello di
   valutazione, «il disegno» contro «il testo» — e per dire «questa è una
   parola citata» il semi-grassetto basta. Senza questa riga il browser
   sintetizzerebbe un finto corsivo inclinando le lettere del tondo, che su
   una grottesca si vede ed è brutto; con essa risparmiamo 22 KB di file per
   un effetto che era peggio del rimedio.

   L'`<em>` resta `<em>` nel markup: il lettore di schermo continua a
   sentire l'enfasi, che è il motivo per cui il tag è lì. Cambia solo come
   si vede.

   (Nella risposta al brief avevo scritto che questi `<em>` sarebbero
   diventati `.level-chip`. Sbagliato: `.level-chip` è un bersaglio di tocco
   da 44 px del form di valutazione, e dentro una frase non ci sta.) */
em {
  font-style: normal;
  font-weight: 600;
}

/* --- Andata a capo e troncamento (v1.27) ---------------------------------
   Prima di questo blocco non esisteva nessuna regola: zero `hyphens`, zero
   `overflow-wrap`, e quattro `text-overflow: ellipsis` messi dove serviva
   caso per caso. Il risultato è che ogni contenitore stretto ha risolto il
   problema a modo suo, e i nomi lunghi sono diventati una sorpresa nuova
   ogni volta.

   Le regole sono tre e dipendono dal **contenitore**, non dalla schermata:

   1. contenitore a riga singola  → `.t-clip`    (una riga + `title`)
   2. contenitore a più righe     → `.t-clamp-2` (due righe al massimo)
   3. testo corrente              → sillabazione, qui sotto, senza classe

   `.t-clip` va usata **solo** insieme a un `title` con il testo intero: un
   nome troncato che non è raggiungibile da nessun'altra parte è un nome
   perso, non un nome corto.

   La sillabazione richiede l'attributo `lang` giusto sull'elemento: senza,
   il browser non sa con quale dizionario spezzare e non spezza. Oggi
   `base.html` dichiara `lang="it"` fisso ed è corretto, perché
   l'interfaccia è in italiano; il giorno in cui seguirà il catalogo
   trilingue quell'attributo deve seguire la lingua scelta, o queste regole
   smettono di funzionare in silenzio proprio in tedesco, che è la lingua
   che ne ha più bisogno («Entscheidungsfreudigkeit» sono ventiquattro
   caratteri indivisibili). --- */
p,
li,
dd,
figcaption {
  hyphens: auto;
  /* Non spezzare parole corte: «bloc-co» in una card da tre righe si legge
     come un difetto, non come composizione. Otto caratteri minimi, quattro
     prima del trattino e tre dopo — sotto quella soglia il vantaggio non
     ripaga il trattino. La sillabazione serve alle parole tedesche da venti
     caratteri, ed è lì che deve intervenire. */
  hyphenate-limit-chars: 8 4 3;
  -webkit-hyphenate-limit-before: 4;
  -webkit-hyphenate-limit-after: 3;
  /* `break-word` e non `anywhere`: spezza solo quando non c'è alternativa e
     non influenza la larghezza minima del contenitore, che con `anywhere`
     collasserebbe e cambierebbe la geometria delle griglie. */
  overflow-wrap: break-word;
}

/* Titoli, occhielli ed etichette non si sillabano: sono corti per
   costruzione, e una parola spezzata in un titolo si legge come un errore
   di stampa. Se un titolo non ci sta, il rimedio è il titolo.

   (v1.56) Nemmeno il codice: `SKATELAB_PASSWOR-D_HASH` a capo si legge
   come un nome che contiene un trattino, e un nome di variabile va
   ricopiato com'è. Succedeva sul profilo, al telefono e nella colonna
   «Accesso». */
h1,
h2,
h3,
h4,
th,
label,
legend,
code,
.badge,
.state-badge,
.kind-badge,
.level-chip {
  hyphens: manual;
}

.t-clip,
.timeline-row-label,
.timeline-periods-row .timeline-segment,
.stack-label,
.stack-table th[scope="row"] {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

.t-clamp-2 {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  /* Perché la seconda riga non resti di una parola sola. */
  text-wrap: balance;
  overflow-wrap: break-word;
}

/* --- Layout condiviso (base.html) --- */

/* (issue #97) Sotto 1024px in cima resta solo il marchio, e **non è fisso**:
   scorre via con la pagina. Era `sticky` quando portava anche la
   navigazione, che su un iPhone andava a capo su quattro righe e restava
   lì, sul 41% dello schermo, per tutto lo scorrimento. La navigazione ora è
   la barra in basso (`.bottom-nav`). Gli inset sono dell'iPhone in
   orizzontale, dove la tacca sta sul lato (`viewport-fit=cover`).

   (15.09.2026) La fascia di marchio della shell, in petrolio scuro: il
   marchio a sinistra, l'avatar del profilo a destra, alta quanto il badge
   con il suo respiro. Sotto, `--shell-sheet-radius` in più: è la parte che
   la pagina copre salendole sopra con gli angoli arrotondati
   (`.app-shell`). Resta **non fissa**, per la ragione di sopra. */
.app-header {
  background: var(--shell-header-bg);
  color: var(--color-primary-contrast);
  padding: calc(var(--space-2) + env(safe-area-inset-top, 0px))
    calc(var(--space-3) + env(safe-area-inset-right, 0px))
    calc(var(--space-2) + var(--shell-sheet-radius))
    calc(var(--space-3) + env(safe-area-inset-left, 0px));
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: calc(3.5rem + var(--shell-sheet-radius));
}

.app-header .brand {
  /* (v1.23) Badge e nome sulla stessa riga, allineati al centro: il badge è
     alto quanto due righe di testo, e senza `align-items` il nome
     galleggerebbe sulla sua linea di base. */
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
  font: var(--type-title);
  font-size: 1.25rem;
  letter-spacing: 0.01em;
  color: inherit;
  text-decoration: none;
}

.app-header .brand:hover {
  text-decoration: none;
}

/* Il focus dell'app è `--color-primary-dark`, cioè il fondo di questa
   fascia: qui sarebbe invisibile. Bianco, come sulla sidebar vale
   l'accento chiaro. */
.app-header a:focus-visible {
  outline: 2px solid var(--color-primary-contrast);
  outline-offset: 2px;
}

/* (15.09.2026) L'avatar del profilo: le iniziali in un tondo chiaro, la
   sola azione della fascia. Il tondo è di 36px e il bersaglio di 44, come
   ogni comando da bordo pista. */
.app-header-profile {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  margin-right: calc(-1 * var(--space-1));
  border-radius: var(--radius-pill);
  text-decoration: none;
}

.app-header-profile:hover {
  text-decoration: none;
}

.app-header-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-pill);
  background: var(--color-primary-soft);
  color: var(--color-primary-dark);
  font: var(--type-label);
  letter-spacing: 0.02em;
}

/* Sul profilo l'avatar è la voce accesa: un anello bianco, come la voce
   della barra in basso lo è per le altre pagine. */
.nav-profilo .app-header-avatar {
  box-shadow: 0 0 0 2px var(--shell-header-bg), 0 0 0 4px var(--color-primary-contrast);
}

.app-main {
  max-width: var(--content-max-width);
  margin: 0 auto;
  padding: var(--space-4) var(--space-3) var(--space-5) var(--space-3);
}

.page-back-link {
  display: inline-block;
  margin-bottom: var(--space-3);
  font: var(--type-caption);
}

.page-subtitle {
  color: var(--color-text-muted);
  margin-top: calc(-1 * var(--space-2));
  margin-bottom: var(--space-4);
}

/* (v1.85, issue #71) Il riassunto delle ancore segue subito il sottotitolo
   della stagione: niente del risalire negativo di `.page-subtitle`, che è
   pensato per incollarsi all'`<h1>` e qui raddoppierebbe lo spazio tolto. */
.season-anchor-summary {
  margin-top: calc(-1 * var(--space-3));
}

/* --- (v1.33) Sotto-navigazione di stagione (`docs/requisito-navigazione`)
   ------------------------------------------------------------------------

   Le quattro pagine che *sono* una stagione — vista d'insieme, calendario,
   gruppo, valutazioni — vivevano in fondo a una riga di sottotitolo che
   dava lo stesso peso a una destinazione e a «Esporta CSV G+S». Qui hanno
   la forma delle destinazioni, e le azioni scendono in `.page-actions-quiet`
   qui sotto.

   È `.timeline-toggle` generalizzata da due voci a quattro, non un secondo
   sistema: stessa scatola incassata, stesso modo di dire quale è attiva
   (`aria-current="page"`, che è anche ciò che uno screen reader annuncia),
   stessa assenza di JavaScript. Il pattern era già in `season_overview.html`
   per commutare timeline/tabella, e riusarlo significa che l'allenatore ne
   impara uno solo. */
.season-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  padding: 2px;
  margin: 0 0 var(--space-4);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  /* La barra si adatta al suo contenuto invece di attraversare una pagina
     larga: su `app-main-wide` (la vista d'insieme) quattro voci stirate su
     1400px sarebbero quattro bersagli lontanissimi fra loro. */
  width: fit-content;
  max-width: 100%;
}
.season-nav a {
  padding: 6px 14px;
  border-radius: 6px;
  font: var(--type-button-sm);
  color: var(--color-text-muted);
  text-decoration: none;
  white-space: nowrap;
}
.season-nav a:hover {
  color: var(--color-text);
}
.season-nav a[aria-current="page"] {
  background: var(--color-surface);
  color: var(--color-text);
  box-shadow: var(--shadow-sm);
}
/* A bordo pista la barra è il modo per saltare fra le quattro pagine con un
   dito: le voci restano su una riga sola finché ci stanno e vanno a capo
   quando no, senza mai far scorrere la pagina di lato. */
@media (max-width: 480px) {
  .season-nav {
    width: 100%;
  }
  .season-nav a {
    flex: 1 1 auto;
    text-align: center;
    padding: 8px 10px;
  }
}
/* Col dito le voci sono alte quanto un bottone (44px): a 30px, la
   navigazione più usata della stagione era sotto la misura del resto
   dell'app (audit mobile del 15.09.2026). Con un mouse resta sottile. */
@media (pointer: coarse) {
  .season-nav a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
  }
}

/* La riga delle azioni: «Parti da un modello», «Modifica stagione»,
   «Esporta CSV G+S». Testo piccolo e non bottoni — stanno sotto le
   destinazioni e devono pesare meno di loro, altrimenti la separazione
   appena fatta si annulla. `.page-actions` (bottoni veri) resta per le
   pagine in cui l'azione *è* il lavoro, come «+ Nuovo giocatore» nel
   roster. */
.page-actions-quiet {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-3);
  margin: calc(-1 * var(--space-2)) 0 var(--space-4);
  font: var(--type-caption);
  color: var(--color-text-muted);
}
.page-actions-quiet a {
  color: var(--color-text-muted);
  text-decoration: none;
  border-bottom: 1px solid var(--color-border);
}
.page-actions-quiet a:hover {
  color: var(--color-text);
  border-bottom-color: currentColor;
}
/* (v1.92, issue #78) Un'azione della riga che è un `POST` — «Aggiorna dal
   calendario» con l'id FSIH, che scarica dal sito: il form non è una
   scheda e il bottone ha la veste dei link accanto, perché pesa come loro. */
.page-actions-quiet form {
  display: inline;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
}
.page-actions-quiet button {
  font: inherit;
  min-height: 0;
  padding: 0;
  color: var(--color-text-muted);
  background: none;
  border: 0;
  border-bottom: 1px solid var(--color-border);
  border-radius: 0;
}
.page-actions-quiet button:hover {
  color: var(--color-text);
  background: none;
  border-bottom-color: currentColor;
}

/* --- Il bersaglio dei link di testo piccoli ------------------------------
   Link in corpo piccolo che si toccano spesso: «← indietro», la riga delle
   azioni sobrie, i link di testata di Oggi, i nomi nelle tabelle. Il testo
   era alto 17–22px, sotto i 24 del minimo per un tocco (audit mobile del
   15.09.2026). Cresce la zona toccabile, con un `::after` trasparente, non
   il testo né lo spazio che occupa: l'aspetto resta quello di prima. Le
   misure non arrivano al bersaglio vicino — nella riga delle azioni sobrie
   l'intervallo fra due righe è di 8px, e ognuna ne prende 4. */
.page-back-link,
.page-actions-quiet a,
.page-actions-quiet button,
.priorities-head-link,
.upcoming-head-link,
.rationale-link,
.context-season-link,
.heatmap-name a,
.speed-lap-table-scroll tbody th a,
.speed-lap-standings-direction tbody th a,
.squad-area-players tbody th a {
  position: relative;
}

.page-back-link::after,
.page-actions-quiet a::after,
.page-actions-quiet button::after,
.priorities-head-link::after,
.upcoming-head-link::after,
.rationale-link::after,
.context-season-link::after,
.heatmap-name a::after,
.speed-lap-table-scroll tbody th a::after,
.speed-lap-standings-direction tbody th a::after,
.squad-area-players tbody th a::after {
  content: "";
  position: absolute;
  inset: -8px -4px;
}

/* Da solo sulla sua riga, «← indietro» può prendersene di più. */
.page-back-link::after {
  inset: -12px -8px;
}

.page-actions-quiet a::after,
.page-actions-quiet button::after {
  inset: -4px -4px;
}

/* Nelle tabelle la cella ha 8px sopra e sotto: il nome prende la sua cella,
   non la riga accanto. */
.heatmap-name a::after,
.speed-lap-table-scroll tbody th a::after,
.speed-lap-standings-direction tbody th a::after,
.squad-area-players tbody th a::after {
  inset: -8px 0;
}

/* --- Form --- */

form {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  margin-bottom: var(--space-4);
}

form.form-inline {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: flex-end;
}

fieldset {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: var(--space-3);
  margin: 0;
}

/* Un riquadro dentro un form ha la colonna del form: etichette, campi e
   aiuti uno sotto l'altro con lo stesso `gap`. Senza, le etichette si
   attaccavano al campo sopra, e l'aiuto (`.field-hint`) — col margine
   negativo pensato proprio per quel `gap` — saliva sopra il campo che
   spiega (screenshot di «Modifica squadra» e «Modifica partita», 13.09).
   Solo i `fieldset` senza classe: quelli con una classe (filtri, gruppi di
   checkbox, aree di valutazione, amministrazione) hanno già il loro
   layout, e `disabled` è il contenitore della scheda esercizio, non un
   gruppo di campi. Due elementi di specificità: vince sulla regola qui
   sopra e perde contro ogni regola con una classe. */
form fieldset:where(:not([class]):not([disabled])) {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

legend {
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  padding: 0 var(--space-1);
}

label {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
}

label.label-inline {
  flex-direction: row;
  align-items: center;
  gap: var(--space-2);
}

input[type="text"],
input[type="email"],
input[type="tel"],
input[type="password"],
input[type="search"],
input[type="date"],
input[type="time"],
input[type="number"],
select,
textarea {
  /* (v1.59, issue #51) `email` e `password` entrano con l'accesso per
     utente: fino a qui l'unica password stava nella pagina di login, che ha
     regole sue, e nessun form dentro l'app ne aveva una.

     (v1.23) Peso e stile appartengono al controllo, non a chi lo contiene.
     Un campo dentro un `<label>` — il caso normale in ogni form dell'app —
     ereditava via `font: inherit` anche il peso dell'etichetta, e usciva più
     marcato di essa: il *valore* («Pista», «Transizione») competeva con il
     titolo di pagina.

     (v1.26) Il ruolo è `--type-body`, uno solo per tutti i controlli. Le
     quattro dimensioni compatte che vivevano qui sotto (0.85/0.9/0.95rem,
     una per schermata densa) sono sparite: la densità la fanno `padding` e
     `min-height`, che restano diversi: rimpicciolire il testo per far
     stare le cose è la cosa che questa revisione toglie. */
  font: var(--type-body);
  padding: var(--space-2) var(--space-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-text);
  min-height: 44px;
  width: 100%;
}

input:disabled,
select:disabled {
  background: var(--color-bg);
  color: var(--color-text-muted);
}

/* (v1.21) Freccia propria al posto di quella di sistema. Il chevron è un
   data URI sul `select` stesso, non un `<svg>` in un wrapper: così ogni
   `<select>` dell'app lo eredita senza che nessun template cambi. Il colore
   del tratto è `--color-text-muted` scritto per esteso — dentro un data URI
   una `var()` non viene risolta. */
select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M6 9l6 6 6-6' fill='none' stroke='%236b7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.7rem center;
  background-size: 16px;
  padding-right: 2.25rem;
}

/* Un `select` disabilitato non è cliccabile: la freccia non deve suggerire
   il contrario. */
select:disabled {
  cursor: default;
  opacity: 0.7;
}

input[type="text"]:hover,
input[type="email"]:hover,
input[type="tel"]:hover,
input[type="password"]:hover,
input[type="search"]:hover,
input[type="date"]:hover,
input[type="time"]:hover,
input[type="number"]:hover,
select:hover:enabled,
textarea:hover {
  border-color: var(--color-text-muted);
}

/* (v1.36) `search` era l'unico tipo di campo rimasto fuori dalla riga sopra:
   compariva col disegno di serie del browser — alto la metà degli altri,
   senza raggio né imbottitura — e l'anello di focus, che l'app disegna per
   tutti i campi, gli girava intorno senza che si capisse intorno a *cosa*.
   Ora eredita il controllo comune; qui resta solo ciò che è suo. */
input[type="search"] {
  -webkit-appearance: none;
  appearance: none;
}

/* La crocetta di WebKit resta — cancellare la ricerca è utile — ma smette di
   portarsi dietro il proprio margine di sistema. */
input[type="search"]::-webkit-search-cancel-button {
  margin-right: 0;
}

/* Anello di focus unico per tutti i campi: il default del browser cambia da
   browser a browser ed è la ragione principale per cui i controlli
   sembravano "non progettati". */
input[type="text"]:focus-visible,
input[type="email"]:focus-visible,
input[type="tel"]:focus-visible,
input[type="password"]:focus-visible,
input[type="date"]:focus-visible,
input[type="time"]:focus-visible,
input[type="number"]:focus-visible,
input[type="search"]:focus-visible,
select:focus-visible,
textarea:focus-visible {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px var(--color-primary-soft);
}

.radio-row {
  display: flex;
  gap: var(--space-4);
  flex-wrap: wrap;
}

.checkbox-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* I temi di un blocco (aggiungi e modifica): una riga per tema, casella,
   pallino della famiglia, nome e quota. Senza queste regole la `<label>`
   restava quella generica, in colonna, e ogni tema diventava cinque righe
   (audit mobile del 15.09.2026). Tutta la riga è la `<label>`: toccarla
   spunta il tema, anche fuori dalla casella. */
.theme-share-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.theme-share-row {
  flex-direction: row;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
}

.theme-share-dot {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 2px;
  background: var(--fam, var(--color-text-muted));
}

.theme-share-label {
  flex: 1 1 auto;
  min-width: 0;
}

input.theme-share-percent {
  flex: none;
  width: 5rem;
  text-align: right;
}

.theme-share-unit {
  flex: none;
  color: var(--color-text-muted);
}

.form-row {
  display: flex;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.form-row > label {
  flex: 1 1 10rem;
}

/* Un campo con ciò che gli sta sotto — il segno «fissato» col suo «Torna al
   calendario», un aiuto —, tenuti insieme anche dentro una `.form-row`:
   accanto a data e ora affiancate, un segno a tutta larghezza non direbbe
   più di quale dei due campi parla. Dentro il gruppo niente margini
   negativi: lo spazio lo dà il `gap`. */
.field-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-width: 0;
}

.form-row > .field-group {
  flex: 1 1 10rem;
}

.field-group > .imported-field,
.field-group > .field-hint {
  margin: 0;
}

/* ==========================================================================
   Editor dell'esercizio (v1.23) — `exercise_edit_form.html`

   Era rimasta l'unica pagina del catalogo senza revisione desktop: 40rem di
   larghezza e trenta controlli in colonna singola. Due colonne da 1024px,
   divise per mestiere e non per riempire lo spazio — a sinistra il contenuto
   della scheda (quello che si stampa e si legge a bordo pista), a destra la
   classificazione (i campi che il motore legge per scegliere l'esercizio).
   ========================================================================== */

.exercise-editor-grid {
  display: grid;
  gap: var(--space-5);
}

/* (v1.66, issue #59) Il fieldset che spegne tutti i campi per chi legge la
   scheda senza modificarla (`<fieldset disabled>`): c'è anche per chi la
   modifica, così la griglia sta nello stesso contenitore nei due casi.
   Nessuna cornice — la cornice è del form — e `min-width: 0`, perché un
   fieldset non si stringe sotto il suo contenuto e le textarea sfonderebbero
   la colonna. */
.exercise-editor-fields {
  border: none;
  padding: 0;
  margin: 0;
  min-width: 0;
}

/* Chi legge ha la stessa scatola del form, senza il form: stesso fondo,
   stessa cornice, stesso respiro. */
.exercise-readonly {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  margin-bottom: var(--space-4);
}

/* La provenienza di un esercizio comune sotto il titolo: revisione, autore,
   ultima modifica, separati come le altre righe di metadati. */
.exercise-provenance {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  margin: calc(-1 * var(--space-2)) 0 var(--space-4);
  color: var(--color-text-muted);
  font: var(--type-caption);
}

/* (v1.69, issue #61) Il motivo di un rifiuto sulla scheda dell'autore: le
   parole dell'amministratore rientrate e segnate a sinistra, perché sono
   citate — non sono l'app a parlare. */
.exercise-rejection p,
.exercise-rejection blockquote {
  margin: 0 0 var(--space-2);
}

.exercise-rejection p:last-child {
  margin-bottom: 0;
}

.exercise-rejection blockquote {
  padding-left: var(--space-3);
  border-left: 3px solid currentColor;
}

@media (min-width: 1024px) {
  .exercise-editor-grid {
    /* Non `1fr 1fr`: la colonna di sinistra è fatta di textarea di prosa, che
       hanno bisogno di righe lunghe; quella di destra di select e numeri, che
       non ne guadagnano niente. */
    grid-template-columns: 1.35fr 1fr;
    gap: var(--space-5);
    align-items: start;
  }
}

.editor-col {
  display: flex;
  flex-direction: column;
  /* (v1.23) Da `--space-3` a `--space-4`: tolta la cornice del fieldset (vedi
     sotto) e con undici fra select e numeri fitti nella colonna di destra, è
     il respiro che tiene separati i gruppi di campi invece di farli leggere
     come una lista unica. */
  gap: var(--space-4);
  min-width: 0;
}

/* (v1.23) Il titolo di colonna deve pesare **più** delle etichette dei campi
   che raggruppa (0.95rem/600), non meno: era 1.05rem in grigio muto, cioè
   più debole di ogni valore sotto di lui. Ora è la voce più forte della
   colonna dopo l'`<h1>` di pagina. */
.editor-col-title {
  margin: 0;
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--color-border);
  font: var(--type-title);
  color: var(--color-text);
}

/* Le textarea hanno bisogno di un'altezza propria e del ridimensionamento
   verticale, che la regola di base non dà: lì `min-height: 44px` basta per un
   campo a una riga, non per dieci righe di svolgimento.
   (v1.23) Il `font-weight: 400` non sta più qui: era l'unica riga che
   correggeva il grassetto ereditato dal `<label>`, e lo faceva solo per le
   textarea. Ora la correzione è nella regola di base sopra, dove il difetto
   nasceva, e vale per tutti i controlli. */
textarea {
  line-height: 1.5;
  min-height: 0;
  resize: vertical;
}

/* Sotto il titolo della colonna, il primo gruppo di temi secondari occupa
   due colonne di caselle invece di undici righe in fila indiana. */
@media (min-width: 480px) {
  .editor-col .checkbox-list {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-2) var(--space-3);
  }
}

/* (v1.23) «Temi secondari» era l'unico campo con cornice e legenda incassata
   in mezzo a una colonna di etichetta-sopra-campo: quella differenza di
   linguaggio visivo era la fonte principale dell'effetto collage. Restano
   `<fieldset>`/`<legend>` — sono il markup corretto per un gruppo di
   checkbox correlate, lettori di schermo compresi — ma spogliati del bordo e
   vestiti come una qualunque etichetta della colonna. Scoped a
   `.editor-col`: il fieldset con bordo resta com'è dove serve ancora. */
.editor-col fieldset {
  border: none;
  padding: 0;
  margin: 0;
}

.editor-col legend {
  padding: 0;
  margin-bottom: var(--space-1);
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  color: var(--color-text);
}

/* (v1.23) Spinner nativi via dai campi numerici della colonna: su una riga
   mista con una `select` accanto (Difficoltà + Porzione di pista) i pulsanti
   +/- rompevano l'allineamento verticale fra i due controlli, e a un form
   compilato da tastiera o a tocco non aggiungono niente. */
.editor-col input[type="number"] {
  -moz-appearance: textfield;
  appearance: textfield;
}

.editor-col input[type="number"]::-webkit-outer-spin-button,
.editor-col input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

/* La nota sotto un campo non deve staccarsi dal campo che spiega: nella
   colonna il `gap` la allontanerebbe quanto il controllo successivo. */
.editor-col > .field-hint {
  margin-top: calc(-1 * var(--space-2));
}

/* ==========================================================================
   (v1.81) Lo schema di pista sulla scheda di modifica — `docs/proposta-
   scheda-esercizio/`, opzione A, scelta dall'utente sul mockup.

   Lo schema è ciò a cui un allenatore guarda per primo per capire se
   l'esercizio è quello giusto, e prima stava sopra il form, stretto a 34rem
   e allineato a sinistra, dopo i banner. Adesso la scheda è fatta di
   **card affiancate** — contenuto, schema, classificazione, altre
   informazioni — come nel mockup: **tre colonne** dove ci stanno, con lo
   schema in mezzo e le altre informazioni sotto di lui; altrimenti lo
   schema è **una fascia in cima**, grande, con accanto i comandi. Si guarda
   e basta: si disegna nel Drill Designer.

   Prima prova (scartata dall'utente): le colonne dentro la card unica del
   form, e sotto lo schema una colonna vuota per metà pagina. Le altre
   informazioni stanno lì sotto proprio per riempirla, e le card di una
   stessa riga si allungano fino alla stessa base.

   L'ordine del markup resta contenuto → schema → classificazione → altre
   informazioni a ogni larghezza — stampa e lettori di schermo leggono
   quello — e la disposizione vive tutta in `grid-template-areas`. Le aree
   sono scoped a `.exercise-editor-with-drill`: la proposta in
   Amministrazione usa la stessa `.exercise-editor-grid` con le sue due
   colonne, e un `grid-area` con un nome che la sua griglia non definisce
   le aprirebbe tracce implicite.
   ========================================================================== */

/* Il tetto della pagina: quello del catalogo, e per la stessa ragione — lo
   spazio sopra le 76rem di `app-main-roomy` non allunga le righe di testo,
   diventa la colonna dello schema. */
.app-main-exercise {
  max-width: 110rem;
}

@media (min-width: 1400px) {
  .app-main-exercise {
    padding-left: var(--space-5);
    padding-right: var(--space-5);
  }
}

/* Il form resta il form — un invio solo per tutte le card — ma senza la
   sua cornice: la cornice ce l'ha ogni colonna. Anche la scheda in sola
   lettura, che ha la stessa scatola senza il form. */
form.exercise-editor-frame,
.exercise-readonly.exercise-editor-frame {
  background: none;
  border: none;
  border-radius: 0;
  padding: 0;
}

.exercise-editor-grid.exercise-editor-with-drill {
  gap: var(--space-4);
  align-items: stretch;
  grid-template-areas:
    "drill"
    "content"
    "classification"
    "details";
}

.exercise-editor-with-drill > .editor-col {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
}

@media (min-width: 640px) {
  .exercise-editor-with-drill > .editor-col {
    padding: var(--space-4);
  }
}

.exercise-editor-with-drill > .editor-col-content {
  grid-area: content;
}

.exercise-editor-with-drill > .exercise-drill {
  grid-area: drill;
}

.exercise-editor-with-drill > .editor-col-classification {
  grid-area: classification;
}

.exercise-editor-with-drill > .editor-col-details {
  grid-area: details;
}

/* Da 1024px le due colonne di sempre (1.35fr 1fr, dalla regola sopra), con
   lo schema a tutta larghezza sopra di loro e a destra le due card una
   sotto l'altra. L'ultima riga è `1fr`: il contenuto, che la attraversa,
   la allunga, e la card delle altre informazioni arriva alla sua base. */
@media (min-width: 1024px) {
  .exercise-editor-grid.exercise-editor-with-drill {
    gap: var(--space-4);
    align-items: stretch;
    grid-template-rows: auto auto 1fr;
    grid-template-areas:
      "drill drill"
      "content classification"
      "content details";
  }
}

/* Tre colonne solo dove le tre tracce minime ci stanno davvero. Il conto,
   che un test rilegge da qui (`la_terza_colonna_nasce_dove_ci_sta`): barra
   laterale 15.5rem + respiro della pagina 2 × 2.5rem + tracce 20 + 37 + 20
   + due spazi da 1.5rem = 100.5rem. Le 102.5rem della soglia lasciano 2rem
   per la barra di scorrimento, che su Windows sta dentro la larghezza della
   finestra. Sotto 20rem le colonne di testo andrebbero a capo parola per
   parola; la traccia dello schema, tolta la cornice della card (2 ×
   1.5rem), non scende sotto le 34rem che il disegno aveva prima sulla
   scheda. */
@media (min-width: 102.5rem) {
  .exercise-editor-grid.exercise-editor-with-drill {
    grid-template-columns: minmax(20rem, 1fr) minmax(37rem, 44rem) minmax(20rem, 1fr);
    grid-template-rows: auto 1fr;
    gap: var(--space-4);
    grid-template-areas:
      "content drill classification"
      "content details classification";
  }
}

/* (v1.81) Le fasi FTEM: la descrizione in tondo, il codice in grassetto.
   L'etichetta eredita il 600 di ogni `label`, e sette righe di due parole
   in grassetto si leggevano come sette titoli. In cima e non al centro,
   perché la descrizione va spesso su due righe. */
.ftem-options label.label-inline {
  font-weight: 400;
  align-items: flex-start;
}

.ftem-options strong {
  font-weight: 600;
}

/* La proposta in Amministrazione: classificazione e altre informazioni
   una sotto l'altra, nella colonna di destra come prima. */
.editor-col-stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  min-width: 0;
}

/* Il pulsante che salva sta sotto le card, a destra, largo quanto il suo
   testo; sul telefono torna a tutta larghezza, dove il pollice lo trova. */
.exercise-editor-actions {
  display: flex;
  justify-content: flex-end;
}

.exercise-editor-actions button {
  width: 100%;
}

@media (min-width: 640px) {
  .exercise-editor-actions button {
    width: auto;
    min-width: 14rem;
  }
}

/* Dentro il riquadro: il disegno e, di fianco, i comandi. Una riga sola
   finché il disegno ha le sue 36rem e i comandi 14rem; sotto, i comandi
   vanno a capo sotto il disegno, che prende tutta la larghezza — è quello
   che succede nella colonna di mezzo e sul telefono. Senza `@media`: lo
   decide la larghezza del riquadro, non quella della finestra. */
.exercise-drill-body {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--space-3) var(--space-4);
}

/* Il disegno cresce tre volte più dei comandi, fino a 44rem: oltre, una
   pista 2:1 occupa da sola la prima schermata. */
.exercise-drill-picture {
  flex: 3 1 36rem;
  max-width: 44rem;
  min-width: 0;
}

.exercise-drill .exercise-diagram-preview {
  margin: 0;
}

/* Il tetto di 34rem della proposta in Amministrazione (`.exercise-diagram-
   preview .rink-diagram`) qui non vale: il tetto lo dà la colonna. */
.exercise-drill .exercise-drill-picture .rink-diagram {
  max-width: none;
  margin: 0;
}

/* Le immagini dell'import prendono il posto del disegno, alla stessa
   larghezza: una sola la occupa tutta, due o tre si affiancano se ci
   stanno. */
.exercise-drill .import-images {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr));
  margin: 0;
}

.exercise-drill .import-images .imported-image {
  max-width: none;
}

/* Nessuno schema: il posto della pista, tratteggiato, con dentro il gesto
   che lo riempie. `aspect-ratio` è una proporzione preferita: sul telefono,
   dove testo e bottone non stanno in 150px d'altezza, il riquadro cresce. */
.exercise-drill-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  aspect-ratio: 2 / 1;
  padding: var(--space-3);
  border: 1.5px dashed color-mix(in srgb, var(--color-text-muted) 45%, transparent);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  color: var(--color-text-muted);
  text-align: center;
}

.exercise-drill-empty p {
  margin: 0;
  max-width: 30rem;
}

.exercise-drill-empty-title {
  font: var(--type-title);
  color: var(--color-text);
}

.exercise-drill-side {
  flex: 1 1 14rem;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
  min-width: 0;
}

.exercise-drill-side p {
  margin: 0;
}

.exercise-drill-side > .field-hint {
  margin-top: calc(-1 * var(--space-1));
}

/* Da dove viene la scheda: sotto i comandi, separata come una riga di
   metadati. */
.exercise-drill-origin {
  align-self: stretch;
  padding-top: var(--space-2);
  border-top: 1px solid var(--color-border);
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* --- Picker giorni della settimana (v1.6, ricorrenza calendario) --- */

.weekday-picker {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.weekday-pill {
  position: relative;
}

.weekday-pill input {
  position: absolute;
  opacity: 0;
  width: 44px;
  height: 44px;
  cursor: pointer;
  margin: 0;
}

.weekday-pill span {
  display: inline-flex;
  min-width: 44px;
  min-height: 44px;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--color-border);
  border-radius: 999px;
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  background: var(--color-surface);
  pointer-events: none;
}

.weekday-pill input:checked + span {
  background: var(--color-primary);
  color: #fff;
  border-color: var(--color-primary);
}

.weekday-pill input:focus-visible + span {
  outline: 2px solid var(--color-primary-dark);
  outline-offset: 2px;
}

/* --- Anteprima serie ricorrente (v1.6) --- */

.preview-panel {
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
}

.preview-count {
  font: var(--type-title);
  margin-bottom: var(--space-2);
}

.preview-warning {
  color: var(--color-warning-text);
  font: var(--type-caption);
  margin-bottom: var(--space-2);
}

.preview-dates {
  max-height: 230px;
  overflow-y: auto;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: var(--space-2);
  background: var(--color-surface);
}

.preview-month-heading {
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin: var(--space-2) 0 var(--space-1) 0;
}

.preview-month-heading:first-child {
  margin-top: 0;
}

.date-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: 999px;
  padding: 2px 10px;
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  margin: 0 4px 4px 0;
}

/* --- Tag "Serie" e badge di tipo voce di calendario (v1.6) --- */

.series-tag {
  background: var(--color-badge-bg);
  color: var(--color-badge-text);
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  padding: 2px 8px;
  border-radius: 999px;
}

.kind-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  padding: 2px 9px;
  border-radius: 999px;
  text-transform: uppercase;
  white-space: nowrap;
}

.kind-training {
  background: var(--color-ok-bg);
  color: var(--color-ok-text);
}

.kind-match {
  background: var(--color-badge-bg);
  color: var(--color-badge-text);
}

.kind-tournament {
  background: var(--color-warning-bg);
  color: var(--color-warning-text);
}

.kind-camp {
  background: var(--color-camp-bg);
  color: var(--color-camp-text);
}

.kind-break {
  background: var(--color-surface);
  color: var(--color-text-muted);
  border: 1px dashed var(--color-border);
}

/* (v1.87, issue #72) La partita di una voce, in una riga: tipo, avversario,
   casa o trasferta, risultato. L'annullata resta in elenco **barrata**, e
   porta «Annullata» scritto accanto: il barrato da solo non lo legge uno
   screen reader, e in stampa si scambia per una correzione. Niente rosso: il
   rosso dell'app è degli allarmi, e una partita annullata non lo è. La
   marca è `inline-block` apposta, perché il barrato del genitore non la
   attraversi. */
.match-opponent {
  color: var(--color-text);
  font-weight: 600;
}

.match-score {
  font-variant-numeric: tabular-nums;
}

.entity-row.is-cancelled .entity-title,
.entity-row.is-cancelled .match-summary {
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}

.match-cancelled-tag {
  display: inline-block;
  margin-left: var(--space-1);
  background: var(--color-badge-bg);
  color: var(--color-badge-text);
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  padding: 2px 8px;
  border-radius: 999px;
}

/* (v1.91, issue #76) La marca *da verificare*: il calendario federativo non
   porta più la partita. La parola, in un riquadro tratteggiato come il
   contorno del segno sulla timeline — forma e testo, non colore, così
   regge in stampa. Accanto, in inchiostro secondario, da dove viene la
   partita: una importata non si cancella, e la riga lo spiega. */
.match-verify-tag {
  display: inline-block;
  margin-left: var(--space-1);
  border: 1px dashed currentColor;
  color: var(--color-text);
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  padding: 1px 7px;
}

.match-origin {
  color: var(--color-text-muted);
}

/* (v1.90, issue #75) La scheda della partita. Il concetto del giorno usa la
   card e i riquadri della seduta (`.session-concept`, `.session-reflection`):
   stesso concetto, stessa identità. Qui solo la riga dei gesti e gli
   spazi fra le sezioni. */
.match-detail-summary {
  margin: 0 0 var(--space-3);
}

.match-detail-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-5);
}

.match-detail-actions form {
  display: contents;
}

.match-concept,
.match-assessment {
  margin-bottom: var(--space-5);
}

.match-criterion {
  margin: 0;
  max-width: 64ch;
}

/* Non `.field-hint`: quella sale addosso al campo che spiega, e qui sta
   sotto un titolo. */
.match-note {
  margin: 0 0 var(--space-3);
  max-width: 64ch;
  color: var(--color-text-muted);
}

/* (v1.91, issue #76) L'anteprima di «Aggiorna dal calendario»: una tabella
   di confronto (requisito calendario §11), il numero, che cosa, l'elenco.
   Le cifre tabulari servono qui, dove due date si leggono per differenza. */
.calendar-import-table {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: var(--space-4);
  font-variant-numeric: tabular-nums;
}
.calendar-import-table th,
.calendar-import-table td {
  padding: var(--space-2) var(--space-3) var(--space-2) 0;
  border-bottom: 1px solid var(--color-border);
  vertical-align: top;
  text-align: left;
}
.calendar-import-table th {
  font-weight: 600;
  white-space: nowrap;
}
.calendar-import-count {
  width: 3rem;
  text-align: right;
  font-weight: 700;
}
.calendar-import-note {
  display: block;
  font-weight: 400;
  color: var(--color-text-muted);
  white-space: normal;
}
.calendar-import-rows {
  margin: 0;
  padding-left: 1.1rem;
}
.calendar-import-choice {
  margin-bottom: var(--space-3);
}
/* (v1.93, issue #79) Le ancore prima e dopo: le due colonne di date si
   leggono per differenza, allineate e senza andare a capo. La riga che
   cambia si distingue col peso, non con un colore: il colore è dei temi, e
   deve reggere in stampa. */
.calendar-import-anchors thead th {
  font-weight: 400;
  color: var(--color-text-muted);
}
.calendar-import-date {
  white-space: nowrap;
}
.calendar-import-anchors .is-changing .calendar-import-after {
  font-weight: 700;
}
.calendar-import-clashes ul {
  margin: var(--space-2) 0;
  padding-left: 1.25em;
}
.calendar-import-clashes p {
  margin: 0;
}
.calendar-import-gs {
  max-width: 64ch;
}
.calendar-import-without-file {
  margin-top: var(--space-4);
}
.imported-field {
  margin: calc(-1 * var(--space-2)) 0 var(--space-3);
  color: var(--color-text-muted);
}

.list-and-form-aside .match-form {
  margin-bottom: var(--space-5);
}

/* L'errore sotto il campo, come le date del piano (v1.78). */
/* Dentro «Modifica partita» i riquadri «Partita» e «Risultato» sono
   `fieldset`, che di suo non ha il `gap` del `form`: senza, le etichette si
   attaccavano al campo sopra e la nota del risultato — col suo margine
   negativo, pensato per il `gap` — saliva sopra i due campi dei gol. */
fieldset.match-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* «Annullata» in fondo e staccata da un filo: è il gesto che toglie la
   partita dal conto, non un dato della partita. */
.match-cancel {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
}

.match-cancel > .field-hint {
  margin: 0;
}

.match-form .is-invalid input,
.match-form .is-invalid select {
  border-color: var(--color-danger-border);
  box-shadow: 0 0 0 1px var(--color-danger-border);
}

/* --- Vista d'insieme della stagione: barra e legenda dei blocchi --- */

.season-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}

.block-legend {
  list-style: none;
  padding: 0;
  margin: 0 0 var(--space-4) 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.block-legend li {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-3);
}

.block-swatch {
  width: 14px;
  height: 14px;
  border-radius: 4px;
  flex: none;
}

.block-legend .block-legend-text {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.block-legend .block-legend-name {
  font: var(--type-title);
}

.block-legend .block-legend-meta {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* ==========================================================================
   Fascia di totali di stagione (v1.23) — `season_overview.html`

   Registro discreto, come il resto dell'app (v1.12): card bianche su fondo
   chiaro, bordo sottile, il numero in petrolio. La fascia informa, non
   grida — sotto di lei c'è già la timeline, che è l'elemento con più
   colore della pagina, e due elementi che gridano non si sentono.
   ========================================================================== */

.season-stats {
  display: grid;
  /* `auto-fit` e non tre colonne fisse: la fascia sta in cima a una pagina
     senza tetto di larghezza (`app-main-wide`), e su un monitor largo tre
     card da 600px sarebbero tre insegne. Il `minmax` le tiene fra i 15rem
     — sotto cui "su 109h 30m" va a capo — e una frazione dello spazio. */
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.season-stat {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
}

.season-stat-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

/* L'icona del riquadro è un'emoji, non testo: non ha peso, non ha
   maiuscoletto e non partecipa alla scala — dimensionarla con un ruolo
   tipografico direbbe una cosa falsa. Presa dal gradino del corpo perché
   deve pareggiare otticamente l'occhiello che le sta accanto. */
.season-stat-icon {
  font-size: var(--t-3);
  line-height: 1;
}

.season-stat-label {
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* Numero e totale sulla stessa riga di base: il totale è il metro del
   numero, e mandarlo a capo lo trasformerebbe in un fatto a sé. */
.season-stat-figure {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: var(--space-2) 0 var(--space-3) 0;
}

.season-stat-value {
  font: var(--type-figure);
  color: var(--color-primary);
  /* Le tre card mostrano numeri di larghezza diversa che cambiano nel
     tempo: le cifre a larghezza fissa impediscono al numero di ballare
     quando passa da 9 a 10.

     (v1.27) È l'eccezione alla regola generale «tabulari solo in colonna».
     Un numero isolato di solito sta meglio proporzionale — a 37 px le cifre
     a larghezza fissa lasciano attorno all'1 due mezzi spazi che si vedono.
     Ma questo numero si aggiorna da sé, ogni giorno, restando dov'è: la
     regola vera è **tabulari quando il numero va confrontato con un altro,
     e l'altro può essere sé stesso ieri**. */
  font-variant-numeric: tabular-nums;
}

.season-stat-total {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.season-stat-bar {
  height: 6px;
  border-radius: 999px;
  background: var(--color-bg);
  overflow: hidden;
}

.season-stat-bar-fill {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--color-primary);
  /* Una barra a zero deve restare visibile come "zero", non sparire: senza
     larghezza minima, «0 allenamenti svolti» e «nessuna barra disegnata»
     sono indistinguibili, e il secondo si legge come un difetto. */
  min-width: 3px;
}

.season-stat-caption {
  margin: var(--space-2) 0 0 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* --- Menu azioni di serie (v1.6) --- */

.series-menu {
  position: relative;
}

.series-menu > summary {
  list-style: none;
}

.series-menu > summary::-webkit-details-marker {
  display: none;
}

.series-menu-panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  position: absolute;
  right: 0;
  top: calc(100% + 4px);
  z-index: 5;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: var(--space-2);
  min-width: 12rem;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}

/* --- Bottoni --- */

button,
.btn {
  font: inherit;
  font: var(--type-button);
  cursor: pointer;
  border: 1px solid var(--color-primary);
  border-radius: var(--radius-sm);
  background: var(--color-primary);
  color: var(--color-primary-contrast);
  min-height: 44px;
  padding: var(--space-2) var(--space-4);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

button:hover,
.btn:hover {
  background: var(--color-primary-dark);
  border-color: var(--color-primary-dark);
}

.btn-block {
  width: 100%;
}

.btn-secondary {
  background: var(--color-surface);
  color: var(--color-primary-dark);
}

.btn-secondary:hover {
  background: var(--color-bg);
}

.btn-small {
  min-height: 36px;
  padding: var(--space-1) var(--space-3);
  font: var(--type-button-sm);
}

/* (correzione runtime, ROADMAP.md Fase 3.5) Pulsante di conferma delle
   pagine di cancellazione (`delete_confirm.html`): stesse dimensioni di
   `.btn`, ma sul colore "pericolo" già in uso per `.banner-error` invece
   del verde primario — la scelta di cancellare definitivamente non deve
   somigliare visivamente a un salvataggio/conferma neutra. */
.btn-danger {
  background: var(--color-danger-border);
  border-color: var(--color-danger-border);
  color: var(--color-primary-contrast);
}

.btn-danger:hover {
  background: var(--color-danger-text);
  border-color: var(--color-danger-text);
}

/* (v1.23) Cancellazione con un click nella vista mese per mese. Rosso pieno
   su ogni riga della stagione sarebbe un muro d'allarme su una pagina che si
   scorre per leggere; rosso pieno solo al passaggio del mouse dice che il
   gesto è irreversibile nel momento in cui si sta per farlo — che è quando
   serve saperlo, visto che non c'è una conferma dopo. */
.btn-quiet-danger {
  background: var(--color-surface);
  border-color: var(--color-border);
  color: var(--color-danger-text);
}

.btn-quiet-danger:hover,
.btn-quiet-danger:focus-visible {
  background: var(--color-danger-bg);
  border-color: var(--color-danger-border);
  color: var(--color-danger-text);
}

/* Il `<form>` che avvolge il bottone non deve introdurre una riga propria
   dentro `.entity-actions`, che è già una fila di azioni con il suo `gap`. */
.entity-actions form {
  display: contents;
}

/* --- Banner (errori, stati vuoti, onboarding) --- */

.banner {
  border-radius: var(--radius-md);
  padding: var(--space-3);
  margin-bottom: var(--space-4);
  border: 1px solid transparent;
}

.banner-error,
p.error {
  background: var(--color-danger-bg);
  border-color: var(--color-danger-border);
  color: var(--color-danger-text);
}

/* (correzione runtime, ROADMAP.md Fase 3.5) Stesso trattamento visivo di
   `.banner-error`: entrambe usano la palette "pericolo" — `banner-error`
   per un tentativo già fallito, `banner-danger` per l'elenco delle
   conseguenze di un'azione distruttiva non ancora eseguita. Classe
   distinta (non un semplice riuso di `banner-error`) perché il contenuto
   non è un messaggio di errore. */
.banner-danger {
  background: var(--color-danger-bg);
  border-color: var(--color-danger-border);
  color: var(--color-danger-text);
}

.banner-danger ul {
  margin: var(--space-2) 0;
  padding-left: 1.25em;
}

.banner-onboarding {
  background: var(--color-surface);
  border-color: var(--color-border);
  text-align: left;
}

/* (v1.13) Un avviso, non un errore: qualcosa è andato come doveva ma il
   risultato è parziale o va letto con una riserva — risultati troncati,
   capitolo di sole figure. */
.banner-warning {
  background: var(--color-warning-bg);
  border-color: var(--color-warning-border);
  color: var(--color-warning-text);
}

.banner-onboarding h2 {
  margin-top: 0;
}

/* --- Guardrail --- */

.guardrail {
  border-radius: var(--radius-md);
  padding: var(--space-3);
  margin-bottom: var(--space-4);
  border: 1px solid transparent;
}

.guardrail.ok {
  background: var(--color-ok-bg);
  border-color: var(--color-ok-border);
  color: var(--color-ok-text);
}

.guardrail.warning {
  background: var(--color-warning-bg);
  border-color: var(--color-warning-border);
  color: var(--color-warning-text);
}

.guardrail p {
  margin: 0 0 var(--space-1) 0;
  font: var(--type-body);
}

/* (v1.26, PIANO-CATEGORIE.md Fase 1) L'avviso sul sovraccarico non è una
   nota fra le note: dice che un esercizio in scheda non va fatto da questa
   squadra. Il filetto a sinistra e il grassetto lo staccano dall'elenco
   delle note ordinarie, che nello stesso riquadro parlano di dosaggio. */
.guardrail-overload {
  margin: 0 0 var(--space-2) 0;
  padding-left: var(--space-3);
  border-left: 3px solid currentColor;
  list-style: none;
  font-weight: 600;
}

.guardrail-overload li + li {
  margin-top: var(--space-1);
}

/* --- Scheda allenamento --- */

/* (v1.27) La testata. La data non è il nome della pagina: è il contesto in
   cui la pagina si trova, e sta nell'occhiello insieme all'ora e al posto —
   le tre cose che dicono *quale* seduta è questa. */
.sheet-head {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  margin-bottom: var(--space-3);
}

.sheet-context {
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin: 0;
}

/* La fascia dei dati della seduta. Erano cinque paragrafi in cui l'etichetta
   pesava quanto il valore; sono quattro coppie, occhiello sopra e dato
   sotto. Il bordo unico con `gap: 1px` sul fondo disegna i separatori senza
   quattro card: sono un dato solo diviso in quattro, non quattro oggetti. */
.session-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));
  gap: 1px;
  background: var(--color-border);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
  margin: 0 0 var(--space-4) 0;
}

.session-facts .fact {
  background: var(--color-surface);
  padding: var(--space-2) var(--space-3);
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

.session-facts dt {
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.session-facts dd {
  font: var(--type-figure-sm);
  /* Il numero di presenti e i minuti si guardano di colpo, non si
     confrontano con una colonna: proporzionali. */
  font-variant-numeric: normal;
  margin: 0;
  min-width: 0;
}

/* «Campionato Fase 2» e «Pista» sono testo, non cifre: stessa gerarchia,
   ma andati a capo come testo e senza il taglio da tabellone. */
.session-facts dd.fact-text {
  font: var(--type-body-strong);
}

.session-facts dd small {
  font: var(--type-caption);
  color: var(--color-text-muted);
  margin-left: 0.15em;
}

/* --- Il concetto del giorno (v1.44, issue #36) --------------------------

   Sta in testa alla scheda, fra i dati della seduta e le fasi, ed è la
   sola cosa della pagina che si legge **prima** di scendere in pista: due
   righe, nome e regola.

   Il filetto sul bordo sinistro è la famiglia del tema, come sulla card
   dell'elenco (`.concept-row`) — stesso concetto, stesso colore, e nessuna
   tinta nuova per questa superficie. La tinta di fase resta dentro il
   glifo. */
.session-concepts {
  margin-bottom: var(--space-5);
}

.session-concepts-title {
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin: 0 0 var(--space-2);
}

.session-concept {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--fam, var(--color-border));
  border-radius: var(--radius-md);
  padding: var(--space-3);
  margin-bottom: var(--space-2);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
}

/* Il testo prende lo spazio, il resto no: con i guanti si legge la regola,
   non si cerca il bottone.

   (v1.47) Con «In pista» accanto a «Togli» la riga porta cinque cose, e a
   360 px la regola restava in una colonna larga zero. La base di 10rem è
   il punto sotto cui la regola smette di leggersi: lì i due bottoni vanno a
   capo, invece di essere il testo a farsi da parte. */
.session-concept-text {
  flex: 1 1 10rem;
  min-width: 0;
}

.session-concept-thumb {
  flex: none;
  width: 56px;
  height: 36px;
  display: block;
}

.session-concept-thumb .rink-thumb {
  display: block;
  width: 100%;
  height: 100%;
}

.session-concept-name {
  font: var(--type-section);
  color: inherit;
  text-decoration: none;
}

.session-concept-name:hover {
  text-decoration: underline;
}

.session-concept-rule {
  font: var(--type-body-strong);
  margin: 2px 0 0;
}

/* Il `form` globale è una scatola — fondo, bordo, padding, colonna — e
   dentro la riga del concetto darebbe un riquadro dentro il riquadro. Qui
   il form torna a essere quello che è, un gesto solo. Stessa correzione già
   fatta su `.exercise-actions form` e `.concept-exercise-remove`. */
.session-concept-remove {
  flex: none;
  display: flex;
  align-items: center;
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  margin: 0;
}

/* Le domande in coda al ritorno alla calma. Riquadro leggero e non una
   sezione a sé: appartengono alla fase, e staccarle le farebbe sembrare un
   secondo argomento. */
.session-reflection {
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  margin-bottom: var(--space-3);
}

.session-reflection h3 {
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin: 0 0 var(--space-2);
}

.session-reflection ul {
  margin: 0;
  padding-left: 1.15em;
}

.session-phase {
  margin-bottom: var(--space-5);
}

/* I minuti della fase erano fra parentesi dentro il titolo. Sono il dato
   che si cerca guardando la scheda in mano: escono, e vanno a destra dove
   l'occhio li trova senza rileggere il titolo. */
.phase-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  border-bottom: 2px solid var(--color-text);
  padding-bottom: var(--space-1);
  margin-bottom: var(--space-3);
}

.phase-head h2 {
  margin: 0;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.phase-duration {
  font: var(--type-figure-sm);
  font-variant-numeric: normal;
  color: var(--color-text-muted);
  white-space: nowrap;
}

.exercise-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-3);
}

/* Durata e difficoltà a sinistra del nome: sono i due dati che servono in
   piedi, e in una frase in mezzo al testo non si trovavano. La colonna è
   fissa perché le righe di più esercizi si incolonnino fra loro. */
.exercise-head {
  display: grid;
  grid-template-columns: 3rem 1fr;
  gap: var(--space-3);
  align-items: baseline;
  margin-bottom: var(--space-2);
}

.exercise-clock {
  font: var(--type-figure-sm);
  font-variant-numeric: tabular-nums;
  margin: 0;
  white-space: nowrap;
}

.exercise-difficulty {
  display: block;
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.exercise-card h3 {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin: 0;
  min-width: 0;
}

/* L'obiettivo dell'esercizio è la riga che si legge davvero: misura di
   lettura, non larghezza della finestra. */
.exercise-goal {
  max-width: 64ch;
}

.badge {
  display: inline-block;
  background: var(--color-badge-bg);
  color: var(--color-badge-text);
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  padding: var(--space-1) var(--space-2);
  border-radius: 999px;
}

.diagram-note,
.goalkeeper-note {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font: var(--type-caption);
  color: var(--color-text-muted);
  font-style: normal;
  margin: 0 var(--space-2) var(--space-2) 0;
}

/* --- Catalogo esercizi / import mobilesport (v1.9, §8bis) --- */

.badge-ok {
  background: var(--color-ok-bg);
  color: var(--color-ok-text);
}

.badge-warning {
  background: var(--color-warning-bg);
  color: var(--color-warning-text);
}

.exercise-image {
  display: block;
  max-width: 100%;
  height: auto;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  margin: var(--space-2) 0;
}

/* Frase di licenza + link all'articolo originale (§8bis.5): condizione
   d'uso della licenza UFSPO/G+S, deve restare leggibile anche in stampa
   (nessuna regola `@media print` la nasconde qui, a differenza di `form`/
   `.no-print`). */
.attribution {
  font: var(--type-caption);
  color: var(--color-text-muted);
  margin: var(--space-2) 0 0 0;
  /* (v1.27) L'URL dell'articolo originale è una stringa di 110 caratteri
     senza spazi: senza questo spinge la card fuori dal telefono, e il
     rimedio non può essere rimpicciolirlo — è una condizione di licenza e
     deve restare leggibile. La regola generale di andata a capo per tutta
     l'app arriva con le regole di troncamento; qui serve subito perché è
     l'unico posto dove l'app stampa un URL per esteso. */
  overflow-wrap: anywhere;
}

/* Il corso G+S in «Modifica stagione»: si legge e non si sceglie, perché
   per l'inline hockey è uno solo. Etichetta come quella dei campi accanto,
   valore in corpo normale e senza cornice — una cornice lo farebbe
   sembrare un campo da riempire. */
.gs-course-fixed {
  display: grid;
  grid-template-columns: max-content 1fr;
  align-items: baseline;
  gap: var(--space-1) var(--space-3);
  margin: var(--space-3) 0;
}

.gs-course-fixed dt {
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
}

.gs-course-fixed dd {
  margin: 0;
}

/* --- Elenchi squadre/stagioni/calendario --- */

.entity-list {
  list-style: none;
  padding: 0;
  margin: 0 0 var(--space-4) 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.entity-row {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.entity-row .entity-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
  /* (v1.15) Cresce per occupare lo spazio fra miniatura e azioni: senza,
     con la miniatura davanti il testo resta stretto sulla sua larghezza
     naturale e le azioni gli si accostano invece di stare a destra. Base
     `12rem` perché sotto quella larghezza la riga vada a capo invece di
     spremere il titolo in due caratteri per riga. */
  flex: 1 1 12rem;
  min-width: 0;
}

.entity-row .entity-info .entity-title {
  font: var(--type-title);
}

.entity-row .entity-info .entity-subtitle {
  color: var(--color-text-muted);
  font: var(--type-caption);
}

.entity-row .entity-actions {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.state-badge {
  display: inline-block;
  background: var(--color-ok-bg);
  color: var(--color-ok-text);
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  padding: var(--space-1) var(--space-2);
  border-radius: 999px;
}

.club-group {
  margin-bottom: var(--space-4);
}

.empty-state {
  background: var(--color-surface);
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  text-align: center;
  color: var(--color-text-muted);
  margin-bottom: var(--space-4);
}

/* ==========================================================================
   Revisione rapida del catalogo (v1.10, `exercise_tagging_table.html`)

   Due layout per la stessa griglia, non uno solo che si arrangia.

   La pagina ha nove campi per esercizio. Sopra ~62rem di finestra stanno
   in colonne allineate, ed è ciò che la rende veloce: si scorre una
   colonna sola con l'occhio e si vede subito quali righe sono ancora
   indietro. Sotto quella soglia le colonne non ci stanno in nessun modo, e
   comprimerle è la via peggiore — è il difetto trovato provandola su una
   finestra stretta: `<select>` larghi 40px che mostravano "Pa", "C",
   "Non", e i due campi dell'età impilati uno sull'altro. Lì la tabella
   diventa una scheda per esercizio, con ogni campo etichettato: si perde
   l'allineamento fra righe (a quella larghezza non era comunque
   ottenibile) ma ogni controllo torna leggibile e compilabile.

   Il riempimento per colonna funziona identico nei due layout: agisce sui
   `data-column`, che non dipendono dalla presentazione.
   ========================================================================== */

/* Nessun tetto di larghezza, non un tetto più alto: questa pagina è una
   griglia, non un testo da leggere, e ogni rem sottratto qui diventa
   scorrimento orizzontale sulla tabella. `--content-max-width` esiste per
   tenere le righe di prosa a una lunghezza leggibile — un vincolo che qui
   non ha alcun significato. Un valore fisso, per quanto grande, sarebbe
   comunque sbagliato su qualche schermo: la larghezza giusta è quella della
   finestra. */
.app-main-wide {
  max-width: none;
}

/* (v1.17) Su uno schermo molto largo una pagina senza tetto arriva a filo
   dei bordi della finestra, che è scomodo quanto una colonna troppo
   stretta. Il respiro cresce con lo spazio disponibile invece di restare la
   spaziatura fissa pensata per il telefono. */
@media (min-width: 1400px) {
  .app-main-wide {
    padding-left: var(--space-5);
    padding-right: var(--space-5);
  }
}

/* Via di mezzo fra la colonna di lettura (40rem) e l'assenza di tetto: le
   pagine che dispongono contenuto *in griglia* — catalogo, dashboard
   squadre, scheda generata — usano bene lo spazio orizzontale ma su uno
   schermo molto largo una griglia a piena finestra sparpaglia le card
   invece di aiutare. `main_modifier` in base.html sceglie per pagina. */
.app-main-roomy {
  max-width: var(--content-max-width-wide);
}

/* (v1.19) La home sta in mezzo fra le due: resta una pagina stretta — è un
   form, non una griglia (VISION §9) — ma da 1024px affianca al form il
   pannello di contesto, e in 40rem quel pannello si riduceva a ~130px, con
   ogni etichetta spezzata su due righe. Larghezza scelta per contenere le
   due colonne (26rem del form + spazio + ~20rem del pannello) e non un rem
   di più: la home non deve diventare un cruscotto. */
.app-main-hero {
  max-width: 52rem;
}

/* (v1.27) Su un monitor da 2500 px quelle 52rem sono un terzo della
   larghezza, e il pannello di contesto restava una colonna stretta e alta
   accanto a un form alto — con un rettangolo vuoto sotto di esso.

   La colonna del form **non** cresce: un campo largo mezzo schermo non è
   più facile da compilare, è solo più largo. Cresce lo spazio, e se lo
   prende il pannello, che ha tre voci da mettere in fila invece che in
   colonna. La home resta un form; smette di essere un form in mezzo al
   nulla. */
@media (min-width: 1440px) {
  .app-main-hero {
    max-width: 64rem;
  }
}

/* (v1.56, issue #46) «Il mio profilo»: da 900px il nome del monitore e
   l'accesso stanno affiancati, nella larghezza di `app-main-hero`.

   La colonna del nome ha una misura sua e **non cresce** (regola v1.27):
   26rem, la stessa aritmetica con cui è stata scelta `app-main-hero` — un
   form, lo spazio, e un pannello da ~20rem. Più stretta della colonna di
   lettura in cui stava prima, e di proposito: larga *come* prima (37rem),
   a 1024px ad «Accesso» resterebbero 7rem e l'avviso senza password
   andrebbe a capo parola per parola. Lo spazio in più, fino alle 64rem da
   1440px, se lo prende «Accesso». Un test rilegge i valori.

   Sotto 900px nessuna regola: le due parti sono blocchi uno sotto l'altro,
   e la pagina è quella di sempre. Solo a schermo, come le tessere di
   Squadre e club: un A4 orizzontale è largo più di 900px, e sul foglio la
   pagina resta una colonna. */
@media screen and (min-width: 900px) {
  .profile-layout {
    display: grid;
    grid-template-columns: 26rem minmax(0, 1fr);
    gap: var(--space-4);
    align-items: start;
  }

}

/* E dove la griglia non c'è la pagina torna alla colonna di lettura.
   `app-main-hero` vale a ogni larghezza: da sola, fra 640 e 899px
   allargherebbe la colonna unica da 40 a 52rem, e con lei il campo del
   nome. La condizione è il complemento di quella qui sopra — non a
   schermo, o sotto 900px — così le due larghezze non si sovrappongono. */
@media not screen, (max-width: 899px) {
  .app-main.profile-page {
    max-width: var(--content-max-width);
  }
}

/* (v1.60, issue #52) Incarichi e qualifica nel profilo, in sola lettura:
   un elenco e una riga, sotto il form del nome, senza la cornice dei form —
   non c'è niente da compilare, e una scatola uguale a quella del nome
   farebbe cercare il pulsante «Salva». */
.profile-duties h2 {
  margin-top: 0;
}

.profile-duties-list {
  margin: 0 0 var(--space-2);
  padding-left: 1.25em;
}

/* (v1.59, issue #51) I due form d'accesso, email e password, uno sotto
   l'altro nella colonna «Accesso». Il titolo sta dentro il form, e lo
   spazio fra titolo e campi lo dà il `gap` del form: il margine del titolo
   si sommerebbe. */
.profile-credentials h3,
.profile-credentials .banner {
  margin: 0;
}

.page-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.page-actions .field-hint {
  margin: 0;
  flex: 1 1 16rem;
}

/* Barra "applica a tutte le righe": il motivo per cui la tabella è più
   veloce del form uno-per-pagina. Sta fuori dal <form> della tabella —
   i suoi controlli non vanno inviati, servono solo a riempire le colonne. */
.fill-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  margin-bottom: var(--space-3);
}

.fill-bar-title {
  font: var(--type-title);
  flex-basis: 100%;
}

.fill-control {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: var(--space-2);
}

/* `display`/`white-space` espliciti: la regola globale rende ogni <label>
   una colonna flex, che qui manderebbe l'etichetta sopra il controllo. */
.fill-control label {
  display: inline-block;
  flex-direction: row;
  white-space: nowrap;
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  color: var(--color-text-muted);
}

.fill-control select,
.fill-control input[type="number"] {
  width: auto;
  min-height: 38px;
  padding: var(--space-1) var(--space-2);
}

.fill-control input[type="number"] {
  width: 4rem;
}

.fill-dash {
  color: var(--color-text-muted);
}

.tagging-actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.tagging-actions .field-hint {
  margin: 0;
}

.tagging-title {
  font: var(--type-title);
}

.tagging-meta {
  display: block;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.tagging-warning {
  display: inline-block;
  font: var(--type-caption);
  white-space: nowrap;
  color: var(--color-warning-text);
  background: var(--color-warning-bg);
  border-radius: var(--radius-sm);
  padding: 0 var(--space-1);
}

/* (v1.71) La fascia che le fasi FTEM propongono accanto al ⚠: testo,
   non un valore nei campi (`TaggingRowView::proposed_age`). */
.age-proposal {
  display: inline-block;
  font: var(--type-caption);
  white-space: nowrap;
  color: var(--color-text-muted);
}

/* (v1.71) Sulla scheda i campi dell'età arrivano con la proposta delle
   fasi FTEM: la nota lo dice col colore dell'avviso, perché salvando la
   proposta diventa un dato. */
.field-hint-proposal {
  color: var(--color-warning-text);
}

.tagging-error {
  display: block;
  margin-top: var(--space-1);
  font: var(--type-caption);
  color: var(--color-danger-text);
  white-space: normal;
}

.cell-na {
  color: var(--color-text-muted);
}

.scroll-hint {
  font: var(--type-caption);
  color: var(--color-text-muted);
  margin: 0 0 var(--space-2) 0;
}

/* --------------------------------------------------------------------------
   Layout A — schede impilate (default, finestra stretta)

   Mobile-first come il resto del file: la tabella vera è l'aggiunta del
   media query sotto, non il contrario. Gli elementi `table` sono resi
   `block`, la testata è nascosta e ogni cella porta la propria etichetta
   in `data-label` — così nessun controllo resta senza nome quando le
   colonne spariscono.
   -------------------------------------------------------------------------- */

.tagging-table,
.tagging-table thead,
.tagging-table tbody,
.tagging-table tr,
.tagging-table th,
.tagging-table td {
  display: block;
}

.tagging-table thead {
  display: none;
}

.tagging-table tr {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  margin-bottom: var(--space-3);
}

.tagging-table td {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-1) 0;
  min-height: 44px;
}

.tagging-table td::before {
  content: attr(data-label);
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
  flex: 0 0 auto;
}

/* La cella del titolo non è un campo: niente etichetta, niente colonna
   destra — è l'intestazione della scheda. */
.tagging-table td.col-title {
  display: block;
  border-bottom: 1px solid var(--color-border);
  padding-bottom: var(--space-2);
  margin-bottom: var(--space-2);
}

.tagging-table td.col-title::before {
  content: none;
}

.tagging-table select,
.tagging-table input[type="number"] {
  width: auto;
  min-height: 40px;
  padding: var(--space-1) var(--space-2);
}

.tagging-table select {
  max-width: 60%;
  background-position: right 0.35rem center;
  background-size: 13px;
  padding-right: 1.5rem;
}

.tagging-table input[type="number"] {
  width: 4rem;
}

.tagging-table input[type="checkbox"] {
  width: 22px;
  height: 22px;
}

/* Età: i due estremi e l'avviso restano su una riga sola, mai impilati. */
.tagging-table .age-fields {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--space-1);
}

.tagging-table .row-invalid {
  background: var(--color-danger-bg);
  border-color: var(--color-danger-border);
}

/* --------------------------------------------------------------------------
   Layout B — tabella vera (finestra larga)

   62rem è la larghezza sotto la quale le nove colonne non entrano senza
   comprimere i controlli: la soglia è quella, non un breakpoint di
   dispositivo.
   -------------------------------------------------------------------------- */

@media (min-width: 62rem) {
  .scroll-hint {
    display: none;
  }

  /* Il riquadro del form regala 1.5rem per lato alla cornice e li toglie
     alla griglia: qui è la griglia che deve avere lo spazio. */
  .tagging-form {
    padding: var(--space-3) var(--space-2);
  }

  /* Nessun `overflow` qui, deliberatamente. `overflow-x: auto` sembrava
     innocuo — "scorri in orizzontale se serve" — ma per le regole di CSS
     un contenitore con overflow su un asse lo diventa **anche sull'altro**:
     `.table-scroll` diventava così il contenitore di riferimento per le
     posizioni sticky al suo interno. La testata, con `top: 76px`, si
     ancorava a 76px dal bordo di un contenitore che in verticale non scorre
     mai — cioè restava piantata sopra le prime due righe, coprendole.
     Senza overflow il riferimento torna a essere la finestra: la testata si
     ferma sotto l'header dell'app scorrendo la pagina, e la prima colonna
     resta ancorata a sinistra se la pagina scorre in orizzontale. */

  .tagging-table {
    display: table;
    /* Solo `max-content`, senza `min-width: 100%`: la tabella prende la
       larghezza che le serve e lascia il resto vuoto, invece di stirarsi
       fino a riempire la finestra distanziando le colonne con spazio morto.
       Su uno schermo largo la differenza è tutta la leggibilità: colonne
       vicine si confrontano con un colpo d'occhio, colonne separate da
       trecento pixel di bianco no. */
    width: max-content;
    border-collapse: collapse;
    font: var(--type-body);
  }

  .tagging-table thead {
    display: table-header-group;
  }

  .tagging-table tbody {
    display: table-row-group;
  }

  .tagging-table tr {
    display: table-row;
    background: none;
    border: 0;
    border-radius: 0;
    padding: 0;
    margin: 0;
  }

  /* Padding stretto su entrambi gli assi: nove celle × due lati in
     orizzontale, e in verticale è ciò che decide l'altezza di riga — con
     settanta righe ogni 4px moltiplica. */
  .tagging-table th,
  .tagging-table td {
    display: table-cell;
    border-bottom: 1px solid var(--color-border);
    padding: var(--space-1);
    text-align: left;
    vertical-align: middle;
    white-space: nowrap;
    min-height: 0;
  }

  /* In tabella il ⚠ basta da solo (vedi la nota nel template): il testo
     esteso resta nella modalità a schede, dove lo spazio non manca. */
  .tagging-table .warning-text {
    display: none;
  }

  .tagging-table td::before {
    content: none;
  }

  .tagging-table thead th {
    position: sticky;
    top: 76px; /* sotto la barra fissa in cima */
    background: var(--color-bg);
    border-bottom: 2px solid var(--color-border);
    font: var(--type-thead);
    letter-spacing: var(--type-thead-ls);
    text-transform: uppercase;
    color: var(--color-text-muted);
    z-index: 5;
  }
}

/* (issue #97) Sotto 1024px in cima non sta più ferma nessuna barra:
   l'intestazione scorre con la pagina, e la testata va al bordo. */
@media (min-width: 62rem) and (max-width: 1023px) {
  .tagging-table thead th {
    top: 0;
  }
}

@media (min-width: 62rem) {
  /* Prima colonna ancorata: scorrendo verso destra per arrivare alle
     caselle di conferma si deve continuare a vedere *di quale esercizio*
     si sta decidendo. */
  .tagging-table .col-title {
    position: sticky;
    left: 0;
    z-index: 4;
    background: var(--color-surface);
    border-right: 1px solid var(--color-border);
    white-space: normal;
    min-width: 12rem;
    max-width: 22rem;
    margin: 0;
    padding: var(--space-1) var(--space-2);
    line-height: 1.25;
  }

  .tagging-table .tagging-meta {
    font: var(--type-caption);
  }

  .tagging-table thead .col-title {
    background: var(--color-bg);
    z-index: 6;
  }

  .tagging-table td.col-title {
    display: table-cell;
  }

  .tagging-table .row-invalid {
    border-color: transparent;
  }

  .tagging-table .row-invalid .col-title {
    background: var(--color-danger-bg);
  }

  /* Larghezze minime dei `<select>`: senza queste il browser li stringe
     sulla colonna e resta visibile solo la prima sillaba dell'opzione
     scelta — il difetto che ha motivato questa revisione. */
  /* Controlli più bassi del resto dell'app (32px contro 44px): l'altezza
     di riga è il costo dominante di una griglia da settanta righe, e questa
     è la sola pagina che non si tocca con i guanti a bordo pista — vedi la
     deroga dichiarata in testa alla sezione. */
  .tagging-table select,
  .tagging-table input[type="number"] {
    max-width: none;
    min-height: 32px;
    padding: 0 var(--space-1);
  }

  .tagging-table select[data-column="session_part"] {
    min-width: 9rem;
  }

  .tagging-table select[data-column="theme"] {
    min-width: 11rem;
  }

  /* Dimensionati sull'opzione *comune*, non sulla più lunga in assoluto:
     "Non richiesto" è il caso di gran lunga più frequente e ci sta;
     "Incompatibile con un portiere" viene troncato dal browser, ed è
     accettabile perché è raro e comunque leggibile aprendo il menu. */
  .tagging-table select[data-column="goalkeeper_requirement"] {
    min-width: 9rem;
  }

  .tagging-table input[type="number"] {
    width: 3.25rem;
  }

  .tagging-table input[type="checkbox"] {
    width: 18px;
    height: 18px;
  }

  .tagging-table .age-fields {
    flex-wrap: nowrap;
    justify-content: flex-start;
  }

  .tagging-table .col-narrow {
    text-align: center;
  }

  .tagging-table .col-narrow input[type="checkbox"] {
    margin: 0 auto;
    display: block;
  }

  .tagging-table thead .col-toggle {
    display: inline-block;
    margin-left: var(--space-2);
    vertical-align: middle;
  }
}

/* --- Breakpoint tablet/desktop --- */

@media (min-width: 640px) {
  .app-main {
    padding-left: var(--space-4);
    padding-right: var(--space-4);
  }

  form.form-inline label {
    flex: 1 1 auto;
  }
}

/* ==========================================================================
   Stat card (v1.12) — mockup/README-desktop-revamp.md, Revisione 2.

   Una riga di numeri in cima a una schermata, non un cruscotto: esistono
   solo dove il numero è già calcolato e **azionabile** (dice cosa fare
   dopo). Deliberatamente assenti dalla home, che resta il form di
   generazione a un click (VISION §9) e non una dashboard da attraversare.
   ========================================================================== */

.stat-card-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

@media (min-width: 640px) {
  .stat-card-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

.stat-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
}

.stat-card-label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin-bottom: var(--space-1);
}

/* (v1.26) Erano due regole. `.stat-card-value` stava a 1.5rem, e a quel
   corpo un nome di blocco come «Campionato Fase 2» andava a capo male:
   `.stat-card-value-text` esisteva solo per rimediarci con un corpo più
   contenuto. Con il valore sceso a `--type-title` il difetto non c'è più, e
   con lui la ragione della seconda regola. Le due classi restano nei
   template — sono la stessa cosa e ora si vede. */
.stat-card-value,
.stat-card-value-text {
  font: var(--type-title);
}

.stat-card-sub {
  margin-top: 2px;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* Il numero stesso è il problema: pochi candidati per una fase significa
   che la generazione non ha scelta lì. Colore di avviso, non di errore. */
.stat-card.is-warning .stat-card-value,
.stat-card.is-warning .stat-card-sub {
  color: var(--color-warning-text);
}

/* (15.09.2026) La card dei nuovi, in testa al catalogo: con almeno un
   esercizio nuovo è un link intero, come la riga del catalogo — a bordo pista
   un bersaglio da una parola si sbaglia. Col bordo primario quando la lista
   sta già mostrando solo i nuovi. */
a.stat-card-link {
  display: block;
  color: inherit;
  text-decoration: none;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}

a.stat-card-link:hover {
  border-color: var(--color-text-muted);
  box-shadow: var(--shadow-md);
}

a.stat-card-link:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

a.stat-card-link.is-current {
  border-color: var(--color-primary);
  box-shadow: inset 0 0 0 1px var(--color-primary);
}

.stat-card-cta {
  color: var(--color-primary-dark);
  font-weight: 600;
  white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
  a.stat-card-link {
    transition: none;
  }
}

/* --- Miniatura di riga del catalogo (v1.15) ---
   Rapporto 2:1 come la pista regolamentare (art. 1.2.1): una foto di
   mobilesport e uno schema disegnato occupano la stessa impronta, e la lista
   non "salta" scorrendo fra esercizi di provenienza diversa. */
.entity-thumb {
  flex: none;
  width: 5rem;
  aspect-ratio: 2 / 1;
  object-fit: cover;
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-border);
  background: var(--color-bg);
}

.entity-thumb-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0.35rem;
}

.entity-thumb-empty svg {
  width: 100%;
  height: 100%;
  fill: none;
  stroke: var(--color-border);
  stroke-width: 1.5;
}

/* La miniatura non si stampa: sulla scheda serve il diagramma a piena
   larghezza, e in un elenco stampato è decorazione. */
@media print {
  .entity-thumb {
    display: none;
  }
}

/* ==========================================================================
   Glifo della fase di gioco (v1.39, issue #31)
   ==========================================================================
   Il markup lo genera `web::game_phase_glyph`, che ne verifica le regole
   con dei test; qui c'è solo la resa. Tre luci in colonna, una sola accesa:
   la accesa è **piena**, le altre due sono contorni — è quel contrasto, e
   non la tinta, che fa sopravvivere il glifo alla fotocopia in bianco e
   nero.

   Chi non ha una luce accesa non ha il glifo — i temi senza fase e il
   portiere, per due ragioni diverse (`web::game_phase_glyph`). Non c'è, e
   non deve esserci, una classe per un quarto stato.

   Attenzione al nome: `.fase-*` è la **fase di gioco** (possesso,
   transizione, non possesso, portiere). `.phase-*`, più in su, è tutt'altro
   — la **parte della seduta** G+S. Sono due tassonomie diverse che
   l'italiano e l'inglese chiamano quasi allo stesso modo: i nomi qui
   ricalcano il mockup approvato (`--fase-verde` e compagni), e la
   distinzione sta nella lingua. */

.fase-glifo {
  /* `flex: none` perché la riga del catalogo è un flex container: senza,
     il glifo si stira e la tinta diventa una superficie, che è la cosa che
     la regola vieta. */
  flex: none;
  width: 17px;
  height: 37px;
  /* Il disegno è tutto in `currentColor` tranne la luce accesa: un solo
     colore da cambiare per adattarlo al contesto. */
  color: var(--color-text-muted);
  --fase-tinta: currentColor;
}

.fase-possesso {
  --fase-tinta: var(--fase-verde);
}

.fase-transizione {
  --fase-tinta: var(--fase-arancio);
}

.fase-non-possesso {
  --fase-tinta: var(--fase-rosso);
}

.fase-cornice {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.3;
  opacity: 0.45;
}

.fase-luce {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.2;
  opacity: 0.35;
}

.fase-luce.is-on {
  fill: var(--fase-tinta);
  stroke: none;
  opacity: 1;
}

/* Su carta il glifo **resta** — al contrario della miniatura, che in un
   elenco stampato è decorazione: la fase è l'informazione che l'aiutante a
   bordo pista legge dal foglio. Tutto a inchiostro pieno, perché una
   fotocopia mangia le opacità e il grigio al 35% sparisce. */
@media print {
  .fase-glifo {
    color: #000;
    --fase-tinta: #000;
  }

  .fase-cornice,
  .fase-luce {
    opacity: 1;
  }
}

/* ==========================================================================
   Catalogo — filtri in colonna laterale da 900px (v1.12)
   ========================================================================== */

.catalog-layout {
  display: block;
}

@media (min-width: 900px) {
  .catalog-layout {
    display: grid;
    grid-template-columns: var(--sidebar-width) minmax(0, 1fr);
    gap: var(--space-4);
    align-items: start;
  }

  /* Scorrere una lista lunga senza perdere i filtri. `top` è l'altezza
     dell'header orizzontale: quando arriverà la shell con sidebar
     (branch dedicato) andrà riletto insieme a quello. */
  .catalog-filters {
    position: sticky;
    top: var(--space-3);
  }
}

/* (v1.21) Contenitore di sola posizione: la cornice (sfondo, bordo, raggio)
   sta su `.filters-panel`, cioè sul `<form>` stesso. Prima erano due
   scatole annidate — `.catalog-filters` con bordo *dentro* il quale il
   `form` generico ne disegnava un secondo — ed è il bordo doppio che si
   vedeva sotto la testata "Tema". */
.catalog-filters {
  margin-bottom: var(--space-3);
}

/* ==========================================================================
   Pannello filtri (v1.21)

   Una sola scatola, etichette "eyebrow" invece di label in grassetto pieno
   (che pesavano quanto il valore sotto e appiattivano il ritmo), select con
   chevron proprio (regola generica sopra), i "Solo…" come righe-toggle
   intere invece di checkbox nude, e in testata quanti filtri sono attivi
   con un modo diretto per azzerarli.

   È un `<form>`, quindi eredita `display:flex; flex-direction:column` dalla
   regola generica ed è già escluso dalla stampa (`@media print`); qui si
   azzerano solo cornice e spaziature.
   ========================================================================== */

.filters-panel {
  gap: var(--space-3);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  margin-bottom: 0;
}

.filters-panel-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.filters-panel-title {
  margin: 0;
  flex: 1;
  font: var(--type-title);
}

.filters-count-badge {
  background: var(--color-chip-bg);
  color: var(--color-chip-text);
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  padding: 2px 9px;
  border-radius: var(--radius-pill);
  white-space: nowrap;
}

/* Compare solo quando c'è qualcosa da azzerare (il template lo omette
   altrimenti): un "Azzera" sempre visibile e sempre inerte è rumore. */
.filters-clear {
  font: var(--type-button-sm);
  color: var(--color-text-muted);
  white-space: nowrap;
}

.filters-panel-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.filter-field {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
}

/* Eyebrow: piccola, maiuscoletta e muta — stesso registro di
   `.switcher-label`, la cosa meglio riuscita che l'app aveva già. Non
   compete col valore selezionato, che è l'informazione vera. */
.filter-eyebrow {
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.toggle-row {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

/* Checkbox travestita da riga selezionabile: input trasparente sovrapposto
   più uno `span` fratello che porta l'aspetto — la stessa tecnica già usata
   per `.weekday-pill`, che non dipende da `:has()`. A riga piena e non a
   pillola: in una colonna da 17.5rem una pillola centrata lascia spazio
   morto ai lati. L'area cliccabile resta l'intera riga, ≥44px. */
.filter-toggle {
  position: relative;
  display: flex;
  /* Anche questo è un `<label>`: annulla `flex-direction: column` e il gap
     della regola generica, che qui aprirebbero uno stacco sotto la riga. */
  flex-direction: row;
  gap: 0;
}

.filter-toggle input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.filter-toggle span {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  min-height: 44px;
  padding: 0 var(--space-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  white-space: nowrap;
  pointer-events: none;
}

.filter-toggle span::before {
  content: "";
  width: 18px;
  height: 18px;
  flex: none;
  border: 1.5px solid var(--color-border);
  border-radius: 4px;
  background: var(--color-surface);
}

.filter-toggle input:hover + span {
  border-color: var(--color-text-muted);
}

.filter-toggle input:checked + span {
  background: var(--color-chip-bg);
  border-color: var(--color-chip-border);
  color: var(--color-chip-text);
}

.filter-toggle input:checked + span::before {
  border-color: var(--color-chip-border);
  background: var(--color-chip-border);
  box-shadow: inset 0 0 0 3px var(--color-chip-bg);
}

.filter-toggle input:focus-visible + span {
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px var(--color-primary-soft);
}

.filters-panel .btn,
.filters-panel button[type="submit"] {
  width: 100%;
}

/* Variante a piena larghezza (tabella di taggatura): stessa cornice e stesse
   etichette, ma i campi in riga — la pagina non ha una colonna laterale e
   una pila di controlli larghi quanto la tabella sarebbe assurda. */
@media (min-width: 700px) {
  .filters-panel-inline .filters-panel-body {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: flex-end;
  }

  .filters-panel-inline .filter-field {
    flex: 1 1 11rem;
  }

  .filters-panel-inline .toggle-row {
    flex-direction: row;
  }

  /* I due toggle stanno su una riga sola: `flex: 1 1 11rem` come gli altri
     campi li stringerebbe fino a spezzare "Solo da confermare" su due
     righe. Larghezza dal contenuto, e vanno in fondo alla riga. */
  .filters-panel-inline .filter-field-toggles {
    flex: 0 0 auto;
  }

  .filters-panel-inline .filters-panel-actions {
    flex: 0 0 auto;
  }

  .filters-panel-inline .btn,
  .filters-panel-inline button[type="submit"] {
    width: auto;
  }
}

/* ==========================================================================
   Catalogo esercizi — trovare l'esercizio giusto in fretta (v1.74)
   `docs/proposta-catalogo-esercizi/proposta-layout.md`

   Tre cose, e l'ordine del DOM è quello del lavoro: i filtri con cui si
   sceglie (ricerca, fase di gioco, tema, categoria) sempre in vista, il
   resto in «Altri filtri»; una barra che dice quanti sono, in che ordine e
   come mostrarli; una riga che dice prima **cos'è** l'esercizio e poi i
   dati con cui lo si scarta.

   Lista e griglia sono **lo stesso markup** (`.catalog-item`): cambia la
   classe dell'elenco, e la disposizione sta tutta qui, in
   `grid-template-areas`. La griglia vale solo a schermo, come le tessere
   delle squadre (v1.55): sul foglio torna la lista.
   ========================================================================== */

/* Il tetto del catalogo: più largo delle 75rem di `app-main-roomy`, perché
   qui lo spazio in più diventa una seconda colonna di righe, non righe più
   lunghe e vuote; non assente, perché a tutta finestra su uno schermo
   molto largo le righe si sparpaglierebbero. Il respiro ai lati cresce con
   lo schermo, come in `app-main-wide`. */
.app-main-catalog {
  max-width: 110rem;
}

@media (min-width: 1400px) {
  .app-main-catalog {
    padding-left: var(--space-5);
    padding-right: var(--space-5);
  }
}

.catalog-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2) var(--space-3);
  margin-bottom: var(--space-3);
}

.catalog-head h1 {
  margin: 0;
}

.catalog-head-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

/* --- La fase di gioco: cinque radio col semaforo ------------------------
   Stessa tecnica di `.filter-toggle`: l'`input` trasparente copre la voce,
   lo `span` fratello ne porta l'aspetto, e `:checked +` non ha bisogno di
   `:has()`. Due colonne nella colonna laterale — «Tutte» su tutta la riga —
   perché in 17,5rem cinque voci in fila non ci stanno col loro nome. */
.phase-filter {
  border: 0;
  padding: 0;
  margin: 0;
  min-width: 0;
}

.phase-filter legend {
  padding: 0;
  margin-bottom: var(--space-1);
}

.phase-filter-options {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-1);
}

.phase-filter-all {
  grid-column: 1 / -1;
}

.phase-filter-option {
  position: relative;
  /* È un `<label>`: annulla la colonna e il gap della regola generica. */
  flex-direction: row;
  gap: 0;
}

.phase-filter-option input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.phase-filter-option > span {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  min-height: 44px;
  padding: 3px var(--space-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  pointer-events: none;
}

/* Il posto del glifo c'è anche per il portiere, che non ne ha: i nomi
   restano in colonna. 17 × 37 è la misura del glifo (`game_phase_glyph`). */
.phase-filter-glyph {
  display: flex;
  flex: none;
  width: 17px;
  height: 37px;
}

.phase-filter-option input:hover + span {
  border-color: var(--color-text-muted);
}

.phase-filter-option input:checked + span {
  background: var(--color-chip-bg);
  border-color: var(--color-chip-border);
  color: var(--color-chip-text);
}

.phase-filter-option input:focus-visible + span {
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px var(--color-primary-soft);
}

/* --- «Altri filtri»: un <details> nativo ---------------------------------
   Si apre senza JavaScript, e il template lo apre da sé quando dentro c'è un
   filtro attivo. La freccia è un bordo ruotato e non un carattere: gira
   insieme al riquadro. */
.filters-more {
  border-top: 1px solid var(--color-border);
  padding-top: var(--space-1);
}

.filters-more > summary {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  cursor: pointer;
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  list-style: none;
}

.filters-more > summary::-webkit-details-marker {
  display: none;
}

.filters-more > summary::before {
  content: "";
  flex: none;
  width: 7px;
  height: 7px;
  margin: 0 3px;
  border-right: 1.6px solid var(--color-text-muted);
  border-bottom: 1.6px solid var(--color-text-muted);
  transform: rotate(-45deg);
  transition: transform 0.15s ease;
}

.filters-more[open] > summary::before {
  transform: rotate(45deg);
}

.filters-more-count {
  color: var(--color-chip-text);
}

.filters-more > .filters-panel-body {
  padding-block: var(--space-2) var(--space-1);
}

@media (prefers-reduced-motion: reduce) {
  .filters-more > summary::before {
    transition: none;
  }
}

/* Fra 700 e 899px il pannello sta sopra la lista, largo quanto la pagina:
   i campi vanno in riga come nella tabella di taggatura, e le cinque fasi
   su una riga sola. Sopra i 900px torna la colonna laterale. */
@media (min-width: 700px) and (max-width: 899px) {
  .catalog-filters-panel .filters-panel-body {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: flex-end;
  }

  .catalog-filters-panel .filter-field {
    flex: 1 1 14rem;
  }

  .catalog-filters-panel .phase-filter {
    flex-basis: 100%;
  }

  .catalog-filters-panel .phase-filter-options {
    grid-template-columns: repeat(5, minmax(0, 1fr));
  }

  .catalog-filters-panel .phase-filter-all {
    grid-column: auto;
  }
}

/* La tabella di taggatura ha i filtri in riga da 700px: le fasi prendono
   una riga loro, in fila. Accanto agli altri campi si stringevano fino a
   tagliare «Transizione». */
@media (min-width: 700px) {
  .filters-panel-inline .phase-filter {
    flex-basis: 100%;
  }

  .filters-panel-inline .phase-filter-options {
    grid-template-columns: repeat(5, minmax(0, 1fr));
  }

  .filters-panel-inline .phase-filter-all {
    grid-column: auto;
  }
}

/* --- La barra dei risultati ---------------------------------------------- */
.catalog-results-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-4);
  margin-bottom: var(--space-2);
}

/* Tabulari: il numero cambia restando dov'è, filtro dopo filtro. La
   scorciatoia `font` le azzera, quindi vengono dopo. */
.catalog-count {
  margin: 0;
  font: var(--type-title);
  font-variant-numeric: tabular-nums;
}

.catalog-count-total {
  margin-left: var(--space-1);
  font: var(--type-caption);
  font-variant-numeric: tabular-nums;
  color: var(--color-text-muted);
}

.catalog-sort {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  color: var(--color-text-muted);
}

.catalog-sort a {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  padding: 0 2px;
  color: var(--color-text-muted);
  text-decoration: none;
}

.catalog-sort a[aria-current="page"] {
  color: var(--color-text);
  text-decoration: underline;
  text-decoration-color: var(--color-primary);
  text-decoration-thickness: 2px;
  text-underline-offset: 5px;
}

/* La vista è `.timeline-toggle`, lo stesso interruttore a due link della
   stagione: se ne impara uno solo. Qui va in fondo alla riga, e i due link
   crescono fino a un bersaglio da dito. */
.catalog-view {
  margin-left: auto;
}

.catalog-view a {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
}

/* I filtri attivi: il vocabolario dei filtri (`--color-chip-*`), e una ×
   grande quanto basta a centrarla col dito. */
.catalog-chips {
  list-style: none;
  margin: 0 0 var(--space-2);
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
}

.catalog-chip {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  min-height: 34px;
  padding: 0 1px 0 var(--space-3);
  background: var(--color-chip-bg);
  color: var(--color-chip-text);
  border: 1px solid var(--color-chip-border);
  border-radius: var(--radius-pill);
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
}

.catalog-chip a {
  display: inline-grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  color: inherit;
  text-decoration: none;
  font-size: 1.15rem;
  line-height: 1;
}

.catalog-chip a:hover,
.catalog-chip a:focus-visible {
  background: var(--color-surface);
}

/* Sotto la barra, e non staccata di un margine negativo come nelle pagine
   di stagione: qui sopra ci sono i filtri attivi, non un titolo. */
.catalog-results-notes {
  margin: 0 0 var(--space-3);
}

/* --- La riga -------------------------------------------------------------
   Un link solo, grande quanto la riga (`.concept-row-link`, stesso
   principio), con l'immagine **vera** del drill: la foto dell'import o lo
   schema disegnato intero, non la miniatura. Per questo il disegno non
   scende sotto i 10rem: sotto i 150px le sagome dello schema intero
   spariscono (v1.48).

   Il caso di partenza è il telefono, dove 10rem lascerebbero al testo metà
   riga: lì la riga è una **tessera**, disegno sopra a tutta larghezza e
   testo sotto — la stessa disposizione della griglia. Da 640px il disegno
   va a sinistra e il testo accanto. Le righe stanno in una griglia che le
   affianca da 32rem: su uno schermo largo due colonne di righe invece di
   una sola riga lunga col testo a sinistra e il vuoto in mezzo. */
.catalog-items {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(32rem, 100%), 1fr));
  gap: var(--space-2) var(--space-3);
}

/* La riga riempie la cella: due righe affiancate hanno la stessa altezza. */
.catalog-items > li {
  display: flex;
}

.catalog-item {
  width: 100%;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 17px;
  grid-template-rows: auto 1fr;
  grid-template-areas:
    "thumb thumb"
    "body phase";
  align-items: start;
  gap: var(--space-2) var(--space-3);
  padding: 0 var(--space-3) var(--space-3);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  color: inherit;
  text-decoration: none;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}

.catalog-item:hover {
  border-color: var(--color-text-muted);
  box-shadow: var(--shadow-md);
  text-decoration: none;
}

.catalog-item:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .catalog-item {
    transition: none;
  }
}

/* 2:1 come la pista regolamentare (v1.15): una foto di mobilesport e uno
   schema disegnato occupano la stessa impronta. `contain` e non `cover`:
   un disegno tagliato perde proprio i giocatori sul bordo. Nella tessera
   il disegno arriva ai bordi della card, sopra il testo. */
.catalog-item-thumb {
  grid-area: thumb;
  display: block;
  width: calc(100% + 2 * var(--space-3));
  max-width: none;
  margin-inline: calc(-1 * var(--space-3));
  aspect-ratio: 2 / 1;
  object-fit: contain;
  padding: var(--space-1);
  background: var(--color-surface);
  border: 0;
  border-bottom: 1px solid var(--color-border);
  border-radius: var(--radius-md) var(--radius-md) 0 0;
}

.catalog-item-thumb-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-3);
  background: var(--color-bg);
}

.catalog-item-thumb-empty svg {
  width: 100%;
  height: 100%;
  fill: none;
  stroke: var(--color-border);
  stroke-width: 1.5;
}

.catalog-item-phase {
  grid-area: phase;
  display: flex;
  align-items: center;
  width: 17px;
  min-height: 37px;
}

.catalog-item-body {
  grid-area: body;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
}

/* Nome e stato a sinistra, «Apri» in alto a destra: una griglia e non un
   flex che va a capo, così con un titolo lungo «Apri» non finisce in mezzo
   alla riga. */
.catalog-item-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: start;
  gap: var(--space-2);
}

.catalog-item-name {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1) var(--space-2);
  min-width: 0;
}

.catalog-item-title {
  font: var(--type-title);
  color: var(--color-text);
  overflow-wrap: anywhere;
}

.catalog-item-badges {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
}

/* Dati corti in fila: una parola spezzata a metà («Diffi-coltà») si legge
   peggio di una riga che va a capo fra due dati. */
.catalog-item-facts {
  font: var(--type-caption);
  color: var(--color-text-muted);
  hyphens: manual;
}

/* «Apri» o «Modifica» in fondo alla riga del titolo: dice cosa succede
   toccando la riga, senza una colonna sua che ruberebbe spazio al disegno.
   Nella tessera non serve. */
.catalog-item-open {
  display: none;
  padding-top: 2px;
  font: var(--type-button-sm);
  color: var(--color-primary-dark);
  white-space: nowrap;
}

.catalog-item-open::after {
  content: " ›";
}

@media (min-width: 640px) {
  .catalog-items-list .catalog-item {
    grid-template-columns: clamp(10rem, 36%, 15rem) 17px minmax(0, 1fr);
    grid-template-rows: auto;
    grid-template-areas: "thumb phase body";
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3) var(--space-2) var(--space-2);
  }

  .catalog-items-list .catalog-item-thumb {
    width: 100%;
    max-width: 100%;
    margin: 0;
    padding: 0;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
  }

  .catalog-items-list .catalog-item-open {
    display: inline;
  }
}

/* --- La griglia: la stessa riga, a tessere -------------------------------
   Nessuna `@media` per le colonne, come per i concetti (v1.48): quante ne
   stanno lo decide la tessera. 16rem e non i 30 dei concetti: qui sotto il
   disegno ci sono tre righe corte, non una pista intera e una regola. La
   tessera è la disposizione di partenza della riga, qui sopra: cambia solo
   quante ne stanno in fila. */
@media screen {
  .catalog-items-grid {
    grid-template-columns: repeat(auto-fill, minmax(min(16rem, 100%), 1fr));
    gap: var(--space-3);
  }
}

/* Su carta l'immagine non c'è — in un elenco stampato è decorazione, come
   nella riga di prima (v1.15) — e il glifo della fase resta. Una colonna
   sola, anche dalla griglia. */
@media print {
  .catalog-item-thumb,
  .catalog-item-open,
  .catalog-head-actions,
  .catalog-results-bar nav,
  .catalog-chips {
    display: none;
  }

  .catalog-items {
    display: flex;
    flex-direction: column;
  }

  .catalog-items .catalog-item {
    grid-template-columns: 17px minmax(0, 1fr);
    grid-template-rows: auto;
    grid-template-areas: "phase body";
    padding: var(--space-2);
  }
}

/* ==========================================================================
   Squadre e club — le squadre come tessere da 900px (v1.55, issue #45)

   Fino alla v1.54 da 900px si affiancavano i **club** (v1.12, «con un club
   solo una griglia 2×N lascia sempre una colonna vuota»). Ma nelle 52rem di
   `app-main-hero` le tracce erano due, `auto-fill` teneva anche quella
   vuota, e col caso reale — un club, tre squadre — le squadre si
   impilavano nella metà sinistra: la colonna vuota c'era lo stesso.

   Adesso ogni club prende tutta la riga, e più club stanno uno sotto
   l'altro (non serve un contenitore: è il flusso normale). Si affiancano
   le squadre dentro il club. `team-tiles` e `team-tile` stanno **accanto**
   a `entity-list`/`entity-row`, non al loro posto: la lista è di altre
   otto pagine, e sotto 900px anche qui resta la lista di sempre.

   `screen` perché sul foglio le griglie a schermo non sopravvivono (vedi
   la sezione di stampa): un A4 orizzontale è largo più di 900px, e senza
   `screen` le tessere ci arriverebbero. Così la stampa resta la lista, e
   non serve una voce in più nell'elenco di stampa.
   ========================================================================== */

@media screen and (min-width: 900px) {
  .team-tiles {
    display: grid;
    /* `auto-fill` e non `auto-fit`: le tracce vuote restano, così una
       squadra sola prende la larghezza di una tessera invece di stirarsi
       per tutta la riga. 15rem sta in una finestra stretta, non è un
       gusto: a 52rem ci stanno tre tessere (47rem nelle 49 utili, tolto il
       rientro da 640px), a 64rem ancora tre e non quattro — da 14,5 in giù
       nascerebbe una quarta traccia, vuota accanto alle tre squadre; da
       15,7 in su la terza andrebbe a capo. Un test rilegge i valori. Resta
       un solo intervallo con due tessere più una, 1024–1047px, dove la
       sidebar è appena comparsa: tre tessere lì vorrebbero dire quattro a
       64rem, e sui monitor grandi la colonna vuota tornerebbe sempre. */
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    gap: var(--space-3);
  }

  /* La riga diventa una tessera: nome e fascia sopra, azioni sotto. In una
     riga della stessa griglia le tessere hanno la stessa altezza, e
     `space-between` tiene i bottoni in fondo, allineati fra loro anche
     quando un nome va a capo. `nowrap` perché il `wrap` della riga, in
     colonna e con l'altezza della traccia, potrebbe aprire una seconda
     colonna dentro la tessera. */
  .team-tile {
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: flex-start;
  }

  /* In colonna, la base `12rem` di `.entity-row .entity-info` diventerebbe
     un'altezza: la tessera sarebbe alta 12rem di vuoto. */
  .team-tile .entity-info {
    flex: 0 0 auto;
    align-self: stretch;
  }
}

/* (19.09.2026, docs/proposta-squadre-club/ e mockup «Squadre») La pagina
   Squadre: una testata con «Nuova squadra» a destra, il club come card, e
   tessere con un divisore fra chi sono e chi le allena, e il bottone largo
   quanto la tessera.

   La tessera è un link: il nome porta alla scheda e il suo `::after` copre
   tutta la tessera, come la riga di «Chi siamo». «Attiva» sta sopra il
   velo, così i due gesti restano distinti al tocco. */
.teams-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.teams-head h1 {
  margin: 0;
}

.teams-head .page-subtitle {
  margin: var(--space-1) 0 0;
}

.teams-head-eyebrow {
  display: flex;
  align-items: center;
  gap: 2px;
  margin: 0 0 var(--space-1);
  color: var(--color-text-muted);
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
}

.teams-head-new {
  gap: var(--space-2);
}

.club-card {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
}

.club-card-icon {
  display: grid;
  place-items: center;
  flex: none;
  width: 3.5rem;
  height: 3.5rem;
  border-radius: 50%;
  background: var(--color-bg);
  color: var(--color-text);
}

.club-card-text {
  flex: 1 1 auto;
  min-width: 0;
}

.club-card-name {
  margin: 0;
  font: var(--type-section);
}

.club-card-season {
  margin: 2px 0 0;
  color: var(--color-text-muted);
  font: var(--type-caption);
}

.club-card-edit,
.society-card-action .btn {
  gap: var(--space-2);
}

.teams-section-title {
  margin: var(--space-4) 0 var(--space-2);
  font: var(--type-section);
}

.team-tile {
  position: relative;
}

.team-tile.is-active {
  border-color: var(--color-primary);
  box-shadow: 0 0 0 1px var(--color-primary);
}

.team-tile-link {
  color: inherit;
  text-decoration: none;
}

.team-tile-link::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
}

.team-tile-link:hover {
  text-decoration: underline;
}

.team-tile-link:focus-visible {
  outline: none;
}

.team-tile-link:focus-visible::after {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.team-tile .entity-actions {
  position: relative;
  z-index: 1;
}

/* Chi la allena e a che punto è la stagione: le due righe che distinguono
   tre squadre della stessa categoria, sotto un filetto. La pastiglia è
   quella del contesto. */
.team-tile-meta {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-1);
  color: var(--color-text-muted);
  font: var(--type-caption);
}

.team-tile-coaches,
.team-tile-season {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.team-tile-season {
  color: var(--color-text);
  font: var(--type-body);
}

/* «Attiva» è tenue — sono sei inviti uguali —, «Squadra attiva» è piena e
   non è un bottone: dice dove si è, non cosa fare. */
.team-tile-activate {
  background: var(--color-primary-soft);
  border-color: var(--color-primary-soft);
  color: var(--color-primary-dark);
}

.team-tile-activate:hover {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-primary-contrast);
}

.team-tile-current {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: 36px;
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-sm);
  background: var(--color-primary);
  color: var(--color-primary-contrast);
  font: var(--type-button-sm);
}

/* Da 900px, a schermo, la tessera del mockup: il filetto sopra gli
   allenatori e l'azione larga quanto la tessera, in fondo. */
@media screen and (min-width: 900px) {
  .team-tile {
    gap: var(--space-3);
    padding: var(--space-3) var(--space-3) var(--space-3);
  }

  .team-tile .entity-info {
    gap: 2px;
  }

  .team-tile .entity-info .entity-subtitle {
    font: var(--type-body);
  }

  .team-tile-meta {
    margin-top: var(--space-2);
    padding-top: var(--space-2);
    border-top: 1px solid var(--color-border);
  }

  .team-tile .entity-actions {
    align-self: stretch;
    margin-top: auto;
  }

  .team-tile .entity-actions form,
  .team-tile-activate,
  .team-tile-current {
    width: 100%;
  }
}

/* Sotto 900px la tessera è una riga bassa: il nome sopra, categoria e
   allenatori sotto sulla stessa riga, e «Attiva» a destra. La stagione la
   dice già la card del contesto in cima, e la squadra attiva il badge. */
@media (max-width: 899px) {
  .teams-head {
    align-items: flex-start;
  }

  .team-tile {
    padding: var(--space-2) var(--space-3);
  }

  .team-tile .entity-info {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    column-gap: var(--space-1);
  }

  .team-tile .entity-info .entity-title {
    flex-basis: 100%;
  }

  .team-tile-meta {
    display: inline;
  }

  .team-tile-meta::before {
    content: "· ";
  }

  .team-tile-coaches svg,
  .team-tile-season,
  .team-tile-current {
    display: none;
  }

  .team-tile-coaches {
    display: inline;
  }

  .club-card {
    padding: var(--space-2) var(--space-3);
  }

  .club-card-edit {
    white-space: nowrap;
  }
}

/* Sul telefono le icone rubano la riga al nome del club e al titolo dei
   riquadri di società: restano da 640px. */
@media (max-width: 639px) {
  /* `.society-card` davanti: la regola dell'icona sta più sotto. */
  .club-card .club-card-icon,
  .society-card .society-card-icon {
    display: none;
  }
}

/* «Altri dati di società»: il giro e la presentazione non sono altre
   squadre. Sopra un filetto, e il riquadro è una riga compatta: icona,
   testo piccolo, azione a destra. La forma è una sola e la portano tutte
   e due (issue #130): due righe della stessa sezione non si disegnano in
   due modi. */
.teams-society-title {
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
}

.society-card {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
}

.society-card-icon {
  display: grid;
  place-items: center;
  flex: none;
  width: 3.5rem;
  height: 3.5rem;
  border-radius: var(--radius-md);
  background: var(--color-bg);
  color: var(--color-text);
}

.society-card-body {
  flex: 1 1 18rem;
  min-width: 0;
}

.society-card-body h2 {
  margin: 0 0 2px;
  font: var(--type-title);
}

.society-card-body p {
  margin: 0;
  font: var(--type-caption);
}

.society-card-action {
  margin: 0;
}

/* La scheda squadra: allenatori e stagioni affiancati da 900px, solo a
   schermo come `.profile-layout`. Sotto, e sul foglio, una colonna, e le
   stagioni si staccano dagli allenatori. */
.team-detail-layout > section + section {
  margin-top: var(--space-4);
}

@media screen and (min-width: 900px) {
  .team-detail-layout > section + section {
    margin-top: 0;
  }

  .team-detail-layout {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-5);
    align-items: start;
  }

  .team-detail-layout > section > h2:first-child {
    margin-top: 0;
  }
}

/* ==========================================================================
   Home "Oggi" (v1.28 — mockup «Mockup pagina principale.png»)

   # Cosa cambia rispetto alla v1.27

   Il protagonista non è più il form. Fino alla v1.27 la pagina si apriva su
   cinque campi, e nel caso normale — la scheda del prossimo allenamento
   esiste già — quei campi non servivano a niente: la cosa da fare era
   aprirla. Adesso in cima c'è l'allenamento (giorno, ora, luogo, stato) e i
   parametri stanno sotto, **aperti**: `skaters_present` è un default fisso e
   i presenti cambiano ogni volta, quindi nasconderli dietro un "modifica"
   avrebbe messo un click davanti al campo che si tocca sempre.

   La colonna di destra resta contesto e non aggiunge passaggi: nessuno dei
   suoi elementi sta fra l'apertura della pagina e il bottone.
   ========================================================================== */

/* Le due colonne da 1024px. L'ordine del markup mette l'azione per prima —
   su telefono il contesto finisce SOTTO il bottone, non sopra: il contesto è
   un'informazione, il bottone è lo scopo della pagina. */
.home-hero {
  display: block;
}

@media (min-width: 1024px) {
  .home-hero {
    display: grid;
    /* La colonna dei campi non cresce oltre 34rem: un input largo mezzo
       schermo non è più facile da compilare, è solo più largo. Lo spazio in
       più se lo prende la colonna di destra, che ha elenchi da mandare a
       capo. */
    grid-template-columns: minmax(0, 1.15fr) minmax(20rem, 1fr);
    gap: var(--space-4);
    align-items: start;
  }
}

.home-main > form {
  border: none;
  padding: 0;
  margin: 0;
}

.home-aside {
  display: grid;
  gap: var(--space-4);
  /* `min-width: 0` su una traccia di griglia: senza, una riga lunga
     dell'elenco allenamenti allargherebbe la colonna oltre la sua frazione
     invece di andare a capo. */
  min-width: 0;
}

/* --- Intestazione: quando siamo ----------------------------------------
   (15.09.2026) Era «chi siamo, e quando siamo»: la squadra a sinistra col
   suo selettore, la data a destra. Chi siamo ora lo dice la card del
   contesto, nella shell di ogni pagina (`partials/context_card.html`), e
   qui resta la data, a sinistra e come titolo della pagina.

   (15.09.2026) Una riga sola: «Oggi, martedì 15 settembre» in inchiostro e
   il prossimo allenamento dopo il punto, in secondo piano. */
.home-head {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
  color: var(--color-text-muted);
}

.home-head svg {
  flex: none;
  margin-top: 0.05rem;
}

/* Un `<h1>` del corpo delle didascalie: il titolo della pagina è la data,
   non una scritta grande. */
.home-head-today {
  margin: 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.home-head-today strong {
  font-weight: 600;
  color: var(--color-text);
}

/* --- (issue #99) I due compiti di pista --------------------------------
   Sul telefono, in cima: «Segui la scheda» (o «Genera la scheda») e
   «Valuta», di peso pari — stessa superficie, stessa altezza, affiancati
   finché ciascuno tiene 10rem. Le due tessere stanno nella prima riga di una
   sottogriglia e il link secondario («Vedi la scheda completa») nella
   seconda: senza, la tessera accanto a un link si accorcerebbe della sua
   altezza e il peso non sarebbe più pari. */
.rink-tasks {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(10rem, 100%), 1fr));
  grid-template-rows: auto auto;
  gap: var(--space-1) var(--space-3);
  margin-bottom: var(--space-4);
}

.rink-task-slot {
  display: grid;
  grid-row: span 2;
  grid-template-rows: subgrid;
  gap: var(--space-1);
  min-width: 0;
}

/* Un bersaglio da guanti: alto quanto due righe di dito, e tutto il riquadro
   è il link. */
.rink-task {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--space-1);
  min-height: 5rem;
  padding: var(--space-3);
  border-radius: var(--radius-lg);
  background: var(--color-primary);
  color: var(--color-primary-contrast);
  text-decoration: none;
}

.rink-task:hover {
  background: var(--color-primary-dark);
}

.rink-task:focus-visible {
  outline: 3px solid var(--color-primary-dark);
  outline-offset: 3px;
}

.rink-task-title {
  font: var(--type-section);
  text-transform: uppercase;
}

.rink-task-caption {
  font: var(--type-caption);
}

/* (15.09.2026) «Così non si genera»: una riga in più nella tessera, col
   triangolo — il colore della tessera non cambia, lo dicono forma e parole. */
.rink-task-warn {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  font: var(--type-label);
}

.rink-task-warn svg {
  flex: none;
}

.rink-task-aside {
  justify-self: start;
  padding: var(--space-1) 0;
  font: var(--type-label);
  color: var(--color-primary-dark);
}

/* (15.09.2026) Il giorno dello Speed Lap Challenge, il cronometro: la terza
   tessera, a tutta larghezza sotto le altre due. */
.rink-task-slot-stopwatch {
  grid-column: 1 / -1;
}

/* Da 1024px la home è quella di prima: la card del prossimo allenamento porta
   «Apri la scheda», e le valutazioni stanno nella barra laterale. Resta solo
   il cronometro, che la card non porta, e resta della misura di una tessera.
   Dopo le regole qui sopra, o perderebbe a pari specificità. */
@media (min-width: 1024px) {
  .rink-tasks:not(.has-stopwatch),
  .rink-task-slot:not(.rink-task-slot-stopwatch) {
    display: none;
  }

  .rink-task-slot-stopwatch {
    max-width: 24rem;
  }
}

/* --- La card dell'allenamento ------------------------------------------ */
/* Fondo tenue dell'accento e non bianco: è l'unica superficie della pagina
   che porta la decisione, e distinguerla per **colore di fondo** invece che
   per un filo in alto (v1.12) è ciò che la fa leggere per prima senza
   ingrandire nulla. Le altre card restano bianche, quindi il secondo colore
   non entra in competizione: compare una volta sola. */
.next-training-card {
  background: var(--color-primary-soft);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  margin-bottom: var(--space-4);
}

/* Senza un allenamento in vista la card non porta nessuna decisione: torna
   bianca come le altre e smette di reclamare lo sguardo. */
.next-training-card-empty {
  background: var(--color-surface);
}

.next-training-head {
  margin: 0 0 var(--space-2) 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}

.next-training-when {
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  color: var(--color-primary-dark);
  background: var(--color-surface);
  border-radius: var(--radius-pill);
  padding: 3px var(--space-3);
}

/* Il titolo della pagina in tutto tranne il livello: `h1` resta il nome
   della squadra, ma la data è ciò che si legge per primo dentro la card. */
.next-training-date {
  font: var(--type-screen);
  margin: 0 0 var(--space-3) 0;
}

.next-training-meta {
  list-style: none;
  margin: 0 0 var(--space-3) 0;
  padding: 0;
  display: grid;
  gap: var(--space-2);
}

.next-training-meta li {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font: var(--type-body-strong);
}

.next-training-meta svg {
  color: var(--color-primary);
  flex: none;
}

.next-training-meta-sub,
.next-training-note {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* Nota di stato, non azione: il testo resta in corpo normale e l'accento se
   lo prendono i bottoni sotto — stessa regola già applicata alla v1.27 a
   `.target-training-ready`, che questo blocco sostituisce. */
.next-training-ready {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  margin: 0 0 var(--space-3) 0;
  padding: var(--space-3);
  background: var(--color-ok-bg);
  border-radius: var(--radius-md);
}

.next-training-ready svg {
  color: var(--color-ok-text);
  flex: none;
  margin-top: 1px;
}

.next-training-ready strong {
  display: block;
  color: var(--color-ok-text);
}

.next-training-ready-sub {
  display: block;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.next-training-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

/* A bordo pista si tocca con il pollice: i due gesti occupano la riga intera
   e si dividono lo spazio, invece di essere due bersagli piccoli a sinistra.
   Da 480px in su tornano della loro larghezza naturale. */
.next-training-actions > * {
  flex: 1 1 12rem;
  text-align: center;
}

@media (min-width: 480px) {
  .next-training-actions > * {
    flex: 0 1 auto;
  }
}

/* --- La card dei parametri --------------------------------------------- */
.params-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  margin-bottom: var(--space-4);
}

.params-card-head,
.priorities-head,
.upcoming-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
  /* Il link va a capo *sotto* il titolo quando la colonna è stretta. Senza
     `wrap`, era il titolo a spezzarsi — «Le priorità / della / squadra» su
     tre righe accanto a un link intero, che è il contrario della gerarchia
     voluta. */
  flex-wrap: wrap;
}

.params-card-head svg,
.priorities-head svg,
.upcoming-head svg {
  color: var(--color-primary);
  flex: none;
}

.params-card-head h2,
.priorities-head h2,
.upcoming-head h2 {
  font: var(--type-title);
  margin: 0;
  flex: 1 1 auto;
}

/* Il link della testata va a fondo riga e cede per primo quando lo spazio
   manca: è la via d'uscita, non il contenuto. */
.priorities-head-link,
.upcoming-head-link {
  margin-left: auto;
  font: var(--type-caption);
  white-space: nowrap;
}

/* Due colonne dove ci stanno: i cinque campi sono corti (una data, una
   tendina, tre numeri) e in colonna singola facevano una pagina alta il
   doppio di quel che serviva. */
.params-grid {
  display: grid;
  gap: var(--space-3);
}

@media (min-width: 560px) {
  .params-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.params-grid label {
  margin: 0;
}

.params-card .btn-block {
  margin-top: var(--space-4);
}

/* (15.09.2026) «Genera scheda» fallirà con i valori proposti: la pastiglia
   della capienza del tema, fra i campi e il bottone. */
.params-card .generation-readiness {
  margin: var(--space-4) 0 0;
}

.params-card-hint {
  margin: var(--space-3) 0 0 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* (15.09.2026) Con la scheda pronta i parametri stanno chiusi: la testata è
   il `<summary>`, e sotto il titolo i valori in uso. La freccia si gira
   quando si apre. */
.params-card-fold {
  margin-bottom: 0;
  flex-wrap: nowrap;
  list-style: none;
  cursor: pointer;
}

.params-card-fold::-webkit-details-marker {
  display: none;
}

.params-card-fold:focus-visible {
  outline: 3px solid var(--color-primary-dark);
  outline-offset: 4px;
  border-radius: var(--radius-sm);
}

details[open] > .params-card-fold {
  margin-bottom: var(--space-3);
}

.params-card-fold-values {
  display: block;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.params-card-fold-chevron {
  flex: none;
  color: var(--color-text-muted);
  transition: transform 0.15s;
}

details[open] > .params-card-fold .params-card-fold-chevron {
  transform: rotate(180deg);
}

@media (prefers-reduced-motion: reduce) {
  .params-card-fold-chevron {
    transition: none;
  }
}

/* --- Colonna di destra: il blocco e il suo avanzamento ------------------ */
.home-context-card,
.priorities-card,
.upcoming-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  min-width: 0;
}

.context-label {
  display: block;
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin-bottom: 2px;
}

.context-block-name {
  font: var(--type-section);
  margin: 0;
}

.context-sub {
  display: block;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.context-figure {
  display: block;
  font: var(--type-figure-sm);
  color: var(--color-primary-dark);
}

.block-progress {
  display: grid;
  gap: var(--space-3);
  margin-top: var(--space-3);
}

/* La barra e il numero affiancati appena c'è spazio, con un filetto in mezzo:
   sono due letture dello stesso blocco — quanto è alle spalle e quanto resta
   — e vicine si confrontano invece di sommarsi. */
@media (min-width: 420px) {
  .block-progress {
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--space-4);
  }

  .block-progress-remaining {
    border-left: 1px solid var(--color-border);
    padding-left: var(--space-4);
  }
}

.block-progress-caption {
  margin: 0 0 var(--space-2) 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.block-progress-caption strong {
  font: var(--type-body-strong);
  color: var(--color-text);
}

.block-progress-track {
  height: 10px;
  border-radius: var(--radius-pill);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  overflow: hidden;
}

.block-progress-fill {
  height: 100%;
  background: var(--color-primary);
  border-radius: var(--radius-pill);
}

/* Il link di uscita non deve toccare la barra sopra di sé: è la riga che si
   legge per ultima, non la didascalia del grafico. */
.home-context-card .field-hint {
  margin: var(--space-4) 0 0 0;
}

/* --- Le priorità -------------------------------------------------------- */
.priority-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-2);
}

/* La riga intera è il bersaglio, non solo il testo: 44px di altezza minima,
   come ogni altro tocco dell'app. */
.priority-row,
.upcoming-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 44px;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  color: inherit;
  text-decoration: none;
}

.priority-row:hover,
.upcoming-row:hover,
.priority-row:focus-visible,
.upcoming-row:focus-visible {
  border-color: var(--color-primary);
  background: var(--color-primary-soft);
}

/* Il quadrato porta la tinta della famiglia del tema — la stessa che quel
   tema ha nella timeline di stagione e sulla scheda (`--fam`, impostata
   dalle classi `.fam-*` già esistenti). Il colore identifica, l'etichetta
   accanto dice: nessuna informazione sta nel solo colore.

   Tinta sul **tratto e sul bordo**, non su un fondo pieno con l'icona in
   bianco come nel mockup: due delle sette famiglie sono un ambra e un rosa
   chiaro, e il bianco sopra di esse sta sotto 3:1. Un fondo pieno avrebbe
   funzionato per cinque famiglie su sette, che è il modo peggiore di
   sbagliare — si nota solo sul tema che capita. */
.priority-row-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  flex: none;
  border-radius: var(--radius-md);
  background: var(--color-bg);
  border: 1px solid var(--fam, var(--color-border));
  color: var(--fam, var(--color-primary));
}

.priority-row-text {
  min-width: 0;
  color: var(--color-text);
}

.priority-row-text strong {
  display: block;
  font: var(--type-body-strong);
}

.priority-row-sub {
  display: block;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.priority-row-go,
.upcoming-go {
  margin-left: auto;
  color: var(--color-text-muted);
  flex: none;
}

/* --- Il riquadro che spiega -------------------------------------------- */
/* Unico contenuto della card quando non ci sono priorità da elencare: senza
   una testata sopra, lo stacco non serve. */
.priorities-card > .rationale-box:first-child {
  margin-top: 0;
}

.rationale-box {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  margin-top: var(--space-3);
  padding: var(--space-3);
  border-radius: var(--radius-md);
  background: var(--color-bg);
}

.rationale-box > svg {
  color: var(--color-warning-border);
  flex: none;
  margin-top: 1px;
}

.rationale-title {
  margin: 0 0 2px 0;
  font: var(--type-body-strong);
}

.rationale-body {
  margin: 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.rationale-body strong {
  color: var(--color-text);
}

/* Il nome del tema dentro la frase porta la tinta della sua famiglia, come
   ovunque nell'app. Resta sottolineato dal link di base: dentro un
   paragrafo il colore da solo non basta a dire che si può cliccare. */
.rationale-theme {
  color: var(--fam, var(--color-primary-dark));
  font: var(--type-body-strong);
}

.rationale-link {
  display: inline-block;
  margin-top: var(--space-2);
  font: var(--type-caption);
}

/* --- I prossimi allenamenti -------------------------------------------- */
.upcoming-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-2);
}

.upcoming-row {
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
}

.upcoming-date {
  font: var(--type-body-strong);
}

.upcoming-weekday,
.upcoming-time,
.upcoming-place {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* Il luogo cede per primo quando la riga è stretta: data, ora e stato sono
   ciò che distingue una riga dall'altra, il luogo di solito è lo stesso per
   tutte. */
.upcoming-place {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Lo stato porta un pallino colorato E la sua etichetta: il colore da solo
   direbbe "verde/giallo/grigio" a chi lo distingue, e niente a chi non lo
   distingue. */
.upcoming-status {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font: var(--type-caption);
  white-space: nowrap;
}

.upcoming-status::before {
  content: "";
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}

.training-status-ready {
  color: var(--color-ok-text);
}

.training-status-todo {
  color: var(--color-warning-text);
}

.training-status-planned {
  color: var(--color-text-muted);
}

/* Il richiamo al piano sulla scheda generata: stessa misura delle altre
   righe di `.session-meta`, ma staccata da un filo sopra — appartiene alla
   pianificazione, non ai parametri di generazione elencati sopra di essa. */
.session-planned {
  margin-top: var(--space-2);
  padding-top: var(--space-2);
  border-top: 1px solid var(--color-border);
}

/* ==========================================================================
   Scheda generata — affiancamenti da 900px (v1.12)

   Solo a schermo: la sezione Stampa più sotto riporta tutto a colonna
   singola, che resta un requisito di prodotto e non una conseguenza del
   layout scelto qui.
   ========================================================================== */

.session-head-grid {
  display: block;
}

@media (min-width: 900px) {
  .session-head-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 20rem;
    gap: var(--space-3);
    align-items: start;
  }

  .session-head-grid .session-meta,
  .session-head-grid .guardrail {
    margin-bottom: 0;
  }
}

.phase-exercise-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
}

@media (min-width: 900px) {
  .phase-exercise-grid {
    grid-template-columns: repeat(2, 1fr);
  }

  .phase-exercise-grid .exercise-card {
    margin-bottom: 0;
    height: 100%;
  }
}

/* ==========================================================================
   Regolamento FSIH (v1.13) — `/rules`, `/rules/signals`.

   L'export offline (`/rules/offline`) NON usa questo foglio: è un documento
   autonomo con lo stile inline, perché deve funzionare sul telefono senza
   questo server. Le due cose si assomigliano di proposito, ma non
   condividono un file — condividerlo significherebbe che il file salvato
   dipende da un indirizzo che in palazzetto non risponde.
   ========================================================================== */

/* Barra di ricerca (v1.36). Il campo e il bottone stanno su una riga sola e
   alla stessa altezza — 48px, un gradino sopra il minimo dell'app, perché su
   queste due pagine il campo *è* la pagina e non un controllo fra tanti.
   Sotto ai 640px il bottone passa a capo a piena larghezza: a bordo pista si
   preme col pollice, e mezza riga di bottone accanto a mezza riga di campo
   non serve a nessuno dei due. */
.rules-search {
  margin-bottom: var(--space-3);
}

.rules-search-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-2);
}

.rules-search-field {
  flex: 1 1 18rem;
}

.rules-search-field input[type="search"],
.rules-search-bar button {
  min-height: 48px;
}

.rules-search-bar button {
  flex: 1 1 100%;
}

@media (min-width: 640px) {
  .rules-search-bar button {
    flex: 0 0 auto;
    min-width: 8rem;
  }
}

/* Le due porte laterali: i bottoni su una riga, la spiegazione del file
   offline sotto e non di fianco. Di fianco, su schermo largo, quella frase
   correva fino al bordo destro e faceva sembrare la riga un paragrafo con
   due bottoni dentro. */
.rules-links {
  margin-bottom: var(--space-4);
}

.rules-links .page-actions {
  margin-bottom: var(--space-2);
}

.rules-links .field-hint {
  margin-top: 0;
  max-width: 46rem;
}

.rules-empty p {
  margin: 0;
}

.rules-empty .rules-examples {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.rules-examples-label {
  font: var(--type-caption);
}

/* Esempi cliccabili: sono ricerche vere, quindi hanno l'aria di qualcosa che
   si preme, non di testo colorato. */
.rules-example {
  display: inline-block;
  background: var(--color-primary-soft);
  color: var(--color-primary-dark);
  border: 1px solid var(--color-chip-border);
  border-radius: var(--radius-pill);
  padding: var(--space-1) var(--space-3);
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  text-decoration: none;
  white-space: nowrap;
}

.rules-example:hover {
  background: var(--color-primary);
  color: var(--color-primary-contrast);
}

.rule-hits {
  list-style: none;
  margin: 0;
  padding: 0;
}

.rule-hit {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  margin-bottom: var(--space-3);
}

.rule-hit-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}

/* Il numero d'articolo è la chiave che si pronuncia a bordo pista: cifre a
   larghezza fissa perché una colonna di numeri resti allineata e leggibile
   di sfuggita. */
.rule-number {
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  font-variant-numeric: tabular-nums;
  color: var(--color-primary);
}

.rule-section-title {
  color: var(--color-text-muted);
  font: var(--type-title);
}

.rule-text {
  margin: 0 0 var(--space-2) 0;
}

.rule-items {
  margin: 0 0 var(--space-2) 0;
  padding-left: 1.25em;
}

.rule-items li {
  margin-bottom: var(--space-1);
}

.rule-meta {
  margin: 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.rule-fr {
  margin-top: var(--space-2);
}

.rule-fr > summary {
  cursor: pointer;
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  color: var(--color-primary);
}

.rule-text-fr {
  margin-top: var(--space-2);
  color: var(--color-text-muted);
}

.signal-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
}

@media (min-width: 900px) {
  .signal-list {
    grid-template-columns: repeat(2, 1fr);
  }
}

.signal-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
}

.signal-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}

.signal-name {
  font: var(--type-title);
}

/* Riquadrato e non colorato soltanto: è una lettera stampata su un referto,
   e va riconosciuta come tale — non è un tag né un link. */
.signal-code {
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  border: 1px solid var(--color-primary);
  color: var(--color-primary);
  border-radius: var(--radius-sm);
  padding: 0 var(--space-2);
}

.signal-note {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* Il disegno del gesto (`signal_images`): sopra il testo, largo quanto la
   card fino a 22rem, a ogni larghezza. A bordo pista è il disegno che si
   cerca, e il testo ne ripete le parole: accanto al testo, a 13rem, il
   disegno perdeva proprio le mani. Con `app-main-roomy` a 75rem la card non
   arriva a 36rem, quindi una disposizione «larga» sarebbe nata solo sugli
   schermi più grandi — una pagina diversa a seconda del monitor. */
.signal-body.has-figure {
  display: grid;
  gap: var(--space-3);
}

.signal-figure {
  display: block;
  justify-self: center;
  width: 100%;
  max-width: 22rem;
}

.signal-figure img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1;
  border-radius: var(--radius-sm);
}

/* La nota sui disegni sta sotto la riga d'edizione, e ne è il seguito: il
   margine negativo la avvicina al sottotitolo invece che alla ricerca. */
.signal-drawings-note {
  margin: calc(-1 * var(--space-3)) 0 var(--space-4);
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* ==========================================================================
   Drill Designer (v1.14) — `/exercises/{id}/diagram`.
   ========================================================================== */

/* (v1.23) Prima: tre colonne — palette 17,5rem, pista, proprietà 17,5rem —
   e su un portatile alla pista restava poco più della metà della finestra,
   con i due pannelli quasi vuoti. Ora la pista è l'unica colonna: gli
   strumenti sono una barra sopra, l'ispettore una barra sotto, e ciò che
   limita la pista è l'altezza della finestra, non due sidebar. */
.designer-layout {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* --- Barra alta: titolo + azioni --- */

.designer-topbar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-2) var(--space-3);
  margin-bottom: var(--space-3);
}

.designer-topbar h1 {
  margin: 0;
  font: var(--type-title);
}

.designer-back {
  font: var(--type-caption);
}

.designer-topbar-actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.designer-dirty {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.designer-history {
  display: flex;
  gap: var(--space-1);
}

/* Annulla/Rifai sono due gesti frequenti ma secondari: due quadrati con la
   freccia, non due bottoni con l'etichetta. Il titolo resta nel `title` e
   in `aria-label`. */
.designer-icon-btn {
  min-height: 38px;
  width: 38px;
  padding: 0;
  font: var(--type-title);
  line-height: 1;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  color: var(--color-text);
}

.designer-icon-btn:hover:not(:disabled) {
  background: var(--color-bg);
  border-color: var(--color-primary);
}

.designer-icon-btn:disabled {
  opacity: 0.4;
  cursor: default;
}

/* Il form di salvataggio è solo il contenitore del bottone: senza questo
   prenderebbe il vestito da card di `form` (bordo, padding, margine). */
.designer-save-form {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  margin: 0;
  gap: 0;
}

.designer-save-form button {
  min-height: 38px;
  padding: var(--space-1) var(--space-3);
  font: var(--type-button-sm);
}

/* --- Barra strumenti --- */

.designer-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-3);
}

.tool-group {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
}

/* Separatore fra gruppi: una riga verticale sottile costa meno spazio di un
   titolo su una riga sua. */
.tool-group + .tool-group {
  border-left: 1px solid var(--color-border);
  padding-left: var(--space-3);
}

.tool-group-label {
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
  white-space: nowrap;
}

.tool-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
}

.designer-help {
  margin-left: auto;
  position: relative;
}

.designer-help > summary {
  list-style: none;
  cursor: pointer;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--color-border);
  color: var(--color-text-muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
}

.designer-help > summary::-webkit-details-marker {
  display: none;
}

.designer-help[open] > summary {
  border-color: var(--color-primary);
  color: var(--color-primary-dark);
}

.designer-help-body {
  position: absolute;
  right: 0;
  top: calc(100% + var(--space-1));
  z-index: 5;
  width: min(26rem, 80vw);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  padding: var(--space-3);
  font: var(--type-body);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  /* (v1.34) Con le tre voci nuove — colore, etichette, duplica — il
     pannello superava l'altezza della finestra e le ultime righe finivano
     sotto il bordo, irraggiungibili. Si ferma prima e scorre: è un elenco
     di istruzioni, non un disegno da vedere tutto insieme. */
  max-height: calc(100dvh - 8rem);
  overflow-y: auto;
  overscroll-behavior: contain;
}

.designer-help-body p {
  margin: 0;
}

.designer-help-body kbd {
  font: inherit;
  font: var(--type-caption);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: 0 0.3em;
  background: var(--color-bg);
}

/* --- Pista --- */

.designer-stage {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-width: 0;
}

.designer-canvas {
  position: relative;
}

.designer-caption {
  font: var(--type-caption);
  color: var(--color-text-muted);
  margin: 0;
}

/* --- Le immagini importate (v1.51, issue #41) ---
   La striscia in fondo alla pagina del designer e il blocco dentro il
   riquadro d'import della scheda di modifica: stesse immagini, stesso
   collegamento all'originale a piena dimensione, due contenitori perché i
   due posti hanno vincoli d'altezza diversi. */

.designer-images,
.import-images {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--space-2);
}

/* (v1.52) La fascia in fondo alla pagina del designer: le immagini e, di
   fianco, la descrizione della scheda. Una riga sola finché ci sta; su uno
   schermo stretto la descrizione va sotto le immagini invece di ridursi a
   una colonna di due parole. */
.designer-reference {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--space-2) var(--space-3);
  margin-top: var(--space-3);
}

/* La descrizione prende quel che resta, e non scende sotto una colonna di
   testo leggibile: sotto quella soglia va a capo sotto le immagini. */
.designer-brief {
  flex: 1 1 22rem;
  min-width: 0;
  font: var(--type-body);
  color: var(--color-text);
}

.designer-brief-goal {
  font: var(--type-body-strong);
  margin: 0 0 var(--space-1);
}

.designer-brief h2 {
  font: var(--type-label);
  color: var(--color-text-muted);
  margin: var(--space-2) 0 var(--space-1);
}

.designer-brief ol {
  margin: 0;
  padding-left: 1.25rem;
}

.designer-brief li + li {
  margin-top: var(--space-1);
}

.import-images {
  margin-top: var(--space-2);
}

/* Il collegamento è la scatola dell'immagine: è lui che si prende il tetto
   d'altezza sotto, e l'immagine lo riempie. */
.imported-image {
  display: block;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  /* Le foto di mobilesport sono disegni su fondo chiaro, e alcune sono PNG
     con trasparenza: senza un fondo dichiarato prenderebbero quello della
     pagina, che qui è grigio. */
  background: var(--color-surface);
  min-width: 0;
}

.imported-image img {
  display: block;
  max-width: 100%;
  height: auto;
  border-radius: inherit;
}

/* Nella scheda di modifica la pagina scorre e le immagini non competono con
   nient'altro: basta che non diventino murali su uno schermo largo. */
.import-images .imported-image {
  max-width: min(100%, 22rem);
}

/* (v1.23) Un ferro della barra strumenti: l'icona sopra — il disegno vero
   che finirà sulla pista, non un'astrazione — e il nome sotto in piccolo,
   perché un triangolo arancione da solo non dice "cono" a chi apre la
   pagina la prima volta.
   Prima erano bottoni col solo nome a 1rem e 44px d'altezza: quattordici
   nomi in fila leggono come un menu, non come una cassetta degli attrezzi.
   Il bersaglio da dito cresce sotto `pointer: coarse` più in basso — a
   bordo pista si usa il tablet, sulla scrivania il mouse. */
.palette-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  /* Larghezza dal nome, con un minimo: "Segnaposto" e "Spostamento" sono
     più larghi dell'icona più larga, e a larghezza fissa uscivano dal
     bordo del ferro. */
  min-width: 3.4rem;
  padding: var(--space-1) 6px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-text);
  font: inherit;
  cursor: grab;
  /* Il gesto di trascinamento è nostro (pointer events in designer.js): se
     il browser interpretasse il primo movimento come uno scorrimento, il
     trascinamento non partirebbe mai. Solo da 1024px, dove la pagina sta
     tutta nello schermo e non c'è niente da scorrere: sul telefono lo
     scorrimento serve, e lì si tocca l'icona e poi la pista. */
  touch-action: manipulation;
}

@media (min-width: 1024px) {
  .palette-btn {
    touch-action: none;
  }
}

/* Le traiettorie sono frecce: un'icona larga in un riquadro quadrato
   sarebbe minuscola. E i loro nomi sono lunghi («Indietro con pallina»):
   larghezza fissa e nome su due righe, altrimenti nove ferri da 130px si
   prendono l'intera barra. */
.palette-btn-wide {
  width: 5.4rem;
}

.palette-btn-wide .tool-label {
  white-space: normal;
}

.tool-icon {
  display: block;
  width: auto;
  height: 24px;
  /* L'icona non intercetta il puntatore: `pointerdown` deve arrivare al
     bottone, che è chi sa quale strumento è. */
  pointer-events: none;
}

.tool-icon-wide {
  height: 14px;
}

.tool-label {
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--color-text-muted);
  text-align: center;
  pointer-events: none;
}

.palette-btn:hover:not(:disabled) {
  border-color: var(--color-primary);
  background: var(--color-bg);
}

.palette-btn:hover:not(:disabled) .tool-label {
  color: var(--color-text);
}

/* Il fantasma che segue il dito/puntatore durante il trascinamento: l'icona
   stessa, un po' più grande, sotto il puntatore e fuori dal flusso. */
.tool-ghost {
  position: fixed;
  z-index: 30;
  pointer-events: none;
  transform: translate(-50%, -50%);
  opacity: 0.85;
  filter: drop-shadow(0 2px 4px rgba(16, 24, 40, 0.25));
}

.tool-ghost .tool-icon {
  height: 34px;
}

.tool-ghost .tool-icon-wide {
  height: 20px;
}

/* Lo strumento "armato" resta visibile: senza, dopo aver scelto "Cono" non
   c'è modo di sapere che il prossimo tocco piazzerà qualcosa. */
.palette-btn.is-armed {
  background: var(--color-primary-soft);
  border-color: var(--color-primary);
  box-shadow: inset 0 0 0 1px var(--color-primary);
}

.palette-btn.is-armed .tool-label {
  color: var(--color-primary-dark);
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
}

/* Etichetta galleggiante sopra la pista: non ruba una riga quando non c'è
   niente da dire, e quando c'è la si legge dove guardi (la pista), non
   sopra. `:empty` la fa sparire — `announce('')` svuota il nodo. */
.designer-status {
  position: absolute;
  top: var(--space-2);
  left: 50%;
  transform: translateX(-50%);
  z-index: 2;
  pointer-events: none;
  font: var(--type-caption);
  color: var(--color-primary-contrast);
  background: var(--color-primary-dark);
  border-radius: var(--radius-pill);
  padding: 0.25rem var(--space-3);
  box-shadow: var(--shadow-sm);
  max-width: calc(100% - var(--space-4));
  text-align: center;
}

.designer-status:empty {
  display: none;
}

.designer-svg {
  width: 100%;
  height: auto;
  display: block;
  border-radius: var(--radius-md);
  /* Senza questo, su un tablet il primo movimento del dito scorre la pagina
     invece di trascinare l'elemento, e il gesto è perso. */
  touch-action: none;
  user-select: none;
}

.designer-svg.is-placing {
  cursor: crosshair;
}

.diagram-element {
  cursor: grab;
}

/* Bersagli di tocco invisibili ma generosi: una pallina ha raggio 0,28 m,
   cioè pochi pixel a schermo, e una traiettoria è una linea sottile. */
.element-hit {
  fill: transparent;
  stroke: none;
}

/* `grab` come gli elementi: una freccia si prende e si sposta tutta. */
.trajectory-hit {
  fill: none;
  stroke: transparent;
  stroke-width: 1.2;
  cursor: grab;
}

.selection-ring {
  fill: none;
  stroke: var(--color-primary);
  stroke-width: 0.16;
  stroke-dasharray: 0.4 0.3;
}

.trajectory.is-selected {
  stroke: var(--color-primary);
}

/* (v1.23) La pallina dentro gli archi del pattinaggio indietro con pallina:
   è un `<circle>`, non un tratto, quindi la selezione la deve tingere di
   riempimento e non di contorno. */
.trajectory-dot.is-selected {
  fill: var(--color-primary);
}

.handle {
  fill: var(--color-surface);
  stroke: var(--color-primary);
  stroke-width: 0.14;
  cursor: grab;
}

/* (v1.30) La manopola che gira porte e ostacoli, e il filo che la tiene
   legata al centro dell'elemento: il filo dice attorno a cosa si gira, e
   dove punta è il verso corrente della forma. */
.handle-rotate {
  cursor: crosshair;
}

.handle-stem {
  stroke: var(--color-primary);
  stroke-width: 0.07;
  stroke-dasharray: 0.25 0.2;
  pointer-events: none;
}

/* (v1.34) Il filo fra un elemento selezionato e la sua etichetta spostata.
   Più sottile del gambo della manopola perché non è una cosa da prendere:
   è un legame da leggere. Solo nell'editor — sulla scheda stampata le
   scritte le ha già sistemate qualcuno. */
.handle-tether {
  stroke: var(--color-primary);
  stroke-width: 0.06;
  stroke-dasharray: 0.2 0.18;
  pointer-events: none;
}

/* I capi di una freccia selezionata: quadrati, perché il cerchio è già dei
   punti di curva e le due maniglie fanno cose diverse — il cerchio curva il
   tracciato, il quadrato sposta la partenza o l'arrivo. */
.handle-end {
  cursor: move;
}

/* L'elemento a cui la freccia che si sta spostando è agganciata in quel
   momento. Pieno e non tratteggiato, per non confondersi con l'anello di
   selezione: questo dice «se lasci qui, resta attaccata a lui». */
.handle-snap {
  fill: none;
  stroke: var(--color-primary);
  stroke-width: 0.12;
  pointer-events: none;
}

/* Un'area si prende e si sposta come il resto. Selezionata, il bordo
   diventa quello della selezione: tratteggiato come l'anello attorno a un
   elemento, pieno al cento per cento anche dove la campitura è un velo. */
.designer-svg .diagram-area {
  cursor: grab;
}

.designer-svg .diagram-area.is-selected {
  stroke: var(--color-primary);
  stroke-width: 0.14;
  stroke-opacity: 1;
  stroke-dasharray: 0.4 0.3;
}

/* Le maniglie agli angoli di un'area: il cursore dice in che verso tirano. */
.handle-corner-nw,
.handle-corner-se {
  cursor: nwse-resize;
}

.handle-corner-ne,
.handle-corner-sw {
  cursor: nesw-resize;
}

/* Le pastiglie del colore di un'area: stesse di quelle delle tinte, col
   riempimento da `--area` invece che da `--tint`. */
.tint-chip.area-chip {
  background: var(--area, transparent);
}

/* Il cursore della trasparenza, con il valore accanto alla didascalia. */
.inspector-opacity input[type="range"] {
  width: 9rem;
  min-height: 0;
  padding: 0;
  accent-color: var(--color-primary);
}

.inspector-opacity output {
  font-variant-numeric: tabular-nums;
}

/* Il tratto a mano libera mentre lo si disegna. Provvisorio, e deve averne
   l'aria: sottile e trasparente, sparisce al rilascio e al suo posto arriva
   la freccia nello stile scelto. */
.stroke-ink {
  fill: none;
  stroke: var(--color-primary);
  stroke-width: 0.14;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.55;
  pointer-events: none;
}

/* Un'etichetta si trascina, e il puntatore lo dice prima di provarci.
   Solo nel designer: la stessa classe finisce nella scheda stampata, dove
   non c'è niente da trascinare. */
.designer-svg .element-label.is-movable,
.designer-svg .trajectory-label.is-movable,
.designer-svg .area-label.is-movable {
  cursor: move;
}

/* --- Ispettore della selezione --- */

/* I campi di un elemento sono tre (ruolo, numero, etichetta): in una riga
   sotto la pista stanno tutti, e la colonna da 17,5rem che li conteneva
   torna alla pista. Il contenuto lo costruisce `renderProperties()` in
   designer.js — qui si stilano i nodi che crea. */
.designer-inspector {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-2) var(--space-3);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-3);
  /* Alta come quando è piena: la pista si prende l'altezza che resta, e se
     l'ispettore crescesse alla selezione la pista si rimpicciolirebbe di
     qualche pixel a ogni tocco. */
  min-height: 4rem;
}

.designer-inspector h3 {
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin: 0;
  align-self: center;
  white-space: nowrap;
}

.designer-inspector > .field-hint {
  margin: 0;
  align-self: center;
}

.designer-inspector label {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 0;
  width: 9rem;
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  color: var(--color-text-muted);
  text-transform: uppercase;
}

/* Un numero di maglia sono due cifre, un'etichetta è una frase breve: la
   larghezza segue il contenuto invece di dare 9rem a entrambi. */
.designer-inspector label:has(input[type="number"]) {
  width: 6rem;
}

.designer-inspector label:has(input[type="text"]) {
  width: 12rem;
}

.designer-inspector input,
.designer-inspector select {
  width: 100%;
  min-height: 34px;
  padding: 0 var(--space-2);
  text-transform: none;
  letter-spacing: normal;
  color: var(--color-text);
}

/* (v1.34) Un campo dell'ispettore che non è un `<label>` — le pastiglie del
   colore sono bottoni, e un `<label>` attorno a dei bottoni non etichetta
   niente. Stessa didascalia minuscola degli altri campi, così la riga resta
   una riga sola. */
.inspector-field {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 0;
}

.inspector-field-label {
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* Le sette pastiglie del colore. Il riempimento viene da `--tint`, definita
   dalle classi `tint-*` di `DIAGRAM_STYLE` (il `<style>` dentro l'SVG in
   linea vale per tutto il documento): il colore della pastiglia e quello
   della forma sulla pista sono lo stesso valore, non due copie. */
.tint-chips {
  display: flex;
  gap: 5px;
  align-items: center;
  min-height: 34px;
}

.tint-chip {
  width: 22px;
  height: 22px;
  /* `button` di base porta `min-height: 44px` per il dito: qui sarebbe un
     ovale invece di una pastiglia, e il bersaglio da dito lo dà il blocco
     `pointer: coarse` in fondo, dove la pastiglia cresce tutta. */
  min-height: 0;
  padding: 0;
  border: 1px solid var(--color-border);
  border-radius: 50%;
  background: var(--tint, transparent);
  cursor: pointer;
}

/* «Come da legenda» non è un colore e non può esserne uno: la sbarra dice
   che qui non si sceglie una tinta, si toglie. */
.tint-chip-default {
  background:
    linear-gradient(135deg,
      var(--color-surface) 44%, var(--color-text-muted) 44%,
      var(--color-text-muted) 56%, var(--color-surface) 56%);
}

.tint-chip[aria-pressed="true"] {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* (15.09.2026) Lo spessore del tratto: due pastiglie accanto a quelle del
   colore, col campione della linea dentro. Stessa misura e stesso segno di
   «scelta», più larghe perché una linea si legge in orizzontale. */
.weight-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 22px;
  min-height: 0;
  padding: 0 4px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-text);
}

.weight-chip svg {
  width: 100%;
  height: 100%;
}

.weight-chip[aria-pressed="true"] {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* Un controllo appeso alla casella invece che alla riga: il ripristino
   dell'etichetta sta dentro il campo «Etichetta», che resta largo 12rem.
   Fuori, manderebbe l'ispettore a capo e la pista perderebbe l'altezza di
   una riga ogni volta che si seleziona un'etichetta spostata. */
.designer-inspector .field-with-suffix {
  display: flex;
  align-items: stretch;
  gap: 4px;
}

.designer-inspector .field-with-suffix input {
  flex: 1 1 auto;
  min-width: 0;
}

.inspector-icon-btn {
  flex: 0 0 auto;
  width: 34px;
  min-height: 34px;
  padding: 0;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-text-muted);
  line-height: 1;
  cursor: pointer;
}

.inspector-icon-btn:hover {
  color: var(--color-text);
  border-color: var(--color-primary);
}

/* "Duplica" ed "Elimina" viaggiano insieme all'estremo destro: sono le due
   azioni della riga, non due campi in coda ai campi. */
.inspector-actions {
  margin-left: auto;
  display: flex;
  align-items: flex-end;
  gap: var(--space-2);
}

.designer-inspector .btn-secondary,
.designer-inspector .btn-danger {
  width: auto;
  min-height: 34px;
  padding: 0 var(--space-3);
  font: var(--type-button-sm);
  background: var(--color-surface);
}

/* "Elimina" all'estremo destro: è l'azione irreversibile della riga, non la
   quarta di quattro nella stessa fila di campi. Dentro `.inspector-actions`
   il margine automatico ce l'ha già il gruppo. */
.designer-inspector .btn-danger {
  margin-left: auto;
}

.designer-inspector .inspector-actions .btn-danger {
  margin-left: 0;
}

.designer-inspector .btn-danger:hover {
  background: var(--color-danger-bg, var(--color-bg));
  color: var(--color-danger-text);
}

/* A bordo pista si usa il tablet, e un chip da 30px non si prende col dito.
   Con un mouse invece 30px sono comodi e la barra resta sottile: la soglia
   è il tipo di puntatore, non la larghezza della finestra (un tablet in
   orizzontale è largo come un portatile). */
@media (pointer: coarse) {
  .palette-btn {
    min-width: 4rem;
    padding: var(--space-2) 8px;
  }

  .palette-btn-wide {
    width: 6rem;
  }

  .tool-icon {
    height: 28px;
  }

  .tool-label {
    font: var(--type-eyebrow);
    letter-spacing: var(--type-eyebrow-ls);
  }

  .designer-icon-btn {
    min-height: 44px;
    width: 44px;
  }

  .designer-save-form button {
    min-height: 44px;
  }

  .designer-inspector input,
  .designer-inspector select,
  .designer-inspector .btn-secondary,
  .designer-inspector .btn-danger {
    min-height: 44px;
  }

  /* Una pastiglia da 22px non si prende col dito: a bordo pista si sceglie
     un colore col pollice, e sette pastiglie da 30px stanno comunque in una
     riga sola. */
  .tint-chip {
    width: 30px;
    height: 30px;
    min-height: 0;
  }

  .tint-chips {
    min-height: 44px;
  }

  .weight-chip {
    width: 44px;
    height: 30px;
    min-height: 0;
  }

  .inspector-icon-btn {
    width: 44px;
    min-height: 44px;
  }
}

/* La pista prende l'altezza che resta, invece di una larghezza calcolata a
   mano: `main` alta quanto la finestra, e il canvas è l'unica riga elastica
   fra la barra strumenti e l'ispettore. Così la pista è sempre la più grande
   che ci sta — senza numeri magici da riaggiustare quando la barra strumenti
   passa da una riga a due.
   `preserveAspectRatio` sull'SVG tiene le proporzioni FSIH e centra la pista
   nella larghezza in eccesso: la scatola è larga tutto, la pista no.
   Solo da 1024px (dove l'header orizzontale ha già ceduto il posto alla
   sidebar) e solo su finestre abbastanza alte: sotto, la pista torna larga
   quanto lo schermo e la pagina scorre. */
@media (min-width: 1024px) and (min-height: 620px) {
  .designer-page {
    height: 100dvh;
    display: flex;
    flex-direction: column;
    /* Se il contenuto non ci sta comunque (zoom del browser, testo grande)
       si scorre dentro `main`: meglio scorrere che tagliare. */
    overflow: auto;
  }

  /* (v1.51) La colonna della pista si stringe fin dove il suo contenuto ha
     un minimo — barra strumenti, pavimento del canvas, ispettore,
     didascalia — e non oltre: sotto quel punto scorre `main`, come dice il
     commento qui sopra. Prima poteva stringersi a zero, e non si vedeva
     perché non c'era niente sotto di lei; con la striscia delle immagini in
     fondo, su una finestra bassa la barra strumenti le finiva sopra. */
  .designer-page .designer-layout {
    flex: 1 1 auto;
    min-height: min-content;
  }

  .designer-page .designer-stage {
    flex: 1 1 auto;
    min-height: 0;
  }

  .designer-page .designer-canvas {
    flex: 1 1 auto;
    min-height: 14rem;
  }

  .designer-page .designer-svg {
    width: 100%;
    height: 100%;
  }

  /* (v1.51, v1.52) Qui la pagina è alta quanto la finestra e la pista è
     l'unica riga elastica: senza un tetto, tre foto e uno svolgimento di
     sei passi se la mangerebbero. Con il tetto la pista cede qualcosa e
     resta la cosa grande, e il pavimento del canvas qui sopra (14rem) non
     viene toccato. Dentro la fascia si scorre — le immagini in
     orizzontale, la descrizione in verticale — perché è un promemoria da
     consultare, non da leggere tutto insieme. */
  .designer-page .designer-reference {
    --designer-reference-max-height: 20vh;
    flex: 0 0 auto;
    flex-wrap: nowrap;
    max-height: var(--designer-reference-max-height);
  }

  /* `nowrap` anche qui: una striscia che va a capo ricomincerebbe a
     crescere in altezza. */
  .designer-page .designer-images {
    flex: 0 1 auto;
    flex-wrap: nowrap;
    overflow-x: auto;
    max-height: var(--designer-reference-max-height);
  }

  .designer-page .designer-images .imported-image {
    flex: 0 0 auto;
    max-height: var(--designer-reference-max-height);
  }

  .designer-page .designer-images img {
    max-height: var(--designer-reference-max-height);
    width: auto;
  }

  /* Chi cede spazio, quando la fascia è stretta: le immagini, che hanno il
     loro scorrimento orizzontale. La descrizione resta larga almeno una
     colonna di testo — sotto, non è più una descrizione ma una parola per
     riga. */
  .designer-page .designer-brief {
    min-width: 16rem;
    max-height: var(--designer-reference-max-height);
    overflow-y: auto;
  }
}

/* Sotto i 700px la barra strumenti diventa una colonna di gruppi: i chip
   affiancati a un'etichetta verticale su un telefono si spezzano in una
   riga per chip. */
@media (max-width: 700px) {
  .tool-group {
    flex-direction: column;
    align-items: flex-start;
    flex-basis: 100%;
  }

  .tool-group + .tool-group {
    border-left: 0;
    padding-left: 0;
    border-top: 1px solid var(--color-border);
    padding-top: var(--space-2);
  }

  .designer-help {
    margin-left: 0;
  }

  .designer-inspector label {
    width: 100%;
  }
}

.btn-danger {
  border-color: var(--color-danger-border);
  color: var(--color-danger-text);
}

/* La scheda mostra il diagramma renderizzato lato server: nessun JS, si
   stampa. */
.rink-diagram {
  display: block;
  width: 100%;
  height: auto;
  margin: var(--space-2) 0;
}

/* Il tetto di larghezza vale **sulla scheda**, dove il diagramma sta dentro
   una card di testo e dilatarlo renderebbe illeggibili i numeri dei
   giocatori. (v1.19) Prima era su `.rink-diagram` e quindi colpiva anche il
   canvas del Designer, che condivide la classe: l'editor si ritrovava una
   pista da 34rem in una colonna larga il doppio, con metà dello spazio
   sprecato proprio dove serve precisione per trascinare. */
.exercise-card .rink-diagram {
  max-width: 34rem;
}

/* (v1.53) Il disegno sulla scheda di modifica, sotto il bottone che porta
   al Drill Designer. Stesso tetto della card, e per la stessa ragione: qui
   la colonna è larga 76rem (`app-main-roomy`), e una pista larga tutto
   rende illeggibili i numeri dei giocatori. */
.exercise-diagram-preview {
  margin: var(--space-3) 0;
}

.exercise-diagram-preview .rink-diagram {
  max-width: 34rem;
}

.exercise-diagram-preview figcaption {
  font: var(--type-caption);
  color: var(--color-text-muted);
  margin: 0;
  max-width: 34rem;
}

/* (v1.65, issue #57) La fase di apprendimento sulla scheda di un esercizio,
   una riga per squadra. Tendina e «Salva» restano accanto: sono un gesto
   solo, e `select` — largo il 100% in tutta l'app — li mandava ciascuno su
   una riga sua, tre righe alte per tre squadre. */
.learning-phases {
  margin-bottom: var(--space-4);
}

.learning-phases .entity-actions {
  flex-wrap: nowrap;
  align-items: center;
}

.learning-phases select {
  width: auto;
}

/* (v1.24) A sparire sotto i 1024px è **il grafico**, non la card.
   Prima spariva tutta, e con essa il riepilogo, la vista tabella e il
   commutatore: da telefono la stessa stagione in cifre — la sola forma che
   lì si legge — non era raggiungibile per niente. Ora la card resta, l'asse
   annuale no, e al suo posto c'è una riga che dice dove sono i numeri. */
.timeline-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  margin-bottom: var(--space-4);
}
.timeline-chart {
  display: none;
}
.timeline-narrow-note {
  margin: 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}
@media (min-width: 1024px) {
  .timeline-chart {
    display: block;
  }
  .timeline-narrow-note {
    display: none;
  }
}

/* ==========================================================================
   Schermo intero sul telefono (issue #126, `partials/fullscreen_layer.html`)

   Uno solo, condiviso: chi lo include non scrive regole sue, salvo riaccendere
   qui dentro il contenuto che sotto 1024px nasconde. Il livello è aperto
   quando è il `:target` dell'indirizzo, e tutte le regole di apertura stanno
   in un `@media screen` sotto 1024px: da lì in su e in stampa il livello è un
   `<div>` qualunque, e la pagina resta quella di prima. Fuori da quel
   `@media` il pulsante non c'è, e barra e invito nemmeno.
   ========================================================================== */
.fullscreen-open {
  display: none;
}
.fullscreen-layer-bar,
.fullscreen-layer-hint {
  display: none;
}
/* Il focus ci arriva dallo script o dall'ancora, mai dal Tab: il contorno
   attorno a tutto il contenuto non indica niente da premere. */
.fullscreen-layer:focus {
  outline: none;
}

@media screen and (max-width: 1023.98px) {
  .fullscreen-open {
    display: inline-flex;
    gap: var(--space-1);
    margin-top: var(--space-2);
  }

  /* Copre tutto, barra in basso e intestazione comprese: sopra il velo e il
     foglio del contesto (40 e 41), che a loro volta stanno sopra la barra.
     Gli inset sui quattro lati, perché in orizzontale il notch sta di lato
     e la linea dell'iPhone in basso. Nessun antenato ha `transform` o
     `filter` (vedi la shell), ed è ciò che misura `fixed` sullo schermo. */
  .fullscreen-layer:target {
    position: fixed;
    inset: 0;
    z-index: 50;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding:
      max(var(--space-2), env(safe-area-inset-top, 0px))
      max(var(--space-3), env(safe-area-inset-right, 0px))
      max(var(--space-2), env(safe-area-inset-bottom, 0px))
      max(var(--space-3), env(safe-area-inset-left, 0px));
    background: var(--color-surface);
  }
  .fullscreen-layer:target .fullscreen-layer-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
  }
  .fullscreen-layer-title {
    margin: 0;
    font: var(--type-title);
  }
  /* A scorrere è il livello, mai la pagina: in orizzontale il contenuto
     largo scorre nel suo contenitore, qui dentro. */
  .fullscreen-layer:target .fullscreen-layer-body {
    flex: 1;
    min-height: 0;
    overflow: auto;
    overscroll-behavior: contain;
  }
  html:has(.fullscreen-layer:target) {
    overflow: hidden;
  }

  /* I contenuti che sotto 1024px si nascondono, riaccesi nel livello. */
  .fullscreen-layer:target .timeline-chart {
    display: block;
  }
  /* (issue #127) Età × livello: riempie il livello, e il `viewBox` lo
     centra nella misura che manca — in orizzontale l'altezza. I tetti di
     larghezza del cruscotto qui non valgono. */
  .dash-scatter .fullscreen-layer:target .squad-scatter {
    display: flex;
    height: 100%;
  }
  .dash-scatter .fullscreen-layer:target .squad-scatter svg {
    width: 100%;
    height: 100%;
    max-width: none;
  }

  /* Un invito, mai un blocco: in verticale il contenuto c'è lo stesso. */
  @media (orientation: portrait) {
    .fullscreen-layer:target .fullscreen-layer-hint {
      display: block;
      margin: 0;
      font: var(--type-caption);
      color: var(--color-text-muted);
    }
  }
}

.timeline-card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}
.timeline-card-head h2 {
  margin: 0;
}
.timeline-card-sub {
  font: var(--type-caption);
  color: var(--color-text-muted);
  margin: 0;
}
/* (v1.24) Legenda: senza, "banda tenue" e "tacca satura" sono due forme che
   il lettore deve indovinare. Le tre voci sono le sole tre cose che la riga
   di un tema può dire, e la tinta della famiglia usata per gli esempi è
   arbitraria — serve solo a mostrare che il colore c'è. */
/* (v1.76) Sotto il grafico e non più in testa, come nel mockup approvato:
   due righe, prima le famiglie — che senza più un'intestazione per
   famiglia si imparano solo qui — poi le forme. Il filetto sopra la separa
   dall'ultima riga del grafico. */
.timeline-legend {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  font: var(--type-caption);
  color: var(--color-text-muted);
}
.timeline-legend-line {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
}
.timeline-legend-item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}
.timeline-legend-swatch {
  width: 14px;
  height: 10px;
  border-radius: 2px;
  background: var(--fam, var(--color-text-muted));
}
.timeline-legend-band {
  width: 26px;
  height: 10px;
  border-radius: 2px;
  background: var(--timeline-band-1);
  background: color-mix(in srgb, var(--fam) 42%, transparent);
}
.timeline-legend-band.is-maintenance {
  background: color-mix(in srgb, var(--fam) 18%, transparent);
}
/* Lo stesso tratteggio di `.timeline-segment.is-rest`, in piccolo. */
.timeline-legend-rest {
  width: 26px;
  height: 10px;
  border-radius: 2px;
  background: repeating-linear-gradient(
    45deg,
    var(--timeline-band-2),
    var(--timeline-band-2) 3px,
    var(--color-surface) 3px,
    var(--color-surface) 6px
  );
  box-shadow: inset 0 0 0 1px var(--timeline-line);
}
.timeline-legend-tick {
  width: 2px;
  height: 12px;
  border-radius: 2px;
  background: var(--fam, var(--timeline-neutral-mark));
}
.timeline-legend-tick.is-out {
  background: var(--timeline-neutral-mark);
}

/* La commutazione timeline/tabella: due link, non due bottoni. Lo stato
   attivo è `aria-current="page"`, che è anche l'attributo che lo dice a chi
   non vede lo sfondo cambiare. */
.timeline-toggle {
  display: inline-flex;
  gap: 2px;
  padding: 2px;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
}
.timeline-toggle a {
  padding: 4px 12px;
  border-radius: 6px;
  font: var(--type-button-sm);
  color: var(--color-text-muted);
  text-decoration: none;
}
.timeline-toggle a[aria-current="page"] {
  background: var(--color-surface);
  color: var(--color-text);
  box-shadow: var(--shadow-sm);
}
/* Come `.season-nav`, di cui è la sorella: col dito 44px, non 22. */
@media (pointer: coarse) {
  .timeline-toggle a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
}

/* Il contenuto scorre nel suo contenitore, mai la pagina (requisito §7). Un
   asse annuale sotto una certa larghezza non è compresso, è illeggibile:
   meglio farlo scorrere che spalmare dodici mesi su 600px. */
.timeline-scroller {
  overflow-x: auto;
}
.timeline-grid {
  min-width: 52rem;
}

/* Scala dei mesi: solo etichette, nessuna griglia verticale — meno rumore,
   e le etichette da sole bastano a orientarsi su un asse di 365 giorni. */
/* (v1.19) La scala vive dentro una `.timeline-row` come tutte le altre
   righe, con la cella etichetta vuota: così la sua origine è **la stessa**
   colonna delle tracce, per costruzione. Prima era un blocco a sé con un
   `margin-left: 160px` scritto a mano che replicava la larghezza della
   colonna etichetta — e quando da 1600px quella colonna è passata a 12rem,
   i mesi si sono staccati dalle tracce di 48px, sbagliando di quasi
   un'etichetta intera a dicembre. Un valore duplicato invece di una
   geometria condivisa: è durato fino alla prima prova in Chrome. */
.timeline-scale {
  position: relative;
  height: 1.1rem;
  /* Nessun margine: l'allineamento con le tracce lo dà la griglia della
     riga che la contiene, non un valore ricopiato a mano. */
  margin: 0;
}
.timeline-scale span {
  position: absolute;
  top: 0;
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
  transform: translateX(-1px);
  white-space: nowrap;
}

/* (v1.24) Tre colonne e non due: a destra di ogni riga di tema c'è il
   conteggio delle sedute che l'hanno toccato. La colonna vuota sulle altre
   righe non è uno spreco — è ciò che tiene le tracce allineate per
   costruzione, la stessa lezione della scala dei mesi (v1.19): due geometrie
   scritte a mano si separano al primo cambio di larghezza. */
/* (v1.76) 10,5rem e non più 9: tolte le intestazioni di famiglia, il nome
   del tema porta il suo pallino, e «Conduzione e controllo» ci sta intero.
   `MIN_BAND_WIDTH_FOR_PERCENT` in `routes/seasons.rs` è calcolata su questa
   colonna: cambiandola, va rifatto il conto scritto lì. */
.timeline-row {
  display: grid;
  grid-template-columns: 10.5rem 1fr 3.25rem;
  align-items: center;
  gap: var(--space-3);
  min-height: 2rem;
}

/* Il troncamento viene da `.t-clip`, in testa al foglio: qui resta solo ciò
   che è proprio di questa riga. Il nome intero è nel `title`, che il
   template mette da `full_label`. */
.timeline-row-label {
  font: var(--type-caption);
  color: var(--color-text);
}

.timeline-track {
  position: relative;
  height: 10px;
  background: var(--timeline-lane);
  border-radius: var(--radius-pill);
}

/* La fascia dei blocchi è più alta e porta il nome dentro al segmento
   stesso — è la riga che orienta tutte le altre. */
/* (v1.76) 2,375rem (38px, la misura del mockup approvato) e non più 2rem:
   è la riga che orienta tutte le altre, e il nome del blocco deve starci
   con aria. */
.timeline-periods-row .timeline-track {
  height: 2.375rem;
  background: var(--color-bg);
  border-radius: 4px;
}
/* Spazio sotto per l'etichetta "oggi", che sta **sotto** la fascia e non
   sopra: sopra coprirebbe l'etichetta del mese (criterio §8.11). */
.timeline-periods-row {
  margin-bottom: 1.1rem;
}

.timeline-segment {
  position: absolute;
  top: 0;
  bottom: 0;
  border-radius: var(--radius-pill);
  overflow: hidden;
}
/* (v1.24, superato il 23.09.2026: ora un tono smorzato per blocco, vedi
   `.tone-1`…) Bande neutre in due grigi alternati. Il colore appartiene ai temi:
   ridirlo qui lo spenderebbe due volte sulla stessa informazione — il blocco
   è già dato dalla posizione e dal nome scritto dentro — e toglierebbe
   identità al tema, che diventerebbe verde a gennaio e blu a luglio.
   L'alternanza risolve per costruzione un difetto della vista precedente:
   due blocchi adiacenti con la stessa tinta si leggevano come uno solo. */
/* (v1.76) `display: block` con `line-height` pari alla fascia, e non più
   flex: `text-overflow` non agisce sul testo diretto di un contenitore
   flex, e i nomi lunghi finivano tagliati a metà lettera («ATLET») invece
   che con i puntini. Centrati come nel mockup; quando non ci stanno,
   `text-overflow` li riallinea da sé a sinistra. Il bordo è del colore
   della card: fra due blocchi resta un filo chiaro, e l'alternanza dei due
   grigi fa il resto. */
.timeline-periods-row .timeline-segment {
  border-radius: 4px;
  display: block;
  text-align: center;
  padding: 0 var(--space-2);
  background: var(--timeline-band-1);
  border: 1px solid var(--color-surface);
  border-width: 1px 2px;
  color: var(--color-text-muted);
  font: var(--type-eyebrow);
  /* Dopo `font`, che lo shorthand riporterebbe a 1,2. */
  line-height: calc(2.375rem - 2px);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  text-decoration: none;
}
/* (23.09.2026) Un tono per blocco, e il nome in tondo e non in
   maiuscoletto: in maiuscolo «PREPARAZIONE AT…» si troncava prima. */
.timeline-periods-row .timeline-segment[class*="tone-"] {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 var(--space-1);
  font: 600 0.8125rem/1.1 var(--font-cond);
  letter-spacing: 0;
  text-transform: none;
  white-space: normal;
}

/* Il nome va a capo una volta, e solo dopo si tronca: «Preparazione
   atletica» ci sta su due righe dove su una si fermava a «Preparazione
   atleti…». */
.timeline-periods-row .timeline-segment[class*="tone-"] .timeline-segment-name {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  overflow-wrap: anywhere;
}
.timeline-periods-row .timeline-segment.tone-1 { background: var(--block-tone-1); color: var(--block-tone-1-ink); }
.timeline-periods-row .timeline-segment.tone-2 { background: var(--block-tone-2); color: var(--block-tone-2-ink); }
.timeline-periods-row .timeline-segment.tone-3 { background: var(--block-tone-3); color: var(--block-tone-3-ink); }
.timeline-periods-row .timeline-segment.tone-4 { background: var(--block-tone-4); color: var(--block-tone-4-ink); }
.timeline-periods-row .timeline-segment.tone-5 { background: var(--block-tone-5); color: var(--block-tone-5-ink); }

/* (v1.34) Un blocco di pausa non è un blocco di lavoro più chiaro: è
   un'altra cosa. Tratteggio obliquo tenue — la stessa grammatica del
   `.gap` qui sotto, che segnala «nessun blocco», ma in grigio invece che in
   rosso: un buco di pianificazione è un difetto, una pausa è una decisione.
   Fino alla v1.33 «Pausa estiva» era una banda identica a «Fase 1», e sei
   settimane di riposo si leggevano a colpo d'occhio come sei settimane di
   lavoro. */
.timeline-periods-row .timeline-segment.is-rest {
  background: repeating-linear-gradient(
    45deg,
    var(--timeline-band-2),
    var(--timeline-band-2) 5px,
    var(--color-surface) 5px,
    var(--color-surface) 10px
  );
  color: var(--color-text-muted);
  font-style: italic;
}

/* La preparazione atletica resta una banda piena — lì si allena — con un
   filetto sul bordo sinistro a dire che il ritmo è un altro. Non un colore
   proprio: il colore in questa vista appartiene ai temi (§10bis.1), e
   spenderlo qui lo toglierebbe a loro. */
.timeline-periods-row .timeline-segment.is-athletic {
  border-left: 3px solid var(--color-text-muted);
}
a.timeline-segment:hover,
a.timeline-segment:focus-visible {
  filter: brightness(1.08);
  text-decoration: none;
  outline: 2px solid var(--color-text);
  outline-offset: -2px;
}
/* Un periodo senza blocco resta segnalato: è un buco di pianificazione, non
   un blocco come gli altri.
   (v1.76) Ma non in rosso. Il rosso della timeline è degli allarmi — il
   conteggio di un tema mai allenato, l'allenamento passato senza scheda —
   e dieci giorni di gennaio prima della preparazione non lo sono: in rosso
   attiravano l'occhio più di tutti i dati sotto. Righe sottili su fondo
   chiaro, con il bordo tratteggiato: si legge «vuoto», e non si confonde
   con la pausa (`.is-rest`), che è una fascia piena a strisce. */
.timeline-segment.gap {
  background: repeating-linear-gradient(
    45deg,
    var(--color-surface),
    var(--color-surface) 4px,
    var(--timeline-line) 4px,
    var(--timeline-line) 5px
  );
}
.timeline-periods-row .timeline-segment.gap {
  color: var(--color-text-muted);
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  border: 1px dashed var(--timeline-neutral-mark);
}

/* --- (v1.24) Righe dei temi: banda, corsia, tacche ---------------------- */

/* (v1.76) Il contenitore di famiglia non ha più una riga d'intestazione né
   un margine: porta `--fam` alle sue righe e basta. Le righe dei temi si
   toccano, così i confini dei blocchi e il filo di «oggi» scendono continui
   dal primo tema all'ultimo invece di spezzarsi a ogni riga. */
.timeline-family {
  margin: 0;
}
/* Il pallino accanto al nome del tema, come nel mockup approvato. Non è
   l'unico identificatore della riga — c'è il nome, e la legenda sotto il
   grafico dice di che famiglia è ogni tinta. */
.timeline-family-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  margin-right: 8px;
  vertical-align: 1px;
  border-radius: 2px;
  background: var(--fam, var(--color-text-muted));
}

/* (23.09.2026) 1,25rem e non più 1,625: la tabella più compatta, su
   richiesta. Banda e cella perdono 2px sopra e 2px sotto, la riga il resto. */
.timeline-theme-row {
  min-height: 1.25rem;
}
.timeline-theme-row .timeline-track {
  height: 1.25rem;
  background: none;
  border-radius: 0;
}

/* Il numero delle partite, in occhiello e in inchiostro secondario: sta
   sotto il nome, allineato al testo e non al pallino. Nella tabella è la
   stessa riga, sotto il nome del tema. */
.timeline-row-aside {
  display: block;
  padding-left: 16px;
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.timeline-table .timeline-row-aside {
  padding-left: calc(8px + var(--space-2));
}

/* (23.09.2026) Le righe dei temi non hanno più la corsia: un filo grigio
   per tutto l'anno sotto ogni tema, dodici linee che facevano rumore. La
   riga si legge per allineamento col suo nome. */
.timeline-theme-row .timeline-track {
  background: none;
}

/* (23.09.2026) La cella di un blocco, sotto le bande: grigio chiarissimo,
   con lo stesso stacco bianco e la stessa altezza della banda. Dove il tema
   c'è la banda la copre, dove non c'è resta la cella — la riga si legge per
   blocchi, come nel mockup. */
.timeline-cell {
  position: absolute;
  top: 2px;
  bottom: 2px;
  border-radius: 3px;
  background: var(--timeline-cell);
  border-left: 2px solid var(--color-surface);
  border-right: 2px solid var(--color-surface);
  background-clip: padding-box;
}

/* La banda: "il tema era pianificato qui". Tenue, perché è un'intenzione e
   non un fatto — i fatti sono le tacche sopra. La prima dichiarazione di
   `background` è il ripiego per i browser senza `color-mix`: un grigio
   invece di niente. */
/* (v1.76) Più piena: 42% della tinta invece del 17%, e senza bordo, verso
   il mockup approvato (52%). Al 17% una stagione intera di bande si leggeva
   come un velo, e la differenza fra tema in focus e mantenimento era
   affidata a un bordo tratteggiato. Non il 52% del mockup, però: il mockup
   non aveva tacche, e la tacca — la tinta pura con l'alone della card —
   deve restare il fatto più saturo della riga. */
.timeline-band {
  position: absolute;
  top: 2px;
  bottom: 2px;
  border-radius: 3px;
  display: block;
  /* La quota dentro la banda non deve mai sbordare sulla banda vicina:
     quello che non ci sta si taglia qui, e il template smette comunque di
     scriverla sotto il 4% di larghezza. */
  overflow: hidden;
  background: var(--timeline-band-1);
  /* (23.09.2026) Mescolata al colore della card e non al trasparente: la
     cella grigia sotto la ingrigirebbe. */
  background: color-mix(in srgb, var(--fam) 42%, var(--color-surface));
  /* (23.09.2026) Al posto delle linee verticali ai confini dei blocchi, uno
     stacco bianco: due bande vicine non si toccano, e il blocco si legge
     dal vuoto fra l'una e l'altra. */
  border-left: 2px solid var(--color-surface);
  border-right: 2px solid var(--color-surface);
  background-clip: padding-box;
}

/* (v1.33) La quota scritta dentro la banda. Piena contro tenue distingue
   soltanto «sopra» da «sotto il 10%»: un tema al 30% e uno al 10% avevano
   la stessa banda, e la differenza — che è la decisione di quanto quel tema
   pesa nel blocco — si leggeva solo nel `title` (cioè col mouse) o nella
   vista tabella.

   Il colore non è il colore della famiglia ma la famiglia **tirata verso il
   testo**: la tinta pura su una banda al 17% della stessa tinta è la stessa
   cosa scritta due volte, e sotto 3:1 di contrasto per tre famiglie su sette
   (§10bis.1). Mescolata al colore del testo resta riconoscibile come tinta e
   diventa leggibile come cifra.

   `aria-hidden` nel template: la percentuale è già dentro l'`aria-label`
   della banda insieme al tema e alle date, e annunciarla due volte di fila
   darebbe «20% Pattinaggio 20% dei minuti del blocco». */
.timeline-band-percent {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font: var(--type-eyebrow);
  font-variant-numeric: tabular-nums;
  color: var(--color-text-muted);
  /* (v1.76) 30% e non più 45% di tinta: sulla banda più piena la cifra
     deve scurirsi con lei, o sulle famiglie chiare (giallo, rosa) sparisce. */
  color: color-mix(in srgb, var(--fam) 30%, var(--color-text));
  white-space: nowrap;
}
/* Sulla banda di mantenimento la cifra segue la banda: tenue come lei.
   Scriverla con lo stesso peso della banda piena renderebbe più forte
   proprio il tema che pesa meno. */
.timeline-band.is-maintenance .timeline-band-percent {
  color: var(--color-text-muted);
}
/* (v1.29) La banda tenue: il tema pesa fra il 5% e il 10% dei minuti del
   blocco — tenuto in vita, non allenato. Il grado è deciso nel dominio
   (`planning::THEME_FOCUS_MIN_PERCENT`), non qui: il CSS lo mostra e non lo
   stabilisce.

   Sotto il 5% non esiste una terza banda ancora più pallida: esiste
   *nessuna* banda. Una quota di quel peso vale pochi minuti su sei
   settimane, e disegnarla darebbe a colpo d'occhio la stessa impressione di
   un tema allenato davvero — cioè l'errore che questa vista esiste per
   rendere visibile. */
/* (v1.76) 18% e senza bordo, come nel mockup: a questo punto la differenza
   dalla banda piena la dice la tinta da sola, e il tratteggio non serve più
   a reggerla. */
.timeline-band.is-maintenance {
  background: var(--timeline-band-1);
  background: color-mix(in srgb, var(--fam) 18%, var(--color-surface));
}

.timeline-band:hover,
.timeline-band:focus-visible {
  background: color-mix(in srgb, var(--fam) 60%, var(--color-surface));
  outline: 2px solid var(--fam);
  outline-offset: -1px;
}

/* La tacca: una seduta svolta che ha lavorato quel tema. Satura, perché è
   l'unico fatto della riga. L'alone del colore della card la stacca dalla
   banda e dalle tacche vicine quando la stagione si infittisce. */
/* (v1.76) Alone di 2px e non più 1,5: sopra una banda al 42% della stessa
   tinta è l'alone, non il colore, a staccare la tacca. */
/* (23.09.2026) Più leggera, su richiesta: 2px e non 4, alta 12px e non 16,
   la tinta al 75% e l'alone di 1px. Resta il segno più scuro della riga,
   ma non la riempie più di sbarre quando la stagione si infittisce. */
.timeline-tick {
  position: absolute;
  top: calc(50% - 6px);
  width: 2px;
  height: 12px;
  border-radius: 1px;
  background: var(--fam, var(--color-primary));
  background: color-mix(in srgb, var(--fam, var(--color-primary)) 75%, var(--color-surface));
  box-shadow: 0 0 0 1px var(--color-surface);
  z-index: 3;
}
/* (23.09.2026) Le tacche si accendono con l'interruttore in legenda, e
   partono spente: sopra le bande facevano rumore. Solo CSS, niente
   JavaScript — la casella è un `<input>` vero, e `:has` la legge da qui. */
body:has(#timeline-ticks-toggle:not(:checked)) .timeline-tick {
  display: none;
}
/* Più specifica del `label` globale, che va in colonna e in grassetto: qui
   la casella sta in riga come le altre voci della legenda. */
label.timeline-ticks-switch {
  display: inline-flex;
  flex-direction: row;
  align-items: center;
  gap: var(--space-1);
  font: inherit;
  letter-spacing: inherit;
  cursor: pointer;
}
.timeline-ticks-switch input {
  margin: 0;
  accent-color: var(--color-primary);
}
/* Fuori dal piano: neutra. Non è un errore — spesso è il segnale che il
   piano andava scritto diversamente — quindi neutra e non d'allarme. */
.timeline-tick.is-out {
  background: var(--timeline-neutral-mark);
}
/* Tema toccato solo come secondario: mezza altezza. Il tema è stato
   sfiorato dentro esercizi che ne allenavano un altro, e una tacca piena lo
   direbbe più forte di com'è. */
.timeline-tick.is-secondary {
  top: calc(50% - 3px);
  height: 6px;
}
.timeline-tick:hover,
.timeline-tick:focus-visible {
  outline: 2px solid var(--timeline-today);
  outline-offset: 2px;
}

/* Il confine di un blocco, ripetuto su ogni riga: costa un pixel e dice
   quello che il colore delle bande diceva male. Sopra la banda e sotto le
   tacche — separa il piano senza coprire i fatti. */
/* (v1.76) Da bordo a bordo della traccia: le righe dei temi si toccano, e
   la linea ripetuta su ognuna si legge come una sola che scende
   attraverso tutte — l'effetto del mockup senza il suo livello sovrapposto
   con le coordinate scritte a mano (vedi la scala dei mesi, v1.19). Un
   grigio più scuro di prima, perché ora passa anche sopra bande piene. */


/* Il conteggio a destra della riga. In avviso quando il tema era
   pianificato e la stagione l'ha allenato due volte o meno: è il numero che
   porta a cambiare la seduta di giovedì. */
.timeline-count {
  font: var(--type-figure-cell);
  font-variant-numeric: tabular-nums;
  color: var(--color-text-muted);
  text-align: right;
  white-space: nowrap;
}
.timeline-count.is-rare {
  color: var(--color-danger-text);
  font: var(--type-figure-cell);
  font-variant-numeric: tabular-nums;
}

/* --- (v1.24) Vista tabella: gli stessi numeri, in cifre ----------------- */
.timeline-table {
  width: 100%;
  border-collapse: collapse;
  font: var(--type-body);
}
.timeline-table th,
.timeline-table td {
  text-align: left;
  padding: var(--space-2);
  border-bottom: 1px solid var(--color-border);
}
.timeline-table thead th {
  font: var(--type-thead);
  letter-spacing: var(--type-thead-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.timeline-table .num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.timeline-table .num.is-rare {
  color: var(--color-danger-text);
  font: var(--type-figure-cell);
  font-variant-numeric: tabular-nums;
}
.timeline-table-swatch {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 2px;
  margin-right: var(--space-2);
  background: var(--fam, var(--color-text-muted));
}

.timeline-note {
  margin: var(--space-3) 0 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
  max-width: 80ch;
}

/* Indicatore "oggi": una linea sottile ripetuta su ogni riga della
   timeline, non un'unica linea continua sovrapposta a tutte — vedi README,
   "dove il risultato è più povero del riferimento".

   (v1.24) In inchiostro e non in rosso: il rosso è riservato agli stati di
   allarme, e oggi non è un allarme. Nella versione precedente attirava
   l'occhio più dei dati che deve solo situare. Un filo di trasparenza per
   restare dietro alle tacche. */
/* (v1.76) Opacità 0,8 e non più 0,5: a metà si perdeva fra i confini dei
   blocchi, che ora sono più scuri. Resta sotto il pieno per non competere
   con le tacche. Sulle righe dei temi va da bordo a bordo, come i confini:
   sovrapposta alla riga vicina, la trasparenza farebbe un nodo scuro a
   ogni giunta. */
.timeline-today-tick {
  position: absolute;
  top: -4px;
  bottom: -4px;
  width: 1px;
  background: var(--timeline-today);
  opacity: 0.8;
  z-index: 2;
}
.timeline-theme-row .timeline-today-tick {
  top: 0;
  bottom: 0;
}
a.timeline-today-tick {
  display: block;
}
/* Sotto la fascia dei blocchi, non sopra: sopra c'è la scala dei mesi, e
   l'etichetta la coprirebbe (criterio §8.11). */
.timeline-today-label {
  position: absolute;
  top: calc(100% + 3px);
  transform: translateX(-50%);
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  /* (v1.76) Inchiostro e non grigio: è l'unica parola sul filo, e in
     grigio chiaro non la si trovava. Mai rosso (§3.4). */
  color: var(--timeline-today);
  background: var(--color-surface);
  padding: 0 4px;
  white-space: nowrap;
  text-decoration: none;
}

.timeline-group {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.timeline-group-title {
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin: 0 0 var(--space-1) 0;
}

/* Marker puntiformi (tornei, camp, pause): un pallino, non una barra — sono
   eventi di un giorno, non intervalli. La partita ha il suo segno (v1.89,
   qui sotto). */
.timeline-marker {
  position: absolute;
  top: 50%;
  width: 9px;
  height: 9px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  border: 2px solid var(--color-surface);
}
.timeline-marker.marker-tournament { background: var(--color-warning-border); }

/* (v1.89, issue #74) La partita non è un pallino: è il segno di
   `match_glyph`, una casetta in casa, una freccia che esce in trasferta, un
   pallino senza lato — la forma lo dice, senza legenda. D'inchiostro e non
   di una tinta: il colore della timeline è dei temi, e il segno si
   distingue dalle tacche per forma. Il contenitore perde fondo, bordo e
   tondo, e resta solo a posizionare. */
.timeline-marker.marker-match {
  width: 14px;
  height: 14px;
  border: 0;
  border-radius: 0;
  background: none;
  color: var(--color-text);
  line-height: 0;
}
.partita-glifo {
  display: block;
  overflow: visible;
}
/* L'alone del colore della card, sotto il riempimento (`paint-order`):
   stacca il segno dai confini di blocco e dal filo di «oggi», come il
   bordo bianco stacca i pallini. */
.partita-segno {
  fill: currentColor;
  stroke: var(--color-surface);
  stroke-width: 1.5;
  paint-order: stroke;
}
/* L'annullata: la stessa forma, attenuata, e barrata. La barra ha il suo
   alone per staccarsi dalla forma che attraversa. */
.partita-glifo.is-annullata .partita-segno {
  opacity: 0.45;
}
.partita-barra-alone {
  stroke: var(--color-surface);
  stroke-width: 3.5;
  stroke-linecap: round;
}
.partita-barra {
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
}

/* (v1.91, issue #76) La partita *da verificare*: il calendario federativo
   non la porta più. La stessa forma di `match_glyph` — casa o trasferta si
   leggono ancora —, ma **vuota** e col contorno tratteggiato: c'è, e non è
   sicura. Il riempimento è il colore della card, così il segno resta
   leggibile sopra un confine di blocco; nessuna tinta, come per le altre. */
.partita-glifo.is-da-verificare .partita-segno {
  fill: var(--color-surface);
  stroke: currentColor;
  stroke-width: 1.3;
  stroke-dasharray: 2 1.4;
  paint-order: normal;
}

/* (v1.16, correzione rispetto al mockup) Il mockup disegnava camp e pause
   come BARRE, assumendo che fossero intervalli. Nel dominio non lo sono:
   `CalendarEntry` porta una `date` singola, quindi un camp di una settimana
   sono sette voci distinte (o una serie ricorrente), non un intervallo.
   Disegnarli come barre avrebbe richiesto di fondere voci consecutive —
   un'inferenza che il dato non autorizza: due camp in giorni contigui non
   sono lo stesso camp. Restano pallini, come le partite. */
.timeline-marker.marker-camp { background: var(--color-camp-text); }
.timeline-marker.marker-break { background: var(--color-text-muted); }


/* --- Allenamenti: pianificato contro svolto (v1.16, 23.09.2026) --------
   Una riga sola: ogni allenamento è un pallino, e il pallino dice com'è
   andata. Fino al 23.09.2026 erano due righe da confrontare a occhio,
   pianificati sopra e svolti sotto; il conto a destra dice ora «svolti su
   pianificati».

   Lo stato è quello che conta davvero: un allenamento già passato di
   cui non risulta nessuna scheda salvata. Non è "non ancora fatto" — è un
   buco. Colore d'allarme e pallino vuoto, così si distingue a colpo d'occhio
   da un allenamento futuro, che è vuoto ma neutro. */
.timeline-marker {
  background: var(--color-border);
}

/* Un allenamento da fare è un cerchio vuoto: pieno di grigio si perdeva
   sulla traccia grigia. Solo gli allenamenti, che non portano `marker-*`. */
.timeline-marker:not([class*="marker-"]) {
  background: var(--color-surface);
  border-color: var(--timeline-neutral-mark);
}

/* (23.09.2026) Nella riga «Allenamenti» un pallino per allenamento, e più
   piccolo: a due allenamenti a settimana i pallini da 9px si toccavano. */
.timeline-marker:not(.marker-match) {
  width: 8px;
  height: 8px;
  border-width: 1.5px;
}

.timeline-legend-marker.timeline-marker {
  position: relative;
  top: auto;
  display: inline-block;
  transform: none;
}

.timeline-marker.is-done {
  background: var(--color-primary);
  border-color: var(--color-primary);
}

.timeline-marker.is-missed {
  background: var(--color-surface);
  border-color: var(--color-danger-border);
}

/* (v1.21) Il terzo stato nato con la generazione guidata dal piano: un
   allenamento ancora da svolgere che ha già la sua scheda. Non è pieno come
   uno svolto — non è successo niente, ancora — e non è vuoto come uno
   scoperto: contorno d'accento su fondo chiaro, cioè "pronto". */
.timeline-marker.is-prepared {
  background: var(--color-primary-soft);
  border-color: var(--color-primary);
}

/* ==========================================================================
   Vista stagione — impaginazione da laptop (v1.17)

   La pagina non ha più il tetto di 40rem (`app-main-wide` su `<main>`): era
   la colonna di lettura pensata per la prosa, e teneva la timeline — che è
   un asse annuale — schiacciata in meno di metà dello schermo di un laptop.
   ========================================================================== */

/* Senza tetto di pagina, tre stat card in `repeat(3, 1fr)` diventano tre
   riquadri da 600px con dentro una parola: "usare tutto lo spazio" non vuol
   dire stirare ogni componente fino ai bordi. Le card restano di una misura
   sensata e la riga parte da sinistra, allineata alla timeline sotto.
   Scoped a `.app-main-wide` per non toccare il catalogo, che ha un tetto
   suo (`app-main-roomy`, 76rem) dove `repeat(3, 1fr)` va già bene. */
@media (min-width: 1024px) {
  .app-main-wide .stat-card-grid {
    grid-template-columns: repeat(3, minmax(14rem, 26rem));
    justify-content: start;
  }
}

/* (v1.24) La vista stagione ha acquisito una quarta card — i temi
   pianificati e quasi mai allenati — e `repeat(3, …)` la manderebbe a capo
   da sola sotto le altre tre. `auto-fit` le dispone come stanno: quattro in
   fila su uno schermo largo, due e due su un laptop. */
.stat-card-grid-four {
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
}
/* Il tetto per card scende da 26rem a 18rem: con `auto-fit` il numero di
   colonne si calcola sulla misura **massima**, e a 26rem l'ultima card
   finiva da sola su una seconda riga su un laptop — una card orfana sotto
   tre allineate si legge come un errore di impaginazione. */
@media (min-width: 1024px) {
  .app-main-wide .stat-card-grid-four {
    grid-template-columns: repeat(auto-fit, minmax(12rem, 18rem));
  }
}

/* (19.09.2026) Tolta la lista «Mese per mese», la legenda dei blocchi è
   sola: una colonna, con il tetto di una riga leggibile. */
.season-layout {
  display: block;
  max-width: 44rem;
}

.season-legend-col > h2 {
  margin-top: 0;
}

/* La timeline occupa tutta la larghezza disponibile e ne guadagna: più
   pixel sull'asse sono più risoluzione sull'anno. Da 1600px l'etichetta di
   riga può permettersi di essere più larga senza rubare spazio all'asse. */
@media (min-width: 1600px) {
  .timeline-row {
    grid-template-columns: 12rem 1fr 3.5rem;
  }
}

/* ==========================================================================
   Elenco + form affiancati (v1.20) — calendario, dettaglio stagione.

   Due pagine con la stessa forma: "ecco cosa c'è" seguito da "ecco come
   aggiungerne". In una colonna sola su un laptop il form finiva sotto la
   piega, e per aggiungere una voce dopo aver guardato l'elenco bisognava
   scorrere fino in fondo. Affiancati, il form resta sott'occhio mentre si
   legge l'elenco — che è l'ordine in cui si lavora davvero.
   ========================================================================== */

.list-and-form {
  display: block;
}

@media (min-width: 1024px) {
  .list-and-form {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 24rem;
    gap: var(--space-4);
    align-items: start;
  }

  /* (v1.62, issue #54) In sola lettura la colonna del form non c'è: senza
     questa regola l'elenco resterebbe nella prima traccia, con 24rem
     vuoti accanto. */
  .list-and-form:not(:has(> .list-and-form-aside)) {
    grid-template-columns: minmax(0, 1fr);
  }

  .list-and-form-aside {
    position: sticky;
    top: var(--space-3);
  }

  .list-and-form-main > h2:first-child,
  .list-and-form-aside > h2:first-child {
    margin-top: 0;
  }
}

/* ==========================================================================
   Shell applicativa (v1.20) — sidebar permanente da 1024px.

   Portata dal mockup `desktop-revamp.html`, con tre differenze rispetto a
   quel documento, tutte perché lì era una dimostrazione e qui è l'app:
   niente offset per il banner/tab del mockup (`top: 0`), niente voce "Drill
   Designer" (non è una destinazione), e la topbar si nasconde quando è
   vuota invece di restare una fascia decorativa.

   Sotto 1024px c'è il marchio in cima e la barra in basso (issue #97, più
   giù). Fino ad allora restava l'header orizzontale di sempre, «senza
   cassetti da aprire»: era la scelta giusta per cinque voci, e con dodici
   l'header copriva il 41% di un iPhone.
   ========================================================================== */

.app-shell {
  display: block;
  /* (issue #97) Il posto della barra in basso, che è fissa e coprirebbe
     l'ultima riga della pagina. Sulla shell e non su `.app-main`: il
     cruscotto e le altre varianti del main riscrivono il suo `padding`. I
     lati sono dell'iPhone in orizzontale. (15.09.2026) Con la barra che
     galleggia, il posto è anche il suo stacco dal fondo
     (`--bottom-nav-space`). */
  padding-bottom: calc(var(--bottom-nav-height) + var(--bottom-nav-gap) + var(--space-3));
  padding-left: env(safe-area-inset-left, 0px);
  padding-right: env(safe-area-inset-right, 0px);
  /* (15.09.2026) Il foglio della pagina sale sull'intestazione, con gli
     angoli in alto arrotondati: fra il marchio e il contenuto non c'è una
     riga, c'è un bordo. Nessun `position`: i figli posizionati della pagina
     devono continuare a misurarsi dove si misuravano. */
  margin-top: calc(-1 * var(--shell-sheet-radius));
  border-radius: var(--shell-sheet-radius) var(--shell-sheet-radius) 0 0;
  background: var(--color-bg);
}

/* (issue #97) Lo stesso posto quando è il browser a scorrere — il focus da
   tastiera, `scrollIntoView` del cursore delle valutazioni: senza, portano
   in vista un elemento appena sotto la barra. */
html {
  scroll-padding-bottom: var(--bottom-nav-space);
}

/* In stampa l'intestazione non c'è (`no-print`), e il foglio non ha niente
   su cui salire: senza, la prima riga finirebbe sopra il bordo del foglio. */
@media print {
  .app-shell {
    margin-top: 0;
    border-radius: 0;
  }
}

/* (15.09.2026) La card del contesto sotto l'intestazione, sul foglio: con i
   margini della pagina ai lati, e il contenuto un respiro più sotto. */
.app-topbar {
  padding: var(--space-3) var(--space-3) 0;
}

.app-shell-main {
  min-width: 0;
}

.icon-sprite {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

/* ==========================================================================
   Sidebar scura (v1.23) — riferimento fornito dall'allenatore, validato.

   Telaio scuro attorno a un contenuto chiaro: la navigazione arretra e la
   pagina avanza. Regge qui per una ragione di contesto, non solo di gusto:
   **la sidebar esiste solo da 1024px in su**. Sotto restano l'header e la
   barra in basso, chiari, che sono la vista di bordo pista — dove uno schermo scuro al
   sole è un problema vero (VISION §8). Il registro scuro si applica quindi
   al solo laptop, dove si pianifica con calma.

   Token scoped su `.app-sidebar` e non in `:root`: sono i colori di un
   componente, e definirli globalmente inviterebbe il resto dell'app a
   pescarci dentro finché il "tema scuro" non sarebbe più circoscritto.
   ========================================================================== */
.app-sidebar {
  display: none;

  /* I tre toni del fondo sono campionati dal riferimento dell'allenatore,
     non stimati a occhio: in cima #303437, assestato #1e2529 all'altezza in
     cui finisce il logo, #1b2125 in fondo. */
  --sidebar-bg-top: #303437;
  --sidebar-bg: #1e2529;
  --sidebar-bg-bottom: #1b2125;
  --sidebar-border: rgba(255, 255, 255, 0.08);
  /* Contrasti verificati sui **tre** toni, non solo sul più scuro: il fondo
     ora è un gradiente, e un colore che passa in basso può non passare in
     cima. Testo 7.7-10.0:1, spento 5.2-6.7:1, accento 5.5-7.2:1. */
  --sidebar-text: #c7cbd2;
  --sidebar-muted: #a1a7b1;
  /* (19.09.2026) Più chiaro del testo spento: le etichette dei gruppi sono
     un titolo di sezione, e restano secondarie per corpo e peso, non per
     contrasto — #8b929c dà 4.9:1 sul tono più chiaro. */
  --sidebar-label: #8b929c;
  --sidebar-strong: #e6e9ed;
  /* Non `--color-primary-dark` (#0a5f67): qui darebbe 1.6:1. Stessa
     tonalità dell'accento petrolio, luminosità alzata per il fondo scuro —
     resta lo stesso colore, non un ciano nuovo. (19.09.2026) Il testo della
     voce attiva un filo più chiaro della barretta e dell'icona: sul fondo
     tinto della voce l'accento pieno scendeva sotto 5:1. */
  --sidebar-accent: #3cbccd;
  --sidebar-accent-text: #6fd6e3;
  --sidebar-active-bg: rgba(60, 188, 205, 0.13);
  --sidebar-hover-bg: rgba(255, 255, 255, 0.06);
  --sidebar-field-bg: rgba(255, 255, 255, 0.035);
  --sidebar-field-border: rgba(255, 255, 255, 0.12);
}

.app-topbar:empty {
  display: none;
}

/* (19.09.2026) Sotto 1024px della sidebar si vede solo la card del
   contesto, che le sta dentro nel markup: l'`<aside>` si scioglie
   (`display: contents`) e la card torna in cima alla pagina, dov'era. */
@media screen and (max-width: 1023.98px) {
  .app-sidebar:has(> .app-topbar:not(:empty)) {
    display: contents;
  }

  .app-sidebar > :not(.app-topbar) {
    display: none;
  }
}

@media (min-width: 1024px) {
  /* L'header orizzontale cede il posto alla sidebar: due navigazioni
     contemporanee sarebbero due modi di fare la stessa cosa. (issue #97)
     Lo stesso vale per la barra in basso — la nasconde la sua sezione, più
     giù — e il posto che le si lasciava torna alla pagina. */
  .app-header {
    display: none;
  }

  html {
    scroll-padding-bottom: 0;
  }

  .app-shell {
    display: grid;
    grid-template-columns: var(--app-sidebar-width) minmax(0, 1fr);
    align-items: start;
    min-height: 100vh;
    padding-bottom: 0;
    /* Senza l'intestazione non c'è niente su cui salire. */
    margin-top: 0;
    border-radius: 0;
  }

  /* (19.09.2026) Ridotta resta la colonna delle icone, e la pagina prende
     il resto: la griglia cambia la sua prima traccia e nient'altro. */
  html[data-sidebar="collapsed"] .app-shell {
    grid-template-columns: var(--app-sidebar-width-collapsed) minmax(0, 1fr);
  }

  .app-sidebar,
  .app-sidebar:has(> .app-topbar:not(:empty)) {
    display: flex;
    flex-direction: column;
    /* Il fondo schiarisce in cima, dove sta il logo, e si assesta appena
       sotto di lui. La misura è in **pixel e non in percentuale**: deve
       finire dove finisce il logo, che è alto quanto è alto. */
    background: linear-gradient(
      to bottom,
      var(--sidebar-bg-top) 0,
      var(--sidebar-bg) 180px,
      var(--sidebar-bg-bottom) 100%
    );
    /* Nessun bordo destro: fra questo fondo e una pagina chiara il salto di
       luminosità è già una separazione, e una riga in più sarebbe rumore. */
    border-right: none;
    /* (19.09.2026) Uguale ai due lati: con 8px a destra e 16 a sinistra
       marchio e selettore stavano 4px fuori asse. */
    padding: var(--space-3);
    position: sticky;
    top: 0;
    /* `sticky` fa della sidebar un contesto di impilamento: lo `z-index`
       del foglio del contesto (41) vale solo qui dentro, e senza un livello
       suo le righe posizionate della pagina — «Chi siamo», che viene dopo
       nel DOM — ci passavano sopra. Sopra la pagina, sotto il livello a
       schermo intero (50). */
    z-index: 42;
    height: 100vh;
    height: 100dvh;
    overflow-y: auto;
    overflow-x: hidden;
    scrollbar-width: thin;
    scrollbar-color: rgba(255, 255, 255, 0.16) transparent;
    color: var(--sidebar-text);
  }

  /* (19.09.2026) La card del contesto sta sotto il marchio, e non ha più la
     barra bianca in cima alla colonna: la card **è** il selettore. */
  .app-topbar {
    display: block;
    flex: none;
    margin: calc(-1 * var(--space-1)) 0 var(--space-2);
    padding: 0 0 var(--space-2);
    background: none;
    border: none;
    border-bottom: 1px solid var(--sidebar-border);
  }
}

/* (v1.23) Il marchio SKATE@LAB. (19.09.2026) Sul fondo trasparente, senza
   il quadrato del badge: il badge resta per la sidebar ridotta, dove il
   marchio intero a 40px non si leggerebbe. */
.sidebar-brand {
  display: flex;
  flex: none;
  justify-content: center;
  margin-bottom: var(--space-3);
  text-decoration: none;
}

.sidebar-brand:hover {
  text-decoration: none;
}

.sidebar-logo {
  display: block;
  width: 100%;
  max-width: 120px;
  height: auto;
}

.sidebar-badge {
  display: none;
  width: 40px;
  height: 40px;
  border-radius: 22%;
}

/* Il badge nell'header orizzontale sotto i 1024px, dove il logo intero non
   entrerebbe: qui la tagline non si leggerebbe comunque, e il nome ce l'ha
   il testo di fianco. (15.09.2026) 36px, alto quanto l'avatar dall'altra
   parte della fascia. Il badge è quasi nero e la fascia petrolio scuro: si
   separano da sé, senza un contorno. */
.brand-badge {
  width: 36px;
  height: 36px;
  border-radius: 22%;
  flex: none;
}

/* (19.09.2026) Il pulsante che riduce e riapre, in fondo sulla riga del
   payoff: l'icona del pannello, nel tratto delle altre, senza fondo finché
   non ci si passa sopra. L'attributo `hidden` lo toglie lo script — senza,
   non farebbe niente. */
.sidebar-toggle {
  display: grid;
  flex: none;
  place-items: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--sidebar-muted);
  cursor: pointer;
}

.sidebar-toggle:hover {
  background: var(--sidebar-hover-bg);
  color: var(--sidebar-strong);
}

.sidebar-toggle[hidden] {
  display: none;
}

/* (19.09.2026) Scorre l'elenco delle voci, non la sidebar: marchio e
   selettore in cima, chi guarda e la firma in fondo restano dove sono anche
   su un portatile basso, dove prima finivano sotto la piega. Il minimo di
   8rem è il punto in cui smette di aver senso: sotto, scorre tutta la
   sidebar, come prima. */
.sidebar-nav {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 8rem;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.16) transparent;
}

/* Un gruppo per domanda: i filetti fra i gruppi sono ciò che li separa, e
   l'etichetta ciò che li nomina. */
.sidebar-group {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.sidebar-group + .sidebar-group {
  margin-top: 0.375rem;
  padding-top: 0.375rem;
  border-top: 1px solid var(--sidebar-border);
}

/* Maiuscoletto piccolo e spaziato: nomina il gruppo senza pesare quanto le
   voci. Barlow e non la condensata, che a questo corpo si impasta. */
.sidebar-group-label {
  padding: 0.25rem 0.625rem 0.1875rem;
  font: 600 0.6875rem / 1.2 var(--font-sans);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--sidebar-label);
}

/* (19.09.2026) Voci compatte, in tondo regolare: prima erano tutte in
   grassetto, e il grassetto su tredici righe non distingue più niente —
   ora lo porta solo la voce attiva. 34px di riga: la sidebar c'è da
   1024px, cioè col mouse o su un tablet in orizzontale. */
.sidebar-link {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-height: 34px;
  padding: 0 0.625rem;
  border-radius: 6px;
  color: var(--sidebar-text);
  text-decoration: none;
  font: var(--type-body);
  line-height: 1.2;
}

/* Il grigio chiarissimo di `--color-bg` era corretto su una sidebar bianca;
   su questo fondo sarebbe un lampo. Un velo di bianco al 6% fa lo stesso
   mestiere senza accendere la riga. */
.sidebar-link:hover {
  background: var(--sidebar-hover-bg);
  color: var(--sidebar-strong);
  text-decoration: none;
}

.sidebar-link svg {
  flex: none;
  color: var(--sidebar-muted);
}

.sidebar-link-text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* (v1.69, issue #61) Il contatore delle proposte in attesa accanto ad
   «Amministrazione»: la pastiglia ambra di `badge-warning`, perché dice che
   c'è qualcosa da fare, ma con le cifre in tondo e non in maiuscoletto. Va in
   fondo alla riga, dove l'occhio cerca un numero: nella barra laterale e
   (issue #97) nel pannello di «Altro». */
.nav-count {
  flex: none;
  min-width: 1.5rem;
  margin-left: var(--space-2);
  padding: 0 var(--space-2);
  border-radius: 999px;
  background: var(--color-warning-bg);
  color: var(--color-warning-text);
  font: var(--type-caption);
  font-weight: 700;
  text-align: center;
}

.sidebar-link .nav-count {
  margin-left: auto;
}

/* Voce attiva. Le coppie sono enumerate perché il CSS non sa confrontare la
   classe di un antenato con quella di un discendente: la pagina dichiara
   `nav-<voce>` sul `<body>` (vedi base.html) e qui si accende la voce
   corrispondente. (19.09.2026) Le cinque pagine di una stagione sono voci
   del gruppo «Stagione», con le chiavi di `season_nav::SECTIONS.nav_key`:
   un test rilegge le loro coppie. */
.nav-oggi .sidebar-link.nav-link-oggi,
.nav-stagione .sidebar-link.nav-link-stagione,
.nav-calendario .sidebar-link.nav-link-calendario,
.nav-roster .sidebar-link.nav-link-roster,
.nav-valutazioni .sidebar-link.nav-link-valutazioni,
.nav-squadra .sidebar-link.nav-link-squadra,
.nav-persone .sidebar-link.nav-link-persone,
.nav-catalogo .sidebar-link.nav-link-catalogo,
.nav-concetti .sidebar-link.nav-link-concetti,
.nav-regolamento .sidebar-link.nav-link-regolamento,
.nav-squadre .sidebar-link.nav-link-squadre,
.nav-organici .sidebar-link.nav-link-organici,
.nav-categorie .sidebar-link.nav-link-categorie,
.nav-profilo .sidebar-link.nav-link-profilo,
.nav-amministrazione .sidebar-link.nav-link-amministrazione {
  background: var(--sidebar-active-bg);
  color: var(--sidebar-accent-text);
  font-weight: 600;
  /* La barra d'accento a sinistra come `box-shadow: inset` e non come
     `border-left`: un bordo occupa spazio nel box e sposterebbe il testo di
     3px nel momento in cui la voce si attiva — uno scatto visibile su ogni
     cambio di pagina. L'ombra interna non tocca il layout. */
  box-shadow: inset 3px 0 0 var(--sidebar-accent);
}

.nav-oggi .sidebar-link.nav-link-oggi svg,
.nav-stagione .sidebar-link.nav-link-stagione svg,
.nav-calendario .sidebar-link.nav-link-calendario svg,
.nav-roster .sidebar-link.nav-link-roster svg,
.nav-valutazioni .sidebar-link.nav-link-valutazioni svg,
.nav-squadra .sidebar-link.nav-link-squadra svg,
.nav-persone .sidebar-link.nav-link-persone svg,
.nav-catalogo .sidebar-link.nav-link-catalogo svg,
.nav-concetti .sidebar-link.nav-link-concetti svg,
.nav-regolamento .sidebar-link.nav-link-regolamento svg,
.nav-squadre .sidebar-link.nav-link-squadre svg,
.nav-organici .sidebar-link.nav-link-organici svg,
.nav-categorie .sidebar-link.nav-link-categorie svg,
.nav-profilo .sidebar-link.nav-link-profilo svg,
.nav-amministrazione .sidebar-link.nav-link-amministrazione svg {
  color: var(--sidebar-accent);
}

/* (19.09.2026) Chi guarda, in fondo e staccato dalla navigazione: avatar
   con le iniziali, nome e ruolo. Porta al profilo. */
.sidebar-me {
  display: flex;
  flex: none;
  align-items: center;
  gap: 0.625rem;
  margin-top: var(--space-2);
  padding: var(--space-3) 0.625rem var(--space-2);
  border-top: 1px solid var(--sidebar-border);
  color: var(--sidebar-text);
  text-decoration: none;
}

.sidebar-me:hover {
  text-decoration: none;
}

.sidebar-me:hover .sidebar-me-avatar {
  border-color: var(--sidebar-accent);
}

.sidebar-me-avatar {
  display: grid;
  flex: none;
  place-items: center;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: #2a3136;
  border: 1px solid rgba(255, 255, 255, 0.16);
  color: var(--sidebar-strong);
  font: 600 0.8125rem / 1 var(--font-sans);
}

.sidebar-me-names {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-width: 0;
  line-height: 1.25;
}

.sidebar-me-name {
  overflow: hidden;
  color: var(--sidebar-strong);
  font: 600 0.875rem / 1.25 var(--font-sans);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.sidebar-me-role {
  overflow: hidden;
  color: var(--sidebar-muted);
  font: 400 0.75rem / 1.3 var(--font-sans);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.sidebar-me-chevron {
  flex: none;
  color: var(--sidebar-muted);
}

/* La firma di marchio, non testo d'interfaccia: due righe che vanno lette
   come un blocco unico. (19.09.2026) Più piccola: è decorativa. */
.sidebar-footer {
  display: flex;
  flex: none;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-2);
  margin-top: var(--space-1);
  padding: var(--space-2) 0.625rem 0;
  border-top: 1px solid var(--sidebar-border);
  font: 600 0.6875rem / 1.5 var(--font-cond);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--sidebar-label);
}

/* Il filetto di marchio sotto la firma. Rosso dell'artwork, non
   `--color-danger-*`: quello significa "errore" in ogni form dell'app.
   Regola che questo colore si porta dietro — **non deve mai più comparire
   altrove nella sidebar**: il giorno che qui servisse un contatore o una
   pastiglia di avviso, quello usa il rosso di stato, altrimenti i due
   significati si sovrappongono. */
.sidebar-footer-rule {
  display: block;
  width: 24px;
  height: 2px;
  margin-top: 0.375rem;
  border-radius: 1px;
  background: #f03c14;
}

/* Il focus dell'app è `--color-primary-dark`, che su questo fondo dà 2.5:1
   — sotto la soglia 3:1 richiesta a un indicatore non testuale. Qui vale
   l'accento chiaro. */
.app-sidebar a:focus-visible,
.app-sidebar summary:focus-visible,
.app-sidebar button:focus-visible {
  outline: 2px solid var(--sidebar-accent);
  outline-offset: 2px;
}

/* --- La sidebar ridotta (19.09.2026) --------------------------------------
   Solo le icone, centrate; niente etichette di gruppo, i filetti restano a
   dividere. Il nome di una voce lo dice il suggerimento che
   `static/sidebar.js` mette accanto (`.sidebar-tip`); le etichette restano
   nel DOM, nascoste come `visually-hidden`, così lo screen reader continua a
   leggerle. La voce attiva tiene fondo, icona e barretta. */
@media (min-width: 1024px) {
  html[data-sidebar="collapsed"] .app-sidebar {
    padding-inline: 0.625rem;
  }

  html[data-sidebar="collapsed"] .sidebar-footer-brand {
    display: flex;
    justify-content: center;
  }

  html[data-sidebar="collapsed"] .sidebar-logo {
    display: none;
  }

  html[data-sidebar="collapsed"] .sidebar-badge {
    display: block;
  }

  html[data-sidebar="collapsed"] .sidebar-group-label {
    display: none;
  }

  html[data-sidebar="collapsed"] .sidebar-link {
    justify-content: center;
    min-height: 40px;
    padding: 0;
  }

  html[data-sidebar="collapsed"] .sidebar-link-text,
  html[data-sidebar="collapsed"] .sidebar-link .nav-count,
  html[data-sidebar="collapsed"] .sidebar-me-names {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  html[data-sidebar="collapsed"] .sidebar-me-chevron,
  html[data-sidebar="collapsed"] .sidebar-footer-text {
    display: none;
  }

  /* Il contatore diventa un puntino ambra sulla chiave: ridotta, la voce
     dice solo «c'è qualcosa». Il numero resta nel DOM, nascosto come il
     nome, per lo screen reader e per il suggerimento. */
  html[data-sidebar="collapsed"] .sidebar-link.has-count::after {
    content: "";
    position: absolute;
    top: 7px;
    right: calc(50% - 13px);
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: #e0a100;
  }

  html[data-sidebar="collapsed"] .sidebar-me {
    justify-content: center;
    padding-inline: 0;
  }

  /* Ridotta, la firma lascia il filetto, e il pulsante gli sta sotto. */
  html[data-sidebar="collapsed"] .sidebar-footer {
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
    padding-inline: 0;
  }
}

/* Il suggerimento accanto a un'icona della sidebar ridotta. Uno solo per
   pagina, messo e tolto da `static/sidebar.js`: `fixed`, perché la sidebar
   scorre e taglierebbe tutto ciò che le esce di lato. */
.sidebar-tip {
  position: fixed;
  z-index: 45;
  padding: 0.3125rem 0.5625rem;
  border-radius: 5px;
  background: #11161a;
  color: var(--sidebar-strong, #e6e9ed);
  font: 400 0.8125rem / 1.2 var(--font-sans);
  white-space: nowrap;
  pointer-events: none;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
  transform: translateY(-50%);
}

.sidebar-tip::before {
  content: "";
  position: absolute;
  top: 50%;
  left: -5px;
  border: 5px solid transparent;
  border-left: 0;
  border-right-color: #11161a;
  transform: translateY(-50%);
}

/* ==========================================================================
   Barra in basso (issue #97) — la navigazione sotto 1024px.

   Fissa in fondo, nella zona del pollice: Oggi, Esercizi, Valuta e
   Regolamento, le quattro che servono in pista, e «Altro» per le altre. Le
   ha scelte l'utente il 14.09.2026 (`docs/proposta-mobile/`, direzione A).
   Da 1024px la nasconde la shell, qui sopra.

   «Altro» è un `<details>`: il pannello si apre sopra la barra senza
   JavaScript, e senza cambiare l'indirizzo né lo scorrimento. Un tocco
   fuori dal pannello lo chiude, perché fuori c'è il velo, che è del
   `<summary>` (vedi sotto).

   Chiaro come la pagina, non scuro come la sidebar: è la vista di bordo
   pista, dove uno schermo scuro al sole è un problema vero (VISION §8).

   (15.09.2026) Galleggia: una barra bianca dagli angoli arrotondati,
   staccata dal fondo di `--bottom-nav-gap` e dai lati di mezzo rem, con
   un'ombra appena. Centrata con `margin-inline: auto` e non con un
   `transform`: un `transform` farebbe della barra il riferimento dei figli
   `fixed`, e il velo di «Altro» (più giù) si stringerebbe a lei.
   ========================================================================== */
.bottom-nav {
  position: fixed;
  left: 0;
  right: 0;
  bottom: var(--bottom-nav-gap);
  z-index: 20;
  display: flex;
  width: var(--bottom-nav-width);
  margin-inline: auto;
  padding: 0 var(--space-1);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--shell-sheet-radius);
  box-shadow: 0 6px 20px rgba(16, 24, 40, 0.1), 0 1px 3px rgba(16, 24, 40, 0.06);
}

.bottom-nav-link,
.bottom-nav-more {
  flex: 1 1 0;
  min-width: 0;
}

/* Le cinque voci, icona sopra ed etichetta sotto. (15.09.2026) L'etichetta
   non è più in maiuscoletto: in tondo condensato si legge come il nome di
   una voce e non come un occhiello, e «Regolamento» sta lo stesso in un
   quinto della barra (misurata: 62px su 70 a 375). La voce accesa è
   petrolio, e ha un segno corto sotto l'etichetta (`::after`): una forma,
   non solo un colore. */
.bottom-nav-link {
  --bottom-nav-active-shadow: none;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  height: var(--bottom-nav-height);
  /* Solo in basso, per il segno della voce accesa: in orizzontale, con
     `flex-basis: 0`, conterebbe come minimo sui quattro link e non sul
     `<details>` di «Altro», che uscirebbe più stretto. */
  padding: 0 0 var(--space-1);
  color: var(--color-text-muted);
  font: 600 0.8125rem / 1.1 var(--font-cond);
  letter-spacing: 0.01em;
  text-decoration: none;
  white-space: nowrap;
  list-style: none;
  cursor: pointer;
}

/* Il segno della voce accesa: acceso dalla regola della voce attiva (più
   giù), che porta `--bottom-nav-indicator` a 1. Sul `::after` perché il
   `::before` del `<summary>` di «Altro» è il velo. */
.bottom-nav-link::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 5px;
  width: 1.25rem;
  height: 3px;
  margin-left: -0.625rem;
  border-radius: 2px;
  background: currentColor;
  opacity: var(--bottom-nav-indicator, 0);
}

/* Sotto 360px — un iPhone SE di prima generazione — «Regolamento» a 13px
   uscirebbe dalla sua cella e toccherebbe le vicine. */
@media (max-width: 359px) {
  .bottom-nav-link {
    font-size: 0.6875rem;
    letter-spacing: 0;
  }
}

.bottom-nav-link::-webkit-details-marker {
  display: none;
}

.bottom-nav-link:hover {
  color: var(--color-text);
  text-decoration: none;
}

.bottom-nav-link svg {
  flex: none;
}

.bottom-nav-link:focus-visible,
.bottom-nav-item:focus-visible {
  outline: 2px solid var(--color-primary-dark);
  outline-offset: -2px;
}

.bottom-nav-more[open] > summary {
  color: var(--color-primary-dark);
}

/* Il velo sopra la pagina mentre il pannello è aperto. È il `::before` del
   `<summary>`, e un tocco su di lui è un tocco sul `<summary>`: chiude il
   pannello, senza una riga di JavaScript. Lascia scoperta la barra, così
   le altre quattro voci restano da toccare.

   (15.09.2026) Con la barra che galleggia, il velo copre tutto lo schermo
   — anche ai lati della barra e sotto — e la barra la lascia fuori con un
   buco (`clip-path` con `evenodd`): il rettangolo di `--bottom-nav-width`
   per `--bottom-nav-height`, a `--bottom-nav-gap` dal fondo, le stesse
   misure della barra. Dove il velo è bucato non riceve il tocco, e il
   tocco arriva alla voce sotto. */
.bottom-nav-more[open] > summary::before {
  --veil-hole-left: calc(50% - var(--bottom-nav-width) / 2);
  --veil-hole-right: calc(50% + var(--bottom-nav-width) / 2);
  /* I 2px sono il bordo della barra, sopra e sotto le voci. */
  --veil-hole-top: calc(100% - var(--bottom-nav-gap) - var(--bottom-nav-height) - 2px);
  --veil-hole-bottom: calc(100% - var(--bottom-nav-gap));
  /* Gli angoli del buco sono smussati, tangenti agli angoli arrotondati
     della barra (r · (2 − √2)): con un rettangolo, fuori da ogni angolo
     restava un quadratino senza velo. */
  --veil-hole-cut: calc(var(--shell-sheet-radius) * 0.586);
  content: "";
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(16, 24, 40, 0.32);
  clip-path: polygon(
    evenodd,
    0 0, 100% 0, 100% 100%, 0 100%, 0 0,
    calc(var(--veil-hole-left) + var(--veil-hole-cut)) var(--veil-hole-top),
    calc(var(--veil-hole-right) - var(--veil-hole-cut)) var(--veil-hole-top),
    var(--veil-hole-right) calc(var(--veil-hole-top) + var(--veil-hole-cut)),
    var(--veil-hole-right) calc(var(--veil-hole-bottom) - var(--veil-hole-cut)),
    calc(var(--veil-hole-right) - var(--veil-hole-cut)) var(--veil-hole-bottom),
    calc(var(--veil-hole-left) + var(--veil-hole-cut)) var(--veil-hole-bottom),
    var(--veil-hole-left) calc(var(--veil-hole-bottom) - var(--veil-hole-cut)),
    var(--veil-hole-left) calc(var(--veil-hole-top) + var(--veil-hole-cut)),
    calc(var(--veil-hole-left) + var(--veil-hole-cut)) var(--veil-hole-top)
  );
  cursor: default;
}

/* Il contatore delle proposte su «Altro»: la pastiglia di `.nav-count`, in
   piccolo e accanto all'icona. */
.bottom-nav-badge {
  position: absolute;
  top: 4px;
  left: calc(50% + 6px);
  min-width: 1.125rem;
  padding: 0 4px;
  border-radius: var(--radius-pill);
  background: var(--color-warning-bg);
  color: var(--color-warning-text);
  font: 700 0.6875rem / 1.125rem var(--font-sans);
  letter-spacing: 0;
  text-align: center;
}

/* Il pannello di «Altro»: sopra la barra, largo quanto lei, a due colonne
   sul telefono — così resta nella metà bassa dello schermo, dove arriva il
   pollice — e a più colonne su un tablet. Scorre da sé se una finestra
   bassa non lo tiene tutto. Larghezza e `bottom` si misurano su
   `.bottom-nav`, che è `fixed`: il `<details>` in mezzo **non** va
   posizionato, o il pannello si stringe a un quinto della barra.
   (15.09.2026) Una card come la barra, staccata da lei di mezzo rem. */
.bottom-nav-panel {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(100% + var(--space-2));
  display: grid;
  /* 10rem: due tracce e lo spazio fra loro nei 374px della barra su un
     iPhone da 390, meno il padding. «Come siamo messi» con la sua icona ne
     prende 9. */
  grid-template-columns: repeat(auto-fill, minmax(min(10rem, 100%), 1fr));
  gap: 2px var(--space-2);
  max-height: calc(100dvh - var(--bottom-nav-height) - var(--bottom-nav-gap) - var(--space-5));
  overflow-y: auto;
  padding: var(--space-2);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--shell-sheet-radius);
  box-shadow: 0 8px 24px rgba(16, 24, 40, 0.14);
}

/* (19.09.2026) Le etichette dei quattro gruppi della sidebar, a tutta
   riga nella griglia del pannello. */
.bottom-nav-group {
  grid-column: 1 / -1;
  padding: var(--space-2) var(--space-2) 0.125rem;
  font: 600 0.6875rem / 1.2 var(--font-sans);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.bottom-nav-group:first-child {
  padding-top: var(--space-1);
}

.bottom-nav-item {
  --bottom-nav-active-shadow: inset 3px 0 0 var(--color-primary);
  --bottom-nav-active-bg: var(--color-primary-soft);
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  padding: 0 var(--space-2);
  border-radius: var(--radius-nav);
  color: var(--color-text);
  font: var(--type-body-strong);
  text-decoration: none;
}

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

.bottom-nav-item svg {
  flex: none;
}

.bottom-nav-item .nav-count {
  margin-left: auto;
}

/* Voce accesa: le stesse coppie della sidebar, una per voce, dalla classe
   del `<body>`. «Altro» si accende quando la pagina non è una delle quattro
   voci fisse — anche su quelle senza voce (`nav-altro`), che stanno tutte
   là sotto. */
.nav-oggi .bottom-nav .nav-link-oggi,
.nav-catalogo .bottom-nav .nav-link-catalogo,
.nav-valutazioni .bottom-nav .nav-link-valutazioni,
.nav-regolamento .bottom-nav .nav-link-regolamento,
.nav-stagione .bottom-nav .nav-link-stagione,
.nav-calendario .bottom-nav .nav-link-calendario,
.nav-roster .bottom-nav .nav-link-roster,
.nav-squadra .bottom-nav .nav-link-squadra,
.nav-persone .bottom-nav .nav-link-persone,
.nav-concetti .bottom-nav .nav-link-concetti,
.nav-squadre .bottom-nav .nav-link-squadre,
.nav-organici .bottom-nav .nav-link-organici,
.nav-categorie .bottom-nav .nav-link-categorie,
.nav-profilo .bottom-nav .nav-link-profilo,
.nav-amministrazione .bottom-nav .nav-link-amministrazione,
body:not(.nav-oggi):not(.nav-catalogo):not(.nav-valutazioni):not(.nav-regolamento) .bottom-nav-more > summary {
  --bottom-nav-indicator: 1;
  color: var(--color-primary-dark);
  background: var(--bottom-nav-active-bg, none);
  box-shadow: var(--bottom-nav-active-shadow);
}

/* Da 1024px c'è la sidebar. **Dopo** le regole della barra: prima, il
   `display: flex` di `.bottom-nav` vincerebbe a pari specificità. */
@media (min-width: 1024px) {
  .bottom-nav {
    display: none;
  }
}

/* ==========================================================================
   Card del contesto (15.09.2026) — la squadra e la stagione su cui si
   lavora, su ogni pagina (`partials/context_card.html`).

   Sul telefono sta sul foglio, subito sotto l'intestazione: una card tinta
   dell'accento chiaro, senza bordo né ombra — un'informazione, non un
   contenuto da leggere, e distinta dalle card bianche della pagina. Da
   1024px sta nella barra in cima alla colonna, stretta al suo contenuto.

   Quando c'è una scelta è un `<details>` e «Cambia», a destra, dice che si
   apre; senza, la stessa card, ferma.
   ========================================================================== */
.context-card {
  position: relative;
}

.context-card-face {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 3.5rem;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-lg);
  background: var(--color-primary-soft);
  color: var(--color-text);
  list-style: none;
}

summary.context-card-face {
  cursor: pointer;
}

.context-card-face::-webkit-details-marker {
  display: none;
}

summary.context-card-face:hover,
.context-card[open] > summary.context-card-face {
  background: color-mix(in srgb, var(--color-primary-soft), var(--color-primary) 8%);
}

summary.context-card-face:focus-visible {
  outline: 2px solid var(--color-primary-dark);
  outline-offset: 2px;
}

.context-card-icon {
  flex: none;
  color: var(--color-primary);
}

.context-card-names {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-width: 0;
}

/* «Mini» è la parola che si riconosce da lontano; la categoria è una
   qualifica del nome, più tenue — com'era nell'intestazione della home. */
.context-card-team {
  font: var(--type-title);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.context-card-category {
  color: var(--color-text-muted);
}

/* `flex-wrap`: dove il nome e la pastiglia non stanno insieme (360px), va a
   capo la pastiglia, non il nome a metà («Stagione / 2026»). */
.context-card-season {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1) var(--space-2);
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* Il gesto detto a parole, e in una pastiglia bianca sulla card tinta: la
   sola freccia accanto alla stagione si leggeva come parte della stagione,
   non come «qui si cambia». */
.context-card-cta {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  margin-left: auto;
  padding: 0.375rem 0.5rem 0.375rem 0.75rem;
  border-radius: var(--radius-pill);
  background: var(--color-surface);
  color: var(--color-primary-dark);
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
}

.context-card-chevron {
  flex: none;
  transition: transform 0.15s ease;
}

.context-card[open] .context-card-chevron {
  transform: rotate(180deg);
}

/* --- Lo stato di una stagione -------------------------------------------
   Sulla card e nel foglio, sempre con la parola. Tre forme, non tre colori:
   **in corso** è piena — è la stagione di stasera —, **in pianificazione**
   è lo stesso inchiostro solo di contorno, **conclusa** è spenta. Niente
   ambra: nell'app è degli avvisi, e una stagione che si prepara non è un
   problema. Piena e contorno si distinguono anche sul fondo tinto della
   card, dove una pastiglia tinta come lei sparirebbe. */
.season-status {
  display: inline-flex;
  align-items: center;
  flex: none;
  min-height: 1.25rem;
  padding: 0 0.5rem;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  font: 600 var(--t-1) / 1.2 var(--font-sans);
  white-space: nowrap;
}

.season-status-running {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-primary-contrast);
}

.season-status-planning {
  background: var(--color-surface);
  border-color: var(--color-primary-dark);
  color: var(--color-primary-dark);
}

.season-status-over {
  background: var(--color-bg);
  border-color: var(--color-border);
  color: var(--color-text-muted);
}

/* --- Il foglio del contesto (`partials/switcher_panel.html`) -------------
   Sul telefono galleggia in basso come la barra, largo quanto lei e sopra
   di lei, nella metà dello schermo dove arriva il pollice: prima la
   squadra, poi le sue stagioni. Non copre più la pagina dall'alto, e la
   card da cui si è partiti resta visibile sotto il velo.

   Il velo è il `::before` del `<summary>`, come in «Altro»: un tocco fuori
   dal foglio lo chiude, senza JavaScript. Qui senza il buco per la barra:
   il foglio la copre, e mentre si sceglie il contesto non c'è un'altra voce
   da toccare. Nessun antenato della card ha un `transform` o un `filter`,
   ed è ciò che tiene `fixed` misurato sullo schermo: non aggiungerne. */
.context-card[open] > summary.context-card-face::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 40;
  background: rgba(16, 24, 40, 0.32);
  cursor: default;
}

.team-switcher-panel {
  position: fixed;
  left: 0;
  right: 0;
  bottom: var(--bottom-nav-gap);
  z-index: 41;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  width: var(--bottom-nav-width);
  max-height: min(75dvh, 36rem);
  margin-inline: auto;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--space-3);
  background: var(--color-surface);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-radius: var(--shell-sheet-radius);
  box-shadow: 0 8px 24px rgba(16, 24, 40, 0.14);
}

/* (19.09.2026, mockup dell'utente) In testa il titolo e la ✕, che accende
   `sidebar.js`; sotto, ogni passo ha la sua intestazione, e quella della
   squadra porta a destra il link a tutte le squadre. */
.ctx-sheet-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: 0 0 var(--space-1) var(--space-1);
}

.ctx-sheet-title {
  font: var(--type-title);
  font-weight: 700;
}

.ctx-close {
  flex: none;
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  padding: 0;
  border: none;
  border-radius: var(--radius-md);
  background: none;
  color: var(--color-text);
  cursor: pointer;
}

.ctx-close[hidden] {
  display: none;
}

.ctx-close:hover {
  background: var(--color-bg);
}

.ctx-close:focus-visible {
  outline: 2px solid var(--color-primary-dark);
  outline-offset: 2px;
}

.ctx-section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
}

.switcher-label {
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
  padding: var(--space-1);
}

.switcher-all {
  padding: var(--space-1);
  font: var(--type-caption);
  font-weight: 600;
  color: var(--color-primary-dark);
  text-decoration: none;
  white-space: nowrap;
}

.switcher-all:hover {
  text-decoration: underline;
}

.switcher-all-foot {
  padding-top: var(--space-2);
  border-top: 1px solid var(--color-border);
}

/* Il primo passo: le squadre in elenco, una per riga — dieci squadre del
   responsabile restano leggibili, e il codice della categoria sta in
   colonna a destra. Ogni squadra è `.ctx-team` (`display: contents`):
   radio, riga e stagioni sono fratelli per `:checked +`, e `order` mette
   tutte le squadre in alto e sotto le stagioni della squadra scelta. */
.ctx-teams {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}

.ctx-team {
  display: contents;
}

.ctx-pick {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  opacity: 0;
}

/* (v1.62, issue #54) Il titolo delle squadre in sola lettura, fra le
   squadre che si allenano e le altre. */
.ctx-teams-heading {
  order: 0;
  padding: var(--space-2) var(--space-1) var(--space-1);
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* Il cerchio di una riga, squadra o stagione: vuoto per le altre, pieno e
   con la spunta per quella scelta. */
.ctx-mark {
  flex: none;
  display: grid;
  place-items: center;
  box-sizing: border-box;
  width: 1.375rem;
  height: 1.375rem;
  border: 2px solid color-mix(in srgb, var(--color-text-muted) 55%, var(--color-surface));
  border-radius: 50%;
  color: var(--color-primary-contrast);
}

.ctx-mark svg {
  display: none;
}

/* È una `<label>`: la regola generale le mette in colonna, per il campo
   sotto l'etichetta. Qui cerchio, nome e categoria stanno su una riga. */
.ctx-chip {
  order: 0;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--space-3);
  min-height: 48px;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  background: none;
  color: var(--color-text);
  font: var(--type-body-strong);
  cursor: pointer;
}

.ctx-chip:hover {
  background: var(--color-bg);
}

.ctx-chip.is-read-only {
  color: var(--color-text-muted);
}

.ctx-chip-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ctx-chip-category {
  flex: none;
  font: var(--type-caption);
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}

.ctx-pick:checked + .ctx-chip {
  background: var(--color-primary-soft);
  color: var(--color-primary-dark);
}

.ctx-pick:checked + .ctx-chip .ctx-chip-category {
  color: inherit;
  font-weight: 700;
}

.ctx-pick:checked + .ctx-chip .ctx-mark,
.ctx-season.is-current .ctx-mark {
  border-color: var(--color-primary-dark);
  background: var(--color-primary-dark);
}

.ctx-pick:checked + .ctx-chip .ctx-mark svg,
.ctx-season.is-current .ctx-mark svg {
  display: block;
}

.ctx-pick:focus-visible + .ctx-chip {
  outline: 2px solid var(--color-primary-dark);
  outline-offset: -2px;
}

/* Il secondo passo: le stagioni della squadra scelta, sotto tutte le
   squadre. Con una squadra sola stanno direttamente nel foglio. */
.ctx-seasons {
  order: 1;
  display: none;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.ctx-pick:checked + .ctx-chip + .ctx-seasons {
  display: flex;
  margin-top: var(--space-2);
  padding-top: var(--space-2);
  border-top: 1px solid var(--color-border);
}

.team-switcher-panel > .ctx-seasons {
  display: flex;
}

/* Ogni stagione è un `<form>` perché sceglierla è un `POST`, ma nel foglio
   non deve sembrare una scheda: lo stile generico dei form qui va spento. */
.team-switcher-panel form {
  border: none;
  background: none;
  padding: 0;
  margin: 0;
  box-shadow: none;
}

/* Una stagione: il cerchio, il nome, lo stato. La stagione su cui si
   lavora ha il cerchio pieno e il fondo tinto, e non è un bottone:
   sceglierla non cambierebbe niente. */
.ctx-season {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  min-height: 48px;
  padding: var(--space-2) var(--space-3);
  border: none;
  border-radius: var(--radius-md);
  background: none;
  color: var(--color-text);
  font: var(--type-body);
  text-align: left;
}

button.ctx-season {
  cursor: pointer;
}

button.ctx-season:hover {
  background: var(--color-bg);
}

button.ctx-season:focus-visible {
  outline: 2px solid var(--color-primary-dark);
  outline-offset: -2px;
}

.ctx-season-name {
  flex: 1 1 auto;
  min-width: 0;
}

.ctx-season.is-current {
  background: var(--color-primary-soft);
  color: var(--color-primary-dark);
  font: var(--type-body-strong);
}

/* Una squadra senza stagioni resta nel foglio invece di sparirne: è la
   squadra a cui manca proprio la cosa che qui si sceglie, e nasconderla
   significherebbe non poterci arrivare da qui. */
.switcher-empty {
  padding: var(--space-2) var(--space-3);
  font: var(--type-caption);
  color: var(--color-text-muted);
}

@media (prefers-reduced-motion: no-preference) {
  .context-card[open] > .team-switcher-panel {
    animation: context-sheet-in 0.18s ease-out;
  }

  .context-card[open] > summary.context-card-face::before {
    animation: context-veil-in 0.18s ease-out;
  }
}

@keyframes context-sheet-in {
  from {
    opacity: 0;
    transform: translateY(1.5rem);
  }
}

@keyframes context-veil-in {
  from {
    opacity: 0;
  }
}

/* (19.09.2026) Da 1024px la card sta nella sidebar e fa parte della
   testata: la squadra in piccolo e la stagione sotto, centrate sull'asse
   del marchio, senza riquadro, chiuse dal filetto che separa la testata
   dalle voci. La freccia sta alla destra della stagione e il puntino dello
   stato alla sua sinistra, fuori dal centro: la stagione resta sull'asse.
   Il puntino ha le stesse tre forme della pastiglia (pieno, contorno,
   spento), e la parola resta nel DOM per chi non vede. Il foglio è lo
   stesso, con gli stessi due passi: si apre di fianco alla sidebar,
   all'altezza della card (`--context-panel-top`, che mette
   `static/sidebar.js`; senza script in cima). `fixed`, perché la sidebar
   scorre e lo taglierebbe. Il velo resta, trasparente, e serve solo a
   chiudere con un clic fuori. */
.context-card-icon-season {
  display: none;
}

@media (min-width: 1024px) {
  .context-card-face {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    /* Nessuno spazio fra le due righe: il `gap` della card sul telefono è
       di 16px, e le staccava. */
    gap: 0 0.375rem;
    min-height: 0;
    padding: 0.375rem var(--space-1);
    border-radius: 8px;
    background: none;
    color: var(--sidebar-text);
    text-align: center;
  }

  summary.context-card-face:hover,
  .context-card[open] > summary.context-card-face {
    background: var(--sidebar-hover-bg);
  }

  summary.context-card-face:focus-visible {
    outline-color: var(--sidebar-accent);
  }

  .context-card-icon {
    display: none;
  }

  /* Squadra e stagione entrano nella griglia della card: la colonna di
     mezzo è loro, quella di destra della freccia. */
  .context-card-names {
    display: contents;
  }

  /* `justify-self: center`: stirate sulla colonna, la stagione (un flex)
     andrebbe a sinistra e il puntino al bordo della colonna, non del
     testo. */
  .context-card-team {
    grid-column: 2;
    grid-row: 1;
    justify-self: center;
    font: 400 0.75rem / 1.3 var(--font-sans);
    color: var(--sidebar-muted);
  }

  .context-card-category {
    color: inherit;
  }

  .context-card-season {
    position: relative;
    grid-column: 2;
    grid-row: 2;
    justify-self: center;
    font: 600 0.9375rem / 1.3 var(--font-sans);
    color: var(--sidebar-strong);
  }

  .context-card-season .season-status {
    position: absolute;
    top: 50%;
    right: calc(100% + 0.4375rem);
    width: 7px;
    height: 7px;
    min-height: 0;
    padding: 0;
    overflow: hidden;
    border-width: 1.5px;
    font-size: 0;
    transform: translateY(-50%);
  }

  .context-card-season .season-status-running {
    background: var(--sidebar-accent);
    border-color: var(--sidebar-accent);
  }

  .context-card-season .season-status-planning {
    background: transparent;
    border-color: var(--sidebar-accent);
  }

  .context-card-season .season-status-over {
    background: transparent;
    border-color: var(--sidebar-muted);
  }

  .context-card-cta {
    grid-column: 3;
    grid-row: 2;
    justify-self: start;
    margin: 0;
    padding: 0;
    background: none;
    color: var(--sidebar-muted);
    font-size: 0;
  }

  .context-card[open] > summary.context-card-face::before {
    background: transparent;
  }

  .team-switcher-panel {
    position: fixed;
    top: var(--context-panel-top, var(--space-3));
    left: calc(var(--app-sidebar-width) + var(--space-2));
    right: auto;
    bottom: auto;
    width: 28rem;
    max-height: min(80vh, 40rem);
    padding: var(--space-4);
    margin-inline: 0;
    border-radius: var(--radius-lg);
  }

  /* Ridotta, la card è la sola icona del calendario, e apre lo stesso
     foglio accanto alla colonna stretta. */
  html[data-sidebar="collapsed"] .context-card-face {
    display: flex;
    justify-content: center;
    min-height: 38px;
    padding: 0;
    background: var(--sidebar-hover-bg);
  }

  html[data-sidebar="collapsed"] .context-card-icon-season {
    display: block;
    color: var(--sidebar-muted);
  }

  html[data-sidebar="collapsed"] .context-card-names,
  html[data-sidebar="collapsed"] .context-card-cta {
    display: block;
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  html[data-sidebar="collapsed"] .team-switcher-panel {
    left: calc(var(--app-sidebar-width-collapsed) + var(--space-2));
  }
}

@media (min-width: 1024px) and (prefers-reduced-motion: no-preference) {
  .context-card[open] > .team-switcher-panel {
    animation-name: context-popover-in;
  }
}

@keyframes context-popover-in {
  from {
    opacity: 0;
    transform: translateY(-0.5rem);
  }
}

/* (v1.33) L'invito a cominciare, sulla vista d'insieme di una stagione
   ancora vuota. È un `empty-state` come gli altri, con una differenza: qui
   il vuoto non è un'assenza da segnalare ma il punto di partenza normale di
   ogni stagione, quindi porta dei bottoni e non solo una frase. Sparisce da
   sé al primo blocco o alla prima voce di calendario. */
.season-empty-start {
  text-align: left;
  margin-bottom: var(--space-4);
}
.season-empty-start .page-actions {
  margin-bottom: 0;
  margin-top: var(--space-3);
}

/* --- (v1.34; v1.75, issue #64: in fondo) Il ritmo settimanale ---------
   Due regimi affiancati da 900px, impilati sotto: sono la stessa domanda
   fatta due volte — quando in pista, quando in palestra — e leggerli uno
   accanto all'altro è ciò che fa notare che in palestra si va una volta
   sola. */
.plan-rhythm-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
}
@media (min-width: 900px) {
  .plan-rhythm-grid {
    grid-template-columns: 1fr 1fr;
  }
}
.plan-rhythm-regime {
  margin: 0;
}
/* Ora, durata e luogo su una riga sola finché ci stanno: sono tre risposte
   brevi alla stessa domanda, e in colonna sembrerebbero tre sezioni. */
.plan-rhythm-fields {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-3);
}
.plan-rhythm-fields label {
  flex: 1 1 8rem;
}

/* Il totale degli allenamenti sotto il titolo dell'anteprima: è il numero
   che dice se il piano ha preso la forma giusta, e va letto prima
   dell'elenco dei blocchi, non dopo. */
.plan-preview-trainings {
  margin: 0 0 var(--space-3);
  font: var(--type-body);
  color: var(--color-text);
}

.topbar-context {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.topbar-context strong {
  color: var(--color-text);
}

/* ==========================================================================
   Stampa — pensato per la scheda allenamento (session.html), ma le regole
   .no-print/navigazione si applicano a qualunque pagina venga stampata per
   errore. Niente bottoni/link interattivi in stampa (VISION §9).
   ========================================================================== */

/* (v1.22) Il rovescio di `.no-print`: markup che esiste solo sul foglio.
   Serve alla scheda G+S di `session.html`, che a schermo sarebbe una
   seconda copia dello stesso contenuto. */
.print-only {
  display: none;
}

@media print {
  @page {
    size: A4;
    margin: 1.5cm;
  }

  /* (v1.22) I margini asimmetrici del modello G+S originale (alto 1.1,
     destro 1.0, basso 0.8, sinistro 1.5 cm), applicati **solo** alla
     scheda tramite pagina con nome: `@page` senza nome è globale, e
     stringere i margini di ogni pagina stampata dall'app per far tornare
     un foglio solo sarebbe un effetto collaterale sproporzionato. Dove
     `page:` non è supportato la scheda esce con i margini di default qui
     sopra — cambia di qualche millimetro, non si rompe. */
  @page gs-sheet {
    size: A4 portrait;
    margin: 1.1cm 1cm 0.8cm 1.5cm;
  }

  .print-only {
    display: block;
  }

  body {
    background: #fff;
    color: #000;
  }

  .app-header,
  .app-sidebar,
  .app-topbar,
  .no-print,
  form {
    display: none !important;
  }

  /* La griglia della shell non deve lasciare la colonna vuota della sidebar
     sul foglio. (issue #97) Né il posto della barra in basso: un A4 è più
     stretto di 1024px. */
  .app-shell {
    display: block !important;
    padding: 0 !important;
  }

  .app-main {
    max-width: none;
    padding: 0;
    margin: 0;
  }

  /* La scheda si stampa sempre a colonna singola a piena larghezza
     (requisito di prodotto): le griglie a schermo non devono sopravvivere
     alla stampa, altrimenti due esercizi affiancati su A4 diventano due
     colonne strette e spezzate. */
  .timeline-card {
    display: none;
  }

  /* (v1.24) La **vista tabella** però si stampa: è metà del suo motivo di
     esistere (requisito §7, "serve a chi stampa"), ed è l'unica forma di
     questa vista che su carta dice ancora qualcosa — un asse di 365 giorni
     con tacche da quattro pixel, no. Fuori dal foglio solo ciò che sulla
     carta non ha funzione: il commutatore, che è un link, e la legenda, che
     spiega forme che la tabella non usa. */
  .timeline-card-table {
    display: block;
    border: 0;
    padding: 0;
  }
  .timeline-toggle,
  .timeline-legend,
  .timeline-narrow-note {
    display: none;
  }

  /* (v1.22) `.session-head-grid` non è più qui: su `session.html` è
     `no-print`, e questa regola — più in basso nel file, a parità di
     `!important` — la rimetteva a video sul foglio. */
  .catalog-layout,
  .list-and-form,
  .season-layout,
  .stat-card-grid,
  .signal-list,
  .home-hero,
  .home-head,
  .params-grid,
  .block-progress {
    display: block !important;
  }

  /* Il diagramma si stampa: è il motivo per cui il renderer è server-side
     e produce un SVG inerte. Il tetto di larghezza a schermo qui non serve. */
  .rink-diagram {
    max-width: 100%;
    break-inside: avoid;
    page-break-inside: avoid;
  }

  /* (v1.22) `.exercise-card` non è più in questo elenco: in stampa le card
     non escono affatto — le sostituisce la tabella G+S, e `session.html`
     le marca `no-print` alla sorgente. */
  .session-meta,
  .guardrail {
    border: 1px solid #999;
    break-inside: avoid;
    page-break-inside: avoid;
  }

  a[href]::after {
    content: none;
  }
}

/* ==========================================================================
   Scheda G+S «Preparazione lezione inline» (v1.22) — stampa e anteprima

   Riproduce l'impaginazione del modello ufficiale UFSPO (docs/gs-thb/,
   foglio omonimo): testata, tre colonne nelle proporzioni dell'originale
   (9% / 48% / 43% della larghezza utile), piede con Formazione,
   Osservazioni e Giudizio all.

   Quello che NON riproduce, per scelta: la rigatura fissa a 12pt su 40
   righe. Quel foglio è pensato per essere riempito a penna; la nostra
   scheda arriva già scritta, e un esercizio con svolgimento, punti chiave
   ed errori comuni non entra in una riga da 12pt. Righe alte quanto il
   contenuto — vedi mockup/README-scheda-gs-print.md per il confronto con
   la variante a griglia rigida, disegnata e scartata.

   Tutto in `pt`/`cm`: sono le unità della pagina fisica. Le stesse unità
   valgono a schermo (1cm CSS = 96dpi/2.54), che è la ragione per cui
   l'anteprima di `?anteprima=1` può essere fedele senza una seconda resa:
   il foglio è **questo**, disegnato una volta sola.

   (v1.23) Il blocco viveva dentro `@media print`, e il foglio era quindi
   invisibile fino al dialogo di stampa. Portato fuori: tutti i selettori
   qui sotto sono `.gs-*` e non toccano nient'altro, quindi a schermo non
   cambia niente finché `.print-only` resta `display: none`. È
   l'interruttore di **visibilità** a decidere quando il foglio si vede —
   in stampa sempre, a schermo solo in anteprima — non più il fatto che
   queste regole esistano.
   ========================================================================== */

.gs-sheet {
  page: gs-sheet;
  font: var(--typep-body);
  color: #000;
}

/* --- Testata --- */

.gs-masthead {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  font: var(--typep-caption);
  color: #555;
  margin-bottom: 6pt;
}

.gs-masthead-right {
  text-align: right;
}

.gs-title {
  font: var(--typep-screen);
  text-align: center;
  margin: 4pt 0 8pt 0;
  border-bottom: 1.5pt solid #000;
  padding-bottom: 6pt;
}

/* I quattro campi d'identità dell'originale su una riga sola. Il filo
   punteggiato sotto ogni valore serve anche quando il valore c'è: dice
   che quello è un campo del modulo, e lascia scrivere sopra a chi
   corregge a bordo pista. */
.gs-identity {
  display: flex;
  flex-wrap: wrap;
  gap: 4pt 18pt;
  font: var(--typep-caption);
  margin: 0 0 10pt 0;
}

.gs-identity dt {
  display: inline;
  font-weight: 700;
}

.gs-identity dd {
  display: inline;
  margin: 0 0 0 3pt;
  min-width: 3cm;
  border-bottom: 0.75pt dotted #555;
  padding-bottom: 1pt;
}

/* Non è un campo dell'originale, che dell'obiettivo non fa un blocco a
   sé. Sta qui perché VISION §9 vuole che l'obiettivo della seduta si
   legga per primo, e nella tabella finirebbe disperso fra gli esercizi. */
.gs-objective {
  font: var(--typep-body-strong);
  border-left: 3pt solid #000;
  padding: 5pt 8pt;
  margin: 0 0 10pt 0;
}

/* Il concetto del giorno, sotto l'obiettivo (v1.44, issue #36): è la
   stessa cosa detta al giocatore invece che all'allenatore. Una riga sola,
   perché è la riga che si rilegge a bordo pista senza il telefono in mano
   — e perché la regola sta in quindici parole per costruzione
   (`ConceptRule`).

   Nessun riquadro pieno: il foglio esce in bianco e nero, e il filetto
   sottile basta a legarlo all'obiettivo qui sopra senza rifare un secondo
   blocco nero. */
.gs-concept {
  font: var(--typep-body);
  border-left: 1pt solid #000;
  padding: 0 8pt;
  margin: -6pt 0 10pt 0;
}

.gs-concept-name {
  font-weight: 700;
}

/* Il secondo concetto — l'eccezione di quando lo stesso momento di gioco si
   spiega in due frasi — prende la sua riga. Il primo resta accanto
   all'etichetta, che è il caso normale. */
.gs-concept-line + .gs-concept-line {
  display: block;
}

/* --- Corpo: la tabella a tre colonne --- */

.gs-table {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
}

col.gs-col-tempo {
  width: 9%;
}

col.gs-col-obiettivi {
  width: 48%;
}

col.gs-col-disegno {
  width: 43%;
}

/* Il motore di impaginazione ripete `thead` a ogni pagina da solo: è il
   motivo per cui questa è una `<table>` vera e non una griglia CSS. */
.gs-table thead {
  display: table-header-group;
}

/* Striscia d'identità in cima alle pagine di continuazione. Non c'è
   nell'originale — che sta su una pagina sola — ed è la sola aggiunta
   che una scheda già scritta rende necessaria: un foglio staccato deve
   dire di chi è. */
.gs-identity-repeat td {
  padding: 3pt 4pt;
  font: var(--typep-eyebrow);
  letter-spacing: var(--typep-eyebrow-ls);
  color: #555;
  border-bottom: 0.75pt solid #999;
}

.gs-col-headers th {
  font: var(--typep-eyebrow);
  letter-spacing: var(--typep-eyebrow-ls);
  text-transform: uppercase;
  text-align: left;
  padding: 5pt 6pt;
  border: 0.75pt solid #000;
  border-top-width: 1.5pt;
}

.gs-table td {
  vertical-align: top;
  padding: 5pt 6pt;
  border: 0.75pt solid #999;
  font: var(--typep-body);
}

.gs-table .gs-col-tempo {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
}

/* Un esercizio non si taglia a metà fra due pagine: o ci sta, o passa
   intero alla successiva. */
.gs-exercise-row {
  break-inside: avoid;
  page-break-inside: avoid;
}

/* Riga di fase a piena larghezza. L'originale non ce l'ha (affida tutto
   alla colonna Tempo), ma senza di essa la struttura della seduta —
   riscaldamento, parte principale, parte finale — sparisce, ed è la
   prima cosa che si cerca guardando il foglio a bordo pista. */
.gs-phase-row td {
  background: #000;
  color: #fff;
  font: var(--typep-section);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: 5pt 6pt;
  border: none;
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}

.gs-phase-duration {
  font-weight: 400;
  text-transform: none;
  margin-left: 6pt;
}

/* Il nome dell'esercizio è un `<h4>` nel markup, e la regola globale su
   `h4` lo vestirebbe da occhiello — maiuscoletto e grigio. Qui è il nome
   della cosa, non l'etichetta di ciò che segue: si riprende la cassa e
   l'inchiostro. Sul foglio stampato il grigio sarebbe anche il colore
   sbagliato: la scheda va in bianco e nero. */
.gs-exercise-title {
  font: var(--typep-title);
  text-transform: none;
  letter-spacing: normal;
  color: #000;
  margin: 0 0 2pt 0;
}

.gs-new-badge {
  font: var(--typep-eyebrow);
  letter-spacing: var(--typep-eyebrow-ls);
  text-transform: uppercase;
  border: 0.5pt solid #555;
  padding: 1pt 4pt;
  border-radius: 3pt;
  margin-left: 4pt;
}

.gs-exercise-meta {
  font: var(--typep-caption);
  color: #555;
  margin: 0 0 4pt 0;
}

.gs-exercise-goal {
  margin: 0 0 4pt 0;
}

.gs-subhead {
  display: block;
  font: var(--typep-eyebrow);
  letter-spacing: var(--typep-eyebrow-ls);
  text-transform: uppercase;
  color: #555;
  margin: 4pt 0 1pt 0;
}

.gs-table ul,
.gs-table ol {
  margin: 0 0 3pt 0;
  padding-left: 1.15em;
}

.gs-table li {
  margin-bottom: 1.5pt;
}

/* Le domande di riflessione, in coda alla parte finale (v1.44, issue #36).
   Una riga della tabella come le altre, non un blocco in fondo al foglio:
   le domande si fanno lì, mentre si defatica. */
.gs-reflection-row td {
  border-top: 0.5pt dashed #555;
}

.gs-goalkeeper-note {
  display: inline-block;
  font: var(--typep-caption);
  border: 0.5pt solid #000;
  padding: 1pt 5pt;
  border-radius: 3pt;
  margin: 3pt 0 0 0;
}

/* Attribuzione senza `<a>`: su carta un link non si clicca, e l'URL per
   esteso mangerebbe mezza colonna. */
.gs-attribution {
  font: var(--typep-caption);
  color: #555;
  margin: 3pt 0 0 0;
}

.gs-no-diagram {
  font: var(--typep-caption);
  color: #999;
}

.gs-col-disegno .rink-diagram,
.gs-exercise-image {
  width: 100%;
  max-width: 100%;
  height: auto;
}

.gs-key-points {
  font: var(--typep-body);
  margin: 6pt 0 0 0;
}

/* Materiale: elenco compatto nel piede, sotto «Osservazioni». Due colonne
   perché una seduta con otto esercizi produce facilmente una decina di
   voci corte, e in colonna singola sprecherebbe metà riga per ognuna. */
.gs-footer-equipment ul {
  margin: 0;
  padding-left: 1.1em;
  columns: 2;
  column-gap: 10pt;
  font: var(--typep-caption);
}

.gs-footer-equipment li {
  margin-bottom: 1pt;
  break-inside: avoid;
}

/* Con il materiale nel piede le righe delle osservazioni si stringono:
   restano tre righe da scrivere, che è quanto serve a una nota a caldo. */
.gs-footer-notes .gs-ruled-lines {
  min-height: 2.2cm;
}

/* --- Piede: Formazione, Osservazioni, Giudizio all. --- */

.gs-footer {
  display: flex;
  gap: 12pt;
  margin-top: 10pt;
  break-inside: avoid;
  page-break-inside: avoid;
}

.gs-footer-formation {
  flex: 0 0 58%;
}

.gs-footer-side {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 8pt;
}

.gs-footer h3 {
  font: var(--typep-eyebrow);
  letter-spacing: var(--typep-eyebrow-ls);
  text-transform: uppercase;
  border-bottom: 1pt solid #000;
  padding-bottom: 2pt;
  margin: 0 0 5pt 0;
}

.gs-formation-row {
  display: flex;
  align-items: center;
  gap: 8pt;
  margin-bottom: 5pt;
}

.gs-formation-label {
  flex: 0 0 2cm;
  font: var(--typep-eyebrow);
  letter-spacing: var(--typep-eyebrow-ls);
}

.gs-formation-goalies {
  display: flex;
  gap: 6pt;
}

.gs-formation-slot {
  position: relative;
  width: 2.4cm;
  height: 1cm;
  border: 0.75pt solid #555;
  border-radius: 3pt;
}

.gs-formation-line {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 2pt;
  flex: 1;
}

.gs-formation-cell {
  position: relative;
  height: 0.85cm;
  border: 0.75pt solid #555;
  border-radius: 3pt;
}

.gs-slot-tag {
  position: absolute;
  top: 1pt;
  left: 2pt;
  font: var(--typep-eyebrow);
  letter-spacing: var(--typep-eyebrow-ls);
  color: #555;
}

/* Righe su cui scrivere a mano: sono la resa a stampa di un campo che si
   compila dopo l'allenamento, non un segnaposto per un dato mancante. */
.gs-ruled-lines {
  min-height: 3.2cm;
  background-image: repeating-linear-gradient(
    to bottom,
    transparent,
    transparent 0.72cm,
    #555 0.72cm,
    #555 calc(0.72cm + 0.75pt)
  );
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}

.gs-notes-written {
  font: var(--typep-body);
}

.gs-notes-written p {
  margin: 0 0 3pt 0;
}

.gs-footer-box {
  min-height: 1.6cm;
  border: 0.75pt solid #555;
  border-radius: 3pt;
}

/* ==========================================================================
   Anteprima di stampa della scheda (v1.23) — `GET /sessions/:id?anteprima=1`

   Perché esiste: la scheda G+S è il formato in cui il lavoro dell'app esce
   davvero, e fino alla v1.22 l'unico modo di vederla era aprire il dialogo
   di stampa — cioè impegnarsi a stampare, o a produrre un file, per
   scoprire com'era venuta. L'anteprima rimuove quel pedaggio.

   Non è una seconda resa: mostra **lo stesso** markup `.gs-sheet` con **le
   stesse** regole di sopra, su un foglio largo quanto un A4 vero. Quello
   che l'anteprima NON sa mostrare sono i salti pagina: il CSS a schermo non
   pagina, quindi qui il foglio è un nastro continuo. Impaginazione,
   proporzioni e tipografia sono esatte; "questo esercizio finisce a pagina
   2" lo sa solo la stampa. Vedi TODO.md per il PDF lato server, che quel
   limite lo toglierebbe.
   ========================================================================== */

/* Fondo grigio attorno al foglio: dice "questa è carta, non è la pagina
   web". Lo stesso mestiere che fa il grigio dietro l'anteprima di Chrome. */
.sheet-preview {
  background: #55585f;
  margin: 0 calc(-1 * var(--space-4));
  padding: var(--space-5) var(--space-4);
  /* Sotto i ~21cm di larghezza il foglio non entra. Scorre questa fascia,
     non la pagina intera: il resto della shell resta al suo posto. */
  overflow-x: auto;
}

/* In anteprima la scheda a schermo (card, form di rigenerazione, meta)
   sparisce: si guarda il foglio, non due copie della stessa seduta. */
.sheet-preview .no-print {
  display: none;
}

/* ...tranne la barra delle azioni, che è il solo modo di uscire
   dall'anteprima e di lanciare la stampa. Selettore per id: vince su
   `.sheet-preview .no-print` per specificità, senza `!important`.
   (v1.25) Due id e non una classe condivisa: sono due barre distinte, in due
   pagine, e un id resta il selettore che vince senza `!important`. */
.sheet-preview #print-actions,
.sheet-preview #player-print-actions {
  /* `display` e non `visibility`: serve a battere `.sheet-preview
     .no-print`. Il resto (flex, gap, a capo) lo porta già `.page-actions`.
     Allineata al foglio, non alla colonna: stessa larghezza, stesso asse. */
  display: flex;
  max-width: 21cm;
  margin: 0 auto var(--space-4) auto;
  color: #fff;
}

.sheet-preview #print-actions .field-hint,
.sheet-preview #player-print-actions .field-hint {
  color: #d3d6dc;
}

.sheet-preview .print-only {
  display: block;
}

/* Il foglio: A4 esatto, con i margini di `@page gs-sheet` resi come
   padding. `box-sizing: border-box` perché 21cm è la carta, non l'area di
   testo — gli stessi 21cm che escono dalla stampante. */
.sheet-preview .gs-sheet {
  box-sizing: border-box;
  width: 21cm;
  min-height: 29.7cm;
  padding: 1.1cm 1cm 0.8cm 1.5cm;
  margin: 0 auto;
  background: #fff;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.35);
}

/* Nota esplicativa sotto un campo di form: serve dove l'etichetta da sola
   si presta a essere fraintesa (es. il "giorno d'allenamento" G+S, che in
   italiano suona come una seduta qualsiasi ma vale solo per attività di
   240-300 minuti). */
.field-hint {
  margin: calc(-1 * var(--space-2)) 0 0 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* Il risalire negativo qui sopra serve a incollare l'aiuto al campo che
   spiega. Dopo una tabella non c'è nessun campo a cui incollarsi: la nota
   è della tabella intera, e quegli 8px la portavano **sopra** l'ultima
   riga — in Categorie il minimo del referto (`SHEET_LEGEND`) finiva
   scritto attraverso i Veterani. Stessa correzione di
   `.season-anchor-summary` per il sottotitolo, e per la stessa ragione. */
.table-scroll + .field-hint {
  margin-top: var(--space-3);
}


/* --- (v1.23 → v1.27) Famiglie di temi su superficie scura -----------------
   Qui c'era un blocco `@media (prefers-color-scheme: dark)` che ridefiniva
   le sette tinte di famiglia. **Rimosso**, e non perché le tinte fossero
   sbagliate.

   L'app ha un solo `:root` chiaro. Quel blocco si attivava da solo su un
   sistema impostato scuro e cambiava il colore dei temi **dentro una pagina
   che restava bianca**: non un tema scuro, un pezzo di tema scuro acceso da
   sé in mezzo a un tema chiaro. La timeline di gennaio diventava di un blu
   diverso da quella di chi ha il sistema in chiaro, senza che nessuno lo
   avesse deciso e senza che si potesse vedere provando l'app.

   È la stessa lezione già scritta poco più sotto per la rampa ordinale, e
   vale identica qui: le variabili di colore si commutano **insieme** al
   tema della pagina, non prima.

   Le tinte verificate non si perdono, stanno in `ThemeFamily::color_dark`
   in core — che è dove erano già, e che questo blocco si limitava a
   copiare. Il giorno in cui l'app avrà un tema scuro suo, si riprendono di
   lì. `--fam-atletica` è deliberatamente identica alla sua tinta chiara:
   quel verde regge entrambe le superfici, e ritoccarlo lo avvicinerebbe a
   `--fam-recupero`. --- */

/* --- (v1.24) I neutri della timeline **non** commutano con
   `prefers-color-scheme`.

   (v1.27) Questo commento diceva «e le tinte di famiglia qui sopra sì»,
   come se le due cose facessero apposta scelte diverse. Facevano scelte
   diverse, ma la sbagliata era l'altra: ora nessuna delle due commuta, e la
   ragione scritta qui sotto — buona già allora — vale per entrambe.

   I neutri **invertono** la chiarezza, e finché il resto della pagina non
   ha un tema scuro proprio (§10bis.4) il risultato è una corsia quasi nera
   dentro una card bianca. Provato in Chrome con il sistema in modalità
   scura: la riga di ogni tema diventava un tratto d'inchiostro.

   I valori scuri esistono e sono stati verificati insieme a quelli chiari —
   `#232322` / `#2d2d2b` / `#26261f` / `#3d3d38` / `#75756e`, con
   `--timeline-today: #f4f4f0` — e vanno attivati **insieme** al tema scuro
   dell'app, non prima. Sono scritti qui perché ritrovarli fra sei mesi
   costerebbe di nuovo la verifica. --- */

/* --- (v1.23) Tema imposto alla generazione ------------------------------
   Il chip che dice, sopra il form della home, che si sta generando su un
   tema scelto altrove (`?theme=<slug>`). Il colore della famiglia sta sul
   nome del tema e non sullo sfondo del chip: qui il colore identifica il
   tema, e riempirci un rettangolo lo farebbe leggere come uno stato. */
.focus-theme-chip {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: 0 0 var(--space-3);
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
}

.focus-theme-chip-label {
  color: var(--color-text-muted);
}

.focus-theme-chip strong {
  /* La tinta di famiglia, scelta dalla classe `.fam-*` sull'elemento. Il
     fallback non è decorativo: se un giorno si aggiunge una famiglia in core
     senza la sua variabile qui, il nome resta leggibile invece di sparire. */
  color: var(--fam, var(--color-text));
}

.focus-theme-chip-clear {
  color: var(--color-text-muted);
  font: var(--type-caption);
}

/* --- (v1.37, issue #1) La capienza del tema ------------------------------
   Il tema imposto filtra solo la parte principale, e con pochi candidati la
   scheda esce corta senza dirlo. La frase sta dentro la stessa pastiglia del
   nome, non in un banner separato: è una precisazione su quel tema lì, e
   staccarla la trasformerebbe in un secondo messaggio da leggere.

   Il colore non è l'unico segnale — la frase dice per esteso quanti esercizi
   ci sono, e nei due casi d'avviso porta l'icona del triangolo. Chi non
   distingue l'ambra dal grigio legge comunque tutto. */
.focus-theme-chip-capacity {
  color: var(--color-text-muted);
  font: var(--type-caption);
}

/* L'icona resta inline con `vertical-align` e non dentro un `inline-flex`:
   la pastiglia allinea i suoi pezzi sulla linea di base, e la linea di base
   di un contenitore flex è quella del suo primo elemento — un'icona, che di
   linea di base non ne ha. La frase finirebbe fuori asse rispetto al nome
   del tema. */
.focus-theme-chip-capacity svg {
  vertical-align: -0.18em;
  margin-right: var(--space-1);
}

/* Due sole regole per tre stati: la forma **capiente** è questo stato base e
   non porta alcuna classe (`FocusCapacityLevel::class`), perché una classe
   senza regola qui sarebbe un appiglio che promette uno stile che non esiste.

   Magro: la parte principale uscirà più corta dei minuti chiesti. */
.focus-theme-chip-short .focus-theme-chip-capacity {
  color: var(--color-warning-text);
}

/* Vuoto: la generazione fallirà. Un fondo, perché questo non è un dettaglio
   sulla scheda che verrà — è la scheda che non verrà. */
.focus-theme-chip-empty .focus-theme-chip-capacity {
  color: var(--color-danger-text);
  background: var(--color-danger-bg);
  border-radius: var(--radius-sm);
  padding: 0 var(--space-2);
}

/* Ogni famiglia espone la sua tinta come `--fam`, così qualunque elemento
   discendente la usa senza sapere quale famiglia sia. È il meccanismo su cui
   si appoggiano sia il chip qui sopra sia le righe della timeline. */
.fam-tecnica { --fam: var(--fam-tecnica); }
.fam-costruzione { --fam: var(--fam-costruzione); }
.fam-recupero { --fam: var(--fam-recupero); }
.fam-transizione { --fam: var(--fam-transizione); }
.fam-gioco { --fam: var(--fam-gioco); }
.fam-atletica { --fam: var(--fam-atletica); }
.fam-portiere { --fam: var(--fam-portiere); }


/* ==========================================================================
   (v1.24) Roster e valutazioni
   ========================================================================== */

/* --- La rampa ordinale ---------------------------------------------------
   Un solo tono, dal chiaro allo scuro, per una scala a quattro livelli. Mai
   una palette a colori diversi per livello — quella dice "categorie", non
   "quantità" — e mai una scala divergente, che implicherebbe un polo
   negativo e un neutro che su una progressione di apprendimento non
   esistono.

   Rispecchia `SkillLevel::color_light`/`color_dark` in core, stesso rapporto
   già in uso fra `ThemeFamily::color_light` e le `--fam-*`.

   **Regola vincolante** (ARCHITECTURE.md §10bis.2): una superficie usa
   questa rampa **oppure** le `--fam-*`, mai entrambe. Due passi qui sotto
   ripetono per hex due tinte di famiglia, e sulla stessa superficie lo
   stesso blu significherebbe sia "livello 2" sia "tecnica individuale".
   Nella heatmap comandano queste; il colore di famiglia compare solo sui
   riquadri di sintesi, dove nessun livello è colorato. --- */
:root {
  --lvl-1: #86b6ef;
  --lvl-2: #3987e5;
  --lvl-3: #1c5cab;
  --lvl-4: #0d3566;
  /* Il testo dentro una cella colorata: scuro sui due passi chiari, chiaro
     sui due scuri. Non un colore solo con opacità — il contrasto va
     verificato su entrambi gli estremi, e a metà rampa fallirebbe. */
  --lvl-1-ink: #0b2440;
  --lvl-2-ink: #ffffff;
  --lvl-3-ink: #ffffff;
  --lvl-4-ink: #ffffff;
  /* Il vuoto della heatmap: una tinta neutra chiarissima, distinta sia dal
     primo passo della rampa sia dalla superficie della card, così che
     "non valutata" si veda come uno stato e non come un buco nel disegno. */
  --lvl-empty: #f0f1f4;
  --lvl-na: #fafbfc;
  /* Il tratteggio di «non pertinente». Non un grigio più chiaro di
     `--lvl-empty`: due grigi vicini si distinguono solo mettendoli uno
     accanto all'altro, e nella matrice non capita mai. Un motivo invece si
     riconosce da solo, e sopravvive alla fotocopia in bianco e nero, che è
     come questa pagina finisce sulla carta di chi la stampa.

     La tinta è volutamente debolissima, e la prima prova l'aveva sbagliata.
     A #d5d9e0 una cella isolata si leggeva bene, ma le celle non pertinenti
     non arrivano mai isolate: un portiere dentro un gruppo di giocatori di
     movimento le accende tutte insieme — cinque colonne per dodici righe —
     e le diagonali si allineavano da una cella all'altra formando un campo
     di righe continue che tirava l'occhio più dei numeri accanto. Questo
     motivo deve dire «qui non c'è niente da guardare», e per dirlo va visto
     soltanto quando lo si cerca.

     `--lvl-na-key` è lo stesso motivo per la pastiglia della legenda, dove
     la tinta debole non basta: un quadratino di 16 px va riconosciuto da
     solo e fuori contesto, come il campione di retino sulla legenda di una
     carta, che è sempre più fitto di quanto lo sia sulla carta. */
  --lvl-na-line: #e5e8ed;
  --lvl-na-key: #c9cfd8;
}

/* --- Perché qui NON c'è un blocco `prefers-color-scheme: dark` -----------
   Trovato provando la pagina in un browser impostato su tema scuro. L'app
   ha un solo `:root` chiaro (§10bis.4): il resto della pagina resta chiaro
   qualunque cosa dica il sistema. Un blocco scuro per queste variabili si
   sarebbe attivato **da solo**, dipingendo celle quasi nere e una rampa
   invertita dentro una pagina per il resto bianca — non un tema scuro, un
   errore.

   Le tinte scure esistono già, verificate, in `SkillLevel::color_dark`: è
   lì che aspettano il giorno in cui l'intera pagina avrà un tema scuro, che
   è l'unico momento in cui applicarle ha senso. Il blocco `--fam-*` più
   sopra se lo può permettere perché quelle tinte vestono testo e bordi di
   pochi elementi; questa rampa dipinge superfici grandi, e sbagliarla si
   vede subito.

   Se un giorno si aggiunge il tema scuro, questo è uno dei punti da
   riaprire. --- */

.lvl-1 { background: var(--lvl-1); color: var(--lvl-1-ink); }
.lvl-2 { background: var(--lvl-2); color: var(--lvl-2-ink); }
.lvl-3 { background: var(--lvl-3); color: var(--lvl-3-ink); }
.lvl-4 { background: var(--lvl-4); color: var(--lvl-4-ink); }

/* --- Elementi di pagina condivisi da queste viste ------------------------ */

.card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  margin: var(--space-4) 0;
}

.card > h2:first-child {
  margin-top: 0;
}

.section-hint,
.empty-state-hint {
  color: var(--color-text-muted);
  font: var(--type-caption);
  margin: 0 0 var(--space-3);
}

.muted-cell {
  color: var(--color-text-muted);
  font: var(--type-caption);
}

.tag-chip {
  display: inline-block;
  background: var(--color-primary-soft);
  color: var(--color-primary-dark);
  border-radius: var(--radius-pill);
  padding: 0 var(--space-2);
  margin-right: var(--space-1);
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  white-space: nowrap;
}

/* Uno stato non "attivo" non deve leggersi come una spunta verde: chi è
   inattivo o infortunato spiega da solo perché la sua riga è vuota. */
.state-open,
.state-injured { background: var(--color-warning-bg); color: var(--color-warning-text); }
.state-trial { background: var(--color-badge-bg); color: var(--color-badge-text); }
.state-inactive { background: var(--color-bg); color: var(--color-text-muted); }

.banner-info {
  background: var(--color-primary-soft);
  border-left: 4px solid var(--color-primary);
  color: var(--color-text);
}

.banner-ok {
  background: var(--color-ok-bg);
  border-left: 4px solid var(--color-ok-border);
  color: var(--color-ok-text);
}

.field-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.field-row > label {
  flex: 1 1 12rem;
}

.checkbox-group {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* --- Il numero di maglia -------------------------------------------------
   Un badge quadrato e monospaziato per numeri: due cifre e una cifra devono
   occupare la stessa larghezza, altrimenti la colonna balla. */
.jersey-number {
  display: inline-block;
  min-width: 2rem;
  padding: 0 var(--space-1);
  background: var(--color-text);
  color: var(--color-surface);
  border-radius: var(--radius-sm);
  font: var(--type-figure-sm);
  /* Dopo la scorciatoia, mai prima: `font` azzera `font-variant-numeric`. */
  font-variant-numeric: tabular-nums;
  text-align: center;
}

.jersey-number-none {
  background: transparent;
  color: var(--color-text-muted);
  font: var(--type-caption);
}

.jersey-number-lg {
  font: var(--type-figure);
  min-width: 2.75rem;
  vertical-align: middle;
}

.roster-number-col {
  width: 3.5rem;
}

/* Una tabella più larga dello schermo scorre dentro il suo `.table-scroll`,
   non allarga la pagina: allargandola, sul telefono il viewport di layout
   cresce con lei e la barra in basso, fissa e centrata su quella larghezza,
   esce dallo schermo (audit mobile del 15.09.2026: roster, scheda giocatore,
   categorie, tabelle rapide). Heatmap, carte della rilevazione e Speed Lap
   hanno una regola di scorrimento loro, più avanti. La tabella di taggatura
   no, apposta: vedi «Layout B», la sua testata sticky vuole la finestra.
   `position: relative` perché un discendente in posizione assoluta (i
   `.visually-hidden` nelle testate) resti dentro lo scorrimento: senza,
   prendeva come riferimento un antenato più su e allargava la pagina lo
   stesso. */
.table-scroll {
  position: relative;
  overflow-x: auto;
}

.tagging-form .table-scroll {
  overflow: visible;
}

.roster-table,
.progress-table,
.profile-table,
.dumbbell-table {
  width: 100%;
  border-collapse: collapse;
}

.roster-table th,
.roster-table td,
.progress-table th,
.progress-table td,
.profile-table th,
.profile-table td,
.dumbbell-table th,
.dumbbell-table td {
  text-align: left;
  padding: var(--space-2);
  border-bottom: 1px solid var(--color-border);
  vertical-align: middle;
}

/* --- L'anagrafica (v1.54, issue #44) --------------------------------------
   Una tabella vera da 900px, e **la stessa** tabella impilata sotto: in
   grande «Cognome Nome», sotto in piccolo «anno · squadra · licenza», a
   destra «Modifica».

   Qui la regola è `max-width` e non `min-width` come in quasi tutto il
   foglio, di proposito: la tabella è la forma che il markup ha già, e
   costruirla da mobile in su vorrebbe dire rimettere a mano ogni
   `display: table-*` a 900px. Si scrive una volta la tabella e una volta
   la sua eccezione. */

.persons-table {
  width: 100%;
  /* `separate` e non `collapse`: con le celle fuse il bordo arrotondato
     del contenitore non si disegna. */
  border-collapse: separate;
  border-spacing: 0;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.persons-table th,
.persons-table td {
  text-align: left;
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--color-border);
  vertical-align: middle;
}

.persons-table tbody tr:last-child > * {
  border-bottom: 0;
}

.persons-table thead th {
  font: var(--type-caption);
  font-weight: 600;
  color: var(--color-text-muted);
}

.persons-table .person-born,
.persons-table .person-license {
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}

/* Grigio per chi la squadra l'ha avuta e non l'ha più, o non l'ha mai
   avuta: la cella dice comunque qualcosa, e il colore dice che è passato. */
.persons-table .person-team-past,
.persons-table .person-team-none {
  color: var(--color-text-muted);
}

.persons-table .person-team-note {
  margin-left: var(--space-1);
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* (v1.97) «da chiedere» dove ci sarebbe l'anno di nascita. Stesso
   trattamento di «mai tesserato» qui sopra — inchiostro spento e una
   parola, mai un colore: manca un dato, non è un allarme, e l'ambra e il
   rosso sono degli avvisi. La cifra sta in cifre tabellari, la parola no:
   senza questa riga «da chiedere» eredita `tabular-nums` dalla cella e le
   spaziature delle lettere si allargano. */
.persons-table .person-born-none {
  font-variant-numeric: normal;
  white-space: nowrap;
}

.persons-table .person-actions {
  text-align: right;
  white-space: nowrap;
}

/* Un cognome doppio e «Novizi · U16 · 2024/25» non vanno a capo: spezzati
   in due righe, la stagione finiva sotto la squadra come se fosse un altro
   dato. Il posto lo si prende dal padding, qui sotto. */
.persons-table .person-last-name,
.persons-table .person-team-past,
.persons-table .person-team-none {
  white-space: nowrap;
}

/* Sette colonne in 52rem: fra una cella e l'altra basta mezzo rem per
   lato, e il rem pieno resta solo sui due bordi della tabella. Misurato
   con i dati veri: a `--space-3` per lato la tabella sforava di 17px. */
@media (min-width: 900px) {
  .persons-table th,
  .persons-table td {
    padding-left: var(--space-2);
    padding-right: var(--space-2);
  }

  .persons-table tr > :first-child {
    padding-left: var(--space-3);
  }

  .persons-table tr > :last-child {
    padding-right: var(--space-3);
  }
}

@media (max-width: 899px) {
  .persons-table,
  .persons-table tbody {
    display: block;
  }

  /* Le intestazioni restano per il lettore di schermo: a chi guarda, la
     riga si spiega da sé. */
  .persons-table thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  /* Una riga flessibile che va a capo dopo il nome: le celle si mettono
     in fila con `order`, perché sul telefono la squadra viene prima della
     licenza e nel markup no. */
  .persons-table tr {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: 0.35em;
    padding: var(--space-3);
    /* Il posto di «Modifica», che sta a destra fuori dal flusso. */
    padding-right: 7rem;
    border-bottom: 1px solid var(--color-border);
  }

  .persons-table tbody tr:last-child {
    border-bottom: 0;
  }

  .persons-table th,
  .persons-table td {
    display: block;
    padding: 0;
    border: 0;
  }

  .persons-table .person-last-name,
  .persons-table .person-first-name {
    order: 1;
    font: var(--type-title);
    color: var(--color-primary-dark);
  }

  /* Il cognome è il link, ma sul telefono lo è tutta la riga: il nome
     accanto ha il colore del link, e il bersaglio di tocco è la riga
     intera — più grande di una parola, che è ciò che serve a bordo pista. */
  .persons-table .person-last-name a {
    text-decoration: none;
  }

  .persons-table .person-last-name a::after {
    content: "";
    position: absolute;
    inset: 0;
  }

  /* L'a capo fra il nome e i dati. */
  .persons-table tr::before {
    content: "";
    flex-basis: 100%;
    order: 2;
  }

  .persons-table .person-born,
  .persons-table .person-team,
  .persons-table .person-license {
    font: var(--type-caption);
    color: var(--color-text-muted);
  }

  .persons-table .person-born { order: 3; }
  .persons-table .person-team { order: 4; }
  .persons-table .person-license { order: 5; }

  /* Il separatore è decorazione: il testo alternativo vuoto lo toglie al
     lettore di schermo. */
  .persons-table .person-team::before {
    content: "· " / "";
  }

  .persons-table .person-license::before {
    content: "· licenza " / "licenza ";
  }

  .persons-table .person-license:empty {
    display: none;
  }

  .persons-table .person-team-note {
    margin-left: 0;
  }

  .persons-table .person-actions {
    position: absolute;
    top: 50%;
    right: var(--space-3);
    transform: translateY(-50%);
    /* Sopra il link steso sulla riga, o toccare «Modifica» aprirebbe la
       persona. */
    z-index: 1;
  }
}

/* --- I numeri dell'anagrafica (15.09.2026) ---------------------------------
   Dal mockup approvato (`docs/proposta-anagrafica/`): cinque schede in fila,
   il grafico delle annate accanto all'anno prossimo, le pastiglie sopra la
   tabella. La disposizione la decide la larghezza della testa, non dello
   schermo: `.census` è un contenitore, e le soglie sono `@container`.

   La tinta di una categoria la porta una variabile (`--census-tint`), messa
   dalla classe `census-tone-N` e letta da chi ne ha bisogno — riempimento
   per barre e quadratini, bordo per le fasce. Una rampa a un tono del
   petrolio perché le categorie sono in ordine d'età; le `--fam-*` restano
   ai temi. Sei passi, dal più chiaro: quattro categorie prendono 0, 2, 3 e
   5 (`census_views::tone`). */

.app-main-persons {
  max-width: 80rem;
}

.census-tone-0 { --census-tint: #A3DAD0; }
.census-tone-1 { --census-tint: #78C2B6; }
.census-tone-2 { --census-tint: #4FA89B; }
.census-tone-3 { --census-tint: #0F766E; }
.census-tone-4 { --census-tint: #0C5A54; }
.census-tone-5 { --census-tint: #0A3F3B; }
.census-none { --census-tint: #CDD2D9; }
/* Chi esce e chi non si sa, nell'anno prossimo: non sono una categoria, e
   non prendono un passo della rampa. */
.census-out {
  --census-fill: repeating-linear-gradient(135deg, #fff 0 3px, #8A929D 3px 4.5px);
  --census-edge: #8A929D;
}
.census-unknown {
  --census-fill: var(--color-surface);
  --census-edge: #9AA1AB;
}

.census-seg,
.census-spark > span,
.census-kpi-split > span,
.census i,
.census-pills i {
  background: var(--census-fill, var(--census-tint, transparent));
  box-shadow: inset 0 0 0 1px var(--census-edge, transparent);
}

.census {
  container-type: inline-size;
  display: grid;
  gap: var(--space-3);
  margin-top: var(--space-3);
}

/* Le schede: il totale in testa a tutta riga finché non ci stanno in fila. */
.census-kpis {
  display: grid;
  gap: 0.75rem;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.census-kpi-total {
  grid-column: 1 / -1;
}

@container (min-width: 34rem) {
  .census-kpis {
    grid-template-columns: repeat(var(--census-kpis), minmax(0, 1fr));
  }
}

@container (min-width: 52rem) {
  .census-kpis {
    grid-template-columns: 1.25fr repeat(var(--census-kpis), minmax(0, 1fr));
  }

  .census-kpi-total {
    grid-column: auto;
  }
}

a.census-kpi {
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: 0.85rem 1rem 0.8rem;
  color: inherit;
  text-decoration: none;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}

a.census-kpi:hover {
  border-color: var(--color-primary);
  box-shadow: 0 0 0 1px var(--color-primary);
}

.census-kpi-label {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.census-kpi-label i {
  flex: none;
  width: 10px;
  height: 10px;
  border-radius: 3px;
}

.census-kpi-value {
  margin-top: 0.2rem;
  font: 700 var(--t-6) / 1.05 var(--font-cond);
  font-variant-numeric: tabular-nums;
}

.census-kpi-total .census-kpi-value {
  font-size: var(--t-7);
}

.census-kpi-value small {
  margin-left: 0.3rem;
  font: var(--type-label);
  color: var(--color-text-muted);
}

.census-kpi-sub {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.census-kpi-split {
  display: flex;
  height: 8px;
  margin: 0.55rem 0 0.45rem;
  overflow: hidden;
  border-radius: 4px;
  background: #CDD2D9;
}

.census-kpi-split > span {
  display: block;
  height: 100%;
  box-shadow: none;
}

/* Le annate della categoria in piccolo: si vede subito se la base si
   assottiglia. L'annata vuota fra due piene è un tratteggio sulla linea. */
.census-spark {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 2.6rem;
  margin-top: auto;
  padding-top: 0.6rem;
}

.census-spark > span {
  flex: 1;
  min-height: 2px;
  border-radius: 2px 2px 0 0;
  box-shadow: none;
}

.census-spark > .census-spark-empty {
  background: repeating-linear-gradient(90deg, var(--color-border) 0 3px, transparent 3px 5px);
}

.census-spark-years {
  display: flex;
  justify-content: space-between;
  margin-top: 3px;
  font: var(--type-eyebrow);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}

/* Il grafico accanto all'anno prossimo, alla stessa base. */
.census-row {
  display: grid;
  gap: var(--space-3);
}

@container (min-width: 54rem) {
  .census-row {
    grid-template-columns: minmax(0, 1.9fr) minmax(17rem, 1fr);
  }
}

.census-card {
  min-width: 0;
  padding: var(--space-3) var(--space-4) calc(var(--space-3) + 2px);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}

@container (max-width: 34rem) {
  .census-card {
    padding: var(--space-3);
  }
}

.census-card-head {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-2) 0.75rem;
  margin-bottom: 0.9rem;
}

.census-card-head-tight {
  margin-bottom: 0.4rem;
}

.census-card-title {
  margin: 0;
  font: var(--type-title);
}

.census-card-sub {
  margin: 0.15rem 0 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* Le due stagioni: radio + CSS. Le radio stanno prima della testa e delle
   viste, che le leggono con `~`. */
.census-seasons {
  display: inline-flex;
  padding: 2px;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
}

.census-tab {
  padding: 0.35rem 0.75rem;
  font: var(--type-label);
  color: var(--color-text-muted);
  border-radius: var(--radius-pill);
  cursor: pointer;
}

.census-pick-now:checked ~ .census-card-head .census-tab-now,
.census-pick-next:checked ~ .census-card-head .census-tab-next {
  color: var(--color-text);
  background: var(--color-surface);
  box-shadow: 0 1px 2px rgba(16, 24, 40, 0.12);
}

.census-pick-now:focus-visible ~ .census-card-head .census-tab-now,
.census-pick-next:focus-visible ~ .census-card-head .census-tab-next {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.census-view-next,
.census-pick-next:checked ~ .census-view-now {
  display: none;
}

.census-pick-next:checked ~ .census-view-next {
  display: block;
}

/* Le colonne non scendono sotto 1,25rem: con molte annate, su un telefono,
   il grafico scorre invece di schiacciarsi. Il margine in alto è il posto
   dei numeri sopra la colonna più alta. */
.census-chart {
  --census-gap: 3px;
  --census-axis: 1.4rem;
}

.census-scroll {
  overflow-x: auto;
  padding-top: 1.2rem;
}

.census-scroll > * {
  min-width: calc(var(--census-cols) * 1.25rem);
}

.census-plot {
  position: relative;
  display: grid;
  grid-template-columns: repeat(var(--census-cols), minmax(0, 1fr));
  gap: var(--census-gap);
  align-items: end;
  height: 13rem;
  margin-left: var(--census-axis);
}

.census-gridline {
  position: absolute;
  left: 0;
  right: 0;
  height: 0;
  border-top: 1px solid #EDEFF2;
}

.census-gridline-base {
  border-top-color: #9AA1AB;
}

.census-gridline em {
  position: absolute;
  right: calc(100% + 6px);
  top: -0.55em;
  font: var(--type-eyebrow);
  font-style: normal;
  text-transform: uppercase;
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}

.census-col {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column-reverse;
  height: 100%;
  min-width: 0;
}

.census-seg {
  display: block;
  flex: none;
  width: 100%;
  border-top: 1px solid var(--color-surface);
}

.census-top {
  display: flex;
  justify-content: center;
  flex: none;
  padding-bottom: 3px;
  font: var(--type-eyebrow);
  text-transform: uppercase;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.census-plus {
  font-weight: 600;
  color: var(--color-text-muted);
}

.census-x {
  display: grid;
  grid-template-columns: repeat(var(--census-cols), minmax(0, 1fr));
  gap: var(--census-gap);
  margin-left: var(--census-axis);
  text-align: center;
  font: var(--type-eyebrow);
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
}

.census-years {
  padding-top: 0.4rem;
}

.census-ages {
  padding-top: 0.25rem;
  font-weight: 600;
  color: var(--color-text-muted);
}

.census-year-short {
  display: none;
}

@container (max-width: 40rem) {
  .census-year-full {
    display: none;
  }

  .census-year-short {
    display: inline;
  }
}

.census-bands {
  position: relative;
  height: 2.6rem;
  margin: 0.55rem 0 0 var(--census-axis);
}

.census-band {
  position: absolute;
  top: 0;
  height: 100%;
  padding-top: 3px;
  overflow: hidden;
  white-space: nowrap;
  border-top: 4px solid var(--census-tint, var(--color-border));
  font: var(--type-eyebrow);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.census-band small {
  display: block;
  font-size: inherit;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--color-text-muted);
}

.census-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1rem;
  margin-top: 0.75rem;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.census-legend span {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}

.census-legend i {
  width: 12px;
  height: 12px;
  border-radius: 3px;
}

.census-note {
  margin: 0.8rem 0 0;
  padding: 0.6rem 0.8rem;
  font: var(--type-caption);
  background: var(--color-bg);
  border-radius: var(--radius-sm);
}

/* L'anno prossimo. */
.census-proj-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto auto 3.2rem;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0;
  border-top: 1px solid var(--color-border);
  font-variant-numeric: tabular-nums;
}

.census-proj-row:first-child {
  border-top: 0;
}

.census-proj-name {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 0.45rem;
  min-width: 0;
  font: var(--type-body-strong);
}

.census-proj-name i {
  width: 10px;
  height: 10px;
  border-radius: 3px;
}

.census-proj-name small {
  font: var(--type-eyebrow);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* (16.09.2026) Perché una riga non si proietta, sotto il nome: come il
   `<small>` delle righe di servizio qui sotto, ma su una riga di categoria,
   che il suo `<small>` lo usa già per la sigla. */
.census-proj-name small.census-proj-why {
  display: block;
  flex-basis: 100%;
  font: var(--type-caption);
  letter-spacing: 0;
  text-transform: none;
  color: var(--color-text-muted);
}

.census-proj-now {
  font: 600 var(--t-3) / 1 var(--font-cond);
  color: var(--color-text-muted);
}

.census-proj-arrow {
  color: var(--color-text-muted);
}

.census-proj-next {
  min-width: 1.6rem;
  text-align: right;
  font: 700 var(--t-5) / 1 var(--font-cond);
}

.census-delta {
  justify-self: end;
  padding: 0.22rem 0.45rem;
  font: var(--type-eyebrow);
  text-transform: uppercase;
  color: var(--color-text-muted);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
  font-variant-numeric: tabular-nums;
}

.census-delta-up {
  color: var(--color-primary-dark);
  background: var(--color-primary-soft);
  border-color: var(--color-primary);
}

/* (17.09.2026) Il calo, in contrapposizione al verde: la terna `danger`, la
   stessa coppia di `.trend-peggiora` qui sotto, e nessuna tinta nuova.

   Non ce l'ha ogni calo: la categoria d'ingresso perde per costruzione, e lì
   la pastiglia resta di contorno — chi lo decide è
   `SocietyCensus::entry_category`, non il CSS.

   Il colore non porta mai il significato da solo: il segno «−» è testo vero,
   e in bianco e nero la pastiglia si legge com'è. */
.census-delta-down {
  color: var(--color-danger-text);
  background: var(--color-danger-bg);
  border-color: var(--color-danger-border);
}

.census-proj-aside .census-proj-name {
  font: var(--type-body);
  color: var(--color-text-muted);
}

.census-proj-aside .census-proj-name small {
  display: block;
  flex-basis: 100%;
  font: var(--type-caption);
  letter-spacing: 0;
  text-transform: none;
}

.census-readings {
  display: grid;
  gap: 0.5rem;
  margin: 0.9rem 0 0;
  padding: 0;
  list-style: none;
}

.census-readings li {
  position: relative;
  padding-left: 0.9rem;
  font: var(--type-caption);
}

.census-readings li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.5em;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--color-primary);
}

.census-fine {
  margin: 0.8rem 0 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* Il filtro della tabella: link veri, come i filtri del catalogo. */
.census-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: var(--space-4) 0 0.75rem;
  scroll-margin-top: var(--space-3);
}

.census-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.3rem 0.7rem;
  font: var(--type-label);
  color: var(--color-text);
  text-decoration: none;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
}

.census-pill:hover {
  border-color: var(--color-primary);
}

.census-pill b {
  font: 700 var(--t-2) / 1 var(--font-cond);
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}

.census-pill i {
  width: 9px;
  height: 9px;
  border-radius: 2px;
}

.census-pill.is-active {
  color: var(--color-chip-text);
  background: var(--color-chip-bg);
  border-color: var(--color-chip-border);
}

.census-pill.is-active b {
  color: var(--color-chip-text);
}

/* In stampa la stagione di oggi, e basta: niente scelta, niente filtro. */
@media print {
  .census-seasons,
  .census-pills,
  .census-pick-next:checked ~ .census-view-next,
  .census-view-next {
    display: none;
  }

  .census-pick-next:checked ~ .census-view-now {
    display: block;
  }
}

.jersey-history {
  list-style: none;
  padding: 0;
  margin: 0 0 var(--space-3);
}

.jersey-history li {
  padding: var(--space-1) 0;
  border-bottom: 1px solid var(--color-border);
}

.player-notes {
  white-space: pre-wrap;
  background: var(--color-bg);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
}

/* --- §6.1: profilo a barre ----------------------------------------------
   Barre orizzontali e non un radar: con nove assi la forma del poligono
   sembra significativa mentre l'ordine delle aree è arbitrario. */
.profile-bar-col {
  width: 40%;
  min-width: 10rem;
}

.profile-track {
  position: relative;
  height: 1.25rem;
  background: var(--lvl-empty);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.profile-bar {
  height: 100%;
  border-radius: var(--radius-sm);
}

.level-cell {
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* «non valutata» non è un livello: si scrive, non si colora. Il grigio
   basta a dirlo — il corsivo che stava qui lo diceva una seconda volta, e
   senza un taglio corsivo imbarcato usciva come un tondo inclinato. */
.not-rated {
  color: var(--color-text-muted);
}

/* --- §6.2: dumbbell ------------------------------------------------------ */

.dumbbell-axis-col {
  width: 45%;
  min-width: 12rem;
}

.dumbbell-scale {
  display: flex;
  justify-content: space-between;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.dumbbell-track {
  position: relative;
  height: 1.5rem;
  background: var(--lvl-empty);
  border-radius: var(--radius-pill);
}

.dumbbell-link {
  position: absolute;
  top: 50%;
  height: 3px;
  transform: translateY(-50%);
  background: var(--color-text-muted);
}

.dumbbell-dot {
  position: absolute;
  top: 50%;
  width: 0.875rem;
  height: 0.875rem;
  margin-left: -0.4375rem;
  transform: translateY(-50%);
  border-radius: 50%;
  border: 2px solid var(--color-surface);
}

/* Il punto di partenza è cavo, quello di arrivo pieno: la differenza fra i
   due estremi non poggia sul colore, che qui è già occupato dal livello. */
.dumbbell-dot-first {
  background: var(--color-surface);
  box-shadow: inset 0 0 0 3px currentColor;
}

.dumbbell-movement {
  font: var(--type-caption);
  white-space: nowrap;
}

.movement-label {
  display: block;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.dumbbell-up .movement-label { color: var(--color-ok-text); }
.dumbbell-down .movement-label { color: var(--color-warning-text); }

/* --- (v1.29) Testata della pagina Valutazioni -----------------------------
   Due cose di natura diversa sulla stessa riga: a sinistra chi stiamo
   guardando, a destra da dove vengono i numeri. Fino alla v1.28 la
   provenienza era una frase sotto il sottotitolo, e la data — l'unica cosa
   che si cerca davvero in quella riga — stava in mezzo alla prosa. Come
   coppia etichetta/valore si legge senza leggerla.

   Il caso «nessuna rilevazione chiusa» resta invece un `.banner-warning` a
   tutta larghezza sotto la testata: lì il messaggio non è *quando* si è
   osservato, ma che i numeri possono cambiare mentre li si guarda, e una
   pastiglia in un angolo non è il posto per dirlo. --- */
.assessments-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-2) var(--space-4);
  margin-bottom: var(--space-4);
}

/* `1 1 20rem`: il blocco dei titoli si tiene tutta la riga finché la coppia
   etichetta/valore ha meno di 20rem in cui stare, e allora va sotto invece
   di stringersi in colonna. */
.assessments-head-titles {
  flex: 1 1 20rem;
  min-width: 0;
}

.assessments-head-titles h1 {
  margin-bottom: var(--space-2);
}

/* `.page-subtitle` porta un margine negativo in alto per incollarsi al
   titolo: qui il titolo ha già il suo spazio e quel correttivo produrrebbe
   una sovrapposizione. */
.assessments-head-titles .page-subtitle {
  margin-top: 0;
  margin-bottom: 0;
}

.source-note {
  flex: 0 1 auto;
  margin: 0;
}

.source-note-label {
  display: block;
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.source-note-value {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: 2px;
  font: var(--type-body-strong);
  color: var(--color-text);
}

.source-note-value svg {
  flex: none;
  color: var(--color-primary);
}

/* Il nome che l'allenatore ha dato alla rilevazione. Sotto la data e non al
   posto suo: «Inizio stagione» dice quale, «1 ottobre» dice quando, e la
   domanda che si fa guardando dei grafici è la seconda. */
.source-note-sub {
  display: block;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* Da 40rem la coppia si allinea al bordo destro, dove il titolo non arriva.
   Sotto quella misura resta allineata a sinistra sotto il sottotitolo: una
   riga di due parole appoggiata al margine destro di un telefono sembra un
   pezzo staccato di un'altra pagina. */
@media (min-width: 40rem) {
  .source-note {
    text-align: right;
  }

  .source-note-value {
    justify-content: flex-end;
  }
}


/* --- (v1.29) Titolo di sezione e strumenti per leggerla, sulla stessa riga
   La legenda della rampa era in fondo alla spiegazione, sopra la tabella:
   per ricordarsi cosa fosse il blu scuro bisognava risalire con gli occhi
   ogni volta, e la legenda è proprio lo strumento con cui la tabella si
   legge. In linea con il titolo, a destra, sta dove la si cerca. Su schermo
   stretto va sotto, che è l'unico posto in cui ci sta. --- */
.section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-2) var(--space-4);
  margin-bottom: var(--space-2);
}

.section-head > h2 {
  margin-bottom: 0;
}

.section-head-tools {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin-left: auto;
}

/* Quattro pixel di scarto ottico, non un capriccio: il titolo è condensato
   a 24 px e la legenda tonda a 14, e allineare le due scatole in alto
   lascia la legenda visibilmente più su della prima linea del titolo. */
.ramp-legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  margin: var(--space-1) 0 0 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.ramp-legend-title {
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
}

/* La variante corta, sotto «Come si muove il gruppo»: qui i nomi dei
   livelli per esteso li ha già dati la sezione sopra, e ripeterli
   allungherebbe la riga fino a mangiarsi il titolo. Il nome resta nel
   `title` di ogni chiave. */
.ramp-legend-compact {
  gap: var(--space-2);
}

.ramp-legend-compact .ramp-key {
  gap: var(--space-1);
  font-variant-numeric: tabular-nums;
}

.ramp-key {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  white-space: nowrap;
}

.ramp-swatch {
  display: inline-block;
  width: 1rem;
  height: 1rem;
  border-radius: 4px;
  vertical-align: middle;
}

.ramp-swatch-empty { background: var(--lvl-empty); }

.ramp-swatch-na {
  background-color: var(--lvl-na);
  background-image: repeating-linear-gradient(
    45deg,
    var(--lvl-na-key) 0 1.5px,
    transparent 1.5px 5px
  );
  border: 1px solid var(--color-border);
}


/* --- (v1.29) Il riquadro di precisazione ---------------------------------
   Il rettangolo azzurro del mockup. Ha il vocabolario di `.banner-info` —
   fondo tenue dell'accento, icona, testo di corpo — ma non è un banner e
   non ne porta la classe: un banner annuncia qualcosa che è appena
   successo e occupa la larghezza della pagina, questo spiega **come conta**
   il grafico che gli sta sotto e vive accanto al suo titolo. --- */
.info-note {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  max-width: 22rem;
  margin: 0;
  padding: var(--space-2) var(--space-3);
  background: var(--color-primary-soft);
  border-radius: var(--radius-sm);
  font: var(--type-caption);
  color: var(--color-text);
}

.info-note-icon {
  flex: none;
  margin-top: 1px;
  color: var(--color-primary);
}


/* --- §6.4: heatmap -------------------------------------------------------
   Righe = giocatori, colonne = aree. Le celle portano la rampa ordinale e
   il colore di famiglia non entra affatto (§10bis.2). Il numero del livello
   resta stampato nella cella: il valore non vive mai solo nel colore.

   (v1.29) La matrice scorre dentro il proprio riquadro, su tutti e due gli
   assi, e ha l'intestazione e la colonna anagrafica appiccicate.

   Il riquadro è indispensabile perché siano appiccicate *bene*, e la
   ragione è la stessa che nella tabella di revisione del catalogo aveva
   portato alla scelta opposta (vedi il commento a ~riga 2136): per le
   regole di CSS un contenitore con `overflow` su un asse lo diventa
   **anche sull'altro**, e diventa il riferimento delle posizioni `sticky`
   al suo interno. Là quel riferimento era un guaio, perché l'intestazione
   voleva fermarsi sotto l'header dell'app, che sta nella finestra; qui è
   esattamente ciò che serve — `top: 0` è il bordo alto del riquadro,
   `left: 0` il suo bordo sinistro — e in cambio la pagina intera non scorre
   più in orizzontale portandosi via le card sopra e sotto, che su un
   telefono a bordo pista è la differenza fra scorrere una tabella e perdere
   la pagina.

   Il tetto d'altezza esiste perché un riquadro che non scorre in verticale
   non ha nulla da cui staccare l'intestazione: `min(72vh, 40rem)` lascia
   sempre un pezzo di pagina intorno, così si vede che a scorrere è la
   tabella. Se le righe ci stanno tutte, il riquadro si accorcia da sé e non
   succede niente.

   `.table-scroll` resta sul `<div>` per coerenza con le altre tabelle
   dell'app, ma non porta stile: lo stile è tutto qui, su una classe che
   riguarda solo questa matrice. --- */
.heatmap-scroll {
  overflow: auto;
  max-height: min(72vh, 40rem);
}

/* `table-layout: fixed` e non `auto`, ed è la riga che tiene in piedi tutta
   la matrice. Con la disposizione automatica la larghezza di una colonna la
   detta il suo **contenuto più largo**, che qui non è mai la cella — una
   cifra sola — ma l'intestazione: «P· LETTURA» è lunga il doppio di «TIRO»,
   e la colonna la seguiva. Ne usciva una griglia di rettangoli di sette
   misure diverse in cui la dimensione della cella sembrava voler dire
   qualcosa, mentre dipendeva soltanto da quanto è lungo il nome dell'area.
   In una matrice il posto di un valore è già un'informazione: se anche la
   sua forma varia, l'occhio legge una differenza che non c'è.

   Con la disposizione fissa comandano le larghezze dichiarate sulla prima
   riga — la riga d'intestazione — e il contenuto non le sposta più. Quel
   che cambia in cambio: un'intestazione più larga della sua colonna va a
   capo invece di allargarla, ed è il motivo per cui qui sotto `.heatmap-area`
   perde il `nowrap` che aveva.

   `width: max-content` non è decorativo e non si tolga: con la larghezza
   **automatica** Chrome determina prima la larghezza della tabella con
   l'algoritmo normale — cioè guardando i contenuti — e le colonne tornavano
   disuguali nonostante `table-layout: fixed`, misurate fra 52 e 68 px. Con
   `max-content` la larghezza della tabella è per definizione la somma delle
   colonne dichiarate, e le dichiarazioni valgono davvero. Se la somma non
   sta nella pagina, scorre: `.heatmap-scroll` e le colonne appiccicate
   esistono per quello. */
.heatmap-table {
  table-layout: fixed;
  width: max-content;
  border-collapse: separate;
  border-spacing: 2px;
  font-variant-numeric: tabular-nums;
  /* Larghezza di riferimento della colonna anagrafica: la usano sia la
     cella del numero sia lo scostamento a cui si ancora quella del nome.
     Un solo posto da cambiare, altrimenti fra le due si apre una fessura
     larga quanto l'errore. */
  --heatmap-num-w: 3rem;
  /* La colonna del nome ha una misura sua, dichiarata: con la disposizione
     fissa nessuna colonna può più crescere da sé, nemmeno questa. Undici
     rem tengono «Arthur Jimmy Giabardo» (163 px misurati) più lo spazio
     che lo stacca dal filetto; oltre, il nome si accorcia con i puntini
     invece di invadere la prima area. */
  --heatmap-name-w: 11rem;
  /* La misura unica di **tutte** le colonne di valutazione. Non nasce dalla
     cella, che di suo starebbe in molto meno, ma dall'intestazione più
     larga che non si può mandare a capo: «ATTEGGIAM.» è una parola sola, e
     una parola sola è il pavimento sotto cui questa misura non può
     scendere. Le altre intestazioni lunghe («P· BASTONE», «P· LETTURA»)
     hanno uno spazio e si spezzano in due righe.

     Sessanta pixel: «ATTEGGIAM.» ne misura 55,4 alla spaziatura ridotta di
     qui sotto. Con questa misura, quattordici aree più l'anagrafica fanno
     1098 px e stanno dentro i 1102 della card sul monitor su cui è stata
     provata — di poco, e di proposito: allargarla anche solo di un gradino
     fa comparire una barra di scorrimento su una tabella che ci starebbe. */
  --heatmap-col-w: 3.75rem;
}

/* Sfondo pieno su tutto ciò che è appiccicato, e non è pignoleria: con
   `border-collapse: separate` una cella non ha un fondo di riga da cui
   ereditare, e una `th` trasparente lascia vedere in trasparenza le righe
   che le passano sotto.

   L'ombra senza sfocatura e con due pixel di espansione serve al problema
   gemello: `border-spacing: 2px` lascia due pixel di **vuoto** fra una
   cella e l'altra, e in quel vuoto passerebbe il contenuto che scorre. Due
   pixel di espansione nel colore della superficie chiudono la fessura su
   tutti e quattro i lati senza invadere la cella vicina, che comincia
   esattamente dove l'ombra finisce. */
.heatmap-table thead th {
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--color-surface);
  box-shadow: 0 0 0 2px var(--color-surface);
  font: var(--type-thead);
  letter-spacing: var(--type-thead-ls);
  text-transform: uppercase;
  text-align: left;
  vertical-align: bottom;
  padding-bottom: var(--space-1);
}

.heatmap-corner,
.heatmap-num {
  width: var(--heatmap-num-w);
}

/* Le larghezze che la disposizione fissa legge: stanno tutte sulla riga
   d'intestazione, perché è quella la riga che decide. */
.heatmap-table thead .heatmap-name {
  width: var(--heatmap-name-w);
}

.heatmap-area {
  width: var(--heatmap-col-w);
}

/* Le due celle dell'anagrafica restano ancorate a sinistra: scorrendo verso
   destra per arrivare alle ultime aree si deve continuare a vedere *di chi*
   si sta leggendo la riga. Lo scostamento del nome è la larghezza del
   numero più i quattro pixel di `border-spacing` che stanno prima e dopo. */
.heatmap-num,
.heatmap-table tbody .heatmap-name {
  position: sticky;
  z-index: 2;
  background: var(--color-surface);
  box-shadow: 0 0 0 2px var(--color-surface);
}

.heatmap-num {
  left: 0;
}

.heatmap-table .heatmap-name {
  left: calc(var(--heatmap-num-w) + 4px);
}

/* Gli angoli in alto a sinistra sono appiccicati su tutti e due gli assi, e
   devono stare sopra sia alla riga d'intestazione sia alla colonna dei
   nomi: quando si scorre in diagonale sono l'unica cosa che si sovrappone
   a entrambe. */
.heatmap-table thead .heatmap-corner,
.heatmap-table thead .heatmap-name {
  position: sticky;
  z-index: 4;
}

.heatmap-table thead .heatmap-corner {
  left: 0;
}

.heatmap-area {
  /* Le intestazioni ruotate sono illeggibili su schermi piccoli e in
     stampa: si abbrevia il testo e il nome intero resta nel `title`. */
  writing-mode: horizontal-tb;
  color: var(--color-text-muted);
  /* Niente `nowrap`: con la disposizione fissa un'intestazione che non ci
     sta va a capo, e andare a capo è esattamente quel che deve fare —
     prima allargava la colonna, e la colonna si portava dietro la cella. */
  padding: 0 0 var(--space-1);
  text-align: center;
  line-height: 1.15;
  /* Spaziatura più stretta della `--type-thead-ls` di regola, e solo qui.
     A 0,07em «ATTEGGIAM.» misura 59 px e obbligherebbe **tutte** e
     quattordici le colonne a 68, portando la tabella a 1250 px e a una
     barra di scorrimento su un monitor che la conterrebbe. Il maiuscoletto
     regge una spaziatura minore senza chiudersi; una colonna in più da
     scoprire scorrendo no. */
  letter-spacing: 0.04em;
}

/* Il nome del giocatore è un `<th scope="row">`, ma è il soggetto della
   riga, non la sua etichetta di colonna: prende il ruolo di un nome. Il
   filetto a destra separa l'anagrafica dal blocco delle valutazioni — due
   cose che si leggono in modo diverso, una per parole e una per colore. */
.heatmap-name {
  font: var(--type-body-strong);
  text-align: left;
  white-space: nowrap;
  /* La colonna ha ora una larghezza dichiarata, e un nome più lungo non la
     allarga più: senza questi due, traboccherebbe sulla prima area invece
     di fermarsi. I puntini sono un ripiego che non dovrebbe mai vedersi —
     il nome intero resta comunque nel link. */
  overflow: hidden;
  text-overflow: ellipsis;
  padding-right: var(--space-2);
  border-right: 1px solid var(--color-border);
}

.heatmap-cell {
  /* Nessuna larghezza qui: con la disposizione fissa la detta la riga
     d'intestazione, e dichiararla due volte in due posti diversi vuol dire
     soltanto avere due numeri da tenere d'accordo. */
  height: 2.25rem;
  text-align: center;
  border-radius: var(--radius-sm);
  font: var(--type-figure-cell);
  /* Dopo la scorciatoia, mai prima: `font` azzera `font-variant-numeric`
     anche quando era ereditato dalla tabella. */
  font-variant-numeric: tabular-nums;
}

.heatmap-cell-empty { background: var(--lvl-empty); }

/* «Non pertinente» è un'altra cosa da «non ancora valutata», e la
   differenza non può essere un grigio un po' più chiaro: due grigi vicini
   si distinguono solo affiancandoli, e nella matrice non capita mai.
   Il tratteggio si riconosce da solo, anche in fotocopia. */
.heatmap-cell-na {
  background-color: var(--lvl-na);
  /* Righe sottili e rade: è una campitura, non una barratura. */
  background-image: repeating-linear-gradient(
    45deg,
    var(--lvl-na-line) 0 1.5px,
    transparent 1.5px 9px
  );
  color: var(--color-text-muted);
  font: var(--type-figure-cell);
  font-variant-numeric: tabular-nums;
}


/* --- §6.3: piccoli multipli ---------------------------------------------
   Quattordici riquadri piccoli invece di quattordici serie colorate su un
   grafico solo: oltre le sette-otto serie i colori non si distinguono più.
   Ogni riquadro porta sotto la sua tabella con i valori esatti, che è anche
   la sua versione leggibile a voce.

   (v1.29) Colonne fisse e non più `auto-fill`: con `auto-fill` era la
   griglia a decidere quante colonne ci stessero, e su un monitor largo ne
   metteva cinque o sei da 13rem — riquadri in cui due barre affiancate
   diventano due strisce e la tabella dei valori va a capo. Quattro è il
   numero su cui i mockup sono disegnati, ed è il massimo con cui la riga
   «Livello 1 2 3 4 n.v.» sta ancora su una riga sola. --- */
.small-multiples {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-3);
}

@media (min-width: 36rem) {
  .small-multiples {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 75rem) {
  .small-multiples {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

.small-multiple {
  margin: 0;
  min-width: 0;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
}

.small-multiple-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}

.small-multiple-title {
  margin: 0;
  min-width: 0;
  font: var(--type-title);
}

/* --- Il badge di tendenza -------------------------------------------------
   Verde, grigio, rosso: sono gli unici tre stati e non una scala, quindi i
   colori di stato dell'app bastano — nessuna tinta nuova. La freccia sta
   **prima** della parola e non al posto suo: il verso non deve vivere solo
   nella tinta, e chi non distingue il verde dal rosso legge «Migliora».

   La freccia arriva già formata da Rust (↗ ↘ →) e quei tre segni cadono
   fuori dal subset imbarcato di Barlow: li disegna il carattere di sistema.
   È accettabile perché sono segni geometrici e non lettere — accanto a un
   glifo condensato non stonano — ed è un altro motivo per cui la parola c'è
   sempre. --- */
.trend-badge {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 2px var(--space-2);
  border-radius: var(--radius-pill);
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  white-space: nowrap;
}

.trend-arrow {
  line-height: 1;
}

.trend-migliora { background: var(--color-ok-bg); color: var(--color-ok-text); }
.trend-stabile { background: var(--color-bg); color: var(--color-text-muted); }
.trend-peggiora { background: var(--color-danger-bg); color: var(--color-danger-text); }

.stack-row {
  display: flex;
  align-items: flex-end;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}

.stack {
  flex: 1 1 0;
  min-width: 0;
}

/* Barre larghe e non colonnine: il numero dei giocatori sta **dentro** il
   segmento, e per starci il segmento deve essere largo quanto una parola.
   Con due rilevazioni la coppia riempie il riquadro; con cinque si
   stringono tutte insieme, ed è il momento in cui la tabella sotto smette
   di essere una ridondanza. */
.stack-bar {
  display: flex;
  flex-direction: column;
  height: 5.5rem;
  background: var(--lvl-empty);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.stack-segment {
  display: flex;
  align-items: center;
  justify-content: center;
  /* Un giocatore su quattordici è un segmento alto sei pixel: la cifra non
     ci sta, e una barra con dei numeri e dei buchi si legge peggio di una
     barra senza numeri. Il minimo è l'altezza di una riga di testo. La
     distorsione che introduce è dichiarata dalla tabella qui sotto, che
     porta gli stessi numeri senza nessuna scala di mezzo. */
  min-height: 1.25rem;
  font: var(--type-figure-cell);
  font-variant-numeric: tabular-nums;
}

/* Sotto la barra il **periodo**, in maiuscoletto: fra due barre affiancate
   la domanda è in che mese si sta guardando, non come quella rilevazione
   era stata chiamata — il nome scelto dall'allenatore vive nel `title` del
   gruppo. Troncamento da `.t-clip`. */
.stack-label {
  margin-top: var(--space-2);
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
  text-align: center;
}

.stack-table {
  width: 100%;
  border-collapse: collapse;
  font: var(--type-caption);
  font-variant-numeric: tabular-nums;
}

.stack-table th,
.stack-table td {
  padding: var(--space-1);
  text-align: right;
  border-bottom: 1px solid var(--color-border);
}

.stack-table thead th {
  font: var(--type-thead);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--type-thead-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.stack-table thead th:first-child,
.stack-table th[scope="row"] {
  text-align: left;
}

/* Troncamento da `.t-clip`. */
.stack-table th[scope="row"] {
  font: var(--type-caption);
  font-variant-numeric: tabular-nums;
  color: var(--color-text-muted);
  max-width: 7rem;
}

/* L'ultima riga non porta filetto: sotto non c'è niente da separare, e la
   riga chiusa fa sembrare la tabella troncata. */
.stack-table tbody tr:last-child th,
.stack-table tbody tr:last-child td {
  border-bottom: 0;
}


/* --- §6.5: riquadri di sintesi ------------------------------------------
   L'unica superficie di queste viste che porta il colore di **famiglia** del
   tema, e può portarlo perché qui non c'è nessun livello colorato con cui
   confondersi. Il colore sta sul filetto e sul nome del tema, non a
   riempire un rettangolo: qui identifica un tema, e un rettangolo pieno si
   leggerebbe come uno stato.

   (v1.29) L'ordine di lettura è cambiato, ed è tutto il senso della
   revisione: prima il nome dell'area, poi la frazione grande, poi sei
   parole che dicono di che cosa è la frazione, poi il bottone. La frase
   lunga che c'era prima («4 giocatori su 12 valutati sono ancora sotto
   Applicare») era giusta ma andava letta tutta per arrivare al numero, e
   queste tre card si guardano di sfuggita per decidere quale toccare. --- */
.summary-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  gap: var(--space-3);
  margin: var(--space-4) 0;
}

.summary-card {
  display: flex;
  flex-direction: column;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-left: 4px solid var(--fam, var(--color-primary));
  border-radius: var(--radius-md);
  padding: var(--space-3);
}

.summary-card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-2);
}

.summary-area {
  margin: 0;
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--fam, var(--color-text));
}

/* La quota in percentuale, nell'angolo. Pastiglia **neutra** e non in tinta
   di famiglia come nel mockup: tre delle sette famiglie sono tinte chiare
   (giallo, arancio, rosa), e un «33 %» giallo su fondo giallo diluito, a
   12 px, non si legge. Il colore di famiglia resta dove porta un
   significato — il filetto e il nome dell'area — e qui non manca a
   nessuno: la pastiglia è già identificata dalla card che la contiene. */
.summary-share {
  flex: none;
  padding: 2px var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--color-bg);
  color: var(--color-text-muted);
  font: var(--type-eyebrow);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--type-eyebrow-ls);
}

/* La frazione, non il solo numeratore: «4» da solo non dice niente, «4 / 12»
   è già un giudizio. Tabulari perché le tre card si leggono in fila e i
   numeri devono incolonnarsi anche quando non stanno in una colonna. */
.summary-figure {
  margin: var(--space-2) 0 0 0;
  font: var(--type-figure);
  font-variant-numeric: tabular-nums;
}

.summary-note {
  margin: 0 0 var(--space-3) 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.summary-note strong {
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  color: var(--color-text);
}

/* Il bottone pieno, a tutta larghezza della card, con la freccia appoggiata
   al bordo destro. `margin-top: auto` lo incolla al fondo: le tre card
   hanno nomi di area di lunghezza diversa, e senza questa riga i tre
   bottoni finirebbero a tre altezze diverse — una fila di bersagli che non
   sono in fila.

   L'altezza minima di 44px arriva da `.btn` e non va ridotta: è il
   bersaglio da pollice. Il corpo scende invece a quello del bottone piccolo
   perché «Pianifica una seduta su Lettura del gioco e transizione» dentro
   17rem, a 16 px, sono tre righe. */
.summary-cta {
  margin-top: auto;
  width: 100%;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  font: var(--type-button-sm);
  text-align: left;
}

.summary-cta-arrow {
  flex: none;
}


/* --- Avanzamento di una rilevazione -------------------------------------- */

.progress-bar-col {
  width: 30%;
  min-width: 6rem;
}

.progress-track {
  height: 0.5rem;
  background: var(--lvl-empty);
  border-radius: var(--radius-pill);
  overflow: hidden;
}

.progress-fill {
  height: 100%;
  background: var(--color-primary);
}

/* (issue #96) Sotto 900px la **stessa** `<table>` impilata, come
   l'anagrafica e la griglia di un'area: su un telefono da 390px le quattro
   colonne arrivavano a 417, e il bottone finiva fuori. In testa il nome e,
   alto quanto le due righe, il bottone; sotto «6 su 12» e la barra, che
   prende il resto della riga — è lei che dice a colpo d'occhio cosa manca.
   Per giocatore la maglia sta a sinistra di tutte e due le righe. */
@media (max-width: 899px) {
  .progress-table,
  .progress-table tbody {
    display: block;
  }

  /* Le intestazioni restano per il lettore di schermo: «6 su 12» accanto
     alla barra si spiega da sé. */
  .progress-table thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  /* Il conteggio ha una misura sua e non `auto`: ogni riga è una griglia a
     sé, e con «0 su 12» sotto «12 su 12» le barre partivano sfalsate. */
  .progress-table tr {
    display: grid;
    grid-template-columns: 4.5rem minmax(0, 1fr) auto;
    grid-template-areas:
      "name  name action"
      "count bar  action";
    align-items: center;
    gap: var(--space-1) var(--space-3);
    padding: var(--space-2);
    border-bottom: 1px solid var(--color-border);
  }

  .progress-table-players tr {
    grid-template-columns: auto 4.5rem minmax(0, 1fr) auto;
    grid-template-areas:
      "number name  name action"
      "number count bar  action";
  }

  .progress-table th,
  .progress-table td {
    display: block;
    padding: 0;
    border: 0;
  }

  .progress-table tbody th {
    grid-area: name;
  }

  .progress-table .roster-number-col {
    grid-area: number;
    width: auto;
  }

  .progress-table .progress-count {
    grid-area: count;
    font: var(--type-caption);
    color: var(--color-text-muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }

  .progress-table .progress-bar-col {
    grid-area: bar;
    width: auto;
    min-width: 0;
  }

  .progress-table .progress-action {
    grid-area: action;
  }
}

/* --- Il form di compilazione --------------------------------------------
   Le scelte di livello sono radio e non una tendina: cinque bersagli
   visibili si toccano con il pollice a bordo pista, una tendina va aperta.
   La prima opzione è «non valutata», che è anche il valore iniziale — il
   form non deve mai suggerire un livello per riempire un vuoto. */
.area-block {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  margin-bottom: var(--space-4);
}

.area-block > legend {
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  padding: 0 var(--space-2);
}

.level-choice {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}

.level-option {
  margin: 0;
  cursor: pointer;
}

/* Il radio nativo resta nel DOM (accessibilità, tastiera, `:focus-visible`)
   ma non si vede: è il chip a fare da bersaglio, con un'area di tocco
   generosa come il resto dell'app. */
.level-option input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}

/* La scelta non selezionata è **desaturata**, non sbiadita: `opacity`
   avrebbe fatto sfumare anche il testo verso lo sfondo della pagina, e sul
   passo più scuro della rampa il bianco sarebbe sceso attorno a 2:1.
   `grayscale` cambia la tinta lasciando intatto il rapporto di luminanza
   fra testo e fondo, quindi tutte e cinque le opzioni restano leggibili e
   quella scelta si riconosce dal colore *e* dal bordo — mai dal solo
   colore. */
.level-chip {
  display: inline-block;
  padding: var(--space-2) var(--space-3);
  min-height: 2.75rem;
  line-height: 1.5rem;
  border-radius: var(--radius-pill);
  border: 2px solid transparent;
  font: var(--type-body-strong);
  filter: grayscale(0.85);
}

.level-chip-empty {
  background: var(--lvl-empty);
  color: var(--color-text-muted);
}

.level-option input:checked + .level-chip {
  filter: none;
  border-color: var(--color-text);
  font-weight: 700;
}

.level-option input:focus-visible + .level-chip {
  outline: 3px solid var(--color-primary);
  outline-offset: 2px;
}

.level-option input:disabled + .level-chip {
  cursor: default;
}

.area-note {
  display: block;
}

/* (issue #91) Il livello dato a questo giocatore in un'altra squadra-stagione
   — «in Mini: Variare · 01.11.2026» —, **solo dopo** che qui se n'è scelto
   uno. Prima lo influenzerebbe, e il livello dei Mini rientrerebbe dalla
   porta di servizio. Il radio «non valutata» (`data-rank="0"`) è anche lui
   `:checked`, quindi non basta `:has(:checked)`. Vale col mouse, dalla
   tastiera (lo script sceglie i radio e basta) e riaprendo una rilevazione
   già compilata; su una chiusa, accanto al livello salvato. Un test rilegge
   il selettore da qui. */
.elsewhere-level {
  display: none;
  margin: 0 0 var(--space-3);
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.level-choice:has(input:checked:not([data-rank="0"])) + .elsewhere-level {
  display: block;
}

.level-choice-compact + .elsewhere-level {
  margin: var(--space-1) 0 0;
}

.sub-skills > summary {
  cursor: pointer;
  font: var(--type-title);
  color: var(--color-text-muted);
  padding: var(--space-2) 0;
}

.sub-skill-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
  gap: var(--space-2);
}

.sub-skill {
  font: var(--type-body);
  margin: 0;
}

.scale-help {
  margin-bottom: var(--space-4);
}

/* «Cambia il nome» di una rilevazione: chiuso sotto la testata, come i
   dettagli dei blocchi del piano. */
.assessment-rename {
  max-width: 40rem;
  margin-bottom: var(--space-4);
}

.assessment-rename > summary {
  cursor: pointer;
  color: var(--color-text-muted);
  font-size: 0.875rem;
}

.assessment-rename > form {
  margin-top: var(--space-2);
  margin-bottom: 0;
}

.scale-help > summary {
  cursor: pointer;
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  padding: var(--space-2) 0;
}

.scale-list dt {
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  margin-top: var(--space-2);
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.scale-list dd {
  margin: 0 0 0 1.5rem;
  color: var(--color-text-muted);
}

/* (v1.45, issue #37) «Cosa guardare»: il criterio osservabile dei concetti
   in perimetro, accanto all'area mentre si valuta.

   Non è un `.banner`: un banner segnala qualcosa di successo o da
   correggere, e questo è materiale di riferimento che sta lì ogni volta.
   Non è nemmeno un campo del form — niente bordi da input, niente colore
   d'accento pieno — perché la copertura si calcola ancora sulle stesse nove
   aree e sulle stesse due soglie, e un riquadro che sembra compilabile
   direbbe il contrario. Superficie neutra e filetto sottile: si legge e si
   scorre. */
.area-criteria {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-text-muted);
  border-radius: var(--radius-sm);
  padding: var(--space-3);
  margin-bottom: var(--space-4);
}

.area-criteria-title {
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  color: var(--color-text-muted);
  margin: 0 0 var(--space-2);
}

.area-criteria-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-2);
}

/* `.field-hint` nasce con un margine negativo per stare attaccata al campo
   che spiega. Qui sta sotto un elenco, non sotto un input, e quel margine la
   farebbe salire addosso all'ultimo criterio. */
.area-criteria > .field-hint {
  margin-top: var(--space-3);
}

/* Il nome sulla sua riga e il criterio sotto: il nome è la parola che si
   urla in pista ed è corto, il criterio è una frase intera. Su una riga
   sola la frase spingerebbe il nome a sinistra di una colonna che cambia
   larghezza a ogni voce. */
.area-criterion {
  display: grid;
  gap: 2px;
}

.area-criterion-name {
  font: var(--type-body-strong);
  color: var(--color-text);
  text-decoration: none;
  justify-self: start;
}

.area-criterion-name:hover,
.area-criterion-name:focus-visible {
  text-decoration: underline;
}

.area-criterion-text {
  font: var(--type-body);
  color: var(--color-text-muted);
}

/* Dentro il fieldset del form per giocatore i criteri non hanno cornice
   propria — ce l'ha già il fieldset — e restano visibili: un contenuto da
   aprire è il contrario di ciò che questa fetta serve a fare. Il filetto a
   sinistra li lega all'area senza chiudere un secondo riquadro dentro il
   primo. */
.area-criteria-inline {
  border-left: 2px solid var(--color-border);
  padding-left: var(--space-3);
  margin-bottom: var(--space-3);
}

.player-pager {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

.player-pager-next {
  margin-left: auto;
}

/* --- Stampa --------------------------------------------------------------
   La scheda del singolo giocatore si stampa per il colloquio con i genitori
   (§8: si esporta il singolo, mai la matrice di squadra). I colori di sfondo
   vanno forzati, altrimenti la rampa sparisce e restano barre bianche. */
@media print {
  .profile-bar,
  .heatmap-cell,
  .stack-segment,
  .dumbbell-dot,
  .ramp-swatch,
  .level-chip {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }

  /* Il riquadro che scorre non ha senso su carta, e lasciarlo com'è
     stamperebbe la sola porzione visibile della matrice tagliando il resto.
     Le posizioni appiccicate vanno sciolte per la ragione gemella: su carta
     non c'è niente da cui staccarsi, e un'intestazione `sticky` in stampa
     esce sovrapposta alle prime righe. */
  .heatmap-scroll {
    overflow: visible;
    max-height: none;
  }

  .heatmap-table thead th,
  .heatmap-table .heatmap-name,
  .heatmap-table .heatmap-num {
    position: static;
    box-shadow: none;
  }

  /* Su schermo la tabella è larga quanto le sue colonne dichiarate e, se
     non ci sta, scorre. Su carta non c'è niente da far scorrere: la stessa
     regola stamperebbe la parte sinistra e taglierebbe il resto al margine.
     `width: 100%` con la disposizione **ancora fissa** lascia al foglio il
     compito di stringere le colonne, e le stringe tutte nella stessa
     proporzione — che è il punto: le celle restano uguali fra loro anche
     quando sono più strette che a monitor. Con `table-layout: auto`
     tornerebbero a inseguire la lunghezza dell'intestazione. */
  .heatmap-table {
    width: 100%;
  }

  /* Il tratteggio del «non pertinente» è un `background-image`, quindi
     `print-color-adjust: exact` più sopra lo porta sul foglio insieme ai
     fondi pieni. Se un driver lo mangia comunque resta il punto in mezzo
     alla cella, che è il motivo per cui il punto c'è. */

  /* Un bottone stampato è inchiostro speso per una cosa che non si può
     toccare (VISION §9). */
  .summary-cta,
  .player-pager {
    display: none;
  }
}

/* --- (v1.25) Tabella di inserimento rapido del roster ---------------------
   Il roster si compila una volta l'anno, tutto insieme, con la lista della
   società di fianco. Le celle sono strette perché la tabella deve stare in
   una schermata: se per vedere l'ultima riga bisogna scorrere, si perde il
   colpo d'occhio che è il motivo per cui la pagina esiste. */
.quick-add-table input[type="text"],
.quick-add-table input[type="number"],
.quick-add-table select {
  width: 100%;
  min-width: 0;
  padding: var(--space-1) var(--space-2);
}

.quick-add-table input[type="number"] {
  /* Anno, mese e maglia sono al massimo quattro cifre: lasciarli larghi
     quanto i cognomi ruberebbe spazio proprio alle colonne che ne hanno
     bisogno. */
  max-width: 6.5ch;
}

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

/* Il numero di riga. Non è decorativo: è il numero che i messaggi d'errore
   usano per dire quale riga correggere («riga 7: manca l'anno di nascita di
   Marco Bernasconi»), e senza averlo davanti quel messaggio non aiuta. */
.quick-add-num {
  width: 2.5rem;
  text-align: right;
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}

/* Le tre caselle dei ruoli su una riga sola: a U12 più di un ruolo è la
   norma, non l'eccezione, quindi si spuntano spesso in due. */
.quick-add-roles {
  white-space: nowrap;
}

.quick-add-roles label {
  display: inline-flex;
  align-items: center;
  gap: 0.15rem;
  margin-right: var(--space-2);
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
}

/* La riga di riempimento. Sfondo diverso dalle righe dati perché non è una
   riga di dati: niente di ciò che si scrive qui viene salvato da solo. */
.quick-add-fill td {
  background: var(--color-surface-2, var(--color-bg));
  border-bottom: 2px solid var(--color-border);
  vertical-align: middle;
}

.quick-add-fill-label {
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
  text-align: right;
}

.quick-add-fill select,
.quick-add-fill input {
  max-width: 11ch;
  margin-right: var(--space-1);
}

/* La riga che non passa e il suo motivo, subito sotto. Il colore da solo non
   basterebbe — chi distingue male i rossi vedrebbe quattordici righe uguali —
   quindi il messaggio è scritto per esteso sotto la riga che lo riguarda, e
   il bordo a sinistra serve a ritrovarla scorrendo. */
.quick-add-row-error td {
  background: var(--color-danger-bg);
}

.quick-add-row-error td:first-child {
  box-shadow: inset 3px 0 0 var(--color-danger-border);
}

.quick-add-error-message td {
  padding-top: 0;
  color: var(--color-danger-text);
  background: var(--color-danger-bg);
  font: var(--type-caption);
}

/* (v1.27) La riga che chiede «è lui?». Colore diverso dall'errore ed è
   voluto: non c'è niente di sbagliato in quello che è stato scritto, manca
   una risposta che solo chi conosce il gruppo può dare. */
.quick-add-question td {
  padding-top: 0;
  background: var(--color-info-bg, var(--color-surface-2, var(--color-bg)));
  font: var(--type-caption);
}

.quick-add-question select {
  margin-left: var(--space-2);
  max-width: 28ch;
}


/* --- (v1.25) Un'area, tutta la squadra -----------------------------------
   La trasposta del form per giocatore: quattordici righe con la stessa scala
   ripetuta. I chip qui portano il solo numero di livello — il nome per esteso
   moltiplicato per quattordici righe riempirebbe lo schermo di parole
   identiche, e la legenda in cima più il `title` di ogni chip dicono già cosa
   significano. Il numero resta perché la scala è ordinale e l'ordine è
   l'informazione. */
.level-choice-compact {
  flex-wrap: nowrap;
  gap: var(--space-1);
  margin-bottom: 0;
}

.level-choice-compact .level-chip {
  /* Quadrato e non pillola: cinque bersagli per riga per quattordici righe,
     e la pillola larga li porterebbe fuori schermo. L'altezza resta quella
     degli altri bersagli dell'app — a bordo pista si tocca col pollice, non
     col mouse. */
  min-width: 2.75rem;
  padding: var(--space-2) 0;
  text-align: center;
  border-radius: var(--radius-sm);
}

.area-grid td {
  vertical-align: middle;
}

.area-grid-name {
  display: block;
  font: var(--type-title);
}

/* L'anno e il mese sotto il nome. Piccoli e in grigio perché sono contesto,
   non un dato da confrontare: servono a ricordare, mentre si guarda una
   colonna di livelli, che fra gennaio e dicembre ci sono undici mesi di
   sviluppo (§2.3). */
.area-grid-age {
  display: block;
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.area-grid-note input[type="text"] {
  width: 100%;
  min-width: 14rem;
  padding: var(--space-1) var(--space-2);
}

.area-grid-details {
  display: block;
  margin-top: var(--space-1);
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* (issue #100) Quello che serve solo alla carta — la testa con «Giocatore 5
   di 12», cosa guardare, il nome dei livelli, il bottone della nota — sta
   nelle celle della tabella e da 900px non si vede: lì le colonne dicono già
   tutto, e il nome del livello per quattordici righe sarebbe la stessa parola
   ripetuta. Lo mostra il blocco del telefono qui sotto. */
.area-card-head,
.area-card-look,
.level-chip-label,
.area-card-note-toggle {
  display: none;
}

/* (issue #95) Sotto 900px la tabella qui sopra era larga 670px su un
   telefono da 390: i cinque chip non vanno a capo e la nota ha 14rem di
   minimo, e per dare «4» bisognava scorrere di lato riga per riga. Qui ogni
   riga diventa un blocco, **con lo stesso markup** — come l'anagrafica
   (v1.54), e per la stessa ragione la regola è `max-width`: la tabella è la
   forma che il markup ha già. L'ordine delle celle non cambia, e con lui
   l'ordine in cui la tastiera (v1.49) le percorre.

   (issue #100) E ogni blocco è una **carta**: un giocatore per schermata, in
   una fila che scorre in orizzontale e si ferma su ogni carta
   (`scroll-snap`). In alto dove si è, poi maglia e nome grandi, cosa
   guardare, e in basso — nella zona del pollice — i quattro livelli a tutta
   larghezza, col loro nome; «Non valutata» più piccola, dopo. Senza
   JavaScript le carte si scorrono e si salva con «Salva»; con lo script un
   tocco salva e passa alla carta seguente (`assessment-keyboard.js`). */
@media (max-width: 899px) {
  .area-grid,
  .area-grid tbody {
    display: block;
  }

  /* Le intestazioni restano per il lettore di schermo: a chi guarda, la
     carta si spiega da sé. */
  .area-grid thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  /* Le carte hanno la loro cornice: quella del form, attorno, le
     stringerebbe di due margini per niente. */
  .area-grid-form {
    padding: 0;
    border: 0;
    background: none;
  }

  /* La fila. `scroll-snap-stop` perché un colpo di pollice deciso non salti
     un ragazzo: si passa una carta alla volta.

     `position: relative`, qui e sulla carta: i radio nascosti (e la testata
     per il lettore di schermo) sono `position: absolute`, e senza un
     antenato posizionato il loro contenitore sarebbe la pagina — che dalla
     decima carta in là si allargava di tremila pixel. */
  .area-grid-scroll {
    position: relative;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    overscroll-behavior-x: contain;
  }

  .area-grid tbody {
    display: flex;
    gap: var(--space-3);
  }

  .area-grid tr {
    position: relative;
    flex: 0 0 100%;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    align-content: start;
    gap: var(--space-2);
    padding: var(--space-3);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    background: var(--color-surface);
    scroll-snap-align: start;
    scroll-snap-stop: always;
  }

  .area-grid td {
    display: block;
    padding: 0;
    border: 0;
  }

  /* La testa: dove si è, con i due bottoni per tornare indietro e per
     saltare ai lati. Tre colonne fisse, così il conteggio resta al centro
     anche senza i bottoni. */
  .area-grid .roster-number-col {
    width: auto;
    text-align: center;
  }

  .area-card-head {
    display: grid;
    grid-template-columns: 2.75rem minmax(0, 1fr) 2.75rem;
    align-items: center;
    margin-bottom: var(--space-2);
  }

  .area-card-count {
    grid-column: 2;
    font: var(--type-eyebrow);
    letter-spacing: var(--type-eyebrow-ls);
    text-transform: uppercase;
    color: var(--color-text-muted);
  }

  /* Solo con lo script: senza, si passa scorrendo. */
  .area-card-step {
    visibility: hidden;
    min-height: 2.75rem;
    padding: 0;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    color: var(--color-text);
    font: var(--type-section);
  }

  .area-card-step[data-card-step="1"] {
    grid-column: 3;
  }

  .is-keyboard .area-card-step {
    visibility: visible;
  }

  .area-grid .jersey-number {
    font: var(--type-figure);
    min-width: 3rem;
    padding: var(--space-1) var(--space-2);
  }

  .area-grid td:nth-child(2) {
    text-align: center;
  }

  .area-grid-name {
    font: var(--type-section);
  }

  /* Cosa guardare in due righe al massimo: l'elenco intero sta sopra la
     fila, dove la pagina lo mostra già. */
  .area-card-look {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
    margin: 0 0 var(--space-3);
    font: var(--type-caption);
    color: var(--color-text-muted);
    text-align: center;
  }

  .area-card-look strong {
    color: var(--color-text);
  }

  /* I quattro livelli uno sotto l'altro, a tutta larghezza, e «Non
     valutata» — la prima nel markup — dopo di loro. */
  .area-grid .level-choice-compact {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
  }

  .area-grid .level-choice-compact .level-option:first-child {
    order: 1;
  }

  /* Il numero e il nome del livello sono due elementi del flex: lo spazio
     fra loro lo dà `gap`, uno spazio nel markup si perderebbe. */
  .area-grid .level-choice-compact .level-chip {
    display: flex;
    align-items: center;
    gap: 0.3em;
    min-height: 3.5rem;
    padding: 0 var(--space-4);
    border-radius: var(--radius-md);
    font: 700 var(--t-4) / 1.2 var(--font-sans);
    text-align: left;
  }

  .area-grid .level-choice-compact .level-chip-empty {
    justify-content: center;
    min-height: 2.75rem;
    font: var(--type-label);
  }

  .level-chip-label {
    display: inline;
  }

  .level-chip-dash {
    display: none;
  }

  /* L'esito del salvataggio subito sotto i livelli, dove si è appena
     toccato; la nota dopo. `td.` per passare davanti a `.area-grid td`. */
  .area-grid td.area-grid-note {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
  }

  .area-grid-note .kb-row-status {
    order: -1;
    margin: 0;
  }

  .area-grid-note input[type="text"] {
    min-width: 0;
  }

  /* Con lo script la nota vuota sta dietro «Nota». */
  .is-keyboard .area-grid-note.is-note-folded input[data-note] {
    display: none;
  }

  .is-keyboard .area-grid-note.is-note-folded .area-card-note-toggle {
    display: block;
    align-self: center;
    min-height: 2.75rem;
    padding: 0 var(--space-4);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-pill);
    background: var(--color-surface);
    color: var(--color-text);
    font: var(--type-label);
  }

  /* Sulla carta i tasti non servono: resta lo stato dei salvataggi. */
  .area-grid-form .kb-keys-list {
    display: none;
  }
}

/* I due link fra un'area e la successiva. Larghi e in fondo: si arriva qui
   dopo aver percorso quattordici righe, e la cosa che serve subito dopo è
   l'area seguente. */
.pager {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

.pager:has(> :only-child) {
  /* Una sola freccia — prima o ultima area — non deve stare al centro né
     saltare da un lato all'altro: resta dove starebbe se ci fossero
     entrambe. */
  justify-content: flex-start;
}

.pager > :last-child:not(:only-child) {
  margin-left: auto;
}


/* --- (v1.25) Selettore della passata: area o una delle sue sotto-voci -----
   Non è una modalità diversa, è la stessa tabella con una lente più stretta:
   quello che si osserva davvero è un'esercitazione di incroci, non «il
   pattinaggio». Un elenco di link e non un menu a tendina, perché le voci
   sono al massimo sette e si vogliono vedere tutte insieme — un menu
   chiederebbe un click per sapere cosa c'è dentro. */
.sub-switch {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}

.sub-switch-label {
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.sub-switch-option {
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  text-decoration: none;
  color: var(--color-text);
}

/* La passata attiva si riconosce dal fondo pieno *e* dal bordo, non dal solo
   colore: `aria-current` la dice a chi non vede la differenza di tinta. */
.sub-switch-option.is-current {
  background: var(--color-chip-bg);
  border-color: var(--color-chip-border);
  color: var(--color-chip-text);
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
}

/* Il nome della sotto-voce nel titolo, dopo quello dell'area: più leggero,
   perché è la lente e non il soggetto. */
.area-sub-title {
  color: var(--color-text-muted);
  font: var(--type-title);
}

/* Riga senza livello d'area: al posto dei chip, il motivo. Controlli che non
   salvano sarebbero peggio di nessun controllo. */
/* Il livello d'area accanto al nome durante una passata su una sotto-voce:
   contesto, quindi discreto, ma non invisibile — è il giudizio che si sta
   approfondendo. */
.area-grid-context {
  font: var(--type-caption);
  color: var(--color-text);
}

.area-grid-blocked {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* --- (v1.49, issue #40) Valutare dalla tastiera ---------------------------
   Tutto quello che segue esiste solo con lo script acceso
   (`static/assessment-keyboard.js`): la riga dei tasti nasce `hidden`, e
   `.is-keyboard` la mette lo script sul form. */

/* I tasti, in una riga sempre visibile sopra la tabella. Compatta: è un
   promemoria, non una spiegazione. */
.kb-keys {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-1) var(--space-3);
  margin-bottom: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
}

/* `hidden` da solo perde contro il `display` di una classe (`.page-actions`
   è un flex): quello che lo script nasconde deve restare nascosto. */
.kb-keys[hidden],
[data-keyboard-hide][hidden] {
  display: none;
}

/* Su uno schermo largo la riga resta ferma sotto la barra in alto mentre
   la tabella scorre: è anche il posto dove si legge se qualcosa non è
   stato salvato. Su un telefono no — l'intestazione, lì, copre già metà
   schermo, e a bordo pista la tastiera non c'è. Quanto è alta la barra lo
   scrive lo script in `--kb-sticky-top`. */
@media (min-width: 1024px) {
  .kb-keys {
    position: sticky;
    top: var(--kb-sticky-top, 0px);
    z-index: 6;
    box-shadow: 0 2px 6px rgb(0 0 0 / 0.06);
  }
}

.kb-keys-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  margin: 0;
  font: var(--type-caption);
}

.kb-keys-status {
  margin: 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.kb-keys-status.is-unsaved {
  color: var(--color-warning-text);
  font-weight: 700;
}

.kb-keys kbd,
.assessment-start kbd {
  font: var(--type-caption);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: 0 0.3em;
  background: var(--color-bg);
}

/* Il cursore. Si riconosce dal fondo *e* dal filetto a sinistra, non dal
   solo colore. Quanto spazio lasciargli sopra quando la pagina scorre lo
   misura lo script (`scroll-padding-top`), perché l'altezza della barra in
   alto cambia con la larghezza. */
.is-keyboard [data-stop],
.is-keyboard tr[data-stop] > td {
  scroll-margin-block-end: var(--space-4);
}

.area-grid tr.is-cursor > td {
  background: var(--color-primary-soft);
}

.area-grid tr.is-cursor > td:first-child {
  box-shadow: inset 4px 0 0 var(--color-primary);
}

.level-choice.is-cursor,
.sub-skill.is-cursor {
  outline: 3px solid var(--color-primary);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
  background: var(--color-primary-soft);
}

/* Nome e numero spostano il cursore: che si possano cliccare deve vedersi. */
.is-keyboard [data-cursor-for] {
  cursor: pointer;
}

/* Una riga (o un'area) non salvata: ambra, come ogni cosa che chiede
   attenzione senza essere un errore da correggere a mano — si riprova da
   sola al tasto successivo. */
.area-grid tr.is-unsaved > td:first-child {
  box-shadow: inset 4px 0 0 var(--color-warning-border);
}

/* (issue #95) Sotto 900px la riga è un blocco e la prima cella è la sola
   maglia: il fondo e il filetto non stanno più lì, o il cursore segnerebbe
   un quadratino in alto a sinistra. (issue #100) E la riga è una carta, una
   per schermata: quella in vista **è** il cursore, e colorarla non direbbe
   niente. Resta l'ambra della carta non salvata, sul bordo. Dopo le regole
   della v1.49 qui sopra, o perderebbe a pari specificità. */
@media (max-width: 899px) {
  .area-grid tr.is-cursor > td,
  .area-grid tr.is-cursor > td:first-child,
  .area-grid tr.is-unsaved > td:first-child {
    background: none;
    box-shadow: none;
  }

  .area-grid tr.is-unsaved {
    border-color: var(--color-warning-border);
    box-shadow: inset 0 0 0 1px var(--color-warning-border);
  }
}

.area-block.is-unsaved {
  border-color: var(--color-warning-border);
}

.kb-row-status {
  margin: var(--space-1) 0 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.kb-row-status strong {
  color: var(--color-warning-text);
}

/* L'ingresso nella pagina della rilevazione. */
.assessment-start {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  margin: var(--space-3) 0 var(--space-4);
}


/* --- (v1.25) Scheda del singolo giocatore ---------------------------------
   §8 del requisito: se serve condividere qualcosa in un colloquio si esporta
   la scheda del singolo, mai la matrice di squadra.

   Fuori da `@media print`, come il foglio G+S della seduta e per lo stesso
   motivo (§10bis... no: ARCHITECTURE.md §7.8septies): è la **visibilità** a
   decidere quando il foglio si vede — in stampa sempre, a schermo solo in
   anteprima — non il fatto che le regole esistano. Tutti i selettori sono
   `.player-sheet-*`, quindi finché `.print-only` resta `display: none` a
   schermo non cambia niente.

   Unità in `pt` e `cm`: sono unità fisiche e valgono anche a schermo
   (1cm CSS = 96dpi/2.54), ed è la ragione per cui l'anteprima può essere
   fedele senza una seconda resa. */
.player-sheet {
  font: var(--typep-body);
  line-height: 1.4;
  color: #000;
}

.player-sheet-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1cm;
  margin-bottom: 10pt;
  padding-bottom: 8pt;
  border-bottom: 1pt solid #000;
}

/* 6cm e non a tutta pagina: il logo ha il fondo nero, e una fascia larga
   21cm sarebbe mezza cartuccia spesa per una riga di identità. */
.player-sheet-logo {
  width: 6cm;
  height: auto;
  display: block;
}

.player-sheet-meta {
  text-align: right;
  font: var(--typep-caption);
  color: #444;
}

.player-sheet-name {
  display: flex;
  align-items: center;
  gap: 8pt;
  margin: 0 0 6pt;
  font: var(--typep-screen);
  line-height: 1.1;
}

.player-sheet-number {
  display: inline-block;
  min-width: 1.5em;
  padding: 1pt 5pt;
  border: 1.5pt solid #000;
  border-radius: 3pt;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* Anagrafica su due colonne che scorrono: etichetta grigia, valore nero.
   `dl` e non una tabella perché sono coppie nome/valore, ed è la struttura
   che una tecnologia assistiva legge come tali. */
.player-sheet-id {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 2pt 10pt;
  margin: 0 0 10pt;
}

.player-sheet-id dt {
  color: #555;
  font: var(--typep-eyebrow);
  letter-spacing: var(--typep-eyebrow-ls);
}

.player-sheet-id dd {
  margin: 0;
}

.player-sheet-note {
  margin: 0 0 10pt;
  padding: 5pt 7pt;
  border-left: 2pt solid #000;
  font: var(--typep-caption);
}

.player-sheet-section {
  margin: 12pt 0 5pt;
  font: var(--typep-section);
  /* Il titolo non deve restare da solo in fondo a una pagina con la sua
     tabella che comincia sulla successiva. */
  break-after: avoid;
  page-break-after: avoid;
}

.player-sheet-table {
  width: 100%;
  border-collapse: collapse;
  font: var(--typep-body);
}

.player-sheet-table th,
.player-sheet-table td {
  text-align: left;
  vertical-align: top;
  padding: 3pt 6pt 3pt 0;
  border-bottom: 0.5pt solid #bbb;
}

.player-sheet-table thead th {
  border-bottom: 1pt solid #000;
  font: var(--typep-eyebrow);
  letter-spacing: var(--typep-eyebrow-ls);
  text-transform: uppercase;
  color: #444;
}

/* La barra **e** l'etichetta del livello, non l'una o l'altra: su carta in
   bianco e nero la rampa di colore non sopravvive, la lunghezza sì, e il
   testo regge da solo se anche quella si perde. */
.player-sheet-bar-track {
  display: inline-block;
  width: 2.2cm;
  height: 7pt;
  margin-right: 5pt;
  border: 0.5pt solid #999;
  vertical-align: middle;
}

/* Il riempimento lo porta la classe `.lvl-N` della rampa ordinale, come nel
   profilo a schermo: una sola tabella di colori per una sola scala. Qui
   c'era `background: currentColor`, che essendo scritto dopo vinceva sulla
   rampa e lasciava la barra vuota — la classe c'era ma non si vedeva.

   `print-color-adjust: exact` perché senza, molti browser stampano gli
   sfondi come bianco e la barra sparirebbe sul foglio. Non è decorazione: la
   lunghezza è il livello. Il livello è comunque scritto per esteso accanto,
   quindi anche una stampante che ignorasse tutto lascia la scheda leggibile. */
.player-sheet-bar {
  display: block;
  height: 100%;
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}

.player-sheet-empty {
  color: #777;
}

.player-sheet-free-note {
  margin: 0;
  font: var(--typep-caption);
}

.player-sheet-foot {
  margin-top: 14pt;
  padding-top: 5pt;
  border-top: 0.5pt solid #999;
  font: var(--typep-caption);
  color: #555;
}

/* In anteprima il foglio è A4 esatto, con i margini di `@page` resi come
   padding — stesso trattamento del foglio G+S. */
.sheet-preview .player-sheet {
  box-sizing: border-box;
  width: 21cm;
  min-height: 29.7cm;
  padding: 1.2cm 1.5cm;
  margin: 0 auto;
  background: #fff;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.35);
}

/* --- (v1.26, PIANO-CATEGORIE.md Fase 2) Categorie d'età --- */

/* Le due fasce di ammissione stanno affiancate, e la larghezza le tiene
   insieme: separate da un a-capo smetterebbero di leggersi come coppia. */
.admission-pair {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.admission-pair > * {
  flex: 1 1 12rem;
}

/* (v1.26, PIANO-CATEGORIE.md Fase 3b) L'avviso di fascia d'età. Corpo
   piccolo e a capo sotto il nome: è un'informazione da leggere una volta e
   poi confermare, non un errore da correggere — un ragazzo fuori fascia si
   tessera lo stesso, motivando. */
.admission-warning {
  display: block;
  font: var(--type-caption);
  color: var(--color-warning-text);
}

/* --- (v1.26, Fase 5) La vista persona --- */

/* Lo Storico (23.09.2026, mockup approvato `storico-persona-mockup.html`,
   variante A). Le stagioni in fila, dalla più recente, separate da un
   filetto orizzontale: il filetto verticale di prima correva lungo tutta la
   sezione senza dire nulla. Numerata nel markup (`<ol>`) perché è una
   sequenza temporale, ma senza i numeri a schermo: una carriera non è una
   classifica. */
.person-timeline {
  list-style: none;
  padding: 0;
  margin: 0;
}

.person-season {
  padding: var(--space-4) 0;
  border-top: 1px solid var(--color-border);
}

.person-season:first-child {
  padding-top: 0;
  border-top: 0;
}

.person-season:last-child {
  padding-bottom: 0;
}

.person-season-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-2) var(--space-3);
  margin-bottom: var(--space-2);
}

/* L'anno è l'occhiello, la squadra il titolo: con «Stagione 2026» come
   titolo due squadre dello stesso anno si leggevano come un doppione. */
.person-season-eyebrow {
  margin: 0 0 2px;
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.person-season-head h3 {
  margin: 0;
  font: var(--type-title);
  font-size: 1.375rem;
}

.person-season-category {
  margin-left: var(--space-1);
  color: var(--color-text-muted);
}

.person-season-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}

/* Il numero di maglia come l'avatar della testata, in piccolo. */
.person-season-number {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  background: var(--color-text);
  color: var(--color-surface);
  font: var(--type-label);
  font-variant-numeric: tabular-nums;
}

.person-season-roles {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* «Nessuna rilevazione» e le rilevazioni di una squadra che non vedi: una
   riga leggera, perché il peso vada alle stagioni che hanno qualcosa. */
.person-season-empty {
  margin: 0;
  font: var(--type-caption);
  font-style: italic;
  color: var(--color-text-muted);
}

.person-block-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-1) var(--space-3);
  margin: var(--space-3) 0 var(--space-2);
}

.person-block-head .ramp-legend {
  margin: 0;
}

.person-block-title {
  margin: 0;
  font: var(--type-thead);
  letter-spacing: var(--type-thead-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* (issue #91) I livelli di una stagione: una tessera per area, larga quanto
   la sua colonna. Le colonne le decide lo spazio (`auto-fill`), non una
   media query: cinque o sei sulla card larga, una sul telefono. Era una
   tabella stretta che troncava i nomi delle aree («Conduzione del…»). */
.person-level-grid {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
  gap: var(--space-2);
  margin: 0;
  padding: 0;
}

.person-level-chip {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  min-height: 3.1rem;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
}

.person-level-area {
  font: var(--type-label);
}

.person-level-value {
  font: var(--type-caption);
  opacity: 0.9;
}

/* (issue #142) I numeri **nostri** sotto la riga della fonte. Si
   distinguono a colpo d'occhio e senza legenda per tre cose che non sono un
   colore: stanno in una riga sotto e non in una colonna, cominciano con
   «Calcolato da noi», e portano un filetto a sinistra che la tabella non ha
   da nessun'altra parte. Inchiostro tenue come `.elsewhere-level`: sono
   contesto, non una scala e non una classifica, e una tinta li farebbe
   sembrare un livello. La riga della fonte perde il suo filetto quando ne
   porta una: senza, la derivata sembrerebbe della riga dopo. */
.fed-stats tbody tr:has(+ .fed-derived) th,
.fed-stats tbody tr:has(+ .fed-derived) td {
  border-bottom: 0;
}

/* La riga derivata non deve allargare la tabella: senza un tetto, la sua
   cella a tutta larghezza diventa la colonna più larga e sparpaglia i
   numeri della fonte sotto le loro intestazioni. Il tetto è anche in
   viewport perché una percentuale, dentro una tabella, si misurerebbe sulla
   larghezza che sta decidendo: in `rem` e basta, sul telefono la frase
   spingeva la tabella oltre lo schermo. Sotto 1024px la barra laterale non
   c'è, quindi `100vw` meno i margini è la larghezza della card; sopra, i
   34rem vincono comunque. */
.fed-derived td {
  text-align: left;
  padding: 0 var(--space-1) var(--space-2);
  max-width: min(34rem, calc(100vw - 5rem));
}

.fed-derived-title {
  font: var(--type-thead);
  letter-spacing: var(--type-thead-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin: 0;
}

.fed-derived-list {
  list-style: none;
  margin: 0;
  padding: 0 0 0 var(--space-2);
  border-left: 2px solid var(--color-border);
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.fed-derived-list li {
  margin: var(--space-1) 0 0;
}

/* (issue #140) Le statistiche federative: la colonna di competizione e
   squadra è testo e sta a sinistra, i numeri a destra come in ogni
   `stack-table`. La squadra va a capo sotto la competizione, in piccolo:
   così la tabella regge a 390px senza scorrere. Nessuna tinta: sono
   statistiche, non livelli. */
.fed-stats {
  width: auto;
  min-width: min(100%, 32rem);
}

.fed-stats .fed-stats-where {
  text-align: left;
  padding-right: var(--space-4);
}

/* Il gesto di scaricare è un bottone solo, non un modulo: senza la cornice
   che `form` porta di suo, come le azioni della scheda di una partita. */
.person-federation-stats form {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  background: none;
  border: 0;
  padding: 0;
  margin: var(--space-3) 0 0;
}

.fed-stats-team {
  display: block;
  color: var(--color-text-muted);
}

.fed-stats th[scope="row"] {
  max-width: none;
  white-space: nowrap;
}

.person-federation-stats h3 {
  margin: var(--space-4) 0 var(--space-2);
}

.fed-history {
  margin: var(--space-2) 0;
}

.fed-history > summary {
  cursor: pointer;
  font-variant-numeric: tabular-nums;
}

/* Le rilevazioni di una stagione: righe compatte, e la riga intera è il
   bersaglio del tocco (il nome è l'unico link, e lo copre con `::after`). */
.person-assessments .person-block-title {
  margin: var(--space-3) 0 var(--space-1);
}

.person-assessment-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--color-border);
}

.person-assessment-row {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-1) var(--space-3);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--color-border);
}

.person-assessment-row:hover {
  background: var(--color-bg);
}

.person-assessment-link {
  color: inherit;
  text-decoration: none;
  font: var(--type-body-strong);
}

.person-assessment-link::after {
  content: "";
  position: absolute;
  inset: 0;
}

.person-assessment-meta {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-left: auto;
  font: var(--type-caption);
  color: var(--color-text-muted);
  white-space: nowrap;
}

.person-assessment-team {
  padding: 1px 10px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
  background: var(--color-bg);
}

.person-assessment-chevron {
  flex: none;
}

/* --------------------------------------------------------------------------
   La scheda persona come cruscotto (23.09.2026, mockup approvato
   `profilo-persona-mockup.html`): una testata con la squadra attuale e il
   suo club, le schede in ancora, le carte della carriera, il grafico
   accanto a «Ruolo attuale», poi statistiche, partite e storico.
   Una scala sola di spazi, `--space-*`: nessuna riga tocca l'altra.
   -------------------------------------------------------------------------- */
.profile-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: var(--space-3);
}

.profile-head-banner {
  flex-basis: 100%;
  margin: 0;
}

.profile-head-main {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  min-width: 0;
}

/* Nessuna fotografia nell'app (issue #139): il numero di maglia è l'avatar;
   senza numero, l'iniziale. */
.profile-avatar {
  flex: none;
  width: 4.5rem;
  height: 4.5rem;
  border-radius: 50%;
  background: var(--color-text);
  color: var(--color-surface);
  display: flex;
  align-items: center;
  justify-content: center;
  font: var(--type-figure);
  font-variant-numeric: tabular-nums;
}

.profile-head h1 {
  margin: 0 0 var(--space-1);
}

.profile-head-line {
  margin: 0;
  font: var(--type-body);
}

.profile-head-line-muted {
  color: var(--color-text-muted);
  margin-top: var(--space-1);
}

.profile-head-actions {
  margin: var(--space-3) 0 0;
}

.profile-head-actions .btn {
  gap: var(--space-2);
}

/* La squadra attuale, a destra; sul telefono va sotto. */
.profile-side {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-2);
  text-align: right;
  min-width: 14rem;
}

.profile-side .field-hint {
  margin: 0;
}

.profile-club {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-direction: row-reverse;
}

/* Il logo del club (issue #133): uno stemma vero non è quadrato, lo si
   contiene e non lo si ritaglia. */
.profile-club-logo {
  flex: none;
  width: 4.5rem;
  height: 3rem;
  object-fit: contain;
}

.profile-club-names {
  display: flex;
  flex-direction: column;
}

.profile-club-team {
  font: var(--type-body-strong);
}

/* Il menu ⋮ dei gesti sul profilo federativo, senza script: un `details`. */
.profile-menu {
  position: relative;
}

.profile-menu-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  color: var(--color-text-muted);
  cursor: pointer;
  list-style: none;
}

.profile-menu-toggle::-webkit-details-marker {
  display: none;
}

.profile-menu-toggle:hover,
.profile-menu[open] .profile-menu-toggle {
  background: var(--color-bg);
  color: var(--color-text);
}

.profile-menu-panel {
  position: absolute;
  right: 0;
  top: calc(100% + var(--space-1));
  width: min(22rem, 88vw);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  padding: var(--space-3);
  z-index: 5;
  text-align: left;
}

.profile-menu-divider {
  border: 0;
  border-top: 1px solid var(--color-border);
  margin: var(--space-3) 0 0;
}

/* Le schede sono ancore nella stessa pagina: nessuna richiesta in più. */
.profile-tabs {
  display: flex;
  gap: var(--space-4);
  overflow-x: auto;
  border-bottom: 1px solid var(--color-border);
  margin: 0 0 var(--space-4);
}

.profile-tab {
  flex: none;
  padding: var(--space-2) 0;
  color: var(--color-text-muted);
  text-decoration: none;
  font: var(--type-label);
  border-bottom: 2px solid transparent;
  white-space: nowrap;
}

.profile-tab:hover {
  color: var(--color-text);
}

.profile-tab.is-current {
  color: var(--color-primary-dark);
  border-bottom-color: var(--color-primary);
}

/* Le cinque carte della carriera (23.09.2026, mockup approvato
   `statistiche-persona-mockup.html`): niente icona, il numero a destra, e
   in testa un filetto del colore che la statistica ha nel grafico sotto —
   verde i gol, blu gli assist. È un aggancio, non il nome: l'etichetta
   resta scritta. */
.profile-kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.profile-kpis > .stat-card {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-3);
  min-width: 0;
  border-top: 3px solid #cdd3db;
}

.profile-kpis > .stat-card.is-goals { border-top-color: var(--color-ok-border); }
.profile-kpis > .stat-card.is-assists { border-top-color: var(--fam-tecnica); }
.profile-kpis > .stat-card.is-points { border-top-color: var(--color-primary); }
.profile-kpis > .stat-card.is-penalties { border-top-color: var(--color-warning-border); }

.profile-kpis-caption {
  margin: 0 0 var(--space-2);
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.profile-kpis .stat-card-label {
  align-self: center;
  margin: 0;
}

.stat-card-num {
  display: flex;
  flex: none;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  text-align: right;
}

/* Qui il numero è la cifra isolata della pagina, non il titolo di una
   carta fra tante. */
.profile-kpis .stat-card-value {
  font: var(--type-figure);
  line-height: 1;
}

.stat-card-derived {
  font: var(--type-caption);
  color: var(--color-text-muted);
  white-space: nowrap;
}

/* Il grafico per stagione più largo, la ciambella accanto; sotto i 900px
   una sopra l'altra. */
.profile-stat-charts {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-4);
  margin-bottom: var(--space-4);
  align-items: start;
}

.profile-stat-charts > .card {
  margin: 0;
  min-width: 0;
}

@media (min-width: 900px) {
  .profile-stat-charts {
    grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
  }
}

/* La ciambella: l'anello è SVG, il totale al centro è testo sopra. */
.stat-donut-layout {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
}

.stat-donut-figure {
  position: relative;
  flex: none;
  width: 168px;
  height: 168px;
  margin: 0 auto;
}

.stat-donut-figure svg {
  display: block;
  width: 100%;
  height: 100%;
}

.stat-donut-track {
  fill: none;
  stroke: var(--color-border);
  stroke-width: 30;
}

.stat-donut-seg {
  fill: none;
  stroke-width: 30;
}

.stat-donut-center {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

.stat-donut-center-value {
  font: var(--type-figure);
  line-height: 1;
}

.stat-donut-center-label {
  margin-top: 3px;
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.stat-donut-legend {
  flex: 1 1 11rem;
  min-width: 10rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.stat-donut-legend li {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  padding: 6px 0;
  border-bottom: 1px solid var(--color-border);
}

.stat-donut-legend li:last-child {
  border-bottom: 0;
}

.stat-donut-swatch {
  flex: none;
  width: 11px;
  height: 11px;
  border-radius: 3px;
  transform: translateY(1px);
}

.stat-donut-legend-name {
  flex: 1;
  min-width: 0;
}

.stat-donut-legend-value {
  font: var(--type-body-strong);
  white-space: nowrap;
}

.stat-donut-legend-pct {
  font: var(--type-caption);
  color: var(--color-text-muted);
  white-space: nowrap;
}

/* I toni: la competizione più grande nel verde acqua del marchio, poi
   verso il verde; il resto in grigio. */
.stat-donut-c1 { stroke: var(--color-primary); background: var(--color-primary); }
.stat-donut-c2 { stroke: #1e9a8c; background: #1e9a8c; }
.stat-donut-c3 { stroke: var(--color-ok-border); background: var(--color-ok-border); }
.stat-donut-other { stroke: #a7aeb8; background: #a7aeb8; }

.stat-donut-note {
  margin: var(--space-3) 0 0;
}

/* Punti o Partite: due pillole su due radio, e la vista scelta col `~`. */
.donut-tabs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
}

.donut-tab {
  margin: 0 -1px var(--space-3) 0;
  padding: 0.35rem 0.9rem;
  font: var(--type-label);
  color: var(--color-text-muted);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  cursor: pointer;
  user-select: none;
}

.donut-tab:first-of-type {
  border-radius: var(--radius-pill) 0 0 var(--radius-pill);
}

.donut-tab:last-of-type {
  margin-right: 0;
  border-radius: 0 var(--radius-pill) var(--radius-pill) 0;
}

.donut-pick:checked + .donut-tab {
  position: relative;
  z-index: 1;
  color: var(--color-text);
  background: var(--color-surface);
  border-color: var(--color-primary);
}

.donut-pick:focus-visible + .donut-tab {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.donut-view {
  display: none;
  flex-basis: 100%;
}

#donut-points:checked ~ #donut-view-points,
#donut-games:checked ~ #donut-view-games {
  display: block;
}

/* Statistiche e partite. Le card partono tutte dalla stessa riga: `.card`
   porta un margine verticale suo. */
.profile-columns,
.profile-two-col {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4);
  margin-bottom: var(--space-4);
  align-items: start;
}

.profile-columns > *,
.profile-two-col > * {
  min-width: 0;
}

.profile-columns > .card,
.profile-two-col > .card,
.profile-side-stack > .card {
  margin: 0;
}

@media (min-width: 900px) {
  .profile-two-col {
    grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr);
  }
}

@media (max-width: 640px) {
  .profile-kpis {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* «Ruolo attuale», il link e le note: una riga di card affiancate, ora che
   il grafico sta sopra con la ciambella. */
.profile-side-stack {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: var(--space-4);
  align-items: start;
}

.info-rows {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-2) var(--space-3);
  margin: 0;
}

.info-rows dt {
  color: var(--color-text-muted);
  font: var(--type-caption);
}

.info-rows dd {
  margin: 0;
  font: var(--type-body-strong);
}

.profile-note {
  margin: 0;
  white-space: pre-line;
}

/* La legenda del grafico: forme, non solo colori. */
.profile-chart-legend {
  display: flex;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin: 0 0 var(--space-2);
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.profile-chart-legend span {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.profile-chart-legend i {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 3px;
}

.profile-chart-legend .swatch-goals { background: var(--color-ok-border); }
.profile-chart-legend .swatch-assists { background: var(--fam-tecnica); }

.profile-chart-legend .line-swatch {
  width: 16px;
  height: 0;
  border-radius: 0;
  border-top: 2px solid var(--color-text);
}

.profile-chart-legend .line-swatch.has-dot {
  position: relative;
}

/* Il pallino sulla linea: ogni pallino del grafico è un punti a partita. */
.profile-chart-legend .line-swatch.has-dot::after {
  content: "";
  position: absolute;
  left: 4px;
  top: -5px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-text);
}

.profile-chart-legend .line-swatch.is-dashed {
  border-top-color: var(--color-text-muted);
  border-top-style: dashed;
}

.profile-chart-legend .dot-swatch.is-hollow {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-surface);
  border: 2px solid var(--color-text);
}

/* Il grafico, reso dal server (`web::person_career_views`). Sotto i 34rem
   le scritte degli assi diventerebbero illeggibili: il grafico si ferma lì
   e scorre **dentro** la sua card, la pagina no. */
.profile-chart-frame {
  overflow-x: auto;
}

.career-chart {
  display: block;
  width: 100%;
  min-width: 34rem;
  height: auto;
}

.career-chart .grid-line {
  stroke: var(--color-border);
  stroke-width: 1;
}

.career-chart .axis-label {
  fill: var(--color-text-muted);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}

.career-chart .axis-season {
  fill: var(--color-text);
  font-weight: 600;
}

.career-chart .axis-games {
  font-size: 10px;
}

.career-chart .axis-title {
  fill: var(--color-text);
  font-weight: 600;
}

/* Il totale della stagione in cima alla sua barra. */
.career-chart .bar-total {
  fill: var(--color-text);
  font-weight: 600;
}

.career-chart .bar-goals { fill: var(--color-ok-border); }
.career-chart .bar-assists { fill: var(--fam-tecnica); }

/* Le due linee passano **sopra** le barre, con un alone del colore della
   scheda: si staccano anche dove attraversano una colonna. */
.career-chart .career-line-halo,
.career-chart .career-avg-halo {
  fill: none;
  stroke: var(--color-surface);
  stroke-width: 6;
  stroke-linejoin: round;
}

.career-chart .career-line {
  fill: none;
  stroke: var(--color-text);
  stroke-width: 2;
  stroke-linejoin: round;
}

.career-chart .career-avg,
.career-chart .career-avg-tick {
  fill: none;
  stroke: var(--color-text-muted);
  stroke-width: 2;
  stroke-dasharray: 5 4;
}

.career-chart .career-dot {
  fill: var(--color-text);
  stroke: var(--color-surface);
  stroke-width: 2;
}

.career-chart .career-dot.is-hollow {
  fill: var(--color-surface);
  stroke: var(--color-text);
}

/* Il link alla scheda della persona, accanto al nome nel roster. Una freccia
   e non una parola: la riga è già piena, e «tutte le sue stagioni» starebbe
   su due righe in una colonna che ne ha una. Il titolo dice il resto. */
.roster-person-link {
  margin-left: var(--space-1);
  text-decoration: none;
  color: var(--color-text-muted);
}

/* ===========================================================================
   Il cancello (PIANO-CLOUD.md §A1)
   ===========================================================================

   `login.html` non estende `base.html` — niente header, niente sidebar,
   niente griglia `.app-shell` — quindi ha bisogno del proprio impianto di
   pagina, non solo di ritocchi. Sono poche regole: una porta chiusa deve
   avere una cosa sola su cui puntare lo sguardo.

   Il fondo è quello dell'app (`--color-bg`) e non un colore a sé: chi
   arriva qui deve riconoscere subito dove sta entrando, e una schermata di
   login che sembra un altro sito è il modo migliore per far dubitare che
   lo sia. */

.login-body {
  min-height: 100vh;
  /* `dvh` dove c'è: su iOS la barra degli indirizzi entra e esce dalla
     viewport, e con `100vh` la card salterebbe mentre si digita. */
  min-height: 100dvh;
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-sans);
}

.login-card {
  width: 100%;
  /* 24 e non 22rem: il marchio esteso è largo tre volte e mezzo la sua
     altezza, e sotto questa misura «TRAIN HARD. PLAY SMART. GROW STRONG.»
     smette di essere una riga e diventa un filetto grigio. */
  max-width: 24rem;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  /* Il padding è sceso in `.login-inner`: la fascia del marchio deve
     arrivare ai bordi della card, non fermarsi a 2,5rem da loro. */
  padding: 0;
  /* Così la fascia prende i due angoli superiori senza doverne ricalcolare
     il raggio meno il bordo — un conto che si disallinea al primo ritocco
     di `--radius-lg`. Niente qui dentro deve debordare: l'anello di focus
     dei campi sta a 2px, e attorno ce ne sono 40. */
  overflow: hidden;
  text-align: center;
}

/* (20.09.2026) La fascia del marchio (`partials/login_head.html`), su
   accesso e invito. Il fondo è `--color-text`, l'inchiostro più scuro del
   foglio: il disegno chiede un fondo scuro e una tinta in più sarebbe una
   tinta da tenere allineata alle altre per sempre. Il marchio porta già
   nome e tagline, quindi qui non serve nessun titolo di testo accanto —
   per la stessa ragione per cui non ce n'è nella sidebar. */
.login-head {
  background: var(--color-text);
  padding: var(--space-4);
}

.login-banner {
  display: block;
  width: 100%;
  height: auto;
}

.login-inner {
  padding: var(--space-5);
}

/* La regola globale `form` (più in alto) dà a ogni form superficie, bordo e
   padding: dentro la card di login diventa una scatola dentro la scatola —
   due cornici concentriche attorno a un campo solo. Qui il form non è un
   blocco *dentro* una pagina, è il contenuto della card, quindi le tre
   proprietà si annullano invece di aggiungere una terza cornice. */
.login-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  text-align: left;
  background: none;
  border: none;
  border-radius: 0;
  padding: 0;
  margin-bottom: 0;
}

.login-form label {
  font-weight: 600;
}

.login-form input[type="email"],
.login-form input[type="password"] {
  width: 100%;
  box-sizing: border-box;
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  /* 16px pieni e non di meno: sotto i 16px iOS ingrandisce la pagina
     appena il campo prende il fuoco, e la card finisce fuori schermo
     proprio mentre si scrive. */
  font-size: 16px;
  font-family: inherit;
}

/* (v1.59) Nessun anello di focus proprio: con `email` e `password` nella
   regola comune dei campi, il login prende lo stesso anello di tutta l'app
   invece di sommargli un secondo contorno. */

.login-card .banner {
  text-align: left;
}

/* Sul telefono — che è il modo in cui questa pagina verrà aperta più spesso,
   a bordo pista — 2.5rem di padding per lato su una card da 22rem lasciano
   al campo meno spazio di quanto meriti l'unica cosa da fare qui. Si stringe
   la cornice, non il contenuto. */
@media (max-width: 26rem) {
  .login-body {
    padding: var(--space-3);
  }

  .login-inner {
    padding: var(--space-4);
  }

  .login-head {
    padding: var(--space-3);
  }
}

/* Il logout vive nel piede della sidebar, dove stava la riga «Uso locale ·
   nessun login» che questo cancello ha reso falsa. Discreto di proposito:
   è un'azione che serve una volta ogni mai, e in una app usata da una
   persona sola un pulsante «esci» in evidenza è solo un modo di uscire per
   sbaglio. */
/* (v1.59) La regola globale `form` dà a ogni form superficie, bordo e
   padding: sotto i due form dell'accesso «Esci» diventava una terza
   scatola, grande quanto loro, per un link. Come per `.login-form`, le tre
   proprietà si annullano. */
.sidebar-logout {
  margin-top: var(--space-2);
  background: none;
  border: none;
  border-radius: 0;
  padding: 0;
}

.sidebar-logout button {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: inherit;
  opacity: 0.75;
  text-decoration: underline;
  cursor: pointer;
}

.sidebar-logout button:hover,
.sidebar-logout button:focus-visible {
  opacity: 1;
}


/* ===========================================================================
   (v1.32) La pagina dei modelli di stagione.

   Tre sezioni impilate nell'ordine delle decisioni: le date, il modello, lo
   stile — e sotto l'anteprima. L'ordine non è estetico: senza le date
   l'anteprima non esiste, e mostrare l'elenco dei modelli prima di poterli
   disegnare inviterebbe a scegliere alla cieca. (v1.75, issue #64) In fondo il
   ritmo, nello stesso form di «Applica»: sotto i link di stile e blocchi,
   che così non buttano giorni scelti e non salvati.
   =========================================================================== */

.plan-anchors,
.plan-choices,
.plan-variants,
.plan-preview,
.plan-rhythm {
  margin: 0 0 var(--space-6);
  /* (v1.77) Ogni sezione è anche un'ancora (`#date`, `#modello`, `#stile`,
     `#anteprima`, `#ritmo`): senza, la barra fissa in cima (`.app-topbar`,
     da 1024px) coprirebbe il titolo della sezione su cui la pagina riapre. */
  scroll-margin-top: 5rem;
}

/* (v1.86, issue #73) Cosa ha spostato l'ultimo salvataggio delle date: un
   elenco corto dentro il banner, con lo stesso rientro di `.banner-danger`. */
.plan-anchors-notice ul {
  margin: 0;
  padding-left: 1.25em;
}

/* Le nove date stanno in griglia e non incolonnate: sono poche e vanno
   guardate insieme — è l'ordine fra loro che l'allenatore deve poter
   verificare a colpo d'occhio.

   (19.09.2026, mockup dell'utente) Un riquadro solo, con la testa — che
   cosa sono, e a destra che si possono lasciare in bianco — e tre gruppi.
   Ogni campo è quattro righe della griglia del gruppo (`subgrid`): nome,
   marche («(facoltativa)», «(stima)», «fissata»), data, note. Così le date
   di una riga stanno alla stessa altezza anche quando solo alcune portano
   una marca: prima l'etichetta avvolgeva tutto e la marca spingeva giù il
   suo campo. */
.plan-anchors {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-4);
}

/* Il riquadro è la sezione: il form dentro non ne disegna un secondo. */
.plan-anchors > form {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  margin: 0;
}

.plan-anchors-head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--space-3) var(--space-4);
  align-items: start;
  margin-bottom: var(--space-4);
}
.plan-anchors-head h2 {
  margin: 0 0 var(--space-1);
}
.plan-anchors-head .field-hint {
  margin: 0;
}
.plan-anchors-emblem {
  display: grid;
  place-items: center;
  width: 3.5rem;
  height: 3.5rem;
  border-radius: 50%;
  background: var(--color-bg);
  color: var(--color-text);
}
.plan-anchors-tip {
  grid-column: 1 / -1;
  display: flex;
  gap: var(--space-2);
  align-items: flex-start;
  padding: var(--space-3);
  border: 1px solid var(--color-primary);
  border-radius: var(--radius-md);
  background: var(--color-primary-soft);
  color: var(--color-primary-dark);
}
.plan-anchors-tip svg {
  flex: none;
  margin-top: 0.1rem;
}
.plan-anchors-tip p {
  margin: 0;
  font-size: 0.9rem;
}
.plan-anchors-tip strong {
  display: block;
  margin-bottom: var(--space-1);
}
@media (min-width: 64rem) {
  .plan-anchors-head {
    grid-template-columns: auto minmax(0, 1fr) minmax(16rem, 24rem);
  }
  .plan-anchors-tip {
    grid-column: auto;
  }
}

.plan-anchor-group {
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4) var(--space-4);
  margin: 0 0 var(--space-3);
}
.plan-anchor-group legend {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0 var(--space-1);
  font-size: 1.05rem;
}
.plan-anchor-group legend svg {
  color: var(--color-primary);
}

.plan-anchor-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(14rem, 100%), 1fr));
  column-gap: var(--space-4);
  row-gap: var(--space-4);
}
.plan-anchor-field {
  grid-row: span 4;
  display: grid;
  grid-template-rows: subgrid;
  row-gap: var(--space-1);
  min-width: 0;
}
.plan-anchor-label {
  align-self: end;
}
.plan-anchor-marks {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 0.4em;
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
}
.plan-anchor-input {
  position: relative;
  display: block;
}
.plan-anchor-input svg {
  position: absolute;
  left: var(--space-3);
  top: 50%;
  transform: translateY(-50%);
  z-index: 1;
  color: var(--color-text-muted);
  pointer-events: none;
}
.plan-anchor-input input {
  position: relative;
  padding-left: calc(var(--space-3) + 18px + var(--space-2));
}
/* Chrome disegna la sua icona a destra: la porta sotto la nostra, a
   sinistra, invisibile — una sola icona, e toccarla apre ancora il
   calendario. Safari l'icona non ce l'ha, e resta la nostra. */
.plan-anchor-input input::-webkit-calendar-picker-indicator {
  position: absolute;
  left: var(--space-3);
  width: 18px;
  height: 18px;
  margin: 0;
  padding: 0;
  opacity: 0;
  cursor: pointer;
}
.plan-anchor-notes {
  min-width: 0;
}
.plan-anchor-hint {
  margin: 0;
  font-size: 0.8rem;
  color: var(--color-text-muted);
}

/* La chiusura ha una data sola: la nota le sta accanto, non sotto, come
   nel mockup — sotto lascerebbe tre quarti del riquadro vuoti. */
.plan-anchor-group-single .plan-anchor-field {
  grid-column: 1 / -1;
  grid-row: auto;
  grid-template-rows: none;
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 48rem) {
  .plan-anchor-group-single .plan-anchor-field {
    grid-template-columns: minmax(14rem, 22rem) minmax(0, 1fr);
    grid-template-areas: "label ." "marks ." "input notes";
    column-gap: var(--space-4);
  }
  .plan-anchor-group-single .plan-anchor-label { grid-area: label; }
  .plan-anchor-group-single .plan-anchor-marks { grid-area: marks; }
  .plan-anchor-group-single .plan-anchor-input { grid-area: input; }
  .plan-anchor-group-single .plan-anchor-notes {
    grid-area: notes;
    align-self: center;
  }
}

.plan-anchors-actions {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  padding-top: var(--space-4);
  margin-top: var(--space-2);
  border-top: 1px solid var(--color-border);
}
.plan-anchors-actions .btn {
  min-width: 12rem;
}

/* (v1.85, issue #71) `.field-estimated` è la marca della data stimata:
   lo stesso trattamento del «facoltativo» — inchiostro secondario e una
   parola, mai un colore (i colori sono dei temi) né il corsivo — perché
   deve reggere anche in stampa, dove il `title` del tooltip non arriva.
   Una data *decisa* non la porta mai. */
.field-optional,
.field-estimated {
  color: var(--color-text-muted);
  font-weight: 400;
}

/* (v1.93, issue #79) Sotto la data, da dove viene: la partita che la dà,
   o cosa dice il calendario accanto a una fissata, col suo «Torna al
   calendario». Sta fuori dalla `<label>`, e le resta attaccata. */
.plan-anchor-source {
  margin: var(--space-1) 0 0;
}
.plan-anchor-source .btn-small {
  margin-left: var(--space-1);
}

/* (v1.78, issue #65) L'errore di un invio rifiutato sta sotto la data da
   correggere, non in cima alla sezione: con nove date bisognava cercare
   quale. Il bordo segna il campo anche a chi non legge il messaggio. */
.plan-anchor-grid .is-invalid input {
  border-color: var(--color-danger-border);
  box-shadow: 0 0 0 1px var(--color-danger-border);
}

.field-error {
  display: block;
  margin-top: var(--space-1);
  color: var(--color-danger-text);
  font-weight: 400;
}

/* (v1.88, issue #77) Il link al blocco che porta il confine fissato da
   staccare: sotto la data, dopo il messaggio che lo nomina. */
.field-error-fix {
  white-space: nowrap;
}

/* (v1.88, issue #77, requisito calendario §11) Il segno di **fissato**, uno
   solo per confini di blocco, ancore e partite (`partials/fixed_mark.html`).
   Forma e testo, mai un colore — i colori sono dei temi, e l'ambra e il
   rosso degli allarmi: la parola in maiuscolo dentro un riquadro a spigoli
   vivi, dove le etichette dei temi sono pastiglie tonde e piene. Il bordo è
   `currentColor`, così in stampa resta un riquadro. */
.fixed-mark {
  display: inline-block;
  padding: 0 var(--space-1);
  border: 1px solid currentColor;
  border-radius: 0;
  background: transparent;
  color: var(--color-text-muted);
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  vertical-align: 0.1em;
  white-space: nowrap;
}

/* Una data e il suo segno vanno a capo insieme (audit mobile del
   15.09.2026): a capo da solo, il segno della fine sembrava un secondo
   avviso. */
.block-bound {
  white-space: nowrap;
}

/* (v1.88) «Torna al calendario» sotto le date di un blocco: la frase che
   dice dove lo riporterebbe, e il pulsante accanto. */
.block-rehang {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1) var(--space-2);
  margin: var(--space-1) 0 0;
}

.block-rehang-text {
  color: var(--color-text-muted);
}

.block-bound-hint .btn {
  margin-left: var(--space-2);
}

.plan-choice-list,
.plan-variant-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
  gap: var(--space-3);
}

.plan-choice,
.plan-variant {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  color: inherit;
  text-decoration: none;
}

.plan-choice:hover,
.plan-variant:hover {
  border-color: var(--color-primary);
}

/* La scelta corrente si vede dal bordo e non da un riempimento: la card
   contiene già del testo, e uno sfondo colorato sotto un paragrafo lo rende
   più difficile da leggere proprio quando è quello che stai valutando. */
.plan-choice.is-selected,
.plan-variant.is-selected {
  border-color: var(--color-primary);
  box-shadow: inset 0 0 0 1px var(--color-primary);
}

.plan-choice-name,
.plan-variant-name {
  font-weight: 600;
}

.plan-choice-meta {
  color: var(--color-text-muted);
  font-size: 0.875rem;
}

.plan-choice-desc,
.plan-variant-desc {
  font-size: 0.9375rem;
  line-height: 1.45;
}

.plan-variant-warning {
  font-size: 0.875rem;
  color: var(--color-warning-text, #8a5a00);
}

.badge-quiet {
  background: transparent;
  border: 1px dashed var(--color-border);
  color: var(--color-text-muted);
}

.plan-toggle {
  display: inline-block;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  text-decoration: none;
  color: inherit;
}

.plan-toggle.is-on {
  border-color: var(--color-primary);
  box-shadow: inset 0 0 0 1px var(--color-primary);
}

/* (v1.79) «Una seduta a settimana» ha nome e descrizione come gli stili, e
   sotto le righe che dicono cosa ha tolto o perché proporla. */
.plan-concentrate {
  margin-top: var(--space-3);
  max-width: 46rem;
}
.plan-concentrate .plan-toggle {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.plan-preview-desc {
  max-width: 46rem;
  line-height: 1.55;
}

/* L'avviso sul catalogo e quello sulla sostituzione dei blocchi condividono
   la stessa forma: sono le due cose che l'allenatore deve leggere prima di
   premere il bottone, non dopo. */
.plan-alert {
  padding: var(--space-3);
  border-left: 3px solid var(--color-warning, #d9822b);
  background: var(--color-surface-muted, #f7f6f2);
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
  max-width: 46rem;
}

.plan-note {
  color: var(--color-text-muted);
  font-size: 0.9375rem;
  max-width: 46rem;
  margin: var(--space-1) 0;
}

/* (v1.77) L'anteprima è la timeline della vista d'insieme, nella sua
   card. Lì sotto i 1024px l'asse sparisce e resta l'elenco dei mesi; qui
   non c'è niente che ne prenda il posto, e l'asse scorre nel suo
   contenitore (`.timeline-scroller`) invece di sparire. */
.timeline-card-preview {
  margin: var(--space-4) 0 var(--space-3);
}
.timeline-card-preview .timeline-chart {
  display: block;
}

/* I blocchi facoltativi, con il loro «Disattiva»/«Riattiva»: una riga sola
   sotto il grafico, perché il blocco spento il grafico non lo disegna. */
.plan-optional-blocks {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-4);
  margin: 0 0 var(--space-3);
}
.plan-optional-label {
  font: var(--type-caption);
  color: var(--color-text-muted);
}
.plan-optional-block.is-disabled {
  color: var(--color-text-muted);
  text-decoration: line-through;
}
.plan-optional-block .plan-block-toggle {
  margin-left: var(--space-1);
}
.plan-optional-condition {
  color: var(--color-text-muted);
  font-size: 0.875rem;
}

.plan-block-more {
  max-width: 46rem;
}
.plan-block-more summary {
  cursor: pointer;
  color: var(--color-text-muted);
  font-size: 0.875rem;
}
.plan-block-detail {
  margin-top: var(--space-3);
}
.plan-block-name {
  font: inherit;
  font-weight: 600;
  margin: 0;
}
.plan-block-dates {
  color: var(--color-text-muted);
  font-size: 0.875rem;
  font-weight: 400;
}
.plan-block-toggle {
  font-size: 0.875rem;
}
.plan-block-objective,
.plan-block-mistake {
  margin: var(--space-1) 0;
  line-height: 1.45;
}
.plan-block-criteria-title {
  margin: var(--space-2) 0 0;
}

/* (v1.75, issue #64) Il form porta anche il ritmo, e i due regimi affiancati
   vogliono tutta la larghezza: il tetto di 46rem resta sulle righe di testo
   e sui pulsanti, che su una riga intera si leggerebbero male. */
.plan-apply {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.plan-apply > .plan-alert,
.plan-apply > .label-inline,
.plan-apply > .field-hint,
.plan-apply-actions {
  max-width: 46rem;
}

/* «Aggiorna l'anteprima» e «Applica» affiancati finché ci stanno, uno sotto
   l'altro sul telefono. Stessa larghezza tutti e due: uno salva il ritmo,
   l'altro lo salva e scrive il piano — il peso lo dà il colore. */
.plan-apply-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}
.plan-apply-actions button {
  flex: 1 1 14rem;
}

/* ============================================================================
   «Come siamo messi» (v1.35, docs/requisito-dashboard-squadra/)

   Una regola sola governa il colore di questa pagina: non nasce un terzo
   sistema di tinte. I ruoli si distinguono per **forma** (pieno/vuoto), i
   livelli portano la rampa ordinale già definita più su (`--lvl-*`), e nulla
   qui prende il colore di famiglia dei temi — ARCHITECTURE.md §10bis.2.
   Il vantaggio secondario è che tutto regge in stampa in bianco e nero, dove
   la rampa diventa una scala di grigi ma tre tinte diverse no.
   ========================================================================= */

.squad-section-title {
  margin-top: var(--space-5);
}

/* Il «/7» accanto alla copertura: è il denominatore, non il numero, e non
   deve pesare quanto lui. */
.stat-card-of {
  font: var(--type-body);
  color: var(--color-text-muted);
}

/* --- ① Chi siamo -----------------------------------------------------------
   Tre fasce, non una pista. Il requisito d'origine ammette che la
   disposizione sul campo «può essere puramente rappresentativa»: è la
   confessione che verrà letta come una formazione, e a U12 non c'è nessuna
   formazione da leggere. */

.squad-band {
  margin-bottom: var(--space-4);
}

.squad-band-title {
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin-bottom: var(--space-2);
}

.squad-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
  gap: var(--space-3);
}

.squad-card {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--space-3);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  color: inherit;
  text-decoration: none;
}

.squad-card:hover,
.squad-card:focus-visible {
  border-color: var(--color-primary);
}

.squad-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}

/* Iniziali e non una fotografia (§8 del requisito roster). A un metro dal
   tablet due lettere con il numero accanto si riconoscono meglio di una
   miniatura da 40px, e non aprono il capitolo «foto di minori nel backup». */
.squad-card-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 50%;
  background: var(--lvl-empty);
  font: var(--type-caption);
  font-weight: 600;
  letter-spacing: 0.04em;
}

.squad-card-name {
  font-weight: 600;
}

.squad-card-age {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* Il dato stimato si dichiara. Un'età posata a metà anno che si presenta
   come esatta è peggio di un'età assente. */
.squad-estimate {
  margin-left: 2px;
  color: var(--color-text-muted);
  cursor: help;
}

.squad-card-roles {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin: 4px 0;
}

/* Il livello tecnico medio, con la copertura sotto — sempre, anche a soglia
   superata: è il modo di non far passare «non valutato» per «male». */
.squad-card-level {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  margin-top: auto;
  padding-top: var(--space-2);
  border-top: 1px solid var(--color-border);
}

.squad-level-value {
  font: var(--type-figure-sm);
  font-variant-numeric: tabular-nums;
}

.squad-level-none {
  color: var(--color-text-muted);
}

.squad-level-coverage {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.squad-card-foot {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* --- ② Le aree ------------------------------------------------------------
   Media e distribuzione sulla stessa riga. Mai la media da sola: con quattro
   livelli ordinali un 2.6 non dice se il gruppo è omogeneo o spaccato in due,
   e la barra sì. */

.squad-areas {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.squad-area {
  display: grid;
  grid-template-columns: minmax(11rem, 16rem) 3rem 1fr;
  grid-template-areas:
    "name mean bar"
    "name mean count";
  align-items: center;
  column-gap: var(--space-3);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--color-border);
}

/* Atletica e Atteggiamento stanno fuori dal livello tecnico medio, non dalla
   pagina: separate visivamente, non nascoste. */
.squad-area-aside {
  opacity: 0.85;
}

.squad-area-name { grid-area: name; }
.squad-area-mean { grid-area: mean; text-align: right; }
.squad-area-bar  { grid-area: bar; }
.squad-area-count {
  grid-area: count;
  font: var(--type-caption);
  color: var(--color-text-muted);
  margin-top: 4px;
}

.squad-area-flag {
  margin-left: var(--space-2);
  font: var(--type-caption);
  color: var(--color-text-muted);
  white-space: nowrap;
}

.squad-mean-value {
  font: var(--type-figure-sm);
  font-variant-numeric: tabular-nums;
}

.squad-mean-none {
  color: var(--color-text-muted);
}

.squad-area-empty {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* La stessa barra impilata dei piccoli multipli, coricata: stesso oggetto e
   stessa rampa, si impara una volta e vale per due grafici. Da sinistra a
   destra il tono si scurisce, così la barra si legge come una progressione. */
.stack-bar-h {
  flex-direction: row;
  height: 0.75rem;
}

.squad-table-toggle {
  margin-top: var(--space-4);
}

.squad-table-toggle summary {
  font: var(--type-caption);
  color: var(--color-text-muted);
  cursor: pointer;
}

/* --- Lo scatter ------------------------------------------------------------ */

.squad-scatter svg {
  width: 100%;
  height: auto;
}

.squad-grid {
  stroke: var(--color-border);
  stroke-width: 1;
}

.squad-axis,
.squad-axis-title {
  fill: var(--color-text-muted);
  font-size: 12px;
}

.squad-axis-title {
  font-style: italic;
}

/* Attaccanti pieni, difensori vuoti. Forma e non colore: i portieri sono già
   fuori da questo grafico, restano due ruoli, e due ruoli sono una
   distinzione binaria che la forma risolve senza inventare una palette. */
.squad-point {
  fill: var(--color-primary);
  stroke: var(--color-primary);
  stroke-width: 2;
}

.squad-point-hollow {
  fill: var(--color-surface);
}

.squad-point-label {
  fill: var(--color-text);
  font-size: 11px;
  /* Non deve rubare il passaggio del puntatore al punto del vicino: il
     nome è dentro il link, ma il bersaglio è il cerchio. */
  pointer-events: none;
}

/* Il bersaglio del puntatore, invisibile. `fill: none` non riceve eventi da
   solo: è `pointer-events: all` a renderlo cliccabile senza dipingere
   niente — così non compare né a schermo né in stampa. */
.squad-point-hit {
  fill: none;
  pointer-events: all;
}

/* Passandoci sopra, il punto si ingrossa: dice quale dei dodici il
   suggerimento sta descrivendo. */
.squad-dot:hover .squad-point,
.squad-dot:focus-visible .squad-point {
  stroke-width: 5;
}

.squad-dot:focus {
  outline: none;
}

/* La tabella è l'equivalente del grafico, non un di più: sotto i 700px è lei
   a restare, perché dodici nomi su 360px non sono un grafico. */
.squad-scatter-table {
  display: none;
}

@media (max-width: 700px) {
  .squad-scatter {
    display: none;
  }

  .squad-scatter-table {
    display: block;
  }

  .squad-area {
    grid-template-columns: 1fr 3rem;
    grid-template-areas:
      "name mean"
      "bar  bar"
      "count count";
  }
}

.squad-not-enough {
  margin-top: var(--space-3);
}

/* --- Il riquadro portieri --------------------------------------------------
   Cinque aree per uno o due nomi: dieci celle, che stanno tutte qui. È il
   motivo per cui le aree da portiere non hanno una pagina di drill-down. */

.squad-keeper + .squad-keeper {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
}

.squad-keeper-head {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}

.squad-keeper-level {
  margin-left: auto;
  font: var(--type-figure-sm);
  font-variant-numeric: tabular-nums;
}

.squad-keeper-areas {
  list-style: none;
  margin: 0;
  padding: 0;
}

.squad-keeper-areas li {
  display: grid;
  grid-template-columns: minmax(10rem, 18rem) 1fr auto;
  gap: var(--space-2);
  align-items: center;
  padding: 4px 0;
}

.squad-keeper-date {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.ramp-swatch-sm {
  width: 0.7rem;
  height: 0.7rem;
}

/* --- Stampa ----------------------------------------------------------------
   La pagina si porta al responsabile tecnico. In bianco e nero la rampa
   diventa una scala di grigi e regge; il pieno/vuoto dello scatter regge da
   sé, ed è l'altra ragione per cui i ruoli non sono un colore. */
@media print {
  .squad-scatter {
    display: block;
    break-inside: avoid;
  }

  .squad-scatter-table,
  .squad-table-toggle {
    display: none;
  }

  .squad-card,
  .squad-keeper,
  .squad-area {
    break-inside: avoid;
  }
}

/* --- Il radar di squadra (v1.35) ------------------------------------------
   Un solo tono in due intensità: pieno per oggi, tratteggiato per inizio
   stagione — la stessa grammatica del dumbbell. Nessuna tinta nuova, e il
   tratteggio sopravvive alla stampa in bianco e nero, dove due colori
   diversi diventerebbero due grigi uguali. */

.squad-radar svg {
  width: 100%;
  max-width: 30rem;
  height: auto;
  display: block;
  margin: 0 auto;
}

.squad-radar-ring {
  fill: none;
  stroke: var(--color-border);
  stroke-width: 1;
}

.squad-radar-axis {
  stroke: var(--color-border);
  stroke-width: 1;
}

.squad-radar-today {
  fill: var(--color-primary);
  fill-opacity: 0.18;
  stroke: var(--color-primary);
  stroke-width: 2;
  stroke-linejoin: round;
}

.squad-radar-before {
  fill: none;
  stroke: var(--color-text-muted);
  stroke-width: 1.5;
  stroke-dasharray: 5 4;
  stroke-linejoin: round;
}

.squad-radar-table {
  display: none;
  margin-top: var(--space-3);
}

@media (max-width: 700px) {
  .squad-radar { display: none; }
  .squad-radar-table { display: block; }
}

@media print {
  .squad-radar { display: block; break-inside: avoid; }
  .squad-radar-table { display: none; }
}

/* --- La matrice ruolo × competenza ---------------------------------------- */

.squad-matrix td {
  font-variant-numeric: tabular-nums;
}

.squad-matrix-count {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* --- ③ Cosa notare --------------------------------------------------------
   Tre stati semantici, con i colori che l'app già usa per gli avvisi: non è
   una palette nuova, è la stessa dei banner. Ogni riquadro porta sotto la
   frazione da cui esce — un'affermazione su dei bambini deve poter essere
   verificata guardando la stessa pagina. */

.squad-insights {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.squad-insight {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-left-width: 4px;
  border-radius: var(--radius-md);
  background: var(--color-surface);
  color: inherit;
  text-decoration: none;
}

a.squad-insight:hover,
a.squad-insight:focus-visible {
  border-color: var(--color-primary);
}

.squad-insight-strength { border-left-color: var(--color-ok-border); }
.squad-insight-develop  { border-left-color: var(--color-warning-border); }
.squad-insight-imbalance { border-left-color: var(--color-text-muted); }

.squad-insight-title {
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.squad-insight-strength .squad-insight-title { color: var(--color-ok-text); }
.squad-insight-develop .squad-insight-title { color: var(--color-warning-text); }

.squad-insight-text {
  font: var(--type-body-strong);
}

.squad-insight-evidence {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* --- Il drill-down di un'area --------------------------------------------- */

.squad-area-detail {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-4);
  align-items: center;
}

@media (max-width: 700px) {
  .squad-area-detail { grid-template-columns: 1fr; }
}

.squad-area-detail-mean {
  display: flex;
  flex-direction: column;
}

.squad-mean-lg {
  font: var(--type-figure);
}

.squad-distribution {
  list-style: none;
  margin: var(--space-2) 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  font: var(--type-caption);
}

.squad-area-players th[scope="row"] {
  font-weight: 500;
}

/* ==========================================================================
   (v1.36) «Come siamo messi» — da colonna a cruscotto
   ==========================================================================

   Il problema misurato sulla pagina viva il 2026-09-09, squadra Mini, 14
   giocatori: 6081px di altezza contro una finestra di 857px, cioè 7,1
   schermate. Tutte e sei le `section.card` larghe 1152px, nessuna accanto a
   un'altra. E soprattutto: «cosa facciamo» — l'unica superficie con un
   bottone che porta a pianificare una seduta — cominciava a y 5795, al 95%
   della pagina.

   Tre cose la fanno stare in una schermata, in quest'ordine di resa:

   1. **La scia dentro la riga dell'area.** «Come si è mosso il gruppo» era
      nove riquadri da 1143px l'uno — la stessa intelaiatura ridisegnata
      nove volte attorno a nove distribuzioni. Ora è una colonna di
      colonnine alte 18px dentro la riga della sua area. Il dato è lo
      stesso, e resta distinto per rilevazione.
   2. **La griglia.** Riquadri affiancati invece che impilati.
   3. **La prosa.** Le 269 parole di `.section-hint` sparse in nove blocchi
      diventano sottotitoli di riquadro da una riga, `title=` e `<details>`.

   **La scala tipografica non si abbassa.** La soglia di 14px in cassa bassa
   (12px solo in maiuscoletto) è quella decisa il 2026-09-06 e documentata
   sopra `--t-1`: qui non si tocca. La densità viene dai titoli, che passano
   da `--type-title` (18px, tondo, cassa bassa) a `--type-eyebrow` (12px,
   condensato, **maiuscoletto**) — legale, ed è la convenzione dei
   cruscotti — e dai margini. */

/* Il cruscotto vuole tutta la finestra: `--content-max-width-wide` (75rem)
   lascerebbe 700px di fondo vuoto su uno schermo da 1800. */
.app-main-dash {
  max-width: none;
  padding: var(--space-3);
}

.squad-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.squad-head h1 {
  margin: 0;
}

.squad-head .page-subtitle {
  margin: 2px 0 0 0;
}

.squad-head .page-back-link {
  margin: 0;
  flex: none;
}

.squad-kpis {
  margin-bottom: var(--space-3);
}

/* ---- La griglia --------------------------------------------------------
   I riquadri sono **figli diretti** e si collocano per nome: l'ordine del
   DOM resta la narrazione di §1 (chi siamo → come siamo messi → cosa notare
   → cosa facciamo), che è ciò che leggono la stampa, uno screen reader e
   chiunque apra la pagina senza CSS.

   Righe `auto` e non frazionarie: le tre colonne non hanno lo stesso numero
   di riquadri, e imporre altezze uguali è ciò che tagliava la matrice al
   primo tentativo. Sotto 1560px la griglia si riduce a una colonna e la
   pagina torna a scorrere — un riquadro troncato senza barra di scorrimento
   è un dato che sparisce senza dirlo, ed è peggio dello scroll. */
.squad-dash {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: 1fr;
}

/* 1560px non è un numero tondo scelto a occhio: è la larghezza sotto la
   quale le tre colonne smettono di reggere, misurata. A 1500 il testo degli
   assi scende a 12,6px resi (radar) e 11,7 (scatter) — sotto la soglia dei
   14 — e a 1440 cominciano a troncarsi i nomi dei giocatori e delle aree.
   Sopra 1560 nessuna delle due cose succede. Chi vuole abbassarla rifaccia
   quelle due misure, non la sposti e basta. */
@media (min-width: 1560px) {
  .squad-dash {
    /* (issue #106) Con le velocità nelle righe di «Chi siamo» queste
       proporzioni cambiano: `.squad-dash-speeds`, in coda al foglio. */
    grid-template-columns: minmax(0, 1.5fr) minmax(0, 1.15fr) minmax(0, 1.05fr);
    grid-template-areas:
      "aree   radar    chi"
      "aree   scatter  chi"
      "keeper scatter  matrix";
  }

  .dash-roster { grid-area: chi; }
  .dash-areas { grid-area: aree; }
  .dash-radar { grid-area: radar; }
  .dash-scatter { grid-area: scatter; }
  .dash-keepers { grid-area: keeper; }
  .dash-matrix { grid-area: matrix; }
}

/* ---- Il riquadro ------------------------------------------------------- */
.dash-panel {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  padding: var(--space-2) var(--space-3) var(--space-3);
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.dash-title {
  margin: 0 0 var(--space-2);
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  flex-wrap: wrap;
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.dash-count {
  color: var(--color-primary-dark);
  background: var(--color-primary-soft);
  border-radius: var(--radius-pill);
  padding: 1px var(--space-2);
  letter-spacing: 0.04em;
}

/* Il sottotitolo del riquadro è ciò che resta delle 269 parole: una riga,
   in cassa bassa, che dice come si legge quel riquadro. Non è un occhiello,
   quindi torna a 14px. */
.dash-note {
  margin-left: auto;
  font: var(--type-caption);
  letter-spacing: 0;
  text-transform: none;
  color: var(--color-text-muted);
}

.dash-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.dash-aside {
  margin: var(--space-2) 0 0 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* ---- ① Chi siamo, a righe ---------------------------------------------
   Quattordici card costavano 1040px — 1,2 schermate — prima che cominciasse
   una parola su come sta messa la squadra. Nessun campo è sparito: numero,
   nome, stati, età, livello e copertura ci sono tutti, su una riga sola. */
.squad-band-label {
  margin: var(--space-2) 0 2px;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.squad-band-label:first-child {
  margin-top: 0;
}

.squad-band-label::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--color-border);
}

.squad-row {
  /* (issue #91) Il riquadro del link del nome si posa sulla riga. */
  position: relative;
  display: grid;
  /* Cinque celle: numero, nome, età, livello, copertura. La copertura è la
     quinta e non un di più — §4.1 la vuole stampata sempre accanto al
     livello. */
  grid-template-columns: 1.75rem minmax(0, 1fr) auto 2.25rem auto;
  align-items: center;
  gap: var(--space-2);
  padding: 1px var(--space-1);
  border-radius: var(--radius-sm);
}

.squad-row:hover,
.squad-row:focus-within {
  background: var(--color-bg);
}

/* (issue #91) La riga non è un `<a>` — dentro c'è il link alla scheda
   persona — ma si clicca tutta come prima: il link del nome la copre con
   `::after`. `.squad-row-name` taglia il testo con `overflow: hidden`, e
   il riquadro assoluto non ne resta tagliato perché si posa sulla riga, che
   è fuori da lui.

   Le celle che portano un `title` suo gli stanno **sopra**, o il
   suggerimento sarebbe quello del nome: la copertura (§4.1, «il pezzo più
   vecchio del suo quadro»), l'età stimata, il livello che manca. Su di loro
   il clic non porta alla scheda, ed è il prezzo: sono bersagli piccoli. */
.squad-row-coverage,
.squad-estimate,
.squad-level-none {
  position: relative;
  z-index: 1;
}

.squad-row-link {
  color: inherit;
  text-decoration: none;
}

.squad-row-link::after {
  content: "";
  position: absolute;
  inset: 0;
}

/* «valutato in Mini»: sotto il nome, in piccolo, sopra il link della riga.
   Una riga sua e non accanto al nome, dove l'ellissi del nome lungo la
   taglierebbe per prima. */
.squad-origin {
  position: relative;
  z-index: 1;
  display: block;
  width: fit-content;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.squad-row-number {
  font: var(--type-figure-sm);
  font-size: var(--t-2);
  font-variant-numeric: tabular-nums;
  text-align: center;
  color: var(--color-text-muted);
  background: var(--lvl-empty);
  border-radius: var(--radius-sm);
}

.squad-row-number.is-none {
  background: none;
  color: var(--lvl-na-key);
}

.squad-row-name {
  font: var(--type-caption);
  color: var(--color-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.squad-row-age {
  font: var(--type-caption);
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* (v1.97) «anno da chiedere» al posto dell'età. Non è il `≈` dell'età
   stimata qui accanto: quello dice che un numero c'è ed è posato, questo
   che il numero non c'è. */
.squad-row-age-none {
  font-variant-numeric: normal;
}

.squad-row-level {
  font: var(--type-figure-sm);
  font-variant-numeric: tabular-nums;
  text-align: right;
}

/* ---- ② Le aree: media, distribuzione e scia sulla stessa riga ---------- */
.area-head,
.area-row {
  display: grid;
  align-items: center;
  gap: var(--space-2);
  /* (issue #106) Da 1560px, con le velocità nelle righe di «Chi siamo», il
     nome dell'area ha un minimo di 12rem: `.squad-dash-speeds`, in coda al
     foglio. */
  grid-template-columns:
    minmax(0, 1.4fr) 2rem minmax(3.5rem, 1fr) auto 2.6rem;
}

.area-head {
  padding-bottom: 3px;
  margin-bottom: 3px;
  border-bottom: 1px solid var(--color-border);
  font: var(--type-thead);
  letter-spacing: var(--type-thead-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.area-head .ta-r { text-align: right; }

/* (issue #106) L'intestazione della distribuzione copre anche la colonna
   della scia, che una parola sua non l'ha: dove la barra si stringe —
   con le velocità, a 1560px è poco meno di 100px — «Com'è distribuita
   oggi» non va a capo. */
.area-head .ta-c {
  grid-column: 3 / 5;
  text-align: center;
}

.area-rows {
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  flex: 1;
}

.area-name {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font: var(--type-caption);
}

.area-name a {
  color: inherit;
}

.area-mean {
  font: var(--type-figure-sm);
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.area-mean-none {
  color: var(--color-text-muted);
}

.area-never {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.area-count {
  font: var(--type-caption);
  font-variant-numeric: tabular-nums;
  color: var(--color-text-muted);
  text-align: right;
}

/* Il separatore che stacca le sette aree tecniche dalle due che restano
   nella pagina ma non entrano nel livello tecnico medio. Uno solo, al posto
   di una pastiglia «fuori media» ripetuta su ogni riga. */
.area-split {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: var(--space-2) 0 2px;
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.area-split::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--color-border);
}

/* La scia. Una colonnina per rilevazione, larghezza fissa: la colonna della
   griglia è `auto`, quindi una stagione con otto rilevazioni allarga la
   colonna invece di comprimere le colonnine sotto la leggibilità — e
   nessuna rilevazione sparisce per far posto. */
.area-trail {
  display: flex;
  align-items: center;
  gap: 2px;
}

.trail-col {
  display: flex;
  flex-direction: column;
  width: 9px;
  height: 18px;
  border-radius: 2px;
  overflow: hidden;
  background: var(--lvl-empty);
}

/* `.stack-segment` porta un `min-height` di 1.25rem, che in una colonnina
   alta 18px farebbe debordare il primo segmento sopra tutti gli altri. Lì
   quel minimo esiste perché un giocatore su quattordici resti leggibile in
   una barra alta 88px; qui la colonnina non è un valore da leggere, è una
   forma da confrontare con quella accanto, e il numero esatto vive nel
   `title` e nella tabella. */
.trail-col .stack-segment {
  min-height: 0;
  border: 0;
}

.trail-trend {
  font: var(--type-eyebrow);
  line-height: 1;
  color: var(--color-text-muted);
}

.dash-legend {
  margin: var(--space-2) 0 0 0;
  padding-top: var(--space-2);
  border-top: 1px solid var(--color-border);
}

.dash-legend-note {
  margin-left: auto;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* ---- I due grafici ----------------------------------------------------- */
.dash-radar .squad-radar,
.dash-scatter .squad-scatter {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 0;
}

/* (issue #127) Sotto 700px è la tabella a restare: la regola qui sopra
   batteva la `display: none` di `.squad-scatter` a specificità più alta, e
   sul telefono si vedevano un grafico da 360px **e** la tabella. Il grafico
   si guarda a schermo intero, dove la regola del livello lo riaccende. */
@media screen and (max-width: 700px) {
  .dash-scatter .squad-scatter {
    display: none;
  }
}

/* Lo scatter non aveva nessun tetto di larghezza: riempiva la card fino a
   1102px e cresceva in altezza in proporzione, fino a 520px, di cui il
   disegno vero occupava 584×304. */
.dash-scatter .squad-scatter svg {
  max-width: 38rem;
  margin: 0 auto;
}

.chart-key {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-2) 0 0 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
  flex-wrap: wrap;
}

.chart-key-item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}

.chart-key-end {
  margin-left: auto;
}

.key-now { stroke: var(--color-primary); stroke-width: 2.4; }
.key-before { stroke: var(--color-text-muted); stroke-width: 1.6; stroke-dasharray: 4 3; }
.key-solid { fill: var(--color-primary); }
.key-hollow { fill: var(--color-surface); stroke: var(--color-primary); stroke-width: 1.8; }

.chart-why summary {
  cursor: pointer;
  color: var(--color-text-muted);
}

.chart-why[open] {
  display: block;
  flex: 1;
}

/* ---- Portieri ---------------------------------------------------------- */
/* 22rem e non 13: con nome dell'area, livello **e** data su una riga (§5.4)
   servono ~21rem perché niente vada in ellissi, e un nome d'area tagliato a
   metà non è un'abbreviazione. Su una colonna da 630px i due portieri
   stanno quindi uno sotto l'altro; dove ci sta, `auto-fit` li affianca da
   sé. `min(…, 100%)` perché su un telefono da 360px la colonna non ha 22rem:
   lì cede l'ellissi del nome d'area, non la pagina (audit mobile del
   15.09.2026: la scheda usciva di 25px e la pagina scorreva di lato). */
.keeper-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr));
  gap: var(--space-2) var(--space-3);
}

.keeper-head {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  margin-bottom: 2px;
}

.keeper-name {
  font: var(--type-label);
  color: inherit;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.keeper-level {
  margin-left: auto;
  font: var(--type-figure-sm);
  font-variant-numeric: tabular-nums;
}

.keeper-areas {
  list-style: none;
  margin: 0;
  padding: 0;
}

.keeper-areas li {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font: var(--type-caption);
}

/* L'area prende lo spazio che resta: livello e data stanno a destra su ogni
   riga, anche accanto a un nome corto come «Parate», dove prima il livello
   gli si incollava a metà riga. */
.keeper-area {
  flex: 1 1 auto;
  min-width: 0;
  color: var(--color-text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Il livello come pastiglia della rampa e non come parola: cinque righe per
   portiere, e la parola ripetuta cinque volte è più lunga dell'area che
   descrive. La parola resta nel `title`, e la tabella dei valori esatti è
   il drill-down del giocatore. */
.keeper-mark {
  margin-left: auto;
  flex: none;
  width: 1.9rem;
  height: 0.7rem;
  border-radius: 3px;
}

.keeper-mark.is-none {
  background: var(--lvl-na);
  border: 1px solid var(--lvl-na-line);
}

/* ---- La matrice: pastiglia neutra --------------------------------------
   **Decisione aperta, non chiusa qui.** ARCHITECTURE.md §10bis.2 e §8.2 del
   requisito dicono entrambi che «le celle della matrice e le barre impilate
   portano la rampa ordinale». Queste celle non la portano — e non la
   portavano nemmeno prima del cruscotto: la v1.35 stampava il numero nudo.
   La pastiglia aggiunge la forma da cruscotto, non un colore.

   La ragione per cui non gliel'ho messa: la rampa è **ordinale** e descrive
   un livello osservato, mentre queste celle portano una **media**.
   Dipingere 3.4 come *Applicare* affermerebbe che il reparto «è» a quel
   livello, e chiederebbe di inventare qui una regola di arrotondamento che
   non sta né in `core` né nel requisito — cioè esattamente il salto che il
   dominio si rifiuta di fare altrove.

   Ma è un argomento contro due documenti, e non tocca al CSS decidere. Chi
   passa di qui: o si colora la cella (e allora la regola di arrotondamento
   va scritta in `core`, non qui), o si corregge quella frase nei due
   documenti. Non si lasci il codice a contraddirli in silenzio. */
.cell-pill {
  display: inline-block;
  min-width: 2.2rem;
  padding: 1px var(--space-1);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  font: var(--type-figure-cell);
  font-variant-numeric: tabular-nums;
}

.dash-matrix .squad-matrix td {
  text-align: center;
}

/* ---- ③ Cosa notare · ④ Cosa facciamo -----------------------------------
   Insieme, in fondo e a tutta larghezza. Prima «cosa facciamo» cominciava a
   y 5795: nel cruscotto è sempre visibile, e in basso a destra, che nella
   lettura a Z di una griglia è il posto di una conclusione. */
.squad-actions {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: var(--space-3);
  margin-top: var(--space-3);
}

.squad-actions .summary-card {
  padding: var(--space-2) var(--space-3);
  gap: 2px;
  text-decoration: none;
  color: inherit;
}

.squad-actions a.summary-card:hover,
.squad-actions a.summary-card:focus-visible {
  border-color: var(--fam, var(--color-primary));
}

.squad-actions .summary-note {
  margin: 0;
  font-variant-numeric: tabular-nums;
}

.squad-actions .summary-cta {
  margin-top: auto;
  padding-top: var(--space-1);
  display: flex;
  align-items: center;
  gap: var(--space-1);
  font: var(--type-button-sm);
  color: var(--color-primary-dark);
}

.squad-actions .squad-insight {
  margin: 0;
}

/* ---- Stampa (§8.3) -----------------------------------------------------
   La pagina si porta al responsabile tecnico, e un A4 non è un cruscotto a
   tre colonne: la griglia si srotola in una colonna sola e ogni riquadro
   evita di spezzarsi a metà. La tabella dei valori delle aree si **apre**,
   perché sul foglio le barre perdono il colore della rampa. */
@media print {
  .app-main-dash {
    padding: 0;
  }

  .squad-dash,
  .squad-actions {
    display: block;
  }

  .dash-panel,
  .squad-actions > * {
    break-inside: avoid;
    box-shadow: none;
    margin-bottom: var(--space-3);
  }

  /* Le tabelle dei valori esatti: la copia `print-only` (la stessa macro
     del `details`, resa una seconda volta). Il `details` resta `no-print`,
     perché chiuso non si apre da CSS. */
  .print-only {
    display: block;
  }

  .dash-scatter .squad-scatter svg {
    max-width: 100%;
  }
}

/* ---- La densità che serve alla schermata unica ------------------------
   Misurato a 1800×950: la pagina era 1319px, cioè 1,39 schermate. I 369px
   di troppo stavano in tre posti, e nessuno dei tre è il corpo del testo.

   1. **264px di cornice** prima che cominci la griglia: titolo di schermata
      a 34px più sottotitolo su due righe (62px), sotto-navigazione (32px),
      quattro riquadri alti 113px. Il titolo dice «Come siamo messi» a un
      centimetro dalla voce di navigazione che dice «Come siamo messi», e la
      barra sopra porta già squadra e stagione: resta, perché una pagina
      senza `h1` non si naviga con uno screen reader, ma su una riga sola.
   2. **Il radar a 444px**: l'SVG si stirava fino alla larghezza della
      colonna. Non ne guadagnava niente — è un ettagono, non una mappa.
   3. **Le righe di tabella a 31px**, che sono giuste in una pagina a
      colonna e sprecate in un riquadro da cruscotto. */
.squad-head {
  align-items: baseline;
  margin-bottom: var(--space-2);
}

.app-main-dash h1 {
  font: var(--type-section);
}

.squad-head > div {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  flex-wrap: wrap;
  min-width: 0;
}

.squad-head .page-subtitle {
  margin: 0;
}

.squad-kpis .stat-card {
  padding: var(--space-2) var(--space-3);
}

.squad-kpis .stat-card-sub {
  margin-top: 0;
}

/* Il radar non si stira: 20rem è la larghezza a cui i sette nomi d'asse
   stanno su una riga ciascuno senza toccarsi, e oltre quella non aggiunge
   informazione, aggiunge solo altezza — che è il bene scarso qui. */
.dash-radar .squad-radar svg {
  max-width: 20rem;
}

.dash-panel .stack-table th,
.dash-panel .stack-table td {
  padding: 2px var(--space-2);
}

.dash-panel .squad-table-toggle {
  margin-top: var(--space-2);
}

.squad-band-label {
  margin-top: var(--space-1);
}

.squad-actions .summary-card {
  gap: 1px;
}

.squad-actions .summary-area {
  font-size: var(--t-2);
}

/* Secondo giro sulla densità, sempre misurando. Il grafico più grande non è
   il più informativo: il radar è un ettagono con sette etichette e lo
   scatter dodici punti su una scala fissa 1–4 — oltre una certa dimensione
   aggiungono solo altezza, che qui è il bene scarso. Il tetto è la
   larghezza a cui le etichette stanno ancora comode. */
.dash-radar .squad-radar svg {
  max-width: 17rem;
}

.dash-scatter .squad-scatter svg {
  max-width: 30rem;
}

.squad-dash {
  gap: var(--space-2);
}

.squad-actions {
  gap: var(--space-2);
  margin-top: var(--space-2);
}

.squad-actions .squad-insight {
  padding: var(--space-2) var(--space-3);
  gap: 1px;
}

.squad-actions .squad-insight-text,
.squad-actions .squad-insight-evidence {
  margin: 0;
}

.squad-row {
  padding: 0 var(--space-1);
}

.squad-kpis .stat-card-value {
  line-height: 1.05;
}

.squad-kpis {
  margin-bottom: var(--space-2);
  gap: var(--space-2);
}

/* La chiave del grafico nella riga del titolo: `.dash-note` la porta a
   destra, in cassa bassa, e le due righe sotto i disegni spariscono. */
.dash-title .chart-key {
  margin: 0;
  gap: var(--space-2);
}

/* Il radar torna grande, e non costa altezza: la riga in cui sta è già
   fissata dall'elenco dei giocatori accanto, che è più alto. Un ettagono
   compresso a 240px con sette etichette a 7px non era un grafico, era una
   macchia. */
.dash-radar .squad-radar svg {
  max-width: 24rem;
}

/* Il testo dentro un `viewBox` si scala col disegno, e la soglia dei 14px
   riguarda ciò che l'occhio vede, non ciò che è scritto nel sorgente: 12
   unità utente in un `viewBox` largo 480 reso a 304px diventano 7,6px.
   Il corpo si dichiara quindi in unità utente e si sceglie **misurando il
   reso**: 23 × (304/480) = 14,6px. Il primo tentativo era 18, che dava
   11,4px — sotto soglia, e il controllo in console descritto sopra `--t-1`
   non l'avrebbe intercettato, perché `getComputedStyle` legge le unità
   utente e non sa niente della scalatura del `viewBox`.

   **Chi tocca questi due valori rimisuri il reso**, così:
   `const t=document.querySelector('.dash-radar .squad-axis'), s=t.ownerSVGElement;`
   `parseFloat(getComputedStyle(t).fontSize) * s.getBoundingClientRect().width / s.viewBox.baseVal.width` */
.dash-radar .squad-axis {
  font-size: 18px;
}

/* Stessa aritmetica del radar, stesso obbligo di misurare il reso: il
   `viewBox` dello scatter è largo 720 e qui viene reso a 416px, quindi
   25 × (416/720) = 14,4px. Le tacche sono l'unico testo rimasto in questo
   grafico — se non si leggono loro non si legge la scala, e un punto senza
   scala è una macchia. */
.dash-scatter .squad-axis,
.dash-scatter .squad-axis-title {
  font-size: 25px;
}

.dash-scatter .squad-scatter svg {
  max-width: 26rem;
}

/* I nomi accanto ai punti spariscono **solo** dove il cruscotto comprime lo
   scatter: la loro collocazione è calcolata in `squad_views` per un
   `viewBox` largo 720, e a 416px resi diventerebbero 6px accavallati — che
   è peggio dell'assenza. Sotto 1560px la griglia si srotola, lo scatter
   torna largo e i nomi tornano; in stampa non spariscono mai.

   Tutti insieme no, **uno alla volta sì**: passando sul punto (o arrivandoci
   col tab) il suo nome torna, e torna leggibile — 25 unità utente, la stessa
   aritmetica delle tacche qui sopra, 25 × (416/720) = 14,4px. Uno alla volta
   l'accavallamento non esiste, ed è per questo che qui il corpo può essere
   più che doppio di quello calcolato in `squad_views`. Il filetto del colore
   di fondo sotto le lettere le stacca dalla griglia e dagli altri punti,
   perché un `text` disegnato prima non può passare sopra a un cerchio
   disegnato dopo. Restano `opacity` e non `display`: una parola nascosta con
   `display: none` non c'è per nessuno, nemmeno per chi legge con lo
   schermo. */
@media screen and (min-width: 1560px) {
  .dash-scatter .squad-point-label {
    opacity: 0;
  }

  .dash-scatter .squad-dot:hover .squad-point-label,
  .dash-scatter .squad-dot:focus-visible .squad-point-label {
    opacity: 1;
    font-size: 25px;
    /* La linea di base era calcolata per 11 unità (cy + 4): a 25 il centro
       del testo risale, e questi 6 lo riportano all'altezza del punto. */
    transform: translateY(6px);
    paint-order: stroke fill;
    stroke: var(--color-surface);
    stroke-width: 5px;
    stroke-linejoin: round;
  }

  /* Chi sta in fondo alla scala dell'età sfonderebbe il bordo destro: il
     nome passa a sinistra del punto. I 18 sono i due `LABEL_GAP` che
     separano la posizione a destra da quella a sinistra. */
  .dash-scatter .squad-dot:hover .squad-point-label-flip,
  .dash-scatter .squad-dot:focus-visible .squad-point-label-flip {
    text-anchor: end;
    transform: translate(-18px, 6px);
  }
}

.app-main-dash {
  padding: var(--space-2) var(--space-3) var(--space-3);
}

/* Il titolo del riquadro non va mai a capo: due righe di occhiello sono
   24px che nel cruscotto valgono tre righe d'area. Se il sottotitolo non ci
   sta, è il sottotitolo a doversi accorciare — il posto delle spiegazioni
   lunghe è il `title`. */
.dash-title {
  flex-wrap: nowrap;
  white-space: nowrap;
}

.dash-title .dash-note {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

.dash-panel .stack-table td,
.dash-panel .stack-table tbody th {
  padding-top: 1px;
  padding-bottom: 1px;
}

.dash-matrix .cell-pill {
  padding-top: 0;
  padding-bottom: 0;
}

/* Ultimo giro. Ogni riga qui sotto è stata scelta misurando, non a occhio:
   la pagina era 1036px contro una finestra di 965. I 71px mancanti stavano
   nell'interlinea dell'elenco dei giocatori (quindici righe, e ogni pixel
   vale quindici), nel margine della sotto-navigazione — 24px sotto una
   barra alta 32 — e nell'altezza del riquadro «cosa notare». */
.app-main-dash .season-nav {
  margin-bottom: var(--space-2);
}

/* Le colonne dei numeri prendono quel che serve alla pastiglia e non un
   rem di più: senza un tetto la tabella le allarga in proporzione e mangia
   la colonna dei nomi, che è l'unica che non si può accorciare — «Lettura
   del gioco e transizione» è il nome dell'area, non un'etichetta. */
.dash-matrix .squad-matrix td {
  width: 3rem;
}

.dash-matrix .squad-matrix tbody th {
  width: auto;
}

.squad-kpis .stat-card-label {
  line-height: 1.1;
}

.squad-actions .squad-insight {
  padding: var(--space-2) var(--space-3);
}

/* ---- Telefono (§8.4) ---------------------------------------------------
   A bordo pista lo schermo è stretto e il nome dell'area è il nome
   dell'area: «Lettura del gioco e transizi…» non è un'abbreviazione, è una
   parola tagliata. Sotto i 700px la riga si apre in due — nome e media
   sopra, distribuzione, scia e conteggio sotto — che è la forma che la
   pagina aveva prima del cruscotto, e lì era giusta. */
@media (max-width: 700px) {
  .area-head {
    display: none;
  }

  .area-row {
    grid-template-columns: minmax(0, 1fr) auto auto;
    grid-template-areas:
      "name  name  mean"
      "bar   trail count";
    row-gap: 2px;
    margin-bottom: var(--space-2);
  }

  .area-name {
    grid-area: name;
    white-space: normal;
  }

  .area-mean { grid-area: mean; }
  .area-bar { grid-area: bar; }
  .area-trail { grid-area: trail; }
  .area-count { grid-area: count; }

  /* Un nome proprio non si abbrevia: sul telefono va a capo. */
  .squad-row {
    grid-template-columns: 1.75rem minmax(0, 1fr) auto 2.25rem auto;
  }

  .squad-row-name {
    white-space: normal;
  }

  .dash-title {
    flex-wrap: wrap;
    white-space: normal;
  }

  .dash-title .dash-note {
    margin-left: 0;
    overflow: visible;
  }
}

/* Settembre: roster pieno, zero rilevazioni (§8.5). Restano due riquadri su
   otto, e la griglia a tre colonne con i suoi nomi di area lascerebbe una
   colonna vuota in mezzo — un buco che si legge come «qui manca qualcosa
   che non ha caricato», invece che come «qui non ho ancora osservato», che
   è quello che il riquadro accanto dice a parole. */
@media (min-width: 1560px) {
  .squad-dash-bare {
    grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
    grid-template-areas: none;
  }

  .squad-dash-bare .dash-panel {
    grid-area: auto;
  }
}

/* Reso solo sul foglio: a schermo lo stesso contenuto sta dentro il
   `details`, che è l'affordance giusta lì. `display: none` lo toglie anche
   dall'albero di accessibilità, quindi non è un doppione per chi legge con
   uno screen reader. */
.print-only {
  display: none;
}

/* §4.1: la copertura si stampa sempre accanto al livello. */
.squad-row-coverage {
  font: var(--type-caption);
  font-variant-numeric: tabular-nums;
  color: var(--color-text-muted);
  text-align: right;
}

/* §5.4: il livello del portiere è la parola, la pastiglia le sta accanto. */
.keeper-level-label {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  flex: none;
  white-space: nowrap;
}

.keeper-none {
  color: var(--color-text-muted);
}

.keeper-date {
  margin-left: auto;
  flex: none;
  font-variant-numeric: tabular-nums;
  color: var(--color-text-muted);
  white-space: nowrap;
}

.keeper-mark {
  margin-left: 0;
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 3px;
}

/* === (v1.38, issue #2) Fasi corte e composizione a mano ==================

   Due cose nuove sulla scheda, e una pagina nuova per sceglierle.

   1. Sotto l'intestazione di fase, una riga che dice di quanto la fase è
      corta e perché. Ha due livelli e non uno: il *resto non riempibile*
      (mancano 3 minuti al riscaldamento perché nessun esercizio a catalogo
      dura meno di 5) è grigio come una nota a piè di pagina, il *buco vero*
      è ambra e porta il triangolo. Se fossero uguali, l'ambra si vedrebbe
      su ogni scheda mai generata e smetterebbe di significare qualcosa.

   2. Sotto ogni esercizio, i tre gesti che mancavano: cambiare, cambiare i
      minuti, togliere. Tutti `no-print` — sulla carta non si preme niente.

   Il colore non è mai l'unico segnale: la frase dice per esteso quanti
   minuti mancano, e il livello che avvisa porta l'icona. */

.phase-gap {
  margin: calc(-1 * var(--space-2)) 0 var(--space-3);
  max-width: 64ch;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* L'icona resta inline con `vertical-align` invece di essere un elemento
   flex, per la stessa ragione già scritta su `.focus-theme-chip-capacity
   svg`: un contenitore flex prende la linea di base dal suo primo elemento,
   e un'icona di linea di base non ne ha — la frase finirebbe fuori asse. */
.phase-gap svg {
  vertical-align: -0.18em;
  margin-right: var(--space-1);
}

/* Il resto non riempibile non ha regola propria e non ha classe
   (`PhaseGapLevel::class` restituisce ""): è lo stato base qui sopra — una
   nota grigia — perché è un'informazione, non un problema. */

.phase-gap-short {
  color: var(--color-warning-text);
}

/* I minuti assegnati accanto a quelli coperti: «25′ di 58». Il secondo
   numero è più piccolo e più chiaro perché è il contesto, non il dato. */
.phase-duration small {
  margin-left: 0.15em;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.phase-actions {
  margin: 0 0 var(--space-4);
}

/* I tre gesti sotto l'esercizio, in riga finché ci stanno. Tre `<form>`
   separati e non uno con tre bottoni: ogni gesto ha il suo indirizzo, e un
   form solo con tre `formaction` diversi funzionerebbe solo con JavaScript
   attivo su un browser recente. */
.exercise-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
}

/* Il `form` globale (riga 783) è una scatola: fondo, bordo, padding, colonna.
   È la forma giusta per un form che occupa la pagina e quella sbagliata per
   tre gesti in fila sotto un esercizio — darebbe tre riquadri dentro il
   riquadro della card. Qui il form torna a essere quello che è, un gruppo di
   controlli in riga. */
.exercise-actions form,
.candidate-add {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--space-1);
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  margin: 0;
}

.exercise-duration-form label {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* Larghezza da tre cifre: i minuti di un esercizio non ne hanno di più, e
   un campo numerico largo mezza riga sembra chiedere un numero grande. */
.exercise-duration-form input {
  width: 4.5rem;
  padding: var(--space-1) var(--space-2);
}

/* «Togli» non ha una conferma dopo, e col dito il rosso del passaggio del
   mouse non arriva: sta in fondo alla riga, lontano da «Aggiorna», anche
   quando la riga va a capo (audit mobile del 15.09.2026). */
.exercise-actions .exercise-remove-form {
  margin-left: auto;
}

@media (pointer: coarse) {
  .exercise-actions .btn-small {
    min-height: 44px;
  }
}

/* --- La pagina per scegliere ---------------------------------------------
   Un elenco da scorrere in piedi con il telefono in mano: ogni riga porta a
   sinistra cosa serve per riconoscere l'esercizio, a destra il gesto per
   metterlo in scheda con i suoi minuti già proposti. */

.candidate-list {
  list-style: none;
  padding: 0;
  margin: 0 0 var(--space-5);
}

.candidate {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-2);
}

.candidate-body {
  flex: 1 1 20rem;
  min-width: 0;
}

.candidate h3 {
  margin: 0 0 var(--space-1);
}

.candidate-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: 0 0 var(--space-1);
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.candidate-duration {
  font-variant-numeric: tabular-nums;
  color: var(--color-text);
}

.candidate-goal {
  margin: 0;
  max-width: 64ch;
}

/* Un esercizio con etichette resta leggibile e resta scegliibile: fondo
   appena diverso e bordo tratteggiato — «questo lo stai scavalcando», non
   «questo è rotto». Sbiadirlo lo renderebbe più difficile da leggere
   proprio quando serve leggerlo con attenzione. */
.candidate-excluded {
  border-style: dashed;
}

.candidate-flags {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  padding: 0;
  margin: var(--space-2) 0 0;
}

.candidate-flags li {
  background: var(--color-warning-bg);
  color: var(--color-warning-text);
  font: var(--type-caption);
  padding: 2px var(--space-2);
  border-radius: 999px;
}

/* Vedi la nota sul `form` globale più sopra: qui la regola è condivisa con
   `.exercise-actions form`, e resta solo ciò che è proprio di questa riga. */
.candidate-add {
  flex: none;
}

.candidate-add label {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.candidate-add input {
  width: 4.5rem;
  padding: var(--space-1) var(--space-2);
}

.candidate-overflow {
  margin-bottom: var(--space-5);
}

.candidate-overflow summary {
  cursor: pointer;
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
}

.candidate-overflow .field-hint {
  display: block;
  margin: var(--space-2) 0 var(--space-3);
  max-width: 64ch;
}

.empty-note {
  color: var(--color-text-muted);
  max-width: 64ch;
}


/* ==========================================================================
   (v1.40, issue #32) Concetti di gioco
   ==========================================================================
   Requisito §10.1/§10.2/§10.7. Tre regole di colore, e sono le stesse che
   valgono già nel resto dell'app:

   1. Il colore **strutturale** è la famiglia del tema, e qui è il filetto
      verticale sul bordo sinistro della card (`--fam`, §10bis.1).
   2. La tinta di **fase** non riempie mai una card, una barra o uno sfondo:
      vive dentro il glifo del semaforo, che ha dimensioni fisse.
   3. I ruoli si distinguono per **forma**, non per colore, e quelli non
      coinvolti sono spenti — resi nel neutro più tenue, non in un'altra
      tinta. */

.concept-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-bottom: var(--space-4);
}

.concept-toolbar-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.concept-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
}

/* `.field-hint` nasce con un margine negativo per stare sotto un campo di
   form; qui sta sotto una fila di pastiglie e va staccata. */
.concept-toolbar-group .field-hint {
  margin: var(--space-1) 0 0;
  max-width: 48ch;
}

/* Un comando, non un'etichetta: bersaglio ampio, perché la pagina si tocca
   anche in piedi. */
.concept-chip {
  display: inline-block;
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
  background: var(--color-surface);
  color: var(--color-text);
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  text-decoration: none;
}

.concept-chip:hover {
  border-color: var(--color-chip-border);
  text-decoration: none;
}

.concept-chip.is-current {
  background: var(--color-chip-bg);
  border-color: var(--color-chip-border);
  color: var(--color-chip-text);
}

/* Il «bersaglio ampio» del commento qui sopra, col dito: il padding da solo
   dava 28px (audit mobile del 15.09.2026). */
@media (pointer: coarse) {
  .concept-chip {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
}

.concept-group {
  margin-bottom: var(--space-4);
}

/* L'intestazione di gruppo porta il glifo della fase accanto al nome: il
   glifo è lo stesso che si vede sulle card e sulle righe del catalogo, e
   ripeterlo qui lega il gruppo a ciò che contiene.

   Maiuscoletto **no**: sono tre parole comuni accanto a un glifo, e non
   hanno bisogno di essere gridate (requisito §10.8). */
.concept-group-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0 0 var(--space-2);
  padding-bottom: var(--space-1);
  border-bottom: 1px solid var(--color-border);
}

.concept-group-head h2 {
  font: var(--type-section);
  margin: 0;
}

.concept-group-count {
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  color: var(--color-text-muted);
  margin-left: auto;
}

/* Il glifo, nell'intestazione, sta in colonna con le tre luci come
   altrove: non si rimpicciolisce e non si stira. */
.concept-group-head .fase-glifo {
  width: 13px;
  height: 28px;
}

/* La griglia a piastrelle (v1.48).

   Fino alla v1.47 era una colonna, ed era l'unica lista dell'app rimasta
   senza la seconda passata: `.app-main` la teneva a 40rem, quindi su un
   monitor da 1920 px si usava il 38% della larghezza e i quindici concetti
   volevano due schermate di scroll. Larghezza sprecata e scroll eccessivo
   erano la stessa causa vista da due lati.

   **Nessuna `@media`, e non per virtuosismo**: quante colonne stanno lo
   decide la larghezza della piastrella, e la larghezza della piastrella la
   decide il disegno che ci sta dentro. Un breakpoint scritto a mano
   direbbe la stessa cosa in un posto dove poi non cambia quando cambia il
   disegno. 30rem è la misura di una piastrella leggibile: pista a 240 px
   più il testo che le sta accanto.

   `min(30rem, 100%)` e non `30rem`: sotto quella misura `auto-fill` terrebbe
   comunque una traccia da 30rem, che sul telefono uscirebbe dallo schermo.

   Il raggruppamento per fase resta sopra la griglia — una griglia per
   gruppo, non una griglia sola con dentro le intestazioni: l'ordine del DOM
   è la narrazione (fase, poi i suoi concetti) e deve restare quello anche
   in stampa e senza CSS. */
.concept-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(30rem, 100%), 1fr));
  gap: var(--space-2);
}

.concept-row {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  /* Il filetto della famiglia del tema. `--fam` arriva dalla classe
     `fam-*` sulla riga stessa. */
  border-left: 3px solid var(--fam, var(--color-border));
  border-radius: var(--radius-md);
  padding: var(--space-3);
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

/* Tutta la riga è il collegamento, tranne il glifo: chi tocca una card
   vuole aprirla, e un bersaglio da una parola sola si sbaglia con i
   guanti. (v1.42) La miniatura sta **dentro** il collegamento, ed è il
   motivo per cui questa è ora una riga con dentro una colonna: si
   riconosce la forma prima del nome, e chi vede il triangolo tocca il
   triangolo. */
.concept-row-link {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex: 1 1 auto;
  min-width: 0;
  color: inherit;
  text-decoration: none;
}

.concept-row-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

/* Il disegno nella card (v1.42, issue #34; v1.48 la pista intera).

   Fino alla v1.47 era la **miniatura** a 56 × 36 — pista ridotta a un
   contorno, sagome ingrandite, una freccia sola — e il commento diceva che
   mostrarla più grande non la migliora. Quel giudizio valeva **nella
   colonna stretta**, dove ogni pixel dato al disegno era un pixel tolto al
   testo. Nella piastrella quel compromesso non esiste più, e allora il
   disegno torna a essere la **pista intera**: le marcature del regolamento
   smettono di essere rumore e dicono *in che parte della pista* succede la
   cosa, che è informazione che la miniatura non poteva dare.

   La misura non è un gusto, è la soglia a cui le sagome si vedono. Le
   sagome della pista intera **non sono ingrandite** (è la miniatura a
   ingrandirle di `THUMB_ELEMENT_SCALE`), quindi il cerchio di un giocatore
   — raggio 0,7 m su una pista da 40,3 — vale `larghezza / 28,8` in pixel di
   diametro. A 160 px sono 5,6 px, cioè il minimo per distinguere un
   cerchio da un triangolo; a 240 px sono 8,3 px e si leggono anche le
   frecce; sotto i 140 px si vedono i cerchi d'ingaggio e non i giocatori,
   ed è esattamente il caso per cui `Detail::Thumb` è stato scritto — che
   resta, e resta in uso sulla scheda di allenamento stampata.

   Tre misure e non una perché la larghezza disponibile è tre:
   telefono in colonna, piastrella a due colonne, piastrella larga.

   `flex: none` perché una riga stretta deve accorciare il testo, mai il
   disegno: una pista schiacciata non si riconosce, e allora tanto vale non
   averla.

   Il riquadro non c'è: il contorno della pista **è** il riquadro. Un bordo
   attorno sarebbe un secondo rettangolo attorno al primo. */
.concept-figure {
  /* Proporzionata alla piastrella e trattenuta ai due capi: 38% di una
     piastrella da 480 px fa 182 px, di una da 600 px fa 228. Il pavimento
     a 150 px è la soglia sotto cui le sagome spariscono, il tetto a 240 px
     è la misura oltre cui il disegno mangia il testo invece di aiutarlo.
     È la stessa idea del `clamp` tipografico: una misura che segue lo
     spazio, non tre misure scritte a mano in tre breakpoint. */
  flex: 0 0 clamp(150px, 38%, 240px);
  display: block;
}

/* Il disegno riempie la larghezza e l'altezza la decide lui: l'SVG ha
   `preserveAspectRatio`, e una pista 2:1 dentro un riquadro dichiarato
   2:1 resta 2:1. Dichiarare anche l'altezza vorrebbe dire ripetere qui il
   rapporto della superficie, che è un dato del dominio (`RinkDimensions`)
   e cambia con una pista non regolamentare. */
.concept-figure .rink-diagram {
  display: block;
  width: 100%;
  height: auto;
  /* `.rink-diagram` nasce con un margine verticale per stare fra due
     paragrafi di una scheda. Qui sta in una riga, e il margine la
     scentrerebbe rispetto al testo accanto. */
  margin: 0;
}

.concept-row-link:hover .concept-name {
  text-decoration: underline;
}

.concept-name {
  font: var(--type-section);
}

.concept-rule {
  font: var(--type-body-strong);
}

.concept-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  color: var(--color-text-muted);
}

/* Il badge del tema porta la tinta della sua **famiglia** (v1.48).

   Il difetto che questo corregge non era il colore di troppo: era che la
   cosa colorata (il filetto) non fosse etichettata e la cosa etichettata
   (il badge) non fosse colorata. Due segni separati per la stessa
   informazione, e chi guarda ricostruisce da sé un significato plausibile e
   sbagliato — «i colori indicano l'area».

   Tinta su **testo e bordo**, fondo neutro: due famiglie su sette sono un
   ambra e un rosa chiaro, e il bianco sopra di esse sta sotto 3:1. Stessa
   scelta e stessa ragione di `.priority-row-mark`.

   Classe propria e non `.badge`: nel catalogo esercizi lo stesso badge non
   porta questo significato. (v1.74) Ora lo porta anche lì: nella riga del
   catalogo il tema è il badge colorato (`.catalog-theme-badge`), e lo stato
   è un badge solo quando è un'eccezione. */
.concept-theme-badge,
.catalog-theme-badge {
  background: var(--color-bg);
  border: 1px solid var(--fam, var(--color-border));
  color: var(--fam, var(--color-text));
}

/* La legenda del colore (v1.48).

   Sempre visibile: sono cinque righe, e una legenda dietro un gesto si
   consulta dopo aver già indovinato. In cima e non in fondo, perché serve
   mentre si guarda la lista.

   Non si stampa: sul foglio la card non porta il filetto, e una legenda di
   colori su una fotocopia in bianco e nero è una promessa che la carta non
   mantiene. */
.concept-legend {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  margin-bottom: var(--space-4);
}

.concept-legend-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
}

.concept-legend-item {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  color: var(--color-text);
}

/* Il segno della legenda ha la **forma del filetto della card** — una
   barretta verticale, non un pallino: chi la cerca sulla card trova quella
   stessa barretta sul bordo sinistro, e non deve tradurre un cerchio in un
   filetto. */
.concept-legend-swatch {
  flex: none;
  width: 3px;
  height: 1.1em;
  border-radius: 2px;
  background: var(--fam, var(--color-border));
}

.concept-legend .field-hint {
  margin: 0;
  max-width: 52ch;
}

@media print {
  .concept-legend {
    display: none;
  }
}

/* --- La scheda ---------------------------------------------------------- */

.concept-title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.concept-detail-rule {
  font: var(--type-body-strong);
  margin: 0 0 var(--space-2);
  max-width: 48ch;
}

.concept-detail-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  color: var(--color-text-muted);
  margin: 0 0 var(--space-4);
}

.concept-block {
  margin-bottom: var(--space-4);
}

.concept-block h2 {
  font: var(--type-section);
  margin: 0 0 var(--space-2);
}

.concept-points,
.concept-questions {
  margin: 0;
  padding-left: var(--space-4);
  max-width: 64ch;
}

.concept-points li,
.concept-questions li {
  margin-bottom: var(--space-1);
}

.concept-mistake,
.concept-criterion {
  margin: 0 0 var(--space-2);
  max-width: 64ch;
}

/* --- I quattro ruoli --------------------------------------------------- */

.concept-roles {
  list-style: none;
  padding: 0;
  margin: 0 0 var(--space-3);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: var(--space-3);
}

.concept-role {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.concept-role-name {
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
}

.concept-role-task {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* Spento: il neutro più tenue, **non** un'altra tinta. Il compito non si
   mostra affatto per un ruolo non coinvolto — direbbe cosa fare a chi
   questo concetto non riguarda. */
.concept-role.is-off .concept-role-name {
  color: var(--color-text-muted);
  opacity: 0.65;
}

/* --- I pittogrammi dei ruoli -------------------------------------------
   Il markup lo genera `web::situational_role_glyph`, che ne verifica le
   regole con dei test; qui c'è solo la resa. Nessuna tinta: tutto
   `currentColor`, così lo stato spento è un colore solo da cambiare e la
   stampa resta a inchiostro. */

.ruolo-glifo {
  flex: none;
  width: 42px;
  height: 34px;
  color: var(--color-text);
}

.ruolo-glifo.is-off {
  color: var(--color-text-muted);
  opacity: 0.55;
}

/* Pieno = ha la pallina; vuoto = non ce l'ha. È la convenzione della
   legenda G+S degli schemi, applicata ai ruoli. */
.ruolo-io-con-pallina,
.ruolo-loro-con-pallina,
.ruolo-pallina {
  fill: currentColor;
}

.ruolo-io {
  fill: none;
  stroke: currentColor;
  stroke-width: 2.4;
}

/* Tratteggiato = passaggio, continuo = pattinare: gli stessi due tratti
   dello schema di pista. */
.ruolo-passaggio {
  fill: none;
  stroke: currentColor;
  stroke-width: 2.2;
  stroke-dasharray: 3.5 3;
}

.ruolo-corsa,
.ruolo-punta {
  fill: none;
  stroke: currentColor;
  stroke-width: 2.2;
}

.ruolo-linea {
  fill: none;
  stroke: currentColor;
  stroke-width: 3;
  stroke-linecap: round;
}

/* Su carta i quattro segni restano, spenti compresi: una fotocopia mangia
   le opacità, e un ruolo che sparisce del tutto si legge come «questo
   concetto ne riguarda tre». */
@media print {
  .ruolo-glifo {
    color: #000;
  }

  .ruolo-glifo.is-off {
    color: #000;
    opacity: 0.4;
  }
}

/* --- Lo schema del concetto (v1.41, issue #33) ---------------------------
   Requisito §7.2 e §10.5. Due meccanismi, e devono **vedersi diversi**:

   - i **fotogrammi** sono lo stesso schema nel tempo, quindi uno per volta,
     con una fila di pastiglie che li scorre e il precedente in traccia
     tenue (il 25% sta in `DIAGRAM_STYLE`, dentro l'SVG: la traccia deve
     restare tenue anche nel file esportato, che non carica questo foglio);
   - le **varianti** sono lo stesso concetto in un'altra zona del campo,
     quindi miniature affiancate, tutte insieme.

   Il primo mockup usava lo stesso segno per entrambi — i pallini di un
   carosello — e non si capiva a quale delle due domande stesse rispondendo.

   Nessun JavaScript: il comando è un gruppo di radio veri, nascosti alla
   vista e non alla tastiera, e la pastiglia è la loro `<label>`. Serve così
   perché questa scheda si stampa e perché il file esportato per i giocatori
   (issue #39) è autonomo e senza rete. */

.concept-frames {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1);
  margin-bottom: var(--space-3);
}

/* Nascosto alla vista, non alla tastiera: restano frecce e `Tab` di un
   gruppo di radio vero. */
.concept-frame-pick {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* `order` mette il disegno sopra e i comandi sotto, pur essendo i comandi
   scritti prima nel markup: devono precedere la loro figura, o le due
   regole `:checked +` qui sotto non avrebbero niente a cui agganciarsi. */
.concept-frame {
  order: 1;
  flex: 1 0 100%;
  display: none;
  margin: 0;
}

.concept-frame-step {
  order: 2;
  display: inline-block;
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
  background: var(--color-surface);
  color: var(--color-text-muted);
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  cursor: pointer;
}

.concept-frame-pick:checked + .concept-frame-step {
  background: var(--color-chip-bg);
  border-color: var(--color-chip-border);
  color: var(--color-chip-text);
}

.concept-frame-pick:checked + .concept-frame-step + .concept-frame {
  display: block;
}

.concept-frame-pick:focus-visible + .concept-frame-step {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* Un concetto di **forma** ha un fotogramma solo: una pastiglia sola non è
   un comando, è un'etichetta che non fa niente. */
.concept-frames.is-single .concept-frame-step {
  display: none;
}

.concept-frame .rink-diagram {
  max-width: 34rem;
  margin: 0;
}

.concept-frame-caption {
  font: var(--type-caption);
  color: var(--color-text-muted);
  max-width: 48ch;
  margin-top: var(--space-1);
}

/* --- La legenda dello schema -------------------------------------------
   Tre segni della legenda G+S riletti: l'attaccante è **chi ha la pallina**
   e il difensore chi non ce l'ha. La forma dice la fase — se sei un
   triangolo, sei in non possesso — e il glifo del semaforo in testa alla
   scheda dice la stessa cosa una seconda volta, che su un foglio
   fotocopiato è ridondanza utile. */

.concept-diagram-legend {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.concept-diagram-legend-item {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  color: var(--color-text-muted);
}

.legend-icon {
  display: block;
  width: auto;
  height: 20px;
}

/* `.field-hint` nasce con un margine negativo per stare sotto un campo di
   form; qui sta sotto una fila di simboli, e senza questo si siederebbe
   sopra la legenda. Stessa correzione già fatta per la barra dei filtri. */
.concept-diagram-legend + .field-hint {
  margin: var(--space-2) 0 0;
  max-width: 60ch;
}

/* --- Le varianti -------------------------------------------------------
   Miniature affiancate e non pallini: rispondono a «dove», non a «quando».
   Un riquadro attorno a ciascuna perché sono più disegni sulla stessa riga
   e senza un bordo si leggerebbero come un disegno solo. */

.concept-variants-title {
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  color: var(--color-text-muted);
  margin: var(--space-4) 0 var(--space-2);
}

.concept-variants {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.concept-variants > li {
  flex: 1 1 12rem;
  max-width: 18rem;
}

.concept-variant {
  margin: 0;
  text-align: center;
}

.concept-variant-box {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: var(--space-1);
  background: var(--color-surface);
}

.concept-variant .rink-diagram {
  margin: 0;
}

.concept-variant figcaption {
  font: var(--type-caption);
  color: var(--color-text-muted);
  margin-top: var(--space-1);
}

/* Su carta non c'è niente da toccare: i fotogrammi si mostrano tutti, uno
   sotto l'altro, ciascuno con la sua didascalia e la sua traccia. È anche
   il modo in cui una sequenza si legge meglio su un foglio — il movimento
   si vede tutto insieme invece che un istante per volta. */
@media print {
  .concept-frame-step {
    display: none;
  }

  .concept-frame {
    display: block;
    margin-bottom: var(--space-3);
  }
}

/* ==========================================================================
   (v1.43, issue #35) Gli esercizi che allenano un concetto, e il vincolo
   ==========================================================================

   Tre cose: la riga che dice che il concetto non è ancora insegnabile, la
   lista degli esercizi collegati con il loro vincolo, e il form da cui si
   aggancia.

   Il colore non cambia di regola: la famiglia del tema resta l'unico colore
   strutturale, la tinta di fase vive solo dentro il glifo — qui c'è un solo
   accento nuovo, l'**ambra del buco vero**, ed è la stessa che la scheda di
   allenamento usa già per la fase corta (`.phase-gap-warn`). Vale la stessa
   disciplina: il colore non è mai l'unico segnale, perché la frase dice per
   esteso quanti esercizi mancano. */

/* Un concetto sotto i due esercizi collegati non è ancora insegnabile.
   Ambra, come il buco vero di una fase corta, e per la stessa ragione — è
   qualcosa da fare, non una nota a piè di pagina. */
.concept-incomplete {
  color: var(--color-warning-text);
}

/* Nell'elenco la segnalazione è **testo dentro la riga dei metadati**, non
   una pastiglia: la card porta un solo badge, ed è il tema (requisito
   §10.2). Un secondo rettangolo colorato accanto al primo direbbe che sono
   due cose dello stesso genere, e non lo sono. */
.concept-meta .concept-incomplete {
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
}

/* Sulla scheda è una riga sopra l'elenco, non un banner: sta dentro la
   sezione a cui si riferisce, e chi la legge ha già sotto gli occhi ciò che
   deve fare. */
.concept-block > .concept-incomplete {
  margin: 0 0 var(--space-3);
  max-width: 64ch;
}

.concept-exercises {
  list-style: none;
  margin: 0 0 var(--space-3);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.concept-exercise {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
}

/* La miniatura ha la stessa misura di quella dei concetti — 56 × 36, la
   misura del requisito §10.3 — perché serve alla stessa cosa: riconoscere
   fra tre righe la scheda che si ha in mente. `flex: none` perché una riga
   stretta deve accorciare il testo, mai il disegno. */
.concept-exercise-thumb {
  flex: none;
  width: 56px;
  height: 36px;
  object-fit: contain;
  border-radius: var(--radius-sm);
}

.concept-exercise-body {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.concept-exercise-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
}

.concept-exercise-title {
  font: var(--type-body-strong);
  color: inherit;
}

/* **Il vincolo è la riga che conta**: è quello che rende quell'esercizio un
   allenamento di *questo* concetto, e chi apre la scheda per preparare la
   seduta legge questa prima del titolo. Sta a corpo pieno mentre i metadati
   scendono a `--type-caption`. */
.concept-exercise-constraint {
  margin: var(--space-1) 0 0;
}

.concept-exercise-effect,
.concept-exercise-meta {
  margin: 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* Il `form` globale è una scatola — fondo, bordo, padding, colonna — ed è
   la forma giusta per un form che occupa la pagina, quella sbagliata per un
   gesto solo dentro una riga: darebbe un riquadro dentro il riquadro. Qui
   il form torna a essere quello che è, un controllo. Stessa correzione già
   fatta su `.exercise-actions form`. */
.concept-exercise-remove {
  flex: none;
  display: flex;
  align-items: center;
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  margin: 0;
}

/* Un esercizio che il catalogo non ha più: la riga resta — il vincolo l'ha
   scritto l'allenatore e sta sul legame — ma si spegne, così non si prova a
   metterlo in una seduta. Il filetto ambra ripete a colore quello che il
   titolo dice a parole. */
.concept-exercise.is-gone {
  border-left: 3px solid var(--color-warning-border);
}

.concept-exercise.is-gone .concept-exercise-title {
  color: var(--color-text-muted);
}

/* (v1.73, issue #63) Uno scartato si spegne come l'esercizio che non c'è,
   e la marca dice perché: la scheda esiste ancora, e l'amministratore la
   può rimettere. Testo ambra, non una pastiglia — la riga ne ha già una. */
.concept-exercise-discarded {
  color: var(--color-warning-text);
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
}

/* Il form di aggancio. Chiuso quando il concetto è già insegnabile, aperto
   quando gli esercizi mancano: allora agganciarne uno è la cosa da fare e
   non deve costare un click. */
.concept-attach > summary {
  cursor: pointer;
  font: var(--type-body-strong);
  padding: var(--space-2) 0;
}

.concept-attach label {
  display: block;
  margin-bottom: var(--space-2);
}

.concept-attach textarea,
.concept-attach select {
  width: 100%;
  max-width: 64ch;
}

/* Su carta il vincolo si legge e non si tocca: «Togli» e il form di
   aggancio portano già `no-print`, e resta l'esercizio con la sua riga, che
   è quello che si porta a bordo pista. Qui serve solo che una riga non si
   spezzi fra due pagine: il titolo su una e il vincolo sull'altra sarebbero
   due mezze informazioni. */
@media print {
  .concept-exercise {
    break-inside: avoid;
  }
}

/* === La modalità pista (v1.47, issue #38) ================================

   Il caso peggiore dell'app: **in piedi, con i guanti, con il sole**
   (requisito §10.6). Nome corto, regola e schema a tutta pagina, niente
   scorrimento, contrasto massimo.

   # Nero su bianco, e non bianco su nero come nel mockup

   Il mockup disegnava la schermata in negativo. Il contrasto è lo stesso,
   21:1 in entrambi i versi, quindi a decidere sono le altre due cose. La
   prima è **il sole**: su un vetro lucido, a luce piena, i riflessi si
   sommano allo sfondo, e uno sfondo nero diventa grigio mentre uno bianco
   al massimo della luminosità resta bianco. La seconda è **lo schema**: il
   renderer disegna inchiostro su una pista bianca, con lo stile dentro
   l'SVG perché il file esportato non carica CSS esterni (§10terdecies). Su
   una pagina nera la pista sarebbe un rettangolo bianco acceso in mezzo al
   buio; per disegnarla in negativo servirebbe una seconda tavolozza del
   renderer, cioè due disegni dello stesso concetto.

   # Stessi ruoli, un altro corpo

   §10.8 vuole «nessun token nuovo» per i campi del concetto, e qui non ce
   n'è uno: nome e regola restano Barlow Condensed 700 e Barlow 600, i pesi
   di `--type-section` e `--type-body-strong`. Cambia il corpo, e cambia
   come cambia nella variante di stampa — stessi ruoli, un'altra misura,
   perché è un altro supporto. Le misure stanno qui come proprietà di
   `.pista-body` e non in `:root`: fuori da questa schermata non valgono.

   # Niente scorrimento, e perché regge

   Il `body` è alto una schermata e taglia il resto. Il testo non si
   rimpicciolisce — il brief §7 lo vieta, *se non ci sta si accorcia il
   testo* — e lo schema prende lo spazio che avanza, fino a un minimo. Che
   avanzi lo garantisce il **dominio**, non questo foglio: `ConceptRule` è
   al massimo quindici parole e `ShortName` tre, e quindici parole a 32 px
   su un telefono da 360 px sono cinque righe. */

.pista-body {
  /* Il corpo minimo della regola: **il doppio di `--t-3`**, che è il corpo
     della regola nell'elenco (`.concept-rule`, `--type-body-strong`). Un
     test lo rilegge da questo file e fa il conto: se l'elenco cresce,
     questo deve crescere con lui. */
  --pista-rule-min: 2rem;
  /* Lo stesso conto per il nome: il doppio di `--t-5`, il corpo del nome
     nell'elenco (`.concept-name`, `--type-section`). */
  --pista-name-min: 3rem;
  --pista-ink: #000000;
  --pista-paper: #ffffff;
  --pista-gutter: clamp(0.75rem, 3vmin, 1.5rem);
  /* Il tocco minimo con i guanti. I 44 px di `.btn` sono il minimo a dita
     nude delle linee guida dei telefoni; un guanto da hockey ne copre di
     più, e qui non c'è nient'altro a contendere lo spazio. */
  --pista-target: 3.5rem;

  /* `100vh` prima come ripiego, `100dvh` dopo: sui telefoni `vh` conta
     anche la barra dell'indirizzo, e con `vh` soltanto l'ultima riga dello
     schema finirebbe sotto la barra — cioè esattamente lo scorrimento che
     questa pagina non deve avere. */
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  background: var(--pista-paper);
  color: var(--pista-ink);
}

.pista {
  height: 100%;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  /* Lo schema ha un **minimo**, un terzo scarso della schermata. Senza, una
     regola troppo lunga lo schiaccerebbe fino a sparire e la pagina
     mostrerebbe solo testo — senza che niente lo dica. Con il minimo, la
     regola troppo lunga esce dal fondo e si vede tagliata: è il collaudo
     di §3.3 che fa il suo mestiere, e il rimedio è accorciarla. */
  grid-template-rows: auto auto minmax(30%, 1fr);
  grid-template-areas:
    "name   exit"
    "rule   rule"
    "schema schema";
  gap: var(--space-3);
  padding: var(--pista-gutter);
  /* La tacca e gli angoli arrotondati: con `viewport-fit=cover` la pagina
     arriva fin sotto, e il nome non deve finirci. */
  padding-top: calc(var(--pista-gutter) + env(safe-area-inset-top, 0px));
  padding-bottom: calc(var(--pista-gutter) + env(safe-area-inset-bottom, 0px));
  padding-left: calc(var(--pista-gutter) + env(safe-area-inset-left, 0px));
  padding-right: calc(var(--pista-gutter) + env(safe-area-inset-right, 0px));
}

/* In orizzontale il testo va a sinistra e lo schema a destra. Impilati, su
   un telefono girato resterebbero cento pixel per lo schema; affiancati,
   lo schema prende tutta l'altezza, ed è in orizzontale che una pista 2:1
   si legge meglio.

   L'uscita passa **sopra lo schema**: accanto al nome, in una colonna
   larga metà schermo, lo costringeva su tre righe e da solo faceva
   traboccare la pagina. Sopra lo schema non toglie niente, perché lì la
   pista è limitata dalla larghezza della colonna, non dall'altezza. */
@media (orientation: landscape) and (min-aspect-ratio: 4/3) {
  .pista {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr);
    grid-template-rows: auto minmax(0, 1fr);
    grid-template-areas:
      "name exit"
      "rule schema";
    column-gap: var(--space-4);
  }

  .pista-exit {
    justify-self: end;
  }
}

/* Un telefono girato è **basso**: 320–430 px meno la barra del browser.
   Lì la regola a 32 px in mezza larghezza va su sette righe e non ci sta;
   con tre quinti della larghezza ne bastano cinque. La pista perde poco,
   perché in una colonna stretta resta limitata dalla larghezza. Su un
   tablet in orizzontale l'altezza c'è e vince la ripartizione di sopra,
   che dà più spazio allo schema. */
@media (orientation: landscape) and (min-aspect-ratio: 4/3) and (max-height: 500px) {
  .pista {
    grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
  }
}

/* `vmin` e non `vw`: la misura segue il lato **corto** dello schermo, così
   girare il telefono non fa esplodere il nome in una colonna larga metà. */
.pista-name {
  grid-area: name;
  font: 700 clamp(var(--pista-name-min), 1.5rem + 6vmin, 5rem) / 1 var(--font-cond);
  margin: 0;
  min-width: 0;
  overflow-wrap: break-word;
}

.pista-rule {
  grid-area: rule;
  font: 600 clamp(var(--pista-rule-min), 1rem + 3vmin, 3.25rem) / 1.2 var(--font-sans);
  margin: 0;
  /* Niente sillabazione: «passa-ta» a capo si legge in due tempi, e questa
     riga va letta in uno, di sbieco, con il sole. Righe più irregolari
     costano meno di una parola spezzata. */
  hyphens: manual;
  /* Una misura di riga e non tutta la larghezza: su un tablet in verticale
     una regola da quindici parole su due righe lunghe si legge peggio che
     su quattro corte. */
  max-width: 24ch;
}

/* (issue #98) Lo stesso comando fa anche «Apri in pista» sulla chiusura
   della seduta: `grid-area` là non vale, perché sta in una colonna. */
.pista-exit,
.pista-link {
  grid-area: exit;
  align-self: start;
  display: inline-flex;
  align-items: center;
  min-height: var(--pista-target);
  padding: 0 var(--space-3);
  border: 2px solid var(--pista-ink);
  border-radius: var(--radius-sm);
  background: var(--pista-paper);
  color: var(--pista-ink);
  font: 600 var(--t-4) / 1 var(--font-sans);
  text-decoration: none;
  white-space: nowrap;
}

.pista-exit:focus-visible,
.pista-link:focus-visible,
.pista-pick:focus-visible + .pista-step {
  outline: 3px solid var(--pista-ink);
  outline-offset: 3px;
}

/* --- Lo schema --------------------------------------------------------
   Il meccanismo è quello della scheda (`.concept-frames`): radio nascosti
   alla vista e non alla tastiera, la `<label>` come comando, il fotogramma
   scelto acceso da `:checked + label + figure`. Cambia la disposizione:
   una griglia con il disegno sopra, largo quanto tutte le colonne, e i
   comandi sotto, una colonna ciascuno. */

.pista-frames {
  grid-area: schema;
  min-height: 0;
  display: grid;
  grid-template-columns: repeat(var(--pista-frames, 1), minmax(0, 1fr));
  grid-template-rows: minmax(0, 1fr) auto;
  gap: var(--space-2);
}

.pista-frames.is-single {
  row-gap: 0;
}

.pista-pick {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.pista-frame {
  grid-row: 1;
  grid-column: 1 / -1;
  display: none;
  min-height: 0;
  margin: 0;
}

.pista-pick:checked + .pista-step + .pista-frame {
  display: block;
}

/* Il disegno riempie il riquadro in entrambe le direzioni, e non si
   deforma: il renderer scrive `preserveAspectRatio="xMidYMid meet"`, che lo
   centra nella misura che ci sta. È questo che permette allo schema di
   cedere lo spazio al testo senza essere tagliato. */
.pista-frame .rink-diagram {
  width: 100%;
  height: 100%;
  margin: 0;
}

.pista-step {
  grid-row: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: var(--pista-target);
  border: 2px solid var(--pista-ink);
  border-radius: var(--radius-sm);
  background: var(--pista-paper);
  color: var(--pista-ink);
  font: 700 var(--t-5) / 1 var(--font-cond);
  cursor: pointer;
}

/* Il fotogramma acceso è **pieno**, gli altri contorno: la stessa
   distinzione del glifo di fase, che regge anche a chi guarda lo schermo
   di sbieco con il sole in faccia. */
.pista-pick:checked + .pista-step {
  background: var(--pista-ink);
  color: var(--pista-paper);
}

.pista-frames.is-single .pista-step {
  display: none;
}

/* --- La modalità pista della seduta (issue #98) -------------------------

   La modalità del concetto estesa a tutta la seduta: un esercizio per
   schermata (`session_rink.html`). Stesse proprietà di `.pista-body` —
   nero su bianco, il tocco da guanti — e la stessa regola: la schermata
   non scorre, il testo non cede, cede lo schema.

   # Una schermata alla volta

   Ogni schermata è una `<section>` dopo la sua radio, e si vede solo
   quella della radio scelta (`:checked +`). Le radio stanno ferme in cima
   alla finestra: scegliendo la successiva il browser le dà il fuoco, e
   una radio rimasta in fondo alla schermata di prima farebbe scorrere la
   pagina fin lì.

   # «Successivo» sempre allo stesso posto

   Ogni schermata è una griglia alta quanto la finestra, e la sua ultima
   riga è la barra dei due comandi. La riga del testo parte da zero
   (`minmax(0, max-content)`): se il testo non ci sta si taglia lui, e non
   spinge fuori la barra. Lo schema ha il minimo della modalità del
   concetto, un terzo scarso. */

/* Le punte di freccia della pagina: presenti e alte zero, **non**
   `display: none` — vedi `session_rink.html`. */
.pista-defs {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

.pista-session {
  height: 100%;
}

.pista-session .pista-pick {
  position: fixed;
  top: 0;
  left: 0;
}

.pista-screen {
  display: none;
  box-sizing: border-box;
  height: 100%;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--space-2) var(--space-3);
  padding: var(--pista-gutter);
  padding-top: calc(var(--pista-gutter) + env(safe-area-inset-top, 0px));
  padding-bottom: calc(var(--pista-gutter) + env(safe-area-inset-bottom, 0px));
  padding-left: calc(var(--pista-gutter) + env(safe-area-inset-left, 0px));
  padding-right: calc(var(--pista-gutter) + env(safe-area-inset-right, 0px));
}

.pista-pick:checked + .pista-screen {
  display: grid;
}

.pista-opening {
  grid-template-rows: auto auto minmax(0, 1fr) auto auto;
  grid-template-areas:
    "where exit"
    "title title"
    "facts facts"
    "awake awake"
    "nav   nav";
}

.pista-exercise {
  grid-template-rows: auto minmax(0, max-content) minmax(30%, 1fr) auto;
  grid-template-areas:
    "where   exit"
    "text    text"
    "drawing drawing"
    "nav     nav";
}

.pista-closing {
  grid-template-rows: auto minmax(0, 1fr) auto;
  grid-template-areas:
    "where exit"
    "text  text"
    "nav   nav";
}

/* Dove si è: la data sull'apertura, «Parte principale · 2 di 4» su un
   esercizio. Occhiello, come sulla scheda, a un corpo che si legge da un
   metro. */
.pista-where {
  grid-area: where;
  align-self: center;
  margin: 0;
  font: 700 var(--t-4) / 1.15 var(--font-cond);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.pista-title {
  margin: 0;
  font: 700 clamp(2rem, 1.25rem + 4vmin, 3.75rem) / 1.02 var(--font-cond);
  overflow-wrap: break-word;
  hyphens: manual;
}

.pista-opening > .pista-title {
  grid-area: title;
  font-size: clamp(2.5rem, 1.5rem + 6vmin, 4.5rem);
}

/* --- L'apertura -------------------------------------------------------- */

.pista-facts {
  grid-area: facts;
  min-height: 0;
  overflow: hidden;
  margin: 0;
  display: grid;
  align-content: start;
  gap: var(--space-3);
}

.pista-facts div {
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--pista-ink);
}

.pista-facts dt {
  font: 700 var(--t-3) / 1.2 var(--font-cond);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.pista-facts dd {
  margin: 0;
  font: 600 clamp(1.375rem, 1rem + 2vmin, 2.25rem) / 1.2 var(--font-sans);
}

.pista-awake {
  grid-area: awake;
  margin: 0;
  font: 600 var(--t-3) / 1.3 var(--font-sans);
}

/* --- Un esercizio ------------------------------------------------------

   Minuti e nome sulla stessa riga, l'obiettivo sotto: una riga intera per
   i soli minuti costerebbe allo schema l'altezza che su un telefono in
   verticale decide se la pista si può girare. */

.pista-text {
  grid-area: text;
  min-height: 0;
  overflow: hidden;
}

.pista-exercise .pista-text {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas:
    "clock title"
    "goal  goal";
  align-items: baseline;
  gap: var(--space-2) var(--space-3);
}

.pista-clock {
  grid-area: clock;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  font: 700 clamp(2.75rem, 1.75rem + 5vmin, 5rem) / 1 var(--font-cond);
}

/* Il glifo cresce con i minuti: alla sua misura di catalogo, 37 px, accanto
   a una cifra da cinquanta sparirebbe. */
.pista-clock .fase-glifo {
  width: auto;
  height: 0.8em;
}

/* Il testo non si rimpicciolisce: se il nome o l'obiettivo sono più lunghi
   di così si tagliano, e i puntini dicono che c'è dell'altro — la scheda
   intera è a un tocco. È il «tagliata e visibile» della modalità del
   concetto, con una differenza: là il dominio garantisce che la regola
   stia (quindici parole), qui nome e obiettivo di un esercizio non hanno
   un tetto, e il taglio è il caso da prevedere, non il collaudo. */
.pista-exercise .pista-title {
  grid-area: title;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
}

.pista-goal {
  grid-area: goal;
  margin: 0;
  max-width: 34ch;
  font: 600 clamp(1.125rem, 0.85rem + 1.6vmin, 1.75rem) / 1.25 var(--font-sans);
  hyphens: manual;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
}

/* Lo schema prende quello che avanza, e **la sua forma** decide quale
   delle due rese si vede: il riquadro è un contenitore, e in un riquadro
   più alto che largo la pista girata viene più grande di quella dritta —
   la pista è 1:2, quindi la girata vince esattamente quando l'altezza
   supera la larghezza. Una domanda sul riquadro e non sullo schermo,
   perché lo stesso telefono in verticale ha sopra lo schema un obiettivo
   di una riga o di tre. Senza query di contenitore resta quella dritta. */
.pista-drawing {
  grid-area: drawing;
  min-height: 0;
  container-type: size;
}

.pista-picture {
  width: 100%;
  height: 100%;
}

.pista-picture .rink-diagram {
  width: 100%;
  height: 100%;
  margin: 0;
}

.pista-portrait {
  display: none;
}

@container (orientation: portrait) {
  .pista-portrait {
    display: block;
  }

  .pista-landscape {
    display: none;
  }
}

/* Le immagini importate, così come sono: una per riga, ciascuna intera
   nella sua parte di riquadro. In un riquadro largo, affiancate. */
.pista-images {
  height: 100%;
  display: grid;
  grid-auto-rows: minmax(0, 1fr);
  gap: var(--space-2);
}

@container (orientation: landscape) {
  .pista-images {
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr);
  }
}

.pista-images img {
  width: 100%;
  height: 100%;
  min-height: 0;
  object-fit: contain;
}

/* --- La chiusura ------------------------------------------------------- */

.pista-closing .pista-text {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
}

/* Il nome del concetto al corpo della sua modalità pista: è lo stesso
   concetto, e il minimo è lo stesso (`--pista-name-min`). */
.pista-closing .pista-title {
  font-size: clamp(var(--pista-name-min), 1.5rem + 6vmin, 5rem);
}

/* --- «‹ Precedente» e «Successivo ›» ------------------------------------

   Due colonne fisse: dove un comando non c'è — nessun «Precedente»
   sull'apertura, nessun «Successivo» sull'ultima schermata — resta il suo
   posto vuoto, e l'altro non si sposta. «Successivo» è pieno, perché è il
   gesto che si fa quasi sempre; alto un pollice più del tocco minimo da
   guanti, perché si fa senza guardare. */
.pista-nav {
  grid-area: nav;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2);
}

.pista-prev,
.pista-next {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: calc(var(--pista-target) + 1rem);
  padding: 0 var(--space-2);
  border: 2px solid var(--pista-ink);
  border-radius: var(--radius-sm);
  font: 700 var(--t-5) / 1 var(--font-cond);
  text-transform: uppercase;
  text-align: center;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
}

.pista-prev {
  grid-column: 1;
  background: var(--pista-paper);
  color: var(--pista-ink);
}

.pista-next {
  grid-column: 2;
  background: var(--pista-ink);
  color: var(--pista-paper);
}

/* Dalla tastiera si scorre con le frecce, sulle radio: il fuoco lo dice la
   barra della schermata. */
.pista-pick:focus-visible + .pista-screen .pista-nav {
  outline: 3px solid var(--pista-ink);
  outline-offset: 3px;
}

/* In orizzontale il testo a sinistra e lo schema a destra, come nella
   modalità del concetto: impilati, su un telefono girato allo schema
   resterebbero cento pixel. */
@media (orientation: landscape) and (min-aspect-ratio: 4/3) {
  .pista-exercise {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr);
    grid-template-rows: auto minmax(0, 1fr) auto;
    grid-template-areas:
      "where exit"
      "text  drawing"
      "nav   nav";
  }

  .pista-screen .pista-exit {
    justify-self: end;
  }

  .pista-facts {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* Un telefono girato è basso: la barra torna al tocco minimo. */
@media (orientation: landscape) and (max-height: 500px) {
  .pista-prev,
  .pista-next {
    min-height: var(--pista-target);
  }
}

/* --- L'ingresso: dalla scheda e dalla seduta ---------------------------- */

.concept-rink-open {
  margin: 0 0 var(--space-4);
}

/* `margin-left: auto` spinge i due bottoni a destra anche quando sono
   andati a capo, dove altrimenti starebbero sotto la miniatura. */
.session-concept-rink {
  flex: none;
  margin-left: auto;
}

/* --- (v1.50, issue #39) Perché il file per i giocatori non porta tutto ---
   Una riga per squadra sotto i link di esportazione (`concepts.html`). Non
   `.field-hint`: quella ha il margine superiore negativo di chi sta sotto
   un campo di form, e due di fila si accavallavano. */
.concept-export-notes {
  list-style: none;
  margin: 0 0 var(--space-4);
  padding: 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}
.concept-export-notes li + li {
  margin-top: var(--space-1);
}
.concept-export-notes strong {
  color: var(--color-text);
  font-weight: 600;
}

/* ===========================================================================
   (v1.60, issue #52) Amministrazione.

   Una scheda per utente, una sotto l'altra: nome ed email in testa, gli
   incarichi con il loro «Salva», e le azioni in fila. Schede e non una
   tabella perché ogni riga porta due form e fino a tre pulsanti, e sul
   telefono una tabella con dentro dei form si impila male.

   I pulsanti di un'azione stanno ognuno nel suo `<form>`: la regola
   globale `form` darebbe a ciascuno superficie, bordo e padding, cioè tre
   scatole per tre pulsanti. Come in `.entity-actions`, il form sparisce
   dal layout (`display: contents`) e restano i pulsanti.
   =========================================================================== */

.admin-section {
  margin-bottom: var(--space-5);
}

/* Una nota che non sta sotto un campo: `.field-hint` ha il margine
   negativo di chi segue un `<input>`, e qui si accavallerebbe. */
.admin-note {
  margin: 0 0 var(--space-3);
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.admin-users {
  list-style: none;
  margin: 0 0 var(--space-3);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.admin-user {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* Disattivato: la scheda resta leggibile — è lì che lo si riattiva — ma
   si fa da parte, e il badge dice perché. */
.admin-user.is-deactivated {
  background: var(--color-bg);
}

.admin-user-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
}

.admin-user-name {
  font: var(--type-title);
}

.admin-user-email {
  flex-basis: 100%;
  color: var(--color-text-muted);
  font: var(--type-caption);
}

/* Il form degli incarichi dentro la scheda: niente seconda cornice, e le
   caselle con il loro pulsante su una riga finché ci stanno. */
.admin-assignments {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: flex-end;
  background: none;
  border: none;
  border-radius: 0;
  padding: 0;
  margin: 0;
}

.admin-assignments fieldset {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  flex: 1 1 18rem;
}

.admin-user-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}

.admin-user-actions form,
.admin-links form {
  display: contents;
}

.admin-user-actions .admin-note {
  margin: 0;
  flex: 1 1 100%;
}

/* Il link appena generato: il campo occupa tutta la riga, perché l'unica
   cosa da fare è selezionarlo e copiarlo. Monospaziato, così un carattere
   del token non si confonde con un altro se lo si detta. */
.admin-issued h2 {
  margin-top: 0;
}

.admin-issued-url {
  width: 100%;
  box-sizing: border-box;
  font-family: var(--font-mono, ui-monospace, monospace);
}

.admin-issued .field-hint {
  margin-top: var(--space-2);
}

.admin-invite fieldset {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
}

/* (v1.69, issue #61) La decisione su una proposta: «Approva» è un form col
   solo pulsante, e come nelle azioni di un utente il form sparisce dal
   layout per lasciare il pulsante accanto a «Correggi e approva». Il form
   del rifiuto resta una scatola: ha un campo da scrivere. */
.proposal-decision .page-actions form {
  display: contents;
}

/* (v1.70, issue #62) Le differenze di una proposta di modifica: una riga per
   campo, e le colonne sono le versioni. Il testo di più righe
   (svolgimento, punti chiave) va a capo dove andava sulla scheda, e lo
   schema è quello della scheda, stretto alla sua colonna. Sul telefono la
   tabella scorre da sola invece di allargare la pagina. */
.proposal-diff-scroll {
  overflow-x: auto;
}

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

.proposal-diff-table th,
.proposal-diff-table td {
  padding: var(--space-2);
  border-bottom: 1px solid var(--color-border);
  text-align: left;
  vertical-align: top;
}

/* Solo i valori: nei nomi dei campi gli a capo sono quelli del markup. */
.proposal-diff-table td {
  white-space: pre-line;
}

.proposal-diff-table thead th {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* Un campo che l'autore non ha cambiato come adesso: approvare lo
   riscriverebbe. L'ambra dell'avviso di proposta superata, a cui rimanda. */
.proposal-diff-table tr.is-overwritten th {
  border-left: 3px solid var(--color-warning-border);
}

.proposal-diff-note {
  display: block;
  font: var(--type-caption);
  color: var(--color-warning-text);
}

.proposal-diff-rink {
  margin: 0;
  min-width: 12rem;
  max-width: 22rem;
  /* Il disegno non è testo della scheda: gli a capo del markup SVG non
     devono diventare righe. */
  white-space: normal;
}

.proposal-full-card {
  margin: var(--space-4) 0;
}

/* La pagina di chi apre un link (`invite.html`): la card del login, con un
   titolo e una riga che dicono per chi è la password. */
.invite-title {
  font: var(--type-title);
  margin: 0 0 var(--space-2);
  text-align: left;
}

.invite-lead {
  margin: 0 0 var(--space-4);
  text-align: left;
}

.login-form .field-hint {
  margin: 0;
}

/* ===========================================================================
   (issue #102) Lo Speed Lap Challenge — `speed_lap_day.html`, e le due
   sezioni in pagine altrui (`partials/speed_lap_section.html` nelle
   Valutazioni, `partials/speed_lap_lap_card.html` in Squadre e club), che
   usano solo `card` ed `entity-list`.
   =========================================================================== */

/* I tempi si confrontano sopra e sotto in colonna: cifre tabulari, e né un
   tempo né una velocità vanno a capo in mezzo («32,45″», «12,5 km/h»). Il
   nome di chi corre resta in tondo come nel roster, non in grassetto da
   intestazione di riga. */
.speed-lap-table th[scope="row"] {
  font-weight: 400;
}

.speed-lap-table .speed-lap-attempts,
.speed-lap-table .speed-lap-best,
.speed-lap-table .speed-lap-speed {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.speed-lap-table .speed-lap-direction {
  text-align: center;
}

/* Sul telefono le due direzioni con tentativi, migliore e velocità non ci
   stanno: scorre la tabella, dentro la sua card, e la pagina mai. Nessuna
   testata `sticky` qui dentro, quindi l'`overflow` non ne sposta il
   riferimento (vedi `.tagging-table`). `position: relative` perché dentro
   ci sono i «non ha corso» nascosti (`.visually-hidden`, assoluti): senza un
   antenato posizionato nel riquadro scappano dal suo `overflow` e allargano
   la pagina lo stesso. */
.speed-lap-table-scroll {
  position: relative;
  overflow-x: auto;
}

/* I tentativi in fila, in grigio; il migliore in inchiostro e in grassetto
   — il peso e non il solo colore, perché la pagina si stampa. È lo stesso
   tempo della colonna «Migliore» accanto. Per chi modifica ogni tentativo è
   un link, e resta sottolineato come ogni link. */
.speed-lap-attempts > a,
.speed-lap-attempts > span {
  margin-right: var(--space-2);
  color: var(--color-text-muted);
}

.speed-lap-attempts > .speed-lap-attempt-best {
  color: var(--color-text);
  font-weight: 600;
}

/* Il trattino di una direzione non corsa: manca, non vale zero. */
.speed-lap-absent {
  color: var(--color-text-muted);
}

.speed-lap-form {
  max-width: 32rem;
}

.speed-lap-form .is-invalid input,
.speed-lap-form .is-invalid select {
  border-color: var(--color-danger-border);
  box-shadow: 0 0 0 1px var(--color-danger-border);
}

/* ===========================================================================
   (issue #104) Le classifiche dello Speed Lap Challenge —
   `speed_lap_standings.html`. Tre sezioni (assoluta, per annata,
   progressione), e in ognuna le due direzioni affiancate quando ci stanno:
   avanti a sinistra, indietro a destra, così la stessa classifica nelle due
   direzioni si legge in una riga. Quante colonne le decide la larghezza
   minima della tabella, **senza `@media`**: sotto, una sotto l'altra, e la
   tabella scorre nel suo `.table-scroll`.
   =========================================================================== */

.speed-lap-standings-pair {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(26rem, 100%), 1fr));
  gap: var(--space-4) var(--space-5);
  align-items: start;
}

/* Contenitore di se stessa: la tabella si stringe secondo la **sua**
   colonna, non secondo lo schermo — a 1400px le due direzioni affiancate
   sono larghe quanto una sola su un tablet. */
.speed-lap-standings-direction {
  min-width: 0;
  container-type: inline-size;
}

.speed-lap-standings-direction > h3 {
  margin-top: 0;
}

/* L'ultima difesa: se una tabella non ci sta nemmeno stretta (un nome
   lunghissimo senza spazi), scorre lei e non la pagina. Nessuna testata
   `sticky` qui dentro, quindi l'`overflow` non ne sposta il riferimento
   (vedi `.tagging-table`). */
.speed-lap-standings-direction > .table-scroll {
  overflow-x: auto;
}

.speed-lap-standings-filter {
  margin-bottom: var(--space-2);
}

.speed-lap-standings-filter select {
  max-width: 20rem;
}

.speed-lap-standings-day {
  margin-top: 0;
}

/* Il nome in tondo come nel roster, non in grassetto da intestazione di
   riga: il grassetto della riga è del tempo, cioè di ciò che mette in fila. */
.speed-lap-standings-table th[scope="row"] {
  font-weight: 400;
}

/* Tempi, velocità, giorni e percentuali si confrontano in colonna: cifre
   tabulari, allineate a destra, e mai a capo in mezzo («32,45″»,
   «18,4 km/h», «+12,5%»). */
.speed-lap-standings-table .speed-lap-figure {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  text-align: right;
}

/* Le testate sì, vanno a capo: «Dal primo all'ultimo» è più largo delle
   cifre sotto, e sul telefono è lui che spingerebbe fuori la colonna. */
.speed-lap-standings-table thead .speed-lap-figure {
  white-space: normal;
}

.speed-lap-standings-table .speed-lap-rank {
  width: 3rem;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.speed-lap-standings-table abbr[title] {
  text-decoration: none;
}

/* Il giorno di un tempo — sotto il migliore nell'assoluta e nell'annata,
   accanto a primo e ultimo nella progressione —: contesto, in piccolo e in
   grigio. */
.speed-lap-standings-table .speed-lap-when {
  color: var(--color-text-muted);
  font-size: var(--t-2);
}

.speed-lap-standings-table td > .speed-lap-when {
  display: block;
}

/* Primo e ultimo giorno della progressione, uno per riga dentro la cella:
   due colonne in meno, e la percentuale resta in vista sul telefono. */
.speed-lap-standings-table .speed-lap-step {
  display: block;
}

/* L'annata apre il suo gruppo di righe: un'intestazione, non una riga di
   dati — maiuscoletto come le testate di colonna. */
.speed-lap-standings-table .speed-lap-year th {
  padding-top: var(--space-3);
  font: var(--type-thead);
  letter-spacing: var(--type-thead-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* Una colonna stretta — il telefono, 22rem di tabella in 20 di posto —:
   via la maglia, che il nome accanto rende superflua, e le celle si
   avvicinano. Posizione, nome, tempo e velocità restano tutti. Dopo le
   regole di sopra, o perderebbe a pari specificità. */
@container (max-width: 28rem) {
  .speed-lap-standings-table .roster-number-col {
    display: none;
  }

  .speed-lap-standings-table th,
  .speed-lap-standings-table td {
    padding-inline: var(--space-1);
  }

  .speed-lap-standings-table .speed-lap-rank {
    width: 2rem;
  }
}


/* ===========================================================================
   (issue #105) La progressione sullo Speed Lap Challenge —
   `partials/speed_lap_progression.html`, sulla scheda persona e sulla scheda
   del giocatore nel roster, col grafico di `speed_lap_chart::render`.
   =========================================================================== */

/* Il disegno scala con la card fino a una misura in cui le sue scritte
   restano del corpo di una didascalia; sotto quella misura si scorre di lato,
   come una tabella larga, invece di rimpicciolire le cifre fino a non
   leggerle. `contain: inline-size` perché lo scorrimento resti qui dentro
   anche se la card finisce in una griglia o in un flex: lì la misura minima
   del disegno allargherebbe la card, e con lei la pagina. */
.speed-chart-frame {
  overflow-x: auto;
  contain: inline-size;
  margin: var(--space-3) 0;
}

.speed-chart {
  display: block;
  width: 100%;
  min-width: 34rem;
  max-width: 48rem;
  height: auto;
  color: var(--color-text);
  font-family: var(--font-sans);
}

/* Inchiostro e nessuna tinta di famiglia: il colore della pagina è dei temi.
   Le due serie si distinguono per forma — linea continua e cerchio pieno in
   avanti, linea tratteggiata e quadrato vuoto all'indietro —, perché la
   pagina si stampa, e in bianco e nero resta solo la forma. */
.speed-chart-line {
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linejoin: round;
}

.speed-chart-line-backward {
  stroke-dasharray: 6 4;
}

.speed-chart-mark-forward {
  fill: currentColor;
}

/* Vuoto del colore della card, non trasparente: la linea tratteggiata ci
   passa sotto e non dentro. */
.speed-chart-mark-backward {
  fill: var(--color-surface);
  stroke: currentColor;
  stroke-width: 2;
}

.speed-chart text {
  fill: currentColor;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}

.speed-chart .speed-chart-kmh,
.speed-chart .speed-chart-unit,
.speed-chart .speed-chart-date,
.speed-chart .speed-chart-season-teams {
  fill: var(--color-text-muted);
}

.speed-chart .speed-chart-time {
  font-weight: 600;
}

.speed-chart .speed-chart-season-name,
.speed-chart .speed-chart-legend {
  font-size: 12px;
}

.speed-chart .speed-chart-season-name {
  font-weight: 700;
}

.speed-chart-grid {
  stroke: var(--color-border);
  stroke-width: 1;
}

.speed-chart-axis,
.speed-chart-tick {
  stroke: currentColor;
  stroke-width: 1;
}

/* La cesura di stagione: punteggiata, e diversa dal tratteggio della serie
   all'indietro. Attraversa l'asse del tempo, sotto la riga delle tacche. */
.speed-chart-season {
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-dasharray: 2 3;
}

/* La tabella accanto: i tempi uno per uno, anche senza lunghezza del giro.
   Cifre tabulari; né un tempo né una velocità vanno a capo in mezzo, ma la
   velocità va sotto il tempo quando la colonna è stretta (il telefono). */
.speed-progression-table {
  width: 100%;
  border-collapse: collapse;
  font: var(--type-caption);
  font-variant-numeric: tabular-nums;
}

.speed-progression-table th,
.speed-progression-table td {
  padding: var(--space-1) var(--space-2);
  text-align: left;
  border-bottom: 1px solid var(--color-border);
}

.speed-progression-table thead th {
  font: var(--type-thead);
  letter-spacing: var(--type-thead-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* Il nome del tratto di stagione, in testa al suo gruppo di righe: la stessa
   cesura del grafico. */
.speed-progression-period th {
  padding-top: var(--space-3);
  font-weight: 700;
}

.speed-progression-time > span {
  white-space: nowrap;
}

.speed-progression-speed {
  color: var(--color-text-muted);
}

/* Il giorno è l'intestazione della riga, ma si legge come le altre celle. */
.speed-progression-table tbody th[scope="row"] {
  font-weight: 400;
}

/* Su carta la misura minima non serve, e il disegno non si spezza fra due
   pagine. **Dopo** le regole di schermo: a pari specificità vince l'ultima,
   e prima di `.speed-chart` il `min-width: 0` perderebbe. */
@media print {
  .speed-chart-frame {
    overflow: visible;
    break-inside: avoid;
  }

  .speed-chart {
    min-width: 0;
  }
}

/* ===========================================================================
   (issue #106) Le velocità in «Come siamo messi»: la quinta scheda della
   testata (`.squad-speed-card`) e le due cifre in fondo a ogni riga di «Chi
   siamo» (`.squad-row-speed`). In coda al foglio e non nella sezione del
   cruscotto: ogni regola qui corregge una regola di là, e vince per ordine
   a pari specificità o per specificità, come dice accanto.
   =========================================================================== */

/* Cinque riquadri in testata, solo quando la squadra-stagione ha almeno un
   challenge (`squad-kpis-five`; senza, la testata resta `stat-card-grid-four`
   com'era). Sotto 1500px valgono le tre colonne di `.stat-card-grid` — tre
   più due, e nessun riquadro orfano sotto quattro in fila, che era ciò che
   l'`auto-fit` di `stat-card-grid-four` avrebbe fatto a 1280 — e sotto 640
   una. Da 1500 i cinque stanno in fila, e la velocità è un poco più larga
   perché porta due cifre con la loro direzione.

   Le misure, sul caso peggiore: «Mercoledì 30 settembre 2026» prende 188px
   nel corpo del valore, e ogni riquadro ne toglie 34 di cornice. Con la
   velocità a 1.2fr, a 1500px ai quattro restano 194px di testo e alla
   velocità 240, contro i 187 di «16,0 avanti · 12,4 indietro»; a 1480 i
   quattro scendono a 190, e sotto il giorno lungo va a capo. A 1560 la riga
   resta alta quanto prima: la scheda della copertura va già su due righe, e
   la velocità non ne ha più di lei. Stessa specificità di `.stat-card-grid`,
   dopo di lei nel file. */
@media (min-width: 1500px) {
  .squad-kpis-five {
    grid-template-columns: repeat(4, minmax(0, 1fr)) minmax(0, 1.2fr);
  }
}

/* L'unità nell'occhiello, a destra (`.stat-card-label` è una riga
   `space-between`). In cassa bassa, quindi a 14px: il maiuscoletto a 12 è
   dell'occhiello, e «KM/H» non è un'unità. Interlinea 1 e un pixel in meno
   sopra e sotto: 14px di corpo in un occhiello alto 13, che altrimenti si
   alzerebbe e non starebbe più in riga con i quattro accanto — e con lui
   tutta la testata, quando la frase sotto va su due righe. */
.squad-speed-card .squad-speed-unit {
  margin: -1px 0;
  font: var(--type-caption);
  line-height: 1;
  letter-spacing: 0;
  text-transform: none;
}

.squad-speed-values {
  font-variant-numeric: tabular-nums;
}

/* Una cifra e la sua direzione non si separano mai: se la riga non ci sta,
   va a capo fra le due direzioni. */
.squad-speed-figure {
  white-space: nowrap;
}

.squad-speed-sep {
  margin: 0 var(--space-2);
}

.squad-speed-none {
  color: var(--color-text-muted);
}

/* ---- Le due cifre nella riga del giocatore ---------------------------------
   Una sesta cella dopo la copertura, che resta accanto al livello (§4.1 del
   requisito della dashboard). Il filetto a sinistra la stacca da «1/7»:
   senza, «1/7 18,4 15,2» si leggerebbe come una cifra sola. Specificità
   (0,2,0) contro la (0,1,0) di `.squad-row`. */
.squad-dash-speeds .squad-row {
  grid-template-columns: 1.75rem minmax(0, 1fr) auto 2.25rem auto auto;
}

/* Sopra il link della riga, come la copertura: le due cifre portano un
   `title` loro (tempo e giorno), che il `::after` del nome coprirebbe. */
.squad-row-speed {
  position: relative;
  z-index: 1;
  display: flex;
  gap: var(--space-1);
  padding-left: var(--space-1);
  border-left: 1px solid var(--color-border);
  font: var(--type-caption);
  font-variant-numeric: tabular-nums;
}

.squad-row-speed-figure {
  min-width: 1.75rem;
  text-align: right;
}

.squad-row-speed-figure.is-none {
  color: var(--color-text-muted);
}

/* A tre colonne la sesta cella costa ai nomi di «Chi siamo» i suoi 73px,
   e a 1560 i nomi erano già al limite: «a 1440 cominciano a troncarsi»,
   dice la sezione del cruscotto. Li restituiscono tre cose, e non la
   colonna di mezzo, i cui due grafici hanno il corpo delle etichette
   misurato sulla loro larghezza:

   - la colonna delle aree, la cui barra si stringe: il nome dell'area tiene
     un minimo di 12rem — «Lettura del gioco e transizione» ne prende 183px,
     e un'area non si abbrevia —, e la distribuzione prende quel che resta.
     Il resto ha un limite: «Com'è distribuita oggi», che copre barra e
     scia, ne vuole 121, e a 1560 le proporzioni 1.32 / 1.15 / 1.23 gliene
     lasciano 127;
   - lo spazio fra le celle della riga del giocatore, 6px invece di 8;
   - la cornice della cella delle velocità, più stretta.

   A 1560, coi Novizi, i nomi di «Chi siamo» hanno così 93px contro i 94 di
   prima delle velocità. Solo con le velocità nelle righe, e non in
   settembre (`squad-dash-bare`), che ha due colonne sue. Specificità
   (0,3,0) contro la (0,1,0) di `.squad-dash`, (0,2,0) contro la (0,1,0) di
   `.area-head, .area-row` e contro la (0,1,0) di `.squad-row`. */
@media (min-width: 1560px) {
  .squad-dash.squad-dash-speeds:not(.squad-dash-bare) {
    grid-template-columns: minmax(0, 1.32fr) minmax(0, 1.15fr) minmax(0, 1.23fr);
  }

  .squad-dash-speeds .area-head,
  .squad-dash-speeds .area-row {
    grid-template-columns:
      minmax(12rem, 1.4fr) 2rem minmax(3.5rem, 1fr) auto 2.6rem;
  }

  .squad-dash-speeds .squad-row {
    column-gap: 6px;
  }

  .squad-dash-speeds .squad-row-speed {
    padding-left: 3px;
  }
}

/* Sul telefono la riga ha già il nome che va a capo: le due cifre scendono
   sotto il nome, invece di stringerlo ancora, e lì c'è posto per dire di
   che direzione sono — la parola che altrove sta solo per chi legge con lo
   schermo. Attaccate alla loro riga (niente spazio fra le due righe della
   griglia) e staccate dalla prossima, o si leggerebbero come sue. Dopo la
   regola a sei colonne, con la sua stessa specificità. */
@media (max-width: 700px) {
  .squad-dash-speeds .squad-row {
    grid-template-columns: 1.75rem minmax(0, 1fr) auto 2.25rem auto;
    row-gap: 0;
    padding-bottom: var(--space-1);
  }

  .squad-row-speed {
    grid-column: 2 / -1;
    gap: var(--space-3);
    padding-left: 0;
    border-left: 0;
    color: var(--color-text-muted);
  }

  .squad-row-speed-figure {
    text-align: left;
    color: var(--color-text);
  }

  .squad-row-speed .squad-row-speed-label {
    position: static;
    width: auto;
    height: auto;
    overflow: visible;
    clip: auto;
    color: var(--color-text-muted);
  }
}
/* ===========================================================================
   (issue #103) Il cronometro dello Speed Lap Challenge —
   `speed_lap_stopwatch.html`, con le proprietà di `.pista-body`: nero su
   bianco, il tocco da guanti (`--pista-target`), niente scorrimento.

   # Un solo bottone che conta

   Si legge al sole, a un braccio di distanza, con i guanti (requisito §12):
   dall'alto chi corre, il tempo, il bottone — alto quanto resta, circa metà
   della schermata su un telefono in verticale — e il prossimo. Il resto
   (tornare, saltare, il tentativo in più) sta sotto, più piccolo, perché si
   tocca guardando.

   «Via» è pieno, «Stop» è vuoto con il bordo spesso: la parola cambia, e
   cambia anche la forma, così lo si riconosce anche di sbieco.

   # Salvato o no: forma e parola

   Lo stato di un tempo non ha colore (§12). Salvato è una pastiglia piena,
   in coda un riquadro **tratteggiato**, rifiutato un bordo **doppio** — e
   ognuno lo dice anche a parole.

   # `[hidden]`

   Il foglio non ha una regola generale per `[hidden]`, e un `display`
   dell'autore la vince su quella del browser: ogni pezzo che lo script
   nasconde ha la sua riga `[hidden]` qui sotto.
   =========================================================================== */

.stopwatch,
.stopwatch-fallback {
  box-sizing: border-box;
  height: 100%;
  display: grid;
  gap: var(--space-2) var(--space-3);
  padding: var(--pista-gutter);
  padding-top: calc(var(--pista-gutter) + env(safe-area-inset-top, 0px));
  padding-bottom: calc(var(--pista-gutter) + env(safe-area-inset-bottom, 0px));
  padding-left: calc(var(--pista-gutter) + env(safe-area-inset-left, 0px));
  padding-right: calc(var(--pista-gutter) + env(safe-area-inset-right, 0px));
}

.stopwatch-fallback {
  grid-template-columns: minmax(0, 1fr);
  align-content: start;
  justify-items: start;
  gap: var(--space-3);
}

/* Occhiello, titolo e uscita della modalità pista hanno ciascuno la sua
   `grid-area`: in questa colonna sola vanno uno sotto l'altro, nell'ordine
   del markup. */
.stopwatch-fallback > * {
  grid-area: auto;
}

.stopwatch-fallback-text {
  margin: 0;
  max-width: 30ch;
  font: 600 var(--t-5) / 1.3 var(--font-sans);
  hyphens: manual;
}

.stopwatch {
  grid-template-columns: minmax(0, 1fr) auto;
  /* Il bottone prende quello che resta, e non scende sotto un minimo:
     senza, una coda lunga lo schiaccerebbe. La coda ha il suo tetto e
     scorre dentro. */
  grid-template-rows: auto auto auto auto minmax(10rem, 1fr) auto auto auto auto;
  grid-template-areas:
    "where    exit"
    "runner   runner"
    "clock    clock"
    "result   result"
    "go       go"
    "next     next"
    "controls controls"
    "queue    queue"
    "awake    awake";
}

.stopwatch[hidden],
.stopwatch-fallback[hidden],
.stopwatch-result[hidden],
.stopwatch-queue[hidden],
.stopwatch-picker[hidden],
.stopwatch-pass[hidden],
.stopwatch-body button[hidden] {
  display: none;
}

.stopwatch-where {
  min-width: 0;
}

.stopwatch-empty {
  grid-row: 2 / -1;
  grid-column: 1 / -1;
  display: grid;
  align-content: start;
  gap: var(--space-3);
}

/* Occhiello, come «Parte principale · 2 di 4» nella modalità pista. */
.stopwatch-caption {
  margin: 0;
  font: 700 var(--t-3) / 1.2 var(--font-cond);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.stopwatch-runner {
  grid-area: runner;
  min-width: 0;
}

/* Il nome si taglia invece di rimpicciolirsi: due righe bastano a
   «21 · Maria Sole Bernasconi», e un nome più lungo resta riconoscibile
   dall'inizio. */
.stopwatch-name {
  margin: 0;
  font: 700 clamp(2rem, 1rem + 6vmin, 3.25rem) / 1.02 var(--font-cond);
  overflow-wrap: break-word;
  hyphens: manual;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

/* Cifre tabulari: mentre il tempo scorre le cifre non ballano, e due tempi
   si confrontano cifra per cifra. Dopo la scorciatoia `font`, che azzera
   `font-variant-numeric`. */
.stopwatch-clock {
  grid-area: clock;
  margin: 0;
  text-align: center;
  font: 700 clamp(3.5rem, 1.5rem + 11vmin, 6.5rem) / 1 var(--font-cond);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}

.stopwatch-result {
  grid-area: result;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}

.stopwatch-result-text {
  flex: 1 1 12rem;
  margin: 0;
  font: 600 var(--t-4) / 1.3 var(--font-sans);
}

.stopwatch-result-time {
  font: 700 var(--t-6) / 1 var(--font-cond);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Lo stato di un tempo: forma e parola. */
.stopwatch-state {
  display: inline-block;
  padding: 0.1rem var(--space-2);
  border: 2px solid transparent;
  border-radius: var(--radius-sm);
  font: 700 var(--t-3) / 1.3 var(--font-sans);
}

.stopwatch-state.is-saved {
  background: var(--pista-ink);
  color: var(--pista-paper);
}

.stopwatch-state.is-queued {
  border: 2px dashed var(--pista-ink);
}

.stopwatch-state.is-refused {
  border: 4px double var(--pista-ink);
}

/* I bottoni del cronometro: il foglio li veste tutti col colore del
   marchio, e il `:hover` di un telefono resta acceso dopo il tocco. Qui
   inchiostro e carta, sempre. */
.stopwatch-body .stopwatch-control {
  min-height: var(--pista-target);
  padding: 0 var(--space-2);
  border: 2px solid var(--pista-ink);
  border-radius: var(--radius-sm);
  background: var(--pista-paper);
  color: var(--pista-ink);
  font: 700 var(--t-4) / 1.1 var(--font-cond);
  text-transform: uppercase;
  text-align: center;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

.stopwatch-body .stopwatch-control:hover {
  background: var(--pista-paper);
  border-color: var(--pista-ink);
}

/* Spento: bordo pieno e sottile, sbiadito. **Mai tratteggiato**: il
   tratteggio in questa schermata vuol dire una cosa sola, «non ancora
   salvato». */
.stopwatch-body .stopwatch-control:disabled {
  border-width: 1px;
  opacity: 0.4;
  cursor: default;
}

/* L'uscita durante un giro: non risponde (lo script ferma il clic), e lo
   si vede. */
.stopwatch-body .stopwatch-exit[aria-disabled="true"] {
  border-width: 1px;
  opacity: 0.4;
  pointer-events: none;
}

.stopwatch-body .stopwatch-control:focus-visible,
.stopwatch-body .stopwatch-go:focus-visible,
.stopwatch-body .stopwatch-pick:focus-visible {
  outline: 3px solid var(--pista-ink);
  outline-offset: 3px;
}

/* Il bottone. `touch-action` e niente selezione né menu: un dito che resta
   giù un attimo di troppo non deve aprire niente, e due tocchi veloci non
   ingrandiscono la pagina. */
.stopwatch-body .stopwatch-go {
  grid-area: go;
  width: 100%;
  min-height: 0;
  padding: 0;
  border: 4px solid var(--pista-ink);
  border-radius: var(--radius-md);
  background: var(--pista-ink);
  color: var(--pista-paper);
  font: 700 clamp(3.5rem, 1.5rem + 12vmin, 7rem) / 1 var(--font-cond);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}

.stopwatch-body .stopwatch-go:hover {
  background: var(--pista-ink);
  border-color: var(--pista-ink);
}

.stopwatch-body .stopwatch-go.is-running,
.stopwatch-body .stopwatch-go.is-running:hover {
  background: var(--pista-paper);
  color: var(--pista-ink);
  border-width: 0.75rem;
}

/* Fila finita (`disabled`) o a riposo dopo lo «Stop» (`aria-disabled`,
   che tiene il fuoco dove la tastiera l'ha lasciato): carta, bordo pieno e
   sottile, sbiadito — non il tratteggio, che è del «non ancora salvato». */
.stopwatch-body .stopwatch-go:disabled,
.stopwatch-body .stopwatch-go[aria-disabled="true"],
.stopwatch-body .stopwatch-go[aria-disabled="true"]:hover {
  background: var(--pista-paper);
  color: var(--pista-ink);
  border-width: 1px;
  opacity: 0.4;
  cursor: default;
}

.stopwatch-next {
  grid-area: next;
  margin: 0;
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  min-width: 0;
  font: 600 var(--t-5) / 1.2 var(--font-sans);
}

.stopwatch-next [data-next-name] {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* A fila finita la riga dice chi manca, per direzione: va a capo, fino a
   tre righe, e i nomi che non ci stanno si trovano nel «Tentativo in più». */
.stopwatch-next.is-missing [data-next-name] {
  white-space: normal;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  font-size: var(--t-4);
}

.stopwatch-controls {
  grid-area: controls;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-2);
}

/* La coda: il tetto, e dentro si scorre. È l'unica parte della schermata
   che scorre, e c'è solo quando un tempo non è ancora sul server. */
.stopwatch-queue {
  grid-area: queue;
  max-height: 22vh;
  max-height: 22dvh;
  overflow-y: auto;
}

.stopwatch-queue-head {
  margin: 0 0 var(--space-2);
  font: 700 var(--t-3) / 1.3 var(--font-sans);
}

/* Le voci della coda anche nella pagina del giorno, dentro il suo avviso:
   il bordo è del colore del testo intorno, qualunque sia. */
.stopwatch-queued-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-2);
}

.stopwatch-queued {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1) var(--space-2);
  padding: var(--space-2);
  border: 2px dashed currentColor;
  border-radius: var(--radius-sm);
  font: 600 var(--t-3) / 1.3 var(--font-sans);
}

.stopwatch-queued.is-refused {
  border: 4px double currentColor;
}

.stopwatch-queued-text {
  font-variant-numeric: tabular-nums;
}

.stopwatch-queued-state {
  font-weight: 700;
}

.stopwatch-dismiss {
  margin-left: auto;
}

.stopwatch-awake {
  grid-area: awake;
  margin: 0;
  font: 600 var(--t-2) / 1.3 var(--font-sans);
}

/* Il tentativo in più: sopra tutto, un nome per riga, alto un tocco da
   guanti. Si mostra la sola passata in corso. */
.stopwatch-picker {
  position: fixed;
  inset: 0;
  z-index: 10;
  box-sizing: border-box;
  overflow-y: auto;
  display: grid;
  align-content: start;
  gap: var(--space-3);
  padding: var(--pista-gutter);
  padding-top: calc(var(--pista-gutter) + env(safe-area-inset-top, 0px));
  padding-bottom: calc(var(--pista-gutter) + env(safe-area-inset-bottom, 0px));
  background: var(--pista-paper);
  color: var(--pista-ink);
}

.stopwatch-picker-head {
  position: sticky;
  top: calc(-1 * var(--pista-gutter));
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: calc(-1 * var(--pista-gutter));
  padding: var(--pista-gutter) 0 var(--space-2);
  background: var(--pista-paper);
}

.stopwatch-picker-title {
  margin: 0;
  font: 700 clamp(2rem, 1rem + 6vmin, 3rem) / 1.05 var(--font-cond);
}

.stopwatch-pass-hint {
  margin: 0 0 var(--space-2);
  font: 600 var(--t-4) / 1.3 var(--font-sans);
}

.stopwatch-picks {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-2);
}

.stopwatch-body .stopwatch-pick {
  width: 100%;
  min-height: var(--pista-target);
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border: 2px solid var(--pista-ink);
  border-radius: var(--radius-sm);
  background: var(--pista-paper);
  color: var(--pista-ink);
  font: 700 var(--t-5) / 1.1 var(--font-cond);
  text-align: left;
  touch-action: manipulation;
}

.stopwatch-body .stopwatch-pick:hover {
  background: var(--pista-paper);
  border-color: var(--pista-ink);
}

/* Chi la fila chiamerebbe adesso: il bordo spesso a sinistra. */
.stopwatch-body .stopwatch-pick.is-current {
  border-left-width: 0.75rem;
}

.stopwatch-pick-best {
  flex: none;
  font: 600 var(--t-4) / 1.1 var(--font-sans);
  font-variant-numeric: tabular-nums;
}

/* In orizzontale il bottone va a destra, alto quanto la schermata: un
   telefono girato è basso, e sotto il resto non ne resterebbe. */
@media (orientation: landscape) and (min-aspect-ratio: 4/3) {
  .stopwatch {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-rows: auto auto auto auto auto auto minmax(0, 1fr) auto;
    grid-template-areas:
      "where    exit"
      "runner   go"
      "clock    go"
      "result   go"
      "next     go"
      "controls go"
      "queue    go"
      "awake    go";
  }

  .stopwatch .pista-exit {
    justify-self: end;
  }

  .stopwatch-queue {
    max-height: none;
  }
}

/* --- (issue #107) La spunta dello Speed Lap Challenge, e il cronometro da
   Oggi ----------------------------------------------------------------------
   La casella sta nel form della singola voce, su ogni voce che non è una
   partita. In «Aggiungi una voce» e nella modifica si nasconde finché il
   tipo scelto non è un allenamento: la regola è della voce
   (`CalendarEntry::set_kind`, `mark_speed_lap_challenge`), questo è solo il
   form che non la offre dove non vale. Senza `:has` resta visibile, e su un
   altro tipo la voce non la porta comunque. */
.calendar-entry-form:has(select[name="kind"] option[value="training"]:not(:checked)) .speed-lap-choice {
  display: none;
}

.speed-lap-choice .field-hint {
  margin-top: 0;
}

/* Nell'elenco la voce lo dice a parole, e la parola non va a capo a metà. */
.speed-lap-entry-mark {
  white-space: nowrap;
}

/* Nei prossimi allenamenti di Oggi sta dopo l'orario, fuori dal luogo:
   `.upcoming-place` è la colonna che cede per prima, coi puntini, e con un
   luogo lungo la spunta sparirebbe. Questa non cede. */
.upcoming-mark {
  flex: none;
  white-space: nowrap;
  font: var(--type-caption);
  font-weight: 600;
}

/* --- (issue #108) L'esercizio del challenge, fisso nella seduta -----------
   La riga lo dice a parole, in occhiello e in inchiostro — forma e testo,
   nessun colore: il colore della scheda è dei temi, e non è il segno di
   «fissato» (`.fixed-mark`), che vuol dire «corretto a mano, non segue più il
   calendario», il contrario di questa riga. «Apri il cronometro» è un bottone
   da guanti come in Oggi. */
.exercise-fixed-note {
  margin: 0 0 var(--space-2);
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
}

.exercise-stopwatch {
  min-height: 3rem;
}

.exercise-actions .field-hint {
  margin: 0;
}

/* --- (issue #112) «L'anno prossimo» sul tesseramento ----------------------
   Due tendine sulla stessa riga, come `.field-row`: sono una domanda sola —
   che cosa si sa, e perché. Il motivo sparisce finché la risposta è
   «Continua», che è il valore di partenza: accanto a lei non vuol dire
   niente, e una tendina in più su ogni scheda si riempirebbe per riflesso.
   Il `<select>` nascosto il suo valore lo manda lo stesso: a scartarlo è il
   dominio (`Player::set_next_season`), non questa regola — qui si toglie
   solo una domanda che non c'è. */
.next-season-fields {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.next-season-fields > label {
  flex: 1 1 12rem;
}

.next-season-fields:has(select[name="next_season"] option[value="continues"]:checked) .next-season-reason {
  display: none;
}
/* --- (issue #113) «Quanti siamo»: l'organico in testa al roster -----------
   Il riquadro dell'idea A di `docs/proposta-organico/`: il termometro delle
   due soglie, i quattro conti, le linee con i buchi al posto giusto.

   I nomi sono prefissati `strength-` apposta: `.count`, `.slot`, `.legend` e
   `.thermo` in un foglio da diciassettemila righe sono nomi che qualcuno
   riuserà. Restano senza prefisso le sole `.verdict`/`.v-*`, che sono la
   pastiglia del verdetto e che la tabella di società (#114) includerà tale e
   quale — una pastiglia sola per tutta la sezione.

   Il colore non porta mai da solo il significato: la parola c'è sempre (la
   pastiglia dice «Al limite», la nota dice «manca 1 dietro»), e ogni casella
   ha il suo `title`. */
.strength-card {
  margin-top: 0;
}

.strength-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2) var(--space-3);
}

.strength-head h2 {
  margin: 0;
}

.strength-sub {
  margin: var(--space-1) 0 var(--space-4);
  color: var(--color-text-muted);
  font: var(--type-caption);
  max-width: 68ch;
}

/* Il termometro: le quattro soglie su una riga sola, tutte in tesserati. */
.strength-thermo {
  margin: 0 0 var(--space-4);
}

.strength-thermo-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-1) var(--space-3);
  margin-bottom: var(--space-1);
}

.strength-now {
  font: var(--type-figure-sm);
}

.strength-now small {
  font: var(--type-caption);
  color: var(--color-text-muted);
  margin-left: 0.35rem;
}

.strength-margins {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* Margine zero o già sotto: la riga si accende, e intanto dice il numero. */
.strength-margins.is-alarm {
  color: var(--color-danger-text);
  font-weight: 600;
}

.strength-thermo-track {
  position: relative;
  height: 1.5rem;
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  box-shadow: inset 0 0 0 1px var(--color-border);
}

.strength-thermo-fill {
  position: absolute;
  inset: 0 auto 0 0;
  border-radius: var(--radius-sm);
  background: var(--color-primary);
}

.strength-thermo-fill.is-under {
  background: var(--color-danger-border);
}

/* (17.09.2026) La coda dei tesserati in prova, dentro la barra: il bordo
   sinistro è dove arriverebbe la squadra senza di loro. Tratteggiata e
   pallida perché è la stessa parola che il posto in pista dice col suo
   anello — provvisorio —, e le tacche si disegnano dopo nel markup, quindi
   restano sopra. */
.strength-thermo-trial {
  position: absolute;
  top: 0;
  bottom: 0;
  border-radius: var(--radius-sm);
  background: var(--color-primary-soft);
  border: 1px dashed var(--color-primary-dark);
}

.strength-thermo-mark {
  position: absolute;
  top: -0.3rem;
  bottom: -0.3rem;
  width: 2px;
  border-radius: 2px;
  background: var(--color-text-muted);
}

/* La tacca del referto è l'unica che decide se si gioca: più spessa. */
.strength-thermo-mark.is-stop {
  width: 3px;
  background: var(--color-danger-border);
}

.strength-thermo-scale {
  position: relative;
  height: 2.6rem;
  margin-top: 0.25rem;
  font-size: 0.72rem;
  color: var(--color-text-muted);
}

.strength-thermo-scale b {
  position: absolute;
  transform: translateX(-50%);
  text-align: center;
  line-height: 1.15;
  font-weight: 400;
  white-space: nowrap;
}

.strength-thermo-scale b em {
  display: block;
  font-style: normal;
  font: var(--type-label);
  font-family: var(--font-cond);
  color: var(--color-text);
}

.strength-thermo-scale b.is-stop em {
  color: var(--color-danger-text);
}

/* L'ultima tacca è il tetto, e sta al 100%: la sua etichetta si appende a
   sinistra invece di stare a cavallo, o esce dal riquadro — e sul telefono
   esce dalla pagina, che poi scorre di lato tutta intera. */
.strength-thermo-scale b.is-last {
  transform: translateX(-100%);
  text-align: right;
}

/* Le tacche a parole: da 900px in su i nomi stanno sotto la barra, e questa
   riga non si vede. Vedi il blocco stretto in fondo. */
.strength-scale-key {
  display: none;
}

/* I quattro conti. Non sommano al totale, ed è voluto: i ruoli si
   sovrappongono, e la riga sotto il numero lo dice. */
.strength-counts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: var(--space-2);
  margin: 0 0 var(--space-4);
}

.strength-count {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: 0.55rem 0.7rem;
  background: var(--color-surface);
}

.strength-count-label {
  display: block;
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.strength-count-value {
  display: block;
  font: var(--type-figure);
  margin: 0.15rem 0 0;
}

.strength-count-value small {
  font: var(--type-caption);
  color: var(--color-text-muted);
  margin-left: 0.3rem;
}

.strength-count-sub {
  display: block;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.strength-count.warn {
  border-color: var(--color-warning-border);
  background: var(--color-warning-bg);
}

.strength-count.warn .strength-count-label,
.strength-count.warn .strength-count-value,
.strength-count.warn .strength-count-sub {
  color: var(--color-warning-text);
}

.strength-count.alarm {
  border-color: var(--color-danger-border);
  background: var(--color-danger-bg);
}

.strength-count.alarm .strength-count-label,
.strength-count.alarm .strength-count-value,
.strength-count.alarm .strength-count-sub {
  color: var(--color-danger-text);
}

.strength-mini {
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin: 0 0 var(--space-2);
}

/* Le linee: un posto per ogni casella, e i buchi al posto giusto.
   Quattro celle per riga — nome, i posti dietro, i posti davanti, la nota —
   e quattro tracce nella griglia: chi ne aggiunge una le aggiunge in tutte e
   due (`partials/squad_strength_card.html`). */
.strength-lineups {
  display: grid;
  gap: 0.45rem;
}

.strength-lineup {
  display: grid;
  grid-template-columns: 5.2rem max-content max-content 1fr;
  align-items: center;
  gap: 0.5rem 1rem;
}

.strength-lineup-name {
  font: var(--type-label);
  color: var(--color-text-muted);
}

.strength-lineup-note {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.strength-lineup-note.is-bad {
  color: var(--color-danger-text);
}

.strength-lineup-sep {
  height: 1px;
  background: var(--color-border);
  margin: 0.35rem 0;
}

.strength-slots {
  display: flex;
  gap: 0.3rem;
}

.strength-slot {
  width: 2rem;
  height: 2rem;
  border-radius: var(--radius-sm);
  display: grid;
  place-items: center;
  font: 700 0.78rem/1 var(--font-cond);
  letter-spacing: 0.04em;
}

.strength-slot-full {
  background: var(--color-primary);
  color: var(--color-primary-contrast);
}

.strength-slot-jolly {
  background: var(--color-primary-soft);
  color: var(--color-primary-dark);
  border: 1px solid var(--color-primary);
}

/* L'ospite è a righe, non di un altro colore: il colore qui dice «c'è / non
   c'è», e un ospite c'è. */
.strength-slot-guest {
  background: repeating-linear-gradient(
    135deg,
    var(--color-primary) 0 5px,
    var(--color-primary-dark) 5px 10px
  );
  color: var(--color-primary-contrast);
}

/* Il giocatore di movimento che tiene la porta: c'è, ma non dove sembra. */
.strength-slot-cover {
  background: var(--color-primary-soft);
  color: var(--color-primary-dark);
  border: 1px dashed var(--color-primary);
}

.strength-slot-hurt {
  background: var(--color-surface);
  border: 1px solid var(--color-primary);
  color: var(--color-primary-dark);
  opacity: 0.55;
}

.strength-slot-gap {
  background: var(--color-danger-bg);
  border: 1px solid var(--color-danger-border);
  color: var(--color-danger-text);
}

.strength-slot-ideal {
  border: 1px dashed var(--color-border);
  color: var(--color-text-muted);
}

.strength-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.1rem 1.1rem;
  margin: var(--space-3) 0 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.strength-legend span {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}

.strength-legend i {
  width: 0.9rem;
  height: 0.9rem;
  border-radius: 4px;
  display: inline-block;
  flex: none;
}

.strength-spare,
.strength-note {
  font: var(--type-caption);
  color: var(--color-text-muted);
  margin: var(--space-2) 0 0;
  max-width: 78ch;
}

.strength-note b {
  color: var(--color-text);
}

/* (17.09.2026) «Chi puoi chiamare»: una riga di testo, non un elenco di
   nomi — la telefonata la fa l'allenatore, e qui bastano quanti sono e se
   quel giorno sono liberi. Il filetto a sinistra la stacca dalla
   formazione senza farne un avviso: non è un allarme, è una possibilità. */
.strength-cover {
  margin: var(--space-3) 0 0;
  padding-left: var(--space-3);
  border-left: 2px solid var(--color-primary-soft);
}

.strength-cover h3 {
  margin: 0 0 0.15rem;
}

.strength-cover p {
  margin: 0;
  font: var(--type-caption);
  color: var(--color-text);
  max-width: 78ch;
}

/* --- Il verdetto: la parola sempre, il colore solo a rinforzo ------------
   Sei pastiglie per cinque verdetti più «non si applica». *Completa* è di
   contorno e spenta — un badge solo se è un'eccezione, come lo stato dei
   pronti nel catalogo —, *Troppi* porta la tinta dell'accento e non l'ambra:
   non è un allarme, è un fatto da gestire. */
.verdict {
  display: inline-block;
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  padding: 0.3rem 0.55rem;
  border-radius: var(--radius-pill);
  white-space: nowrap;
}

.v-full {
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  color: var(--color-text-muted);
}

.v-limit {
  border: 1px solid var(--color-warning-border);
  background: var(--color-warning-bg);
  color: var(--color-warning-text);
}

.v-short {
  border: 1px solid var(--color-danger-border);
  background: var(--color-danger-bg);
  color: var(--color-danger-text);
}

.v-stop {
  border: 1px solid var(--color-danger-border);
  background: var(--color-danger-border);
  color: #fff;
}

.v-many {
  border: 1px solid var(--color-primary);
  background: var(--color-primary-soft);
  color: var(--color-primary-dark);
}

.v-none {
  border: 1px dashed var(--color-border);
  background: transparent;
  color: var(--color-text-muted);
}

/* Sotto 900px, come il resto del foglio: la riga delle linee diventa due
   celle — i posti sotto il nome, la nota sotto i posti —, e le caselle non
   si stringono, perché sotto i 2rem la lettera non si legge più. Il
   termometro resta largo quanto la pagina: è una riga sola, e stringerla
   ammucchierebbe le quattro etichette. */
@media (max-width: 900px) {
  /* Le quattro tracce della riga larga qui non ci stanno, e non basta
     spezzarle in due: una traccia `max-content` si allarga fino a contenere
     su una riga sola anche la nota che la attraversa, e la pagina intera
     finiva per scorrere di lato. In colonna stretta la riga è una fila che va
     a capo — nome, i posti, la nota —, e il testo torna a capo come deve. */
  .strength-lineup {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.3rem 0.6rem;
  }

  .strength-lineup-name,
  .strength-lineup-note {
    flex: 0 0 100%;
  }

  .strength-counts {
    grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
  }

  /* Sotto la barra restano i soli numeri: «le linee minime» e «comodi»
     distano due tesserati, cioè trentacinque pixel, e i due nomi si
     toccherebbero. I nomi si leggono nella riga qui sotto, che è lo stesso
     fatto detto una volta sola per larghezza. */
  .strength-mark-name {
    display: none;
  }

  .strength-thermo-scale {
    height: 1.2rem;
  }

  .strength-scale-key {
    display: block;
    font: var(--type-caption);
    color: var(--color-text-muted);
    margin: 0;
  }
}

/* --- (issue #117) «L'anno prossimo, per ruolo» in anagrafica --------------
   Il riquadro dell'idea E di `docs/proposta-organico/`: sotto il grafico
   delle annate, una piastrella per categoria con portieri, difesa e attacco
   dell'anno prossimo e la tacca del minimo di stagione.

   I conti li fa `society_census::census`, le larghezze `census_views::
   roles_card`. Il colore non porta mai da solo il significato: il valore
   («0 / 2») c'è sempre, la pastiglia dice la parola, e ogni riga ha il suo
   `title`. La tacca è un segno, non una tinta — e dove il minimo non c'è
   non c'è nemmeno la traccia, perché una scala inventata direbbe una cosa
   che nessuno ha impostato. */
.census-roles-grid {
  display: grid;
  gap: 0.7rem;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
}

.census-role-cat {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.35rem;
  padding: 0.7rem 0.8rem;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
}

.census-role-name {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 0.45rem;
  margin: 0;
  font: var(--type-body-strong);
}

.census-role-name i {
  width: 10px;
  height: 10px;
  border-radius: 3px;
  background: var(--census-tint, transparent);
}

.census-role-name small {
  font: var(--type-eyebrow);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.census-role-flow {
  margin: 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}

.census-role-flow b {
  font: var(--type-body-strong);
  color: var(--color-text);
}

.census-role-bars {
  display: grid;
  gap: 0.25rem;
  width: 100%;
  margin: 0.15rem 0 0.1rem;
}

/* Tre colonne fisse: il nome, la traccia, il valore. La traccia c'è anche
   vuota (categoria senza minimo), o il valore ballerebbe di riga in riga. */
.census-role-bar {
  display: grid;
  grid-template-columns: 4.4rem minmax(0, 1fr) max-content;
  align-items: center;
  gap: 0.5rem;
  font: var(--type-caption);
  font-variant-numeric: tabular-nums;
}

.census-role-label {
  color: var(--color-text-muted);
}

.census-role-track {
  position: relative;
  height: 0.55rem;
  border-radius: var(--radius-pill);
  background: var(--color-border);
}

/* Senza traccia dentro non c'è niente da misurare: il binario sparisce
   invece di sembrare una barra a zero. */
.census-role-track:empty {
  background: none;
}

.census-role-fill {
  display: block;
  height: 100%;
  border-radius: var(--radius-pill);
  background: var(--color-primary);
}

/* La tacca del minimo: un segno d'inchiostro che attraversa la traccia, più
   alto di lei perché si veda anche sopra la parte piena. C'è solo dove cade
   **dentro** la traccia (`census_views::role_bar`), cioè quando ce n'è più
   del minimo: alla fine della traccia sembrava un separatore fra la barra e
   il numero, e non diceva niente che il vuoto non dicesse già. */
.census-role-need {
  position: absolute;
  top: -2px;
  bottom: -2px;
  width: 1px;
  margin-left: -1px;
  background: var(--color-text-muted);
}

.census-role-bar.is-warn .census-role-fill {
  background: var(--color-warning-border);
}

.census-role-bar.is-warn .census-role-value {
  color: var(--color-warning-text);
}

.census-role-bar.is-alarm .census-role-fill {
  background: var(--color-danger-border);
}

.census-role-bar.is-alarm .census-role-value {
  color: var(--color-danger-text);
  font-weight: 600;
}

.census-role-why,
.census-role-from {
  margin: 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* =========================================================================
   (issue #114) La sezione «Organici» e la pagina *Adesso*
   `docs/proposta-organico/pagina-adesso.html` + `organici.css`.

   Le pastiglie del verdetto (`.verdict`/`.v-*`) stanno già più su, dalla
   #113, e qui si riusano tali e quali: una sola per tutta la sezione. I nomi
   nuovi sono prefissati `organici-`, `org-`, `prio-`… tranne `.panel`,
   `.strip` e `.verdict-key`, che vengono dal mockup e non hanno un gemello
   nel foglio.

   Il colore non porta mai da solo il significato: il verdetto è una parola,
   la striscia ha un riassunto per chi non la vede e ogni casella il suo
   `title`, e «Regge» dice il numero prima di accendersi.
   ========================================================================= */
.app-main-organici {
  max-width: 110rem;
}

@media (min-width: 1400px) {
  .app-main-organici {
    padding-left: var(--space-5);
    padding-right: var(--space-5);
  }
}

/* --- Le due pagine della sezione ---------------------------------------- */
.organici-tabs {
  display: flex;
  gap: 0.3rem;
  margin: 0 0 var(--space-4);
  border-bottom: 1px solid var(--color-border);
}

.organici-tab {
  display: block;
  padding: 0.55rem 0.9rem;
  text-decoration: none;
  color: var(--color-text-muted);
  font: var(--type-body-strong);
  /* Il filetto della scheda aperta copre il bordo del contenitore: il
     margine negativo lo fa cadere sulla stessa riga di pixel. */
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}

.organici-tab.is-current {
  color: var(--color-primary-dark);
  border-bottom-color: var(--color-primary);
}

.organici-section {
  margin: 0 0 var(--space-5);
}

.organici-section > h2 {
  font: var(--type-title);
  margin: 0 0 0.2rem;
}

.organici-section > .section-sub {
  margin: 0 0 var(--space-3);
  color: var(--color-text-muted);
  font: var(--type-caption);
  max-width: 70ch;
}

/* Il pannello della tabella: una card con la testata su una riga. */
.panel {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
}

.panel-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.4rem 1rem;
  margin-bottom: var(--space-3);
}

.panel-head h2 {
  font: var(--type-title);
  margin: 0;
}

.panel-head .panel-meta {
  color: var(--color-text-muted);
  font: var(--type-caption);
}

.panel-note {
  margin: var(--space-3) 0 0;
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.panel-note b {
  color: var(--color-text);
}

/* --- La legenda dei cinque verdetti ------------------------------------- */
.verdict-key {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 0.9rem;
  align-items: center;
  margin: var(--space-3) 0 0;
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.verdict-key span {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}

/* --- La tabella degli organici ------------------------------------------ */
.org-table {
  width: 100%;
  border-collapse: collapse;
  font: var(--type-body);
}

.org-table th,
.org-table td {
  text-align: left;
  padding: 0.6rem 0.5rem;
  border-bottom: 1px solid var(--color-border);
  vertical-align: middle;
}

.org-table thead th {
  font: var(--type-thead);
  letter-spacing: var(--type-thead-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.org-table td.num,
.org-table th.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.org-table tbody tr:last-child td {
  border-bottom: 0;
}

.org-team {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  /* Nome e sigla restano sulla stessa riga: sono l'identità della riga, e
     «Novizi» sopra «U16» si legge come due cose. La colonna si allarga da sé
     — è una tabella —, e sotto 900px la riga è una carta e questa regola non
     morde. */
  white-space: nowrap;
}

/* Il pallino della categoria prende la rampa dell'anagrafica
   (`census-tone-0`…`5`, che mette `--census-tint`): le stesse categorie,
   nello stesso ordine d'età, con le stesse tinte. */
.org-team i {
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 3px;
  flex: none;
  background: var(--census-tint, var(--color-border));
}

.org-team b {
  font-weight: 600;
}

.org-team small {
  color: var(--color-text-muted);
}

.org-verdict-why {
  display: block;
  font: var(--type-caption);
  color: var(--color-text-muted);
  margin-top: 0.15rem;
}

/* Il verdetto sta due volte nel markup — accanto al nome e nella sua colonna
   — e a ogni larghezza se ne vede uno solo: sopra 900px la colonna, sotto la
   carta. Un `<tr>` non può spostare una cella, e due markup diversi per la
   stessa riga sono la cosa che il resto dell'app evita. */
.org-team-verdict {
  display: none;
}

.org-low {
  color: var(--color-danger-text);
  font-weight: 600;
}

.org-cell-nomin {
  color: var(--color-text-muted);
  font: var(--type-caption);
}

/* La striscia: una casella per tesserato, i portieri prima, due tacche. */
.strip {
  display: flex;
  gap: 2px;
  align-items: center;
}

.strip span {
  width: 0.6rem;
  height: 1.1rem;
  border-radius: 2px;
}

.s-keeper {
  background: var(--color-primary-dark);
}

.s-keepergap {
  background: var(--color-danger-border);
}

.s-full {
  background: var(--color-primary);
}

.s-jolly {
  background: var(--color-primary-soft);
  box-shadow: inset 0 0 0 1px var(--color-primary);
}

.s-gap {
  background: var(--color-danger-bg);
  box-shadow: inset 0 0 0 1px var(--color-danger-border);
}

.s-ideal {
  box-shadow: inset 0 0 0 1px #c6cbd3;
}

/* (17.09.2026, la sera) In prova: una **riserva**, e la sua casella sta in
   coda dopo le soglie. Tiene la tinta del suo ruolo — piena, perché una
   persona c'è e un vuoto pallido si confonderebbe col posto che manca — e la
   porta più chiara e più bassa, perché a 0,6rem di larghezza un tratteggio
   non si legge e ciò che si vede a questa misura è il tono e l'altezza.
   L'`opacity` è una regola sola per le tre tinte — qui nessun'altra casella
   è sbiadita, quindi non porta via un significato a nessuno —, e la parola
   la dicono il titolo della casella e la nota sotto la tabella. */
.strip .s-trial {
  height: 0.65rem;
  opacity: 0.45;
}

/* Le due tacche sono caselle anche loro — un ciclo solo nel template — e
   qui perdono la larghezza: sono una riga verticale fra due caselle. */
.strip .stopmark {
  width: 0;
  border-left: 2px solid var(--color-danger-border);
  height: 1.45rem;
  margin: 0 3px;
}

.strip .gapmark {
  width: 0;
  border-left: 1px solid var(--color-text-muted);
  height: 1.35rem;
  margin: 0 3px;
}

/* --- Le tre priorità ----------------------------------------------------- */
.prio {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.5rem;
}

.prio li {
  display: grid;
  grid-template-columns: 1.5rem 1fr max-content;
  gap: 0.75rem;
  align-items: baseline;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: 0.65rem 0.8rem;
  background: var(--color-surface);
}

.prio .rank {
  font: var(--type-figure-sm);
  color: var(--color-text-muted);
}

.prio p {
  margin: 0;
}

.prio small {
  display: block;
  color: var(--color-text-muted);
  font: var(--type-caption);
  margin-top: 0.15rem;
}

/* Il filetto a sinistra, non un fondo: la gravità è già nelle parole, e tre
   riquadri rossi pieni di fila sarebbero un allarme antincendio. `soon` è la
   terza famiglia, «chi lo sarà l'anno prossimo», e la costruirà la
   proiezione (#115). */
.prio li.alarm {
  border-left: 3px solid var(--color-danger-border);
}

.prio li.warn {
  border-left: 3px solid var(--color-warning-border);
}

.prio li.soon {
  border-left: 3px solid var(--color-border);
}

/* =========================================================================
   (issue #114) Sotto 900px — la stessa `<table>`, a carte
   ========================================================================= */
@media (max-width: 900px) {
  /* Come l'anagrafica e la pagina per area: nessun secondo markup, e
     l'ordine delle celle non cambia — e con lui non cambia niente per chi
     legge con uno screen reader. */
  .org-table,
  .org-table tbody,
  .org-table tr,
  .org-table td {
    display: block;
  }

  .org-table thead {
    display: none;
  }

  .org-table tr {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: 0.7rem 0.8rem;
    margin-bottom: 0.6rem;
    background: var(--color-surface);
  }

  .org-table td {
    border: 0;
    padding: 0.15rem 0;
  }

  /* Il nome della colonna torna davanti al numero: senza la testata, «2» da
     solo non vuol dire niente. */
  .org-table td[data-label]::before {
    content: attr(data-label) " ";
    font: var(--type-eyebrow);
    letter-spacing: var(--type-eyebrow-ls);
    text-transform: uppercase;
    color: var(--color-text-muted);
    margin-right: 0.4rem;
  }

  .org-table td.org-cell-team {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.4rem;
  }

  .org-table td.num {
    display: inline-block;
    margin-right: 1rem;
    text-align: left;
    white-space: nowrap;
  }

  .org-table td.org-cell-regge {
    display: block;
    margin-top: 0.35rem;
  }

  .org-table .org-team-verdict {
    display: inline-block;
  }

  .org-table .org-cell-verdict .verdict {
    display: none;
  }

  .org-table .org-cell-verdict {
    margin-top: 0.2rem;
  }

  .org-table td.org-cell-action {
    margin-top: 0.5rem;
  }

  .org-table td.org-cell-action .btn {
    width: 100%;
    text-align: center;
  }
}

@media (max-width: 560px) {
  /* Il pulsante non sta più sulla riga del testo: va sotto, nella colonna
     del testo, e non sotto il numero d'ordine. */
  .prio li {
    grid-template-columns: 1.4rem 1fr;
  }

  .prio li > .btn {
    grid-column: 2;
    justify-self: start;
    margin-top: 0.4rem;
  }
}

/* =========================================================================
   (issue #115) La pagina *Organici → Proiezione*
   `docs/proposta-organico/pagina-proiezione.html` + `organici.css`.

   Un grafico solo: una tabella categorie × stagioni col numero dentro. Due
   forme sono state buttate e non tornano — la griglia di barre annate ×
   stagioni e la banda continua per annata —, perché ogni annata fa lo
   stesso identico percorso spostato di un anno: se lungo un asse non cambia
   niente, quell'asse non serve. `proposta.md` §«Un grafico solo, dopo due
   tentativi» lo spiega per intero.

   Il colore non porta mai da solo il significato: la cella scrive «ne
   mancano 4», il contorno ha la sua riga di legenda, e le due soglie sono
   scritte a parole sotto la tabella.

   `.prio` («Chi devi trovare») e `.panel` sono quelli di *Adesso*: una
   serie sola per tutta la sezione.
   ========================================================================= */
.proj-scroll {
  overflow-x: auto;
  overscroll-behavior-x: contain;
}

/* `separate` con `border-spacing`: le celle sono piastrelle staccate, e il
   contorno tratteggiato di una non deve toccare quello della vicina.

   `table-layout: fixed`: le stagioni sono colonne **uguali**, a 4, 7 e 10
   — tolta la colonna delle categorie, il resto si divide in parti uguali,
   e le colonne in `display: none` dell'orizzonte non contano. Senza, una
   colonna era larga quanto la sua parola più lunga («+2 per livello
   comodo» contro «livello comodo»), e la tabella a quattro stagioni si
   fermava a metà pannello. */
.proj-table {
  border-collapse: separate;
  border-spacing: 3px;
  width: 100%;
  table-layout: fixed;
}

.proj-table thead th:first-child {
  width: 9rem;
  text-align: left;
}

.proj-table th {
  font: var(--type-thead);
  letter-spacing: var(--type-thead-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
  padding: 0 0 0.2rem;
}

/* La colonna di oggi in inchiostro pieno: è l'unica di cui si sa qualcosa
   senza proiettare niente. */
.proj-table thead th.now {
  color: var(--color-text);
}

.proj-table th[scope="row"] {
  text-align: left;
  font: var(--type-body-strong);
  letter-spacing: 0;
  text-transform: none;
  color: var(--color-text);
  white-space: nowrap;
  padding-right: 0.6rem;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  height: 3.6rem;
}

.proj-table th[scope="row"] small {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* Lo stesso pallino di `.org-team i`, con la rampa dell'anagrafica: le
   stesse categorie, nello stesso ordine d'età, con le stesse tinte. */
.proj-dot {
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 3px;
  flex: none;
  background: var(--census-tint, var(--color-border));
}

/* Un'altezza sola per tutte le celle, a ogni orizzonte: il numero e due
   righe di parola. A dieci stagioni «+2 per livello comodo» va a capo
   **dentro** la cella, e la riga non cresce. Il numero sta in alto, alla
   stessa altezza in tutta la riga — si legge per righe, e l'occhio scorre
   sui numeri, che la parola sotto sia su una riga o su due. */
.proj-table td {
  height: 3.6rem;
  text-align: center;
  vertical-align: top;
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  box-shadow: inset 0 0 0 1px var(--color-border);
  font-variant-numeric: tabular-nums;
  padding: 0.5rem 0.4rem 0.2rem;
}

.proj-table td b {
  display: block;
  font: var(--type-body-strong);
  font-size: var(--t-4);
  line-height: 1.1;
}

/* La parola nella scala del foglio. Il corpo fuori scala di 0,66rem della
   #115 esisteva perché dodici colonne dovevano stare in 1440px: con dieci
   colonne uguali e la cella alta due righe non serve più. */
.proj-table td small {
  display: block;
  font: 400 var(--t-1) / 1.15 var(--font-sans);
  overflow-wrap: anywhere;
}

.proj-table td.p-warn {
  background: var(--color-warning-bg);
  box-shadow: inset 0 0 0 1px var(--color-warning-border);
  color: var(--color-warning-text);
}

.proj-table td.p-stop {
  background: var(--color-danger-bg);
  box-shadow: inset 0 0 0 1px var(--color-danger-border);
  color: var(--color-danger-text);
}

/* Tratteggiato = si riempie ancora reclutando fra i piccoli: è un compito,
   non un fatto. Pieno = chi manca è già grande, e la leva vera diventa non
   perdere chi c'è. */
.proj-table td.p-open {
  background: transparent;
  box-shadow: none;
  border: 1px dashed currentColor;
}

/* La categoria che non si misura, e la riga di chi comincia negli anni
   futuri: niente fondo, niente contorno, niente da guardare. */
.proj-table td.p-none {
  background: transparent;
  box-shadow: none;
  color: var(--color-text-muted);
  vertical-align: middle;
  padding-top: 0.2rem;
}

.proj-table td.p-none em {
  font-style: normal;
  font-size: var(--t-2);
}

.proj-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1.1rem;
  margin: var(--space-3) 0 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.proj-legend span {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

.proj-legend i {
  width: 1.1rem;
  height: 0.8rem;
  border-radius: 3px;
  display: inline-block;
  flex: none;
}

.proj-legend i.warn {
  background: var(--color-warning-bg);
  box-shadow: inset 0 0 0 1px var(--color-warning-border);
}

.proj-legend i.stop {
  background: var(--color-danger-bg);
  box-shadow: inset 0 0 0 1px var(--color-danger-border);
}

.proj-legend i.dash {
  background: transparent;
  border: 1px dashed currentColor;
}

/* =========================================================================
   (issue #115) Sotto 900px — la tabella scorre, la categoria resta ferma
   ========================================================================= */
@media (max-width: 900px) {
  /* Come le tabelle dello Speed Lap: dodici colonne non stanno in 390px, e
     stringerle le renderebbe illeggibili. Lo scroller esce dal padding del
     pannello, o la prima e l'ultima colonna sembrerebbero tagliate. */
  .proj-scroll {
    margin-inline: calc(-1 * var(--space-4));
    padding-inline: var(--space-4);
  }

  .proj-scroll::after {
    content: "scorri per vedere gli anni →";
    display: block;
    font: var(--type-caption);
    color: var(--color-text-muted);
    padding-top: 0.5rem;
  }

  .proj-table {
    min-width: max-content;
  }

  /* La colonna delle categorie resta ferma: senza, scorrendo non si sa più
     di quale riga si stia leggendo il numero. Anche l'angolo, o la tabella
     terrebbe le etichette di riga e perderebbe quella di colonna.

     Il `box-shadow` non è un'ombra: è un velo. Lo scroller ha un padding
     laterale (esce dal pannello per non tagliare la prima e l'ultima
     colonna) e `left: 0` si aggancia al suo bordo **interno**: senza il
     velo, in quella striscia si vedrebbero scorrere i numeri a sinistra
     del nome. Offset negativo, nessuna sfocatura e nessuna estensione —
     una copia della cella spostata a coprire esattamente quella striscia. */
  .proj-table th[scope="row"],
  .proj-table thead th:first-child {
    position: sticky;
    left: 0;
    background: var(--color-surface);
    box-shadow: calc(-1 * var(--space-4)) 0 0 0 var(--color-surface);
    z-index: 1;
  }

  .proj-table td {
    min-width: 3.1rem;
  }
}

/* =========================================================================
   «Chi non puoi perdere», per gruppi (19.09.2026,
   `docs/proposta-chi-non-puoi-perdere/`, proposta B)

   Una **scheda per gruppo di lavoro**, non una riga per annata: il titolo è
   la squadra che il gruppo tiene, il numero grande è quanti sono, e la
   prima riga dice a parole che cosa costa perderne **uno** — il colore non
   porta mai da solo il significato. Rosso per chi tiene in piedi una
   squadra (uno in meno e non si gioca), grigio per chi è da tenere
   d'occhio. I nomi stanno chiusi: **nessun numero accanto a un nome**, e
   dentro un gruppo nessuno vale più di un altro.

   La griglia `auto-fill` non ha `@media`: sul telefono le schede vanno una
   sotto l'altra da sole.
   ========================================================================= */
.retain-grid {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: repeat(auto-fill, minmax(min(17rem, 100%), 1fr));
  align-items: start;
}

.retain-card {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-bg);
  padding: var(--space-3) var(--space-4);
  display: grid;
  gap: 0.3rem;
}

.retain-card > * {
  margin: 0;
}

.retain-stop {
  background: var(--color-danger-bg);
  border-color: var(--color-danger-border);
}

.retain-stake {
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.retain-stop .retain-stake,
.retain-stop .retain-figure {
  color: var(--color-danger-text);
}

.retain-figure {
  font: var(--type-figure);
  color: var(--color-text-muted);
}

.retain-figure small {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.retain-title {
  font: var(--type-title);
  margin-top: 0.2rem;
}

.retain-cohorts,
.retain-extra {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.retain-sentence {
  font: var(--type-body);
}

.retain-doubt {
  font: var(--type-caption);
  color: var(--color-warning-text);
}

.retain-who summary {
  cursor: pointer;
  font: var(--type-caption);
  color: var(--color-primary-dark);
}

.retain-who .watch-names {
  margin: 0.35rem 0 0;
  font: var(--type-caption);
  color: var(--color-text);
}

/* Chi non continua: barrato **e** detto a parole. La griglia l'ha già
   tolto, e il suo posto è diventato un buco fra quelli da trovare. */
.watch-names .gone {
  color: var(--color-text-muted);
  text-decoration: line-through;
}

.watch-names .gone b,
.watch-names .maybe b {
  text-decoration: none;
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  margin-left: 0.2rem;
}

.watch-names .gone b {
  color: var(--color-danger-text);
}

.watch-names .maybe b {
  color: var(--color-warning-text);
}

/* È una lista di minorenni con accanto quanto pesano per la società: **non
   esce dalla pagina**. Niente esportazione, niente link che la porti fuori,
   e niente foglio da lasciare sul tavolo di una riunione. */
@media print {
  .organici-watch {
    display: none;
  }
}

/* ==========================================================================
   La scheda della categoria — `category_form.html`
   (`docs/proposta-categoria/`, mockup approvato dall'utente il 17.09.2026)

   Era una colonna sola da 40rem con cinque `<fieldset>` impilati e un
   aiuto di quattro-cinque righe ciascuno: su un monitor restavano vuoti
   mille pixel ai lati, e i due gruppi che si leggono insieme —
   l'ammissione e i quattro minimi di stagione — non stavano mai nella
   stessa schermata.

   Principio: **card affiancate**, come la scheda dell'esercizio (v1.81),
   stesso pattern visivo di `.editor-col` e non uno nuovo. L'ordine del
   markup — generale → ammissione → minimi → generatore → temi — non
   cambia mai: a cambiare, per larghezza, è solo `grid-template-areas`,
   così stampa e lettori di schermo leggono quello. Sotto 900px nessuna
   regola di questa sezione si applica: la pagina resta una colonna
   nell'ordine del DOM, come prima.

   Due cose che il mockup dell'utente aveva e qui **non** ci sono, per
   scelta: le tinte azzurro/rosa sulle due fasce d'ammissione (`--fam-*`
   sono le famiglie di temi, e il testo dice già tutto) e la colonna
   «Anteprima categoria», scartata perché ripeteva campi visibili sulla
   stessa schermata — con lei è caduta la terza colonna che l'ospitava.
   ========================================================================== */

/* Il tetto della pagina. Quello del catalogo e della scheda esercizio
   (110rem) qui è eccessivo: questa pagina non ha una colonna di prosa
   lunga né uno schema da disegnare, e a 110rem le due colonne di campi
   diventerebbero larghe e vuote — un `<input>` da 40rem per due cifre
   d'età. La pagina ne ha due, e le vuole di ~30rem l'una: 30 + 30 + un
   gap da 1.5rem (--space-4) = 61.5rem, arrotondato a 64. (Nella prima
   stesura erano 82rem, misurate su una terza colonna — il «Riepilogo» —
   che l'utente ha scartato il 17.09.2026: senza di lei quel tetto
   lasciava due colonne larghe il doppio del necessario.) */
.app-main-category {
  max-width: 64rem;
}

/* Il form resta un form solo — un invio per tutte le card — ma perde la
   sua cornice: `form` in `style.css` è una card (fondo, bordo, raggio,
   padding), e qui la cornice ce l'ha ogni sezione. Senza questa regola le
   card stanno dentro una card, come succedeva alla scheda esercizio prima
   della v1.81 (vedi `form.exercise-editor-frame` lì). */
.category-form {
  background: none;
  border: none;
  border-radius: 0;
  padding: 0;
}

/* --------------------------------------------------------------------
   La griglia delle card. Mobile-first: una colonna, ordine del markup,
   esattamente il comportamento di oggi. Le due soglie sotto aggiungono
   colonne senza mai spostare una sezione fuori dal suo posto nel DOM.
   -------------------------------------------------------------------- */
.category-form-grid {
  display: grid;
  gap: var(--space-4);
  grid-template-areas:
    "generale"
    "ammissione"
    "minimi"
    "generatore"
    "temi"
    "presentazione";
}

.category-generale { grid-area: generale; }
.category-ammissione { grid-area: ammissione; }
.category-minimi { grid-area: minimi; }
.category-generatore { grid-area: generatore; }
.category-temi { grid-area: temi; }
/* (issue #130) La sesta card sta in fondo e a tutta larghezza: è l'unica
   con un testo lungo da scrivere, e una textarea in mezza colonna si
   riempie in tre parole. Ultima anche nel markup — le cinque di prima
   dicono com'è fatta la categoria, questa che cosa se ne racconta. */
.category-presentazione { grid-area: presentazione; }

/* Ogni sezione è una card sua, come `.editor-col` dentro
   `.exercise-editor-with-drill` (v1.81): stesso fondo, stesso bordo,
   stesso raggio, così la pagina si legge come un insieme di riquadri e
   non come un unico modulo lunghissimo. */
.category-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  min-width: 0;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-4);
}

.category-card-title {
  margin: 0;
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--color-border);
  font: var(--type-title);
  color: var(--color-text);
}

/* La nota sotto il titolo non deve staccarsi da lui più del normale
   `.field-hint` (già negativo di --space-2): qui basta azzerare il resto
   del gap della card. */
.category-card > .field-hint:first-of-type {
  margin-top: 0;
}

/* Due colonne di card, senza aside: da qui in su una griglia 2×2 tiene
   tutte e cinque le sezioni editabili senza scorrimento verticale lungo
   su un tablet o un laptop stretto — è la soglia che la vision chiede
   esplicitamente («sotto 900px la pagina resta quella di oggi»), quindi
   qui sopra comincia la revisione desktop.
   Conto di leggibilità: a 900px, senza barra laterale (che compare solo
   da 1024px), il contenuto disponibile è ~900px meno il respiro della
   pagina (2 × var(--space-3) ≈ 32px) ≈ 55rem — abbondante per due
   colonne da ~20rem l'una con un gap di --space-4 (1.5rem) in mezzo
   (41.5rem in tutto). */
@media (min-width: 900px) {
  .category-form-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-areas:
      "generale generale"
      "ammissione minimi"
      "generatore temi"
      "presentazione presentazione";
  }
}

/* --------------------------------------------------------------------
   «Chi può essere tesserato»: due colonne (maschile/femminile) SENZA
   tinta di genere — la proposta dell'utente le coloriva di azzurro e
   rosa, scartato apposta (vedi proposta.md). La distinzione è solo
   nell'intestazione di ciascuna colonna e in un filetto verticale fra le
   due, come due sezioni di una stessa tabella.
   -------------------------------------------------------------------- */
.category-admission-cols {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.category-admission-heading {
  margin: 0 0 var(--space-2);
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  color: var(--color-text);
}

/* Da 480px (stessa soglia di `.editor-col .checkbox-list`, v1.23) le due
   colonne si affiancano: sotto, due gruppi di "Da/A" affiancati ad altri
   due gruppi di "Da/A" sarebbero quattro caselle strette in fila. */
@media (min-width: 480px) {
  .category-admission-cols {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-4);
  }

  .category-admission-group + .category-admission-group {
    border-left: 1px solid var(--color-border);
    padding-left: var(--space-4);
  }
}

/* --------------------------------------------------------------------
   «Quanti ne servono in una stagione»: lo stato attuale (persistito, non
   una convalida — vedi proposta.md) sopra i campi, in un badge muto: né
   verde né ambra, perché «non si applica» non è un avviso, è la scelta
   con cui i Kids nascono.
   -------------------------------------------------------------------- */
.category-minimi-status {
  margin: 0;
}

/* Il fatto — «7 giocatori» o «non nominata» — resta sempre in vista;
   solo il testo di legge esteso va dietro al `<details>` qui sotto. */
.category-referto-figure {
  margin: 0;
  font: var(--type-body-strong);
}

.category-referto-figure .field-hint {
  margin: 0 0 0 var(--space-1);
  display: inline;
  font: var(--type-caption);
}

/* Il minimo del referto: un <details> come «Altri filtri» del catalogo
   (v1.72) — stessa freccia che ruota, stesso bordo sopra —, perché il
   testo del regolamento (`SHEET_LEGEND`/`GOALKEEPER_SWAP_LEGEND`) non si
   riscrive più corto: lo si può solo richiudere finché non serve. */
.category-referto {
  border-top: 1px solid var(--color-border);
  padding-top: var(--space-2);
}

.category-referto > summary {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  cursor: pointer;
  font: var(--type-label);
  letter-spacing: var(--type-label-ls);
  list-style: none;
  color: var(--color-text-muted);
}

.category-referto > summary::-webkit-details-marker {
  display: none;
}

.category-referto > summary::before {
  content: "";
  flex: none;
  width: 7px;
  height: 7px;
  margin: 0 3px;
  border-right: 1.6px solid var(--color-text-muted);
  border-bottom: 1.6px solid var(--color-text-muted);
  transform: rotate(-45deg);
  transition: transform 0.15s ease;
}

.category-referto[open] > summary::before {
  transform: rotate(45deg);
}

.category-referto > .field-hint {
  margin-top: var(--space-2);
}

/* Il bottone sotto le card, allineato a destra su schermi larghi come in
   `.exercise-editor-actions` (v1.81) — stesso gesto, stessa posizione. */
.category-form-actions {
  display: flex;
  justify-content: flex-end;
}

.category-form-actions .btn {
  width: 100%;
}

@media (min-width: 640px) {
  .category-form-actions .btn {
    width: auto;
    min-width: 12rem;
  }
}

/* =========================================================================
   (18.09.2026) *Organici › Proiezione*, seconda stesura
   `docs/proposta-proiezione/proposta.md` + `pagina-proposta.html`.

   Quello che cambia rispetto al blocco della #115 qui sopra: l'orizzonte a
   tre viste (4 · 7 · 10, si parte da 4), il quarto fondo della cella, e le
   due tabelle nuove — «Chi devi trovare» per gruppi di annate, «Chi non
   puoi perdere» come striscia di stagioni.

   Niente JavaScript: l'orizzonte è radio + CSS, come le due stagioni del
   censimento e il foglio del contesto. Il colore non porta mai il
   significato da solo — ogni banda scrive la sua parola nella cella.
   ========================================================================= */

/* --- 1. L'orizzonte: tre chiavi, una sola accesa ----------------------- */
.proj-keys {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  margin: 0 0 var(--space-3);
}

.proj-keys input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.proj-keys label {
  display: inline-flex;
  flex-direction: column;
  gap: 0.05rem;
  padding: 0.4rem 0.75rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-text-muted);
  cursor: pointer;
  white-space: nowrap;
}

.proj-keys label b {
  font: var(--type-body-strong);
  color: var(--color-text);
}

.proj-keys label small {
  font: var(--type-caption);
}

.proj-keys input:checked + label {
  border-color: var(--color-primary);
  box-shadow: inset 0 0 0 1px var(--color-primary);
  color: var(--color-text);
}

.proj-keys input:focus-visible + label {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* Le colonne oltre la scelta spariscono. `nth-child` conta anche la colonna
   delle categorie: 4 stagioni = cinque celle. */
#proiezione:has(#oriz-4:checked) .proj-table :is(th, td):nth-child(n + 6),
#proiezione:has(#oriz-7:checked) .proj-table :is(th, td):nth-child(n + 9) {
  display: none;
}

/* Quello che cambia con l'orizzonte e non è una colonna: la data in fondo
   alla riga del pannello, e la nota che compare solo da lontano. */
.oriz-say {
  display: none;
}

#proiezione:has(#oriz-4:checked) .oriz-4,
#proiezione:has(#oriz-7:checked) .oriz-7,
#proiezione:has(#oriz-10:checked) .oriz-10 {
  display: inline;
}

#proiezione:has(#oriz-4:checked) p.oriz-4,
#proiezione:has(#oriz-7:checked) p.oriz-7,
#proiezione:has(#oriz-10:checked) p.oriz-10 {
  display: block;
}

/* --- 2. La legenda, sempre sotto la tabella ---------------------------- */
/* A ogni orizzonte nello stesso posto, allineata alla prima colonna di
   celle: a destra si prendeva 21rem proprio nella vista a quattro stagioni,
   e cambiava posto passando a sette. */
.proj-body {
  display: grid;
  gap: var(--space-3) var(--space-5);
}

@media (min-width: 901px) {
  .proj-body .proj-legend {
    gap: 0.4rem 1.4rem;
    margin: 0;
    padding-left: 9rem;
  }

  .proj-body .proj-legend span {
    max-width: 26rem;
  }
}

/* --- 3. Sotto 900px la stessa `<table>` diventa carte ------------------- */
/* Il pattern dell'anagrafica e di *Adesso*, al posto dello scorrimento
   orizzontale: una carta per categoria, e dentro **una riga per stagione** —
   anno, numero, parola. Non una griglia di piastrelle: con tre fondi ogni
   cella porta la sua parola («+5 per livello comodo»), e in 70px quella parola
   va a capo tre volte. Una riga per stagione la tiene su una riga sola, e vale
   uguale per quattro, sette e dieci stagioni: nessuna colonna resta fuori
   dallo schermo, che con lo scorrimento succede già oggi — e la colonna che
   non si vede non si cerca.

   I selettori portano `#proiezione` perché devono battere le regole della
   vista corta qui sopra, che hanno un `id` e un `:has`. */
@media (max-width: 900px) {
  #proiezione .proj-scroll {
    overflow: visible;
    margin-inline: 0;
    padding-inline: 0;
  }

  #proiezione .proj-scroll::after {
    content: none;
  }

  #proiezione .proj-table {
    min-width: 0;
    width: 100%;
    max-width: none;
  }

  #proiezione .proj-table thead {
    display: none;
  }

  #proiezione .proj-table,
  #proiezione .proj-table tbody,
  #proiezione .proj-table tbody tr {
    display: block;
  }

  #proiezione .proj-table tbody tr {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: 0.5rem;
    margin-bottom: 0.5rem;
  }

  #proiezione .proj-table th[scope="row"] {
    height: auto;
    padding: 0 0 0.4rem;
    position: static;
    box-shadow: none;
  }

  /* Anno, numero, parola. L'anno lo porta la cella, come `data-label` nelle
     altre tabelle impilate: senza la testata, una riga sola non saprebbe di
     che stagione parla. */
  #proiezione .proj-table td {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem;
    height: auto;
    text-align: left;
    padding: 0.35rem 0.5rem;
    margin-bottom: 3px;
  }

  #proiezione .proj-table td::before {
    content: attr(data-year);
    font: var(--type-caption);
    color: var(--color-text-muted);
    flex: none;
    width: 2.6rem;
  }

  #proiezione .proj-table td b {
    display: inline;
    flex: none;
    width: 1.6rem;
    text-align: right;
  }

  #proiezione .proj-table td small {
    display: inline;
    font: var(--type-caption);
    white-space: normal;
  }

  /* Su una riga larga la cella grigia dei Kids non ha bisogno del contorno
     degli altri, ma sì dell'anno: resta una riga come le sue sorelle. */
  #proiezione .proj-table td.p-none {
    justify-content: flex-start;
  }
}

/* Le tre chiavi si dividono la riga invece di andare a capo. */
@media (max-width: 640px) {
  .proj-keys label {
    flex: 1 1 0;
    align-items: center;
    padding-inline: 0.4rem;
  }
}

/* --- 3bis. I due verdi ------------------------------------------------- */
/* I due fondi della #115 dicevano solo che cosa non va. Il verde dice dove si
   sta bene, e si sdoppia sulle **due soglie che la categoria dichiara già**:
   `lines` (portieri + difesa + attacco) e `comfortable` (portieri + movimento
   comodo). Acceso da `comfortable` in su, sbiadito in mezzo — le linee si
   compongono, ma comodi non si sta.

   Fra i due verdi la distanza è di tinta, non di chiarezza: il fondo sbiadito
   è la stessa tinta al 45% sul fondo della scheda, il contorno la stessa al
   50% sul grigio dei bordi. Il colore non porta comunque il significato da
   solo — la cella scrive «livello comodo», «+2 per livello comodo», «+5 per
   le linee», «non si gioca». */
/* Il **verdino** è `--color-ok-bg`, il verde chiaro che l'app usa già ovunque:
   è il più pallido che si distingua ancora dal bianco della scheda. Schiarito
   ancora — il primo tentativo lo mescolava al 45% col fondo — diventava
   bianco, e un fondo che non si vede non è un fondo.

   Il **verde** sta quindi un gradino sopra, e in tavolozza non c'era: è la
   stessa tinta del bordo al 25% sul fondo della scheda. Il testo scende a un
   verde più scuro, o su quel fondo il corpo piccolo resta sotto 4,5:1.
   In produzione questi due diventano token accanto a `--color-ok-*`. */
.proj-table td.p-ok {
  background: color-mix(in srgb, var(--color-ok-border) 25%, var(--color-surface));
  box-shadow: inset 0 0 0 1px var(--color-ok-border);
  color: #14532d;
}

.proj-table td.p-ok-soft {
  background: var(--color-ok-bg);
  box-shadow: inset 0 0 0 1px
    color-mix(in srgb, var(--color-ok-border) 45%, var(--color-border));
  color: var(--color-ok-text);
}

.proj-legend i.ok {
  background: color-mix(in srgb, var(--color-ok-border) 25%, var(--color-surface));
  box-shadow: inset 0 0 0 1px var(--color-ok-border);
}

.proj-legend i.ok-soft {
  background: var(--color-ok-bg);
  box-shadow: inset 0 0 0 1px
    color-mix(in srgb, var(--color-ok-border) 45%, var(--color-border));
}

/* --- 4. In stampa si stampa tutto -------------------------------------- */
/* Il foglio che finisce sul tavolo del comitato non deve dipendere da quale
   pastiglia era accesa: la scelta è un dettaglio di lettura a schermo. Le
   chiavi spariscono, le colonne tornano tutte. */
@media print {
  .proj-keys {
    display: none;
  }

  .proj-table :is(th, td) {
    display: table-cell;
  }

  .oriz-say.oriz-10 {
    display: inline;
  }

  .oriz-say.oriz-4,
  .oriz-say.oriz-7 {
    display: none;
  }

  /* Difetto preesistente, non introdotto qui: il bottone «Anagrafica» di
     «Chi devi trovare» finisce su carta. «Chi non puoi perdere» è già
     `display: none` in stampa, questo no. */
  .prio li > .btn {
    display: none;
  }
}

/* =========================================================================
   «Chi devi trovare», seconda stesura: l'onda scritta una volta

   La griglia è la **stessa onda letta tre volte** — la diagonale è costante,
   perché le stesse tre annate consecutive diventano Mini, poi Novizi, poi
   Juniori: 48 celle, 18 numeri distinti. Qui l'onda si scrive una volta, una
   riga per gruppo di tre annate, e sparisce la prosa che ripeteva tre volte
   le stesse due frasi.

   Il tono è un filetto a sinistra, come `.prio li`: non un fondo pieno, o la
   tabella diventerebbe una seconda griglia colorata accanto alla prima.
   ========================================================================= */
.find-table {
  width: 100%;
  /* Non si stira: cinque colonne corte su 110rem diventerebbero cinque
     colonne lontane, e l'occhio perderebbe la riga. */
  max-width: 62rem;
  border-collapse: collapse;
}

.find-table thead th {
  font: var(--type-thead);
  letter-spacing: var(--type-thead-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
  text-align: left;
  padding: 0 0.6rem 0.4rem;
}

.find-table th[scope="row"],
.find-table td {
  padding: 0.4rem 0.6rem;
  border-top: 1px solid var(--color-border);
  vertical-align: baseline;
  text-align: left;
}

.find-table th[scope="row"] {
  font: var(--type-body-strong);
  white-space: nowrap;
  border-left: 3px solid transparent;
  padding-left: 0.7rem;
}

.find-table tr.stop th[scope="row"] {
  border-left-color: var(--color-danger-border);
}

.find-table tr.warn th[scope="row"] {
  border-left-color: var(--color-warning-border);
}

.find-table th[scope="row"] small {
  display: block;
  font: var(--type-caption);
  color: var(--color-text-muted);
  font-weight: 400;
}

/* Il numero che si cerca guardando: è la colonna per cui la tabella esiste. */
.find-table .find-miss {
  font: var(--type-body-strong);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.find-table tr.stop .find-miss {
  color: var(--color-danger-text);
}

.find-table tr.warn .find-miss {
  color: var(--color-warning-text);
}

/* I gruppi che stanno bene non fanno una riga a testa: una riga sola dice
   quali sono e quanti fanno, così la sequenza delle annate resta intera e
   non sembra che manchi un pezzo. */
.find-table .find-fine td {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* Sotto 900px la **stessa** `<table>` diventa carte, come l'anagrafica e
   *Adesso*: le etichette le portano i `data-label`. */
@media (max-width: 900px) {
  .find-table,
  .find-table tbody,
  .find-table th[scope="row"],
  .find-table td {
    display: block;
  }

  /* I quattro campi scorrono in linea invece di impilarsi: cinque righe a
     carta, per nove carte, sarebbero due schermate per una tabella che
     deve stare in un colpo d'occhio. */
  .find-table tbody tr {
    display: flex;
    flex-wrap: wrap;
    column-gap: 1rem;
  }

  .find-table th[scope="row"] {
    flex: 1 0 100%;
  }

  .find-table tbody td {
    flex: 0 0 auto;
  }

  /* La riga «ci siamo» è un `colspan`: prende la larghezza intera e va a
     capo, o con `nowrap` spinge la pagina fuori dallo schermo. */
  .find-table .find-fine td {
    flex: 1 0 100%;
    white-space: normal;
  }

  .find-table th[scope="row"] {
    white-space: normal;
  }

  .find-table thead {
    display: none;
  }

  .find-table tbody tr {
    border: 1px solid var(--color-border);
    border-left: 3px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: 0.5rem 0.65rem;
    margin-bottom: 0.5rem;
  }

  .find-table tbody tr.stop {
    border-left-color: var(--color-danger-border);
  }

  .find-table tbody tr.warn {
    border-left-color: var(--color-warning-border);
  }

  .find-table th[scope="row"],
  .find-table td {
    border-top: 0;
    padding: 0.1rem 0;
  }

  .find-table th[scope="row"] {
    border-left: 0;
    padding-left: 0;
    margin-bottom: 0.25rem;
  }

  .find-table td::before {
    content: attr(data-label) " ";
    font: var(--type-caption);
    color: var(--color-text-muted);
  }
}

/* ===================================================================== */
/* (issue #124) Il simulatore del settore giovanile                      */
/* (docs/proposta-simulatore/proposta.md)                                */
/* ===================================================================== */
/* Nessuna forma nuova per la griglia: la tabella, le quattro bande e i due
   contorni sono quelli della Proiezione. Qui c'è solo ciò che il simulatore
   aggiunge — le leve, il grafico, il vecchio numero barrato e la scia. */

.sim-hypothesis {
  margin: 0 0 var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  background: var(--color-warning-bg);
  box-shadow: inset 0 0 0 1px var(--color-warning-border);
  font: var(--type-body);
}

.sim-layout {
  display: grid;
  grid-template-columns: 19rem minmax(0, 1fr);
  gap: var(--space-4);
  align-items: start;
}

.sim-views {
  display: grid;
  gap: var(--space-4);
  min-width: 0;
}

/* Sotto 900px le leve vanno in colonna e il **grafico resta appiccicato in
   alto** mentre si scorre: è l'unico modo di vedere l'effetto mentre si
   tira. In colonna unica il pannello delle leve non è più una colonna, e
   `sticky` lo lascerebbe appeso sopra la tabella. */
@media (max-width: 900px) {
  .sim-layout {
    grid-template-columns: minmax(0, 1fr);
  }

  .sim-levers {
    position: static;
  }

  .sim-views {
    display: contents;
  }

  .sim-stage {
    position: sticky;
    top: 0;
    z-index: 2;
    order: -1;
    padding: var(--space-2) 0;
    background: var(--color-surface);
  }
}

/* --- Le leve ---------------------------------------------------------- */

@media (min-width: 901px) {
  .sim-levers {
    position: sticky;
    top: var(--space-3);
  }
}

.sim-levers .panel {
  margin-bottom: 0;
  max-height: calc(100vh - 4rem);
  overflow: auto;
}

.sim-lever {
  display: grid;
  grid-template-columns: 3.6rem minmax(0, 1fr) 2.6rem;
  align-items: center;
  gap: 0.5rem;
  padding: 0.15rem 0;
}

.sim-lever > label {
  font: var(--type-body-strong);
  font-size: var(--t-2);
  font-variant-numeric: tabular-nums;
}

.sim-lever > label small {
  display: block;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.sim-track {
  position: relative;
  display: block;
}

.sim-track input[type="range"] {
  display: block;
  width: 100%;
  accent-color: var(--color-primary-dark);
}

.sim-delta {
  text-align: right;
  font: var(--type-body-strong);
  font-size: var(--t-2);
  font-variant-numeric: tabular-nums;
}

.sim-delta.up b {
  color: var(--color-ok-text);
}

.sim-delta.down b {
  color: var(--color-danger-text);
}

.sim-lever.is-active {
  background: var(--color-bg);
  border-radius: var(--radius-sm);
}

.sim-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.sim-note {
  margin-top: var(--space-3);
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* --- Il grafico ------------------------------------------------------- */

.sim-chart {
  display: block;
  width: 100%;
  height: auto;
}

.sim-chart .grid-line {
  stroke: var(--color-border);
  stroke-width: 1;
}

/* La riga rossa è **una sola**: il referto. Sopra, la squadra c'è; le altre
   due soglie sono sfumature dentro il «c'è», e restano sottili. */
.sim-chart .rule-sheet {
  stroke: var(--color-danger-border);
  stroke-width: 2;
}

.sim-chart .rule-soft {
  stroke: var(--color-border);
  stroke-width: 1;
  stroke-dasharray: 3 4;
}

.sim-chart .rule-label {
  font: 400 11px var(--font-sans);
  fill: var(--color-danger-text);
}

.sim-chart .rule-label.soft {
  fill: var(--color-text-muted);
}

.sim-chart .axis-label {
  font: 400 11px var(--font-sans);
  fill: var(--color-text-muted);
}

.sim-chart .band-bad {
  fill: var(--color-danger-border);
  opacity: 0.06;
}

.sim-chart .was {
  fill: none;
  stroke: var(--census-tint);
  stroke-width: 1.5;
  stroke-dasharray: 2 4;
  opacity: 0.55;
}

.sim-chart .now {
  fill: none;
  stroke: var(--census-tint);
  stroke-width: 2.5;
}

/* L'alone, e il nome sulla linea.

   Le categorie sono in **ordine d'età**, quindi la rampa a un tono solo è
   l'encoding giusto e resta — ma dentro una tinta sola quattro linee o si
   vedono o si distinguono, non tutte e due (i conti stanno in
   `docs/proposta-linee-categorie/`). Allora il colore smette di dover dire
   *quale* linea è quale: lo dice il **nome scritto dove la linea finisce**,
   d'inchiostro come ogni testo, col filetto che porta la tinta. Il colore
   torna a dire una cosa sola, l'ordine.

   L'alone è il colore della **scheda**, non un bianco scritto a mano: a un
   incrocio una linea passa sopra l'altra invece di fondersi. */
.sim-chart .halo {
  fill: none;
  stroke: var(--color-surface);
  stroke-width: 6.5;
}

.sim-chart .dot {
  fill: var(--census-tint);
  stroke: var(--color-surface);
  stroke-width: 1.5;
}

.sim-chart .tie {
  stroke: var(--census-tint);
  stroke-width: 1;
  opacity: 0.55;
}

.sim-chart .tip {
  font: 600 18px var(--font-sans);
  fill: var(--color-text);
}

/* Trascinare un punto: il gesto è una **richiesta**, non un risultato. */
.sim-chart .grab {
  fill: transparent;
  cursor: ns-resize;
}

.sim-chart .grab:hover {
  fill: color-mix(in srgb, var(--census-tint) 18%, transparent);
}

.sim-key {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem var(--space-4);
  margin-top: var(--space-2);
}

.sim-key span {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.sim-key i {
  display: inline-block;
  width: 1.4rem;
  height: 0;
  border-top: 2.5px solid var(--census-tint, currentColor);
}

.sim-key i.was {
  border-top-style: dashed;
  border-top-color: var(--color-border);
}

/* Sotto il grafico, che cosa hai chiesto e **su quale canale**: trascinare un
   punto su una stagione vicina vuol dire chiedere ragazzi ad altre società, e
   il grafico da solo fa sembrare facile la cosa più difficile. Un fatto sul
   gesto, non un giudizio. */
.sim-gesture {
  margin: var(--space-2) 0 0;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  box-shadow: inset 0 0 0 1px var(--color-border);
  font: var(--type-body);
}

/* --- Prima e dopo, dentro la cella ------------------------------------ */

.proj-table td .sim-was {
  display: block;
  font: var(--type-caption);
  color: var(--color-text-muted);
  text-decoration: line-through;
}

.proj-table td .sim-add {
  display: block;
  font: var(--type-body-strong);
  font-size: var(--t-1);
  color: var(--color-primary-dark);
}

/* La **scia** dell'annata che stai toccando: un nato nel 2020 non è «i Mini»,
   è una diagonale che attraversa Mini, poi Novizi, poi Juniori. È l'unica
   cosa che dice qualcosa *prima* che tu muova una leva. */
.proj-table td.sim-trace {
  box-shadow: inset 0 0 0 2px var(--color-primary-dark);
}

.proj-table td.sim-trace-soft {
  box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--color-primary-dark) 35%, transparent);
}

/* In stampa il simulatore non c'è: la carta porta un fatto, e uno scenario è
   un'ipotesi che si muove. */
@media print {
  .sim-levers,
  .sim-stage,
  .sim-hypothesis {
    display: none;
  }

  .sim-layout {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* «Quanto manca» è una **lunghezza e un numero**, non una linea sottile
   (issue #124). Il primo tentativo metteva una tacca di 2px sulla pista di
   uno slider largo 260, presente su metà delle leve: l'occhio scorreva il
   pannello e non incontrava niente, proprio dove sta l'informazione più
   importante.

   Il conto del pollice sta **qui**, in un posto solo: Rust e lo script si
   passano una frazione fra 0 e 1 (`--from`, `--to`, `--at`) e non sanno
   quanto è largo un pollice. */
.sim-track {
  --lever-thumb: 16px;
}

.sim-track .gap,
.sim-track .tick {
  position: absolute;
  top: 50%;
  pointer-events: none;
}

/* Il tratteggio rosso: da dove sei a dove devi arrivare. */
.sim-track .gap {
  left: calc(var(--from) * (100% - var(--lever-thumb)) + var(--lever-thumb) / 2);
  width: calc((var(--to) - var(--from)) * (100% - var(--lever-thumb)));
  height: 0.3rem;
  margin-top: -0.15rem;
  border-radius: 0.15rem;
  background: repeating-linear-gradient(
    -45deg,
    var(--color-danger-border) 0 3px,
    color-mix(in srgb, var(--color-danger-border) 45%, #fff) 3px 6px
  );
}

.sim-track .tick {
  left: calc(var(--at) * (100% - var(--lever-thumb)) + var(--lever-thumb) / 2);
  width: 0;
}

/* La piena è il referto — «sotto qui non si gioca». */
.sim-track .tick-sheet {
  height: 1.15rem;
  margin-top: -0.575rem;
  border-left: 2px solid var(--color-danger-border);
}

/* Il target: la tacca chiude il tratto ambra, nel colore delle celle «sotto
   il target». La tacca grigia di un pixel c'era già, e chi chiedeva «che cosa
   serve per arrivare a 12?» non la trovava. */
.sim-track .tick-target {
  height: 1rem;
  margin-top: -0.5rem;
  border-left: 2px solid var(--color-warning-border);
}

/* Il tratto ambra continua il rosso: dal referto (o dal pollice) al target. */
.sim-track .gap-target {
  left: calc(var(--gap-from) * (100% - var(--lever-thumb)) + var(--lever-thumb) / 2);
  width: calc((var(--to) - var(--gap-from)) * (100% - var(--lever-thumb)));
  background: repeating-linear-gradient(
    -45deg,
    var(--color-warning-border) 0 3px,
    color-mix(in srgb, var(--color-warning-border) 40%, #fff) 3px 6px
  );
}

/* La leva che hai mosso porta **di quanto**, e si stacca dalle altre: un
   numero appena più verde non diceva che l'avevi toccata tu. */
.sim-delta .shift {
  display: block;
  font: var(--type-caption);
  font-weight: 700;
  color: var(--color-primary-dark);
}

.sim-delta.down .shift {
  color: var(--color-danger-text);
}

.sim-lever.is-moved {
  box-shadow: inset 3px 0 0 var(--color-primary);
  /* Il rientro esce nel margine del pannello: la griglia resta larga uguale,
     o la colonna del numero finiva tagliata. */
  margin-inline: -0.5rem;
  padding-inline: 0.5rem;
  border-radius: var(--radius-sm);
  background: var(--color-primary-soft);
}

/* Le leve che l'ultimo trascinamento ha mosso: il grafico sta a destra, le
   leve a sinistra, e spesso fuori schermo. */
.sim-lever.is-gestured {
  box-shadow: inset 0 0 0 2px var(--color-primary);
}

/* --- La legenda delle leve ------------------------------------------- */

.sim-legend {
  margin: 0 0 var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  font: var(--type-caption);
}

.sim-legend summary {
  cursor: pointer;
  font: var(--type-body-strong);
  font-size: var(--t-1);
}

.sim-legend ul {
  display: grid;
  gap: 0.55rem;
  margin: var(--space-2) 0 0;
  padding: 0;
  list-style: none;
}

/* Il simbolo **sopra** la frase: il pannello è stretto, e una colonna per
   il simbolo lasciava alla frase due parole per riga. */
.sim-legend li {
  display: grid;
  gap: 0.25rem;
}

.sim-legend li > .sim-legend-track {
  width: 8rem;
}

.sim-legend li > .sim-legend-moved {
  justify-self: start;
  width: 3.2rem;
}

.sim-legend-foot {
  margin: var(--space-2) 0 0;
  color: var(--color-text-muted);
}

.sim-legend-sheet {
  color: var(--color-danger-text);
}

.sim-legend-target {
  color: var(--color-warning-text);
}

/* La pista finta: stessa `--lever-thumb` e stesse regole di posizione della
   vera, con binario e pollice disegnati invece di un `<input>`. */
.sim-legend-track {
  height: var(--lever-thumb);
  margin-top: 0.1rem;
}

.sim-legend-track .rail {
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: 0.3rem;
  margin-top: -0.15rem;
  border-radius: 0.15rem;
  background: var(--color-border);
}

.sim-legend-track .thumb {
  position: absolute;
  top: 0;
  left: calc(var(--from) * (100% - var(--lever-thumb)));
  width: var(--lever-thumb);
  height: var(--lever-thumb);
  border-radius: 50%;
  background: var(--color-primary-dark);
}

.sim-legend-moved {
  padding: 0.1rem 0.4rem;
  border-radius: var(--radius-sm);
  box-shadow: inset 3px 0 0 var(--color-primary);
  background: var(--color-primary-soft);
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.sim-legend-moved.is-gestured {
  box-shadow: inset 0 0 0 2px var(--color-primary);
}

.sim-legend-moved b {
  display: block;
  font: var(--type-body-strong);
  color: var(--color-ok-text);
}

.sim-legend-moved small {
  display: block;
  font-weight: 700;
  color: var(--color-primary-dark);
}

.sim-gesture a {
  font-weight: 700;
}

.sim-gesture small {
  color: var(--color-text-muted);
}

/* --- (issue #123) L'obiettivo di reclutamento ------------------------- */

/* Il segno dell'obiettivo, sulla pista della leva. È **inchiostro**: le due
   tacche che ci sono già parlano la lingua delle soglie (rosso = referto,
   grigio = target), e una terza tinta lì direbbe che l'obiettivo è una terza
   soglia. Non lo è: è una decisione. Forma piena e chiusa, diversa da tutte e
   due, e la stessa del pallino della didascalia. */
.sim-track .goal {
  position: absolute;
  top: 50%;
  left: calc(var(--at) * (100% - var(--lever-thumb)) + var(--lever-thumb) / 2);
  width: 0.55rem;
  height: 0.55rem;
  margin-top: -0.275rem;
  margin-left: -0.275rem;
  border-radius: 50%;
  background: var(--color-text);
  pointer-events: none;
}

/* Il buco chiuso non è un colore: è la parola, e il pallino si svuota. */
.sim-track .goal.done {
  background: none;
  border: 2px solid var(--color-text);
}

/* Il riquadro che dichiara **che cosa scriverà**, prima di scriverlo: senza,
   «Fissa» su tre leve tirate è un gesto al buio. */
.sim-commit {
  margin-top: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
}

.sim-commit p {
  margin: 0 0 var(--space-2);
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.sim-commit-list {
  margin: 0 0 var(--space-3);
  padding-left: 1.1rem;
  font: var(--type-caption);
}

.sim-commit-list b {
  color: var(--color-text);
}

.sim-commit-note {
  display: block;
  margin-bottom: var(--space-3);
}

.sim-commit-note span {
  display: block;
  font: var(--type-caption);
  color: var(--color-text-muted);
  margin-bottom: 0.2rem;
}

.sim-commit-note input {
  width: 100%;
}

.sim-commit .sim-actions {
  margin-top: 0;
}

/* La riga di un obiettivo, sotto quella del piano della sua annata. Quelle
   restano quelle di sempre: il piano non cambia perché hai deciso qualcosa.

   L'annata si legge **alla pari** di quella del piano — stesso corpo, stesso
   posto —: rientrata e in corpo piccolo, il 2012 sembrava una sotto-riga del
   2011. A dire che riga è ci pensa il sottotitolo, come «oggi 15 anni» sulla
   riga del piano: nessun fondo e nessun colore, che è la regola della
   tabella. */
.find-table tr.find-goal th[scope="row"],
.find-table tr.find-goal td {
  border-top: 0;
  color: var(--color-text-muted);
}

.find-table tr.find-goal th[scope="row"] {
  font-weight: 400;
  color: var(--color-text);
}

.find-table tr.find-goal b {
  color: var(--color-text);
}

.find-table tr.find-goal .mark {
  display: inline-block;
  width: 0.5rem;
  height: 0.5rem;
  margin-right: 0.35rem;
  border-radius: 50%;
  background: var(--color-text);
}

/* Il buco chiuso non è un colore: è la parola, e il pallino si svuota. */
.find-table tr.find-goal.done .mark {
  background: none;
  border: 2px solid var(--color-text);
}

/* La nota è **tua**, non un pezzo del verdetto: riga sua, corpo piccolo e
   corsivo fra virgolette. Attaccata al verdetto si leggeva come la sua coda —
   «il buco è chiuso «Settembre 2026»» —, e in una colonna sua finiva sotto
   un'intestazione che diceva un'altra cosa. */
.find-goal-note {
  display: block;
  margin-top: 0.15rem;
  font: var(--type-caption);
  font-style: italic;
}

/* «Togli» sta sulla riga che si sta leggendo: è lì che nasce la domanda «e
   questo come lo cancello?». Un form suo, fuori da quello delle leve.

   Non un bottone pieno: cancellare non è l'azione principale della pagina, e
   un rettangolo verde per riga ruberebbe l'occhio al numero. Contorno e
   inchiostro, come il resto della tabella; il tocco resta di 44px in altezza,
   che è la misura del dito. */
.find-goal-remove {
  /* Il `form` del foglio è una scatola con fondo, bordo e respiro: qui è solo
     l'involucro di un bottone, e va spogliato. */
  display: inline-block;
  vertical-align: baseline;
  margin: 0 0 0 var(--space-2);
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
  gap: 0;
}

.find-goal-remove button {
  min-height: 0;
  padding: 0.15rem 0.6rem;
  font: var(--type-caption);
  background: none;
  color: var(--color-text-muted);
  border-color: var(--color-border);
}

.find-goal-remove button:hover,
.find-goal-remove button:focus-visible {
  background: none;
  color: var(--color-text);
  border-color: var(--color-text-muted);
}

/* =========================================================================
   ORGANICI → PROIEZIONE, la gerarchia (mockup «Settore giovanile»)
   -------------------------------------------------------------------------
   La pagina risponde a una domanda sola — «se non cambia nulla, come sarà il
   settore giovanile, e dove devo intervenire oggi?» — e l'ordine è quello
   della risposta: **vedi** (il grafico), **capisci** (la griglia),
   **simula** (le leve, con l'impatto accanto), **agisci** (chi trovare, chi
   non perdere), **approfondisci** (Cosa significa, chiuso).

   Tre regole che non vanno sciolte:

   1. Il **grafico** e la **griglia** sono due pannelli, e il **simulatore**
      è il terzo. Prima erano una colonna di leve alta 1400px accanto a mezza
      pagina vuota: il difetto non era il contenuto, era la disposizione.
   2. L'**impatto** sta accanto alle leve, non accanto al grafico: la domanda
      per cui si tira una leva è «e allora?», e si legge dove si è mosso.
   3. Il **colore non porta mai da solo il significato**: la cella tiene la
      sua parola anche quando il fondo la direbbe già, solo rimpicciolita —
      la legenda di due parole sotto la tabella è la stessa parola.

   Questo blocco viene **dopo** le regole della #115/#116/#123/#124 apposta:
   a pari specificità vince l'ultimo, e le poche che riscrive sono la
   disposizione, mai i segni.
   ========================================================================= */

/* --- 0. La testata: titolo e schede da una parte, il file dall'altra ----- */
.organici-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-2) var(--space-4);
}

.organici-head h1 {
  margin-bottom: 0.2rem;
}

.organici-head .organici-tabs {
  margin-bottom: 0;
  border-bottom: 0;
}

/* Le schede tengono il loro filetto: senza il bordo del contenitore il
   margine negativo lo farebbe uscire di un pixel. */
.organici-head .organici-tab {
  margin-bottom: 0;
}

.organici-report {
  flex: none;
  margin-top: 0.4rem;
}

/* La riga di contesto, sotto le schede e sopra tutto: una frase, e il bordo
   che separa le schede dal contenuto. */
.organici-lead {
  margin: 0 0 var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
  color: var(--color-text-muted);
  font: var(--type-body);
  max-width: 80ch;
}

.organici-lead b {
  color: var(--color-text);
}

/* --- 1. VEDI: l'orizzonte e la nota, sulla stessa riga ------------------ */
.proj-controls {
  display: grid;
  gap: var(--space-3);
  align-items: start;
  margin-bottom: var(--space-3);
}

@media (min-width: 60rem) {
  .proj-controls {
    grid-template-columns: minmax(0, 1fr) minmax(18rem, 24rem);
  }
}

.proj-controls .proj-keys {
  margin: 0;
}

/* Le tre chiavi dell'orizzonte sono la scelta principale del pannello: più
   grandi delle pastiglie di un filtro, e quella accesa in pieno. */
.proj-controls .proj-keys label {
  padding: 0.6rem 1rem;
}

.proj-controls .proj-keys input:checked + label {
  background: var(--color-primary-dark);
  border-color: var(--color-primary-dark);
  box-shadow: none;
  color: #fff;
}

.proj-controls .proj-keys input:checked + label b,
.proj-controls .proj-keys input:checked + label small {
  color: inherit;
}

/* Il riquadro che dice che cosa si sta guardando: quieto, senza tinta —
   non è un avviso, è il perimetro del numero. */
.proj-about {
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  box-shadow: inset 0 0 0 1px var(--color-border);
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.proj-about p {
  margin: 0;
}

.proj-about p + p {
  margin-top: 0.4rem;
}

.proj-about b {
  color: var(--color-text);
}

/* --- 2. CAPISCI: la legenda corta --------------------------------------- */
/* La cella resta com'è (numero in `--t-4`, parola in `--t-1`): **il colore
   non porta mai da solo il significato**, quindi la parola non si toglie
   dalle celle che stanno bene — chi non distingue i verdi leggerebbe quattro
   celle uguali. Quello che si accorcia è la legenda.

   La legenda: due parole per banda, in fila, allineata sotto la tabella. */
.proj-body .proj-legend span {
  font: var(--type-caption);
}

@media (min-width: 901px) {
  .proj-body .proj-legend span {
    max-width: none;
  }
}

/* --- 3. SIMULA: leve a sinistra, che cosa cambia a destra ---------------- */
.sim-panel .sim-layout {
  grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 64rem) {
  .sim-panel .sim-layout {
    /* Le leve prendono quello che serve alle loro quattro colonne, il
       **grafico si prende il resto**: è lì che si guarda mentre si tira, e su
       un asse di dieci stagioni ogni pixel in più è una stagione più
       leggibile. */
    grid-template-columns: 27rem minmax(0, 1fr);
  }
}

/* Le leve non sono più una colonna appiccicata: scorrono **dentro la loro
   lista**, e il pannello resta alto quanto una schermata. Con diciannove
   annate l'elenco intero faceva da solo l'altezza della pagina. */
.sim-panel .sim-levers {
  position: static;
  min-width: 0;
}

.sim-lever-list {
  max-height: 32rem;
  overflow: auto;
  padding-right: var(--space-2);
  /* Il rientro della leva mossa esce nel margine: senza questo lo scroller
     lo taglierebbe. */
  padding-left: 0.3rem;
  margin-left: -0.3rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding-top: 0.2rem;
  padding-bottom: var(--space-2);
}

@media (max-width: 63.99rem) {
  .sim-lever-list {
    max-height: 24rem;
  }
}

/* La leva, densa come nel mockup: annata, pista, totale e di quanto l'hai
   mossa su **una riga**. «Quanto manca» e l'obiettivo restano, ma sotto, e
   solo sulla leva che ne ha uno. */
.sim-panel .sim-lever {
  grid-template-columns: 3.4rem minmax(0, 1fr) 3.2rem;
  padding: 0.1rem 0.4rem;
}

.sim-panel .sim-lever > label {
  display: flex;
  align-items: baseline;
  gap: 0.3rem;
}

.sim-panel .sim-lever > label small {
  display: inline;
}

/* Dove le due colonne diventano una, il **grafico va in cima e resta
   appiccicato** mentre si scorre l'elenco delle leve: è l'unico modo di
   vedere l'effetto mentre si tira. `display: contents` sulla colonna destra
   serve a questo — senza, `order` e `sticky` agirebbero dentro un contenitore
   che scorre via tutto insieme.

   La soglia è **quella delle due colonne** (64rem), non i 900px del resto del
   foglio: con 900 restava una finestra fra 901 e 1023px in cui il pannello era
   già a colonna unica e il grafico non seguiva più — si tirava alla cieca. */
@media (max-width: 63.99rem) {
  .sim-panel .sim-views {
    display: contents;
  }

  .sim-panel .sim-stage {
    position: sticky;
    top: 0;
    z-index: 2;
    order: -1;
    padding: var(--space-2) 0;
    background: var(--color-surface);
  }
}

/* --- «Impatto della modifica» ------------------------------------------- */
.sim-impact h3,
.sim-commit h3 {
  font: var(--type-body-strong);
  margin: 0 0 var(--space-2);
}

.sim-impact {
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
}

.sim-impact p {
  margin: 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.sim-impact p[hidden] {
  display: none;
}

.sim-impact-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.3rem;
}

.sim-impact-list:empty {
  display: none;
}

/* Una riga è una cella che cambia banda. Il filetto è la **direzione**, e le
   due tinte sono quelle della griglia: non un terzo alfabeto. */
.sim-impact-list li {
  padding: 0.3rem 0.5rem;
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  box-shadow: inset 0 0 0 1px var(--color-border);
  border-left: 3px solid var(--color-border);
  font: var(--type-caption);
}

.sim-impact-list li.up {
  border-left-color: var(--color-ok-text);
}

.sim-impact-list li.down {
  border-left-color: var(--color-danger-text);
}

.sim-impact-list li b {
  font: var(--type-body-strong);
  font-size: var(--t-1);
}

.sim-impact-list li small {
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.sim-impact-quiet {
  margin-top: 0.4rem !important;
}

/* Il riquadro degli obiettivi non è più in fondo al form: è la terza cosa
   della colonna, dopo aver visto che cosa la modifica cambia. */
.sim-panel .sim-commit {
  margin-top: 0;
}

.sim-panel .sim-views > * + * {
  margin-top: var(--space-3);
}

.sim-panel .sim-note {
  margin-top: 0;
}

/* --- 4. AGISCI: «Chi devi trovare», una tabella sola --------------------- */
/* Il canale è un **badge** e non una seconda sezione: due mestieri distinti
   non hanno bisogno di due tabelle, e i due totali restano due nella riga
   del pannello, perché non si sommano. */
.find-keys {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  margin: 0 0 var(--space-3);
}

.find-keys input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.find-keys label {
  display: inline-block;
  padding: 0.3rem 0.7rem;
  border: 1px solid var(--color-border);
  border-radius: 999px;
  background: var(--color-surface);
  color: var(--color-text-muted);
  font: var(--type-caption);
  cursor: pointer;
}

.find-keys input:checked + label {
  border-color: var(--color-primary-dark);
  color: var(--color-primary-dark);
  font-weight: 700;
}

.find-keys input:focus-visible + label {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* Il filtro: nessuna riga sparisce se la scelta è «Tutte». Il canale lo dice
   il **badge** della riga e non un attributo sul `<tr>`: la riga di un
   obiettivo porta nella sua classe l'unica cosa che si legge da fuori — se è
   `done` o no —, e qualunque altra cosa lì sopra la nasconderebbe. */
#trovare:has(#can-bambini:checked) .find-table tbody tr:not(:has([data-canale="bambini"])),
#trovare:has(#can-societa:checked) .find-table tbody tr:not(:has([data-canale="societa"])) {
  display: none;
}

.find-badge {
  display: inline-block;
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  font: var(--type-caption);
  font-weight: 700;
  white-space: nowrap;
  box-shadow: inset 0 0 0 1px currentcolor;
  color: var(--color-text-muted);
}

.find-badge-bambini {
  color: var(--color-primary-dark);
}

/* --- 6. APPROFONDISCI: «Cosa significa?» -------------------------------- */
/* Tutto il metodo, chiuso di partenza. Non è testo tolto: è testo che ha
   smesso di competere coi numeri. */
.proj-glossary {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  padding: var(--space-3) var(--space-4);
}

.proj-glossary > summary {
  cursor: pointer;
  font: var(--type-body);
  color: var(--color-text-muted);
}

.proj-glossary > summary b {
  color: var(--color-text);
}

.glossary-body {
  margin-top: var(--space-3);
}

.glossary-keys {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  margin-bottom: var(--space-3);
  border-bottom: 1px solid var(--color-border);
}

.glossary-keys input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.glossary-keys label {
  display: block;
  padding: 0.45rem 0.8rem;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  color: var(--color-text-muted);
  font: var(--type-body-strong);
  cursor: pointer;
}

.glossary-keys input:checked + label {
  color: var(--color-primary-dark);
  border-bottom-color: var(--color-primary);
}

.glossary-keys input:focus-visible + label {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.glossary-pane {
  display: none;
  max-width: 78ch;
}

#significa:has(#gloss-stati:checked) .glossary-stati,
#significa:has(#gloss-metodo:checked) .glossary-metodo {
  display: block;
}

.glossary-pane p {
  margin: 0 0 var(--space-2);
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.glossary-pane b {
  color: var(--color-text);
}

.glossary-list {
  list-style: none;
  margin: 0 0 var(--space-3);
  padding: 0;
  display: grid;
  gap: 0.35rem;
}

.glossary-list li {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.glossary-list li b {
  flex: none;
}

/* I quadratini sono **gli stessi** della legenda sotto la tabella: un segno
   solo per ogni banda, o la spiegazione spiegherebbe un altro disegno. Per
   questo la lista del glossario *è* una `.proj-legend`: le tinte stanno in un
   posto solo, e ritoccarne una le cambia tutte e due. */
.glossary-list i {
  align-self: center;
}

/* In stampa: il simulatore e la spiegazione non ci sono — la carta porta un
   fatto —, e «Chi non puoi perdere» resta fuori come prima. */
@media print {
  .sim-panel,
  .proj-glossary,
  .organici-report,
  .find-keys {
    display: none;
  }

  .organici-lead {
    border-top: 0;
  }
}

/* Il pulsante del file: la veste quieta di un'azione secondaria. `.button`
   non esiste come classe in questo foglio — i bottoni li veste il selettore
   d'elemento —, e un `<a>` da solo si leggeva come un link di testo in una
   riga in cui il mockup mostra un bottone. Scoped a questa pagina apposta. */
.organici-report {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.5rem 0.9rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-text);
  font: var(--type-button-sm);
  text-decoration: none;
}

.organici-report:hover,
.organici-report:focus-visible {
  border-color: var(--color-primary);
  color: var(--color-primary-dark);
}

/* --- Il riquadro delle leve, denso ------------------------------------- */
/* Diciannove annate in un riquadro alto una schermata: se una riga costa
   48px per uno slider da 16, se ne vede un terzo e si scorre per tutto il
   resto. L'aria stava quasi tutta nel conteggio, che impilava il numero e il
   «+5» su due righe **anche quando il «+5» è vuoto** — cioè su ogni leva che
   non hai toccato.

   Qui non si toglie niente: il numero, di quanto l'hai mossa, «quanto manca»
   e l'obiettivo restano tutti. Cambia che stanno **su una riga**, e la riga
   è alta quanto il suo slider. */
.sim-panel .sim-delta {
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: 0.3rem;
}

/* Di quanto l'hai mossa, **accanto** al conteggio e non sotto: su una leva
   ferma è vuoto, e una riga vuota costava 16px a diciannove leve. */
.sim-panel .sim-delta .shift {
  display: inline;
}

.sim-panel .sim-lever {
  padding: 0 0.35rem;
  column-gap: 0.45rem;
  row-gap: 0;
}

/* Il nome dell'annata e l'età sulla stessa riga, l'età appena più piccola:
   erano due righe per due parole. */
/* Anno ed età **sulla stessa riga**. `display: flex` da solo non bastava: il
   foglio ha un `label { flex-direction: column }` globale, e senza rimettere
   la direzione i due restavano impilati — erano i 40px di una riga che ne
   vuole 20. La colonna dell'annata si allarga di quel tanto che serve a
   «2017 9 anni» senza andare a capo, e li prende dalla pista, che ne ha 340. */
.sim-panel .sim-lever > label {
  flex-direction: row;
  gap: 0.25rem;
  line-height: 1.2;
  white-space: nowrap;
}

.sim-panel .sim-lever {
  grid-template-columns: 4.7rem minmax(0, 1fr) 3.2rem;
}

.sim-lever-list {
  padding-top: 0;
  padding-bottom: 0.35rem;
}

/* --- La pista, disegnata da noi --------------------------------------- */
/* Il pollice nativo con `accent-color` è una pallina piena su un filo di
   4px: su una riga alta 21px non si vede dove sei. Qui la pista è **una
   barra piena con il riempimento fino al pollice**, e il pollice è un
   cerchio bianco — la forma del mockup.

   Perché la disegniamo noi: il tratteggio rosso, quello ambra, le due
   barrette e il pallino dell'obiettivo sono `<i>` **dopo** l'input nel
   markup, quindi ci passano sopra. Se la pista fosse quella nativa, il suo
   fondo coprirebbe i segni, o i segni coprirebbero il pollice. Così invece:
   pista e riempimento sono i due pseudo-elementi di `.sim-track` (sotto), i
   segni stanno in mezzo, e l'input porta **solo il pollice**, sopra tutto —
   che è la lettura giusta: il pollice è dove sei adesso.

   Le due misure stanno in `--lever-thumb` e `--lever-rail`, e il conto della
   posizione resta quello di sempre: chi le cambia non tocca nient'altro. */
.sim-track {
  --lever-thumb: 20px;
  --lever-rail: 8px;
}

.sim-track::before,
.sim-track::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 0;
  height: var(--lever-rail);
  margin-top: calc(var(--lever-rail) / -2);
  border-radius: 999px;
  pointer-events: none;
}

/* La pista intera. */
.sim-track::before {
  right: 0;
  background: color-mix(in srgb, var(--color-border) 65%, var(--color-text-muted));
}

/* Il riempimento, fino al **centro del pollice**. */
.sim-track::after {
  width: calc(var(--from) * (100% - var(--lever-thumb)) + var(--lever-thumb) / 2);
  background: linear-gradient(90deg, var(--color-primary-dark), var(--color-primary));
}

.sim-track input[type="range"] {
  position: relative;
  z-index: 3;
  -webkit-appearance: none;
  appearance: none;
  display: block;
  width: 100%;
  height: var(--lever-thumb);
  margin: 0;
  padding: 0;
  background: transparent;
  cursor: pointer;
  /* `accent-color` ridipingerebbe il pollice nativo: qui non c'è più. */
  accent-color: auto;
}

.sim-track input[type="range"]::-webkit-slider-runnable-track {
  height: var(--lever-rail);
  background: transparent;
  border: 0;
}

.sim-track input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  box-sizing: border-box;
  width: var(--lever-thumb);
  height: var(--lever-thumb);
  margin-top: calc((var(--lever-rail) - var(--lever-thumb)) / 2);
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--color-text-muted) 40%, #fff);
  background: #fff;
  box-shadow: 0 1px 3px rgb(23 26 33 / 22%);
}

.sim-track input[type="range"]::-moz-range-track {
  height: var(--lever-rail);
  background: transparent;
  border: 0;
}

.sim-track input[type="range"]::-moz-range-thumb {
  box-sizing: border-box;
  width: var(--lever-thumb);
  height: var(--lever-thumb);
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--color-text-muted) 40%, #fff);
  background: #fff;
  box-shadow: 0 1px 3px rgb(23 26 33 / 22%);
}

/* Il fuoco sta sul pollice, non sul rettangolo dell'input: è quello che si
   muove con le frecce. */
.sim-track input[type="range"]:focus {
  outline: none;
}

.sim-track input[type="range"]:focus-visible::-webkit-slider-thumb {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.sim-track input[type="range"]:focus-visible::-moz-range-thumb {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* I segni stanno **fra** la pista e il pollice: sopra il riempimento, sotto
   il cerchio bianco. Le barrette restano più alte della pista — sono
   barrette, si devono vedere sporgere. */
.sim-track .gap,
.sim-track .tick,
.sim-track .goal {
  z-index: 1;
}

/* Il tratteggio è ora **spesso quanto la pista**: su un filo di 4px era una
   sfumatura, sulla barra è un tratto di strada che manca. */
.sim-track .gap {
  height: var(--lever-rail);
  margin-top: calc(var(--lever-rail) / -2);
  border-radius: 999px;
}

/* I finti slider della legenda portano già `.sim-track`, quindi la pista e il
   riempimento glieli disegnano gli stessi due pseudo-elementi: il `<i
   class="rail">` scritto a mano diventa di troppo, e il pollice si veste come
   quello vero. Così la legenda non può spiegare un disegno diverso da quello
   che spiega. */
.sim-legend-track .rail {
  display: none;
}

.sim-legend-track .thumb {
  box-sizing: border-box;
  border: 1px solid color-mix(in srgb, var(--color-text-muted) 40%, #fff);
  background: #fff;
  box-shadow: 0 1px 3px rgb(23 26 33 / 22%);
}

/* Il tratteggio comincia **dove finisce il pollice**, non al suo centro: con
   un pollice da 20px il primo pezzo di strada che manca gli finiva sotto, e
   su una leva a cui ne manca uno solo non si vedeva niente. Il traguardo
   resta dov'è — è un valore —, quindi i due capi si scrivono con `left` e
   `right`: quando non ci sta nulla in mezzo la barra si chiude da sé invece
   di avere una larghezza negativa. */
.sim-track .gap {
  left: calc(var(--from) * (100% - var(--lever-thumb)) + var(--lever-thumb));
  right: calc(100% - (var(--to) * (100% - var(--lever-thumb)) + var(--lever-thumb) / 2));
  width: auto;
}

/* L'ambra parte dalla barretta del referto; solo quando quella non c'è —
   niente da recuperare per giocare — parte anche lei dal pollice, e allora
   vale la stessa distanza. */
.sim-track .gap-target {
  left: max(
    calc(var(--gap-from) * (100% - var(--lever-thumb)) + var(--lever-thumb) / 2),
    calc(var(--from) * (100% - var(--lever-thumb)) + var(--lever-thumb))
  );
  right: calc(100% - (var(--to) * (100% - var(--lever-thumb)) + var(--lever-thumb) / 2));
  width: auto;
}

/* La riga mossa esce nel margine del riquadro — è il suo rientro colorato —,
   ma il **contenuto** deve restare incolonnato con le righe sopra e sotto:
   il padding orizzontale vale quello della riga **più** il margine negativo,
   o la pista di una leva mossa comincia più a sinistra delle altre. */
.sim-panel .sim-lever.is-moved {
  margin-inline: -0.35rem;
  padding-inline: 0.7rem;
}

/* «dal 2027 in poi», accanto al titolo dell'impatto: dice che la stagione in
   corso non è contata, ed è un'informazione sull'elenco, non un titolo. */
.sim-impact h3 small {
  font: var(--type-caption);
  font-weight: 400;
  color: var(--color-text-muted);
  margin-left: 0.35rem;
}

/* --- La lista delle leve, piatta ---------------------------------------- */
/* (18.09.2026) Una riga per annata, dalla più giovane: niente terzine e
   niente divisione fra i due canali. Le colonne sono quattro — annata ed età,
   quanti ne hai **ora**, la pista, quanti ne avresti col delta —, e sono le
   stesse dell'intestazione, che le nomina una volta in cima invece di
   ripeterle su ogni riga.

   (20.09.2026) La quinta, fra la pista e il numero, è la coppia «− +»: i due
   pulsanti prendono 2,8rem alla pista, che resta lunga abbastanza perché il
   tratteggio del piano e le due barrette si leggano. */
.sim-panel .sim-lever,
.sim-lever-head {
  display: grid;
  grid-template-columns: 4.7rem 1.8rem minmax(0, 1fr) 2.8rem 3.4rem;
  align-items: center;
  column-gap: 0.45rem;
  padding: 0 0.35rem;
}

.sim-lever-head {
  position: sticky;
  top: 0;
  z-index: 2;
  margin: 0 0 0.2rem;
  padding-top: 0.2rem;
  padding-bottom: 0.25rem;
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-border);
  font: var(--type-caption);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-text-muted);
}

/* «Ora» sopra la colonna dei numeri di adesso, «Ipotesi» sopra quella del
   risultato: la pista in mezzo non ha bisogno di un nome, è l'unica cosa che
   si tira. */
.sim-lever-head span:nth-child(2) {
  text-align: right;
}

/* «Ipotesi» sta sopra l'ultima colonna, che dalla coppia di pulsanti in poi è
   la quinta: l'intestazione ha tre celle e la riga cinque. */
.sim-lever-head span:nth-child(3) {
  grid-column: 5;
  text-align: right;
}

/* --- I due pulsanti di una leva ---------------------------------------- */
/* Uno in meno, uno in più. Il vestito è quello dei comandi discreti: contorno
   d'inchiostro tenue, nessun fondo pieno — la tinta della riga è del
   riempimento della pista, e un pulsante colorato ruberebbe quella lettura.

   `min-height: 0` non è un dettaglio da togliere: il foglio dà **44px** a
   ogni `button` (il bersaglio del dito), e senza questo la riga della leva
   raddoppia — diciannove annate, mezza schermata in più. Il bersaglio grande
   torna dove serve, nel riquadro girato del telefono, in coda a questo
   foglio. */
.sim-steps {
  display: flex;
  gap: 0.2rem;
}

/* Il pulsante di default del form, che esiste solo perché **Invio** continui
   a voler dire «Aggiorna» (vedi il commento nel template). `hidden` da solo
   non basta: il foglio dà `display: inline-flex` a ogni `button`, e una regola
   d'elemento vince sull'attributo — come dice la nota su `[hidden]` più
   sopra, chi si nasconde ha la sua riga. */
.sim-levers > button[hidden] {
  display: none;
}

.sim-step {
  appearance: none;
  display: grid;
  place-items: center;
  width: 1.3rem;
  height: 1.3rem;
  min-height: 0;
  padding: 0;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-text-muted);
  font-size: var(--t-2);
  font-weight: 700;
  line-height: 1;
}

.sim-step:hover:not(:disabled) {
  background: var(--color-surface);
  border-color: var(--color-primary);
  color: var(--color-primary-dark);
}

/* Al capo della corsa: niente da aggiungere, o niente più da togliere. */
.sim-step:disabled {
  opacity: 0.3;
  cursor: default;
}

/* Dove il pannello è una colonna sola — telefono e tablet — la riga ha 250px
   scarsi, e cinque colonne non ci stanno: la pista ne usciva schiacciata a
   cinque pixel, cioè un pollice su una corsa che non c'è.
   Se ne vanno **l'età e la colonna «Ora»**, che sono le due cose che la riga
   dice già altrove: l'età è l'ordine stesso dell'elenco, e «Ora» è «Ipotesi»
   quando la leva è ferma, o «Ipotesi» meno il «+6» quando l'hai mossa. Quello
   che resta è quello che si tocca: la pista, i due pulsanti, il risultato. */
@media (max-width: 63.99rem) {
  .sim-panel .sim-lever,
  .sim-lever-head {
    grid-template-columns: 2.8rem minmax(0, 1fr) 2.8rem 3.4rem;
    column-gap: 0.3rem;
  }

  .sim-panel .sim-lever > label small,
  .sim-now,
  .sim-lever-head span:nth-child(2) {
    display: none;
  }

  /* «Annata» sopra due colonne: in 2,8rem la parola andrebbe a capo, e la
     seconda colonna è la pista, che un'intestazione non ce l'ha. */
  .sim-lever-head span:nth-child(1) {
    grid-column: 1 / 3;
  }

  .sim-lever-head span:nth-child(3) {
    grid-column: 4;
  }
}

/* Quanti ne hai adesso: inchiostro secondario, perché è il punto di partenza
   e non la risposta. */
.sim-now {
  text-align: right;
  font: var(--type-body-strong);
  font-size: var(--t-2);
  font-variant-numeric: tabular-nums;
  color: var(--color-text-muted);
}

/* Le annate che il piano chiede sotto i sei anni: un totale che sparisce
   dalle leve senza una riga che lo spieghi è un numero che non torna. */
.sim-out {
  margin: var(--space-2) 0 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* Il riquadro degli obiettivi sta **dopo la tabella**, fuori dal pannello del
   simulatore: fra il grafico e il dettaglio non deve restare niente di alto.
   Il selettore non è più `.sim-panel .sim-commit` proprio per questo — da lì
   non lo prenderebbe più. A tutta larghezza la riga di testo diventerebbe
   lunga il doppio di quanto si legge, quindi il tetto resta. */
.sim-commit {
  margin-top: var(--space-4);
  max-width: 52rem;
}

/* Le leve prendono quello che serve alle loro quattro colonne e non di più;
   il resto è del grafico. Sotto i 30rem la pista si accorcia troppo per
   tirarla con precisione, sopra ruba stagioni all'asse. */
@media (min-width: 64rem) {
  .sim-panel .sim-layout {
    grid-template-columns: minmax(26rem, 30rem) minmax(0, 1fr);
  }
}

/* --- L'impatto, a griglia ---------------------------------------------- */
/* Una lista a una colonna faceva dieci riquadri larghi 1280px per frasi da
   400: il pannello cresceva di 380px e sotto le leve restava il bianco.

   **`auto-fill` e `1fr`, e non si può fare altrimenti**: il numero di tracce
   di un `repeat()` automatico si calcola sul **massimo** della `minmax()`
   quando è definito (CSS Grid §7.2.3.1), quindi `minmax(16rem, 24rem)` in una
   colonna da 526px ne conta **una sola** — misurato, non dedotto. Col massimo
   a `1fr` il conto passa al minimo e le tracce diventano due.

   `auto-fill` e non `auto-fit` proprio perché le tracce vuote **restano**:
   con una riga d'impatto sola, `auto-fit` le collasserebbe e quella riga si
   allargherebbe a tutta la colonna. Una traccia vuota non disegna niente — il
   bordo ce l'ha la card, non la traccia —, quindi «restare» qui vuol dire
   solo tenere la card larga come le altre.

   L'ordine del DOM resta quello della stagione più vicina: la griglia lo
   segue riga per riga, e chi legge con la tastiera lo trova uguale. */
.sim-impact-list {
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
  gap: 0.3rem 0.4rem;
}

/* In due colonne da 259px la frase più lunga non ci sta: la card va a capo, e
   deve andarci **fra un pezzo e l'altro**, non a metà di «livello comodo». Il
   flex tiene insieme il nome della cella, la parola e i due numeri, e manda
   giù intero quello che non entra. */
.sim-impact-list li {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: 0.35rem;
  padding: 0.2rem 0.5rem;
  line-height: 1.35;
}

/* --- Le date da rispettare, sulla riga ---------------------------------- */
/* Un trasferimento si fa solo in due finestre, e la nota sotto la tabella lo
   diceva a dieci righe di distanza da chi sta leggendo «2011 · +6». Qui il
   segno sta **sulla riga**, ed è **testo**, non un simbolo: quello sulla
   carta arriva, un `title` no — e infatti il suggerimento manda alla
   citazione, non la sostituisce.

   Un `<abbr>` e non uno `<span>`: il browser gli dà da sé il cursore della
   domanda e lo annuncia come voce con una spiegazione. */
.find-windows {
  display: inline-block;
  margin-left: 0.3rem;
  padding: 0 0.35rem;
  border-radius: var(--radius-sm);
  background: var(--color-warning-bg);
  box-shadow: inset 0 0 0 1px var(--color-warning-border);
  color: var(--color-warning-text);
  font: var(--type-caption);
  font-weight: 700;
  white-space: nowrap;
  text-decoration: none;
  cursor: help;
}

.find-windows::before {
  content: "⚠\00a0";
  font-weight: 400;
}

/* L'avviso della cancellazione: fissare con le leve a zero **toglie** gli
   obiettivi salvati, e il riquadro lo diceva in coda a «non c'è niente da
   fissare», in gergo. Ora è un avviso, e ha la veste degli avvisi — il
   selettore è `.sim-commit p…` perché quello del riquadro ha una classe e un
   elemento, e da solo `.sim-commit-clears-lead` perdeva il colore. */
.sim-commit p.sim-commit-clears-lead {
  color: var(--color-warning-text);
}

.sim-commit p.sim-commit-clears-lead b {
  color: inherit;
}

.sim-commit p.sim-commit-keep {
  font: var(--type-caption);
  color: var(--color-warning-text);
}

.sim-commit p[hidden] {
  display: none;
}

/* --- Il telefono girato (issue #128) ----------------------------------- */
/* Un iPhone in orizzontale è largo circa 844px — sotto la soglia delle due
   colonne — e alto circa 390: in colonna unica il grafico appiccicato in cima
   si mangiava lo schermo, e alle leve restava una striscia. Qui leve e grafico
   tornano **affiancati** come su computer: si tira una leva e il grafico
   cambia accanto. Non è spazio in più, è la disposizione.

   Solo **basso e largo**: in verticale, da 64rem in su e in stampa la pagina è
   quella di prima. Sta **dopo** la colonna unica, che a pari specificità
   altrimenti vincerebbe.

   Le leve scorrono **nel loro pannello**, alto quanto lo schermo tolta la
   barra in basso: messo il pannello in cima alla finestra, leve e grafico
   stanno tutti e due sullo schermo. Il grafico resta appiccicato nella sua
   colonna, così non scappa nemmeno se si scorre la pagina. Le due colonne
   sono **metà e metà**: con le leve strette la pista restava di 75px, troppo
   corta per tirarla col dito, e il grafico largo metà schermo si legge
   ancora.

   Il bersaglio del dito: su iOS lo slider risponde **solo sul pollice**, non
   sulla pista, quindi è il pollice che si deve afferrare — 28px invece di 20 —
   e la riga alta 44px (la misura minima di Apple) lo tiene lontano da quello
   della leva accanto. Toccare fra due pollici scorre l'elenco. */
@media screen and (max-width: 63.99rem) and (orientation: landscape) and (max-height: 30rem) {
  .sim-panel .sim-layout {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--space-3);
  }

  .sim-panel .sim-views {
    display: grid;
  }

  /* `sticky` e `top: 0` restano quelli della colonna unica qui sopra: in
     colonna tornano ad agire dentro la colonna destra, che è ciò che si
     vuole. Qui si toglie solo lo spostamento in cima. */
  .sim-panel .sim-stage {
    order: 0;
    padding: 0;
  }

  .sim-lever-list {
    max-height: calc(100dvh - var(--bottom-nav-space) - var(--space-4));
  }

  .sim-panel .sim-track {
    --lever-thumb: 28px;
  }

  .sim-panel .sim-lever {
    min-height: 2.75rem;
  }

  /* Qui la riga è alta 44px apposta (è il dito): i due pulsanti crescono con
     lei e tornano bersagli grandi come il foglio chiede ovunque. Più di così
     no: quello che prendono lo prende alla pista, ed è la sola larghezza in
     cui si tira davvero. */
  .sim-step {
    width: 2.2rem;
    height: 2.2rem;
    font-size: var(--t-1);
  }

  .sim-panel .sim-lever,
  .sim-lever-head {
    grid-template-columns: 2.8rem minmax(0, 1fr) 4.6rem 3.4rem;
  }
}

/* =====================================================================
   (19.09.2026, `docs/proposta-calendario/` §8) La pagina Calendario come
   cruscotto: «Da fare», due settimane in griglia, prossima partita, blocco,
   partite, carico. Prefisso `cal-`, e `calendar-toolbar`/`calendar-add` per
   la riga delle azioni. Sotto 80rem una colonna sola; le due colonne
   diventano `display: contents` e `order` rimette la prossima partita
   subito dopo la griglia.
   ===================================================================== */

.calendar-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  margin: calc(-1 * var(--space-2)) 0 var(--space-3);
}
.calendar-toolbar .page-actions-quiet {
  margin: 0;
}
.calendar-add > summary {
  list-style: none;
  cursor: pointer;
}
.calendar-add > summary::-webkit-details-marker {
  display: none;
}
/* Aperto, il modulo prende tutta la riga e va in fondo: l'altro bottone
   resta dov'era. */
.calendar-add[open] {
  flex-basis: 100%;
  order: 10;
}
/* Il form ha già la sua veste di card (`form`): il pannello no. */
.calendar-add-panel {
  margin-top: var(--space-2);
  max-width: 40rem;
}
.calendar-add-panel > h2 {
  margin-top: 0;
}

.cal-todo {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}
.cal-todo-label {
  margin: 0;
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.cal-todo-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}
.cal-todo-chip {
  display: inline-block;
  padding: 4px 12px;
  border-radius: var(--radius-pill);
  background: var(--color-warning-bg);
  color: var(--color-warning-text);
  font: var(--type-caption);
  text-decoration: none;
}
.cal-todo-chip:hover {
  text-decoration: underline;
}

.cal-dash {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.cal-dash-main,
.cal-dash-side {
  display: contents;
}
.cal-calendar { order: 1; }
.cal-next-match { order: 2; }
.cal-block { order: 3; }
.cal-matches { order: 4; }
.cal-load { order: 5; }
.cal-sync { order: 6; }

.cal-panel {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  min-width: 0;
}
.cal-panel-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}
.cal-panel-title {
  margin: 0 0 var(--space-2);
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.cal-panel-head .cal-panel-title {
  margin: 0;
}
.cal-panel-head .match-verify-tag {
  margin-left: 0;
}
.cal-panel-empty {
  margin: 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}
/* (issue #134) I gesti sul calendario federativo sono due — il file `.ics`
   e la pagina della squadra —: su una riga, e a capo quando non ci stanno.
   Con un gesto solo si vede uguale a prima. */
.cal-panel-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-3);
  padding-top: var(--space-2);
  border-top: 1px solid var(--color-border);
}
/* Il form di «Aggiorna dal calendario» è solo un bottone: niente veste di
   card (`form`) e niente larghezza piena. */
.cal-panel-foot form {
  display: block;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
}
.cal-panel-foot .btn {
  width: auto;
}

/* --- Il calendario: mese, settimana, lista (19.09.2026) ------------- */

/* Sezioni della stagione a sinistra, azioni a destra, sulla stessa riga. */
.cal-top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2) var(--space-3);
  margin-bottom: var(--space-3);
}
.cal-top .season-nav {
  margin: 0;
}
.cal-top .calendar-toolbar {
  margin: 0;
  gap: var(--space-2);
}
.calendar-toolbar .btn {
  width: auto;
}

/* Titoli delle card in grassetto, e «Vedi tutte →» a destra. */
.cal-card-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}
.cal-card-title {
  margin: 0 0 var(--space-2);
  font: var(--type-title);
}
.cal-card-head .cal-card-title {
  margin: 0;
}
.cal-card-head .match-verify-tag {
  margin-left: 0;
}
.cal-card-more {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  margin-left: auto;
  font: var(--type-caption);
  font-weight: 600;
  color: var(--color-primary-dark);
  text-decoration: none;
}
.cal-card-more:hover {
  text-decoration: underline;
}

/* La testata: «Oggi», ‹ ›, il titolo, e le tre viste a destra. */
.cal-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  margin-bottom: var(--space-3);
}
.cal-head .btn {
  width: auto;
}
.cal-step {
  display: inline-flex;
}
.cal-step .btn {
  display: inline-flex;
  align-items: center;
  padding-inline: 8px;
}
.cal-step .btn:first-child {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}
.cal-step .btn + .btn {
  margin-left: -1px;
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}
.cal-step-back {
  transform: scaleX(-1);
}
.cal-title {
  margin: 0;
  font: var(--type-section);
}
.cal-modes {
  display: inline-flex;
  margin-left: auto;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.cal-modes a {
  padding: 6px 14px;
  font: var(--type-label);
  color: var(--color-text);
  text-decoration: none;
}
.cal-modes a + a {
  border-left: 1px solid var(--color-border);
}
.cal-modes a:hover {
  background: var(--color-primary-soft);
}
.cal-modes a[aria-current="page"] {
  background: var(--color-primary);
  color: var(--color-primary-contrast);
}

/* La griglia: sette colonne, i giorni separati da un filetto. */
.cal-weekdays,
.cal-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  margin: 0;
  padding: 0;
  list-style: none;
}
.cal-weekdays li {
  padding: 0 0 6px;
  text-align: center;
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.cal-grid {
  border-top: 1px solid var(--color-border);
  border-left: 1px solid var(--color-border);
}
.cal-cell {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
  min-height: 6.5rem;
  padding: 6px;
  border-right: 1px solid var(--color-border);
  border-bottom: 1px solid var(--color-border);
  background: var(--color-surface);
}
.cal-view-settimana .cal-cell {
  min-height: 12rem;
}
.cal-cell.is-outside .cal-cell-num {
  color: var(--color-text-muted);
  opacity: 0.55;
}
.cal-cell.is-today {
  outline: 2px solid var(--color-primary);
  outline-offset: -2px;
  background: var(--color-primary-soft);
  border-radius: var(--radius-sm);
}
.cal-cell-date {
  font: var(--type-label);
}
.cal-cell.is-today .cal-cell-num {
  display: inline-grid;
  place-items: center;
  min-width: 1.6em;
  height: 1.6em;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--color-primary);
  color: var(--color-primary-contrast);
}
/* Il giorno per esteso serve allo screen reader e al telefono, dove la
   griglia diventa un elenco; nella griglia lo dice la colonna. */
.cal-cell-long {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
.cal-grid-empty {
  display: none;
}

/* La pastiglia di una voce: la tinta dice il tipo. */
.cal-chip {
  position: relative;
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  font: var(--type-caption);
  min-width: 0;
}
.cal-chip-training {
  background: var(--color-ok-bg);
  border-color: transparent;
}
.cal-chip-match {
  background: var(--color-danger-bg);
  border-color: transparent;
}
.cal-chip-match .partita-glifo {
  color: var(--color-danger-text);
}
.cal-chip-tournament {
  background: var(--color-warning-bg);
  border-color: transparent;
}
.cal-chip-camp {
  background: var(--color-camp-bg);
  border-color: transparent;
}
.cal-chip-break {
  border-style: dashed;
  color: var(--color-text-muted);
}
.cal-chip-main {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 4px 6px;
  color: inherit;
  text-decoration: none;
  min-width: 0;
}
a.cal-chip-main:hover .cal-chip-line {
  text-decoration: underline;
}
.cal-chip-line {
  display: flex;
  align-items: center;
  gap: 5px;
  font-weight: 600;
  white-space: nowrap;
}
/* La matita sta in alto a destra, sulla riga dell'ora: solo lì le si
   lascia il posto, così «Allenamento» sotto usa tutta la larghezza. */
.cal-chip:has(.cal-chip-edit) .cal-chip-line {
  padding-right: 18px;
}
/* Nel mese la colonna è stretta: un corpo più piccolo, come nel mockup,
   perché «Allenamento» ci stia intero. */
.cal-view-mese .cal-chip {
  font-size: 0.75rem;
}
.cal-view-mese .cal-cell {
  padding: 5px 4px;
}
.cal-view-mese .cal-chip-main {
  padding: 3px 5px;
}
/* Nella settimana le colonne sono le stesse ma alte: il testo va a capo
   invece di troncarsi. */
.cal-view-settimana .cal-chip-kind,
.cal-view-settimana .cal-chip-detail {
  white-space: normal;
  overflow-wrap: anywhere;
}
.cal-chip-line .partita-glifo {
  display: inline-block;
  flex: none;
}
.cal-chip-kind,
.cal-chip-detail {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cal-chip-detail {
  color: var(--color-text-muted);
}
.cal-chip-note {
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.cal-chip-status-todo .cal-chip-note,
.cal-chip-status-regenerate .cal-chip-note {
  color: var(--color-warning-text);
}
.cal-chip .match-verify-tag {
  margin-left: 0;
  align-self: flex-start;
}
.cal-chip.is-cancelled .cal-chip-line,
.cal-chip.is-cancelled .cal-chip-kind,
.cal-chip.is-cancelled .cal-chip-detail {
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}
.cal-chip-mark {
  color: var(--color-ok-text);
  flex: none;
}
/* Il pallino dello stato: pieno quando la scheda c'è o è pianificata,
   ambra quando c'è da fare, vuoto senza scheda nel passato. */
.cal-chip-dot {
  width: 8px;
  height: 8px;
  flex: none;
  border-radius: 50%;
  border: 1.5px solid currentColor;
  color: var(--color-text-muted);
}
.cal-chip-status-ready .cal-chip-dot,
.cal-chip-status-planned .cal-chip-dot {
  color: var(--color-ok-text);
  background: currentColor;
}
.cal-chip-status-todo .cal-chip-dot,
.cal-chip-status-regenerate .cal-chip-dot {
  color: var(--color-warning-text);
  background: currentColor;
}
.cal-chip-tournament .cal-chip-dot {
  color: var(--color-warning-text);
  background: currentColor;
}
.cal-chip-camp .cal-chip-dot {
  color: var(--color-camp-text);
  background: currentColor;
}
.cal-chip-edit {
  position: absolute;
  top: 3px;
  right: 3px;
  display: grid;
  place-items: center;
  width: 18px;
  height: 18px;
  border-radius: 4px;
  color: var(--color-text-muted);
}
.cal-chip-edit:hover {
  background: var(--color-surface);
  color: var(--color-primary-dark);
}
.cal-cell.is-past .cal-chip {
  opacity: 0.8;
}
.cal-cell-delete,
.cal-list-delete {
  font: var(--type-caption);
  color: var(--color-text-muted);
}
.cal-cell-delete {
  align-self: flex-end;
}
.cal-cell-delete:hover,
.cal-list-delete:hover {
  color: var(--color-danger-text);
}

/* Il segno della partita è `display: block` sulla timeline; qui sta nella
   riga, davanti all'avversario. */
.cal-match-opponent .partita-glifo,
.cal-next-title .partita-glifo {
  display: inline-block;
  vertical-align: -1px;
}

/* La lista: un mese dopo l'altro, una riga per voce. */
.cal-list-filter {
  margin: 0 0 var(--space-2);
  font: var(--type-caption);
  color: var(--color-text-muted);
}
.cal-list-month {
  margin: var(--space-3) 0 var(--space-1, 4px);
  font: var(--type-label);
}
.cal-list-month:first-of-type {
  margin-top: 0;
}
.cal-list {
  margin: 0;
  padding: 0;
  list-style: none;
}
.cal-list-row {
  display: grid;
  grid-template-columns: 4rem minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-2);
  padding: 4px 0;
  scroll-margin-top: var(--space-4);
}
.cal-list-row + .cal-list-row {
  border-top: 1px solid var(--color-border);
}
.cal-list-row.is-past {
  color: var(--color-text-muted);
}
.cal-list-row.is-today .cal-list-date {
  color: var(--color-primary-dark);
  font-weight: 700;
}
.cal-list-date {
  font: var(--type-label);
  white-space: nowrap;
}
.cal-list .cal-chip-main {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px 10px;
}

/* --- La sincronizzazione con la federazione (issue #138) ------------ */

.cal-sync-when,
.cal-sync-page {
  margin: 0 0 var(--space-2);
  font: var(--type-caption);
}
.cal-sync-when {
  color: var(--color-text-muted);
}
.cal-sync-head {
  margin: var(--space-2) 0 var(--space-1);
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.cal-sync-list {
  margin: 0;
  padding-left: 1.1rem;
  font: var(--type-caption);
}
/* Ciò che non è arrivato: la parola «Non arrivato» e il bordo, mai il solo
   colore. */
.cal-sync-failed {
  border-left: 3px double var(--color-text-muted);
  padding-left: 1.4rem;
}

/* (issue #149) Il passaggio che gira, nel riquadro: lo stesso della pagina
   della sincronizzazione, più raccolto; il titolo è un `h3`. */
.cal-sync .sync-running {
  padding: var(--space-3);
  margin: 0 0 var(--space-3);
}
.cal-sync .sync-running-title {
  font: var(--type-body-strong);
}
/* «Tutta la sincronizzazione →», solo all'amministratore. */
.cal-sync-all {
  margin: var(--space-2) 0 0;
  font: var(--type-caption);
}

/* --- Partite di stagione ------------------------------------------- */

.cal-match-list {
  margin: 0;
  padding: 0;
  list-style: none;
}
.cal-match-list > li + li {
  border-top: 1px solid var(--color-border);
}
.cal-match-row {
  display: grid;
  grid-template-columns: 8.5rem minmax(0, 1fr) auto auto;
  align-items: baseline;
  gap: var(--space-2);
  padding: 6px 0;
  font: var(--type-caption);
  color: inherit;
  text-decoration: none;
}
.cal-match-row:hover .cal-match-opponent {
  text-decoration: underline;
}
.cal-match-row.is-past {
  color: var(--color-text-muted);
}
.cal-match-row.is-cancelled .cal-match-opponent {
  text-decoration: line-through;
}
.cal-match-date {
  white-space: nowrap;
}
.cal-match-opponent {
  font-weight: 600;
  color: var(--color-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cal-match-kind {
  color: var(--color-text-muted);
}
.cal-match-result .match-verify-tag {
  margin-left: 0;
}

/* --- Prossima partita ---------------------------------------------- */

.cal-next-match p {
  margin: 0;
}
.cal-next-body {
  display: flex;
  gap: var(--space-3);
  align-items: flex-start;
}
.cal-next-icon {
  display: grid;
  place-items: center;
  flex: none;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--radius-sm);
  background: var(--color-danger-bg);
  color: var(--color-danger-text);
}
.cal-next-when {
  font: var(--type-label);
  color: var(--color-text-muted);
}
.cal-next-match .cal-next-title {
  font: var(--type-title);
  margin-top: 2px;
}
.cal-next-details {
  font: var(--type-caption);
  color: var(--color-text-muted);
}
.cal-next-match .cal-next-open {
  display: block;
  width: 100%;
  margin-top: var(--space-3);
  text-align: center;
}

/* --- Blocco corrente ----------------------------------------------- */

.cal-block p {
  margin: 0;
}
.cal-block-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-3);
}
.cal-block-name {
  font: var(--type-title);
}
.cal-block-head .cal-block-count {
  text-align: right;
  max-width: 9rem;
}
.cal-block-head .cal-block-count strong {
  display: block;
  color: var(--color-text);
}
.cal-block-dates,
.cal-block-count,
.cal-block-next {
  font: var(--type-caption);
  color: var(--color-text-muted);
}
.cal-progress {
  height: 8px;
  margin: var(--space-2) 0 4px;
  border-radius: var(--radius-pill);
  background: var(--color-border);
  overflow: hidden;
}
.cal-progress > span {
  display: block;
  height: 100%;
  background: var(--color-primary);
}
.cal-theme-legend {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 2px var(--space-2);
  margin: 6px 0 0;
  padding: 0;
  list-style: none;
  font: var(--type-caption);
}
.cal-theme-legend li {
  display: flex;
  align-items: baseline;
  gap: 6px;
  min-width: 0;
}
.cal-theme-legend li::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--fam);
  flex: none;
}
.cal-theme-legend span {
  color: var(--color-text-muted);
}
.cal-block .cal-block-next {
  margin-top: var(--space-2);
  padding-top: var(--space-2);
  border-top: 1px solid var(--color-border);
}

/* --- Carico e regolarità ------------------------------------------- */

.cal-load-weeks {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(3rem, 1fr));
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}
.cal-load-week {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  font: var(--type-caption);
}
.cal-load-slots {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 3px;
  min-height: 12px;
}
.cal-load-slots > i {
  width: 10px;
  height: 10px;
  border: 1.5px solid var(--color-text);
  border-radius: 2px;
}
.cal-load-slots > i.is-done {
  background: var(--color-text);
}
.cal-load-count {
  font-weight: 600;
}
.cal-load-label {
  color: var(--color-text-muted);
}
.cal-load-summary {
  margin: var(--space-2) 0 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* --- Telefono: il mese diventa un elenco di giorni ------------------ */

@media screen and (max-width: 899.98px) {
  /* Sette colonne in 390px non si leggono: la stessa griglia diventa
     l'elenco dei soli giorni con qualcosa, col giorno per esteso. */
  .cal-modes {
    margin-left: 0;
  }
  .cal-title {
    flex-basis: 100%;
    order: -1;
  }
  .cal-weekdays {
    display: none;
  }
  .cal-grid {
    display: flex;
    flex-direction: column;
    border: 0;
    gap: var(--space-2);
  }
  .cal-cell {
    min-height: 0;
    padding: 6px 8px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
  }
  .cal-view-settimana .cal-cell {
    min-height: 0;
  }
  .cal-cell.is-empty,
  .cal-cell.is-outside {
    display: none;
  }
  .cal-cell-num {
    display: none;
  }
  .cal-cell.is-today .cal-cell-num {
    display: none;
  }
  .cal-cell-long {
    position: static;
    width: auto;
    height: auto;
    overflow: visible;
    clip: auto;
  }
  .cal-grid-empty {
    display: block;
  }
  /* Qui la colonna stretta non c'è più: il corpo torna quello di sempre. */
  .cal-view-mese .cal-chip {
    font-size: var(--t-2);
  }
  .cal-list-row {
    grid-template-columns: 3.25rem minmax(0, 1fr) auto;
  }
  .cal-match-row {
    grid-template-columns: minmax(0, 1fr) auto auto;
  }
  .cal-match-date {
    grid-column: 1 / -1;
  }
  .calendar-add-panel {
    max-width: none;
  }
}

/* --- Da 80rem: due colonne ----------------------------------------- */

@media screen and (min-width: 80rem) {
  .cal-dash {
    display: grid;
    grid-template-columns: minmax(0, 1.65fr) minmax(0, 1fr);
    align-items: start;
  }
  .cal-dash-main,
  .cal-dash-side {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
  }
  .cal-panel {
    padding: var(--space-3) var(--space-4);
  }
}

/* =====================================================================
   «Il ritmo del vivaio» (19.09.2026, docs/proposta-ritmo-vivaio/).
   Tre file sulla stessa griglia — le categorie di oggi, le barre per
   annata, le finestre di tre annate —, così una finestra sta sotto la sua
   annata di mezzo e tutte stanno in una riga. Il colore non porta mai da
   solo il significato: la barra ha la parola sotto l'anno, la finestra il
   margine scritto.
   ===================================================================== */
.vg {
  --vg-gap: 0.3rem;
  --vg-gutter: 4.2rem;
  padding-left: var(--vg-gutter);
  margin-top: var(--space-3);
}
.vg-row {
  display: grid;
  grid-template-columns: repeat(var(--vg-cols), minmax(0, 1fr));
  gap: var(--vg-gap);
  list-style: none;
  margin: 0;
  padding: 0;
}
.vg-bands {
  margin-bottom: var(--space-2);
}
.vg-band {
  border-bottom: 2px solid var(--color-primary);
  color: var(--color-primary-dark);
  font: var(--type-eyebrow);
  letter-spacing: 0.04em;
  padding-bottom: 0.2rem;
  text-align: center;
  text-transform: uppercase;
}
.vg-band.is-unmeasured {
  border-bottom-color: var(--color-border);
  color: var(--color-text-muted);
}
.vg-plot {
  height: 7.5rem;
  position: relative;
}
.vg-tick {
  border-top: 1px dashed var(--color-text-muted);
  left: calc(-1 * var(--vg-gutter));
  opacity: 0.55;
  position: absolute;
  right: 0;
}
.vg-tick > span {
  color: var(--color-text-muted);
  font: var(--type-caption);
  font-size: 0.7rem;
  left: 0;
  position: absolute;
  transform: translateY(-100%);
  white-space: nowrap;
}
.vg-bars {
  align-items: end;
  height: 100%;
  position: relative;
}
.vg-bar {
  align-items: center;
  display: flex;
  flex-direction: column;
  height: 100%;
  justify-content: flex-end;
}
.vg-bar b {
  font: 800 1rem/1 var(--font-cond);
  margin-bottom: 0.2rem;
}
.vg-stem {
  border-radius: 5px 5px 0 0;
  min-height: 2px;
  width: 62%;
}
.vg-comfortable .vg-stem { background: var(--color-ok-border); }
.vg-on .vg-stem { background: #f0c04d; }
.vg-below .vg-stem { background: var(--color-warning-border); }
.vg-thin .vg-stem { background: var(--color-danger-border); }
.vg-early .vg-stem {
  background: transparent;
  border: 2px dashed var(--color-border);
  border-bottom: 0;
}
.vg-bar.vg-comfortable b { color: var(--color-ok-text); }
.vg-bar.vg-on b,
.vg-bar.vg-below b { color: var(--color-warning-text); }
.vg-bar.vg-thin b { color: var(--color-danger-text); }
.vg-bar.vg-early b { color: var(--color-text-muted); }
.vg-years {
  border-top: 1px solid var(--color-border);
  margin-top: 0;
  padding-top: 0.3rem;
}
.vg-years > span {
  font: var(--type-caption);
  font-size: 0.72rem;
  text-align: center;
}
.vg-years small {
  color: var(--color-text-muted);
  display: block;
  font-size: 0.66rem;
}
.vg-brackets {
  height: 1.3rem;
  margin-top: var(--space-2);
}
.vg-bracket {
  border-bottom: 2px solid var(--color-primary-dark);
  position: relative;
}
.vg-bracket::before,
.vg-bracket::after {
  background: var(--color-primary-dark);
  bottom: -2px;
  content: "";
  height: 0.45rem;
  position: absolute;
  width: 2px;
}
.vg-bracket::before { left: 0; }
.vg-bracket::after { right: 0; }
.vg-bracket > span {
  background: var(--color-surface);
  bottom: 0.2rem;
  color: var(--color-primary-dark);
  font: var(--type-label);
  font-size: 0.72rem;
  left: 50%;
  padding: 0 0.3rem;
  position: absolute;
  transform: translateX(-50%);
  white-space: nowrap;
}
.vg-windows {
  margin-top: 0.3rem;
}
.vg-window {
  border-radius: 6px;
  line-height: 1.15;
  padding: 0.3rem 0.1rem;
  text-align: center;
}
.vg-window b {
  display: block;
  font: 800 0.95rem/1 var(--font-cond);
}
.vg-window span:not(.visually-hidden) {
  display: block;
  font: 700 0.7rem/1.2 var(--font-sans);
}
.vg-window small {
  display: block;
  font-size: 0.64rem;
  opacity: 0.85;
}
.vg-w-plus { background: var(--color-ok-bg); box-shadow: inset 0 0 0 1px var(--color-ok-border); color: var(--color-ok-text); }
.vg-w-zero { background: var(--color-warning-bg); box-shadow: inset 0 0 0 1px var(--color-warning-border); color: var(--color-warning-text); }
.vg-w-minus { background: var(--color-danger-bg); box-shadow: inset 0 0 0 1px var(--color-danger-border); color: var(--color-danger-text); }
.vg-w-stop { background: var(--color-danger-border); color: #fff; }
.vg-w-early,
.vg-w-decided { border: 1px dashed var(--color-border); color: var(--color-text-muted); }
.vg-w-decided { opacity: 0.6; }
.vg-legend {
  color: var(--color-text-muted);
  display: flex;
  flex-wrap: wrap;
  font: var(--type-caption);
  gap: 0.4rem 1.1rem;
  list-style: none;
  margin: var(--space-3) 0;
  padding: 0;
}
.vg-legend li {
  align-items: center;
  display: inline-flex;
  gap: 0.35rem;
}
.vg-legend i {
  border-radius: 3px;
  display: inline-block;
  height: 0.65rem;
  width: 0.9rem;
}
.vg-cards {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: repeat(auto-fit, minmax(min(24rem, 100%), 1fr));
}
.vg-card {
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
}
.vg-card h3 {
  font: var(--type-title);
  margin: 0 0 var(--space-2);
}
.vg-card p {
  color: var(--color-text-muted);
  font: var(--type-caption);
  margin: var(--space-2) 0;
}
.vg-card p b { color: var(--color-text); }
.vg-scenarios {
  border-collapse: collapse;
  margin: var(--space-2) 0;
  width: 100%;
}
.vg-scenarios th,
.vg-scenarios td {
  border-bottom: 1px solid var(--color-border);
  padding: 0.4rem 0.5rem;
  text-align: center;
}
.vg-scenarios thead th {
  color: var(--color-text-muted);
  font: var(--type-thead);
  text-transform: uppercase;
}
.vg-scenarios th:first-child { text-align: left; }
.vg-scenarios td { font: 700 1.05rem/1 var(--font-cond); }
.vg-note {
  background: var(--color-surface);
  border-radius: var(--radius-sm);
  box-shadow: inset 0 0 0 1px var(--color-border);
  padding: 0.5rem 0.7rem;
}
.find-empty {
  background: var(--color-bg);
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-md);
  color: var(--color-text-muted);
  margin: var(--space-3) 0 0;
  padding: var(--space-3) var(--space-4);
  text-align: center;
}
.find-empty a { color: var(--color-primary-dark); font-weight: 600; }
/* Sotto 900px diciassette colonne non stanno: la griglia scorre dentro il
   suo riquadro, e con lei le tre file insieme — una finestra resta sotto la
   sua annata. */
@media (max-width: 900px) {
  .vg {
    --vg-gutter: 0rem;
    overflow-x: auto;
    padding-bottom: var(--space-2);
  }
  .vg-row,
  .vg-plot {
    min-width: calc(var(--vg-cols) * 3.2rem);
  }
  .vg-tick > span { display: none; }
}
@media print {
  .vg-w-stop { border: 2px solid var(--color-danger-border); }
}

/* =====================================================================
   (ADR 0007) La presentazione di una categoria

   Quattro pagine A4. A schermo `.sheet-preview` le mostra a misura vera,
   in stampa sono il documento e basta.

   Due cose da sapere prima di toccare questa sezione.

   1. **Nessuna tinta d'allarme, mai.** Ambra e rosso in tutto il foglio
      dicono «c'è un problema», e questo documento non ha problemi da
      segnalare: ha numeri da dire. Il colore qui è uno solo, quello del
      club, e serve ad accendere il gradino di chi legge.
   2. **Il misto in `pt` è voluto.** Come il foglio G+S e la scheda del
      giocatore, tutto ciò che sta dentro il foglio è in punti e
      centimetri, così l'anteprima a schermo è fedele alla stampa senza
      una seconda resa.
   ===================================================================== */

/* Il foglio è largo 21cm: senza una colonna che lo contenga, l'anteprima
   scorre di lato a ogni larghezza. 56rem sono i 21cm più il respiro della
   fascia scura attorno. */
.app-main-presentation {
  max-width: 56rem;
}

.pres-lead {
  max-width: 44rem;
  color: var(--color-text-muted);
}

.pres-choice {
  margin-top: var(--space-4);
}

.pres-choice-list {
  list-style: none;
  margin: var(--space-2) 0 0;
  padding: 0;
  display: grid;
  gap: var(--space-2);
  grid-template-columns: repeat(auto-fill, minmax(min(18rem, 100%), 1fr));
}

.pres-choice-list li {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
}

.pres-choice-note {
  font-size: 0.85em;
  color: var(--color-text-muted);
}

.pres-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
}

.pres-bar-actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.pres-bar-hint {
  margin-top: calc(-1 * var(--space-2));
  color: var(--color-text-muted);
}

/* Il foglio. A schermo è un A4 esatto coi margini di `@page` resi come
   padding — stesso trattamento della scheda del giocatore e del foglio
   G+S, per la stessa ragione: chi guarda l'anteprima deve vedere ciò che
   esce dalla stampante. */
.pres-sheet {
  box-sizing: border-box;
  width: 21cm;
  min-height: 29.7cm;
  padding: 1.6cm 1.8cm;
  margin: 0 auto var(--space-4);
  background: #fff;
  color: #171A21;
  font: var(--typep-body);
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.35);
}

.pres-head {
  border-bottom: 1.5pt solid var(--color-primary);
  padding-bottom: 6pt;
}

/* (issue #133) Lo stemma e il nome, come la carta intestata che il club
   userebbe: il logo a sinistra, il nome accanto, allineati sulla linea di
   base della fascia. La riga nasce solo se il logo c'è — senza, `.pres-club`
   resta il paragrafo di prima e il foglio non cambia di un punto. */
.pres-head-with-logo {
  display: flex;
  align-items: center;
  gap: 10pt;
}

/* L'altezza è la misura, non la larghezza: gli stemmi sono quadrati, alti
   o larghi, e quello che deve restare uguale fra due club è **quanto
   spazio verticale prende la fascia**. Il tetto di larghezza esiste perché
   uno stemma a nastro non spinga il nome fuori dalla pagina. */
.pres-logo {
  height: 46pt;
  max-width: 45%;
  width: auto;
  object-fit: contain;
}

.pres-club {
  margin: 0;
  font: var(--typep-section);
  color: var(--color-primary-dark);
}

.pres-title {
  margin: 24pt 0 0;
  font: var(--typep-screen);
  font-size: 34pt;
  line-height: 1.05;
}

.pres-code {
  font-size: 16pt;
  color: var(--color-text-muted);
  vertical-align: super;
}

.pres-page-title {
  margin: 0 0 10pt;
  font: var(--typep-section);
  color: var(--color-primary-dark);
}

.pres-headline {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 18pt;
  margin: 20pt 0 0;
  padding: 0;
}

.pres-headline li {
  display: flex;
  flex-direction: column;
}

.pres-headline b {
  font: var(--typep-screen);
  font-size: 26pt;
  color: var(--color-primary-dark);
}

.pres-headline span {
  font: var(--typep-caption);
  color: #555;
}

.pres-headline-small b {
  font-size: 18pt;
}

.pres-section {
  margin-top: 18pt;
}

/* (issue #132) «E dopo» è una riga di nomi sotto la scala, non una seconda
   scala: i gradini sono già disegnati sopra, con i loro numeri. */
.pres-after {
  margin-top: 14pt;
  color: #555;
}

.pres-section h3 {
  margin: 0 0 8pt;
  font: var(--typep-title);
}

/* La scala. Una riga per categoria, dalla più giovane: il gradino di chi
   legge è acceso, gli altri sono il percorso. Il gradino **vuoto resta**,
   con la barra a zero — una categoria senza nessuno è una cosa da dire. */
.pres-ladder {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 5pt;
}

.pres-rung {
  display: grid;
  grid-template-columns: 4.6cm 1fr 1.2cm;
  align-items: baseline;
  gap: 8pt;
}

/* Nome e «3 squadre» sulla stessa riga: mandato a capo, il secondo si
   leggeva come una voce in più della scala invece che come una nota del
   gradino sopra. */
.pres-rung-name {
  font: var(--typep-body);
}

.pres-rung-teams {
  font: var(--typep-caption);
  color: #555;
}

.pres-rung-track {
  height: 11pt;
  background: #eceef1;
  border-radius: 2pt;
}

.pres-rung-bar {
  display: block;
  height: 100%;
  min-width: 0;
  background: #c9ced6;
  border-radius: 2pt;
}

.pres-rung-count {
  text-align: right;
  font: var(--typep-body-strong);
}


.pres-rung.is-current .pres-rung-name,
.pres-rung.is-current .pres-rung-count {
  font: var(--typep-body-strong);
  color: var(--color-primary-dark);
}

.pres-rung.is-current .pres-rung-bar {
  background: var(--color-primary);
}

.pres-squads {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 6pt;
}

.pres-squads li {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10pt;
  padding-bottom: 4pt;
  border-bottom: 0.5pt solid #ddd;
}

.pres-squad-name {
  font: var(--typep-title);
}

.pres-squad-count {
  font: var(--typep-screen);
  font-size: 20pt;
  color: var(--color-primary-dark);
}

.pres-alone {
  display: flex;
  align-items: baseline;
  gap: 8pt;
  margin: 0;
}

.pres-alone b {
  font: var(--typep-screen);
  font-size: 30pt;
  color: var(--color-primary-dark);
}

.pres-alone span {
  font: var(--typep-body);
}

.pres-sheet-note,
.pres-call {
  margin-top: 12pt;
  font: var(--typep-body);
}

.pres-call {
  padding: 10pt 12pt;
  border-left: 2pt solid var(--color-primary);
  background: var(--color-primary-soft);
}

/* (issue #131) La stagione a colpo d'occhio: non più le sole bande dei
   periodi, ma la **striscia della vista d'insieme** — i temi col loro nome
   in chiaro, mese per mese. «In marzo si lavora sul pattinaggio, in maggio
   sul gioco di squadra» non è gergo per un genitore, ed è la prova migliore
   che gli allenamenti non si improvvisano.

   Il markup è lo stesso della pagina di stagione
   (`partials/season_timeline_chart.html`) e ciò che **non** porta lo decide
   la vista, non questo foglio: niente quote del mix, niente tacche, niente
   sedute svolte (regola 3), niente link, niente conteggio a destra. Qui
   restano solo le misure di stampa — il grafico è nato per uno schermo
   largo, e la pagina è larga 17,4 cm.

   Quello che c'era prima — `.pres-plan-track`, `.pres-plan-band`,
   `.pres-plan-label` — non c'è più: le bande dei periodi sono la prima riga
   della striscia, e un secondo asse sulla stessa pagina avrebbe due
   geometrie diverse per la stessa stagione. */
.pres-plan {
  margin: 14pt 0 0;
}

/* Il grafico è `display: none` fin sotto i 1024px di **finestra** — sulla
   pagina di stagione, dove al suo posto c'è la riga che manda alla vista
   tabella. Qui il foglio è largo 21 cm qualunque sia la finestra, e in
   stampa la finestra è la carta (≈ 794px): senza questa riga la striscia
   non si vedrebbe mai, né in anteprima su un portatile né sul PDF. */
.pres-strip .timeline-chart {
  display: block;
}

/* Niente scorrimento: sulla carta non si scorre. La griglia lascia la sua
   larghezza minima da schermo (52rem = 832px, più della pagina) e si adatta
   alla colonna. */
.pres-strip .timeline-scroller {
  overflow: visible;
}

.pres-strip .timeline-grid {
  min-width: 0;
}

/* Due colonne e non tre: il conteggio delle sedute non c'è
   (`draws_counts`), ed è la stessa decisione che toglie i numeri che quella
   colonna conterrebbe. 3,6 cm bastano a «Costruzione del gioco», il più
   lungo dei nomi di tema, a 9 pt. */
.pres-strip .timeline-row {
  grid-template-columns: 3.6cm 1fr;
  gap: 6pt;
  min-height: 0;
}

.pres-strip .timeline-row-label {
  font: var(--typep-caption);
  color: #333;
}

.pres-strip .timeline-scale {
  height: 10pt;
}

.pres-strip .timeline-scale span {
  font: var(--typep-eyebrow);
  letter-spacing: var(--typep-eyebrow-ls);
  color: #555;
}

/* La fascia dei periodi. Bassa, perché sul foglio **non porta i nomi**: i
   nomi stanno nell'elenco qui sotto, che c'è sempre e non dipende da quanto
   dura una banda (`TimelineUse::draws_block_names`). Quello che resta è
   ciò che serve — dove un periodo comincia e dove finisce —, e i confini
   scendono poi lungo tutte le righe dei temi. */
.pres-strip .timeline-periods-row .timeline-track {
  height: 11pt;
  background: #f2f3f5;
  border-radius: 2pt;
}

.pres-strip .timeline-periods-row .timeline-segment {
  line-height: 11pt;
}

.pres-strip .timeline-theme-row,
.pres-strip .timeline-theme-row .timeline-track {
  min-height: 0;
  height: 14pt;
}

/* Le tinte delle famiglie **sono** l'informazione: senza, undici righe di
   bande grigie non dicono su che cosa si lavora. Molte stampanti buttano
   via gli sfondi se non glielo si impedisce. */
.pres-strip .timeline-band,
.pres-strip .timeline-cell,
.pres-strip .timeline-family-dot,
.pres-strip .timeline-legend-swatch,
.pres-strip .timeline-periods-row .timeline-segment {
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}

.pres-strip .timeline-band,
.pres-strip .timeline-cell {
  top: 2px;
  bottom: 2px;
}

/* La legenda delle famiglie, in corpo di didascalia. Le forme — banda
   piena, banda tenue, tacca — non ci sono: le spiegano le quote, e le
   quote sul foglio non ci sono (`draws_mix`). */
.pres-strip .timeline-legend {
  margin-top: 6pt;
}

.pres-strip .timeline-legend-item {
  font: var(--typep-caption);
  color: #555;
}

.pres-plan-periods {
  list-style: none;
  margin: 10pt 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 3pt 14pt;
}

.pres-plan-periods li {
  display: flex;
  justify-content: space-between;
  gap: 8pt;
  font: var(--typep-caption);
  border-bottom: 0.5pt dotted #ccc;
}

.pres-plan-periods span {
  color: #555;
  white-space: nowrap;
}

.pres-plan figcaption {
  margin-top: 5pt;
  font: var(--typep-caption);
  color: #555;
}

/* (issue #132) La scheda di allenamento vera: l'unica pagina che un
   dépliant non può imitare.

   Due colonne — le parti a sinistra, lo schema a destra —, perché è così
   che la scheda vera è fatta e perché in colonna unica la pista larga
   quanto il foglio si mangerebbe la mezza pagina da sola. La misura dello
   schema è fissa e non una frazione: sotto i 6cm le sagome dei giocatori
   smettono di distinguersi, e una scheda illeggibile non dimostra niente. */
.pres-card {
  margin: 9pt 0 0;
  padding: 8pt 10pt 9pt;
  border: 1pt solid var(--color-border);
}

.pres-card-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 4pt 10pt;
  padding-bottom: 5pt;
  border-bottom: 0.75pt solid var(--color-border);
}

.pres-card-team {
  font: var(--typep-section);
  color: var(--color-primary-dark);
}

.pres-card-when {
  font: var(--typep-caption);
  color: #555;
}

.pres-card-objective {
  margin: 6pt 0 0;
  font-weight: 600;
}

.pres-card-borrowed {
  color: #555;
}

.pres-card-body {
  display: grid;
  align-items: start;
  gap: 6pt 12pt;
  margin-top: 6pt;
}

/* Due colonne **solo** con uno schema: la misura della pista è fissa e non
   una frazione, perché sotto i 6cm le sagome dei giocatori smettono di
   distinguersi. Senza disegno la riserva lascerebbe un buco largo quanto
   lei, e l'elenco degli esercizi si prende tutta la riga. */
.pres-card-body.has-drawing {
  grid-template-columns: 1fr 7.6cm;
}

.pres-card-parts,
.pres-card-parts ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.pres-card-parts {
  display: grid;
  gap: 6pt;
}

.pres-card-part {
  display: flex;
  justify-content: space-between;
  gap: 8pt;
  margin: 0 0 2pt;
  font: var(--typep-caption);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-primary-dark);
}

.pres-card-parts ul li {
  display: flex;
  justify-content: space-between;
  gap: 10pt;
  padding-bottom: 1.5pt;
  border-bottom: 0.25pt dotted var(--color-border);
}

.pres-card-minutes {
  color: #555;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.pres-card-drawing .rink-diagram {
  margin: 0;
}

.pres-card-caption {
  margin: 4pt 0 0;
  font: var(--typep-caption);
  color: #555;
}

.pres-card-foot {
  margin: 8pt 0 0;
  padding-top: 5pt;
  border-top: 0.5pt solid var(--color-border);
  font: var(--typep-caption);
  color: #555;
}

.pres-free {
  white-space: pre-line;
}

.pres-contact {
  display: grid;
  grid-template-columns: 4.6cm 1fr;
  gap: 4pt 8pt;
  margin: 0;
}

.pres-contact dt {
  font: var(--typep-caption);
  color: #555;
}

.pres-contact dd {
  margin: 0;
  font: var(--typep-body);
}

.pres-foot {
  margin-top: 26pt;
  padding-top: 5pt;
  border-top: 0.5pt solid #999;
  font: var(--typep-caption);
  color: #555;
}

/* In stampa il foglio è la pagina: via l'ombra e la misura d'anteprima, e
   una pagina per foglio. `@page { size: A4 }` è già globale. */
@media print {
  /* La fascia scura è l'anteprima, non il documento: senza questo riquadro
     `#55585f` finisce sulla carta e copre tutte e quattro le pagine.
     Scoped alla presentazione e non a `.sheet-preview` in generale: le
     altre due anteprime (foglio G+S, scheda del giocatore) hanno una
     stampa loro, verificata, e non si tocca da qui. */
  .app-main-presentation .sheet-preview {
    background: none;
    margin: 0;
    padding: 0;
    overflow-x: visible;
  }

  /* E nemmeno il fondo della shell (`--color-bg`), che sotto l'ultima riga
     di ogni foglio riempiva di grigio il resto della pagina: in stampa il
     foglio finisce dove finisce il testo, e sotto c'è la carta. `:has` per
     non toccare le altre pagine, che una stampa loro ce l'hanno già. */
  html:has(.app-main-presentation) .app-shell {
    background: none;
  }

  .pres-sheet {
    width: auto;
    min-height: 0;
    padding: 0;
    margin: 0;
    box-shadow: none;
    break-after: page;
  }

  .pres-sheet:last-child {
    break-after: auto;
  }

  .pres-section,
  .pres-plan,
  .pres-card,
  .pres-rung {
    break-inside: avoid;
  }

  /* (issue #131) La legenda delle famiglie resta. La regola generale della
     stampa la toglie perché lì si stampa la **vista tabella**, che di bande
     e tacche non ne ha; qui si stampa il grafico, e senza la legenda le
     tinte delle righe non le spiega più niente. */
  .pres-strip .timeline-legend {
    display: flex;
  }

  /* (issue #133) Lo stemma va sulla carta come si vede a schermo: i
     browser, di loro, tolgono i fondi e appiattiscono le immagini per
     risparmiare inchiostro, e un logo stampato grigio è un logo che il
     club non riconosce. */
  .pres-logo {
    print-color-adjust: exact;
    -webkit-print-color-adjust: exact;
  }
}

/* (issue #133) Il logo nel form del club: quello che c'è si vede accanto ai
   comandi, perché un caricamento di cui non si vede il risultato è un
   caricamento che si rifà. Sotto i 40rem le due parti si impilano, e
   l'anteprima resta sopra i comandi: è l'ordine in cui si guarda. */
.club-logo-field {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--space-3);
}

.club-logo-preview {
  width: 6rem;
  height: 6rem;
  object-fit: contain;
  padding: var(--space-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  /* Il fondo è bianco e non quello della pagina: uno stemma scuro su
     trasparente, in tema scuro, sparirebbe — e sul foglio va su carta
     bianca, che è il posto in cui deve funzionare. */
  background: #fff;
}

/* La colonna dei comandi è la **stessa** del form che la contiene —
   etichetta, campo e aiuto con lo stesso `gap` —, perché il margine
   negativo di `.field-hint` è calcolato su quel `gap`: dentro un `div`
   qualunque l'aiuto saliva sopra il campo che spiega (screenshot del
   13.09, e di nuovo qui). */
.club-logo-controls {
  flex: 1 1 18rem;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* --------------------------------------------------------------------------
   (issue #134) L'anteprima della pagina di squadra della federazione

   Le colonne strette — data, ora, risultato, tempi — non vanno a capo: «2:0
   | 5:1 | 4:2» è **una** misura, e spezzata su tre righe si legge come tre.
   I nomi delle squadre sì, e sono loro a decidere la larghezza; quando non
   ci stanno, la tabella scorre dentro `.table-scroll` come le altre.
   -------------------------------------------------------------------------- */

.federation-games .federation-tight {
  white-space: nowrap;
}

.federation-unreadable {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: var(--space-2);
}

/* (issue #135) Le righe ambigue: una partita dell'app per riga, dentro la
   cella «Nel calendario», senza la cornice di un fieldset. */
.federation-choice {
  border: 0;
  padding: 0;
  margin: var(--space-2) 0 0;
  display: grid;
  gap: var(--space-1);
}


/* ==========================================================================
   (issue #136, #137) La pagina della partita, come il mockup dell'utente
   (20.09.2026): la testa con squadre, risultato e stato; tre riquadri —
   tabellone, informazioni, esito —; il concetto del giorno; e da 1100px tre
   colonne — cronaca, formazioni, referto. Sotto, una colonna sola, nello
   stesso ordine del markup: l'ordine del DOM è la narrazione (requisito
   inline-hockey §10.1), la disposizione è tutta qui.
   ========================================================================== */
.app-main-match {
  max-width: 84rem;
}

.match-head {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-3) var(--space-4);
  margin-bottom: var(--space-3);
}

.match-head-main {
  min-width: 0;
  flex: 1 1 28rem;
}

.match-title {
  margin-bottom: var(--space-1);
  overflow-wrap: anywhere;
}

.match-status {
  display: inline-block;
  vertical-align: middle;
  margin-left: var(--space-2);
  padding: 2px var(--space-2);
  border-radius: var(--radius-sm);
  font: var(--type-label);
  border: 1px solid var(--color-border);
  color: var(--color-text-muted);
}

.match-status.is-final {
  background: var(--color-ok-bg);
  border-color: transparent;
  color: var(--color-ok-text);
}

/* Annullata: la parola la dice, il bordo tratteggiato la distingue anche
   in bianco e nero. */
.match-status.is-cancelled {
  border-style: dashed;
}

.match-competition {
  margin: 0 0 var(--space-2);
  color: var(--color-text-muted);
  font: var(--type-label);
  letter-spacing: 0.02em;
}

.match-meta {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-4);
  color: var(--color-text-muted);
}

.match-meta li {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}

.match-head-side {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-2);
}

.match-head-side .match-detail-actions {
  margin: 0;
  justify-content: flex-end;
}

.match-head-side .btn svg,
.match-sheet-head .btn svg {
  vertical-align: -3px;
}

.match-source {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font: var(--type-label);
}

.match-taken {
  margin: 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* Un bottone, non un form da compilare: via la cornice che `form` ha di
   suo. */
.match-take {
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
}

.match-notice {
  margin: 0 0 var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-ok-border);
  background: var(--color-ok-bg);
  color: var(--color-ok-text);
}

.match-notice.is-warning {
  border-color: var(--color-warning-border);
  background: var(--color-warning-bg);
  color: var(--color-warning-text);
}

.match-notice.is-error {
  border-color: var(--color-danger-border);
  background: var(--color-danger-bg);
  color: var(--color-danger-text);
}

/* Le schede del mockup sono ancore della stessa pagina: una sola pagina,
   si scorre (§10.1). */
.match-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin: 0 0 var(--space-4);
  border-bottom: 1px solid var(--color-border);
}

.match-tabs a {
  padding: var(--space-2) 0;
  color: var(--color-text-muted);
  font: var(--type-label);
  text-decoration: none;
  border-bottom: 3px solid transparent;
  margin-bottom: -1px;
}

.match-tabs a:hover,
.match-tabs a:focus-visible {
  color: var(--color-primary-dark);
  border-bottom-color: var(--color-primary);
}

.match-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  min-width: 0;
}

.match-card h2 {
  font: var(--type-title);
  margin-bottom: var(--space-3);
}

.match-overview {
  display: grid;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
  scroll-margin-top: var(--space-4);
}

@media (min-width: 900px) {
  .match-overview {
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  }
}

@media (min-width: 1100px) {
  .match-overview {
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr) minmax(10rem, 0.5fr);
  }
}

.match-sides {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  gap: var(--space-3);
}

.match-side {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
}

.match-side.is-away {
  flex-direction: row-reverse;
  text-align: right;
}

.match-side-name {
  display: flex;
  flex-direction: column;
  font: var(--type-body-strong);
  overflow-wrap: anywhere;
}

.match-side-name small {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.match-side.is-ours .match-side-name {
  color: var(--color-primary-dark);
}

.match-crest {
  flex: none;
  width: 3rem;
  height: 3rem;
  object-fit: contain;
}

.match-monogram {
  display: inline-grid;
  place-items: center;
  border-radius: 50%;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  font: var(--type-label);
  color: var(--color-text-muted);
}

.match-big-score {
  font: var(--type-figure);
  font-size: 2.75rem;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.match-periods {
  width: 100%;
  margin-top: var(--space-4);
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
}

.match-periods th,
.match-periods td {
  padding: var(--space-2);
  border-top: 1px solid var(--color-border);
  text-align: center;
}

.match-periods th[scope="row"],
.match-periods thead th:first-child {
  text-align: left;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.match-periods thead th:first-child {
  font: var(--type-label);
  color: var(--color-text);
}

.match-periods td {
  font: var(--type-body-strong);
}

.match-info dl {
  margin: 0;
}

.match-info dl > div {
  display: grid;
  grid-template-columns: 9rem minmax(0, 1fr);
  gap: var(--space-2);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--color-border);
}

.match-info dl > div:last-child {
  border-bottom: 0;
}

.match-info dt {
  color: var(--color-text-muted);
  font: var(--type-caption);
}

.match-info dd {
  margin: 0;
  overflow-wrap: anywhere;
}

.match-outcome {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  text-align: center;
  background: var(--color-bg);
}

.match-outcome p {
  margin: 0;
  display: flex;
  flex-direction: column;
  font: var(--type-title);
}

.match-outcome.is-win {
  background: var(--color-primary-soft);
  color: var(--color-primary-dark);
}

.match-link {
  margin: 0 0 var(--space-4);
}

.match-link > summary {
  cursor: pointer;
  color: var(--color-text-muted);
  font: var(--type-label);
  margin-bottom: var(--space-2);
}

.match-federation {
  display: grid;
  gap: var(--space-3);
  align-items: start;
  margin-bottom: var(--space-5);
}

@media (min-width: 1100px) {
  .match-federation {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

.match-federation > section {
  scroll-margin-top: var(--space-4);
}

/* La cronaca. */
.match-filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}

.match-filter-input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.match-filter {
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
  background: var(--color-bg);
  font: var(--type-label);
  cursor: pointer;
}

.match-filter-input:checked + .match-filter {
  background: var(--color-primary-dark);
  border-color: var(--color-primary-dark);
  color: var(--color-primary-contrast);
}

.match-filter-input:focus-visible + .match-filter {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.match-events:has(.match-filter-input[value="goal"]:checked) .match-event:not(.is-goal),
.match-events:has(.match-filter-input[value="penalty"]:checked) .match-event:not(.is-penalty),
.match-events:has(.match-filter-input[value="timeout"]:checked) .match-event:not(.is-timeout) {
  display: none;
}

.match-period + .match-period {
  margin-top: var(--space-3);
}

.match-period-title {
  margin: 0;
  padding: var(--space-2) var(--space-3);
  background: var(--color-bg);
  border-radius: var(--radius-sm);
  font: var(--type-label);
}

.match-period-title span {
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}

.match-event-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.match-event {
  display: grid;
  grid-template-columns: 3.2rem 1.5rem 3rem minmax(0, 1fr);
  gap: var(--space-2);
  align-items: start;
  padding: var(--space-2) var(--space-1);
  border-bottom: 1px solid var(--color-border);
}

.match-event:last-child {
  border-bottom: 0;
}

.match-event-clock,
.match-event-score {
  font-variant-numeric: tabular-nums;
  padding-top: 1px;
}

.match-event-icon {
  color: var(--color-text-muted);
  line-height: 0;
}

.match-event.is-penalty .match-event-icon {
  color: var(--color-danger-text);
}

.match-event.is-timeout .match-event-icon {
  color: var(--color-primary);
}

.match-event-score {
  font: var(--type-body-strong);
}

.match-event.is-ours .match-event-score {
  color: var(--color-danger-text);
}

.match-event-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.match-event-headline {
  font: var(--type-body-strong);
}

.match-event-detail {
  color: var(--color-text-muted);
  font: var(--type-caption);
}

/* Le formazioni: la nostra aperta, l'altra chiusa. */
.match-lineups {
  display: grid;
  gap: var(--space-3);
}

.match-lineup > summary {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  cursor: pointer;
  list-style: none;
}

.match-lineup > summary::-webkit-details-marker {
  display: none;
}

.match-lineup > summary::after {
  content: "›";
  margin-left: auto;
  font-size: 1.4rem;
  color: var(--color-text-muted);
  transition: transform 0.15s;
}

.match-lineup[open] > summary::after {
  transform: rotate(90deg);
}

.match-lineup > summary h2 {
  margin: 0;
}

.match-lineup .match-crest {
  width: 2.5rem;
  height: 2.5rem;
}

.match-lineup-table {
  width: 100%;
  margin-top: var(--space-3);
  border-collapse: collapse;
}

.match-lineup-table th {
  font: var(--type-thead);
  letter-spacing: var(--type-thead-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
  text-align: left;
  padding: var(--space-2);
  background: var(--color-bg);
}

.match-lineup-table td {
  padding: var(--space-1) var(--space-2);
  border-bottom: 1px solid var(--color-border);
}

.match-lineup-jersey,
.match-lineup-role {
  font-variant-numeric: tabular-nums;
  color: var(--color-text-muted);
  width: 3rem;
}

.match-lineup-role abbr {
  text-decoration: none;
}

.match-lineup-coach {
  margin: var(--space-3) 0 0;
  display: flex;
  flex-direction: column;
}

/* Il referto: la nostra copia, incorporata. */
.match-sheet-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  margin-bottom: var(--space-3);
}

.match-sheet-head p {
  margin: 0;
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
}

.match-sheet-head p span {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.match-sheet-kind {
  flex: none;
  padding: var(--space-2) var(--space-1);
  border-radius: 4px;
  background: var(--color-danger-bg);
  color: var(--color-danger-text);
  font: var(--type-eyebrow);
}

.match-sheet-frame {
  display: block;
  width: 100%;
  height: 34rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  margin-bottom: var(--space-2);
}

@media (max-width: 899px) {
  .match-tabs {
    gap: 0 var(--space-3);
  }

  .match-head-side {
    align-items: flex-start;
  }

  .match-head-side .match-detail-actions {
    justify-content: flex-start;
  }

  .match-big-score {
    font-size: 2rem;
  }

  .match-crest {
    width: 2.25rem;
    height: 2.25rem;
  }

  .match-sheet-frame {
    height: 26rem;
  }
}

@media print {
  .match-take,
  .match-link,
  .match-filters,
  .match-sheet-frame {
    display: none;
  }
}

/* --------------------------------------------------------------------------
   (issue #139) «Chi è chi»: il roster della pagina della federazione accanto
   ai tesserati. Una proposta è un `fieldset` con la riga della federazione
   nella legenda e le risposte in fila (`.radio-row`); gli elenchi dei già
   abbinati e dei senza abbinamento sono righe semplici, senza elenco puntato.
   -------------------------------------------------------------------------- */

.federation-proposals,
.federation-persons-list {
  list-style: none;
  padding: 0;
  margin: 0 0 var(--space-4);
  display: grid;
  gap: var(--space-2);
}

.federation-proposal {
  margin: 0;
}

.federation-proposal legend .field-hint {
  margin-left: var(--space-2);
}

/* Il profilo federativo sulla scheda persona: cercarlo dalla licenza,
   scriverlo a mano, scioglierlo. Gesti in fila, senza la cornice che `form`
   porta di suo, come lo scarico delle statistiche: nel riquadro «Link» chi
   non è abbinato, nel menu ⋮ chi lo è. */
.profile-link form,
.profile-menu-panel form {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  background: none;
  border: 0;
  padding: 0;
  margin: var(--space-3) 0 0;
}

.person-federation-player input {
  width: min(24rem, 100%);
}


/* --------------------------------------------------------------------------
   (issue #141) La classifica del girone sulla pagina della squadra. La
   tabella è quella del roster; qui solo i numeri allineati a destra, in cifre
   della stessa larghezza, e la riga della squadra — in grassetto **e** con
   «noi» scritto, perché il peso da solo non si legge ovunque.
   -------------------------------------------------------------------------- */

.competition-standings {
  margin-top: var(--space-5);
}

.competition-standings h3 {
  margin-bottom: var(--space-1);
}

.competition-standings-table .competition-figure {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.competition-standings-us th,
.competition-standings-us td {
  font-weight: 700;
}

.competition-us-mark {
  font-weight: 400;
  color: var(--color-text-muted);
}

/* ==========================================================================
   (23.09.2026, docs/proposta-profilo/) L'avatar: una foto, un simbolo della
   palette o le iniziali, nei tre posti in cui compare — l'intestazione del
   telefono, il fondo della sidebar, la testata del profilo. La macro è
   `partials/avatar.html`; qui le tinte e il contenuto del tondo, la misura
   la dà la classe di chi lo ospita.
   ========================================================================== */

/* Le tinte della palette, una per simbolo e fisse. Variabili nuove e non
   `--fam-*` né `--color-block-*`: quei colori dicono già un tema o un
   blocco di stagione, e un avatar non deve sembrare dire niente del genere.
   Tutte portano un simbolo bianco sopra a più di 4,5:1. */
:root {
  --avatar-tint-casco: #115E59;
  --avatar-tint-stick: #A84B17;
  --avatar-tint-pattino: #3457A6;
  --avatar-tint-porta: #6B3FA0;
  --avatar-tint-pallina: #B0306A;
  --avatar-tint-fischietto: #3E6B3A;
}

.avatar-face {
  overflow: hidden;
}

.avatar-face img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.avatar-face svg {
  width: 62%;
  height: 62%;
}

.avatar-tint-casco { --avatar-tint: var(--avatar-tint-casco); }
.avatar-tint-stick { --avatar-tint: var(--avatar-tint-stick); }
.avatar-tint-pattino { --avatar-tint: var(--avatar-tint-pattino); }
.avatar-tint-porta { --avatar-tint: var(--avatar-tint-porta); }
.avatar-tint-pallina { --avatar-tint: var(--avatar-tint-pallina); }
.avatar-tint-fischietto { --avatar-tint: var(--avatar-tint-fischietto); }

/* Un simbolo è bianco sulla sua tinta, dovunque stia: nell'intestazione
   verde, sulla sidebar scura, nella pagina chiara. */
.avatar-face[class*="avatar-tint-"] {
  background: var(--avatar-tint);
  color: #fff;
}

.sidebar-me-avatar.avatar-face[class*="avatar-tint-"],
.sidebar-me-avatar.avatar-face-photo {
  border-color: transparent;
}

/* --- La testata del profilo ------------------------------------------------ */

.account-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

/* Il contenitore posizionato del foglio: il foglio si apre sotto l'avatar,
   fuori dal flusso, o da aperto spingerebbe il nome lontano. */
.account-avatar-wrap {
  position: relative;
  flex: none;
}

.account-avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 5.5rem;
  height: 5.5rem;
  border-radius: 50%;
  background: var(--color-text);
  color: #fff;
  font: 700 2rem / 1 var(--font-cond);
}

.account-head-text {
  min-width: 0;
}

.account-head-name {
  margin: 0;
  font: var(--type-section);
  overflow-wrap: anywhere;
}

.account-head-role {
  margin: 2px 0 0;
  color: var(--color-text-muted);
}

/* La matita che morde l'angolo dell'avatar: il `<summary>` del foglio. Il
   bordo del colore di fondo la stacca dal tondo. */
.avatar-editor > summary {
  position: absolute;
  right: -4px;
  bottom: -4px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 50%;
  background: var(--color-surface);
  border: 2px solid var(--color-bg);
  box-shadow: var(--shadow-md);
  color: var(--color-text);
  cursor: pointer;
  list-style: none;
}

.avatar-editor > summary::-webkit-details-marker {
  display: none;
}

.avatar-editor > summary:hover {
  background: var(--color-bg);
}

.avatar-editor > summary:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.avatar-editor[open] > summary {
  background: var(--color-text);
  color: var(--color-surface);
}

/* Il foglio: dentro la pagina, non un livello a schermo intero, e stretto
   da solo sul telefono. */
.avatar-editor-panel {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 20;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-2);
  padding: var(--space-3);
  width: min(26rem, calc(100vw - 2 * var(--space-3)));
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
}

.avatar-editor-title {
  margin: 0;
  font: var(--type-label);
}

.avatar-editor-panel label {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.avatar-editor-panel .field-hint,
.avatar-editor-panel .banner {
  margin: 0;
}

/* Le due schede: due radio nascosti alla vista ma non alla tastiera, e le
   loro etichette vestite da pulsanti. La scheda accesa e il suo pannello li
   dice `:has()` sul foglio: senza JavaScript. */
.avatar-tabs {
  display: flex;
  gap: var(--space-1);
  width: fit-content;
  padding: 2px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
}

.avatar-tab-input,
.avatar-swatch-input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.avatar-editor-panel .avatar-tabs label {
  display: inline-flex;
  padding: 6px 14px;
  border-radius: 6px;
  font: var(--type-button-sm);
  color: var(--color-text-muted);
  cursor: pointer;
}

.avatar-tab-input:checked + label {
  background: var(--color-surface);
  color: var(--color-text);
  box-shadow: var(--shadow-sm);
}

.avatar-tab-input:focus-visible + label {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.avatar-tab-panel {
  display: none;
}

.avatar-editor-panel:has(#avatar-tab-foto:checked) .avatar-tab-panel-foto,
.avatar-editor-panel:has(#avatar-tab-palette:checked) .avatar-tab-panel-palette {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.avatar-photo-current {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: 0;
}

.avatar-photo-preview {
  display: flex;
  flex: none;
  width: 3.5rem;
  height: 3.5rem;
  border-radius: 50%;
}

/* Le pastiglie: icona e didascalia, sempre. La scelta la dice il bordo
   pieno, mai il colore da solo; le altre sono appena spente. */
.avatar-swatches {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(4.25rem, 1fr));
  gap: var(--space-1);
  margin: 0;
  padding: 0;
  border: 0;
}

.avatar-editor-panel .avatar-swatch {
  align-items: center;
  padding: var(--space-2) var(--space-1);
  border: 2px solid transparent;
  border-radius: var(--radius-sm);
  font: var(--type-caption);
  color: var(--color-text-muted);
  text-align: center;
  cursor: pointer;
}

.avatar-swatch-face {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  background: var(--color-text);
  color: #fff;
  font: 700 1.125rem / 1 var(--font-cond);
  opacity: 0.8;
}

.avatar-swatch:hover .avatar-swatch-face {
  opacity: 1;
}

.avatar-editor-panel .avatar-swatch:has(.avatar-swatch-input:checked) {
  border-color: var(--color-text);
  color: var(--color-text);
  font-weight: 600;
}

.avatar-swatch:has(.avatar-swatch-input:checked) .avatar-swatch-face {
  opacity: 1;
}

.avatar-swatch:has(.avatar-swatch-input:focus-visible) {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* --- Le due colonne: ciò che modifichi, ciò che sei ---------------------- */

/* Incarichi, squadre e proposte in sola lettura, ognuno nella sua carta:
   nella seconda colonna da 900px, sotto l'accesso prima. */
.profile-society {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.profile-society > section,
.profile-access {
  padding: var(--space-4);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}

.profile-access {
  margin-top: var(--space-4);
}

/* Con l'accesso configurato la carta porta i due form delle credenziali:
   dentro la carta non sono carte a loro volta, li separa un filetto. */
.profile-access .profile-credentials {
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
}

.profile-access .profile-credentials + .profile-credentials {
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
}

.profile-society h2,
.profile-access h2 {
  margin-top: 0;
}

.profile-society > section > :last-child,
.profile-access > :last-child {
  margin-bottom: 0;
}

@media screen and (max-width: 899px) {
  .profile-society {
    margin-top: var(--space-4);
  }
}

/* Le squadre affidate: pastiglie che vanno a capo da sole. */
.team-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.team-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  padding: 6px var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--color-chip-bg);
  color: var(--color-primary-dark);
  font: var(--type-body-strong);
  text-decoration: none;
}

.team-chip:hover {
  text-decoration: underline;
}

.team-chip-code {
  font: var(--type-caption);
  opacity: 0.8;
}

@media print {
  .account-head .avatar-editor {
    display: none;
  }
}


/* ==========================================================================
   (issue #147) La pagina della sincronizzazione in Amministrazione: i
   quattro lanci, il resoconto per squadra e la completezza. Riprodotta dal
   mockup approvato (`docs/proposta-sincronizzazione/mockup.html`, stati A e
   D). Ciò che manca è `.sync-gap`, giallo con «⚠»: una mancanza, mai un
   fallito, e mai rosso.
   ========================================================================== */

.sync-launches {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}

.sync-launch-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.sync-launch-card h3 {
  font: var(--type-title);
  margin: 0;
}

.sync-launch-desc {
  margin: 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.sync-launch-card .btn {
  width: 100%;
  margin-top: auto;
}

.sync-launch-card.is-disabled {
  background: var(--color-bg);
}

.sync-launch-card.is-disabled h3 {
  color: var(--color-text-muted);
}

.sync-launch-card .btn:disabled {
  background: var(--color-bg);
  border-color: var(--color-border);
  color: var(--color-text-muted);
  cursor: not-allowed;
}

/* (issue #148) Il `<form>` che avvolge un lancio o «Ferma» non è una carta
   (la regola generica di `form`): il bottone resta figlio della carta, e il
   suo `margin-top: auto` lo porta in fondo come quelli spenti. */
.sync-launch-card form,
.sync-running-head form {
  display: contents;
}

.sync-launch-meta {
  margin: 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.sync-launch-meta strong {
  color: var(--color-text);
  font-weight: 600;
}

/* (issue #148) Il passaggio che gira, sopra i lanci (mockup, stato B): la
   barra è `.block-progress-*`, la stessa dei blocchi, riusata pari pari. */
.sync-running {
  background: var(--color-surface);
  border: 1px solid var(--color-primary);
  border-left: 4px solid var(--color-primary);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  margin-bottom: var(--space-4);
}

.sync-running-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
  flex-wrap: wrap;
}

.sync-running-title {
  font: var(--type-title);
  margin: 0;
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.sync-running-count {
  margin: 0 0 4px;
  font: var(--type-caption);
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}

.sync-running-eta {
  margin: 6px 0 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.sync-running-now {
  margin: var(--space-3) 0 0;
  font: var(--type-body-strong);
  display: flex;
  align-items: baseline;
  gap: 6px;
  flex-wrap: wrap;
}

.sync-running-now[hidden] {
  display: none;
}

.sync-running-now .eyebrow {
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-primary-dark);
}

.sync-running-stages {
  margin: 4px 0 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.sync-running-origin {
  margin: var(--space-3) 0 0;
  padding-top: var(--space-2);
  border-top: 1px solid var(--color-border);
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.sync-running-refresh {
  font: var(--type-caption);
  color: var(--color-primary-dark);
}

/* Sul telefono: il titolo sopra «Ferma», e la spiegazione dei dieci
   secondi resta fuori — la stima basta (mockup, cornice del telefono). */
@media (max-width: 1023px) {
  .sync-running-head {
    flex-direction: column;
    align-items: flex-start;
  }

  .sync-running-title {
    font-size: 1rem;
  }

  .sync-running-why {
    display: none;
  }
}

.sync-report-when {
  margin: 0 0 var(--space-3);
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.sync-interrupt-note {
  margin: 0 0 var(--space-3);
  padding: var(--space-2) var(--space-3);
  background: var(--color-warning-bg);
  border-left: 3px solid var(--color-warning-border);
  border-radius: var(--radius-sm);
  color: var(--color-warning-text);
  font: var(--type-caption);
}

.sync-team-list {
  list-style: none;
  margin: 0 0 var(--space-3);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.sync-team {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.sync-team-summary,
.sync-team-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  padding: var(--space-2) var(--space-3);
}

.sync-team-summary {
  list-style: none;
  cursor: pointer;
}

.sync-team-summary::-webkit-details-marker {
  display: none;
}

.sync-team-summary::before {
  content: "▸";
  color: var(--color-text-muted);
  font-size: 0.8em;
  transition: transform 0.1s;
}

.sync-team[open] > .sync-team-summary::before {
  transform: rotate(90deg);
}

.sync-team-name {
  font: var(--type-body-strong);
}

.sync-team-category,
.sync-team-not-reached {
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.sync-team-summary .badge {
  margin-left: auto;
}

.sync-team-body {
  padding: 0 var(--space-3) var(--space-3);
  border-top: 1px solid var(--color-border);
}

.sync-team-body p {
  margin: var(--space-2) 0 0;
  font: var(--type-caption);
  overflow-wrap: anywhere;
}

.sync-team-body .sync-sub-head,
.sync-sub-head {
  margin: var(--space-2) 0 2px;
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.sync-list {
  margin: 0;
  padding-left: 1.1rem;
  font: var(--type-caption);
  overflow-wrap: anywhere;
}

.sync-list.is-failed {
  list-style: none;
  border-left: 3px double var(--color-text-muted);
  padding-left: 1.4rem;
  margin-left: -1px;
}

.sync-competitions {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.sync-competitions li {
  font: var(--type-caption);
  display: flex;
  gap: 6px;
  align-items: baseline;
  overflow-wrap: anywhere;
}

.sync-competitions li::before {
  content: "·";
  color: var(--color-text-muted);
}

.sync-completeness-table {
  width: 100%;
  border-collapse: collapse;
  font: var(--type-caption);
  font-variant-numeric: tabular-nums;
}

.sync-completeness-table th,
.sync-completeness-table td {
  padding: var(--space-2);
  text-align: right;
  border-bottom: 1px solid var(--color-border);
}

.sync-completeness-table thead th {
  font: var(--type-thead);
  letter-spacing: var(--type-thead-ls);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.sync-completeness-table thead th:first-child,
.sync-completeness-table th[scope="row"] {
  text-align: left;
}

.sync-completeness-table th[scope="row"] {
  font: var(--type-body-strong);
}

.sync-completeness-table th[scope="row"] span {
  display: block;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

.sync-completeness-table tbody tr:last-child th,
.sync-completeness-table tbody tr:last-child td {
  border-bottom: 0;
}

.sync-ok {
  color: var(--color-text);
}

.sync-gap {
  color: var(--color-warning-text);
  font-weight: 600;
}

.sync-gap::after {
  content: " ⚠";
}

.sync-completeness-foot {
  margin: var(--space-2) 0 0;
  font: var(--type-caption);
  color: var(--color-text-muted);
}

/* Sotto 900px la tabella diventa carte, come `.progress-table`: le
   intestazioni restano per lo screen reader, ogni cella porta la sua
   etichetta. Niente scorrimento orizzontale. */
@media (max-width: 900px) {
  .sync-launches {
    grid-template-columns: minmax(0, 1fr);
  }

  .sync-completeness-table,
  .sync-completeness-table tbody {
    display: block;
  }

  .sync-completeness-table thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  .sync-completeness-table tr {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 2px var(--space-3);
    padding: var(--space-3);
    margin-bottom: var(--space-2);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
  }

  .sync-completeness-table th,
  .sync-completeness-table td {
    display: block;
    padding: 0;
    border: 0;
    text-align: left;
  }

  .sync-completeness-table th[scope="row"] {
    grid-column: 1 / -1;
    margin-bottom: 4px;
  }

  .sync-completeness-table td::before {
    content: attr(data-label);
    display: block;
    font: var(--type-eyebrow);
    letter-spacing: var(--type-eyebrow-ls);
    text-transform: uppercase;
    color: var(--color-text-muted);
  }
}
