/* =========================================================
   Scalpore — Michele Porcaro | Portfolio landing page
   ========================================================= */

/* Inter (primario) e Martian Mono (secondario) serviti dal sito e non
   dalla CDN di Google: nessun indirizzo IP dei visitatori esce verso
   terzi. Licenza SIL Open Font in assets/fonts/OFL.txt. Sono gli stessi
   due font — con la stessa divisione dei ruoli, display in Inter ed
   elementi tecnici/etichette in mono — usati da titarvl.framer.website,
   il riferimento scelto per il sito. */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/inter-400-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/inter-400-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../assets/fonts/inter-500-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../assets/fonts/inter-500-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../assets/fonts/inter-600-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../assets/fonts/inter-600-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../assets/fonts/inter-700-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../assets/fonts/inter-700-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url("../assets/fonts/inter-800-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url("../assets/fonts/inter-800-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Martian Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/martian-mono-400-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Martian Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/martian-mono-400-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Martian Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../assets/fonts/martian-mono-500-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Martian Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../assets/fonts/martian-mono-500-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Martian Mono";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../assets/fonts/martian-mono-600-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Martian Mono";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../assets/fonts/martian-mono-600-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Martian Mono";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../assets/fonts/martian-mono-700-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Martian Mono";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../assets/fonts/martian-mono-700-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --bg: whitesmoke;
  --fg: #0a0807;
  --muted: #4d4a47;
  --muted-2: #6f6b67;
  --border: rgba(10, 8, 7, 0.18);
  --accent: #e5480c;
  --hero-tint: #c8531f;
  --card-bg: #e8e6e2;
  --nav-bg: rgba(245, 245, 245, 0.78);
  --cta-bg: rgba(245, 245, 245, 0.72);
  --radius-md: 12px;
  --font: "Inter", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-mono: "Martian Mono", "SFMono-Regular", Menlo, monospace;
}

/* Tema scuro della sezione Lavori. La classe la mette e la toglie
   main.js quando la sezione attraversa la fascia centrale dello schermo,
   in qualunque verso si stia scorrendo: è il momento buio dentro un sito
   chiaro, lo stesso ruolo che prima aveva il lampo chiaro dentro un sito
   nero, solo capovolto. Qui cambiano solo le variabili: tutto quello che
   le usa — fondo, testi, bordi, barra, bottone — segue da sé, senza
   regole doppie per ogni elemento. */
.tema-scuro {
  --bg: #000;
  --fg: #fff;
  --muted: #b5b5b5;
  --muted-2: #7d7d7d;
  --border: rgba(255, 255, 255, 0.14);
  --card-bg: #0d0d0d;
  --nav-bg: rgba(10, 8, 7, 0.4);
  --cta-bg: rgba(10, 8, 7, 0.55);
}

/* Sfondo scuro delle pagine progetto: stessa palette del tema scuro di
   Lavori, ma fissa invece che legata allo scroll — la pagina nasce già
   così, tutto quello che usa le variabili segue da sé. La nav in queste
   pagine è "--solid" (niente vetro sfumato, vedi sopra) apposta perché
   sotto ci scorre la galleria: qui il bianco fisso del suo CSS si
   sovrascrive con --bg, cioè nero pieno, non con --nav-bg (che ha alfa
   e lascerebbe intravedere le immagini dietro, lo stesso problema che
   "--solid" esiste per evitare). */
body.pagina-progetto {
  --bg: #000;
  --fg: #fff;
  --muted: #b5b5b5;
  --muted-2: #7d7d7d;
  --border: rgba(255, 255, 255, 0.14);
  --card-bg: #0d0d0d;
  --nav-bg: rgba(10, 8, 7, 0.55);
  --cta-bg: rgba(10, 8, 7, 0.55);
}

body.pagina-progetto .hero-nav,
body.pagina-progetto .hero-nav--solid.is-scrolled {
  background: var(--bg);
}

/* Logo, voci e orologio sulla barra sono leggibili in "difference" solo
   quando --fg è bianco: sulla barra chiara e semitrasparente di adesso il
   testo scuro in difference verrebbe pesato dall'alfa dello sfondo e
   uscirebbe un grigio slavato. Di default quindi si passa a colori
   espliciti; il momento scuro della sezione Lavori, dove --fg torna
   bianco, riaccende l'inversione qui sotto. */
.hero-logo,
.nav-link,
.hero-nav-clock {
  mix-blend-mode: normal;
  color: var(--fg);
}

.tema-scuro .hero-logo,
.tema-scuro .nav-link,
.tema-scuro .hero-nav-clock {
  mix-blend-mode: difference;
}

/* Il passaggio fra i due temi è graduale sugli elementi che reggono il
   colore della pagina. Chi ha già una sua transizione (voci di menu,
   immagini dei lavori) la tiene: usa mix-blend-mode o filtri e si adatta
   comunque. */
body,
main,
article,
.section,
.section-tag,
.section-num,
.section-lead,
.servizio,
.servizio-testa,
.work-card-media,
.work-card-year,
.work-card-kind,
.hero-nav,
.talk-cta {
  transition: background-color 0.7s ease, color 0.7s ease, border-color 0.7s ease;
}

* { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  overflow-x: hidden;
  /* Riserva sempre lo spazio della scrollbar verticale, anche quando non
     serve (pagine corte) o sparisce (scroll bloccato per un pannello o il
     lightbox): senza, nascondere la scrollbar allarga il viewport di
     qualche px e rimpagina tutto, spostando visibilmente gli elementi
     sticky della pagina progetto proprio nel momento dello scatto che
     si voleva evitare. */
  scrollbar-gutter: stable;
  /* La nav è fissa in alto: senza questo margine di scroll un elemento
     raggiunto col tab o con un'ancora finisce sotto la barra. */
  scroll-padding-top: 104px;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.4;
  -webkit-font-smoothing: antialiased;
}

/* <main> (home) e <article> (pagine progetto) stanno sopra al footer
   (z-index più alto, sfondo pieno) per tutta la loro altezza: finché
   scorrono loro, il footer sottostante — già agganciato al fondo della
   finestra, vedi .site-footer — resta coperto. Solo quando finiscono,
   negli ultimi px di scroll, il footer smette di essere coperto e
   "sale" in vista da sotto. */
main,
article {
  position: relative;
  z-index: 1;
  background: var(--bg);
}

/* Cursore a pallino. La classe sull'<html> la mette main.js: senza JS
   la freccia di sistema resta al suo posto. Il pallino è bianco e in
   "difference", quindi inverte qualunque cosa si trovi sotto — bianco sul
   nero, nero sulle immagini chiare. */
.cursore-attivo,
.cursore-attivo * { cursor: none; }

.cursore {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  overflow: hidden;
  background: #fff;
  mix-blend-mode: difference;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.2s ease,
              width 0.35s cubic-bezier(0.16, 1, 0.3, 1),
              height 0.35s cubic-bezier(0.16, 1, 0.3, 1),
              background-color 0.35s ease,
              backdrop-filter 0.35s ease,
              -webkit-backdrop-filter 0.35s ease;
  will-change: transform;
}

.cursore.is-visible { opacity: 1; }

/* Sopra un progetto il pallino diventa un disco di vetro. Il backdrop-filter
   sfoca e scurisce quello che gli passa sotto, quindi la scritta bianca non
   dipende dal colore della foto: anche sul mockup più chiaro il fondo non
   supera il grigio medio (bianco su ~#565656, circa 7:1). L'inversione qui
   va spenta, perché difference e backdrop-filter non convivono: il filtro
   isola l'elemento e il disco resterebbe opaco. */
.cursore.is-grande {
  width: 116px;
  height: 116px;
  background: rgba(10, 8, 7, 0.26);
  backdrop-filter: blur(10px) brightness(0.45) saturate(1.1);
  -webkit-backdrop-filter: blur(10px) brightness(0.45) saturate(1.1);
  border: 1px solid rgba(255, 255, 255, 0.22);
  box-shadow: 0 10px 36px rgba(0, 0, 0, 0.18);
  mix-blend-mode: normal;
}

/* Dove il vetro non c'è (Firefox con il filtro disattivato) resta un disco
   pieno scuro: perde lo sfocato, tiene la leggibilità. */
@supports not ((backdrop-filter: blur(4px)) or (-webkit-backdrop-filter: blur(4px))) {
  .cursore.is-grande { background: rgba(10, 8, 7, 0.86); }
}

.cursore-testo {
  color: #fff;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  line-height: 1.25;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  text-align: center;
  /* 90px invece di 76: "Visualizza" (etichetta della galleria) da sola
     misura ~81px a questo font, e col margine più stretto veniva tagliata
     a metà parola invece di andare a capo pulita. Il cerchio (116px) ha
     margine per starci senza sembrare stretto. */
  max-width: 90px;
  opacity: 0;
  transition: opacity 0.25s ease;
}

.cursore.is-grande .cursore-testo { opacity: 1; }

a { color: inherit; text-decoration: none; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
  border-radius: 2px;
}

.skip-link {
  position: fixed;
  top: -60px;
  left: 16px;
  z-index: 300;
  background: var(--fg);
  color: var(--bg);
  padding: 12px 20px;
  font-size: 14px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  border-radius: 4px;
  transition: top 0.2s ease;
}

.skip-link:focus { top: 16px; }

/* ---------- Preloader ---------- */

.preloader {
  position: fixed;
  inset: 0;
  z-index: 500;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 20px;
  background: var(--bg);
  transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1), visibility 0s linear 0.6s;
}

.preloader.is-hidden {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

body.is-loading { overflow: hidden; }

/* Preloader a stack di immagini, solo home (index.html): coreografato via
   GSAP in js/main.js. Qui solo lo stato di partenza (nascosto), così se lo
   script tarda o fallisce non lampeggia una pila di immagini disordinata.
   Sfondo nero indipendente dal tema chiaro del resto del sito. */
.preloader.preloader--stack {
  background: #050505;
}

.preloader-stack {
  position: relative;
  width: 100%;
  height: 100%;
}

/* "Michele"/card/"Porcaro" sempre centrati sullo schermo, in assoluto
   rispetto a .preloader-stack. */
.preloader-stack-row {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  max-width: 720px;
  gap: 24px;
  padding: 0 24px;
}

.preloader-stack-text {
  font-family: var(--font);
  font-size: clamp(24px, 4vw, 36px);
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: -0.02em;
  color: rgb(255, 255, 255);
  white-space: nowrap;
  opacity: 0;
}

.preloader-stack-imgs {
  position: relative;
  width: clamp(130px, 17vw, 230px);
  height: clamp(130px, 17vw, 230px);
  flex: none;
}

.preloader-stack-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  opacity: 0;
}

