/*
Theme Name:   Distretto del Cibo di Udine
Description:  Tema figlio North (Ensoul) di Twenty Twenty-Five per il sito del Distretto del Cibo di Udine. Nato dallo starter North; colori, tipografia, spaziature e gutter arrivano dai design token del plugin North (uploads/north/north-variables.css, opzione ensoul_design_tokens). Qui vivono solo le regole proprie del sito.
Tags: Tailwind, Accessibility, Custom Menu, Custom Logo, Editor Style, Featured Images, Site Editor, Block Editor Patterns, Wide Blocks, Block Editor Styles, Accessible, Corporate, Blocks
Author:       Ensoul
Template:     twentytwentyfive
Version:      0.1.0
Text Domain:  distrettocibo
North Starter: 1.1.0
*/

/* body {
  overflow-x: hidden;
} */

#inactivity_message {
  display: flex;
  align-items: center;
  justify-content: center;
  background: black;
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  z-index: 999;
  color: #999;
  padding: 0 2rem;
  text-align: center;
}

.sticky {
  position: sticky;
  top: 20px;
}

/* ==========================================================================
   Distretto del Cibo di Udine — regole di progetto
   Sorgente: Figma ufcHAwdBiCRX7FulmajqsR, pagina di consegna "figma to claude".
   Qui vive SOLO cio' che i blocchi North non esprimono da attributo:
   capolettera, sbordo orizzontale, dualita' header desktop/mobile, sfondo.
   NB cascata: le utility Tailwind di North stanno in @layer utilities, quindi
   una regola NORMALE non-layered qui sotto le batte; una !important no (GC-078).
   Percio' i font-size restano sempre affidati agli slug dei blocchi.
   ========================================================================== */

/* Trama della mappa del territorio sotto al colore di base. Il PNG e' gia'
   composto sul crema e l'intensita' della trama si regola nell'immagine: un
   velo semitrasparente in CSS grande quanto la pagina dimezza i fotogrammi
   dello scroll su Firefox. ?v= cambia a ogni nuova versione del file, per
   non farlo restare in cache. */
body {
  background-image: url("assets/img/sfondo-mappa.png?v=70");
  background-repeat: repeat-y;
  background-position: top center;
  background-size: 100% auto;
}

/* Parola in evidenza terracotta (corsivo degli H1, parole chiave dei lead) */
.accento { color: var(--color-secondary-500); }

/* --- Lead Playfair con capolettera --------------------------------------- */
.lead p { font-family: var(--font-heading); }

/* Capolettera: con il solo float la lettera si appoggia in cima al box di
   riga e la sua baseline finisce PIÙ IN BASSO di quella della prima riga.
   Il margine negativo la rialza fino a farle condividere la baseline con la
   prima riga di testo, come nel Figma (misurato: scarto 0.375em della
   lettera, cioè 0.75em del testo). */
/* Deve occupare ESATTAMENTE 2 righe (richiesta Giulia): initial-letter lo fa
   dire al browser, che allinea da solo la testa della lettera alla prima riga
   e la sua baseline a quella della seconda. Il float qui sotto resta come
   ripiego per Firefox, che initial-letter non lo supporta ancora: 2,6em su
   testo 30px/40px fa un box di ~2 righe. */
.lead p::first-letter,
.lead-sm p::first-letter {
  float: left;
  font-family: var(--font-heading);
  font-size: 2.6em;
  line-height: 0.78;
  margin-top: 0;
  padding-right: 0.06em;
}

@supports (initial-letter: 2) or (-webkit-initial-letter: 2) {
  .lead p::first-letter,
  .lead-sm p::first-letter {
    float: none;
    font-size: inherit;
    line-height: inherit;
    -webkit-initial-letter: 2;
    initial-letter: 2;
    padding-right: 0.08em;
  }
}

.lead.no-dropcap p::first-letter {
  float: none;
  font-size: inherit;
  padding-right: 0;
  -webkit-initial-letter: normal;
  initial-letter: normal;
}

/* --- Riga allineata a destra (sottotitolo hero, testo delle slide) -------- */
@media (min-width: 768px) {
  .riga-destra > div { justify-content: flex-end; }
}

/* --- Nastro di foto: scorre da solo, lento e infinito ----------------------
   Il JS (animazioni.js) smonta lo swiper, clona le slide in 3 set e la track
   scorre in translateX; il keyframe torna indietro di un terzo, così il giro
   è senza cuciture. Stessa meccanica della galleria di craneng. */
.striscia-foto .swiper-wrapper.marquee-track {
  display: flex;
  gap: 20px;
  width: max-content;
  animation: dc-nastro 90s linear infinite;
  will-change: transform;
}
.striscia-foto .swiper-slide { pointer-events: none; }
.striscia-foto .swiper-button-prev,
.striscia-foto .swiper-button-next,
.striscia-foto .swiper-pagination { display: none !important; }

@keyframes dc-nastro { to { transform: translateX(calc(-100% / 3)); } }

@media (prefers-reduced-motion: reduce) {
  .striscia-foto .swiper-wrapper.marquee-track { animation: none; }
}

/* --- Mappa dei Comuni: SVG a larghezza piena ------------------------------ */
.mappa-comuni .icon-svg-wrapper { display: block; width: 100%; height: auto; }
.mappa-comuni svg { display: block; width: 100%; height: auto; }

/* I Comuni sono bottoni North (className "comune comune-<nome>", URL dal
   pannello del blocco) posati sopra i cerchi. Le coordinate sono quelle del
   Figma (12627:752) nel sistema 709 x 691 dei cerchi: --x il bordo sinistro,
   --y la linea di base. La colonna e' il contenitore delle container query,
   cosi' posizione e corpo scalano con la larghezza della mappa. */
/* Il corpo va sul bottone e non sul wrapper: il wrapper porta
   text-paragraph-base! di North, un !important in @layer che dal tema non si
   batte. Per lo stesso motivo il top non usa em: 1.0745 corpi e' la distanza
   fra la cima del box del testo e la linea di base di Playfair. */
.mappa-comuni { position: relative; container-type: inline-size; }
.mappa-comuni .comune {
  --corpo: 19.991;
  position: absolute;
  left: calc(var(--x) * 100cqw / 709);
  top: calc((var(--y) - 1.0745 * var(--corpo)) * 100cqw / 709);
  z-index: 1;
}
.mappa-comuni .comune:hover,
.mappa-comuni .comune:focus-within { z-index: 2; }
.mappa-comuni .comune .ens-btn {
  --spacing-padding-button: 0;
  --color-button: var(--color-primary-600);
  --color-button-hover: var(--color-secondary-500);
  position: relative;
  font-family: var(--font-heading);
  font-size: calc(var(--corpo) * 100cqw / 709);
  font-weight: 400;
  line-height: normal;
  text-transform: none;
  white-space: nowrap;
}
.mappa-comuni .comune-udine { --corpo: 26.654; }
.mappa-comuni .comune-udine .ens-btn { --color-button: var(--color-secondary-500); }

/* Freccia (Figma 12627:804, senza il bordo a pillola). La disegna il tema e
   non l'icona del blocco: l'editor di North, aprendo la pagina, sostituisce
   l'icona inline di un bottone con quella di default. E' una maschera sul
   colore del testo, fuori dal box del link, cosi' finche' e' spenta non ruba
   l'hover ai nomi vicini; in hover si disegna da sinistra e scivola. */
