/* =========================================================
   BLACKLEMON — style.css
   Design system: bold black + lemon yellow (da presentazione Canva)
   ========================================================= */

/* ---------- TOKENS ---------- */
:root {
  --ink: #0e0e0e;
  --ink-soft: #161616;
  --ink-card: #1c1c1c;
  --lemon: #ffcd00;
  --lemon-deep: #f6c500;
  --paper: #ffffff;
  --paper-dim: #f4f4f2;
  --ash: #9a9a9a;
  --ash-dark: #6a6a6a;
  --line: rgba(255, 255, 255, 0.14);
  --line-dark: rgba(0, 0, 0, 0.12);

  --font-display: "Anton", "Archivo", system-ui, sans-serif;
  --font-head: "Inter", system-ui, sans-serif;
  --font-body: "Inter", system-ui, -apple-system, sans-serif;

  --maxw: 1280px;
  --pad: clamp(1.25rem, 5vw, 4rem);
  --radius: 18px;

  /* Respiro verticale delle sezioni. Sopra e sotto sono token distinti
     perché il fondo sezione ha bisogno di più aria del bordo superiore:
     il contenuto che si chiude a ridosso del limite inferiore (o del
     bordo della scheda, nello stack) legge come "tagliato". Usati sia
     dalla regola generica "section" (mobile e pagine non-stack) sia da
     .stack-inner.has-overflow nello stack desktop, così il valore si
     regola in un punto solo. */
  --section-pad-top: clamp(4rem, 9vw, 8rem);
  --section-pad-bottom: clamp(7rem, 15vw, 14rem);

  /* Padding verticale del footer. Token perché va dichiarato in due
     punti: su .site-footer (mobile e pagine senza stack) e sul suo
     .stack-inner nello stack desktop, dove .stack-section azzera il
     padding del footer stesso. Un solo valore da regolare. */
  --footer-pad-top: clamp(2rem, 3vw, 2.75rem);
  --footer-pad-bottom: clamp(4rem, 7vw, 5.5rem);

  /* Altezza dell'header fisso. Non è un valore arbitrario: è quanto
     misura davvero .site-header (logo + padding-block di .header-inner)
     — verificato in Chrome, 79px a desktop. Serve al footer nello stack,
     che si aggancia esattamente sotto l'header con
     min-height: calc(100vh - var(--header-h)) (vedi il blocco
     STACK SCROLL in fondo al file). Se cambi il padding dell'header o la
     dimensione del logo, aggiorna questo valore: è l'unico punto da
     toccare. */
  --header-h: 79px;

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* ---------- RESET ---------- */
*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
  /* `clip` invece di `hidden`. ATTENZIONE alla motivazione: NON è questa
     la causa del bug dello stack su Chrome (quella è il numero di layer
     compositi — vedi la nota nel blocco STACK SCROLL in fondo al file).
     Verificato direttamente su Chrome 152 con un test isolato: `hidden`,
     `clip` e `visible` danno esattamente le stesse posizioni di aggancio
     per le sticky. La teoria "hidden su html rompe lo sticky" è stata
     misurata e smentita, non va reintrodotta.

     `clip` resta comunque preferibile: quando l'overflow di <html> è
     diverso da `visible` viene propagato al viewport, e con `hidden` il
     viewport diventa un contenitore di scorrimento sull'asse X (una
     barra di scorrimento orizzontale latente, e la pagina resta
     scrollabile lateralmente via tastiera o trackpad anche quando non
     dovrebbe). `clip` taglia l'eccedenza senza creare nulla di
     scorrevole. È il sostituto moderno di `hidden` per il solo
     contenimento.

     Sull'asse Y: `overflow-y` non è dichiarato, quindi resta `visible`
     nel foglio di stile — ma poiché i due assi non possono essere
     `visible` e `clip` insieme, il valore USATO diventa `auto`. È
     esattamente ciò che serve (la pagina deve scorrere in verticale) ed è
     anche il comportamento che si aveva prima con `hidden`, quindi non
     cambia nulla rispetto a com'era. Da non "correggere" mettendo
     `overflow-y: visible` esplicito: non otterrebbe l'effetto e
     rischierebbe di reintrodurre il problema. */
  overflow-x: clip;
}
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

body {
  font-family: var(--font-body);
  background: var(--ink);
  color: var(--paper);
  line-height: 1.6;
  font-size: 1rem;
  /* NIENTE overflow-x qui: il contenimento orizzontale sta su <html>
     come `clip` (vedi la nota lì, che spiega perché `hidden` — su body
     o su html, indifferentemente — rompeva lo sticky su Chrome). */
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img,
video,
svg {
  display: block;
  max-width: 100%;
}
a {
  color: inherit;
  text-decoration: none;
}
ul {
  list-style: none;
}
em {
  font-style: normal;
  color: var(--lemon);
}

.container {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--pad);
}

/* Bootstrap compensa il gutter di .row con margini NEGATIVI laterali
   (g-5 = -1.5rem = -24px per lato) dando per scontato che il contenitore
   abbia almeno altrettanto padding. Il nostro --pad e' fluido
   — clamp(1.25rem, 5vw, 4rem) — e sotto i 480px 5vw scende a ~18px:
   meno dei 24px del gutter, cosi' la riga sporgeva di ~5px per lato e
   l'intera pagina acquisiva scroll orizzontale. Qui il margine negativo
   viene limitato al padding realmente disponibile. */
.container > .row {
  --bs-gutter-x: min(3rem, calc(var(--pad) * 2));
}

/* ---------- ACCESSIBILITY ---------- */
.skip-link {
  position: absolute;
  left: -999px;
  top: 0;
  z-index: 200;
  background: var(--lemon);
  color: var(--ink);
  padding: 0.8rem 1.2rem;
  font-weight: 700;
  border-radius: 0 0 8px 0;
}
.skip-link:focus {
  left: 0;
}

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

/* Testo leggibile dagli screen reader ma non a schermo (usato dove
   il testo visibile è spezzato in elementi non leggibili, es. i
   titoli lettera-per-lettera di .cc-card). */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.section-yellow :focus-visible,
.section-light :focus-visible {
  outline-color: var(--ink);
}

/* ---------- BUTTONS ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.8rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 0.95rem 1.7rem;
  border-radius: 100px;
  cursor: pointer;
  border: 2px solid transparent;
  transition:
    transform 0.3s var(--ease),
    background 0.3s var(--ease),
    color 0.3s var(--ease);
  /* will-change solo al passaggio del mouse, non sempre: dichiarato in
     modo permanente ogni bottone della pagina diventa un layer composito
     a sé: su portfolio (2 frecce per case study + le CTA) sono decine, e
     insieme alle schede dello stack esaurivano il budget di layer di
     Chrome. Il browser ha comunque il tempo di prepararlo prima che la
     transizione parta. */
}
.btn:hover {
  transform: translateY(-3px);
}
.btn:hover,
.btn:focus-visible {
  will-change: transform;
}
.btn-primary {
  background: var(--lemon);
  color: var(--ink);
}
.btn-primary:hover,
.btn-primary:focus {
  background: var(--paper);
  color: var(--ink);
  border-color: var(--ink);
}
.btn-ghost {
  border-color: rgba(255, 255, 255, 0.4);
  color: var(--paper);
}
.btn-ghost:hover,
.btn-ghost:focus {
  border-color: var(--lemon);
  color: var(--lemon);
}
.btn-dark {
  background: var(--ink);
  color: var(--paper);
}
.btn-dark:hover,
.btn-dark:focus {
  background: var(--paper);
  color: var(--ink);
}
.btn-pill {
  background: var(--lemon);
  color: var(--ink);
  padding: 0.7rem 1.3rem;
}
.btn-pill:hover,
.btn-pill:focus {
  background: var(--paper);
  color: var(--ink);
}
.btn-lg {
  font-size: 1.05rem;
  padding: 1.15rem 2.4rem;
}

/* ---------- SECTION HELPERS ---------- */
section {
  padding-top: var(--section-pad-top);
  padding-bottom: var(--section-pad-bottom);
  position: relative;
}
.section-dark {
  background: var(--ink);
  color: var(--paper);
}
.section-light {
  background: var(--paper-dim);
  color: var(--ink);
}
.section-yellow {
  background: var(--lemon);
  color: var(--ink);
}

.section-kicker {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.85rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin-bottom: 1.2rem;
  opacity: 0.9;
}
/* Coerenza: se il kicker sta dentro .prose non deve ereditare font/spaziatura del testo lungo */
.prose p.section-kicker {
  font-size: 0.85rem;
  line-height: 1.4;
  opacity: 0.9;
  margin-top: 0;
  margin-bottom: 1.2rem;
}
/* Icona limone del kicker: file SVG diversi in base allo sfondo.
   Default (sezioni scure) = limone chiaro; su sfondo giallo/chiaro = limone scuro. */
.kicker-mark {
  display: inline-block;
  flex-shrink: 0;
  width: 1.15em;
  height: 1.15em;
  background: url("/assets/images/lemon-pic-light.svg") center / contain
    no-repeat;
}
.section-yellow .kicker-mark,
.section-light .kicker-mark {
  background-image: url("/assets/images/lemon-pic-dark.svg");
}
.section-title {
  font-family: var(--font-display);
  font-weight: 400;
  line-height:  1;
  text-transform: uppercase;
  font-size: clamp(2.4rem, 7vw, 5rem);
  letter-spacing: -0.01em;
  margin-bottom: 1.5rem;    
  margin-bottom: 2.5rem;
}
.section-title .accent {
  color: var(--lemon);
}
.section-light .section-title .accent {
  color: var(--lemon-deep);
}
.section-yellow .section-title .accent {
  color: var(--ink);
}
.section-title s {
  color: var(--paper);
  text-decoration-color: var(--lemon);
  text-decoration-thickness: 4px;
}

/* ---------- PHILOSOPHY TITLE: sequenza "siamo fornitori" -> barrato -> "siamo partner strategici" ---------- */
.philosophy-strike {
  position: relative;
  text-decoration: none;
}
.philosophy-strike::after {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  width: 0;
  height: 4px;
  background: var(--lemon);
  transform: translateY(-50%);
  transition: width 0.6s var(--ease);
}
.philosophy-title-swap.is-struck .philosophy-strike::after {
  width: 100%;
}
.philosophy-partner {
  display: inline-block;
  opacity: 0;
  transform: translateY(12px);
  transition:
    opacity 0.6s var(--ease),
    transform 0.6s var(--ease);
}
.philosophy-title-swap.is-swapped .philosophy-partner {
  opacity: 1;
  transform: none;
}
@media (prefers-reduced-motion: reduce) {
  .philosophy-strike::after,
  .philosophy-partner {
    transition: none;
  }
}

/* =========================================================
   HEADER
   ========================================================= */
.site-header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 100;
  transition:
    background 0.35s var(--ease),
    backdrop-filter 0.35s var(--ease),
    border-color 0.35s var(--ease);
  border-bottom: 1px solid transparent;
}
.site-header.scrolled {
  background: rgba(14, 14, 14, 0.82);
  backdrop-filter: blur(14px);
  border-bottom-color: var(--line);
}
.header-inner {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  padding-block: 1rem;
}

.logo {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  color: var(--paper);
}
.logo-img {
  height: 34px;
  width: auto;
}
.logo-mark {
  color: var(--lemon);
  display: inline-flex;
}
.logo-text {
  font-family: var(--font-head);
  font-weight: 900;
  letter-spacing: 0.02em;
  font-size: 1.25rem;
  text-transform: uppercase;
}
.logo-text-accent {
  color: var(--lemon);
}

.main-nav {
  margin-left: auto;
}
.nav-list {
  display: flex;
  align-items: center;
  gap: 2rem;
}
.nav-list > li {
  display: flex;
  align-items: center;
}
/* NB: lo stile dei link nav (colore, underline animato) è gestito dalle regole
   .navbar .nav-link nel blocco "BOOTSTRAP — integrazione". Qui niente ::after
   per non entrare in conflitto con la freccia del dropdown Servizi. */

/* Vecchio menu custom (submenu/mobile-nav/nav-toggle) rimosso:
   ora la navbar usa Bootstrap. Vedi blocco 'BOOTSTRAP — integrazione'. */

/* =========================================================
   HERO
   ========================================================= */
.hero {
  min-height: 100svh;
  display: flex;
  align-items: center;
  padding-top: 6rem;
  padding-bottom: 4rem;
  overflow: hidden;
  /* NIENTE `isolation: isolate` — stessa ragione per cui e' stato tolto
     da .stack-section (vedi la nota estesa la' sotto): su Chrome crea un
     contesto di impilamento che, dentro una sticky con overflow:hidden,
     viene ricomposto male. Qui il sintomo era il titolo che perdeva la
     parola "UN" tornando in cima dopo uno scroll, finche' un click non
     forzava il repaint.
     Serviva a confinare i figli con z-index negativo (.hero-video a -2,
     .hero-overlay a -1) perche' non finissero dietro il fondo pagina: a
     questo basta gia' `position: relative` — che .stack-section fornisce
     sullo stack e che qui viene ripristinato esplicitamente per la vista
     senza stacking. */
  position: relative;
  background: var(--ink); /* fondo neutro finché il video non è pronto */
}
.hero-video {
  position: absolute;
  inset: 0;
  z-index: -2;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* NIENTE `filter` qui (era grayscale(0.2) contrast(1.05)).
     Il filtro promuove il video a layer di composizione proprio; dentro
     una .stack-section sticky con overflow:hidden, tornando in cima dopo
     uno scroll Chrome ricomponeva quel layer senza ridipingere il testo
     sovrapposto, e la parola piu' corta del titolo ("UN") spariva finche'
     un click non forzava il repaint.
     La desaturazione la fa ora .hero-overlay con un velo neutro in piu':
     stesso risultato a schermo, nessun layer aggiuntivo. */
  opacity: 0;
  transition: opacity 0.6s var(--ease); /* fade-in quando c'è un frame reale */
}
.hero-video.is-ready {
  opacity: 1;
}
.hero-overlay {
  position: absolute;
  inset: 0;
  z-index: -1;
  /* Scrim semitrasparente: scurisce quel tanto che basta a leggere le scritte,
     ma lascia intravedere il video dietro. */
  background:
    radial-gradient(
      120% 90% at 70% 10%,
      rgba(255, 205, 0, 0.12),
      transparent 55%
    ),
    linear-gradient(
      180deg,
      rgba(10, 10, 10, 0.35),
      rgba(10, 10, 10, 0.62) 70%,
      rgba(10, 10, 10, 0.78)
    ),
    /* Velo grigio uniforme: sostituisce il grayscale(0.2) che stava sul
       video, smorzando la saturazione senza usare un filtro. */
    linear-gradient(
      rgba(128, 128, 128, 0.14),
      rgba(128, 128, 128, 0.14)
    );
}
.hero-content {
  max-width: 60rem;
}
.hero-eyebrow {
  font-family: var(--font-head);
  font-weight: 700;
  color: var(--lemon);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-size: 0.85rem;
  margin-bottom: 1.4rem;
}
.hero-title {
  font-family: var(--font-display);
  font-weight: 400;
  text-transform: uppercase;
  line-height: 1;
  font-size: clamp(3.2rem, 10vw, 8rem);
  letter-spacing: -0.015em;
  margin-bottom: 1.2rem;
}
.hero-title em {
  color: var(--lemon);
}
.hero-sub {
  font-family: var(--font-head);
  font-weight: 800;
  font-size: clamp(1.3rem, 3.5vw, 2.4rem);
  color: var(--paper);
  margin-bottom: 2rem;
}
.hero-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-bottom: 2.4rem;
}
.hero-tags li {
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  padding: 0.4rem 0.9rem;
  border: 1px solid var(--line);
  border-radius: 100px;
  color: rgba(255, 255, 255, 0.82);
  backdrop-filter: blur(4px);
  background: rgba(255, 255, 255, 0.04);
}
.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}

.hero-scroll {
  position: absolute;
  left: 50%;
  bottom: 1.6rem;
  transform: translateX(-50%);
  width: 26px;
  height: 42px;
  border: 2px solid rgba(255, 255, 255, 0.5);
  border-radius: 100px;
  display: grid;
  place-items: start center;
  padding-top: 7px;
}
.hero-scroll-dot {
  width: 4px;
  height: 8px;
  border-radius: 4px;
  background: var(--lemon);
  animation: scrollDot 1.6s var(--ease) infinite;
}
@keyframes scrollDot {
  0% {
    opacity: 0;
    transform: translateY(-4px);
  }
  40% {
    opacity: 1;
  }
  80%,
  100% {
    opacity: 0;
    transform: translateY(12px);
  }
}

/* ---------- PROVA "cinematic": reveal di scritte + gradiente a fine video ----------
   Confinata alla classe .hero--cinematic. Rimuovere quella classe in HTML per annullare. */
.hero--cinematic .hero-content,
.hero--cinematic .hero-overlay,
.hero--cinematic .hero-scroll {
  opacity: 0;
  transition:
    opacity 1.1s var(--ease),
    transform 1.1s var(--ease);
}
.hero--cinematic .hero-content {
  transform: translateY(24px);
}
.hero--cinematic.hero--revealed .hero-content,
.hero--cinematic.hero--revealed .hero-overlay,
.hero--cinematic.hero--revealed .hero-scroll {
  opacity: 1;
}
.hero--cinematic.hero--revealed .hero-content {
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  /* Niente attesa del video: mostra tutto subito, senza transizioni */
  .hero--cinematic .hero-content,
  .hero--cinematic .hero-overlay,
  .hero--cinematic .hero-scroll {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* =========================================================
   CLAIM (yellow)
   ========================================================= */
.claim-title {
  font-family: var(--font-display);
  font-weight: 400;
  text-transform: uppercase;
  line-height: 1;
  font-size: clamp(2.4rem, 7vw, 5rem);
  letter-spacing: -0.01em;
  color: var(--ink);
  margin-bottom: 0.6rem;
}
.claim-sub {
  font-family: var(--font-head);
  font-weight: 800;
  font-size: 2rem;
  color: var(--paper);
  /* lo stacco dalla griglia è gestito da .claim-grid margin-top */
  margin-bottom: 0;
  letter-spacing: -0.8px;
}
.claim-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.5rem;
  margin-top: 2.5rem;
}
/* Mobile (1 colonna): separazione orizzontale in alto a ogni blocco */
.claim-card {
  border-top: 2px solid var(--ink);
  padding-top: 1.2rem;
}
.claim-card h3 {
  font-family: var(--font-head);
  font-weight: 800;
  font-size: 1.6rem;
  margin-bottom: 0.4rem;
  letter-spacing: -0.8px;
}
.claim-card p {
  max-width: 30ch;
  font-weight: 500;
}
.claim-card .accent {
  color: var(--lemon);
}

/* Variante su fondo scuro. .claim-* nasce per la sezione gialla (testo
   nero, filetti neri): su .section-dark quei valori sparirebbero contro
   il fondo. Qui si invertono solo i colori — dimensioni, pesi e
   spaziature restano quelle della variante gialla, così le due sezioni
   si leggono come lo stesso componente. */
.section-dark .claim-title {
  color: var(--paper);
}
.section-dark .claim-title .accent {
  color: var(--lemon);
}
.section-dark .claim-sub {
  color: var(--lemon);
}
/* Su fondo chiaro il bianco della variante base e' illeggibile (1.5:1 sul
   giallo). Serve il nero, come .claim-title che lo ha gia' di default.
   Senza questa regola la home usava uno style="color:black" inline. */
.section-yellow .claim-sub,
.section-light .claim-sub {
  color: var(--ink);
}
/* mobile (1 colonna): il filetto in alto a ogni blocco */
.section-dark .claim-card {
  border-top-color: var(--paper);
}

/* =========================================================
   FEATURE — badge + titolo + griglia di voci con check
   (porting in HTML/CSS vanilla di un componente React/Tailwind:
   Badge, titolo, lead e griglia 1 → 2 → 3 colonne con icona check.
   Le utility Tailwind sono tradotte in classi semantiche qui, e i
   valori arbitrari (text-3xl/md:text-5xl, gap-10, pt-12) in scale
   fluide coerenti col resto del sito invece che in px fissi.)
   ========================================================= */
.feature-badge {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.78rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 0.4rem 0.85rem;
  border-radius: 999px;
  background: var(--ink);
  color: var(--lemon);
  margin-bottom: 2rem;
}
/* Su fondo scuro il badge si inverte (giallo pieno, testo nero) così
   resta leggibile ovunque la sezione venga riusata. Su fondo giallo
   resta nero con testo giallo, come sul chiaro. */
.section-dark .feature-badge {
  background: var(--lemon);
  color: var(--ink);
}

.feature-head {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}
.feature-title {
  font-family: var(--font-head);
  font-weight: 800;
  /* equivalente fluido di text-3xl → md:text-5xl */
  font-size: clamp(1.9rem, 4.2vw, 3rem);
  line-height: 1.08;
  /* tracking-tighter */
  letter-spacing: -0.03em;
  max-width: 20ch;
}
.feature-lead {
  font-size: 1.125rem;
  line-height: 1.7;
  letter-spacing: -0.01em;
  /* text-muted-foreground: stesso ruolo, tinta del sistema */
  color: var(--ash-dark);
  max-width: 62ch;
}
.section-dark .feature-lead {
  color: var(--ash);
}

.feature-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2.2rem;
  margin-top: 3rem;
  list-style: none;
  /* padding:0 — è un <ul> e il padding-left di default del browser
     (40px, qui 32px) rientrava i blocchetti rispetto al titolo e al
     lead di sezione, che partono dal bordo del .container.
     list-style:none toglie i marker ma non l'indentazione. */
  padding: 0;
  margin-inline: 0;
}
.feature-item {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 1.1rem;
}
/* Voci numerate: il numero va SOPRA il titolo, non di fianco.
   :has() applica il cambio solo alle voci che contengono davvero un
   numero, così le voci con il check (altre sezioni) restano in riga. */
.feature-item:has(.feature-num) {
  flex-direction: column;
  align-items: stretch;
  gap: 0.85rem;
}
.feature-check {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  /* allinea l'icona alla prima riga di testo del titolo */
  margin-top: 0.15rem;
  border-radius: 50%;
  background: var(--lemon);
  color: var(--ink);
}
.feature-check svg {
  width: 0.95rem;
  height: 0.95rem;
  display: block;
}