.preloader-stack-img img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@media (max-width: 640px) {
  /* Riga in colonna: "Michele" e "Porcaro" sopra/sotto la card. Gap ampio
     perché restino fuori dal raggio dello stack aperto a ventaglio. */
  .preloader-stack-row { flex-direction: column; gap: 130px; }

  /* Su mobile il 17vw della regola base è troppo piccolo (schermo stretto):
     qui la card cresce in proporzione allo schermo invece di restare
     ancorata al minimo. */
  .preloader-stack-imgs {
    width: clamp(160px, 45vw, 220px);
    height: clamp(160px, 45vw, 220px);
  }
}

/* subtle grain overlay across the whole page */
.grain {
  position: fixed;
  inset: 0;
  z-index: 200;
  pointer-events: none;
  opacity: 0.045;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ---------- Hero (piena pagina) ---------- */

.hero {
  position: relative;
  /* Sta, come <main> dopo di lei, sopra al footer sticky in fondo alla
     pagina (vedi il perché su .site-footer): senza uno z-index esplicito
     un elemento "position" qualsiasi (anche a z-index 0, come il footer)
     dipinge comunque sopra un fratello non posizionato come questa
     sezione altrimenti sarebbe. */
  z-index: 1;
  min-height: 100vh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}

/* Scura apposta, indipendente dal tema chiaro del resto del sito: il
   "difference" del titolo (vedi .hero-title .line > span) ha bisogno di
   un fondo scuro per restare un riquadro bianco sempre visibile invece
   di confondersi con lo sfondo. */
.hero-media {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  background: #0a0807;
}

.hero-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 0;
}

/* Scia di card che segue il cursore nella hero. Lo strato sta fra lo sfondo
   (z-index 0) e i testi (z-index 2): le card passano dietro al titolo invece
   di coprirlo. Il JS lo crea e lo riempie solo dove ha senso — vedi hero-trail.js. */
.hero-trail {
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  pointer-events: none; /* nessuna interferenza con i link della hero */
}

.hero-trail-card {
  position: absolute;
  overflow: hidden;
  background: var(--card-bg);
  pointer-events: none;
  will-change: transform, opacity;
  /* Dimensioni, posizione, opacità e transform li scrive il loop rAF in
     hero-trail.js: qui nessuna transition, altrimenti i due sistemi si
     contenderebbero le stesse proprietà a ogni frame. */
}

.hero-trail-card img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Vignettatura scura (non whitesmoke): la hero ora ha un fondo quasi
   nero apposta, questa scurisce ancora un po' ai bordi invece di
   schiarire, così il canvas WebGL resta leggibile sotto ai tag. */
.hero-media::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  background-image: linear-gradient(to top, rgba(10, 8, 7, 0.65) 0%, rgba(10, 8, 7, 0.05) 40%, rgba(10, 8, 7, 0.3) 100%);
  opacity: 0.85;
}

/* Bianca piena di default apposta: quella è la posizione di partenza
   (scroll a 0, sopra al nero della hero), e senza un default statico
   qui la barra dipenderebbe dal JS anche per il primo render — con
   backdrop-filter quel primo istante può restare "sporco" (grigio
   invece che bianco) prima che main.js aggiunga is-scrolled. Appena si
   scrolla, main.js aggiunge .is-scrolled e torna la trasparenza. */
.hero-nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 28px 40px;
  background: #fff;
  backdrop-filter: blur(20px) saturate(1.6);
  -webkit-backdrop-filter: blur(20px) saturate(1.6);
  border-bottom: 1px solid var(--border);
  transition: background-color 0.3s ease;
}

.hero-nav.is-scrolled {
  background: var(--nav-bg);
}

/* Pagine progetto: niente vetro sfumato. Lì sotto la nav non c'è più una
   foto di copertina ma la galleria che scorre, spesso colorata — con lo
   sfondo semi-trasparente il blur lasciava intravedere le immagini sotto
   la barra. Restando piena la nav non fa da finestra su nulla. */
