/* ═══════════════════════════════════════════════════════
   francescobortolan.it — v2.2
   Helvetica ovunque. Minimal bold. Arancione di branding.
   Nessun serif, nessun corsivo: il contrasto è di peso.
═══════════════════════════════════════════════════════ */

:root {
  --orange: #f7833f;
  --ink: #0e0e0e;
  --paper: #ffffff;
  --grey: #8d8d8d;
  --font: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  --panel-w: min(560px, 40vw);
  --ease: cubic-bezier(.72, 0, .22, 1);

  /* Molla vera: linear() campiona una curva elastica in molti punti,
     con un filo di scavalcamento finale. È ciò che distingue il
     movimento "di libreria" da una transizione CSS qualunque —
     e non costa una libreria, è CSS nativo. */
  --molla: linear(0, 0.0033, 0.0131 1.9%, 0.0525 3.9%, 0.1181 6%, 0.2101 8.2%,
                  0.4176 12.1%, 0.7583 17.7%, 0.9004 20.5%, 1.0093 23.3%,
                  1.0509 24.9%, 1.0787 26.6%, 1.0928 28.4%, 1.0937 30.4%,
                  1.0817 32.6%, 1.0455 36%, 1.0004 40.4%, 0.9767 43.6%,
                  0.9673 47.3%, 0.9793 54.3%, 1.0011 66.5%, 1.0028 74.7%, 1);
  /* uscite: veloci e senza rimbalzo — chi esce non deve farsi notare */
  --uscita: cubic-bezier(.55, 0, .35, 1);

}

* { box-sizing: border-box; margin: 0; padding: 0; }

html, body {
  height: 100%;
  overflow: hidden;
  overscroll-behavior: none;      /* niente rimbalzo elastico */
}
body {
  position: fixed;                /* la pagina non si muove: mai */
  inset: 0;
  width: 100%;
  font-family: var(--font);
  background: #000;
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
}

::selection { background: var(--orange); color: #fff; }

/* ═══════════════════════════════════════
   HOME
═══════════════════════════════════════ */
.stage {
  position: fixed;
  top: 0; bottom: 0; left: 0; right: 0;
  height: 100dvh;
  overflow: hidden;
}

/* ── Quando si apre la scheda, la macchina si riquadra ──
   A muoversi e' la fotografia, non la scena: `.stage` ha
   `overflow: hidden`, e spostando lei l'immagine veniva tagliata
   al suo bordo — quella era la striscia nera. Ora la scena resta
   ferma a coprire tutta la finestra e dentro di lei la fotografia
   scorre a sinistra allargandosi.

   Le due misure sono la stessa detta due volte: con l'origine sul
   bordo sinistro, `scale(1.08)` allunga l'immagine di 0,08·W verso
   destra, e lo scarto e' 8vw. Il bordo destro resta esattamente sul
   bordo della finestra — a ogni istante della corsa, non solo alla
   fine, perche' i due valori corrono con lo stesso tempo e la stessa
   curva. Non si scopre mai niente. */
@media (min-width: 701px) {
  body.mail-open .hero {
    transform: translateX(calc(-1 * var(--riquadro-scarto))) scale(var(--riquadro-scala));
    transition-delay: 0s;                /* in apertura parte per prima */
  }
}

.hero {
  position: absolute; inset: 0;
  background: #000;
  /* si allarga verso destra: il bordo sinistro resta al suo posto
     e l'immagine cresce dove serve coprire */
  transform-origin: left center;
  transition: transform var(--mail-durata) var(--mail-ease) var(--mail-ritardo);
}
.hero__media {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  /* La scala sta qui, non in JS: dev'esserci dal primo fotogramma,
     altrimenti al primo movimento del mouse la foto farebbe uno scatto.
     Il margine in più è quello che permette alla parallasse di spostarsi
     senza scoprire i bordi. */
  transform: scale(var(--z, 1)) translate(var(--px, 0px), var(--py, 0px));
}
@media (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .hero__media { --z: 1.06; }
}

.hero__scrim {
  position: absolute; inset: 0;
  pointer-events: none;
  background:
    linear-gradient(rgba(0,0,0,.42) 0%, rgba(0,0,0,0) 26%),
    linear-gradient(rgba(0,0,0,0) 52%, rgba(0,0,0,.62) 100%);
  opacity: 0;
}

/* Vignetta: chiude i bordi della fotografia e porta l'occhio al centro.
   Sta dentro .hero, quindi resta sotto la firma e i link. Solo da tablet
   in su: su telefono c'è già .hero__scrim acceso, e si sommerebbero. */
@media (min-width: 701px) {
  .hero::after {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: radial-gradient(115% 85% at 50% 42%,
                transparent 52%, rgba(0, 0, 0, .30) 100%);
  }
}

.signature {
  position: absolute;
  left: 50%; top: 45%;
  transform: translate(-50%, -50%) translate(var(--px, 0px), var(--py, 0px));
  width: clamp(300px, 34vw, 560px);
  user-select: none;
  pointer-events: none;
  filter: drop-shadow(0 2px 14px rgba(0,0,0,.35));
  opacity: 0;
  animation: fadeIn 1.1s ease .3s forwards;
}

/* I link sparsi vivono in una tela: le loro percentuali si misurano
   su di lei, non sulla finestra. Quando la scheda si apre la tela si
   accorcia a destra e le parole si ridistribuiscono nello spazio che
   resta — nessuna finisce sotto la scheda.

   Tutto questo vale **solo da tablet in su**. Su telefono la scheda
   e' un foglio che sale dal basso e la home non si sposta: la sua
   composizione e' un'altra, gia' approvata, e non va toccata. Per
   questo le regole stanno dentro il breakpoint e non fuori — una
   `.scatter { inset: 0 }` scritta per tutti arrivava fin li' e le
   spostava le parole. */
@media (min-width: 701px) {
  .scatter {
    position: absolute;
    inset: 0;
    transition: right var(--mail-durata) var(--mail-ease) var(--mail-ritardo);
  }
  body.mail-open .scatter { right: var(--spazio-libero); transition-delay: 0s; }

  /* Anche la firma si ricentra su quello che resta */
  body.mail-open .signature {
    left: calc(50% - var(--spazio-libero) / 2);
    transition-delay: 0s;
  }
  .signature { transition: left var(--mail-durata) var(--mail-ease) var(--mail-ritardo); }
}

/* Link sparsi */
.scatter__link {
  position: absolute;
  color: #fff;
  font-size: clamp(15px, 1.05vw, 20px);
  font-weight: 400;
  letter-spacing: .01em;
  text-decoration: none;
  cursor: default;
  text-shadow: 0 1px 8px rgba(0,0,0,.45);
  opacity: 0;
  animation: fadeIn .9s ease forwards;
  transform: translate(calc(var(--px, 0px) + var(--mx, 0px)),
                       calc(var(--py, 0px) + var(--my, 0px)));
  transition: color .25s, transform .45s var(--ease);
}
a.scatter__link { cursor: pointer; }
a.scatter__link:hover { color: var(--orange); text-shadow: none; }
.scatter__link.is-active {
  text-decoration: underline;
  text-underline-offset: 5px;
  text-decoration-thickness: 1px;
}

.scatter__link--portfolio { left: 17%;   top: 26.9%; animation-delay: .55s; }
.scatter__link--dream     { left: 77%;   top: 33.5%; animation-delay: .75s; }
/* about viene prima di contacts anche nello sguardo: si incontra
   salendo la pagina, non dopo */
.scatter__link--about     { left: 63.4%; top: 59.5%; animation-delay: .65s; }
.scatter__link--contacts  { left: 33%;   top: 75%;   animation-delay: .85s; }

@keyframes fadeIn { to { opacity: 1; } }

/* Richiamo al messaggio — in alto a destra, dove la scheda si è chiusa */
.mail-reopen {
  position: fixed;
  top: 34px; right: 40px;
  z-index: 15;
  display: flex;
  align-items: center;
  gap: 11px;
  background: none;
  border: none;
  cursor: pointer;
  font-family: var(--font);
  font-size: clamp(14px, .95vw, 17px);
  font-weight: 400;
  letter-spacing: .01em;
  color: #fff;
  text-shadow: 0 1px 8px rgba(0,0,0,.45);
  padding: 6px 0;
  opacity: 0;
  pointer-events: none;
  transform: translateX(14px);
  transition: opacity .55s ease, transform .55s var(--ease), color .25s;
}
body:not(.mail-open):not(.view-portfolio) .mail-reopen {
  opacity: 1;
  pointer-events: auto;
  transform: none;
  transition-delay: .45s;
}
.mail-reopen:hover { color: var(--orange); text-shadow: none; }

.mail-reopen__ico {
  width: 24px; height: 18px;
  flex: 0 0 auto;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform .35s var(--ease);
}
.mail-reopen:hover .mail-reopen__ico { transform: translateY(-2px); }

/* pallino di notifica sulla busta */
.mail-reopen::after {
  content: '';
  position: absolute;
  left: 19px; top: 3px;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--orange);
  animation: pulse 2.4s ease infinite;
}
@keyframes pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(247,131,63,.55); }
  55%      { box-shadow: 0 0 0 8px rgba(247,131,63,0); }
}

