/* =============================================================================
   M-Cube 2026 — base.css

   Tre cose e basta:
     1. token, come custom properties in :root
     2. reset minimo
     3. tipografia ed elementi globali

   Tutto il resto sta nel CSS del modulo che lo riguarda
   (assets/css/modules/<gruppo>/<modulo>.css), che legge questi token con var().
   Non aggiungere qui regole di layout di un singolo modulo: base.css lo scarica
   ogni pagina.

   VALORI, stato al 25 agosto 2026. **Non sono più segnaposto.** Vengono dal file
   Figma *design* `qz5sg4DeraRvBXbPiEDdag`, frame `homepage` (nodo 649:2349), che
   ha sostituito i Make come sorgente: colori, font, corpi e interlinee sono
   quelli. I Make restano la specifica del *movimento* (OBIETTIVO 04.1), non dei
   valori.

   Un avvertimento che vale per chi ci torna: nel file di design **i colori non
   sono variabili Figma**, solo la tipografia lo è. `get_variable_defs` restituisce
   gli otto stili di testo e nessuna tinta; le tinte si leggono nodo per nodo. Se
   un domani il design system le pubblica come variabili, questa sezione si
   riallinea a quelle — ma oggi i valori qui sotto sono l'unica copia.

   Nel design il tracking è in **percentuale del corpo**, non in px: `label-md`
   dice `letterSpacing: 16` e sul testo a 14px rende 2,24px. Qui è in `em`, che è
   la stessa cosa scritta in CSS.

   Nessun build step: questo file è esattamente quello che il browser scarica.
   Si usa il nesting CSS nativo, supportato da tutti i browser correnti.

   ========================================================================== */

/* -----------------------------------------------------------------------------
   0. Font

   Raleway, self-hostata. Il Make la importa da fonts.googleapis.com: nel tema
   non si fa, perché un font servito da terzi è una richiesta a un dominio
   esterno, quindi una voce da gestire nel consenso di Complianz. Qui i byte sono
   nostri. Raleway è OFL, self-hostarla è lecito.

   Un file per subset, non uno per peso: il woff2 di Google è VARIABILE (asse
   wght 100–900, verificato: contiene fvar, avar, HVAR, STAT), quindi lo stesso
   file copre il 500 e il 900 del design e qualunque valore in mezzo. Le due
   @font-face qui sotto sono lo stesso font su due intervalli di caratteri.

   `latin-ext` si scarica solo se in pagina compare un glifo fuori da `latin`:
   le 6 lingue del sito sono tutte latine, quindi quasi mai. È rete risparmiata
   senza rinunciare alla copertura.
   -------------------------------------------------------------------------- */

@font-face {
    font-family: "Raleway";
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url("../fonts/raleway-latin.woff2") format("woff2");
    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: "Raleway";
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url("../fonts/raleway-latin-ext.woff2") format("woff2");
    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;
}

/* -----------------------------------------------------------------------------
   1. Token
   -------------------------------------------------------------------------- */