.hero-nav--solid.is-scrolled {
  background: #fff;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

/* Il colore e il "difference" del logo (quando torna, nel momento scuro
   della sezione Lavori) li decide il blocco di regole in cima al file:
   qui restano solo le misure. Il blend andrebbe messo solo sul logo,
   mai su .hero-nav, altrimenti il pannello del menu mobile — che ne è
   un discendente — verrebbe anch'esso "invertito" e sparirebbe. */
.hero-logo {
  font-weight: 600;
  font-size: 18px;
  letter-spacing: -0.01em;
}

/* Orologio locale al centro della navbar — posizionato in assoluto sul
   contenitore .hero-nav (già position:absolute) per restare centrato a
   prescindere dalla larghezza di logo e voci di menu. */
.hero-nav-clock {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  pointer-events: none;
}

.hero-nav-clock-icon {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  opacity: 0.6;
}

.hero-nav-clock-code {
  opacity: 0.6;
}

.hero-nav-clock-time {
  font-variant-numeric: tabular-nums;
}

.hero-nav-links {
  display: flex;
  align-items: center;
  gap: 20px;
}

.nav-link {
  display: inline-block;
  overflow: hidden;
  height: 1.3em;
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

.nav-link-inner {
  display: block;
  transition: transform 0.45s cubic-bezier(0.16, 1, 0.3, 1);
}

.nav-link-inner span {
  display: block;
  height: 1.3em;
  line-height: 1.3em;
}

.nav-link:hover .nav-link-inner { transform: translateY(-1.3em); }

/* Numero della voce, riga "Menu / 01-05" e link legale vivono solo nel
   pannello mobile a tutto schermo: sulla barra desktop sarebbero rumore. */
.nav-link-num,
.nav-meta,
.nav-legal { display: none; }

.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 5px;
  width: 44px;
  height: 44px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
  flex-shrink: 0;
  position: relative;
  z-index: 100;
}

.nav-toggle span {
  width: 20px;
  height: 2px;
  background: var(--fg);
  transition: transform 0.25s ease, opacity 0.25s ease;
}

.nav-toggle[aria-expanded="true"] span:first-child {
  transform: translateY(3.5px) rotate(45deg);
}

.nav-toggle[aria-expanded="true"] span:last-child {
  transform: translateY(-3.5px) rotate(-45deg);
}

/* Sticker sparsi nella hero. Posizione, grandezza e rotazione le calcola
   js/main.js su ogni schermo: misura titolo, scritte in basso, navbar e
   [Let's talk] e mette gli sticker solo nello spazio libero, il più
   distanti possibile fra loro. Finché non sono piazzati restano nascosti
   (niente sticker che compaiono in un punto e poi saltano altrove). */
.hero-stickers {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.hero-sticker {
  position: absolute;
  z-index: 2;
  height: auto;
  filter: drop-shadow(0 18px 28px rgba(10, 8, 7, 0.3));
  opacity: 0;
}

.hero-stickers.is-pronti .hero-sticker {
  animation: fade-up 0.8s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

.hero-sticker.is-escluso { display: none; }

/* Come il titolo: in pausa finché il preloader è a schermo. */
body.is-loading .hero-sticker {
  animation-play-state: paused;
}

@keyframes fade-up {
  from { opacity: 0; transform: rotate(var(--rot, 0deg)) translateY(18px); }
  to { opacity: 1; transform: rotate(var(--rot, 0deg)) translateY(0); }
}

/* Titolo centrato con ogni riga evidenziata come da un pennarello nero:
   lo sfondo pieno sta sullo span interno, che è anche l'elemento che la
   maschera overflow:hidden della riga esterna anima dal basso — stessa
   struttura di prima, qui il "reveal" sembra un tratto di evidenziatore
   che scorre invece di un testo che scorre.

   Niente z-index qui apposta: un valore esplicito creerebbe un proprio
   stacking context e isolerebbe il "difference" delle righe (sotto) da
   arco WebGL e scia cursore, che stanno fuori da questo contenitore. Ad
   auto, l'ordine di stacking lo decide l'ordine nel DOM rispetto a loro
   — vedi il commento su .hero-title .line > span. */
.hero-content {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 110px 20px;
}

.hero-title {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(4px, 1vw, 10px);
  margin: 0;
}

.hero-title .line {
  overflow: hidden;
}

/* Riquadro bianco + testo dello stesso nero della hero, in "difference"
   (stessa ricetta del pallino cursore, vedi .cursore). Bianco su fondo
   scuro non sparisce mai in difference — resta un riquadro pieno e
   leggibile — ma dove gli passano dietro l'arco WebGL o una card della
   scia si inverte a colori diversi: la reazione "sotto l'animazione"
   senza sacrificare la visibilità del riquadro. Il testo è dello stesso
   nero del fondo hero apposta: differenza di due colori quasi uguali
   resta vicina allo zero, cioè resta leggibile e scuro sul riquadro
   bianco invece di sparire anche lui. Per lo stesso motivo della scia,
   .hero-content non ha uno z-index proprio e l'animazione finisce a
   "transform:none" — vedi i commenti lì. */
.hero-title .line > span {
  display: inline-block;
  background: #fff;
  color: #0a0807;
  mix-blend-mode: difference;
  font-weight: 800;
  text-transform: uppercase;
  line-height: 1;
  letter-spacing: -0.02em;
  white-space: nowrap;
  padding: 0.22em 0.32em;
  font-size: clamp(20px, 5.4vw, 78px);
  transform: translateY(105%);
  animation: reveal-up 0.9s cubic-bezier(0.16, 1, 0.3, 1) 0.15s forwards;
}

.hero-title .line:nth-child(2) > span { animation-delay: 0.26s; }
.hero-title .line:nth-child(3) > span { animation-delay: 0.37s; }

/* In pausa finché il preloader è a schermo (classe su body, vedi
   js/main.js), altrimenti l'animazione — che parte da sola al caricamento
   della pagina — finisce dietro al loader e il titolo appare già fermo
   quando il loader sparisce, invece di entrare davvero in scena. */
body.is-loading .hero-title .line > span {
  animation-play-state: paused;
}

@keyframes reveal-up {
  to { transform: none; }
}

/* main.js la aggiunge a "animationend": spegne l'animazione invece di
   lasciarla in pausa a fine corsa via "forwards", perché quella pausa
   tiene il transform come matrice (vedi il commento in main.js) e
   isolerebbe il "difference" per sempre. Senza JS (o se qualcosa va
   storto) resta comunque leggibile: solo il "difference" non si
   innesca mai sul fondo scuro, il riquadro bianco c'è a prescindere. */
.hero-title .line > span.is-revealed {
  animation: none;
  transform: none;
}

/* A schermo largo il wrapper non ha un position proprio: i due figli
   restano posizionati in modo assoluto rispetto alla hero, esattamente
   come se il wrapper non ci fosse. Sotto i 560px il wrapper prende
   posizione lui e li mette in colonna — vedi il breakpoint mobile. */
/* Colore fisso, non var(--muted): quella variabile è pensata per testo
   scuro su fondo chiaro, qui il fondo hero è scuro a prescindere dal
   tema del resto del sito. */
.hero-tag-bottom {
  position: absolute;
  z-index: 2;
  left: 40px;
  bottom: 32px;
  margin: 0;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #fff;
  max-width: 26ch;
  opacity: 0;
  animation: fade-up 0.8s cubic-bezier(0.16, 1, 0.3, 1) 0.6s forwards;
}

.hero-scroll {
  position: absolute;
  z-index: 2;
  left: 50%;
  bottom: 32px;
  transform: translateX(-50%);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: rgba(255, 255, 255, 0.65);
  opacity: 0;
  animation: fade-up 0.8s cubic-bezier(0.16, 1, 0.3, 1) 0.7s forwards;
  transition: color 0.2s ease;
}

.hero-scroll:hover { color: #fff; }

.hero-scroll-bracket { color: var(--accent); }

/* ---------- Word marquee (under hero) ---------- */

.word-marquee {
  /* Sopra al footer sticky, come .hero e <main>: vedi .site-footer. */
  position: relative;
  z-index: 1;
  background: whitesmoke;
  border-bottom: 1px solid var(--border);
  padding: 12px 0;
  overflow: hidden;
}

.word-marquee-row {
  overflow: hidden;
  width: 100%;
}

.word-marquee-row + .word-marquee-row {
  margin-top: 10px;
}

.word-marquee-track {
  display: flex;
  align-items: center;
  gap: 28px;
  width: max-content;
  white-space: nowrap;
}

.word-marquee-track span:not(.star) {
  font-size: clamp(26px, 4.6vw, 52px);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: -0.01em;
  color: black;
}

.word-marquee-track .star {
  font-size: clamp(34px, 5.6vw, 56px);
  line-height: 1;
  color: #ef2b2b;
  transform: translateY(-0.05em);
}

.word-marquee-track.dir-ltr {
  animation: word-scroll-ltr 58s linear infinite;
}

.word-marquee-track.dir-rtl {
  animation: word-scroll-rtl 58s linear infinite;
}

@keyframes word-scroll-ltr {
  from { transform: translateX(-50%); }
  to { transform: translateX(0); }
}

@keyframes word-scroll-rtl {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

@media (max-width: 560px) {
  .word-marquee { padding: 14px 0; }
  .word-marquee-row + .word-marquee-row { margin-top: 4px; }
  .word-marquee-track { gap: 16px; }
}

@media (prefers-reduced-motion: reduce) {
  .word-marquee-track.dir-ltr,
  .word-marquee-track.dir-rtl {
    animation: none;
  }
}

/* Titolo presente per screen reader e navigazione per intestazioni,
   ma fuori dal flusso visivo. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- Sections ---------- */

/* Niente linea in testa alla sezione: la riga di separazione ora sta sotto
   l'intestazione (numero, nome, anno), dentro i margini del testo. */
.section {
  padding: 160px 40px;
  scroll-margin-top: 32px;
}

.section-tag {
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 500;
  color: var(--muted);
  margin: 0 0 56px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--border);
}

/* Sezioni numerate: il numero resta a filo sinistro, il nome va a filo
   destro. Le altre .section-tag (pagine progetto, privacy) non hanno il
   numero e restano un semplice blocco di testo allineato a sinistra. */
/* Tre celle con i lati uguali: così il nome resta centrato sulla riga, non
   a metà strada fra il numero e l'anno (che hanno larghezze diverse). */
.section-tag--num {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: baseline;
  gap: 16px;
}

.section-tag-anno {
  text-align: right;
  color: var(--muted-2);
}

/* In Martian Mono il © è già disegnato all'altezza di una cifra e appoggiato
   sulla linea di base (a differenza di Space Mono, dove era minuscolo e
   sollevato): qui non serve più correggerlo cambiando famiglia. La classe
   resta nel markup solo come marcatore. */

.section-num {
  color: var(--muted-2);
  letter-spacing: 0.08em;
}

/* ---------- Scroll reveal ---------- */

.reveal {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity 0.8s cubic-bezier(0.16, 1, 0.3, 1), transform 0.8s cubic-bezier(0.16, 1, 0.3, 1);
}

.reveal.in-view { opacity: 1; transform: translateY(0); }

/* ---------- Parallasse legato allo scroll (modulo) ---------- */

/* La cornice ha una proporzione fissa e taglia quello che esce. L'involucro
   dentro è più alto della cornice e sborda in parti uguali sopra e sotto:
   quell'eccedenza è tutto lo spazio che il parallasse ha per muoversi senza
   scoprire un bordo vuoto. Chi imposta --sbordo decide insieme la corsa
   disponibile e quanto dell'immagine resta fuori: il JS non ha una costante
   da tenere allineata, misura l'eccedenza dal DOM. */
.parallax-card {
  position: relative;
  overflow: hidden;
}

.parallax-inner {
  --sbordo: 10%;
  position: absolute;
  left: 0;
  right: 0;
  top: calc(-1 * var(--sbordo));
  height: calc(100% + 2 * var(--sbordo));
  will-change: transform;
}

.parallax-inner img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Testo che si "accende" parola per parola mentre si scorre la pagina */
.text-scan .reveal-word {
  color: var(--muted-2);
  transition: color 0.5s ease;
}

.text-scan .reveal-word.lit {
  color: var(--fg);
}

/* ---------- Info ---------- */

/* Foto a sinistra con le sue proporzioni (4/5), testi a destra raccolti
   in un blocco centrato in altezza rispetto alla foto. Niente
   align-items: stretch sulla foto: con aspect-ratio e contenuto tutto
   assoluto, Safari la schiacciava ad altezza zero. */
.info-grid {
  display: grid;
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
  align-items: start;
  gap: 64px;
  margin-bottom: 120px;
  padding-bottom: 80px;
  border-bottom: 1px solid var(--border);
}

/* Oggi la griglia è l'ultima cosa della sezione: la sua riga di chiusura
   resterebbe appesa nel vuoto e i 200px sotto si sommerebbero ai 160 della
   sezione, che è lo spazio che separava About me da Visione. Se un giorno
   sotto tornerà del contenuto, il divisorio si ripresenta da sé. */
.info-grid:last-child {
  margin-bottom: 0;
  padding-bottom: 0;
  border-bottom: 0;
}

/* Foto con doppia tenda: due pannelli coprono l'immagine e si aprono
   (scaleY verso l'alto/basso) la prima volta che entra nel viewport,
   poi l'immagine interna deriva leggermente (parallax) allo scroll. */
.info-media {
  position: relative;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  background: var(--card-bg);
}

.info-media-inner {
  position: absolute;
  inset: -10% 0;
  will-change: transform;
}

.info-media-inner img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 58% 12%;
  filter: grayscale(0.25) contrast(1.05) brightness(0.85);
}

.info-curtain {
  position: absolute;
  left: 0;
  right: 0;
  height: 51%;
  background: var(--bg);
  z-index: 2;
  transition: transform 1.1s cubic-bezier(0.76, 0, 0.24, 1);
}

.info-curtain.top { top: 0; transform-origin: top; }
.info-curtain.bottom { bottom: 0; transform-origin: bottom; }

.info-media.in-view .info-curtain { transform: scaleY(0); }

/* Frase grande e paragrafo piccolo stanno vicini, come un unico blocco,
   al centro dell'altezza della foto. */
.info-text-col {
  align-self: center;
  display: flex;
  flex-direction: column;
  gap: 36px;
  align-items: flex-start;
}

/* Frase grande, compatta e con la prima riga rientrata, come quella di
   Visione. */
.info-intro {
  font-size: clamp(26px, 3.3vw, 54px);
  font-weight: 500;
  letter-spacing: -0.035em;
  line-height: 0.98;
  text-indent: 12%;
  margin: 0;
}

/* Paragrafo piccolo in fondo, a destra. */
.info-dettaglio {
  align-self: flex-end;
  max-width: 52ch;
  font-size: clamp(15px, 1.15vw, 18px);
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: var(--muted);
}

.info-dettaglio p { margin: 0; }
.info-dettaglio p + p { margin-top: 1em; }

/* Ogni sezione tranne About me si apre con una frase e un paragrafo breve:
   stessa coppia di classi ovunque, così le sezioni hanno tutte lo stesso
   attacco. Le vecchie .vision-claim, .faq-heading e .contact-heading
   facevano la stessa cosa con numeri appena diversi. */
.section-claim {
  font-size: clamp(26px, 3.6vw, 48px);
  font-weight: 500;
  letter-spacing: -0.02em;
  line-height: 1.2;
  max-width: 22ch;
  margin: 0 0 24px;
}

.section-lead {
  font-size: 17px;
  line-height: 1.4;
  color: var(--muted);
  max-width: 56ch;
  margin: 0 0 72px;
}

/* ---------- Visione ---------- */

/* Titolo e paragrafo restano incollati al centro dello schermo mentre il muro
   di immagini gli passa dietro. Il testo è bianco in "difference", quindi si
   inverte da sé su qualunque immagine gli finisca sotto: nero sulle foto
   chiare, bianco sul fondo scuro, senza dover scegliere un colore che vada
   bene per tutte. */
/* Nessun padding e nessun margine aggiunto: lo spazio dopo l'ultima immagine
   è quello con cui la sezione chiude comunque, così la distanza da Servizi è
   la stessa che c'è fra tutte le altre sezioni. */
.visione-scena {
  position: relative;
}

.muro-testo {
  position: -webkit-sticky;
  position: sticky;
  /* --alt-testo è l'altezza di questo blocco, misurata dal JS. Togliendone la
     metà a 50vh il blocco si ferma col proprio centro esattamente a metà
     finestra, senza translate: era il translate a farlo salire sopra la riga
     col numero di sezione, perché si applica dopo il layout.
     Il margine che distanzia il muro sta sul muro e non qui: se stesse qui,
     negativo, il blocco avrebbe altezza zero per il calcolo dello sticky e
     resterebbe incollato anche a scena finita, finendo sopra la sezione dopo.
     Stando là, il vincolo guarda l'altezza vera e il titolo si sgancia quando
     la sua base arriva in fondo alle immagini. */
  top: calc(50vh - var(--alt-testo, 300px) / 2);
  z-index: 2;
  text-align: left;
  pointer-events: none;
  color: #fff;
  mix-blend-mode: difference;
}

/* Paragrafo-manifesto su tutta la larghezza: maiuscolo, grande, righe
   e lettere strette, allineato a sinistra. */
.muro-claim {
  font-size: clamp(26px, 4.3vw, 88px);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: -0.045em;
  line-height: 0.95;
  margin: 0;
  /* Solo la prima riga parte più avanti, le altre restano a filo. */
  text-indent: 10%;
}

/* Il muro esce dai margini della sezione e arriva ai bordi della finestra.
   Le immagini non sono in griglia: ognuna è appoggiata dove dice il suo --x /
   --y, con la larghezza che dice --w, tutto in vw. Misurare in vw e non in
   pixel tiene la composizione identica a ogni larghezza di finestra, e
   l'altezza del muro (in vw anche lei) resta quella giusta per contenerle. */
.muro {
  position: relative;
  z-index: 0;
  /* L'altezza è quella dell'ultima immagine più il suo --y: la stessa
     espressione col tetto, così il muro finisce esattamente sul suo bordo
     inferiore anche quando il tetto entra in gioco e l'immagine si accorcia.
     Da lì dipendono lo sgancio del titolo e la distanza da Servizi. */
  height: calc(92vw + min(21vw, 294px) * 1.398);
  /* Il margine è lo stesso valore del "top" del titolo, e non è un caso: il
     titolo si blocca quando la sua cima arriva a quel valore dall'alto della
     finestra, e in quell'istante il bordo superiore del muro cade esattamente
     sul fondo della finestra. Le immagini cominciano quindi a crescere da zero
     nel momento in cui il testo si ferma al centro. */
  margin: calc(50vh - var(--alt-testo, 300px) / 2) -40px 0;
}

.muro-cella {
  position: absolute;
  left: calc(var(--x) * 1vw);
  top: calc(var(--y) * 1vw);
  /* Cresce con la finestra ma si ferma alla larghezza vera del file: oltre
     quella si vedrebbero i pixel. Il tetto accorcia la cella da destra e
     dall'basso, quindi non può creare sovrapposizioni. */
  width: min(calc(var(--w) * 1vw), var(--wmax, 100vw));
}

/* La scala sta su un figlio e non sulla cella: così la cella resta ferma e
   della sua dimensione vera, e il JS può misurarla e osservarla senza leggere
   il risultato della propria trasformazione. */
.muro-cella-interno {
  transform-origin: center;
  will-change: transform;
}

/* Senza JS le immagini restano al loro posto, ferme e visibili. È il JS che
   aggiunge js-muro e prende in mano la scala. */
.js-muro .muro-cella-interno { transform: scale(0); }

/* Dove il browser sa animare sulla posizione di scorrimento (Chrome 115+,
   Safari 26+) la scala la fa il compositore invece del JS: nessun frame di
   ritardo fra dove sta la pagina e quanto è grande l'immagine, che è quello
   che sul telefono si vede come tremolio. La timeline la dichiara la cella,
   che non viene mai trasformata, così la misura non si legge addosso il
   risultato della scala. */
@supports (animation-timeline: view()) {
  .muro-cella-interno {
    animation: muro-scala linear both;
    /* view() e non una timeline con un nome: otto celle sorelle che
       dichiarano lo stesso nome lo rendono ambiguo, e una timeline che non si
       risolve spegne l'animazione. Con view() ogni elemento è la propria
       timeline. La posizione la legge dal layout, non dal riquadro già
       scalato, quindi il conto non si legge addosso il proprio risultato. */
    animation-timeline: view(block);
    /* L'intera traversata: da quando entra dal bordo basso a quando esce da
       quello alto. */
    animation-range: cover 0% cover 100%;
  }

  /* La stessa curva del modulo JS: cubica in uscita mentre entra, cubica in
     entrata mentre esce. */
  @keyframes muro-scala {
    0% {
      transform: scale3d(0, 0, 1);
      animation-timing-function: cubic-bezier(0.33, 1, 0.68, 1);
    }
    50% {
      transform: scale3d(1, 1, 1);
      animation-timing-function: cubic-bezier(0.32, 0, 0.67, 0);
    }
    100% { transform: scale3d(0, 0, 1); }
  }
}

.muro-cella img {
  display: block;
  width: 100%;
  height: auto;
  filter: grayscale(1) contrast(1.06);
}

@media (prefers-reduced-motion: reduce) {
  .js-muro .muro-cella-interno { transform: none; }
  .muro-cella-interno { animation: none; transform: none; }
}

/* ---------- Lavori ---------- */

/* Griglia editoriale asimmetrica: ogni card ha la sua larghezza, il suo
   lato e la sua proporzione. Gli scarti verticali negativi fanno incastrare
   le card a coppie (una a sinistra, una a destra): le larghezze sono scelte
   perché la somma di due card affiancate resti sotto il 100%, così non si
   sovrappongono mai in orizzontale. Cambiando le sei righe qui sotto si
   ridisegna la sezione senza toccare il markup. */
.works-list {
  margin-top: 40px;
  display: flex;
  flex-direction: column;
}

.work-card {
  display: block;
  width: 100%;
  color: inherit;
}

.work-card:nth-child(1) { width: 46%; }
.work-card:nth-child(2) { width: 50%; margin: -200px 0 0 auto; }
.work-card:nth-child(3) { width: 54%; margin-top: 140px; }
.work-card:nth-child(4) { width: 54%; margin: 140px 0 0 auto; }
.work-card:nth-child(5) { width: 44%; margin-top: -180px; }
.work-card:nth-child(6) { width: 56%; margin: 120px 0 0 auto; }

/* La cornice ha la proporzione dell'immagine, quindi la sua altezza è la
   stessa che avevano le card prima del parallasse. Per avere spazio in cui
   muoversi l'immagine viene ingrandita del 130% (vedi --sbordo): a cornice
   piena si vede la parte centrale, e agli estremi della corsa si perde una
   fascia su un lato solo. La varietà della griglia la danno le larghezze. */
.work-card-media {
  aspect-ratio: 4 / 3;
  background: var(--card-bg);
}

/* Quanto può muoversi un'immagine dipende da quanto margine ha attorno al
   soggetto: l'ingrandimento che le dà la corsa è anche quello che le mangia
   i bordi. Ho misurato immagine per immagine dove finisce il soggetto e qui
   sotto ogni card tiene lo sbordo che il suo margine regge, agli estremi
   della corsa e non solo da fermo. Il JS legge la corsa dal DOM, quindi non
   c'è nient'altro da aggiornare. Ordine card: Manenti, Scalvi, Botanica,
   Matt, Officina, Bertolini (coming soon, in fondo).

   Manenti: i biglietti arrivano al 9,3% dal bordo sinistro.
   Scalvi: la scheda tecnica sta al 10% dal bordo alto.
   Botanica: i bugiardini stanno all'11,7%.
   Matt: il calendario sta al 24% dai bordi, sta larghissimo.
   Officina: il mockup sta al 17,6% dal bordo alto.
   Bertolini: è una fotografia, non ha un soggetto da contenere. */
.work-card:nth-child(1) .parallax-inner { --sbordo: 8%; }
.work-card:nth-child(2) .parallax-inner { --sbordo: 5%; }
.work-card:nth-child(3) .parallax-inner { --sbordo: 6%; }
.work-card:nth-child(4) .parallax-inner { --sbordo: 15%; }
.work-card:nth-child(5) .parallax-inner { --sbordo: 9%; }
.work-card:nth-child(6) .parallax-inner { --sbordo: 15%; }

.work-card-media img {
  filter: grayscale(0.2) contrast(1.05) brightness(0.9);
  transition: transform 0.7s cubic-bezier(0.16, 1, 0.3, 1), filter 0.6s ease;
}

.work-card:hover .work-card-media img,
.work-card:focus-visible .work-card-media img {
  transform: scale(1.04);
  filter: grayscale(0) contrast(1.05) brightness(1);
}

/* Bertolini: progetto non ancora finito, card presente ma non cliccabile.
   Immagine sfocata fissa (niente sblur/zoom in hover) + badge "Coming soon". */
.work-card--soon {
  cursor: default;
}

.work-card--soon .work-card-media {
  position: relative;
}

.work-card--soon .work-card-media img {
  filter: grayscale(0.3) contrast(1.05) brightness(0.85) blur(6px);
  transform: scale(1.08);
}

.work-card--soon:hover .work-card-media img,
.work-card--soon:focus-visible .work-card-media img {
  transform: scale(1.08);
  filter: grayscale(0.3) contrast(1.05) brightness(0.85) blur(6px);
}

.work-card-soon-badge {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-size: clamp(13px, 1.3vw, 16px);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #fff;
  background: rgba(0, 0, 0, 0.25);
  pointer-events: none;
}

.work-card-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  margin-top: 18px;
}

.work-card-name {
  font-size: clamp(17px, 1.6vw, 22px);
  font-weight: 500;
  letter-spacing: -0.01em;
}

.work-card-year,
.work-card-kind {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--muted-2);
}

.work-card-year { flex: none; }
.work-card-kind { margin: 6px 0 0; }

/* ---------- Servizi (fisarmonica) ---------- */

/* Qui titolo e paragrafo non stanno uno sopra l'altro come nelle altre
   sezioni: il titolo resta a sinistra, il paragrafo va a destra, alla sua
   altezza. */
.servizi-intro {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: start;
  gap: 80px;
  margin-bottom: 72px;
}

.servizi-intro .section-claim,
.servizi-intro .section-lead {
  margin: 0;
}

/* Un servizio per riga, uno sotto l'altro: la riga sborda di 20px oltre
   il testo (padding sull'item + margine negativo) così il riempimento nero
   al passaggio del mouse arriva fino al bordo della sezione invece di
   fermarsi al testo. */
.servizi-lista {
  display: flex;
  flex-direction: column;
}

.servizio {
  position: relative;
  border-top: 1px solid var(--border);
  padding: 0 20px;
  margin: 0 -20px;
  overflow: hidden;
}

/* Sopra il primo item c'è già la riga che chiude l'intro: un secondo
   bordo qui sopra sarebbe doppio. */
.servizio:first-child {
  border-top: none;
}

.servizio:last-child {
  border-bottom: 1px solid var(--border);
}

/* Il riempimento è un pseudo-elemento che parte a larghezza zero ancorato a
   sinistra (transform-origin) e si apre verso destra: transform invece di
   width/background-color perché anima sulla GPU senza ricalcolare il
   layout. Si attiva sia al passaggio del mouse sia, in modo permanente,
   sulla riga aperta: var(--fg)/var(--bg), quindi segue da solo il tema
   chiaro/scuro del momento. */
.servizio::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--fg);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.45s cubic-bezier(0.65, 0, 0.35, 1);
  pointer-events: none;
}

