/* ═════════════════════════════════════════════════════════════
   motion.css — il sistema di movimento di gabrieledifazio.com

   Una sola grammatica per tutto il sito: l'inchiostro.
   L'inchiostro copre, si assorbe, lascia il testo. Da lì derivano
   il caricamento, il cambio pagina, l'entrata in scena di ogni
   elemento e i micro-movimenti al passaggio del cursore.

   Tre livelli di intensità, assegnati secondo il peso SEO della
   sezione (vedi AUDIT-SEO.md § 4 e § 6):

     · evidente  — H1, titoli di sezione, riconoscimenti, nomi dei
                   ristoranti. Sono i contenuti che portano traffico:
                   devono farsi guardare.
     · media     — schede articolo, paragrafi, servizi.
     · appena accennata — fotografie, footer, micro-etichette.
                   Sulle foto il movimento non deve mai competere
                   con l'immagine: è l'immagine il contenuto.

   Regola non negoziabile: nessun testo viene creato dal JavaScript
   e nessuno viene nascosto con display:none. Si anima solo opacity,
   transform e filter, così quello che legge Google resta identico
   a quello che legge una persona.
   ───────────────────────────────────────────────────────────── */

/* ─── TEMPI E CURVE ─────────────────────────────────────────── */
:root {
  /* Curve. La "ink" decelera a lungo: è quella che dà il tono romantico. */
  --ease-ink:   cubic-bezier(.16, .84, .32, 1);
  --ease-soft:  cubic-bezier(.22, .61, .36, 1);
  --ease-inout: cubic-bezier(.62, .02, .34, 1);
  --ease-out:   cubic-bezier(.33, 1, .68, 1);

  --dur-xs: .22s;
  --dur-s:  .38s;
  --dur-m:  .62s;
  --dur-l:  .92s;
  --dur-xl: 1.35s;

  /* Passo dello sfalsamento fra elementi della stessa famiglia. */
  --stagger: 70ms;

  /* Spessore e colori della barra di avanzamento perimetrale. */
  --rail-size: 2px;
  --rail-ink: #0b1d3d;
  --rail-light: #eeeeef;
}

/* La proprietà registrata serve ad animare il gradiente della
   maschera d'inchiostro: senza @property un gradiente non è
   interpolabile e la transizione salterebbe da 0 a 1. */
@property --ink-pos {
  syntax: '<percentage>';
  inherits: false;
  initial-value: 0%;
}

/* ═════════════════════════════════════════════════════════════
   1. BARRA DI AVANZAMENTO PERIMETRALE
   Non finisce più al margine destro: gira intorno allo schermo.
   Alto sinistra→destra, destra alto→basso, sotto destra→sinistra,
   sinistra basso→alto. Quattro segmenti, stessa altezza e stesso
   colore di prima, animati in scala (nessun ricalcolo di layout).
   ───────────────────────────────────────────────────────────── */
.scroll-rail {
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
}

.scroll-rail span {
  position: absolute;
  background: var(--rail-ink);
  transition: background var(--dur-s) var(--ease-soft);
  will-change: transform;
}

/* Alto: da sinistra verso destra */
.scroll-rail .rl-top {
  top: 0; left: 0;
  width: 100%; height: var(--rail-size);
  transform: scaleX(0);
  transform-origin: left center;
}
/* Destra: dall'alto verso il basso */
.scroll-rail .rl-right {
  top: 0; right: 0;
  width: var(--rail-size); height: 100%;
  transform: scaleY(0);
  transform-origin: center top;
}
/* Sotto: da destra verso sinistra */
.scroll-rail .rl-bottom {
  bottom: 0; left: 0;
  width: 100%; height: var(--rail-size);
  transform: scaleX(0);
  transform-origin: right center;
}
/* Sinistra: dal basso verso l'alto, e si chiude in alto a sinistra */
.scroll-rail .rl-left {
  bottom: 0; left: 0;
  width: var(--rail-size); height: 100%;
  transform: scaleY(0);
  transform-origin: center bottom;
}

/* Sopra le fotografie e le sezioni blu la barra si schiarisce,
   altrimenti sparisce dentro l'immagine. */
.scroll-rail span.is-light { background: var(--rail-light); }

/* La vecchia barra resta nel markup solo per compatibilità:
   se per qualche motivo motion.js non parte, non deve comparire
   una seconda barra sopra a quella nuova. */
#scrollProgress { display: none; }