:root {
    /* --- Colore -------------------------------------------------------------
       Nomi per ruolo, non per tinta: `--color-page-bg`, non `--color-blue`.

       **Il sito è scuro sempre.** Non c'è un tema chiaro, e i due blocchi
       `prefers-color-scheme` / `data-theme` che stavano qui sotto sono stati
       tolti il 25 agosto: il file di design ha una palette sola, e un fondo
       pagina che l'editor scegli per pagina non può convivere con un'inversione
       automatica — l'editore mette il suo blu e il browser gliela ribalta. Se un
       domani servisse un tema chiaro, torna come riassegnazione di questi token:
       i moduli non lo sapranno, perché leggono solo `var(--…)`. */

    /* Fondo della pagina, e l'unico colore che **l'editor scelga**: un color
       picker per pagina, con questo come default. Il valore scelto arriva come
       override di questa property (inc/page-background.php), quindi tutto ciò
       che ci si appoggia — il pattern, le superfici derivate — segue senza
       sapere niente.

       #05355a è il fondo del componente `sfondo` del design (nodo 649:2370). */
    --color-page-bg: #05355a;

    /* Il pattern del fondo: cerchi scuri sfumati, uno per colonna, su una
       piastrella alta poco meno di una volta e mezza la propria larghezza. Vedi
       la regola su `body` più sotto, che è dove viene composto. */
    --pattern-blob: rgb(0 0 0 / 0.75);
    --pattern-tile: 100vw;
    --pattern-ratio: 1.448;

    /* Sopra i cerchi, la griglia di quadrati e la grana: due piastrelle da 208px,
       che è la misura dei due nodi del design da cui sono esportate (1111:4270 e
       1111:4273). In px e non in vw, perché la cella della griglia è 104px a
       qualunque larghezza di schermo. */
    --pattern-grid-tile: 208px;
    --pattern-noise-tile: 208px;

    /* Superficie sopra il fondo. **Derivata, non è nel design**: serve al footer
       e ai template di servizio, che il design non copre ancora. Espressa come
       mescolanza del fondo pagina e non come esadecimale, così segue il colore
       che l'editor ha scelto invece di litigarci. */
    --color-surface: color-mix(in srgb, var(--color-page-bg) 84%, #000);

    /* Fondo dietro un media — la card di una scena, il riquadro di un'immagine —
       per il tempo in cui il filmato non è ancora arrivato o l'immagine manca.
       **Non è il fondo di una sezione**: nel design le sezioni lasciano vedere il
       fondo pagina col suo pattern, e dipingerlo di nuovo lo coprirebbe.

       Derivato dal fondo pagina, non un esadecimale a sé. Era il #1a1717 del
       Make, un grigio caldo che accanto al blu del design si vedeva per quello che
       era: un colore di un altro progetto, sotto ogni card in attesa del filmato.
       Così invece è scuro e appartiene alla pagina, e segue il colore che l'editor
       ha scelto.

       Il testo dentro una scena è `--color-scene-text`, non
       `--color-text-primary`: oggi valgono lo stesso, ma una scena sta sopra un
       filmato e resta chiara anche se un domani la pagina non lo è. */
    --color-scene-bg: color-mix(in srgb, var(--color-page-bg) 34%, #000);
    --color-scene-text: #ffffff;

    /* I quattro livelli di bianco del design, che sono un sistema e non quattro
       casi: pieno per il testo, 0,7 per il menu, 0,5 per i vicini nella ruota dei
       servizi, 0,16 per i lontani e per il paragrafo d'apertura. La ruota li
       ottiene come `opacity` sulla riga, che sul bianco è la stessa cosa. */
    --color-text-primary: #ffffff;
    --color-text-secondary: rgb(255 255 255 / 0.7);
    --color-text-dim: rgb(255 255 255 / 0.5);
    --color-text-faint: rgb(255 255 255 / 0.16);

    /* L'unico accento del design è il blu che tinge il fondo delle headline in
       display: rgba(29,104,196,.8) sopra il bianco. Vedi `.display-gradient`. */
    --color-accent: #1d68c4;
    --color-accent-hover: #164f96;
    --color-accent-contrast: #ffffff;

    /* Il filetto sotto il titolo della card dei case study, che nel design è
       l'unico bordo esistente: rgba(255,255,255,.2). */
    --color-border: rgb(255 255 255 / 0.2);

    /* Bianco pieno: su questi fondi è l'unica cosa che si veda sempre, e un
       outline di focus non può dipendere dal colore che l'editor ha scelto. */
    --color-focus: #ffffff;

    /* --- Tipografia ---------------------------------------------------------
       Raleway, dichiarata nella sezione 0 e servita da assets/fonts/. La stack
       di sistema resta come fallback per il tempo di `font-display: swap`. */
    --font-sans: "Raleway", system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    --font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;

    /* Scala fluida: clamp(min, preferito, max). Un solo valore per breakpoint
       invece di media query sulla tipografia. Il valore preferito è il corpo del
       design diviso 1512, la larghezza del frame: 72px fanno 4,76vw.

       **Gli stili del design, uno per riga.** Erano otto; `title-lower-xl` è il
       nono, arrivato il 1 settembre col titolo della pagina servizi — 128px in
       minuscolo, l'unico posto del design in cui un titolo non è in maiuscolo.
       `title-upper-xg` è il decimo, arrivato lo stesso giorno con l'archivio dei
       progetti (nodo 75:1858): 128px come il nono, ma **in maiuscolo**, con
       l'interlinea sotto il corpo — 104 su 128 — e un tracking negativo del 3%.
       Non è il nono scritto diverso: 136 e 104 di interlinea su un titolo alto
       128px sono due ritmi differenti, e sul primo il tracking è −2% invece di
       −3%. A destra il nome nel file
       Figma e la terna corpo/interlinea/tracking, perché interlinea e tracking
       non sono liberi: sono parte dello stile, e un titolo a 40px con
       l'interlinea del corpo non è lo stesso stile scritto un po' peggio, è un
       altro stile. Chi applica un corpo applica anche gli altri due — i token
       stanno nei due blocchi qui sotto.

       Restano fuori dal design le tre voci `--text-title-*`: il design salta da
       72 a 40 e in mezzo non ha niente, ma h1–h6 e il wysiwyg una scala in mezzo
       la vogliono. Sono **derivate**, marcate come tali, e se un domani il design
       copre gli articoli si sostituiscono. */
    --text-display-xl: clamp(3rem, 8.47vw, 8rem);   /* title-upper-xg 128 / 104 / -0,03em */
    --text-title-lower: clamp(3rem, 8.47vw, 8rem);  /* title-lower-xl 128 / 136 / -0,02em */
    --text-display: clamp(2.5rem, 4.76vw, 4.5rem);  /* title-upper-lg  72 / 64 / 0     */
    --text-title: clamp(1.75rem, 2.65vw, 2.5rem);   /* title-upper-md  40 / 40 / 0     */
    --text-body-l: clamp(1.25rem, 1.59vw, 1.5rem);  /* body-lg         24 / 40 / 0     */
    --text-body-m: 1.125rem;                        /* body-sm         18 / 28 / 0     */
    --text-body-s: 1rem;                            /* body-xs         16 / 24 / 0,01em */
    --text-label-l: 0.875rem;                       /* label-md        14 / 20 / 0,16em */
    --text-label: 0.75rem;                          /* label-sm        12 / 16 / 0,12em */
    --text-label-s: 0.625rem;                       /* label-sm 10     10 / 16 / 0,08em */
    --text-title-s: 1.25rem;                        /* title-upper-sm  20 / 24 / 0,04em */

    /* Derivati, non nel design: solo per h1–h6 e il contenuto redazionale.

       `--text-title-s` stava qui fino al 1 settembre. Adesso è nel blocco sopra
       perché il design ce l'ha: è `title-upper-sm`, l'etichetta in maiuscolo che
       porta il nome di una card o di un riquadro nella pagina di un servizio. Il
       valore non è cambiato — quello dedotto e quello del design coincidono — ma
       adesso ha interlinea e tracking suoi, che prima non aveva. */
    --text-title-xl: clamp(2.25rem, 3.7vw, 3.5rem);
    --text-title-m: clamp(1.375rem, 2vw, 1.75rem);

    /* `--leading-display` è sotto 1: il display è 72px su 64px di interlinea
       (0,889). Serve un token a sé perché un'interlinea negativa non si può usare
       su un titolo qualunque, solo sul display in maiuscolo.

       `--leading-solid` è l'interlinea dei titoli a 40px, che nel design è
       esattamente il corpo: 40 su 40. Stesso discorso, un caso diverso. */
    --leading-title-lower: 1.0625; /* 136 su 128 */
    --leading-display-xl: 0.8125;  /* 104 su 128 */
    --leading-display: 0.89;
    --leading-solid: 1;
    --leading-title-s: 1.2;   /* 24 su 20 */
    --leading-body-l: 1.667;  /* 40 su 24 */
    --leading-body-m: 1.556;  /* 28 su 18 */
    --leading-body-s: 1.5;    /* 24 su 16 */

    /* ⚠ Le tre etichette hanno **tre rapporti diversi**, e non è una sottigliezza:
       il design fissa l'interlinea in px, non in multipli del corpo, e a 12px sono
       16 mentre a 10px sono ancora 16. Con un rapporto solo il pulsante viene
       alto 54 invece dei 48 del design — misurato, non temuto. */
    --leading-label-l: 1.4286; /* 20 su 14 */
    --leading-label: 1.3333;   /* 16 su 12 */
    --leading-label-s: 1.6;    /* 16 su 10 */

    --leading-tight: 1.08;
    --leading-snug: 1.25;
    --leading-normal: 1.55;

    /* Le etichette in maiuscolo del design sono spaziate, e di quanto dipende dal
       corpo: più è piccolo, più larga la spaziatura in proporzione. Sono tre
       valori e non uno. */
    --tracking-label-l: 0.16em;
    --tracking-label: 0.12em;
    --tracking-label-s: 0.08em;

    /* `title-upper-sm` è spaziato come le etichette, ma meno: 4% invece del 12%.
       Non è un'etichetta, è un titolo piccolo. */
    --tracking-title-s: 0.04em;
    --tracking-body-s: 0.01em;
    --tracking-tight: -0.02em;
    --tracking-normal: 0;

    /* `title-upper-xg`, il titolo dell'archivio progetti: −3,84px su 128, che è
       −0,03em. È l'unico tracking negativo del design che non sia
       `--tracking-tight`, e va tenuto separato perché quello è −2% e appartiene a
       `title-lower-xl`: due titoli alti 128px con due spaziature diverse. */
    --tracking-display-xl: -0.03em;

    /* Il design usa tre pesi: 500 per il corpo e per il menu, 600 per le
       etichette in maiuscolo, 900 per i titoli. Regular e bold restano per i
       template di servizio e il wysiwyg. Sono pesi veri e non sintetizzati:
       Raleway qui è variabile sull'asse wght, quindi il 600 esiste come il 500. */
    --weight-regular: 400;
    --weight-medium: 500;
    --weight-semibold: 600;
    --weight-bold: 700;
    --weight-black: 900;

    /* --- Spazi --------------------------------------------------------------
       Scala geometrica: un modulo compone da questi, non da valori in px. */
    --space-3xs: 0.25rem;
    --space-2xs: 0.5rem;
    --space-xs: 0.75rem;
    --space-s: 1rem;
    --space-m: 1.5rem;
    --space-l: 2rem;
    --space-xl: 3rem;
    --space-2xl: 4rem;
    --space-3xl: 6rem;
    --space-4xl: 8rem;

    /* Ritmo verticale fra moduli: un modulo usa questo per il proprio padding,
       così la pagina resta coerente qualunque sia l'ordine dei moduli. */
    --space-section: clamp(var(--space-2xl), 8vw, var(--space-4xl));

    /* **Metà** stacco, per i moduli statici che si susseguono.

       Nel design fra il contenuto di due sezioni consecutive ci sono 160px, e li
       dichiarano tutte e due: i frame della pagina di un servizio hanno 160 sopra
       e 160 sotto, e **si sovrappongono di 160** — il fondo del frame 528:3601 e
       la cima del 530:3955 occupano gli stessi pixel. Il gap visibile è quindi
       160, non 320.

       Un modulo però possiede il proprio padding e deve reggere qualunque ordine,
       quindi non può contare sul vicino: ne mette metà per lato, e due moduli
       adiacenti fanno i 160 esatti. 80 su 1512 sono 5,29vw.

       `--space-section` resta quello dei moduli-scena, che sono alti una
       schermata e non si susseguono mai a due a due.

       ⚠ `flexible-content/logo-wall` mette invece 160 **per lato**: nella home non
       si vede perché ha due scene per vicine, ma il giorno che finisce accanto a un
       modulo statico è lui il posto da correggere, non questo token. */
    --space-section-half: clamp(var(--space-2xl), 5.29vw, 5rem);

    /* --- Layout -------------------------------------------------------------
       Nel design la pagina ha 32px di margine per lato su un frame da 1512,
       quindi la colonna di contenuto è 1448. `.container` mette il margine come
       padding *dentro* il proprio max-width, perciò il token è la somma:
       1448 + 64 = 1512, cioè il frame. */
    --container-max: 94.5rem;
    --container-narrow: 46rem;
    --gutter: clamp(var(--space-s), 2.12vw, var(--space-l));

    /* Altezza dell'header: 112px nel design, cioè 16 + 64 di logo + 32. Serve ai
       soli template che non aprono con una hero a pieno campo, perché l'header è
       in `absolute` e non occupa spazio — vedi la sezione Header. */
    --header-h: 7rem;

    /* Geometria della card video: il contratto fra la hero e il modulo dei
       servizi, ed è la ragione per cui questi quattro token stanno in base e non
       nei due CSS di modulo. Nel Make il video è un solo elemento che attraversa
       le due sezioni; qui sono due moduli indipendenti, ognuno col proprio
       video, e alla giunzione le due card devono trovarsi nello stesso punto,
       altrimenti il crossfade si vede saltare. Se un domani il design sposta la
       card, si sposta qui e i due moduli restano d'accordo.

       Card 716×918 a x=764 y=32 su una scena 1512×982, in percentuale della
       scena, che è grande come il viewport. Il file di design (nodo 649:2382)
       corregge la y del Make, che era 24: nel design i quattro margini attorno
       alla card sono **tutti 32px**, cioè il margine di pagina. La card è la
       colonna destra incassata del gutter su tre lati, non un riquadro con
       margini asimmetrici — e la colonna di testo a sinistra è larga uguale. */
    --scene-card-x: 50.53%;
    --scene-card-y: 3.26%;
    --scene-card-w: 47.35%;
    --scene-card-h: 93.48%;

    /* Quanto si **sovrappongono** due scene consecutive, cioè per quanto scroll le
       due sticky sono inchiodate insieme. Vedi le due regole di adiacenza nella
       sezione 3.

       ⚠ Non è la durata della dissolvenza: quella ne occupa **metà**, e il resto è
       margine. Il perché sta in `lib.js` → `SCENE_ENTER_FRACTION`, e in breve è
       questo: la sovrapposizione finisce esattamente dove la scena uscente si
       sgancia e comincia a scorrere via, quindi se la dissolvenza finisse là,
       coprire la uscente e vederla partire cadrebbero nello stesso frame — e
       coprirla è una decisione del main thread, mentre lo scroll è già stato
       composto. Scrollando di scatto si vedeva lampeggiare.

       Erano 100vh quando la dissolvenza durava tutta la sovrapposizione; sono 200
       perché la dissolvenza resti quella — 100vh — con 100vh di margine dietro. */
    --scene-handoff: 200vh;

    /* --- Raggi --------------------------------------------------------------
       Il design ne usa tre: 16px sul bottone e sui riquadri, 24px sulla card del
       video, 32px sulla card espandibile della pagina di un servizio. Sono
       `--radius-m`, `--radius-l` e `--radius-xl`. `--radius-s` resta segnaposto. */
    --radius-s: 0.375rem;
    --radius-m: 1rem;
    --radius-l: 1.5rem;
    --radius-xl: 2rem;
    --radius-full: 9999px;

    /* --- Movimento ---------------------------------------------------------
       Anche le animazioni GSAP e gli IntersectionObserver leggono da qui, via
       getComputedStyle, così non esistono durate scritte due volte. */
    --duration-fast: 150ms;
    --duration-base: 300ms;
    --duration-slow: 600ms;
    --duration-slower: 750ms;
    --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
    --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);

    /* Curva delle scene in scroll, dal Make: la usano il movimento verticale
       della ruota dei servizi (0,75s) e i cambi di opacità (0,55s). Sta accanto
       alle altre perché GSAP la legge da qui con token(), invece di riscriverla
       nel JS del modulo. */
    --ease-scene: cubic-bezier(0.25, 0, 0.2, 1);

    /* --- Livelli ----------------------------------------------------------- */
    --z-header: 100;
    --z-overlay: 200;
}

/* -----------------------------------------------------------------------------
   2. Reset
   -------------------------------------------------------------------------- */

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

* {
    margin: 0;
}

/* Lo scroll della pagina si **aggancia dove una sezione lo chiede**, con
   `scroll-snap-align` sulla sezione. Qui c'è solo la metà del contratto che
   riguarda il contenitore; chi si aggancia lo dichiara da sé, come per
   `data-scene`. **Oggi non lo chiede nessuno**: lo chiedeva il prefooter, che era
   alto una schermata, e dal 26 agosto quella CTA è un modulo-scena alto cinque —
   la cima di una scena non è un posto dove ha senso fermarsi. La riga resta perché
   la prima sezione che si aggancerà trovi il contenitore già pronto, e perché non
   fa niente finché nessuno la usa.

   ⚠ **`proximity` e non `mandatory`**, ed è la scelta che tiene in piedi il resto
   della pagina. Questa pagina è fatta di scene in pin scrubbate dallo scroll: con
   `mandatory` il contenitore riporta la posizione al punto d'aggancio più vicino
   ogni volta che lo scroll si ferma, quindi ogni pausa in mezzo a una scena
   diventerebbe uno strappo, e i tratti lontani da un punto d'aggancio possono
   perfino diventare irraggiungibili. Con `proximity` l'aggancio scatta solo se ci
   si ferma **vicino** a un punto, e le scene — che non ne dichiarano nessuno — non
   se ne accorgono.

   Sta fuori dalla media query delle scene apposta: è una proprietà del
   contenitore, e non fa niente finché nessun figlio si aggancia. Il vincolo di
   larghezza sta sulla sezione che si aggancia, dove il layout lo decide. */
html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
    scroll-snap-type: y proximity;
}

