/* =====================================================================
   LION BRAND — il vestito del markup di Elementor
   ---------------------------------------------------------------------
   Elementor mette struttura e contenuto, questo foglio mette il design.
   E' il gradino 4 della scala di COME-SI-COSTRUISCE.md, e su questo progetto
   e' la norma: il prototipo ha un hero a tutto schermo con velo sfumato,
   tipografia che si adatta riga per riga, grana da stampa in sovrapposizione
   e un carosello a scorrimento continuo. Nessuna impostazione di Elementor
   fa queste cose, e vestire il widget giusto costa meno che riscriverlo.

   REGOLA DI QUESTO FILE: si aggancia solo alle classi `lb-*` messe a mano
   sui Container e sui widget, mai alle classi che genera Elementor
   (`.elementor-element-xxxxx`). Quelle cambiano quando qualcuno duplica un
   elemento nell'editor, e il giorno che succede il sito si scompone senza
   che nessuno capisca perche'.

   I valori vengono dal prototipo (`fornito/prototipo/`), non sono inventati.
   I colori e i font passano dai Global di Elementor: cambiarli nel pannello
   cambia anche queste regole, che e' il punto.
   ===================================================================== */

/* Gli alias stanno su `body`, non su `:root` (giro 6, decisione 279). Elementor stampa i
   Global come variabili su `.elementor-kit-9`, cioe' sul body: una `var()` si risolve
   sull'elemento che la dichiara, e su `:root` i Global non esistono ancora. Dichiarati li',
   gli alias leggevano sempre il ripiego e il pannello di Elementor non cambiava niente
   (misurato il 2026-09-15: colore e famiglia del Kit forzati, reso invariato). */