.mappa-comuni .comune .ens-btn > span { display: none; }
.mappa-comuni .comune .ens-btn::after {
  content: "";
  position: absolute;
  left: 100%;
  top: 50%;
  width: 0.77em;
  aspect-ratio: 18.8426 / 14.5649;
  margin-left: 0.33em;
  background-color: currentColor;
  -webkit-mask: url("assets/img/freccia-comune.svg") center / contain no-repeat;
  mask: url("assets/img/freccia-comune.svg") center / contain no-repeat;
  opacity: 0;
  clip-path: inset(0 100% 0 0);
  transform: translate(-0.35em, -50%);
  pointer-events: none;
  transition: opacity 200ms ease, clip-path 450ms var(--curva-testo), transform 450ms var(--curva-testo);
}
.mappa-comuni .comune .ens-btn:hover::after,
.mappa-comuni .comune .ens-btn:focus-visible::after {
  opacity: 1;
  clip-path: inset(0 0 0 0);
  transform: translate(0, -50%);
}

.comune-udine { --x: 324.02; --y: 344.80; }
.comune-basiliano { --x: 154.93; --y: 431.23; }
.comune-campoformido { --x: 228.22; --y: 417.90; }
.comune-codroipo { --x: 0; --y: 522.02; }
.comune-mariano-del-friuli { --x: 543.08; --y: 569.50; }
.comune-martignacco { --x: 187.41; --y: 288.80; }
.comune-mortegliano { --x: 224.06; --y: 549.51; }
.comune-nimis { --x: 352.34; --y: 118.04; }
.comune-povoletto { --x: 390.65; --y: 251.31; }
.comune-pradamano { --x: 427.00; --y: 395.59; }
.comune-reana-del-rojale { --x: 279.04; --y: 204.67; }
.comune-ruda { --x: 395.00; --y: 651.43; }
.comune-talmassons { --x: 158.26; --y: 582.82; }
.comune-tavagnacco { --x: 249.88; --y: 236.32; }

/* Nell'editor i Comuni restano un elenco sotto la mappa: si leggono e si
   cliccano per cambiare testo e URL. */
.editor-styles-wrapper .mappa-comuni .comune { position: static; }
.editor-styles-wrapper .mappa-comuni .comune .ens-btn { font-size: var(--text-paragraph-lg); }

/* --- Novita' ed eventi (Figma 12627:413 pagina, 12627:367 home mobile) -----
   Blocco Query sugli articoli con className "elenco-novita": data evento e
   titolo, non cliccabili, fra righe terracotta. Senza articoli sparisce tutto
   il blocco (in home anche il titolo "Le novita'", che gli sta dentro). */
.elenco-novita:not(:has(.wp-block-post-template)) { display: none; }
/* il Query e' in layout flow: WP darebbe 16px di block-gap fra titolo, riga
   ed elenco; gli spazi li decidono i padding dei blocchi */
.wp-block-query.elenco-novita > * { margin-block: 0; }
.elenco-novita .wp-block-post-template p { color: var(--color-primary-400); }
@media (min-width: 768px) {
  .elenco-novita .wp-block-post-template h3 { max-width: calc(886 * var(--px, 1px)); }
}

/* --- Foto: angoli arrotondati (8px nel Figma) ----------------------------- */
@media (max-width: 767px) {
  .foto-arrotondata-mobile img,
  .foto-prodotto img { border-radius: 8px; }
}
/* Regola generale: TUTTE le foto hanno gli angoli da 8px (nel Figma ogni
   immagine è un rettangolo arrotondato). Fuori solo le foto a tutto schermo
   dell'hero pinnato e i loghi. Nella home il raggio della foto dell'hero si
   annulla mentre si espande a tutto schermo (--velo 0→1). */
main figure img { border-radius: calc(8 * var(--px, 1px)); }
/* Con la parallasse l'<img> è scalata 1.12 dentro la figure (overflow:hidden):
   gli angoli tondi dell'immagine finiscono FUORI dal riquadro e la figure
   ritaglia squadrato. Il raggio quindi va anche sulla figure. */
.js-anima .parallasse { border-radius: calc(8 * var(--px, 1px)); }
.hero-pin__sfondi figure,
.hero-pin__sfondi img,
.logo-finanziatore img,
.mappa-comuni img { border-radius: 0; }
/* La figure resta in flusso solo come segnaposto (tiene la geometria della
   riga con le etichette): l'<img> vive nello strato a tutto schermo, quindi
   il rapporto 16:9 del blocco va ridato alla figure vuota. */
.js-hero-pin .hero-pin__foto { visibility: hidden; aspect-ratio: 16 / 9; }

/* --- Chi siamo: fascia terracotta della prima riga di obiettivi ----------- */
@media (min-width: 768px) {
  .obiettivi-primo { background-color: var(--color-secondary-500); }
  .obiettivi-primo p,
  .obiettivi-primo h3 { color: var(--color-secondary-50); }
}

/* --- Righe sottili -------------------------------------------------------- */
.riga-chiara  { color: var(--color-secondary-200); }
.riga-accento { color: var(--color-secondary-300); }
.riga-tenue { opacity: 0.35; }
/* Aziende: la voce si accende in terracotta e mostra la freccia lunga del
   Figma solo su hover/focus. La freccia è una maschera SVG, così eredita il
   colore corrente ed è centrata sulla riga a qualsiasi altezza. */
.azienda-voce {
  position: relative;
  padding-right: calc(72 * var(--px, 1px));
  transition: color 240ms ease;
}
.azienda-voce:hover,
.azienda-voce:focus-within { color: var(--color-secondary-500); }
.azienda-voce::after {
  content: "";
  position: absolute;
  right: 0;
  top: 50%;
  width: calc(46 * var(--px, 1px));
  height: calc(14 * var(--px, 1px));
  opacity: 0;
  transform: translateY(-50%) translateX(-10px);
  transition: opacity 240ms ease, transform 240ms cubic-bezier(0.16, 1, 0.3, 1);
  background-color: currentColor;
  -webkit-mask: var(--freccia-lunga) no-repeat center / contain;
  mask: var(--freccia-lunga) no-repeat center / contain;
}
.azienda-voce:hover::after,
.azienda-voce:focus-within::after { opacity: 1; transform: translateY(-50%) translateX(0); }

:root {
  --freccia-lunga: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 46 14'%3E%3Cpath d='M0 7h43M36.5 1.5 43 7l-6.5 5.5' fill='none' stroke='%23000' stroke-width='1.4'/%3E%3C/svg%3E");
}

/* --- Header: nav inline su desktop, hamburger North su mobile ------------- */
.header-bar .wp-block-navigation { display: none; }
.header-bar .wp-block-navigation__responsive-container-open { display: none; }
@media (min-width: 768px) {
  .header-bar .wp-block-navigation { display: block; }
  .header-bar .header-hamburger-block { display: none; }
}
.header-bar .wp-block-navigation__container {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: center;
  gap: calc(40 * var(--px, 1px));
  list-style: none;
  margin: 0;
  padding: 0;
}
.header-bar .wp-block-navigation__responsive-container:not(.is-menu-open) {
  display: contents;
}
.header-bar .wp-block-navigation-item__content {
  font-family: var(--font-body);
  font-weight: 400;
  text-decoration: none;
}
/* La voce si ingrassa sotto il puntatore (Manrope e' variabile: il peso si
   anima davvero). Il grassetto e' piu' largo: l'etichetta riserva gia' la
   larghezza della versione bold con una copia nascosta (data-testo, messo da
   animazioni.js), cosi' le voci accanto non si spostano. */