/* Movimento ridotto: nessun aggancio. Un aggancio è uno scorrimento che l'utente
   non ha chiesto — con `scroll-behavior: auto` diventa perfino un salto secco
   invece di uno scorrimento — e chi ha espresso quella preferenza ha chiesto di
   non averlo. Il blocco della sezione 2 azzera transizioni e animazioni, non
   questo. */
@media (prefers-reduced-motion: reduce) {
    html {
        scroll-snap-type: none;
    }
}

/* Il fondo della pagina: un colore e tre livelli sopra, e il colore lo scegli
   l'editor (inc/page-background.php). Dal basso: i cerchi sfumati, la griglia di
   quadrati, la grana. Nella lista di `background-image` il primo è quello in
   cima, e `background-blend-mode` va per livello nello stesso ordine.

   **I cerchi** sono il pattern del design, **procedurale**, un fill shader di
   Figma: cerchi molto sfumati, quasi larghi come il frame, uno per riga, con un
   intervallo di fondo fra l'uno e l'altro. Misurato sul render del nodo 649:2370 —
   piastrella 1512×2190 px, cerchio Ø ~1400, centro nero all'incirca al 75% di
   opacità, e la sfumatura che arriva al fondo pieno poco oltre il bordo del
   cerchio.

   Qui è un `radial-gradient` e non un'immagine, per tre ragioni che vanno
   insieme: si **tinge** dal colore scelto invece di essere un PNG per colore, non
   pesa niente, e non ha risoluzione. Le tre fermate riproducono la campana
   misurata: pieno fino a metà raggio, poi due tratti di caduta sempre più rapidi.

   `farthest-side` dal centro di una piastrella più alta che larga dà un raggio
   pari a metà dell'altezza, ed è su quello che valgono le percentuali.

   La piastrella è legata alla **larghezza del viewport**, non a px: nel design il
   cerchio è larghezza-di-schermo, e su un telefono deve restare tale, altrimenti
   diventa un pois. `background-attachment` resta quello di default, quindi il
   fondo scorre con la pagina come nel design — dove `sfondo` è un elemento alto
   6094px dentro la pagina, non un livello fisso.

   **La griglia** è il nodo 1111:4270 (`Mask group`): una piastrella 208×208 con un
   filetto bianco da 2px al 2% ogni 104px, in `assets/img/square-pattern.svg`.
   L'export di Figma è stato ripulito — via la maschera, che era un rettangolo
   pieno quanto la piastrella e non mascherava niente, via gli id, e il tracciato
   che disegnava anche le celle fuori piastrella è diventato i quattro segmenti che
   si vedono. Il 2% sta **nel file**, perché un livello di `background-image` non
   ha un'opacità propria.

   **La grana** è il nodo 1111:4273 (`Rectangle 56`): un rettangolo bianco con
   l'effetto Noise di Figma e blend multiply. Sul bianco il multiply non cambia
   niente, e i punti più scuri scuriscono il fondo — il più scuro della piastrella
   è 214 su 255, cioè al massimo il 16%, in media il 3,5%. Il file
   `assets/img/page-noise.png` è il render del nodo, 208×208, ricodificato a 16
   grigi fra 214 e 255 (errore massimo 1/255, 18 KB invece dei 44 dell'export). Un
   raster e non un gradiente perché una grana lo è; e una piastrella diversa da
   `img/noise.png`, che è la grana **animata delle scene** (`.noise`, più sotto) e
   ha un altro compito.

   ⚠ `background-blend-mode` e non uno pseudo-elemento con `mix-blend-mode`. La
   grana è un livello **del fondo**, sotto il contenuto: un livello sopra la pagina
   moltiplicherebbe anche il testo, e su un titolo bianco i punti scuri si
   vedrebbero. Così invece la sezione, la card, il filmato la coprono come coprono
   i cerchi. Verificato in headless su Chromium, WebKit e Firefox che il multiply
   arriva anche al fondo propagato dal body al canvas: nessun pixel più chiaro del
   colore di pagina, e i punti scuri della grana ci sono. */