/* ═══════════════════════════════════════
   MAIL — la scheda bianca sopra la home
   Non c'e' piu' il pannello arancione: nessun fondale colorato a
   tutta altezza, nessuna colonna che spinge via la pagina. Resta
   la scheda di sempre — stessa misura, stessa aria, stesso testo —
   appoggiata a destra, al centro dell'altezza, staccata dal bordo.
   `.mailpanel` non e' piu' una superficie: e' solo il telaio che
   tiene la scheda in posizione, e non si vede.
═══════════════════════════════════════ */

/* La scheda conserva la misura che aveva dentro il pannello:
   la larghezza del pannello meno i suoi margini interni. Cambia
   dove sta, non quanto e' larga. */
:root {
  --scheda-w: calc(var(--panel-w) - 2 * clamp(24px, 2.4vw, 44px));
  /* Lo spazio che la home lascia libero: la scheda, il suo margine
     destro e un respiro a sinistra perche' non si tocchino mai. */
  --scheda-margine: 48px;
  /* Il riquadro: la scena si allarga del 6% e scorre a sinistra
     della stessa quantita' — 1.06 su una finestra larga W lascia
     scoperti 0.06·W, ed e' esattamente lo scarto. Nessun vuoto. */
  --riquadro-scala: 1.08;
  --riquadro-scarto: 8vw;
  /* Quanto si accorcia la tela delle parole: la scheda, il suo
     margine e un respiro, meno lo scarto che la scena ha gia'
     fatto per conto suo. */
  --spazio-libero: calc(var(--scheda-w) + var(--scheda-margine) + 24px);
  /* Un solo tempo e una sola curva per tutto il movimento: la home
     e la scheda partono e arrivano insieme. In uscita, al contrario. */
  /* Il movimento e' lento e pesante: parte deciso e si posa piano,
     senza mai andare oltre. La scheda arriva un attimo dopo, mentre
     la scena si sta ancora spostando; in chiusura e' lei ad andarsene
     per prima, e la fotografia torna al suo posto con calma. */
  --mail-durata: .62s;
  --mail-ease: cubic-bezier(.22, 1, .36, 1);
  --mail-ritardo: .1s;
}

.mailpanel {
  position: fixed;
  top: 0; bottom: 0; right: 0; left: auto;
  width: auto;
  background: none;
  display: flex;
  align-items: center;              /* centrata in altezza */
  justify-content: flex-end;
  padding-right: var(--scheda-margine);   /* mai attaccata al bordo */
  z-index: 20;
  pointer-events: none;             /* la home dietro resta viva */
}

/* Un velo appena accennato: la home deve restare chiaramente
   leggibile, serve solo a staccare la scheda dal fondo. */
.mailpanel__velo {
  position: fixed;
  inset: 0;
  background: rgba(10, 9, 8, .05);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--mail-durata) var(--mail-ease) var(--mail-ritardo);
}
body.mail-open .mailpanel__velo { opacity: 1; transition-delay: 0s; }

/* L'entrata: dissolvenza e un passo da destra. La chiusura e'
   la stessa strada al contrario — stesse curve, stessi tempi. */
.mailpanel__scroll {
  position: relative;
  flex: 0 0 auto;
  width: var(--scheda-w);
  max-width: calc(100vw - 96px);
  padding: 0;
  opacity: 0;
  transform: translateX(30px);
  pointer-events: none;
  /* nessun ritardo qui: in chiusura la scheda parte per prima */
  transition: opacity var(--mail-durata) var(--mail-ease),
              transform var(--mail-durata) var(--mail-ease);
}
body.mail-open .mailpanel__scroll {
  opacity: 1;
  transform: none;
  pointer-events: auto;
  /* in apertura arriva mentre la scena si sta ancora spostando */
  transition-delay: var(--mail-ritardo);
}

/* La chiusura e' entrata nella scheda, in alto a destra: sta
   nel margine bianco, sopra la riga del mittente. Stesso segno
   di prima, solo in inchiostro invece che in bianco. */
.mailpanel__close {
  position: absolute;
  top: 13px; right: 13px;
  width: 30px; height: 30px;
  padding: 8px;
  background: none;
  border: none;
  cursor: pointer;
  z-index: 3;
  opacity: .45;
  transition: transform .35s var(--ease), opacity .25s;
}
.mailpanel__close:hover { transform: rotate(90deg); opacity: 1; }
.mailpanel__close svg { width: 100%; height: 100%; display: block; }
.mailpanel__close line {
  stroke: var(--ink);
  stroke-width: 1.8;
  stroke-linecap: round;
}

/* Messaggio in stile Apple Mail: bianco, angoli ampi, respiro */
.mail-card {
  background: var(--paper);
  color: var(--ink);
  border-radius: 26px;
  padding: clamp(26px, 2.3vw, 40px) clamp(24px, 2.2vw, 38px) clamp(24px, 2.2vw, 34px);
  box-shadow: 0 24px 60px rgba(0,0,0,.20), 0 2px 10px rgba(0,0,0,.08);
  /* Il movimento sta sul telaio (§ .mailpanel__scroll): qui la
     scheda resta ferma, o i due spostamenti si sommerebbero. */
  max-height: calc(100dvh - 96px);
  overflow-y: auto;
  scrollbar-width: thin;
}

/* intestazione: avatar · mittente · orario */
.mail-card__top {
  display: flex;
  align-items: center;
  gap: 13px;
  padding-bottom: 20px;
  margin-bottom: 20px;
  border-bottom: 1px solid #ececec;
}
.mail-avatar {
  width: 42px; height: 42px;
  flex: 0 0 auto;
  border-radius: 50%;
  background: var(--ink);
  color: #fff;
  display: grid;
  place-items: center;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: .01em;
}
.mail-from {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.mail-from__name {
  font-size: 16px;
  font-weight: 700;
  letter-spacing: -.015em;
  color: var(--ink);
}
.mail-from__to { font-size: 13px; color: var(--grey); }
.mail-time {
  margin-left: auto;
  flex: 0 0 auto;
  font-size: 13px;
  color: var(--grey);
}

.mail-subject {
  font-size: clamp(24px, 2vw, 34px);
  font-weight: 700;
  letter-spacing: -.03em;
  line-height: 1.05;
  color: var(--ink);
  margin-bottom: 18px;
}

.mail-body {
  font-size: clamp(13.5px, .92vw, 16px);
  line-height: 1.6;
}
.mail-body p + p { margin-top: 1em; }
.mail-body a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--orange);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
  transition: color .2s;
}
.mail-body a:hover { color: var(--orange); }
.mail-body strong { font-weight: 700; }

.mail-sign { margin-top: 1.8em !important; }
.mail-sign b { font-weight: 700; }

/* azioni: pulsante pillola come su iOS */
.mail-actions {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 30px;
  padding-top: 22px;
  border-top: 1px solid #ececec;
}
.mail-btn {
  flex: 0 0 auto;
  background: var(--ink);
  color: #fff;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: -.01em;
  text-decoration: none;
  padding: 11px 26px;
  border-radius: 999px;
  transition: background .22s, transform .22s var(--ease);
}
.mail-btn:hover { background: var(--orange); transform: translateY(-1px); }
.mail-addr {
  font-size: 13px;
  color: var(--grey);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}