.header-bar .wp-block-navigation-item__label {
  display: inline-block;
  text-align: center;
}
.header-bar .wp-block-navigation-item__label::after {
  content: attr(data-testo);
  display: block;
  height: 0;
  overflow: hidden;
  visibility: hidden;
  font-weight: 700;
}
#site-logo { font-family: var(--font-heading); line-height: 1.25; }
#site-logo a { text-decoration: none; }

/* --- Pannello menu mobile: overlay a schermo intero (Figma 12508:1734) -----
   Ancorato con top E bottom al viewport (position:fixed): è il modo affidabile
   di riempire lo schermo anche quando la barra URL del browser si nasconde
   allo scroll e l'altezza cambia (vh resterebbe "alta", svh "bassa": con
   inset segue sempre il viewport visibile). Niente min-height dal blocco.
   North apre/chiude con display:none→block, che azzera ogni transizione: qui
   il pannello resta display:block e si anima con visibility+opacity+transform;
   le voci salgono a cascata. */
.header-menu-panel a { text-decoration: none; }
.header-fixed .header-menu-panel {
  position: fixed;
  top: var(--barra-admin);
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 90;
  display: block !important;
  height: auto;
  min-height: 0 !important;
  max-height: none;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  visibility: hidden;
  opacity: 0;
  transform: translateY(-12px);
  transition: opacity 380ms ease, transform 520ms cubic-bezier(0.16, 1, 0.3, 1),
              visibility 0s linear 520ms;
}
.header-fixed .header-menu-panel.header-menu-panel--open {
  visibility: visible;
  opacity: 1;
  transform: none;
  transition-delay: 0s, 0s, 0s;
}
/* le voci entrano una dopo l'altra, come i testi dell'hero */
.header-menu-panel h3 {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 420ms ease, transform 600ms cubic-bezier(0.16, 1, 0.3, 1);
}
.header-menu-panel--open h3 { opacity: 1; transform: none; }
.header-menu-panel--open h3:nth-child(1) { transition-delay: 120ms; }
.header-menu-panel--open h3:nth-child(2) { transition-delay: 180ms; }
.header-menu-panel--open h3:nth-child(3) { transition-delay: 240ms; }
.header-menu-panel--open h3:nth-child(4) { transition-delay: 300ms; }
.header-menu-panel--open h3:nth-child(5) { transition-delay: 360ms; }
.header-menu-panel--open h3:nth-child(6) { transition-delay: 420ms; }
@media (prefers-reduced-motion: reduce) {
  .header-fixed .header-menu-panel,
  .header-menu-panel h3 { transition-duration: 0s; transition-delay: 0s; }
}
/* la pagina sotto non scorre (North mette header-menu-open sul body) */
body.header-menu-open { overflow: hidden; }
/* a menu aperto l'header è sempre a schermo, e la barra passa al chiaro sul
   verde scuro del pannello (nel Figma resta solo la X) */
.header-fixed.header-menu-open.header-nascosto { transform: none; }
.header-fixed.header-menu-open .header-bar { background-color: transparent !important; }
.header-fixed.header-menu-open .header-bar,
.header-fixed.header-menu-open .header-bar p,
.header-fixed.header-menu-open .header-bar a { color: var(--color-secondary-50) !important; }
/* l'hamburger di North deve restare cliccabile sopra al pannello */
.header-bar { position: relative; z-index: 100; }

/* --- Footer ---------------------------------------------------------------
   WP mette il block-gap (16px) fra i figli di .wp-site-blocks: fra main e
   footer restava una riga crema. Lo spazio prima del footer lo decide la
   pagina (spacer finale), non il template: le fasce full-bleed lo toccano. */
.wp-site-blocks > * + * { margin-block-start: 0; }
.footer-brand p { font-family: var(--font-heading); line-height: 1.25; }
.site-footer a { text-decoration: none; }

/* ==========================================================================
   Animazioni — home (Figma "home con animazioni desktop" 12508:1609,
   componente "home hero animation" 12508:1645)
   Stato iniziale letto dal Figma: righe dell'H1 e sottotitolo dentro maschere
   con il testo spostato sotto, foto dentro una maschera alta 1px ancorata in
   basso, etichette "14 COMUNI"/"7 FILIERE" a opacity 0.
   ========================================================================== */

/* Curva di TUTTI i reveal dei testi (righe dell'H1, parole in maschera, testi
   delle slide, nomi della mappa). Le immagini tengono le loro. */
:root { --curva-testo: cubic-bezier(0.38, 0.005, 0.215, 1); }

/* La struttura a righe serve anche senza animazione: l'H1 va comunque a capo.
   L'entrata dell'H1, delle etichette e del paragrafo crema e' lo stesso reveal
   a parole del resto del sito (animazioni.js li marca come tutti gli altri):
   una grammatica sola. I ritardi dell'hero stanno nel JS (anima-3/anima-4). */
.anima-righe .riga { display: block; }

@media (prefers-reduced-motion: no-preference) {

  /* La foto si scopre dal basso verso l'alto (maschera che cresce) */
  /* La tendina parte solo quando l'immagine è decodificata (.e-pronta lo
     mette animazioni.js): senza, su foto pesanti la tenda finiva a vuoto e
     l'immagine compariva di colpo dopo. */
  .anima-tenda img { clip-path: inset(100% 0 0 0); }
  .anima-tenda.e-pronta img {
    animation: dc-tenda 1.1s cubic-bezier(0.16, 1, 0.3, 1) 0.35s both;
  }

}