/* Numero progressivo al posto del check: usato dove le voci sono le
   FASI di un processo, quindi l'ordine conta ed è un'informazione in
   sé (il check dice "incluso", il numero dice "prima questo, poi
   quello"). Pastiglia scura piena: sul fondo giallo un cerchio
   --lemon sarebbe invisibile. */
.feature-num {
  /* align-self:flex-start (non flex:0 0 auto): in colonna la pastiglia
     non deve stirarsi per tutta la larghezza della cella, ma restare
     larga quanto il numero. */
  align-self: flex-start;
  display: grid;
  place-items: center;
  min-width: 2.75rem;
  height: 2.75rem;
  padding-inline: 0.55rem;
  border-radius: 999px;
  background: var(--ink);
  color: var(--lemon);
  font-family: var(--font-head);
  font-weight: 800;
  font-size: 1.15rem;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
}
/* su fondo scuro si inverte, così la sezione resta riusabile */
.section-dark .feature-num {
  background: var(--lemon);
  color: var(--ink);
}

/* Variante su fondo giallo: i testi secondari usano --ash-dark, pensato
   per il chiaro neutro — sul giallo perde contrasto e "sbiadisce".
   Nero pieno per i titoli, nero attenuato per i descrittivi. */
.section-yellow .feature-lead,
.section-yellow .feature-item-desc {
  color: rgba(0, 0, 0, 0.72);
}
.section-yellow .feature-title,
.section-yellow .feature-item-title {
  color: var(--ink);
}
.feature-item-text {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.feature-item-title {
  font-family: var(--font-head);
  font-weight: 800;
  font-size: 1.6rem;
  line-height: 1.2;
  letter-spacing: -0.8px;
  margin-bottom: 0.4rem;
}
.feature-item-desc {
  font-size: 0.925rem;
  line-height: 1.6;
  color: var(--ash-dark);
}
.section-dark .feature-item-desc {
  color: var(--ash);
}

/* grid-cols-2 dal breakpoint intermedio, lg:grid-cols-3 da 992px */
@media (min-width: 576px) {
  .feature-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 2.4rem 2rem;
  }
}
@media (min-width: 992px) {
  .feature-grid {
    grid-template-columns: repeat(3, 1fr);
    gap: 3rem 2.5rem;
  }
}

/* =========================================================
   CC-CARD — card immagine con hover "color change" + roll del titolo
   (porting vanilla di un componente React/framer-motion: le varianti
   `hover` con staggerChildren diventano transizioni CSS su :hover /
   :focus-visible del contenitore, con il ritardo per lettera passato
   come --i da initColorChangeCards() in script.js. Nessuna libreria.)
   ========================================================= */
.cc-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
  margin-top: 3rem;
  list-style: none;
  /* padding:0 — è un <ul>: il padding-left di default del browser
     rientrava la griglia rispetto a kicker, titolo e lead di sezione,
     che partono dal bordo del .container. list-style:none toglie i
     marker ma non l'indentazione. */
  padding: 0;
  margin-inline: 0;
}
@media (min-width: 768px) {
  .cc-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 1.5rem;
  }
}
@media (min-width: 992px) {
  .cc-grid {
    grid-template-columns: repeat(3, 1fr);
    gap: 1.5rem;
  }
}

.cc-card {
  position: relative;
  display: block;
  height: 16rem; /* h-64 */
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--ink-card);
  text-decoration: none;
  isolation: isolate;
}

/* immagine di sfondo: <img> reale invece di background-image, così
   resta lazy-loaded e con dimensioni note (niente layout shift). */
.cc-media {
  position: absolute;
  inset: 0;
  z-index: 0;
}
.cc-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  /* solo transform: la transizione su filter serviva al passaggio
     desaturato → colori, ora rimosso (immagini sempre a colori). */
  transition: transform 0.5s var(--ease);
}
/* Immagini sempre a colori (nel componente originale erano desaturate
   e tornavano a colori sull'hover): resta lo zoom come feedback
   dell'hover, senza il passaggio di saturazione. */
.cc-card:hover .cc-media img,
.cc-card:focus-visible .cc-media img {
  transform: scale(1.1);
}

/* velo scuro: il testo bianco deve restare leggibile su qualsiasi foto */
/* Velo scuro: il testo bianco deve restare leggibile su QUALSIASI foto.
   I valori non sono estetici ma calcolati: sulle immagini più chiare le
   zone luminose arrivano a ~RGB 188, che contro il bianco darebbe 1.9:1,
   sotto il minimo WCAG AA (4.5:1). Con 0.92 di nero sulla fascia del
   testo quelle stesse zone scendono sotto RGB 60 → oltre 7:1. */
.cc-media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to top,
    rgba(0, 0, 0, 0.92) 0%,
    rgba(0, 0, 0, 0.88) 38%,
    rgba(0, 0, 0, 0.42) 66%,
    rgba(0, 0, 0, 0.12) 100%
  );
  transition: background 0.5s var(--ease);
}

.cc-body {
  position: relative;
  z-index: 2;
  display: flex;
  height: 100%;
  flex-direction: column;
  justify-content: space-between;
  padding: 1.25rem;
  color: rgba(255, 255, 255, 0.82);
  transition: color 0.5s var(--ease);
}
.cc-card:hover .cc-body,
.cc-card:focus-visible .cc-body {
  color: #fff;
}

/* freccia in alto a destra, ruota di -45° sull'hover */
.cc-arrow {
  margin-left: auto;
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.12);
  transition:
    transform 0.5s var(--ease),
    background 0.5s var(--ease),
    color 0.5s var(--ease);
}
.cc-arrow svg {
  width: 1.15rem;
  height: 1.15rem;
  display: block;
}
.cc-card:hover .cc-arrow,
.cc-card:focus-visible .cc-arrow {
  transform: rotate(-45deg);
  background: var(--lemon);
  color: var(--ink);
}

.cc-text {
  display: block;
}
.cc-desc {
  display: block;
  margin-top: 0.35rem;
  font-size: 0.9rem;
  line-height: 1.55;
  max-width: 46ch;
}

/* --- Titolo: roll per lettera ---------------------------------
   Ogni lettera è una finestra alta 1em con overflow:hidden che
   contiene la stessa lettera due volte impilate; sull'hover la
   pila trasla di -50%, così la copia inferiore prende il posto
   della superiore. Il ritardo progressivo (--i, scritto da JS)
   riproduce staggerChildren: 0.035. */
.cc-heading {
  display: block;
  font-family: var(--font-head);
  font-weight: 800;
  font-size: clamp(1.35rem, 2.4vw, 1.75rem);
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: #fff;
}
/* height 1.25em (non 1em): a 1em la finestra taglia i discendenti —
   la "g" di "Strategica" veniva mozzata. Il padding-bottom dà aria
   sotto la linea di base senza spostare l'allineamento del testo. */
.cc-letter {
  display: inline-block;
  height: 1.25em;
  overflow: hidden;
  vertical-align: bottom;
}
.cc-letter > span {
  display: flex;
  flex-direction: column;
  min-width: 4px;
  transform: translateY(0);
  transition: transform 0.5s var(--ease);
  transition-delay: calc(var(--i, 0) * 35ms);
}
.cc-card:hover .cc-letter > span,
.cc-card:focus-visible .cc-letter > span {
  transform: translateY(-50%);
}
/* Le lettere di una stessa parola stanno in un .cc-word che non si
   spezza: senza, ogni .cc-letter essendo inline-block diventa un punto
   di a-capo valido e le parole si rompevano a metà sulle card strette. */
.cc-word {
  display: inline-block;
  white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
  .cc-media img,
  .cc-body,
  .cc-arrow,
  .cc-letter > span {
    transition: none;
  }
  .cc-card:hover .cc-media img,
  .cc-card:focus-visible .cc-media img {
    transform: none;
  }
  .cc-card:hover .cc-arrow,
  .cc-card:focus-visible .cc-arrow {
    transform: none;
  }
  .cc-card:hover .cc-letter > span,
  .cc-card:focus-visible .cc-letter > span {
    transform: none;
  }
}

/* =========================================================
   AGENZIA / STATS
   ========================================================= */
.agency-lead {
  max-width: 62ch;
  color: rgba(255, 255, 255, 0.78);
  /* font-size: 1.1rem; */
  margin-bottom: 1.2rem;
}
.agency-lead a {
  color: var(--lemon);
  font-weight: 800;
  text-decoration: none;
}
.agency-lead a:hover {
  color: var(--paper);
}
.stats-grid {
  margin-top: 3.5rem;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
}
.stat {
  background: var(--ink);
  padding: 2rem 1.6rem;
}
.stat-num {
  display: block;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(3rem, 9vw, 5.5rem);
  line-height: 1;
  color: var(--lemon);
  letter-spacing: -0.02em;
}
.stat-label {
  display: block;
  margin-top: 0.6rem;
  color: rgba(255, 255, 255, 0.7);
  font-size: 0.98rem;
}
.stats-grid--3 {
  grid-template-columns: 1fr;
}
@media (min-width: 640px) {
  .stats-grid--3 {
    grid-template-columns: repeat(3, 1fr);
  }
}
.stat-label strong {
  color: var(--paper);
  font-weight: 700;
}

/* =========================================================
   SERVIZI
   ========================================================= */
.services-grid {
  margin-top: 3rem;
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.2rem;
}
/* ---------- SERVICE CARD (servizi.html) ----------
   Card numerate senza immagine, usate solo in servizi.html. La home ora
   usa .cc-card (vedi blocco successivo), ma questa variante resta viva:
   riquadro chiaro, numero in alto e wipe giallo sull'hover. */
.service-card {
  background: var(--paper);
  border: 1px solid var(--line-dark);
  border-radius: var(--radius);
  padding: 2rem;
  position: relative;
  overflow: hidden;
  transition:
    transform 0.35s var(--ease),
    box-shadow 0.35s var(--ease),
    background 0.35s var(--ease);
}
.service-card::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background: var(--lemon);
  transform: translateY(101%);
  transition: transform 0.4s var(--ease);
}
.service-card > * {
  position: relative;
  z-index: 1;
}
.service-card:hover {
  transform: translateY(-6px);
  box-shadow: 0 24px 50px -24px rgba(0, 0, 0, 0.4);
}
.service-card:hover::before {
  transform: translateY(0);
}
.service-card h3 {
  font-family: var(--font-head);
  font-weight: 800;
  font-size: 1.6rem;
  margin: 0.4rem 0 0.8rem;
  letter-spacing: -1.3px;
}
.service-card p {
  color: #3a3a3a;
}
.service-card:hover p,
.service-card:hover h3 {
  color: var(--ink);
}
@media (prefers-reduced-motion: reduce) {
  .service-card,
  .service-card::before {
    transition: none;
  }
  .service-card:hover {
    transform: none;
  }
}

/* ---------- SERVICE CARD (home) — variante di .cc-card ----------
   La sezione "Un ecosistema di competenze a 360°" usa lo stesso
   componente della sezione "cosa comprende il lavoro di Blacklemon"
   (.cc-grid/.cc-card, vedi più sopra): zoom dell'immagine, freccia che
   ruota, titolo con roll lettera per lettera.
   L'unica differenza è la proporzione: .cc-card ha height:16rem fissa
   (card orizzontali), mentre in home le card erano verticali 4:5 —
   .services-cc ripristina quel formato senza toccare il componente
   base, così le due sezioni restano indipendenti. */
.services-cc .cc-card {
  /* height:auto annulla i 16rem di .cc-card: senza, l'aspect-ratio non
     avrebbe effetto perché un'altezza esplicita ha la precedenza. */
  height: auto;
  aspect-ratio: 4 / 5;
}
/* Le illustrazioni di card-services sono 1023x1537 (2:3), più verticali
   della card 4:5: object-fit:cover ne taglia circa il 17% in altezza.
   Con l'ancoraggio centrato di default il taglio si divide fra sopra e
   sotto e mangia la parte alta del cerchio giallo, che è il fulcro di
   ogni illustrazione. Ancorando in alto il soggetto resta intero e il
   ritaglio cade in fondo, dove c'è lo sfondo e dove passa comunque il
   velo scuro con la didascalia. */
.services-cc .cc-media img {
  object-position: top center;
}
/* Su schermi molto stretti la card 4:5 diventa altissima (a 375px
   sarebbero ~420px di sola immagine): sotto i 768px il formato torna
   più compatto, come nel resto del sito. */
@media (max-width: 767.98px) {
  .services-cc .cc-card {
    aspect-ratio: 4 / 3;
  }
}


/* =========================================================
   PHILOSOPHY
   ========================================================= */
.pillars {
  display: flex;
  flex-wrap: wrap;
  gap: 0.8rem;
  margin: 1.5rem 0 3rem;
}
.pillars li {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.95rem;
  padding: 0.6rem 1.2rem;
  border: 1px solid var(--lemon);
  color: var(--lemon);
  border-radius: 100px;
}
.philosophy-text {
  columns: 2;
  column-gap: 3rem;
  /* max-width: 70rem; */
}
.philosophy-text p {
  break-inside: avoid;
  margin-bottom: 1.3rem;
  color: rgba(255, 255, 255, 0.74);
}
.philosophy-text a {
  color: var(--lemon);
  font-weight: bold;
}
.philosophy-text a:hover {
  color: var(--paper);
}
/* Variante su fondo chiaro. Il blocco .philosophy nasce per la sezione
   scura (testo bianco al 74%, link gialli): su .section-light quei
   valori sparirebbero contro lo sfondo. Qui si invertono solo i
   colori — struttura e spaziature restano identiche. */
.section-light .philosophy-text p {
  color: rgba(0, 0, 0, 0.7);
}
.section-light .philosophy-text a {
  /* --lemon su bianco ha contrasto ~1.5:1, illeggibile: sul chiaro il
     link resta nero con sottolineatura, che è già il segnale di
     interattività, e la sottolineatura diventa gialla in hover. */
  color: var(--ink);
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
  text-decoration-color: var(--lemon-deep);
}
.section-light .philosophy-text a:hover,
.section-light .philosophy-text a:focus-visible {
  color: var(--ink);
  text-decoration-color: var(--ink);
}
/* =========================================================
   IL MONDO È CAMBIATO
   ========================================================= */
.changed-grid {
  margin-top: 2.5rem;
  display: grid;
  grid-template-columns: 1fr;
  gap: 2rem;
}
.changed-grid li {
  border-top: 2px solid var(--ink);
  padding-top: 1rem;
}
.changed-num {
  display: block;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(3.4rem, 11vw, 6rem);
  line-height: 0.9;
  color: var(--ink);
}
.changed-grid p {
  max-width: 26ch;
  font-weight: 600;
  color: rgba(0, 0, 0, 0.78);
}

/* =========================================================
   REFERENZE / GALLERY
   ========================================================= */
.references-lead {
  max-width: 62ch;
  /* color: rgba(255, 255, 255, 0.72); */
  /* font-size: 1.1rem; */
  margin-bottom: 2.5rem;
  margin-top: 0.5rem;
}
.gallery {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}
.gallery a {
  position: relative;
  display: block;
  overflow: hidden;
  border-radius: var(--radius);
  aspect-ratio: 4/3;
  border: 1px solid var(--line);
}
.gallery a img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition:
    transform 0.6s var(--ease),
    filter 0.4s var(--ease);
  filter: grayscale(0.3);
}
.gallery a:hover img {
  transform: scale(1.06);
  filter: grayscale(0);
}
.gallery a::after {
  content: attr(data-caption);
  position: absolute;
  inset: auto 0 0 0;
  z-index: 2;
  padding: 1.4rem 1.2rem 0.9rem;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 1rem;
  color: var(--paper);
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.82));
  transform: translateY(8px);
  opacity: 0;
  transition:
    opacity 0.3s var(--ease),
    transform 0.3s var(--ease);
}
.gallery a:hover::after,
.gallery a:focus-visible::after {
  opacity: 1;
  transform: translateY(0);
}

/* ---------- PORTFOLIO SHOWCASE (stack di card animate, home) ---------- */
.portfolio-showcase {
  display: grid;
  grid-template-columns: 1fr;
  gap: 3.5rem;
  align-items: center;
  /* Stacco sotto lo showcase, oltre al padding di sezione: è l'ultimo
     elemento della sezione e con lo stack alto le card arrivano vicine
     al bordo inferiore della scheda, dove la sezione successiva sale a
     coprirla. Sta qui e non sulla section così vale sia nel ramo
     centrato che in .has-overflow dello stack desktop.
     padding e non margin: initStackReveal.measure() (script.js) calcola
     l'overflow da inner.scrollHeight, che NON include il margine
     inferiore dell'ultimo figlio — con un margin lo spazio non entrerebbe
     nella misura e verrebbe tagliato da overflow:hidden sulla scheda. */
  padding-bottom: clamp(2rem, 4vw, 3.5rem);
}
.portfolio-showcase-media {
  position: relative;
}
.portfolio-showcase-stack {
  position: relative;
  height: 340px;
  /* Le .portfolio-card sono position:absolute; inset:0, quindi si
     stirano a tutta la larghezza di questo contenitore e il loro
     aspect-ratio 3/4 non riesce a rimpicciolirle: è la larghezza dello
     stack, non l'aspect-ratio, a decidere quanto sono grandi. Legandola
     all'altezza (3/4 di height) la card resta proporzionata e cambiare
     height basta a ridimensionare tutto il carosello. */
  width: min(100%, calc(340px * 4 / 5));
  margin-inline: auto;
  transform-style: preserve-3d;
  perspective: 1000px;
}
.portfolio-card {
  position: absolute;
  inset: 0;
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--line-dark);
  opacity: 0;
  transform-origin: bottom center;
  scale: 0.9;
  rotate: var(--portfolio-rot, 0deg);
  translate: 0 0;
  transition:
    opacity 0.4s var(--ease),
    scale 0.4s var(--ease),
    rotate 0.4s var(--ease),
    z-index 0s;
  pointer-events: none;
  will-change: transform, opacity;
  aspect-ratio: 4 / 5;
}
.portfolio-card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.portfolio-card.is-stacked {
  opacity: 0.7;
  scale: 0.95;
  rotate: var(--portfolio-rot, 0deg);
}
.portfolio-card.is-active {
  opacity: 1;
  scale: 1;
  rotate: 0deg;
  pointer-events: auto;
}
.portfolio-card.is-active.is-lifting {
  animation: portfolio-lift 0.4s ease-in-out;
}
@keyframes portfolio-lift {
  0% {
    translate: 0 0;
  }
  50% {
    translate: 0 -80px;
  }
  100% {
    translate: 0 0;
  }
}
@media (prefers-reduced-motion: reduce) {
  .portfolio-card {
    transition: opacity 0.3s linear;
  }
  .portfolio-card.is-active.is-lifting {
    animation: none;
  }
}
.portfolio-showcase-content {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 2.5rem;
  min-height: 260px;
}
.portfolio-showcase-text {
  min-height: 180px;
}
.portfolio-showcase-text.is-entering {
  animation: portfolio-text-in 0.2s ease-in-out;
}
@keyframes portfolio-text-in {
  0% {
    translate: 0 20px;
    opacity: 0;
  }
  100% {
    translate: 0 0;
    opacity: 1;
  }
}
@media (prefers-reduced-motion: reduce) {
  .portfolio-showcase-text.is-entering {
    animation: none;
  }
}
.portfolio-showcase-category {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.8rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(0, 0, 0, 0.55);
  margin-bottom: 0.6rem;
}
.portfolio-showcase-title {
  font-family: var(--font-head);
  font-weight: 900;
  font-size: 2.5rem;
  line-height: 1.05;
  margin-bottom: 1rem;
}
.portfolio-showcase-desc {
  color: rgba(0, 0, 0, 0.72);
  max-width: 46ch;
}
.portfolio-showcase-desc .word {
  display: inline-block;
  opacity: 0;
  filter: blur(6px);
  transform: translateY(4px);
  transition:
    opacity 0.35s var(--ease),
    filter 0.35s var(--ease),
    transform 0.35s var(--ease);
}
.portfolio-showcase-desc.is-revealed .word {
  opacity: 1;
  filter: blur(0);
  transform: none;
}
@media (prefers-reduced-motion: reduce) {
  .portfolio-showcase-desc .word {
    transition: opacity 0.2s linear;
  }
}
.portfolio-showcase-nav {
  display: flex;
  gap: 0.8rem;
}
/* =========================================================
   FRECCE DEI CAROSELLI - base condivisa
   Quattro caroselli usano la stessa freccia: portfolio-showcase e
   references-full (home), formats-slider (pagine servizi) e
   case-study-carousel (creato da script.js, initCaseStudyCarousel).
   Erano quattro blocchi separati con geometrie divergenti senza motivo
   (46/44/44/40px, border-radius 50% e 100px, centratura con place-items
   e con align-items+justify-content).
   Qui sta solo cio' che DEVE essere uguale: dimensione, forma, struttura
   e transizione. I colori restano nei blocchi sotto, perche' dipendono
   davvero dal fondo su cui la freccia appare.
   ========================================================= */
.portfolio-showcase-btn,
.references-full-btn,
.formats-slider-btn,
.case-study-carousel-btn {
  width: 44px;
  height: 44px;
  border-radius: 100px;
  display: grid;
  place-items: center;
  cursor: pointer;
  border: 1px solid transparent;
  transition:
    background 0.25s var(--ease),
    color 0.25s var(--ease),
    border-color 0.25s var(--ease);
}

/* Hover/focus giallo: comune ai tre caroselli su fondo scuro.
   portfolio-showcase-btn e' escluso di proposito - sta su fondo chiaro
   e usa il negativo (vedi sotto). */
.references-full-btn:hover,
.references-full-btn:focus-visible,
.formats-slider-btn:hover,
.formats-slider-btn:focus-visible,
.case-study-carousel-btn:hover,
.case-study-carousel-btn:focus-visible {
  background: var(--lemon);
  border-color: var(--lemon);
  color: var(--ink);
}