/* ═══════════════════════════════════════════════════════
   TRANSIZIONI FRA VISTE
   Il browser fotografa il prima e il dopo e interpola.
   Mentre lo fa, le dissolvenze fatte a mano vanno spente:
   altrimenti il movimento verrebbe animato due volte.
═══════════════════════════════════════════════════════ */
html[data-vt] .page,
html[data-vt] .portfolio { transition: none; }

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: .52s;
  animation-timing-function: var(--ease);
  animation-fill-mode: both;
}

/* Parallasse in discesa. Sotto la home c'è una grande lavagna bianca:
   entrando non si dissolve niente, si SCENDE. La lavagna sale da sotto
   come un foglio solido — nessuna opacità, altrimenti l'illusione cade —
   e la home si ritira verso l'alto a metà velocità. Sono i due ritmi
   diversi a dare la profondità.

   Attenzione all'ordine: l'istantanea nuova è già disegnata sopra la
   vecchia, quindi basta farla salire perché copra la home. */
html[data-vt="dentro"]::view-transition-old(root),
html[data-vt="dentro"]::view-transition-new(root),
html[data-vt="fuori"]::view-transition-old(root),
html[data-vt="fuori"]::view-transition-new(root) {
  animation-duration: 1.15s;
  /* Non un expo-out: quello parte a razzo e sembra uno scatto.
     Questa accelera piano e frena a lungo — una discesa ha peso. */
  animation-timing-function: cubic-bezier(.62, .02, .16, 1);
}

html[data-vt="dentro"]::view-transition-old(root) { animation-name: vt-home-su; }
html[data-vt="dentro"]::view-transition-new(root) { animation-name: vt-lavagna-sale; }
/* Il ritorno scorre nello stesso verso dell'andata: tutto sale.
   La scheda risale via dallo schermo — e per vederla serve tenerla
   SOPRA l'istantanea della home, che di suo starebbe davanti. */
html[data-vt="fuori"]::view-transition-old(root)  { animation-name: vt-lavagna-giu; z-index: 2; }
html[data-vt="fuori"]::view-transition-new(root)  { animation-name: vt-home-scende; z-index: 1; }

/* La home si ritira in alto, piano. Nessuna scala: ridimensionando
   l'istantanea si scoprono i bordi, ed è quella la striscia sbagliata
   che compariva in cima. Solo spostamento, e di poco. */
@keyframes vt-home-su {
  from { transform: translateY(0); }
  to   { transform: translateY(-14%); }
}
/* la lavagna sale da sotto, piena: nessuna dissolvenza */
@keyframes vt-lavagna-sale {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}
/* IL RITORNO È L'ANDATA AL CONTRARIO — la porta si richiude
   ripercorrendo la stessa strada. Entrando la lavagna era salita
   da sotto e la home si era ritirata in alto del 14%; tornando la
   lavagna riscende da dov'era venuta e la home ridiscende al suo
   posto, dall'alto.

   Questa coppia è l'unica che non lascia buchi: mentre la lavagna
   scende scopre la parte ALTA dello schermo, ed è esattamente lì
   che sta arrivando la home. Se scendessero entrambe alla stessa
   velocità non ci sarebbe parallasse; se la home venisse dal basso
   resterebbe una banda scoperta in cima. */
@keyframes vt-lavagna-giu {
  from { transform: translateY(0); }
  to   { transform: translateY(100%); }
}
@keyframes vt-home-scende {
  from { transform: translateY(-14%); }
  to   { transform: translateY(0); }
}
}

/* Niente ombra sul bordo della lavagna: in movimento diventava una
   banda scura che saliva sullo schermo, e si leggeva come un difetto.
   Il foglio pieno che copre basta già a dare la profondità. */

/* fra due viste interne il salto è breve: cambia il contenuto, non il luogo */
html[data-vt="lato"]::view-transition-old(root),
html[data-vt="lato"]::view-transition-new(root) { animation-duration: .34s; }

@keyframes vt-sale {
  from { opacity: 0; transform: translateY(26px); }
  to   { opacity: 1; transform: none; }
}
@keyframes vt-arretra {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: scale(.985); }
}
@keyframes vt-scende {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translateY(22px); }
}
@keyframes vt-avanza {
  from { opacity: 0; transform: scale(1.012); }
  to   { opacity: 1; transform: none; }
}

/* L'intestazione è la stessa cosa in portfolio, about e contatti:
   dandole un nome il browser la riconosce e la fa scivolare da una
   vista all'altra invece di ricrearla. Le viste nascoste hanno
   display:none, quindi il nome resta unico e non va in conflitto. */
.pf-head { view-transition-name: intestazione; }

@media (prefers-reduced-motion: reduce) {
  .pf-head { view-transition-name: none; }
}


/* ═══════════════════════════════════════════════════════
   GRANA, CURSORE, PARALLASSE
   Lo strato di pellicola sopra tutto, il cursore su misura
   e la profondità allo scorrimento. Nessuna libreria.
═══════════════════════════════════════════════════════ */

/* ── Grana ──────────────────────────────────────────────
   Rumore generato al volo da un filtro SVG: nessun file da
   scaricare. Sta sopra ogni cosa e non intercetta il mouse.
   In "overlay" scurisce appena la carta bianca e schiarisce
   appena la fotografia: si comporta come grana vera.        */
.grana {
  position: fixed;
  top: -10%; left: -10%;
  width: 120%; height: 120%;
  z-index: 80;
  pointer-events: none;
  opacity: .055;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23g)'/%3E%3C/svg%3E");

  /* Nome proprio: così durante un cambio di vista il browser la tratta
     a parte e la lascia ferma, invece di trascinarla con la pagina. */
  view-transition-name: grana;
}

/* Il tremolio costa un livello grande da comporre: lo accendo
   solo su schermi ampi, e mai a chi ha chiesto meno movimento. */
@media (min-width: 861px) and (prefers-reduced-motion: no-preference) {
  .grana { animation: grana .7s steps(5) infinite; }
}
@keyframes grana {
  0%   { transform: translate(0, 0); }
  20%  { transform: translate(-3%, 2%); }
  40%  { transform: translate(2%, -3%); }
  60%  { transform: translate(-2%, -2%); }
  80%  { transform: translate(3%, 1%); }
  100% { transform: translate(0, 0); }
}

/* ── Cursore su misura ──────────────────────────────────
   Il punto segue esatto, così non si perde precisione;
   l'anello insegue con ritardo. In "difference" resta
   leggibile sia sul nero sia sul bianco, senza doppioni.   */
.cursore {
  position: fixed;
  inset: 0;
  z-index: 95;
  pointer-events: none;
  mix-blend-mode: difference;
  opacity: 0;
  transition: opacity .3s ease;
}
html.cursore-su .cursore { opacity: 1; }

.cursore__punto,
.cursore__anello {
  position: absolute;
  top: 0; left: 0;
  border-radius: 50%;
  will-change: transform;
}
.cursore__punto {
  width: 6px; height: 6px;
  background: #fff;
}
.cursore__anello {
  width: 34px; height: 34px;
  border: 1px solid rgba(255, 255, 255, .8);
  transition: width .3s var(--ease), height .3s var(--ease),
              border-color .3s ease, background .3s ease;
}
html.cur-attivo .cursore__anello { width: 56px; height: 56px; }
html.cur-video  .cursore__anello {
  width: 82px; height: 82px;
  background: rgba(255, 255, 255, .16);
  border-color: transparent;
}

/* Il cursore di sistema si nasconde solo quando il nostro è acceso:
   se motion.js non parte, resta quello vero e non si perde nulla. */
@media (pointer: fine) {
  html.cursore-su,
  html.cursore-su * { cursor: none; }
}

/* mentre il puntatore lo tiene, il magnete non deve rincorrere
   la transizione: quella serve solo al ritorno a posto */
.magnete-vivo { transition: none !important; }

/* ═══════════════════════════════════════
   CONTATTI — intestazione su carta
   (il portfolio ha la sua, scura, più sotto)
═══════════════════════════════════════ */
.pf-head {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 20px;
  padding: 26px clamp(24px, 4vw, 70px) 22px;
  /* trasparente: sotto c'e' la carta, e una fascia bianca la
     taglierebbe in due. La pagina non scorre, quindi non serve
     coprire nulla mentre scivola via. */
  background: none;
  pointer-events: none;
}
.pf-head > * { pointer-events: auto; }