body {
    min-height: 100vh;
    background-color: var(--color-page-bg);
    background-image:
        url("../img/page-noise.png"),
        url("../img/square-pattern.svg"),
        radial-gradient(
            circle farthest-side at 50% 50%,
            var(--pattern-blob) 0 48%,
            color-mix(in srgb, var(--pattern-blob) 35%, transparent) 74%,
            transparent 100%
        );
    background-size:
        var(--pattern-noise-tile) var(--pattern-noise-tile),
        var(--pattern-grid-tile) var(--pattern-grid-tile),
        var(--pattern-tile) calc(var(--pattern-tile) * var(--pattern-ratio));
    background-repeat: repeat;
    background-blend-mode: multiply, normal, normal;
    color: var(--color-text-primary);
    font-family: var(--font-sans);
    font-size: var(--text-body-s);
    line-height: var(--leading-body-s);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

img,
picture,
video,
canvas,
svg {
    display: block;
    max-width: 100%;
    height: auto;
}

input,
button,
textarea,
select {
    font: inherit;
    color: inherit;
}

button {
    cursor: pointer;
}

ul,
ol {
    padding: 0;
}

h1,
h2,
h3,
h4,
h5,
h6 {
    text-wrap: balance;
}

p {
    text-wrap: pretty;
}

/* Focus visibile su tutto, sempre: è un requisito di accessibilità, non uno
   stile. I moduli possono ridefinire il colore, non rimuovere l'outline. */
:focus-visible {
    outline: 2px solid var(--color-focus);
    outline-offset: 3px;
}

/* Rispetto della preferenza di sistema: azzera transizioni e animazioni. Il JS
   dei moduli deve controllare la stessa media query prima di animare
   (vedi assets/js/base.js → prefersReducedMotion). */
@media (prefers-reduced-motion: reduce) {
    html {
        scroll-behavior: auto;
    }

    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

/* -----------------------------------------------------------------------------
   3. Tipografia ed elementi globali
   -------------------------------------------------------------------------- */

/* ⚠ Niente `letter-spacing` su h1 e h2. C'era `--tracking-tight`, un segnaposto,
   e nel design **tutti i titoli hanno tracking zero** — sia `title-upper-lg` sia
   `title-upper-md`. Fin qui non si vedeva, ma i titoli dei moduli sono `h2`:
   ognuno si ritrovava −0,02em che nessuno aveva chiesto, e per correggerlo
   avrebbe dovuto azzerarlo uno per uno. Toglierlo qui li sistema tutti. */
h1 {
    font-size: var(--text-title-xl);
    line-height: var(--leading-tight);
    font-weight: var(--weight-bold);
}

h2 {
    font-size: var(--text-title);
    line-height: var(--leading-snug);
    font-weight: var(--weight-bold);
}

h3 {
    font-size: var(--text-title-m);
    line-height: var(--leading-snug);
    font-weight: var(--weight-bold);
}

h4,
h5,
h6 {
    font-size: var(--text-title-s);
    line-height: var(--leading-snug);
    font-weight: var(--weight-medium);
}

/* ⚠ Niente hover globale su `a`. C'era un `&:hover { opacity: 0.7 }` e ha fatto
   danni sproporzionati alla sua utilità: su un link che è una card intera
   (`.case-carousel__card`) velava il 30% del contenuto proprio mentre il filmato
   partiva, e su un pulsante col riempimento bianco faceva passare il fondo scuro
   attraverso il bianco — cioè lo rendeva grigio, esattamente quando ci si passava
   sopra. È costato tre correzioni al pulsante del carosello prima che si capisse
   che il pulsante non c'entrava.

   La lezione, che vale oltre questa riga: un effetto di hover appartiene al
   componente che sa cosa sta stilando. Un `a` nel tema può essere una parola in
   un paragrafo o mezza schermata di card, e non esiste un hover buono per
   entrambi. Il testo corrente ce l'ha in `.wysiwyg`, più sotto. */
a {
    color: inherit;
    text-decoration-thickness: 1px;
    text-underline-offset: 0.2em;
}

/* Il riempimento delle headline in display: nel design non sono bianche piene ma
   sfumate, appena azzurrate in cima e bianche in fondo.

   Ce l'hanno **tutte tranne una**: il nome del servizio attivo, i due titoli di
   sezione della home, il titolo del prefooter, il modulo dei loghi. L'eccezione è
   la headline della hero (nodo 649:2352), che è bianca piena. Non c'è una regola
   dietro — non è «sfumato sulla pagina, bianco sul video», perché anche il
   prefooter sta sopra un'immagine ed è sfumato. È l'apertura ad essere trattata a
   parte, e sta scritto qui perché è la cosa che si sbaglia riapplicando la classe
   «a tutti i display».

   I due valori strani vengono dal design e sono verificati sul render, non
   dedotti. Nel file Figma il fill è dichiarato con le fermate al 226,04% e al
   107,89% in un ordine che il browser non sa riprodurre — messe in CSS così come
   sono, danno testo blu pieno. Campionando invece il render del nodo 649:2384 la
   rampa risulta lineare fra rgb(74,134,208) a −192% e il bianco a 115,5%, con
   scarto sotto l'unità sui tre canali alle due estremità. Il blu è il
   rgba(29,104,196,.8) del design già composto sul bianco del livello sotto:
   pre-composto e non trasparente, perché con `background-clip: text` un fill
   traslucido lascerebbe passare il fondo pagina invece del bianco.

   Le percentuali sono **relative all'altezza dell'elemento**, come in Figma:
   perciò un titolo su tre righe è più azzurrato in cima di uno su due, e non c'è
   niente da ricalcolare per riga.

   `color: transparent` è la parte da non toccare: senza, il testo copre il
   proprio riempimento. E per la stessa ragione qui non si può usare
   `text-decoration`, che sparisce — sui titoli non serve.

   Il gradiente è **anche un token**, `--fill-display`, perché non tutti quelli che
   lo usano possono usare la classe: nella ruota dei servizi il riempimento
   dipende da quale nome è attivo, cioè da uno stato, non dall'elemento, e va
   applicato dal CSS del modulo. Il token è l'unica copia dei valori. */
:root {
    --fill-display: linear-gradient(
        180deg,
        rgb(74 134 208) -192%,
        #ffffff 115.5%
    );

    /* Quanto il riempimento deve poter dipingere **oltre** la scatola del testo.
       Vedi il commento sotto: senza, le maiuscole accentate perdono l'accento. */
    --fill-display-bleed: 0.18em;
}

/* ⚠ **`background-clip: text` non dipinge fuori dalla scatola, e le maiuscole
   accentate ci finiscono fuori.** È il difetto segnalato il 27 agosto sul titolo del
   carosello: in «CHI SI È AFFIDATO A NOI» l'accento della È risultava tagliato di
   netto in orizzontale.

   Non è la line box a tagliare — un glifo sborda tranquillamente dalla propria riga,
   e infatti lo stesso titolo in bianco pieno si vede intero. È il **riempimento**:
   l'area che un `background-image` può dipingere finisce col border box, quindi
   l'inchiostro che sta più in alto resta senza colore, cioè trasparente.

   Misurato a 72px con l'interlinea del design (0,89): l'inchiostro delle maiuscole
   accentate sale a 67px sopra la baseline, la baseline sta a 57,5 dal bordo alto
   della scatola, quindi **9,5px di accento cadono fuori**. A 40px con interlinea 1
   sono 2,5px.

   ⚠ **Alzare l'interlinea non è la leva giusta**, ed è la prima cosa che si prova:
   metà dell'interlinea aggiunta va sotto la baseline, quindi per recuperare 9,5px
   sopra servono 19px di interlinea in più — da 64 a 83, il 30% in più, che cambia il
   ritmo di ogni titolo in display su più righe. Verificato: a `line-height: 1`
   l'accento è ancora tagliato di 5,5px, e serve 1,15 perché smetta.

   La leva giusta è **allargare l'area di disegno**: un padding cresce la scatola e
   un margine negativo uguale rimette il testo dov'era. Il padding non si vede —
   `background-clip: text` dipinge solo dentro i glifi — e i due si annullano in
   flusso, in flex e in grid, quindi nessuna misura del design si muove. Su un
   elemento in assoluto ancorato per `inset-block-end` si annullano anche loro:
   `top: auto` assorbe il margine e il padding riporta il testo.

   Simmetrico e non solo in cima, perché la coda della Q e della J sborda in fondo
   per la stessa ragione — meno, ma sborda. */
.display-gradient {
    padding-block: var(--fill-display-bleed);
    margin-block: calc(-1 * var(--fill-display-bleed));
    background-image: var(--fill-display);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

/* --- Utility minime -----------------------------------------------------
   Non è un layer di utility: sono le tre cose che ogni modulo ripeterebbe. */

.container {
    width: 100%;
    max-width: var(--container-max);
    margin-inline: auto;
    padding-inline: var(--gutter);
}

.container-narrow {
    width: 100%;
    max-width: var(--container-narrow);
    margin-inline: auto;
    padding-inline: var(--gutter);
}

/* Spaziatura verticale fra fratelli, senza margini da azzerare a mano. */
.flow > * + * {
    margin-block-start: var(--space-m);
}

/* Ritmo verticale di un modulo. Sta in base e non nei moduli perché è la cosa
   che tiene insieme la pagina qualunque sia l'ordine in cui i moduli finiscono
   uno sotto l'altro: se ognuno decidesse il proprio padding, la spaziatura
   dipenderebbe dalla combinazione. Il modulo si occupa del suo interno. */
.section {
    padding-block: var(--space-section);
}

.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

.skip-link {
    position: absolute;
    inset-inline-start: var(--space-s);
    inset-block-start: calc(var(--space-s) * -4);
    z-index: var(--z-overlay);
    padding: var(--space-2xs) var(--space-s);
    background-color: var(--color-surface);
    color: var(--color-text-primary);
    border-radius: var(--radius-s);
    transition: inset-block-start var(--duration-fast) var(--ease-out);

    &:focus {
        inset-block-start: var(--space-s);
    }
}

/* --- Contenuto WYSIWYG -------------------------------------------------
   Unico posto in cui si stila markup che non controlliamo (l'editor ACF).
   Il modulo wysiwyg e gli articoli avvolgono l'output in .wysiwyg. */

.wysiwyg {
    & > * + * {
        margin-block-start: var(--space-s);
    }

    & h2,
    & h3,
    & h4 {
        margin-block-start: var(--space-l);
    }

    /* L'hover che *non* sta su `a` globale — vedi il commento lì sopra. Qui il
       link è una parola dentro un paragrafo, e una velatura ci sta bene.

       Sottolineato e dello stesso colore del testo, non `--color-accent`: quel
       blu sul fondo pagina fa 2,3:1, cioè illeggibile per del testo corrente.
       Su un fondo scuro un link si distingue sottolineandolo, non tingendolo. */
    & a {
        color: inherit;
        text-decoration: underline;
        transition: opacity var(--duration-fast) var(--ease-out);

        &:hover {
            opacity: 0.7;
        }
    }

    & ul,
    & ol {
        padding-inline-start: var(--space-m);
    }

    & li + li {
        margin-block-start: var(--space-3xs);
    }

    & blockquote {
        padding-inline-start: var(--space-s);
        border-inline-start: 2px solid var(--color-border);
        color: var(--color-text-secondary);
    }

    & img {
        border-radius: var(--radius-m);
    }

    & strong {
        font-weight: var(--weight-bold);
    }
}

/* --- Bottoni ------------------------------------------------------------
   .button è la CTA del sito. Usata dai moduli e dai template, e applicata al
   submit di Gravity Forms via CSS — mai riscrivendo il markup del plugin
   (OBIETTIVO 07.1). */

.button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2xs);
    padding: var(--space-xs) var(--space-m);
    background-color: var(--color-accent);
    color: var(--color-accent-contrast);
    font-size: var(--text-body-s);
    font-weight: var(--weight-medium);
    line-height: 1;
    text-decoration: none;
    border: 0;
    border-radius: var(--radius-full);
    transition:
        background-color var(--duration-fast) var(--ease-out),
        transform var(--duration-fast) var(--ease-out);

    &:hover {
        background-color: var(--color-accent-hover);
        opacity: 1;
    }

    &:active {
        transform: translateY(1px);
    }
}

.button--ghost {
    background-color: transparent;
    color: var(--color-text-primary);
    box-shadow: inset 0 0 0 1px var(--color-border);

    &:hover {
        background-color: var(--color-surface);
    }
}

/* --- Il pulsante del design: `cta-l-white` -------------------------------
   Nel file di design **di pulsanti ce n'è uno**, e questa è la sua traduzione:
   fondo trasparente, bordo bianco di un pixel, angoli a 16px, etichetta a 12px in
   maiuscolo spaziato del 12%, altezza 48px (16 + 16 di interlinea + 16). Lo usano
   il prefooter e il carosello dei case study.

   All'hover si riempie di bianco **dal basso verso l'alto** e l'etichetta passa
   da bianca a nera. Stava nel CSS del carosello, che era l'unico ad averlo; qui
   c'è arrivato quando è diventato chiaro che è *il* pulsante e non un caso
   particolare — con lui la curva e la durata, che erano scritte lì.

   Cinque cose sul come, e ognuna è stata sbagliata almeno una volta:

     - il riempimento **cresce**, non si scopre. È uno `scale` con
       `transform-origin: bottom`, quindi il rettangolo si allunga verso l'alto.
       Un `clip-path` darebbe la stessa geometria e un effetto diverso: taglia le
       lettere a metà mentre passa, invece di scorrere dietro di loro.
     - sta in uno **pseudo-elemento**, così l'etichetta resta il testo del link e
       basta: nessuna copia da nascondere agli screen reader, nessuno `<span>`
       obbligatorio nel markup.
     - `z-index: -1` più `isolation: isolate`, non `z-index: 1` più uno span
       sopra. Un figlio posizionato a z-index non negativo dipinge **sopra** il
       testo del proprio genitore, e da lì nasceva la necessità dello span; a
       −1 dipinge fra il fondo e il testo, che è esattamente il posto giusto.
       `isolation` serve perché il −1 resti dentro il pulsante invece di
       scivolare sotto la sezione.
     - il bordo è **bianco dichiarato**, non `currentColor`: il testo diventa nero
       all'hover, e con `currentColor` il bordo lo seguirebbe disegnando un
       contorno scuro attorno al riempimento bianco.
     - il nero dell'etichetta è `#000` crudo e non un token: è il nero del
       componente di design, e un token per il nero non esiste — come per i veli
       dei moduli, che usano `#000` per la stessa ragione.

   ⚠ Non c'è nessun `opacity: 1` di difesa, e non serve più: la regola
   `a:hover { opacity: 0.7 }` che lo rendeva necessario è stata togliata da `a`
   (vedi il commento lassù). Se un domani ricompare un hover globale sui link,
   questo pulsante è il primo posto in cui si vede. */
.button--outline {
    --cta-duration: 600ms;
    --cta-ease: cubic-bezier(0.48, 0, 0.12, 1);

    position: relative;
    isolation: isolate;
    overflow: hidden;

    /* Altezza dichiarata, non dedotta da padding e interlinea. Nel design il
       pulsante è alto **48** con il bordo dentro: Figma disegna la traccia senza
       aggiungerla alla misura, mentre in CSS il bordo si somma. Lasciando che
       l'altezza venga da 16 + 16 + 16 vengono 50, misurati. Con `box-sizing:
       border-box` — che il reset applica a tutto — 3rem sono 48 tutto compreso, e
       il padding verticale non serve più: centra `align-items` di `.button`. */
    height: 3rem;
    padding: 0 var(--space-m);
    background-color: transparent;
    color: var(--color-text-primary);
    border: 1px solid var(--color-text-primary);
    border-radius: var(--radius-m);
    font-size: var(--text-label);
    line-height: var(--leading-label);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    white-space: nowrap;

    /* `transform` è riscritto e non ereditato da `.button`: questa dichiarazione
       sostituisce quella, quindi se non lo si rimette il press di `:active`
       scatta senza transizione. */
    transition:
        color var(--cta-duration) var(--cta-ease),
        transform var(--duration-fast) var(--ease-out);

    /* Il riempimento. `scale: 1 0` è alto zero appoggiato al bordo inferiore; a
       `1 1` copre il pulsante. */
    &::after {
        content: "";
        position: absolute;
        inset: 0;
        z-index: -1;
        background-color: var(--color-text-primary);
        transform-origin: bottom center;
        scale: 1 0;
        transition: scale var(--cta-duration) var(--cta-ease);
    }

    &:is(:hover, :focus-visible) {
        background-color: transparent;
        color: #000;
    }

    &:is(:hover, :focus-visible)::after {
        scale: 1 1;
    }
}

/* --- La taglia media del pulsante: `cta-m-white` -------------------------
   Il commento sopra diceva che «nel file di design di pulsanti ce n'è uno». Dal 1
   settembre ce ne sono due, e differiscono **soltanto per l'altezza**: 40 invece
   di 48. Tutto il resto — bordo bianco di un pixel, angoli a 16px, etichetta a
   12px in maiuscolo spaziato del 12%, il riempimento che cresce dal basso — è
   identico, quindi questo è un modificatore di taglia e non un secondo pulsante.

   Lo usano le tre card di `flexible-content/offer-cards`.

   Si scrive insieme all'altro: `class="button button--outline button--m"`. Deve
   stare **dopo** `.button--outline` nel file, perché a pari specificità vince
   l'ultima dichiarazione — al contrario, l'altezza da 48 rimarrebbe.

   Il padding orizzontale non cambia: nel design il pulsante medio ha gli stessi
   16px, ed è solo più basso. */
.button--m {
    height: 2.5rem;
}

/* Submit di Gravity Forms: stessa CTA, per selettore. */
.gform_wrapper .gform_footer button[type="submit"],
.gform_wrapper .gform_footer input[type="submit"] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-xs) var(--space-m);
    background-color: var(--color-accent);
    color: var(--color-accent-contrast);
    font-size: var(--text-body-s);
    font-weight: var(--weight-medium);
    line-height: 1;
    border: 0;
    border-radius: var(--radius-full);
    cursor: pointer;

    &:hover {
        background-color: var(--color-accent-hover);
    }
}