/* Su fondo chiaro: inchiostro su trasparente, hover in negativo.
   E' l'unico dei quattro a NON usare il giallo nell'hover. */
.portfolio-showcase-btn {
  border-color: var(--line-dark);
  background: transparent;
  color: var(--ink);
}
.portfolio-showcase-btn:hover,
.portfolio-showcase-btn:focus-visible {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}
@media (min-width: 768px) {
  .portfolio-showcase {
    grid-template-columns: 1fr 1fr;
    gap: 5.5rem;
  }
  .portfolio-showcase-stack {
    height: 500px;
    width: min(100%, calc(500px * 4 / 5));
  }
}

/* =========================================================
   CTA FINALE
   ========================================================= */
.cta-inner {
  text-align: center;
  max-width: 50rem;
  margin-inline: auto;
}
.cta-title {
  font-family: var(--font-display);
  font-weight: 400;
  text-transform: uppercase;
  font-size: clamp(2.4rem, 7vw, 5rem);
  line-height: 0.98;
  color: var(--ink);
  margin-bottom: 1rem;
}
/* Variante ridotta: CTA non-titolo (es. call-to-action affiancata al bottone) */
.cta-title--sm {
  font-size: clamp(1.8rem, 4vw, 2.8rem);
  line-height: 1.05;
}
.cta-sub {
  font-size: 1.2rem;
  font-weight: 500;
  margin-bottom: 2.5rem;
  color: rgba(0, 0, 0, 0.8);
}
/* Nel layout affiancato (testo + bottone di fianco) il sottotitolo non ha
   bisogno del margine inferiore, che servirebbe solo col bottone impilato sotto. */
.cta .row .cta-sub {
  margin-bottom: 0;
}
.cta-contacts {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.5rem;
  margin-bottom: 2.5rem;
}
.contact-block {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  padding-top: 1rem;
  border-top: 2px solid rgba(0, 0, 0, 0.18);
}
.contact-label {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.8rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(0, 0, 0, 0.55);
}
.contact-value {
  font-family: var(--font-head);
  font-weight: 800;
  font-size: 1.2rem;
  line-height: 1.35;
  color: var(--ink);
}
.contact-value a {
  white-space: nowrap;
  color: inherit;
  text-decoration: none;
}
.contact-value a:hover,
.contact-value a:focus-visible {
  text-decoration: underline;
}

/* =========================================================
   FOOTER
   ========================================================= */
.site-footer {
  background: #0a0a0a;
  color: var(--paper);
  padding-block: var(--footer-pad-top) var(--footer-pad-bottom);
  border-top: 1px solid var(--line);
}
.footer-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2.5rem;
}
.footer-brand .logo-text {
  font-size: 1.4rem;
}
.footer-logo-img {
  height: 40px;
  width: auto;
  margin-bottom: 0.4rem;
}
.footer-brand p {
  color: var(--ash);
  margin-top: 1.1rem;
}
.footer-col h3 {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.85rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--lemon);
  margin-bottom: 1rem;
}
.footer-col ul {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}
.footer-col a {
  color: var(--ash);
  transition: color 0.25s var(--ease);
}
.footer-col a:hover {
  color: var(--paper);
}
.footer-contacts address {
  font-style: normal;
  color: var(--ash);
  line-height: 1.8;
  margin-bottom: 1.2rem;
}
.footer-contacts address a:hover {
  color: var(--lemon);
}
.footer-contact-list {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}
.footer-contact-list li {
  display: flex;
  align-items: center;
  gap: 0.7rem;
}
.footer-contact-list i {
  flex: 0 0 auto;
  width: 1em;
  color: var(--lemon);
  font-size: 0.85rem;
  text-align: center;
}
.footer-col ul.socials,
.socials--brand {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: 0.6rem;
}
.socials--brand {
  margin-top: 1.5rem;
}
.socials a {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border: 1px solid var(--line);
  border-radius: 10px;
  color: var(--lemon);
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.9rem;
  text-transform: lowercase;
  transition:
    background 0.25s var(--ease),
    color 0.25s var(--ease);
}
.socials a:hover {
  background: var(--lemon);
  color: var(--ink);
}
.footer-assoc-band {
  /* Spazio ESTERNO alla banda (stacco dalle colonne del footer sopra e
     dalla riga legale sotto), non il padding interno che ne definisce
     l'altezza: quello resta 2rem. Il margine inferiore va dichiarato
     qui e non lasciato al margin-top di .footer-bottom, così i due
     stacchi restano simmetrici e regolabili da un punto solo. */
  margin-block: clamp(4rem, 7vw, 6rem);
  padding-block: 2rem;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.02);
}
.footer-assoc-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 1.5rem 2.5rem;
  text-align: center;
}
.footer-assoc-label {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.78rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ash-dark);
  flex: 0 0 auto;
}
.footer-assoc-logos {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 1.8rem 4rem;
}
.footer-assoc-logos li {
  display: flex;
  align-items: center;
  justify-content: center;
}
.footer-assoc-logos img {
  height: 64px;
  width: auto;
  max-width: 160px;
  object-fit: contain;
  opacity: 0.68;
  filter: grayscale(1) brightness(1.6);
  transition:
    opacity 0.3s var(--ease),
    filter 0.3s var(--ease);
}
.footer-assoc-logos img:hover {
  opacity: 1;
  filter: grayscale(0) brightness(1);
}
@media (max-width: 575.98px) {
  .footer-assoc-inner {
    gap: 1.2rem;
  }
  .footer-assoc-logos {
    gap: 1.4rem 1.6rem;
  }
  .footer-assoc-logos img {
    height: 34px;
  }
}
.footer-bottom {
  margin-top: 2rem;
  padding-top: 0;
  border-top: none;
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
  color: var(--ash-dark);
  font-size: 0.85rem;
}
.footer-legal {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.5rem;
}
.footer-legal a {
  color: var(--ash-dark);
  transition: color 0.25s var(--ease);
}
.footer-legal a:hover {
  color: var(--paper);
}

@media (min-width: 768px) {
  .footer-bottom {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
  }
}

/* =========================================================
   REVEAL ANIMATIONS
   ========================================================= */
.reveal {
  opacity: 0;
  transform: translateY(28px);
  transition:
    opacity 0.7s var(--ease),
    transform 0.7s var(--ease);
}
.reveal.is-visible {
  opacity: 1;
  transform: none;
}

.reveal-line {
  display: block;
  overflow: hidden;
}
.reveal-line > span {
  display: inline-block;
  transform: translateY(110%);
  transition: transform 0.8s var(--ease);
}
.reveal-line.is-visible > span {
  transform: translateY(0);
}

/* stagger helpers applied via JS inline delay */

/* =========================================================
   RESPONSIVE
   ========================================================= */
@media (min-width: 640px) {
  .claim-grid {
    grid-template-columns: repeat(3, 1fr);
    column-gap: 2.5rem;
  }
  /* Su 3 colonne: divisori verticali tra i blocchi (dal 2° in poi), niente linea in alto */
  .claim-card {
    border-top: 0;
    padding-top: 0;
  }
  .claim-card + .claim-card {
    border-left: 1.5px solid var(--ink);
    padding-left: 2.5rem;
  } 
  .section-dark .claim-card + .claim-card {
    border-left: 1.5px solid var(--paper);
    padding-left: 2.5rem;
  }
  .changed-grid {
    grid-template-columns: repeat(2, 1fr);
  }
  .cta-contacts {
    grid-template-columns: repeat(3, 1fr);
  }
  .gallery {
    grid-template-columns: repeat(2, 1fr);
  }
  .footer-grid {
    grid-template-columns: 1.4fr 1fr 1fr 1.2fr;
  }
  .footer-bottom {
    flex-direction: row;
    justify-content: space-between;
    align-items: center;
  }
}

@media (min-width: 768px) {
  .services-grid {
    grid-template-columns: repeat(2, 1fr);
  }
  .stats-grid {
    grid-template-columns: repeat(4, 1fr);
  }
  .stats-grid--3 {
    grid-template-columns: repeat(3, 1fr);
  }
  .changed-grid {
    grid-template-columns: repeat(4, 1fr);
  }
}

@media (min-width: 1024px) {
  .services-grid {
    grid-template-columns: repeat(3, 1fr);
  }
  .gallery {
    grid-template-columns: repeat(4, 1fr);
  }
}

/* Visibilità nav/CTA gestita da Bootstrap (navbar-expand-lg + d-none/d-lg-*).
   Il vecchio toggle/drawer custom è stato rimosso. */

/* =========================================================
   INTERNAL PAGES — shared layouts
   (pagine interne: agenzia, servizi, referenze, contatti, blog)
   ========================================================= */

/* ---------- PAGE HERO (intestazione pagine interne) ---------- */
.page-hero {
  position: relative;
  isolation: isolate;
  padding-top: clamp(8rem, 16vh, 11rem);
  padding-bottom: clamp(3rem, 8vw, 6rem);
  background: var(--ink);
  color: var(--paper);
  overflow: hidden;
}
.page-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(
      120% 100% at 85% -10%,
      rgba(255, 205, 0, 0.16),
      transparent 55%
    ),
    linear-gradient(180deg, #0a0a0a, var(--ink));
}
.page-hero::after {
  content: "";
  position: absolute;
  right: -8%;
  top: -30%;
  z-index: -1;
  width: 46vw;
  height: 46vw;
  max-width: 620px;
  max-height: 620px;
  background: radial-gradient(circle, rgba(255, 205, 0, 0.1), transparent 68%);
  border-radius: 50%;
}

/* ---------- SFONDO HERO: UNA IMMAGINE PER PAGINA ----------

   COME CAMBIARE LO SFONDO DI UNA PAGINA
   1. metti l'immagine in assets/images/
   2. cerca qui sotto la riga della pagina (blocco "MAPPA PAGINE")
      e cambia il nome del file
   3. per una pagina NUOVA: aggiungi al suo <section class="page-hero
      page-hero--image ..."> una classe `hero-<nome>`, poi aggiungi qui
      sotto la riga corrispondente

   Tutto passa da una sola custom property, --hero-img: le regole di
   contorno (velo scuro, sfumature, dimensioni) restano dichiarate una
   volta sola nella regola generica .page-hero--image e non vanno
   ritoccate. Niente style inline in pagina: il CSS resta l'unico posto
   dove si decide l'aspetto.

   IMMAGINE DI RIPIEGO: se una pagina ha .page-hero--image ma nessuna
   classe hero-*, --hero-img non è definita e vale il fallback indicato
   nella var() — la pagina non resta mai senza sfondo.              */

.page-hero--media {
  /* L'immagine non e' piu' uno sfondo CSS ma un <img class="page-hero-img">
     dentro la sezione: cosi' porta il proprio alt, entra nel preload dello
     scanner HTML (fetchpriority="high") e puo' un domani avere srcset.
     Qui resta solo il fondo di base, che si vede finche' l'immagine carica
     e attraverso il velo. */
  background: var(--ink);
}
.page-hero-img {
  position: absolute;
  inset: 0;
  z-index: -2; /* sotto il velo (::before, z-index -1) e sotto il contenuto */
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--hero-pos, center);
  /* L'immagine e' decorativa rispetto al layout: non deve intercettare
     click ne' selezioni sul testo della hero che le sta sopra. */
  pointer-events: none;
}
/* Velo scuro sopra l'immagine per la leggibilita' del testo; niente cerchio
   giallo. L'opacita' e' a sua volta una variabile: su una foto molto chiara
   si puo' scurire di piu' senza toccare altro (vedi --hero-veil sotto). */
.page-hero--media::before {
  background: rgba(0, 0, 0, var(--hero-veil, 0.6));
  z-index: -1;
}
.page-hero--media::after {
  display: none;
}

/* ===================== INQUADRATURA E VELO =====================
   L'immagine di ogni pagina si sceglie ora nell'HTML, nel tag
   <img class="page-hero-img"> dentro la .page-hero (con il suo alt).
   Da qui si regolano solo, se servono, questi due parametri sulla
   sezione:
     --hero-pos: center top;   (inquadratura: default e' center)
     --hero-veil: 0.55;        (velo scuro: default 0.6, piu' basso = piu' chiara)
   ================================================================ */

.breadcrumb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 0.82rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ash);
  margin-bottom: 1.4rem;
}
.breadcrumb a {
  color: var(--ash);
  transition: color 0.25s var(--ease);
}
.breadcrumb a:hover {
  color: var(--lemon);
}
.breadcrumb span[aria-hidden] {
  color: var(--ash-dark);
}
.breadcrumb [aria-current] {
  color: var(--lemon);
}

.page-hero-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.82rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--lemon);
  margin-bottom: 1rem;
}
.page-title {
  font-family: var(--font-display);
  font-weight: 400;
  text-transform: uppercase;
  line-height: 1;
  font-size: clamp(2.6rem, 8vw, 8rem);
  letter-spacing: -0.015em;
  max-width: 18ch;
}
.page-title .accent {
  color: var(--lemon);
}
.page-lead {
  margin-top: 1.4rem;
  max-width: 60ch;
  font-size: clamp(1.05rem, 2.2vw, 1.3rem);
  color: rgba(255, 255, 255, 0.78);
  font-weight: 400;
}

/* ---------- PROSE (testo lungo / articoli) ---------- */
.prose {
  max-width: 70ch;
}
.prose > * + * {
  margin-top: 1.25rem;
}
.prose p {
  color: inherit;
  opacity: 0.92;
  font-size: 1.08rem;
  line-height: 1.75;
}
.section-dark .prose p {
  color: rgba(255, 255, 255, 0.8);
  opacity: 1;
}
/* Heading: stacca dal blocco precedente, ma resta "attaccato" al testo che segue */
.prose h2 {
  font-family: var(--font-display);
  font-weight: 400;
  text-transform: uppercase;
  font-size: clamp(1.7rem, 4vw, 2.6rem);
  line-height: 1.05;
  letter-spacing: -0.01em;
  margin-top: 3rem;
  margin-bottom: 0.9rem;
}
.prose h3 {
  font-family: var(--font-head);
  font-weight: 800;
  font-size: 1.35rem;
  margin-top: 2.2rem;
  margin-bottom: 0.6rem;
}
/* L'elemento subito dopo un heading non deve ereditare il gap di * + * */
.prose h2 + *,
.prose h3 + * {
  margin-top: 0;
}
/* Titolo subito dopo il kicker: il distacco è già dato dal margine del kicker */
.prose .section-kicker + h2,
.prose .section-kicker + h3 {
  margin-top: 0;
}
/* Un vero titolo di sezione dentro .prose deve usare lo stile section-title, non prose h2 */
.prose h2.section-title {
  font-size: clamp(2.4rem, 7vw, 5rem);
  line-height: 1;
  margin-bottom: 1.5rem;
}
/* Primo elemento del flusso prose: niente margine in alto */
.prose > :first-child {
  margin-top: 0;
}
.prose strong {
  color: inherit;
  font-weight: 800;
}
.section-dark .prose strong,
.section-dark .prose h2,
.section-dark .prose h3 {
  color: var(--paper);
}
.prose ul.prose-list {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}
.prose ul.prose-list li {
  position: relative;
  padding-left: 1.8rem;
  line-height: 1.6;
}
.prose ul.prose-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.55em;
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 50%;
  background: var(--lemon);
}

/* ---------- IMMAGINE LATERALE (es. sezione storia agenzia) ---------- */
.story-image {
  margin: 0;
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--line-dark);
  box-shadow: 0 24px 50px -28px rgba(0, 0, 0, 0.35);
}
.story-image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  aspect-ratio: 4 / 3;
  transition: transform 0.6s var(--ease);
}
.story-image:hover img {
  transform: scale(1.03);
}

/* ---------- COLLAGE IMMAGINI (composizione asimmetrica) ---------- */
/* Griglia 2 colonne a righe fisse: ogni tassello occupa un numero di righe
   diverso (grid-row: span N) per un effetto mosaico. Le righe hanno altezza
   esplicita, così le immagini non collassano mai. */
.story-collage {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-auto-rows: 90px;
  gap: 0.9rem;
}
.story-collage-item {
  margin: 0;
  min-height: 0;
  overflow: hidden;
  border-radius: 14px;
  border: 1px solid var(--line-dark);
  box-shadow: 0 20px 44px -28px rgba(0, 0, 0, 0.35);
}
/* 1ª: colonna sx, alta (3 righe) · 2ª e 3ª: dx (2 righe ciascuna) · 4ª: sx sotto */
.story-collage-item:nth-child(1) { grid-column: 1; grid-row: span 3; }
.story-collage-item:nth-child(2) { grid-column: 2; grid-row: span 2; }
.story-collage-item:nth-child(3) { grid-column: 2; grid-row: span 3; }
.story-collage-item:nth-child(4) { grid-column: 1; grid-row: span 2; }
.story-collage-item img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.6s var(--ease);
}
.story-collage-item:hover img {
  transform: scale(1.05);
}
@media (min-width: 1200px) {
  .story-collage { grid-auto-rows: 104px; }
}

/* ---------- STORY ICON (icona servizio al posto del collage) ----------
   Alternativa a .story-collage nella colonna laterale della sezione
   "storia": una sola icona di servizio, grande, come elemento focale.
   Le icone SVG sono già nella palette del brand (giallo + tratto nero
   su bianco), quindi nessun filtro colore: vanno su .section-light. */
.story-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(1rem, 3vw, 2.5rem);
}
.story-icon img {
  width: 100%;
  /* tetto in px oltre al 100%: la colonna lg-5 su schermi molto larghi
     supera abbondantemente la dimensione utile dell'icona, che
     diventerebbe sproporzionata rispetto al blocco di testo accanto. */
  max-width: 400px;
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: contain;
}

/* ---------- TILT CARD (immagine 3D che segue il puntatore) ----------
   Porting vanilla del componente Framer "InteractiveTiltCard": nessuna
   libreria, il JS scrive solo le variabili --tilt-x/--tilt-y/--glare-*,
   le transizioni restano in CSS così l'animazione gira sul compositor. */
.tilt-card {
  --tilt-x: 0deg;
  --tilt-y: 0deg;
  --tilt-scale: 1;
  --glare-x: 50%;
  --glare-y: 50%;
  --glare-opacity: 0;
  position: relative;
  width: 100%;
  max-width: 520px;
  margin-inline: auto;
  perspective: 1000px;
}
.tilt-card-inner {
  position: relative;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--paper, #fff);
  transform-style: preserve-3d;
  transform: scale(var(--tilt-scale)) rotateX(var(--tilt-x)) rotateY(var(--tilt-y));
  box-shadow: 0 10px 30px -10px rgba(0, 0, 0, .2);
  transition: transform .2s ease-out, box-shadow .2s ease-out;
}
.tilt-card.is-tilting .tilt-card-inner {
  box-shadow: 0 25px 50px -12px rgba(0, 0, 0, .5);
}
.tilt-card-inner img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1023 / 1537;
  object-fit: cover;
  position: relative;
  z-index: 1;
}
/* Riflesso: segue il puntatore, resta sopra l'immagine ma non intercetta
   il mouse (altrimenti il mousemove si spegnerebbe sull'overlay). */
.tilt-card-glare {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  opacity: var(--glare-opacity);
  transition: opacity .2s ease-out;
  background: radial-gradient(circle at var(--glare-x) var(--glare-y),
              rgba(255, 255, 255, .5) 0%, rgba(255, 255, 255, 0) 80%);
}
@media (prefers-reduced-motion: reduce) {
  .tilt-card-inner,
  .tilt-card-glare { transition: none; }
  .tilt-card-inner { transform: none; }
  .tilt-card-glare { opacity: 0; }
}