.servizio:hover::before,
.servizio:has(.servizio-testa[aria-expanded="true"])::before {
  transform: scaleX(1);
}

.servizio-testa {
  position: relative;
  z-index: 1;
  width: 100%;
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr) auto 22px;
  align-items: center;
  gap: 20px;
  background: none;
  border: none;
  color: var(--fg);
  font-family: inherit;
  font-size: clamp(15px, 1.3vw, 17px);
  font-weight: 500;
  letter-spacing: -0.01em;
  text-align: left;
  padding: 26px 0;
  cursor: pointer;
  transition: color 0.3s ease;
}

.servizio:hover .servizio-testa,
.servizio:has(.servizio-testa[aria-expanded="true"]) .servizio-testa {
  color: var(--bg);
}

.servizio-num {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--muted-2);
  transition: color 0.3s ease;
}

.servizio:hover .servizio-num,
.servizio:has(.servizio-testa[aria-expanded="true"]) .servizio-num {
  color: var(--bg);
  opacity: 0.6;
}

.servizio-nome {
  min-width: 0;
  font-family: var(--font-mono);
  font-weight: 400;
  letter-spacing: 0.01em;
  text-transform: uppercase;
}

.servizio-tag {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted-2);
  white-space: nowrap;
  transition: color 0.3s ease;
}