/* --- Grana ---------------------------------------------------------------
   Il velo di grana animata delle scene video. Sta qui e non nel CSS dei moduli
   perché nel design compare sia nella hero sia nel prefooter: metterla nei
   moduli vorrebbe dire duplicare anche gli undici keyframes.

   Il contenitore che la ospita deve avere `position: relative` e
   `overflow: hidden`: è compito del modulo. L'`inset` negativo la fa più grande
   del contenitore, così le traslazioni dei keyframes non scoprono mai i bordi.

   `steps(2)` è il punto della cosa: la grana scatta fra posizioni discrete
   invece di scorrere, ed è quello che la fa leggere come rumore di pellicola.
   Il PNG è 100×100 reso a 50×50, quindi è già a densità doppia.

   Sotto prefers-reduced-motion il blocco della sezione 2 la congela lasciandola
   visibile: è la resa giusta, perché è texture e non movimento informativo.

   Dentro una giunzione fra scene la grana svanisce con `--exit`, ma quella regola
   **non sta qui**: sta in *Passaggio fra scene*, dentro la media query della
   soglia. Qui resta il valore pieno, che è quello giusto ovunque una scena non si
   stia passando il testimone.
   -------------------------------------------------------------------------- */

.noise {
    position: absolute;
    inset: -10rem;
    z-index: 0;
    background-image: url("../img/noise.png");
    background-size: 50px 50px;
    background-position: 50%;
    opacity: 0.6;
    pointer-events: none;
    will-change: transform;
    animation: noise-shift 1s steps(2) infinite;
}

@keyframes noise-shift {
    0%   { transform: translate3d(0, 9rem, 0); }
    10%  { transform: translate3d(-1rem, -4rem, 0); }
    20%  { transform: translate3d(-8rem, 2rem, 0); }
    30%  { transform: translate3d(9rem, -9rem, 0); }
    40%  { transform: translate3d(-2rem, 7rem, 0); }
    50%  { transform: translate3d(-9rem, -4rem, 0); }
    60%  { transform: translate3d(2rem, 6rem, 0); }
    70%  { transform: translate3d(7rem, -8rem, 0); }
    80%  { transform: translate3d(-9rem, 1rem, 0); }
    90%  { transform: translate3d(6rem, -5rem, 0); }
    100% { transform: translate3d(-7rem, 0, 0); }
}