/* ---------- INTRO SPLIT (testo + box laterale) ---------- */
.split {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2.5rem;
  align-items: start;
}
.split-aside {
  background: var(--ink-card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.8rem;
}
.section-light .split-aside {
  background: var(--paper);
  border-color: var(--line-dark);
}
.split-aside h3 {
  font-family: var(--font-head);
  font-weight: 800;
  font-size: 1.15rem;
  margin-bottom: 1rem;
}
.split-aside ul {
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
}
.split-aside ul li {
  display: flex;
  gap: 0.7rem;
  align-items: baseline;
  padding-bottom: 0.7rem;
  border-bottom: 1px solid var(--line);
  font-weight: 500;
}
.section-light .split-aside ul li {
  border-color: var(--line-dark);
}
.split-aside ul li:last-child {
  border-bottom: 0;
  padding-bottom: 0;
}
.split-aside ul li a {
  text-decoration: underline;
  text-decoration-color: var(--line-dark);
  text-underline-offset: 3px;
  transition: color 0.2s var(--ease), text-decoration-color 0.2s var(--ease);
}
.section-dark .split-aside ul li a {
  text-decoration-color: var(--line);
}
.split-aside ul li a:hover {
  color: var(--lemon-deep);
  text-decoration-color: var(--lemon-deep);
}
.section-dark .split-aside ul li a:hover {
  color: var(--lemon);
  text-decoration-color: var(--lemon);
}
.split-aside ul li .num {
  font-family: var(--font-display);
  color: var(--lemon-deep);
  font-size: 1.1rem;
  flex-shrink: 0;
}

/* ---------- SUB-SERVICE / FEATURE TILES ---------- */
.tiles {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.section-light .tiles {
  background: var(--line-dark);
  border-color: var(--line-dark);
}
.tile {
  background: var(--ink);
  padding: 1.8rem;
  transition:
    background 0.3s var(--ease),
    color 0.3s var(--ease);
}
.section-light .tile {
  background: var(--paper-dim);
}
.tile:hover {
  background: var(--lemon);
  color: var(--ink);
}
.tile h3 {
  font-family: var(--font-head);
  font-weight: 800;
  font-size: 1.2rem;
  margin-bottom: 0.5rem;
}
.tile p {
  font-size: 0.96rem;
  opacity: 0.82;
}
.tile:hover p {
  opacity: 1;
  color: var(--ink);
}

/* ---------- TEAM SHOWCASE ---------- */
.team-showcase {
  margin-top: 3rem;
  display: flex;
  flex-direction: column;
  gap: 2.5rem;
}
.team-showcase-photos {
  display: flex;
  gap: 0.6rem;
  overflow-x: auto;
  padding-bottom: 0.3rem;
  -webkit-overflow-scrolling: touch;
}
.team-photo-col {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  flex: 0 0 auto;
}
.team-photo-col--b {
  margin-top: 66px;
}
.team-photo-col--c {
  margin-top: 30px;
}
.team-photo-card {
  flex: 0 0 auto;
  width: 152px;
  height: 165px;
  border-radius: 16px;
  overflow: hidden;
  cursor: pointer;
  transition: opacity 0.4s var(--ease);
}
.team-photo-col--b .team-photo-card {
  width: 168px;
  height: 182px;
}
.team-photo-col--c .team-photo-card {
  width: 159px;
  height: 172px;
}
.team-photo-card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  filter: grayscale(1) brightness(0.77);
  transition: filter 0.5s var(--ease);
}
.team-photo-card.is-active img {
  filter: grayscale(0) brightness(1);
}
.team-showcase.has-hover .team-photo-card:not(.is-active) {
  opacity: 0.6;
}

.team-showcase-list {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}
.team-list-row {
  cursor: pointer;
  transition: opacity 0.3s var(--ease);
}
.team-showcase.has-hover .team-list-row:not(.is-active) {
  opacity: 0.5;
}
.team-list-row-top {
  display: flex;
  align-items: center;
  gap: 0.65rem;
}
.team-list-dash {
  flex: 0 0 auto;
  width: 16px;
  height: 3px;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.28);
  transition:
    width 0.3s var(--ease),
    background 0.3s var(--ease);
}
.team-list-row.is-active .team-list-dash {
  width: 22px;
  background: var(--paper);
}
.team-list-name {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 1.1rem;
  letter-spacing: -0.01em;
  color: rgba(255, 255, 255, 0.82);
  transition: color 0.3s var(--ease);
}
.team-list-row.is-active .team-list-name {
  color: var(--paper);
}
.team-list-socials {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin-left: 0.15rem;
  opacity: 0;
  transform: translateX(-8px);
  transition:
    opacity 0.2s var(--ease),
    transform 0.2s var(--ease);
  pointer-events: none;
}
.team-list-row.is-active .team-list-socials {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}
.team-list-socials a {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 6px;
  color: var(--ash);
  font-size: 0.7rem;
  transition:
    color 0.15s var(--ease),
    background 0.15s var(--ease);
}
.team-list-socials a:hover {
  color: var(--paper);
  background: rgba(255, 255, 255, 0.1);
}
.team-list-role {
  display: block;
  margin: 0.35rem 0 0 1.75rem;
  color: var(--ash);
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

/* Etichetta nome+ruolo dentro la card foto. Da 768px in su i nomi stanno
   nella lista a fianco e la card resta muta; sotto quella soglia la lista
   sparisce e questa etichetta prende il suo posto. */
.team-photo-caption {
  display: none;
}
@media (prefers-reduced-motion: reduce) {
  .team-photo-card,
  .team-photo-card img,
  .team-list-row,
  .team-list-dash,
  .team-list-name,
  .team-list-socials {
    transition: none;
  }
}

/* ---------- TEAM: mobile ----------
   Sotto i 768px il layout a tre colonne sfalsate non regge: le colonne
   scorrevano orizzontalmente (overflow-x:auto) e i margin-top di
   spaziatura lasciavano buchi in cima, mentre i nomi restavano in una
   lista separata raggiungibile solo con l'hover — che su touch non
   esiste. Qui le foto tornano una griglia verticale a due colonne e
   ogni card porta scritto il proprio nome. */
@media (max-width: 767.98px) {
  .team-showcase-photos {
    /* Le tre .team-photo-col non servono piu' come colonne: diventano
       contenitori trasparenti (display:contents) e le card cadono
       direttamente nella griglia del padre, cosi' l'ordine dei membri
       resta quello dei dati invece di leggersi a zig-zag per colonna. */
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.75rem;
    overflow-x: visible;
    padding-bottom: 0;
  }
  .team-photo-col {
    display: contents;
  }
  /* Via gli sfalsamenti verticali: in griglia lascerebbero buchi. */
  .team-photo-col--b,
  .team-photo-col--c {
    margin-top: 0;
  }
  /* Le card riempiono la colonna: niente piu' larghezze fisse diverse
     per colonna (152/168/159px), che in griglia sfondavano la cella. */
  .team-photo-card,
  .team-photo-col--b .team-photo-card,
  .team-photo-col--c .team-photo-card {
    position: relative;
    width: 100%;
    height: auto;
    aspect-ratio: 1 / 1;
    cursor: default;
  }
  /* Su touch non c'e' hover: le foto restano a colori pieni, senza il
     grigio di attesa che su desktop si scioglie al passaggio del mouse. */
  .team-photo-card img {
    filter: grayscale(0) brightness(1);
  }
  .team-photo-caption {
    display: block;
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 1.6rem 0.7rem 0.6rem;
    /* Sfumatura, non fondo pieno: il volto resta leggibile fin quasi al
       bordo inferiore e il testo ha comunque il suo contrasto. */
    background: linear-gradient(to top, rgba(0, 0, 0, 0.85) 35%, rgba(0, 0, 0, 0));
    pointer-events: none;
  }
  .team-photo-caption-name {
    display: block;
    font-family: var(--font-head);
    font-weight: 800;
    font-size: 0.82rem;
    line-height: 1.15;
    color: var(--paper);
  }
  .team-photo-caption-role {
    display: block;
    margin-top: 0.15rem;
    font-family: var(--font-head);
    font-weight: 600;
    font-size: 0.6rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    line-height: 1.25;
    color: rgba(255, 255, 255, 0.72);
  }
  /* La lista nomi diventa ridondante: gli stessi nomi sono ora sulle card. */
  .team-showcase-list {
    display: none;
  }
}
@media (min-width: 768px) {
  .team-showcase {
    flex-direction: row;
    align-items: flex-start;
    gap: 3.5rem;
  }
  .team-showcase-photos {
    overflow-x: visible;
  }
  .team-photo-card {
    width: 195px;
    height: 210px;
  }
  .team-photo-col--b .team-photo-card {
    width: 216px;
    height: 231px;
  }
  .team-photo-col--c .team-photo-card {
    width: 207px;
    height: 221px;
  }
  .team-showcase-list {
    flex: 1;
    padding-top: 0.5rem;
  }
}
@media (min-width: 1024px) {
  .team-photo-card {
    width: 217px;
    height: 231px;
  }
  .team-photo-col--b {
    margin-top: 95px;
  }
  .team-photo-col--b .team-photo-card {
    width: 241px;
    height: 255px;
  }
  .team-photo-col--c {
    margin-top: 45px;
  }
  .team-photo-col--c .team-photo-card {
    width: 227px;
    height: 241px;
  }
}

/* ---------- CLIENT LOGO GRID (referenze) ---------- */
.client-logo-grid {
  margin-top: 3rem;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1rem;
}
.client-logo-card {
  background: var(--paper);
  border: 1px solid var(--line-dark);
  border-radius: var(--radius);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
  display: flex;
  align-items: center;
  justify-content: center;
  height: 110px;
  padding: 1rem;
  opacity: 0;
  scale: 0.85;
  rotate: var(--client-rot, 0deg);
  transition:
    opacity 0.55s var(--ease),
    scale 0.55s var(--ease),
    rotate 0.55s var(--ease),
    background 0.4s var(--ease),
    border-color 0.4s var(--ease),
    box-shadow 0.4s var(--ease),
    transform 0.3s var(--ease);
}
.client-logo-card.is-visible {
  opacity: 1;
  scale: 1;
  rotate: 0deg;
}
.client-logo-card img {
  display: block;
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  filter: grayscale(1);
  opacity: 0.7;
  transition:
    filter 0.5s var(--ease),
    opacity 0.5s var(--ease);
}
.client-logo-card:hover {
  border-color: var(--lemon-deep);
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.08);
  transform: translateY(-4px);
}
.client-logo-card:hover img {
  filter: grayscale(0);
  opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
  .client-logo-card {
    transition: opacity 0.3s linear;
    scale: 1;
    rotate: 0deg;
  }
}

/* ---------- CLIENTS MARQUEE (home) ---------- */
.clients-marquee {
  background: var(--paper);
  /* niente padding-bottom ridotto qui: valeva meno del respiro standard
     di sezione e, essendo un selettore di classe, vinceva sulla regola
     "section" lasciando il carosello quasi a filo del bordo inferiore.
     Il fondo lo gestiscono i token --section-pad-bottom (mobile) e
     .stack-inner (desktop). */
  overflow: hidden;
}
.clients-marquee .section-title {
  margin-bottom: 0;
}
.clients-marquee .section-title .accent {
  color: var(--lemon-deep);
}
.marquee-stack {
  margin-top: 3rem;
  padding-block: 8px;
  display: flex;
  flex-direction: column;
  gap: 1rem;
  -webkit-mask-image: linear-gradient(
    to right,
    transparent 0,
    #000 8%,
    #000 92%,
    transparent 100%
  );
  mask-image: linear-gradient(
    to right,
    transparent 0,
    #000 8%,
    #000 92%,
    transparent 100%
  );
}
.marquee-row {
  width: 100%;
  overflow: hidden;
  padding-block: 8px;
  margin-block: -8px;
}
.marquee-track {
  display: flex;
  width: max-content;
  gap: 1rem;
  will-change: transform;
  animation-name: marquee-scroll;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-duration: var(--marquee-duration, 90s);
  animation-direction: var(--marquee-direction, normal);
}
.marquee-row:hover .marquee-track {
  animation-play-state: paused;
}
@keyframes marquee-scroll {
  from {
    transform: translate3d(0, 0, 0);
  }
  to {
    transform: translate3d(-50%, 0, 0);
  }
}
.marquee-card {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 200px;
  height: 120px;
  padding: 1.5rem;
  background: var(--paper);
  border: 1px solid var(--line-dark);
  border-radius: var(--radius);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
  transition:
    transform 0.3s var(--ease),
    background 0.5s var(--ease),
    border-color 0.5s var(--ease),
    box-shadow 0.5s var(--ease);
}
.marquee-card img {
  display: block;
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  transform: scale(1.15);
  filter: grayscale(1);
  opacity: 0.7;
  transition:
    filter 0.6s var(--ease),
    opacity 0.6s var(--ease);
}
.marquee-card:hover {
  border-color: var(--lemon-deep);
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.08);
  transform: translateY(-4px);
}
.marquee-card:hover img {
  filter: grayscale(0);
  opacity: 1;
}
.clients-marquee--dark {
  background: var(--ink);
}
.clients-marquee--dark .marquee-card {
  background: var(--ink-card);
  border-color: var(--line);
  box-shadow: none;
}
.clients-marquee--dark .marquee-card img {
  opacity: 1;
  filter: grayscale(1) invert(1) brightness(1.35) contrast(0.9);
  transition:
    filter 0.6s var(--ease),
    opacity 0.6s var(--ease);
}
.clients-marquee--dark .marquee-card:hover {
  background: var(--paper);
  border-color: var(--lemon);
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.35);
}
.clients-marquee--dark .marquee-card:hover img {
  filter: grayscale(0) invert(0) brightness(1) contrast(1);
}
@media (max-width: 767.98px) {
  .marquee-card {
    width: 150px;
    height: 92px;
    padding: 1.1rem;
  }
  .marquee-stack {
    gap: 0.75rem;
    margin-top: 2rem;
  }
}
@media (prefers-reduced-motion: reduce) {
  .marquee-track {
    animation: none !important;
  }
  .marquee-row {
    overflow-x: auto;
  }
}

/* ---------- FORMATS (figure con didascalia) ----------
   Non sono card: niente riquadro, niente fondo, niente bordo attorno al
   testo. Restano l'immagine e la sua didascalia, trattate come in un
   impaginato editoriale. Immagini tutte con la stessa proporzione e
   allineate in griglia regolare.
   L'unico movimento è il filo sopra la didascalia, che si allunga da
   sinistra sull'hover al posto del sollevamento tipico della card. */
.formats-list {
  margin: 3.5rem 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: 1fr;
  gap: 3rem 1.75rem;
}
/* Tre colonne solo da 992px: a 768px le figure scendevano a 214px e il
   testo si spezzava a tre-quattro parole per riga. In mezzo due colonne. */
@media (min-width: 768px) {
  .formats-list { grid-template-columns: repeat(2, 1fr); gap: 2.5rem 1.5rem; }
}
@media (min-width: 992px) {
  .formats-list { grid-template-columns: repeat(3, 1fr); gap: 2rem 1.5rem; }
}
.format-item {
  min-width: 0;
  /* Le didascalie partono tutte dalla stessa quota: le figure sono
     allineate in cima e le immagini hanno proporzione uguale, quindi il
     filo sopra il testo cade alla stessa altezza in tutte e tre. */
  display: flex;
  flex-direction: column;
}

.format-figure {
  position: relative;
  margin: 0;
}
.format-frame {
  display: block;
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--ink);
  /* Stessa proporzione per tutte e tre. Sta sulla cornice e non
     sull'immagine, così è il contenitore a decidere il ritaglio e le
     foto di partenza possono avere formati diversi fra loro. */
  aspect-ratio: 4 / 5;
}
.format-frame img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: grayscale(0.3);
  transition: transform 0.7s var(--ease), filter 0.45s var(--ease);
}
.format-figure:hover .format-frame img {
  transform: scale(1.05);
  filter: grayscale(0);
}
.format-caption {
  /* Il filo sopra la didascalia sta a top:0: senza stacco dall'immagine
     gli finiva appoggiato contro, leggendosi come un bordo della foto
     invece che come un elemento della didascalia. */
  margin-top: 1.35rem;
  padding-top: 1.15rem;
  position: relative;
}
/* Filo sopra la didascalia: parte corto e si allunga sull'hover. È il
   solo "effetto" della sezione, al posto del sollevamento della card. */
.format-caption::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  height: 2px;
  width: 2.5rem;
  background: var(--ink);
  transition: width 0.45s var(--ease), background-color 0.45s var(--ease);
}
.format-figure:hover .format-caption::before {
  width: 100%;
  background: var(--lemon-deep);
}
.format-caption h3 {
  font-family: var(--font-head);
  font-weight: 800;
  font-size: 1.25rem;
  line-height: 1.2;
  margin: 0 0 0.5rem;
  color: var(--ink);
}
.format-caption p {
  color: #3a3a3a;
  font-size: 0.95rem;
  line-height: 1.6;
  margin: 0;
}
@media (prefers-reduced-motion: reduce) {
  .format-frame img,
  .format-caption::before { transition: none; }
  .format-figure:hover .format-frame img { transform: none; }
}

/* ---------- FORMATS SLIDER (immagini a piena sezione + didascalia) ----------
   Variante della sezione .formats: le stesse voci diventano immagini a
   piena scheda che si alternano in dissolvenza, con la didascalia in
   overlay. Stessa impostazione di .references-full in home, ma con i
   testi presi dal markup invece che da un array JS, così il componente
   è riusabile su qualunque pagina. */
.formats-slider {
  /* Niente padding di sezione: l'immagine deve toccare i bordi della
     scheda. Il fondo scuro copre l'istante prima che la foto (lazy) sia
     decodificata, evitando il lampo bianco. */
  padding-block: 0;
  background: var(--ink);
  overflow: hidden;
}
.formats-slider .stack-inner {
  padding: 0;
  position: relative;
  overflow: hidden;
}
.formats-slider-media {
  position: absolute;
  inset: 0;
  /* Ritaglia il micro-zoom delle immagini non attive (scale 1.06): senza,
     sborderebbero sotto la scheda e initStackReveal leggerebbe quello
     sbordo come overflow reale, riservando uno spacer di scroll inutile. */
  overflow: hidden;
}
.formats-slider-media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  opacity: 0;
  transform: scale(1.06);
  transition: opacity .8s var(--ease), transform 1.4s var(--ease);
}
.formats-slider-media img.is-active {
  opacity: 1;
  transform: scale(1);
}
/* Velo: il testo bianco deve restare leggibile su qualunque foto.
   Gradiente e non tinta piatta, più denso in basso dove sta il testo. */
.formats-slider-overlay {
  /* absolute e non relative: un overlay nel flusso alto quanto il
     viewport più il proprio padding supera l'altezza della scheda e fa
     scattare .has-overflow su initStackReveal, che rimetterebbe il
     padding di sezione spostando la didascalia a metà scheda. */
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  align-items: flex-end;
  padding-block: clamp(2rem, 5vw, 4rem);
  background: linear-gradient(to top,
    rgba(0, 0, 0, .86) 0%,
    rgba(0, 0, 0, .62) 34%,
    rgba(0, 0, 0, .22) 66%,
    rgba(0, 0, 0, .05) 100%);
}
.formats-slider-overlay > .container { width: 100%; }
.formats-slider-caption {
  max-width: 46rem;
  color: var(--paper);
}
.formats-slider-kicker {
  font-family: var(--font-head);
  font-size: .78rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--lemon);
  margin: 0 0 .7rem;
}
.formats-slider-title {
  font-family: var(--font-display);
  font-size: clamp(1.9rem, 4.5vw, 3.25rem);
  line-height: 1.05;
  text-transform: uppercase;
  margin: 0 0 .9rem;
  color: var(--paper);
}
.formats-slider-desc {
  font-size: clamp(.98rem, 1.2vw, 1.1rem);
  line-height: 1.6;
  color: rgba(255, 255, 255, .85);
  margin: 0;
  max-width: 52ch;
}
/* Cambio didascalia: esce e rientra con un piccolo scorrimento, così il
   testo non "salta" da una voce all'altra. La classe la mette il JS. */
.formats-slider-caption.is-swapping .formats-slider-kicker,
.formats-slider-caption.is-swapping .formats-slider-title,
.formats-slider-caption.is-swapping .formats-slider-desc {
  opacity: 0;
  transform: translateY(10px);
}
.formats-slider-kicker,
.formats-slider-title,
.formats-slider-desc {
  transition: opacity .35s var(--ease), transform .35s var(--ease);
}
.formats-slider-nav {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-top: 1.8rem;
}
/* Sopra un'immagine a piena scheda: il velo scuro stacca la freccia dalla
   foto, che puo' essere chiara in quel punto. Geometria, transizione e
   hover arrivano dalla base condivisa (vedi FRECCE DEI CAROSELLI). */
.formats-slider-btn {
  border-color: var(--line);
  background: rgba(0, 0, 0, .25);
  color: var(--paper);
}
.formats-slider-count {
  font-family: var(--font-head);
  font-size: .85rem;
  letter-spacing: .08em;
  color: rgba(255, 255, 255, .72);
  margin: 0;
  font-variant-numeric: tabular-nums;
}
@media (prefers-reduced-motion: reduce) {
  .formats-slider-media img {
    transition: opacity .2s linear;
    transform: none;
  }
  .formats-slider-media img.is-active { transform: none; }
  .formats-slider-kicker,
  .formats-slider-title,
  .formats-slider-desc { transition: none; }
}
/* Fuori dallo stack (mobile) la sezione non ha height:100vh: media e
   overlay sono entrambi assoluti, quindi senza un'altezza esplicita il
   wrapper collasserebbe. */
@media (max-width: 767.98px) {
  .formats-slider .stack-inner { min-height: 82vh; }
}

/* ---------- BLOG GRID ---------- */
.blog-grid {
  margin-top: 3rem;
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.5rem;
}
.post-card {
  display: flex;
  flex-direction: column;
  background: var(--ink-card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  transition:
    transform 0.35s var(--ease),
    border-color 0.35s var(--ease);
}
.post-card:hover {
  transform: translateY(-6px);
  border-color: var(--lemon);
}
.post-card .post-thumb {
  aspect-ratio: 16/10;
  overflow: hidden;
  background: #000;
}
.post-card .post-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: grayscale(0.25);
  transition:
    transform 0.6s var(--ease),
    filter 0.4s var(--ease);
}
.post-card:hover .post-thumb img {
  transform: scale(1.06);
  filter: grayscale(0);
}
.post-card .post-body {
  padding: 1.6rem;
  display: flex;
  flex-direction: column;
  flex: 1;
}
.post-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.8rem;
  align-items: center;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.74rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ash);
  margin-bottom: 0.8rem;
}
.post-tag {
  color: var(--lemon);
}
.post-card h3 {
  font-family: var(--font-head);
  font-weight: 800;
  font-size: 1.3rem;
  line-height: 1.2;
  margin-bottom: 0.6rem;
}
.post-card p {
  color: rgba(255, 255, 255, 0.66);
  font-size: 0.96rem;
  flex: 1;
}
.post-card .post-more {
  margin-top: 1.1rem;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.82rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--lemon);
}

/* ---------- ARTICLE (post singolo) ---------- */
/* <article> non è <section>: senza questa regola non eredita il padding verticale,
   quindi l'immagine resta attaccata in alto e i pulsanti in basso. */
article.section-light,
article.section-dark,
article.section-yellow {
  padding-top: var(--section-pad-top);
  padding-bottom: var(--section-pad-bottom);
  position: relative;
}
.article-wrap {
  max-width: 760px;
  margin-inline: auto;
}
.article-figure {
  margin: 0 0 2.5rem;
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--line);
}
.article-figure img {
  width: 100%;
  height: auto;
}
.article-lead {
  font-family: var(--font-head);
  font-weight: 600;
  font-size: clamp(1.2rem, 2.6vw, 1.55rem);
  line-height: 1.45;
  color: var(--lemon);
  /* il margine sotto è gestito dal margin-top del primo heading (no doppio gap) */
  padding-bottom: 1.6rem;
  border-bottom: 1px solid var(--line-dark);
}
.section-dark .article-lead {
  border-bottom-color: var(--line);
}
/* Il giallo della variante base e' pensato per il fondo scuro: su chiaro
   scende a 1.5:1 ed e' illeggibile. I post del blog usano section-light. */
.section-light .article-lead,
.section-yellow .article-lead {
  color: var(--ink);
}
.article-share {
  margin-top: 3rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  align-items: center;
  justify-content: space-between;
}