@keyframes dc-riga-su {
  from { transform: translateY(110%); }
  to   { transform: translateY(0); }
}
@keyframes dc-tenda {
  from { clip-path: inset(100% 0 0 0); }
  to   { clip-path: inset(0 0 0 0); }
}
@keyframes dc-sale {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

/* --- Home: hero + slide fusi in una sezione pinnata ------------------------
   Figma component set "home hero animation" 12416:1287 (5 varianti).
   Il driver alto e le interpolazioni le fa assets/js/home-hero.js; qui c'è
   solo l'impalcatura degli strati. Senza JS la sezione resta un hero normale. */

.hero-pin { position: relative; overflow: hidden; }
.hero-pin > * { position: relative; z-index: 0; }
/* Ordine degli strati mentre la foto si espande: i testi crema restano sotto,
   la foto ci passa sopra (richiesta: "l'immagine deve sovrapporsi al
   paragrafo iniziale"), poi le foto dei passi, il velo e infine la slide. */
.js-hero-pin .hero-pin__h1,
.js-hero-pin .hero-pin__svanisce { z-index: 1; }
/* la foto sta SOPRA i testi crema: espandendosi li copre, e il velo (z 4) è
   sopra a tutto quanto */
.js-hero-pin .hero-pin__foto { position: relative; z-index: 2; }

/* Altezza della barra admin di WordPress: tutto ciò che è sticky o fixed deve
   scalarla, altrimenti finisce sotto la barra. Sotto i 600px WP la rende
   absolute (scorre via), quindi l'offset torna a zero. */
:root { --barra-admin: 0px; }
body.admin-bar { --barra-admin: 32px; }
@media screen and (max-width: 782px) { body.admin-bar { --barra-admin: 46px; } }
@media screen and (max-width: 600px) { body.admin-bar { --barra-admin: 0px; } }

/* North rende l'header position:fixed (.header-fixed, top:0 in global.css):
   con la barra admin di WP ci finisce sotto. */
body.admin-bar .header-fixed { top: var(--barra-admin); }

/* Sopra la sezione pinnata l'header perde il fondo crema e passa al chiaro,
   come nella preview Figma (nav crema sulla foto). */
/* !important e specificità alta: il colore dell'header arriva da
   .has-contrast-color delle global styles di WP, che è già !important. */
.header-fixed.header-su-scuro .header-bar { background-color: transparent !important; }
.header-fixed.header-su-scuro .header-bar,
.header-fixed.header-su-scuro .header-bar p,
.header-fixed.header-su-scuro .header-bar a { color: var(--color-secondary-50) !important; }
.header-fixed .header-bar,
.header-fixed .header-bar a { transition: color 320ms ease, background-color 320ms ease; }

/* Nascondi/mostra con ISTERESI (animazioni.js, modulo header): North toglie e
   mette .header-hidden-on-scroll a ogni frame appena il verso dello scroll si
   inverte anche di 1px, e col trackpad l'header sfarfallava. Qui la sua classe
   viene resa inerte e comanda .header-nascosto, che scatta solo dopo 48px
   percorsi nello stesso verso. */
.header-fixed.header-scroll-hide.header-hidden-on-scroll { transform: none; }
.header-fixed.header-scroll-hide.header-nascosto { transform: translateY(-100%); }

/* driver + stage li crea il JS: lo sticky sta sullo stage, mai sulla sezione
   (.ensoul-breakout usa left:50% + margin-left:-50vw e con sticky si rompe). */
.hero-pin-driver { position: relative; }
/* Alto 100lvh, cioè lo schermo A BARRA URL NASCOSTA: sui browser mobile la
   barra sparisce al primo scroll e finché il browser non aggiorna il dvh la
   finestra visibile è più alta dello stage, che lasciava una striscia sotto.
   Coi contenuti succede il contrario: a scroll 0 la barra c'è, quindi testo e
   barra di avanzamento si tengono dentro il dvh (padding extra = lvh - dvh,
   zero su desktop e a barra nascosta). Così la corsa del driver non cambia
   mai mentre la barra va e viene. */
.js-hero-pin .hero-pin-stage {
  --hero-extra: 0px;
  position: sticky;
  top: var(--barra-admin);
  height: calc(100vh - var(--barra-admin));
}
/* Le custom property non si scartano al parse: un valore in lvh su un browser
   che non lo conosce annullerebbe tutto il padding che lo usa. */
@supports (height: 1lvh) {
  .js-hero-pin .hero-pin-stage {
    --hero-extra: calc(100lvh - 100dvh);
    height: calc(100lvh - var(--barra-admin));
  }
}
.js-hero-pin .hero-pin-stage > .hero-pin { height: 100%; display: block; }
/* il wrapper interno di North (relative! non sovrascrivibile: sta in
   @layer utilities) è l'ancora degli strati assoluti: lo porto a tutta altezza. */
/* Il contenuto crema NON è centrato: l'H1 deve partire alla stessa altezza
   dei titoli delle altre pagine, cioè subito sotto l'header fisso. Richiesta
   Giulia. Va tolto ANCHE il padding verticale del gruppo North, o i due si
   sommano e il titolo scende invece di salire.
   Lo stage parte dal bordo alto della finestra ed è agganciato fin dal primo
   px di scroll: prima stava sotto lo spaziatore che North mette sul body
   (alto quanto l'header) e nei primi px di scroll saliva, col titolo che
   finiva sotto il nome del sito. Il posto dell'header lo fa il padding
   interno, che ne segue l'altezza reale (--altezza-header, animazioni.js). */
html.js-hero-pin body { padding-top: 0 !important; }
.js-hero-pin .hero-pin > div {
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  padding-top: var(--altezza-header, 72px);
}
.js-hero-pin .ensoul-group.hero-pin { padding-top: 0; }
/* Il contenuto crema si distribuisce sull'altezza piena: H1 in cima, paragrafo
   in fondo alla stessa distanza dal bordo che l'H1 ha dal nome del sito (a
   1440: 48px fra le scatole, misurati sui glifi), foto e etichette esattamente
   in mezzo. I padding-top delle due righe (m e 4xl) vanno azzerati su desktop
   o l'equidistanza salta. SOLO sul contenitore interno: la classe hero-pin
   sta anche sul gruppo North, e col padding messo pure lì il contenitore
   interno (ancora degli strati assoluti) restava più corto dello stage, con
   la foto espansa che spuntava sotto al velo. */
.js-hero-pin .ensoul-group.hero-pin > div {
  justify-content: space-between;
  padding-bottom: calc(var(--spacing-l) + var(--hero-extra, 0px));
}
@media (min-width: 768px) {
  .js-hero-pin .ensoul-group.hero-pin > div {
    padding-bottom: calc(48 * var(--px, 1px) + var(--hero-extra, 0px));
  }
  .js-hero-pin .hero-pin .ensoul-columns-block:has(> .ensoul-column-wrapper > .column > .hero-pin__foto),
  .js-hero-pin .hero-pin .riga-destra:not(.hero-pin__testi) > .ensoul-columns-block { padding-top: 0; }
}

/* strato 1 — foto dei passi successivi, in crossfade sopra quella dell'hero */
.js-hero-pin .hero-pin__sfondi {
  position: absolute; top: 0; bottom: 0; left: 50%; width: 100vw;
  transform: translateX(-50%);
  z-index: 3; pointer-events: none; margin: 0 !important; max-width: none;
}
.hero-pin__sfondi .hero-pin__sfondo { position: absolute; inset: 0; opacity: 0; }
.hero-pin__sfondi .hero-pin__sfondo figure,
.hero-pin__sfondi .hero-pin__sfondo img { width: 100%; height: 100%; }
.hero-pin__sfondi .hero-pin__sfondo img { object-fit: cover; }

/* strato 2 — velo + scrim: il velo piatto non bastava sulle foto chiare
   (ortofrutta), il testo crema ci spariva dentro. Qui il velo è al 55% e in
   più c'è una sfumatura più densa in alto e in basso, dove stanno occhiello,
   testo e barra.
   Vive come ::after del contenitore degli sfondi (z 3) e NON della sezione:
   il wrapper interno di North è uno stacking context (relative + z-index), e
   uno pseudo-elemento sulla sezione finiva sopra a TUTTO il contenuto, slide
   e testi compresi (era il velo scuro "sopra i testi"). Qui invece sta sopra
   le foto e sotto la slide (z 5). */
.js-hero-pin .hero-pin__sfondi::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  opacity: var(--velo, 0);
  /* velo-hero.png e' la sfumatura nera 60% / 0 / 0 / 80% (a 0, 22, 38 e
     100%) gia' composta sul nero al 55%, stirata a tutto schermo: lo stesso
     velo scritto come linear-gradient semitrasparente dimezza i fotogrammi
     dello scroll su Firefox, un'immagine costa quanto una tinta piatta. */
  background: url("assets/img/velo-hero.png") 0 0 / 100% 100% no-repeat;
}

/* La foto NON si ingrandisce: è già grande quanto lo schermo, nello strato
   .hero-pin__foto-strato (creato da home-hero.js, che ci sposta l'<img>), e a
   espandersi è la MASCHERA: un clip-path inset che parte dal riquadro della
   figure (--mt/--mr/--mb/--ml, in px dal JS) e arriva a zero, con il raggio
   che si annulla insieme. La tendina d'ingresso sta sul wrapper interno, così
   le due maschere non si pestano. */