/* --- Passaggio fra scene -------------------------------------------------
   Due scene consecutive — hero, servizi, prefooter — devono sembrare una sola
   card che sta ferma mentre il contenuto dentro cambia. Senza queste due regole
   non lo sembrano: la sticky di una scena è vincolata alla propria sezione,
   quindi si sgancia e scorre via appena la sezione finisce, proprio mentre la
   successiva entra dal basso. Si vedono due card che slittano.

   Non basta sovrapporle con lo z-index: il problema non è l'ordine di
   sovrapposizione, è che la scena uscente **non è più inchiodata**. Servono due
   cose insieme, ed è quello che fanno le due regole:

     - la scena che ne precede un'altra si allunga di `--scene-handoff`, e quella
       coda è tenuta in pin: resta ferma mentre la successiva compare;
     - la scena che segue un'altra si tira su di `100vh + --scene-handoff`, così
       si inchioda quando la precedente è ancora inchiodata. `100vh` perché la
       sua sticky sta in cima alla sezione e deve già essere arrivata a bordo
       schermo; `--scene-handoff` è la sovrapposizione vera.

   Il risultato: una finestra di `--scene-handoff` in cui entrambe le sticky sono
   a `top: 0` contemporaneamente. La scena entrante ci dissolve dentro, e siccome
   entrambe leggono `--scene-card-*` la card è nello stesso punto: a schermo è una
   sola card, ferma.

   È un contratto fra scene, non fra due moduli specifici: qualunque coppia di
   `[data-scene]` adiacenti si passa il testimone così. Un modulo-scena da solo,
   senza vicini, non prende nessuna delle due regole e si comporta normalmente.

   **Rinunciare alla sovrapposizione: `data-scene="after"`.** Non tutte le scene
   vogliono nascere sopra la precedente. Una scena la cui prima inquadratura non
   ha niente in comune con l'ultima di quella sopra — composizione diversa, card
   in un altro punto — con la dissolvenza fa peggio: si vedono due immagini
   estranee mescolarsi. Quella scena dichiara `data-scene="after"` e diventa la
   sezione successiva, raggiunta con lo scroll normale della pagina.

   È l'unica cosa che la scena **entrante** decide, ed è per questo che il valore
   sta su di lei: se non si sovrappone, la precedente non ha nessuno da aspettare
   e non deve allungarsi. Le due regole sotto sono infatti la stessa decisione
   letta dai due lati, e per questo condizionano entrambe sullo stesso selettore.

   Chi porta `data-scene="after"` resta comunque una scena a tutti gli effetti:
   la successiva può sovrapporsi a lei, e quindi lei prende la coda in pin. Si
   rinuncia all'ingresso, non al mestiere.

   ⚠ La scena entrante, prima di inchiodarsi, sale dal basso per una schermata:
   deve essere invisibile in quel tratto, e siccome un elemento a `opacity: 0`
   resta cliccabile, deve anche essere `pointer-events: none`. È il modulo a
   gestirlo, guidato dal proprio progresso d'ingresso.

   ⚠ «Invisibile» vuol dire **anche il fondo**, ed è la parte che sfugge. Un
   `background-color` sulla sezione entrante è opaco dal primo pixel in cui la
   sezione tocca il viewport, cioè per tutta la salita: copre la scena
   precedente, e a schermo si vede arrivare una banda che sale, e solo dopo il
   contenuto. Il progresso `--enter` non aiuta, perché in quel tratto vale ancora
   zero: la finestra che misura comincia quando la scena si inchioda, non quando
   comincia a salire. Due modi di sistemarlo, e quale dipende dall'effetto:

     - se la scena entra in dissolvenza, `opacity: var(--enter, 1)` sulla
       sezione — così sparisce anche il fondo (`services-scroll`);
     - se entra traslando, il fondo va sull'elemento che trasla, e la sezione
       resta trasparente nel layout animato;
     - oppure il fondo sta su un **livello interno** che porta l'opacità
       dell'ingresso, così la sezione non dipinge niente e il contenuto che deve
       restare visibile sta fuori da quel livello (`cta-fullscreen`, che si
       costruisce l'ingresso da sé — vedi più sotto).

   È già costato una schermata di nero prima del prefooter.

   Sta dentro la media query perché fuori — layout statico, movimento ridotto, o
   viewport stretto — le scene sono sezioni normali una sotto l'altra, e un
   margine negativo di due schermate le farebbe collassare una sull'altra.

   ⚠ SOGLIA SCENE: 901px. Sotto quella larghezza le scene non esistono, perché un
   layout mobile per queste scene non è disegnato. La condizione va ripetuta
   identica ovunque: questo blocco, il layout animato di **ogni** modulo-scena, e
   la costante `ANIMATED_FROM` nel JS di ogni modulo-scena che ne ha. Quanti punti
   siano non va scritto qui, perché il numero invecchia a ogni modulo nuovo — ed è
   già invecchiato due volte.

   Cercare `soglia scene` per trovarli tutti. Non è una duplicazione che si possa
   togliere: una media query non legge le custom property. Se non combaciano il
   risultato è silenzioso e brutto — il CSS mette in pin una scena che il JS non
   guida, quindi schermate di scroll in cui non succede niente. */

/* soglia scene: 901px */
@media (scripting: enabled) and (prefers-reduced-motion: no-preference) and (min-width: 901px) {

    /* Le due facce della stessa decisione, presa dalla scena entrante. Il
       selettore `:not([data-scene="after"])` è lo stesso nelle due regole, e
       deve restarlo: se una sola delle due scattasse, la coda in pin e la
       sovrapposizione non sarebbero più della stessa lunghezza. */
    [data-scene]:has(+ [data-scene]:not([data-scene="after"])) {
        --scene-hold: var(--scene-handoff);
    }

    [data-scene] + [data-scene]:not([data-scene="after"]) {
        --scene-enter: var(--scene-handoff);

        margin-block-start: calc(-100vh - var(--scene-handoff));
    }

    /* La scena coperta si spegne. `is-covered` la scrive base.js quando il
       progresso della sovrapposizione arriva a 1, cioè quando la scena entrante è
       opaca e le due card coincidono: in quell'istante spegnerla non si vede.

       Serve perché una sezione-scena **non dipinge il proprio fondo** — nel design
       si vede il fondo pagina attraverso — quindi l'entrante non copre più tutto lo
       schermo, e la uscente, che proprio lì si sgancia, scorrerebbe via in vista
       dietro le zone trasparenti della successiva. Con i fondi opachi il problema
       non esisteva e questa regola non c'era.

       `visibility` e non `opacity: 0`: toglie l'elemento anche dall'hit-testing e
       dalla composizione, invece di lasciare una scena invisibile ma viva sopra
       mezza pagina. Sta dentro la media query, quindi sotto la soglia scene non si
       applica mai — e non può lasciare una sezione nascosta in un layout statico. */
    [data-scene].is-covered {
        visibility: hidden;
    }

    /* La grana della scena uscente svanisce mentre l'entrante compare.
       **Due scene adiacenti hanno entrambe la grana**, quindi nella finestra i due
       veli si sommerebbero — 0,84 invece di 0,6, cioè uno scatto di densità per
       una schermata — e soprattutto la uscente si spegne di colpo a fine finestra,
       quindi la grana tornerebbe a 0,6 con un salto visibile. Facendola svanire,
       all'istante dello spegnimento non c'è più niente da spegnere.

       Sta qui e non accanto a `.noise` per la stessa ragione della regola sopra:
       dentro la media query un `--exit` rimasto appeso — da un resize, da un
       modulo, da un bug — non può lasciare una grana invisibile in un layout dove
       nessuno la sta passando a nessuno. Misurato: fuori dalla soglia la grana
       resta 0,6 anche con `--exit: 1` scritto a mano sulla sezione. */
    [data-scene] .noise {
        opacity: calc(0.6 * (1 - var(--exit, 0)));
    }
}

/* --- Reveal allo scroll -------------------------------------------------
   Traduzione del `whileInView` di Figma Make (OBIETTIVO 04.1): il markup porta
   `data-reveal`, base.js aggiunge `is-revealed` quando l'elemento entra nel
   viewport. Qui c'è solo il CSS, così un modulo che vuole il reveal non ha
   bisogno di JS proprio.

   `@media (scripting: enabled)`: senza JavaScript il contenuto resta visibile
   invece di rimanere a opacity 0. */

@media (scripting: enabled) {
    [data-reveal] {
        opacity: 0;
        transform: translateY(var(--reveal-distance, 1.5rem));
        transition:
            opacity var(--duration-slow) var(--ease-out),
            transform var(--duration-slow) var(--ease-out);
        transition-delay: var(--reveal-delay, 0ms);
    }

    [data-reveal].is-revealed {
        opacity: 1;
        transform: none;
    }
}

/* -----------------------------------------------------------------------------
   Header e footer

   L'header è quello del design (nodo 649:2353): logo a sinistra, sei voci in
   maiuscolo spaziato, la lingua all'estremo destro, e **niente fondo** — sta
   sopra il video della hero, con solo un velo scuro sfumato che tiene leggibili le
   voci sopra un'immagine in movimento.

   ⚠ **Non è `sticky`, ed è una scelta.** Nel design il menu compare una volta,
   in cima alla prima schermata, e non c'è una seconda istanza: la pagina che
   segue è fatta di scene a schermo pieno, dove una barra che galleggia — anche
   solo il suo velo — si vedrebbe attraversare composizioni pensate senza. Perciò
   è in `absolute` sopra la cima del documento e scorre via.
   La conseguenza è che non occupa spazio, e i template che *non* aprono con una
   hero a pieno campo devono lasciarglielo: lo fanno con `--header-h`.

   Il footer il design non lo copre: resta scaffolding, adeguato alla palette.
   -------------------------------------------------------------------------- */

/* L'header non occupa spazio, quindi una pagina che **non** apre con qualcosa a
   pieno campo gli passa il contenuto sotto — e siccome l'header è trasparente, il
   testo finisce dentro il menu. Succede a una pagina senza hero il cui primo
   modulo è testo: il design non la mostra, il tema la permette.

   La condizione è generica e non un elenco di moduli: `[class^="hero-"]` sfrutta
   la convenzione del tema — una hero sta in `template-parts/heroes/<nome>.php` e
   la sua sezione porta `<nome>` come classe — e `[data-scene]` copre i moduli-scena
   a pieno campo che possono aprire una pagina senza essere heroes. Un modulo nuovo
   che rispetti la convenzione è coperto senza toccare questa riga.

   ⚠ **La condizione guarda il PRIMO figlio, e la versione senza `:first-child` era
   un difetto.** Era `main:not(:has(> [class^="hero-"], > [data-scene]))`, cioè
   «main non contiene da nessuna parte una hero o una scena»: bastava un
   modulo-scena **in fondo** alla pagina per togliere lo spazio in testa a una
   pagina che apre con del testo. Non era teorico — è comparso il 26 agosto, quando
   `flexible-content/cta-fullscreen` è diventato un modulo-scena e si è messo per
   ultimo: su una pagina «muro dei loghi + CTA», misurato, `main` aveva margine 0 e
   i loghi cominciavano a y 0, sotto un header trasparente. Solo il primo modulo
   decide se lo spazio serve, perché è l'unico che può stare sotto l'header.

   È un **margine** e non un padding di proposito: `main` a volte è anche
   `.container.flow`, che dichiara il proprio `padding-block`, e due
   `padding-block-start` non si sommano — uno vince e l'altro sparisce, in
   silenzio. Il margine convive con qualunque padding il template si dia. */
main:not(:has(> :first-child:is([class^="hero-"], [data-scene]))) {
    margin-block-start: var(--header-h);
}