.pf-back {
  background: none;
  border: none;
  font-family: var(--font);
  font-size: clamp(14px, .95vw, 17px);
  font-weight: 400;
  letter-spacing: .01em;
  color: var(--ink);
  cursor: pointer;
  padding: 4px 0;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  transition: color .25s;
}
.pf-back .arr { display: inline-block; transition: transform .3s var(--ease); }
.pf-back:hover { color: var(--orange); }
.pf-back:hover .arr { transform: translateX(-5px); }
.pf-back span:last-child { padding-bottom: 2px; }
.pf-back:hover span:last-child {
  text-decoration: underline;
  text-underline-offset: 5px;
  text-decoration-thickness: 1px;
}



/* ── Selettore di categoria: stessa voce dei link sparsi della home ── */


/* ═══════════════════════════════════════
   PORTFOLIO — una sala per lavoro
   Nessun pannello, nessun bordo: il fotogramma prende tutto
   lo schermo e le poche parole galleggiano sopra di lui.
   Si scorre in verticale e ogni lavoro tiene una schermata.
═══════════════════════════════════════ */
.portfolio {
  position: fixed; inset: 0;
  height: 100dvh;
  background: #000;
  color: #fff;
  overflow: hidden;                 /* a scorrere è la scena, non la vista */
  z-index: 30;
}
/* `hidden` deve vincere, altrimenti la vista spenta resta a
   raccogliere i clic sopra la home */
.portfolio[hidden] { display: none; }
body.view-portfolio { overflow: hidden; }

/* ── le parole sopra l'immagine ──────────────────────────────
   Indice e firma non stanno su un fondo loro: galleggiano sul
   fotogramma. La voce è quella dei link sparsi della home —
   stesso corpo, stesso peso, stessa interlettera, stessa
   sottolineatura sotto la sola voce attiva. */
.pf-filters {
  position: absolute;
  top: 7.8%;
  left: 5.5%;
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: clamp(10px, 1.2vh, 18px);
}
.pf-filter {
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  font-family: var(--font);
  font-size: clamp(15px, 1.05vw, 20px);
  font-weight: 400;
  letter-spacing: .01em;
  line-height: 1.1;
  color: #fff;
  text-align: left;
  text-shadow: 0 1px 8px rgba(0, 0, 0, .45);
  transition: color .3s ease;
}
.pf-filter:hover { color: var(--orange); }
.pf-filter.is-on {
  text-decoration: underline;
  text-underline-offset: 5px;
  text-decoration-thickness: 1px;
}

/* La firma è il tasto per tornare a casa: qui non c'è altro
   modo di uscire, ed è il segno che sulla home sta al centro. */
.pf-firma {
  position: absolute;
  top: 6.5%;
  right: 5%;
  z-index: 10;
  width: clamp(120px, 11vw, 212px);
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  line-height: 0;
  filter: drop-shadow(0 2px 14px rgba(0, 0, 0, .35));
  transition: opacity .3s ease, transform .45s var(--ease);
}
.pf-firma img { width: 100%; height: auto; display: block; user-select: none; }
.pf-firma:hover { opacity: .75; transform: translateY(-2px); }

/* ── la scena: una sala sotto l'altra ── */
.pf-scena {
  position: relative;
  height: 100dvh;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  scroll-snap-type: y proximity;    /* accompagna, non costringe */
  scrollbar-width: none;
}
.pf-scena::-webkit-scrollbar { display: none; }

.pf-sala {
  position: relative;
  display: block;
  width: 100%;
  height: 100dvh;
  scroll-snap-align: start;
  border: none;
  background: #000;
  padding: 0;
  cursor: pointer;
  font-family: var(--font);
  color: #fff;
  overflow: hidden;
}

/* Il fotogramma riempie lo schermo da bordo a bordo. Qui il
   taglio è voluto: una schermata sola, piena, vale più del
   rispetto del formato originale. */
.pf-sala__media {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: #000;
}
.pf-sala__media img {
  width: 100%;
  height: 100%;
  /* `cover`: la sala e' una schermata piena, dal bordo al bordo. I
     file sono 4:3 e la finestra no, quindi qualcosa si perde — ed e'
     accettato: l'impatto vale piu' del rispetto di ogni pixel
     esportato. Copertina e clip usano la **stessa** regola, o al
     passaggio del mouse la scena salterebbe da un'inquadratura
     all'altra. */
  object-fit: cover;
  object-position: center;
  display: block;
  border: 0;
}
/* Il player non si può ritagliare con `object-fit`: il video vive
   dentro l'iframe. Allora è l'iframe a prendere il rapporto nativo
   del lavoro (`--arn`) e a farsi **piu' grande della sala quanto
   basta a coprirla** — `max()`, non `min()` — restando centrato:
   e' il ritaglio di `cover` ottenuto con la geometria. Cosi' iframe,
   clip e copertina si inquadrano tutti allo stesso modo, e passando
   dall'uno all'altro non si vede nessuno scatto. */
.pf-sala__media iframe,
.pf-sala__media video {
  position: absolute;
  top: 50%; left: 50%;
  width: max(100%, calc(100dvh * var(--arn, 1.7778)));
  height: max(100%, calc(100vw / var(--arn, 1.7778)));
  transform: translate(-50%, -50%);
  border: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity .55s ease;
}
.pf-sala__media iframe.is-on,
.pf-sala__media video.is-on { opacity: 1; }

/* La clip locale si inquadra come la copertina: riempie la sala e si
   ritaglia. Sta sul suo strato per non ridisegnare nulla d'altro
   mentre suona. */
.pf-sala__media video {
  top: 0; left: 0;
  width: 100%;
  height: 100%;
  transform: none;
  object-fit: cover;
  object-position: center;
  z-index: 1;
  will-change: opacity;
  background: #000;
}

/* ── il titolo: una riga sola, in basso al centro ── */
.pf-sala__meta {
  position: absolute;
  left: 50%;
  bottom: clamp(18px, 4.4%, 60px);
  transform: translateX(-50%) translateY(10px);
  z-index: 3;
  max-width: 88%;
  text-align: center;
  pointer-events: none;
  opacity: 0;
  transition: opacity .5s ease, transform .6s var(--ease);
}
.pf-sala:hover .pf-sala__meta,
.pf-sala:focus-visible .pf-sala__meta,
.pf-sala.is-previewing .pf-sala__meta { opacity: 1; transform: translateX(-50%); }

/* una riga sola: il titolo di Vimeo così com'è, senza andare a capo */
.pf-sala__titolo {
  display: block;
  font-size: clamp(15px, 1.05vw, 20px);
  font-weight: 400;
  line-height: 1.2;
  letter-spacing: .01em;
  color: #fff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-shadow: 0 1px 8px rgba(0, 0, 0, .45);
}

/* ── il ritmo editoriale del monitor: piena, meta' | meta' ──────
   Su schermo largo le sale si susseguono in un ritmo fisso: una
   piena, due a meta', e da capo. L'ordine non lo decide questa
   regola — `nth-child` legge la sequenza che arriva gia' ordinata da
   `content.json` e la dispone. Nessun riordino, nessuna scelta per
   tipo di lavoro.

   **La sala piena e' una schermata intera**, come e' sempre stata:
   copre tutto lo schermo, dal bordo al bordo. Le due a meta' stanno
   sulla stessa riga e tengono il loro 16:9: una riga di meta' e'
   quindi piu' bassa di una piena, ed e' giusto cosi' — e' lo stacco
   fra le due misure a dare il ritmo. Allungare la piena per farla
   pesare quanto la riga di meta' la ridurrebbe a una fascia
   panoramica: il colpo d'occhio della schermata piena si perderebbe.

   Sul telefono (<=700px) non arriva niente di tutto questo: la'
   resta una sala per schermata, esattamente come prima. */