.js-hero-pin .hero-pin__foto-strato {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  clip-path: inset(var(--mt, 0px) var(--mr, 0px) var(--mb, 0px) var(--ml, 0px)
                   round calc(8 * var(--px, 1px) * (1 - var(--velo, 0))));
  will-change: clip-path;
}
.js-hero-pin .hero-pin__foto-tenda { width: 100%; height: 100%; }
.js-hero-pin .hero-pin__foto-strato img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 0;
}
@media (prefers-reduced-motion: no-preference) {
  .js-hero-pin .hero-pin__foto-tenda { clip-path: inset(100% 0 0 0); }
  .js-hero-pin .hero-pin__foto-strato.e-pronta .hero-pin__foto-tenda {
    animation: dc-tenda 1.1s cubic-bezier(0.16, 1, 0.3, 1) 0.35s both;
  }
}
/* Finestre basse (portatili 1366x768, monitor larghi e bassi): la foto si
   restringe quanto basta a far stare tutto il contenuto crema nello stage,
   altrimenti il paragrafo finiva tagliato sotto il bordo. Il valore lo misura
   home-hero.js (--hero-foto-max); la riga si centra, cosi' le due etichette
   restano attaccate alla foto. */
.js-hero-pin .hero-pin .ensoul-column-wrapper:has(> .column > .hero-pin__foto) { max-width: var(--hero-foto-max, none); }
.js-hero-pin .hero-pin .ensoul-columns-block:has(> .ensoul-column-wrapper > .column > .hero-pin__foto) { justify-content: center; }

/* sequenza semplice: gli sfondi sono già tutti qui, il primo sempre acceso */
.js-hero-pin .hero-pin-semplice .hero-pin__sfondo:first-child { opacity: 1; }
.js-hero-pin .hero-pin-semplice { --velo: 1; }

/* Slide dell'hero (Figma 12508:1645): il titolo sta al centro dello spazio
   fra occhiello e paragrafo; il paragrafo, stessa colonna del paragrafo crema
   (33%, a destra), poggia appena sopra la barra. */
@media (min-width: 768px) {
  .js-hero-pin .hero-pin:not(.hero-pin-semplice) .hero-pin__slide .column > .hero-pin__titoli {
    margin-top: auto;
    margin-bottom: auto;
  }
  .js-hero-pin .hero-pin:not(.hero-pin-semplice) .hero-pin__slide .column > .hero-pin__testi {
    margin-bottom: var(--spacing-l);
  }
}
/* Prodotti in home (Figma 12508:1648): i due paragrafi stanno affiancati nel
   contenitore narrow, centrati, e il primo apre con una "I" Playfair 30px. */
@media (min-width: 768px) {
  .js-hero-pin .hero-pin-semplice .hero-pin__testi > div { justify-content: center; }
  /* nel Figma i due paragrafi occupano il contenitore narrow (814 a 1280) */
  .js-hero-pin .hero-pin-semplice .hero-pin__testi-int { max-width: var(--spacing-container-narrow); }
  /* Occhiello attaccato al titolo e paragrafi 40px sotto: i tre formano un
     gruppo centrato nello spazio sopra la barra (margini auto), non quattro
     blocchi distribuiti sull'altezza. */
  .js-hero-pin .hero-pin-semplice .hero-pin__slide .column > p:first-child { margin-top: auto; margin-bottom: 0; }
  .js-hero-pin .hero-pin-semplice .hero-pin__slide .hero-pin__testi {
    margin-top: calc(40 * var(--px, 1px));
    margin-bottom: auto;
  }
  .js-hero-pin .hero-pin-semplice .hero-pin__testo .ensoul-column-wrapper:first-child > .column > p::first-letter {
    font-family: var(--font-heading);
    font-size: calc(30 / 18 * 1em);
    line-height: 1;
  }
}

/* Nell'editor le foto dei passi non ci sono (le mostra solo il JS del front)
   e i testi crema della slide sparivano sul fondo crema: qui la slide ha un
   fondo verde scuro, come quando sta sopra al velo. */
.editor-styles-wrapper .hero-pin__slide {
  background-color: var(--color-primary-600);
  padding: var(--spacing-l) var(--spacing-m);
}

/* strato 3 — la slide sopra la foto */
.hero-pin__slide { z-index: 5; }
.js-hero-pin .hero-pin__slide {
  position: absolute; inset: 0; margin: 0 !important; max-width: none;
  display: flex; flex-direction: column; justify-content: center;
  /* in alto si aggiunge l'header: la foto passa a tutto schermo dietro la
     barra (che è in difference), ma l'occhiello non deve finirci sotto. */
  padding: calc(var(--altezza-header, 72px) + var(--spacing-l))
           var(--north-gutter) calc(var(--spacing-l) + var(--hero-extra, 0px));
  opacity: 0; pointer-events: none;
  transition: opacity 520ms ease;
}
.js-hero-pin .hero-pin.e-scena .hero-pin__slide { opacity: 1; }
/* la slide distribuisce i suoi quattro blocchi sull'altezza della sezione:
   occhiello in alto, titolo al centro, testo, riga in basso (come nel Figma) */
/* width:100%: il blocco ha mx-auto e nel flex a colonna della slide i margini
   automatici lo stringevano alla larghezza del contenuto (su mobile 130px:
   i titoli andavano a capo dentro la maschera alta una riga). */
.js-hero-pin .hero-pin__slide > .ensoul-columns-block { width: 100%; height: 100%; }
.js-hero-pin .hero-pin__slide > .ensoul-columns-block > .ensoul-column-wrapper { height: 100%; }
.js-hero-pin .hero-pin__slide > .ensoul-columns-block > .ensoul-column-wrapper > .column {
  height: 100%;
  justify-content: space-between;
}

/* Barra di avanzamento: la riga tenue è il binario, il ::after è il riempimento
   e la sua larghezza è legata all'avanzamento dello scroll (--avanzamento). */
.js-hero-pin .hero-pin__barra { position: relative; }
/* A FRAMMENTI, uno per passo (li crea home-hero.js): ognuno ha la sua traccia
   tenue e si riempie da sinistra mentre il suo passo avanza (--riempimento
   0→1); i passati restano pieni. L'hr del blocco tiene la riga, ma non si vede. */
.js-hero-pin .hero-pin__barra hr { visibility: hidden; }
.js-hero-pin .hero-pin__segmenti {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  gap: calc(12 * var(--px, 1px));
  pointer-events: none;
}
.js-hero-pin .hero-pin__segmento {
  position: relative;
  flex: 1 1 0;
  height: 2px;
  background: rgba(250, 250, 236, 0.25);
  overflow: hidden;
}
.js-hero-pin .hero-pin__segmento::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--color-secondary-50);
  transform: scaleX(var(--riempimento, 0));
  transform-origin: left center;
}

/* i testi della scena stanno su foto anche chiare: un'ombra minima li tiene
   leggibili senza incupire il bianco */
.js-hero-pin .hero-pin__slide p,
.js-hero-pin .hero-pin__slide h2 { text-shadow: 0 1px 18px rgba(0, 0, 0, 0.45); }

/* Carosello dei titoli A PASSI: la maschera è alta una riga e i titoli sono
   impilati uno sull'altro. L'attivo sta a 0, i passati sono usciti in alto,
   i futuri aspettano sotto: il cambio è una transizione, non un'interpolazione
   sullo scroll, così non si vede mai mezzo titolo. */
.js-hero-pin .hero-pin__titoli { overflow: hidden; }
/* la catena di wrapper del blocco columns deve portare l'altezza della
   maschera fino al contenitore dei titoli, o gli assoluti collassano */