.site-header {
    position: absolute;
    inset-inline: 0;
    inset-block-start: 0;
    z-index: var(--z-header);

    /* Il velo. Nel design è un'immagine dentro il componente del menu (nodo
       500:3795); qui è una sfumatura, che fa la stessa cosa senza un file e segue
       qualunque altezza.

       **I valori sono campionati dal raster, non stimati**, e la forma non è quella
       che si indovina: il nero non parte pieno e scende: sta **piatto a 0,4 per il
       primo 40%** dell'altezza, poi scende lineare a zero. Campionando la colonna
       centrale dell'export, alfa 101/255 dal bordo alto fino al 40%, e da lì
       101 → 0 con scarto sotto l'unità su tutti i campioni (86 a 48%, 60 a 64%, 33
       a 80%, 7 a 96%). Il tratto piatto è quello che tiene le voci leggibili: sono
       alte 20px dentro i primi 64.

       Era `0.45` che scendeva da subito, cioè più scuro in cima e più chiaro dove
       stanno le voci. */
    background-image: linear-gradient(
        to bottom,
        rgb(0 0 0 / 0.4) 0%,
        rgb(0 0 0 / 0.4) 40%,
        rgb(0 0 0 / 0) 100%
    );
    color: var(--color-scene-text);
}

/* 16px sopra e 32 sotto, come nel design: il menu è ottico rispetto al logo, non
   centrato nella propria banda. */
.site-header__inner {
    display: flex;
    align-items: center;
    gap: var(--space-xl);
    width: 100%;
    max-width: var(--container-max);
    margin-inline: auto;
    padding: var(--space-s) var(--gutter) var(--space-l);
}

/* Il logo. Nel design il link è un box **110×64** (nodo 436:3105) e il marchio lo
   riempie: entrambe le misure sono dichiarate, non lasciate a `auto`, così il posto
   che occupa non dipende dal file che ci si mette dentro.

   `assets/logo.svg` è l'export del design, ripulito di tre cose che Figma aggiunge
   e che fanno danni: `preserveAspectRatio="none"`, che **deforma** il marchio se il
   box non è esattamente 110×64; `overflow="visible"`, che lo lascerebbe dipingere
   fuori dal viewBox; e un `id` con uno spazio dentro, che in HTML non è valido. Gli
   id generici di Figma sono prefissati, perché un `clip-path="url(#clip0_0_4)"` è una
   collisione che aspetta un secondo SVG inline.

   L'SVG è `aria-hidden`: il nome accessibile lo porta l'`aria-label` sul link, e
   averlo due volte lo farebbe leggere due volte.

   `object-fit: contain` serve al caso `img`, non all'svg — un SVG col viewBox si
   inscrive da sé — ma il fallback del template può essere un'immagine caricata dal
   cliente, e senza quella riga si schiaccerebbe nel box. */
.site-header__brand {
    flex: 0 0 auto;
    width: 6.875rem;
    height: 4rem;
    text-decoration: none;

    & svg,
    & img {
        display: block;
        width: 100%;
        height: 100%;
        object-fit: contain;
        object-position: left center;
    }
}

.site-header__nav {
    display: flex;
    flex: 1 1 auto;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-xl);
    margin-inline-start: auto;
}

.site-header__toggle {
    display: none;
}

/* Le voci: label-md del design — 14px su 20, peso 500, tracking 16%, maiuscolo,
   bianco al 70%. Piene sull'hover, e **basta**.

   ⚠ Nel componente del menu (nodo 500:3795) **una voce su sei è bianca piena**,
   `services`, e le altre cinque al 70%. Qui è resa come **hover**, non come stato
   «pagina corrente», e la scelta è deliberata: il menu del sito vero non
   sosterrebbe un current — quattro voci su sei sono link personalizzati ad ancore
   (`/#services` e simili), e WordPress marca un link personalizzato come
   `current-menu-item` appena l'URL combacia, quindi sulla home ne risultano correnti
   quattro. Una voce sola evidenziata in un mockup è l'affordance di stato che il
   componente mostra accesa; un current che i dati sbagliano è un difetto. Se il
   design intendesse davvero un current, serve prima sistemare il menu — link a
   pagine vere invece di ancore — e allora si aggiunge qui in due righe. */
/* `gap: 2.5rem` sono i 40px del design, e valgono per **entrambi** i menu: header
   (nodo 500:3795) e footer (nodo 658:2737) lo hanno identico. Non è un passo della
   scala — che va da 32 a 48 — quindi è un valore scritto qui, dove la regola
   riguarda i due soli menu del tema. */
.nav-menu,
.footer-menu {
    display: flex;
    flex-wrap: wrap;
    gap: 2.5rem;
    list-style: none;
    margin: 0;
    padding: 0;

    & a {
        font-size: var(--text-label-l);
        line-height: var(--leading-label-l);
        font-weight: var(--weight-medium);
        letter-spacing: var(--tracking-label-l);
        text-transform: uppercase;
        text-decoration: none;
        color: var(--color-text-secondary);
        transition: color var(--duration-fast) var(--ease-out);

        &:hover {
            color: var(--color-text-primary);
        }
    }
}

.lang-switcher {
    position: relative;
    flex: 0 0 auto;
}

/* Nel design la lingua è una voce come le altre, non un pulsante con un bordo:
   "it" in fondo alla riga, stesso stile del menu. */
.lang-switcher__toggle {
    padding: 0;
    background: none;
    border: 0;
    color: var(--color-text-secondary);
    font-size: var(--text-label-l);
    line-height: var(--leading-label-l);
    font-weight: var(--weight-medium);
    letter-spacing: var(--tracking-label-l);
    text-transform: uppercase;
    transition: color var(--duration-fast) var(--ease-out);

    &:hover,
    &[aria-expanded="true"] {
        color: var(--color-text-primary);
    }
}

.lang-switcher__menu {
    position: absolute;
    inset-block-start: calc(100% + var(--space-s));
    inset-inline-end: 0;
    z-index: var(--z-overlay);
    min-width: 10rem;
    padding: var(--space-2xs);
    list-style: none;
    margin: 0;
    background-color: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-m);

    & a {
        display: block;
        padding: var(--space-3xs) var(--space-2xs);
        border-radius: var(--radius-s);
        font-size: var(--text-label-l);
        letter-spacing: var(--tracking-label-l);
        text-transform: uppercase;
        text-decoration: none;
    }

    & a[aria-current="true"] {
        font-weight: var(--weight-semibold);
    }
}

/* --- La riga dell'header a larghezza piena --------------------------------
   Il gruppo delle voci è **centrato sulla pagina**, non appoggiato al logo, e nel
   design è l'unica cosa non ovvia della composizione. Misurato sul nodo 500:3795:
   le voci vanno da x 420 a x 1090, quindi il loro centro cade a 755 su un frame da
   1512, che ha il centro a 756. Un pixel: è centrato.

   Il logo occupa 110 e la riga ne è larga 1448, quindi con un semplice
   `space-between` fra logo e navigazione le voci partirebbero a 182 — duecentoquaranta
   pixel troppo a sinistra. È il difetto che c'era.

   Tre colonne, e le due laterali **uguali**: è quello che tiene il gruppo al centro
   della pagina qualunque cosa ci sia ai lati. In Figma la stessa cosa è espressa
   come un blocco largo 1060 ancorato a destra, e i due conti danno lo stesso
   risultato a 1512; la griglia però regge una voce in più — e il menu lo compila il
   cliente, quindi una voce in più prima o poi arriva.

   ⚠ `display: contents` sul `<nav>`, e serve: la lista e lo switcher lingua devono
   essere **due celle della griglia dell'header**, non due figli di un box che
   comincia dopo il logo. L'elemento resta nel DOM e resta un landmark col suo
   `aria-label` — sparisce solo la sua scatola.

   ⚠ Il breakpoint è il complemento di `max-width: 900px`, cioè del menu a comparsa
   qui sotto: i due vanno cambiati insieme. **Non è la soglia scene**, che è un altro
   901 e non c'entra niente con l'header. */
@media (min-width: 901px) {
    .site-header__inner {
        display: grid;
        grid-template-columns: 1fr auto 1fr;
        align-items: center;
    }

    .site-header__nav {
        display: contents;
    }

    .site-header__brand {
        grid-column: 1;
        justify-self: start;
    }

    .nav-menu {
        grid-column: 2;
        flex-wrap: nowrap;
    }

    .lang-switcher {
        grid-column: 3;
        justify-self: end;
    }
}

