/* NOIRMANCER — see design-notes.md */

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

/* Il VERO Microgramma, in locale (cartella fonts/, 2026-07-15):
   - Bold Extended → solo la parola NOIRMANCER (tracciata in WebGL da
     js/intro3d.js); il peso è intrinseco al font, NIENTE stroke aggiunto
   - Medium Extended → tutti gli altri testi del sito */
@font-face {
  font-family: "Microgramma Bold";
  src: url("../fonts/Microgramma D Extended Bold/Microgramma D Extended Bold.woff2") format("woff2"),
       url("../fonts/Microgramma D Extended Bold/Microgramma D Extended Bold.otf") format("opentype");
  font-weight: 400;
  font-style: normal;
  font-display: block; /* usato solo dall'intro, che comunque ne attende il load */
}

@font-face {
  /* file precedente (MicrogrammaDEEMedExt, da fontsgeek.com) aveva la cmap
     rotta sulle lettere accentate italiane (à/è/ì/ò/ù mostravano glifi
     est-europei sbagliati, es. "ò" → "ň"): sostituito 2026-07-26 con questo
     file (fornito dal cliente), stesso disegno ma accenti corretti — testato
     su tutte le vocali accentate maiuscole e minuscole. */
  font-family: "Microgramma Medium";
  src: url("../fonts/Microgramma D Extended Medium/Microgramma D Extended Medium.woff2") format("woff2"),
       url("../fonts/Microgramma D Extended Medium/Microgramma D Extended Medium.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

:root {
  --form-bg: #1e1e1e;
  --grey: #6e6e6e;
  --ease-wipe: cubic-bezier(0.87, 0, 0.13, 1);
  /* più morbida di --ease-wipe (pensata per i grandi movimenti, apertura
     menu/pagina): per micro-interazioni come l'hover, decelerazione pulita
     senza rimbalzo */
  --ease-hover: cubic-bezier(0.33, 1, 0.68, 1);
  --font: "Microgramma Medium", sans-serif;
}

html, body { height: 100%; }

body {
  font-family: var(--font);
  font-weight: 400;
  background-color: #000;
  color: #fff;
  text-transform: uppercase;
  overflow: hidden;
}

::selection { background: #fff; color: #000; }

a, button, [role="button"], label, input[type="checkbox"] { cursor: crosshair; }
a { all: unset; cursor: crosshair; }

/* ---------- Video di intro (home): prima del nome ---------- */

.intro-video {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100vw;
  height: 100vh;
  object-fit: contain; /* verticale: su desktop bande nere invisibili sul nero */
  background: #000;
  z-index: 3000;
  cursor: crosshair;
  transition: opacity 0.5s ease;
}

.intro-video.is-done {
  opacity: 0;
  pointer-events: none;
}

.intro-video.is-hidden { display: none; }

/* Intro già vista in questa sessione (scelta cliente 2026-07-23): non si
   riproduce più. La classe `intro-seen` la mette uno script inline nell'<head>
   (da sessionStorage) prima ancora che il <body> sia letto, così il video non
   compare mai — nessun flash del primo fotogramma. main.js va dritto al menu. */
.intro-seen .intro-video { display: none; }

/* hint mostrato solo se l'autoplay è negato (serve un tap per avviare) */

.intro-video__hint {
  position: fixed;
  bottom: 12vh;
  left: 0;
  right: 0;
  margin: 0 auto;
  width: max-content;
  z-index: 3001;
  color: var(--grey);
  font-size: 0.7rem;
  opacity: 0;
  pointer-events: none; /* i tap passano al video sotto */
  transition: opacity 0.4s ease;
}

.intro-video__hint.is-visible {
  opacity: 1;
  animation: blink 2.6s ease-in-out infinite;
}

@keyframes blink {
  0%, 50%, 100% { opacity: 1; }
  25%, 75% { opacity: 0; }
}

/* ---------- Stage (home): nome animato -> logo, unico elemento ---------- */

.stage {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* tela WebGL dell'intro anamorfica: copre il palco finché il logo non atterra */

.intro3d-canvas {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100vw;
  height: 100vh;
  z-index: 900;
  pointer-events: none; /* i click passano al link invisibile sotto (bersaglio reale) */
  transition: opacity 0.24s linear, transform 0.6s var(--ease-wipe);
}

/* ANDROID — rotazione 3D col pollice. Senza touch-action il browser reclama lo
   swipe come scroll / pull-to-refresh / navigazione e ANNULLA il flusso di
   pointermove (pointercancel) → la rotazione muore al primo movimento. Su
   desktop non accade (il mouse non genera gesti di scroll). I listener della
   rotazione sono passive:true e NON possono preventDefault: l'unica via è la
   CSS. Ambito: solo mentre il logo WebGL è vivo (classe su <body>, presente
   solo sulla home, che non scrolla mai) → works/form/about restano scrollabili.
   touch-action:none copre anche i discendenti (il link .logo) per intersezione
   con gli antenati; overscroll-behavior spegne il pull-to-refresh. */
body.webgl-logo { touch-action: none; overscroll-behavior: none; }

/* Il logo resta questo canvas PER SEMPRE (nessun handoff verso un SVG dopo
   l'atterraggio): niente secondo elemento con cui far combaciare risoluzione o
   antialiasing, quindi niente "pop" percepibile, per costruzione. Il canvas è
   un elemento a sé (figlio di <body>, non del link — un discendente fisso
   dentro un antenato trasformato erediterebbe le trasformazioni in modo
   fragile/da verificare), quindi la transizione di apertura menu, normalmente
   su .logo, è gemellata qui con gli stessi valori: main.js sincronizza la classe
   is-open sul canvas quando la cambia sul link. Niente zoom sull'hover (rimosso
   su richiesta cliente 2026-07-24): il logo non si ingrandisce al passaggio. */

/* apertura menu: SOLO zoom in dissolvenza (niente più volo in alto + rotazione
   + rimpicciolimento) — il logo si ingrandisce e sfuma, rivela il menu sotto.
   La chiusura non passa più dal tap sul logo (che è invisibile, ingrandito):
   overlay/Esc restano gli unici modi (già presenti in main.js). */
.intro3d-canvas.is-open { transform: scale(1.2); opacity: 0; transition: opacity 0.6s var(--ease-wipe), transform 0.6s var(--ease-wipe); }

/* invito al click: finita la rotazione (classe is-landed da js/intro3d.js),
   un accenno di bounce — una volta sola, dopo un piccolo respiro. Se il menu
   è già aperto l'animazione decade (:not(.is-open)): il transform resta a
   is-open, nessuna contesa. */
.intro3d-canvas.is-landed:not(.is-open) {
  animation: logo-invite 0.85s var(--ease-hover) 0.4s 1;
}

@keyframes logo-invite {
  0%, 100% { transform: scale(1); }
  40% { transform: scale(1.045); }
  75% { transform: scale(0.99); }
}

/* nome reale fuori schermo (SEO/screen-reader): l'intro visibile è tutta WebGL */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---------- Logo (monogramma N bianco) ---------- */

.logo {
  position: relative;
  z-index: 1010;
  display: block;
  padding: 0.5rem;
  transition: transform 0.6s var(--ease-wipe), opacity 0.22s ease;
}

.logo:hover:not(.is-open) { transform: scale(1.06); transition-timing-function: var(--ease-hover); }

.logo__mark {
  display: block;
  height: 3rem;
  width: auto;
}

/* variante home: al centro del palco */

.logo--stage { position: absolute; }

.logo--stage .logo__mark { height: 13rem; } /* taglia finale dell'intro: js/intro3d.js atterra su QUESTO box */

/* prima dell'atterraggio dell'intro: invisibile e non cliccabile */

.logo--stage.is-waiting {
  opacity: 0;
  pointer-events: none;
}

/* quando il canvas WebGL è la visuale (js/intro3d.js, tutta la home): l'SVG
   del link non si vede mai — è solo il bersaglio invisibile per click,
   tastiera e accessibilità. `is-waiting` continua a bloccare i click finché
   l'intro non atterra, ma non deve nascondere il link stesso (nasconderebbe
   anche il canvas, se annidato) → l'opacità resta 1. */
.webgl-logo .logo--stage .logo__mark { opacity: 0; }
.webgl-logo .logo--stage.is-waiting { opacity: 1; }

/* il link resta invisibile ma la regola generica `.logo:hover{scale(1.06)}`
   (pensata per l'SVG visibile delle pagine interne) resterebbe attiva anche
   qui, scalando il link invisibile. Sulla home l'hover non deve trasformare
   nulla: niente zoom sul logo (rimosso su richiesta cliente 2026-07-24). */
.webgl-logo .logo--stage:hover:not(.is-open) { transform: none; }

/* apertura menu: SOLO zoom in dissolvenza (richiesta cliente) — il logo si
   ingrandisce e sfuma, rivela il menu sotto. Copre anche il fallback (niente
   WebGL): lì l'SVG è il logo visibile e riceve questo stesso trattamento
   direttamente; nel percorso WebGL è invisibile comunque, lo zoom+dissolve
   vero lo fa il gemello .intro3d-canvas.is-open (sopra). Non cliccabile
   mentre aperto: la chiusura passa da overlay/Esc (main.js), non più dal
   tap sul logo, che non si sposta più in un angolo. */
.logo--stage.is-open {
  transform: scale(1.2);
  opacity: 0;
  pointer-events: none;
  transition: transform 0.6s var(--ease-wipe), opacity 0.6s var(--ease-wipe);
}

@media (max-width: 768px) {
  .logo__mark { height: 2.2rem; }
  .logo--stage .logo__mark { height: 8.5rem; }
}

/* ---------- Nav (pagine interne: logo in alto al centro) ---------- */

.nav {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
  padding: 1.5rem 1rem;
}

/* ---------- Menu (aperto dal logo) ---------- */

.menu__overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: #000;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.4s ease;
  z-index: 1000;
}

/* touch-action:none quando aperto: uno swipe verticale genera pointermove
   (per il rubberband di overscroll in main.js) invece di uno scroll del browser */
.menu__overlay.is-active { opacity: 1; pointer-events: auto; touch-action: none; }

.menu__list {
  list-style: none;
  position: absolute;
  top: 50%;
  left: 50%;
  /* --menu-pull: scostamento verticale del rubberband di overscroll (main.js).
     Si compone col centraggio; a 0 il menu è centrato come sempre. */
  transform: translate(-50%, calc(-50% + var(--menu-pull, 0px)));
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.4rem;
  width: max-content;
  max-width: 95vw;
}

.menu__list li { line-height: 1; }

/* Voci come bottoni "mirino": pannello semiopaco che stacca dal nero, con
   due angoli a squadra (::before top-left, ::after bottom-right). Al tocco
   gli angoli si chiudono in una cornice piena e il pannello si schiarisce —
   resta nel mood dark, nessun lampo bianco. Gli angoli sono grafica pura
   (pseudo-elementi): l'HTML del menu non cambia. */
.menu__list a {
  position: relative;
  display: block;
  min-width: 15.5em; /* largh. della voce più lunga ("about me"): tutte uguali. In em → scala coi breakpoint */
  font-size: 1.9rem;
  letter-spacing: 0.14em;
  color: #fff;
  background: rgba(255, 255, 255, 0.06);
  padding: 0.72em 1.8em;
  text-align: center;
  transition: background 0.28s ease;
}

.menu__list a::before,
.menu__list a::after {
  content: "";
  position: absolute;
  width: 0.85em;
  height: 0.85em;
  border: 1px solid #9a9a9a;
  pointer-events: none;
  transition: width 0.28s ease, height 0.28s ease, border-color 0.28s ease;
}

.menu__list a::before { top: 0; left: 0; border-right: 0; border-bottom: 0; }
.menu__list a::after { bottom: 0; right: 0; border-left: 0; border-top: 0; }

.menu__list a:hover,
.menu__list a:focus-visible {
  background: rgba(255, 255, 255, 0.13);
  outline: none;
}

.menu__list a:hover::before,
.menu__list a:hover::after,
.menu__list a:focus-visible::before,
.menu__list a:focus-visible::after {
  border-color: #fff;
  width: 100%;
  height: 100%;
}

/* switch lingua nel menu della home (piccolo, staccato in basso) */
.menu__lang {
  position: absolute;
  left: 50%;
  bottom: 2.2rem;
  transform: translateX(-50%);
  font-size: 0.85rem;
  letter-spacing: 0.12em;
  color: var(--grey);
}

@media (max-width: 820px) {
  .menu__list a { font-size: 1.5rem; }
}

@media (max-width: 768px) {
  .menu__list { gap: 1.1rem; }
  .menu__list a { font-size: 1.1rem; padding: 0.72em 1.5em; }
}

/* switch lingua d'angolo (pagine interne): discreto in alto a destra */
.lang-switch {
  position: fixed;
  top: 1.6rem;
  right: 1.25rem;
  z-index: 1020;
  font-size: 0.8rem;
  letter-spacing: 0.12em;
  color: var(--grey);
  padding: 0.3rem 0.4rem;
  transition: color 0.2s ease;
}

.lang-switch:hover { color: #fff; }

/* ---------- Transizioni di pagina ----------
   Sfondo nero sempre fisso: gli elementi (marcati data-animate) escono
   in sequenza alla navigazione e i nuovi entrano al load. */

[data-animate] { opacity: 0; }

/* ---------- Pagine statiche (works / about me) ---------- */

.static {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 1rem;
}

.static h1 {
  font-size: 4rem;
  line-height: 1;
}

.static p { padding: 1.5rem 0; color: var(--grey); font-size: 0.85rem; }

@media (max-width: 768px) {
  .static h1 { font-size: 2rem; }
}

/* ---------- WORKS: stories (tap = next) ----------
   La playlist è scoperta a runtime da js/works.js (assets/works/1.mp4, 2…):
   qui c'è solo il palco. Sul telefono occupa tutto lo schermo; su desktop è
   una colonna 9:16 centrata (come le stories viste da computer). I video sono
   già normalizzati a 9:16 fill (crop centrato, vedi normalize in design-notes):
   object-fit: cover riempie sempre la colonna senza bande, per costruzione. */

.stories {
  position: fixed;
  inset: 0;
  margin: auto;
  height: 100vh;
  height: 100dvh;
  width: min(100vw, calc(100vh * 9 / 16));
  width: min(100vw, calc(100dvh * 9 / 16));
  cursor: crosshair;
  -webkit-tap-highlight-color: transparent;
}

.stories__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  /* metà del cambio-story: fade out al nero totale, poi fade in dal nero —
     la sequenza (mai un crossfade) la orchestra js/works.js, che aspetta
     FADE_MS = 130ms tra i due tempi: i valori devono combaciare */
  transition: opacity 0.13s linear;
  pointer-events: none; /* il tap lo riceve il palco */
}

.stories__video.is-front { opacity: 1; }

/* barre di avanzamento in alto, un segmento per video (stile stories) */
.stories__bars {
  position: absolute;
  top: 0.6rem;
  left: 0.6rem;
  right: 0.6rem;
  display: flex;
  gap: 4px;
  z-index: 2;
}

.stories__bar {
  flex: 1;
  height: 2px;
  background: rgba(255, 255, 255, 0.25);
  overflow: hidden;
}

.stories__bar-fill {
  width: 100%;
  height: 100%;
  background: #fff;
  transform: scaleX(0);
  transform-origin: left;
}

/* spinner di caricamento (stile YouTube): arco bianco che ruota su una
   traccia appena percettibile. In scena da subito (markup), js/works.js lo
   nasconde al primo 'playing' e lo rimostra sugli stalli di rete ('waiting') */
.stories__spinner {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 42px;
  height: 42px;
  margin: -21px 0 0 -21px;
  border: 3px solid rgba(255, 255, 255, 0.18);
  border-top-color: #fff;
  border-radius: 50%;
  animation: spinner-rot 0.8s linear infinite;
  z-index: 3;
  pointer-events: none;
  transition: opacity 0.2s linear;
}

.stories__spinner.is-hidden { opacity: 0; }

@keyframes spinner-rot {
  to { transform: rotate(360deg); }
}

/* CTA "book yours": pill bianca in basso al centro. Nascosta di default
   (non intercetta i tap sul palco); js/works.js aggiunge .is-visible alla
   4ª storia caricata → dissolvenza dal basso, poi resta. */
.stories__cta {
  position: absolute;
  left: 50%;
  bottom: 3.4rem;
  transform: translate(-50%, 16px);
  z-index: 4;
  background: #fff;
  color: #000;
  font-size: 1.05rem;
  letter-spacing: 0.05em;
  padding: 0.9em 2em;
  border-radius: 999px;
  white-space: nowrap;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.55);
  cursor: crosshair;
  opacity: 0;
  pointer-events: none; /* invisibile = non blocca i tap sotto */
  transition: opacity 0.45s linear, transform 0.45s var(--ease-hover);
}

.stories__cta.is-visible {
  opacity: 1;
  transform: translate(-50%, 0);
  pointer-events: auto;
}

.stories__cta:hover { transform: translate(-50%, -2px); }

.stories__empty {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  color: var(--grey);
  font-size: 0.85rem;
}

/* ---------- About: bio (da bio.txt) ----------
   Testo in Microgramma Medium (il default del sito, ereditato) e con il CASE
   ORIGINALE: il body è uppercase globale, qui si annulla. La pagina scorre
   (il body globale è overflow:hidden). */

.page-about { overflow-y: auto; }

.bio {
  max-width: 620px;
  margin: 0 auto;
  padding: 1.5rem 1.25rem 4rem;
}

/* avatar sopra il titolo: quadrato su fondo nero, si fonde col sito */
.bio__avatar {
  display: block;
  width: 220px;
  max-width: 60vw;
  height: auto;
  margin: 0 auto 1.5rem; /* respiro tra avatar e "about me" */
}

.bio h1 {
  font-size: 2rem;
  text-align: center;
  margin-bottom: 2.5rem;
}

.bio article {
  text-transform: none; /* rispetta il case di bio.txt */
  font-size: 0.9rem;
  line-height: 1.85;
  letter-spacing: 0.06em; /* aria tra le lettere: leggibilità sul nero */
  color: #d6d6d6;
}

.bio article p + p { margin-top: 1.15em; }

/* punti chiave in grassetto: il VERO Microgramma Bold (peso intrinseco,
   registrato a font-weight 400 in @font-face) — mai faux-bold sintetico,
   regola del progetto. */
.bio article strong {
  font-family: "Microgramma Bold", sans-serif;
  font-weight: 400;
}

/* CTA in fondo alla bio: pill bianca centrata, porta al form (transizione
   animata via data-transition). Stesso linguaggio della CTA di works. */
.bio__cta {
  display: block;
  width: max-content;
  max-width: 100%;
  margin: 3rem auto 0;
  padding: 0.9em 2em;
  background: #fff;
  color: #000;
  font-size: 1.05rem;
  letter-spacing: 0.05em;
  text-align: center;
  border-radius: 999px;
  cursor: crosshair;
  transition: transform 0.3s var(--ease-hover);
}

.bio__cta:hover { transform: translateY(-2px); }

/* ---------- Funnel form ---------- */

/* La pagina del form scorre come una pagina qualsiasi: niente strati fissi
   centrati sul viewport. È questo che rende impossibile la sovrapposizione —
   nav, sottotitolo e funnel sono una colonna sola, quindi se la finestra si
   accorcia (tastiera, orizzontale, WebView di Instagram) non c'è niente che
   possa finire sopra a qualcos'altro: il browser ci scorre dentro e basta.
   NON rimettere `overflow: hidden` qui, toglierebbe lo scorrimento. */
.page-form { padding-bottom: 4rem; }

/* Blocco elastico del rubberband: contiene TUTTO il contenuto nel flusso
   (nav, sottotitolo, funnel) e si sposta in blocco. --form-pull lo scrive
   js/form.js. Gli elementi position:fixed (barra di avanzamento, switch
   lingua, pillola WhatsApp) stanno FUORI: un antenato con transform sarebbe
   il loro blocco contenente e se li trascinerebbe dietro. */
.form-pull { transform: translateY(var(--form-pull, 0px)); }

.funnel__counter {
  color: var(--grey);
  font-size: 0.75rem;
}

/* sottotitolo incentivante sopra il funnel: sempre visibile in alto, non
   intercetta il form (pointer-events:none), sta sopra all'overlay del funnel */
.form-intro {
  position: relative;
  z-index: 90;
  max-width: 360px;
  margin: 0.9rem auto 0;
  padding: 0 1.25rem;
  text-align: center;
  color: var(--grey);
  font-size: 0.72rem;
  line-height: 1.7;
  letter-spacing: 0.04em;
  pointer-events: none;
}

.funnel__progress {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 2px;
  background: var(--form-bg);
  z-index: 100;
}

.funnel__bar {
  height: 100%;
  width: 0%;
  background: #fff;
  transition: width 0.6s var(--ease-wipe);
}

.funnel {
  /* relative, non fixed: sta nel flusso ma resta il contenente dei passi,
     che restano sovrapposti fra loro per la dissolvenza incrociata */
  position: relative;
  min-height: 17rem; /* i passi sono absolute: senza, il blocco sarebbe alto 0 */
  margin-top: 2rem;
  display: flex;
  justify-content: center;
}

.funnel__step {
  position: absolute;
  width: min(680px, calc(100vw - 2rem));
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  opacity: 0;
  transform: translateY(6vh);
  pointer-events: none;
  transition: opacity 0.6s var(--ease-wipe), transform 0.6s var(--ease-wipe);
}

.funnel__step.is-active {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

.funnel__step.is-leaving {
  opacity: 0;
  transform: translateY(-6vh);
}

.funnel__step > label {
  font-size: 2rem;
  line-height: 1.15;
  padding-bottom: 1.5rem;
}

.funnel__step input[type="text"],
.funnel__step input[type="email"],
.funnel__step textarea {
  background: var(--form-bg);
  border: none;
  color: #fff;
  text-transform: uppercase;
  text-align: center;
  font-family: inherit;
  font-size: 1rem;
  height: 56px;
  /* spazio ai lati per la freccia: simmetrico, se no il testo centrato
     si sposterebbe fuori asse */
  padding: 0 3.25rem;
  width: 100%;
}

.funnel__step textarea {
  height: auto;
  padding: 1rem 3.25rem;
  resize: none;
}

.funnel__step input:focus,
.funnel__step textarea:focus {
  border: 1px solid #fff;
  outline: none;
}

.funnel__step input::placeholder,
.funnel__step textarea::placeholder {
  color: var(--grey);
  text-transform: uppercase;
}

.funnel__hint {
  padding-top: 1rem;
  font-size: 0.6rem;
  color: var(--grey);
  max-width: 52ch;
}

.funnel__step.is-invalid input,
.funnel__step.is-invalid textarea {
  border: 1px solid #fff;
}

/* Marcatore campo non valido ("// required" / "// necessario"): DOM reale, non
   ::after — servono due animazioni indipendenti, il testo che sembra "digitato"
   (clip di larghezza a scatti) e il cursore finale che lampeggia solo a
   battitura conclusa, in tema con i doppi slash già nei testi del form. Il
   testo vive nell'HTML (localizzato per pagina, come il resto del sito); qui
   c'è solo la messinscena. display:none → inline-flex al toggle di is-invalid
   fa ripartire le animazioni da capo ogni volta: il browser le resetta quando
   l'elemento esce e rientra dal render tree.

   ATTENZIONE: mai scrivere la sequenza di chiusura commento dentro questo
   blocco (nemmeno fra virgolette, per citare il glifo del cursore). Chiuderebbe
   il commento in anticipo e il parser inghiottirebbe la regola .req-mark qui
   sotto: il marcatore resterebbe SEMPRE visibile invece che solo a validazione
   fallita. È già successo — vedi design-notes.md.

   Tipografia identica al sottotitolo (.form-intro): stesso grigio, stesso
   corpo, stessa spaziatura. NIENTE opacity qui — smorzare var(--grey) dava un
   grigio diverso da tutti gli altri testi secondari della pagina, e stonava. */
.req-mark {
  display: none;
  color: var(--grey);
  font-size: 0.72rem;
  letter-spacing: 0.04em;
}
.funnel__step.is-invalid .req-mark { display: inline-flex; }

.req-mark__txt {
  display: inline-block;
  overflow: hidden;
  white-space: nowrap;
  max-width: 0;
  animation: reqType 0.6s steps(16, end) forwards;
}

.req-mark__cursor {
  animation: reqBlink 0.9s step-end infinite;
  animation-delay: 0.6s; /* parte a lampeggiare quando la "battitura" finisce */
}

@keyframes reqType { to { max-width: 50ch; } }
@keyframes reqBlink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }

/* ---------- CTA WhatsApp (pillola verde, scelta cliente 2026-07-26) ----------
   Scorciatoia per chi preferisce scrivere invece di compilare il funnel. Verde
   di WhatsApp: rompe DI PROPOSITO il monocromo del sito, è l'unica cosa
   colorata e si riconosce a colpo d'occhio.

   Due elementi, non uno: il WRAPPER porta data-animate, quindi entra "a
   comparsa" con la sequenza di pageEnter (ed esce con pageExit) — quelle sono
   animazioni WAAPI con fill:forwards, che vincono sul CSS; se l'opacità della
   pillola vivesse lì, la regola di nascondimento qui sotto non avrebbe alcun
   effetto. Tenendole separate, wrapper e pillola non si contendono nulla. */
.wa-cta-wrap {
  position: fixed;
  right: 1.5rem;
  bottom: 5rem; /* ben staccata dal fondo (richiesta cliente) */
  z-index: 95;
}

.wa-cta {
  /* esplicito: `a { all: unset }` (in cima al foglio) riporta box-sizing a
     content-box sugli anchor, vincendo sul reset `*`. Senza questa riga, a
     schermo stretto width:100% + padding sfora il wrapper di 32px. */
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.9rem 1.4rem;
  background: #25d366;
  color: #05210f;
  border-radius: 999px;
  font-size: 0.8rem;
  letter-spacing: 0.04em;
  white-space: nowrap;
  box-shadow: 0 6px 24px rgba(37, 211, 102, 0.35);
  transition: transform 0.3s var(--ease-hover), opacity 0.25s linear;
}

.wa-cta:hover { transform: translateY(-2px); }
.wa-cta__icon { width: 18px; height: 18px; flex-shrink: 0; }
.wa-cta__fade { opacity: 0.55; }

/* PERSISTENTE anche a campo attivo (richiesta cliente 2026-07-26): c'era una
   regola che la nascondeva al :focus, tolta. Da sapere: su Android la tastiera
   rimpicciolisce il LAYOUT viewport, quindi la pillola (fixed) si riancora
   sopra la tastiera invece di restare a fondo pagina — è il motivo per cui sta
   parecchio staccata dal fondo, così ha spazio e non si appoggia al campo. */

/* schermi stretti: a tutta larghezza, se no la pillola occuperebbe quasi
   tutta la riga lasciando un margine sbilenco */
@media (max-width: 480px) {
  .wa-cta-wrap { right: 1rem; bottom: 4rem; left: 1rem; }
  .wa-cta { width: 100%; justify-content: center; padding: 0.85rem 1rem; font-size: 0.72rem; }
}

/* consent step */

.funnel__check {
  display: flex;
  justify-content: center;
  line-height: 140%;
  align-items: flex-start;
  margin-bottom: 1rem;
  font-size: 0.85rem;
  text-align: left;
}

.funnel__check input[type="checkbox"] {
  appearance: none;
  border: 0.1rem solid #fff;
  background: #000;
  height: 1rem;
  width: 1rem;
  flex-shrink: 0;
  margin-right: 0.5rem;
  margin-top: 0.15rem;
}

.funnel__check input[type="checkbox"]:checked { background: #fff; }

.funnel__step.is-invalid .funnel__check input[type="checkbox"]:not(:checked) {
  border-color: var(--grey);
  box-shadow: 0 0 0 2px var(--form-bg);
}

/* NESSUNA media query sull'altezza, di proposito. Alla tastiera si risponde
   nel meta viewport di form.html (`interactive-widget=resizes-visual`), non
   qui: la pagina non si ricalcola mai, resta un blocco solo e il browser ci
   pana sopra. Se rimetti regole su `max-height` torni al comportamento che
   faceva "smontare" la pagina su Android — vedi design-notes.md. */

/* campo + freccia: la freccia sta DENTRO la barra, a destra. Su mobile è
   lì che la cerchi, e non allunga la colonna come faceva il bottone sotto. */
.funnel__field {
  position: relative;
  width: 100%;
}

.funnel__next {
  all: unset;
  position: absolute;
  right: 0.5rem;
  top: 50%;
  transform: translateY(-50%);
  cursor: crosshair;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  border: 1px solid var(--grey);
  color: #fff;
  font-size: 1.1rem;
  line-height: 1;
  transition: background 0.25s ease, color 0.25s ease, border-color 0.25s ease;
}

/* nel campo "idea" il testo cresce verso il basso: la freccia si appoggia
   in fondo invece di stare a metà altezza */
.funnel__field--area .funnel__next {
  top: auto;
  bottom: 0.5rem;
  transform: none;
}

.funnel__next:hover,
.funnel__next:focus-visible {
  background: #fff;
  color: #000;
  border-color: #fff;
}

.funnel__submit {
  all: unset;
  cursor: crosshair;
  color: #000;
  background: #fff;
  padding: 1.5rem;
  text-transform: uppercase;
  text-align: center;
  margin-top: 2rem;
  width: 100%;
}

.funnel__submit:disabled { opacity: 0.5; }

@media (max-width: 768px) {
  .funnel__step > label { font-size: 1.15rem; }
}

/* ---------- Thank you (centrata) ---------- */

.thanks {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 1rem;
}

.thanks h1 {
  font-size: 5.5rem;
  line-height: 0.95;
}

.thanks p { padding: 1.5rem 0; color: var(--grey); font-size: 0.85rem; }

@media (max-width: 768px) {
  .thanks h1 { font-size: 2.2rem; }
}