.servizio:hover .servizio-tag,
.servizio:has(.servizio-testa[aria-expanded="true"]) .servizio-tag {
  color: var(--bg);
  opacity: 0.75;
}

.servizio-icona {
  position: relative;
  flex-shrink: 0;
  width: 22px;
  height: 22px;
}

.servizio-icona::before,
.servizio-icona::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  background: var(--fg);
  transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.3s ease, background-color 0.3s ease;
}

.servizio-icona::before {
  width: 14px;
  height: 1.5px;
  transform: translate(-50%, -50%);
}

.servizio-icona::after {
  width: 1.5px;
  height: 14px;
  transform: translate(-50%, -50%);
}

.servizio:hover .servizio-icona::before,
.servizio:hover .servizio-icona::after {
  background: var(--bg);
}

.servizio-testa[aria-expanded="true"] .servizio-icona::after {
  transform: translate(-50%, -50%) rotate(90deg);
  opacity: 0;
}

.servizio-testa[aria-expanded="true"] .servizio-icona::before {
  background: var(--accent);
}

/* Il testo chiuso esce anche dall'albero di accessibilità: senza
   visibility: hidden gli screen reader la leggono comunque, in
   contraddizione con aria-expanded="false" sul bottone. */
.servizio-testo {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-rows: 0fr;
  overflow: hidden;
  transition: grid-template-rows 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}

.servizio:has(.servizio-testa[aria-expanded="true"]) .servizio-testo {
  grid-template-rows: 1fr;
}

/* Il testo chiuso viene marcato inert dal JS: esce dall'albero di
   accessibilità e dall'ordine di tabulazione senza toccare il layout,
   quindi l'animazione qui sopra resta intatta. */

.servizio-testo p {
  min-height: 0;
  overflow: hidden;
  margin: 0;
  max-width: 640px;
  padding-right: 40px;
  padding-bottom: 0;
  padding-left: 60px;
  font-size: 15px;
  line-height: 1.4;
  color: var(--muted);
  transition: padding-bottom 0.4s cubic-bezier(0.16, 1, 0.3, 1), color 0.3s ease;
}

/* Rientro pari alla colonna del numero (40px + il gap di 20px del grid
   qui sopra), così il testo parte allineato al nome invece che
   al bordo della sezione. */

.servizio:has(.servizio-testa[aria-expanded="true"]) .servizio-testo p {
  padding-bottom: 26px;
  color: var(--bg);
  opacity: 0.75;
}

/* ---------- Footer (contatti + footer uniti in un solo blocco) ---------- */

/* Il footer ha una sua palette scura fissa (le stesse variabili di
   .tema-scuro/.pagina-progetto), così contrasta sempre con lo sfondo
   chiaro delle sezioni sopra, qualunque sia il tema attivo lì. */
.site-footer {
  --bg: #000;
  --fg: #fff;
  --muted: #b5b5b5;
  --muted-2: #7d7d7d;
  --border: rgba(255, 255, 255, 0.14);
  background: var(--bg);
  color: var(--fg);
  /* "sticky" invece di "static": il footer resta agganciato al fondo
     della finestra da subito, ma per tutta la pagina resta coperto
     da <main> (che sta sopra, z-index più alto, sfondo pieno). Si vede
     solo negli ultimi px di scroll, quando <main> finisce: è lì che
     sembra "salire da sotto" invece di scorrere come il resto. */
  position: sticky;
  bottom: 0;
  z-index: 0;
}

/* Nelle pagine con la sola barra finale (progetti, privacy, 404) una
   riga sopra la stacca dalla pagina, che ha lo stesso colore di fondo.
   In home la stessa riga c'è già: è il bordo inferiore del palco. */
.site-footer:not(.site-footer--palco) {
  border-top: 1px solid var(--border);
}

/* Barra chiara per le pagine a fondo chiaro (privacy, 404): stessi
   valori del tema chiaro di :root. */
.site-footer--chiaro {
  --bg: whitesmoke;
  --fg: #0a0807;
  --muted: #4d4a47;
  --muted-2: #6f6b67;
  --border: rgba(10, 8, 7, 0.18);
}

/* Palco della vasca di stelle: canvas, frase-claim e contatti
   sovrapposti. Nella home il footer è alto quanto la finestra (vedi
   .site-footer--palco), il palco prende tutto lo spazio sopra la riga
   finale. In cima l'intestazione della sezione, sotto i social e la frase
   al centro dello spazio che resta. */
.footer-stage {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  padding: 40px 40px 110px;
  border-bottom: 1px solid var(--border);
}

/* Intestazione "05 [Contatti] © 2026" come quella delle sezioni sopra:
   stessa riga tenue che ha Lavori sul fondo scuro. */
.footer-tag {
  position: relative;
  z-index: 1;
  align-self: stretch;
  margin: 0;
}

/* Alto quanto la finestra meno la navbar: col footer "sticky" un footer
   più alto avrebbe la parte superiore irraggiungibile, e così non passa
   mai sotto la barra (che sopra il nero diventerebbe grigia).
   --altezza-nav la misura js/main.js. */
.site-footer--palco {
  display: flex;
  flex-direction: column;
  height: calc(100vh - var(--altezza-nav, 0px));
  height: calc(100svh - var(--altezza-nav, 0px));
  /* dvh e non svh: su iPhone, in fondo alla pagina la barra di Safari è
     ridotta e la finestra è più alta di 100svh. Con svh il footer restava
     basso e fra la navbar e "05 [Contatti]" si vedeva la fine di <main>;
     con dvh segue l'altezza visibile e le stelle hanno più spazio. */
  height: calc(100dvh - var(--altezza-nav, 0px));
}

.site-footer--palco .footer-stage {
  flex: 1;
  min-height: 0;
}

/* Finestre molto basse: il contenuto non ci starebbe in una schermata,
   quindi niente effetto "sale da sotto" e footer alto quanto serve. */
@media (max-height: 560px) {
  .site-footer--palco {
    position: relative;
    height: auto;
  }
  .site-footer--palco .footer-stage { min-height: 520px; }
}

/* Vasca di stelline: riempie tutto il palco, dietro frase e social.
   Cadono quando il footer è scoperto a metà, si accumulano sul fondo e
   schizzano via al passaggio del cursore (o a un tap) — vedi
   js/footer-fall.js. */
.footer-fall {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
}

.footer-fall-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* Frase-claim su due righe sfalsate, la seconda rientrata a destra,
   sopra le stelle che cadono dietro. */
.footer-claim {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  order: 2;
  margin: auto 0;
  font-size: clamp(36px, 6vw, 112px);
  font-weight: 500;
  letter-spacing: -0.035em;
  line-height: 1.05;
  gap: 0.12em;
  color: var(--fg);
  pointer-events: none;
}

.footer-claim-riga {
  display: block;
  white-space: nowrap;
}

.footer-claim-riga--rientro { margin-left: 2.4em; }

.footer-claim-emoji {
  display: inline-block;
  margin-left: 0.25em;
  font-size: 0.85em;
}

/* Contatti social in alto a sinistra, sotto l'intestazione e sopra la
   frase, senza riquadro. */
.footer-connect {
  position: relative;
  z-index: 1;
  order: 1;
  align-self: flex-start;
  margin-top: 32px;
}

.footer-connect-links {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

/* Identici al bottone [Let's talk] sul fondo scuro: pillola nera, bordo
   sottile, testo mono maiuscolo tra parentesi quadre arancio (stessa
   classe .talk-cta-bracket). Sfondo pieno perché le stelle passano lì
   dietro. */
.footer-pill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 13px 22px;
  background: #0a0908;
  border: 1px solid var(--border);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--fg);
  transition: border-color 0.25s ease, transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