/* ═════════════════════════════════════════════════════════════
   2. INCHIOSTRO — caricamento e cambio pagina

   Il velo blu copre lo schermo. Il bordo non è mai una linea
   retta: un filtro di turbolenza SVG lo sfrangia, così quando si
   ritira sembra inchiostro assorbito dalla carta invece di una
   tendina che si apre.

   Lo stato di partenza è "coperto" SOLO se il JavaScript è vivo
   (classe .js sull'<html>, messa da uno script inline nel <head>).
   Senza JavaScript il velo non esiste e la pagina si vede subito:
   una pagina bianca perché è saltato uno script è il modo più
   veloce per perdere una posizione su Google.
   ───────────────────────────────────────────────────────────── */

.ink-stage {
  position: fixed;
  inset: -8vmax;              /* margine per la deformazione del filtro */
  z-index: 10500;
  pointer-events: none;
  visibility: hidden;
  /* Nessun filtro a riposo. Il filtro di turbolenza costa parecchio su
     un elemento grande quanto lo schermo, e mentre la pagina carica il
     velo è comunque un rettangolo blu pieno: sfrangiarne il bordo non
     si vedrebbe, si pagherebbe soltanto. Si accende nei due momenti in
     cui serve davvero — quando si assorbe e quando ricopre — e si
     spegne subito dopo. */
  filter: none;
}

.js .ink-stage { visibility: visible; }