.js-hero-pin .hero-pin__titoli > .ensoul-columns-block,
.js-hero-pin .hero-pin__titoli .ensoul-column-wrapper { height: 100%; }
.js-hero-pin .hero-pin__titoli-int > .column {
  position: relative;
  display: block;
  height: 100%;
}
/* Ancorati in alto con altezza propria, NON inset:0: North tiene il wrapper
   di colonna a height:auto !important (blocchi flex allineati), la colonna
   resta alta 0 e con inset:0 il testo avrebbe scatola nulla. La maschera
   (.hero-pin__titoli/.hero-pin__testi) è alta quanto il più alto, dal JS. */
.js-hero-pin .hero-pin__titolo {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
  transform: translateY(105%);
  opacity: 0;
  transition: transform 900ms var(--curva-testo),
              opacity 480ms ease;
  will-change: transform;
}
.js-hero-pin .hero-pin__titolo.e-attivo { transform: translateY(0); opacity: 1; }
.js-hero-pin .hero-pin__titolo.e-passato { transform: translateY(-105%); opacity: 0; }

/* Paragrafi A PASSI, stessa maschera dei titoli: uno per passo, impilati;
   l'attivo entra dal basso con dissolvenza, il passato esce in alto. */
.js-hero-pin .hero-pin__testi { overflow: hidden; }
.js-hero-pin .hero-pin__testi > .ensoul-columns-block,
.js-hero-pin .hero-pin__testi .ensoul-column-wrapper { height: 100%; }
.js-hero-pin .hero-pin__testi-int > .column { position: relative; display: block; height: 100%; }
.js-hero-pin .hero-pin__testo {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  margin: 0;
  opacity: 0;
  transform: translateY(16px);
  transition: transform 900ms var(--curva-testo) 120ms, opacity 480ms ease 120ms;
}
.js-hero-pin .hero-pin__testo.e-attivo { opacity: 1; transform: none; }
.js-hero-pin .hero-pin__testo.e-passato { transform: translateY(-16px); }
/* senza JS: tutti i paragrafi in colonna, leggibili */
html:not(.js-hero-pin) .hero-pin__testi .column > p + p { margin-top: var(--spacing-s); }

/* senza JS: la slide sta sotto l'hero, come una sezione normale */
html:not(.js-hero-pin) .hero-pin__sfondi { display: none; }

/* --- Focus: anello SOLO da tastiera (regola generale del tema) -------------
   animazioni.js mette html.usa-puntatore dopo un click/tap e la toglie al
   primo Tab: a mouse/dito nessun outline (neanche sul focus spostato via
   script: hamburger, prima voce del menu, popup), da tastiera resta il giallo
   di North (--focus-outline). */
html.usa-puntatore *:focus,
html.usa-puntatore *:focus-visible { outline: none !important; box-shadow: none; }

/* --- Mappa dei Comuni: entrata e respiro dei cerchi ------------------------
   L'entrata è la stessa grammatica dell'hero (risalita + dissolvenza) e scatta
   quando la sezione entra nel viewport (assets/js/animazioni.js aggiunge
   .e-dentro). Il respiro è volutamente minimo — scale 1 → 1.008/1.012/1.016 —
   con durate e sfasamenti diversi per cerchio, così non pulsano all'unisono. */

.mappa-comuni svg circle,
.mappa-comuni svg .cerchio { transform-box: fill-box; transform-origin: center; }

@media (prefers-reduced-motion: no-preference) {
  /* Nascondo lo stato di partenza SOLO se il JS c'è (html.js-anima, messo da
     uno snippet inline nell'head): senza, la mappa resterebbe invisibile. */
  /* Entrata dal CENTRO VERSO FUORI, in ordine: prima il cerchio piccolo, poi
     i due grandi; i nomi salgono dal più vicino a Udine al più lontano.
     L'indice --o lo calcola animazioni.js sulla distanza dal centro. */
  .js-anima .anima-entrata .cerchio,
  .js-anima .anima-entrata .comune { opacity: 0; }

  .anima-entrata.e-dentro .cerchio {
    animation: dc-cerchi-in 1s cubic-bezier(0.16, 1, 0.3, 1) both;
    animation-delay: calc(var(--o, 0) * 130ms);
  }
  .anima-entrata.e-dentro .comune {
    animation: dc-nome-in 0.9s var(--curva-testo) both;
    animation-delay: calc(0.35s + var(--o, 0) * 55ms);
  }

  /* Niente respiro: solo rotazione, molto lenta, versi alternati e durate
     prime fra loro perché il movimento non si legga mai come un ciclo. */
  .e-dentro .cerchio:nth-of-type(1) circle { animation: dc-gira-avanti 420s linear infinite; }
  .e-dentro .cerchio:nth-of-type(2) circle { animation: dc-gira-indietro 310s linear infinite; }
  .e-dentro .cerchio:nth-of-type(3) circle { animation: dc-gira-avanti 230s linear infinite; }
}

@keyframes dc-cerchi-in {
  from { opacity: 0; transform: scale(0.92); }
  to   { opacity: 1; transform: scale(1); }
}
@keyframes dc-nome-in {
  from { opacity: 0; transform: translateY(9px); }
  to   { opacity: 1; transform: none; }
}
/* respiro: ampiezze e periodi diversi per cerchio, sempre sotto il 3,5% */
@keyframes dc-respiro-l { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.015); } }
@keyframes dc-respiro-m { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.024); } }
@keyframes dc-respiro-s { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.033); } }
@keyframes dc-gira-avanti   { from { transform: rotate(0deg); }   to { transform: rotate(360deg); } }
@keyframes dc-gira-indietro { from { transform: rotate(360deg); } to { transform: rotate(0deg); } }

/* --- Testi: reveal all'ingresso nel viewport (li marca animazioni.js) ------
   Ogni parola sta in una maschera (.rv-w) e sale da sotto (.rv-i); le parole
   della stessa riga partono insieme, le righe successive con 90ms di ritardo
   e gli elementi che entrano insieme con 70ms l'uno dall'altro (i ritardi
   li scrive animazioni.js). La maschera è un clip-path e non overflow:hidden,
   così la linea di base non si sposta. Nei paragrafi con capolettera (.lead)
   la prima lettera resta testo nudo, fuori dalle maschere, così ::first-letter
   continua a prenderla; se qualcosa non si può spezzare resta la dissolvenza.
   Lo stato nascosto vive SOLO sotto .t-anima: i cloni della finestra (Chi
   siamo) perdono la classe e devono leggersi subito. */
.rv-w { display: inline-block; clip-path: inset(-0.22em -0.08em); }
.rv-i { display: inline-block; }
@media (prefers-reduced-motion: no-preference) {
  /* Corsa corta (60%): da sola non basta a nascondere la parola nella
     maschera (che sborda di 0.22em sotto la riga), quindi allo stato di
     partenza si aggiunge la dissolvenza. Stesso ritardo per le due. */
  .js-anima .t-anima .rv-i {
    opacity: 0;
    transform: translate3d(0, 60%, 0);
    transition: transform 1.15s var(--curva-testo), opacity 0.7s ease;
  }
  .js-anima .t-anima.e-dentro .rv-i { opacity: 1; transform: none; }
  .js-anima .t-anima:not(.e-spezzato) { opacity: 0; }
  .js-anima .t-anima.e-dentro:not(.e-spezzato) {
    animation: dc-sale 0.9s var(--curva-testo) both;
    animation-delay: var(--ritardo, 0s);
  }
}

/* --- Parallasse sulle immagini (lo muove animazioni.js) -------------------- */
@media (prefers-reduced-motion: no-preference) {
  .js-anima .parallasse { overflow: hidden; }
  .js-anima .parallasse img { will-change: transform; }
}