@media (min-width: 701px) {
  .pf-scena {
    display: grid;
    grid-template-columns: 1fr 1fr;
    /* Le due misure stanno qui, e le righe se le passano a turno:
       una schermata intera, poi una riga di meta'. E' `grid-auto-rows`
       che cicla da solo fra i due valori — cosi' il ritmo non dipende
       da quanti lavori ci sono.

       **La riga di meta' e' alta quanto il 4:3 di mezzo schermo**, ed
       e' il formato dei lavori: quasi tutti sono girati in 4:3. Una
       mezza sala in 16:9 li avrebbe tagliati sopra e sotto — copertina
       e anteprima insieme — e in una card larga meta' schermo quel
       taglio si mangia proprio le teste. Sulla sala piena il taglio
       resta, ed e' voluto: li' vale la schermata intera. */
    grid-auto-rows: 100dvh calc(50vw * 3 / 4);
    align-content: start;
    gap: 0;              /* le distanze restano quelle di prima: nessuna */
  }
  .pf-sala { width: auto; height: auto; }

  /* una sala ogni tre prende la riga intera, a tutto schermo */
  .pf-sala:nth-child(3n + 1) { grid-column: 1 / -1; }

  /* Il ritaglio dell'iframe si misurava sulla finestra (100dvh /
     100vw) perche' la sala *era* la finestra. Le mezze sale non lo
     sono piu': le misure diventano quelle della sala (`cq*`), o una
     mezza sala riceverebbe un player enorme, inquadrato diversamente
     dalla copertina — cioe' uno scatto al passaggio del mouse. */
  .pf-sala { container-type: size; }
  .pf-sala__media iframe {
    width: max(100%, calc(100cqh * var(--arn, 1.7778)));
    height: max(100%, calc(100cqw / var(--arn, 1.7778)));
  }
}

.pf-load {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-family: var(--font);
  font-size: 12px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, .5);
}

/* ══════════════════════════════════════
   IL PLAYER — resta quello di prima,
   ma qui si apre sopra il nero, a tutto schermo
══════════════════════════════════════ */
.pf-open {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(0, 0, 0, .97);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: clamp(16px, 2.4vh, 30px);
  padding: clamp(24px, 6vh, 70px) clamp(24px, 6vw, 110px);
  opacity: 0;
  transition: opacity .4s ease;
}
.pf-open.is-on { opacity: 1; }

.pf-open__player {
  flex: 0 0 auto;
  width: min(100%, calc(62dvh * 16 / 9));
  aspect-ratio: 16 / 9;
  background: #000;
}
.pf-open__player iframe { width: 100%; height: 100%; border: 0; display: block; }

.pf-open__info {
  width: min(100%, calc(62dvh * 16 / 9));
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  color: #fff;
}
.pf-open__t {
  margin: 0;
  font-size: clamp(15px, 1.25vw, 22px);
  font-weight: 400;
}
.pf-open__rule { display: none; }
/* i crediti sono lunghi: scorrono dentro il loro riquadro
   invece di spingere il player fuori dallo schermo */
.pf-open__d {
  margin: 0;
  max-width: 62ch;
  max-height: 16vh;
  overflow-y: auto;
  scrollbar-width: none;
  font-size: clamp(12.5px, .82vw, 15px);
  line-height: 1.6;
  color: rgba(255, 255, 255, .55);
  white-space: pre-line;
}
.pf-open__x {
  position: absolute;
  top: clamp(20px, 3.2vh, 40px);
  right: clamp(24px, 3.2vw, 60px);
  background: none;
  border: none;
  font-family: var(--font);
  font-size: clamp(13px, .85vw, 16px);
  color: rgba(255, 255, 255, .6);
  cursor: pointer;
  transition: color .3s ease;
}
.pf-open__x:hover { color: var(--orange); }

/* ═══════════════════════════════════════
   MOBILE
═══════════════════════════════════════ */
@media (max-width: 700px) {
  :root { --panel-w: 100vw; }

  /* la velatura entra solo su telefono: il testo bianco deve reggere
     su qualsiasi fotogramma */
  .hero__scrim { opacity: 1; }

  /* ── Firma: più in alto, lascia respiro alla navigazione ── */
  .signature {
    width: min(72vw, 330px);
    top: 30%;
  }

  /* ── Navigazione: colonna in basso, ampia e leggibile ──
     Le posizioni sparse sono un gesto legato alla composizione
     della foto: su verticale diventerebbero illeggibili. */
  .scatter {
    position: absolute;
    left: 7vw;
    bottom: calc(env(safe-area-inset-bottom, 0px) + 8vh);
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
  }
  .scatter__link {
    position: static !important;
    top: auto !important;
    left: auto !important;
    font-size: 26px;
    font-weight: 400;
    letter-spacing: -.02em;
    line-height: 1.1;
    padding: 11px 0;              /* area di tocco ampia */
    text-shadow: 0 2px 14px rgba(0,0,0,.5);
  }
  .scatter__link.is-active {
    font-weight: 700;
    text-decoration: none;
  }
  /* pallino arancione sulla voce attiva, invece della sottolineatura */
  .scatter__link.is-active::after {
    content: '';
    display: inline-block;
    width: 7px; height: 7px;
    border-radius: 50%;
    background: var(--orange);
    margin-left: 11px;
    vertical-align: middle;
  }
  /* le pagine non ancora pronte restano leggibili ma in secondo piano */
  .scatter__link[data-soon] { opacity: .55; }

  /* ── Richiamo al messaggio ── */
  .mail-reopen {
    top: calc(env(safe-area-inset-top, 0px) + 20px);
    right: 6vw;
    gap: 9px;
    font-size: 14px;
    padding: 10px 0;
  }
  .mail-reopen__ico { width: 21px; height: 16px; }
  .mail-reopen::after { left: 16px; top: 6px; width: 7px; height: 7px; }

  /* ══════════════════════════════════════
     MAIL — foglio che sale dal basso
     Su telefono il gesto resta quello di sempre: la scheda
     risale e si chiude trascinandola giu'. Cambia solo che
     sotto non c'e' piu' l'arancione — la scheda sale da sola,
     con la home che continua a vedersi tutt'intorno.
  ══════════════════════════════════════ */
  .mailpanel {
    top: auto;
    left: 0; right: 0; bottom: 0;
    width: 100%;
    height: 92dvh;
    max-height: 92dvh;
    padding: 0;
    display: block;
    /* il foglio si trascina: qui il dito deve poterlo prendere */
    pointer-events: auto;
    transform: translateY(100%);
    transition: transform .62s var(--ease);
  }
  body.mail-open .mailpanel { transform: translateY(0); }

  .mailpanel.is-dragging { transition: none; }

  /* Il foglio sale gia' per conto suo: qui niente passo da destra,
     o i due movimenti si sommerebbero. */
  .mailpanel__scroll {
    width: auto;
    max-width: none;
    opacity: 1;
    transform: none;
    transition: none;
    pointer-events: auto;
    padding: 52px 5vw calc(env(safe-area-inset-bottom, 0px) + 32px);
    height: 100%;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
  }
  body.mail-open .mailpanel__scroll { transform: none; }

  /* maniglia, come sui fogli iOS */
  .mailpanel__grab {
    position: absolute;
    top: 10px; left: 50%;
    transform: translateX(-50%);
    width: 40px; height: 5px;
    border-radius: 3px;
    background: rgba(255,255,255,.75);
    z-index: 4;
  }

  /* anche qui la chiusura sta dentro la scheda: il foglio comincia
     52px sotto il bordo del pannello e 5vw dentro. */
  .mailpanel__close {
    top: 64px; right: calc(5vw + 12px);
    width: 32px; height: 32px;
    padding: 9px;
  }

  .mail-card {
    border-radius: 22px;
    padding: 26px 22px 24px;
    max-height: none;
    overflow: visible;
  }
  .mail-avatar { width: 40px; height: 40px; font-size: 13px; }
  .mail-subject { font-size: 30px; }
  .mail-body { font-size: 15.5px; line-height: 1.62; }

  .mail-actions {
    flex-direction: column;
    align-items: stretch;
    gap: 12px;
    text-align: center;
  }
  .mail-btn { padding: 15px 26px; font-size: 15px; }
  .mail-addr { font-size: 12.5px; }

  /* intestazione su carta dei contatti */
  .pf-head {
    padding: calc(env(safe-area-inset-top, 0px) + 16px) 0 14px;
    background: none;
    gap: 14px;
  }
  .pf-head .pf-back { margin: 0 6vw; font-size: 17px; padding: 8px 0; }

  /* ══════════════════════════════════════
     PORTFOLIO — pieno schermo anche qui
  ══════════════════════════════════════ */
  .pf-filters {
    top: calc(env(safe-area-inset-top, 0px) + 20px);
    left: 6vw;
    gap: 8px;
  }
  .pf-filter { font-size: 15px; }

  .pf-firma {
    top: calc(env(safe-area-inset-top, 0px) + 16px);
    right: 6vw;
    width: 108px;
  }

  .pf-scena { scroll-snap-type: y mandatory; }

  /* Il pieno campo adesso e' la regola generale, non piu'
     un'eccezione del telefono: le righe che stavano qui ripetevano
     parola per parola quelle di sopra, e due copie della stessa
     scelta sono due posti dove sbagliarla. */

  /* senza passaggio del mouse il nome resta scritto */
  .pf-sala__meta {
    bottom: calc(env(safe-area-inset-bottom, 0px) + 22px);
    max-width: 88vw;
    opacity: 1;
    transform: translateX(-50%);
  }
  .pf-sala__titolo { font-size: 14px; }

  .pf-open { padding: 0; gap: 22px; }
  .pf-open__player { width: 100%; }
  .pf-open__info { width: 100%; padding: 0 6vw; }
  .pf-open__t { font-size: 18px; }
  .pf-open__d { font-size: 13.5px; }
}