/* ---------- CONTACT (contatti) ---------- */
.contact-layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: 3rem;
  align-items: start;
}
.contact-info-list {
  display: flex;
  flex-direction: column;
  gap: 1.6rem;
}
.info-item {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding-bottom: 1.4rem;
  border-bottom: 1px solid var(--line);
}
.info-item:last-child {
  border-bottom: 0;
}
.info-item .info-label {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.78rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--lemon);
}
.info-item .info-value {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 1.2rem;
  line-height: 1.4;
}
.info-item .info-value a:hover {
  color: var(--lemon);
}

.contact-form {
  display: grid;
  gap: 1.2rem;
}
.field {
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
}
.field label {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.82rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.85);
}
.field input,
.field textarea {
  font-family: var(--font-body);
  font-size: 1rem;
  background: var(--ink-card);
  color: var(--paper);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 0.9rem 1rem;
  transition:
    border-color 0.25s var(--ease),
    background 0.25s var(--ease);
}
.field input::placeholder,
.field textarea::placeholder {
  color: var(--ash-dark);
}
.field input:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--lemon);
  background: #181818;
}
.field textarea {
  resize: vertical;
  min-height: 140px;
}
.field-check {
  flex-direction: row;
  align-items: flex-start;
  gap: 0.7rem;
}
.field-check input {
  width: 1.15rem;
  height: 1.15rem;
  margin-top: 0.15rem;
  accent-color: var(--lemon);
  flex-shrink: 0;
}
.field-check label {
  text-transform: none;
  letter-spacing: 0;
  font-weight: 500;
  font-size: 0.9rem;
  color: var(--ash);
}
.field-check label a {
  color: var(--lemon);
  text-decoration: underline;
}
.form-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.2rem;
}
.form-status {
  font-weight: 600;
  font-size: 0.95rem;
  min-height: 1.2em;
}
.form-status.ok {
  color: var(--lemon);
}
.form-status.err {
  color: #ff6b6b;
}

.map-embed {
  margin-top: 3rem;
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--line);
  aspect-ratio: 21/9;
  background: var(--ink-card);
}
.map-embed iframe {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
  filter: grayscale(0.4) invert(0.9) contrast(0.85);
}

/* ---------- CONTATTI su fondo chiaro ----------
   Il blocco .contact-form / .info-item / .map-embed qui sopra è scritto
   per fondo scuro (etichette bianche, campi --ink-card, bordi --line,
   mappa invertita). Su .section-light quei valori sparirebbero o
   andrebbero fuori contrasto, quindi vengono ridichiarati qui — ambito
   ristretto a .contact-section, così le stesse classi restano valide
   sulle altre pagine che le usano su fondo scuro. */
.contact-section .info-item {
  border-bottom-color: var(--line-dark);
}
.contact-section .info-item .info-label {
  /* Il giallo del brand su fondo chiaro non tiene il contrasto per un
     testo piccolo (è un'etichetta da 0.78rem): serve l'inchiostro. */
  color: var(--ink);
}
.contact-section .info-item .info-value,
.contact-section .info-item .info-value a {
  color: var(--ink);
}
.contact-section .info-item .info-value a:hover {
  color: var(--lemon-deep);
}
.contact-section .field label {
  color: var(--ink);
}
.contact-section .field input,
.contact-section .field textarea {
  background: var(--paper);
  color: var(--ink);
  border-color: var(--line-dark);
}
.contact-section .field input::placeholder,
.contact-section .field textarea::placeholder {
  color: var(--ash-dark);
}
.contact-section .field input:focus,
.contact-section .field textarea:focus {
  border-color: var(--ink);
  background: var(--paper);
}
.contact-section .field-check label {
  color: #3a3a3a;
}
.contact-section .field-check label a {
  color: var(--ink);
}
.contact-section .form-status.ok {
  /* Il giallo su chiaro non si legge: per un messaggio di conferma
     serve un colore che si veda davvero. */
  color: #1a7f37;
}
.contact-section .form-status.err {
  color: #b3261e;
}
.contact-section .map-embed {
  border-color: var(--line-dark);
  background: var(--paper-dim);
}
.contact-section .map-embed iframe {
  /* Senza invert: quel filtro serviva a far sembrare scura una mappa
     chiara. Su fondo chiaro la mappa può restare com'è, con una leggera
     desaturazione per non competere con il giallo del brand. */
  filter: grayscale(0.25);
}

/* ---------- NEXT/PREV + RELATED CTA strip ---------- */
.mini-cta {
  display: flex;
  flex-direction: column;
  gap: 1.2rem;
  align-items: flex-start;
  text-align: left;
}
.mini-cta .mini-cta-title {
  font-family: var(--font-display);
  font-weight: 400;
  text-transform: uppercase;
  font-size: clamp(1.8rem, 5vw, 3rem);
  line-height: 1;
  color: var(--ink);
}

/* ---------- service card link spacing ---------- */
.service-card .service-link {
  margin-top: 1rem;
}

/* Lo stato attivo della nav (aria-current) è gestito da .navbar .nav-link nel
   blocco "BOOTSTRAP — integrazione". */

/* ---------- RESPONSIVE (internal pages) ---------- */
@media (min-width: 640px) {
  .tiles {
    grid-template-columns: repeat(2, 1fr);
  }
  .client-logo-grid {
    grid-template-columns: repeat(3, 1fr);
  }
  .blog-grid {
    grid-template-columns: repeat(2, 1fr);
  }
  .form-row {
    grid-template-columns: 1fr 1fr;
  }
}
@media (min-width: 768px) {
  .split {
    grid-template-columns: 1.6fr 1fr;
    gap: 3.5rem;
  }
  .contact-layout {
    grid-template-columns: 1fr 1.2fr;
    gap: 4rem;
  }
  .mini-cta {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
  }
}
@media (min-width: 1024px) {
  .tiles {
    grid-template-columns: repeat(3, 1fr);
  }
  .client-logo-grid {
    grid-template-columns: repeat(5, 1fr);
  }
  .blog-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* =========================================================
   REDUCED MOTION
   ========================================================= */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .reveal {
    opacity: 1;
    transform: none;
  }
  .reveal-line > span {
    transform: none;
  }
  .hero-video {
    display: none;
  }
  .hero-scroll-dot {
    animation: none;
  }
}

/* =========================================================
   BOOTSTRAP — integrazione / override
   Bootstrap 5.3 gestisce layout (grid, navbar, utilities);
   qui riporto i componenti al look brand Blacklemon.
   ========================================================= */

/* Container: mantiene la larghezza del design system (1280px).
   La regola base e' gia' in cima al file; qui serve SOLO l'override
   dentro le media query, perche' Bootstrap ridefinisce .container a ogni
   breakpoint con la stessa specificita'. */
@media (min-width: 576px), (min-width: 768px), (min-width: 992px),
       (min-width: 1200px), (min-width: 1400px) {
  .container { max-width: var(--maxw); }
}

/* Navbar: trasparente, senza padding Bootstrap (li gestiamo noi) */
.site-header .navbar { padding: 0; background: transparent; }
.site-header .header-inner { min-height: auto; }

/* Link nav in stile brand (sovrascrive .nav-link di Bootstrap) */
.navbar .nav-link {
  font-family: var(--font-head); font-weight: 600; font-size: 0.95rem;
  color: var(--paper); padding: 0.3rem 0; position: relative;
}
.navbar .nav-link:hover, .navbar .nav-link:focus { color: var(--paper); }
/* Underline animato su ::before, così NON entra in conflitto con la freccia
   del dropdown (Servizi), che usa ::after. */
.navbar .nav-link::before {
  content: ""; position: absolute; left: 0; bottom: -2px;
  width: 0; height: 2px; background: var(--lemon); transition: width 0.3s var(--ease);
}
.navbar .nav-link:hover::before,
.navbar .nav-link:focus-visible::before,
.navbar .nav-link[aria-current="page"]::before { width: 100%; }
.navbar .nav-link[aria-current="page"] { color: var(--lemon); }
/* spaziatura tra le voci come prima (gap 2rem) su desktop */
@media (min-width: 992px) {
  .navbar-nav.nav-list { gap: 2rem; }
  .navbar-nav.nav-list .nav-item { display: flex; align-items: center; }
}

/* Dropdown Servizi in stile brand (glassmorphism scuro) */
.navbar .dropdown-toggle::after {
  border: 0; width: 0.45rem; height: 0.45rem; margin-left: 0.5rem;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: translateY(-2px) rotate(45deg); transition: transform 0.3s var(--ease);
  vertical-align: middle;
}
.navbar .dropdown.show .dropdown-toggle::after { transform: translateY(1px) rotate(-135deg); }
.navbar .dropdown-menu {
  background: rgba(18, 18, 18, 0.96); backdrop-filter: blur(14px);
  border: 1px solid var(--line); border-radius: 14px;
  padding: 0.6rem; min-width: 260px; margin-top: 0.9rem;
  box-shadow: 0 24px 50px -24px rgba(0, 0, 0, 0.7);
}
.navbar .dropdown-item {
  font-family: var(--font-head); font-weight: 600; font-size: 0.92rem;
  color: rgba(255, 255, 255, 0.82); border-radius: 9px; padding: 0.65rem 0.9rem;
}
.navbar .dropdown-item:hover, .navbar .dropdown-item:focus,
.navbar .dropdown-item.active {
  background: var(--lemon); color: var(--ink);
}

/* Hamburger: mantiene lo stile custom (niente bordo/outline Bootstrap) */
.navbar-toggler.nav-toggle {
  border: 0; padding: 0.4rem; box-shadow: none;
  display: none; flex-direction: column; gap: 5px; background: none;
}
.navbar-toggler.nav-toggle:focus { box-shadow: none; }
.navbar-toggler.nav-toggle span {
  width: 26px; height: 2px; background: var(--paper);
  transition: transform 0.3s var(--ease), opacity 0.3s var(--ease);
}
.navbar-toggler.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.navbar-toggler.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.navbar-toggler.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* Menu mobile (collapse): pannello scuro a tutta larghezza sotto la navbar */
@media (max-width: 991.98px) {
  .navbar-toggler.nav-toggle { display: inline-flex; }
  .navbar .navbar-collapse {
    background: var(--ink); border-top: 1px solid var(--line);
    margin: 1rem calc(var(--pad) * -1) 0; padding: 1rem var(--pad) 1.5rem;
  }
  .navbar .navbar-nav.nav-list { gap: 0; }
  .navbar .nav-item { border-bottom: 1px solid var(--line); }
  .navbar .nav-link {
    font-family: var(--font-head); font-weight: 700; font-size: 1.4rem;
    text-transform: uppercase; padding: 0.7rem 0;
  }
  .navbar .nav-link::before { display: none; } /* niente underline su mobile */
  .navbar .dropdown-menu {
    background: none; backdrop-filter: none; border: 0; border-radius: 0;
    box-shadow: none; margin: 0 0 0.6rem 0.4rem; padding: 0.2rem 0; min-width: 0;
  }
  .navbar .dropdown-item {
    text-transform: none; font-size: 1.05rem; padding: 0.5rem 0.4rem;
    color: rgba(255, 255, 255, 0.78);
  }
  .navbar .dropdown-item:hover, .navbar .dropdown-item:focus {
    background: none; color: var(--lemon);
  }
  .navbar .nav-item.d-lg-none .btn-pill { margin-top: 0.5rem; }
}

/* Footer convertito a Bootstrap grid (.row): disattiva il CSS grid custom
   così col-* funziona. Le pagine non ancora migrate usano .footer-grid da solo. */
.footer-grid.row {
  display: flex;
  /* Niente gutter orizzontali: i contenuti restano a filo del container
     (allineati con la riga copyright sotto). Lo spazio tra colonne è dato dal row-gap. */
  --bs-gutter-x: 0;
  --bs-gutter-y: 2.5rem;
  margin-top: 0;
}
.footer-grid.row > [class*="col-"] {
  padding-right: 0;
  padding-left: 0;
}
/* Spazio tra le colonne su desktop (senza sfasare l'allineamento a sinistra) */
@media (min-width: 992px) {
  .footer-grid.row { column-gap: 2.5rem; flex-wrap: nowrap; }
  .footer-grid.row > [class*="col-"] { flex: 1 1 0; width: auto; max-width: none; }
  .footer-grid.row > .footer-brand { flex-grow: 1.6; }
  .footer-grid.row > .footer-contacts { flex-grow: 1.2; }
}
/* Su mobile/tablet: due colonne affiancate per Navigazione/Servizi, spaziatura verticale */
@media (max-width: 991.98px) {
  .footer-grid.row { column-gap: 1.5rem; }
}

/* Il padding-left del Reboot di Bootstrap e' gia' azzerato per
   .footer-col ul (e .socials) dal reset globale qui sotto: qui resta solo
   il margin-bottom, che quel reset non tocca. */
.footer-col ul { margin-bottom: 0; }

/* Reboot di Bootstrap aggiunge padding-left:2rem a tutti gli <ul>.
   Le liste del design system Blacklemon non devono averlo: reset globale mirato. */
.nav-list, .submenu, .stats-grid, .team-showcase-list, .client-logo-grid, .blog-grid,
.gallery, .tiles, .pillars, .hero-tags, .socials, .footer-col ul,
.split-aside ul, .prose ul.prose-list, .changed-grid, .services-grid,
.claim-grid, .cta-contacts, .contact-info-list,
.case-study-desc, .footer-assoc-logos {
  padding-left: 0;
  margin-bottom: 0;
}

/* =========================================================
   PORTFOLIO PAGE
   ========================================================= */
/* La hero di portfolio.html NON ha più regole proprie: usa
   .page-hero + .page-hero--image + .page-title come tutte le altre
   pagine interne (vedi il blocco PAGE HERO più sopra).

   Qui c'erano ~90 righe di CSS di una hero precedente e ormai
   smantellata: .portfolio-hero (contenitore alternativo a .page-hero),
   .portfolio-hero-bg (gradienti di sfondo), -lead, -stats, -stat,
   -stat-num, -stat-label, -scroll, -scroll-dot e la relativa
   @keyframes portfolio-scroll-bounce. Nessuna di quelle classi era
   più presente in nessun file HTML: erano rimaste orfane.
   Sopravvivevano solo .portfolio-hero-inner (che non aveva nemmeno
   una regola CSS: era una classe scritta nell'HTML e mai definita) e
   .portfolio-hero-title, praticamente identica a .page-title salvo
   dettagli di dimensione. Entrambe rimosse dall'HTML. */

/* =========================================================
   SEZIONE A SOLA IMMAGINE (.full-image)
   Scheda dello stack senza testo: la foto riempie tutta la
   sezione e fa da stacco visivo fra due blocchi di contenuto.
   ========================================================= */
.full-image {
  /* Fondo scuro sotto l'immagine: si vede solo nell'istante prima che
     la foto (lazy) sia decodificata, ed evita il lampo bianco della
     pagina fra due sezioni scure. */
  background: var(--ink);
  /* Azzera il padding verticale generico di "section" (inizio file):
     qui l'immagine deve arrivare ai bordi, senza cornice. */
  padding-block: 0;
  overflow: hidden;
}
/* Default globale dell'intensità di parallasse. Sta sulla SEZIONE e non
   sull'immagine così initFullImageParallax (script.js) può sovrascriverla
   per la singola scheda leggendo data-parallax dall'HTML: una custom
   property dichiarata sul figlio vincerebbe sempre sull'override del
   padre, rendendo l'attributo inefficace. */
.full-image {
  --parallax-zoom: 140%;
}
.full-image .stack-inner {
  /* Fuori dallo stack (sotto i 768px). Dentro lo stack serve un
     selettore più forte: vedi .stack-viewport .full-image .stack-inner
     nel blocco STACK SCROLL in fondo al file. */
  padding: 0;
  /* Riferimento per l'immagine in position:absolute (vedi sotto) e
     ritaglio della parte che eccede durante la parallasse: è questo box
     a fare da "finestra" sulla foto più alta. Nello stack .stack-inner è
     già absolute — quindi già un contenitore per il posizionamento — ma
     qui serve anche fuori dallo stack. */
  position: relative;
  overflow: hidden;
}
.full-image-img {
  display: block;
  width: 100%;
  /* PARALLASSE — l'immagine è più ALTA della scheda e ancorata in alto:
     l'eccedenza è la corsa disponibile per farla scorrere. Senza questo
     margine non ci sarebbe nulla da traslare e il transform mostrerebbe
     una banda vuota.
     La traslazione vera è applicata da initFullImageParallax()
     (script.js) come translate3d sull'asse Y, ricalcolata a ogni scroll
     in base a quanto la scheda è avanzata nello stack.

     --parallax-zoom È IL PARAMETRO DELL'EFFETTO: quanto l'immagine è più
     alta della scheda. 100% = nessun movimento, 140% = corsa del 40%
     dell'altezza della scheda. Più alto = effetto più marcato (ma la
     foto viene ingrandita di più, quindi si perde definizione: oltre
     ~160% su immagini piccole si vede).
     Si cambia da qui per TUTTE le sezioni, oppure sulla singola sezione
     con l'attributo data-parallax in HTML (vedi initFullImageParallax). */
  height: var(--parallax-zoom, 140%);
  position: absolute;
  top: 0;
  left: 0;
  /* cover + object-position centrata: la foto riempie la scheda a
     qualunque proporzione, ritagliando ciò che eccede invece di
     deformarsi. Su schermi molto larghi o molto stretti resta
     comunque piena. */
  object-fit: cover;
  object-position: center;
  /* will-change qui è sicuro e utile: è UN solo elemento che si muove
     davvero a ogni frame, non le 20 sezioni dello stack (vedi la nota
     sui layer compositi in script.js — lì la promozione di massa era
     dannosa, qui è il caso d'uso corretto). */
  will-change: transform;
}
/* Senza JS o con animazioni ridotte l'immagine resta ferma e centrata:
   il 120% di altezza da solo la mostrerebbe tagliata in basso. */
@media (prefers-reduced-motion: reduce) {
  .full-image-img {
    height: 100%;
    object-position: center;
    will-change: auto;
  }
}
/* Fuori dallo stack (mobile, sotto i 768px) la sezione non ha
   height:100vh: senza un'altezza esplicita l'immagine si allungherebbe
   a tutta la sua proporzione naturale, occupando mezzo schermo di
   scroll. Un'altezza in vh la mantiene una banda.

   L'altezza va data al WRAPPER, non all'immagine: è il wrapper a fare da
   finestra e l'immagine deve restare al 120% di quella finestra, così
   resta la corsa per la parallasse. Dandola all'immagine (come in una
   prima versione) la corsa si azzerava, e con essa il denominatore del
   calcolo: l'immagine veniva traslata di tutta la propria altezza e
   usciva dall'inquadratura, lasciando la sezione vuota. */
@media (max-width: 767.98px) {
  .full-image .stack-inner {
    height: 60vh;
  }
}

/* ---------- VARIANTE CON CITAZIONE (.full-image--quote) ----------
   Stessa scheda a sola immagine, con una citazione in overlay. Tutto
   ciò che segue vive DENTRO .stack-inner (che è già position:relative
   e overflow:hidden per la parallasse): il velo e il testo si sovrappongono
   all'immagine senza toccarne il funzionamento. */

/* Il velo. Copre TUTTA la scheda, non solo la fascia sotto il testo.
   Due livelli sovrapposti:
   1. una velatura piatta uniforme su tutta l'area, che è ciò che rende
      la sezione un blocco scuro coerente con le schede vicine invece di
      una fotografia chiara con una banda scura in fondo;
   2. un gradiente che aggiunge densità verso il basso, dove sta il testo
      e dove serve il contrasto WCAG AA sul bianco.
   inset:0 lo àncora al wrapper .stack-inner, che è la finestra reale
   sulla foto: l'immagine sotto è più alta e scorre in parallasse, ma è
   la finestra a definire ciò che si vede, quindi il velo resta sempre
   esattamente sovrapposto alla porzione visibile. */
.full-image-scrim {
  position: absolute;
  /* NON `inset: 0`. Con inset il velo prendeva l'altezza della FINESTRA
     (.stack-inner, un viewport) mentre l'immagine sotto è più alta —
     --parallax-zoom, di default il 140% — e scorre. Misurato: fino a
     332px di fotografia restavano fuori dal velo, e agli estremi della
     corsa quella fascia scoperta entrava in campo.
     Il velo deve quindi coprire l'IMMAGINE, non la finestra: stessa
     origine, stessa altezza e — sotto — stessa traslazione, così i due
     piani restano incollati per tutta la parallasse. */
  top: 0;
  left: 0;
  width: 100%;
  height: var(--parallax-zoom, 140%);
  z-index: 1;
  pointer-events: none;
  /* Segue l'immagine: la traslazione la scrive initFullImageParallax()
     su .full-image (--parallax-y), un punto solo per entrambi i piani. */
  transform: translate3d(0, var(--parallax-y, 0px), 0);
  will-change: transform;
  background:
    linear-gradient(
      to top,
      rgba(14, 14, 14, 0.82) 0%,
      rgba(14, 14, 14, 0.62) 45%,
      rgba(14, 14, 14, 0.5) 100%
    ),
    rgba(14, 14, 14, 0.28);
}
/* Senza parallasse (reduced motion) l'immagine torna a height:100% e
   ferma: il velo deve fare lo stesso, altrimenti resterebbe più alto
   della finestra e sfalsato. */
@media (prefers-reduced-motion: reduce) {
  .full-image-scrim {
    height: 100%;
    transform: none;
    will-change: auto;
  }
}

/* La cornice del testo: riempie la scheda e allinea la citazione in basso.
   display:flex + align-items:flex-end invece di un position:absolute con
   bottom fisso, così il blocco si adatta a qualunque altezza di scheda
   (nello stack è 100vh, su mobile 60vh). */