.footer-pill:hover,
.footer-pill:focus-visible {
  border-color: var(--accent);
  transform: translateY(-2px);
}

.footer-pill:hover .talk-cta-bracket,
.footer-pill:focus-visible .talk-cta-bracket {
  transform: scale(1.4);
}

/* Grid a tre colonne invece di flex + space-between: con tre elementi di
   larghezza diversa (copyright, Top, privacy+social) space-between non
   centra davvero quello di mezzo, la colonna centrale sì. */
.footer-bottom {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  padding: 14px 40px;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--muted-2);
  gap: 12px;
}

.footer-top {
  justify-self: center;
}

.footer-right {
  /* Il bottone [Let's talk] su desktop si aggancia sopra la vasca di
     stelle prima di arrivare qui (vedi il codice in main.js), quindi
     non c'è più bisogno di scostare "Privacy policy" per lasciargli
     spazio: può stare all'estremità destra della riga. */
  justify-self: end;
}

.footer-right:hover { color: var(--fg); }

/* Etichette corte come "Top" o "Privacy policy" restavano larghe pochi
   pixel: sotto il minimo WCAG 2.2 di 24 CSS px per i target di puntamento. */
.footer-top,
.footer-right {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
}

.to-top {
  display: inline-block;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--fg);
  transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

.to-top:hover { transform: translateY(-4px); }

/* ---------- Pagina progetto ---------- */

/* Niente più foto piena a copertura hero: titolo e intro stanno su fondo
   piatto, sotto la nav fissa, sullo schema testo+meta a due colonne
   (rif. layout tipo "Cartes d'Atelier" portato nei toni del sito). */
.project-hero {
  padding: 160px 40px 64px;
}

/* Stessa pillola del [Let's talk] e dei social nel footer: testo mono
   maiuscolo fra parentesi quadre arancio (.talk-cta-bracket), che al
   passaggio si ingrandiscono mentre il bordo si accende. */
.back-link {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-bottom: 64px;
  padding: 7px 13px;
  border: 1px solid var(--border);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--fg);
  transition: border-color 0.25s ease, transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

.back-link:hover,
.back-link:focus-visible {
  border-color: var(--accent);
  transform: translateY(-2px);
}

.back-link:hover .talk-cta-bracket,
.back-link:focus-visible .talk-cta-bracket {
  transform: scale(1.4);
}

/* Freccia disegnata in SVG, tratto dritto e punte squadrate come il
   carattere mono: scivola a sinistra al passaggio, indica il ritorno. */
.back-link-freccia {
  display: block;
  flex: none;
  transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

.back-link:hover .back-link-freccia,
.back-link:focus-visible .back-link-freccia {
  transform: translateX(-3px);
}

.project-hero-title {
  margin: 0 0 64px;
  font-weight: 500;
  text-transform: uppercase;
  line-height: 0.9;
  letter-spacing: -0.07em;
  font-size: clamp(40px, 8vw, 130px);
  color: var(--fg);
  max-width: 20ch;
}

/* Metadati allineati a sinistra, sotto il titolo, con un filo che chiude
   la riga. Niente più abstract testuale qui: ripeteva il testo della
   sezione [Il progetto] poco più sotto. */
.project-meta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 32px 56px;
  padding-bottom: 56px;
  border-bottom: 1px solid var(--border);
}

.project-meta-row div {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.project-meta-label {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted-2);
}

.project-meta-value {
  font-size: 15px;
  font-weight: 500;
  color: var(--fg);
}

/* Testo e galleria affiancati: il testo resta fermo (sticky) mentre la
   galleria scorre libera accanto. Se la galleria ha più immagini del
   testo (caso comune), l'etichetta [Il progetto] si stacca prima di
   [Galleria] e la galleria continua a scorrere da sola: è il
   comportamento nativo di due colonne sticky di altezza diversa.
   Abbiamo provato a farle "uscire insieme" pareggiando l'altezza della
   colonna testo con quella della galleria (via align-items:stretch,
   poi via un padding-bottom calcolato in JS): in entrambi i casi lo
   sticky del testo smette di funzionare — verificato con test isolati.
   Il motivo: la finestra di aggancio di un elemento sticky è (altezza
   del suo containing block - altezza propria dell'elemento); se
   l'elemento sticky è l'intera colonna e la si allarga per pareggiarla
   alla galleria, è la sua STESSA altezza a crescere, e la finestra
   crolla quasi a zero. Non risolvibile senza uno sticky-tag separato
   dal testo (come la galleria già fa) — cambio di struttura più ampio,
   rimandato. Sotto i 900px (vedi media query) torna in colonna. */
.project-split {
  display: flex;
  align-items: flex-start;
  gap: 64px;
}

.project-split-text {
  flex: 0 0 44%;
  position: sticky;
  top: 125px;
}

.project-split-gallery {
  flex: 1 1 auto;
  min-width: 0;
}

/* Anche l'etichetta [Galleria] resta ferma in alto, come [Il progetto]:
   sfondo pieno perché le immagini le scorrono sotto. Stesso top del testo
   (125px) così le due etichette si agganciano allo stesso punto di scroll
   e restano allineate: se avessero soglie diverse ci sarebbe una finestra
   di scroll in cui una è già ferma e l'altra ancora in movimento, sfalsate.
   Lo spazio verso la nav (che finisce a ~82px) lo copre lo pseudo-elemento
   sotto, non un margine (un margine lascerebbe una fascia trasparente in
   cui le immagini sarebbero visibili sopra l'etichetta durante lo scroll).
   L'altezza del pseudo-elemento è un po' più alta del gap reale (125-82=
   43px) per assorbire le piccole variazioni di altezza della nav tra
   browser/zoom — ma non va esagerata: essendo un ::before assoluto
   ancorato all'etichetta, quando questa non è ancora "stuck" (posizione
   statica, più in basso nel documento) si estende comunque verso l'alto
   a partire da lì, e se troppo alto arriva a coprire contenuto sopra
   (es. la riga cliente/anno/località nell'hero). 60px lascia margine
   sufficiente senza rischiare di risalire così tanto. */
.project-split-gallery > .section-tag {
  position: sticky;
  top: 125px;
  z-index: 1;
  background: var(--bg);
}

.project-split-gallery > .section-tag::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: -60px;
  height: 60px;
  background: var(--bg);
}

.project-content {
  max-width: 68ch;
}

.project-content p {
  font-size: clamp(15px, 1.4vw, 18px);
  line-height: 1.3;
  color: var(--muted);
  margin: 0 0 24px;
}

.project-content p:last-child { margin-bottom: 0; }

.project-content strong {
  font-weight: 600;
  color: var(--fg);
}

.project-gallery {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
}

/* overflow:hidden sul contenitore, non sull'immagine: è lui a ritagliare
   lo zoom in hover (stessa ricetta di .work-card-media) così l'immagine
   ingrandita non sborda fuori dalla sua cella di griglia. */
.project-gallery > div {
  overflow: hidden;
}

.project-gallery img {
  width: 100%;
  height: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  transition: transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
}

.project-gallery > div:hover img,
.project-gallery > div:focus-visible img {
  transform: scale(1.04);
}

.project-gallery .full {
  grid-column: 1 / -1;
}

.project-gallery .full img {
  aspect-ratio: 16 / 9;
}

/* Lightbox: un overlay solo per pagina (vedi main.js), src e alt cambiano
   a ogni click invece di costruirne uno per immagine. hidden lo toglie
   dal flusso e dall'albero di accessibilità quando chiuso; is-open guida
   la transizione, con lo stesso ritardo su visibility usato per il
   pannello "Let's talk" così il fade-out finisce prima di sparire di scatto. */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 32px;
  background: rgba(10, 8, 7, 0.92);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.3s ease, visibility 0s linear 0.3s;
}

.lightbox.is-open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity 0.3s ease;
}

.lightbox-img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  cursor: default;
  transform: scale(0.96);
  transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

.lightbox.is-open .lightbox-img { transform: scale(1); }

.lightbox-close {
  position: fixed;
  top: 24px;
  right: 24px;
  z-index: 201;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.3);
  background: transparent;
  color: #fff;
  cursor: pointer;
  transition: background-color 0.2s ease, transform 0.2s ease;
}

.lightbox-close:hover,
.lightbox-close:focus-visible {
  background: rgba(255, 255, 255, 0.12);
  transform: rotate(90deg);
}

/* Frecce precedente/successiva: stesso disco della X, ai lati invece che in
   alto. disabled invece di nascoste ai due estremi della galleria, così il
   bottone resta al suo posto (niente salto di layout) ma segnala con
   opacità/cursore che lì non si può andare oltre. */
.lightbox-nav {
  position: fixed;
  top: 50%;
  z-index: 201;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.3);
  background: transparent;
  color: #fff;
  cursor: pointer;
  transform: translateY(-50%);
  transition: background-color 0.2s ease, opacity 0.2s ease;
}

.lightbox-prev { left: 24px; }
.lightbox-next { right: 24px; }

.lightbox-nav:hover,
.lightbox-nav:focus-visible {
  background: rgba(255, 255, 255, 0.12);
}

.lightbox-nav:disabled {
  opacity: 0.25;
  cursor: default;
  pointer-events: none;
}

@media (max-width: 560px) {
  .lightbox { padding: 16px; }
  .lightbox-close { top: 16px; right: 16px; width: 40px; height: 40px; }
  .lightbox-nav { width: 40px; height: 40px; }
  .lightbox-prev { left: 8px; }
  .lightbox-next { right: 8px; }
}

/* ---------- Carosello "Prossimo lavoro" (pagine progetto) ---------- */

/* Le copertine degli altri progetti scorrono da destra a sinistra senza
   fine (il movimento e le copie per chiudere il giro le fa js/main.js).
   Il carosello esce dai margini della sezione e va da bordo a bordo.
   touch-action: pan-y lascia al dito lo scroll verticale della pagina; lo
   spostamento in orizzontale lo gestisce lo script. */
.carosello {
  margin: 0 -40px;
  overflow: hidden;
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
}

/* Allineate in alto e di altezze diverse, come un collage: niente righe
   ordinate. --k scala tutto il giro sugli schermi più stretti. */