/* Chi preferisce meno movimento */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    transition-duration: .01ms !important;
  }
}

/* ═══════════════════════════════════════════════════════
   PAGINE INTERNE — About e Contacts
   Stessa voce della home: Helvetica regular, tutto minuscolo,
   parole sparse sulla carta. Niente bold, niente riquadri,
   niente righine. La gerarchia è solo corpo, posizione, opacità.
═══════════════════════════════════════════════════════ */
.page {
  position: fixed; inset: 0;
  height: 100dvh;
  background: var(--paper);
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  z-index: 30;
  opacity: 0;
  transition: opacity .45s ease;
}
body.view-about    .page--about,
body.view-contacts .page--contacts { opacity: 1; }
body.view-about, body.view-contacts { overflow: hidden; }

/* ═══════════════════════════════════════
   TELA SPARSA
   12 colonne, posizioni libere.
   Ogni blocco porta con sé le sue coordinate:
     --c  colonna d'inizio      --w  quante colonne occupa
     --r  riga                  --rs quante righe occupa
     --dy scarto verticale (numero puro, scalato da --u)
═══════════════════════════════════════ */
.sp {
  --u: clamp(.5px, .075vw, 1.05px);
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: clamp(10px, 1.4vw, 26px);
  row-gap: clamp(16px, 1.8vw, 30px);
  align-items: start;
  max-width: 1500px;
  margin: 0 auto;
  padding: clamp(8px, 1.6vw, 34px) clamp(24px, 4vw, 70px) clamp(100px, 10vw, 170px);
}

/* i contatti stanno in una schermata: non serve coda */
.ct-sp { padding-bottom: clamp(50px, 5vw, 90px); }

.sp__b {
  grid-column: var(--c) / span var(--w);
  grid-row: var(--r) / span var(--rs, 1);
  margin-top: calc(var(--dy, 0) * var(--u));
  font-weight: 400;
}

/* entrata: le parole salgono nell'ordine in cui si leggono */
body.view-contacts .ct-sp > .sp__b {
  animation: salita .7s var(--ease) both;
  animation-delay: calc(var(--r, 1) * .035s);
}
@keyframes salita {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: none; }
}

/* ── Tela libera: composizione a pixel, riscalata ──────────
   Quando il pannello ha salvato delle posizioni, la tela smette
   di essere una griglia e diventa un piano: ogni blocco sta dove
   l'hai messo. Tutto viene riscalato in blocco con `--k`, così
   la composizione è identica su ogni monitor. Sotto gli 861px la
   classe non viene messa e torna la colonna: su un telefono una
   composizione pensata per 1440 sarebbe illeggibile.            */
.sp.is-tela {
  display: block;
  position: relative;
  padding-left: 0;
  padding-right: 0;
  max-width: none;
}
.sp.is-tela > .sp__b {
  position: absolute;
  left: 0;
  top: 0;
  width: var(--tw);
  margin-top: 0;
  transform: translate(calc(var(--x) * var(--k, 1)),
                       calc(var(--y) * var(--k, 1)))
             scale(var(--k, 1));
  transform-origin: top left;
}
/* i blocchi non ancora posati restano fuori dai piedi */
.sp.is-tela > .sp__b:not([data-posato]) { display: none; }

/* Sulla tela il `transform` non è decorazione: è la posizione.
   L'animazione d'ingresso finisce su `transform: none` e la
   cancellerebbe, ammucchiando tutti i blocchi in alto a sinistra.
   Qui dunque si entra in sola dissolvenza. L'!important serve
   perché il filtro dell'about assegna l'animazione da JS, in linea. */
.sp.is-tela > .sp__b { animation-name: comparsa !important; }
@keyframes comparsa { from { opacity: 0; } to { opacity: 1; } }

/* ═══════════════════════════════════════════════════════
   ABOUT — cinque tavole su una tela in scala
   Le tavole sono composizioni a schermo pieno, non pagine che
   scorrono: la tela misura 1920×1080 e viene riscalata in blocco
   per stare dentro la finestra. Così le posizioni restano quelle
   disegnate, su qualunque monitor, senza mai tagliare nulla.
═══════════════════════════════════════════════════════ */
.page--about { overflow: hidden; }

.ab-tela {
  position: absolute;
  left: 0; top: 0;
  width: 1920px;
  height: 1080px;
  transform: translate(var(--ox, 0px), var(--oy, 0px)) scale(var(--k, 1));
  transform-origin: top left;
}

.ab-b {
  position: absolute;
  left: 0; top: 0;
  width: calc(var(--w, 300) * 1px);
  transform: translate(calc(var(--x) * 1px), calc(var(--y) * 1px));
  font-weight: 400;
  color: var(--ink);
}

/* ── il perno ──────────────────────────────────────────
   Unico elemento presente in tutte e cinque le tavole.
   Non entra e non esce: alla prima scelta scivola al centro
   e da lì non si muove più. È l'ancora di tutta la pagina. */
.ab-claim {
  font-size: 30px;
  letter-spacing: -.01em;
  line-height: 1.2;
  white-space: nowrap;
  transition: transform .78s var(--ease);
}
.ab-claim i { font-style: normal; opacity: .55; padding: 0 .18em; }
body.ab-sez .ab-claim {
  transform: translate(calc(var(--x2) * 1px), calc(var(--y2) * 1px));
}

/* ── indietro ── */
.ab-back {
  background: none; border: 0; padding: 0;
  font-family: var(--font);
  font-size: 19px;
  font-weight: 400;
  letter-spacing: .01em;
  color: var(--ink);
  cursor: pointer;
  display: inline-flex; gap: 11px; align-items: center;
  transition: color .25s;
}
.ab-back__a { display: inline-block; transition: transform .3s var(--ease); }
.ab-back:hover { color: var(--ink); }
.ab-back:hover .ab-back__a { transform: translateX(-6px); }
.ab-back:hover .ab-back__t {
  text-decoration: underline;
  text-underline-offset: 5px;
  text-decoration-thickness: 1px;
}

/* ── i quattro selettori ───────────────────────────────
   Sono gli stessi quattro elementi in tutte le tavole: sparsi
   nella prima, incolonnati in alto a destra nelle altre. Non
   vengono ricreati, si spostano — ed è quello il movimento. */
.ab-sez {
  background: none; border: 0; padding: 0 0 4px;
  font-family: var(--font);
  font-size: 19px;
  font-weight: 400;
  letter-spacing: .01em;
  text-align: left;
  color: var(--ink);
  cursor: pointer;
  /* si stringe sul testo: il segno sotto deve essere lungo quanto
     la parola, non quanto il blocco che la contiene */
  width: max-content;
  transform: translate(calc(var(--x) * 1px + var(--mx, 0px)),
                       calc(var(--y) * 1px + var(--my, 0px)));
  transition: transform .78s var(--ease), color .25s;
}
.ab-sez:hover { color: var(--ink); }

/* Nella prima tavola sono parole in mezzo ad altre parole: senza un
   segno non si capisce che si premono. Una sottolineatura sottile,
   appena accennata, che si accende al passaggio. */
.ab-sez::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: currentColor;
  /* Nella tavola nuova la soglia e' una riga di menu, non parole
     sparse: il segno a riposo sotto ognuna faceva rumore e non
     c'e' nel disegno. Resta solo al passaggio, come ovunque. */
  opacity: 0;
  transform: scaleX(1);
  transform-origin: left center;
  transition: opacity .25s ease, transform .4s var(--ease);
}
.ab-sez:hover::after { opacity: 1; }