.full-image-quote {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  /* Ancorata in basso, ma sollevata dal bordo: il padding-bottom è il
     parametro con cui si alza o si abbassa la citazione nella scheda.
     Più alto = testo più in alto. Tenuto separato dal padding-top proprio
     perché è l'unico dei due che si regola. */
  align-items: flex-end;
  padding-top: clamp(2.5rem, 6vw, 5rem);
  padding-bottom: clamp(5rem, 14vw, 11rem);
  /* Il testo non deve intercettare il puntatore: sopra c'è solo una foto,
     non ci sono elementi interattivi da raggiungere, ma così la sezione
     resta "trasparente" a eventuali gesti di scroll sui touch device. */
  pointer-events: none;
}
.quote {
  position: relative;
  /* ATTENZIONE all'unità: `ch` è la larghezza dello "0" del font, e su
     Anton — che è una grottesca condensata — vale poco più di metà di
     quella di una sans normale. Una misura di 26ch, che altrove darebbe
     una riga larga, qui produceva una colonna stretta di sei righe
     addossata al bordo sinistro, con due terzi della fotografia vuoti.
     Serve una misura in percentuale della scheda: la citazione occupa
     poco più di metà larghezza e si spezza in tre righe. */
  max-width: 100%;
}
@media (min-width: 768px) {
  .quote {
    max-width: 62%;
  }
}
@media (min-width: 1200px) {
  .quote {
    max-width: 54%;
  }
}

/* La virgoletta d'apertura. Enorme e in giallo: è l'elemento "firma" della
   sezione. Sta fuori dal flusso e leggermente sopra e a sinistra del testo,
   così la frase gli scorre accanto invece di partire dopo.
   line-height:1 e un font display: il glifo di Anton ha un'occhiello molto
   compatto, con l'interlinea di default lascerebbe un vuoto enorme sotto. */
.quote-mark {
  position: absolute;
  top: clamp(-2.6rem, -5vw, -4rem);
  left: clamp(-0.6rem, -1.4vw, -1.2rem);
  font-family: var(--font-display);
  font-size: clamp(6rem, 14vw, 12rem);
  line-height: 1;
  color: var(--lemon);
  opacity: 0.85;
  /* Non deve entrare nel calcolo dell'altezza della citazione: è decoro. */
  pointer-events: none;
  user-select: none;
}

.quote-text {
  position: relative;
  z-index: 1;
}
.quote-text p {
  font-family: var(--font-head);
  font-weight: 800;
  font-size: clamp(1.85rem, 4.4vw, 3.9rem);
  line-height: 1.02;
  letter-spacing: 0.005em;
  /* text-transform: uppercase; */
  color: var(--paper);
  /* Ombra appena percettibile: non è un effetto, è la rete di sicurezza
     sul contrasto quando sotto capita una zona chiara della foto. */
  text-shadow: 0 2px 24px rgba(0, 0, 0, 0.45);
}

/* LA COMPARSA — una parola per volta.
   .quote-word fa da maschera (overflow:hidden), lo <span> interno è ciò
   che si muove: sale da sotto il proprio bordo. È la stessa meccanica di
   .reveal-line, applicata parola per parola invece che riga per riga.
   Il wrapping lo fa initQuoteReveal() in script.js: qui c'è solo lo stato.

   display:inline-flex e non inline-block: con inline-block il browser
   allinea la maschera sulla baseline e le lettere discendenti (p, g, q)
   venivano tagliate dall'overflow. Con flex + un filo di padding-bottom
   la maschera contiene tutta l'altezza reale del glifo. */
.quote-word {
  display: inline-flex;
  overflow: hidden;
  vertical-align: bottom;
  padding-bottom: 0.08em;
}
.quote-word > span {
  display: inline-block;
  transform: translateY(110%);
  opacity: 0;
  transition:
    transform 0.9s var(--ease),
    opacity 0.9s var(--ease);
  /* Il ritardo di ogni parola arriva inline da JS: --word-delay. */
  transition-delay: var(--word-delay, 0ms);
}
.quote-text.is-visible .quote-word > span {
  transform: translateY(0);
  opacity: 1;
}

/* L'autore entra dopo l'ultima parola: il delay lo calcola JS in base a
   quante parole ha la frase, così resta giusto anche cambiando il testo. */
/* Non usa .reveal: quella classe ha un proprio IntersectionObserver che
   riscrive transitionDelay inline, cancellando l'attesa calcolata da
   initQuoteReveal() sulla lunghezza della frase. Stato e transizione
   sono quindi dichiarati qui, e li accende .is-visible messo dal JS
   della citazione. */
.quote-author {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  opacity: 0;
  transform: translateY(18px);
  transition:
    opacity 0.7s var(--ease),
    transform 0.7s var(--ease);
  margin-top: clamp(1.1rem, 2.4vw, 1.8rem);
  font-family: var(--font-head);
  font-size: clamp(0.82rem, 1.5vw, 1rem);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--paper);
}
/* Il trattino giallo che precede il nome: cresce da zero insieme al testo. */
.quote-author-rule {
  display: block;
  width: clamp(2rem, 5vw, 3.5rem);
  height: 2px;
  background: var(--lemon);
  transform-origin: left center;
  transform: scaleX(0);
  transition: transform 0.8s var(--ease);
  /* inherit e non un valore fisso: il ritardo dell'autore è calcolato da
     JS sulla lunghezza della frase e scritto inline sul <figcaption>.
     Ereditarlo tiene il trattino in tempo con il nome anche cambiando la
     citazione. */
  transition-delay: inherit;
}
.quote-author.is-visible {
  opacity: 1;
  transform: none;
}
.quote-author.is-visible .quote-author-rule {
  transform: scaleX(1);
}

/* Con animazioni ridotte la citazione è già al suo posto: nessuna
   traslazione, nessuna cascata, solo il testo leggibile. */
@media (prefers-reduced-motion: reduce) {
  .quote-word > span {
    transform: none;
    opacity: 1;
    transition: none;
  }
  .quote-author,
  .quote-author-rule {
    opacity: 1;
    transform: none;
    transition: none;
  }
  .quote-author-rule {
    transform: scaleX(1);
  }
}

/* Sotto i 768px la scheda è alta 60vh: la citazione si stringe e la
   virgoletta rientra, altrimenti uscirebbe dal bordo sinistro. */
@media (max-width: 767.98px) {
  .quote {
    max-width: 100%;
  }
  .quote-mark {
    top: clamp(-2rem, -7vw, -2.6rem);
    left: -0.2rem;
  }
}

/* ---------- REFERENZE FULL-BLEED (variante di .portfolio-showcase) ----------
   Stessa sorgente dati dello showcase a stack, ma qui l'immagine riempie
   la scheda e i testi vivono in una card in overlay. Nessun titolo di
   sezione: la scheda è tutta immagine, il testo sta solo nella card. */
.references-full {
  /* Come .full-image: niente padding verticale di sezione, l'immagine
     deve arrivare ai bordi della scheda. Il fondo scuro copre l'istante
     prima che la foto (lazy) sia decodificata. */
  padding-block: 0;
  background: var(--ink);
  overflow: hidden;
}
.references-full .stack-inner {
  /* Fuori dallo stack (sotto i 768px) il padding generico rientrerebbe
     l'immagine; dentro lo stack serve il selettore più forte in fondo
     al file. position:relative fa da riferimento sia al media in
     absolute sia all'overlay. */
  padding: 0;
  position: relative;
  overflow: hidden;
}
.references-full-media {
  position: absolute;
  inset: 0;
  /* Ritaglia il micro-zoom: le immagini non attive stanno a scale(1.06)
     e sborderebbero di ~27px sotto la scheda. Non è solo estetica —
     initStackReveal misura inner.scrollHeight, quindi quello sbordo
     veniva letto come overflow reale e alla sezione veniva riservato
     uno spacer di scroll da 27px che non serve a nulla. */
  overflow: hidden;
  /* Le immagini sono impilate in absolute e si dissolvono l'una
     sull'altra: senza un contenitore proprio finirebbero sotto
     l'overlay nel flusso. */
}
.references-full-media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  opacity: 0;
  /* Micro-zoom in uscita: l'immagine attiva torna a scale(1), le altre
     restano leggermente ingrandite, così il cambio non è un taglio
     secco ma un movimento. */
  transform: scale(1.06);
  transition: opacity .7s var(--ease), transform 1.2s var(--ease);
}
.references-full-media img.is-active {
  opacity: 1;
  transform: scale(1);
}
/* Velo scuro: le foto di portfolio sono molto varie e senza un velo il
   testo bianco della card non raggiungerebbe il contrasto AA su quelle
   chiare. Gradiente e non tinta piatta: più denso dove sta la card. */
.references-full-overlay {
  /* absolute e non relative: initStackReveal (script.js) decide se una
     scheda "eccede" misurando inner.scrollHeight. Un overlay nel flusso
     alto quanto il viewport PIÙ il proprio padding verticale supera di
     poco l'altezza della scheda e fa scattare .has-overflow, che
     rimette il padding 96/216px e sposta la card a metà scheda.
     Togliendolo dal flusso (come il media) scrollHeight resta pari al
     viewport e la sezione è trattata per quello che è: una scheda piena
     senza scroll interno. */
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  align-items: flex-end;
  padding-block: clamp(2rem, 5vw, 4rem);
  background: linear-gradient(
    to top,
    rgba(0, 0, 0, .82) 0%,
    rgba(0, 0, 0, .55) 38%,
    rgba(0, 0, 0, .15) 70%,
    rgba(0, 0, 0, .05) 100%
  );
}
.references-full-overlay > .container {
  width: 100%;
}
.references-full-card {
  max-width: 620px;
  background: rgba(14, 14, 14, .72);
  /* Glass: il velo del gradiente da solo non basta a staccare la card
     dalla foto quando sotto c'è un soggetto molto dettagliato. */
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: clamp(1.5rem, 3vw, 2.25rem);
  color: var(--paper);
  box-shadow: 0 30px 60px -30px rgba(0, 0, 0, .8);
}
.references-full-category {
  font-family: var(--font-head);
  font-size: .78rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--lemon);
  margin: 0 0 .6rem;
}
.references-full-title {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 3vw, 2.25rem);
  line-height: 1.08;
  text-transform: uppercase;
  margin: 0 0 .8rem;
  color: var(--paper);
}
.references-full-desc {
  font-size: clamp(.95rem, 1.1vw, 1.05rem);
  line-height: 1.6;
  color: rgba(255, 255, 255, .82);
  margin: 0;
}
/* Rivelazione parola per parola, come .portfolio-showcase-desc: gli
   span li crea il JS, qui c'è solo lo stato di partenza e di arrivo. */
.references-full-desc .word {
  display: inline-block;
  /* Lo spazio finale che il JS mette dentro ogni span verrebbe
     collassato da inline-block e le parole si attaccherebbero fra loro
     ("Dallascomposizione..."). pre-wrap lo preserva lasciando comunque
     andare a capo il testo. */
  white-space: pre-wrap;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity .5s var(--ease), transform .5s var(--ease);
}
.references-full-desc.is-revealed .word {
  opacity: 1;
  transform: none;
}
.references-full-nav {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-top: 1.6rem;
}
/* Su fondo scuro pieno: basta il contorno, senza velo. */
.references-full-btn {
  border-color: var(--line);
  background: transparent;
  color: var(--paper);
}
.references-full-count {
  font-family: var(--font-head);
  font-size: .85rem;
  letter-spacing: .08em;
  color: rgba(255, 255, 255, .7);
  margin: 0;
  font-variant-numeric: tabular-nums;
}
@media (prefers-reduced-motion: reduce) {
  .references-full-media img {
    transition: opacity .2s linear;
    transform: none;
  }
  .references-full-media img.is-active { transform: none; }
  .references-full-desc .word {
    opacity: 1;
    transform: none;
    transition: none;
  }
}
/* Fuori dallo stack (mobile) la sezione non ha height:100vh: senza
   un'altezza esplicita il wrapper collasserebbe sull'overlay, perché
   l'immagine è in position:absolute e non occupa spazio nel flusso. */
@media (max-width: 767.98px) {
  .references-full .stack-inner {
    min-height: 78vh;
  }
  .references-full-card {
    max-width: none;
  }
}

/* ---------- EXPANDING CARDS (referenze a fisarmonica) ----------
   Porting vanilla del componente React "ExpandingCards": la card attiva
   prende 5fr, le altre 1fr, e la transizione sta su
   grid-template-columns/rows. Il JS scrive solo le due custom property
   --ec-cols/--ec-rows, tutto il resto è CSS. */
.expanding-cards {
  list-style: none;
  margin: 0 auto;
  padding: 0;
  width: 100%;
  max-width: 1152px;
  display: grid;
  gap: .5rem;
  /* MOBILE: colonna unica, l'espansione avviene in verticale (righe).
     È il ramo "else" del componente originale. */
  grid-template-columns: 1fr;
  grid-template-rows: var(--ec-rows, 1fr);
  /* 9 progetti in colonna: con 600px le card chiuse restavano strisce da
     41px (illeggibili) e quella aperta 206px non conteneva icona,
     titolo e descrizione, che sbordavano. Servono ~64px per ogni card
     chiusa più ~330px per quella aperta. */
  height: 860px;
  transition: grid-template-columns .5s var(--ease), grid-template-rows .5s var(--ease);
}
/* La soglia è 992px e non i 768px del componente originale: lì le card
   erano 7, qui sono 9. A 768px servirebbero 9x80px di min-width più i
   gap (~784px), cioè più della finestra stessa: le card sbordavano a
   destra e quella "aperta" restava larga 80px come le chiuse, con la
   descrizione strizzata a una parola per riga. Sotto i 992px resta
   quindi il layout verticale, che con 9 voci è anche più leggibile. */
@media (min-width: 992px) {
  .expanding-cards {
    /* DESKTOP: riga unica, l'espansione avviene in orizzontale. */
    grid-template-columns: var(--ec-cols, 1fr);
    grid-template-rows: 1fr;
    height: 500px;
  }
}
@media (prefers-reduced-motion: reduce) {
  .expanding-cards { transition: none; }
}

.expanding-card {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--ink-card);
  cursor: pointer;
  /* Senza questi due, una traccia 1fr non riesce a scendere sotto la
     dimensione del proprio contenuto e l'effetto fisarmonica si blocca. */
  min-width: 0;
  min-height: 0;
}
@media (min-width: 992px) {
  /* Sotto gli 80px la card chiusa non ospiterebbe più il titolo
     verticale, che verrebbe tagliato. */
  .expanding-card { min-width: 80px; }
}
.expanding-card:focus-visible {
  outline: 2px solid var(--lemon);
  outline-offset: 3px;
}
.expanding-card-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Card chiusa: desaturata e leggermente ingrandita. Aprendosi torna a
     colori pieni e scala 1 — è il "grayscale + scale-110" del
     componente originale. */
  filter: grayscale(1);
  transform: scale(1.1);
  transition: filter .3s var(--ease), transform .3s var(--ease);
}
.expanding-card[data-active="true"] .expanding-card-img {
  filter: grayscale(0);
  transform: scale(1);
}
@media (prefers-reduced-motion: reduce) {
  .expanding-card-img { transition: none; }
}
/* Velo scuro: il testo bianco deve restare leggibile su qualunque foto. */
.expanding-card-veil {
  position: absolute;
  inset: 0;
  background: linear-gradient(to top,
    rgba(0, 0, 0, .85) 0%,
    rgba(0, 0, 0, .45) 45%,
    rgba(0, 0, 0, .05) 100%);
}
.expanding-card-body {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: .5rem;
  padding: 1.25rem;
}
/* Titolo verticale della card CHIUSA (solo desktop): ruotato di 90°,
   sparisce quando la card si apre. */
.expanding-card-vtitle {
  display: none;
  font-family: var(--font-head);
  font-size: .82rem;
  font-weight: 400;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, .85);
  margin: 0;
  transition: opacity .3s var(--ease);
}
@media (min-width: 992px) {
  .expanding-card-vtitle {
    display: block;
    /* rotate(90deg) da solo ruoterebbe attorno al centro, spingendo il
       testo fuori dalla card stretta: origin a sinistra + traslazione lo
       tiene ancorato in basso a sinistra come nell'originale. */
    transform-origin: left bottom;
    transform: rotate(-90deg) translateX(0);
    position: absolute;
    bottom: 1.25rem;
    left: 1.25rem;
    white-space: nowrap;
  }
}
.expanding-card[data-active="true"] .expanding-card-vtitle {
  opacity: 0;
  pointer-events: none;
}
/* Mobile: il titolo verticale non esiste (serve una card alta e stretta,
   qui sono basse e larghe). Senza, le card chiuse sarebbero strisce
   anonime: la categoria torna orizzontale, in basso a sinistra. */
@media (max-width: 991.98px) {
  .expanding-card-vtitle {
    display: block;
    position: absolute;
    left: 1.25rem;
    bottom: 1rem;
    margin: 0;
    font-size: .72rem;
  }
}
/* Contenuto della card APERTA: icona, titolo e descrizione entrano in
   sequenza (delay crescenti, come nel componente originale). */
.expanding-card-icon,
.expanding-card-title,
.expanding-card-desc {
  opacity: 0;
  transition: opacity .3s var(--ease);
}
.expanding-card-icon { color: var(--lemon); font-size: 1.4rem; line-height: 1; }
.expanding-card-title {
  font-family: var(--font-display);
  font-size: clamp(1.45rem, 1.6vw, 2rem);
  line-height: 1.1;
  text-transform: uppercase;
  color: var(--paper);
  margin: 0;
}
.expanding-card-desc {
  font-size: .9rem;
  line-height: 1.5;
  color: rgba(255, 255, 255, .85);
  margin: 0;
  max-width: 34ch;
}
@media (max-width: 991.98px) {
  .expanding-card-body { padding: 1rem; gap: .35rem; }
  .expanding-card-desc { font-size: .82rem; line-height: 1.45; }
}
.expanding-card[data-active="true"] .expanding-card-icon { opacity: 1; transition-delay: .075s; }
.expanding-card[data-active="true"] .expanding-card-title { opacity: 1; transition-delay: .15s; }
.expanding-card[data-active="true"] .expanding-card-desc { opacity: 1; transition-delay: .225s; }
/* Su card chiusa il testo non deve restare raggiungibile dallo screen
   reader come contenuto visibile né catturare il puntatore. */
.expanding-card:not([data-active="true"]) .expanding-card-icon,
.expanding-card:not([data-active="true"]) .expanding-card-title,
.expanding-card:not([data-active="true"]) .expanding-card-desc {
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .expanding-card-icon,
  .expanding-card-title,
  .expanding-card-desc { transition: none; }
}

/* ---------- EXPANDING CARDS — variante su fondo chiaro ----------
   Cambia solo la "cornice" della sezione: fondo, titolo e lead li dà
   già .section-light. Qui si aggiustano bordo e ombra delle card, che
   sul chiaro hanno bisogno di essere staccate dallo sfondo.
   NON si tocca il testo dentro le card: sta sopra le fotografie, quindi
   resta bianco su velo scuro in entrambe le varianti — invertirlo lo
   renderebbe illeggibile sulle foto chiare. */
.expanding-cards--light .expanding-card {
  border-color: var(--line-dark);
  background: var(--paper);
  /* Su fondo chiaro il bordo sottile non basta a separare le card fra
     loro: l'ombra dà la profondità che sul nero arrivava dal contrasto. */
  box-shadow: 0 10px 30px -18px rgba(0, 0, 0, .45);
}
.expanding-cards--light .expanding-card:focus-visible {
  /* Il giallo del brand su fondo chiaro ha poco contrasto: per l'anello
     di focus serve l'inchiostro, che resta ben visibile sia sulla
     sezione chiara sia sopra la foto della card. */
  outline-color: var(--ink);
}
/* Categoria della card CHIUSA: nella variante scura è bianca sopra la
   foto desaturata; qui la card chiusa è comunque una foto scura sotto
   velo, quindi il bianco resta corretto. Nessun override. */

/* ---------- CASE STUDIES ---------- */
/* Ogni .case-study è una .stack-section (scheda a piena altezza che
   scorre sopra la precedente, vedi blocco STACK SCROLL più sotto):
   .stack-inner resta il wrapper generico (flex-center, vedi quel
   blocco), il layout a griglia (immagine + testo) vive un livello più
   sotto, su .case-study-inner, esattamente come .container nelle
   altre stack-section. */
.case-study {
  background: var(--paper);
  /* Il coverflow dentro .case-study-media ha overflow:visible per
     lasciar sporgere le card ruotate: .case-study-inner e' gia'
     protetta con minmax(0,1fr) nel calcolo della griglia, ma la
     sporgenza resta comunque disegnata e su viewport stretti finiva
     oltre il bordo destro, dando scroll orizzontale all'intera pagina.
     Il ritaglio va qui, sulla sezione, non sul media: sul media
     taglierebbe l'effetto stesso. */
  overflow-x: clip;
}
.case-study-inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr); /* minmax(0, …), non 1fr nudo:
    le colonne grid hanno min-width:auto implicito, cioè si allargano
    fino al contenuto "minimo" della cella — con .case-study-media che
    ha overflow:visible (le card del carosello sporgono oltre i propri
    bordi), quel contenuto include la sporgenza, e la colonna si
    allargava oltre lo spazio nominale spingendo tutta la sezione fuori
    dal viewport su schermi stretti. minmax(0, 1fr) forza il minimo a
    0, ignorando il contenuto traboccante nel calcolo della colonna. */
  align-items: center;
  gap: 2.5rem;
  max-width: var(--maxw);
  margin-inline: auto;
  /* Il padding verticale scala con l'ALTEZZA del viewport (vh), non con
     la larghezza. Prima era clamp(3.5rem, 8vw, 6rem): su una finestra
     larga ma bassa (es. 1280x800, o un portatile con molte barre) 8vw
     si risolveva comunque al massimo, aggiungendo ~12rem verticali a una
     scheda che ne aveva già pochi da spendere. Risultato: la scheda
     diventava più alta del viewport, prendeva .has-overflow e con essa
     uno spacer + uno scroll-reveal interno — ed è quello a sfasare la
     sovrapposizione da lì in poi (vedi la nota su OVERFLOW_EPSILON in
     script.js). Con vh il respiro si riduce da solo quando lo spazio
     verticale manca, che è esattamente quando serve. */
  padding-block: clamp(1.5rem, 4vh, 4rem);
  padding-inline: var(--pad); /* stesso margine laterale di .container
    (hero, CTA finale, header) — senza, la griglia immagine+testo
    toccava i bordi della finestra invece di allinearsi al resto
    della pagina. */
}
.case-study-media {
  position: relative;
  border-radius: var(--radius);
  overflow: hidden;
  /* Formato 3:4 verticale, LO STESSO del coverflow (.has-carousel
     sotto): i case study sotto la soglia mostrano un media singolo
     fisso (vedi initCaseStudyCarousels) e devono avere la stessa
     proporzione di quelli col carosello, altrimenti scorrendo la
     pagina il riquadro cambia forma da una sezione all'altra.
     È la LARGHEZZA a dimensionare il box e l'aspect-ratio a derivarne
     l'altezza — non il contrario. Dimensionare per altezza (height in
     vh + width:auto) crea una dipendenza circolare dentro
     .case-study-inner, che è una griglia con align-items:center: la
     colonna viene misurata prima che l'altezza sia risolta, e Chrome
     la chiude diversamente da Safari collassando il riquadro (il
     coverflow si appiattiva in una striscia orizzontale). Con la
     larghezza come sorgente il calcolo è lineare e identico sui due
     browser.
     max-height limita comunque il box sulle finestre basse: la scheda
     sticky è alta 100vh con overflow:hidden, quindi un 3:4 pieno
     verrebbe tagliato. Lì è la larghezza a cedere, mantenendo la
     proporzione. */
  aspect-ratio: 4 / 5;
  width: min(100%, calc(58vh * 4 / 5));
  max-height: 58vh;
  margin-inline: auto;
  background: var(--paper-dim);
  box-shadow: 0 30px 60px -30px rgba(0, 0, 0, 0.25);
}
/* Con più immagini (coverflow, .case-cover-img sotto) il contenitore
   NON deve più tagliare i bordi: le immagini laterali sono spostate,
   scalate e ruotate oltre i confini del riquadro — overflow:hidden qui
   le clipperebbe rendendo l'effetto invisibile. Il border-radius si
   sposta sulla singola .case-cover-img (ognuna arrotondata per sé). */