.ink-veil {
  position: absolute;
  inset: 0;
  background: var(--navy);
  /* La maschera dice quanta parte del velo è ancora inchiostro.
     `--ink-pos` è il bordo dell'inchiostro, misurato dall'alto:

       --ink-pos = -30%  → il bordo è sopra lo schermo: tutto coperto
       --ink-pos =  50%  → metà pagina scoperta, metà ancora blu
       --ink-pos = 130%  → il bordo è sotto lo schermo: tutto scoperto

     Il verso conta, e la prima volta l'avevo scritto al contrario:
     con `to top` e gli stop in ordine inverso il velo partiva
     trasparente e si riempiva di blu — cioè l'effetto opposto a
     quello che serve. Si vedeva la pagina, sfumava nel blu, e poi
     `is-clear` la tagliava di netto. */
  -webkit-mask-image: linear-gradient(to bottom,
      transparent var(--ink-pos),
      rgba(0,0,0,.55) calc(var(--ink-pos) + 9%),
      #000 calc(var(--ink-pos) + 26%));
  mask-image: linear-gradient(to bottom,
      transparent var(--ink-pos),
      rgba(0,0,0,.55) calc(var(--ink-pos) + 9%),
      #000 calc(var(--ink-pos) + 26%));
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  will-change: --ink-pos;
}

/* Stato di riposo: coperto. È lo stato con cui nasce ogni pagina.
   Il bordo sta sopra lo schermo, quindi dallo 0% in giù è tutto nero,
   cioè tutto inchiostro. */
.js .ink-veil { --ink-pos: -30%; }

/* L'inchiostro si assorbe e libera il testo, dall'alto verso il basso:
   la prima cosa che riemerge è il titolo, che è blu — l'inchiostro che
   si ritira sembra raccogliersi dentro le lettere.
   Il filtro sta sul contenitore, non sul velo: in CSS la maschera si
   applica dopo il filtro, quindi per sfrangiare il bordo della maschera
   il filtro deve stare un livello più su. */
.js .ink-stage.is-absorbing { filter: url('#ink-bleed'); }
.js .ink-stage.is-absorbing .ink-veil {
  transition: --ink-pos var(--dur-xl) var(--ease-ink);
  --ink-pos: 130%;
}

/* Assorbito del tutto: il velo esce di scena e smette di costare. */
.js .ink-stage.is-clear {
  visibility: hidden;
  filter: none;
}

/* Uscita: l'inchiostro cresce dal punto in cui hai cliccato e riempie
   lo schermo. Qui la maschera è radiale, centrata sul click: sembra che
   il colore parta dalla parola toccata. `--ink-pos` diventa il raggio
   della macchia, e riparte dal valore di riposo (-30%) per crescere
   fino a coprire tutto.

   Gli stop sono spostati di +30%, e non è un ritocco: il valore di
   riposo di `--ink-pos` è -30% perché quel numero serve alla maschera
   LINEARE dell'assorbimento, dove -30% vuol dire «coperto». Sulla
   maschera radiale lo stesso numero portava il primo stop a -44%: tre
   stop tutti negativi, cioè gradiente interamente trasparente. Per i
   primi 360 ms dopo il clic non compariva NIENTE — nessuna macchia —
   mentre la pagina sotto stava già arretrando. Si vedeva la pagina
   schiarirsi da sola, e solo dopo arrivava il blu: è il «flash bianco».
   Con +30% il primo stop parte da 0%: al primo fotogramma c'è già una
   goccia sfrangiata sotto il dito, e lo schermo è coperto del tutto a
   --ink-pos 70%, cioè a metà corsa invece che ai tre quarti.
   Se un giorno si cambia il valore di riposo (-30%), qui va cambiato
   l'offset dello stesso identico numero, o la goccia riparte in ritardo. */
.js .ink-stage.is-flooding {
  visibility: visible;
  filter: url('#ink-bleed');
}
.js .ink-stage.is-flooding .ink-veil {
  -webkit-mask-image: radial-gradient(circle at var(--ink-x, 50%) var(--ink-y, 50%),
      #000 calc(var(--ink-pos) + 30%),
      rgba(0,0,0,.5) calc(var(--ink-pos) + 40%),
      transparent calc(var(--ink-pos) + 44%));
  mask-image: radial-gradient(circle at var(--ink-x, 50%) var(--ink-y, 50%),
      #000 calc(var(--ink-pos) + 30%),
      rgba(0,0,0,.5) calc(var(--ink-pos) + 40%),
      transparent calc(var(--ink-pos) + 44%));
  transition: --ink-pos var(--dur-l) var(--ease-inout);
  --ink-pos: 175%;
}

/* La parola toccata è la sorgente: sbava un istante prima che il
   velo la raggiunga, così l'inchiostro sembra uscire da lì e non
   comparire dal nulla. */
.js .ink-source {
  animation: ink-source-bleed .5s var(--ease-inout) forwards;
}
@keyframes ink-source-bleed {
  0%   { filter: blur(0);   opacity: 1; }
  100% { filter: blur(5px); opacity: .25; }
}

/* Mentre si esce il resto della pagina arretra di un soffio.
   Serve a togliere l'impressione di taglio netto: la pagina si
   allontana, non viene tagliata via.

   L'arretramento non deve MAI arrivare prima dell'inchiostro. Con
   `opacity: .55` su --dur-m partiva insieme al velo ma su un tempo più
   corto: misurato fotogramma per fotogramma, a 310 ms la pagina era già
   a 0.81 e a 460 ms a 0.60, con la macchia ancora a metà schermo. Il
   fondo del sito è chiaro (#eeeeef): abbassare l'opacità qui sopra non
   allontana, SCHIARISCE — il pannello blu della bio in home si sbiancava
   per quasi mezzo secondo prima che il blu lo raggiungesse. Da fuori si
   legge come un lampo bianco, ed è esattamente quello che si vedeva
   premendo «Continua a leggere».

   Ora il velo dura --dur-l e l'arretramento pure, con 120 ms di ritardo:
   la pagina si muove SOTTO l'inchiostro, non davanti. E 0,9 invece di
   0,55, perché anche a corsa finita non deve schiarire nulla. */
.js body.is-leaving main,
.js body.is-leaving > section,
.js body.is-leaving > article,
.js body.is-leaving .site-footer {
  transition: opacity var(--dur-l) var(--ease-inout) 120ms,
              transform var(--dur-l) var(--ease-inout) 120ms;
  opacity: .9;
  transform: scale(.992);
}

/* Il filtro di turbolenza è nel DOM una volta sola, in fondo al body. */
.ink-defs {
  position: absolute;
  width: 0; height: 0;
  overflow: hidden;
  pointer-events: none;
}

/* ─── Schermata di caricamento ─────────────────────────────── */
/* Blu pieno, barra bianca che avanza. Stessa grammatica della
   barra di scorrimento: 2px, nessun bordo arrotondato. */
.ink-loader {
  position: fixed;
  inset: 0;
  z-index: 10600;
  background: var(--navy);
  display: none;
  pointer-events: none;
}
.js .ink-loader { display: block; }

.ink-loader-bar {
  position: absolute;
  top: 0; left: 0;
  height: var(--rail-size);
  width: 100%;
  background: var(--rail-light);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform .28s var(--ease-out);
  will-change: transform;
}

/* Il conteggio è discreto: sta in basso a destra come una nota
   a margine, non al centro come un contatore da videogioco. */
.ink-loader-count {
  position: absolute;
  right: 28px;
  bottom: 26px;
  font-family: 'PPNueMontreal', sans-serif;
  font-weight: 400;
  font-size: 10px;
  letter-spacing: 0.18em;
  color: rgba(238, 238, 239, .45);
  font-variant-numeric: tabular-nums;
}

/* Sotto la schermata di caricamento c'è il velo, dello stesso identico
   blu: l'unica cosa che si vede sparire sono la barra bianca e il
   conteggio. La dissolvenza è breve e deve concludersi prima che
   l'inchiostro cominci a ritirarsi — motion.js aspetta 240 ms. */
.ink-loader.is-done {
  opacity: 0;
  transition: opacity var(--dur-xs) var(--ease-soft);
}

/* Mentre l'inchiostro lavora la pagina non si trascina. */
html.ink-busy, html.ink-busy body { overflow: hidden; }

/* …e non si clicca. Il velo è dichiarato `pointer-events: none` perché
   a riposo non deve intercettare niente, ma finché copre la pagina i
   clic ci passavano attraverso: si finiva su un collegamento che non
   si vedeva ancora. Trovato provando l'anteprima, non leggendo il
   codice. Mentre l'inchiostro lavora, il velo prende i clic e li ferma. */
html.ink-busy .ink-stage { pointer-events: auto; }
.ink-loader { pointer-events: auto; }

/* ═════════════════════════════════════════════════════════════
   3. ENTRATA IN SCENA
   Un solo motore, tre intensità. L'attributo data-anim decide
   quale; data-anim-delay sfalsa gli elementi di una stessa
   famiglia. Lo stato di partenza vive qui, quello di arrivo si
   attiva con .is-in.
   ───────────────────────────────────────────────────────────── */

[data-anim] {
  transition-delay: var(--anim-delay, 0ms);
  will-change: opacity, transform, filter;
}
[data-anim].is-in { will-change: auto; }

/* Se il JavaScript non parte, tutto è visibile. */
html:not(.js) [data-anim] {
  opacity: 1 !important;
  transform: none !important;
  filter: none !important;
  clip-path: none !important;
}

/* ── evidente: l'inchiostro che si posa sulla carta ──
   Sfocatura che rientra a fuoco più una risalita corta. È la
   traduzione in piccolo dell'effetto del velo: stessa idea,
   stessa curva, un decimo della durata. */
[data-anim="ink"] {
  opacity: 0;
  transform: translate3d(0, 18px, 0);
  filter: blur(9px);
  transition: opacity var(--dur-l) var(--ease-ink),
              transform var(--dur-l) var(--ease-ink),
              filter var(--dur-l) var(--ease-ink);
}
[data-anim="ink"].is-in {
  opacity: 1;
  transform: none;
  filter: blur(0);
}

/* ── evidente: riga di testo che emerge da sotto una soglia ──
   Per i titoli grandi. Serve un contenitore con overflow nascosto,
   che il motore aggiunge da sé (.anim-clip). */
[data-anim="rise"] {
  opacity: 0;
  transform: translate3d(0, 108%, 0);
  transition: opacity var(--dur-m) var(--ease-ink),
              transform var(--dur-l) var(--ease-ink);
}
[data-anim="rise"].is-in {
  opacity: 1;
  transform: none;
}
.anim-clip { overflow: hidden; display: block; }

/* ── media: risalita semplice ── */
[data-anim="up"] {
  opacity: 0;
  transform: translate3d(0, 26px, 0);
  transition: opacity var(--dur-m) var(--ease-soft),
              transform var(--dur-m) var(--ease-soft);
}
[data-anim="up"].is-in { opacity: 1; transform: none; }

/* ── media: entra di lato. Per i testi nei margini della galleria ── */
[data-anim="in-left"],
[data-anim="in-right"] {
  opacity: 0;
  transition: opacity var(--dur-l) var(--ease-ink),
              transform var(--dur-l) var(--ease-ink);
}
[data-anim="in-left"]  { transform: translate3d(-22px, 0, 0); }
[data-anim="in-right"] { transform: translate3d(22px, 0, 0); }
[data-anim="in-left"].is-in,
[data-anim="in-right"].is-in { opacity: 1; transform: none; }

/* ── appena accennata: la fotografia ──
   Nessuno spostamento, nessuna sfocatura. Solo il velo che si
   ritira e un respiro di scala impercettibile. Sulle foto qualsiasi
   cosa di più diventa un effetto, e l'effetto ruba l'attenzione al
   contenuto che porta traffico da Google Immagini. */
[data-anim="veil"] {
  opacity: 0;
  transition: opacity var(--dur-l) var(--ease-soft);
}
[data-anim="veil"].is-in { opacity: 1; }

[data-anim="veil"] img,
[data-anim="veil"] picture {
  transform: scale(1.035);
  transition: transform var(--dur-xl) var(--ease-ink);
}
[data-anim="veil"].is-in img,
[data-anim="veil"].is-in picture { transform: scale(1); }

/* ── l'immagine più grande della pagina ──
   Serve una variante che NON tocchi l'opacità.

   Chrome sceglie l'elemento LCP — il «Largest Contentful Paint», la
   metrica di velocità che Google usa come segnale di posizionamento —
   fra quelli effettivamente disegnati, e **scarta tutto ciò che ha
   `opacity: 0`**. L'immagine di apertura e la copertina degli articoli
   sono esattamente gli elementi LCP di questo sito: farle entrare in
   dissolvenza significava dire a Chrome che per un secondo e mezzo la
   pagina non aveva ancora disegnato niente di grande.

   Qui si anima solo la scala. Il `transform` non toglie un elemento
   dai candidati LCP: la misura resta quella del disegno vero, e
   l'entrata in scena la fa comunque l'inchiostro, che è il gesto
   giusto per l'apertura. */
[data-anim="settle"] img,
[data-anim="settle"] picture {
  transform: scale(1.035);
  transition: transform var(--dur-xl) var(--ease-ink);
}
[data-anim="settle"].is-in img,
[data-anim="settle"].is-in picture { transform: scale(1); }

/* ── appena accennata: dissolvenza pura. Footer, micro-etichette ── */
[data-anim="fade"] {
  opacity: 0;
  transition: opacity var(--dur-m) var(--ease-soft);
}
[data-anim="fade"].is-in { opacity: 1; }

/* ── linea che si allunga. Divisori e sottolineature ── */
[data-anim="line"] {
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--dur-l) var(--ease-ink);
}
[data-anim="line"].is-in { transform: scaleX(1); }

/* ═════════════════════════════════════════════════════════════
   4. MICRO-MOVIMENTI
   Galleggiamento, calamita, respiro. Tutti guidati da un solo
   ciclo di animazione in motion.js: le trasformazioni si sommano
   in una variabile sola, così non si combattono fra loro.
   ───────────────────────────────────────────────────────────── */

/* Gli elementi guidati dal JavaScript non devono avere una
   transition sul transform, o il movimento continuo diventa
   appiccicoso. L'ammorbidimento lo fa il JavaScript, per
   interpolazione: è quello che evita gli strattoni. */
[data-float],
[data-magnet] {
  will-change: transform;
}

/* Il cursore sopra un elemento calamitato resta il cerchio del
   sito: nessun puntatore di sistema che rompe l'incantesimo. */
@media (hover: hover) and (pointer: fine) {
  [data-magnet] { cursor: none; }
}

/* ═════════════════════════════════════════════════════════════
   5. CURSORE — anteprima della pagina di destinazione
   Il cerchio cresce e dentro compare la copertina della pagina
   verso cui stai andando, con il titolo sotto.
   ───────────────────────────────────────────────────────────── */
.cursor-preview {
  position: fixed;
  left: 0; top: 0;
  width: 168px;
  height: 168px;
  margin: -84px 0 0 -84px;
  border-radius: 50%;
  overflow: hidden;
  pointer-events: none;
  z-index: 10001;
  opacity: 0;
  transform: scale(.35);
  transition: opacity var(--dur-s) var(--ease-soft),
              transform var(--dur-m) var(--ease-ink);
  /* Fondo bianco, non blu. Il cuore dell'apertura è fotografato su
     bianco: su fondo blu, e con l'immagine a opacità ridotta, arrivava
     grigiastro — sembrava velato invece che pulito. */
  background: #ffffff;
  will-change: transform, opacity;
}
.cursor-preview.is-visible {
  opacity: 1;
  transform: scale(1);
}
.cursor-preview img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Piena, non velata. */
  opacity: 1;
  transform: scale(1.08);
  transition: transform var(--dur-xl) var(--ease-ink);
}
.cursor-preview.is-visible img { transform: scale(1); }