.carosello-traccia {
  --k: 1;
  display: flex;
  align-items: flex-start;
  gap: 12px;
  width: max-content;
  will-change: transform;
}

/* Larga quanto la sua immagine: il nome sotto va a capo invece di
   allargare la voce. */
.carosello-voce {
  flex: none;
  display: block;
  width: min-content;
  color: inherit;
  text-decoration: none;
  -webkit-user-drag: none;
}

/* Cinque altezze che si ripetono, come un collage. Le immagini non si
   ritagliano: la larghezza segue le proporzioni di ognuna (width/height
   nell'HTML), così biglietti, bugiardini e schermate si vedono interi. */
.carosello-voce:nth-child(5n+1) { --h: 24; }
.carosello-voce:nth-child(5n+2) { --h: 19; }
.carosello-voce:nth-child(5n+3) { --h: 27; }
.carosello-voce:nth-child(5n+4) { --h: 17; }
.carosello-voce:nth-child(5n+5) { --h: 21; }

.carosello-media {
  position: relative;
  display: block;
  height: calc(var(--h) * var(--k) * 1vw);
  overflow: hidden;
  background: var(--card-bg);
}

.carosello-media img {
  display: block;
  width: auto;
  height: 100%;
  max-width: none;
  transition: transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
}

.carosello-voce:hover .carosello-media img,
.carosello-voce:focus-visible .carosello-media img {
  transform: scale(1.05);
}

/* Bertolini: non ancora online, come nella home. Immagine sfocata ferma,
   scritta "Coming soon" sopra, niente link. */
.carosello-voce--soon .carosello-media img,
.carosello-voce--soon:hover .carosello-media img {
  filter: grayscale(0.3) contrast(1.05) brightness(0.85) blur(6px);
  transform: scale(1.08);
}

.carosello-soon {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-size: clamp(12px, 1.2vw, 15px);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #fff;
  background: rgba(0, 0, 0, 0.25);
  pointer-events: none;
}

.carosello-nome {
  display: block;
  margin-top: 12px;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  transition: color 0.3s ease;
}

.carosello-voce:not(.carosello-voce--soon):hover .carosello-nome,
.carosello-voce:focus-visible .carosello-nome {
  color: var(--fg);
}

@media (max-width: 900px) {
  .carosello-traccia { --k: 1.4; }
}

@media (max-width: 560px) {
  .carosello { margin: 0 -20px; }
  .carosello-traccia { --k: 1.8; gap: 8px; }
  .carosello-nome { margin-top: 10px; font-size: 11px; }
}

/* ---------- Let's talk (CTA fisso + pannello contatto) ---------- */

.talk-cta {
  position: fixed;
  right: 32px;
  bottom: 28px;
  z-index: 120;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  background: var(--cta-bg);
  backdrop-filter: blur(16px) saturate(1.6);
  -webkit-backdrop-filter: blur(16px) saturate(1.6);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 13px 22px;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--fg);
  transition: border-color 0.25s ease, transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

/* Quando il bottone sta sopra al footer nero (lo decide js/main.js)
   prende gli stessi colori del tema scuro della sezione Lavori. */
.talk-cta.su-footer {
  --cta-bg: rgba(10, 8, 7, 0.55);
  --fg: #fff;
  --border: rgba(255, 255, 255, 0.14);
}

.talk-cta:hover,
.talk-cta:focus-visible {
  border-color: var(--accent);
  transform: translateY(-2px);
}

.talk-cta-bracket {
  display: inline-block;
  color: var(--accent);
  transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

.talk-cta:hover .talk-cta-bracket,
.talk-cta:focus-visible .talk-cta-bracket {
  transform: scale(1.4);
}

.talk-overlay {
  position: fixed;
  inset: 0;
  z-index: 150;
  background: rgba(0, 0, 0, 0.6);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.4s ease, visibility 0s linear 0.4s;
}

.talk-overlay.is-open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity 0.4s ease;
}

.talk-panel {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 160;
  width: min(460px, 100vw);
  max-width: 100vw;
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--bg);
  border-left: 1px solid var(--border);
  padding: 104px 48px 32px;
  visibility: hidden;
  pointer-events: none;
  transform: translateX(100%);
  transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1), visibility 0s linear 0.5s;
}

.talk-panel.is-open {
  visibility: visible;
  pointer-events: auto;
  transform: translateX(0);
  transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}

.talk-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  padding: 16px 48px;
  border-bottom: 1px solid var(--border);
}

.talk-panel-tag {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}

.talk-panel-close {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--fg);
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 10px 6px;
  min-height: 44px;
  transition: color 0.2s ease;
}

.talk-panel-close:hover,
.talk-panel-close:focus-visible { color: var(--accent); }

.talk-panel-close-icon {
  display: inline-flex;
  transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

.talk-panel-close:hover .talk-panel-close-icon,
.talk-panel-close:focus-visible .talk-panel-close-icon { transform: rotate(90deg); }

.talk-panel-title {
  margin: 0 0 12px;
  font-size: clamp(30px, 4vw, 40px);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: -0.02em;
  line-height: 1.05;
}

.talk-panel-title .reg { color: var(--accent); }

.talk-panel-sub {
  margin: 0 0 36px;
  font-size: 15px;
  color: var(--muted);
}

.talk-form {
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin-bottom: 32px;
}

.talk-field {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.talk-field label {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted-2);
}

.talk-field input,
.talk-field textarea {
  background: none;
  border: none;
  border-bottom: 1px solid var(--border);
  color: var(--fg);
  font-family: var(--font);
  font-size: 16px;
  padding: 8px 2px;
  resize: none;
  transition: border-color 0.25s ease;
}

.talk-field textarea { min-height: 48px; }

.talk-field input:focus,
.talk-field textarea:focus {
  outline: none;
  border-bottom-width: 2px;
  border-color: var(--accent);
  padding-bottom: 7px; /* compensa il bordo più spesso: niente scatto */
}

.talk-submit {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--fg);
  font-family: var(--font-mono);
  font-size: 15px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 10px 4px;
  margin-top: 8px;
}

.talk-submit:hover .talk-cta-bracket,
.talk-submit:focus-visible .talk-cta-bracket { transform: scale(1.4); }

.talk-submit:disabled {
  opacity: 0.5;
  cursor: default;
}

/* Esito dell'invio: vuoto finché non serve, così non occupa spazio. */
.talk-status {
  margin: 12px 0 0;
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1.4;
}

.talk-status:empty { display: none; }
/* Verde più scuro del solito: quello chiaro leggibile sul nero di prima
   spariva quasi del tutto su un fondo già chiaro come questo. */