.case-study-media.has-carousel {
  /* Proporzione e dimensioni sono ora nella regola base sopra (le
     condivide con il media singolo): qui restano solo le differenze
     vere del coverflow, cioè il riquadro che smette di essere un
     contenitore visibile e lascia sporgere le card laterali —
     arrotondamento, ombra e fondo passano alle singole .case-cover-img. */
  overflow: visible;
  border-radius: 0;
  box-shadow: none;
  background: transparent;
}
/* Le card in stack (fino a ±10° di rotazione su un box quadrato)
   possono sporgere di ~L×0.17 per lato oltre i bordi del riquadro:
   senza margine di sicurezza invadono la colonna di testo accanto o,
   su viewport stretti, escono dai bordi della pagina. Il margine va
   sulla card stessa (non sul contenitore .has-carousel, che
   sposterebbe l'intera cella della griglia rompendo l'allineamento
   con .case-study-content): .is-active ha inset:0 puro (combacia
   sempre con i bordi originali del riquadro, zero spostamento),
   .is-stacked si restringe verso l'interno prima di ruotare/scalare,
   così anche il punto più sporgente resta dentro un margine sicuro. */
/* NIENTE inset qui (a differenza di un primo tentativo): restringere il
   box PRIMA di applicare rotate sposta anche il transform-origin
   (bottom center) verso l'interno, "schiacciando" l'effetto ventaglio
   che rende visibile l'angolo della card dietro — esattamente il
   comportamento dell'originale .portfolio-card in home, che infatti
   NON ha inset e lascia le immagini sporgere alla loro piena
   dimensione: il coverflow (.case-cover-img) sposta le laterali fuori
   dal riquadro per costruzione. */
/* :not(.case-cover-img) — questa è la regola della SINGOLA immagine
   (zoom lento all'ingresso in vista). Senza l'esclusione colpiva anche
   le immagini del coverflow: stessa specificità ma dichiarata dopo,
   quindi vinceva la cascata e imponeva loro la propria transizione
   (1.1s, curva --ease) al posto di quella elastica del componente
   (0.8s, cubic-bezier(.4,2,.3,1)) — ecco perché l'effetto non
   "rimbalzava" — oltre a sovrascriverne il transform con scale(1.12). */
.case-study-media img:not(.case-cover-img) {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.12);
  filter: saturate(0.92);
  transition:
    transform 1.1s var(--ease),
    filter 1.1s var(--ease);
}
.case-study-media.is-visible img:not(.case-cover-img) {
  transform: scale(1);
  filter: saturate(1);
}

/* Coverflow multi-immagine dentro .case-study-media (popolato via JS,
   vedi initCaseStudyCarousels in script.js). Porting del componente
   React/framer-motion: tre immagini visibili — attiva al centro,
   precedente a sinistra e successiva a destra, scalate a 0.85,
   sollevate e ruotate di ±15° sull'asse Y. Le posizioni sono calcolate
   in JS (dipendono dalla larghezza del riquadro) e applicate come
   transform inline; qui restano prospettiva, transizione e aspetto.
   Con una sola immagine il JS applica solo lo stato attivo: il riquadro
   resta identico a un case study senza carosello, nessuna freccia. */
.case-cover {
  position: absolute;
  inset: 0;
}
/* NESSUNA `perspective` sul contenitore — è stata rimossa.
   La proprietà apre un contesto di rendering 3D permanente su TUTTE le
   .case-cover che la ricevono, indipendentemente dal fatto che una
   rotazione sia in corso. Con 17 schede sticky (ognuna con
   overflow:hidden) quei contesti annidati saturano il compositing di
   Chrome: le schede oltre una certa posizione smettono di comporsi.
   Limitarla alle sole .has-carousel aveva spostato la soglia dall'8ª
   alla 9ª scheda — il carico era ridotto ma non eliminato.
   La prospettiva ora viaggia DENTRO il transform delle singole immagini
   laterali, come primo termine: `perspective(1000px) rotateY(±15deg)`
   (vedi render() in script.js). Lo scorcio visivo è identico, ma il
   contesto 3D esiste solo sull'elemento realmente ruotato e solo mentre
   la rotazione è applicata — le due card laterali per carosello, invece
   di un contesto permanente per scheda.
   Resta valido non usare transform-style:preserve-3d: ordinerebbe gli
   elementi per profondità reale ignorando z-index, e il bordo vicino
   delle laterali ruotate finirebbe davanti alla card attiva. */
.case-cover-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: var(--radius); /* il genitore .has-carousel non taglia
    più i bordi (deve lasciar sporgere le laterali) — ogni immagine si
    arrotonda per conto suo. */
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
  opacity: 0;
  cursor: pointer;
  /* Identica al componente originale: `all 0.8s cubic-bezier(.4,2,.3,1)`.
     Il punto di controllo a 2 fa superare il valore finale e poi
     rientrare — è da lì che nasce il rimbalzo elastico della card che
     entra al centro. */
  transition: all 0.8s cubic-bezier(0.4, 2, 0.3, 1);
}
/* ---------- STACK A VENTAGLIO (ex coverflow) ----------
   Le card dei case study usano ora lo stesso linguaggio visivo dello
   stack "Dalle start-up alle multinazionali" che stava in home
   (.portfolio-card): tutte le foto restano impilate una dietro l'altra,
   ciascuna con la propria rotazione, e l'attiva viene in primo piano
   dritta e a scala piena. Prima erano visibili solo tre card (attiva +
   due laterali) posizionate con transform inline dal JS; qui rotazione
   e stato passano da --portfolio-rot e dalle classi
   .is-stacked / .is-active, esattamente come nel componente originale. */
.case-cover-img {
  transform-origin: bottom center;
  rotate: var(--portfolio-rot, 0deg);
  scale: 0.9;
}
.case-cover-img.is-stacked {
  opacity: 0.7;
  scale: 0.95;
  cursor: pointer;
}
.case-cover-img.is-active {
  opacity: 1;
  scale: 1;
  rotate: 0deg;
  /* l'immagine centrale non è cliccabile per "andare al centro": ci è già */
  cursor: default;
}
/* Rimbalzo della card che entra al centro: riusa gli stessi keyframes
   del componente originale (portfolio-lift, definito piu' sopra per
   .portfolio-card). La classe la mette e la toglie il JS a ogni
   cambio di card attiva. */
.case-cover-img.is-active.is-lifting {
  animation: portfolio-lift 0.4s ease-in-out;
}
@media (prefers-reduced-motion: reduce) {
  .case-cover-img.is-active.is-lifting {
    animation: none;
  }
}
/* fade-in iniziale delle foto quando la sezione entra in vista
   (.is-visible arriva da initCaseStudyMedia) */
.case-study-media:not(.is-visible) .case-cover-img {
  filter: saturate(0.92);
}
.case-study-media.is-visible .case-cover-img {
  filter: saturate(1);
}
@media (prefers-reduced-motion: reduce) {
  .case-cover-img {
    transition: opacity 0.3s linear;
  }
}

/* ---------- COPERTINA VIDEO nel case study (item con type:'video') ----
   La copertina è avvolta in un <div class="case-cover-img case-cover-video">
   così eredita posizionamento, raggio e trasformazioni del coverflow
   dalle card immagine; il wrapper serve a ospitare il pulsante play.
   Il video vero non sta qui: il click apre la lightbox (vedi più sotto),
   quindi .case-cover-videoel è un <img> normale, non un <video>. */
.case-cover-video {
  overflow: hidden; /* la copertina non ha il border-radius del wrapper:
    senza questo, gli angoli resterebbero quadrati sopra la card */
}
.case-cover-videoel {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* Pulsante play: apre la lightbox con il video (vedi .video-lightbox). */
.case-cover-play {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: rgba(0, 0, 0, 0.28);
  color: var(--ink);
  cursor: pointer;
  transition:
    background 0.3s var(--ease),
    opacity 0.3s var(--ease);
}
.case-cover-play::before {
  /* pastiglia gialla dietro il triangolo: il play deve leggersi anche
     sui fotogrammi chiari, dove un'icona bianca sparirebbe */
  content: "";
  position: absolute;
  width: clamp(54px, 18%, 76px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--lemon);
  box-shadow: 0 6px 22px rgba(0, 0, 0, 0.3);
  transition: transform 0.3s var(--ease);
}
.case-cover-play svg {
  position: relative; /* sopra la pastiglia */
  width: clamp(22px, 7%, 30px);
  height: auto;
  margin-left: 6%; /* il triangolo ha il baricentro spostato a sinistra:
    centrarlo otticamente richiede questo scarto */
  max-width: 30px;
}
.case-cover-play:hover,
.case-cover-play:focus-visible {
  background: rgba(0, 0, 0, 0.18);
}
.case-cover-play:hover::before,
.case-cover-play:focus-visible::before {
  transform: scale(1.08);
}
@media (prefers-reduced-motion: reduce) {
  .case-cover-play,
  .case-cover-play::before {
    transition: none;
  }
}

/* ---------- LIGHTBOX VIDEO ----------
   Apre a schermo intero il video collegato a una copertina video del
   coverflow (vedi openVideoLightbox in script.js). Un solo overlay,
   creato una volta e riusato da tutti i caroselli della pagina. */
.video-lightbox {
  position: fixed;
  inset: 0;
  z-index: 1200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 5vh 5vw;
  background: rgba(6, 6, 6, 0.92);
  opacity: 0;
  transition: opacity 0.3s var(--ease);
}
.video-lightbox:not(.is-open) {
  pointer-events: none;
}
.video-lightbox.is-open {
  opacity: 1;
}
.video-lightbox[hidden] {
  display: none; /* [hidden] da solo perderebbe contro .video-lightbox{display:flex}
    a parità di specificità: qui la coppia attributo+classe vince e la
    toglie davvero dall'albero di accessibilità a chiusura avvenuta. */
}
.video-lightbox-inner {
  position: relative;
  width: min(1100px, 100%);
}
.video-lightbox-video {
  display: block;
  width: 100%;
  max-height: 88vh;
  border-radius: var(--radius);
  background: #000;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
}
.video-lightbox-close {
  position: fixed;
  top: max(20px, env(safe-area-inset-top));
  right: max(20px, env(safe-area-inset-right));
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
  cursor: pointer;
  transition: background 0.3s var(--ease);
}
.video-lightbox-close:hover,
.video-lightbox-close:focus-visible {
  background: rgba(255, 255, 255, 0.22);
}
@media (prefers-reduced-motion: reduce) {
  .video-lightbox,
  .video-lightbox-close {
    transition: none;
  }
}

/* Didascalia sincronizzata con l'immagine attiva + contatore: è la
   colonna di testo del componente originale (lì nome/ruolo/citazione).
   Le parole entrano una a una sfocate, come le varianti per-parola
   di framer-motion. */
.case-cover-captionwrap {
  display: flex;
  align-items: baseline;
  gap: 0.9rem;
  margin-bottom: 1.6rem;
  min-height: 1.6em; /* riserva la riga: senza, il testo che cambia
    lunghezza farebbe saltare in verticale tutto ciò che sta sotto */
}
.case-cover-caption {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.5;
  color: rgba(0, 0, 0, 0.6);
  max-width: 44ch;
}
.case-cover-counter {
  flex: 0 0 auto;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  color: var(--ash-dark);
  font-variant-numeric: tabular-nums;
}
.case-cover-word {
  display: inline-block;
  animation: case-word-in 0.22s ease-in-out both;
}
@keyframes case-word-in {
  from {
    opacity: 0;
    filter: blur(10px);
    transform: translateY(5px);
  }
  to {
    opacity: 1;
    filter: blur(0);
    transform: translateY(0);
  }
}
@media (prefers-reduced-motion: reduce) {
  .case-cover-word {
    animation: none;
  }
}

/* Frecce nella colonna di testo (posizione del componente originale)
   invece che sovrapposte alla foto. */
.case-study-carousel-nav--inline {
  position: static;
  margin-top: 0.4rem;
}
.case-study-carousel-nav {
  position: absolute;
  right: 1rem;
  bottom: 1rem;
  z-index: 10; /* sopra le immagini del coverflow (z-index max 3 via
    JS), ma ben sotto l'header fisso (z-index:100) */
  display: flex;
  gap: 0.6rem;
}
/* Geometria, transizione e hover dalla base condivisa. */
.case-study-carousel-btn {
  border-color: rgba(255, 255, 255, 0.4);
  /* Fondo opaco, NIENTE backdrop-filter (c'era: blur(6px)).
     backdrop-filter promuove sempre l'elemento a layer composito GPU
     ("BackdropFilter" tra i compositing reasons di Chrome), e in questa
     pagina i bottoni sono 2 per carosello: layer che a loro volta
     costringono ogni .stack-section sovrapposta a essere promossa
     ("Overlap"), facendo esplodere il numero di layer (vedi la nota
     sullo stack in fondo al file). Le frecce stanno sopra la colonna di
     testo, su fondo pieno: la sfocatura del retro non si vedeva
     comunque. Un colore opaco dà lo stesso risultato visivo a costo
     zero. */
  background: rgb(26, 26, 26);
  color: var(--paper);
}

.case-study-content {
  position: relative;
}
.case-study-num {
  display: block;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(3rem, 7vw, 4.5rem);
  line-height: 1;
  color: transparent;
  -webkit-text-stroke: 1.5px rgba(0, 0, 0, 0.14);
  margin-bottom: 0.5rem;
}
.case-study-category {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.8rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--lemon-deep); 
  margin-bottom: 0.4rem;
}
.case-study-title {
  font-family: var(--font-head);
  font-weight: 900;
  line-height: 1.02;
  font-size: 3.2rem;
  letter-spacing: -0.01em;
  margin-bottom: 1.3rem;
  max-width: 16ch;
  color: var(--ink-card);
}
.case-study-desc {
  max-width: 48ch;
  color: rgba(0, 0, 0, 0.68);
  font-size: 1.02rem;
  margin-bottom: 2rem;
}
.case-study-logo-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--ink);
  border-radius: 10px;
  padding: 0.9rem 1.3rem;
  height: 56px;
  box-sizing: border-box;
}
.case-study-logo {
  display: block;
  height: 100px;
  width: auto;
  max-width: 160px;
  object-fit: contain;
  /* Spazio riservato anche quando il file manca. Un <img> rotto perde
     le dimensioni intrinseche: con width:auto Chrome lo collassa, e
     poiché questi loghi sono loading="lazy" il collasso avviene DURANTE
     lo scroll, proprio mentre lo stack è agganciato. Ogni collasso
     cambia lo scrollHeight di .stack-inner, il ResizeObserver di
     initStackReveal rimisura, e gli spacer di tutte le sezioni vengono
     ricalcolati a metà transizione: da lì la sovrapposizione delle
     schede va fuori sincrono. Con min-width/min-height il box resta
     della stessa dimensione a prescindere dall'esito del caricamento. */
  min-height: 100px;
  min-width: 60px;
}

/* ---------- Case study: fondo alternato chiaro / bianco ----------
   Le schede alternano due toni chiari molto vicini: il grigio caldo di
   --paper-dim e il bianco pieno di --paper. Lo stacco e' volutamente
   minimo — serve solo a far percepire il confine tra una scheda e la
   successiva mentre scorrono sovrapposte, senza spezzare la lettura
   come faceva la sequenza colorata.
   Come prima, ogni variante imposta quattro custom property locali e i
   figli leggono solo quelle: aggiungerne una in futuro vuol dire
   scrivere un blocco di poche righe, non ritoccare tutte le regole. */
.case-study--light {
  --cs-bg: var(--paper-dim);
  --cs-fg: var(--ink-card);
  --cs-fg-soft: rgba(0, 0, 0, 0.68);
  /* Non il giallo: --lemon-deep su fondo chiaro da' 1.48:1, sotto
     qualunque soglia leggibile. La categoria e' testo piccolo (0.8rem)
     e maiuscolo, quindi serve il 4.5:1 pieno: nero morbido. */
  --cs-accent: rgba(0, 0, 0, 0.62);
  --cs-num: rgba(0, 0, 0, 0.14);
}
.case-study--paper {
  --cs-bg: var(--paper);
  --cs-fg: var(--ink-card);
  --cs-fg-soft: rgba(0, 0, 0, 0.68);
  --cs-accent: rgba(0, 0, 0, 0.62);
  /* Sul bianco pieno il numero in filigrana ha bisogno di un filo di
     tratto in piu' per restare percepibile quanto sul grigio. */
  --cs-num: rgba(0, 0, 0, 0.16);
}

.case-study--light,
.case-study--paper {
  background: var(--cs-bg);
  color: var(--cs-fg);
}
.case-study--light .case-study-title,
.case-study--paper .case-study-title {
  color: var(--cs-fg);
}
.case-study--light .case-study-desc,
.case-study--paper .case-study-desc {
  color: var(--cs-fg-soft);
}
.case-study--light .case-study-category,
.case-study--paper .case-study-category {
  color: var(--cs-accent);
}
.case-study--light .case-study-num,
.case-study--paper .case-study-num {
  -webkit-text-stroke-color: var(--cs-num);
}


@media (min-width: 992px) {
  .case-study-inner {
    /* minmax(0, …), non Nfr nudo: stesso motivo della regola base
       sopra — evita che .case-study-media (overflow:visible per il
       carosello) allarghi la propria colonna oltre lo spazio nominale. */
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
    gap: 3vw;
  }
  .case-study-content {
    /* padding-inline: clamp(1rem, 3vw, 3rem); */
  }
  .case-study--reverse .case-study-inner {
    grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr);
  }
  .case-study--reverse .case-study-media {
    order: 2;
  }
  .case-study--reverse .case-study-content {
    order: 1;
  }
}
@media (prefers-reduced-motion: reduce) {
  .case-study-media img:not(.case-cover-img) {
    transition: none;
    transform: none;
  }
}

/* =========================================================
   SERVICE PAGES — varianti distintive (hero icona, tile con
   icona watermark, mosaico immagini)
   ========================================================= */

/* ---------- PAGE HERO ICON (per pagine senza foto stock pertinente) ---------- */
.page-hero-icon-wrap {
  position: absolute;
  right: clamp(1rem, 6vw, 6rem);
  top: 50%;
  transform: translateY(-50%);
  width: clamp(120px, 16vw, 220px);
  height: clamp(120px, 16vw, 220px);
  opacity: 0.9;
  pointer-events: none;
  display: none;
}
.page-hero-icon-wrap img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  filter: brightness(0) invert(1);
  opacity: 0.28;
}
@media (min-width: 768px) {
  .page-hero-icon-wrap {
    display: block;
  }
}

/* ---------- TILE SECTION — icona watermark grande dietro il titolo ---------- */
.tiles-section {
  position: relative;
  overflow: hidden;
}
.tiles-section-icon {
  position: absolute;
  right: clamp(-2rem, -2vw, 1rem);
  top: 50%;
  transform: translateY(-50%);
  width: clamp(220px, 26vw, 420px);
  height: clamp(220px, 26vw, 420px);
  opacity: 0.05;
  pointer-events: none;
}
.tiles-section-icon img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  filter: brightness(0) invert(1);
}

/* ---------- MEDIA STRIP (mosaico immagini) ---------- */
.media-strip {
  margin-top: 3rem;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-auto-rows: 200px;
  gap: 1rem;
}
.media-strip-item {
  position: relative;
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--line-dark);
}
.media-strip-item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  filter: grayscale(0.15);
  transition:
    transform 0.6s var(--ease),
    filter 0.4s var(--ease);
}
.media-strip-item:hover img {
  transform: scale(1.05);
  filter: grayscale(0);
}
.media-strip-item--tall {
  grid-column: span 2;
}
@media (min-width: 640px) {
  .media-strip {
    grid-template-columns: repeat(4, 1fr);
    grid-auto-rows: 160px;
  }
  .media-strip-item--tall {
    grid-column: span 2;
    grid-row: span 2;
  }
}
@media (prefers-reduced-motion: reduce) {
  .media-strip-item img {
    transition: none;
  }
}