/* Una velatura sottile solo dove poggia il titolo: senza, su una
   fotografia chiara il testo bianco sparisce. */
.cursor-preview::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 52%;
  background: linear-gradient(to bottom, rgba(11,29,61,0) 0%, rgba(11,29,61,.62) 100%);
  pointer-events: none;
}

/* Occhiello e titolo stanno tutti e due in basso, dentro la stessa
   velatura. Con l'occhiello in alto, su una fotografia chiara — il
   cuore su bianco — spariva del tutto. */
.cursor-preview .cp-label {
  position: absolute;
  z-index: 2;
  left: 0; right: 0;
  bottom: 22px;
  text-align: center;
  padding: 0 20px;
  font-family: 'PPNueMontreal', sans-serif;
  font-weight: 500;
  font-size: 11px;
  line-height: 1.3;
  letter-spacing: 0.04em;
  color: #eeeeef;
}
.cursor-preview .cp-kicker {
  position: absolute;
  z-index: 2;
  left: 0; right: 0;
  bottom: 54px;
  text-align: center;
  font-size: 8.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(238, 238, 239, .7);
}

/* Quando l'anteprima è aperta il pallino pieno sparisce: due
   cerchi sovrapposti sono un errore, non un effetto. */
.cursor.is-previewing { opacity: 0; }