/* Nessun margine in testa: la pagina finisce con un modulo a schermo pieno — la
   CTA — e uno stacco lì lascerebbe vedere una banda di fondo pagina fra il media e
   il footer. Il footer si attacca a quello che ha sopra; se un modulo vuole aria
   prima del footer, la mette lui col proprio padding.

   ⚠ **Qui c'era `scroll-snap-align: end`, ed è stato tolto il 26 agosto.** Era il
   contrappeso all'aggancio del prefooter: un punto d'aggancio vicino al fondo del
   documento rende la coda irraggiungibile — la fine del documento distava dal punto
   quanto è alto il footer, misurato 162px su una schermata da 895, quindi fermarsi
   laggiù cadeva nella zona di prossimità del punto sopra e lo scroll tornava su.
   Dare alla coda un punto proprio lo risolveva.

   Non serve più, perché non c'è più nessun punto d'aggancio in pagina: la CTA è
   diventata un modulo-scena e non aggancia niente. Restava come agganciarsi a un
   avversario che non c'è, e si sentiva scorrendo il fondo della pagina.

   Va rimesso **insieme** al primo `scroll-snap-align` che qualcuno dichiara su una
   sezione, se quella sezione dista dal fondo del documento meno di una schermata.
   Il controllo è aritmetico e sta in CLAUDE.md → *Aggancio dello scroll*.

   --- Il footer è quello del design (nodo 914:6281) ------------------------
   Due righe: una bordata sopra e sotto col logo bianco, le voci e i social; poi la
   riga legale, centrata. Sedi e contatti non ci sono — il design non li mette.

   L'altezza torna esatta: 160 di aria + (16+64+16) della riga + (32+16+32) della
   legale = 336, che è l'altezza del frame.

   ⚠ **Il footer non dipinge un colore pieno: dipinge una sfumatura al nero**, e
   lascia vedere il fondo pagina in cima. Nel design è un asset — un'ellisse nera
   molto sfocata — e i suoi numeri sono nel file SVG, non stimati: centro sull'asse
   della pagina a 884px dalla cima del footer, raggi 1120×800, nero pieno fino al
   67,5% del raggio e poi giù verso il trasparente, più 100px di sfocatura. Da qui i
   valori sotto: `55.25rem` è 884, `70rem × 50rem` sono 1120×800, e le fermate
   allargate rendono la sfocatura.

   Due conseguenze volute. Il nero non è un `background-color`, quindi in cima al
   footer si vede il fondo pagina col suo pattern e il passaggio dalla CTA non ha un
   bordo. E i due angoli in alto restano più chiari, perché l'ellisse è larga 1120
   di raggio e ai bordi dello schermo è già oltre la propria fermata piena — è così
   anche nel design.

   ⚠ Nel design questa sfumatura serve la variante col **CTA bianco** sopra
   (`cta-m-white/footer`), dove il salto da bianco a nero è il punto. La nostra
   pagina finisce con un modulo-scena che si dissolve nel fondo pagina, quindi qui
   fa un lavoro più modesto: separa il footer dalla pagina. Se un domani si vuole
   togliere, è una dichiarazione sola. */
.site-footer {
    /* 160px sopra la riga del menu nel design, e non è un passo della scala, che si
       ferma a 128 (`--space-4xl`). Valore locale del footer, come `--intro-gap` in
       cards-fullscreen. */
    --footer-lead: 10rem;

    /* Il filetto del footer è al **12%** di bianco, non il 20% di
       `--color-border`: è più sottile di qualunque altro bordo del tema, ed è
       voluto — separa due righe dello stesso blocco, non due blocchi. */
    --footer-rule: rgb(255 255 255 / 0.12);

    padding-block-start: var(--footer-lead);

    /* Il centro dell'ellisse è ancorato **al fondo del footer**, non alla sua cima:
       nel design sta 548px sotto il bordo inferiore (884 dalla cima meno i 336 di
       altezza), e contarlo da lì è quello che tiene il fondo nero qualunque altezza
       abbia il footer. Contandolo dalla cima, un footer più alto — il menu che va a
       capo su schermo stretto — finirebbe oltre l'ellisse e tornerebbe trasparente
       proprio in fondo alla pagina. */
    background-image: radial-gradient(
        70rem 50rem at 50% calc(100% + 34.25rem),
        rgb(0 0 0) 0%,
        rgb(0 0 0) 66%,
        rgb(0 0 0 / 0.6) 100%,
        rgb(0 0 0 / 0) 110%
    );
    color: var(--color-scene-text);
}

.site-footer__inner {
    width: 100%;
    max-width: var(--container-max);
    margin-inline: auto;
    padding-inline: var(--gutter);
}

/* La riga bordata. `justify-content: space-between` e basta: qui, a differenza
   dell'header, il gruppo delle voci cade già al centro da sé, perché logo (109) e
   social (128) sono quasi larghi uguali. Misurato sul design: le voci vanno da 418 a
   1082, centro 750 su un frame che centra a 756 — sei pixel, che è quanto vale la
   differenza fra i due gruppi ai lati. È la costruzione del design, non un caso da
   correggere con una griglia. */
.site-footer__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-l);
    padding-block: var(--space-s);
    border-block: 1px solid var(--footer-rule);
}

/* Il logo del footer è 109×64, un pixel più stretto di quello dell'header: sono due
   asset diversi, e questo è la versione monocroma bianca (nodo 773:1942). Entrambe
   le misure dichiarate, come nell'header. */
.site-footer__brand {
    flex: 0 0 auto;
    width: 6.8125rem;
    height: 4rem;

    & svg {
        display: block;
        width: 100%;
        height: 100%;
    }
}

.site-footer__nav {
    display: flex;
    min-width: 0;
}

/* ⚠ Nel footer le voci sono **bianche piene**, non al 70% come nell'header: il
   design le dà così (nodo 658:2737), e non è una svista da uniformare.

   L'hover va al 70%, ed è deciso qui: il design non copre lo stato. È l'inverso di
   quello dell'header — là si accende, qui si attenua — perché il punto di partenza
   è opposto. Passa da `color` e non da `opacity`, per la ragione scritta in
   CLAUDE.md: ridurre l'opacità del bianco *è* fare del grigio, e su un testo va
   bene ma su un riempimento no. */
.footer-menu a {
    color: var(--color-text-primary);

    &:hover {
        color: var(--color-text-secondary);
    }
}

.site-footer__socials {
    display: flex;
    align-items: center;
    gap: var(--space-s);
    list-style: none;
    margin: 0;
    padding: 0;
}

/* 32×32 nel design, e il leaf riempie il box. Le icone sono cerchi bianchi pieni
   col glifo in nero: non prendono `currentColor`, quindi non cambiano col testo
   attorno — e per questo l'hover è un'opacità e non un colore, che è l'unica cosa
   che si può cambiare su un disegno a due tinte. */
.site-footer__social {
    display: block;
    width: 2rem;
    height: 2rem;
    transition: opacity var(--duration-fast) var(--ease-out);

    & svg {
        display: block;
        width: 100%;
        height: 100%;
    }

    &:hover {
        opacity: 0.7;
    }
}

/* La riga legale: label-sm del design — 12px su 16, peso 600, tracking 12%,
   maiuscolo, bianco al 70% — con 32px sopra e sotto. */
.site-footer__legal {
    padding-block: var(--space-l);
    font-size: var(--text-label);
    line-height: var(--leading-label);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    text-align: center;
    text-wrap: balance;
    color: var(--color-text-secondary);

    /* «Privacy & Cookies» chiude la riga, e nel design non si distingue dal resto:
       stesso corpo, stesso bianco al 70%, nessun filetto sotto. Verificato sul
       render del nodo 658:2760 a 4x — la riga è un testo solo, e sotto quelle due
       parole non c'è nessun pixel. È lo stesso trattamento delle voci del menu qui
       sopra, che pure sono link e pure non hanno una sottolineatura.
       Il filetto resta per il puntatore e per la tastiera, dove un'affordance
       serve e non costa niente al fotogramma fermo. */
    & a {
        color: inherit;
        text-decoration: none;
        text-underline-offset: 0.2em;

        &:hover,
        &:focus-visible {
            color: var(--color-text-primary);
            text-decoration: underline;
        }
    }
}

/* Menu principale a comparsa sotto i 900px. Il bottone è già nel markup con
   aria-expanded gestito da base.js. */
@media (max-width: 900px) {
    .site-header__toggle {
        display: block;
        margin-inline-start: auto;
        padding: var(--space-2xs);
        background: none;
        border: 1px solid var(--color-border);
        border-radius: var(--radius-s);
    }

    .site-header__toggle-bars {
        display: block;
        width: 1.25rem;
        height: 2px;
        background-color: currentColor;
        box-shadow:
            0 -6px 0 currentColor,
            0 6px 0 currentColor;
    }

    .site-header__nav {
        display: none;
        flex-direction: column;
        align-items: flex-start;
        justify-content: flex-start;
        flex-basis: 100%;
        gap: var(--space-m);
        margin-inline-start: 0;
        padding-block: var(--space-s);
    }

    .site-header__inner {
        flex-wrap: wrap;
        gap: var(--space-s);
    }

    .site-header__nav.is-open {
        display: flex;
    }

    /* L'header è in `absolute` e non ha fondo proprio: il menu aperto starebbe
       sopra il contenuto senza niente dietro. Il fondo arriva solo da aperto,
       quindi da chiuso resta il velo sfumato del design. */
    .site-header:has(.site-header__nav.is-open) {
        background-image: none;
        background-color: var(--color-surface);
    }

    .nav-menu {
        flex-direction: column;
        gap: var(--space-s);
    }

    /* Il footer stretto: il design non lo copre, e la scelta è la più ovvia — i tre
       gruppi della riga si impilano e si centrano, invece di stringersi fino a
       spezzare le voci. L'aria in testa scende da 160 a 96: 160 su uno schermo alto
       700 è una schermata di nero prima di arrivarci. */
    .site-footer {
        --footer-lead: var(--space-3xl);
    }

    .site-footer__row {
        flex-direction: column;
        gap: var(--space-l);
    }

    .site-footer__nav {
        justify-content: center;
    }

    .footer-menu {
        justify-content: center;
        gap: var(--space-s) var(--space-l);
    }
}

/* -----------------------------------------------------------------------------
   Template di servizio (index, single, 404) — SCAFFOLDING
   -------------------------------------------------------------------------- */

/* Lo spazio per l'header non sta qui: lo mette il margine su `main` nella sezione
   Header, che copre anche le pagine a moduli. */
.container.flow,
.container-narrow.flow {
    padding-block: var(--space-2xl);
}

.post-teaser + .post-teaser {
    margin-block-start: var(--space-xl);
    padding-block-start: var(--space-xl);
    border-block-start: 1px solid var(--color-border);
}

.error-404 {
    text-align: center;
}