body.ab-sez .ab-sez {
  transform: translate(calc(var(--x2) * 1px + var(--mx, 0px)),
                       calc(var(--y2) * 1px + var(--my, 0px)));
}
/* incolonnati, il segno resta solo su quello dove sei */
body.ab-sez .ab-sez::after { opacity: 0; transform: scaleX(.4); }
body.ab-sez .ab-sez:hover::after { opacity: .5; transform: scaleX(1); }
body.ab-sez .ab-sez.is-on::after { opacity: 1; transform: scaleX(1); }

/* ── contenuti delle tavole ── */
.ab-txt { font-size: 19px; line-height: 1.45; }

/* La colonna della soglia: un blocco solo, allineato a sinistra.
   Le misure sono quelle della tavola — righe a 22px su corpo 19
   (1.18) e 21px di stacco fra un paragrafo e l'altro. I <p> non
   sono decorazione: lo stacco e' una misura, non una riga vuota. */
.ab-testo { line-height: 1.18; text-align: left; }
.ab-testo p { margin: 0 0 21px; }
.ab-testo p:last-child { margin-bottom: 0; }
/* qui la tavola prende fiato: si cambia argomento */
.ab-testo__stacco { margin-top: 24px; }

/* Nella tavola della soglia i paragrafi sono centrati, e sono due dati
   a riprodurne l'impaginazione riga per riga: la larghezza del blocco,
   che decide dove il testo va a capo, e l'interlinea. Misurate sulla
   tavola, le righe distano 22px su un corpo di 19 — cioè 1.18, non il
   1.45 di prima: con quello i blocchi crescevano e si scontravano. */
[data-centro] { text-align: center; line-height: 1.18; }

.ab-voce b { display: block; font-size: 19px; font-weight: 400; line-height: 1.35; }
.ab-voce b i { font-style: italic; }
.ab-voce span {
  display: block;
  font-size: 15px;
  line-height: 1.45;
  margin-top: 3px;
  opacity: .55;               /* come ovunque nel sito: gerarchia per opacità */
}
.ab-voce b i { opacity: .5; }
.ab-voce b strong, .ab-voce span strong { font-weight: 600; }
.ab-voce span em { opacity: .78; }
.ab-txt { opacity: 1; }

.ab-foto picture { display: block; overflow: hidden; }
.ab-foto img {
  width: 100%;
  display: block;
  /* il rapporto lo detta la tavola, non il file: lo stesso scatto
     è ritagliato diversamente su desktop e su telefono */
  height: auto;
  aspect-ratio: var(--ar, auto);
  object-fit: cover;
}

/* alcuni blocchi vivono solo nella tavola del telefono */
[data-solo-mobile] { display: none; }

/* ── entrata e uscita ──────────────────────────────────
   Tre cose insieme, ed è la combinazione a fare la differenza:
   uno spostamento su molla (con un filo di scavalcamento), una
   sfocatura che si riprende — l'occhio la legge come velocità —
   e una cascata di ritardi assegnata dal JS. Chi esce lo fa in
   fretta e senza rimbalzo: l'attenzione va a chi entra.
─────────────────────────────────────────────────────── */
.ab-b[data-vista] {
  opacity: 0;
  filter: blur(8px);
  /* Invisibile non basta: senza questo un blocco spento di un'altra
     categoria resta sopra i pulsanti e ne ruba i click. È il bug che
     rendeva "cinema" e "tv & brand" impossibili da premere. */
  pointer-events: none;
  will-change: transform, opacity, filter;
  transition:
    opacity .34s var(--uscita),
    filter  .34s var(--uscita),
    transform .42s var(--uscita);
  transition-delay: var(--rit, 0ms);
}
.ab-b[data-vista].is-on {
  opacity: 1;
  filter: blur(0);
  transition:
    opacity .5s ease-out,
    filter  .62s ease-out,
    transform .92s var(--molla);
  transition-delay: var(--rit, 0ms);
}

/* Nulla di ciò che è testo o immagine intercetta il puntatore:
   la loro larghezza è un rettangolo invisibile che coprirebbe i
   selettori. Si clicca solo ciò che è davvero un pulsante. */
.ab-txt, .ab-voce, .ab-foto,
.ab-txt *, .ab-voce *, .ab-foto * { pointer-events: none !important; }

/* le fotografie si ritraggono verso il proprio centro */
.ab-foto[data-vista] {
  transform: translate(calc(var(--x) * 1px), calc(var(--y) * 1px)) scale(.9);
}
/* Le variabili --sy (scorrimento), --sc (scala) e --bl (sfocatura)
   sono l'aggancio lasciato dal carosello, oggi spento. A riposo
   valgono 0, 1, 0: così com'è, questa regola è lo stato di quiete
   ed è la posizione vera delle fotografie sulla tela. */
.ab-foto[data-vista].is-on {
  transform: translate(calc(var(--x) * 1px),
                       calc(var(--y) * 1px + var(--sy, 0px)))
             translate(var(--deck-x, 0px), var(--deck-y, 0px))
             rotate(var(--deck-r, 0deg))
             scale(var(--sc, 1));
  filter: blur(var(--bl, 0px));
}
.ab-tela .ab-txt[data-vista].is-on,
.ab-tela .ab-voce[data-vista].is-on { opacity: 1; }

/* I comandi stanno sopra a tutto. Le fotografie salgono fino a
   z-index 30: senza questo si sovrappongono al menù e al tasto
   indietro, che diventano invisibili e soprattutto non più
   cliccabili. */
.ab-back, .ab-sez { z-index: 60; }
.ab-claim { z-index: 50; }

/* le parole salgono da sotto */
.ab-txt[data-vista], .ab-voce[data-vista] {
  transform: translate(calc(var(--x) * 1px), calc(var(--y) * 1px + 34px));
}
.ab-txt[data-vista].is-on, .ab-voce[data-vista].is-on {
  transform: translate(calc(var(--x) * 1px), calc(var(--y) * 1px));
}

/* Il perno e i selettori viaggiano sulla molla: sono gli unici
   elementi che si spostano davvero da un posto a un altro, e il
   piccolo scavalcamento finale è ciò che rende il viaggio vivo. */
.ab-claim { transition: transform .95s var(--molla), opacity .4s ease; }

/* Nella tavola music video il centro spetta al paragrafo: il perno
   si spegne in dissolvenza e si riaccende cambiando sezione. */
body.ab-senza-perno .ab-claim { opacity: 0; }

/* il grassetto voluto dalla tavola ("dirigo"): unico punto dell'about */
.ab-txt b { font-weight: 700; }
.ab-txt strong { font-weight: 600; }
.ab-txt em { font-style: italic; }
.ab-sez   { transition: transform .95s var(--molla), color .25s; }

/* ── Telefono: le tavole iPhone ──────────────────────────
   Tela 393 di larghezza, altezza variabile: le tavole mobili
   sono più alte dello schermo e alcune foto sono tagliate dal
   bordo apposta — si scoprono con un piccolo scorrimento.
   La scala si calcola sulla sola larghezza, così il progetto
   non viene rimpicciolito per farci stare l'altezza.          */