.talk-status[data-state="ok"] { color: #1c7a41; }
.talk-status[data-state="error"] { color: var(--accent); }

/* Esca antispam: fuori dallo schermo, ma non display:none, altrimenti
   i bot la riconoscono e la saltano. */
.talk-form input[name="_gotcha"] {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  opacity: 0;
}

.talk-panel-direct {
  font-size: 13px;
  color: var(--muted-2);
  padding-top: 26px;
  border-top: 1px solid var(--border);
}

.talk-panel-direct a {
  color: var(--fg);
  text-decoration: underline;
  text-underline-offset: 3px;
  transition: color 0.2s ease;
}

.talk-panel-direct a:hover { color: var(--accent); }

/* ---------- Responsive ---------- */

/* L'orologio è centrato in assoluto sulla barra, quindi non "sa" quanto
   spazio occupano logo e voci: sotto una certa larghezza ci finisce sopra.
   Da qui in giù torna un elemento in fila, e poco più sotto lascia solo
   icona e ora. */
@media (max-width: 1180px) {
  .hero-nav-clock {
    position: static;
    left: auto;
    top: auto;
    transform: none;
  }
}

/* Schermi medi e tablet: colonna del testo più stretta, il paragrafo può
   allargarsi di più e restare su meno righe. */
@media (max-width: 1200px) {
  .info-grid { gap: 48px; }
  .info-text-col { gap: 32px; }
  .info-dettaglio { max-width: 58ch; }
}

@media (max-width: 1050px) {
  .hero-nav-clock-code { display: none; }

  /* Sotto i 1050px titolo e paragrafo dei servizi tornano uno sopra
     l'altro. */
  .servizi-intro {
    grid-template-columns: minmax(0, 1fr);
    gap: 24px;
    margin-bottom: 56px;
  }

  .servizi-intro .section-lead { margin: 0; }
}

@media (max-width: 900px) {
  .nav-toggle { display: flex; }

  .section-lead { margin-bottom: 56px; }

  .project-split { flex-direction: column; gap: 0; }
  .project-split-text { position: static; flex: none; }
  .project-split-gallery { margin-top: 64px; }
  .project-split-gallery > .section-tag { position: static; background: none; }
  .project-split-gallery > .section-tag::before { display: none; }

  .hero-nav-clock { font-size: 11px; gap: 5px; }
  .hero-nav-clock-icon { width: 12px; height: 12px; }

  .hero-nav-links {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 100dvh;
    z-index: 90;
    flex-direction: column;
    justify-content: center;
    align-items: flex-start;
    gap: 8px;
    padding: 0 40px;
    background: var(--bg);
    mix-blend-mode: normal;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transform: translateY(-8px);
    transition: opacity 0.25s ease, transform 0.25s ease, visibility 0s linear 0.25s;
  }

  .hero-nav-links.nav-open {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transform: translateY(0);
    transition: opacity 0.25s ease, transform 0.25s ease;
  }

  .nav-link {
    display: flex;
    gap: 16px;
    /* Nel pannello a tutto schermo il testo sta su una superficie piena e
       non serve l'inversione: il "difference" ribalterebbe anche
       l'arancione del numero attivo, che diventerebbe azzurro. Vale
       sempre, anche nel momento scuro di Lavori: lo specifica di nuovo
       qui sotto, perché lì .tema-scuro .nav-link ha due classi e
       vincerebbe altrimenti su questa regola. */
    mix-blend-mode: normal;
    color: var(--fg);
    width: 100%;
    height: auto;
    padding: 16px 0;
    font-size: clamp(28px, 8vw, 40px);
    border-top: 1px solid var(--border);
  }

  .tema-scuro .nav-link {
    mix-blend-mode: normal;
  }

  /* Numero a filo sinistro, nome spinto a destra dal margine automatico. */
  .nav-link-num {
    display: block;
    flex: none;
    align-self: flex-start;
    margin-top: 0.5em;
    font-family: var(--font-mono);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.08em;
    color: var(--muted-2);
    transition: color 0.2s ease;
  }

  /* Sezione in cui si sta navigando: il suo numero si accende. Lo stato
     lo scrive main.js sullo scroll, come aria-current. */
  .nav-link[aria-current] .nav-link-num { color: var(--accent); }

  .nav-meta,
  .nav-legal {
    display: block;
    position: absolute;
    left: 40px;
    margin: 0;
    font-family: var(--font-mono);
    font-size: 12px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--muted-2);
  }

  .nav-meta { top: 32px; }
  .nav-legal { bottom: 36px; text-transform: none; }

  .nav-meta-dot {
    display: inline-block;
    width: 7px;
    height: 7px;
    margin-right: 10px;
    background: var(--accent);
    vertical-align: middle;
  }

  .nav-legal a {
    color: var(--muted-2);
    text-decoration: none;
    border-bottom: 1px solid var(--border);
    padding-bottom: 3px;
  }

  .nav-link:last-of-type { border-bottom: 1px solid var(--border); }
  .nav-link-inner { transition: none; }
  .nav-link:hover .nav-link-inner { transform: none; }
  .nav-link-inner span:last-child { display: none; }

  .info-grid { grid-template-columns: 1fr; gap: 32px; margin-bottom: 80px; padding-bottom: 56px; }
  /* Una colonna sola: frase grande, paragrafo spostato sulla metà destra,
     foto in fondo. */
  /* Foto in verticale con le proporzioni dello scatto (3/4): in 4/3 si
     vedeva solo la testa. Il margine per il parallax è ridotto al minimo
     che copre lo spostamento (22px), così il ritaglio sopra e sotto è
     quasi nullo e si vede tutta la figura. */
  .info-media { order: 1; aspect-ratio: 3 / 4; width: 100%; max-width: 460px; }
  .info-media-inner { inset: -28px 0; }
  .info-text-col { gap: 28px; }

  /* Su schermo stretto le card restano in colonna e nell'ordine, ma
     conservano larghezze e lati diversi: incastri e scarti negativi no,
     lì lo spazio non basta e finirebbero una sull'altra. Le proporzioni
     sono quelle delle immagini, come sul desktop. */
  /* Sotto i 900px il tag di categoria toglie spazio proprio dove serve
     al nome: esce dalla riga, restano numero, nome e croce. */
  .servizio {
    padding: 0 16px;
    margin: 0 -16px;
  }

  .servizio-testa {
    grid-template-columns: 32px minmax(0, 1fr) 20px;
    gap: 14px;
  }

  .servizio-tag { display: none; }

  .servizio-testo p {
    padding-left: 46px;
    padding-right: 0;
  }

  .works-list { gap: 56px; margin-top: 24px; }

  .work-card:nth-child(1) { width: 100%; margin: 0; }
  .work-card:nth-child(2) { width: 84%;  margin: 0 0 0 auto; }
  .work-card:nth-child(3) { width: 100%; margin: 0; }
  .work-card:nth-child(4) { width: 78%;  margin: 0; }
  .work-card:nth-child(5) { width: 86%;  margin: 0 0 0 auto; }
  .work-card:nth-child(6) { width: 92%;  margin: 0; }

  .project-meta-row { gap: 20px 32px; }

  /* A 375px i cinque link in fila sforavano di otto pixel e la pagina
     scorreva di lato: spazi più corti e, se non bastano, vanno a capo. */
  .footer-connect-links { gap: 8px 10px; }

  .nav-link,
  .footer-top,
  .footer-right {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }

  /* La pillola resta piccola: l'area di tocco la allarga un bordo
     invisibile fino a 44px di altezza. */
  .back-link { position: relative; }
  .back-link::after {
    content: "";
    position: absolute;
    inset: -9px 0;
  }

  /* Sotto i 900px: frase al centro, sotto i social in colonna a
     sinistra; il [Let's talk] si ferma alla loro altezza, a destra
     (vedi js/main.js). */
  .footer-stage {
    align-items: stretch;
    justify-content: flex-start;
    padding: 40px 24px 40px;
  }

  .footer-connect {
    order: 3;
    margin-top: 40px;
  }

  .footer-connect-links {
    flex-direction: column;
    align-items: flex-start;
  }

  .footer-claim {
    margin: auto 0;
    font-size: clamp(34px, 8vw, 72px);
  }

  .footer-claim-riga { white-space: normal; }
  .footer-claim-riga--rientro { margin-left: 10vw; }

  .talk-panel { padding: 96px 32px 32px; }
  .talk-panel-head { padding: 14px 24px; }
}

@media (max-width: 560px) {
  .hero-nav { padding: 20px; }
  .hero-nav-links { padding: 0 20px; }
  .nav-meta, .nav-legal { left: 20px; }
  .nav-meta { top: 24px; }
  /* Hera più bassa di 100vh, di nuovo un po' meno di prima (90vh): con
     lo spessore del bagliore alzato in hero-background.js questa
     misura basta a farlo toccare il titolo senza schiacciare troppo
     la composizione. */
  .hero { min-height: 82vh; }

  .hero-content { padding: 90px 16px; }

  /* Righe un po' più grandi: sotto i 560px il fattore vw del clamp di
     base è quasi sempre al minimo (20px), qui il minimo si alza. */
  .hero-title { gap: 8px; }
  .hero-title .line > span { font-size: clamp(28px, 9vw, 60px); padding: 0.2em 0.3em; }

  /* Il tag prende il posto di "Scroll down" in basso a sinistra (vedi
     .hero-scroll subito sotto) invece di stare in alto: un solo testo
     nell'angolo invece di due. Bottom un po' più alto del bordo della
     hera, per dare un minimo di respiro rispetto al fondo. */
  .hero-tag-bottom {
    position: absolute;
    left: 20px;
    top: auto;
    bottom: 28px;
    max-width: 20ch;
  }

  /* "Scroll down" tolto su mobile: al suo posto ora c'è il tag qui sopra. */
  .hero-scroll {
    display: none;
  }

  .section { padding: 100px 20px; }

  /* Sotto i 560px vale la composizione gemella scritta nell'HTML: immagini
     più piccole e sfalsate una a una fra sinistra e destra, perché in 390px
     di larghezza la disposizione da desktop si leggerebbe come una colonna.
     Fra 560 e 900px resta quella da desktop, che essendo in vw rimpicciolisce
     da sé restando proporzionata. */
  .muro {
    height: calc(202vw + min(50vw, 294px) * 1.398);
    margin: calc(50vh - var(--alt-testo, 300px) / 2) -20px 0;
  }

  .muro-claim { font-size: 8.8vw; text-indent: 0; }

  .muro-cella {
    left: calc(var(--xm) * 1vw);
    top: calc(var(--ym) * 1vw);
    width: calc(var(--wm) * 1vw);
  }
  .info-grid { margin-bottom: 56px; padding-bottom: 40px; gap: 48px; }
  /* Su telefono frase e paragrafo partono entrambi dal bordo sinistro:
     niente rientro e niente spostamento a destra. */
  .info-intro { font-size: 7vw; text-indent: 0; }
  .info-dettaglio {
    align-self: flex-start;
    max-width: none;
    font-size: 13px;
    line-height: 1.2;
  }

  /* Su telefono: copyright su due righe a sinistra, privacy a destra,
     niente "Back to top". Lo spazio sotto è per il [Let's talk], che qui
     resta fisso in basso a destra e non deve coprire la privacy. */
  .footer-bottom {
    padding: 14px 20px;
    grid-template-columns: 1fr auto;
    align-items: end;
    font-size: 10px;
  }
  body:has(#talkCta) .footer-bottom { padding-bottom: 84px; }
  .footer-top { display: none; }
  .footer-right { justify-self: end; }
  .footer-copy { display: flex; flex-direction: column; }
  .footer-copy-sep { display: none; }

  /* Frase in cima sotto il menu, social in un'unica riga orizzontale
     poco sotto; le stelle cadono nello spazio che resta. Bottoni
     rimpiccioliti in vw perché i quattro stiano affiancati anche sui
     telefoni stretti. */
  .site-footer--palco .footer-connect {
    margin-top: 32px;
  }
  .site-footer--palco .footer-connect-links {
    flex-direction: row;
    flex-wrap: nowrap;
    justify-content: space-between;
    gap: 1.6vw;
  }
  .site-footer--palco .footer-pill {
    gap: 0.3em;
    padding: 8px 1.6vw;
    font-size: clamp(7.5px, 2.3vw, 11px);
    letter-spacing: 0;
  }
  /* Seconda riga (con l'emoji) sempre intera: la dimensione in vw è
     calcolata perché "You know what to do 🤟🏼" ci stia col suo rientro. */
  .site-footer--palco .footer-claim {
    margin: 40px 0 0;
    font-size: 7.6vw;
    gap: 0.35em;
  }
  .site-footer--palco .footer-claim-riga { white-space: nowrap; }
  .site-footer--palco .footer-claim-riga--rientro { margin-left: 8vw; }

  .project-hero { padding: 100px 20px 40px; }
  .project-hero-title { margin-bottom: 40px; }
  .project-meta-row { flex-direction: column; gap: 20px; padding-bottom: 40px; }
  .project-gallery { grid-template-columns: 1fr; }
  .project-gallery .full img,
  .project-gallery img { aspect-ratio: 4 / 3; }

  .talk-cta { right: 16px; bottom: 24px; padding: 11px 18px; font-size: 12px; }
  .footer-pill { padding: 11px 18px; font-size: 12px; }
  .footer-connect-links { gap: 10px; }
  .footer-stage { padding: 32px 20px 24px; }
  .talk-panel { padding: 88px 20px 28px; }
  .talk-panel-head { padding: 12px 20px; }
  .talk-panel-title { font-size: clamp(26px, 8vw, 34px); }
}

/* Telefoni molto stretti (sotto i 360px): quattro bottoni in riga
   sarebbero illeggibili, quindi vanno a capo su due righe. */
@media (max-width: 359px) {
  .site-footer--palco .footer-connect-links {
    flex-wrap: wrap;
    justify-content: flex-start;
    gap: 8px;
  }
  .site-footer--palco .footer-pill { font-size: 9px; padding: 8px 10px; }
}