@media (hover: none) {
  .cursor-preview { display: none; }
}

/* ═════════════════════════════════════════════════════════════
   6. CHI PREFERISCE MENO MOVIMENTO
   Non si spegne tutto: si tolgono spostamento, sfocatura e
   movimento continuo, e restano le dissolvenze brevi. Una
   transizione che sparisce del tutto disorienta quanto una
   che esagera.
   ───────────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-m: .2s;
    --dur-l: .2s;
    --dur-xl: .2s;
    --stagger: 0ms;
  }

  [data-anim] {
    transform: none !important;
    filter: none !important;
    transition: opacity .2s linear !important;
    transition-delay: 0ms !important;
  }

  .ink-stage { display: none !important; }
  .ink-loader { transition: opacity .2s linear; }
  .js .ink-source { animation: none; }
  .js body.is-leaving main,
  .js body.is-leaving > section,
  .js body.is-leaving > article,
  .js body.is-leaving .site-footer { transition: none; opacity: 1; transform: none; }

  [data-float], [data-magnet] { transform: none !important; }

  .scroll-rail span { transition: none; }
  .cursor-preview { transition: opacity .15s linear; transform: scale(1); }
}

/* ═════════════════════════════════════════════════════════════
   7. VISUALIZZATORE A PIENO SCHERMO — LA PILA
   L'apertura e il cambio di fotografia sono l'unico punto del
   sito dove il movimento può permettersi di essere ampio: lì
   l'immagine è il contenuto e nient'altro compete con lei.

   Il cambio non è più uno scambio nello stesso posto. Le
   fotografie ci sono tutte, in fila, incastrate una sotto
   l'altra, e a scorrere è la fila. Da qui la fine di is-in e
   is-out: con tre fotografie in scena, un'uscita che dissolve
   avrebbe mostrato la stessa immagine in due punti insieme.

   Una cosa sola si muove davvero — il nastro. Le lastre non
   traslano: cambiano solo il proprio arretramento, la propria
   sfocatura e il proprio velo. Posizione e scala non si
   contendono mai la stessa proprietà su nessun elemento, quindi
   non possono sfasarsi e le distanze restano rigide mentre la
   pila scorre. È esattamente questo che vuol dire «una pila di
   stampe tirata di lato».
   ───────────────────────────────────────────────────────────── */