/* --- Prodotti: colonna immagini sticky, cambio a tendina -------------------
   A sinistra una sola finestra alta quanto il viewport che resta appesa; le
   quattro foto ci stanno dentro impilate nell'ordine del DOM (l'ultima sopra).
   Sono SCOPERTE tutte quelle fino all'indice attivo: avanzando, la foto
   successiva sale con la maschera SOPRA quella corrente, che resta ferma
   sotto; tornando indietro la stessa maschera riscende e riscopre la
   precedente, che non se n'era mai andata. Una sola tendina per cambio, in
   entrambi i versi. A destra i testi scorrono normali.
   L'indice attivo lo tiene animazioni.js (.prodotti-scroll). */
@media (min-width: 768px) {
  /* specificità doppia + min-height: il wrapper di colonna è un flex item e
     con i figli in absolute la sua altezza di contenuto è 0; la sola `height`
     non bastava a portarlo a schermo intero. */
  .js-anima .prodotti-scroll__media.prodotti-scroll__media {
    position: sticky;
    top: var(--barra-admin);
    height: calc(100dvh - var(--barra-admin));
    min-height: calc(100dvh - var(--barra-admin));
    align-self: flex-start;
    overflow: hidden;
  }
  /* ATTENZIONE: qui NON va ridichiarato position sul wrapper, o si mangia lo
     sticky della regola sopra (era il bug). Serve solo che il .column interno
     faccia da contenitore per le foto assolute e porti l'altezza. */
  /* NIENTE position:relative qui: il .column non ha altezza propria (i figli
     sono assoluti) e diventerebbe lui il blocco contenitore, azzerando
     inset:0. Lasciandolo statico le foto si ancorano al wrapper sticky, che
     è alto quanto il viewport. */
  .js-anima .prodotti-scroll__media > .column {
    position: static;
    display: block;
  }
  /* La classe finisce SIA sulla <section> SIA sul .ensoul-group interno: se
     la regola le prende entrambe, l'interno diventa assoluto dentro un
     esterno alto 0 e collassano tutte e due. Posiziono solo l'esterno e
     faccio riempire l'interno. */
  .js-anima .prodotti-scroll__media > .column > .prodotti-scroll__foto {
    position: absolute;
    inset: 0;
    clip-path: inset(100% 0 0 0);
    /* Il titolo sta dentro la tendina e compare quando la maschera lo
       raggiunge: con 260ms di attesa arrivava tardi. Partenza quasi subito,
       corsa un po' più lunga e la curva dei testi. */
    transition: clip-path 1000ms var(--curva-testo) 60ms;
  }
  .js-anima .prodotti-scroll__media > .column > .prodotti-scroll__foto.e-scoperta {
    clip-path: inset(0 0 0 0);
  }
  .js-anima .prodotti-scroll__foto > .ensoul-group { height: 100%; }
  /* Titoli fuori dalla tendina (li sposta animazioni.js): strato sopra le
     foto, uno alla volta. Stessa durata, stesso ritardo e stessa curva della
     tendina: il titolo nuovo sale verso il centro mentre la linea della
     maschera sale, e il vecchio se ne va nello stesso momento, non spazzato
     via dopo. Tornando indietro tutto si inverte con la maschera che scende. */
  .js-anima .prodotti-scroll__titoli {
    position: absolute;
    inset: 0;
    z-index: 2;
    pointer-events: none;
  }
  .js-anima .prodotti-scroll__titoli > * {
    position: absolute;
    left: var(--spacing-content-padding);
    right: var(--spacing-content-padding);
    top: 50%;
    margin: 0;
    opacity: 0;
    transform: translateY(calc(-50% + 70px));
    /* questa e' la transizione di USCITA: rapida e senza attesa, cosi' il
       titolo vecchio e' gia' via quando arriva il nuovo */
    transition: transform 550ms var(--curva-testo), opacity 300ms ease;
  }
  /* ENTRATA: parte dopo che il vecchio se n'e' andato, con la corsa della tendina */
  .js-anima .prodotti-scroll__titoli > .e-attivo {
    opacity: 1;
    transform: translateY(-50%);
    transition: transform 1000ms var(--curva-testo) 320ms, opacity 500ms ease 320ms;
  }
  .js-anima .prodotti-scroll__titoli > .e-passato { transform: translateY(calc(-50% - 70px)); }
  /* la prima resta scoperta finché il JS non decide altrimenti */
  html:not(.js-anima) .prodotti-scroll__foto { clip-path: none; position: static; }
}


/* --- Finestra (Chi siamo, "si propone di") ---------------------------------
   Stessa meccanica della sezione Valori di craneng: la lista degli obiettivi
   scorre NORMALMENTE; una banda terracotta, tenuta a metà viewport dal JS
   (animazioni.js, modulo finestra), inquadra un CLONE della lista mosso in
   sincrono. Ciò che attraversa la banda si legge crema su terracotta con
   l'etichetta "si propone di:" fissa a sinistra; a riposo la banda copre la
   prima riga, come nel Figma statico. Niente pin, niente scatti.
   Le righe vengono portate TUTTE all'altezza della più alta (min-height sul
   blocco colonne, messa dal JS e ricalcolata al resize): così la banda ha
   un'altezza sola, inquadra sempre una riga intera e alle due estremità
   coincide esattamente con la prima e con l'ultima.
   Vale anche su mobile (lì l'etichetta resta nella prima riga); con
   reduced-motion resta la lista statica del Figma. */
.finestra { position: relative; }
/* la prima riga cede il terracotta alla banda, che a riposo la copre */
.finestra-attiva .obiettivi-primo { background-color: transparent; }
.finestra-attiva .obiettivi-primo p,
.finestra-attiva .obiettivi-primo h3 { color: inherit; }
@media (min-width: 768px) {
  /* l'etichetta vera resta in flusso (tiene la geometria) ma la mostra la banda */
  .finestra-attiva .obiettivi-primo p { visibility: hidden; }
  .finestra-banda .finestra-banda__etichetta-clone { visibility: hidden; }
}
/* left/width li mette il JS copiandoli dalla <section> breakout della prima
   riga: 100vw qui sborderebbe di 4px (barra di scorrimento) rispetto alle
   righe vere, e la banda non coinciderebbe col clone. */
.finestra-banda {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 2;
  pointer-events: none;
  will-change: transform;
}
.finestra-banda__clip {
  position: relative;
  height: 100%;
  overflow: hidden;
  background-color: var(--color-secondary-500);
  color: var(--color-secondary-50);
}
.finestra-banda__lista { will-change: transform; }
.finestra-banda h3,
.finestra-banda p { color: var(--color-secondary-50); }
.finestra-banda .obiettivi-primo { background-color: transparent; }
.finestra-banda hr { visibility: hidden; }
@media (min-width: 768px) {
  .finestra-banda__etichetta {
    position: absolute;
    top: 50%;
    left: var(--finestra-sinistra, var(--north-gutter));
    width: var(--finestra-larghezza, auto);
    transform: translateY(-50%);
    margin: 0;
  }
}
/* su mobile l'etichetta sta nella prima riga (vera e clonata), niente fissa */
@media (max-width: 767px) {
  .finestra-banda__etichetta { display: none; }
}

/* --- Prodotti su mobile (Figma 12508:1905): foto piena col titolo, poi i testi,
   un prodotto dopo l'altro. animazioni.js sposta ogni foto davanti al suo testo
   sotto i 768px; niente sticky né tendina. */
@media (max-width: 767px) {
  .prodotti-scroll__foto { min-height: 100dvh; }
  /* il wrapper flex interno ha h-full, ma il gruppo ha solo una min-height e
     il 100% non si risolve: senza questo il titolo restava in cima */
  .prodotti-scroll__foto.ensoul-group > .flex { min-height: 100dvh; }
}