@media (max-width: 860px) {
  .page--about { overflow-y: auto; -webkit-overflow-scrolling: touch; }
  .ab-tela { position: relative; width: 393px; }

  .ab-b:not([data-m]) { display: none; }
  .ab-b {
    width: calc(var(--wm) * 1px);
    transform: translate(calc(var(--xm) * 1px), calc(var(--ym) * 1px));
  }
  .ab-sez { width: max-content; }
  .ab-foto img { aspect-ratio: var(--arm, auto); object-fit: cover; }
  [data-solo-mobile] { display: block; }

  /* i selettori restano sparsi in alto: qui non migrano in colonna */
  body.ab-sez .ab-sez {
    transform: translate(calc(var(--xm) * 1px), calc(var(--ym) * 1px));
  }

  .ab-claim { font-size: 17px; white-space: normal; text-align: center; }
  body.ab-sez .ab-claim {
    transform: translate(calc(var(--xm2) * 1px), calc(var(--ym2) * 1px));
  }
  body[data-ab-vista="musicvideo"] .ab-claim,
  body[data-ab-vista="awards"] .ab-claim,
  body[data-ab-vista="tvbrand"] .ab-claim { opacity: 0; }

  [data-mcenter] { text-align: center; }

  /* corpi rialzati: sullo schermo del telefono i valori del
     progetto desktop risultavano troppo minuti */
  .ab-txt        { font-size: 13px; line-height: 1.45; }
  /* sulla tavola del telefono: righe a 16px su corpo 13 */
  [data-centro]  { line-height: 1.24; }
  [data-id="mv-testo"] { font-size: 12px; line-height: 1.45; }
  .ab-voce b     { font-size: 14px; }
  .ab-voce b i   { font-size: 12px; }
  .ab-voce span  { font-size: 11.5px; margin-top: 3px; }
  .ab-back, .ab-sez { font-size: 15px; }

  /* stesse entrate e uscite, sulle coordinate mobile */
  .ab-foto[data-vista] {
    transform: translate(calc(var(--xm) * 1px), calc(var(--ym) * 1px)) scale(.9);
  }
  .ab-foto[data-vista].is-on {
    transform: translate(calc(var(--xm) * 1px), calc(var(--ym) * 1px)) scale(1);
    filter: none;
  }

  .ab-txt[data-vista], .ab-voce[data-vista] {
    transform: translate(calc(var(--xm) * 1px), calc(var(--ym) * 1px + 26px));
  }
  .ab-txt[data-vista].is-on, .ab-voce[data-vista].is-on {
    transform: translate(calc(var(--xm) * 1px), calc(var(--ym) * 1px));
  }
}

/* Chi ha chiesto meno movimento vede la stessa pagina, senza viaggi */
@media (prefers-reduced-motion: reduce) {
  .ab-claim, .ab-sez, .ab-b[data-vista] { transition-duration: .01ms !important; }
}

/* ═══════════════════════════════════════
   CONTATTI — una tavola, non una pagina
   La composizione è quella disegnata in XD (1920×1080): l'indirizzo
   al centro, i due profili ai lati, le città sparse come pesi che
   tengono in equilibrio il vuoto. Le posizioni sono in percentuale,
   così la tavola respira con la finestra invece di rompersi a una
   sola risoluzione. Su telefono la tavola è un'altra: colonna
   centrata, niente città (§ media query in fondo).
═══════════════════════════════════════ */
/* ── la carta ──────────────────────────────────────────
   La tavola non e' su bianco: e' su un foglio incollato al muro,
   con le sue pieghe. E' una fotografia, non un motivo che si
   ripete: sta ferma, `cover`, e non si taglia mai in modo visibile
   perche' non c'e' un soggetto da perdere. Due file: quello
   orizzontale per il monitor, quello verticale per il telefono —
   ridimensionare il primo su uno schermo alto stirerebbe le pieghe.

   ATTENZIONE alla cache: questi URL stanno nel CSS, non in
   index.html, quindi il `?v=` non viene alzato dallo script del
   deploy. Se si cambia la texture va alzato a mano, o per un anno
   nessuno vedra' quella nuova. */
.page--contacts {
  background-color: var(--paper);
  background-image: url("assets/carta/carta-desktop.jpg?v=1");
  background-image: image-set(
    url("assets/carta/carta-desktop.webp?v=1") type("image/webp"),
    url("assets/carta/carta-desktop.jpg?v=1")  type("image/jpeg"));
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

.ct {
  position: absolute;
  inset: 0;
  font-weight: 400;
  letter-spacing: -.01em;
  color: var(--ink);
}

/* ── il centro: indirizzo e ruoli, un blocco solo ── */
.ct__centro {
  position: absolute;
  left: 50%;
  top: 51.5%;
  transform: translate(-50%, -50%);
  width: max-content;
  max-width: 92vw;
  text-align: center;
}

.ct__mail {
  display: inline-block;
  font-size: clamp(21px, 1.77vw, 38px);
  color: var(--ink);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 5px;
  /* il transform resta al magnetismo: l'entrata è in sola dissolvenza */
  transform: translate(var(--mx, 0px), var(--my, 0px));
  transition: color .25s, transform .45s var(--ease);
}
.ct__mail:hover { color: var(--orange); }

.ct__ruoli {
  margin: clamp(9px, .95vw, 20px) 0 0;
  font-size: clamp(15px, 1.3vw, 28px);
  white-space: nowrap;
}
.ct__ruoli i { font-style: normal; opacity: .55; padding: 0 .3em; }

/* ── i due profili, una riga sola ai due bordi ──
   Stessa altezza dell'indirizzo nella tavola: sono le sue ali. */
.ct__social {
  position: absolute;
  left: 0; right: 0;
  top: 51.6%;
  transform: translateY(-50%);
  display: flex;
  justify-content: space-between;
  /* Asimmetrico di proposito: nella tavola instagram sta a 337px dal
     bordo sinistro e vimeo a 320 da quello destro. Non è un centro. */
  padding: 0 clamp(24px, 16.7vw, 334px) 0 clamp(24px, 14.5vw, 290px);
  pointer-events: none;
}
.ct__link {
  pointer-events: auto;
  font-size: clamp(16px, 1.35vw, 29px);
  color: var(--orange);
  text-decoration: none;
  transform: translate(var(--mx, 0px), var(--my, 0px));
  transition: color .25s, transform .45s var(--ease);
}
.ct__link:hover {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 5px;
}

/* ── le città ──
   Non sono navigazione né informazione: sono i pesi della
   composizione. Perciò aria-hidden nel markup e nessun hover qui. */
.ct__luogo {
  position: absolute;
  transform: translate(-50%, -50%);
  font-size: clamp(13px, 1.15vw, 25px);
  white-space: nowrap;
}
.ct__luogo--milano  { left: 37.3%; top: 30.6%; }
.ct__luogo--roma    { left: 73.5%; top: 19.6%; }
.ct__luogo--ovunque { left: 55.0%; top: 66.2%; }
.ct__luogo--torino  { left: 13.0%; top: 78.4%; }
.ct__luogo--biella  { left: 84.4%; top: 76.8%; }

/* ── entrata ──
   Le città salgono; centro e profili si limitano a comparire,
   perché sul transform ci vive il magnetismo del puntatore e
   una `salita` che finisce su `transform: none` lo spegnerebbe. */
body.view-contacts .ct__centro,
body.view-contacts .ct__link,
body.view-contacts .ct__luogo {
  animation: ct-appare .8s var(--ease) both;
  animation-delay: var(--rit, 0s);
}
body.view-contacts .ct__luogo { animation-name: ct-sale; }
@keyframes ct-appare { from { opacity: 0; } to { opacity: 1; } }
@keyframes ct-sale {
  from { opacity: 0; transform: translate(-50%, calc(-50% + 16px)); }
  to   { opacity: 1; transform: translate(-50%, -50%); }
}

/* ═══════════════════════════════════════
   Schermi stretti — la tela si richiude in colonna
   ma conserva il respiro: gli scarti diventano aria.
═══════════════════════════════════════ */
@media (max-width: 860px) {
  .sp {
    grid-template-columns: 1fr;
    row-gap: 0;
    padding: 4px 6vw 100px;
  }
  .sp__b {
    grid-column: 1 / -1;
    grid-row: auto;
    margin-top: calc(18px + var(--dy, 0) * .26px);
  }

  /* Il foglio verticale: quello orizzontale, stirato su uno
     schermo alto, allungherebbe le pieghe fino a farle sembrare
     un difetto di compressione. */
  .page--contacts {
    background-image: url("assets/carta/carta-mobile.jpg?v=1");
    background-image: image-set(
      url("assets/carta/carta-mobile.webp?v=1") type("image/webp"),
      url("assets/carta/carta-mobile.jpg?v=1")  type("image/jpeg"));
  }

  /* ── Contatti su telefono: tavola dedicata, non il desktop
     ristretto. Le città spariscono — nella tavola iPhone non ci
     sono: reggevano un vuoto che qui non esiste. ── */
  .ct__luogo { display: none; }

  .ct__centro { top: 42%; width: 100%; max-width: none; padding: 0 8vw; }
  .ct__mail  { font-size: clamp(16px, 4.6vw, 21px); }
  .ct__ruoli { margin-top: 7px; font-size: clamp(12.5px, 3.6vw, 16px); }

  .ct__social {
    top: 56.8%;
    flex-direction: column;
    align-items: center;
    gap: 22px;
    padding: 0;
  }
  .ct__link { font-size: clamp(14px, 4.2vw, 19px); }
}