.lightbox { transition: opacity var(--dur-s) var(--ease-out); }
.lightbox.open { transition: opacity var(--dur-m) var(--ease-ink); }

/* L'entrata: la stessa misura che aveva la vecchia immagine sola —
   una scala che rientra — ma più piccola, perché qui a muoversi non è
   una fotografia da 85vh, è tutta la scena. Il 6% di prima avrebbe
   spostato i bordi di più di cento pixel e si sarebbe letto come uno
   zoom; il 3,5% le fa posare senza che si veda che si sono mosse. */
.lightbox-stage { transition: transform var(--dur-l) var(--ease-ink); }

/* --dur-m sulla curva dell'inchiostro: una corsa di circa cinquecento
   pixel. A --dur-s si legge come uno scatto; a --dur-m, con la lunga
   decelerazione, si legge come una pila tirata di lato. La curva è
   monotona: arriva e si ferma, niente molla. */
.lightbox-track { transition: transform var(--dur-m) var(--ease-ink); }

.lb-slide {
  transition: transform var(--dur-m) var(--ease-ink),
              filter    var(--dur-m) var(--ease-soft),
              opacity   var(--dur-m) var(--ease-soft);
}
.lb-slide::after { transition: opacity var(--dur-m) var(--ease-soft); }

/* L'ENTRATA A VENTAGLIO.
   La fotografia scelta si ingrandisce — è la scala del palco, qui sopra
   — e da dietro le vicine escono e si aprono. Non c'è nessun fotogramma
   scritto e nessun ritardo calcolato: a visualizzatore chiuso le lastre
   stanno già raccolte dietro il centro (`--lb-raduna` a 7vw, ventaglio a
   zero) e `.lightbox.open` porta il raduno a 0 e apre l'angolo. Il resto
   lo fa la transizione che le lastre hanno già: un valore che cambia
   dentro una `transform` si interpola come qualunque altro.

   --dur-l e non --dur-m: il ventaglio è un'entrata, e le entrate
   importanti del sito durano 0,92 s. La corsa del nastro, che è un
   movimento di servizio, resta a --dur-m. Sono due gesti diversi e non
   devono finire insieme — è lo sfasamento a far leggere le vicine come
   qualcosa che esce da DIETRO la fotografia, e non come una fila che
   scorre.

   Lo sfalsamento fra la prima e la seconda vicina non è scritto:
   arriva da sé, perché la seconda ha il doppio dell'angolo da percorrere
   sulla stessa curva. Una decelerazione lunga su una distanza doppia
   arriva dopo, e il ventaglio si apre in ordine. */