/* ==========================================================================
   Revisione Giulia — 2026-09-08
   ========================================================================== */

/* --- Margini desktop: il contenuto segue lo schermo ------------------------
   Il contenitore "content" era fisso a 1232px: a 1280 dava i 24px del Figma,
   ma a 1440 i margini salivano a 100px e a 1920 a ~344. Da 768px in su il
   contenitore diventa fluido, così il margine resta sempre il content-padding.
   I cap tipografici (heading 1070, narrow 814, paragraph 768) NON si toccano:
   quelli governano la lunghezza di riga, non il margine di pagina. */
@media (min-width: 768px) {
  :root {
    --spacing-container-content:
      calc(100vw - var(--scrollbar-compensation, 0px) - 2 * var(--spacing-content-padding));
  }
}

/* --- Header: sempre fisso, senza fondo, in difference ----------------------
   Il blend va sull'elemento FISSO, non sulla barra interna: position:fixed
   crea uno stacking context, quindi un mix-blend-mode messo dentro si
   fonderebbe solo con l'header stesso (cioè con niente) invece che con la
   pagina che gli scorre sotto. Il testo resta bianco e difference lo inverte
   da solo: scuro sul crema, chiaro sulle foto. */
.header-fixed { mix-blend-mode: difference; }
.header-fixed .header-bar,
.header-fixed.header-su-scuro .header-bar { background-color: transparent !important; }
.header-fixed .header-bar,
.header-fixed .header-bar p,
.header-fixed .header-bar a { color: #fff !important; }

/* "menu sempre fisso": non si nasconde più allo scroll. Il modulo header di
   animazioni.js continua a mettere la classe, ma qui non muove più nulla. */
.header-fixed.header-scroll-hide.header-nascosto,
.header-fixed.header-scroll-hide.header-hidden-on-scroll { transform: none; }

/* col pannello mobile aperto difference invertirebbe anche il verde scuro del
   pannello: lì si torna al bianco pieno su verde. */
body.header-menu-open .header-fixed { mix-blend-mode: normal; }
body.header-menu-open .header-fixed .header-bar,
body.header-menu-open .header-fixed .header-bar p,
body.header-menu-open .header-fixed .header-bar a { color: var(--color-secondary-50) !important; }

/* voce del menu desktop: da regular a bold sotto il puntatore. Specificità
   con .header-fixed: la regola del colore dell'header (piu' sopra) ha la sua
   transition e altrimenti si mangiava questa. */
.header-fixed .header-bar .wp-block-navigation-item__content,
.header-bar .wp-block-navigation-item__content {
  transition: color 320ms ease, background-color 320ms ease, font-weight 260ms var(--curva-testo);
}
.header-bar .wp-block-navigation-item__content:hover,
.header-bar .wp-block-navigation-item__content:focus-visible { font-weight: 700; }

/* --- Menu mobile: blocco dei link centrato verticalmente -------------------
   Il pannello è fixed top/bottom, ma il blocco colonne dentro restava alto
   quanto il contenuto (più il suo min-height 80vh), quindi il
   justifyContent:center non aveva spazio su cui centrare.
   NIENTE height:100% a catena: il pannello ha height:auto (è ancorato con
   top+bottom) e una percentuale su un genitore auto non si risolve. Il
   pannello diventa un flex a colonna e il blocco si prende quello che resta:
   così l'altezza è definita e le percentuali sotto funzionano. */
.header-fixed .header-menu-panel {
  display: flex !important;
  flex-direction: column;
}
.header-fixed .header-menu-panel > .ensoul-columns-block {
  flex: 1 1 auto;
  min-height: 0 !important;
}
.header-fixed .header-menu-panel > .ensoul-columns-block > .ensoul-column-wrapper,
.header-fixed .header-menu-panel > .ensoul-columns-block > .ensoul-column-wrapper > .column {
  height: 100%;
}
/* voci centrate, non appoggiate a sinistra */
.header-fixed .header-menu-panel > .ensoul-columns-block > .ensoul-column-wrapper > .column {
  align-items: center;
}
.header-menu-panel h3,
.header-menu-panel p { text-align: center; }

/* --- Aziende: la lista mobile (accordion) tutta in serif ------------------- */
.lista-aziende-mobile,
.lista-aziende-mobile p,
.lista-aziende-mobile h2,
.lista-aziende-mobile h3,
.lista-aziende-mobile button { font-family: var(--font-heading); }

/* --- Paragrafi grandi in serif: reveal in maschera -------------------------
   Una tendina che scopre il paragrafo dall'alto, non una riga per volta: le
   righe andrebbero spezzate in span e la capolettera (initial-letter) verrebbe
   ritagliata dalla maschera della prima riga.
   ATTENZIONE — la maschera è `mask-image`, NON `clip-path`: un clip-path che
   riduce l'elemento a zero lo rende invisibile anche a IntersectionObserver,
   che quindi non gli dà mai .e-dentro e il paragrafo resta nascosto per
   sempre (stallo circolare, verificato). Il mask agisce solo in pittura e
   l'observer continua a vederlo.
   La maschera è alta il doppio dell'elemento: metà nera (copre = visibile),
   metà trasparente (nasconde). Scorrendo la posizione da 100% a 0% la parte
   nera scende sul testo e lo scopre dall'alto.
   Il gancio è .t-anima, che mette animazioni.js: se il JS non gira non c'è
   maschera e il testo resta leggibile. */
@media (prefers-reduced-motion: no-preference) {
  .js-anima .lead p.t-anima,
  .js-anima .lead-sm p.t-anima {
    -webkit-mask-image: linear-gradient(to bottom, #000 50%, transparent 50%);
    mask-image: linear-gradient(to bottom, #000 50%, transparent 50%);
    -webkit-mask-size: 100% 200%;
    mask-size: 100% 200%;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: 0% 100%;
    mask-position: 0% 100%;
    transform: translateY(10px);
  }
  .js-anima .lead p.t-anima.e-dentro,
  .js-anima .lead-sm p.t-anima.e-dentro {
    -webkit-mask-position: 0% 0%;
    mask-position: 0% 0%;
    transform: none;
    transition: -webkit-mask-position 950ms cubic-bezier(0.16, 1, 0.3, 1),
                mask-position 950ms cubic-bezier(0.16, 1, 0.3, 1),
                transform 950ms cubic-bezier(0.16, 1, 0.3, 1);
  }
  /* qui il reveal generico dei testi non serve: farebbe due animazioni sullo
     stesso paragrafo (dissolvenza + tendina) */
  .js-anima .lead p.t-anima,
  .js-anima .lead-sm p.t-anima { opacity: 1; animation: none; }
}

/* --- Mobile: le fasce con foto a tutta pagina occupano lo schermo intero ----
   Richiesta Giulia ("le parti con le immagini in full screen devono occupare
   100vh"). dvh e non vh: su iOS la barra URL si nasconde allo scroll e vh
   resterebbe fermo sull'altezza a barra nascosta, lasciando una striscia
   scoperta. Il prodotti-scroll e l'hero pinnato sono già a 100dvh. */
@media (max-width: 767px) {
  /* !important: North scrive il min-height mobile in una regola con
     !important propria, e il display block! arriva da una utility. */
  .fascia-panorama.ensoul-group,
  .blocco-video.ensoul-group {
    min-height: 100dvh !important;
    display: flex !important;
    flex-direction: column;
    justify-content: center;
  }
}