/* =========================================================
   STACK SCROLL — navigazione a schede con sovrapposizione
   Ogni .stack-section è sticky (top:0), altezza fissa 100vh e
   overflow:hidden: resta agganciata finché la sezione dopo non
   la copre scorrendo sopra. Nessuna intercettazione JS della
   rotellina — è sticky nativo al 100%.
   Se il contenuto (.stack-inner) è più alto di un viewport (es.
   la griglia servizi), initStackReveal() in script.js riserva lo
   spazio extra nel documento (padding-bottom sulla section, così
   la pagina scrolla per quella distanza) e trasla .stack-inner
   verso l'alto in base allo scroll — con un listener 'scroll'
   passivo, mai preventDefault: il contenuto extra scorre in vista
   pixel per pixel prima che la sezione successiva salga sopra.
   ========================================================= */
@media (min-width: 768px) {
  /* NIENTE display:contents qui (c'era, ed è stato rimosso).
     Serviva a far seguire alle sticky tutto il documento invece che
     fermarsi al bordo di <main>, ma su Chrome `display:contents` ha
     difetti noti e di lunga data proprio con sticky e sotto-griglie:
     l'elemento perde il proprio box e i discendenti possono perdere i
     vincoli di larghezza, degradando progressivamente lungo una lista
     lunga di figli — su portfolio (19 schede) le ultime si stiravano a
     tutta pagina invece di restare card a misura di viewport, mentre
     Safari (che implementa la proprietà correttamente) non mostrava
     nulla.
     Un <main> come blocco normale ottiene lo stesso risultato: il
     contenitore delle sticky diventa <main> stesso, che è alto quanto
     tutte le schede insieme — quindi il range di aggancio copre
     comunque l'intera sequenza. La condizione da rispettare è non
     introdurre qui overflow diverso da visible né trasformazioni, che
     creerebbero un nuovo blocco contenitore e taglierebbero lo sticky. */
  .stack-viewport {
    display: block;
    overflow: visible;
  }
  .stack-viewport .stack-section {
    position: sticky;
    top: 0;
    height: 100vh;
    overflow: hidden;
    box-shadow: 0 -24px 60px rgba(0, 0, 0, 0.35);
    /* angoli superiori stondati, non gli inferiori: rinforza la lettura
       "scheda che scorre sopra la precedente" quando si sovrappone —
       il bordo inferiore resta dritto perché è sempre coperto dalla
       sezione successiva (o dal footer, per l'ultima), non si vede mai. */
    border-radius: 2.5rem 2.5rem 0 0;
    /* NIENTE `isolation: isolate` qui — ERA LA CAUSA del bug storico su
       Chrome ("da una certa scheda in poi le schede si sfarfallano e non
       si sovrappongono più"). La nota precedente lo dava per innocuo; è
       il contrario.

       `isolation` crea un contesto di impilamento su OGNI scheda. Con 20
       sticky tutte agganciate a top:0 e sovrapposte, Chrome finisce per
       comporre male quei contesti: si vedevano il titolo di una scheda,
       l'immagine di un'altra e la foto di una terza sovrapposte, senza
       lo sfondo bianco opaco che dovrebbe coprire la scheda precedente.

       Verificato su Chrome 152 alla stessa posizione di scroll (y=9200,
       Metronotte/Stella Bianca), iniettando una regola alla volta:
         - build con isolation                  -> tre schede compenetrate
         - isolation:auto                       -> render pulito
         - contain:paint (in aggiunta)          -> render pulito
         - translateZ(0) / backface-visibility  -> non risolvono
       Safari compone correttamente in entrambi i casi, da qui la
       differenza tra i due browser.

       La motivazione originale (confinare il contesto 3D del coverflow)
       non vale più: `perspective` è stata tolta da .case-cover e vive
       ora dentro il transform delle singole immagini laterali, che si
       confina da sé (vedi la nota su .case-cover). Senza `isolation` il
       coverflow resta corretto — l'overflow:hidden qui sopra basta già a
       tenere ogni scheda nel proprio riquadro.

       NB: non rimpiazzarlo con contain:paint "per sicurezza". Anche se
       nel test risolve, crea un blocco contenitore sull'elemento sticky,
       cioè tocca il meccanismo stesso che regge lo stack. Rimuovere la
       causa è più sicuro che aggiungere un secondo vincolo sopra. */
  }
  /* la prima scheda dello stack (hero) non ha nulla sopra da "coprire":
     stondarla darebbe un bordo curvo contro il fondo pagina, incoerente
     con l'effetto voluto solo per le transizioni tra sezioni. */
  .stack-viewport .stack-section:first-child {
    border-radius: 0;
  }

  /* azzera il padding-block generico di "section" (inizio file): con
     box-sizing:border-box e height:100vh fissa, quel padding comprime il
     content-box invece di aggiungere spazio, lasciando meno dei 900px
     attesi per .stack-inner (min-height:100% si risolve quindi a un
     valore più basso) — measure() in script.js misura scrollHeight
     ignaro di questo, sottostimando l'overflow reale e lasciando uno
     spazio vuoto (il colore di fondo della pagina) tra questa sezione e
     la successiva. Tutto il padding verticale utile è già gestito
     dentro .stack-inner (padding-block del centraggio o padding-top/
     bottom di .has-overflow), qui va rimosso. */
  .stack-viewport .stack-section {
    padding-block: 0;
  }

  .stack-viewport .stack-inner {
    /* position:absolute invece di height:100%: alcune section (es. .hero)
       sono flex container con align-items:center per centrare il proprio
       contenuto — un figlio in flow con height:100% lì dentro si può
       comunque risolvere alla sua altezza intrinseca (flex-basis:auto la
       preferisce quando il contenuto non riempie lo spazio), lasciando
       .stack-inner (e il video full-bleed al suo interno) più basso della
       section. absolute lo rende indipendente dal flex layout del
       genitore. SOLO top/left/right (mai bottom, mai inset:0): bottom:0
       vincolerebbe anche l'altezza a 900px fissi, e con quel vincolo
       scrollHeight smette di riflettere correttamente il contenuto (+
       padding-bottom) che eccede oltre quei 900px, anche con
       overflow:visible — misurato empiricamente ~100px in meno del
       reale. min-height:100% garantisce comunque che riempia tutto lo
       spazio quando il contenuto è più corto (sezioni senza overflow,
       centrate via flex), ma può crescere liberamente oltre. */
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    min-height: 100%;
    overflow: visible; /* il contenuto extra (griglie più alte di 100%)
      deve restare visibile qui: è .stack-section, non .stack-inner, ad
      avere overflow:hidden e tagliare ciò che eccede. */
    /* NIENTE will-change qui — vedi .stack-inner.has-overflow più sotto.
       Dichiararlo su TUTTE le .stack-inner promuove ogni sezione a layer
       composito permanente: su portfolio sono 19 e Chrome, superato il
       proprio budget di layer, smette silenziosamente di promuoverle.
       Da quel punto in poi le schede perdono il layer su cui si reggono
       sticky + transform e la sovrapposizione si rompe (Safari gestisce
       i layer diversamente e non mostrava il problema). */
    /* Centra verticalmente il contenuto di ogni sezione che sta per
       intero in un viewport: senza, il contenuto si accumula dall'alto e
       lo spazio vuoto residuo in fondo varia sezione per sezione in base
       a quanto è alto il contenuto — risultato disomogeneo (alcune
       sezioni con margine enorme sotto, altre quasi nullo). Padding
       uguale sopra e sotto (non solo sul top per l'header) fa sì che
       align-items centri nello spazio rimanente in modo simmetrico —
       un padding-top pieno da solo sbilancerebbe il blocco verso il
       basso, spingendolo via dal margine che dovrebbe riservare.
       3rem basta come margine minimo dall'header fisso (6rem intero
       serviva alle sezioni .has-overflow, ancorate in alto, dove il
       contenuto può davvero iniziare a ridosso della navbar). Alcune
       sezioni (es. clients-marquee) hanno più di un figlio diretto
       (.container col titolo + .marquee-stack) che devono restare
       impilati verticalmente come blocchi normali, non affiancati
       dalla riga di default di flex — da qui flex-direction:column. */
    display: flex;
    flex-direction: column;
    align-items: stretch;
    justify-content: center;
    /* Asimmetrico di proposito. Con justify-content:center il contenuto
       è centrato nello spazio che resta tra i due padding: alzare
       entrambi in modo uguale non aggiunge aria in fondo, sposta solo i
       limiti restando centrato. Un padding-bottom maggiore del top
       sposta invece il blocco verso l'alto, lasciando più stacco dal
       bordo inferiore della scheda — dove la sezione successiva sale a
       coprirla e il contenuto a ridosso legge come tagliato. */
    padding-top: 3rem;
    padding-bottom: clamp(6.5rem, 13vw, 12rem);
  }
  /* Sezione a sola immagine: nessun padding e nessun centraggio flex —
     la foto deve toccare tutti e quattro i bordi della scheda.
     Serve questo selettore (0-3-0) e non basta `.full-image .stack-inner`
     dichiarato più sopra: la regola generica qui accanto è
     `.stack-viewport .stack-inner` (0-2-0) ma viene DOPO nel file, quindi
     a parità di specificità vincerebbe lei e l'immagine resterebbe
     rientrata di ~96px dall'alto.
     bottom:0 (che le altre schede non hanno, di proposito) chiude il box
     in altezza: qui non c'è contenuto che debba poter eccedere, anzi
     serve esattamente il contrario — un riquadro alto quanto la scheda
     da riempire con object-fit:cover. */
  .stack-viewport .full-image .stack-inner,
  .stack-viewport .references-full .stack-inner,
  .stack-viewport .formats-slider .stack-inner,
  .stack-viewport .formats-slider .stack-inner.has-overflow,
  /* .has-overflow nel selettore per pareggiare la specificità di
     `.stack-viewport .stack-inner.has-overflow` (0-3-0, più sotto) e
     vincere per ordine. Senza, su queste sezioni si innescava un ciclo:
     il padding-top di .has-overflow spostava in basso il blocco
     contenitore dei figli assoluti, scrollHeight superava il viewport,
     la classe veniva riconfermata a ogni misurazione e non si toglieva
     più — con la card di overlay spinta a metà scheda. Qui il contenuto
     è tutto in position:absolute: overflow non ce n'è mai. */
  .stack-viewport .references-full .stack-inner.has-overflow {
    padding: 0;
    display: block;
    bottom: 0;
  }

  /* Sezioni con overflow (.has-overflow, applicata da initStackReveal in
     script.js quando il contenuto eccede un viewport) restano ancorate
     dall'alto: il loro contenuto extra scorre in vista via transform,
     centrarle comprometterebbe quel calcolo. padding-top pieno (non il
     padding-block ridotto usato per il centraggio): qui il contenuto
     inizia davvero a ridosso della navbar, serve tutto lo spazio per
     non finirci sotto. */
  .stack-viewport .stack-inner.has-overflow {
    display: block;
    padding-top: 6rem;
    padding-bottom: var(--section-pad-bottom); /* stesso respiro delle
      section normali (vedi "section" a inizio file) — senza, la sezione
      finiva quasi al bordo del box, con il contenuto rivelato via
      transform che toccava il fondo dello schermo senza margine.
      scrollHeight lo include automaticamente, initStackReveal.measure()
      ricalcola l'overflow di conseguenza. */
    /* will-change SOLO qui: sono le uniche .stack-inner che ricevono
       davvero un transform (il reveal dello scroll interno, applicato da
       initStackReveal). Le altre restano ferme e non hanno nulla da
       promuovere — dichiararlo anche su quelle esauriva il budget di
       layer compositi di Chrome (vedi nota sulla regola base). */
    will-change: transform;
  }

  /* I case study NON devono usare il padding di sezione generoso: il loro
     contenuto (riquadro media ad altezza fissa + poche righe di testo) sta
     comodamente in un viewport, ma 6rem sopra + 13.5rem sotto lo portavano
     a 1026px contro i 900 disponibili. Risultato: measure() classificava
     TUTTE e 17 le schede come .has-overflow e assegnava a ciascuna uno
     scroll-reveal interno da 126px — un transform per scheda, che è ciò
     che mandava fuori sincrono la sovrapposizione oltre le prime schede.
     Il respiro ampio serve alle sezioni di contenuto (home, pagine
     servizio), non a queste card a misura di schermo. */
  .stack-viewport .case-study .stack-inner,
  .stack-viewport .case-study .stack-inner.has-overflow {
    /* Anche qui il padding scala con l'altezza del viewport. Era
       5rem/3rem fissi: su finestre basse quegli 8rem non comprimibili,
       sommati al padding di .case-study-inner, spingevano le schede con
       il titolo su 4 righe oltre l'altezza disponibile. Il minimo di
       3.5rem in cima resta sufficiente a stare sotto l'header fisso. */
    padding-top: clamp(3.5rem, 7vh, 5rem);
    padding-bottom: clamp(1.5rem, 3vh, 3rem);
  }

  /* z-index: assegnato da initStackReveal() in script.js, non qui.
     Prima c'era una catena di 22 regole con il combinatore ~, una per
     ogni possibile posizione (…~ .stack-section ripetuto fino a 22
     volte, l'ultimo selettore lungo oltre 400 caratteri). Problemi:
     era limitata a un numero fisso di schede — superato quello la
     sovrapposizione smetteva di funzionare — e la specificità cresceva
     a ogni livello, rendendo imprevedibile quale regola vincesse.
     Ora il JS scorre le sezioni in ordine di DOM e assegna
     z-index = indice + 1: funziona con qualsiasi numero di schede,
     senza selettori generati. */

  /* Spacer inseriti da initStackReveal dopo ogni sezione con overflow:
     riservano nel flusso lo scroll necessario al reveal (vedi script.js).
     Puramente strutturali — nessuno sfondo, mai sopra le schede, e fuori
     dal flusso di lettura (aria-hidden lato JS). */
  .stack-viewport .stack-spacer {
    position: relative;
    z-index: 0;
    pointer-events: none;
  }

  /* ultima scheda: nessuna sezione dopo che la copra, niente ombra.
     :last-child non basta più (e non bastava già: dopo il footer ci sono
     gli spacer inseriti da initStackReveal) — il footer va indirizzato
     per quello che è, non per la sua posizione tra i fratelli. */
  .stack-viewport .site-footer.stack-section,
  .stack-viewport .stack-section:last-child {
    box-shadow: none;
    /* z-index esplicito e molto alto invece di affidarsi alla catena di
       combinatori ~ sopra: su pagine con poche sezioni (es. agenzia.html,
       4 in tutto) è stata osservata — solo su questa, riproducibile al
       100% su Chrome e Safari — la sezione precedente (con overflow,
       rimasta "stuck" più a lungo durante il proprio scroll-reveal)
       ridiventare visibile sopra l'ultima scheda durante la transizione
       finale, nonostante lo z-index calcolato risultasse già corretto.
       !important perché la catena di combinatori ~ qui sopra cresce di
       specificità con il numero di sezioni (su portfolio arriva a 20+
       selettori di classe): nessun selettore di lunghezza fissa può
       vincerla in modo affidabile su tutte le pagine. */
    z-index: 999 !important;
  }

  /* Il footer è l'ultima scheda dello stack, ma NON deve prendere
     height:100vh come le altre: il suo contenuto è molto più basso di un
     viewport (~520px in home, ~340px nelle interne) e forzarlo a 900px lo
     faceva finire, a fine scroll, da solo a schermo pieno — perdendo gli
     angoli stondati fuori vista e la sezione precedente dietro, cioè
     esattamente l'effetto "scheda" che deve avere. Con height:auto la
     scheda si ferma alla propria altezza reale e l'ultima sezione resta
     visibile sopra di essa: il footer si legge come una card che scorre
     sopra, non come una pagina piena.
     height va messa qui e non su .site-footer generico perché la regola
     .stack-viewport .stack-section sopra ha la stessa specificità e
     verrebbe dopo nella cascata. */
  .stack-viewport .site-footer.stack-section {
    height: auto;
    /* ...MA con un'altezza minima che lo porta ad agganciarsi appena
       sotto l'header.

       Con la sola height:auto il footer prendeva l'altezza del proprio
       contenuto, che cambia da pagina a pagina: 762px in home e su
       portfolio (footer "lungo", con la fascia associazioni), ma solo
       472px sulle pagine interne. In quei casi, arrivati in fondo,
       restava una banda di ~262px tra il bordo superiore del footer e
       l'header, dalla quale si vedeva la sezione precedente: la scheda
       finale sembrava non salire fino in cima.

       min-height la porta sempre a filo dell'header, su ogni pagina,
       senza toccare i footer già più alti (height:auto continua a
       lasciarli crescere): è un minimo, non un'imposizione.

       Il valore è il viewport meno l'altezza dell'header, presa dalla
       variabile --header-h definita in :root — così se cambia
       l'altezza dell'header non c'è nulla da riallineare qui. Il footer
       resta comunque una scheda: sopra rimane esattamente la striscia
       occupata dall'header, quindi gli angoli stondati e l'effetto
       "card che scorre sopra" non si perdono. */
    min-height: calc(100vh - var(--header-h));
    /* Il min-height qui sopra rende la scheda più alta del contenuto sulle
       pagine con footer corto: senza questo, il contenuto si accumula in
       alto e resta una grande area vuota in fondo. Centrandolo
       verticalmente lo spazio in eccesso si divide sopra e sotto e la
       scheda risulta bilanciata a qualunque altezza. */
    display: flex;
    flex-direction: column;
    justify-content: center;
    /* overflow:hidden serve alle schede a 100vh per tagliare il contenuto
       in eccesso; qui non c'è eccesso da tagliare e mantenerlo
       ritaglierebbe l'ombra della scheda. */
    overflow: visible;
  }
  /* .stack-inner è position:absolute per le schede a altezza fissa (vedi
     commento sopra): dentro un footer ad altezza automatica lo toglierebbe
     dal flusso, collassando la scheda a zero. Qui torna un blocco normale
     in flow, così è il contenuto stesso a dare l'altezza al footer. */
  .stack-viewport .site-footer.stack-section > .stack-inner {
    position: static;
    min-height: 0;
    /* Il padding verticale del footer va rimesso QUI. Su desktop il
       footer riceve .stack-section da initStackReveal (script.js), quindi
       la regola ".stack-viewport .stack-section { padding-block: 0 }" qui
       sopra lo azzera su .site-footer (0-2-0 batte 0-1-0): senza questa
       riga il padding-block dichiarato su .site-footer non ha alcun
       effetto nello stack, solo sotto i 768px. Stessi token di
       .site-footer, applicati al wrapper interno che non è soggetto a
       quell'azzeramento. */
    padding-block: var(--footer-pad-top) var(--footer-pad-bottom);
    display: block;
  }
}

/* =========================================================
   FAQ — accordion custom (numero animato, titolo che scorre,
   croce che ruota a 45°, sottolineatura animata). Vanilla JS,
   popolato via array in script.js. Pensato per sfondo scuro.
   ========================================================= */
.faq-accordion {
  margin-top: 3rem;
}
.faq-item {
  border-top: 1px solid var(--line);
}
.faq-item:last-child {
  border-bottom: 1px solid var(--line);
}
.faq-trigger {
  position: relative;
  display: flex;
  align-items: center;
  gap: 1.5rem;
  width: 100%;
  padding: 1.3rem 0.2rem;
  background: none;
  border: 0;
  cursor: pointer;
  text-align: left;
  font-family: var(--font-body);
}

/* Numero + cerchio */
.faq-trigger-num {
  position: relative;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 2.6rem;
  height: 2.6rem;
}
.faq-trigger-num-circle {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: var(--lemon);
  opacity: 0;
  transform: scale(0);
  transition:
    transform 0.35s var(--ease),
    opacity 0.35s var(--ease);
}
.faq-trigger-num-text {
  position: relative;
  z-index: 1;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.9rem;
  letter-spacing: 0.02em;
  color: var(--ash);
  transition: color 0.25s var(--ease);
}
.faq-item:hover .faq-trigger-num-circle {
  opacity: 0.14;
  transform: scale(0.85);
}
.faq-item.is-open .faq-trigger-num-circle {
  opacity: 1;
  transform: scale(1);
}
.faq-item.is-open .faq-trigger-num-text {
  color: var(--ink);
}

/* Titolo */
.faq-trigger-title {
  flex: 1 1 auto;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: clamp(1.15rem, 2.2vw, 1.5rem);
  letter-spacing: -0.01em;
  color: var(--ash);
  transition:
    color 0.25s var(--ease),
    transform 0.3s var(--ease);
}
.faq-item:hover .faq-trigger-title,
.faq-item.is-open .faq-trigger-title {
  color: var(--paper);
  transform: translateX(4px);
}

/* Croce che ruota a 45° = "+" diventa "x" */
.faq-trigger-icon {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  color: var(--paper);
  opacity: 0.4;
  transition:
    transform 0.35s var(--ease),
    opacity 0.25s var(--ease);
}
.faq-item:hover .faq-trigger-icon {
  opacity: 1;
}
.faq-item.is-open .faq-trigger-icon {
  opacity: 1;
  transform: rotate(45deg);
}
.faq-trigger-icon svg {
  width: 1rem;
  height: 1rem;
}

/* Pannello risposta: altezza animata via max-height (niente framer-motion) */
/* I pannelli sono APERTI di default: le FAQ stanno nell'HTML (non più
   generate da JS) e devono restare leggibili anche se lo script non parte
   — sia per i crawler che non eseguono JavaScript, sia se il JS fallisce.
   La chiusura è applicata solo da .js-faq, classe che buildFaqAccordion()
   mette sul contenitore appena prende il controllo dell'accordion. */
.faq-panel {
  overflow: hidden;
  transition:
    max-height 0.45s var(--ease),
    opacity 0.3s var(--ease);
}
.js-faq .faq-panel {
  max-height: 0;
  opacity: 0;
}
.faq-item.is-open .faq-panel {
  opacity: 1;
}
.faq-panel-inner {
  padding: 1rem 3rem 1.8rem 4.1rem;
  max-width: 62ch;
  color: rgba(255, 255, 255, 0.7);
  font-size: 1.02rem;
  line-height: 1.7;
}

@media (max-width: 575.98px) {
  .faq-trigger {
    gap: 1rem;
  }
  .faq-panel-inner {
    padding-left: 0.2rem;
    padding-right: 0.2rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .faq-trigger-num-circle,
  .faq-trigger-title,
  .faq-trigger-icon,
  .faq-trigger-line,
  .faq-panel {
    transition: none;
  }
}