body {
	/* Alias leggibili dei Global di Elementor. Servono a rendere questo file
	   leggibile senza dover ricordare a memoria quale slot e' quale — e a farlo
	   restare corretto se il cliente cambia un colore dal pannello. */
	--lb-arancio: var(--e-global-color-primary, #f2571f);
	--lb-titolo:  var(--e-global-color-secondary, #fdf9f2);
	--lb-testo:   var(--e-global-color-text, #efe8dd);
	--lb-tenue:   var(--e-global-color-accent, #8c8478);
	--lb-fondo:   var(--e-global-color-lbfondo, #0a0908);
	--lb-superficie: var(--e-global-color-lbsuperf, #151210);
	--lb-su-arancio: var(--e-global-color-lbsuaranc, #160a05);
	--lb-filo:    var(--e-global-color-lbfilo, rgba(240, 225, 200, .14));

	--lb-extrawide: var(--e-global-typography-primary-font-family, "roc-grotesk-extrawide"), system-ui, sans-serif;
	--lb-regular:   var(--e-global-typography-text-font-family, "roc-grotesk"), system-ui, sans-serif;
	/* Titoli di sezione, CTA, nomi dei servizi, lead: il ruolo «Titoli di sezione» del Kit.
	   Dal giro 6 sono ExtraWide su richiesta del cliente (decisione 276). */
	--lb-titolo-famiglia: var(--e-global-typography-secondary-font-family, "roc-grotesk-extrawide"), system-ui, sans-serif;
}

/* Angoli (giro 6, decisione 273): su richiesta del cliente foto, pillole, chip, badge e box sono
   rettangolari, «come il carattere, squadrato e su base rettangolare». Le dichiarazioni a 14-18 px
   e a 999px sono diventate `border-radius: 0` lasciate al loro posto, così chi cerca il raggio di
   un elemento lo trova dove l'aveva sempre trovato. Restano tondi per disegno: ritratti del team,
   frecce dell'hero, cursore, spinner, × del filtro attivo, segno di /grazie/, focus ring. */

:root {
	/* L'unico valore davvero costante di tutto il prototipo. */
	--lb-margine: clamp(22px, 4vw, 54px);
	--lb-respiro-sezione: clamp(84px, 11vw, 160px);
}

/* --- Base -------------------------------------------------------------- */

/* `border-box` su tutto. Stava in `site.css` del prototipo e cancellandolo si e'
   perso: senza, ogni padding si somma alla larghezza dichiarata invece di starci
   dentro, e le celle del muro loghi erano alte 180px invece di 140. Il reset di
   Hello Elementor lo dava, ed e' stato tolto di proposito (vedi functions.php):
   quello che serviva va ridichiarato qui, non riesumando il reset. */
*, *::before, *::after { box-sizing: border-box; }

body {
	background: var(--lb-fondo);
	color: var(--lb-testo);
	font-family: var(--lb-regular);
	line-height: 1.55;
}
/* Il carosello dei lavori sborda a destra di proposito: le card continuano oltre
   il bordo dello schermo, com'è nel prototipo. Senza tagliare a livello di
   documento quello sbordo diventa una barra di scorrimento orizzontale.
   `clip` e non `hidden`: `hidden` renderebbe l'elemento un contenitore di
   scorrimento e romperebbe l'header fisso e le àncore. */
html, body { overflow-x: clip; }

/* La larghezza massima del contenuto e il margine laterale.
   La larghezza e' la stessa del kit (1360px, impostata in `kit-globali.php`), ma
   qui e' dichiarata invece che ereditata: Elementor la scrive in
   `uploads/elementor/css/base-desktop.css`, che in questo ambiente risponde 500
   e non viene servito. Dipendere da un file che non arriva vuol dire una pagina
   larga 1440 invece di 1360 senza che nessuno capisca perche'.
   Il margine laterale `clamp(22px,4vw,54px)` e' l'unico valore davvero costante
   di tutto il prototipo, e il kit non lo gestisce con clamp(). */
.e-con-boxed > .e-con-inner {
	max-width: 1360px;
	margin-inline: auto;
	padding-inline: var(--lb-margine);
}

/* --- La catena di altezza del widget immagine di JetEngine ---------------
   **G3-05, G3-08 e G3-11 non erano tre difetti: era questa riga, mancante in
   cinque punti su tredici.** Diagnosi di `dev-wp-g3` il 2026-08-26, confermata
   da `dev-wp` in autonomia e rimisurata qui prima di scrivere.

   JetEngine stampa il widget immagine come una catena di quattro scatole:

       [riquadro]  ha `aspect-ratio` (4/5, 4/3) e quindi un'altezza vera
        └ .elementor-widget-jet-listing-dynamic-image
           └ .elementor-widget-container
              └ div.jet-listing.jet-listing-dynamic-image
                 └ <img height:100%; object-fit:cover>

   **Le tre scatole di mezzo nascono ad altezza `auto`.** `height: 100%`
   sull'`<img>` si risolve contro un antenato `auto` e **degrada ad `auto`**:
   l'immagine tiene la propria proporzione naturale e `object-fit: cover` non ha
   niente da coprire. Un `cover` fra due proporzioni uguali è una dichiarazione
   inerte — c'era su tutte le immagini del sito, e su cinque componenti non
   faceva niente.

   **La prova che è una causa sola e non tre:** il tema aveva già questa riga
   scritta a mano in **otto punti** — `.lb-hero__sfondo`, `.lb-card__immagine`,
   `.lb-progetto-hero__sfondo`, `.lb-prossimo__foto`, `.lb-im-hero__sfondo`,
   `.lb-cap__foto`, `.lb-im-bleed`, `.lb-ss-hero__media`, `.lb-ss-anima__foto`.
   **Sono esattamente i punti dove funzionava.** I cinque dove mancava —
   `.lb-galleria__img`, `.lb-duo__img`, `.lb-pezzo__img`, `.lb-split__cornice`,
   `.lb-servizio-riga__immagine` — hanno tutti la loro regola `img { height:100% }`
   e non serve a niente. Non è una regola nuova: è la nona copia, scritta una
   volta sola per tutti.

   **Servono tutte e quattro le righe.** Senza la prima, `.lb-split__cornice` e
   il parallasse restano rotti, perché lì l'`aspect-ratio` sta sul contenitore
   **padre** e non sul widget; senza la terza si rompe tutto il resto.

   ⚠️ **Cosa cambia e va detto, non nascosto.** Dove l'immagine era **più bassa**
   del riquadro spariscono i vuoti (`/servizi/` 165px, galleria di Alto 93).
   Dove era **più alta** il ritaglio c'era già ma era **ancorato in cima**: ora è
   **centrato**. È la resa del prototipo, non una scelta nuova — là
   `#sr .frame img` e `#sr2 .hstory .panel .im img` sono
   `width:100%; height:100%; object-fit:cover` **senza `object-position`**, cioè
   il centrato di serie. Vale la decisione 32: il prototipo è la specifica.

   **Cosa la falsificherebbe:** un riquadro in cui l'immagine deve entrare intera
   senza ritaglio — un logo, uno screenshot. Sul sito non ce n'è: gli unici
   `<img>` fuori dai riquadri sono i loghi di testata e piede e il muro clienti,
   e **nessuno dei due passa da `jet-listing-dynamic-image`**. Se un giorno ne
   compare uno, la regola va scopata via di lì, non indebolita.

   **Le otto copie a mano restano dove sono.** Sono ridondanti ma non nocive
   (misurate), e cancellarle nella stessa passata renderebbe impossibile
   attribuire una regressione. È una pulizia separata e reversibile. */
.elementor-widget-jet-listing-dynamic-image,
.elementor-widget-jet-listing-dynamic-image > .elementor-widget-container,
.jet-listing-dynamic-image,
.jet-listing-dynamic-image__link { height: 100%; }

/* --- Tipografia dei ruoli ricorrenti ----------------------------------- */

/* Eyebrow: l'etichetta corta arancione con il filo davanti. Nel prototipo ha sei
   valori di letter-spacing diversi fra le pagine; il design system ne fissa uno. */
/* **Due classi sul widget, non una.** Elementor scrive in ogni foglio di pagina
   e **in ogni foglio di blocco di libreria** la stessa regola non qualificata
   `.elementor-widget-heading .elementor-heading-title { line-height: ... }`, che
   porta l'interlinea 1,02 del kit. Ha la stessa specificita' di
   `.lb-eyebrow .elementor-heading-title`, quindi decide l'ordine di caricamento —
   e i fogli dei blocchi (`post-87`, `post-88`, `post-90`) sono accodati DOPO
   quelli del tema.

   Conseguenza misurata il 2026-08-25: l'eyebrow e' alto 15,9px su home, Chi
   siamo, Lavori e Grazie, e **10,4px sulle due schede progetto**, che sono le
   uniche pagine a usare blocchi di libreria. Stessa regola, stesso tema, due
   risultati — e il colpevole non e' visibile guardando il tema.
   `.elementor-widget.lb-eyebrow` porta la specificita' a tre classi e chiude la
   corsa. Dove l'interlinea era gia' 1,55 non cambia niente: forza il valore che
   quelle pagine calcolavano gia'. */
.elementor-widget.lb-eyebrow .elementor-heading-title {
	/* `flex`, non `inline-flex`: da elemento in linea il widget che lo contiene
	   eredita l'interlinea del corpo e diventa 25px alto invece di 16. */
	display: flex;
	align-items: center;
	gap: 13px;
	font-family: var(--lb-extrawide);
	font-weight: 700;
	font-size: .64rem;
	letter-spacing: .32em;
	text-transform: uppercase;
	color: var(--lb-arancio);
	line-height: 1.55;
	margin: 0;
}
.elementor-widget.lb-eyebrow .elementor-heading-title::before {
	content: "";
	width: 30px;
	height: 1px;
	background: currentColor;
	flex: 0 0 auto;
}

/* Titolo di sezione: Regular bold maiuscolo, con la parte evidenziata in arancione.
   L'`<span>` dentro il titolo e' il modo in cui il cliente evidenzia una parola:
   nell'editor si seleziona il testo e si usa il pulsante del colore. */
.lb-titolo-sezione .elementor-heading-title {
	/* Giro 6 (decisione 276): ExtraWide su richiesta del cliente, corpo ridotto perché
	   l'ExtraWide è largo 1,7 volte il Regular. Era Regular, clamp(1.5rem, 3.3vw, 2.8rem), 24ch. */
	font-family: var(--lb-titolo-famiglia);
	font-weight: 700;
	font-size: clamp(1.3rem, 2.7vw, 2.3rem);
	line-height: 1.14;
	letter-spacing: -.01em;
	text-transform: uppercase;
	color: var(--lb-titolo);
	text-wrap: balance;
	max-width: 20ch;
	/* Nessun margine sotto. Nel prototipo il titolo è un <p> e porta il margine di
	   default del browser, che però COLLASSA con quello del blocco successivo:
	   l'effetto reale è la distanza maggiore fra i due, non la loro somma. Qui i
	   blocchi sono elementi flex e i margini non collassano — sommarli
	   allungherebbe ogni sezione di una quarantina di pixel. La distanza la
	   dichiara il blocco che segue. */
	margin: 22px 0 0;
}

/* Variante larga: nelle sezioni Lavori e Servizi il titolo è più piccolo e non
   viene strozzato a 24 caratteri. Sono due valori diversi nel prototipo, non una
   svista: il titolo che sta sopra una griglia respira in orizzontale, quello che
   apre una sezione di testo respira in verticale. */
.lb-titolo-sezione--largo .elementor-heading-title {
	font-size: clamp(1.55rem, 3.4vw, 2.5rem);
	line-height: 1.04;
	max-width: none;
	margin: 16px 0 0;
}
/* Sopra la galleria il titolo sta su una riga sola e l'interlinea è compatta. */
.lb-lavori .lb-titolo-sezione--largo .elementor-heading-title { line-height: 1; }
/* Nei Servizi il titolo va a capo: nel prototipo la larghezza è `60ch` misurata
   sul contenitore a 16px, cioè ~580px. Espressa in `em` del titolo resta stabile
   quando la dimensione cambia con la finestra. */
.lb-servizi .lb-titolo-sezione--largo .elementor-heading-title { max-width: 14.5em; }
.lb-titolo-sezione .elementor-heading-title span,
.lb-titolo-sezione .elementor-heading-title em { color: var(--lb-arancio); font-style: normal; }

/* Titolo grande: ExtraWide, per l'hero e la CTA di chiusura. */
.lb-titolo-grande .elementor-heading-title {
	font-family: var(--lb-extrawide);
	font-weight: 700;
	font-size: clamp(1.9rem, 4.6vw, 3.4rem);
	line-height: 1.02;
	letter-spacing: .02em;
	text-transform: uppercase;
	color: var(--lb-titolo);
	text-wrap: balance;
	max-width: 16ch;
	margin: 0;
}
.lb-titolo-grande .elementor-heading-title span,
.lb-titolo-grande .elementor-heading-title em { color: var(--lb-arancio); font-style: normal; }

/* Titolo della CTA di chiusura: stessa misura del titolo grande, ma famiglia dei
   testi. Nel prototipo `.cta-h` usa `--font-body`, non `--font-head`. */
.lb-titolo-cta .elementor-heading-title {
	/* Giro 6: ExtraWide (decisione 276). Era Regular, clamp(1.9rem, 4.6vw, 3.4rem), 16ch. */
	font-family: var(--lb-titolo-famiglia);
	font-weight: 700;
	font-size: clamp(1.6rem, 3.7vw, 2.75rem);
	line-height: 1.02;
	letter-spacing: -.01em;
	text-transform: uppercase;
	color: var(--lb-titolo);
	text-wrap: balance;
	max-width: 15ch;
	margin: 0;
}
.lb-titolo-cta .elementor-heading-title em { color: var(--lb-arancio); font-style: normal; }

/* --- Pulsante ----------------------------------------------------------
   I selettori portano davanti `.elementor-widget-button` per una ragione
   precisa, non per abitudine: Elementor stampa
   `.elementor-widget-button .elementor-button{background-color:var(--e-global-color-accent)}`
   come stile di default del widget, DOPO il CSS del kit e con la stessa
   specificità del nostro. Senza quel prefisso ogni pulsante del sito diventa
   grigio-beige — il colore del testo attenuato — e sembra un errore di stampa.
   Un selettore più specifico risolve senza `!important`. */
.elementor-widget-button.lb-cta .elementor-button,
.lb-cta .elementor-button {
	background: var(--lb-arancio);
	color: var(--lb-su-arancio);
	font-family: var(--lb-regular);
	font-weight: 700;
	font-size: .82rem;
	letter-spacing: .02em;
	/* **L'interlinea va ridichiarata, e non è una rifinitura: è 7px su ogni
	   pulsante del sito.** Elementor scrive l'interlinea del kit in `em`, quindi
	   diventa una lunghezza già sul widget e i figli la ereditano come tale; sul
	   pulsante invece non arriva affatto e resta 1,0. Nel prototipo il pulsante
	   non dichiara interlinea e prende quella del corpo, 1,55: la pillola grande
	   è alta 50px e non 43, quella compatta dell'intestazione 37 e non 31.
	   Da qui **l'intestazione torna alta 79px come nel prototipo** sopra i 600,
	   e smette di essere uno scostamento da spiegare. Trovato da `dev-servizi`
	   misurando il footer e l'intestazione sulla sua pagina. */
	line-height: 1.55;
	padding: 15px 28px;
	/* Giro 6 (B1): il raggio dei pulsanti lo decide il Kit di Elementor (oggi 0). Il tema
	   non lo dichiara più, altrimenti il pannello «Pulsanti» del Kit non cambierebbe niente. */
	border: none;
	transition: transform .2s, filter .2s;
}
.elementor-widget-button.lb-cta .elementor-button:hover,
.lb-cta .elementor-button:hover { transform: translateY(-2px); filter: brightness(1.08); }

.elementor-widget-button.lb-cta--compatta .elementor-button,
.lb-cta--compatta .elementor-button {
	padding: 10px 20px;
	font-size: .68rem;
	text-transform: uppercase;
	letter-spacing: .12em;
}
/* Sotto i 600px il prototipo toglie la CTA dall'header e lascia solo il menu.
   Il design system segnala che così su mobile sparisce l'unico modo esplicito di
   contattare dall'alto: resta come nel prototipo, ma è annotato come domanda aperta. */
@media (max-width: 600px) { .lb-cta--compatta { display: none; } }

/* --- Sezioni ----------------------------------------------------------- */
.lb-sezione { padding-block: var(--lb-respiro-sezione); }

/* --- Header ------------------------------------------------------------ */
.lb-top {
	position: fixed;
	inset-inline: 0;
	top: 0;
	z-index: 120;
}
/* Scrim: sopra l'hero l'header non ha fondo, ma una sfumatura che parte dal nero
   e svanisce. Senza, logo e menu spariscono quando la foto sotto è chiara. */
.lb-top::before {
	content: ""; position: absolute; left: 0; right: 0; top: 0; height: 190%;
	pointer-events: none; z-index: -1; opacity: 1; transition: opacity .4s ease;
	background: linear-gradient(180deg, rgba(10, 9, 8, .72) 0%, rgba(10, 9, 8, .34) 46%, rgba(10, 9, 8, 0) 100%);
}
/* `.lb-top--fondo` la mette comportamenti.js dopo 24px di scroll: da lì in poi
   serve un fondo vero, e la sfumatura si spegne.

   🔴 **Il fondo sfocato sta su `::after` e NON sull'header, e non è una
   preferenza di stile: spostarlo sull'header rompe il velo dei due pannelli.**

   Chiude **G3-13②**, «l'overlay sfocato e scuro a volte si applica solo alla
   testata». Il «a volte» era «da 24 px di scorrimento in poi».

   La catena. I due Off-Canvas — menu e cassetto del modulo — sono widget
   **dentro `<header class="lb-top">`**. Un elemento con `backdrop-filter` è
   **blocco contenitore per i `position: fixed` che ha dentro**, e crea un
   **backdrop root**: da lì in giù la sfocatura campiona solo ciò che sta dentro
   quell'elemento. Quindi appena l'header prendeva `backdrop-filter`, il
   `.e-off-canvas` (che è `position: fixed; height: 100%`) smetteva di misurarsi
   sulla finestra e si misurava sull'header. Misurato in browser, stessa pagina,
   stesso pannello:

       pagina in cima      velo 1873 × 1000 px    pixel che cambiano lontano
                                                  dalla testata: 100%
       scorsa di 900 px    velo 1873 × 78,9 px    pixel che cambiano: 0%

   78,9 px è **l'altezza dell'header**. Non era una sfocatura debole: era un velo
   grande quanto la testata.

   Su uno pseudo-elemento la resa è identica al pixel — stesso colore, stessa
   sfocatura, stessa dissolvenza — ma l'header torna un elemento qualunque e il
   `position: fixed` dentro di lui torna a misurarsi sulla finestra.
   Lo pseudo-elemento non ha discendenti, quindi il blocco contenitore che crea
   lui non riguarda nessuno. */
.lb-top--fondo { border-bottom: 1px solid var(--lb-filo); }
.lb-top::after {
	content: ""; position: absolute; inset: 0; z-index: -1;
	pointer-events: none; opacity: 0;
	background: color-mix(in srgb, var(--lb-fondo) 80%, transparent);
	-webkit-backdrop-filter: blur(14px) saturate(1.08);
	backdrop-filter: blur(14px) saturate(1.08);
	transition: opacity .3s ease;
}
.lb-top--fondo::after { opacity: 1; }
.lb-top--fondo::before { opacity: 0; }
/* L'header su una riga sola non sta più qui: è un'impostazione del Container,
   in `costruisci-tema-builder.php`. La regola che c'era cadeva su `.e-con`
   invece che su `.e-con-inner` — in un Container `boxed` il contenitore flex è
   il secondo — e comunque perdeva contro `.e-con.e-flex > .e-con-inner` di
   Elementor, che ha una classe in più. A 390px l'intestazione veniva **122px
   invece di 73**, con «Menu» a capo sotto il logo e sopra la prima riga di
   contenuto. Si vedeva solo su una pagina senza hero a tutta altezza — cioè
   su Lavori, che è dove è saltata fuori. Trovato da `dev-lavori`. */
 /* Il widget Logo del sito impone `width:100%` all'immagine: il logo e' un
   lockup orizzontale 300x37, e con la larghezza forzata si schiaccia. Qui comanda
   l'altezza e la larghezza segue le proporzioni. */
.lb-logo img,
.elementor-widget-theme-site-logo.lb-logo img {
	/* 23px nell'header, 26px nel footer: sono i due valori del prototipo. */
	height: 23px; width: auto; max-width: none; display: block;
}
.lb-foot .lb-logo img { height: 26px; }

/* --- Pulsante del menu ---
   Nel prototipo accanto all'icona c'è la parola «Menu»: su un sito dove la
   navigazione è tutta dentro un pannello, l'icona da sola lascia il visitatore
   a indovinare. Elementor emette solo l'icona, l'etichetta la mette il tema.
   `aria-label` sul pulsante lo mette già Elementor, quindi per chi usa uno
   screen reader non cambia niente: questa parola è per chi guarda. */
.lb-menu .elementor-menu-toggle {
	display: flex; align-items: center; gap: 11px;
	background: none; border: none; color: var(--lb-titolo);
}
.lb-menu .elementor-menu-toggle::before {
	content: "Menu";
	font-family: var(--lb-extrawide);
	font-weight: 700; text-transform: uppercase;
	letter-spacing: .16em; font-size: .68rem;
}
.lb-menu .elementor-menu-toggle i,
.lb-menu .elementor-menu-toggle svg { font-size: 22px; width: 26px; height: auto; fill: currentColor; }
/* Il widget Logo del sito rende `<a><img></a>` e basta: niente
   `.elementor-widget-container`, niente `figure`. I tre selettori che c'erano
   qui non agganciavano nulla — restano per i widget Immagine, che invece li
   hanno. Quello che serve è **il link**: da `inline-block` si porta dietro lo
   spazio sotto la linea di base (23px di logo dentro una riga alta 24,8), e
   l'intestazione veniva 8px più alta del dovuto. Si vede solo sotto i 600px,
   dove il pulsante «Contattaci» sparisce e il logo diventa l'elemento più alto
   della riga: lì l'intestazione era 73px invece dei 66 del prototipo. */
.lb-logo .elementor-widget-container,
.lb-logo figure, .lb-logo .elementor-image { display: flex; width: auto; }
.lb-logo > a, .lb-logo .elementor-widget-container > a { display: block; }
/* Il widget e' un elemento flex dell'header. Elementor gli impone una larghezza
   calcolata (`--container-widget-width`) e dentro forza `width:100%` sull'immagine:
   il risultato e' un lockup orizzontale di 772x96 schiacciato in 48px.
   `!important` qui e' mirato e motivato: si sta scavalcando una larghezza che il
   framework impone a tutti i widget e che per un logo non ha senso. */
.lb-logo { flex: 0 0 auto; width: auto !important; }
.lb-logo img { width: auto !important; }

/* --- Pannello del menu (Off-Canvas di Elementor Pro) ---
   A tutto schermo, come nel prototipo: la navigazione di questo sito è un gesto,
   non una barra. Il widget porta da sé `role="dialog"`, `aria-modal` e `inert`,
   quindi con il Tab non si esce dal pannello aperto — cosa che l'overlay del
   prototipo non faceva. */
.lb-pannello-menu .e-off-canvas {
	--e-off-canvas-width: 100vw;
	--e-off-canvas-height: 100vh;
}
/* **Col pannello aperto la pagina sotto deve stare ferma, e non stava.**
   Correzione del difetto 5 della revisione di interazione: misurato, a pannello
   aperto una rotellata di 900 px portava `scrollY` da 1200 a 2100 a 390 px di
   larghezza, e da 1500 a 2700 con 1200 px a 1440 — e alla chiusura si restava
   là, 900 px più in basso di dove si era, senza aver fatto niente di volontario
   sulla pagina.

   Elementor Pro il suo lavoro lo fa: all'apertura mette `e-off-canvas__no-scroll`
   su `<body>` (`off-canvas.131ce85cfbdc3b72eac1.bundle.js:176`) e il suo foglio
   ci appende `overflow:hidden`. Solo che **in questo sito a scorrere non è
   `body`: è `html`**, per via del `overflow-x: clip` di riga 63 che rende `body`
   un blocco qualunque. Il blocco era messo sull'elemento sbagliato.

   Si aggancia la classe che il plugin mette già, non se ne inventa una: se un
   giorno Elementor cambia il modo di chiudere lo scorrimento, questa riga smette
   di agire e non resta a bloccare la pagina per conto suo.
   `overflow: hidden` su `html` non azzera `scrollTop`: alla chiusura si torna
   esattamente dove si era, che è la seconda metà del difetto.

   **⚠️ `e-off-canvas__no-scroll-animation` NON va aggiunta a questa regola, e
   la ragione è stata misurata sul sito servito.** Il foglio del plugin la elenca
   accanto all'altra — `body.e-off-canvas__no-scroll,body.e-off-canvas__no-scroll-animation{overflow:hidden}`
   — e sembra quindi che vadano scritte tutte e due, per non lasciare lo
   scorrimento libero durante l'animazione. **Provato: fa il danno opposto.**

   Il ciclo di vita, letto in `off-canvas.…bundle.js` e poi verificato in browser:
   `maybeOnCloseAnimation()` (riga 152) **aggiunge** `-animation` alla chiusura, e
   a toglierla è `removeAnimationClasses()` (riga 161), che è agganciata a
   `animationend` (riga 63). Qui l'animazione d'uscita è **`none`**: nessuna
   animazione parte, `animationend` **non arriva mai**, e la classe **resta su
   `<body>` per sempre**. Misurato: presente ancora 2,6 secondi dopo la chiusura,
   e a quel punto immutabile.

   Per Elementor è innocuo, perché la sua regola parla di `body` e qui `body` non
   scorre. Per noi no: con `-animation` in questa riga, **dopo il primo menu
   aperto e chiuso `html` resta `overflow: hidden` e la pagina non scorre mai
   più**, su ogni pagina. Un istante di scorrimento dietro un pannello che sta
   sparendo è un difetto piccolo; una pagina che si blocca per sempre è un sito
   rotto. Non si scambiano.

   **Due segnali che devono concordare.** `e-off-canvas__no-scroll` la mette
   `maybeDisableScroll()` e la toglie `enableScroll()`: nessuna delle due dipende
   da un'animazione. In più si chiede che il pannello sia davvero aperto —
   `aria-hidden="false"`, che è esatto: misurato `true` da chiuso, `false` da
   aperto, `true` di nuovo già a 150 ms dalla chiusura. Se un domani una delle
   due vie di uscita si inceppa, l'altra sblocca comunque la pagina. */
html:has(body.e-off-canvas__no-scroll .e-off-canvas[aria-hidden="false"]) { overflow: hidden; }
.lb-pannello-menu__dentro { padding-inline: clamp(22px, 8vw, 108px); }

/* Le voci: numerate dal CSS, non a mano. Se il cliente ne aggiunge una o le
   riordina da Aspetto → Menu, i numeri si aggiornano da soli. */
 /* Il numero sta sul <li>, non sul link: Elementor usa già `::before` e `::after`
   dell'`.elementor-item` per gli effetti del puntatore, e il suo `content:""`
   vince sul nostro. Sul <li> il campo è libero. */
.lb-voci-menu .elementor-nav-menu { counter-reset: lb-voce; }
.lb-voci-menu .elementor-nav-menu > li {
	counter-increment: lb-voce;
	display: flex;
	align-items: center;
	gap: 26px;
}
.lb-voci-menu .elementor-nav-menu > li::before {
	content: counter(lb-voce, decimal-leading-zero);
	font-family: var(--lb-extrawide);
	font-weight: 700;
	font-size: .78rem;
	letter-spacing: .1em;
	color: var(--lb-arancio);
	flex: 0 0 auto;
}
.lb-voci-menu .elementor-nav-menu .elementor-item {
	padding: 10px 0;
	font-family: var(--lb-extrawide);
	font-weight: 700;
	font-size: clamp(2rem, 6vw, 4.6rem);
	line-height: 1.06;
	letter-spacing: .01em;
	text-transform: uppercase;
	color: var(--lb-titolo);
	transition: color .25s ease, transform .25s ease;
}
.lb-voci-menu .elementor-nav-menu .elementor-item:hover,
.lb-voci-menu .elementor-nav-menu .elementor-item:focus-visible {
	color: var(--lb-arancio);
	transform: translateX(14px);
}

/* Il pulsante di chiusura: in alto a destra, come nel prototipo, e dice «Chiudi»
   e non solo una crocetta — una parola costa niente e toglie un'incertezza. */
.lb-menu-chiudi {
	position: absolute;
	top: clamp(18px, 3vw, 30px);
	right: clamp(22px, 4vw, 54px);
	width: auto !important;
}
.elementor-widget-button.lb-menu-chiudi .elementor-button,
.lb-menu-chiudi .elementor-button {
	background: none; border: none; padding: 0;
	color: var(--lb-titolo);
	font-family: var(--lb-extrawide);
	font-weight: 700; text-transform: uppercase;
	letter-spacing: .14em; font-size: .68rem;
}
.lb-menu-chiudi .elementor-button::after { content: "✕"; margin-left: 10px; font-size: .9rem; }
.lb-menu-chiudi .elementor-button:hover { color: var(--lb-arancio); }
.lb-pannello-menu__dentro { position: relative; }

/* =====================================================================
   I DUE PANNELLI A SCOMPARSA — menu e cassetto del modulo
   ---------------------------------------------------------------------
   Sono due Off-Canvas di Elementor con lo stesso involucro, quindi la resa
   condivisa sta qui una volta sola. Chiude G2-26, G2-27, G2-28, G2-30 e G2-31.
   ===================================================================== */

/* **G2-26 e G2-28/G2-30: il velo dietro il pannello.** Fabrizio: «non è stato
   applicato l'effetto sfocato e scurente al resto della pagina», e «manca il
   fadeout» sia alla chiusura del cassetto sia a quella del menu.

   Misurato: il velo **c'era già ed era del colore giusto** — `rgba(6,5,4,.48)`
   sul cassetto, identico al prototipo — ma con `backdrop-filter: none` e
   `transition: 0s`. Mancavano quindi **lo sfocato** e **il tempo**: appariva e
   spariva di scatto, ed è quello che si legge come «poco armonico».

   I valori vengono dal prototipo, `.lb-drawer-scrim`:
   `backdrop-filter: blur(9px) saturate(.92)` e `transition: opacity .4s ease`.
   La leggera desaturazione conta quanto la sfocatura: senza, le fotografie
   dietro restano vivaci e il pannello sembra appoggiato invece che davanti. */
.lb-pannello-menu .e-off-canvas__overlay,
.lb-cassetto-contatti .e-off-canvas__overlay {
	-webkit-backdrop-filter: blur(9px) saturate(.92);
	backdrop-filter: blur(9px) saturate(.92);
	transition: opacity .4s ease;
}

/* 🔴 **G4-09: su un telefono vero il pulsante «Invia» non si raggiunge, e non
   perché il cassetto non scorra.** Fabrizio: «lo slider funziona bene ma anche
   se scrollo non riesco a raggiungere il pulsante per inviare il messaggio».

   ⚠️ **Il primo rilievo puntava l'elemento sbagliato**, ed è la famiglia della
   decisione 176 — numero giusto, oggetto sbagliato. `.e-off-canvas` è
   `overflow-y: visible` con `scrollHeight == clientHeight`, quindi letto da lì
   sembra che non ci sia niente da scorrere. **A scorrere non è lui: è
   `.e-off-canvas__content`**, che ha già `overflow-y: auto` e 253 px di corsa.
   Il «+166 px sotto il bordo» del triage è esatto — 825 − 659 — ma è la
   posizione dell'invio a corsa NON fatta.

   La causa vera è l'unità di misura. Il cassetto è alto
   `--e-off-canvas-height: 100vh`, valore che mette Elementor sul contenitore
   `lbmod` (`post-57.css`). **Su iOS `vh` non è l'area visibile: è l'area
   visibile PIÙ la barra degli indirizzi** — nella specifica si chiama *large
   viewport* ed è fissa, non si accorcia quando la barra c'è. Quindi il cassetto
   è più alto della finestra, **la sua corsa di scorrimento si accorcia della
   stessa quantità**, e il fondo del modulo resta sotto il bordo anche dopo aver
   scorso tutto.

   Misurato in WebKit emulando la geometria di iOS (cassetto = finestra + barra),
   con lo stato opposto misurato dalla stessa sonda:

       finestra 659 · cassetto 745   corsa 167   invio a 705   -> 46 px FUORI
       finestra 629 · cassetto 711   corsa 201   invio a 671   -> 42 px FUORI
       finestra 659 · cassetto 659   corsa 253   invio a 619   -> dentro
       finestra 629 · cassetto 629   corsa 283   invio a 589   -> dentro

   `dvh` è l'unità che vale l'area visibile **adesso**: con la barra a schermo il
   cassetto si accorcia insieme a lei e la corsa torna intera. La riga `100vh`
   resta sopra come ripiego per Safari < 15.4, che ignora la seconda.

   ⚠️ **Dentro WebKit sul Mac `dvh` e `vh` valgono lo stesso**, perché non c'è
   nessuna barra che si ritrae: le quattro righe qui sopra sono l'emulazione
   della geometria, **non la prova sul telefono**. Su iOS vero la verifica finale
   è Fabrizio.

   La variabile si **definisce**, non si combatte (trappola 4): Elementor la mette
   sul contenitore e la fa ereditare, quindi dichiararla sull'`.e-off-canvas`
   stesso la batte senza alzare la specificità. Stessa forma già in uso per il
   pannello del menu, venti righe più su. */
.lb-cassetto-contatti .e-off-canvas {
	--e-off-canvas-height: 100vh;
	--e-off-canvas-height: 100dvh;
}

/* **Il margine di sicurezza sotto l'ultimo campo non è respiro: è la rete se
   `dvh` non c'è.** Safari lo conosce dalla 15.4; sotto quella versione la riga
   qui sopra cade su `100vh` e il difetto tornerebbe intero.

   La quantità non è scelta a occhio, è l'aritmetica del caso peggiore. Con una
   spaziatura `P`, a cassetto alto quanto finestra+barra la corsa vale `167 + P`
   e il fondo dell'invio finisce a `705 − P`; deve stare dentro 659, quindi
   **`P ≥ 46`**. Sull'iPhone 12 Mini lo stesso conto dà `P ≥ 42`. **56 px
   coprono entrambi** e non dipendono da `dvh`: verificato forzando il cassetto
   alla geometria di iOS **con `dvh` fuori gioco**.

   Solo su telefono: sul desktop il cassetto non scorre e la riga non serve. */
@media (max-width: 767px) {
	.lb-cassetto-contatti .e-off-canvas__content { padding-bottom: 56px; }
}

/* **G2-27: il pulsante «chiudi» del modulo era diverso dal prototipo perché non
   aveva nessuno stile nostro.** Cercato in questo foglio: `.lb-cassetto__chiudi`
   non compariva da nessuna parte, quindi usciva col vestito predefinito del
   widget Pulsante — fondo, bordo e spaziatura di Elementor.

   I valori sono quelli di `.lb-drawer-x` nel prototipo. Il colore parte da
   `--lb-tenue` e va a `--lb-titolo` al passaggio del mouse: un comando di
   servizio che non deve competere col titolo del modulo. */
.elementor-widget-button.lb-cassetto__chiudi .elementor-button,
.lb-cassetto__chiudi .elementor-button {
	background: none; border: none; padding: 4px 0;
	color: var(--lb-tenue);
	font-family: var(--lb-regular);
	font-weight: 700; text-transform: uppercase;
	letter-spacing: .14em; font-size: .68rem;
	transition: color .2s;
}
/* **Il pulsante «chiudi» andava a capo: «CHIUDI» sopra, la «✕» sotto.**
   Visto in Chrome vero a 1873px, non a 1440: a 1440 il riquadro era più
   stretto e la parola ci stava comunque, quindi il difetto non si vedeva.

   Ci sono arrivato in tre passi, e i primi due erano sbagliati:

   1. `align-self: flex-end`, preso dal prototipo (`.lb-drawer-x`). **Fa danno
      qui:** il widget smette di occupare la riga e si stringe a 46px, così il
      testo esce dal pulsante. Nel prototipo è un `<button>` nudo dentro una
      colonna flex; qui è un widget Elementor con un involucro in mezzo, e la
      stessa dichiarazione dà un risultato diverso. L'allineamento a destra il
      widget ce l'ha già: bastava non toglierlo.
   2. `white-space: nowrap` da solo. **Non poteva bastare:** dentro
      `.elementor-button` la parola sta in un `.elementor-button-content-wrapper`
      che Elementor rende **flex di livello blocco**, e il nostro `::after` gli
      sta dopo. Fra due box di blocco non c'è riga da spezzare — `nowrap` non ha
      voce in capitolo.
   3. Quella che funziona: **il pulsante diventa lui stesso una riga flex**, così
      involucro e crocetta sono due elementi affiancati.

   Il resto del blocco — colore, corpo, spaziatura — è di `.lb-drawer-x`. */
.lb-cassetto__chiudi .elementor-button {
	display: inline-flex; align-items: center; white-space: nowrap;
}
.lb-cassetto__chiudi { margin-bottom: 18px; }
.lb-cassetto__chiudi .elementor-button::after { content: "✕"; margin-left: 10px; font-size: .9rem; }
.lb-cassetto__chiudi .elementor-button:hover { color: var(--lb-titolo); }

/* **Occhiello del cassetto («Iniziamo»).** Nel tema non esisteva nessuna regola
   per `lb-cassetto__occhiello`: usciva con la tipografia del kit (16px,
   letter-spacing -0.5px) attaccato al titolo, a tutte le larghezze. Valori del
   prototipo, `.lb-drawer-eyebrow` (site.css r.163): maiuscolo, .3em, .6rem, 700,
   arancione, a blocco; il margine sotto e' il `margin-top: 14px` di
   `.lb-drawer-title`. NON riuso `.lb-eyebrow`: ha il filo davanti e .32em, che
   qui il prototipo non ha. Tre classi sul widget (Elementor stampa la tipografia
   del kit a due), e il margine sul titolo del widget e non sul widget. */
.elementor-widget-heading.lb-cassetto__occhiello .elementor-heading-title {
	display: block;
	font-family: var(--lb-regular);
	font-weight: 700;
	font-size: .6rem;
	letter-spacing: .3em;
	line-height: 1.55;
	text-transform: uppercase;
	color: var(--lb-arancio);
	margin: 0 0 14px;
}

/* **G2-31: alle voci di menu mancavano le frecce.** Nel prototipo
   (`site.css`, `.site .overlay nav a`) ogni voce ha una freccia arancione che
   **entra da sinistra** al passaggio del mouse, la voce stessa scivola a destra,
   e **le altre voci si spengono a .38** — è quest'ultimo dettaglio a far sembrare
   il menu una scelta e non un elenco.

   La freccia sta sul link e non sul `<li>`: il `::before` del `<li>` porta già
   il numero progressivo, e i due non si contendono lo stesso pseudo-elemento. */
/* **G3-15: la freccia finiva all'inizio della voce, sopra il testo.**
   Fabrizio, 2026-08-26: *«dovrebbe stare dopo la scritta invece qui appare
   sovrapposta al testo all'inizio»*. Riprodotto e misurato aprendo il menu in
   browser: in hover lo `::after` risultava a **x=204** e la parola «Chi siamo»
   comincia a **x=204**. Esattamente sopra.

   **La causa: lo pseudo-elemento non era nostro da solo.** Elementor lo usa per
   l'effetto «pointer» delle voci di menu e lo dichiara
   `position: absolute; inset: 10px 570px 10px 0; width: 70px; height: 78px;
   background-color: …`. Noi ci scrivevamo dentro `content` e `color`, cioè le
   due cose che *non* decidono dove sta: il posto lo decidevano le sue. Con
   `opacity: 0` a riposo non si vedeva niente, e in hover comparivano insieme la
   freccia arancione **e il rettangolo grigio del pointer**.

   Quindi la geometria va **riportata a noi tutta**, non ritoccata: `static`
   rimette lo pseudo nel flusso — e dentro una voce che Elementor rende
   `display: flex` questo vuol dire «l'ultimo elemento della riga», cioè dopo la
   scritta, come nel prototipo (`site.css`, `.site .overlay nav a::after`).
   `background: none` spegne la barra del pointer, che non è mai stata nostra.

   ⚠️ Stessa famiglia di G3-01: **su un componente di Elementor uno pseudo-
   elemento ha già un proprietario**, e le proprietà che non dichiari resta lui
   a dichiararle. */
.lb-voci-menu .elementor-nav-menu .elementor-item::after {
	content: "\2192";
	position: static;
	inset: auto;
	width: auto; height: auto;
	background: none;
	display: inline-block;
	flex: 0 0 auto;
	margin-left: .38em;
	color: var(--lb-arancio);
	opacity: 0;
	transform: translateX(-16px);
	transition: opacity .4s ease, transform .45s cubic-bezier(.16, 1, .3, 1);
}
/* **La barra del pointer resiste anche a `background: none`, e non per ordine
   di sorgente: per specificità.** Misurata sul reso dopo la prima correzione —
   `backgroundColor: rgb(140, 132, 120)` ancora addosso allo pseudo-elemento.
   La dichiara il **foglio della pagina**, `post-57.css`, con
   `.elementor-widget-nav-menu .elementor-nav-menu--main:not(.e--pointer-framed)
   .elementor-item::after { background-color: var(--e-global-color-accent) }`:
   **quattro classi**, mentre la regola qui sopra ne ha tre. Con `opacity: 0` a
   riposo non si vedeva, e in hover comparivano insieme la freccia e un
   rettangolo grigio da 70×78.

   Le cinque classi qui sotto stanno tutte su **quei due elementi**, non su
   antenati: `lb-voci-menu` ed `elementor-widget-nav-menu` sul widget,
   `elementor-nav-menu--main` ed `elementor-nav-menu__container` sul `<nav>`,
   `elementor-item` sul link. Nessuna dipende da dove il menu è appeso, quindi
   la regola non smette di applicarsi se il pannello cambia involucro. */
.lb-voci-menu.elementor-widget-nav-menu
	.elementor-nav-menu--main.elementor-nav-menu__container
	.elementor-item::after { background-color: transparent; }
.lb-voci-menu .elementor-nav-menu .elementor-item:hover {
	color: var(--lb-arancio);
	transform: translateX(clamp(8px, 1.4vw, 20px));
}
.lb-voci-menu .elementor-nav-menu .elementor-item:hover::after {
	opacity: 1;
	transform: translateX(0);
}
/* Le altre si spengono: e' il gesto che rende il menu una scelta. */
.lb-voci-menu .elementor-nav-menu:hover .elementor-item:not(:hover) { opacity: .38; }
.lb-voci-menu .elementor-nav-menu .elementor-item { transition: color .3s ease, opacity .3s ease, transform .45s cubic-bezier(.16, 1, .3, 1); }

/* Il pulsante che lo apre: «Menu» più le due barre del prototipo (due, non tre). */
.elementor-widget-button.lb-menu-apri .elementor-button,
.lb-menu-apri .elementor-button {
	background: none; border: none; padding: 0;
	color: var(--lb-titolo);
	font-family: var(--lb-extrawide);
	font-weight: 700; text-transform: uppercase;
	letter-spacing: .16em; font-size: .68rem;
}
.lb-menu-apri .elementor-button-content-wrapper { display: flex; align-items: center; gap: 12px; }
.lb-menu-apri .elementor-button-text { flex: 0 0 auto; }
/* **Il pulsante è un contenitore flex, e senza questa riga le due barre non si
   vedono.** Le barre sono un `::after` con larghezza e altezza: dentro un
   `inline-block` — il valore predefinito di `.elementor-button` — lo
   pseudo-elemento è un box **in linea**, e su un box in linea non replaced
   larghezza e altezza non si applicano. La regola c'era, il disegno no: nel
   prototipo accanto alla parola «Menu» ci sono due trattini da 26px
   (`partials/header.html`, `.menu-btn .ln i`). Trovato da `dev-servizi`
   affiancando i due screenshot. */
.elementor-widget-button.lb-menu-apri .elementor-button,
.lb-menu-apri .elementor-button { display: inline-flex; align-items: center; gap: 12px; }
.lb-menu-apri .elementor-button::after {
	content: "";
	width: 26px; height: 11px;
	border-top: 1.5px solid currentColor;
	border-bottom: 1.5px solid currentColor;
	flex: 0 0 auto;
}

/* --- Hero -------------------------------------------------------------- */
.lb-hero {
	position: relative;
	min-height: 100svh;
	overflow: hidden;
	padding: 0;
}
 /* Lo sfondo dell'hero deve arrivare ai bordi dello schermo: il Container di
   Elementor e la griglia di JetEngine portano entrambi una spaziatura di default
   che qui lascerebbe una cornice nera intorno alla foto. */
.lb-hero__sfondo { position: absolute; inset: 0; z-index: 0; padding: 0; }
.lb-hero__sfondo > .e-con-inner,
.lb-hero__sfondo .elementor-widget-container,
.lb-hero__sfondo .jet-listing-grid,
.lb-hero__sfondo .jet-listing-grid__items,
.lb-hero__sfondo .jet-listing-grid__item { padding: 0; margin: 0; }
.lb-hero__sfondo > .e-con-inner { height: 100%; }
.lb-hero__sfondo .elementor-widget,
.lb-hero__sfondo .elementor-widget-container { height: 100%; }
 /* La catena di contenitori fra il Container e la foto: se anche uno solo non è
   alto quanto il padre, l'hero mostra una banda nera invece della fotografia.
   Su desktop reggeva per caso, su mobile no. */
.lb-hero__sfondo .jet-listing-grid,
.lb-hero__sfondo .jet-listing-grid__slider,
.lb-hero__sfondo .jet-listing-grid__items,
.lb-hero__sfondo .jet-listing-grid__item,
.lb-hero__sfondo .swiper,
.lb-hero__sfondo .swiper-wrapper,
.lb-hero__sfondo .swiper-slide { height: 100%; }
/* Nell'hero la card del progetto serve solo per la foto: titolo e tipologia
   li mostra la barra sotto il claim, non la slide. */
.lb-hero__sfondo .lb-card__meta { display: none; }
/* **G2-03: l'«ombreggiatura strana ai margini dx e sx» dell'hero era il velo
   della card.** Nell'hero la card serve solo per la fotografia: nome, categoria
   e link li mostra la barra sotto il claim, ed è per questo che due righe sopra
   la meta è nascosta. Il velo invece era rimasto acceso, e al passaggio del
   mouse dipingeva il suo gradiente scuro sulla diapositiva sotto il puntatore —
   comprese quelle di fianco, che nel ciclo continuo affacciano ai due bordi. Da
   qui i «margini dx e sx».

   Nel prototipo l'hero è uno sfondo, non una scheda: nessun velo. */
.lb-hero__sfondo .lb-card__vai { display: none; }
 /* Nell'hero la foto riempie lo schermo: va tolto il rapporto 4:5 che la card usa
   nella griglia, o l'immagine si ridimensiona su quello e lascia il resto nero. */
.lb-hero__sfondo .lb-card,
.lb-hero__sfondo .lb-card__foto { height: 100%; gap: 0; aspect-ratio: auto; border-radius: 0; }
 /* Posizionata in assoluto invece che con `height:100%`: fra il Container e
   l'immagine ci sono tre involucri di Elementor e di JetEngine, e basta che uno
   non propaghi l'altezza perché la foto torni alle sue proporzioni e lasci
   metà schermo nero. Così l'unico riferimento è il riquadro dell'hero. */
 /* Fra la slide e la foto ci sono sei involucri — due di Elementor, tre di
   JetEngine, uno del template del listing — e l'altezza si propaga solo se la
   dichiarano tutti. Basta che uno resti `auto` perché l'immagine torni alle sue
   proporzioni e l'hero mostri una banda nera. */
.lb-hero__sfondo .elementor,
.lb-hero__sfondo .lb-card,
.lb-hero__sfondo .lb-card__foto,
.lb-hero__sfondo .lb-card__immagine,
.lb-hero__sfondo .jet-listing-dynamic-image,
.lb-hero__sfondo .jet-listing-dynamic-image__link { height: 100%; }
.lb-hero__sfondo .lb-card__foto img {
	position: absolute; inset: 0;
	width: 100%; height: 100%; object-fit: cover; border-radius: 0;
	animation: lb-ken 6.5s ease forwards;
}
@keyframes lb-ken { from { transform: scale(1.03); } to { transform: scale(1.15); } }

/* **Il parallasse non è in ritardo: è in transizione.** G2-07 e G2-15, e sono
   la stessa cosa — Fabrizio le ha viste su Chi siamo e su Splendide, «come ci
   sia un ritardo».

   La causa non è nel nostro JavaScript, che scrive il `transform` a ogni
   fotogramma dentro `requestAnimationFrame`. È che questi elementi sono
   **Container di Elementor**, e `.e-con` porta di serie
   `transition: background, border, box-shadow, transform` con **0,4s sul
   transform**. Misurato sul sito servito, identico su tutti e tre gli hero.

   Ogni valore che il JS scrive viene quindi **interpolato per quattro decimi di
   secondo** prima di arrivare a schermo: la foto insegue lo scroll invece di
   seguirlo. È l'unico caso in cui una transizione, che di solito addolcisce,
   fa il contrario — perché qui il valore cambia già sessanta volte al secondo e
   non ha bisogno di essere addolcito da nessuno.

   Si spegne **solo sul transform e solo dove il JS lo guida**: il resto delle
   transizioni di Elementor su questi contenitori resta, e gli altri contenitori
   del sito non vengono toccati. `.lb-ss-hero__media img` non è in elenco perché
   è un `img` e non un Container: lì la transizione non c'era, ed è il motivo
   per cui il parallasse di Packaging non era stato segnalato. */
.lb-hero__sfondo,
.lb-cs-hero__sfondo,
.lb-im-hero__sfondo,
.lb-im-hero__testo,
.lb-cs-duo__foto img,
.lb-prossimo__foto {
	transition-property: none;
}
/* **Uno dei sei strati perdeva, e l'ha trovato la riverifica del 2026-08-26.**
   Cinque su sei calcolavano `none`; `.lb-prossimo__foto` no, e rendeva
   `background, border, border-radius, box-shadow, transform` — cioe' la
   transizione di serie dei Container, che era proprio quello che G2-07/15
   toglieva. La riga sopra e' **(0,1,0)** e perde contro

     .elementor-element:where(...):where(...):not(:has(.elementor-widget-container))

   di `frontend.min.css`, che vale **(0,2,0)**: `:where()` non conta niente,
   ma `:not(:has(.classe))` conta come una classe. Gli altri cinque reggono solo
   perche' quel selettore non li intercetta — non perche' siano piu' forti.

   Qui serve **(0,3,0)**, non (0,2,0): a parita' deciderebbe l'ordine di
   caricamento, e l'ordine fra tema e Kit **cambia da pagina a pagina** — e'
   la stessa cosa che ci ha fatto uscire il titolo del piede arancione.
   Gli altri cinque restano dove sono: alzarli tutti sarebbe debito su un
   problema che oggi non hanno. */
.lb-prossimo .elementor-element.lb-prossimo__foto { transition-property: none; }

/* **Gli altri tre, trovati cercando per sintomo invece che per elenco.**
   La passata del lead ha posto la domanda giusta — «chi porta ancora una
   transizione?» — invece di controllare i selettori che gia' conoscevamo. Un
   elenco verifica solo cio' che qualcuno ha pensato di metterci dentro.

   Filtrati per **durata**, perche' una `transition-property: all` con durata
   `0s` non ritarda niente e correggerla sarebbe rumore. Ne restano tre veri:

     /chi-siamo/            .lb-cs-duo__testo                       0,4s
     /progetto/splendide/   .lb-split__cornice--parallasse img      0,7s
     /progetto/alto/        .lb-bleed ... jet-listing-dynamic-image 0,4s

   ⚠️ **Quella da 0,7s e' nostra, non di Elementor**, ed e' la piu' istruttiva:
   la riga 1610 di questo foglio mette `transition: transform .7s` per lo zoom
   al passaggio del mouse. Sulla stessa immagine il JavaScript scrive `transform`
   a ogni scatto di scroll — quindi **lo zoom dell'hover ritardava il parallasse
   di sette decimi**. E' letteralmente la frase di Fabrizio: «come ci sia un
   ritardo». Non «come»: c'era, e l'avevamo scritto noi.

   Lo zoom su quelle immagini non si perde perche' **non funzionava gia'**: il
   JavaScript scrive un `transform` in linea, che batte la regola `:hover` del
   foglio. Una cornice in parallasse non ha mai avuto lo zoom.

   Le prime due sono la transizione di serie dei Container e vogliono (0,3,0),
   per la ragione di sempre: a (0,2,0) deciderebbe l'ordine di caricamento. */
/* Due classi e non una: `.lb-split__cornice img` (riga 1610) ha la **stessa**
   specificita' (0,1,1) e sta **piu' in basso** nel foglio, quindi vincerebbe per
   ordine. L'ho caricata cosi' e infatti restava a 0,7s: una sola pagina su nove
   ancora in ritardo. Con la classe ripetuta si passa a (0,2,1) e l'ordine non
   conta piu'. */
.lb-split__cornice.lb-split__cornice--parallasse img { transition-property: none; }
.lb-cs-duo .elementor-element.lb-cs-duo__testo,
.lb-bleed .elementor-element.elementor-widget-jet-listing-dynamic-image {
	transition-property: none;
}

.lb-hero__velo {
	position: absolute; inset: 0; z-index: 2; padding: 0; pointer-events: none;
	background: linear-gradient(180deg,
		rgba(10, 9, 8, .60) 0%, rgba(10, 9, 8, .12) 24%, rgba(10, 9, 8, .30) 50%,
		rgba(10, 9, 8, .74) 74%, rgba(10, 9, 8, .95) 100%);
}
/* --- Il velo di leggibilità sotto il testo che sta su fotografia ----------
   **Una regola sola per i tre hero fotografici del sito**, ed è dichiarata qui
   e non tre volte perché il problema non è di una pagina: è di chiunque metta
   testo piccolo sopra una foto che non ha scelto lui.

   Da cosa nasce. La revisione di accessibilità del 2026-08-25 ha misurato il
   contrasto **sui pixel della fotografia**, non sul CSS: reso il testo
   trasparente, fotografata la sua area, calcolato il rapporto sullo sfondo che
   c'era sotto. Quattro occhielli fuori soglia — `/progetto/splendide-royal/`
   **1,14:1**, `/chi-siamo/` **1,30:1** e i due arancioni a 2,56 e 3,13 contro i
   4,5:1 richiesti — e uno praticamente invisibile.

   **La cosa che conta più dei quattro casi**: `/progetto/alto-cocktail-bar/` ha
   lo stesso componente con la stessa regola e passa (4,73:1) **solo perché la
   sua foto è scura**. L'occhiello non aveva nessuna protezione: né velatura, né
   ombra del testo. Reggeva o no a seconda della fotografia — e le sette
   diapositive dell'hero della home **le cambia il cliente dalla bacheca**,
   come le foto delle dieci schede progetto non ancora pubblicate. Correggere le
   foto di oggi non sarebbe stato correggere niente.

   Perché legato al blocco di testo e non al velo dell'hero. Il velo è una
   gradazione sull'**altezza della sezione**: rinforzarlo vorrebbe dire scurire
   la foto dove il testo non c'è, e resterebbe tarato sull'impaginazione di
   oggi. Questo invece si aggancia al riquadro che **contiene** il testo: se il
   cliente sposta o allunga il blocco, la protezione lo segue.

   La forma: nasce trasparente 200px sopra il blocco e arriva a coprire dove sta
   l'occhiello, che è la riga piccola in cima. Il titolo sotto è tipografia
   gigante (fino a 7rem) e la soglia dei testi grandi la passa da sé; la riga da
   10,6px no, ed è quella che va protetta. `-1`: sta dietro al testo e davanti
   al velo, dentro il contesto di sovrapposizione che lo `z-index: 3` del blocco
   crea già. Stesso inchiostro dei tre veli — `rgba(10, 9, 8, …)`, nessun colore
   nuovo — così il raccordo non si vede. */
.lb-hero__fold,
.lb-cs-hero__fold,
.lb-im-hero__testo { isolation: isolate; }
.lb-hero__fold::before,
.lb-cs-hero__fold::before,
.lb-im-hero__testo::before {
	content: "";
	position: absolute;
	left: 0; right: 0; bottom: 0;
	top: -200px;
	/* **G3-01 e G3-02: senza questa riga il velo finisce 200px prima del fondo.**
	   Segnalazione di Fabrizio del 2026-08-26, e le sue parole erano esatte al
	   pixel: *«ora arriva dalla scritta creative factory al copy dalla prima
	   idea»*. Misurato in browser sull'hosting: la sfumatura si spegneva a
	   **y=800** su tutte e due le pagine — che sulla home è dove finisce il copy
	   «Dalla prima idea…» e su Chi siamo è un gradino netto a metà fotografia.

	   **La causa non è in questa dichiarazione, è in una che non c'è.**
	   `elementor/assets/css/frontend.min.css` dichiara
	   `.e-con:before { height: max(100% + …, 100%) }` con specificità **(0,1,0)**,
	   la stessa di questo selettore. `top` e `bottom` li scriviamo noi e vincono
	   per ordine di sorgente; **`height` non lo scrivevamo**, quindi l'altezza la
	   decideva Elementor: `100%` del contenitore, cioè 410px sulla home e 510 su
	   Chi siamo — che partendo da `top:-200px` finiscono **200px sopra** il fondo
	   del blocco. `bottom: 0` c'era e non serviva a niente.

	   `height: auto` restituisce la geometria alle due dichiarazioni che la
	   descrivono: `top` e `bottom`. Misurato dopo: 390→1000 sulla home (era
	   390→800) e 290→1000 su Chi siamo (era 290→800), cioè fino a «Guarda il
	   progetto» e fino allo «Scorri».

	   ⚠️ **La classe di errore vale per ogni `::before` su un contenitore
	   Elementor**: se scrivi `top`/`bottom`/`left`/`right` e non `height`/`width`,
	   le due che mancano te le scrive `.e-con:before`. */
	height: auto;
	z-index: -1;
	pointer-events: none;
	/* Le prime tre fermate sono in **pixel** e non in percentuale, di proposito:
	   devono cadere dentro i 200px di rincorsa, che sono una lunghezza fissa. In
	   percentuale si sposterebbero al variare dell'altezza del blocco, cioè della
	   lunghezza del titolo — e la protezione dell'occhiello dipenderebbe da
	   quante righe scrive il cliente.

	   **I valori vengono dal caso peggiore, non dalle foto di oggi.** Con .80 a
	   200px i numeri sulle foto attuali erano buoni — 5,53:1 sulla home, 4,52:1
	   su Chi siamo — ma sostituendo la diapositiva con un **bianco pieno**, cioè
	   il peggio che il cliente possa caricare dalla bacheca, scendevano a
	   **4,43** e **4,04**: sotto la soglia di 4,5. Un trattamento tarato sulle
	   foto in libreria non è un trattamento, è una coincidenza che dura fino al
	   prossimo caricamento.

	   A .90 sul bordo alto del blocco l'inchiostro complessivo, sommato al velo
	   della sezione, tiene il fondo abbastanza scuro perché `--lb-tenue` a
	   10,6px passi **anche sul bianco pieno**. Chi ritocca questi numeri li
	   riprovi così: si sostituisce la foto con un rettangolo bianco e si
	   rimisura, non si guarda la pagina com'è oggi. */
	background: linear-gradient(180deg,
		rgba(10, 9, 8, 0) 0,
		rgba(10, 9, 8, .34) 96px,
		rgba(10, 9, 8, .66) 150px,
		rgba(10, 9, 8, .90) 200px,
		rgba(10, 9, 8, .94) 100%);
}

/* **G2-01: la piega si mangiava i clic sulla slide.**
   Diagnosi di `dev-wp`, misurata con una griglia di dodici punti sull'hero:
   sei navigavano e sei no, e i sei muti erano tutti nella fascia **590-879**,
   dove l'ancora della slide c'e' ma la piega le sta sopra con `z-index: 3`.

   Sopra i 853px la piega **non contiene niente di interattivo**: occhiello,
   claim, sottotitolo, barra. Sono duecentosessanta pixel di testo che
   bloccavano il clic all'immagine che sta sotto.

   `pointer-events: none` fa passare il clic attraverso la piega; la riga dopo
   **riaccende i suoi quattro comandi veri** — il nome del progetto, «Guarda il
   progetto» e le due frecce — che restano cliccabili come prima. Il contatore
   non e' un comando e resta trasparente ai clic, che e' giusto.

   ⚠️ Vale solo per la piega dell'hero, dove sotto c'e' una slide da aprire.
   Non e' un rimedio da spargere: altrove `pointer-events: none` renderebbe il
   testo non selezionabile senza che nessuno ne guadagni niente. */
.lb-hero__fold {
	position: relative; z-index: 3;
	width: 100%;
	padding-bottom: clamp(40px, 7vh, 80px);
	pointer-events: none;
}
.lb-hero__fold a,
.lb-hero__fold button,
.lb-hero__fold input,
.lb-hero__fold [role="button"] { pointer-events: auto; }
.lb-hero__fold .lb-eyebrow .elementor-heading-title { margin-bottom: 20px; }

.lb-claim .elementor-heading-title {
	font-family: var(--lb-extrawide);
	/* 700, non 300: nel prototipo il peso è dichiarato 300 in cima al file e
	   ridichiarato 700 più in basso. Vince l'ultima regola. */
	font-weight: 700;
	font-size: clamp(2.1rem, 6vw, 5rem);
	line-height: 1.02;
	letter-spacing: .04em;
	text-transform: uppercase;
	color: var(--lb-titolo);
	text-wrap: balance;
	max-width: 15ch;
	margin: 0 0 18px;
}
 /* Solo <em>: il claim viene spezzato riga per riga in <span class="lb-riga">,
   quindi colorare gli <span> colorerebbe tutto il titolo. */
.lb-claim .elementor-heading-title em { color: var(--lb-arancio); font-style: normal; }
.lb-claim .lb-riga { display: block; white-space: nowrap; line-height: .92; }
@media (max-width: 700px) { .lb-claim .lb-riga { white-space: normal; } }

.lb-hero__sottotitolo {
	color: rgba(255, 255, 255, .82);
	font-size: clamp(1rem, 1.5vw, 1.25rem);
	line-height: 1.4;
	max-width: 34ch;
	margin-bottom: 40px;
}
.lb-hero__barra {
	border-top: 1px solid rgba(255, 255, 255, .2);
	padding-top: 22px;
	flex-wrap: wrap;
}
.lb-hero__progetto { min-width: 260px; width: auto; }
.lb-hero__progetto .lb-categoria {
	font-size: .64rem; text-transform: uppercase; letter-spacing: .22em; color: var(--lb-arancio);
}
.lb-hero__progetto .lb-nome {
	font-weight: 400; letter-spacing: .02em; color: var(--lb-titolo);
	font-size: clamp(1.3rem, 2.4vw, 1.9rem);
}
.lb-hero__progetto .lb-vai {
	display: inline-flex; align-items: center; gap: 9px; margin-top: 4px;
	font-size: .72rem; text-transform: uppercase; letter-spacing: .14em;
	color: rgba(255, 255, 255, .85);
}
.lb-hero__progetto .lb-vai::after { content: "→"; color: var(--lb-arancio); }
/* I comandi di un carosello: una forma sola per i due che il sito ha.
   Erano scritti solo per `.lb-hero__comandi`; dalla decisione 134c li porta
   anche la giostra dei progetti, e due caroselli sulla stessa pagina con due
   disegni diversi sarebbero peggio che uno senza comandi.
   `:is()` prende la specificità del suo argomento più alto — qui una classe —
   quindi il peso delle regole resta quello di prima. */
.lb-hero__comandi { width: auto; }
/* **`flex: 0 0 46px` fa il cerchio: senza, erano ovali.** G2-02, prima metà.
   Misurato: **58×46** con `border-radius: 50%`, che su un rettangolo disegna
   un'ellisse — ed era proprio «ora è un ovale».

   `width: 46px` c'era già e non bastava: come elemento flex il pulsante ha
   `min-width: auto`, cioè un minimo pari al proprio contenuto, e il glifo del
   segno in Roc Grotesk è più largo di 46 px. La larghezza dichiarata perdeva
   contro il minimo automatico. `flex-basis` fisso più `min-width: 0` tolgono la
   questione: il cerchio è 46×46 qualunque cosa ci sia dentro.

   La seconda metà di G2-02 — il segno che deve essere un'icona SVG e non un
   carattere di font — sta in `comportamenti.js`, dove i pulsanti vengono
   costruiti. Con l'icona il glifo sparisce, ma queste due righe restano: se un
   domani qualcuno rimette del testo, il cerchio non torna ovale. */
.lb-hero__comandi .lb-freccia {
	width: 46px; height: 46px; flex: 0 0 46px; min-width: 0; padding: 0; border-radius: 50%;
	border: 1px solid rgba(255, 255, 255, .28);
	background: rgba(255, 255, 255, .04);
	color: var(--lb-titolo); font-size: 1.4rem; line-height: 1;
	display: flex; align-items: center; justify-content: center;
	transition: border-color .2s, color .2s, background .2s, transform .15s;
}
.lb-hero__comandi .lb-freccia:hover {
	border-color: var(--lb-arancio); color: var(--lb-arancio); background: rgba(242, 87, 31, .09);
}
.lb-hero__comandi .lb-freccia:active { transform: scale(.9); }
.lb-hero__comandi .lb-contatore {
	font-size: .72rem; letter-spacing: .16em; color: rgba(255, 255, 255, .7);
	font-variant-numeric: tabular-nums;
}
.lb-hero__comandi .lb-contatore b { color: var(--lb-titolo); font-weight: 400; }
.lb-hero__comandi .lb-avanzamento {
	width: 120px; height: 2px; background: rgba(255, 255, 255, .2);
	position: relative; overflow: hidden;
}
.lb-hero__comandi .lb-avanzamento i {
	position: absolute; inset-block: 0; left: 0; width: 0; background: var(--lb-arancio);
}

/* --- Statement --------------------------------------------------------- */
.lb-statement__colonne { margin-top: 60px; align-items: flex-start; }
.lb-statement__colonne .elementor-widget-text-editor p,
.lb-statement__colonne p { color: var(--lb-tenue); font-size: 1.02rem; max-width: 46ch; margin: 0; }
.lb-statement__colonne b { color: var(--lb-titolo); font-weight: 600; }
@media (max-width: 900px) { .lb-statement__colonne { flex-direction: column; } }

/* --- Muro clienti ---------------------------------------------------------
   Widget Galleria di Elementor. Le linee di separazione non sono bordi ma il
   fondo che si vede attraverso un `gap` di 1px: e' il modo in cui il prototipo
   le fa, e regge qualunque numero di loghi per riga senza doppie linee agli
   incroci. `object-fit: contain`, mai `cover`: 24 loghi hanno 24 proporzioni
   diverse, e ritagliarli per riempire la cella li mutila. */
.lb-muro-clienti { margin-top: 52px; }
.lb-muro-clienti > .elementor-widget-container { padding: 0; }
.lb-muro-clienti .elementor-image-gallery .gallery {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
	gap: 1px;
	background: var(--lb-filo);
	border: 1px solid var(--lb-filo);
	border-radius: 0;
	overflow: hidden;
	margin: 0;
}
/* WordPress mette `max-width:16.66%` su ogni cella della galleria a 6 colonne.
   Dentro una griglia CSS quel 16,66% si calcola sulla cella e non sulla riga: il
   logo si riduceva a 34px e la parete sembrava vuota. */
.lb-muro-clienti .elementor-image-gallery .gallery-item {
	margin: 0 !important; width: auto !important; max-width: none !important; float: none;
	background: var(--lb-fondo);
	min-height: 140px;
	display: flex; align-items: center; justify-content: center;
	padding: 20px;
	transition: background .25s ease;
}
.lb-muro-clienti .elementor-image-gallery .gallery-item:hover { background: var(--lb-superficie); }
.lb-muro-clienti .elementor-image-gallery .gallery-icon {
	width: auto; display: flex; align-items: center; justify-content: center;
}
/* **Il trattamento comune del muro, dichiarato — e dichiarato anche cosa non
   copre.** Prima non ce n'era nessuno (né `filter`, né `opacity` normalizzante,
   né `mix-blend-mode`): la resa dipendeva interamente dal file che il cliente
   aveva caricato, e con ventiquattro loghi arrivati come sono arrivati si vedeva.

   `grayscale(1)` toglie la dominante cromatica, ed è la parte che **funziona
   per costruzione**: nessun logo può più stonare per colore, nemmeno quello che
   il cliente caricherà domani senza chiedere niente a nessuno. Misurato su
   «TRECCANI», l'unico colorato della griglia: saturazione media dei suoi pixel
   di inchiostro **da 0,543 a 0,014**, cioè dentro il rumore degli altri
   ventitré (il secondo più colorato sta a 0,036).

   **Quello che il codice qui NON risolve, e non lo finge.** Gli altri due
   rilievi della revisione — il logo a scudo che «si intuisce che c'è qualcosa,
   non che cosa», e «ACQUOLINA» e «So Chic» al limite — sembrano un problema di
   luminosità e non lo sono: misurati, quei loghi sono **già bianchi**, e a
   renderli deboli è il tratto sottile visto attraverso `opacity: .72`. Un
   `brightness()` su del bianco non ha niente da sollevare: provato a 1,9 e
   misurato **inerte** su ventitré celle su ventiquattro. Toglierlo è preferibile
   a lasciarlo lì a somigliare a una correzione.

   La via giusta il prototipo ce l'ha già, ed è **per singolo logo**:
   `#v4 .cwall .cell--ext img { filter: brightness(0) invert(1) }`
   (`lion-brand-home.html:141`) appiattisce a silhouette bianca **solo** le celle
   che ne hanno bisogno. Applicarlo a tutte non si può: «Il Pensiero Scientifico
   Editore» è un PNG **senza canale alfa**, e brightness(0)+invert(1) ne farebbe
   un rettangolo bianco pieno — si scambierebbe un difetto con uno più grosso.
   Il Widget Galleria di WordPress non dà nessun aggancio per cella, quindi il
   `--ext` qui richiede una scelta sul markup **e** i file monocromatici dal
   cliente: due cose che non stanno in questo foglio. Segnalate, non nascoste. */
/* Giro 8: i loghi arrivano già equilibrati, tutti sulla stessa tela 480×300 con la
   superficie pesata da `ambiente/build/normalizza-loghi.py`. Il peso visivo sta nel
   file, quindi qui si dà a tutti la stessa larghezza: un tetto in altezza li
   rimpicciolirebbe di nuovo in modo diseguale. */
.lb-muro-clienti .elementor-image-gallery img {
	width: 100%; max-width: 200px; height: auto;
	object-fit: contain; border: none;
	filter: grayscale(1);
	opacity: .72; transition: opacity .25s ease, transform .25s ease;
}
/* Il grigio resta anche al passaggio del mouse. La tentazione era restituire il
   colore su hover — sembra generoso col marchio del cliente — ma il criterio di
   chiusura della revisione dice «nella griglia nessun logo ha una dominante
   cromatica diversa dagli altri», e una parete uniforme che diventa a chiazze
   sotto il puntatore lo rispetta solo da ferma. */
.lb-muro-clienti .elementor-image-gallery .gallery-item:hover img { opacity: 1; transform: scale(1.06); }
@media (max-width: 600px) {
	.lb-muro-clienti .elementor-image-gallery .gallery { grid-template-columns: 1fr 1fr; }
	.lb-muro-clienti .elementor-image-gallery .gallery-item { min-height: 120px; }
}
/* Giro 8: la cella arancione che chiude l'ultima riga del muro. In pagina è un
   Container Elementor (`.lb-muro-cta`, link al cassetto del modulo) messo dopo la
   galleria, così il cliente ne cambia il testo dall'editor. `pagine.js` la sposta
   dentro la griglia solo se l'ultima riga è incompleta e le dà `span` pari alle
   celle vuote. Non `grid-column: auto / -1`: quello occupa UNA colonna in fondo,
   non tutte quelle libere (misurato a 1024 px: 1 su 3). Senza script non compare:
   il muro resta com'era, con la cella vuota. */
.lb-muro-cta { display: none !important; }
.elementor-editor-active .lb-muro-cta,
.lb-muro-clienti .gallery > .lb-muro-cta { display: flex !important; }
.lb-muro-clienti .gallery > .lb-muro-cta[hidden] { display: none !important; }
.lb-muro-clienti .gallery > .lb-muro-cta {
	flex-direction: column; align-items: center; justify-content: center; gap: 6px;
	min-height: 140px; padding: 20px; text-align: center;
	background: var(--lb-arancio); color: var(--lb-su-arancio);
	transition: filter .25s ease;
}
.lb-muro-clienti .gallery > .lb-muro-cta:hover { filter: brightness(1.08); }
/* Una riga sola, nel carattere dei titoli: in una cella larga quanto un logo
   due righe in maiuscoletto a .7rem non si leggevano (Fabrizio, 2026-10-13). */
.lb-muro-cta .elementor-heading-title {
	margin: 0; color: var(--lb-su-arancio);
	font-family: var(--lb-titolo-famiglia); font-weight: 700;
	font-size: clamp(.95rem, 1.25vw, 1.15rem); line-height: 1.15; text-transform: uppercase;
}
.lb-muro-cta__vai .elementor-heading-title::after { content: " \2192"; }
@media (max-width: 600px) { .lb-muro-clienti .gallery > .lb-muro-cta { min-height: 120px; } }

/* Giro 8: il rimando dalla home alla pagina Clienti. Stessa voce di «Guarda il
   progetto» nell'hero (`.lb-vai`): un link, non un secondo pulsante arancione che
   faccia concorrenza a «Raccontaci il tuo progetto». */
.lb-link-vai { margin-top: 28px; }
.elementor-widget-button.lb-link-vai .elementor-button,
.lb-link-vai .elementor-button {
	display: inline-flex; align-items: center; gap: 9px; padding: 0;
	background: none; border: 0; border-radius: 0;
	font-family: var(--lb-regular); font-size: .72rem; font-weight: 400;
	text-transform: uppercase; letter-spacing: .14em; color: var(--lb-titolo);
	transition: color .2s ease;
}
.lb-link-vai .elementor-button::after { content: "\2192"; color: var(--lb-arancio); }
.elementor-widget-button.lb-link-vai .elementor-button:hover,
.lb-link-vai .elementor-button:hover { color: var(--lb-arancio); transform: none; filter: none; }

/* --- Lavori: testata e carosello --------------------------------------- */
.lb-lavori { padding-bottom: clamp(70px, 9vw, 120px); }
.lb-lavori__indizio .elementor-heading-title {
	font-size: .66rem; text-transform: uppercase; letter-spacing: .2em;
	color: var(--lb-tenue); display: flex; align-items: center; gap: 10px; margin: 0;
	white-space: nowrap;
}
.lb-lavori__indizio { flex: 0 0 auto; }
.lb-lavori__indizio .elementor-heading-title::after { content: "→"; color: var(--lb-arancio); }
.lb-lavori__pista { margin-top: 48px; padding-left: var(--lb-margine); }
@media (max-width: 900px) { .lb-lavori__testata { flex-direction: column; align-items: flex-start; } }

/* **G4-01: il carosello veniva tagliato a sinistra prima del bordo dello
   schermo, e non è una preferenza — è uno scostamento dal prototipo.**
   Fabrizio, da telefono: «a sinistra vengono tagliati prima della fine dello
   schermo. il carosello dovrebbe avere i margini dello schermo come limiti».

   Nel prototipo le due cose stanno **sullo stesso elemento**
   (`lion-brand-home.html:108-110`):

       #v4 .gwrap { overflow: hidden; padding-left: clamp(22px,4vw,54px); }

   e `.gwrap` è largo quanto lo schermo. `overflow` ritaglia sul **riquadro di
   riempimento**, non su quello del contenuto: quindi la prima card comincia al
   margine, ma trascinando le card scorrono *dentro* il riempimento e si
   tagliano **al bordo dello schermo**.

   Qui invece le due cose erano finite su elementi diversi — il riempimento sulla
   `.lb-lavori__pista`, il ritaglio sullo `.jet-listing-grid__slider` che ci sta
   dentro — e il riquadro di ritaglio cominciava a 22 invece che a 0. Misurato su
   iPhone 15: **slider 22 → 393**, cioè 22 px di schermo a sinistra in cui una
   card sparisce pur avendo ancora spazio.

   Il rimedio ricompone la coppia **senza toccare il trascinamento e senza
   cambiare le misure che Swiper legge**: margine negativo per portare il
   riquadro a 0, riempimento uguale e contrario per lasciare il contenuto a 22.
   Swiper sottrae il riempimento quando calcola la larghezza delle card, quindi
   la card resta larga come prima — verificato, non dedotto.

   ⚠️ **A riposo non cambia niente**, e per questo vale a tutte le larghezze e
   non solo su telefono: la differenza si vede solo mentre si trascina. */
.lb-lavori__pista .jet-listing-grid__slider {
	margin-left: calc(-1 * var(--lb-margine));
	padding-left: var(--lb-margine);
}

/* Il carosello di JetEngine deve sbordare a destra, come nel prototipo: le card
   continuano oltre il bordo dello schermo invece di fermarsi al contenitore. */
.lb-carosello .jet-listing-grid__slider { overflow: visible; }
.lb-carosello .swiper { overflow: visible; }
/* 🔴 **L'offset delle griglie JetEngine, tolto in un posto solo invece che in
   sei.** Richiesta di Fabrizio del 2026-08-26 dopo G4-07: *«allinea tutti i
   margini»*.

   `frontend.css` di JetEngine impagina ogni griglia con una coppia:

       .jet-listing-grid__items { width: calc(100% + 20px); margin: 0 -10px; }

   È il vecchio trucco delle gutter — si allarga di 20 e si riprendono 10 per
   lato — e **le due righe vanno insieme o non funziona nessuna delle due**. Le
   nostre regole per blocco ne hanno neutralizzata una alla volta, e il
   censimento su otto pagine mostra che ne erano nate **due famiglie di guasto**:

       margine tolto, larghezza no   ->  20 px tutti a destra   (era G4-07)
       margine intatto, larghezza no ->  10 px fuori per lato, SIMMETRICI

   ⚠️ **La seconda famiglia è la più insidiosa: essendo simmetrica non sembra
   rotta**, e infatti in tre giri di revisione nessuno l'ha segnalata. Si trova
   solo misurando, ed è il motivo per cui Fabrizio ha chiesto un allineamento e
   non una correzione.

   **Una riga per tutte invece di una per blocco**, perché è esattamente la forma
   della decisione 167: una neutralizzazione scritta a mano in più punti e
   dimenticata in alcuni. Scritta qui, chi aggiunge domani una griglia nuova la
   trova già allineata e non deve sapere niente di tutto questo.

   Due scelte di forma, e nessuna delle due è cosmetica:

   **① `.jet-listing-grid .jet-listing-grid__items` e non il selettore nudo.**
   Il nudo sarebbe `(0,1,0)`, identico a quello di JetEngine, e vincerebbe solo
   perché `tema.css` è servito dopo (indice 27 contro 4). Con l'involucro è
   `(0,2,0)` e vince **per specificità** — trappola 2.

   **② Sta qui in alto apposta.** Le due piste trascinabili dichiarano la propria
   larghezza più giù nel foglio con la stessa specificità `(0,2,0)`:
   `.lb-pezzi__pista` vuole `width: max-content` perché deve essere più larga
   della finestra. **A parità di specificità vince chi viene dopo**, quindi
   mettendo questa regola PRIMA le piste continuano a vincere e restano
   trascinabili. Spostarla in fondo al foglio le romperebbe tutte e due. */
.jet-listing-grid .jet-listing-grid__items {
	width: 100%;
	margin-left: 0;
	margin-right: 0;
}

.lb-carosello .jet-listing-grid__items { margin: 0; }

/* --- La pista continua dei progetti (decisione 152) -----------------------
   Il prototipo (`#v4 .gtrack`) non usa un carosello: usa **una fila sola che
   scorre da sola**, `display:flex; width:max-content`, mossa da JavaScript. Da
   noi c'era il carosello di JetEngine, che va a scatti di una scheda — e
   accanto c'era scritto «SCORRE DA SOLO · TRASCINA →», cioè il sito dichiarava
   un comportamento che non aveva.

   `overflow: hidden` sostituisce qui il `visible` di due righe più su, e vale
   **solo** quando la pista è viva: serviva a Swiper, che teneva le schede
   dentro con la sua matematica. Una fila larga `max-content` invece esce
   davvero dal contenitore, e senza ritaglio si stenderebbe su tutta la pagina.
   Il taglio cade esattamente sul bordo destro della finestra, perché la pista
   parte da `--lb-margine` e il contenitore è largo il resto.

   ⚠️ Niente `cursor: grab`: su questo sito il puntatore è disegnato da noi, e
   un cursore di sistema sopra quello dipinto se ne vedrebbero due. */
.lb-carosello .swiper.lb-vista-continua {
	overflow: hidden;
	/* **G3-03: perché la pista non si trascinava.** Il codice del trascinamento
	   c'era già; il gesto glielo rubava il browser. Le schede sono `<a>` con
	   dentro una `<img>`, e dopo pochi pixel Chrome decide che stai trascinando
	   il link: parte il trascinamento nativo, arriva `pointercancel`, il flusso
	   dei puntatori si interrompe. Misurato: su 320 px chiesti la pista si
	   muoveva **13,3 px**, con `dragstart 1` e `pointerup 0`.

	   `user-select: none` toglie anche la selezione del testo, che sul secondo
	   difetto ci si mette d'accordo: trascinando si evidenziavano i titoli.

	   ⚠️ **`touch-action: pan-y`, non `none`.** Serve a dire al browser che il
	   movimento orizzontale è nostro e quello verticale è suo. Con `none` si
	   prende anche il verticale, e su telefono **la pagina non si scorre più
	   passando il dito sopra il carosello**. */
	touch-action: pan-y;
	cursor: grab;
	user-select: none; -webkit-user-select: none;
}
.lb-carosello .swiper.lb-vista-continua.lb-trascina { cursor: grabbing; }
.lb-pista-continua img,
.lb-pista-continua a { -webkit-user-drag: none; }
.lb-pista-continua {
	display: flex; width: max-content;
	will-change: transform; transform: translate3d(0, 0, 0);
}
.lb-pista-continua > * { flex: 0 0 auto; }

/* --- Card di progetto ------------------------------------------------------
   Un componente, tre posizionamenti: griglia Lavori, galleria della home,
   progetti correlati della scheda servizio. Il prototipo ne aveva tre markup
   diversi (`.card`, `.gcard`, `.pcard`); qui cambia solo il vestito.

   Le misure sono quelle di `#lv .card` in `lion-brand-lavori.html`, che è la
   variante piena. La galleria della home usa `--compatta`, più sotto. */
.lb-card__foto {
	position: relative;
	aspect-ratio: 4 / 5;
	border-radius: 0;
	overflow: hidden;
	border: 1px solid var(--lb-filo);
	background: var(--lb-superficie);
	padding: 0;
}
.lb-card__immagine,
.lb-card__immagine > .elementor-widget-container,
.lb-card__immagine .jet-listing-dynamic-image { height: 100%; }
.lb-card__foto img {
	position: absolute; inset: 0;
	width: 100%; height: 100%; object-fit: cover; display: block;
	transition: transform .7s cubic-bezier(.16, 1, .3, 1);
}
.lb-card:hover .lb-card__foto img { transform: scale(1.06); }

/* Il velo: è anche l'unico link della card, e copre tutta la foto — quindi
   cliccare la foto porta al progetto, come nel prototipo dove la card intera
   era un `<a>`. Un solo link vuol dire anche una sola fermata col Tab. */
.lb-card__vai { position: absolute; inset: 0; z-index: 2; }
/* **`width` e `border-radius` chiudono G2-10, e il difetto era letterale.**
   Fabrizio: «in hover appare un overlay strano con un'ombra che taglia a metà
   la foto». Misurato su `/lavori/`: il link del velo era **190×471 dentro una
   foto di 379×473** — largo esattamente metà. Il gradiente è identico a quello
   del prototipo (`transparent 45%` → `rgba(10,9,8,.75)`), quindi il difetto non
   era nel colore: era che il velo copriva **mezza foto**, e il suo bordo dritto
   in mezzo all'immagine si legge come un taglio.

   L'`<a>` aveva `height: 100%` e nessuna larghezza: come elemento flex si
   dimensionava sul contenuto, cioè sulla parola «Guarda il progetto».

   `border-radius: inherit` è la seconda metà: il velo era a spigoli vivi
   (`0px`) sopra una foto con angoli da 14px, e negli angoli sbordava.

   **È la terza volta oggi che lo stesso difetto si presenta con tre facce
   diverse:** un'immagine che non copre il riquadro, un velo che ne copre metà,
   due frecce ovali. Ogni volta un elemento che non riceve una larghezza e se la
   prende dal contenuto. Vale la pena cercarlo quando qualcosa «non copre». */
/* Il riquadro da riempire e' il wrapper di JetEngine, non solo il link: misurato
   `.jet-listing.jet-listing-dynamic-link` a **190x471** dentro una foto di 379.
   Portare al 100% solo l'`<a>` non serviva a niente — restava il 100% di 190.
   `border-radius: inherit` scende in catena dalla foto fino al link. */
.lb-card__vai,
.lb-card__vai .jet-listing-dynamic-link,
.lb-card__vai .elementor-widget-container {
	width: 100%; height: 100%; border-radius: inherit;
}
.lb-card__vai .jet-listing-dynamic-link__link {
	display: flex;
	align-items: flex-end;
	width: 100%;
	height: 100%;
	border-radius: inherit;
	padding: 16px;
	background: linear-gradient(transparent 45%, rgba(10, 9, 8, .75));
	opacity: 0;
	transition: opacity .45s ease;
	font-size: .66rem;
	text-transform: uppercase;
	letter-spacing: .14em;
	color: #fff;
}
.lb-card:hover .lb-card__vai .jet-listing-dynamic-link__link,
.lb-card__vai .jet-listing-dynamic-link__link:focus-visible { opacity: 1; }
.lb-card__vai .jet-listing-dynamic-link__label { transform: translateY(8px); transition: transform .45s ease; }
.lb-card:hover .lb-card__vai .jet-listing-dynamic-link__label,
.lb-card__vai .jet-listing-dynamic-link__link:focus-visible .jet-listing-dynamic-link__label { transform: none; }
.lb-card__vai .jet-listing-dynamic-link__label::after { content: "→"; color: var(--lb-arancio); margin-left: 8px; }

.lb-card__meta {
	margin-top: 15px;
	padding-top: 15px;
	border-top: 1px solid var(--lb-filo);
	transition: border-color .3s ease;
}
.lb-card:hover .lb-card__meta { border-color: color-mix(in srgb, var(--lb-arancio) 55%, var(--lb-filo)); }
/* **`:is(h1…h6)` e non `h3`: la tipografia non deve dipendere dal livello del
   titolo.** `dev-wp` deve correggere otto salti `h1`→`h3` girando i tag, e con
   un selettore che nomina `h3` la regola smetterebbe di agganciare nel momento
   esatto in cui il tag cambia — misurato dal suo tentativo di stamattina, poi
   annullato. `:is()` prende la specificità del suo argomento più alto: qui sono
   tutti selettori di tipo, quindi il peso resta **identico a `h3`** e nessun
   equilibrio della cascata si sposta. È l'unica ragione per cui si può fare
   adesso senza rimisurare tutto.

   Vale per le tre regole marcate così in questo foglio. **Non** per
   `.lb-ss-mat__titolo h3`: promuovendo il dittico a `h2`, gli `h3` dei
   materiali si trovano sotto un `h2` e il salto sparisce da sé — girarli
   sarebbe lavoro che si disfa da solo. */
.lb-card__titolo .jet-listing-dynamic-field__content,
.lb-card__titolo :is(h1, h2, h3, h4, h5, h6) {
	font-family: var(--lb-regular);
	font-weight: 700; text-transform: uppercase; letter-spacing: -.01em;
	color: var(--lb-titolo); font-size: 1.02rem; line-height: 1.04; margin: 0;
	/* Il content model consiglia titoli entro 34 caratteri e dice che oltre si
	   tronca a due righe: qui succede, invece di spingere i tag fuori dalla card. */
	display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.lb-card__tag { margin-top: 11px; }
/* **Una pillola per termine, e la pillola sta sul termine, non sul contenitore.**
   Corretto da `dev-lavori`: prima la stessa regola vestiva sia
   `.jet-listing-dynamic-terms` (l'involucro) sia `__link` (il singolo termine).
   Con un termine solo — il caso della home — non si vedeva; nella griglia
   Lavori, dove i termini sono due o tre, diventava una pillola grande con due
   pilloline dentro e una virgola in mezzo. */
.lb-tag .jet-listing-dynamic-terms {
	display: flex; flex-wrap: wrap; gap: 6px;
	border: 0; padding: 0;
}
.lb-tag .jet-listing-dynamic-terms__link {
	font-size: .55rem; text-transform: uppercase; letter-spacing: .12em;
	color: var(--lb-tenue);
	border: 1px solid var(--lb-filo);
	padding: 4px 9px; border-radius: 0; white-space: nowrap;
	display: inline-block;
}
/* La virgola che JetEngine mette fra un termine e l'altro: qui i termini sono
   pillole affiancate, il separatore lo fa lo spazio. */
.lb-tag .jet-listing-dynamic-terms__delimiter { display: none; }
.lb-tag--settore .jet-listing-dynamic-terms__link {
	color: var(--lb-arancio);
	border-color: color-mix(in srgb, var(--lb-arancio) 42%, transparent);
}
/* Nella variante piena si vedono settore e tutte le tipologie; la versione a un
   termine solo serve alla home. Vedi il commento in costruisci-listing.php: il
   numero di termini è un'impostazione del widget, non una proprietà CSS. */
.lb-tag--tipologia-una { display: none; }

/* --- G6-E1: le etichette nella griglia Lavori (decisione 280) ---------------
   Con i nomi nuovi delle tipologie («Allestimento visivo degli spazi», «Packaging &
   Cartotecnica») una card portava 1, 2 o 3 righe di pillole, e il blocco dei tag era ancorato
   in fondo alla card mentre il titolo stava in cima: nella stessa fila titolo ed etichette
   finivano ad altezze diverse (52 card su 170 misurate, anteprima `etichette-b.css`).
   Tre mosse:
   1. la meta si impila dall'alto con un passo fisso, così la distanza titolo → etichette è la
      stessa su ogni card (quattro classi per battere il `--gap` che Elementor scrive a (0,3,0));
   2. il settore esce dalle pillole e diventa una riga di testo arancione, la stessa voce delle
      sezioni «Progetti di …» delle schede servizio;
   3. le tipologie restano pillole ma compatte, così due nomi lunghi stanno su una riga anche a
      1024, dove la card è larga 300 px. Sotto i 560 vale la regola a colonna singola più giù. */
.lb-lv-griglia .e-con.elementor-element.lb-card__meta {
	/* G6-E2 (decisione 281): le righe della griglia erano tutte alte uguali, quindi la riga del
	   titolo (17 px) prendeva l'altezza del blocco tag (42): 25 px vuoti + 10 di passo = 35 fra
	   titolo ed etichette. Righe alte quanto il contenuto e passo di 8 px. */
	grid-auto-rows: auto; grid-template-rows: none;
	align-content: start;
	--gap: 8px 8px; --row-gap: 8px; --column-gap: 8px;
}
.lb-lv-griglia .lb-card__titolo { align-self: start; }
.lb-lv-griglia .lb-card__tag { margin-top: 0; min-height: 42px; }
.lb-lv-griglia .lb-tag--settore { flex: 0 0 100%; width: 100%; }
.lb-lv-griglia .lb-tag--settore .jet-listing-dynamic-terms__link {
	border: 0; padding: 0;
	font-size: .6rem; letter-spacing: .16em;
	color: var(--lb-arancio);
}
.lb-lv-griglia .lb-tag--tipologia .jet-listing-dynamic-terms { gap: 5px; }
.lb-lv-griglia .lb-tag--tipologia .jet-listing-dynamic-terms__link {
	font-size: .52rem; letter-spacing: .03em;
	padding: 3px 6px; white-space: nowrap;
}

/* La variante si applica dal contenitore, non dalla card: la card è un Listing
   condiviso e non può portare classi diverse a seconda di dove viene inserita. */
/* --- Variante compatta: la galleria della home ---
   Nel prototipo (`.gcard`) la card ha angoli più morbidi, niente bordo, niente
   velo, e la meta è una riga sola con nome a sinistra e categoria a destra. */
.lb-card-compatta-dentro .lb-card__foto { border-radius: 0; border: none; background: none; }
.lb-card-compatta-dentro .lb-card__vai { display: none; }
/* **`display` esplicito, ed è la correzione del difetto 1 della revisione di
   resa** (decisione 133): l'altra faccia dello stesso interruttore che rompeva
   il processo di Servizi, vista da sopra il confine dei 768 px.

   La meta è un Container di Elementor e non dichiarava il proprio `display`:
   restava flex, e da 768 px in su `flex-wrap` torna `nowrap`. Il titolo è un
   widget JetEngine la cui catena interna è `display:inline` — non ha nessuna
   larghezza intrinseca su cui ripiegare — quindi come elemento flex in una riga
   che deve stare dentro si stringeva a **zero**: misurato 0×48 dentro un
   genitore di 349 px, 20 titoli su 20 a 768, 1024 e 1440. Il nome del progetto
   era nel DOM e non veniva dipinto. Sotto i 768 il `wrap` lo mandava a capo e
   se ne salvavano 9 su 18: rotto in due modi diversi, non funzionante a metà.

   Una griglia toglie la domanda invece di rispondere: `minmax(0, 1fr)` dà al
   titolo una traccia che **non può scendere sotto la sua riga**, e `auto` dà
   alla pillola esattamente quello che le serve. `space-between` non serve più —
   lo fa la traccia `1fr` — ed è per questo che sparisce da qui.
   Resa identica al prototipo (`#v4 .gcard .meta`, `lion-brand-home.html:116`):
   nome a sinistra, categoria a destra, allineati sulla linea di base. */
.lb-card-compatta-dentro .lb-card__meta {
	margin-top: 18px; padding-top: 0; border-top: none;
	display: grid; grid-template-columns: minmax(0, 1fr) auto;
	align-items: baseline; gap: 14px;
}
/* **Questa è la riga che rompeva la home, ed è il motivo per cui i tag non si
   girano da soli.** Il titolo della scheda vive in **un Listing solo**, usato
   da `/lavori/` e dal carosello della home. Su Lavori il cambio di tag
   sopravvive perché lassù c'è anche un selettore per classe; qui la variante
   compatta era scritta **solo sul tag**, quindi girando l'`h3` la home perdeva
   14 px di corpo — misurato — mentre Lavori restava a posto. Un difetto che si
   manifesta su una pagina sola è il modo più caro di sbagliare. Vedi la nota
   su `:is()` più sopra. */
.lb-card-compatta-dentro .lb-card__titolo :is(h1, h2, h3, h4, h5, h6) {
	font-family: var(--lb-regular); font-weight: 400; text-transform: none;
	letter-spacing: .02em; font-size: 1.15rem; line-height: 1.3;
}
.lb-card-compatta-dentro .lb-card__tag { margin-top: 0; flex: 0 0 auto; }
.lb-card-compatta-dentro .lb-tag--settore,
.lb-card-compatta-dentro .lb-tag--tipologia { display: none; }
.lb-card-compatta-dentro .lb-tag--tipologia-una { display: block; }
.lb-card-compatta-dentro .lb-tag .jet-listing-dynamic-terms__link {
	border: none; padding: 0; font-size: .64rem; letter-spacing: .16em;
	color: var(--lb-arancio);
}
/* G6-E1 (decisione 280): sotto i 600 px la tipologia lunga sulla riga del titolo lo stringeva
   su due righe e la card cresceva di 48 px (515 → 563 a 390). Qui la meta diventa una colonna:
   titolo, poi tipologia sotto, a sinistra. Da 600 in su resta la riga del prototipo. Media query
   del tema (ne usa già a 560 e 600), non breakpoint di Elementor. */
@media (max-width: 600px) {
	.lb-card-compatta-dentro .lb-card__meta { grid-template-columns: minmax(0, 1fr); gap: 6px; align-items: start; }
	.lb-card-compatta-dentro .lb-card__tag { justify-self: start; }
	.lb-card-compatta-dentro .lb-tag .jet-listing-dynamic-terms__link { white-space: nowrap; }
}

/* --- Servizi ------------------------------------------------------------ */
.lb-servizi__elenco { margin-top: 52px; border-top: 1px solid var(--lb-filo); }
.lb-servizio {
	padding: 30px 6px;
	border-bottom: 1px solid var(--lb-filo);
	transition: padding-left .35s ease, background .35s ease;
}
/* Hover e freccia solo dove la riga è un link (`a`, contenitore Elementor con link): dal
   giro 7 le righe della home sono un elenco, e un hover su una cosa che non si clicca mente. */
a.lb-servizio:hover { padding-left: 22px; background: var(--lb-superficie); }
.lb-servizio__numero {
	width: 64px; flex: 0 0 64px;
}
.lb-servizio__numero .elementor-heading-title {
	color: var(--lb-arancio); font-size: .8rem; font-variant-numeric: tabular-nums; margin: 0;
}
.lb-servizio__nome { flex: 1 1 auto; }
.lb-servizio__nome .elementor-heading-title {
	/* Giro 6: ExtraWide (decisione 276). Era Regular, .02em, clamp(1.4rem, 3.2vw, 2.4rem), interlinea 1. */
	font-family: var(--lb-titolo-famiglia); font-weight: 700; text-transform: uppercase;
	letter-spacing: -.01em; color: var(--lb-titolo);
	font-size: clamp(1.15rem, 2.5vw, 1.9rem); line-height: 1.05; margin: 0;
}
.lb-servizio__descrizione { flex: 0 1 40ch; }
.elementor-widget-text-editor.lb-servizio__descrizione p,
.lb-servizio__descrizione p {
	color: var(--lb-tenue); font-size: .9rem; text-align: right; margin: 0;
}
/* G6-F1 (giro 5, G5-01): era `max-width: 760px`, ma Elementor passa al layout mobile a 767.
   Fra 761 e 767 la riga dei servizi usava le regole di una larghezza e il contenitore quelle
   dell'altra, e raddoppiava d'altezza. Le tre soglie a 760 di questo file diventano 767. */
@media (max-width: 767px) { .lb-servizio__descrizione { display: none; } }

/* Giro 6 (decisione 276): con i nomi nuovi in ExtraWide, «Allestimento visivo degli spazi» non
   sta accanto alla descrizione nemmeno a 1440. Sopra i 767 la descrizione scende sotto il nome,
   allineata al suo inizio (88 = numero 64 + spazio 24), com'era già il disegno sotto i 760.
   La freccia dell'hover è un elemento flex (`::after`): con `order` resta sulla riga del nome
   invece di finire da sola in fondo. Sotto i 767 non cambia niente: vale G4-02 qui sotto. */
@media (min-width: 768px) {
	.lb-servizi__elenco .e-con.elementor-element.lb-servizio {
		--flex-wrap: wrap;
		--row-gap: 10px;
	}
	.lb-servizio__nome { flex: 1 1 calc(100% - 64px - 26px - 48px); order: 0; }
	.lb-servizio::after { order: 1; }
	.lb-servizio__descrizione { flex: 0 0 100%; order: 2; padding-left: 88px; }
	.elementor-widget-text-editor.lb-servizio__descrizione p,
	.lb-servizio__descrizione p { text-align: left; max-width: 56ch; }
}

/* **G4-02: due titoli di servizio su sei finivano su una seconda riga, e non
   perché il testo andasse a capo.** Fabrizio, da telefono: «alcuni servizi vanno
   su due righe, proviamo a ridurre lo spazio del titolo dai numeri. se ancora
   non si riesce a mettere su una sola riga, ridurre eccezionalmente il corpo del
   carattere».

   ⚠️ **Il testo non andava a capo: andava a capo la riga.** Misurato con un
   `Range` sui titoli, ciascuno occupava **una sola riga di testo**; a scendere
   era il riquadro `.lb-servizio__nome` intero, che il contenitore Elementor
   mandava sotto al numero perché è `flex-wrap: wrap`. Guardando l'altezza del
   titolo non si vedeva niente — si vede solo guardando **da dove comincia**:
   x=116 quando sta in riga, x=28 quando è sceso.

   Nel prototipo non può succedere: `.srv-row` è una **griglia**
   `64px 1fr auto 26px`, e sotto i 760 diventa `44px 1fr 22px`
   (`lion-brand-home.html:124` e `:132`). Una colonna `1fr` non scende mai: al
   massimo manda a capo il proprio testo. Da noi la griglia è diventata un flex
   che va a capo, **e la stretta del numero a 44px sotto i 760 non era stata
   portata**.

   Il conto dello spazio, misurato e non stimato. La riga contiene numero,
   titolo, descrizione (nascosta sotto i 760) e la freccia `::after` da 26px,
   quindi lo spazio del titolo è
   `riga − 12 di riempimento − numero − 2 spazi − 26 di freccia`:

       PRIMA   numero 64, spazio 24   iPhone 15  199    iPhone 12 Mini  181
       DOPO    numero 44, spazio 14   iPhone 15  239    iPhone 12 Mini  221

   **Il primo passo che Fabrizio ha chiesto non basta, e si dimostra con
   l'aritmetica invece che con un tentativo.** Il titolo più lungo,
   «PROGETTAZIONE GRAFICA», misura **298 px a 22,4** — cioè **13,30 px di
   larghezza per ogni px di corpo**. Anche portando lo spazio a zero e il numero
   a 44, sull'iPhone 12 Mini restano 249 px: **48 in meno del necessario.**

   Quindi si scende di corpo, ed è **l'eccezione che ha autorizzato lui**.
   Il vincolo stringente è il Mini: `13,30 × corpo ≤ 221` dà **corpo ≤ 16,6 px**.
   `clamp(.95rem, 4.2vw, 1.4rem)` sta sotto quel tetto dove serve e **risale da
   sé a 22,4** — il valore di prima — da 533 px di larghezza in su, quindi il
   desktop non cambia di un pixel.

   ⚠️ **Sotto i 360 px il titolo lungo torna a spezzarsi in due righe di testo**,
   e va bene così: con `--flex-wrap: nowrap` si spezza **dentro la sua colonna**,
   restando accanto al proprio numero. È il modo in cui degrada il prototipo, ed
   è anche la rete se il cliente rinomina un servizio con qualcosa di più lungo.

   Sul contenitore si **definiscono le variabili** invece di combattere la regola
   (trappola 4): Elementor dichiara `gap: var(--row-gap) var(--column-gap)` e
   `flex-wrap: var(--flex-wrap)`, e il valore vive in `--gap: 24px 24px` scritto
   da `post-59.css` su `.elementor-element-lbsrv1`, che è **(0,3,0)**. Per batterlo
   serve un gradino vero, non l'ordine dei fogli: da qui le quattro classi.

   ⚠️ **E `--gap` da solo non basta: non è la variabile che `gap` legge.** Scritta
   quella e misurata, lo spazio calcolato restava **24 px**. La catena è
   `gap: var(--row-gap) var(--column-gap)`, e `--gap` sta un anello più in su:
   chi ridefinisce solo il capostipite non tocca i due anelli che contano. Si
   scrivono tutt'e tre — `--gap` per coerenza con quello che Elementor mostra in
   bacheca, `--row-gap` e `--column-gap` perché sono quelli che agiscono. */
@media (max-width: 767px) {
	.lb-servizi__elenco .e-con.elementor-element.lb-servizio {
		--gap: 14px 14px;
		--row-gap: 14px;
		--column-gap: 14px;
		--flex-wrap: nowrap;
	}
	.lb-servizio__numero { width: 44px; flex: 0 0 44px; }
	.lb-servizio__nome .elementor-heading-title { font-size: clamp(.85rem, 3.6vw, 1.15rem); }
}

/* **G3-04: la freccia in hover che mancava.** Segnalazione di Fabrizio del
   2026-08-26 — *«non appare la freccia come nel prototipo in hover. Vedi come
   funziona la voce in hover del servizio Packaging. Quella è la versione
   corretta»*. Aperta e misurata: nel prototipo `.srv-row` è una griglia
   `64px 1fr auto 26px` e la quarta colonna è `<span class="sar">→</span>`,
   arancione, `opacity:0` e `translateX(-8px)` a riposo, `opacity:1` e nessuna
   traslazione in hover. Da noi quella colonna **non esisteva**: misurato
   `content: none` sullo `::after` di `.lb-servizio`, cioè nessun elemento.

   **Pseudo-elemento e non widget**, per due ragioni: la riga è già un `<a>` e
   un widget in più sarebbe una quinta cosa che il cliente può cancellare per
   sbaglio; e la freccia non è contenuto, è la stessa affordance che il tema
   disegna con `content: "→"` in altri sei punti.

   Le misure vengono dal prototipo, non dal gusto: larghezza 26px, corpo
   1,2rem, `translateX(-8px)`, transizione .3s. In hover la punta cade a
   **x=1531** nel prototipo. */
.lb-servizio::after {
	content: "→";
	flex: 0 0 26px;
	align-self: center;
	text-align: right;
	color: var(--lb-arancio);
	font-size: 1.2rem; line-height: 1;
	opacity: 0;
	transform: translateX(-8px);
	transition: opacity .3s ease, transform .3s ease;
}
a.lb-servizio:hover::after,
a.lb-servizio:focus-visible::after { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
	.lb-servizio::after { transition: opacity .2s ease; transform: none; }
}

/* --- Footer -------------------------------------------------------------
   Le misure vengono da `site.css` del prototipo (`.foot` e discendenti). Il
   footer non ha padding proprio: ha un margine sopra e una spaziatura interna
   sul blocco, ed è quella differenza che teneva la sezione 300px più alta. */
.lb-foot {
	position: relative;
	overflow: hidden;
	padding-block: 0;
	margin-top: clamp(60px, 9vw, 120px);
	background: linear-gradient(180deg, var(--lb-fondo), var(--lb-superficie));
}
/* La spaziatura interna del footer, e perché il selettore che c'era prima non
   agganciava niente.

   `.lb-foot` è un Container **`e-con-full`**: un `.e-con-inner` ce l'hanno solo
   i Container «boxed». La regola precedente — `.lb-foot > .e-con-inner >
   *:first-child` — cadeva nel vuoto, e il footer partiva incollato al blocco
   sopra e finiva incollato al bordo della pagina. **116px persi su ogni
   pagina** (72 sopra, 44 sotto). Trovato da `dev-servizi` misurando il footer
   sulla *sua* pagina: seconda volta che un difetto del footer condiviso lo
   trova chi lavora su un'altra pagina.

   Perché 72/44 e non zero, visto che in cinque prototipi su otto il footer non
   ha spaziatura: `site.css` la dichiara nel footer stesso
   (`.site .foot .wrap{padding-top:clamp(46px,6vw,72px);padding-bottom:44px}`),
   e le pagine che la perdono la perdono **per effetto collaterale** — scrivono
   `#id .wrap{padding: 0 clamp(22px,4vw,54px)}` per dare il margine laterale al
   proprio contenuto, e con la scorciatoia `padding` azzerano anche il verticale
   di un blocco che non stavano guardando. Nessuno ha deciso che il footer della
   home non debba respirare sopra: è semplicemente successo (decisione 33).
   Qui il footer è **un template solo** e ha una spaziatura sola, che è quella
   che il footer dichiara per sé. */
.lb-foot > *:first-child { padding-top: clamp(46px, 6vw, 72px); }
.lb-foot > *:last-child { padding-bottom: 44px; }

.lb-foot__cta { text-align: center; }
.lb-foot__cta .lb-eyebrow .elementor-heading-title {
	/* Nel footer l'etichetta è centrata e senza il filo davanti: la riga
	   orizzontale funziona a inizio riga, non in mezzo a un blocco centrato. */
	display: block; letter-spacing: .3em; font-size: .62rem; margin-bottom: 14px;
}
.lb-foot__cta .lb-eyebrow .elementor-heading-title::before { display: none; }
/* Il prefisso `.elementor-widget-heading` sulla famiglia, e stavolta si vedeva
   solo su una pagina. `.lb-titolo-cta .elementor-heading-title` ha la stessa
   specificità di `.elementor-widget-heading .elementor-heading-title`, che
   Elementor stampa **dentro la pagina** con la tipografia del kit: vince chi
   arriva dopo, e su `/servizi/` quel blocco viene stampato una terza volta
   **dopo** `tema.css`. Risultato: il titolo della CTA del footer in ExtraWide,
   `max-width: 20ch` che diventa 606px invece di 337, e il titolo su cinque
   righe invece di tre — 57px di footer in più su quella pagina sola.
   Quarta occorrenza della stessa famiglia su questo progetto. */
.elementor-widget-heading.lb-titolo-cta .elementor-heading-title,
.lb-titolo-cta .elementor-heading-title { font-family: var(--lb-titolo-famiglia); }
.lb-foot__cta .lb-titolo-cta .elementor-heading-title {
	/* Giro 6: era clamp(1.7rem, 4.4vw, 3rem) in Regular. */
	font-size: clamp(1.45rem, 3.5vw, 2.4rem);
	line-height: 1.05;
	letter-spacing: -.01em;
	max-width: 20ch;
	margin: 0 auto;
}
.lb-foot__cta .lb-cta { margin-top: 24px; }

.lb-foot__griglia {
	/* 80px, non i 72 di site.css: la home lo sovrascrive nel suo blocco di stile. */
	margin-top: 80px; padding-top: 44px; border-top: 1px solid var(--lb-filo);
	gap: 40px;
}
.lb-foot__identita { flex: 1.3 1 0; }
.lb-foot__link { flex: 1 1 0; gap: 56px; }
/* Tablet (768-1024): le due colonne di link vanno una sotto l'altra e la colonna
   di destra diventa alta ~720px; con l'allineamento centrale ereditato il logo
   restava a meta' altezza con un vuoto sopra. Identita' in alto, solo in questa
   fascia: a 1440 il desktop e' approvato, sotto 768 la griglia e' gia' in colonna.
   `--align-self` definita oltre a `align-self`, perche' Elementor dichiara
   `align-self: var(--align-self)` sui container figli. */
@media (min-width: 768px) and (max-width: 1024px) {
	.lb-foot__griglia > .e-con-inner > .lb-foot__identita {
		--align-self: flex-start;
		align-self: flex-start;
	}
}
.elementor-widget-text-editor.lb-foot__descrizione p,
.lb-foot__descrizione p { color: var(--lb-tenue); font-size: .95rem; max-width: 38ch; margin: 18px 0 0; }
.lb-foot__descrizione b { color: var(--lb-titolo); font-weight: 600; }
/* **Il Kit di Elementor batte il tema sui titoli, e solo su certe pagine.**
   Il Kit emette in **ogni** `post-*.css`:
       .elementor-widget-heading .elementor-heading-title { color: … }   (0,2,0)
   Il tema dichiara `.lb-qualcosa .elementor-heading-title`, che è **la stessa
   specificità**. A parità decide l'ordine di caricamento, e l'ordine cambia da
   pagina a pagina: sulle pagine servite da un template **Single** il `post-*.css`
   arriva **dopo** il tema e vince lui.

   **Censito il 2026-08-25**, misurando le pagine invece di dedurre dall'ordine:
   **13 regole `.lb-*` a (0,2,0)** che colorano un `.elementor-heading-title`,
   tutte in questo foglio — in `pagine.css` zero, lì sono già tutte a (0,3,0).
   Di quelle 13, **due perdono davvero**, e sono le due qui alzate:

       .lb-foot__titolo      arancione invece di crema su /servizi/ e su ogni
                             scheda servizio o progetto  (2 elementi per pagina)
       [class*="-hero__cue"] arancione invece di bianco al 60% su
                             /progetto/splendide-royal/

   Le altre 11 reggono **solo perché su quelle pagine non ci sono**. Non sono
   state alzate di proposito: alzarle tutte sarebbe debito su un problema che
   oggi non esiste. **Quando una finisce su una pagina Single, si alza allora** —
   e il sintomo da cercare è sempre lo stesso: un titolo del tema che diventa
   arancione. Lo `scorri` è arrivato lì il giorno dopo il censimento.

   Come si alza: si aggiunge `.elementor-widget-heading` davanti, perché la
   classe del tema sta **sul widget**, non su un contenitore intermedio. Da
   (0,2,0) a (0,3,0), che è quanto basta: niente `!important`. */
.elementor-widget-heading.lb-foot__titolo .elementor-heading-title,
.lb-foot__titolo .elementor-heading-title {
	font-family: var(--lb-extrawide); font-weight: 700; text-transform: uppercase;
	letter-spacing: .16em; font-size: .62rem; color: var(--lb-titolo); margin: 0;
}
.lb-foot__elenco .elementor-icon-list-items { margin: 0; }
.lb-foot__elenco .elementor-icon-list-item { margin-top: 10px; }
.lb-foot__elenco .elementor-icon-list-text { color: var(--lb-tenue); font-size: .9rem; }
.lb-foot__elenco .elementor-icon-list-item a:hover .elementor-icon-list-text { color: var(--lb-titolo); }

 /* I selettori portano davanti `.elementor-widget-text-editor` per la stessa
   ragione dei pulsanti: Elementor stampa la tipografia «Testo» del kit sul
   widget con la nostra stessa specificità e dopo di noi. Senza il prefisso questi
   due blocchi restano a 16px invece che a 12, e in fondo alla pagina si vede. */
.lb-foot__coda { margin-top: 48px; }
.elementor-widget-text-editor.lb-foot__coda p,
.lb-foot__coda p { color: var(--lb-tenue); font-size: .76rem; margin: 0; }
 /* `line-height:0` sul contenitore del widget, non sul testo: il div che
   Elementor mette attorno al paragrafo eredita l'interlinea del corpo (16×1.55)
   e diventa più alto del testo che contiene. È lo stesso motivo per cui
   l'etichetta arancione è `display:flex` invece che `inline-flex`. */
.lb-foot__firma {
	margin-top: 26px; padding-top: 22px; border-top: 1px solid var(--lb-filo);
	line-height: 0;
}
.lb-foot__coda { line-height: 0; }
.lb-foot__coda p, .lb-foot__firma p { line-height: 1.55; }
.elementor-widget-text-editor.lb-foot__firma p,
.lb-foot__firma p { color: var(--lb-tenue); font-size: .72rem; letter-spacing: .04em; margin: 0; }
.lb-foot__firma b { color: var(--lb-titolo); font-weight: 700; }
.lb-cuore { color: var(--lb-arancio); }
@media (max-width: 900px) {
	.lb-foot__griglia { flex-direction: column; }
	.lb-foot__link { flex-wrap: wrap; }
}

/* --- Intro loader (solo home) ---------------------------------------------
   L'animazione sta QUI e non nel JavaScript, di proposito: il pannello copre
   tutto lo schermo, e se la sua chiusura dipendesse da uno script che non parte
   la home tornerebbe a essere un rettangolo nero. È già successo una volta.
   Il JavaScript aggiunge solo il contatore percentuale, che è un di più. */
.lb-intro {
	position: fixed;
	inset: 0;
	z-index: 1200;
	background: var(--lb-fondo);
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 26px;
	animation: lb-intro-esce .8s cubic-bezier(.76, 0, .24, 1) 1.38s forwards;
}
@keyframes lb-intro-esce {
	to { transform: translateY(-100%); visibility: hidden; }
}
.lb-intro__logo { height: clamp(44px, 8vw, 68px); width: auto; display: block; }
.lb-intro__barra {
	width: min(300px, 60vw);
	height: 1px;
	background: var(--lb-filo);
	position: relative;
	overflow: hidden;
}
.lb-intro__barra i {
	position: absolute;
	inset-block: 0;
	left: 0;
	width: 0;
	background: var(--lb-arancio);
	animation: lb-intro-barra 1.2s cubic-bezier(.4, 0, .2, 1) forwards;
}
@keyframes lb-intro-barra { to { width: 100%; } }
.lb-intro__pct {
	font-size: .7rem;
	letter-spacing: .2em;
	color: var(--lb-tenue);
	font-variant-numeric: tabular-nums;
}

/* --- Payoff «Creative Factory» animato (giro 9) -----------------------------
   L'animazione «Lettere che saltano» scelta da Fabrizio sull'anteprima del
   giro 7: le lettere compaiono, poi un'onda di piccoli salti da sinistra a
   destra con ritorno elastico. Solo opacità e pochi pixel di spostamento.
   Nel pannello d'intro parte da sola e finisce (~1,26 s) prima che il pannello
   salga (1,38 s). In pagina la fa partire lo script quando il payoff entra
   nello schermo: fino ad allora `--attesa` tiene le lettere spente, e senza
   script nessuna delle due classi arriva e il payoff resta fermo e visibile.
   In pagina la comparsa avviene UNA volta; poi si ripete solo l'onda, ogni 3 s
   (Fabrizio: «in loop solo il movimento, senza che riparta da zero»). */
.lb-payoff { --lb-logo: #e95c0c; display: block; }
.lb-payoff__svg { display: block; width: 100%; height: auto; overflow: visible; }
.lb-payoff__svg path, .lb-payoff__svg polygon { fill: var(--lb-logo); }
.lb-payoff__svg .lt, .lb-payoff__svg .testa { transform-box: fill-box; transform-origin: center; }
.lb-payoff--attesa .lt, .lb-payoff--attesa .testa { opacity: 0; }
.lb-payoff--intro .lt {
	animation: lb-salta-entra 240ms ease-out backwards, lb-salta-onda 360ms ease-out backwards;
	animation-delay: calc(var(--i) * 22ms), calc(520ms + var(--i) * 24ms);
}
.lb-payoff--salta .lt {
	animation: lb-salta-entra 240ms ease-out backwards, lb-salta-onda-ciclo 3s linear infinite backwards;
	animation-delay: calc(var(--i) * 22ms), calc(520ms + var(--i) * 24ms);
}
/* `--onda` (hero della home): sempre visibile, solo il salto ogni 3 s. Il primo
   parte a 2,25 s, appena il pannello d'intro ha finito di salire (1,38 s + 0,8 s):
   prima era a 1,4 s e cadeva mentre il pannello copriva ancora la hero. */
.lb-payoff--onda .lt {
	animation: lb-salta-onda-ciclo 3s linear infinite backwards;
	animation-delay: calc(2250ms + var(--i) * 24ms);
}
.lb-payoff--intro .testa, .lb-payoff--salta .testa { animation: lb-salta-entra 300ms ease-out 300ms backwards; }
@keyframes lb-salta-entra { 0% { opacity: 0; } 100% { opacity: 1; } }
@keyframes lb-salta-onda {
	0%   { transform: none; }
	35%  { transform: translateY(-8px); }
	62%  { transform: translateY(1.6px); }
	82%  { transform: translateY(-.6px); }
	100% { transform: none; }
}
/* Lo stesso salto dentro un periodo di 3 s (7 s fino al 13/10, poi 4 s): l'onda occupa il primo 12% (360 ms), poi quiete.
   Le percentuali dipendono dal periodo: se lo si cambia, vanno ricalcolate (ms del salto / periodo). */
@keyframes lb-salta-onda-ciclo {
	0%    { transform: none; animation-timing-function: ease-out; }
	4.2%  { transform: translateY(-8px); animation-timing-function: ease-in-out; }
	7.43%  { transform: translateY(1.6px); animation-timing-function: ease-in-out; }
	9.83%  { transform: translateY(-.6px); animation-timing-function: ease-in-out; }
	12%  { transform: none; }
	100%  { transform: none; }
}
}
}
.lb-intro .lb-payoff { width: min(78vw, 440px); }
/* Nelle hero di Chi siamo e Metodo (decisione 309) prende il posto dell'occhiello
   «… · Creative Factory». Lì non c'è il pannello d'intro: compare all'apertura
   della pagina e poi ripete l'onda, come nel footer. */
.lb-hero-interna__payoff.elementor-widget { width: clamp(230px, 25vw, 360px); margin: 0 0 clamp(12px, 1.4vw, 20px); }
/* Nella hero prende il posto dell'occhiello «Creative Factory»: stessa riga,
   largo quanto basta a leggersi senza contendere la scena al titolo. */
.lb-hero__fold .lb-hero__payoff.elementor-widget { width: clamp(250px, 29vw, 400px); margin: 0 0 clamp(14px, 1.6vw, 22px); }
/* In cima al footer, sopra «Iniziamo», su tutte le pagine: piccolo, centrato.
   Fa branding prima della chiamata all'azione, senza contenderle la scena. */
/* Doppia classe: la regola di Elementor sul margine dei widget è più specifica
   di una classe sola e azzerava lo spazio (misurato: payoff attaccato a «Iniziamo»). */
.lb-foot__cta .lb-foot__payoff.elementor-widget { width: min(340px, 70vw); margin: 0 auto clamp(36px, 5vw, 56px); }
@media (prefers-reduced-motion: reduce) {
	.lb-payoff .lt, .lb-payoff .testa { animation: none !important; opacity: 1 !important; }
}

/* --- Cursore personalizzato ------------------------------------------------
   Il cursore di sistema sparisce solo dove ha senso: su un dispositivo a tocco
   non c'è un puntatore da sostituire, e nasconderlo lascerebbe l'utente senza
   niente. La protezione c'era già nel prototipo e resta.

   Nasconderlo è comunque una scelta con un costo: chi naviga da tastiera non
   vede il puntatore ma deve vedere dove sta. Per questo il focus visibile
   (in fondo a questo foglio) non è una rifinitura ma la contropartita. */
@media (pointer: fine) {
	body.lb-cursore-custom,
	body.lb-cursore-custom a,
	body.lb-cursore-custom button,
	body.lb-cursore-custom [role="button"] { cursor: none; }
}
.lb-cursore { position: fixed; top: 0; left: 0; z-index: 1100; pointer-events: none; }
.lb-cursore__punto {
	position: absolute; width: 7px; height: 7px; margin: -3.5px;
	border-radius: 50%; background: var(--lb-arancio); transition: opacity .2s;
}
.lb-cursore__anello {
	position: absolute; width: 40px; height: 40px; margin: -20px;
	border-radius: 50%; border: 1px solid var(--lb-arancio);
	display: flex; align-items: center; justify-content: center;
	transition: width .22s, height .22s, margin .22s, background .22s, border-color .22s;
}
.lb-cursore__etichetta {
	font-family: var(--lb-extrawide);
	font-size: .55rem; font-weight: 700; text-transform: uppercase;
	letter-spacing: .06em; color: var(--lb-su-arancio);
	opacity: 0; max-width: 70px; text-align: center; line-height: 1.02;
	transition: opacity .2s;
}
.lb-cursore--sopra .lb-cursore__anello { width: 54px; height: 54px; margin: -27px; }
.lb-cursore--sopra .lb-cursore__punto { opacity: 0; }
.lb-cursore--etichettato .lb-cursore__anello {
	width: 84px; height: 84px; margin: -42px;
	background: var(--lb-arancio); border-color: var(--lb-arancio);
}
.lb-cursore--etichettato .lb-cursore__etichetta { opacity: 1; }
@media (pointer: coarse) { .lb-cursore { display: none; } }

/* --- Grana da stampa ----------------------------------------------------
   Una texture fissa sopra tutta la pagina. Non è decorazione gratuita: è quello
   che dà al fondo nero l'aspetto di carta stampata invece che di schermo spento.
   La inserisce comportamenti.js una volta sola, fuori dal contenuto. */
.lb-grana {
	position: fixed; inset: 0; z-index: 600; pointer-events: none;
	opacity: .05; mix-blend-mode: overlay;
	background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
}

/* --- Comparsa allo scroll ----------------------------------------------- */
.lb-appare { opacity: 0; transform: translateY(26px); transition: opacity .9s ease, transform .9s ease; }
.lb-appare.lb-dentro { opacity: 1; transform: none; }

/* --- Focus da tastiera ---------------------------------------------------
   Decisione 20: va aggiunto ovunque, non copiato dal prototipo che ce l'ha solo
   sui campi del form. Su un sito che si vende come «a norma» è un bloccante. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
.elementor-button:focus-visible {
	outline: 2px solid var(--lb-arancio);
	outline-offset: 3px;
	border-radius: 2px;
}
/* **I campi del modulo: una collisione di specificità, non una regola mancante.**
   Diagnosi di `dev-wp`, 2026-08-25: costruito il modulo di contatto, il fuoco
   visibile è sceso da 24/24 a **18/24** sulla home, 23→17 su Chi siamo, 28→22 su
   Lavori. I sei mancanti sono esattamente i campi di testo, con `outline: 0px
   none` calcolato; la casella privacy e il pulsante invece il fuoco ce l'hanno,
   ed è il dettaglio che dice dove guardare.

   **Il peso da battere è (0,3,0), non (0,2,0).** Cercato nel foglio servito
   invece che assunto: la regola è
   `.elementor-field-group .elementor-field-textual:focus{…;outline:0}` —
   **con l'antenato davanti**, ed è quello a spostare il peso.

   **Sta in Elementor _core_, non in Pro**, e la distinzione non è pedanteria:
   chi la cerca nel posto sbagliato non la trova e conclude che il colpevole sia
   un altro. Verificato scaricando i due file dal sito servito:
   `plugins/elementor/assets/css/frontend.min.css` risponde **200** e contiene
   **una** occorrenza; `plugins/elementor-pro/assets/css/frontend.min.css`
   risponde **404 — non esiste**. In `widget-form.min.css` di Pro non c'è
   nessuna regola `outline`. Il commento precedente attribuiva la riga a Pro:
   era sbagliato, e uno strumento che stampava solo il nome del file senza la
   cartella me l'aveva lasciato credere.
   Un `input.elementor-field-textual:focus-visible` pesa (0,2,1) e **perde
   lo stesso**: provato sul sito servito, `outline` restava `none 0px` anche col
   selettore qualificato. Ripetere l'antenato porta a (0,3,1) e chiude la
   questione per specificità, senza `!important` e senza dipendere dall'ordine
   di caricamento fra il foglio di un plugin e quello di un tema figlio.

   Tre selettori e non uno perché Elementor mette quella classe su `input`,
   `textarea` e `select`. Il `box-shadow` che la sua regola imposta non si tocca:
   qui serve solo il contorno del fuoco.

   Il widget Form di Elementor Pro **non espone nessun controllo di fuoco** —
   cercato fra i suoi controlli, `focus` non c'è — quindi dal lato bacheca si
   chiuderebbe solo mettendo CSS nel database, che è la strada sbagliata.
   Vale anche quando il modulo si sposterà nel cassetto Off-Canvas
   (decisione 135): il cassetto ha gli stessi campi. */
.elementor-field-group input.elementor-field-textual:focus-visible,
.elementor-field-group textarea.elementor-field-textual:focus-visible,
.elementor-field-group select.elementor-field-textual:focus-visible {
	outline: 2px solid var(--lb-arancio);
	outline-offset: 3px;
	border-radius: 2px;
}

/* **Bersaglio minimo per il pollice: campi a 44px (decisione di Fabrizio, 13-10).**
   Valeva 40px e veniva dalla taglia `elementor-size-sm` del widget Form di
   Elementor Pro (`min-height: 40px`), misurato a 390 su /contatti/. Vale per
   i due moduli che portano `lb-modulo` sul widget: il cassetto (header #57,
   `lbmodform`) e la pagina Contatti (#300, `ctform`).

   Specificità: la regola di Elementor è una classe-taglia sull'elemento, al
   massimo (0,3,0). Qui l'antenato `.lb-modulo` + `.elementor-field-group` +
   `input.elementor-field-textual.elementor-size-sm` fa (0,5,1): vince senza
   `!important` e senza dipendere dall'ordine dei fogli. `min-height` e non
   `height`: il testo resta centrato dal padding di Elementor (5px) e un campo
   con zoom del font alto cresce invece di tagliare. Font, colori e bordi non
   si toccano. La textarea è fuori: 105px va già bene.

   Casella privacy: nativa (niente casella finta, quindi tastiera, fuoco e
   lettori di schermo restano quelli del browser), portata a 18px con
   `width/height`. L'area toccabile è la riga: la label ha 13px di padding
   sopra e sotto (18 + 26 = 44) ed è cliccabile; il distacco dalla casella è
   padding della label e non margine, così anche lo spazio fra casella e testo
   risponde al tocco. La casella ha lo stesso 13px di margine sopra: la label misurata è alta 18px
   con una riga, quindi la casella (18px) cade esattamente sulla prima riga, e se
   il testo va a capo resta lì invece di centrarsi sull'intero blocco. Se la
   riga di testo cambiasse altezza, il 13px va riallineato a mano. */
.lb-modulo .elementor-field-group input.elementor-field-textual.elementor-size-sm,
.lb-modulo .elementor-field-group select.elementor-field-textual.elementor-size-sm {
	min-height: 44px;
}
.lb-modulo .elementor-field-type-acceptance .elementor-field-subgroup .elementor-field-option {
	display: flex;
	align-items: flex-start;
	min-height: 44px;
}
.lb-modulo .elementor-field-type-acceptance .elementor-field-subgroup .elementor-field-option input[type="checkbox"] {
	flex: 0 0 18px;
	width: 18px;
	height: 18px;
	margin: 13px 0 0;
}
.lb-modulo .elementor-field-type-acceptance .elementor-field-subgroup .elementor-field-option label {
	display: block;
	flex: 1 1 auto;
	padding-block: 13px;
	padding-inline-start: 10px;
}

@media (prefers-reduced-motion: reduce) {
	.lb-hero__sfondo .lb-card__foto img { animation: none; transform: scale(1.05); }
	/* L'intro sparisce subito: chi ha chiesto meno movimento non deve aspettare
	   un secondo e mezzo davanti a un pannello nero. */
	.lb-intro { animation: none; display: none; }
	.lb-intro__barra i { animation: none; width: 100%; }
	.lb-cursore { display: none; }
	.lb-appare { opacity: 1; transform: none; transition: none; }
}

/* =====================================================================
   SCHEDA PROGETTO — layout editoriale
   Misure prese da `lion-brand-progetto-alto.html`, non inventate. Il
   contenitore di questa pagina è 1240px, non 1360 come la home: nel
   prototipo le pagine editoriali hanno una lettura più stretta, ed è una
   scelta di design, non un'incoerenza (design system §3).
   ===================================================================== */
.single-progetto .e-con-boxed > .e-con-inner { max-width: 1240px; }
.single-progetto .lb-sezione { padding-block: clamp(72px, 10vw, 140px); }

/* Ritorno alla griglia: pillola fissa in basso a sinistra, fuori dal flusso.
   Nel prototipo la destinazione cambia da pagina a pagina; qui è costante per
   template, quindi corretta per costruzione invece che per disciplina. */
.lb-indietro { position: fixed; left: clamp(16px, 3vw, 28px); bottom: 20px; z-index: 130; width: auto; }
.elementor-widget-button.lb-indietro__link .elementor-button,
.lb-indietro__link .elementor-button {
	display: inline-flex; align-items: center; gap: 9px;
	padding: 10px 17px;
	font-family: var(--lb-regular);
	font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .14em;
	color: var(--lb-titolo);
	background: color-mix(in srgb, var(--lb-fondo) 68%, transparent);
	backdrop-filter: blur(10px);
	border: 1px solid var(--lb-filo);
	transition: background .2s, border-color .2s, color .2s;
}
.lb-indietro__link .elementor-button::before { content: "\2190"; color: var(--lb-arancio); font-size: .9rem; line-height: 1; }
.elementor-widget-button.lb-indietro__link .elementor-button:hover,
.lb-indietro__link .elementor-button:hover { border-color: var(--lb-arancio); color: var(--lb-arancio); transform: none; filter: none; }

/* **G4-10: col cassetto aperto «← PROGETTI» resta sopra, e finisce a galleggiare
   sul campo «AZIENDA / STRUTTURA».** Trovata e misurata da `dev-wp-g4`, che me
   l'ha passata come patch perché la penna su questo foglio è mia. Riprodotta da
   me prima di scrivere, su iPhone 15, `/progetto/alto-cocktail-bar/`:
   `elementFromPoint(78, 621)` torna **il pulsante stesso** a cassetto aperto,
   con `inVista: true`.

   ⚠️ **Il lead non l'aveva trovata perché cercava la parola:** sul sito il
   pulsante dice **«Progetti»**, e la freccia è un `::before`. Cercare «torna ai
   progetti» dà zero, e zero non è una prova (decisione 161).

   **I numeri nudi direbbero il contrario:** il cassetto è a `z-index 2147483630`
   e questo pulsante a 130. Ma il cassetto **vive dentro `header.lb-top`**, che è
   `fixed; z-index: 120` e **apre un contesto di impilamento**: quel numero
   enorme vale solo lì dentro, e rispetto al `body` **il cassetto intero vale
   120**. `.lb-indietro` sta a 130 e **fuori** dalla gabbia, quindi dipinge sopra.

   È la decisione 164 dall'altro capo: là un antenato imprigionava un `fixed`,
   qui un antenato declassa un pannello. **Il contesto di impilamento non si vede
   leggendo l'elemento: si vede leggendo i suoi antenati.**

   **Non si corregge con lo z-index**, pur essendo un numero solo: abbassare
   `.lb-indietro` sotto 120 funzionerebbe per la coincidenza che oggi la gabbia
   vale 120, e chi domani tocca lo z-index della testata romperebbe il rimedio
   **senza nessun segnale**. Si toglie di mezzo il pulsante quando un pannello è
   aperto, che è la cosa che si vuole davvero.

   **Due regole separate e non una lista, ed è la parte che conta.** Le due vie
   sono ridondanti apposta (decisione 199): `e-off-canvas__no-scroll` è una classe
   interna di Elementor Pro e un aggiornamento può rinominarla, `aria-hidden` è un
   contratto di accessibilità e regge di più. **Ma un selettore non valido dentro
   una lista invalida tutta la lista**, quindi scritte insieme un browser senza
   `:has()` le perderebbe **tutt'e due** — cioè la ridondanza si annullerebbe da
   sé proprio nel caso per cui esiste. Separate, ognuna vive per conto suo.

   Vale anche col **menu** aperto, non solo col modulo: stessa causa, stessa
   riga. Fabrizio ha segnalato il modulo perché era quello che stava usando. */
body.e-off-canvas__no-scroll .lb-indietro {
	opacity: 0;
	visibility: hidden;
	pointer-events: none;
}
body:has(.e-off-canvas[aria-hidden="false"]) .lb-indietro {
	opacity: 0;
	visibility: hidden;
	pointer-events: none;
}

/* --- Apertura a tutto schermo --- */
.lb-progetto-hero { position: relative; min-height: 100svh; overflow: hidden; padding: 0; }
.lb-progetto-hero__sfondo { position: absolute; inset: 0; z-index: 0; padding: 0; }
.lb-progetto-hero__sfondo,
.lb-progetto-hero__sfondo > .e-con-inner,
.lb-progetto-hero__sfondo .elementor-widget,
.lb-progetto-hero__sfondo .elementor-widget-container,
.lb-progetto-hero__sfondo .jet-listing-dynamic-image { height: 100%; }
.lb-progetto-hero__sfondo img {
	position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
	animation: lb-kb 7s ease forwards;
}
/* decisione 308 (F1): JetEngine dichiara `.jet-listing-dynamic-image__img.wp-post-image { width: auto }`
   a (0,2,0) e batte `.lb-progetto-hero__sfondo img { width: 100% }` a (0,1,1): a 1920 l'img restava al 76 %
   del riquadro. Questa e' (0,2,1), speculare a quella che protegge l'hero immersivo. */
.lb-progetto-hero__sfondo .jet-listing-dynamic-image img { width: 100%; max-width: none; }
@keyframes lb-kb { from { transform: scale(1.03); } to { transform: scale(1.14); } }
.lb-progetto-hero__velo {
	position: absolute; inset: 0; z-index: 2; padding: 0; pointer-events: none;
	background: linear-gradient(180deg, rgba(10,9,8,.58) 0%, rgba(10,9,8,.12) 26%,
		rgba(10,9,8,.32) 50%, rgba(10,9,8,.76) 74%, rgba(10,9,8,.96) 100%);
}
.lb-progetto-hero__testo { position: relative; z-index: 3; width: 100%; padding-bottom: clamp(44px, 7vh, 84px); }
.lb-progetto-hero__sede { font-size: .66rem; text-transform: uppercase; letter-spacing: .28em; color: var(--lb-tenue); }
.lb-progetto-hero__titolo h1 {
	font-family: var(--lb-extrawide);
	font-weight: 700; text-transform: uppercase; letter-spacing: -.01em; line-height: .92;
	color: var(--lb-titolo); font-size: clamp(2.6rem, 8vw, 6.4rem); margin: 16px 0 0;
}
.lb-progetto-hero__claim p {
	margin-top: 22px; font-size: clamp(1.05rem, 1.7vw, 1.35rem);
	color: var(--lb-testo); max-width: 30ch;
}
/* Sul telefono il titolo scende a una scala sua: `clamp(1.9rem, 9vw, 3rem)` al
   posto di `clamp(2.6rem, 8vw, 6.4rem)`. Non è un aggiustamento a occhio, è la
   riga `@media (max-width:700px)` del prototipo editoriale, mai portata. Senza,
   il titolo esce a 41,6px invece di 35,1 — la stessa dichiarazione che
   l'immersiva ha già. */
@media (max-width: 700px) {
	.lb-progetto-hero__titolo h1 { font-size: clamp(1.9rem, 9vw, 3rem); }
}

/* --- Apertura e risultato: stessa impaginazione, due contenuti --- */
.lb-lead p {
	/* Giro 6: ExtraWide. Era Regular, clamp(1.6rem, 4.2vw, 3.2rem), 20ch. */
	font-family: var(--lb-titolo-famiglia);
	font-weight: 700; text-transform: uppercase; letter-spacing: -.01em;
	color: var(--lb-titolo); font-size: clamp(1.35rem, 3.4vw, 2.6rem); line-height: 1.08;
	text-wrap: balance; max-width: 18ch; margin: 18px 0 0;
}
/* La parte fra **doppi asterischi** diventa <em> in render: vedi lb_evidenzia(). */
.lb-lead em { color: var(--lb-arancio); font-style: normal; }
.lb-kick p { color: var(--lb-tenue); font-size: 1.05rem; max-width: 52ch; margin-top: 24px; }

/* --- Immagine a tutta larghezza --- */
.lb-bleed { padding: 0; position: relative; }
.lb-bleed img { width: 100%; height: clamp(60vh, 80vh, 860px); object-fit: cover; display: block; }
.lb-bleed::after {
	content: ""; position: absolute; inset: 0; pointer-events: none;
	background: linear-gradient(180deg, rgba(10,9,8,.25), transparent 30%, transparent 70%, rgba(10,9,8,.5));
}

/* --- Split testo/immagine: la classe più riusata del sito --- */
.lb-split__testo { flex: 1 1 0; }
.lb-split__cornice {
	flex: 1 1 0; aspect-ratio: 4/5; border-radius: 0; overflow: hidden;
	border: 1px solid var(--lb-filo); padding: 0;
}
.lb-split__cornice img { width: 100%; height: 100%; object-fit: cover; transition: transform .7s ease; }
.lb-split__cornice:hover img { transform: scale(1.05); }
.lb-split .lb-titolo-sezione h2 {
	/* Giro 6: ExtraWide. Era Regular, clamp(1.7rem, 3.6vw, 2.9rem). */
	font-family: var(--lb-titolo-famiglia);
	font-weight: 700; text-transform: uppercase; letter-spacing: -.01em;
	color: var(--lb-titolo); font-size: clamp(1.45rem, 2.9vw, 2.35rem); line-height: 1.02;
	max-width: none; margin: 16px 0 0;
}
.lb-split__paragrafo p { color: var(--lb-tenue); font-size: 1.02rem; margin-top: 18px; max-width: 44ch; }
/* Il blocco «La produzione» ha immagine a sinistra: è l'alternanza del prototipo. */
.lb-split--rovescio { flex-direction: row-reverse; }
@media (max-width: 900px) {
	.lb-split, .lb-split--rovescio { flex-direction: column; }
	.lb-split__cornice { width: 100%; }
}

/* --- Le foto dentro il racconto: il `.duo` del prototipo -------------------
   Due per riga, subito dopo «L'idea». Valori presi dal prototipo: griglia a due
   colonne, stesso gap della galleria, e un rapporto **4/3** — piu' larghe di
   quelle della galleria, che sono 4/5.

   **La sezione sparisce se il ripetitore e' vuoto** (decisione 26). Non c'e' un
   interruttore per farlo: un Listing con zero elementi stampa comunque il suo
   contenitore, e quello che resta e' una sezione alta quanto il suo padding.
   Lo chiude `:has()`, che qui e' lo strumento giusto perche' la condizione la
   detta il **contenuto**, e il contenuto lo conosce solo il browser. */
.lb-duo { margin-top: clamp(20px, 3vw, 32px); }

/* **Le colonne le fa la griglia del tema, e la misura del plugin va neutralizzata.**
   Non e' una scelta: e' il motivo per cui la galleria qui sotto funziona da sempre.

   JetEngine impagina in flex, con questa regola nel suo `frontend.css`:
       .jet-listing-grid__items > .jet-listing-grid__item {
           max-width: calc(100% / var(--columns));
           flex: 0 0 calc(100% / var(--columns));
       }
   E' un **`max-width`**, non un `width`. Dentro una griglia CSS quel `100%/2`
   diventa meta' *traccia* invece di meta' riga: la foto usciva **282px in una
   colonna da 563**.

   Un `width: 100%` non lo tocca — un massimo non si vince con una larghezza. E
   nemmeno rimettere `--columns: 1`, perche' la regola del plugin arriva da un
   selettore col figlio diretto (`>`) che pesa piu' del nostro. Le due righe qui
   sotto sono la **stessa forma** che il tema usa gia' per `.lb-galleria__griglia`
   e per `.lb-lv-griglia`: si spegne il massimo, e le colonne restano quelle
   dichiarate qui. */
.lb-duo__griglia .jet-listing-grid__items {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: clamp(14px, 2vw, 26px);
}
.lb-duo__griglia .jet-listing-grid__items > .jet-listing-grid__item {
	max-width: none; flex: none; width: auto; margin: 0; padding: 0;
}
.lb-duo__foto { padding: 0; }
.lb-duo__img {
	display: block; aspect-ratio: 4/3; border-radius: 0; overflow: hidden;
	border: 1px solid var(--lb-filo);
}
.lb-duo__img img { width: 100%; height: 100%; object-fit: cover; transition: transform .7s ease; }
.lb-duo__foto:hover .lb-duo__img img { transform: scale(1.05); }

/* Se il ripetitore e' vuoto la sezione non compare (decisione 26). Un Listing a
   zero elementi stampa comunque il suo contenitore: a farlo sparire e' `:has()`,
   perche' la condizione la detta il contenuto e il contenuto lo sa il browser. */
.lb-duo:not(:has(.jet-listing-grid__item)) { display: none; }

@media (max-width: 700px) {
	.lb-duo__griglia .jet-listing-grid__items { grid-template-columns: 1fr; }
}

/* --- La galleria: tante foto quante ne servono, nessun tetto (decisione 25) --- */
.lb-galleria__griglia { margin-top: 52px; }
/* **G4-07: le foto de «Il sistema» avevano 22 px di margine a sinistra e 2 a
   destra**, e la riga che le sbilanciava è la `width` qui sotto — che prima non
   c'era. Fabrizio: «le foto de "il sistema" hanno margini differenti a dx e sx.
   manterrei la stessa distanza di tutta la pagina, prendi da esempio la foto de
   "la produzione"».

   La causa sta in `frontend.css` di JetEngine, che impagina le sue griglie con
   una coppia:

       .jet-listing-grid__items { width: calc(100% + 20px); margin: 0 -10px; }

   È il vecchio trucco delle gutter: si allarga di 20 e si riprendono 10 per
   lato col margine negativo, così le colonne restano allineate al contenitore.
   **Le due righe vanno insieme o non funziona nessuna delle due.** Qui il
   `margin: 0` toglieva la metà che rientra e lasciava la metà che sborda: i
   20 px in più finivano **tutti a destra**.

   Misurato su tre larghezze, con «La produzione» come riferimento (è la foto che
   Fabrizio ha indicato, ed è un `.lb-split__cornice`, non una griglia JetEngine):

                     produzione        Il sistema PRIMA      Il sistema DOPO
        393 px       22 / 22           22 / 2                22 / 22
       1024 px       41 / 41           41 / 21               41 / 41
       1440 px      154 / 154         154 / 134             154 / 154

   ⚠️ **Non è un difetto del telefono: erano 20 px a ogni larghezza**, desktop
   compreso, e tre giri di revisione su desktop non l'avevano visto. Per questo la
   correzione non sta in una fascia: la simmetria dei margini non è una scelta
   responsive.

   ✅ **La larghezza non si corregge più qui.** Il 2026-08-26, su richiesta di
   Fabrizio di allineare *tutti* i margini, il censimento ha trovato **sei
   griglie** con lo stesso offset e la neutralizzazione è diventata **una riga
   sola per tutte** — cercala come «L'offset delle griglie JetEngine», molto più
   su in questo foglio. Qui resta la spiegazione del caso che l'ha fatta
   scoprire, e **nient'altro**: dichiarare la stessa larghezza anche qui
   significherebbe averla in due posti che col tempo divergono.

   ⚠️ **Un'ultima cosa che il numero dell'oracolo non dice.** Dopo la correzione
   la sezione risulta **−9 px** contro il prototipo, mentre prima era **−1**: la
   misura sembra peggiorata. Non lo è. Griglia e fotografie ora combaciano col
   prototipo **al pixel** — `154 → 1286`, foto larga 360 e alta 450 di qua e di
   là — e ogni componente della sezione è identico (riempimento 140/140,
   occhiello 16, stacco 52, griglia 480, didascalia 18). I 9 px sono
   nell'**occhiello del prototipo**, che è uno `<span>` in linea e occupa una
   riga di 25 px invece dei 16 del nostro widget. Prima **due errori si
   annullavano**: la griglia larga 20 di troppo compensava quei 9. Togliendone
   uno, l'altro è venuto a galla. **Il difetto esposto non l'ha creato questa
   correzione.** */
.lb-galleria__griglia .jet-listing-grid__items {
	display: grid; grid-template-columns: repeat(3, 1fr);
	gap: clamp(14px, 2vw, 26px); margin: 0;
}
.lb-galleria__griglia .jet-listing-grid__item { margin: 0 !important; padding: 0 !important; width: auto !important; max-width: none !important; }
.lb-galleria__foto { padding: 0; }
.lb-galleria__img {
	display: block; aspect-ratio: 4/5; border-radius: 0; overflow: hidden;
	border: 1px solid var(--lb-filo);
}
.lb-galleria__img img { width: 100%; height: 100%; object-fit: cover; transition: transform .7s ease; }
.lb-galleria__foto:hover .lb-galleria__img img { transform: scale(1.05); }
.lb-galleria__didascalia {
	margin-top: 12px; font-size: .72rem; text-transform: uppercase;
	letter-spacing: .14em; color: var(--lb-tenue);
}
@media (max-width: 900px) { .lb-galleria__griglia .jet-listing-grid__items { grid-template-columns: 1fr 1fr; } }
@media (max-width: 600px) { .lb-galleria__griglia .jet-listing-grid__items { grid-template-columns: 1fr; } }

/* --- Scheda tecnica --- */
.lb-scheda { border-top: 1px solid var(--lb-filo); margin-top: 8px; padding-block: 0 clamp(72px, 10vw, 140px); }
 /* La riga è un Container flex: l'etichetta ha larghezza fissa e il valore
   prende il resto. Nel prototipo è una griglia `200px 1fr`, che in flex si
   ottiene con `flex: 0 0 200px` sull'etichetta — senza forzare `display:grid`
   su un elemento che Elementor gestisce come flex. */
.lb-scheda__riga { border-bottom: 1px solid var(--lb-filo); align-items: flex-start; }
.lb-scheda__chiave { flex: 0 0 200px; }
.lb-scheda__valore { flex: 1 1 auto; }
/* **Tre classi, non due, e per la stessa ragione dell'eyebrow.** La regola che
   Elementor scrive in ogni foglio di blocco di libreria dichiara `line-height` e
   `letter-spacing` con la stessa specificità di questa, e quei fogli si caricano
   dopo il tema: le due dichiarazioni contese le perdeva, le altre no.
   Si vedeva solo a 390, dove la riga si impila e l'altezza dell'etichetta entra
   nel conto — a 1440 l'etichetta è più bassa del valore e non sposta niente.
   È lo stesso guasto due volte: quando una regola del tema riguarda un widget
   dentro un blocco di libreria, due classi non bastano. */
.elementor-widget.lb-scheda__chiave .elementor-heading-title {
	font-size: .7rem; text-transform: uppercase; letter-spacing: .18em;
	line-height: 1.55;
	color: var(--lb-arancio); padding-top: 4px; margin: 0;
	font-family: var(--lb-regular); font-weight: 400;
}
.lb-scheda__valore { color: var(--lb-titolo); font-size: 1.05rem; }
/* Una riga senza valore non compare vuota: sparisce (decisione 26). Il widget
   dentro si nasconde da sé con `hide_if_empty`; qui sparisce anche l'etichetta. */
.lb-scheda__riga:not(:has(.jet-listing-dynamic-field, .jet-listing-dynamic-repeater)) { display: none !important; }

/* A 860 la riga della scheda si impila, e lo stacco fra etichetta e valore
   scende da 24 a 6: sono due dichiarazioni del prototipo
   (`@media (max-width:860px){ .facts .row{ grid-template-columns:1fr; gap:6px } }`)
   che il tema non aveva mai ricevuto. Valgono su tutti e due i layout, perché il
   blocco è lo stesso — ed è così che il difetto si è fatto trovare: usciva
   identico, +12, su entrambe le schede. */
@media (max-width: 860px) {
	/* **Si scrive la variabile, non la proprietà** — ed è la quarta volta in un
	   giorno che questa distinzione decide l'esito.

	   Elementor non mette `flex-direction: row` sul contenitore: mette
	   `--flex-direction: row`, e poi una sua regola fa
	   `flex-direction: var(--flex-direction)`. Una dichiarazione del tema che
	   scriva direttamente `flex-direction: column` perde da quella, perché la
	   regola di Elementor ha una classe in più — e i fogli dei blocchi si
	   caricano **dopo** il tema, quindi a pari specificità vincono comunque.
	   `row-gap: 6px` passava e `flex-direction: column` no: due dichiarazioni
	   nella stessa regola, una applicata e l'altra scartata in silenzio.

	   Qui si scrive la variabile che Elementor legge, con **quattro classi** —
	   una più di `.elementor-87 .elementor-element.elementor-element-lbschcl`.
	   Funziona anche `flex-direction: column !important`, misurato: dà lo stesso
	   risultato al pixel. Si preferisce questa perché usa il meccanismo invece di
	   scavalcarlo, e perché un `!important` qui insegnerebbe a metterne altri. */
	.single-progetto .lb-scheda .lb-scheda__riga.e-con {
		--flex-direction: column;
		--gap: 6px 6px; --row-gap: 6px; --column-gap: 6px;
	}
	/* In colonna, `flex: 0 0 200px` sull'etichetta varrebbe sull'ALTEZZA. */
	.lb-scheda__chiave { flex: 0 0 auto; }
}

/* --- CTA di chiusura --- */
.lb-cta-progetto { padding-block: 0 clamp(72px, 10vw, 140px); }
.lb-cta-progetto .lb-cta { margin-top: 30px; }

/* --- G3-09 e G3-12: «Il risultato» allineato a sinistra ------------------
   La stessa segnalazione sulle due schede, editoriale e immersiva, il
   2026-08-26: *«la tabella ha spazio vuoto a sinistra rispetto
   all'allineamento di titolo e copy. Lo stesso vale per la CTA che è molto in
   basso e spostata ancora più a destra»*. Due difetti, non uno, e nessuno dei
   due era di gusto.

   **① I 54px a sinistra: il gutter pagato due volte.** `.lb-risultato` è un
   contenitore «boxed», e il gutter glielo mettiamo **noi** in testa a questo
   foglio — `.e-con-boxed > .e-con-inner { padding-inline: var(--lb-margine) }`,
   che a 1873px vale 54px. Occhiello, frase e copy cadono così a **x=371**, che
   è il valore del prototipo. Ma la scheda tecnica e la CTA arrivano dal blocco
   di libreria #87, e **anche loro sono contenitori boxed**: la stessa regola li
   prende una seconda volta e aggiunge altri 54px sul già rientrato. Non è un
   difetto di quella regola — è giusta per un contenitore di primo livello, ed è
   sbagliata solo quando due si annidano.
   Misurato sull'hosting, identico sulle due pagine — tabella e pulsante a
   **x=425** contro i **371** di titolo e copy, e 1449 invece di 1503 a destra.
   Nel prototipo `.facts` e `.btn` stanno **dentro lo stesso `.wrap`** del
   titolo, quindi il gutter lo pagano una volta sola.

   **② I 130px sotto: la scheda paga uno stacco da sezione che qui non è.**
   `.lb-scheda` porta `padding-bottom: clamp(72px, 10vw, 140px)` perché **è**
   una sezione — un blocco di libreria di Elementor non può essere altro. Dentro
   «Il risultato» però non è una sezione: è la tabella prima del pulsante. Fra
   la fine della tabella e il pulsante c'erano **170px** (140 di stacco + 30 di
   `margin-top` della CTA); nel prototipo di Splendide il bottone ha
   `margin-top: 40px` e basta. È lo stesso argomento dei due casi nominati qui
   sotto, e questo è il terzo. */
.lb-risultato .lb-scheda > .e-con-inner,
.lb-risultato .lb-cta-progetto > .e-con-inner { padding-inline: 0; }
.lb-risultato .lb-scheda { padding-bottom: 0; }
.lb-risultato .lb-cta-progetto .lb-cta { margin-top: 40px; }

/* --- Lo stacco fra blocchi che il prototipo tiene dentro una sezione --------
   **La struttura è imposta, il `padding` no**, e la differenza fra le due cose è
   quello che questo blocco corregge.

   Un blocco di libreria di Elementor **è** una sezione: non c'è modo di
   appoggiarne uno dentro un'altra senza che sia un contenitore. Quella parte non
   si discute. Ma ogni sezione porta con sé `padding-block`, e dove il prototipo
   tiene due blocchi **dentro la stessa sezione** noi paghiamo lo stacco due
   volte — una in fondo al primo e una in cima al secondo.

   Costava **+287 px** sulle foto dentro il racconto e **+104** in chiusura di
   pagina: da soli, i due terzi dei 568 px per cui la scheda editoriale era la
   sola pagina del sito fuori riga (+7,0%, contro un +0,2/+4% di tutte le altre).

   Non è una deviazione legittima e non è una scelta di design: **niente ce lo
   impediva, era lavoro non fatto** (decisione 83). Rivede la decisione 102, che
   era stata scritta quando sembrava riguardare la sola scheda immersiva — cambia
   perché è cambiato quanto sapevamo, non perché abbiamo cambiato idea.

   ⚠️ **Non si azzera lo stacco fra due sezioni qualsiasi.** Le sezioni che il
   prototipo tiene davvero separate lo stacco lo devono pagare, e infatti sono già
   a −36 (140 contro 150): togliere altro le porterebbe fuori dall'altra parte.
   Si nominano i due casi, uno per uno. */

/* 1. Le foto dentro il racconto continuano la sezione «L'idea»: nel prototipo
      stanno dentro quel `<section>`, separate solo da un margine. */
.single-progetto .lb-sezione:has(+ .lb-duo) { padding-bottom: 0; }
.single-progetto .lb-duo { padding-top: 0; }

/* 2. Il pulsante di chiusura sta dentro «Il risultato», su tutti e due i layout:
      la sezione paga già il proprio stacco in fondo, il blocco non lo paga una
      seconda volta. Vale per entrambi perché il template immersivo è stato
      allineato all'editoriale — il widget è dentro la sezione, non un fratello. */
.single-progetto .lb-risultato .lb-cta-progetto { padding-bottom: 0; }

/* --- Le tre cose che si muovono solo qui ---------------------------------
   Parallassi e barra di avanzamento esistono **solo nel prototipo immersivo**:
   quello editoriale non ha nessun movimento allo scroll, e le classi che
   servirebbero — `.lb-split__cornice`, `.lb-prossimo__foto` — sono le stesse dei
   due layout. Da qui i modificatori: `--parallasse` sta sul contenitore della
   scheda immersiva, non dentro il blocco condiviso, così l'editoriale non li
   vede nemmeno. Stesso schema di `.lb-lead--immersivo`.

   Le trasformazioni le scrive `comportamenti.js`, e sono tutte sotto
   `prefers-reduced-motion`: qui c'è solo lo spazio in cui possono muoversi. */

/* La fotografia deve essere più alta del riquadro, o non ha dove scorrere. */
.lb-split__cornice--parallasse img {
	height: 118%; margin-top: -9%; will-change: transform;
}
/* Lo sfondo di «Prossimo progetto» esce di un decimo sopra e sotto. */
.lb-prossimo--parallasse .lb-prossimo__foto {
	inset: -10% 0; opacity: .32; will-change: transform;
}

/* La barra di avanzamento in cima. La costruisce `comportamenti.js` e la mette
   fuori dal contenuto, come la grana: se fosse un Container il cliente potrebbe
   cancellarla per sbaglio senza capire cosa è cambiato. */
.lb-prog {
	position: fixed; top: 0; left: 0; height: 3px; width: 100%;
	background: var(--lb-arancio);
	transform: scaleX(0); transform-origin: 0 50%;
	z-index: 700; pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
	.lb-split__cornice--parallasse img,
	.lb-prossimo--parallasse .lb-prossimo__foto { transform: none !important; }
}

/* --- Le sei descrizioni del processo: generica, o quella del servizio -----
   Il blocco è **uno solo** ed è condiviso fra la pagina Servizi e le sette
   schede. I sei **titoli** sono struttura — sono il processo dell'azienda, e se
   cambiano devono cambiare ovunque senza eccezioni. Le sei **descrizioni** no:
   quattro su sei divergono già oggi fra Servizi e Packaging, e divergono di
   proposito (content model §7).

   Perciò ogni fase porta due descrizioni: quella generica, scritta nel blocco, e
   quella specifica, letta da `processo_0N` del servizio corrente. Questa regola
   nasconde la generica quando la specifica ha reso qualcosa.

   **Lo stesso meccanismo serve i due casi senza un interruttore.** Sulla pagina
   Servizi quei campi non esistono, restano vuoti, `hide_if_empty` non stampa
   niente e la generica resta al suo posto. È il ripiego di «Cliente», applicato
   sei volte. */
.lb-processo__corpo:has(.lb-processo__testo--specifico .jet-listing-dynamic-field__content)
	.lb-processo__testo--generico { display: none; }

/* --- «Cliente»: il campo, e il titolo come ripiego ------------------------
   Due widget nella stessa riga: il primo legge il campo `cliente` e sparisce se
   e' vuoto, il secondo stampa il titolo del progetto. Questa regola nasconde il
   secondo quando il primo ha reso qualcosa.

   Non e' un ripiego per mancanza di alternative, e le due che sembrano ovvie non
   funzionano: `field_fallback` di JetEngine prende **una stringa fissa** —
   `wp_kses_post( $settings['field_fallback'] )` alla riga 282 di
   `listings/render/dynamic-field.php` — e non puo' contenere il titolo; e un
   `filter_callback` non viene **mai chiamato su un valore vuoto**, perche' il
   controllo su vuoto sta prima, alla riga 277, e fa `return null`.

   Il selettore verifica il **contenuto reso**, non il widget: con `hide_if_empty`
   JetEngine non stampa nessun `__content`, quindi «il primo ha reso qualcosa» e
   «esiste un `__content` dentro `.lb-cliente`» sono la stessa cosa. */
.lb-scheda__riga:has(.lb-cliente .jet-listing-dynamic-field__content) .lb-cliente--ripiego {
	display: none;
}

/* --- Prossimo progetto --- */
.lb-prossimo { position: relative; overflow: hidden; padding: 0; }
.lb-prossimo__card { padding: 0; }
 /* Lo sfondo del prossimo progetto: stessa catena di altezze dell'hero. Fra il
   widget e la foto ci sono tre involucri, e basta che uno resti `auto` perché
   la fotografia non si veda affatto. */
.lb-prossimo__foto { position: absolute; inset: 0; z-index: 0; opacity: .35; }
.lb-prossimo__foto,
.lb-prossimo__foto .elementor-widget-container,
.lb-prossimo__foto .jet-listing-dynamic-image { height: 100%; width: 100%; }
 /* Il selettore passa da `.jet-listing-dynamic-image` perché JetEngine stampa
   una propria regola sull'immagine e vince su un selettore più corto: senza,
   la foto resta larga quanto la sua proporzione e lascia il resto nero. */
.lb-prossimo__foto .jet-listing-dynamic-image img,
.lb-prossimo__foto img { width: 100%; height: 100%; object-fit: cover; display: block; max-width: none; }
/* **G2-17, le «barre quadrate nere»: cercato, e dalla nostra parte non c'è.**
   La segnalazione descrive bene quello che si vede, ma la causa non è CSS. Ho
   provato ad aggiungere il velo del prototipo convinto che mancasse: **c'era
   già**, venti righe più sotto, identico. La mia sonda non l'aveva trovato
   perché guardava gli **elementi** e un velo è uno **pseudo-elemento**.
   La prova che ha chiuso la questione: luminanza media dello stesso riquadro
   fotografato prima e dopo l'aggiunta — **19,3 e 19,3**, deviazione 25,05 e
   25,05. Identiche: la regola in più non dipingeva niente.

   Cosa sono davvero i rettangoli: la fotografia è un piano di carte da lettera
   su fondo scuro, e a `opacity:.32` sotto il velo i fogli restano più chiari
   dello sfondo. Quelle sono le carte, non delle bande. Ci si aggiunge che la
   sorgente è verticale (1200x1440) dentro una fascia 2,25:1, quindi se ne vede
   un terzo scarso. **È il materiale**: non si corregge da qui, e infilare altro
   nero sopra spegnerebbe anche il poco che si legge. */
/* Il separatore fra le voci degli elenchi della scheda tecnica. */
/* Le voci degli elenchi della scheda tecnica vanno su una riga sola, separate da
   un punto medio. JetEngine le stampa come `<div>` dentro `<div>`, e quello
   interno è `display:flex`: senza portare in linea anche lui, «Brand Identity ·
   Progettazione grafica · …» diventa un elenco verticale di sette righe. */
.lb-scheda__valore .jet-listing-dynamic-repeater__items,
.lb-scheda__valore .jet-listing-dynamic-repeater__item,
.lb-scheda__valore .jet-listing-dynamic-repeater__delimiter { display: inline; }
/* **`inline-block`, non `inline`, e la differenza vale 26px.** Con `inline` la
   prima voce si spezzava a metà — «Oro» su una riga e «satinato» sulla
   successiva, con la prima riga larga 32px su 346 disponibili — e la scheda
   usciva a tre righe invece di due. Con `inline-block` ogni voce è un blocco
   atomico e l'a capo cade fra una voce e l'altra: 124,4px da entrambe le parti,
   e due righe come nel prototipo.

   ⚠️ **Non so spiegare perché `inline` rompa la prima riga**, e lo scrivo invece
   di inventare un meccanismo: il contenitore `__items` misura 351px dentro un
   genitore da 346, quindi qualcosa deborda, ma non ho isolato cosa. Il rimedio è
   verificato per misura su sei candidati — è l'unico che dà insieme l'altezza
   giusta e il numero di righe giusto.

   C'è anche una ragione di merito, ed è quella per cui il rimedio è accettabile
   e non solo funzionante: in una scheda tecnica una voce è un'unità, e
   «Cartotecnica su / misura» spezzata a metà si legge peggio. `inline-block` lo
   garantisce; il prototipo, che ha un testo unico, ci arriva per fortuna. */
.lb-scheda__valore .jet-listing-dynamic-repeater__item > div { display: inline-block; }
.lb-prossimo__card::after {
	content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
	background: linear-gradient(180deg, rgba(10,9,8,.85), rgba(10,9,8,.75));
}
.lb-prossimo__testo {
	position: relative; z-index: 2; text-align: center;
	padding-block: clamp(80px, 12vw, 170px);
}
.lb-prossimo__testo .lb-eyebrow .elementor-heading-title { justify-content: center; }
.lb-prossimo__titolo h2 {
	/* Giro 6: ExtraWide. Era Regular, clamp(2rem, 6vw, 4.4rem). */
	font-family: var(--lb-titolo-famiglia);
	font-weight: 700; text-transform: uppercase; letter-spacing: -.01em;
	color: var(--lb-titolo); font-size: clamp(1.7rem, 4.8vw, 3.5rem); line-height: 1; margin: 14px 0 0;
}
.lb-prossimo__vai .jet-listing-dynamic-link__link {
	display: inline-flex; align-items: center; gap: 10px; margin-top: 26px;
	font-size: .78rem; text-transform: uppercase; letter-spacing: .14em;
	color: var(--lb-titolo); border-bottom: 1px solid var(--lb-arancio); padding-bottom: 7px;
}
.lb-prossimo__vai .jet-listing-dynamic-link__link::after { content: "→"; color: var(--lb-arancio); }
/* Se la cascata di §3.5 non trova nessun progetto — portfolio con una scheda
   sola — la griglia resta vuota e la sezione sparisce, invece di mostrare un
   link che rimanda a se stesso. */
.lb-prossimo:not(:has(.lb-prossimo__card)) { display: none; }

/* =====================================================================
   SCHEDA PROGETTO — LAYOUT IMMERSIVO
   ---------------------------------------------------------------------
   Misure prese dal blocco `<style>` di
   `lion-brand-progetto-splendide-royal-v2.html`. Il layout editoriale e
   questo condividono tutto quello che sta sopra (`.lb-indietro`,
   `.lb-lead`, `.lb-split`, `.lb-scheda`, `.lb-prossimo`): qui c'è solo
   ciò che l'immersiva ha in più — l'apertura a tutta finestra, i capitoli
   pinnati, l'immagine con la citazione e il carosello che si trascina.

   **Due sezioni di questa pagina non esistono senza JavaScript**, e questo
   file le porta in uno stato statico leggibile invece che in un buco nero:
   i capitoli mostrano il primo fotogramma, il carosello mostra i primi
   pannelli. `comportamenti.js` aggiunge `lb-guidata` e da lì in poi
   comanda lui. Una sezione che senza JS non mostra niente non è una
   degradazione: è una pagina rotta che sembra solo più corta.
   ===================================================================== */

/* --- Apertura a tutta finestra ------------------------------------------
   Non è `.lb-progetto-hero` con un'altra classe: il prototipo immersivo ha
   altezza fissa invece che minima, titolo più grande, e la fotografia si
   muove col mouse invece di fare il carrello. Sono numeri diversi, non una
   svista, quindi vivono separati. */
.lb-im-hero { position: relative; height: 100svh; min-height: 620px; overflow: hidden; padding: 0; }
.lb-im-hero__sfondo { position: absolute; inset: -8% -4%; z-index: 0; padding: 0; will-change: transform; }
.lb-im-hero__sfondo,
.lb-im-hero__sfondo .elementor-widget,
.lb-im-hero__sfondo .jet-listing-dynamic-image { height: 100%; }
.lb-im-hero__sfondo .jet-listing-dynamic-image img,
.lb-im-hero__sfondo img { width: 100%; height: 100%; object-fit: cover; display: block; max-width: none; }
.lb-im-hero__velo {
	position: absolute; inset: 0; z-index: 2; padding: 0; pointer-events: none;
	background: linear-gradient(180deg, rgba(10,9,8,.55) 0%, rgba(10,9,8,.12) 26%,
		rgba(10,9,8,.34) 50%, rgba(10,9,8,.78) 74%, rgba(10,9,8,.96) 100%);
}
.lb-im-hero__testo {
	position: absolute; left: 0; right: 0; bottom: 0; z-index: 3;
	padding-bottom: clamp(44px, 7vh, 84px); will-change: transform;
}
.lb-im-hero__sede { font-size: .66rem; text-transform: uppercase; letter-spacing: .28em; color: var(--lb-tenue); }
.lb-im-hero__titolo h1 {
	font-family: var(--lb-extrawide);
	font-weight: 700; text-transform: uppercase; letter-spacing: -.01em; line-height: .9;
	color: var(--lb-titolo); font-size: clamp(2.6rem, 8.5vw, 6.6rem); margin: 16px 0 0;
}
.lb-im-hero__claim p {
	margin-top: 20px; font-size: clamp(1.05rem, 1.7vw, 1.35rem);
	color: var(--lb-testo); max-width: 30ch;
}
/* Il richiamo allo scroll. Nel prototipo la barretta è un `<i>` vuoto; qui il
   widget stampa solo il testo, quindi la barretta è uno pseudo-elemento —
   stessa resa, un widget in meno da spiegare al cliente. */
/* **Lo «scorri» è uno solo, e la regola sta qui perché non possa più divergere.**
   G2-33. Fabrizio l'ha trovato diverso da pagina a pagina — «a volte c'è la linea
   animata arancione, a volte no» — e ha deciso: **scritta bianca più linea
   animata arancione, ovunque.**

   Contato prima di correggere, su otto pagine: esisteva in **due**, e in due
   forme diverse. `/chi-siamo/` aveva la scritta che ondeggiava e **nessuna
   linea**; `/progetto/splendide-royal/` aveva la linea animata e la scritta
   ferma. Sulle altre sei l'elemento **non c'è nel DOM**: quello è markup, non CSS.

   **Il prototipo qui non aiuta, perché è incoerente da solo** — `.cue` su Chi
   siamo (scritta che ondeggia, niente linea), `.scrollcue` sulla scheda
   immersiva (linea animata), e su Alto `.scrollcue { display: none }`. È il
   secondo caso, dopo G2-21, in cui Fabrizio dichiara che il prototipo sbaglia e
   detta lui la forma. Vince la sua.

   Una regola per tutti i «cue» invece di una per pagina: è il modo per non
   ritrovarsi la stessa segnalazione fra un mese. Chi aggiunge un hero nuovo dà
   al widget la classe `lb-*-hero__cue` e la resa arriva da sé. */
[class*="-hero__cue"] {
	position: absolute; left: 50%; bottom: 24px; transform: translateX(-50%); z-index: 4;
	display: flex; flex-direction: column; align-items: center; gap: 9px; width: auto;
}
.elementor-widget-heading[class*="-hero__cue"] .elementor-heading-title {
	font-family: var(--lb-regular);
	font-size: .58rem; letter-spacing: .3em; text-transform: uppercase;
	color: rgba(255,255,255,.6); margin: 0;
	/* La scritta sta ferma: a muoversi è la linea. Due animazioni sovrapposte
	   sullo stesso gruppo si leggevano come un tremolio. */
	animation: none;
}
[class*="-hero__cue"]::after {
	content: ""; display: block; width: 1px; height: 30px;
	background: linear-gradient(var(--lb-arancio), transparent);
	transform-origin: top; animation: lb-cue 1.8s ease-in-out infinite;
}
@keyframes lb-cue {
	0%, 100% { transform: scaleY(.4); opacity: .4; }
	50%      { transform: scaleY(1);  opacity: 1; }
}
/* **L'eccezione sta qui, accanto alla regola che governa, e non trecento righe
   più giù.** Segnalato da `dev-wp`: la linea era fermata solo per
   `.lb-im-hero__cue`, quindi con «riduci animazioni» attivo su una scheda
   immersiva stava ferma e su Chi siamo continuava a pulsare all'infinito. Il
   caso è nato **unificando la resa**: prima su Chi siamo la linea non c'era.

   La regola per la fascia deve valere `[class*="-hero__cue"]` come quella che
   corregge, o si ripresenta al prossimo hero che qualcuno aggiunge. */
@media (prefers-reduced-motion: reduce) {
	[class*="-hero__cue"]::after { animation: none; opacity: 1; transform: none; }
}

/* --- L'apertura e il risultato, nella misura dell'immersiva --------------
   `.lb-lead` e `.lb-kick` portano i numeri dell'editoriale, e sono giusti li':
   `lion-brand-progetto-alto.html` scrive `clamp(1.6rem, 4.2vw, 3.2rem)`,
   interlinea 1,08, 20ch. L'immersiva ha una scala sua — 3,4rem, 1,06, 22ch — e
   un respiro di 22px invece di 24 sotto la frase.

   Non e' una svista del prototipo: sono due pagine con due pesi tipografici, e
   il modificatore lo dice invece di lasciarlo capire. La differenza a schermo e'
   di 3,2px di corpo, cioè una riga di testo che va a capo in un punto diverso. */
.lb-lead--immersivo p {
	/* Giro 6: era clamp(1.7rem, 4.6vw, 3.4rem). In ExtraWide «UN'ESPERIENZA» a 27,2 px è larga
	   356 px e sbordava di 10 px dalla colonna da 346 a 390 (sonda dei titoli del lead). Il minimo
	   scende perché la parola stia anche a 360; il massimo scende del 20% come gli altri titoli. */
	font-size: clamp(1.45rem, 3.8vw, 2.75rem); line-height: 1.06; max-width: 22ch;
}
.lb-kick--immersivo p { margin-top: 22px; }

/* --- I capitoli pinnati --------------------------------------------------
   Il prototipo scrive `height:340vh` per tre capitoli. **Quel numero è un
   parametro, non una misura** (decisione 25): tre capitoli sono quelli che
   ha Splendide Royal, non quelli che il template accetta. L'altezza è
   `N × 100svh + 40vh`, e N lo conta `comportamenti.js` — che è anche l'unico
   che può contarlo, perché il CSS non sa quanti elementi ha un Listing.

   Senza JS resta un fotogramma solo, alto una finestra: si legge il primo
   capitolo e la pagina prosegue. Con JS la sezione si allunga e i fotogrammi
   si alternano in dissolvenza. */
.lb-capitoli { position: relative; padding: 0; height: 100svh; display: block; }
.lb-capitoli.lb-guidata { height: calc(var(--lb-capitoli-n, 1) * 100svh + 40vh); }
/* `display:block` non è una preferenza: Elementor mette `height:100%` sui suoi
   contenitori flex, e dentro un blocco alto 340vh quella percentuale rende alto
   340vh anche l'elemento che deve restare incollato a 100svh. È lo stesso
   inciampo già risolto su `.lb-lv-risultati`. */
.lb-capitoli__pista {
	position: sticky; top: 0; height: 100svh; overflow: hidden; z-index: 1;
}
.lb-capitoli__pista .jet-listing-grid,
.lb-capitoli__pista .jet-listing-grid__items { height: 100%; margin: 0; display: block; }
.lb-capitoli__pista .jet-listing-grid__item {
	position: absolute; inset: 0; margin: 0; padding: 0;
	max-width: none; width: auto; flex: none;
	opacity: 0; transition: opacity .7s ease;
}
/* Fra l'elemento del Listing e il contenitore del capitolo JetEngine infila il
   suo involucro `.elementor`: se resta alto quanto il contenuto, la fotografia
   non ha da cosa prendere il 100%. */
.lb-capitoli__pista .jet-listing-grid__item > .elementor { height: 100%; }
.lb-capitoli__pista .jet-listing-grid__item:first-child { opacity: 1; }
.lb-capitoli.lb-guidata .jet-listing-grid__item { opacity: 0; }
.lb-capitoli.lb-guidata .jet-listing-grid__item.lb-on { opacity: 1; }

.lb-cap { position: absolute; inset: 0; padding: 0; display: block; }
.lb-cap__foto { position: absolute; inset: 0; z-index: 0; padding: 0; }
.lb-cap__foto,
.lb-cap__foto .elementor-widget,
.lb-cap__foto .jet-listing-dynamic-image { height: 100%; }
/* ⛔ **La transizione da 1,2 secondi qui sotto è VOLUTA. Non è il ritardo del
   parallasse, e non va tolta.** È l'effetto per cui la fotografia del capitolo
   si allarga quando il capitolo diventa attivo: `scale(1.06)` a riposo, `1`
   quando la riga prende `.lb-on`.

   **Scriverlo qui è la correzione.** È stata segnalata **due volte** come
   difetto, ed è comprensibile: assomiglia al ritardo che avevamo davvero, quello
   dello zoom hover che rallentava il parallasse di 0,7s (vedi la nota sulle
   transizioni più in alto in questo foglio). La differenza si riconosce con una
   misura sola: **queste immagini non hanno `transform` in linea**, cioè nessun
   JavaScript le muove. Quelle del parallasse sì. */
.lb-cap__foto .jet-listing-dynamic-image img,
.lb-cap__foto img {
	width: 100%; height: 100%; object-fit: cover; display: block; max-width: none;
	transform: scale(1.06); transition: transform 1.2s ease;
}
.lb-capitoli .jet-listing-grid__item:first-child .lb-cap__foto img,
.lb-capitoli .jet-listing-grid__item.lb-on .lb-cap__foto img { transform: scale(1); }
.lb-cap::after {
	content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
	background: linear-gradient(90deg, rgba(10,9,8,.86), rgba(10,9,8,.2) 55%, transparent);
}
.lb-cap__testo {
	position: absolute; left: 0; right: 0; bottom: 0; z-index: 3;
	padding: 0 var(--lb-margine) clamp(50px, 8vh, 90px);
}
.lb-cap__etichetta {
	font-size: .7rem; letter-spacing: .24em; text-transform: uppercase; color: var(--lb-arancio);
}
.lb-cap__titolo h2 {
	/* Giro 6: ExtraWide. Era Regular, clamp(1.9rem, 5vw, 3.8rem). */
	font-family: var(--lb-titolo-famiglia);
	font-weight: 700; text-transform: uppercase; letter-spacing: -.01em;
	color: var(--lb-titolo); font-size: clamp(1.55rem, 4vw, 3.05rem); line-height: 1;
	margin: 14px 0 0; max-width: 16ch;
}
.lb-cap__paragrafo p { color: var(--lb-testo); max-width: 34ch; margin-top: 14px; }
/* Zero capitoli: la sezione sparisce invece di lasciare una finestra nera
   (content model §3.4). Vale anche per l'editoriale, che di capitoli non ne ha
   mai — ma lì il template non ha nemmeno la sezione. */
.lb-capitoli:not(:has(.jet-listing-grid__item)) { display: none; }

/* --- L'immagine a tutta larghezza con la citazione ---------------------- */
.lb-im-bleed {
	position: relative; padding: 0; display: block;
	height: clamp(60vh, 86vh, 900px); overflow: hidden;
}
.lb-im-bleed > .elementor-widget-jet-listing-dynamic-image {
	position: absolute; inset: -12% 0; z-index: 0; will-change: transform;
}
.lb-im-bleed > .elementor-widget-jet-listing-dynamic-image,
.lb-im-bleed .jet-listing-dynamic-image { height: 100%; }
.lb-im-bleed .jet-listing-dynamic-image img,
.lb-im-bleed img { width: 100%; height: 100%; object-fit: cover; display: block; max-width: none; }
.lb-im-bleed::after {
	content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
	background: linear-gradient(180deg, rgba(10,9,8,.32), rgba(10,9,8,.5));
}
.lb-im-bleed__frase {
	position: absolute; inset: 0; z-index: 2;
	display: flex; align-items: center; justify-content: center;
	text-align: center; padding: 0 8vw;
}
/* Il prototipo usa un `<blockquote>`. JetEngine ammette una lista chiusa di tag
   e lo scarta: qui il testo esce in un `<div>`. Differenza dichiarata, non
   subita — forzare il tag costerebbe un widget HTML, cioè il criterio di finito. */
.lb-im-bleed__frase .jet-listing-dynamic-field__content {
	/* Giro 6: la frase a tutta pagina sopra la foto è un titolo, quindi ExtraWide come gli altri
	   (decisione 276). Era Regular, clamp(1.4rem, 3.6vw, 2.8rem). */
	font-family: var(--lb-titolo-famiglia);
	font-weight: 700; text-transform: uppercase; color: #fff;
	font-size: clamp(1.2rem, 2.9vw, 2.25rem); line-height: 1.1;
	text-wrap: balance; max-width: 24ch; text-shadow: 0 2px 40px rgba(0,0,0,.5);
}
.lb-im-bleed:not(:has(img)) { display: none; }

/* --- Il carosello che si trascina ---------------------------------------
   L'altezza della sezione la scrive `comportamenti.js`: dipende da quanto è
   larga la pista, che dipende da quanti pezzi ci sono e da quanto è largo lo
   schermo. Nessuno dei due si sa da qui.

   Senza JS la pista resta ferma e mostra i primi pannelli: `overflow:hidden`
   sulla parte incollata evita che il resto esca dalla pagina e faccia comparire
   una barra di scorrimento orizzontale su tutto il sito. */
.lb-pezzi { position: relative; padding: 0; display: block; min-height: 100svh; }
/* **G3-10: «I pezzi · trascina lo scroll» non si incolla più.** Fabrizio, il
   2026-08-26: *«allo scroll mi segue fino alla prossima sezione mentre dovrebbe
   rimanere fermo e non essere sticky»*.

   Cosa faceva davvero, misurato: era `position: sticky` con `height: 0`, e un
   elemento incollato si stacca quando il **fondo del genitore meno la propria
   altezza** gli passa sotto. Altezza zero vuol dire che quel momento arriva al
   **fondo esatto** di `.lb-pezzi`, cioè 3.083px più giù. La pista accanto è
   incollata anche lei ma è alta `100svh`, quindi si stacca **1.000px prima**:
   in quella finestra l'occhiello restava sospeso da solo sopra il contenuto
   successivo. Non era un difetto di soglia, era una soglia diversa dall'altra.

   ⚠️ **Divergenza dichiarata dal prototipo.** Là (`.hstory .intro`) l'etichetta
   è `position: absolute` **dentro `.hstory .sticky`**, quindi cavalca la parte
   incollata e resta a schermo per tutto lo scorrimento orizzontale. Qui la
   rendiamo assoluta rispetto alla **sezione**, non alla parte incollata: sale
   con la pagina e se ne va. È quello che Fabrizio ha chiesto — *«dovrebbe
   rimanere fermo»* — e in questo giro il suo riscontro vince sul prototipo.
   Le dichiarazioni restano quelle sue: stesso `top`, stesso rientro. */
.lb-pezzi__occhiello {
	position: absolute; top: clamp(70px, 12vh, 120px); left: 0; right: 0;
	z-index: 3; height: 0;
	padding-inline: clamp(22px, 6vw, 90px);
}
.lb-pezzi__pista {
	position: sticky; top: 0; height: 100svh; overflow: hidden; z-index: 1;
	display: flex; align-items: center;
}
.lb-pezzi__pista > .jet-listing-grid { width: 100%; }
.lb-pezzi__pista .jet-listing-grid__items {
	display: flex; flex-wrap: nowrap; align-items: flex-start;
	gap: clamp(20px, 3vw, 44px); padding: 0 clamp(22px, 6vw, 90px); margin: 0;
	width: max-content; will-change: transform;
	counter-reset: lb-pezzo;
}
/* JetEngine impagina i suoi elementi con `max-width` calcolata sulle colonne: un
   massimo non si vince con una larghezza, va tolto. Stessa medicina di
   `.lb-galleria__griglia` e `.lb-lv-griglia`. */
.lb-pezzi__pista .jet-listing-grid__items > .jet-listing-grid__item {
	flex: 0 0 auto; width: min(66vw, 720px); max-width: none;
	margin: 0; padding: 0; counter-increment: lb-pezzo;
}
.lb-pezzo { padding: 0; display: block; }
.lb-pezzo__foto { padding: 0; }
.lb-pezzo__img {
	display: block; aspect-ratio: 4/3; border-radius: 0; overflow: hidden;
	border: 1px solid var(--lb-filo); background: var(--lb-superficie);
}
.lb-pezzo__img .jet-listing-dynamic-image img,
.lb-pezzo__img img { width: 100%; height: 100%; object-fit: cover; display: block; max-width: none; }
.lb-pezzo__meta { display: flex; align-items: baseline; gap: 14px; margin-top: 16px; }
/* Il numero progressivo del prototipo («01», «02», …) è un contatore CSS e non
   un campo: il cliente aggiunge una foto in mezzo e la numerazione si rifà da
   sola. Un campo `numero` sarebbe un dato da tenere allineato a mano. */
.lb-pezzo__meta::before {
	content: counter(lb-pezzo, decimal-leading-zero);
	color: var(--lb-arancio); font-weight: 700; font-variant-numeric: tabular-nums;
}
.lb-pezzo__meta .jet-listing-dynamic-field__content {
	font-family: var(--lb-regular);
	font-weight: 700; text-transform: uppercase; letter-spacing: -.01em;
	color: var(--lb-titolo); font-size: clamp(1.2rem, 2.2vw, 1.7rem);
}
.lb-pezzi:not(:has(.jet-listing-grid__item)) { display: none; }

@media (max-width: 860px) {
	.lb-pezzi__pista .jet-listing-grid__items > .jet-listing-grid__item { width: 82vw; }
	.lb-cap__paragrafo p { max-width: 26ch; }
}
@media (max-width: 700px) {
	.lb-im-hero__titolo h1 { font-size: clamp(1.8rem, 8.5vw, 3rem); }
	.lb-im-hero__cue { display: none; }
	.lb-im-hero__claim p { max-width: none; }
	/* Sul telefono la sfumatura del capitolo va dal basso, non da sinistra: il
	   testo sta sotto la fotografia, non di fianco. */
	.lb-cap::after {
		background: linear-gradient(0deg, rgba(10,9,8,.92), rgba(10,9,8,.35) 48%, rgba(10,9,8,.1));
	}
	.lb-cap__titolo h2 { font-size: clamp(1.8rem, 7.5vw, 2.6rem); }
	.lb-cap__testo { padding-bottom: clamp(44px, 9vh, 80px); }
}
@media (prefers-reduced-motion: reduce) {
	.lb-cap__foto img { transform: none; transition: none; }
	.lb-capitoli__pista .jet-listing-grid__item { transition: none; }
}

/* --- Schede progetto da telefono: foto intera SOPRA, testo SOTTO ---------
   Decisione di Fabrizio, 13-10: «in alcuni progetti, le sezioni a schermo pieno
   con l'immagine a sfondo non si vedono bene perché l'immagine è decentrata o
   viene tagliata molto». Su un telefono verticale una foto orizzontale a
   `object-fit: cover` dentro 100svh ne mostra una striscia (≈ 1/4 della
   larghezza): «SOUL FLOOR» troncato, il vinile a metà. Qui la fotografia
   mantiene il suo formato (`height: auto`, `object-fit: contain` solo come
   tetto per le verticali) e il testo sta sotto, su fondo scuro, senza velo.

   Vale sotto 768 px **in verticale**: in orizzontale (un telefono girato) la
   finestra è larga e `cover` taglia poco, quindi resta il layout di desktop.
   Da 768 in su non cambia niente. Tutto il responsive sta qui, nessun pannello
   breakpoint di Elementor (vedi CLAUDE.md di progetto).

   I `!important` sono tutti su proprietà che `comportamenti.js` scrive IN LINEA
   (`transform`, `opacity` della parallasse): un foglio non batte uno stile in
   linea in nessun altro modo, e qui la parallasse va spenta perché con la foto
   in flusso farebbe scivolare l'immagine sul testo. */
@media (max-width: 767px) and (orientation: portrait) {
	/* Aperture (editoriale e immersiva): foto in cima, titolo sotto. 72px = la
	   testata fissa (66,8px misurati), così la foto non finisce sotto di essa. */
	.lb-progetto-hero, .lb-im-hero {
		height: auto; min-height: 0; overflow: visible;
		justify-content: flex-start; padding-top: 72px;
	}
	.lb-progetto-hero__sfondo, .lb-im-hero__sfondo {
		position: relative; inset: auto; height: auto; will-change: auto;
		transform: none !important;
	}
	.lb-progetto-hero__sfondo > .e-con-inner,
	.lb-progetto-hero__sfondo .elementor-widget,
	.lb-progetto-hero__sfondo .elementor-widget-container,
	.lb-progetto-hero__sfondo .jet-listing-dynamic-image,
	.lb-im-hero__sfondo .elementor-widget,
	.lb-im-hero__sfondo .jet-listing-dynamic-image { height: auto; }
	.lb-progetto-hero__sfondo img, .lb-im-hero__sfondo img,
	.lb-progetto-hero__sfondo .jet-listing-dynamic-image img,
	.lb-im-hero__sfondo .jet-listing-dynamic-image img {
		position: static; inset: auto; width: 100%; height: auto; max-height: 62svh;
		object-fit: contain; animation: none; transform: none;
	}
	.lb-progetto-hero__velo, .lb-im-hero__velo { display: none; }
	.lb-progetto-hero__testo, .lb-im-hero__testo {
		position: relative; left: auto; right: auto; bottom: auto;
		padding-top: 26px; padding-bottom: 40px;
		transform: none !important; opacity: 1 !important; will-change: auto;
	}
	[class*="-hero__cue"] { display: none; }

	/* Capitoli: restano pinnati e in dissolvenza, ma ogni fotogramma è una
	   colonna allineata in alto, subito sotto la testata — foto intera, poi etichetta, titolo e
	   testo. `JS` e altezza della sezione non cambiano. */
	.lb-cap {
		display: flex; flex-direction: column; justify-content: flex-start;
		padding: 72px 0 28px;
	}
	.lb-cap__foto { position: relative; inset: auto; flex: none; height: auto; }
	.lb-cap__foto,
	.lb-cap__foto .elementor-widget,
	.lb-cap__foto .jet-listing-dynamic-image { height: auto; }
	.lb-capitoli .lb-cap__foto img {
		height: auto; max-height: 46svh; object-fit: contain;
		transform: none; transition: none;
	}
	.lb-cap::after { display: none; }
	.lb-cap__testo { position: static; padding: 22px var(--lb-margine) 0; }
	.lb-cap__paragrafo p { max-width: none; }

	/* Immagine a tutta larghezza: editoriale (`.lb-bleed`, nessun testo) e
	   immersiva (`.lb-im-bleed`, con la frase che passa SOTTO la foto). */
	.lb-bleed img {
		height: auto; max-height: 70svh; object-fit: contain;
	}
	.lb-bleed::after, .lb-im-bleed::after { display: none; }
	.lb-im-bleed { height: auto; overflow: visible; }
	.lb-im-bleed > .elementor-widget-jet-listing-dynamic-image {
		position: relative; inset: auto; height: auto; will-change: auto;
		transform: none !important;
	}
	.lb-im-bleed .jet-listing-dynamic-image { height: auto; }
	.lb-im-bleed .jet-listing-dynamic-image img, .lb-im-bleed img {
		height: auto; max-height: 70svh; object-fit: contain;
	}
	.lb-im-bleed__frase {
		position: static; inset: auto; justify-content: flex-start; text-align: left;
		padding: 26px var(--lb-margine) 8px;
	}

	/* I pezzi. Il grande vuoto nero veniva dalla pista alta 100svh con le
	   schede centrate (≈ 260px sopra e sotto). Qui la pista è alta quanto serve
	   (`--lb-pz-h`: occhiello + scheda + didascalia) e si incolla al centro della
	   finestra (`top` = metà della differenza). `comportamenti.js` misura la corsa
	   sull'altezza della pista e non su quella della finestra, quindi la sezione
	   scorre via appena il carosello è finito: nessuna pausa morta agli estremi. */
	.lb-pezzi { --lb-pz-h: min(470px, 100svh); min-height: var(--lb-pz-h); }
	.lb-pezzi__pista { height: var(--lb-pz-h); top: calc((100svh - var(--lb-pz-h)) / 2); }
	.lb-pezzi__occhiello { top: 22px; }
	/* Didascalie: nel flex il testo non andava a capo e usciva dalla scheda,
	   accavallandosi alla successiva. */
	.lb-pezzo__meta > .elementor-widget-container, .lb-pezzo__meta .jet-listing-dynamic-field__content {
		min-width: 0; overflow-wrap: anywhere; white-space: normal;
	}
	.lb-pezzo__meta .jet-listing-dynamic-field__content { font-size: clamp(1rem, 4.6vw, 1.2rem); }
}

/* =====================================================================
   SCHEDA SERVIZIO — le sette pagine dedicate
   ---------------------------------------------------------------------
   Misure dal blocco `<style>` di `lion-brand-servizio-packaging.html`,
   l'unica delle sette costruita nel prototipo. Le altre sei seguono lo
   stesso template: quello che le rende diverse non è il CSS, è **la
   regola per cui una sezione senza contenuto non compare** (decisione
   26) — in fondo a questo blocco.

   Tre pezzi arrivano già fatti e non si riscrivono: `.lb-eyebrow`,
   `.lb-indietro` e la **card del portfolio**, che la griglia dei progetti
   collegati riusa così com'è. Il **processo** pure: `dev-servizi` aveva
   lasciato in `pagine.css` l'innesto `--righe` con la resa pronta e
   l'istruzione su dove metterlo. Qui non se ne riscrive una riga.
   ===================================================================== */

/* La stessa lettura stretta delle schede progetto: 1240, non i 1360 della
   home. È una scelta del design system per le pagine editoriali. */
.single-servizio .e-con-boxed > .e-con-inner { max-width: 1240px; }
.single-servizio .lb-sezione { padding-block: clamp(60px, 9vw, 120px); }

/* --- Apertura: testo a sinistra, fotografia a destra --------------------
   Non è l'apertura a tutta finestra delle schede progetto. Qui il prototipo
   mette una griglia 1.2fr/.8fr: il servizio si presenta leggendo, non
   guardando. */
/* **G2-20: `align-items: center` era dichiarato e non si applicava.**
   Elementor emette `.e-con.e-flex > .e-con-inner { align-items: var(--align-items) }`,
   che e' **(0,3,0)** contro i nostri **(0,2,0)**: vince lei. E siccome
   `--align-items` non e' definita, `var()` non risolve e la proprieta' cade sul
   valore iniziale — **`normal`**. Il calcolato diceva `normal` mentre il foglio
   diceva `center`, ed e' per questo che leggendo il file sembrava a posto.

   Risultato a schermo: la seconda colonna ancorata in alto e la prima stirata a
   tutta altezza. Misurato su Packaging a 1873: `h 700 · vuoto sotto 0` e
   `h 531 · vuoto sotto 169`. Con la variabile definita: `h 345` e `h 531`,
   centrate, con lo spazio diviso sopra e sotto.

   **Non si alza la specificita': si definisce la variabile che Elementor legge.**
   La sua regola diventa cosi' quella che fa la cosa giusta, invece di essere
   scavalcata — nessuna guerra di selettori e nessun `!important`. La
   dichiarazione diretta resta come ripiego se un giorno quella regola sparisse.

   Il valore e' quello del prototipo, verificato: `#pk .phero { ... align-items:
   center }`. La regola era gia' giusta, semplicemente non arrivava. */
.lb-ss-hero > .e-con-inner {
	display: grid; grid-template-columns: 1.2fr .8fr;
	gap: clamp(30px, 5vw, 70px);
	--align-items: center; align-items: center;
	min-height: 92svh; padding-block: clamp(96px, 14vh, 150px) clamp(50px, 8vh, 90px);
}
.lb-ss-hero__occhiello {
	font-size: .66rem; text-transform: uppercase; letter-spacing: .3em; color: var(--lb-tenue);
}
.lb-ss-hero__titolo h1 {
	font-family: var(--lb-extrawide);
	font-weight: 700; text-transform: uppercase; letter-spacing: -.02em; line-height: .92;
	/* Giro 6 (decisione 276): era clamp(1.8rem, 4vw, 3.4rem). Con «Packaging & Cartotecnica»
	   la parola CARTOTECNICA sbordava di 75 px sotto la foto a 1440 e di 31 px a 390: il corpo
	   scende quanto basta perché la parola più lunga dei sei nomi stia nella colonna. */
	color: var(--lb-titolo); font-size: clamp(1.55rem, 3.2vw, 3rem); margin: 16px 0 0;
}
.lb-ss-hero__frase p {
	margin-top: 26px; font-size: clamp(1.05rem, 1.6vw, 1.35rem);
	color: var(--lb-testo); max-width: 34ch;
}
.lb-ss-hero__nota p { margin-top: 16px; color: var(--lb-tenue); max-width: 38ch; }
.lb-ss-hero__media {
	position: relative; border-radius: 0; overflow: hidden;
	border: 1px solid var(--lb-filo); aspect-ratio: 4/5; padding: 0;
	box-shadow: 0 40px 90px rgba(0,0,0,.5);
}
/* 112% e non 100%: l'eccedenza è lo spazio in cui la parallasse si muove. */
.lb-ss-hero__media .jet-listing-dynamic-image,
.lb-ss-hero__media .elementor-widget-jet-listing-dynamic-image { height: 100%; }
/* **`width` sta in una regola più pesante perché quella qui sotto perdeva, e la
   conseguenza si vedeva.** `.lb-ss-hero__media img` pesa (0,1,1) e non bastava:
   l'immagine usciva **592 px in una cornice da 425**, cioè 169 px oltre il
   proprio contenitore. La cornice ritaglia (`overflow: hidden` due righe sopra),
   quindi non sbordava dallo schermo — ma il ritaglio cadeva **tutto a destra**,
   e a schermo si vedeva la metà sinistra della foto invece del centro.

   `height: 112%` invece passava, e questo è il dettaglio che smaschera il
   problema: **una regola sola con due dichiarazioni, una che vince e una che
   perde.** Vuol dire che a batterci non è l'ordine di caricamento — è un'altra
   regola che dichiara **solo** `width`. Con l'altezza applicata e la larghezza
   no, l'immagine prendeva la larghezza dal proprio rapporto intrinseco: infatti
   usciva 592×592 esatti, quadrata come la sorgente.

   Provato per gradi sul sito servito: (0,1,1) → 592 px, **(0,2,1) → 423 px**.
   Si riusa `.jet-listing-dynamic-image`, che questo blocco cita già due righe
   sopra: nessuna dipendenza nuova dal plugin, e nessun `!important` per una
   cosa che si risolve dichiarando meglio.

   Con la larghezza giusta l'eccedenza torna a essere **verticale** — 592 di
   altezza contro 531 di cornice — che è esattamente lo spazio della parallasse
   descritto qui sopra, e `object-fit: cover` ritaglia **centrato** come deve. */
.lb-ss-hero__media .jet-listing-dynamic-image img {
	width: 100%;
}
.lb-ss-hero__media img {
	width: 100%; height: 112%; object-fit: cover; display: block; max-width: none;
	will-change: transform;
}
/* La pillola credita il progetto vero da cui viene la foto. */
.lb-ss-hero__pillola {
	position: absolute; left: 16px; bottom: 16px; z-index: 2; width: auto;
	font-size: .6rem; text-transform: uppercase; letter-spacing: .16em; color: #fff;
	background: rgba(10,9,8,.55); backdrop-filter: blur(6px);
	padding: 8px 13px; border-radius: 0;
}

/* --- Cosa realizziamo --- */
.lb-ss-kinds {
	border-top: 1px solid var(--lb-filo);
	padding-block: clamp(30px, 6vw, 80px) clamp(60px, 9vw, 120px);
}
.lb-ss-kinds__grande p {
	font-family: var(--lb-extrawide);
	font-weight: 700; text-transform: uppercase; letter-spacing: -.01em;
	color: var(--lb-titolo); font-size: clamp(1.5rem, 4.4vw, 3.4rem); line-height: 1.06;
	text-wrap: balance; margin: 22px 0 0;
}
/* Il prototipo usa `<b>`; qui la parte fra **doppi asterischi** diventa `<em>`
   via `lb_evidenzia()`, come su tutto il resto del sito. */
.lb-ss-kinds__grande em { color: var(--lb-arancio); font-style: normal; font-weight: 700; }
.lb-ss-kinds__nota p { margin-top: 22px; color: var(--lb-tenue); max-width: 56ch; }

/* --- Due anime, un oggetto --- */
.lb-ss-dittico {
	display: grid; grid-template-columns: 1fr 1fr;
	gap: clamp(20px, 3vw, 40px); margin-top: 52px;
}
.lb-ss-anima { position: relative; padding: 0; }
.lb-ss-anima__foto {
	position: relative; border-radius: 0; overflow: hidden;
	border: 1px solid var(--lb-filo); aspect-ratio: 4/5; padding: 0;
}
.lb-ss-anima__foto .jet-listing-dynamic-image,
.lb-ss-anima__foto .elementor-widget-jet-listing-dynamic-image { height: 100%; }
.lb-ss-anima__foto img {
	width: 100%; height: 100%; object-fit: cover; display: block; max-width: none;
	transition: transform .8s cubic-bezier(.16,1,.3,1);
}
.lb-ss-anima:hover .lb-ss-anima__foto img { transform: scale(1.05); }
.lb-ss-anima__etichetta {
	position: absolute; left: 16px; top: 16px; z-index: 2; width: auto;
	font-size: .62rem; text-transform: uppercase; letter-spacing: .2em; font-weight: 700;
	color: var(--lb-arancio); background: rgba(10,9,8,.5); backdrop-filter: blur(6px);
	padding: 8px 13px; border-radius: 0;
}
.elementor-widget.lb-ss-anima__etichetta .elementor-heading-title {
	font-size: inherit; letter-spacing: inherit; line-height: 1; color: inherit;
	font-family: var(--lb-regular); font-weight: 700; text-transform: uppercase; margin: 0;
}
.elementor-widget.lb-ss-anima__numero .elementor-heading-title {
	font-family: var(--lb-extrawide); font-weight: 700; line-height: 1.55;
	color: var(--lb-arancio); font-size: .9rem; margin: 22px 0 0; letter-spacing: normal;
}
/* Indipendente dal tag: è il titolo del dittico delle sette schede servizio, ed
   è quello che `dev-wp` promuove ad `h2` per chiudere il salto `h1`→`h3`.
   Col selettore su `h3` il corpo sarebbe sceso da 36 a 24 px — misurato.
   Vedi la nota su `:is()` più sopra. */
.lb-ss-anima__titolo :is(h1, h2, h3, h4, h5, h6) {
	font-family: var(--lb-extrawide);
	font-weight: 700; text-transform: uppercase; letter-spacing: -.01em;
	color: var(--lb-titolo); font-size: clamp(1.5rem, 2.8vw, 2.2rem); line-height: 1.02;
	margin: 8px 0 0;
}
.lb-ss-anima__testo p { color: var(--lb-tenue); margin-top: 14px; max-width: 42ch; }

/* La frase che tiene insieme le due anime. È struttura, non contenuto: dice il
   posizionamento del brand — chi progetta e chi produce sotto lo stesso tetto —
   e non cambia da servizio a servizio.
   ⚠️ Ma oggi parla di packaging, e su sette schede sei parlerebbero di un
   oggetto che non hanno. Va resa neutra prima del lancio: è lavoro di `copy`,
   ed è già in `PRIMA-DELLA-PUBBLICAZIONE.md`. */
.lb-ss-giunzione {
	display: flex; flex-wrap: wrap; align-items: baseline;
	gap: clamp(20px, 4vw, 60px);
	margin-top: clamp(40px, 6vw, 70px);
	border-top: 1px solid var(--lb-filo);
	padding-top: clamp(34px, 4vw, 50px);
}
.elementor-widget.lb-ss-giunzione__titolo .elementor-heading-title {
	font-family: var(--lb-extrawide);
	font-weight: 700; text-transform: uppercase; letter-spacing: -.01em;
	color: var(--lb-titolo); font-size: clamp(1.4rem, 3vw, 2.4rem); line-height: 1.02;
	max-width: 14ch; margin: 0;
}
.lb-ss-giunzione__titolo em { color: var(--lb-arancio); font-style: normal; }
.lb-ss-giunzione__testo { flex: 1; min-width: 260px; }
.lb-ss-giunzione__testo p { color: var(--lb-tenue); max-width: 44ch; margin: 0; }

/* --- Materiali & finiture: il carosello che si trascina ------------------
   Non è il carosello pinnato della scheda immersiva. Qui il prototipo usa un
   `overflow-x` con trascinamento del mouse: **non ruba lo scroll verticale**,
   e col dito si scorre come qualunque altra lista. Sono due meccanismi diversi
   per due pagine diverse, e la differenza è deliberata. */
.lb-ss-mats { border-top: 1px solid var(--lb-filo); }
.lb-ss-mats__testa {
	display: flex; justify-content: space-between; align-items: flex-end;
	gap: 20px; flex-wrap: wrap;
}
.lb-ss-mats__lead p {
	font-family: var(--lb-extrawide);
	font-weight: 700; text-transform: uppercase; letter-spacing: -.01em;
	color: var(--lb-titolo); font-size: clamp(1.5rem, 3.6vw, 2.7rem); line-height: 1.06;
	text-wrap: balance; max-width: 20ch; margin: 16px 0 0;
}
.lb-ss-mats__lead em { color: var(--lb-arancio); font-style: normal; }
.elementor-widget.lb-ss-mats__suggerimento .elementor-heading-title {
	font-family: var(--lb-regular); font-weight: 400;
	font-size: .64rem; text-transform: uppercase; letter-spacing: .14em;
	color: var(--lb-tenue); line-height: 1.55; margin: 0;
}
/* La pista è la griglia di JetEngine, portata a riga scorrevole. Il `max-width`
   che JetEngine calcola sulle colonne va tolto: un massimo non si vince con una
   larghezza. Stessa medicina di `.lb-galleria__griglia` e `.lb-lv-griglia`. */
.lb-ss-mats__pista .jet-listing-grid__items {
	display: flex; flex-wrap: nowrap; align-items: stretch;
	gap: clamp(14px, 1.6vw, 22px); margin: 0;
	padding: 44px 2px 6px;
	overflow-x: auto; cursor: grab;
	scrollbar-width: none; user-select: none; -webkit-user-select: none;
	-webkit-overflow-scrolling: touch;
	/* **G2-21: la sfumatura c'era solo a destra, e a sinistra il contenuto veniva
	   tagliato di netto.** Fabrizio: «quando si scrolla la parte di sx che scorre
	   viene tagliata, va aggiunta una sfumatura anche lì come l'hai messa a
	   destra» — e ha aggiunto: **«era sbagliato anche nel prototipo»**. È uno dei
	   due punti del giro in cui il prototipo non fa testo e si corregge.

	   Le due sfumature non sono fisse: le larghezze arrivano da
	   `--lb-sfuma-sx` e `--lb-sfuma-dx`, che `comportamenti.js` aggiorna a ogni
	   scorrimento. Una sfumatura a sinistra sempre accesa mangerebbe il bordo del
	   **primo** materiale anche da fermo, quando a sinistra non c'è niente di
	   nascosto: si accende solo quando qualcosa è davvero uscito da quel lato, e
	   quella a destra si spegne quando si è arrivati in fondo. È la stessa
	   informazione che dà una barra di scorrimento, che qui è nascosta. */
	--lb-sfuma-sx: 0px;
	--lb-sfuma-dx: 40px;
	-webkit-mask-image: linear-gradient(90deg, transparent 0, #000 var(--lb-sfuma-sx), #000 calc(100% - var(--lb-sfuma-dx)), transparent 100%);
	mask-image: linear-gradient(90deg, transparent 0, #000 var(--lb-sfuma-sx), #000 calc(100% - var(--lb-sfuma-dx)), transparent 100%);
	transition: --lb-sfuma-sx .2s, --lb-sfuma-dx .2s;
}
.lb-ss-mats__pista .jet-listing-grid__items::-webkit-scrollbar { display: none; }
.lb-ss-mats__pista .jet-listing-grid__items.lb-trascina { cursor: grabbing; }
.lb-ss-mats__pista .jet-listing-grid__items > .jet-listing-grid__item {
	flex: 0 0 clamp(230px, 26vw, 320px); max-width: none; margin: 0; padding: 0;
	scroll-snap-align: start;
}
.lb-ss-mats__pista .jet-listing-grid__item > .elementor { height: 100%; }
.lb-ss-mat {
	border: 1px solid var(--lb-filo); border-radius: 0;
	padding: clamp(22px, 2.4vw, 32px);
	background: linear-gradient(180deg, var(--lb-superficie), #0e0b09);
	min-height: 230px; height: 100%;
	justify-content: space-between;
	transition: border-color .3s ease, transform .3s ease;
}
.lb-ss-mat:hover {
	border-color: color-mix(in srgb, var(--lb-arancio) 45%, var(--lb-filo));
	transform: translateY(-4px);
}
.lb-ss-mat__etichetta {
	font-family: var(--lb-extrawide); font-weight: 700;
	color: var(--lb-arancio); font-size: .82rem; font-variant-numeric: tabular-nums;
}
.lb-ss-mat__titolo h3 {
	font-family: var(--lb-extrawide);
	font-weight: 700; text-transform: uppercase; letter-spacing: -.01em;
	color: var(--lb-titolo); font-size: 1.15rem; line-height: 1.04; margin: 14px 0 0;
}
.lb-ss-mat__testo p { color: var(--lb-tenue); font-size: .92rem; margin: 10px 0 0; }

/* --- Come nasce: la resa arriva da `pagine.css` -------------------------
   `.lb-processo-innesto--righe` è l'innesto lasciato pronto da chi ha fatto la
   pagina Servizi. Qui c'è solo ciò che è di questa sezione. */
.lb-ss-proc { border-top: 1px solid var(--lb-filo); }
.lb-ss-proc__lead p {
	color: var(--lb-tenue); max-width: 50ch; margin: 16px 0 0; font-size: 1.05rem;
}

/* --- Progetti di [servizio] ---------------------------------------------
   La card è quella del portfolio, riusata: stesso oggetto, stessa resa, e non
   se ne mantiene una seconda. Ma **il prototipo qui la ritaglia 4/3, non 4/5**
   come nel portfolio, e non è una svista: in una pagina di servizio i progetti
   sono una prova a corredo, non la vetrina, e una card più bassa lo dice.

   Misurato: la card passa da 769px a 481, cioè da +288 a +0 rispetto al
   prototipo. Su due righe erano **577 px**, l'80% dello scostamento di tutta la
   pagina. Restano +14 a card di riquadro sotto, dove la card del portfolio
   manda Settore e Tipologia a capo e il prototipo li tiene su una riga: quello
   è del blocco condiviso e non si tocca da qui. */
.lb-ss-progetti { border-top: 1px solid var(--lb-filo); }
.lb-ss-progetti__griglia .lb-card__foto { aspect-ratio: 4/3; }
/* **G2-24: «le immagini non coprono l'intera area, devono essere su cover».**
   `object-fit: cover` c'era già ed era giusto — a non coprire era l'**elemento**:
   misurato **328×410 dentro un riquadro di 547×410**, cioè 219 px di area
   scoperta a destra. `object-fit` ritaglia il contenuto dentro la scatola
   dell'immagine; se la scatola è più stretta del riquadro, non ha niente da
   riempire.

   È la stessa famiglia del velo di `/lavori/` e delle frecce ovali: un elemento
   che non riceve una larghezza e se la prende dal proprio contenuto. Qui la
   larghezza gliela si dà. */
.lb-ss-progetti__griglia .lb-card__foto img {
	width: 100%; height: 100%; object-fit: cover;
}
/* **La griglia usciva dalla sezione e finiva dentro il piede**, di 80px a 390.
   Non si vedeva a 1440 e non lo diceva nessun controllo: l'oracolo dava −144 su
   questa sezione, un numero che sembrava un artefatto di misura e invece era il
   pezzo che mancava.

   È lo stesso inciampo di `.lb-lv-risultati`, terzo caso: Elementor mette
   `height: 100%` su `.e-con-inner`, che dentro un Container di altezza
   automatica è una percentuale senza riferimento — e **la griglia di JetEngine è
   l'unico blocco del sito abbastanza alto da farlo emergere**. Qui il
   contenitore non deve distribuire niente: ha l'occhiello e la griglia, uno
   sotto l'altro. `block` è anche la cosa giusta, non solo quella che funziona. */
.lb-ss-progetti > .e-con-inner,
.lb-ss-mats > .e-con-inner { display: block; }
.lb-ss-progetti__griglia .jet-listing-grid__items {
	display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
	/* G6-E2: fra le file come nella griglia Lavori; era clamp(18px, 2.4vw, 34px) nei due sensi. */
	gap: clamp(40px, 4vw, 56px) clamp(18px, 2.4vw, 34px); margin: 48px 0 0; width: auto;
}
.lb-ss-progetti__griglia .jet-listing-grid__items > .jet-listing-grid__item {
	max-width: none; flex: none; padding: 0; min-width: 0;
}

/* --- Sezione senza contenuto, sezione assente ---------------------------
   È la regola che rende le sette schede diverse fra loro **senza sette
   template**. Non si decide a tavolino quali servizi «meritano» quale blocco:
   ogni blocco narrativo è un campo opzionale, e se il contenuto non c'è la
   sezione sparisce.

   Il caso concreto, dal content model §7: «Progettazione grafica» perde due
   blocchi su tre — non ha finiture da elencare perché non è un manufatto, e non
   ha una griglia di progetti perché è stata esclusa dalla tassonomia Tipologia
   (§2), dove non discriminava. Non sono buchi: è un servizio che si struttura
   diversamente dagli altri sei, e la pagina lo mostra invece di riempirla di
   contenuto inventato.

   ⚠️ Il content model dichiara il caso «**entrambi** i testi del dittico
   vuoti». Non dichiara cosa fare quando ne manca **uno solo**: qui resta
   visibile con la sola fotografia, che è ciò che la specifica permette. Se non
   è quello che si vuole, è una decisione di `content`, non mia. */
.lb-ss-souls:not(:has(.lb-ss-anima__testo .jet-listing-dynamic-field__content)) { display: none; }
.lb-ss-mats:not(:has(.jet-listing-grid__item)) { display: none; }
/* **G2-23: le pillole non erano «graficamente diverse», erano senza stile.**
   Misurate in Chrome vero: corpo 16px, `letter-spacing: normal`, minuscolo e
   **colore `rgb(0,0,238)`** — il blu di sistema. Nessuna regola del tema le
   raggiungeva: le tre esistenti qui sopra vestono la foto e la griglia, non i
   termini. Il prototipo (`#pk .pcard .c`) le vuole testo semplice.

   **E il secondo pezzo, che vale quanto il primo: sono tre, il prototipo ne
   mostra due.** Il markup stampa `--settore` (un termine), `--tipologia`
   (tutti i termini, separati da virgola) e `--tipologia-una` (solo il primo).
   La coppia giusta è settore + una tipologia, unite dal punto medio; quella coi
   termini multipli si nasconde. Il colore si dà allo `span` di JetEngine e non
   al contenitore, perché è lo `span` a portare il blu. */
.lb-ss-progetti__griglia .lb-card__tag {
	flex-direction: row; align-items: baseline; gap: 0; margin-top: 0;
}
/* G6-E2: stesso difetto della griglia Lavori (righe alte uguali): qui titolo → etichette era
   58,8 px sopra i 900. Righe alte quanto il contenuto, passo 8 px, e il margine sopra i tag
   (era 8) passa a zero perché il passo lo fa già la griglia. */
.lb-ss-progetti__griglia .e-con.elementor-element.lb-card__meta {
	grid-auto-rows: auto; grid-template-rows: none; align-content: start;
	--gap: 8px 8px; --row-gap: 8px; --column-gap: 8px;
}
.lb-ss-progetti__griglia .lb-card__titolo { align-self: start; }
.lb-ss-progetti__griglia .lb-tag--tipologia { display: none; }
/* `--tipologia-una` è spenta da una regola generale (riga 1078) e va riaccesa
   qui, come fa già `.lb-card-compatta-dentro`. Nasconderne una senza accendere
   l'altra lasciava **un solo termine e un punto medio appeso**, che è peggio
   di prima: l'avevo caricato così e si vedeva. */
.lb-ss-progetti__griglia .lb-tag--tipologia-una { display: block; }
.lb-ss-progetti__griglia .lb-tag,
.lb-ss-progetti__griglia .lb-tag .jet-listing-dynamic-terms__link {
	color: var(--lb-tenue);
	font-size: .6rem; text-transform: uppercase; letter-spacing: .14em;
	text-decoration: none;
}
.lb-ss-progetti__griglia .lb-tag--settore::after {
	content: "·"; margin: 0 .5em; color: var(--lb-tenue);
}

.lb-ss-progetti:not(:has(.lb-card)) { display: none; }

/* **G4-06 e G4-08: le pastiglie stavano più vicine al progetto DOPO che al
   proprio titolo.** Stessa segnalazione su due pagine, chiusa in una passata
   sola come chiedeva il triage. Fabrizio: «le pills sono troppo distanti dal
   titolo del progetto e troppo vicine al progetto successivo confondendo
   l'attribuzione».

   Misurato su iPhone 15, dal **testo** del titolo (non dal riquadro del widget,
   che è più alto del testo e nasconde il difetto):

       /lavori/              testo → pastiglia  49    pastiglia → card dopo  18
       /servizio/packaging/  testo → pastiglia  46    pastiglia → card dopo  18

   Quasi tre volte più lontane dal titolo che le raccontano di quanto lo siano
   dalla fotografia che non c'entra niente. **L'occhio attribuisce per
   prossimità**, quindi le legge sul progetto sbagliato: è esattamente ciò che
   Fabrizio descrive.

   I 49 px erano tre cose sovrapposte, e nessuna era la spaziatura che si
   sarebbe cercata per prima. `.lb-card__meta` è una **griglia** con
   `gap: 20px`; sopra ci sono gli 11 px di `margin-top` della riga 1432; e il
   widget del titolo veniva **stirato** dalla riga della griglia a 34,6 px pur
   contenendo un testo alto 17. `align-self: start` toglie lo stiramento, il
   resto lo fanno le due spaziature.

   **Il divisore lo aveva chiesto lui e serve davvero**, perché avvicinare le
   pastiglie da solo lascia le due schede appoggiate a 18 px l'una dall'altra:
   la spaziatura sotto porta la distanza a 45 e la riga la rende una separazione
   dichiarata invece che dedotta. Esito, stessa sonda:

       testo → pastiglia   49 → 11        pastiglia → card dopo   18 → 45

   **Le due larghezze sono diverse perché lo sono le due griglie**, e sono
   quelle già dichiarate in questo foglio, non numeri nuovi: Lavori diventa a
   colonna singola a **560** (riga 3410), i progetti correlati di una scheda
   servizio a **900** (riga 3120). La confusione nasce solo a colonna singola —
   affiancate, le schede si attribuiscono da sé — quindi la correzione vive
   esattamente nella fascia in cui esiste il difetto, e il desktop approvato non
   cambia di un pixel.

   ⚠️ **Quattro classi e non due, e non è pignoleria.** Il `--gap` della griglia
   lo scrive Elementor su `.elementor-NN .elementor-element.elementor-element-lbcmeta`,
   che è **(0,3,0)**. Provato prima per iniezione in fondo al `<head>`, dove
   `.lb-lv-griglia .lb-card__meta` **sembrava** vincere: vinceva per ordine
   (trappola 2). Da qui il gradino vero. */
@media (max-width: 560px) {
	/* G6-E2: passo 8 come sopra (era 4), e più aria fra un progetto e l'altro: 34 + 24 (era 26 + 18). */
	.lb-lv-griglia .e-con.elementor-element.lb-card__meta { --gap: 8px 8px; --row-gap: 8px; --column-gap: 8px; }
	.lb-lv-griglia .lb-card__titolo { align-self: start; }
	.lb-lv-griglia .lb-card__tag { margin-top: 0; }
	.lb-lv-griglia .jet-listing-grid__items { row-gap: 24px; }
	.lb-lv-griglia .jet-listing-grid__items > .jet-listing-grid__item:not(:last-child) {
		padding-bottom: 34px;
		border-bottom: 1px solid var(--lb-filo);
	}
}
@media (max-width: 900px) {
	/* G6-E2: passo 8 (era 4) e 34 + 24 fra un progetto e l'altro a colonna singola. */
	.lb-ss-progetti__griglia .e-con.elementor-element.lb-card__meta { --gap: 8px 8px; --row-gap: 8px; --column-gap: 8px; }
	.lb-ss-progetti__griglia .lb-card__titolo { align-self: start; }
	.lb-ss-progetti__griglia .lb-card__tag { margin-top: 0; }
	.lb-ss-progetti__griglia .jet-listing-grid__items { row-gap: 24px; }
	.lb-ss-progetti__griglia .jet-listing-grid__items > .jet-listing-grid__item:not(:last-child) {
		padding-bottom: 34px;
		border-bottom: 1px solid var(--lb-filo);
	}
}

@media (max-width: 900px) {
	.lb-ss-hero > .e-con-inner { grid-template-columns: 1fr; min-height: 0; gap: 30px; }
	.lb-ss-hero__media { aspect-ratio: 16/11; }
	.lb-ss-dittico { grid-template-columns: 1fr; gap: 40px; }
	.lb-ss-progetti__griglia .jet-listing-grid__items { grid-template-columns: minmax(0, 1fr); }
}
@media (prefers-reduced-motion: reduce) {
	/* Senza parallasse l'eccedenza del 12% non serve, e lascerebbe la
	   fotografia tagliata in basso invece che centrata. */
	.lb-ss-hero__media img { height: 100%; transform: none !important; }
	.lb-ss-anima__foto img { transition: none; }
	.lb-ss-mat { transition: none; }
}

/* =====================================================================
   PAGINA LAVORI — la griglia filtrata
   ---------------------------------------------------------------------
   Misure prese da `lion-brand-lavori.html`, non inventate: il blocco
   `<style>` in testa a quel file è la specifica di questa pagina.

   Sta in `tema.css` e non in `pagine.css` per un motivo di proprietà — il
   secondo file è di `dev-servizi` — ma anche perché due dei tre pezzi qui
   sotto non sono di questa pagina: i chip di JetSmartFilters e lo stato
   vuoto serviranno a ogni elenco filtrabile che verrà dopo. Quello che è
   davvero solo di qui porta il prefisso `.lb-lv-`.
   ===================================================================== */

/* L'altezza dell'intestazione, e perché serve un valore.
   Nel prototipo di QUESTA pagina `.top` è `position:sticky` e occupa flusso:
   spinge giù il contenuto di 66px (24 di padding sopra, 23 di logo, 18 sotto,
   1 di filo). In WordPress l'intestazione è `fixed` — deve esserlo, o l'hero a
   tutta altezza di quattro pagine su otto smette di riempire lo schermo — e non
   occupa niente. Senza questo valore l'occhiello «Lavori» finirebbe sotto il
   logo, e la barra dei filtri si fermerebbe dietro l'intestazione.

   79px è l'altezza misurata dell'intestazione di questo sito sopra i 600px (24
   di padding, 37 di pulsante, 18 sotto) — sotto i 600 il pulsante «Contattaci»
   sparisce e restano 66. Si tiene il valore **maggiore** perché senza
   JavaScript riservare troppo lascia un po' d'aria, riservare troppo poco
   mette il contenuto sotto l'intestazione. È un ripiego, non la verità: quella la
   scrive `comportamenti.js`, che misura l'intestazione vera e riscrive questa
   variabile — anche al ridimensionamento. Serve perché l'altezza dipende da
   quanto è lungo il testo dei pulsanti, e quello il cliente lo cambia dalla
   bacheca: un numero scritto qui invecchierebbe in silenzio. Senza JavaScript
   restano questi 73px, che oggi sono giusti. */
:root { --lb-alt-top: 79px; }

/* --- Hero compatto ---
   Titolo a sinistra, riga di testo a destra, allineati sul basso. */
/* Il contenitore di questa pagina è 1300px, non i 1360 del kit.
   Il design system (§ «Container width») registra tre misure e dice che sono
   intenzionali per ruolo di pagina: 1360 la home, 1240 le editoriali, e **1300
   il default condiviso di `site.css`, che `lavori` e `servizi` ereditano senza
   ridichiararlo**. Il kit porta il valore della home perché la home è stata la
   prima pagina costruita; qui si scende ai 1300 che questa pagina ha davvero.
   Senza, il titolo dell'hero sta su due righe invece di tre e le card sono
   20px più larghe di quelle del prototipo. Stessa scelta di `.lb-sv` e
   `.lb-cs` in `pagine.css`: eccezione per tipo di pagina, non kit riscritto. */
.lb-lv-hero.e-con-boxed > .e-con-inner,
.lb-lv-filtri.e-con-boxed > .e-con-inner,
.lb-lv-risultati.e-con-boxed > .e-con-inner { max-width: 1300px; }

.lb-lv-hero { padding-block: calc(var(--lb-alt-top) + clamp(30px, 5.5vw, 80px)) clamp(4px, 1.4vw, 14px); }
.lb-lv-hero__riga { gap: clamp(18px, 4vw, 60px); }
.lb-lv-hero__sinistra { max-width: 60%; }
.lb-lv-hero__titolo .elementor-heading-title {
	/* **ExtraWide, non Regular.** Il titolo di questa pagina non dichiara una
	   famiglia nel suo blocco `<style>`: eredita `--font-head` di `site.css`,
	   che è ExtraWide. È l'opposto dei titoli di sezione della home, che usano
	   `--font-body` — e non è una svista del prototipo, è la distinzione della
	   decisione 14 (titoli ExtraWide, testi Regular).
	   Con la famiglia sbagliata il titolo sta su due righe invece di tre e
	   l'apertura della pagina cambia faccia: stessa dimensione, altra pagina. */
	font-family: var(--lb-extrawide);
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: -.01em;
	color: var(--lb-titolo);
	font-size: clamp(1.65rem, 4.2vw, 3.3rem);
	line-height: 1;
	margin: 12px 0 0;
}
/* Il prefisso `.elementor-widget-text-editor` non è pedanteria: Elementor stampa
   la tipografia del kit sui propri widget DOPO questo foglio e con la stessa
   specificità di una regola di classe. È la causa comune di tre difetti già
   trovati sulla home, e qui il testo verrebbe 16px invece di 15. */
.elementor-widget-text-editor.lb-lv-hero__lead p {
	color: var(--lb-tenue);
	font-size: clamp(.92rem, 1.15vw, 1.05rem);
	/* **Senza questa riga l'interlinea si eredita come LUNGHEZZA.** Il genitore ha
	   `line-height: 1.55` con corpo 16px, quindi passa ai figli **24,8px fissi**;
	   qui il corpo scende a 14,72px e il testo esce a 24,8 invece di 22,8 — due
	   righe, 4px in più. Non si vede a occhio e non fa suonare niente: l'ha
	   trovato `MARGINE_SOTTILE`, segnalando che il confronto passava con 0px di
	   margine su 24. Ridichiararla qui la fa tornare un fattore del PROPRIO corpo. */
	line-height: 1.55;
	max-width: 40ch;
	margin: 0 0 6px;
}
@media (max-width: 767px) {
	.lb-lv-hero__sinistra { max-width: none; }
	.elementor-widget-text-editor.lb-lv-hero__lead p { max-width: none; margin-top: 14px; }
}

/* --- La barra dei filtri ---
   Appiccicata sotto l'intestazione. `z-index` 90: sotto l'header (120) e sopra
   le card, com'è nel prototipo. Il fondo è velato e non pieno, o scorrendo si
   vedrebbe una fascia opaca staccata dal resto della pagina. */
.lb-lv-filtri {
	position: sticky;
	top: var(--lb-alt-top);
	z-index: 90;
	padding-block: 14px 15px;
	background: color-mix(in srgb, var(--lb-fondo) 90%, transparent);
	backdrop-filter: blur(10px);
	border-bottom: 1px solid var(--lb-filo);
}

/* =====================================================================
   G4-05 — I FILTRI A COMPARSA SU TELEFONO
   ---------------------------------------------------------------------
   **Scritta da `dev-interazioni-g4`** il 2026-08-26 e applicata qui da
   `dev-tema-g4`: il comportamento è suo, la penna su questo foglio è mia.

   Nasce da una domanda di Fabrizio, non da un difetto — *«pensiamo a un
   sistema di filtri a comparsa/scomparsa solo per il mobile?»* — dopo aver
   misurato quanto schermo si prendeva la barra appiccicata: **331,9 px su
   659, cioè il 60,5 %**, che su `/lavori/` è la pagina resa inconsultabile.

   Le classi `lb-lv-filtri--pieghevole`, `lb-lv-filtri--aperta`,
   `.lb-lv-comando`, `.lb-lv-tasto` e `.lb-lv-pannello` **le mette
   `comportamenti.js`** (funzione `lavori()`). Senza quel JavaScript qui non
   succede niente e la barra resta quella di oggi; **senza questo CSS invece
   il pulsante comparirebbe non vestito e il pannello resterebbe sempre
   aperto.** L'asimmetria è la ragione per cui il CSS è arrivato per primo.

   🔴 **Un pavimento che questa regola NON prova a scavalcare.**
   JetSmartFilters dichiara `opacity: .6 !important` sui figli e
   `pointer-events: none !important` sul contenitore durante l'attesa
   (trappola 2-bis), e il suo foglio viene **dopo** il nostro. Niente qui
   sotto lo tocca, **ed è voluto**: quello è lo stato d'attesa e deve
   continuare a funzionare anche a pannello aperto.

   ⚠️ **La specificità qui è salita per una ragione misurata, non per
   abitudine.** La prima versione aveva `.lb-lv-filtri--pieghevole` a
   `(0,1,0)`, **la stessa** di `.lb-lv-filtri` venti righe più su: provata
   iniettandola **in testa** al `<head>` ha perso per ordine e la barra chiusa
   è venuta **74 px invece di 45**. Iniettata in fondo avrebbe vinto, e sarebbe
   stata certificata una regola che in produzione perde. Le regole sul
   contenitore usano quindi la doppia classe e quelle sui discendenti il
   prefisso: **vincono per specificità, non per posizione nel file.**
   ===================================================================== */

/* Sopra i 767 px la barra deve restare **identica a com'è oggi**, e questo è
   il modo di garantirlo invece di sperarlo: il guscio del pannello non esiste
   per il layout (`display: contents`) e il pulsante non esiste affatto. Le due
   righe di pastiglie e il conteggio restano figli diretti di `.e-con-inner`
   esattamente come prima che il JavaScript li spostasse.
   **Verificato: barra 190,0 px a 1440×900 prima e dopo.** */
.lb-lv-pannello { display: contents; }
.lb-lv-comando { display: none; }

/* G6-F2 (giro 5, G5-02): il pieghevole valeva solo sotto i 767 px, e su tablet in orizzontale
   la barra incollata prendeva il 32-35% dello schermo, proprio nell'uso «in riunione» per cui il
   sito esiste. Ora vale fino a 1024 px **e su ogni schermo a tocco** (iPad in orizzontale è largo
   1133-1194 px). La stessa condizione sta in `comportamenti.js` (TELEFONO): le due devono restare
   uguali, o il pannello parte aperto dove il pulsante esiste. Il pannello dei breakpoint di
   Elementor non è toccato. */
@media ( max-width: 1024px ), ( hover: none ) and ( pointer: coarse ) {

	/* Chiusa, la barra è alta **solo quanto il suo pulsante**. Il padding di
	   14/15 px se ne va da qui e passa al pannello: se restasse sulla barra
	   costerebbe 29 px anche da chiusa, cioè il 4,4 % di schermo per del vuoto.
	   Il filo di sotto (`border-bottom: 1px`) resta: separa la barra dal
	   contenuto che le scorre sotto, e da chiusa serve più di prima. */
	.lb-lv-filtri.lb-lv-filtri--pieghevole { padding-block: 0; }

	/* 🔴 **Sopra i 767 px il pulsante è `display: none`, e NON va cambiato in
	   `visibility: hidden` né in `opacity: 0`.** Sembra una scelta di gusto e non
	   lo è.

	   Il JavaScript monta il pulsante **a ogni larghezza, desktop compreso**.
	   `display: none` lo toglie dall'ordine di tabulazione; `visibility: hidden` e
	   l'opacità **no**. Chi un giorno volesse farlo dissolvere invece che sparire
	   otterrebbe **un pulsante invisibile, raggiungibile col Tab, che apre un
	   pannello invisibile**: il fuoco entra in una cosa che non si vede.

	   **È la stessa ragione per cui il pannello, più sotto, ha `display: none`
	   vero e non l'opacità.** La condizione vale quindi in **due punti**, e una
	   modifica «estetica» a uno solo la romperebbe senza che nessuno colleghi le
	   due cose: se tocchi questa riga, guarda anche quella.

	   Trovata misurando che a 1440 px un `.click()` da codice ribalta comunque la
	   classe — **`.click()` raggiunge un elemento `display: none`, un dito e un
	   Tab no**. Oggi **non è un difetto**: è la condizione che lo tiene tale. */
	.lb-lv-filtri .lb-lv-comando { display: block; }

	/* **Il pulsante è largo quanto la barra, e non è pigrizia.**
	   L'altezza è 44 px, il minimo per un bersaglio da pollice — e sotto i 44
	   non si scende nemmeno per guadagnare schermo, perché il giro 4 non ha
	   segnalato bersagli piccoli **solo perché non li ha ancora visti**, non
	   perché non ci siano (il pulsante MENU è alto 11 px).
	   Largo tutto, il bersaglio diventa 393×44: si prende senza guardare, che
	   è la condizione in cui lo userà chi sta parlando con un cliente.

	   La tipografia è quella delle etichette `.lb-lv-etichetta` — stesso corpo,
	   stesso `letter-spacing`, stesso peso — perché **è un'etichetta anche
	   questo**, non un pulsante d'azione: dice cosa c'è sotto, non invita a
	   comprare. Vestirlo da CTA lo farebbe litigare con l'arancione dei chip. */
	.lb-lv-filtri .lb-lv-tasto {
		display: flex;
		align-items: center;
		gap: 9px;
		width: 100%;
		min-height: 44px;
		margin: 0;
		padding: 0;
		background: none;
		border: 0;
		border-radius: 0;
		color: var( --lb-titolo );
		font-family: var( --lb-regular );
		font-weight: 700;
		text-transform: uppercase;
		letter-spacing: .18em;
		font-size: .58rem;
		line-height: 1;
		text-align: left;
		cursor: pointer;
		-webkit-tap-highlight-color: transparent;
	}

	/* La stessa fermata di tastiera delle pastiglie, così i due si assomigliano
	   invece di essere due cose diverse nella stessa barra. */
	.lb-lv-filtri .lb-lv-tasto:focus-visible {
		outline: 2px solid var( --lb-arancio );
		outline-offset: 3px;
	}

	/* Il numero dei filtri accesi. Arancione pieno come la pastiglia accesa —
	   è la stessa informazione detta più corta, e deve leggersi come tale.
	   `min-width` uguale all'altezza così a una cifra è un cerchio e non un
	   ovale schiacciato; `font-variant-numeric: tabular-nums` perché passando
	   da 1 a 2 il pulsante non deve cambiare larghezza sotto il dito.
	   ⚠️ A zero sparisce, e lo nasconde il JavaScript con `hidden`: questa
	   regola non lo sa e non deve saperlo. */
	.lb-lv-filtri .lb-lv-tasto__n {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		min-width: 18px;
		height: 18px;
		padding: 0 5px;
		border-radius: 0;
		background: var( --lb-arancio );
		color: #fff;
		font-size: .6rem;
		font-weight: 700;
		letter-spacing: 0;
		line-height: 1;
		font-variant-numeric: tabular-nums;
	}
	/* `hidden` da solo perde contro `display: inline-flex` — è una regola di
	   elemento contro una di classe. Senza questa riga il pallino resterebbe
	   visibile a zero filtri, cioè il caso più comune. */
	.lb-lv-filtri .lb-lv-tasto__n[ hidden ] { display: none; }

	/* La freccia sta a destra e si gira quando il pannello è aperto: è l'unico
	   pezzo che dice «questo si apre» senza una parola in più — e Fabrizio ha
	   chiesto «Filtri e il numero, e basta». */
	.lb-lv-filtri .lb-lv-tasto__freccia {
		display: inline-flex;
		margin-left: auto;
		color: var( --lb-tenue );
		transition: transform .18s ease;
	}
	.lb-lv-filtri.lb-lv-filtri--aperta .lb-lv-tasto__freccia { transform: rotate( 180deg ); }
	@media ( prefers-reduced-motion: reduce ) {
		.lb-lv-filtri .lb-lv-tasto__freccia { transition: none; }
	}

	/* Il pannello. `display: flex` in colonna e non `block`: i margini fra le
	   righe (`.lb-lv-riga + .lb-lv-riga { margin-top: 12px }`) restano quelli, e
	   in un contesto flex **non collassano** con quelli del guscio — che in
	   `block` avrebbero potuto uscire dal pannello e cambiare la spaziatura
	   rispetto al desktop senza che nessuno l'avesse chiesto.
	   Il filo sopra separa le pastiglie dal pulsante che le comanda. */
	.lb-lv-filtri--pieghevole .lb-lv-pannello {
		display: flex;
		flex-direction: column;
		padding-block: 13px 0;
		border-top: 1px solid var( --lb-filo );
	}

	/* 🔴 **Chiuso è `display: none` VERO, e ci vuole.** Un pannello nascosto
	   con `visibility` o con l'opacità lascerebbe **dodici radio raggiungibili
	   col Tab dentro qualcosa che non si vede**: il fuoco sparisce e chi naviga
	   da tastiera non capisce dove è finito. `display: none` le toglie
	   dall'ordine di tabulazione, che è l'unica cosa corretta qui. */
	.lb-lv-filtri--pieghevole:not( .lb-lv-filtri--aperta ) .lb-lv-pannello { display: none; }

	/* Il padding di sotto torna solo quando c'è qualcosa da distanziare. */
	.lb-lv-filtri.lb-lv-filtri--aperta { padding-bottom: 15px; }

	/* 🔴 **Il conteggio: invisibile agli occhi, NON ai lettori di schermo.**
	   `.lb-lv-conta` porta `role="status"` e `aria-live="polite"`, messi su
	   G3-07③ perché tolto il ricaricamento **il filtro non annunciava più
	   niente**. Una regione live dentro un `display: none` **non annuncia**:
	   esce dall'albero di accessibilità. Quindi qui si nasconde con `clip-path`,
	   che la lascia dentro.

	   ⚠️ **È il motivo per cui non si può usare la stessa tecnica sul pannello**
	   (vedi sopra): lì dentro ci sono cose su cui il Tab si ferma, qui no — è
	   testo e basta. **Stessa esigenza, due tecniche opposte, e scambiarle
	   rompe qualcosa in entrambi i versi.**

	   La prova che serve: filtrare a pannello chiuso e sentire annunciare
	   «4 progetti». Se un giorno questa regola diventasse `display: none`,
	   l'annuncio sparirebbe **in silenzio** — che è il modo peggiore. */
	.lb-lv-filtri--pieghevole:not( .lb-lv-filtri--aperta ) .lb-lv-conta {
		position: absolute;
		width: 1px;
		height: 1px;
		margin: -1px;
		padding: 0;
		overflow: hidden;
		clip-path: inset( 50% );
		white-space: nowrap;
		border: 0;
	}
}

.lb-lv-riga + .lb-lv-riga { margin-top: 12px; }
.lb-lv-etichetta .elementor-heading-title {
	display: block;
	font-family: var(--lb-regular);
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: .18em;
	font-size: .58rem;
	color: var(--lb-tenue);
	margin: 0 0 9px;
	line-height: 1.55;
}

/* --- I chip ---
   Sono un filtro Radio di JetSmartFilters vestito, non dei pulsanti: il widget
   rende `<fieldset><label><input type=radio>`, quindi il gruppo è dichiarato, le
   frecce funzionano e uno screen reader annuncia «Settore, Hotel & Resort,
   selezionato». Il prototipo usava `<button aria-pressed>`, che non dà nessuna
   delle tre cose.

   La spaziatura NON è qui: sta nei controlli del widget («Horizontal Offset» e
   «Space Between»), così resta una manopola nel pannello. Qui c'è solo la forma,
   che il pannello non sa fare. */
.lb-chips fieldset {
	/* Il `<fieldset>` del browser porta bordo scanalato, padding e
	   `min-inline-size: min-content`. Il reset di Hello Elementor è tolto di
	   proposito (vedi functions.php): quello che serviva va ridichiarato. */
	display: flex;
	flex-wrap: wrap;
	/* 9px come nel prototipo. Lo spazio lo fa `gap` e non i due controlli di
	   spaziatura del widget, che lo esprimono come margine sulle righe: un
	   margine sotto un elemento flex alza tutta la linea, e la fila di chip
	   veniva 42px invece di 35. Il costo è che la distanza non è più una
	   manopola nel pannello — dichiarato in RAPPORTO-LAVORI.md. */
	gap: 9px;
	border: 0;
	margin: 0;
	padding: 0;
	min-inline-size: 0;
}
.lb-chips .jet-radio-list__item { position: relative; display: block; cursor: pointer; margin: 0; }
/* **Il radio torna raggiungibile col Tab, e questa è una correzione, non uno
   stile.** Il foglio di JetSmartFilters mette
   `.jet-radio-list .jet-radio-list__item .jet-radio-list__input { display:none }`
   e un elemento `display:none` esce dall'ordine di tabulazione: senza questa
   riga il doppio filtro — cioè lo strumento principale della pagina — si usa
   **solo col mouse**. Il plugin lo compensa con un `tabindex` sull'etichetta,
   ma solo se si accende l'opzione «Use tabindex» nelle sue impostazioni
   generali, che vale per tutto il sito e di default è spenta.
   Qui l'input resta un radio vero, invisibile e sovrapposto alla pillola: si
   raggiunge col Tab, si cambia con le frecce, e lo screen reader annuncia
   gruppo e stato. Il selettore ha un livello in più del loro perché il loro è
   a tre classi. Decisione 20. */
.lb-chips .jet-radio-list .jet-radio-list__item .jet-radio-list__input {
	display: block;
	position: absolute; inset: 0; width: 100%; height: 100%;
	margin: 0; opacity: 0; cursor: pointer; appearance: none;
	visibility: visible;
}
.elementor-widget-jet-smart-filters-radio.lb-chips .jet-radio-list__button {
	display: inline-flex;
	align-items: center;
	font-family: var(--lb-regular);
	font-size: .78rem;
	/* 1.55 come il corpo del prototipo: il chip lì è un `<button>` senza
	   interlinea propria, e con 1.3 la pillola veniva 3px più bassa. */
	line-height: 1.55;
	padding: 7px 14px;
	border: 1px solid var(--lb-filo);
	border-radius: 0;
	color: var(--lb-tenue);
	white-space: nowrap;
	transition: color .2s, border-color .2s, background .2s;
}
.elementor-widget-jet-smart-filters-radio.lb-chips .jet-radio-list__item:hover .jet-radio-list__button {
	color: var(--lb-titolo);
	border-color: var(--lb-tenue);
}
.elementor-widget-jet-smart-filters-radio.lb-chips .jet-radio-list__input:checked + .jet-radio-list__button {
	background: var(--lb-arancio);
	color: var(--lb-su-arancio);
	border-color: var(--lb-arancio);
}
/* Il focus da tastiera va sul chip, non sul radio invisibile: senza, chi naviga
   col Tab vede l'anello attorno al nulla. Decisione 20. */
.lb-chips .jet-radio-list__input:focus-visible + .jet-radio-list__button {
	outline: 2px solid var(--lb-arancio);
	outline-offset: 3px;
}
/* **I chip vanno a capo a ogni larghezza. Decisione 134a, ed è un cambio di
   rotta rispetto a com'era scritto qui prima.**

   Fino al 2026-08-25 sotto i 640 px questo blocco metteva `flex-wrap: nowrap`
   sul fieldset e affidava il resto a una striscia scorrevole sul `<form
   class="jet-radio-list-wrapper">`, con una maschera che sfumava l'ultimo chip.
   Misurato a 390 px: **quattro opzioni su dodici erano dipinte fuori dallo
   schermo e irraggiungibili** — fra cui «Corporate» ed «Editoria & Istituzioni»,
   cioè due dei quattro mercati — e altre due tagliate a metà parola. Il taglio
   a metà è esattamente il segnale con cui si dice «qui si scorre»: si prova lo
   swipe e non succede niente. Comincia a 620 px; sopra i 700 non si vede nulla
   del problema, ed è per questo che non era emerso.

   L'affordance «SCORRE DA SOLO · TRASCINA →» che il progetto usa altrove esiste
   per le **piste di contenuto**, dove ciò che resta fuori è un esempio in più.
   Qui ciò che resta fuori sono **comandi**, e il doppio filtro Settore ×
   Tipologia è la funzione decisa col cliente per l'uso davanti al cliente. Un
   comando che non si vede non esiste, e nessuna sfumatura ripara il fatto che
   la striscia non scorreva nemmeno.

   Andare a capo costa due righe di altezza e rende visibili dodici opzioni su
   dodici. Non serve nessuna regola: il `flex-wrap: wrap` del fieldset qui sopra
   vale già a tutte le larghezze — la correzione è **togliere l'eccezione**. */

/* --- Il conteggio ---
   Le due parole stanno nel contenuto del widget, non qui e non nel JavaScript:
   lo script scrive il numero e mette `--uno` quando il risultato è uno solo.
   Prima che parta — e nell'editor di Elementor — si vede il plurale. */
.lb-lv-conta { margin-top: 14px; }
.elementor-widget-text-editor.lb-lv-conta p {
	font-family: var(--lb-regular);
	font-size: .66rem;
	/* L'interlinea va ridichiarata: il kit di Elementor la scrive in `em`, che
	   diventa una **lunghezza** già sul widget (16px × 1.55 = 24,8px) e si
	   eredita così com'è. Senza questa riga la riga del conteggio è alta 25px
	   invece di 16, su un testo di 10,5px. Vale per ogni testo del sito più
	   piccolo del corpo: è la stessa famiglia dei tre difetti tipografici già
	   trovati sul footer della home. */
	line-height: 1.55;
	text-transform: uppercase;
	letter-spacing: .16em;
	color: var(--lb-tenue);
	margin: 0;
}
.lb-lv-conta b { color: var(--lb-titolo); font-weight: 700; }
.lb-conta__uno { display: none; }
.lb-lv-conta--uno .lb-conta__uno { display: inline; }
.lb-lv-conta--uno .lb-conta__molti { display: none; }

/* --- La griglia ---
   Tre colonne, due sotto 900px, una sotto 560. Sono le soglie del prototipo, e
   nessuna delle due coincide con i breakpoint di Elementor (767 e 1024): è
   esattamente il punto in cui qualcuno sarebbe tentato di aggiungerne uno nel
   pannello, che è la mossa che toglie gli stili a tutto il sito.

   Perché `display:grid` e non i `--columns` di JetEngine: la variabile e la mia
   regola avrebbero la stessa specificità, e vincerebbe l'ordine in cui i due
   fogli arrivano — cioè una cosa che si rompe da sola il giorno che qualcuno
   sposta un `wp_enqueue_style`. */
.lb-lv-risultati { padding-block: clamp(30px, 4vw, 52px) clamp(70px, 9vw, 120px); }
/* **La sezione dei risultati impila i suoi figli come blocchi, non come flex.**
   Non è una preferenza di stile, è una misura: a 390px la griglia a una colonna
   è alta 6.379px e il contenitore flex che la conteneva si fermava a 5.922 —
   **457 pixel di contenuto tagliati fuori dall'altezza del genitore**, con le
   ultime due card che finivano sopra al footer. A 1440px, con la griglia a tre
   colonne, il difetto non si vedeva: è il caso in cui guardare solo il desktop
   avrebbe fatto consegnare una pagina rotta sul telefono.
   La causa sta a monte: Elementor mette `height:100%` su `.e-con-inner`, che
   dentro un Container di altezza automatica è una percentuale senza riferimento,
   e la griglia di JetEngine è l'unico blocco del sito abbastanza alto da farlo
   emergere. Qui il contenitore non deve distribuire niente — ha un figlio solo,
   più lo stato vuoto che non compare mai insieme a lui — quindi `block` è anche
   la cosa giusta, non solo quella che funziona. */
.lb-lv-risultati > .e-con-inner { display: block; }
.lb-lv-griglia .jet-listing-grid__items {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	/* G6-E2 (decisione 281): più aria fra una fila di progetti e la successiva, 56/41/40 px a
	   1440/1024/834. Era clamp(18px, 2.2vw, 32px) in verticale; lo spazio fra le colonne resta. */
	gap: clamp(40px, 4vw, 56px) clamp(16px, 2vw, 28px);
	margin: 0;
	width: auto;
}
.lb-lv-griglia .jet-listing-grid__items > .jet-listing-grid__item {
	max-width: none;
	flex: none;
	padding: 0;
	min-width: 0;
}
@media (max-width: 900px) { .lb-lv-griglia .jet-listing-grid__items { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .lb-lv-griglia .jet-listing-grid__items { grid-template-columns: minmax(0, 1fr); } }

/* --- Lo stato di attesa del filtro ---------------------------------------
   **Quello che mancava era il cerchietto, non l'attenuazione.** La riga era
   arrivata come «il tema non stila `jet-filters-loading`, opacità campionata
   ogni 20 ms sempre 1». L'ho riprodotta e **l'opacità 1 era di un altro
   oggetto**: JetSmartFilters **sostituisce** `.jet-listing-grid` a ogni filtro,
   quindi una referenza catturata prima del clic punta a un **nodo staccato** —
   e un nodo staccato risponde stringhe vuote e conserva le classi che aveva.
   Ri-interrogando il DOM a ogni campione, il quadro è un altro.

   **Cosa fa già il plugin, e non lo sapevamo.**
   `jet-smart-filters/assets/css/public.css`:

       .jet-filters-loading                                { position: relative;
                                                             pointer-events: none !important }
       .jet-filters-loading > :not(.jsf_provider-preloader) { opacity: .6 !important }

   Quindi la griglia **si attenuava già a 0,6 e i clic erano già bloccati**,
   da sempre. Il buco vero è il **cerchietto** che `deliverable/ux/design-system.md`
   §5 chiede insieme all'opacità — *«griglia con opacità ridotta + spinner»* —
   e che non c'è mai stato.

   🔴 **Perché non porto l'opacità a 0,32 come avevo scritto in prima battuta.**
   Ci ho provato: prima con `> *`, che vale **(0,2,0)** perché `*` conta zero e
   quindi **pareggiava**; poi ripetendo il loro `:not()` per salire a **(0,3,0)**.
   Il pavimento misurato è rimasto **0,6 in tutti e due i casi**, e la ragione la
   dà `getPropertyPriority`: la loro dichiarazione è **`!important`**. Non la
   batte nessuna specificità — solo un altro `!important`.

   **E non lo metto.** Portarla a 0,32 vorrebbe dire scavalcare con `!important`
   un valore che un plugin di terzi ha scelto, per cambiare 0,6 in 0,32 che
   **nessuno ha chiesto**: la specifica dice «opacità ridotta», e 0,6 è ridotta.
   Sarebbe una decisione di design inventata qui, che si romperebbe al primo
   aggiornamento del plugin. Il tema aggiunge **la sola cosa che manca**.

   ⚠️ **Due riscritture prima di arrivarci, e la prima sembrava funzionare.** Con
   `> *` la griglia si oscurava davvero: a occhio era «fatto». Il pavimento
   misurato però era **0,6, cioè il valore di JetSmartFilters, non il nostro**.
   Una correzione che produce un effetto visibile e **non quello che dichiara**
   passa per buona: è la stessa forma della trappola #2, vista dall'altro lato.

   🔴 **Il cerchietto è `fixed`, e la prima versione assoluta era invisibile.**
   Prima stava a `top: 96px` dal bordo della griglia, cioè «in alto nei
   risultati». Fotografato lo stato di attesa: **non si vedeva**. La griglia
   comincia sotto la **barra dei filtri appiccicata**, alta ~265 px, quindi 96 px
   dal bordo della griglia cadono **dietro la barra**. La regola c'era, il
   disegno no — stessa forma del difetto che ci ha già preso due volte oggi.

   `fixed` risolve due cose insieme: il cerchietto **non finisce mai dietro la
   barra**, e non dipende più da dove si trova chi filtra. Prima avevo dichiarato
   come limite che chi filtra da molto in basso non l'avrebbe visto, e che
   sarebbe passato quando la vista fosse tornata sui risultati: **con `fixed`
   quel limite non esiste più**, e la riga di `dev-interazioni` resta utile per
   sé, non come stampella di questa.

   ⚠️ **`fixed` ha un antenato che lo può catturare:** un `transform`, un
   `filter` o un `backdrop-filter` su un antenato lo aggancia a quello invece che
   alla finestra — è **la stessa causa della decisione 164**, dove la testata
   sfocata imprigionava l'overlay del pannello. Qui non ce ne sono (verificato
   sul reso: il cerchietto misura 1873×1000 di riferimento, non il riquadro
   della griglia), ma se un domani qualcuno mette una `transform` su
   `.lb-lv-griglia` o su un suo antenato, **il cerchietto torna dentro la
   griglia senza dire niente**. È scritto qui perché è invisibile altrove.

   **Due agganci, e il secondo è nostro.** `jet-filters-loading` la mette
   JetSmartFilters: se un aggiornamento la rinomina, il cerchietto smette di
   comparire **e non lo dice nessuno** — era la falsificazione che avevo scritto
   per questa regola, ed è un punto singolo di rottura fuori dal nostro
   controllo. `dev-interazioni` mette `aria-busy="true"` su `.lb-lv-griglia` da
   `comportamenti.js`: **misurato, copre esattamente la stessa finestra** — 18
   campioni a 20 ms, ~360 ms, gli stessi della classe del plugin, sovrapposti.
   Ce ne fosse solo uno dei due, il cerchietto compare lo stesso — verificato
   spegnendoli a turno: **classe sola SI, attributo solo SI, nessuno dei due no.**

   ⚠️ **Il `:not()` serve a non disegnarne due.** Quando ci sono entrambi — cioè
   sempre, oggi — senza di esso lo `::after` nasce su **tutti e due** gli
   elementi, perfettamente sovrapposti: a schermo il filo del cerchio passa da
   18% a 33% di bianco, perché due velature si sommano. Non è un difetto grave,
   è **una cosa disegnata due volte**, e chi legge il foglio fra un mese non
   capirebbe quale delle due regole comanda. Così l'attributo è dichiaratamente
   **il ripiego**: entra solo se la classe del plugin non c'è.

   *(Niente `position: relative` sul contenitore: serviva alla prima versione
   assoluta, e con `fixed` non fa niente. Un antenato con `transform` lo
   catturerebbe comunque, e `relative` non lo impedisce — quindi sarebbe una
   dichiarazione che sembra una protezione senza esserlo.)* */
.lb-lv-griglia .jet-filters-loading::after,
.lb-lv-griglia[aria-busy="true"]:not(:has(.jet-filters-loading))::after {
	content: "";
	position: fixed; left: 50%; top: 50%;
	width: 34px; height: 34px; margin: -17px 0 0 -17px;
	z-index: 90;
	border: 2px solid rgba(255, 255, 255, .18);
	border-top-color: var(--lb-arancio);
	border-radius: 50%;
	animation: lb-attesa .7s linear infinite;
}
@keyframes lb-attesa { to { transform: rotate(360deg); } }
/* A movimento ridotto il cerchietto **sparisce** invece di restare fermo: un
   cerchio immobile non dice «sto caricando», dice «c'è un cerchio». L'attenuazione
   della griglia resta — è del plugin e non ha bisogno di muoversi. */
@media (prefers-reduced-motion: reduce) {
	.lb-lv-griglia .jet-filters-loading::after,
	.lb-lv-griglia[aria-busy="true"]:not(:has(.jet-filters-loading))::after { display: none; }
}
/* ⚠️ **Qui c'era una graffa di troppo, e non era innocua: si mangiava la regola
   subito sotto.** Trovata il 2026-08-26 da `dev-tema-g4` con un contatore di
   parentesi lanciato per controllare **un'altra** modifica, e confermata con un
   parser vero invece che col conteggio: `CSSStyleSheet.replaceSync` dava **585
   regole con la graffa e 586 senza**.

   La regola persa era `.lb-lv-griglia .lb-card__foto:not(:has(img))`, cioè
   **proprio quella che veste le schede senza copertina** — e le copertine
   mancanti sono cinque, in attesa dal cliente. Il difetto che quella regola
   doveva nascondere si vedeva, e la regola scritta per nasconderlo non è mai
   stata applicata.

   **Un carattere in più non rompe il foglio: il parser lo scarta e si riprende
   alla regola dopo, buttando quella.** Nessun errore, nessun sintomo che punti
   alla causa. È il motivo per cui il conteggio delle parentesi vale la pena
   anche quando «tanto il sito si vede bene». */

/* Un progetto senza immagine in evidenza mostra il riquadro del tema, non un
   quadrato rotto: `.lb-card__foto` ha già proporzione, filo e fondo, e senza la
   foto resta una cornice vuota. Nel prototipo quelle cinque card sono rettangoli
   sfumati; qui non si riproducono, perché il content model §4 dice «nessun
   placeholder gradient in produzione» e §3.1 rende la foto obbligatoria. Lo
   stato esiste solo nella demo, e deve essere dignitoso lo stesso. */
.lb-lv-griglia .lb-card__foto:not(:has(img)) { background: var(--lb-superficie); }

/* --- Lo stato vuoto ---
   Non è un messaggio: è una schermata. Con quattro settori per sei tipologie una
   combinazione su tre non ha niente da mostrare, e al lancio — due soli progetti
   caricati da noi, decisioni 10 e 11 — saranno quasi tutte. Il titolare aprirà
   il sito davanti a un cliente e ci finirà dentro al primo tentativo.

   Si accende guardando dentro la griglia, non con una classe messa da uno
   script: se il JavaScript non parte, la pagina resta corretta. */
.lb-vuoto { display: none; }
.lb-lv-risultati:has(.jet-listing-not-found) .lb-vuoto {
	display: flex;
	position: relative;
	overflow: hidden;
	text-align: center;
	padding: clamp(56px, 9vw, 110px) clamp(22px, 4vw, 54px);
	border: 1px solid var(--lb-filo);
	border-radius: 0;
	background:
		radial-gradient(120% 100% at 50% 0%, color-mix(in srgb, var(--lb-arancio) 9%, transparent), transparent 62%),
		var(--lb-superficie);
}
/* Il filo arancione sopra: è lo stesso segno che apre ogni occhiello del sito,
   usato qui a tutta larghezza. Serve a dire «questa è una sezione», non «questo
   è un errore». */
.lb-vuoto::before {
	content: "";
	position: absolute; top: 0; left: 50%; transform: translateX(-50%);
	width: min(340px, 62%); height: 1px;
	background: linear-gradient(90deg, transparent, var(--lb-arancio), transparent);
}
.lb-vuoto .lb-eyebrow .elementor-heading-title { justify-content: center; }
.lb-vuoto__titolo .elementor-heading-title {
	max-width: 26ch;
	margin: 18px auto 0;
	text-align: center;
}
.elementor-widget-text-editor.lb-vuoto__testo p {
	color: var(--lb-tenue);
	font-size: .95rem;
	max-width: 52ch;
	margin: 16px auto 0;
}

/* Azione 1 — «Togli un filtro». Il widget Active Filters si nasconde da solo
   quando non c'è nessun filtro acceso: è la ragione per cui questo blocco regge
   anche il caso «portfolio ancora vuoto», dove non c'è nessun filtro da togliere
   e resta la sola CTA. */
.lb-vuoto__togli { margin-top: 30px; }
/* I selettori hanno una classe in più del necessario, e serve: il foglio di
   JetSmartFilters mette `.jet-smart-filters-active .jet-active-filter__remove
   { position:absolute; top:0; right:0 }` — la crocetta finisce **sopra** il
   testo del filtro, illeggibile, e col fondo scuro del sito non si vede
   nemmeno. Qui torna un elemento in riga come il resto della pillola. */
.lb-vuoto .jet-active-filters {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: 10px;
	width: auto;
}
.lb-vuoto .jet-active-filters .jet-active-filters__list {
	display: flex; flex-wrap: wrap; justify-content: center; align-items: center;
	gap: 10px; width: auto;
}
.lb-vuoto .jet-active-filters .jet-active-filters__title {
	font-size: .6rem; text-transform: uppercase; letter-spacing: .18em;
	line-height: 1.55; color: var(--lb-tenue); margin-right: 0;
}
.lb-vuoto .jet-active-filters .jet-active-filter {
	display: inline-flex; align-items: center; gap: 9px;
	padding: 6px 10px 6px 14px;
	border: 1px solid color-mix(in srgb, var(--lb-arancio) 42%, transparent);
	border-radius: 0;
	font-size: .72rem; line-height: 1.55; color: var(--lb-titolo);
	cursor: pointer;
	transition: border-color .2s, background .2s;
}
.lb-vuoto .jet-active-filters .jet-active-filter:hover {
	background: color-mix(in srgb, var(--lb-arancio) 14%, transparent);
	border-color: var(--lb-arancio);
}
.lb-vuoto .jet-active-filters .jet-active-filter__label {
	color: var(--lb-tenue); text-transform: uppercase; letter-spacing: .12em;
	font-size: .6rem; padding-right: 0;
}
.lb-vuoto .jet-active-filters .jet-active-filter__remove {
	position: static;
	display: inline-flex; align-items: center; justify-content: center;
	width: 20px; height: 20px; border-radius: 50%; flex: 0 0 auto;
	background: color-mix(in srgb, var(--lb-arancio) 26%, transparent);
	color: var(--lb-arancio); font-size: .9rem; line-height: 1;
}
.lb-vuoto .jet-active-filters .jet-active-filter:hover .jet-active-filter__remove {
	background: var(--lb-arancio); color: var(--lb-su-arancio);
}
/* Azione 2 — la CTA. Porta all'àncora dei contatti nel footer, come «Contattaci»
   nell'intestazione: il drawer col modulo è in standby (decisione 31) e non si
   finge che esista. La provenienza la scrive `comportamenti.js` nell'indirizzo. */
.lb-vuoto__cta { margin-top: 24px; }