.lightbox.open .lb-slide.is-prev,
.lightbox.open .lb-slide.is-next {
  transition: transform var(--dur-l) var(--ease-ink),
              filter    var(--dur-m) var(--ease-soft),
              opacity   var(--dur-m) var(--ease-soft);
}

/* L'ingrandimento è il gesto più lento del visualizzatore: è il momento
   in cui si smette di sfogliare e si guarda. --dur-xl è la durata che
   il sito riserva alle fotografie e a nient'altro. */
.lightbox.is-zoom .lb-slide.is-focus {
  transition: transform var(--dur-xl) var(--ease-ink);
}

/* Percorrere non è entrare. La durata lunga qui sopra è quella
   dell'ingrandimento — un gesto solo, all'inizio — ma vale per la stessa
   proprietà che sposta la fotografia mentre la si guarda, e a 1,35 s ogni
   trascinamento sarebbe di gomma. Le due regole che seguono la
   scavalcano, e devono stare DOPO: valgono (0,3,0) come lei, quindi
   decide l'ordine.
   Col dito o col mouse la transizione sparisce del tutto — l'immagine sta
   sotto la mano, non la insegue. Con le frecce resta, corta: lì il salto
   è discreto, e senza una curva sarebbe uno scatto. */
.lightbox.sta-trascinando .lb-slide.is-focus { transition: none; }
.lightbox.sta-percorrendo .lb-slide.is-focus {
  transition: transform var(--dur-s) var(--ease-ink);
}

/* Solo cinque livelli di composizione, non cinquantadue. */
.lightbox.open .lightbox-track { will-change: transform; }
.lightbox.open .lb-slide.is-scena { will-change: transform, filter; }

/* Il fotogramma muto.
   L'avvolgimento salta da una lastra di rincorsa alla sua gemella
   dall'altro capo. Le due portano la stessa fotografia, con le stesse
   vicine, delle stesse larghezze, agli stessi incastri: il fotogramma
   è identico e il salto non si vede — ma solo se in quel fotogramma
   non parte nessuna transizione. Da qui questa classe, che il
   JavaScript mette e toglie nello stesso giro. Vale (0,3,0) e (0,3,1)
   contro le regole qui sopra: vince senza !important. */
.lightbox-track.senza-moto,
.lightbox-track.senza-moto .lb-slide,
.lightbox-track.senza-moto .lb-slide::after { transition: none; }

.lightbox-close { transition: color var(--dur-s) var(--ease-soft); }
.lb-nav { transition: color var(--dur-s) var(--ease-soft); }

/* ═════════════════════════════════════════════════════════════
   7-bis. IL FORM DI CONTATTO
   Le durate sono quelle del sistema, non numeri nuovi: la riga che
   si accende sotto un campo è un movimento normale (--dur-m sulla
   curva dell'inchiostro), l'etichetta che si schiarisce è un
   cambio di colore (--dur-s, curva morbida), e il riempimento
   della parola «Invia» dura quanto quello delle voci del menu,
   perché è lo stesso gesto e deve sembrare la stessa cosa.
   ───────────────────────────────────────────────────────────── */
.contact-form .field-riga {
  transition: transform var(--dur-m) var(--ease-ink);
}
.contact-form label,
.contact-form .field-n {
  transition: color var(--dur-s) var(--ease-soft);
}
.contact-form input::placeholder,
.contact-form textarea::placeholder {
  transition: opacity var(--dur-s) var(--ease-soft);
}
/* Il segnaposto si toglie di mezzo quando si comincia a scrivere:
   resta leggibile finché serve a capire cosa va scritto, e sparisce
   nell'istante in cui non serve più. */
.contact-form .field:focus-within input::placeholder,
.contact-form .field:focus-within textarea::placeholder { opacity: .35; }

.cs-fill { transition: clip-path .62s var(--ease-ink); }
.contact-submit .cs-parola { transition: transform var(--dur-xs) var(--ease-soft); }

@media (prefers-reduced-motion: reduce) {
  .contact-form .field-riga,
  .cs-fill { transition: none; }
}

/* Il contatore entra dopo le fotografie. Regola già esistente: cambia
   solo che il selettore non porta più con sé le frecce cancellate. */
.lightbox .lightbox-counter {
  opacity: 0;
  transition: opacity var(--dur-m) var(--ease-soft) .2s;
}
.lightbox.open .lightbox-counter { opacity: .4; }

/* ─── Chi preferisce meno movimento ───
   ATTENZIONE ALLA POSIZIONE. Il blocco generale della sezione 6 sta
   PRIMA di questa sezione: le regole del visualizzatore scritte lì
   perderebbero contro quelle qui sopra per ordine di sorgente, e
   perderebbero in silenzio — nessun errore, solo un visualizzatore che
   continua a muoversi. Perciò stanno qui, in fondo, dove vincono senza
   !important.

   La composizione resta: le vicine sono ancora lì, ancora dietro,
   ancora sfocate, ancora in ombra, ancora cliccabili. Toglierle
   vorrebbe dire togliere la navigazione, che è il contrario di
   un'accomodazione. E una sfocatura ferma è profondità di campo, non
   movimento: non scorre, non lampeggia, non si muove. Se ne va tutto
   quello che viaggia.

   Il velo del fondo non si spegne: la sezione 6 riporta già --dur-m a
   .2s, e una transizione che sparisce del tutto disorienta quanto una
   che esagera. */
@media (prefers-reduced-motion: reduce) {
  .lightbox-stage,
  .lightbox-track,
  .lightbox .lb-slide,
  .lightbox .lb-slide::after { transition: none; }

  /* La scala d'ingresso si spegne alla fonte, non solo nel movimento:
     altrimenti la pila si aprirebbe a .92 e ci resterebbe.
     Stessa cosa per il raduno del ventaglio: senza transizione, le
     vicine resterebbero accatastate dietro il centro invece di stare al
     loro posto. Il ventaglio APERTO invece rimane — è una composizione
     ferma, non un movimento: le fotografie stanno di sbieco come stampe
     posate sul tavolo, e una fotografia inclinata non scorre, non
     lampeggia e non si muove. */
  .lightbox { --lb-open: 1; --lb-raduna: 0px; }

  .lightbox.open .lightbox-track,
  .lightbox.open .lb-slide.is-scena { will-change: auto; }
}

/* ═════════════════════════════════════════════════════════════
   8. APERTURA — il cuore
   Galleggiamento, richiamo verso il cursore e respiro di scala
   arrivano dal ciclo di motion.js. Qui resta solo quello che il
   CSS fa meglio: il segnale che l'immagine è cliccabile.
   ───────────────────────────────────────────────────────────── */
.hero-link {
  display: block;
  text-decoration: none;
  color: inherit;
}

.hero-img-frame { transition: filter var(--dur-l) var(--ease-ink); }
.hero-link:hover .hero-img-frame { filter: brightness(1.03) contrast(1.02); }

/* Sotto l'immagine compare la ragione per cui è cliccabile.
   Prima non c'era: la foto premiata non portava da nessuna parte. */
.hero-caption {
  display: block;
  text-align: center;
  margin-top: 18px;
  font-size: 9px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(11, 29, 61, 0.45);
  opacity: 0;
  transform: translateY(6px);
  transition: opacity var(--dur-m) var(--ease-ink),
              transform var(--dur-m) var(--ease-ink),
              color var(--dur-s) var(--ease-soft);
}
.hero-link:hover .hero-caption,
.hero-link:focus-visible .hero-caption {
  opacity: 1;
  transform: none;
  color: rgba(11, 29, 61, 0.8);
}

/* Il premio nell'apertura: la linea si allunga al passaggio. */
a.vertical-text {
  position: absolute;
  padding-bottom: 0;
}
a.vertical-text::after {
  content: "";
  position: absolute;
  left: -6px;
  top: 0;
  width: 1px;
  height: 0;
  background: currentColor;
  transition: height var(--dur-l) var(--ease-ink);
}
a.vertical-text:hover::after { height: 100%; }
a.vertical-text:hover { opacity: 1; }

/* ═════════════════════════════════════════════════════════════
   9. GALLERIA — il passaggio del cursore
   Appena percettibile: la fotografia si avvicina di un soffio e
   le sorelle intorno restano ferme. Il portfolio non deve
   sembrare un carosello di effetti.
   ───────────────────────────────────────────────────────────── */
.gallery-grid .grid-item img {
  transition: transform var(--dur-xl) var(--ease-ink),
              filter var(--dur-l) var(--ease-ink);
}
.gallery-grid .grid-item:hover img {
  transform: scale(1.045);
  filter: brightness(1.04);
}
.gallery-grid .grid-item::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--navy);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-m) var(--ease-soft);
}
.gallery-grid .grid-item:active::after { opacity: .12; }
