/* ==========================================================================
   GASPARE SILVERII — MASTER UI
   DNA visivo: 70% "UI UX Pro Max" (tech scuro, grotesk marcato, parola
   accento in gradiente, box statistici/feature bordati, pannelli di
   processo numerati, bagliore radiale) + 30% "StringMaster" (eyebrow a
   badge-pill, widget card riccamente bordata, anello accento sulla
   featured-card, badge fluttuante d'angolo).

   Sommario:
   1. Design tokens
   2. Reset / base
   3. Tipografia
   4. Layout / utility
   5. Bottoni / badge / pillole
   6. Header / navigazione
   7. Hero
   8. Intro / value proposition
   9. Feature card
   10. Sezione visual / immagine
   11. Processo / passaggi
   12. Corsi
   13. Carosello testimonianze
   14. Team card
   15. Statistiche
   16. CTA finale
   17. Footer
   18. Responsive
   19. Motion / reveal
   20. Componenti blend (plugin, capacità, spotlight, band, team)
========================================================================== */

/* --------------------------------------------------------------------------
   1. DESIGN TOKENS
-------------------------------------------------------------------------- */
:root{
  /* colore — base (corrisponde quasi esattamente allo sfondo pagina scuro
     di ui-ux-pro-max-skill.nextlevelbuilder.io) */
  --bg:            hsl(222 20% 8% / 1);
  --surface:        hsl(222 19% 8% / 1);
  --surface-2:      hsl(222 19% 10% / 1);
  --border:         hsl(222 15% 16% / 1);
  --border-strong:  hsl(222 13% 23% / 1);

  /* colore — testo */
  --text:           #f5f5f7;
  --text-muted:     #9a9ba8;
  --text-faint:     #636470;
  --text-on-accent: #ffffff;

  /* colore — accento, agganciato al primary/accent del sito di riferimento
     (rgb(37,99,235) / rgb(249,115,22) dal suo Tailwind CSS compilato) —
     resta un azzurro pulito, niente violetto tendente al rosso, e arancio,
     niente viola */
  --accent:         #0099FF;
  --accent-hover:   #0099FF;
  --accent-soft:    rgba(0,153,255,.14);
  --grad-a:         #0099ff;
  --grad-b:         #cccccc;
  --grad-c:         #f97316;

  /* colore — accento secondario (arancio caldo, usato con parsimonia) */
  --accent-2:       #f97316;
  --accent-2-soft:  rgba(249,115,22,.14);
  --ok:             #3ecf8e;

  /* colore — viola, usato con parsimonia per elementi che devono
     distinguersi dal verde di --ok (es. il codice coupon, il popup di
     carrello abbandonato) */
  --violet:         #8b5cf6;
  --violet-soft:    rgba(139,92,246,.3);

  /* glass — superfici a vetro smerigliato, ricetta letta dalla .glass-card
     del sito di riferimento (dark mode): bianco traslucido, non tinta scura */
  --glass-bg:            rgba(255,255,255,.05);
  --glass-bg-strong:     rgba(255,255,255,.09);
  --glass-border:        rgba(255,255,255,.10);
  --glass-border-strong: rgba(255,255,255,.18);
  --glass-blur:          blur(24px) saturate(150%);
  --glass-shadow:        0 8px 32px rgba(0,0,0,.28);

  /* tipografia — Space Grotesk per i titoli, DM Sans per il corpo,
     esattamente lo stesso abbinamento del sito di riferimento */
  --font: "DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-heading: "Space Grotesk", "DM Sans", -apple-system, BlinkMacSystemFont, sans-serif;

  --fs-eyebrow: .78rem;
  --fs-small:   .86rem;
  --fs-body:    1rem;
  --fs-lead:    1.1rem;
  --fs-h3:      1.2rem;
  --fs-h2:      clamp(1.9rem, 1.5rem + 1.7vw, 2.75rem);
  --fs-h1:      clamp(2.4rem, 1.7rem + 3vw, 4rem);

  /* scala di spaziatura */
  --space-1: .25rem;
  --space-2: .5rem;
  --space-3: .75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 2.75rem;
  --space-8: 3.5rem;
  --space-9: 5rem;
  --space-10: 7rem;

  /* layout */
  --container-w: 1240px;
  --container-pad: clamp(1.25rem, 3vw, 2.5rem);
  --radius-sm: 8px;
  --radius-md: 14px;
  --radius-lg: 16px;
  --radius-pill: 999px;

  /* movimento */
  --ease: cubic-bezier(.22,.61,.36,1);
  --dur-fast: .18s;
  --dur-base: .3s;
  --dur-slow: .55s;

  color-scheme: dark;
}

/* --------------------------------------------------------------------------
   2. RESET / BASE
-------------------------------------------------------------------------- */
*, *::before, *::after{ box-sizing: border-box; }
/* overflow-x:hidden solo su body (non su html) — la nav mobile è un
   pannello fisso a larghezza piena spinto fuori schermo con
   translateX(100%), e alcuni browser continuano a contare quel box
   trasformato nella larghezza scrollabile della pagina, aprendo una
   fessura di scroll orizzontale che permette di trascinare in vista il
   pannello "chiuso". Impostarlo su ENTRAMBI html e body trasforma
   ciascuno nel proprio contenitore di scroll, il che rompe
   position:sticky sull'header (smette di restare fisso e scrolla via) —
   quindi vive solo su body, lasciando html come l'unico vero elemento
   scrollabile. */
html{ -webkit-text-size-adjust: 100%; scroll-behavior: smooth; overflow-x: clip; }
body{
  margin: 0;
  overflow-x: clip;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--fs-body);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
/* Ingresso morbido universale della pagina: ogni navigazione (non solo il
   cambio lingua) parte completamente trasparente e si dissolve dentro,
   così un caricamento fresco non si percepisce mai come un rendering
   brusco e improvviso. Puro CSS — gira al primo paint indipendentemente
   dai tempi di JS/'load', quindi non può fare gare né perdere una pagina.
   Ambito ristretto solo a #main (non body) così header/nav e footer —
   fratelli di #main, per partials/header.php e footer.php — restano
   completamente opachi e cliccabili fin dal primissimo frame; il chrome
   del sito deve sempre sembrare istantaneamente reattivo, solo l'area di
   contenuto si dissolve dentro. */
#main{
  opacity: 0;
  animation: tm-page-in var(--dur-slow) var(--ease) forwards;
}
@keyframes tm-page-in{ to{ opacity: 1; } }
img{ max-width: 100%; display: block; }
a{ color: inherit; text-decoration: none; }
ul, ol{ margin: 0; padding: 0; list-style: none; }
h1,h2,h3,h4,p,dl,dd,figure{ margin: 0; }
button{ font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
svg{ display: block; }

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

.skip-link{
  position: absolute; left: -999px; top: auto;
  background: var(--accent); color: #fff;
  padding: .75rem 1.25rem; border-radius: var(--radius-sm);
  z-index: 200; font-weight: 700;
}
.skip-link:focus{ left: var(--space-5); top: var(--space-5); }

.sr-only{
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}

/* --------------------------------------------------------------------------
   3. TIPOGRAFIA
-------------------------------------------------------------------------- */
h1, h2, h3{
  font-family: var(--font-heading);
  font-weight: 700;
  letter-spacing: -.02em;
  line-height: 1.1;
  color: var(--text);
}
h2{ font-size: var(--fs-h2); }
h3{ font-size: var(--fs-h3); font-weight: 700; }

.text-grad{
  background: linear-gradient(90deg, var(--grad-a), var(--grad-b) 55%, var(--grad-c));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.eyebrow{
  display: inline-flex; align-items: center; gap: .55em;
  font-size: var(--fs-eyebrow); font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 var(--space-4);
}
.eyebrow::before{ content: ""; width: 1.4em; height: 2px; border-radius: 2px; background: currentColor; }
.eyebrow--center{ justify-content: center; }
.eyebrow--right{ justify-content: flex-end; flex-direction: row-reverse; }

.section-sub{ color: var(--text-muted); font-size: var(--fs-lead); max-width: 42ch; margin-top: var(--space-3); }

.text-link{
  display: inline-flex; align-items: center; gap: .5em;
  font-weight: 600; font-size: var(--fs-small); color: var(--text);
  padding-bottom: 2px; border-bottom: 1px solid var(--border-strong);
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.text-link svg{ transition: transform var(--dur-fast) var(--ease); flex-shrink: 0; }
.text-link:hover{ color: var(--accent); border-color: var(--accent); }
.text-link:hover svg{ transform: translateX(3px); }
.text-link-sm{ font-size: .8rem; }

/* --------------------------------------------------------------------------
   4. LAYOUT / UTILITY
-------------------------------------------------------------------------- */
.container{ width: 100%; max-width: var(--container-w); margin-inline: auto; padding-inline: var(--container-pad); }
section{ position: relative; }

/* Spazio per l'header sticky così i salti d'ancora (#plugins, #workflow,
   ecc.) non fanno atterrare il contenuto sotto di esso.
   #tonality-masks: nessun elemento con questo id esiste più in nessuna
   delle due home (IT l'ha tolto per prima, EN allineata dopo) — voce
   morta, lasciata per ora, innocua in una lista di selettori. */
#plugins, #workflow, #about, #tonality-masks, #press, #features{ scroll-margin-top: 90px; }

.section-heading{ max-width: 48ch; margin-bottom: var(--space-8); }
.section-heading--center{ max-width: 52ch; margin-inline: auto; text-align: center; }
/* .section-sub ha un max-width suo (42ch) più stretto del contenitore
   (52ch): senza margini automatici il suo box resta a sinistra invece di
   centrarsi, anche se il testo dentro è center-aligned — stesso motivo
   per cui .section-heading--right ha bisogno di margin-left:auto sotto. */
.section-heading--center .section-sub{ margin-inline: auto; }
.section-heading--right{ margin-left: auto; text-align: right; }
.section-heading--right .section-sub{ margin-left: auto; }

/* Prova hero home: foto al posto di aurora+glow, solo lì. Le regole
   .hero-glow/.aurora-bg sotto restano ATTIVE (servono a tutte le altre
   pagine, es. page-hero dei singoli plugin) — per la home si disattivano
   invece togliendo la classe (vedi index.html: "aurora-bg_hardDisabled" /
   "hero-glow_hardDisabled" al posto di "aurora-bg" / "hero-glow", così il
   selettore qui sotto semplicemente non trova nulla da stilizzare lì).
   background-position: top così in modalità cover il ritaglio necessario
   si mangia solo il basso, mai la cima della foto. */
.hero-photo-bg{
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: url('../content/Fotografia_Paesaggio_Gaspare_Silverii_Islanda_The_Last_Throne.jpg') top center / cover no-repeat;
  opacity: .3;
}

/* Macchie di gradiente sfocate fluttuanti dietro hero + CTA finale —
   tecnica presa dalla hero di ui-ux-pro-max-skill.nextlevelbuilder.io
   (due grandi cerchi blur-3xl, brand-primary + brand-accent, leggero
   fluttuare verticale), riprodotta qui con i nostri token blu/arancio. */
.hero-glow{ position: absolute; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
.hero-glow::before, .hero-glow::after{
  content: ""; position: absolute; border-radius: 50%; filter: blur(64px);
  animation: hero-float 6s ease-in-out infinite;
}
/* Posizionate dal centro orizzontale con offset fissi in rem (non %) così
   le due macchie restano vicine e si sovrappongono di una quantità
   costante a qualunque larghezza di viewport, invece di allontanarsi su
   schermi desktop larghi. */
.hero-glow::before{ top: 10%; left: 50%; margin-left: -22rem; width: 26rem; height: 26rem; background: var(--accent); opacity: .32; }
.hero-glow::after{ top: 21%; left: 50%; margin-left: -2rem; width: 24rem; height: 24rem; background: var(--accent-2); opacity: .26; animation-delay: -3s; }

@keyframes hero-float{ 0%, 100%{ transform: translateY(0); } 50%{ transform: translateY(-20px); } }

/* Lavaggio aurora ambientale — tre gradienti radiali morbidi (blu in
   alto, arancio a destra, blu in basso a sinistra), dipinto come sfondo
   proprio di .hero, dietro le macchie fluttuanti di .hero-glow. Statico —
   animarlo (sia una posizione di gradiente in movimento sia macchie
   sfocate vere e proprie con transform) è stato provato e non si è mai
   percepito come chiaramente animato, quindi resta fermo. Il pannello
   before/after vive nella propria sezione subito dopo .hero (vedi
   index.html) così questo può restare un semplice sfondo a piena pagina
   senza giochi di masking/ritaglio. */
.aurora-bg{
  background:
    radial-gradient(ellipse 80% 80% at 50% -20%, rgba(37, 99, 235, .3), transparent),
    radial-gradient(ellipse 50% 50% at 80% 50%, rgba(249, 115, 22, .15), transparent),
    radial-gradient(ellipse 50% 50% at 20% 80%, rgba(59, 130, 246, .2), transparent);
}

/* --------------------------------------------------------------------------
   5. BOTTONI / BADGE / PILLOLE
-------------------------------------------------------------------------- */
.btn{
  display: inline-flex; align-items: center; gap: .55em;
  padding: .85rem 1.5rem; border-radius: var(--radius-sm);
  font-weight: 700; font-size: .92rem; letter-spacing: -.005em;
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.btn:active{ transform: translateY(1px); }

.btn-primary{ background: linear-gradient(to right, var(--accent), var(--accent-hover)); color: #fff; border: 1px solid transparent; }
.btn-primary:hover{ filter: brightness(1.08); }

.btn-ghost{ background: var(--glass-bg); color: var(--text); border: 1px solid var(--glass-border); }
.btn-ghost:hover{ border-color: var(--glass-border-strong); background: var(--glass-bg-strong); }

.btn-accept{ background: rgba(62,207,142,.5); color: #fff; border: 1px solid transparent; }
.btn-accept:hover{ background: rgba(62,207,142,.65); }

.btn-sm{ padding: .55rem 1.1rem; font-size: .82rem; }
.btn-lg{ padding: 1rem 1.9rem; font-size: 1rem; }

.badge-pill{
  display: inline-flex; align-items: center; gap: .55em;
  padding: .5rem 1rem;
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-pill);
  font-size: .82rem; font-weight: 600; color: var(--text-muted);
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  margin-bottom: var(--space-5);
}
.badge-dot{ width: 7px; height: 7px; border-radius: 50%; background: var(--accent-2); flex-shrink: 0; }

.tag-row{ display: flex; flex-wrap: wrap; gap: .5rem; margin-top: var(--space-4); }
.tag{
  font-size: .74rem; font-weight: 600; letter-spacing: .01em;
  padding: .32rem .7rem; border-radius: var(--radius-pill);
  background: var(--accent-soft); color: var(--accent);
}

/* Toggle lingua nel footer — semplice pillola a due vie, invariata. */
.lang-switch{ display: inline-flex; border: 1px solid var(--border-strong); border-radius: var(--radius-pill); padding: 3px; gap: 2px; }
.lang-btn{ padding: .32rem .7rem; border-radius: var(--radius-pill); font-size: .74rem; font-weight: 700; letter-spacing: .03em; color: var(--text-faint); transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease); }
.lang-btn.is-active{ background: var(--surface-2); color: var(--text); }
.lang-btn:hover{ color: var(--text); }

/* Cambio lingua nell'header — un dropdown (come Plugin), pronto per altre
   lingue oltre EN/IT senza cambiare lo schema. Stesso bordo/raggio/hover
   dell'icona Account accanto (.header-actions .nav-account), per leggersi
   come lo stesso controllo bordato invece che due stili diversi. */
.lang-trigger{
  position: relative; display: inline-flex; align-items: center; gap: .5em;
  padding: .35rem .7rem; border: 2px solid var(--border-strong); border-radius: var(--radius-md);
  background: none;
  font-size: .92rem; font-weight: 500; color: var(--text-muted);
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.lang-trigger:hover{ color: var(--text); border-color: var(--accent); }
.lang-flag{ width: 16px; height: 16px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
/* Sempre rivolta verso il basso, mai capovolta all'apertura: il pannello
   si apre sempre sotto il trigger, mai sopra, quindi una freccia che
   punta in su all'apertura sarebbe spazialmente contraddittoria con dove
   il contenuto compare davvero. L'apertura è già segnalata dal pannello
   stesso e dalla sottolineatura del trigger, non serve un terzo segnale. */
.lang-caret, .nav-caret{ color: var(--text-faint); flex-shrink: 0; }
.nav-dropdown-menu a.is-active{ color: var(--accent); }

.check-row{ display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-6); margin-top: var(--space-7); }
.check-row li{ display: inline-flex; align-items: center; gap: .5em; font-size: .84rem; color: var(--text-muted); }
.check-row svg{ color: var(--ok); flex-shrink: 0; }

/* --------------------------------------------------------------------------
   6. HEADER / NAVIGAZIONE
-------------------------------------------------------------------------- */
.site-header{
  position: sticky; top: 0; z-index: 100;
  border-bottom: 1px solid transparent;
  background: rgba(11,11,16,.34);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  transition: background var(--dur-base) var(--ease), border-color var(--dur-base) var(--ease);
}
.site-header.is-scrolled{ background: rgba(11,11,16,.55); border-bottom-color: var(--glass-border); }
/* backdrop-filter su un antenato crea un nuovo containing block per i
   discendenti position:fixed (come .main-nav a schermo intero su mobile),
   che finiscono ancorati alla posizione "in flusso" dell'header invece che
   al viewport — invisibili non appena si scrolla, perché il loro top
   diventa -scrollY. Il menu a schermo intero copre comunque l'header con
   il proprio sfondo, quindi disattivarlo mentre il menu è aperto non si
   vede e risolve il bug (classe aggiunta/rimossa da js/menu/nav.js). */
.site-header.is-menu-open{ backdrop-filter: none; -webkit-backdrop-filter: none; }

/* Padding fisso (nessun restringimento allo scroll) così l'header non
   cambia mai altezza — un cambio d'altezza a metà di uno smooth-scroll si
   percepisce come l'intera barra che scatta. */
.header-inner{ display: flex; align-items: center; gap: var(--space-6); padding-block: 1rem; }

/* position+z-index: stesso bugfix di .menu-toggle/.header-actions .nav-account
   più sotto — l'overlay del menu mobile a schermo intero (.main-nav,
   z-index:90) è un fratello DOM dentro lo stesso .site-header, quindi lo
   "inghiotte" a meno che non stia esplicitamente sopra di lui. Senza
   questo il logo spariva dietro l'overlay non appena si apriva il menu. */
.logo{ position: relative; z-index: 95; display: inline-flex; align-items: center; gap: .65rem; margin-right: auto; }
.logo-icon{ width: 2.35rem; height: 2.35rem; object-fit: contain; flex-shrink: 0; }
.logo-name{ font-family: var(--font-heading); font-weight: 700; font-size: 1.3rem; letter-spacing: -.01em; }
.site-header .logo-name{ font-size: 2rem; }

.main-nav ul{ display: flex; align-items: center; gap: var(--space-6); }
.main-nav a{
  position: relative; font-size: .92rem; font-weight: 500; color: var(--text-muted);
  padding-block: .3rem; transition: color var(--dur-fast) var(--ease);
}
.main-nav a::after{
  content: ""; position: absolute; left: 0; right: 0; bottom: -2px; height: 2px;
  background: var(--accent); border-radius: 2px;
  transform: scaleX(0); transform-origin: left; transition: transform var(--dur-fast) var(--ease);
}
.main-nav a:hover{ color: var(--text); }
.main-nav a:hover::after{ transform: scaleX(1); }
/* La pagina/sezione corrente tiene la stessa sottolineatura che mostra
   l'hover, in modo permanente — la nav riflette visibilmente dove ti trovi,
   non solo dove sta il mouse. */
.main-nav a[aria-current="page"]{ color: var(--text); }
.main-nav a[aria-current="page"]::after{ transform: scaleX(1); }
.main-nav a.btn::after{ content: none; }
.main-nav a.btn, .main-nav a.btn:hover{ color: #fff; }
/* Trigger del dropdown "Il Progetto"/"About": stesso aspetto/hover di un
   link .main-nav (sottolineatura, colore, stato aria-current) ma è un
   <button>, non un <a> — non porta a una pagina propria (le due sotto-voci
   sono pari grado, vedi header.php), quindi le regole .main-nav a sopra
   non lo raggiungono e vanno ridichiarate qui sull'elemento giusto. */
.main-nav .nav-dropdown-trigger{
  display: inline-flex; align-items: center; gap: .4em;
  font: inherit; background: none; border: 0; cursor: pointer;
  position: relative; font-size: .92rem; font-weight: 500; color: var(--text-muted);
  padding-block: .3rem; transition: color var(--dur-fast) var(--ease);
}
.main-nav .nav-dropdown-trigger::after{
  content: ""; position: absolute; left: 0; right: 0; bottom: -2px; height: 2px;
  background: var(--accent); border-radius: 2px;
  transform: scaleX(0); transform-origin: left; transition: transform var(--dur-fast) var(--ease);
}
.main-nav .nav-dropdown-trigger:hover{ color: var(--text); }
.main-nav .nav-dropdown-trigger:hover::after{ transform: scaleX(1); }
.main-nav .nav-dropdown-trigger[aria-current="page"]{ color: var(--text); }
.main-nav .nav-dropdown-trigger[aria-current="page"]::after{ transform: scaleX(1); }

.nav-account{ display: inline-flex !important; align-items: center; gap: .45em; }
.nav-account svg{ flex-shrink: 0; }

/* Variante header: solo icona, bottone smussato (non un cerchio pieno —
   il .nav-account del footer tiene la sua etichetta). position+z-index
   sopra .main-nav (z-index:90) per lo stesso motivo di .menu-toggle: da
   quando l'overlay del menu mobile copre correttamente tutto il viewport
   (bugfix backdrop-filter), senza questo l'icona account restava coperta
   e invisibile a menu aperto. */
.header-actions .nav-account{
  position: relative; z-index: 95;
  width: 2.5rem; height: 2.5rem; justify-content: center;
  border: 2px solid var(--border-strong); border-radius: var(--radius-md); color: var(--text-muted);
  transition: border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.header-actions .nav-account:hover{ border-color: var(--accent); color: var(--text); }
.header-actions .nav-account svg{ width: 19px; height: 19px; }

/* Dropdown lingua nell'header: il bottone trigger disegna già la propria
   pillola — togli bordo/padding del wrapper così non è annidata dentro una
   seconda. */
.nav-dropdown.lang-switch{ border: 0; padding: 0; }

/* Dropdown nav Plugin — hover/focus rivela un pannello a vetro di link
   rapidi. Il comportamento fluttuante/hover è limitato a min-width:901px
   di proposito: sotto quella soglia subentra il drawer mobile con il suo
   layout sempre espanso (vedi il blocco max-width:900px), e senza questa
   protezione un :hover/:focus-within rimasto attivo da un'interazione a
   larghezza desktop potrebbe lasciare il pannello fluttuante visibilmente
   bloccato aperto dopo aver ridotto la finestra. */
.nav-dropdown{ position: relative; }
.nav-dropdown-menu a{
  display: flex; align-items: center; gap: .65em;
  padding: .55rem .75rem; border-radius: var(--radius-sm);
  font-size: .86rem; font-weight: 500; color: var(--text-muted); white-space: nowrap;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.nav-dropdown-menu a::after{ content: none; }
.nav-dropdown-menu a:hover{ background: var(--glass-bg); color: var(--text); }

/* Etichetta "NEW" su una voce del dropdown — stesso linguaggio
   arancio + lampeggio del pallino di stato del badge release nella hero
   (vedi badge-blink, sezione 7). radius-sm invece di radius-pill: angoli
   smussati non a pillola, stessa convenzione di .hub-before-after-tab.
   6px fisso invece di --radius-sm (8px, il più piccolo token del tema,
   pensato per bottoni ben più grandi di questo badge minuscolo): 8px
   risultava ancora troppo arrotondato qui, 4px troppo spigoloso. */
.new-flag{
  display: inline-flex; align-items: center; font-size: .62rem; font-weight: 800;
  letter-spacing: .04em; text-transform: uppercase; line-height: 1; color: var(--accent-2);
  background: rgba(249,115,22,.14); border: 1px solid rgba(249,115,22,.35); border-radius: 6px;
  padding: .2rem .45rem; margin-left: auto; animation: badge-blink 1.6s ease-in-out infinite;
}

/* -- Prova A/B dropdown Plugin: elenco verticale, un plugin per riga —
   icona a sinistra, titolo e descrizione impilati a destra — invece
   dell'elenco stretto originale di .nav-dropdown-menu-inner o della
   griglia 4-per-riga tentata prima. Vedi $tm_plugin_menu_v2 in
   header.php. Selettori con un secondo aggancio di classe
   (.nav-plugin-grid ...) apposta: servono più specifici di
   ".nav-dropdown-menu a"/".nav-dropdown-menu-inner" di base (pensati per
   l'elenco stretto originale) per poterne azzerare padding/white-space/
   display qui dentro, altrimenti quelle regole — dentro il media query
   desktop, dopo nel file — vincono a parità di specificità. */
.nav-dropdown-menu.nav-dropdown-menu--grid{ min-width: 20rem; padding-top: var(--space-4); }
.nav-dropdown-menu-inner.nav-plugin-grid{
  display: flex; flex-direction: column; gap: .1rem;
}
.nav-plugin-grid .nav-plugin-grid-item{
  position: relative;
  display: flex; flex-direction: row; align-items: center; text-align: left;
  gap: .85rem; padding: .6rem .75rem; white-space: normal;
  border-radius: var(--radius-md); color: var(--text-muted);
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.nav-plugin-grid .nav-plugin-grid-item:hover,
.nav-plugin-grid .nav-plugin-grid-item.is-active{ background: var(--glass-bg); color: var(--text); }
.nav-plugin-grid-icon{ width: 30px; height: 30px; object-fit: contain; flex-shrink: 0; }
.nav-plugin-grid-text{ display: flex; flex-direction: column; gap: .1rem; }
.nav-plugin-grid-name{
  display: inline-flex; align-items: center; gap: .35rem; white-space: nowrap;
  font-size: .86rem; font-weight: 600; color: var(--text);
}
/* Il badge "New" resta nell'angolo in alto a destra della riga, blu
   accent invece dell'arancio di .new-flag usato altrove (es. il
   dropdown originale sotto, spento — non tocca quella versione). */
.nav-plugin-grid-item .new-flag{
  position: absolute; top: .5rem; right: .5rem; margin-left: 0;
  color: var(--accent); background: var(--accent-soft); border-color: rgba(0,153,255,.35);
}
.nav-plugin-grid-desc{ font-size: .74rem; line-height: 1.3; color: var(--text-muted); }

/* Oscura la pagina dietro al dropdown "Plugin" aperto — senza questo, il
   pannello fluttuante è più stretto del contenuto pagina che sta subito
   dietro (es. il titolo della sezione "Our Plugins" quando ci sei appena
   scrollato), così quel contenuto sbircia visibilmente accanto agli
   angoli arrotondati del pannello e si legge come un glitch di
   rendering. Sta sotto header/dropdown (z-index 100/110) ma sopra tutto
   il resto della pagina. Usa :has() così l'elemento scrim può vivere fuori
   dal proprio stacking context dell'header e coprire semplicemente
   l'intero viewport. */
.page-scrim{
  position: fixed; inset: 0; z-index: 99;
  background: rgba(5,6,10,.6);
  backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity var(--dur-fast) var(--ease);
}
@media (min-width: 901px){
  body:has(.nav-dropdown--plugins:hover) .page-scrim,
  body:has(.nav-dropdown--plugins.is-hover-open) .page-scrim,
  body:has(.nav-dropdown--plugins:focus-within) .page-scrim,
  body:has(.nav-dropdown--project:hover) .page-scrim,
  body:has(.nav-dropdown--project.is-hover-open) .page-scrim,
  body:has(.nav-dropdown--project:focus-within) .page-scrim{
    opacity: 1; visibility: visible;
  }

  .nav-dropdown-menu{
    /* top:100% senza margine — a filo col trigger, nessuno spazio vuoto,
       così il mouse non attraversa mai uno spazio "morto" tra link e
       pannello perdendo l'hover a metà strada. Lo spazio visivo arriva
       invece dal padding-top, che resta comunque dentro il box hoverable. */
    position: absolute; top: 100%; left: 50%; padding-top: var(--space-4);
    transform: translateX(-50%);
    min-width: 12.5rem;
    opacity: 0; visibility: hidden; pointer-events: none;
    transition: opacity var(--dur-fast) var(--ease);
    z-index: 110;
  }
  .nav-dropdown-menu-inner{
    /* Pannello scuro quasi solido (non il solito vetro a bassa opacità) —
       fluttuando sopra contenuto che scorre, deve restare leggibile
       indipendentemente da cosa c'è dietro. */
    background: rgba(15,16,23,.97); border: 1px solid var(--glass-border-strong); border-radius: var(--radius-md);
    padding: var(--space-2);
    display: flex; flex-direction: column; gap: .15rem;
    box-shadow: 0 20px 40px -12px rgba(0,0,0,.6);
    transform: translateY(4px);
    transition: transform var(--dur-fast) var(--ease);
  }
  /* .is-hover-open: stesso effetto di :hover, ma con un margine di
     tolleranza gestito da js/menu/nav.js — con un trigger stretto ("Plugin",
     ~49px) e un pannello molto più largo e centrato sotto di esso, un
     mouse in diagonale può uscire per un istante sia dal trigger sia
     dal pannello prima di rientrarci, perdendo l'hover CSS puro a metà
     strada anche se ci si stava dirigendo dritti verso il pannello. */
  .nav-dropdown:hover .nav-dropdown-menu,
  .nav-dropdown.is-hover-open .nav-dropdown-menu,
  .nav-dropdown:focus-within .nav-dropdown-menu{
    opacity: 1; visibility: visible; pointer-events: auto;
  }
  .nav-dropdown:hover .nav-dropdown-menu-inner,
  .nav-dropdown.is-hover-open .nav-dropdown-menu-inner,
  .nav-dropdown:focus-within .nav-dropdown-menu-inner{
    transform: translateY(0);
  }

  /* Una sola sottolineatura "pagina corrente" accesa alla volta. Senza
     questo, stando su una pagina (es. Storia, sotto Il Progetto) e
     passando il mouse su un'altra voce (es. Plugin), restavano accese
     ENTRAMBE le sottolineature — quella nuova dell'hover e quella vecchia
     della pagina corrente — disorientante con due voci vicine. Prima
     regola: appena il mouse entra da qualche parte nella nav, spegne
     tutte le sottolineature "pagina corrente". Seconda regola, più
     specifica (un selettore <li> in più, vince sempre sulla prima a
     prescindere dall'ordine): la riaccende se il mouse è dentro il <li>
     di quella stessa voce — copre sia il link semplice sia, per i
     dropdown, il trigger E il suo pannello aperto (stesso <li>), non solo
     il trigger stesso. Appena il mouse esce dalla nav, ul:hover torna
     falso e la sottolineatura reale torna da sola (regola base, fuori da
     questo media query). */
  .main-nav ul:hover a[aria-current="page"]::after,
  .main-nav ul:hover .nav-dropdown-trigger[aria-current="page"]::after{
    transform: scaleX(0);
  }
  .main-nav ul li:hover a[aria-current="page"]::after,
  .main-nav ul li:hover .nav-dropdown-trigger[aria-current="page"]::after{
    transform: scaleX(1);
  }

  /* La sottolineatura hover dei due trigger dropdown (Plugin, Il Progetto —
     entrambi <button class="nav-dropdown-trigger">, stessa struttura)
     resta accesa mentre il mouse scende nel pannello sotto: :hover
     "semplice" (.main-nav .nav-dropdown-trigger:hover::after sopra)
     richiede il puntatore esattamente sull'elemento trigger, ma il
     pannello è un fratello nel DOM, non un discendente — appena il mouse
     lascia il trigger per entrare nel pannello, quell'hover si spegne
     anche se si è ancora dentro lo stesso menu aperto. Qui invece basta
     essere dentro lo stesso <li> (trigger o pannello, non importa),
     stessa logica di is-hover-open/focus-within già usata per aprire il
     pannello stesso. */
  .main-nav .nav-dropdown:hover .nav-dropdown-trigger::after,
  .main-nav .nav-dropdown.is-hover-open .nav-dropdown-trigger::after,
  .main-nav .nav-dropdown:focus-within .nav-dropdown-trigger::after{
    transform: scaleX(1);
  }
}

.header-actions{ display: flex; align-items: center; gap: var(--space-4); }
/* Su desktop, .header-inner (logo/nav/header-actions) usa un gap più
   largo (--space-6) di quello interno a .header-actions (--space-4) —
   lo spazio tra l'ultima voce della nav (Shop) e Account risultava quindi
   il doppio di quello tra Account e Lingua. Margine negativo che compensa
   esattamente la differenza, così i tre bottoni restano equidistanti.
   Solo desktop: sotto i 900px .main-nav esce dal flusso (position:fixed),
   quindi qui .header-inner ha solo logo+header-actions e questo margine
   non è più il seam da correggere. */
@media (min-width: 901px){
  .header-actions{ margin-left: calc(var(--space-4) - var(--space-6)); }
}
/* Colonna normale del flex (non più position:absolute ancorato al bordo
   vero della finestra): quell'ancoraggio sfruttava il margine morto tra
   il bordo del .container centrato (max-width:1240px) e il bordo vero
   della finestra sui monitor larghi, ma sotto quella soglia il container
   tocca quasi il bordo vero — il lang-switch assoluto, non prenotando
   spazio in flusso, finiva sovrapposto all'icona Account che invece lo
   spazio ce l'ha. Da colonna flex riserva sempre il proprio spazio, mai
   sovrapponibile, fino al breakpoint mobile (dove viene nascosto). */
/* EN/IT invece del nome per esteso (English/Italiano): il pannello è
   stretto quanto il trigger, quindi anche centrato (comportamento di
   default di .nav-dropdown-menu) resta comodamente dentro il viewport
   pur con il trigger vicino al bordo destro — non serve più forzarlo
   ad aprirsi dal bordo destro come quando doveva ospitare i nomi lunghi. */
.header-actions .lang-switch .nav-dropdown-menu{
  min-width: 0;
}
.header-actions .lang-switch .nav-dropdown-menu a{
  padding: .5rem .6rem; gap: .5em; font-size: .82rem; justify-content: center;
}

/* A differenza della lingua, il trigger Account non è incollato al bordo
   destro — resta centrato sul trigger (comportamento di default di
   .nav-dropdown-menu), solo più largo per il testo più lungo delle voci. */
.header-actions .nav-dropdown--account .nav-dropdown-menu{ min-width: 15rem; }
.menu-toggle{
  /* Proprio stacking context, sopra al .main-nav a schermo intero
     (z-index:90) che comanda — altrimenti l'overlay aperto sta sopra e
     inghiotte il click destinato a chiuderlo. */
  position: relative; z-index: 95;
  display: none; flex-direction: column; justify-content: center; align-items: center; gap: 5px;
  width: 2.4rem; height: 2.4rem; border: 2px solid var(--border-strong); border-radius: var(--radius-md);
  transition: border-color var(--dur-fast) var(--ease);
}
/* Stesso trattamento bordo/hover di .header-actions .nav-account, per
   coerenza visiva tra le due icone accanto. */
.menu-toggle:hover{ border-color: var(--accent); }
/* Resta un semplice hamburger in entrambi gli stati (nessuna
   trasformazione a X) — lo stesso bottone apre e chiude il menu; solo il
   bordo si illumina quando è aperto. */
.menu-toggle-bar{ width: 17px; height: 1.5px; background: var(--text); }
.menu-toggle[aria-expanded="true"]{ border-color: var(--accent); }

/* --------------------------------------------------------------------------
   7. HERO — ref1 blocco di testo centrato + ref2 widget card riccamente bordata
-------------------------------------------------------------------------- */
.hero{ padding-top: var(--space-9); overflow: hidden; }

/* Vetrina passiva "showcase-before-after" — autoplay in loop, nessuna
   interazione utente (a differenza dell'hub before/after sotto): stesso
   trattamento vetro/ombra di .hub-before-after-panel per coerenza
   visiva, ma senza tab/miniature/handle trascinabile. Motore in
   js/media/showcase-before-after.js, dati in
   partials/showcase-before-after-data.php (stesse foto dell'hub sotto). */
.showcase-before-after-section{ padding-block: var(--space-9); border-top: 1px solid var(--border); }

.showcase-before-after-panel{
  position: relative; aspect-ratio: 3 / 2; overflow: hidden;
  border-radius: var(--radius-lg);
  box-shadow: 0 30px 60px -30px rgba(0,0,0,.6), var(--glass-shadow);
}

.showcase-before-after-img{ position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }

/* "after" entra come un taglio diagonale che scorre da destra a sinistra
   (niente più opacity/fade): clip-path polygon a 4 punti, stessi 2 punti
   in alto/basso in entrambi gli stati così il browser può interpolarli
   in modo continuo. Il bordo obliquo va da in alto-sinistra a in basso-
   destra ("\", inclinato verso il basso-destra): topX sempre 24 punti
   percentuali più piccolo di bottomX. Nascosto: entrambi i punti oltre il
   100% (tutto il poligono fuori campo a destra, .showcase-before-after-
   panel ha overflow:hidden). Rivelato: entrambi sotto lo 0% (poligono che
   copre tutta la larghezza del frame).

   "before" fa l'esatto complementare con lo stesso confine (topX/bottomX):
   copre tutto da un bordo lontano a sinistra (-50%, ben oltre il frame)
   fino alla stessa diagonale, quindi sparisce (si ritira verso sinistra)
   esattamente dove/quando "after" compare — non è "after" a coprirlo per
   sovrapposizione, sono due tagli sincronizzati e opposti sullo stesso
   confine.

   Nessuna CSS transition qui: clip-path è scritto via inline style, un
   frame alla volta, da requestAnimationFrame in js/media/showcase-
   before-after.js — sia in autoplay sia trascinando/passando sopra col
   mouse sullo scrubber sotto, stesso identico motore. È l'unico modo per
   "riprendere da dove si era rimasti" quando si esce dall'hover a metà
   taglio invece di ripartire sempre da capo, e sfugge in automatico a
   prefers-reduced-motion (che riguarda solo transition/animation CSS).
   Questi due valori restano solo come stato di riposo per il primissimo
   paint, prima che il JS giri. */
.showcase-before-after-img--before{ z-index: 1; clip-path: polygon(-50% 0%, 103% 0%, 127% 100%, -50% 100%); }
.showcase-before-after-img--after{ z-index: 2; clip-path: polygon(103% 0%, 100% 0%, 100% 100%, 127% 100%); }

.showcase-before-after-credit{
  position: absolute; z-index: 3; right: var(--space-3); bottom: var(--space-3);
  background: rgba(11,11,16,.72); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  color: var(--text-faint); border: 1px solid var(--border-strong);
  font-size: .68rem; font-weight: 700; letter-spacing: .04em;
  padding: .3rem .65rem; border-radius: var(--radius-sm); pointer-events: none;
}

/* Rullino/timeline sotto il pannello — completamente a parte (markup e
   classi proprie .showcase-before-after-strip-*, non tocca il pannello
   sopra). Ogni coppia è una "clip" .showcase-before-after-strip-clip con
   dentro due fotogrammi distinti e separati (.showcase-before-after-
   strip-frame--before/--after) in sequenza, come in una timeline di
   video editing — NESSUN gap da nessuna parte (né fra i due fotogrammi
   della stessa clip né fra una clip e la successiva, vedi sotto): un
   raggruppamento a coppie visibile farebbe "scattare" lo scrubber che
   scorre in continuo sopra la striscia. Niente riquadro/bordo sulla
   clip: l'unica evidenza è l'opacità del SINGOLO fotogramma corrente,
   scritta inline frame per frame dallo stesso rAF del pannello sopra
   (vedi js/media/showcase-before-after.js) — nessuna classe/CSS
   coinvolta, quindi il fotogramma acceso segue esattamente il taglio
   diagonale in corso. Righe di forellini sopra/sotto la striscia via
   background-image per il tocco "pellicola analogica". */
.showcase-before-after-strip{
  position: relative;
  display: flex;
  margin-top: var(--space-4); padding: 22px var(--space-2);
  background: var(--surface-2); border-radius: var(--radius-md);
  overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none;
  /* Due righe di forellini quadrati smussati (alto/basso) via un'unica
     tile SVG (rounded rect, non un cerchio) ripetuta in orizzontale, non
     un vero elemento: niente da riservare nel flusso flex della striscia,
     solo un tocco "pellicola analogica" nello sfondo. */
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='8'%3E%3Crect x='4.5' y='1.5' width='5' height='5' rx='1.2' fill='white' fill-opacity='.35'/%3E%3C/svg%3E"),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='8'%3E%3Crect x='4.5' y='1.5' width='5' height='5' rx='1.2' fill='white' fill-opacity='.35'/%3E%3C/svg%3E");
  background-repeat: repeat-x, repeat-x;
  background-size: 14px 8px, 14px 8px;
  background-position: top 5px left 4px, bottom 5px left 4px;
}
.showcase-before-after-strip::-webkit-scrollbar{ display: none; }

.showcase-before-after-strip-clip{ display: flex; flex: 0 0 auto; }

/* Non cliccabili (<span>, non <button>): il rullino è di sola lettura,
   l'unica interazione è lo scrubber sotto. Fotogrammi tutti a contatto,
   nessun gap fra "prima" e "dopo" della stessa coppia né fra una coppia
   e la successiva (vedi .showcase-before-after-strip/-clip sopra, niente
   più gap lì) — un raggruppamento a coppie visibile avrebbe fatto
   "scattare" lo scrubber che scorre in continuo sopra. L'arrotondamento
   resta, spostato qui dal contenitore-coppia al singolo fotogramma. */
.showcase-before-after-strip-frame{
  position: relative; display: block; flex: 0 0 auto; width: 4.2rem; aspect-ratio: 3 / 2;
  overflow: hidden; background: var(--surface-2); border-radius: 3px;
  opacity: .4;
  transition: opacity var(--dur-fast) var(--ease);
}
.showcase-before-after-strip-frame img{ position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }

/* Scrubber verticale — playhead stile timeline: scorre in continuo e in
   modo lineare per l'intero ciclo (still + taglio + hold), non solo
   durante il taglio, così si legge chiaramente che il tempo sta
   scorrendo. Posizionato via requestAnimationFrame in JS, non con una
   CSS transition (vedi tickScrubber in js/media/showcase-before-after.js)
   — niente "left" animato qui, JS scrive la posizione ad ogni frame sia
   in autoplay sia trascinando/passando sopra col mouse. È un figlio
   assoluto DENTRO la striscia scrollabile apposta: scorre insieme al
   contenuto invece di restare fisso sul viewport. */
.showcase-before-after-scrubber{
  position: absolute; top: 0; bottom: 0; left: 0;
  width: 3px; background: #fff;
  pointer-events: none; z-index: 2;
}
.showcase-before-after-scrubber-grip{
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 1.6rem; height: 1.15rem; border-radius: var(--radius-sm);
  background: #fff; color: #111;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 2px 8px rgba(0,0,0,.5);
  cursor: ew-resize; pointer-events: auto; touch-action: none;
}

/* Avvolge .hub-before-after-panel fuori da .hero stesso (vedi commento in
   index.html) così lo sfondo proprio della hero non deve mai essere
   vincolato per evitare di mostrarsi dietro il widget before/after.
   Padding superiore extra + un divisore gli danno chiaro respiro dalla
   hero sopra, invece di attaccarsi subito a ridosso. */
.hub-before-after-section{ padding-block: var(--space-9); border-top: 1px solid var(--border); }

.hero .container{ position: relative; z-index: 1; }

.hero-top{ display: grid; grid-template-columns: 1.15fr .85fr; gap: var(--space-8); align-items: stretch; }

.hero-copy{ max-width: 40rem; }
.hero-title{ margin-bottom: var(--space-5); }
.hero-lead{ font-size: var(--fs-lead); color: var(--text-muted); max-width: 42ch; margin: 0 0 var(--space-7); }

/* Prova home IT: lista con spunta bianca al posto dell'elenco dentro la
   frase di .hero-lead. Primo elemento a piena larghezza, il resto su 2
   colonne (vedi index-it.html, ".hero-check-list-full" sul primo <li>). */
.hero-check-list{
  display: grid; grid-template-columns: max-content max-content; gap: var(--space-3) var(--space-6);
  margin: 0 0 var(--space-6); list-style: none; padding: 0;
}
.hero-check-list li{ display: flex; align-items: center; gap: .65em; font-size: 1rem; color: var(--text); }
.hero-check-list-full{ grid-column: 1 / -1; }
/* Stessa spunta di .spotlight-check (vedi "Selezioni e Maschere
   Istantanee" negli spotlight sotto), stessa dimensione e colore. */
.hero-check-icon{
  flex-shrink: 0; width: 1.6rem; height: 1.6rem;
  color: var(--accent-2);
  display: flex; align-items: center; justify-content: center;
}
.hero-check-icon svg{ width: 22px; height: 17px; }

.hero-adobe-badge{ display: block; width: 16rem; height: auto; margin: var(--space-4) 0 var(--space-6); }
.hero-actions{ display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4); }

/* -- Riga "licenza a vita" (index.html + shop.html, subito prima di
   .platform-row) — ispirata al blocco benefit di shop.dxo.com/nik-collection,
   ridotta a due soli claim invece dei tre dell'originale (pagamento
   rateale escluso, non rilevante qui): licenza perpetua senza costi
   ricorrenti + sconto sui prossimi upgrade, lo stesso meccanismo già
   descritto in it/en/faq/multi-device-use.html e
   it/en/faq/update-and-upgrade-policy.html.
   Niente card/pillola colorata su nessuno dei due: entrambi i claim
   riusano lo stesso linguaggio visivo "piatto" di .platform-badge qui
   sotto (stessa dimensione icona, stesso colore bianco), così la riga si
   legge come un'estensione naturale della riga piattaforme invece che
   come un elemento promozionale a parte — peso normale apposta, non
   .platform-badge, per restare un filo più leggero della riga piattaforme.
   Il selettore ".hero-actions + .license-row" (non una classe dedicata)
   sfrutta l'ordine del DOM per aggiungere margine extra SOLO in home
   (index.html/index-it.html, dove .license-row segue .hero-actions): in
   shop.html .license-row segue invece .hero-adobe-badge, quindi lì la
   regola non scatta e resta il margine di base. */
.license-row{ display: flex; flex-wrap: wrap; align-items: center; row-gap: var(--space-2); column-gap: var(--space-6); margin: var(--space-5) 0 var(--space-6); }
.hero-actions + .license-row{ margin-top: var(--space-7); }
/* Sotto sempre, mai affiancato: dopo aver accorciato il testo della
   licenza (tolto "né costi nascosti") ci sarebbe spazio per stare sulla
   stessa riga del tag offerta lancio, ma quest'ultimo è ormai un blocco
   a due righe/numero grande pensato per stare sotto, non di fianco. Il
   :has() limita la regola solo a questa riga (quella con l'offerta
   lancio), senza toccare .license-row nelle altre pagine che riusano lo
   stesso componente. */
.license-row:has(.license-discount-tag--offer-countdown){ flex-direction: column; align-items: flex-start; }
.license-badge, .license-discount-tag{ display: inline-flex; align-items: center; gap: .6em; font-size: 1.1rem; font-weight: 400; color: var(--text); }
.license-badge svg, .license-discount-tag svg{ flex-shrink: 0; color: var(--text); }

/* -- Variante "offerta di lancio" del tag sconto: nome prodotto (bianco)
   sopra, poi la riga con numero enorme a sinistra e testo su due righe a
   destra (idea presa dalla sezione "10 anni",
   .anniversary-mark-number + .anniversary-title-wrap) — invece
   dell'icona + singola riga di prima. */
.license-discount-tag--offer-countdown{ flex-direction: column; align-items: flex-start; gap: .35rem; font-weight: 600; }
.license-discount-tag--offer-countdown .discount-product{
  font-family: var(--font-heading); font-weight: 700; font-size: 1.5rem; line-height: 1.2;
  letter-spacing: 1px; color: var(--ok); text-transform: uppercase;
}
.license-discount-tag--offer-countdown .discount-row{ display: flex; align-items: center; gap: var(--space-4); }
.license-discount-tag--offer-countdown .discount-percent{
  flex-shrink: 0;
  font-family: var(--font-heading); font-weight: 700; font-size: 2.5rem; line-height: 1;
  letter-spacing: -.02em; color: var(--ok);
}
.license-discount-tag--offer-countdown .discount-text{ display: flex; flex-direction: column; gap: 0; }
.license-discount-tag--offer-countdown .discount-line{
  font-family: var(--font-heading); font-weight: 700; text-transform: uppercase; letter-spacing: -.01em;
  color: var(--ok); line-height: 1.2;
}
.license-discount-tag--offer-countdown .countdown-timer{
  font-family: var(--font-heading); font-weight: 700; letter-spacing: -.01em;
  font-variant-numeric: tabular-nums; color: var(--text); line-height: 1.2;
}
.license-discount-tag--offer-countdown .discount-coupon{
  font-family: var(--font-heading); font-weight: 700; font-size: 1.5rem; letter-spacing: 1px; line-height: 1.2;
}
.license-discount-tag--offer-countdown .discount-coupon-label{ color: var(--text); }
.license-discount-tag--offer-countdown .discount-coupon-code{ color: var(--violet); }

/* -- Box fluttuante del countdown offerta (pagine elencate in
   CONFIG.floatPages, js/cart/offer-countdown.js): riusa di peso
   .license-discount-tag--offer-countdown per contenuto e tipografia (percentuale,
   etichetta, countdown), qui dentro una card autonoma con sfondo/bordo
   propri — stessi valori di .upgrade-promo — perché deve restare
   leggibile sopra sfondi di pagina qualsiasi, non solo dentro l'hero
   scuro. Solo desktop per ora: sotto i 900px (stesso breakpoint di
   .main-nav) resta nascosto, il mobile si affronta a parte. Il top fisso
   usa lo stesso riferimento di altezza header di #plugins/#workflow ecc.
   (90px, vedi sopra) più un margine. */
.offer-float{ display: none; }
@media (min-width: 901px){
  .offer-float{
    display: inline-flex;
    position: fixed; top: 6.5rem; right: var(--space-6); z-index: 80;
    padding: var(--space-4) var(--space-5);
    /* Sfondo pieno (non rgba trasparente come .upgrade-promo): qui il box
       resta visibile durante lo scroll sopra contenuto qualsiasi, una
       tinta trasparente lascerebbe intravedere ciò che scorre sotto. */
    background: var(--surface-2);
    border: 1px solid rgba(62,207,142,.3);
    border-radius: var(--radius-lg);
    box-shadow: 0 10px 30px rgba(0,0,0,.25);
  }
}

/* -- Riga piattaforme "disponibile su" (sotto le CTA della hero) --------- */
.platform-row{ display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-3); margin: var(--space-5) 0 var(--space-6); }
.platform-row-label{ font-size: .78rem; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; color: var(--text-faint); }
.platform-badges{ display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-6); }
.platform-badge{ display: inline-flex; align-items: center; gap: .6em; font-size: 1.1rem; font-weight: 600; color: var(--text); }
.platform-badge svg{ flex-shrink: 0; color: var(--text); }

/* -- Terzo badge della .license-row (shop.html + index.html): stessa
   struttura testo+icona di .license-badge, con l'aggiunta di un trigger
   "i" e un mini tooltip che spiega la verifica periodica della licenza
   senza far uscire l'utente dalla pagina d'acquisto — l'approfondimento
   completo resta in it/en/faq/activation-and-offline-use.html, linkato
   in fondo al tooltip per chi lo vuole comunque. Si apre a :hover/
   :focus-within, puramente CSS: niente JS da mantenere, e la tastiera
   (Tab sul trigger) funziona gratis via :focus-within. Sfondo quasi
   solido come .nav-dropdown-menu-inner, non il vetro a bassa opacità
   usato altrove, perché deve restare leggibile sopra contenuti diversi
   dietro di sé.
   Il :has() sotto risolve un problema di stacking, non decorativo: le due
   .page-hero-inner di shop.html (questa e quella della riga pagamenti,
   più sotto nel DOM) condividono lo stesso z-index:1, quindi a parità di
   valore vince l'ordine nel DOM e quella dei pagamenti sta sempre sopra
   — un z-index alto sul tooltip da solo non basta a scavalcarla, perché
   compete solo dentro il proprio stacking context. Alzare lo z-index
   della .page-hero-inner intera, ma solo mentre il tooltip è aperto,
   la fa vincere anche verso l'esterno. */
.license-info{ position: relative; display: inline-flex; }
.license-info-trigger{
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.2rem; height: 1.2rem; padding: 0; margin-left: .1em;
  background: none; border: none; cursor: pointer;
  color: var(--text-faint);
}
.license-info:hover .license-info-trigger,
.license-info:focus-within .license-info-trigger{ color: var(--text); }
.page-hero-inner:has(.license-info:hover),
.page-hero-inner:has(.license-info:focus-within){ z-index: 2; }
.license-tooltip{
  /* top:100% a filo, zero margine — lo spazio visivo verso il trigger
     arriva dal padding-top maggiorato qui sotto, non da un gap nel
     posizionamento: un margine vero lascerebbe una fascia morta tra "i"
     e tooltip dove il mouse non è sopra nessun elemento hoverable, e
     :hover su .license-info si perderebbe a metà strada. Stessa identica
     tecnica di .nav-dropdown-menu più sopra in questo file. */
  position: absolute; top: 100%; left: 50%;
  transform: translateX(-50%) translateY(-4px);
  width: 17rem; max-width: 80vw;
  padding: calc(var(--space-3) + var(--space-4)) var(--space-4) var(--space-4);
  background: rgb(15,16,23); border: 1px solid var(--glass-border-strong);
  border-radius: var(--radius-md);
  box-shadow: 0 20px 40px -12px rgba(0,0,0,.6);
  font-size: .86rem; line-height: 1.55; color: var(--text-muted); text-align: left;
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
  z-index: 20;
}
.license-info:hover .license-tooltip,
.license-info:focus-within .license-tooltip{
  opacity: 1; visibility: visible; pointer-events: auto;
  transform: translateX(-50%) translateY(0);
}
.license-tooltip p{ margin: 0 0 var(--space-2); }
.license-tooltip a{ color: var(--text); font-weight: 700; text-decoration: underline; text-underline-offset: 2px; }
.license-tooltip a:hover{ color: var(--accent-2); }

/* Tooltip a immagine su "queste immagini" (story.html, sezione Filosofia):
   stessa tecnica hover/focus-within di .license-info/.license-tooltip
   sopra, ma capovolta (si apre verso l'alto, bottom:100% non top:100%) per
   non sovrapporsi alla firma subito sotto nel testo, e con un'immagine al
   posto del testo — niente div "-inner" separato per il buffer anti-gap
   (vedi nota su .nav-dropdown-menu più sopra nel file): qui basta il
   padding-bottom sul box di posizionamento stesso, l'unico figlio è
   l'immagine che porta già tutto lo stile visibile (bordo/ombra/radius),
   così il buffer resta invisibile invece di allargare un padding visibile
   intorno alla foto. */
.image-tooltip{ position: relative; display: inline-flex; }
.image-tooltip-card{
  position: absolute; bottom: 100%; left: 50%;
  padding-bottom: var(--space-3);
  transform: translateX(-50%) translateY(-4px);
  width: 18rem; max-width: 80vw;
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
  z-index: 20;
}
.image-tooltip-card a{ position: relative; display: block; }
.image-tooltip-card img{
  display: block; width: 100%; height: auto;
  border-radius: var(--radius-sm); border: 1px solid var(--glass-border-strong);
  box-shadow: 0 20px 40px -12px rgba(0,0,0,.6);
}
/* Etichetta invito al click, sempre visibile sopra la foto finché la card
   è aperta (non un secondo hover sull'immagine stessa: la card compare
   già solo al hover/focus del link "queste immagini", un ulteriore
   passaggio a comparsa qui sopra sarebbe un gesto in più per capire che
   la foto è cliccabile). */
.image-tooltip-overlay{
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,.5);
  border-radius: var(--radius-sm);
  pointer-events: none;
}
.image-tooltip-btn{
  padding: .35rem .9rem;
  background: rgba(0,0,0,.85);
  color: #fff; font-size: .78rem; font-weight: 700; letter-spacing: .02em;
  border-radius: var(--radius-pill);
}
.image-tooltip:hover .image-tooltip-card,
.image-tooltip:focus-within .image-tooltip-card{
  opacity: 1; visibility: visible; pointer-events: auto;
  transform: translateX(-50%) translateY(0);
}

/* -- it/en/shop.html (dentro .page-hero-inner) + index.html (fuori da
   .hero-top, sotto sia .hero-copy che .hero-preview, larga quanto l'intero
   .container): "Disponibile su" + "Compatibile con" + "Licenza" affiancate
   come colonne di una stessa riga invece di righe impilate una sotto
   l'altra — sostituisce la vecchia .check-row/.compat-row con checkmark
   verdi (rimossa). In index.html sta apposta FUORI da .hero-copy: dentro
   sarebbe stata compressa nella sola metà sinistra della hero (~55% del
   container), qui invece ha tutta la larghezza della pagina per restare
   leggibile su una riga sola senza bisogno di rimpicciolire testo/icone.
   Ogni colonna resta un .platform-row normale, solo con margine azzerato
   perché lo spazio sopra/sotto lo gestisce già .specs-columns per l'intero
   gruppo. */
.specs-columns{ display: flex; flex-wrap: wrap; gap: var(--space-8); margin: var(--space-5) 0 var(--space-6); }
.specs-columns .platform-row{ margin: 0; }

/* -- Anteprima plugin nella hero (badge release + screenshot statico) ---- */
.hero-preview{ display: flex; flex-direction: column; justify-content: center; gap: var(--space-4); }

.badge-pill--release{
  border-width: 2px; border-radius: var(--radius-md);
  border-color: rgba(249,115,22,.35); background: rgba(249,115,22,.12); color: var(--text);
  backdrop-filter: none; -webkit-backdrop-filter: none;
}
.badge-dot--blink{ background: var(--accent-2); animation: badge-blink 1.6s ease-in-out infinite; }
@keyframes badge-blink{
  0%, 100%{ opacity: 1; transform: scale(1); }
  50%{ opacity: .35; transform: scale(.7); }
}

/* flex column invece di blocco semplice: se un'anteprima ha due
   screenshot impilati restano separati da uno spazio invece di
   toccarsi — con una sola immagine si comporta come prima (position:
   relative resta, serve alla variante --stack sotto con i figli
   assoluti). */
.hero-preview-media{ position: relative; display: flex; flex-direction: column; align-items: center; gap: var(--space-4); }
.hero-preview-media img{
  display: block; width: auto; height: auto;
  max-width: 100%; margin-inline: auto;
  filter: drop-shadow(0 24px 32px rgba(0,0,0,.45));
}
/* Scoped a .page-hero (le 8 pagine prodotto), non alla home: la home usa
   lo stesso identico markup .hero-preview-media--stack .hero-preview-img
   (sezione .hero, non .page-hero) per lo screenshot di Tonality Masks in
   hero, che deve restare immune a questo trattamento -- vedi le due
   varianti sotto (--cascade/--stack), stesso scoping. */
.page-hero .hero-preview-media img{
  border: 2px solid #0099ff; box-shadow: #0099ff 0px 0px 10px 0px; border-radius: 8px;
}

/* -- Cascata di 3 pannelli identici per dimensione (hero Sharpen: Detail/
   Structure/Volume, stesso screenshot delle tab hub sotto) invece
   dell'unico screenshot "pannello intero" altissimo. Offset diagonale
   fisso in px (non %, i tre pannelli sono già alla stessa dimensione
   naturale), spaziatura ampia per staccarli bene — z-index crescente
   verso l'ultimo dichiarato nell'HTML (Volume), che finisce così più
   in vista/avanti. */
.hero-preview-media--cascade{
  position: relative;
  width: 446px; height: 566px;
}
.hero-preview-media--cascade img{
  position: absolute; top: 0; left: 0; width: 266px; height: auto;
  filter: drop-shadow(0 20px 28px rgba(0,0,0,.4));
}
.page-hero .hero-preview-media--cascade img{
  border-radius: 8px; border: 2px solid #0099ff; box-shadow: #0099ff 0px 0px 10px 0px;
}
.hero-preview-media--cascade img:nth-child(1){ z-index: 1; top: 0; left: 0; }
.hero-preview-media--cascade img:nth-child(2){ z-index: 2; top: 105px; left: 90px; }
.hero-preview-media--cascade img:nth-child(3){ z-index: 3; top: 210px; left: 180px; }

/* -- Prova: due immagini leggermente sovrapposte, stile "presentazione app"
   (prima a sinistra/sopra, seconda a destra/sotto), con una vera aurora
   boreale dietro le card: 3 fasce conic-gradient (verde/ciano/blu/viola/
   rosa nordici) che ruotano di continuo tramite @property + custom angle,
   sfocate. Tecnica ispirata a codepen.io/Ahmod-Musa/pen/emNqPQd (variabili
   e classi proprie, non è codice copiato). Solo desktop, solo test. */
.hero-preview-media--stack{ padding: 0 8% 10% 0; }

@property --nlights-a1{ syntax: '<angle>'; initial-value: 0deg; inherits: false; }
@property --nlights-a2{ syntax: '<angle>'; initial-value: 60deg; inherits: false; }
@property --nlights-a3{ syntax: '<angle>'; initial-value: 120deg; inherits: false; }

/* Una sola istanza condivisa dietro l'intero stack (le due card insieme),
   non una per singola immagine. Il riquadro sporge oltre i bordi dello
   stack su tutti i lati (inset negativo, clippato da overflow:hidden):
   niente mask-image (rimossa — in Chrome dava un taglio netto invece che
   sfumato), la morbidezza viene solo dal blur e dalle fasce stesse che
   sfumano naturalmente a transparent nel loro conic-gradient. Niente
   traslazione del gruppo: il movimento resta SOLO la rotazione dei
   conic-gradient via @property, esattamente come nel codepen di
   riferimento. */
.nlights{
  position: absolute; inset: -125% -75% -25% 5%; z-index: 0; pointer-events: none;
  filter: blur(20px);
  opacity: .8;
  animation: nlights-pulse 12s ease-in-out infinite alternate;
}
@keyframes nlights-pulse{
  0%  { opacity: .6; filter: blur(20px); }
  50% { opacity: .9;  filter: blur(16px); }
  100%{ opacity: .65; filter: blur(24px); }
}
.nlights-band{
  position: absolute; width: 200%; left: -50%;
  border-radius: 50%; transform-origin: center center;
}
.nlights-band-1{
  height: 60%; top: -12%;
  background: conic-gradient(from var(--nlights-a1) at 40% 60%,
    transparent 0%,
    rgba(0,255,200,.55) 15%,
    rgba(0,170,255,.45) 30%,
    transparent 45%,
    rgba(170,68,255,.35) 60%,
    transparent 75%
  );
  animation: nlights-spin1 18s linear infinite;
}
.nlights-band-2{
  height: 65%; top: 6%;
  background: conic-gradient(from var(--nlights-a2) at 60% 40%,
    transparent 0%,
    rgba(255,102,170,.45) 10%,
    rgba(0,255,200,.35) 25%,
    transparent 40%,
    rgba(0,170,255,.55) 55%,
    transparent 70%
  );
  animation: nlights-spin2 24s linear infinite reverse;
}
.nlights-band-3{
  height: 55%; top: -6%;
  background: conic-gradient(from var(--nlights-a3) at 30% 70%,
    transparent 0%,
    rgba(68,255,170,.45) 20%,
    transparent 35%,
    rgba(255,102,170,.35) 50%,
    rgba(170,68,255,.45) 65%,
    transparent 80%
  );
  animation: nlights-spin3 20s linear infinite;
}
@keyframes nlights-spin1{ to{ --nlights-a1: 360deg; } }
@keyframes nlights-spin2{ to{ --nlights-a2: 420deg; } }
@keyframes nlights-spin3{ to{ --nlights-a3: 480deg; } }
.hero-preview-media--stack .hero-preview-img{
  position: relative; width: 320px; height: 498px; margin-inline: 0;
}
/* .page-hero: le 8 pagine prodotto, bordo/bagliore blu accent. La home
   (sezione .hero, Tonality Masks v10) usa questa stessa variante --stack
   per il proprio screenshot in hero ma ha il suo trattamento a parte,
   bianco invece di blu -- vedi .hero .hero-preview-media--stack
   .hero-preview-img qui sotto. */
.page-hero .hero-preview-media--stack .hero-preview-img{
  border-radius: 8px; border: 2px solid #0099ff; box-shadow: #0099ff 0px 0px 10px 0px;
}
.hero .hero-preview-media--stack .hero-preview-img{
  border-radius: 8px; border: 1px solid rgba(255,255,255,.35); box-shadow: rgba(255,255,255,.35) 0px 0px 10px 0px;
}
.hero-preview-media--stack .hero-preview-img--back{
  z-index: 2;
  margin-left: 0; margin-right: auto;
  filter: drop-shadow(0 20px 28px rgba(0,0,0,.4));
}
.hero-preview-media--stack .hero-preview-img--front{
  position: absolute; z-index: 1;
  right: 0; bottom: -6%; left: auto;
  filter: drop-shadow(0 30px 40px rgba(0,0,0,.55));
}

.hub-before-after-panel{
  /* Ricetta glass standard (nessun boost saturate(), così le macchie di
     hero-glow dietro la tingono meno pur restando altrettanto trasparente). */
  background: var(--glass-bg); border: 1px solid var(--glass-border); border-radius: var(--radius-lg);
  backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px);
  overflow: hidden;
  box-shadow: 0 30px 60px -30px rgba(0,0,0,.6), var(--glass-shadow);
}
.hub-before-after-tabs{
  display: flex; flex-wrap: wrap; gap: var(--space-2);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border);
}
.hub-before-after-tab{
  padding: .45rem 1rem; border-radius: var(--radius-sm);
  font-size: .82rem; font-weight: 600; color: var(--text-muted);
  border: 1px solid transparent;
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.hub-before-after-tab:hover{ color: var(--text); }
.hub-before-after-tab.is-active{ color: var(--text); background: var(--accent-soft); border-color: var(--accent); }

/* Slider di comparazione before/after trascinabile. --pos è una custom
   property registrata così sia il ritaglio sia la maniglia possono fare
   transizione morbidamente quando impostati via codice (tab, bottoni
   Before/After, tastiera); il trascinamento disattiva la transizione
   tramite .is-dragging così la linea segue il puntatore senza ritardo. */
@property --pos{
  syntax: '<percentage>';
  inherits: true;
  initial-value: 50%;
}

.compare-media{
  --pos: 50%;
  position: relative; aspect-ratio: 3 / 2; overflow: hidden;
  touch-action: none;
}

.compare-img{ position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.compare-img--after{ z-index: 1; }
.compare-img--before{
  z-index: 2;
  clip-path: inset(0 calc(100% - var(--pos)) 0 0);
  transition: clip-path var(--dur-slow) var(--ease);
}
.compare-media.is-dragging .compare-img--before{ transition: none; }

.compare-handle{
  position: absolute; top: 0; bottom: 0; left: var(--pos); transform: translateX(-50%);
  width: 2.75rem; z-index: 3;
  display: flex; align-items: center; justify-content: center;
  cursor: ew-resize;
  transition: left var(--dur-slow) var(--ease);
}
.compare-media.is-dragging .compare-handle{ transition: none; }
.compare-handle::before{
  content: ""; position: absolute; top: 0; bottom: 0; left: 50%; width: 2px;
  background: var(--text); opacity: .85; transform: translateX(-50%);
}
.compare-handle-grip{
  position: relative; width: 2.25rem; height: 2.25rem; border-radius: 50%;
  background: var(--text); color: var(--bg);
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 4px 14px rgba(0,0,0,.35);
}
.compare-handle:focus-visible{ outline: none; }
.compare-handle:focus-visible .compare-handle-grip{ outline: 2px solid var(--accent); outline-offset: 3px; }

.compare-label{
  position: absolute; top: var(--space-3);
  background: rgba(11,11,16,.72); backdrop-filter: blur(6px);
  color: var(--text); border: 1px solid var(--border-strong);
  font-size: .68rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  padding: .3rem .65rem; border-radius: var(--radius-sm); z-index: 3; pointer-events: none;
}
.compare-label--before{ left: var(--space-3); }
.compare-label--after{ right: var(--space-3); }

/* Credito fotografo opzionale (data-credit, vedi js/media/before-after.js)
   — testo semplice, niente pillola/sfondo come .compare-label sopra: solo
   un'ombra leggera per restare leggibile sopra la foto qualunque sia la
   sua luminosità. */
.compare-credit{
  position: absolute; z-index: 3; right: var(--space-3); bottom: var(--space-3);
  color: rgba(255,255,255,.5);
  font-size: .72rem; font-weight: 400; letter-spacing: .02em;
  text-shadow: 0 1px 3px rgba(0,0,0,.7);
  pointer-events: none;
}

.hub-before-after-foot{
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  border-top: 1px solid var(--border);
  font-size: .78rem; color: var(--text-faint);
  font-variant-numeric: tabular-nums;
  cursor: default;
}
.hub-before-after-credit{ color: var(--text-faint); transition: color var(--dur-fast) var(--ease); }
.hub-before-after-credit:hover{ color: var(--text); }
/* Senza href (credito foto reale, non un link Unsplash) niente hover
   acceso e niente cursore a manina — è testo, non un link. */
.hub-before-after-credit:not([href]){ cursor: default; }
.hub-before-after-credit:not([href]):hover{ color: var(--text-faint); }

/* -- Selettore foto before/after — miniature per categoria, stesso filtro
   di finto editing del layer "before" dello slider principale così la
   divisione si legge chiaramente anche in piccolo. ------------------- */
.hub-before-after-thumbs{
  display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border);
}
.hub-before-after-thumb{
  position: relative; flex: 0 0 auto; width: 7.5rem; aspect-ratio: 3 / 2;
  border-radius: var(--radius-sm); overflow: hidden; background: #000;
  border: 2px solid transparent; padding: 0; cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease), opacity var(--dur-fast) var(--ease);
}
.hub-before-after-thumb:hover{ border-color: var(--border-strong); }
.hub-before-after-thumb.is-active{ border-color: var(--accent); }
.hub-before-after-thumb-media{ position: absolute; inset: 0; }
.hub-before-after-thumb-img{ position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.hub-before-after-thumb-img--before{ clip-path: inset(0 50% 0 0); }
.hub-before-after-thumb-divider{ position: absolute; inset: 0 auto 0 50%; width: 1px; background: rgba(255,255,255,.6); }

/* --------------------------------------------------------------------------
   8. INTRO / VALUE PROPOSITION
-------------------------------------------------------------------------- */

.intro{ padding-block: var(--space-10); border-top: 1px solid var(--border); }
.intro-grid{ display: grid; grid-template-columns: 1.3fr .7fr; gap: var(--space-9); align-items: start; }
.intro-lead h2{ margin-bottom: var(--space-5); }
.intro-body{ color: var(--text-muted); font-size: var(--fs-lead); max-width: 46ch; margin-bottom: var(--space-6); }

.intro-card{
  padding: var(--space-6);
}
.intro-facts{ display: flex; flex-direction: column; gap: var(--space-5); }
.intro-facts li{ display: flex; flex-direction: column; gap: .3rem; padding-bottom: var(--space-5); border-bottom: 1px solid var(--border); }
.intro-facts li:last-child{ padding-bottom: 0; border-bottom: 0; }
.intro-fact-num{ font-size: 1.9rem; font-weight: 800; letter-spacing: -.02em; color: var(--text); }
.intro-fact-label{ font-size: .82rem; color: var(--text-faint); }

/* --------------------------------------------------------------------------
   9. FEATURE CARD / METODO
-------------------------------------------------------------------------- */
.feature-section{ padding-block: var(--space-10); border-top: 1px solid var(--border); }
.feature-grid{ display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-5); }

.feature-card{
  padding: var(--space-6) var(--space-5);
}

.feature-card--featured{ border-color: var(--accent-2); background: var(--accent-2-soft); }
.feature-card--featured .feature-icon{ background: var(--accent-2); color: #fff; }

.feature-icon{
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.5rem; height: 2.5rem; border-radius: var(--radius-sm);
  background: var(--accent-soft); color: var(--accent);
  margin-bottom: var(--space-4);
}
.feature-card h3{ font-size: 1.08rem; margin-bottom: var(--space-2); }
.feature-card p{ color: var(--text-muted); font-size: .9rem; }

/* Corpo lungo in prosa (about-gaspare.html) — più largo del default di
   .section-sub (42ch, pensato per un sottotitolo breve sotto un heading
   centrato, non per un intero racconto) e allineato a sinistra: un blocco
   di testo giustificato al centro della pagina su più righe si legge
   peggio di uno a bandiera a sinistra, soprattutto quando è lungo come
   qui. I paragrafi che aprono un nuovo argomento hanno un margin-top
   maggiore (var(--space-8), inline nell'HTML) invece di un'etichetta
   dedicata: la spaziatura da sola basta a segnare lo stacco. */
.bio-body{ max-width: 46rem; margin-inline: auto; text-align: left; }
.bio-body p{ color: var(--text-muted); font-size: var(--fs-lead); margin-top: var(--space-4); }
.bio-body p:first-child{ margin-top: 0; }

/* --------------------------------------------------------------------------
   10. SEZIONE VISUAL / IMMAGINE
-------------------------------------------------------------------------- */
.visual-section{ position: relative; overflow: hidden; padding-block: var(--space-10); border-top: 1px solid var(--border); }
.visual-section::before{
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: radial-gradient(50% 65% at 12% 40%, rgba(0,114,245,.14), transparent 70%);
}
.visual-section:has(.visual-grid--reverse)::before{ background: radial-gradient(50% 65% at 88% 40%, rgba(240,153,61,.14), transparent 70%); }
/* PROVA (it/blending.html): la sezione before/after+Lorem Ipsum non deve
   avere il bagliore blu di sfondo (di serie su ogni .visual-section). */
.visual-section--no-glow::before{ display: none; }
.visual-section > .container{ position: relative; z-index: 1; }

.visual-grid{ display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-9); align-items: center; }
/* Duplicato SPECTRUM LUM/COL: card testo e slider before/after quasi a
   contatto (non il gap ampio di default), e colonna dello slider più
   larga (2fr) di quella della card testo (1fr) invece di metà a testa. */
/* align-items:stretch invece del centrato di .visual-grid: la card nera
   deve arrivare alta quanto la foto before/after accanto (che resta
   sempre lei a dettare l'altezza — il suo aspect-ratio 3/2 in
   .compare-media non è toccato), non restare più corta e centrata in
   mezzo a spazio vuoto sopra/sotto. .visual-photo-card-copy sotto
   diventa un flex a colonna apposta per distribuire lo spazio in più
   (bottone spinto in fondo con margin-top:auto) invece di lasciarlo
   tutto vuoto sotto la lista. */
.visual-grid--tight{ gap: var(--space-2); grid-template-columns: 1fr 2fr; align-items: stretch; }
/* Blocco speculare (Blending/Pirola): stessi proporzioni ma capovolte,
   slider più largo a sinistra invece che a destra — l'ordine nel DOM è
   già invertito (slider prima, card dopo), qui serve solo rispecchiare
   le proporzioni delle colonne. */
.visual-grid--tight-reverse{ grid-template-columns: 2fr 1fr; }
/* Sezione "Filosofia" di story.html: immagine (a sinistra, ordine DOM
   normale, nessun --reverse) ridotta a ~1/3 dello spazio invece di metà,
   il testo prende il resto. */
.visual-grid--narrow-media{ grid-template-columns: 1fr 2fr; }
/* Stesse proporzioni di sopra ma con foto a destra (about-gaspare.html/
   about-luca.html): con --reverse i due elementi si scambiano posto via
   order (vedi 2122-2123 più sotto), quindi è .visual-copy a finire nella
   prima colonna del grid-template e .visual-media nella seconda — la
   sequenza di larghezze va invertita di conseguenza (2fr 1fr, non
   1fr 2fr) per tenere la foto stretta anche qui. */
.visual-grid--narrow-media.visual-grid--reverse{ grid-template-columns: 2fr 1fr; }
/* Foto allineata in alto invece che centrata verticalmente (default
   align-items: center di .visual-grid): con la bio lunga il ritratto
   tondo, più basso della colonna di testo, altrimenti finirebbe
   centrato a metà del blocco invece che in cima come una foto profilo. */
.visual-grid--narrow-media.visual-grid--reverse .visual-media{ align-self: start; margin-top: var(--space-8); }

/* -- Secondo before/after "extra" a piena larghezza, sotto la coppia
   testo/media di .visual-grid nella stessa sezione: invece di stare
   compresso a metà larghezza accanto al testo, occupa tutta la
   larghezza del container. -- */
.visual-extra{ margin-top: var(--space-7); }

.visual-media{ position: relative; }
.visual-media img{ width: 100%; height: auto; display: block; border-radius: var(--radius-lg); }
/* Modifier for screenshots (e.g. a plugin panel crop) that shouldn't
   stretch to fill the column: natural width, centered, capped at the
   column's own width. Used on the project-philosophy about page and on
   articles; the plain .visual-media img default above stays untouched
   for the many product/team pages that DO want full-width. */
.visual-media--auto img{ width: auto; max-width: 100%; margin-inline: auto; }
/* Ritratto rotondo (about-gaspare.html/about-luca.html), stesso
   trattamento di .team-media--round ma sulla foto grande della bio
   invece che sulla card team: aspect-ratio 1/1 + object-fit forzano il
   cerchio anche se in futuro arrivasse una foto non quadrata. */
.visual-media--round img{ border-radius: 50%; aspect-ratio: 1 / 1; object-fit: cover; }
/* clip-path (non border-radius + overflow:hidden) arrotonda gli angoli —
   un <video> decodificato via hardware spesso renderizza su una propria
   superficie del compositor che ignora il clipping basato su overflow, ma
   clip-path viene rispettato. Il drop-shadow vive su questo wrapper così
   non viene ritagliato dal proprio path. */
.visual-video-frame{
  width: 100%;
  clip-path: inset(0 round var(--radius-lg));
  filter: drop-shadow(0 20px 36px rgba(0,0,0,.45));
}
.visual-video-frame video{ width: 100%; height: auto; display: block; }

/* Sfondo del duplicato di SPECTRUM LUM/COL — VERO sfondo (niente bordi,
   arrotondamento o overflow propri: non deve leggersi come una card a sé
   sopra la sezione), a piena larghezza (edge to edge, non limitato al
   .container), alto quanto tutta .visual-section--photo-bg qui sotto.
   Statica, una sola immagine (niente più taglio diagonale/play-on-scroll:
   quel confronto ora vive nello slider trascinabile a destra, vedi
   .visual-photo-card-copy/.visual-media più sotto) — allineata in alto
   (la parte alta della foto è quella che conta, il resto può uscire dal
   ritaglio object-fit:cover) e sfocata via filter, puramente decorativa
   dietro la card di testo. .visual-section è già position:relative +
   overflow:hidden (regola base sopra in questo file) e .container è già
   z-index:1 (stessa regola base): basta questo layer assoluto dietro a
   tutto, senza bisogno di ridichiarare lo stacking. */
.visual-section--photo-bg{ min-height: 32rem; display: flex; align-items: center; }
.visual-section-bg{ position: absolute; inset: 0; z-index: 0; }
.visual-section-bg-img{
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: top; display: block;
  /* brightness abbassata insieme al blur nello stesso filter: sfondo
     scuro invece che una foto sfocata ma ancora chiara/luminosa dietro
     al testo. */
  filter: blur(6px) brightness(.5);
}

/* Titolo tirato fuori dalla card nera (era dentro insieme a lista/CTA),
   sopra la griglia a due colonne — stesso .eyebrow/h2 di sempre, solo
   senza lo sfondo scuro della card attorno: la foto scurita sopra basta
   già da sola a tenerlo leggibile. */
/* Niente più max-width stretto: senza il <br> forzato nel titolo (vedi
   index-it.html) deve poter stare su una riga sola invece di andare a
   capo per forza contro un limite di larghezza artificiale. */
.visual-photo-card-heading{ position: relative; z-index: 1; margin-bottom: var(--space-6); }
.visual-photo-card-heading--right{ text-align: right; }
.visual-photo-card-heading h2{ white-space: nowrap; }

/* Riga icona+titolo (prodotto associato al blocco, es. iconRed per
   Tonality Masks) — opzionale, solo sui blocchi che la usano. Sui
   blocchi --right sia il gruppo si sposta a destra (justify-content)
   sia l'ordine si inverte (row-reverse): l'icona deve stare dalla
   stessa parte in cui il testo è allineato, quindi a destra del titolo
   invece che a sinistra. */
.visual-photo-card-heading-row{ display: flex; align-items: center; gap: var(--space-3); }
.visual-photo-card-heading--right .visual-photo-card-heading-row{ flex-direction: row-reverse; justify-content: flex-start; }
.visual-photo-card-icon{ width: 5rem; height: 5rem; object-fit: contain; flex-shrink: 0; }

/* Card nera semi-trasparente con lista funzionalità + CTA, sovrapposta a
   sinistra sopra lo sfondo (parte come blocco normale nel flusso della
   griglia, quindi resta naturalmente a sinistra — lo slider before/after
   resta visibile a destra). Stesso trattamento vetro/blur delle altre
   card del sito (--glass-bg/--glass-border sono troppo chiari qui sopra
   una foto, quindi nero diretto invece del token glass). */
.visual-photo-card-copy{
  position: relative; z-index: 1;
  display: flex; flex-direction: column;
  background: rgba(8,8,12,.72); backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  border: 1px solid rgba(255,255,255,.08);
  border-radius: var(--radius-lg);
  padding: var(--space-7);
}
.visual-photo-card-copy .section-sub{ margin-bottom: var(--space-6); }
/* Card stirata alta quanto la foto (.visual-grid--tight, align-items:
   stretch sopra): il bottone va spinto in fondo invece di lasciare tutto
   lo spazio in più vuoto subito sotto la lista funzionalità. */
.visual-photo-card-copy .btn{ margin-top: auto; align-self: flex-start; }
/* Stesso trattamento "glass tinto" di .plugin-buy-btn--in-cart (prodotto
   già nel carrello, shop) e .hub-plugin-cta: non pieno, solo un velo di
   accent blu sopra il .btn-ghost neutro di base — .btn-ghost fornisce
   spessore/stile del bordo, qui si sovrascrive solo il colore (stesso
   doppio-selettore per vincere in specificità). */
.btn-ghost.visual-photo-card-cta{ background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.btn-ghost.visual-photo-card-cta:hover{ background: rgba(0,153,255,.22); }
/* .spotlight-features ha un border-top globale (era il divisorio dal
   titolo sopra, ora tirato fuori dalla card — vedi .visual-photo-card-
   heading): qui non serve più, tolto solo in questa card, non nella
   regola condivisa (.spotlight-features resta invariata per tutte le
   altre pagine che la usano). */
.visual-photo-card-copy .spotlight-features{ border-top: none; padding-top: 0; margin-top: 0; }

.visual-copy h2{
  margin-bottom: var(--space-4);
  font-size: clamp(2.1rem, 1.6rem + 2vw, 3rem);
}
.visual-copy .section-sub{ margin-bottom: var(--space-6); }

.meta-list{
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-4);
  padding: var(--space-5) 0; margin-bottom: var(--space-6);
  border-top: 1px solid var(--border); border-bottom: 1px solid var(--border);
}
.meta-label{ display: block; font-size: .7rem; text-transform: uppercase; letter-spacing: .06em; color: var(--text-faint); margin-bottom: .3rem; }
.meta-value{ display: block; font-weight: 700; font-size: 1rem; color: var(--text); }

/* --------------------------------------------------------------------------
   11. PROCESSO / PASSAGGI — pattern dominante ref1: pannello numerato + flusso a frecce
-------------------------------------------------------------------------- */
.process-section{ padding-block: var(--space-10); border-top: 1px solid var(--border); }

.process-flow{ display: flex; flex-direction: column; gap: var(--space-4); max-width: 640px; margin: var(--space-8) auto 0; }

.process-panel{
  padding: var(--space-6);
}
.process-panel-head{ display: flex; align-items: center; gap: var(--space-4); margin-bottom: var(--space-1); }
.process-panel-head h3{ font-size: 1.15rem; }

.process-num{
  display: inline-flex; align-items: center; justify-content: center;
  width: 2rem; height: 2rem; border-radius: 50%; flex-shrink: 0;
  font-family: var(--font-heading); font-weight: 700; font-size: .95rem; color: #fff;
}
.process-num--1{ background: var(--grad-a); }
.process-num--2{ background: color-mix(in srgb, var(--grad-a) 45%, var(--grad-b)); }
.process-num--3{ background: var(--grad-b); }
.process-num--4{ background: var(--grad-c); }

.mini-grid{
  display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-3);
  margin-top: var(--space-5); padding-top: var(--space-5); border-top: 1px solid var(--border);
}
.mini-grid div{ display: flex; flex-direction: column; gap: .25rem; }
.mini-grid span{ font-size: .68rem; text-transform: uppercase; letter-spacing: .05em; color: var(--text-faint); }
.mini-grid strong{ font-size: .84rem; font-weight: 600; color: var(--text); }

.process-arrow{ align-self: center; color: var(--text-faint); font-size: 1.1rem; line-height: 1; }

/* --------------------------------------------------------------------------
   12. CORSI
-------------------------------------------------------------------------- */
.courses-section{ padding-block: var(--space-10); border-top: 1px solid var(--border); }
.courses-grid{ display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-6); }

.course-card{
  display: flex; flex-direction: column;
}
.course-card--featured{ border-color: var(--accent-2); }

.course-badge{
  position: absolute; top: 0; left: 50%; transform: translate(-50%, -50%);
  z-index: 2;
  background: var(--accent-2); color: #17110a;
  font-size: .7rem; font-weight: 700; letter-spacing: .02em;
  padding: .35rem .9rem; border-radius: var(--radius-pill);
  box-shadow: 0 8px 16px -8px rgba(0,0,0,.5);
}

.course-media{ position: relative; display: block; aspect-ratio: 4 / 3; overflow: hidden; border-radius: var(--radius-lg) var(--radius-lg) 0 0; }
.course-media img{ width: 100%; height: 100%; object-fit: cover; transition: transform .6s var(--ease); }
.course-card:hover .course-media img{ transform: scale(1.06); }

.course-tag{
  position: absolute; top: var(--space-3); left: var(--space-3);
  background: rgba(11,11,16,.75); backdrop-filter: blur(6px);
  color: var(--text); border: 1px solid var(--border-strong);
  font-size: .68rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  padding: .3rem .65rem; border-radius: var(--radius-pill);
}

.course-body{ padding: var(--space-5); display: flex; flex-direction: column; gap: var(--space-2); flex: 1; }
.course-meta{ font-size: .72rem; letter-spacing: .04em; text-transform: uppercase; color: var(--text-faint); }
.course-body h3{ font-size: 1.1rem; }
.course-body h3 a:hover{ color: var(--accent); }
.course-body p{ color: var(--text-muted); font-size: .88rem; flex: 1; margin-bottom: var(--space-2); }

/* --------------------------------------------------------------------------
   13. CAROSELLO TESTIMONIANZE
-------------------------------------------------------------------------- */
.testimonials-section{ padding-block: var(--space-10); border-top: 1px solid var(--border); overflow: hidden; }

.carousel{ position: relative; max-width: 760px; margin-inline: auto; }

.carousel-viewport{ overflow: hidden; border-radius: var(--radius-lg); }

.carousel-track{
  display: flex;
  gap: var(--space-5);
  transition: transform var(--dur-slow) var(--ease);
  will-change: transform;
}

.testimonial-card{
  flex: 0 0 100%;
  min-width: 0;
  display: flex; flex-direction: column; gap: var(--space-5);
  padding: var(--space-7) var(--space-6);
}

.testimonial-stars{ display: flex; gap: .25rem; color: var(--accent-2); }

.testimonial-quote{ margin: 0; font-size: var(--fs-lead); color: var(--text); line-height: 1.55; }

.testimonial-person{ display: flex; align-items: center; gap: var(--space-4); margin-top: auto; }
.testimonial-avatar{ width: 3rem; height: 3rem; border-radius: 50%; object-fit: cover; border: 1px solid var(--border-strong); flex-shrink: 0; }
.testimonial-name{ font-weight: 700; font-size: .92rem; color: var(--text); }
.testimonial-role{ font-size: .8rem; color: var(--text-faint); }

.carousel-controls{ display: flex; align-items: center; justify-content: center; gap: var(--space-4); margin-top: var(--space-6); }

.carousel-arrow, .carousel-play{
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.75rem; height: 2.75rem; border-radius: 50%; flex-shrink: 0;
  border: 1px solid var(--glass-border-strong); color: var(--text); background: var(--glass-bg);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  transition: border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.carousel-arrow:hover, .carousel-play:hover{ border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }

.carousel-play{ margin-left: var(--space-2); color: var(--text-muted); }
.carousel-play .icon-play{ display: none; }
.carousel-play.is-paused .icon-pause{ display: none; }
.carousel-play.is-paused .icon-play{ display: block; }

.carousel-dots{ display: flex; align-items: center; gap: .5rem; }
.carousel-dot{
  width: 2.75rem; height: 2.75rem; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.carousel-dot::before{
  content: ""; width: .55rem; height: .55rem; border-radius: 50%; background: var(--border-strong);
  transition: background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.carousel-dot:hover::before{ background: var(--text-muted); }
.carousel-dot.is-active::before{ background: var(--accent); transform: scale(1.3); }

/* Modalità "autorotation" (js/media/carousel.js, initAutorotation): nastro
   continuo, niente scatti a pagine. Il track contiene esattamente due
   serie identiche di card (il clone, inserito PRIMA nel DOM, e
   l'originale) quindi la sua larghezza (width: fit-content, non 100%)
   è sempre il doppio di una singola serie: -50% equivale perciò sempre
   a "una serie intera", qualunque sia la larghezza reale delle card,
   calcolata dal browser al volo invece che con un valore in pixel
   congelato da JS in un istante potenzialmente sbagliato (variabile
   --marquee-distance, rimossa: era il punto fragile del vecchio
   approccio — vedi il CodePen di riferimento che usa la stessa tecnica
   percentuale, senza calcolo JS della distanza). Da 0 a -50% (una serie
   intera), il giro scorre verso sinistra e si richiude senza scatto
   visibile quando torna alla posizione di partenza: al 100% si vede il
   clone (identico all'originale) esattamente dove si vedeva l'originale
   all'inizio. animation-play-state passa a "paused" su hover/focus/fuori
   schermo (stessa gestione della pausa già usata per l'autoplay
   'static', vedi initAutorotation). */
@keyframes tm-carousel-autorotate{
  from{ transform: translateX(0); }
  to{ transform: translateX(-50%); }
}
.carousel-track--autorotation{
  width: fit-content;
  animation-name: tm-carousel-autorotate;
  animation-duration: 60s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

/* Stesso carosello, seconda "forma" di slide: .press-section lo riusa per
   le recensioni (.press-card) invece delle testimonianze — 3 card visibili
   insieme su desktop, 1 sotto i 900px (vedi data-carousel-visible* nel
   markup e la logica "visibleCount" in js/media/carousel.js, che
   imposta la larghezza di ogni .press-card via JS). Niente max-width
   stretto qui: riempie il .container come faceva la vecchia .press-grid. */
.carousel--press{ max-width: 100%; }
.carousel-track > .press-card{ min-width: 0; }

/* .press-card ha un bagliore esterno al passaggio del mouse (::after,
   inset:-1.5rem, blur 22px — vedi ".course-card, .team-card--link,
   .press-card"), che l'overflow:hidden di .carousel-viewport taglierebbe
   sopra/sotto. Soluzione: il ritaglio orizzontale (necessario solo per
   nascondere le slide fuori vista) si sposta su questo contenitore più
   esterno, che include anche titolo/descrizione sopra e frecce/pallini
   sotto — il bordo del ritaglio resta così lontano dalle card e il
   bagliore ha spazio per propagarsi. .carousel-viewport per la variante
   press torna quindi "aperto" (overflow:visible), il ritaglio stretto
   resta il default per qualunque altro uso di questo carosello. */
/* padding-inline + margin-inline negativo uguale: stesso trucco usato in
   verticale, ma qui il ritaglio orizzontale resta necessario (nasconde le
   slide fuori vista durante lo scorrimento, non può sparire del tutto).
   Su un box a width:auto i due si annullano a vicenda per il contenuto
   (la "finestra" a 3 card resta larga uguale), spostando solo il bordo
   del ritaglio. Il buffer deve restare ≤ al gap tra le card nel track
   (sotto), altrimenti si rivelerebbe un pezzo della card vicina fuori
   vista: per questo il gap qui è stato alzato da --space-5 (1.5rem) a
   2.5rem, così il bagliore delle card ai bordi ha più margine. */
.press-carousel-frame{ overflow: hidden; border-radius: var(--radius-lg); padding-inline: 2.5rem; margin-inline: -2.5rem; }
/* Su desktop allarga sia il wrapper (.section-heading--center, di suo
   52ch, troppo stretto per la frase intera a fs-lead) sia il tetto di
   .section-sub (42ch): così la descrizione sta su una riga sola invece
   di andare a capo a metà frase. Sotto i 901px restano i limiti di
   default, va a capo in base alla viewport come il resto del sito. */
@media (min-width: 901px){
  .press-section .section-heading--center{ max-width: 700px; }
  .press-section .section-sub{ max-width: none; }
}
.carousel--press .carousel-viewport{ overflow: visible; border-radius: 0; }

/* Il buffer sopra basta per l'inset (-1.5rem) del bagliore, ma non per la
   sua sfocatura: blur() in CSS è una deviazione standard gaussiana, la
   resa visibile si estende per circa 2.5-3× quel valore, quindi blur(22px)
   da solo propaga il bagliore altri ~55-65px oltre l'inset — molto più dei
   2.5rem (40px) di margine disponibile, motivo per cui ai bordi si vedeva
   ancora un limite netto. Qui per le card nel carosello si riduce inset e
   sfocatura così la resa visibile reale rientra nel buffer (~8px + ~30px
   ≈ 38px < 40px): bagliore più contenuto ma senza alcun taglio. */
.carousel-track > .press-card::after{ inset: -0.5rem; filter: blur(12px); }
.carousel--press .carousel-track{ gap: 2.5rem; }

/* Il buffer orizzontale sopra non nasconde solo il bagliore: ".press-card"
   ha anche un'ombra statica sempre attiva (box-shadow, non legata a
   :hover), e quella della card ancora fuori vista può bleedare dentro il
   buffer visibile. Il carosello (js/media/carousel.js, render()) mette
   già aria-hidden="true" sulle slide fuori dalla finestra corrente ad ogni
   scorrimento — riusiamo quell'attributo per spegnere l'ombra solo lì,
   senza toccare JS/DOM: si riattiva da sola non appena la card rientra
   nella finestra visibile. */
.carousel-track > .press-card[aria-hidden="true"]{ box-shadow: none; }

/* --------------------------------------------------------------------------
   14. TEAM CARD
-------------------------------------------------------------------------- */
/* Guscio comune a entrambi i contesti in cui appare .team-card: la
   sezione team della home (più link distinti dentro — social + Learn
   more — quindi la card NON può essere lei stessa un link, resta un
   <article> statico) e about.html (un solo link "Learn more", lì diventa
   .team-card--link, vedi 17d, e tutta la card è cliccabile). Stesso
   sfondo/bordo/radius/ombra di .course-card/.press-card in entrambi i
   casi, per restare visivamente identica; niente backdrop-filter qui per
   lo stesso motivo spiegato in 17d. */
.team-card{
  display: flex; flex-direction: column;
  /* var(--surface-2), non var(--glass-bg): la variante --link (Gaspare/
     Luca) mostra in realtà var(--surface-2) grazie alla maschera opaca
     ::before di 17d (dietro cui si nasconde il bagliore), che dipinge
     SOPRA questo background — usare qui lo stesso colore invece del
     var(--glass-bg) traslucido evita lo scarto visibile tra le due
     varianti di card. */
  background: var(--surface-2); border: 1px solid var(--glass-border); border-radius: var(--radius-lg);
  box-shadow: var(--glass-shadow);
  transition: border-color var(--dur-base) var(--ease);
}
/* Hover leggero (solo bordo, niente bagliore/cursor:pointer) per la
   variante senza link proprio — es. le card del tier 'team' senza pagina
   dedicata: il bagliore whole-card di 17d resta riservato a
   .team-card--link, dove la card è l'unico link (nessun social/
   link interno), altrimenti due hover in conflitto sulla stessa card. */
.team-card:hover{ border-color: var(--accent); }
.team-card--featured{ border-color: var(--accent-2); }

.team-media{ position: relative; aspect-ratio: 4 / 3; overflow: hidden; border-radius: var(--radius-lg) var(--radius-lg) 0 0; }
.team-media img{ width: 100%; object-fit: cover; }

.team-badge{
  position: absolute; top: var(--space-3); left: var(--space-3);
  background: var(--accent); color: #fff;
  font-size: .68rem; font-weight: 700; letter-spacing: .03em;
  padding: .32rem .7rem; border-radius: var(--radius-pill);
}
.team-card--featured .team-badge{ background: var(--accent-2); color: #17110a; }

/* Niente position:relative qui (non serve a nient'altro in questo
   componente): farebbe da "contenitore posizionato" più vicino per
   .team-more::after (lo stretched link, vedi sotto) invece di lasciarlo
   arrivare fino a .team-card--link, restringendo l'area cliccabile
   allargata alla sola .team-body invece che a tutta la card. */
.team-body{ display: flex; flex-direction: column; flex: 1; }
.team-body h3{ font-size: 1.05rem; margin-bottom: .2rem; }
/* Padding del corpo card volutamente NON qui sopra ma duplicato per
   variante (--link, --grid, --compact più sotto): oggi --link e --grid
   usano lo stesso valore, ma tenerli separati vuol dire poter cambiare
   uno dei due in futuro senza toccare l'altro. */
.team-card--link .team-body{ padding: var(--space-5); }
.team-card--grid .team-body{ padding: var(--space-5); }
/* Colore di default blu (--accent): è il caso base perché il tier 'team'
   (ambassador/divulgatori/content creator...) è la maggioranza delle
   persone in anagrafica — vedi partials/team-member-data.php. Il tier 'core'
   (Founder/CEO, Business Development) è invece l'eccezione, resa
   esplicita dal modificatore .team-role--core sotto, ricolorato in
   arancio (--accent-2) per distinguerlo a colpo d'occhio dal resto del
   team. */
.team-role{
  color: var(--text-muted); font-size: .74rem; font-weight: 400;
  text-transform: uppercase; letter-spacing: .05em;
  margin-bottom: .1rem;
}
/* Il core (Gaspare/Luca) ha vita propria, separata dal tier 'team': badge
   con bordo fuori/sopra la foto, non testo muted sotto come nel resto del
   team — override completo, non eredita nulla dalla base .team-role
   sopra (uppercase/letter-spacing/peso/colore/margine tutti diversi). */
.team-role--core{
  align-self: flex-start;
  margin: var(--space-4) var(--space-5) 0;
  padding: .3rem .85rem;
  border: 1px solid var(--accent-2); border-radius: var(--radius-sm);
  color: var(--accent-2); font-size: .78rem; font-weight: 700;
  text-transform: none; letter-spacing: normal;
}

.team-footer{
  margin-top: auto; display: flex; flex-direction: column; gap: var(--space-5);
  align-items: flex-start;
}
/* Link vero e proprio (mai <a> annidato: i social qui sotto sono anche
   loro <a> veri, e un <a> dentro un altro <a> è HTML non valido — per
   questo .team-card--link stessa non è più il tag <a>, vedi 17d).
   "Stretched link": ::after allarga l'area cliccabile di QUESTO link a
   coprire l'intera card (position:absolute; inset:0). Niente
   position:relative qui su .team-more stesso — diventerebbe lui il
   "contenitore posizionato" più vicino per il proprio ::after invece di
   lasciarlo risalire fino a .team-card--link (che ha position:relative,
   vedi 17d), restringendo l'area cliccabile al solo testo del link. Non
   serve nemmeno per lo z-index: senza position, .team-more resta
   comunque sopra allo sfondo/bagliore della card (entrambi a z-index
   negativo) per il normale ordine di pittura del CSS — l'unico z-index
   che serve davvero è quello dei social qui sotto, per restare sopra
   allo stretched link invece di finirci sotto. */
.team-more{
  display: inline-flex; align-items: center; gap: .4em;
  font-size: .82rem; font-weight: 700; color: var(--accent);
  transition: color var(--dur-fast) var(--ease);
}
.team-more::after{ content: ""; position: absolute; inset: 0; }
.team-more:hover{ color: var(--accent-2); }
.team-more svg{ transition: transform var(--dur-fast) var(--ease); }
.team-more:hover svg{ transform: translateX(2px); }

/* z-index:2, sopra all'overlay ::after di .team-more qui sopra: altrimenti
   i social sarebbero visivamente cliccabili ma il click verrebbe
   intercettato dallo stretched link sottostante invece di aprire il
   social. */
.team-social-row{ position: relative; z-index: 2; display: flex; flex-wrap: wrap; gap: var(--space-3); }
.team-social{
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--text-muted);
  transition: color var(--dur-fast) var(--ease);
}
.team-social:hover{ color: var(--accent); }

/* --------------------------------------------------------------------------
   15. STATISTICHE — griglia stat-box bordata ref1
-------------------------------------------------------------------------- */
.stats-section{ padding-block: var(--space-9); border-top: 1px solid var(--border); }
.stats-grid{ display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-4); }

.stat-box{
  display: flex; flex-direction: column; align-items: center; gap: var(--space-2);
  text-align: center; padding: var(--space-6) var(--space-4);
}
.stat-value{ font-family: var(--font-heading); font-size: clamp(1.9rem, 1.5rem + 1.4vw, 2.5rem); font-weight: 700; letter-spacing: -.02em; color: var(--text); }
.stat-label{ font-size: .82rem; color: var(--text-muted); }

/* --------------------------------------------------------------------------
   16. CTA FINALE
-------------------------------------------------------------------------- */
.final-cta{ padding-block: var(--space-10); border-top: 1px solid var(--border); overflow: hidden; }
.final-cta-inner{ position: relative; z-index: 1; text-align: center; max-width: 44rem; margin-inline: auto; }
.final-cta-lead{ color: var(--text-muted); font-size: var(--fs-lead); margin: var(--space-5) 0 var(--space-7); }
.final-cta-actions{ justify-content: center; }

/* --------------------------------------------------------------------------
   17. FOOTER
-------------------------------------------------------------------------- */

.site-footer{
  border-top: 1px solid var(--glass-border); padding-top: var(--space-9);
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
}

.footer-top{ display: grid; grid-template-columns: 1.4fr 1fr 1fr 1fr; gap: var(--space-7); padding-bottom: var(--space-8); }
.footer-brand p{ color: var(--text-muted); font-size: .9rem; margin: var(--space-4) 0 var(--space-5); max-width: 32ch; }

.social-row{ display: flex; gap: var(--space-3); }
.social-row a{
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--text-muted);
  transition: color var(--dur-fast) var(--ease);
}
.social-row a:hover{ color: var(--accent); }

.footer-col h4{
  font-size: .72rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--text-faint); margin-bottom: var(--space-4); font-weight: 700;
}
.footer-col ul{ display: flex; flex-direction: column; gap: var(--space-3); }
.footer-col a{ color: var(--text-muted); font-size: .9rem; transition: color var(--dur-fast) var(--ease); }
.footer-col a:hover{ color: var(--text); }

.footer-col--stacked{ display: flex; flex-direction: column; }
.footer-col-group + .footer-col-group{ margin-top: var(--space-6); }

.lang-switch--footer{ margin-top: var(--space-5); }

.footer-bottom{
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-3);
  padding-block: var(--space-5); border-top: 1px solid var(--border);
}
.footer-bottom p{ color: var(--text-faint); font-size: .8rem; }
.footer-legal{ display: flex; flex-wrap: wrap; row-gap: var(--space-2); }
.footer-legal li{ display: flex; align-items: center; }
.footer-legal li:not(:first-child)::before{ content: "|"; margin: 0 var(--space-3); color: var(--text-faint); }
.footer-legal a{ color: var(--text-faint); font-size: .8rem; transition: color var(--dur-fast) var(--ease); }
.footer-legal a:hover{ color: var(--text); }

.payment-icons{ display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; list-style: none !important; padding: 0; margin: 0; }
.payment-icons li{ line-height: 0; }
.payment-icons svg{
  width: 2.9rem; height: auto; border-radius: 6px;
  box-shadow: 0 0 0 1px var(--border);
}

.payment-row{ display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-3); margin-top: var(--space-6); }
.payment-row-label{ font-size: .78rem; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; color: var(--text-faint); }

/* --------------------------------------------------------------------------
   17b. SPOTLIGHT DEL PUNTATORE — MORTO: .plugin-card--home era la vecchia
   griglia plugin stile shop della home EN, sostituita dal dashboard
   hub-plugin-section (allineamento EN→IT). Nessun HTML nel progetto
   genera più questa classe, verificato su tutti i file — lasciato per
   ora, da rimuovere quando si pulisce.
   Luce morbida e rotonda DENTRO la card,
   che segue il cursore (--mx/--my via card-glow.js). Applicato SOLO a
   .plugin-card--home (home): su Shop .plugin-card è una card con più
   elementi cliccabili al suo interno (link "Scopri di più" + bottone
   "Aggiungi al Carrello"), quindi su richiesta l'hover/lo sfondo dinamico
   restano disattivati sull'intera card — il solo hover che deve restare
   è quello nativo del bottone .plugin-buy-btn.
   È proprio questo meccanismo — backdrop-filter (blur) + angoli
   arrotondati + un ::before che si ridisegna ad ogni pixel di mousemove
   — a far sfarfallare una sottile striscia in alto sulle altre card:
   per questo le card cliccabili (17c) usano un bagliore ESTERNO invece,
   che non convive con blur+clip sullo stesso elemento.
-------------------------------------------------------------------------- */
.plugin-card{
  position: relative; isolation: isolate; overflow: hidden;
}
.plugin-card::before{
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: radial-gradient(240px circle at var(--mx, 50%) var(--my, 50%), rgba(0,114,245,.22), transparent 70%);
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease);
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   17c. CARD BASE — guscio "vetro" condiviso da tutte le card statiche,
   non cliccabili (nessun link dentro, quindi nessun hover): stesso
   sfondo/bordo/radius/blur/ombra per tutte.
-------------------------------------------------------------------------- */
.feature-card, .intro-card, .testimonial-card, .process-panel, .stat-box{
  background: var(--glass-bg); border: 1px solid var(--glass-border); border-radius: var(--radius-lg);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-shadow);
}

/* --------------------------------------------------------------------------
   17d. CARD GLOW — guscio + comportamento hover condiviso dalle card
   dove tutta la superficie è un unico link cliccabile: bordo che si
   accende in hover, cursor:pointer, e un bagliore sfocato ESTERNO che
   segue il puntatore (--mx/--my, stesso script.js) invece di uno
   spotlight interno — sta dietro la card (::after, z:-2) e sconfina
   oltre i suoi bordi; una maschera opaca (::before, z:-1) nasconde il
   bagliore dove trasparirebbe attraverso la faccia della card, quindi è
   visibile solo nell'anello fuori dai bordi. Niente backdrop-filter di
   proposito: non serve (la maschera opaca già impedisce che qualcosa
   trasparisca) ed è esattamente la combinazione che sfarfallava altrove.
   L'elemento con questa classe è sempre l'unico link della card — nessun
   link "interno" separato, altrimenti avremmo due indicazioni di hover
   diverse in conflitto (regola concordata: se la card ha un link, tutta
   la card è cliccabile e il testo del link non cambia colore al hover).
   .team-card entra qui solo nella variante --link (about.html): la
   card della home ha più link distinti al suo interno (social + Learn
   more) quindi non può essere lei stessa cliccabile — il suo guscio
   statico è definito a parte, sopra.
-------------------------------------------------------------------------- */
.course-card, .team-card--link, .press-card{
  position: relative; isolation: isolate;
  background: var(--glass-bg); border: 1px solid var(--glass-border); border-radius: var(--radius-lg);
  box-shadow: var(--glass-shadow);
  cursor: pointer;
  transition: border-color var(--dur-base) var(--ease);
}
.course-card:hover, .team-card--link:hover, .press-card:hover{
  border-color: var(--accent);
}
.course-card::before, .team-card--link::before, .press-card::before{
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: var(--surface-2); border-radius: inherit;
}
.course-card::after, .team-card--link::after, .press-card::after{
  content: ""; position: absolute; inset: -1.5rem; z-index: -2;
  background: radial-gradient(220px circle at var(--mx, 50%) var(--my, 50%), rgba(37,99,235,.45), transparent 70%);
  filter: blur(22px);
  opacity: 0; transition: opacity var(--dur-base) var(--ease);
  pointer-events: none;
}
.course-card:hover::after, .team-card--link:hover::after, .press-card:hover::after{
  opacity: 1;
}

/* -- Variante home di .plugin-card ("Our Plugins") — stesso trattamento
   17d applicato SOLO qui via classe aggiuntiva. .plugin-card da sola
   (usata anche in Shop) resta quella di 17b, invariata. -------------- */
.plugin-card.plugin-card--home{
  overflow: visible;
  cursor: pointer;
}
.plugin-card.plugin-card--home::before{
  background: var(--surface-2); opacity: 1; border-radius: inherit;
}
.plugin-card.plugin-card--home::after{
  content: ""; position: absolute; inset: -1.5rem; z-index: -2;
  background: radial-gradient(220px circle at var(--mx, 50%) var(--my, 50%), rgba(37,99,235,.45), transparent 70%);
  filter: blur(22px);
  opacity: 0; transition: opacity var(--dur-base) var(--ease);
  pointer-events: none;
}
.plugin-card.plugin-card--home:hover{
  border-color: var(--accent); background: var(--glass-bg);
}
.plugin-card.plugin-card--home:hover::after{
  opacity: 1;
}
.plugin-card-cta{ font-size: .92rem; font-weight: 700; color: var(--accent); }

/* --------------------------------------------------------------------------
   18. RESPONSIVE
-------------------------------------------------------------------------- */
/* Sotto i 1440px ma sopra i 900px (dove scatta il menu hamburger, vedi il
   media query sotto): il nome testuale del logo si rimpicciolisce invece
   di sparire, per dare al menu principale più larghezza disponibile senza
   che vada a capo su due righe — e senza che "Tonality Masks" stesso vada
   a capo (white-space:nowrap). Range esplicito min+max in un'unica regola
   invece di due regole separate (rimpicciolisci sotto 1440 / torna alla
   dimensione piena sotto 900) apposta: due regole della stessa
   specificità che si contraddicono a seconda dell'ordine nel file sono il
   tipo di bug già capitato con la sezione "10 anni" più sopra in questo
   stesso file — un range esplicito lo rende impossibile. */
@media (min-width: 901px) and (max-width: 1440px){
  .site-header .logo-name{ font-size: 1.1rem; white-space: nowrap; }
  /* Anche le voci di due parole ("Chi Siamo", "Video Tutorial") andavano
     a capo internamente non appena lo spazio si stringeva, pur restando
     il menu su una riga sola — nowrap le tiene su una riga, il gap più
     stretto libera lo spazio che serve perché nowrap non causi overflow. */
  .main-nav ul{ gap: 1.3rem; }
  .main-nav a{ white-space: nowrap; }
}

@media (max-width: 1080px){
  .feature-grid{ grid-template-columns: repeat(2, 1fr); }
  .courses-grid{ grid-template-columns: repeat(2, 1fr); }
  .faq-columns{ grid-template-columns: repeat(2, 1fr); }
  .footer-top{ grid-template-columns: 1.2fr 1fr 1fr; }
  .footer-col:nth-child(3){ grid-column: 1 / 2; }
}

@media (max-width: 900px){
  /* Barra più bassa che in versione desktop, non più alta: poco spazio
     verticale su mobile, e il logo a piena dimensione (2rem, riga alta
     51px per via del line-height del font titoli) era l'elemento che
     decideva l'altezza della barra, più alto persino dell'icona account
     (2.5rem/40px). Ridotto a scala "colonna" invece che testo titolo. */
  .header-inner{ padding-block: .6rem; min-height: 3.75rem; }
  .site-header .logo-name{ font-size: 1.2rem; }

  /* Overlay a schermo intero — completamente opaco (non il vetro
     leggermente trasparente usato dall'header), così il contenuto pagina
     dietro non compete visivamente col menu. Il precedente 97% di opacità
     lasciava trapelare il logo/hero molto luminosi sotto, visibili in
     controluce soprattutto nella fascia alta dietro hamburger e account. */
  /* Scorre tramite `left` (non `transform`) — un elemento position:fixed
     trasformato contribuisce ancora alla larghezza scrollabile del
     documento in alcuni browser anche se visivamente fuori schermo, ed è
     esattamente ciò che permetteva di trascinare in vista il menu
     "chiuso" con uno scroll orizzontale indesiderato. */
  .main-nav{
    position: fixed; top: 0; left: 100%; width: 100%; height: 100vh;
    /* Niente backdrop-filter qui: con sfondo pieno al 100% non c'è nulla
       da sfumare dietro, e backdrop-filter è proprio la proprietà che ha
       causato il bug del position:fixed rotto su .site-header — meglio
       non tenerla anche qui senza motivo. */
    background: #0a0a0d;
    /* Tarato sulla nuova altezza più bassa della barra (vedi sopra in
       questo blocco): prima 6.5rem presupponeva l'altezza piena da
       desktop (84px), ora la barra è ~60px, quel valore lasciava un
       vuoto morto sopra le voci del menu. */
    padding: 5rem var(--space-6) var(--space-6);
    overflow-y: auto;
    visibility: hidden;
    transition: left var(--dur-base) var(--ease), visibility 0s linear var(--dur-base);
    z-index: 90;
  }
  .main-nav.is-open{ left: 0; visibility: visible; transition: left var(--dur-base) var(--ease); }
  .main-nav ul{ flex-direction: column; align-items: flex-end; gap: var(--space-5); text-align: right; }
  .main-nav a{ font-size: 1.05rem; }
  /* Il trigger "Il Progetto" è un <button>, non un <a> — la regola sopra
     non lo raggiunge, resterebbe alla taglia desktop (.92rem) mentre i
     link semplici accanto (Video Tutorial, Articoli...) salgono a 1.05rem
     qui su mobile. Stessa cosa per Storia/Team dentro il suo pannello,
     sempre espanso in linea su mobile (vedi .nav-dropdown-menu più sotto):
     scoperto solo qui, non sul dropdown Plugin (.nav-plugin-grid-item ha
     una propria taglia pensata per la sua griglia icona+testo). */
  .main-nav .nav-dropdown-trigger{ font-size: 1.05rem; }
  .nav-dropdown--project .nav-dropdown-menu a{ font-size: 1.05rem; }

  /* Nessun hover su touch — il dropdown Plugin è sempre espanso in linea,
     senza controllo per chiuderlo/comprimerlo. Rientrato da destra per
     abbinarsi al menu allineato a destra. */
  .nav-dropdown-menu{
    position: static; opacity: 1; visibility: visible; pointer-events: auto;
    padding: 0 var(--space-4) 0 0; margin-top: var(--space-3);
  }
  .nav-dropdown-menu-inner{
    background: transparent; border: 0; box-shadow: none; padding: 0; transform: none;
    display: flex; flex-direction: column; align-items: flex-end; gap: .15rem;
  }
  .nav-dropdown-menu a{ font-size: .92rem; padding: .65rem .75rem; margin-inline: -.75rem; }

  /* Non più nascosta: order:-1 la porta prima di Account e hamburger, a
     sinistra. Il trigger e il pannello restano quelli desktop (stesse
     voci EN/IT) — l'apertura al tap invece che all'hover è gestita più
     sotto in questo blocco, condivisa con Account. */
  .header-actions .lang-switch{ display: block; order: -1; }
  .header-actions .btn-primary{ display: none; }
  .menu-toggle{ display: inline-flex; }
  /* Account e Lingua su mobile: niente hover (assente su touch) e niente
     più azione diretta a tap singolo — il tap apre/chiude il pannello con
     le stesse voci del dropdown desktop (.is-tap-open, gestito da
     nav.js). Il pannello desktop (901px+, style sotto) è dentro un altro
     media query e qui non si applica; quello "inline trasparente" più in
     alto in questo blocco è pensato per il sottomenu Plugin dentro il
     menu a schermo intero, non per questi due — serve quindi un vero
     pannello flottante anche qui, con lo stesso stile della card
     desktop. */
  .header-actions .nav-dropdown-menu{
    position: absolute; top: 100%; right: 0; margin-top: .6rem; padding: 0;
    min-width: 13rem;
    opacity: 0; visibility: hidden; pointer-events: none;
    transition: opacity var(--dur-fast) var(--ease);
  }
  .header-actions .nav-dropdown-menu-inner{
    background: rgba(15,16,23,.97); border: 1px solid var(--glass-border-strong); border-radius: var(--radius-md);
    padding: var(--space-2); box-shadow: 0 20px 40px -12px rgba(0,0,0,.6);
    flex-direction: column; align-items: stretch; text-align: left;
    /* Riempie la larghezza del contenitore posizionato (.nav-dropdown-menu,
       già largo quanto il trigger per la Lingua) invece di restringersi al
       proprio contenuto — altrimenti la card visibile risultava più
       stretta del bottone anche se il box di posizionamento combaciava. */
    width: 100%;
  }
  .header-actions .nav-dropdown.is-tap-open .nav-dropdown-menu{
    opacity: 1; visibility: visible; pointer-events: auto;
  }
  /* Il colore hover di .nav-dropdown-menu a (riga sopra, in questo blocco)
     porta anche margin-inline:-.75rem, pensato per far arrivare
     l'evidenza a filo del contenitore trasparente e senza padding del
     sottomenu Plugin — dentro la card di Account/Lingua (con il suo
     padding, sopra) quello stesso margine negativo sporgeva oltre il
     bordo arrotondato della card. Azzerato solo qui. */
  .header-actions .nav-dropdown-menu a{ margin-inline: 0; }
  /* Pannello Lingua largo quanto il suo trigger (il bottone "IT ⌄"),
     non una larghezza fissa arbitraria come il min-width condiviso qui
     sopra — .lang-switch (position:relative, il contesto di ancoraggio)
     è già largo quanto il bottone, essendo un flex-item senza grow. */
  .header-actions .lang-switch .nav-dropdown-menu{ width: 100%; min-width: 0; }
  /* Pannello Account allineato al bordo destro della riga (non della sola
     icona, stretta e non all'estremità — a destra sua c'è ancora
     l'hamburger): .header-actions come contesto di ancoraggio al posto
     del wrapper .nav-dropdown stesso, con un piccolo margine dal bordo
     vero invece di stare a filo. */
  .header-actions{ position: relative; }
  .header-actions .nav-dropdown--account{ position: static; }
  .header-actions .nav-dropdown--account .nav-dropdown-menu{ right: .5rem; }

  /* A menu aperto, non basta che le singole icone (account/hamburger)
     abbiano ognuna il proprio sfondo: qualunque spazio nel contenitore non
     coperto individualmente (i vuoti tra le icone, i loro bordi arrotondati)
     lascia comunque intravedere le voci del menu che scorrono dietro con
     overflow-y:auto. Il contenitore stesso copre l'intera riga (width:100%,
     stesso colore pieno della barra header desktop — var(--bg), la
     versione opaca dello stesso rgb(11,11,16) usato con alpha in
     .site-header), escluso dal flusso flex con position:absolute e
     ancorato a .site-header (position:sticky, fa da containing block):
     top:0+bottom:0 combaciano sempre con l'altezza reale della riga.
     Dentro questo media query, non a livello globale: se la classe
     .is-menu-open resta sull'header perché si allarga la finestra da
     mobile a desktop senza chiudere il menu, questa regola smette
     semplicemente di valere oltre i 900px invece di spostare l'icona
     account fuori posto in vista desktop. */
  .site-header.is-menu-open .header-actions{
    position: absolute; top: 0; right: 0; bottom: 0;
    width: 100%; padding-right: var(--container-pad);
    display: flex; align-items: center; justify-content: flex-end; gap: var(--space-4);
    z-index: 96;
    background: #0a0a0d;
  }

  /* .cart-toggle è fixed e indipendente dall'header (scritto in ogni
     pagina prodotto, non dentro l'header stesso), quindi z-index:200 lo
     teneva visibile sopra il menu a schermo intero aperto — nascosto
     mentre .is-menu-open è sul body (vedi js/menu/nav.js). */
  body.is-menu-open .cart-toggle{ display: none; }

  /* Ordine naturale su mobile — prima testo/anteprima della hero, poi il
     pannello before/after (non prima, come faceva la vecchia hero a
     colonna singola). */
  .hero-top{ grid-template-columns: 1fr; gap: var(--space-6); margin-bottom: var(--space-9); }

  /* Aurora di test disattivata su mobile: solo desktop, come da richiesta
     originale della prova. */
  .nlights{ display: none; }
  .hero-copy{ max-width: none; }

  .intro-grid{ grid-template-columns: 1fr; gap: var(--space-6); }
  .visual-grid{ grid-template-columns: 1fr; }
  .visual-grid .visual-media{ order: -1; }

  /* Sfondo del duplicato SPECTRUM LUM/COL: la card nera del testo prende
     tutta la larghezza invece di restare stretta a sinistra sulla foto,
     meno spazio riservato alla sola foto di sfondo. */
  .visual-section--photo-bg{ min-height: 22rem; }
  .visual-photo-card-copy{ max-width: none; }
  /* Su schermi stretti il titolo su una riga sola andrebbe fuori dal
     bordo: qui torna a poter andare a capo normalmente. */
  .visual-photo-card-heading h2{ white-space: normal; }

  .mini-grid{ grid-template-columns: repeat(2, 1fr); }
  .footer-top{ grid-template-columns: 1fr 1fr; }
  .footer-brand{ grid-column: 1 / -1; }

  /* Tiene corta la striscia di miniature before/after su mobile — solo le
     prime due foto di ogni genere, invece di tutte e quattro. */
  .hub-before-after-thumb--extra{ display: none; }
}

@media (max-width: 640px){
  .feature-grid{ grid-template-columns: 1fr; }
  .courses-grid{ grid-template-columns: 1fr; }
  .faq-columns{ grid-template-columns: 1fr; }
  .account-band{ grid-template-columns: 1fr; }
  .account-band .btn{ width: 100%; justify-content: center; }
  .stats-grid{ grid-template-columns: repeat(2, 1fr); }
  .footer-top{ grid-template-columns: 1fr; }
  .final-cta-actions{ flex-direction: column; align-items: stretch; width: 100%; }
  .btn{ justify-content: center; }

  /* Buy Now / See All Plugins restano fianco a fianco anche sui telefoni
     piccoli — elementi flex a larghezza uguale invece che a restringimento
     sul contenuto, così la lunghezza del testo di nessuno dei due bottoni
     può forzare un a-capo su una seconda riga. */
  .hero-actions{ flex-wrap: nowrap; gap: var(--space-3); }
  .hero-actions .btn{ flex: 1 1 0; min-width: 0; padding-inline: var(--space-3); font-size: .86rem; white-space: nowrap; }
  .meta-list{ grid-template-columns: 1fr; }
  .check-row{ gap: var(--space-4) var(--space-5); }
  .testimonial-card{ padding: var(--space-6) var(--space-5); }
  .carousel-controls{ gap: var(--space-3); }
}

/* --------------------------------------------------------------------------
   19. MOTION / REVEAL
-------------------------------------------------------------------------- */

[data-reveal]{ opacity: 0; transform: translateY(16px); transition: opacity var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease); }
[data-reveal].is-visible{ opacity: 1; transform: translateY(0); }

@media (prefers-reduced-motion: reduce){
  [data-reveal]{ opacity: 1; transform: none; transition: none; }
  *{ animation-duration: .001ms !important; transition-duration: .001ms !important; }
  /* Piccolo pallino di stato "live" — tenuto lampeggiante anche con
     reduced motion, stessa eccezione che la maggior parte dei siti fa
     per gli indicatori di registrazione/diretta. */
  .badge-dot--blink{ animation-duration: 1.6s !important; }
  /* Fade universale di ingresso pagina — tenuto morbido anche con reduced
     motion, stessa eccezione deliberata delle due sopra, su esplicita
     richiesta del proprietario del sito. */
  #main{ animation-duration: var(--dur-slow) !important; }
  /* Dissolvenza al cambio lingua — tenuta morbida anche con reduced
     motion, stessa eccezione deliberata del toggle smooth-scroll sopra;
     senza di essa ogni transizione del sito collasserebbe a ~0 e il cambio
     si leggerebbe come un taglio secco e istantaneo invece della
     dissolvenza di cortesia voluta. */
  .page-transition-scrim{ transition-duration: var(--dur-base) !important; }
  /* Scorrimento del carosello recensioni — tenuto morbido anche con
     reduced motion, stessa eccezione deliberata delle altre sopra, su
     esplicita richiesta del proprietario del sito: qui il movimento è
     sempre avviato dall'utente (frecce/pallini), mai automatico (vedi
     "playing = !reduceMotion" in js/media/carousel.js, che infatti
     continua a disattivare l'autoplay per chi ha richiesto reduced
     motion — solo la transizione manuale resta animata). */
  .carousel-track{ transition-duration: var(--dur-slow) !important; }
  /* Nastro "Feedback reali dai professionisti" (autorotation) — tenuto in
     movimento anche con reduced motion, stessa eccezione deliberata delle
     altre sopra: qui il movimento è continuo e automatico per scelta
     esplicita (vedi js/media/carousel.js), non una svista. Senza questa riga il
     reset globale *{animation-duration:.001ms !important} qui sopra vince
     comunque sulla regola non-!important in .carousel-track--autorotation,
     lasciando il nastro fermo anche dopo aver scelto di ignorare
     prefers-reduced-motion lato JS: era esattamente questo il vero motivo
     per cui sembrava non muoversi mai, a schermo intero o in automazione. */
  .carousel-track--autorotation{ animation-duration: 60s !important; }
  /* Aurora boreale di test dietro le due immagini hero — tenuta in
     movimento anche con reduced motion, su esplicita richiesta del
     proprietario del sito per poter valutare l'effetto durante lo
     sviluppo (è puramente decorativa: da riconsiderare se/quando
     l'effetto passa da test a definitivo). */
  .nlights{ animation-duration: 12s !important; }
  .nlights-band-1{ animation-duration: 18s !important; }
  .nlights-band-2{ animation-duration: 24s !important; }
  .nlights-band-3{ animation-duration: 20s !important; }
  /* Showcase before/after: nessuna voce qui, di proposito. Taglio
     diagonale, opacità dei fotogrammi nel rullino e scrubber sono tutti
     scritti inline frame per frame da requestAnimationFrame in
     js/media/showcase-before-after.js, non da transition/animation CSS
     — prefers-reduced-motion non li tocca comunque, quindi non serve
     nessuna eccezione con !important come per gli altri autoplay sopra. */
}

/* --------------------------------------------------------------------------
   20. COMPONENTI BLEND — sezioni con contenuto reale aggiunte portando le
   pagine vere di Static Page sul sistema visivo New UI (catalogo plugin,
   capacità del workflow, spotlight prodotto alternati, band di
   affermazione divisorie, team, iscrizione newsletter/download gratuito).
-------------------------------------------------------------------------- */

/* -- Catalogo plugin ------------------------------------------------------- */
.plugins-section{ padding-block: var(--space-10); border-top: 1px solid var(--border); }
.plugin-grid{ display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-5); }

/* MORTO: era il mockup sotto la vecchia griglia plugin della home EN,
   sostituita dal dashboard hub-plugin-section (allineamento EN→IT).
   Nessun HTML lo referenzia più — lasciato per ora, da rimuovere quando
   si pulisce. .plugins-section/.plugin-grid sopra restano vivi, li usa
   ancora shop.html. */
.plugins-showcase{ margin-top: var(--space-8); }
.plugins-showcase img{ display: block; width: 100%; height: auto; border-radius: var(--radius-lg); }

.plugin-card{
  position: relative;
  display: flex; flex-direction: column; gap: var(--space-4);
  background: var(--glass-bg); border: 1px solid var(--glass-border); border-radius: var(--radius-md);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-shadow);
  /* Padding-top maggiorato rispetto agli altri lati: spazio fisso per il
     badge "Offerta" (assente o presente a seconda dello sconto), così il
     titolo del plugin non gli finisce mai troppo a ridosso, badge
     mostrato o no. */
  padding: var(--space-5); padding-top: calc(var(--space-5) + var(--space-4));
  transition: border-color var(--dur-base) var(--ease), transform var(--dur-base) var(--ease), background var(--dur-base) var(--ease);
}

/* Badge "Offerta" in alto a destra sulla card — stesso stile pillola
   arancio del badge "-53%" del bundle (.bundle-price-badge), ma
   posizionato in overlay sull'angolo invece che accanto al prezzo.
   Presente nel markup di ogni card; prices.js lo mostra/nasconde in base
   a chi ha davvero un prezzo scontato in PRICES, quindi non richiede di
   ricordarsi manualmente quali card aggiornare quando cambia lo sconto. */
.plugin-badge-offer{
  position: absolute; top: var(--space-3); right: var(--space-3);
  font-size: .68rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--accent-2); background: var(--accent-2-soft);
  padding: .25rem .6rem; border-radius: var(--radius-sm);
}

.plugin-card-head{ display: flex; align-items: center; gap: var(--space-3); }
.plugin-icon{ width: 2.75rem; height: 2.75rem; object-fit: contain; flex-shrink: 0; }
.plugin-card h3{ font-size: 1rem; }
.plugin-card p{ color: var(--text-muted); font-size: .86rem; flex: 1; }

.plugin-price-row{ display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); margin-top: auto; padding-top: var(--space-3); border-top: 1px solid var(--border); }
/* margin-left:auto invece di affidarsi solo a justify-content:space-between
   sul contenitore: così il prezzo resta ancorato a destra anche nelle
   card senza link "Scopri di più" (es. Basic), dove sarebbe l'unico
   figlio e finirebbe a sinistra per default. */
.plugin-price{ display: flex; align-items: baseline; gap: .45rem; margin-left: auto; }
.plugin-price-old{ font-size: 1.15rem; color: rgba(249,115,22,.5); }
/* Prezzo pieno nel bottone "Acquista Ora" dell'hero prodotto — testo
   bianco del bottone, solo più smorzato e barrato. prices.js nasconde
   questo span di suo quando lo SKU non ha uno sconto configurato, quindi
   può stare in ogni pagina prodotto senza badare a quali sono in sconto. */
.hero-price-old{ text-decoration: line-through; opacity: .65; font-weight: 500; margin-right: .3em; }
/* Prova IT/Tonality Masks: niente più trattino lungo tra etichetta e
   prezzo nel bottone hero — gap ampio sul bottone stesso al posto del
   trattino, così la spaziatura resta coerente anche se cambia la lingua
   o il prezzo. */
.hero-buy-btn{ gap: 1.6em; }
.hero-buy-price{ display: inline-flex; align-items: baseline; }
.plugin-price-new{ font-size: 1.5rem; font-weight: 800; color: var(--text); font-family: var(--font-heading); }
/* Prefisso "a "/"to " davanti al prezzo scontato grande (prices.js):
   stessa dimensione/peso di .plugin-price-old ("da "), ma senza il suo
   arancio — resta del colore bianco del prezzo, non colorata. */
.plugin-price-new .price-prefix{ font-size: 1.15rem; font-weight: 500; font-family: var(--font); color: var(--text); }
/* Colore volutamente smorzato e nessun cambio al hover: è un link
   secondario dentro una card il cui invito principale è "Aggiungi al
   Carrello", non deve competere per l'attenzione né invitare a
   "girovagare" via dallo shop. */
.plugin-price-link{ font-size: .92rem; font-weight: 700; color: var(--text-muted); }
/* Niente margin-top qui: il gap del flex .plugin-card (var(--space-4))
   basta da solo, un margine in più sommato al gap raddoppiava lo spazio
   tra il prezzo/link sopra e il bottone. */
.plugin-buy-btn{ width: 100%; justify-content: center; }
/* Prodotto già nel carrello (js/cart/cart.js aggiunge/toglie la classe
   in base allo stato reale del carrello): blu accent, non verde — il
   verde nel carrello è già usato per "confermato/fatto"
   (.cart-coupon-msg[data-state="ok"]) e per gli stati di sistema, quindi
   qui userebbe lo stesso colore per un significato diverso (azione,
   non conferma). Blu accent tiene il richiamo visivo al bottone
   flottante .cart-toggle, che questo bottone in pratica sostituisce.
   Vince su .btn-primary e .btn-ghost via specificità (classe doppia). */
.plugin-buy-btn.plugin-buy-btn--in-cart{
  background: var(--accent-soft); border-color: var(--accent); color: var(--accent);
}
.plugin-buy-btn.plugin-buy-btn--in-cart:hover{ background: rgba(0,153,255,.22); }
.plugin-buy-btn.plugin-buy-btn--in-cart svg{ flex-shrink: 0; }

/* -- Intro pagina compatta (sottopagine che non servono la hero completa della home) ---- */
.page-hero{ position: relative; overflow: hidden; padding-block: var(--space-9) var(--space-8); }
/* Le pagine prodotto con lo screenshot "impilato" (.hero-preview-media--stack)
   arrivano quasi a toccare il divider sotto con il padding-bottom
   standard, per via del drop-shadow/offset dell'immagine in primo piano —
   un filo di respiro in più solo per questa variante. */
.page-hero:has(.hero-preview-media--stack){ padding-bottom: var(--space-10); }
.page-hero-inner{ position: relative; z-index: 1; max-width: 46rem; }
/* Titolo a due righe della hero Tonality Masks v10: dimensione fissa
   invece del clamp responsive di default, scoped a questa sola pagina
   (non un h1 generico riusato altrove). */
.hero-title--tm10{ font-size: 2.5rem; }
.page-hero-inner .section-sub{ margin-top: var(--space-4); }
.page-hero-inner .hero-actions{ margin-top: var(--space-7); }
.page-hero-top{ position: relative; z-index: 1; margin-bottom: 0; }

/* -- Card bundle (pagina Shop) ----------------------------------------------- */
.bundle-card{
  position: relative; overflow: hidden;
  background: var(--glass-bg); border: 1px solid var(--accent-2); border-radius: var(--radius-lg);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-shadow);
  padding: var(--space-8) var(--space-7);
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-7);
  margin-bottom: var(--space-8);
}
.bundle-ribbon{
  position: absolute; top: var(--space-5); left: var(--space-7);
  background: var(--accent-2); color: #17110a;
  font-size: .72rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  padding: .35rem .9rem; border-radius: var(--radius-sm);
}
.bundle-icons{ display: grid; grid-template-columns: repeat(4, 2.75rem); gap: .5rem; flex: 1 1 16rem; margin-top: var(--space-4); }
.bundle-icons img{ width: 2.75rem; height: 2.75rem; object-fit: contain; }
.bundle-copy{ flex: 2 1 20rem; margin-top: var(--space-4); }
.bundle-copy h2{ margin-bottom: var(--space-3); }
.bundle-copy p{ color: var(--text-muted); font-size: .94rem; }
.bundle-buy{ flex: 1 1 14rem; text-align: center; margin-top: var(--space-4); }
.bundle-price{ display: flex; align-items: baseline; justify-content: center; gap: .6rem; margin-bottom: var(--space-4); }
.bundle-price-old{ font-size: 1rem; color: rgba(249,115,22,.5); }
.bundle-price-new{ font-size: 2rem; font-weight: 800; color: var(--text); font-family: var(--font-heading); }
.bundle-price-new .price-prefix{ font-size: 1rem; font-weight: 500; font-family: var(--font); color: var(--text); }
/* Stesso stile del badge "Offerta" delle card plugin (.plugin-badge-offer),
   solo leggermente più grande, e in overlay sull'angolo in alto a destra
   della card bundle (che ha già position:relative) invece che in linea
   accanto al prezzo — speculare a .bundle-ribbon che occupa l'angolo
   in alto a sinistra. */
.bundle-price-badge{
  position: absolute; top: var(--space-3); right: var(--space-3);
  font-size: 1rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--accent-2); background: var(--accent-2-soft);
  padding: .4rem .9rem; border-radius: var(--radius-sm);
}


/* -- Spotlight prodotto alternato (riusa .visual-section/.visual-grid) --- */
.visual-grid--reverse .visual-media{ order: 2; }
.visual-grid--reverse .visual-copy{ order: 1; }

/* -- Sezione fiducia "10 anni" in home (solo index-it.html per ora, in
   revisione): il ritratto da solo non ha bisogno di metà riga come lo
   screenshot di un pannello, quindi qui la .visual-grid resta stretta sul
   lato foto — modificatore ad hoc, non tocca il rapporto 1:1 di default
   usato da about.html/story.html altrove. Il "10" gigante con il nastro
   diagonale dietro fa da cerniera visiva tra la foto e il testo: il
   nastro entra da sinistra (passa dietro all'"1") e si interrompe dentro
   lo "0" invece di uscire dall'altro lato, per dare l'idea che il nastro
   attraversi il numero invece di stare semplicemente dietro. */
.visual-grid--10y{ grid-template-columns: 1fr 3fr; }
.visual-section--10y{ padding-block: var(--space-8); }
.anniversary-title-row{ display: flex; align-items: center; gap: var(--space-5); }
.anniversary-mark{ position: relative; display: inline-block; flex-shrink: 0; }
.anniversary-mark-number{
  display: block;
  font-family: var(--font-heading); font-weight: 700;
  font-size: 6.5rem; line-height: 1; letter-spacing: -.03em; color: var(--text);
}
.anniversary-title-row h2{ font-size: 2.4rem; }
.ten-years-section .visual-copy h2{ margin-bottom: 0; }
.visual-section--10y .section-sub{ max-width: none; }
.visual-section--philosophy .section-sub{ max-width: none; }
/* Firma di chiusura del blocco filosofia, in stile classico "nota
   personale": corsivo, stessa dimensione del testo che precede
   (.section-sub, fs-lead), allineata a sinistra come il resto del
   paragrafo, staccata dall'ultimo paragrafo con una riga di spazio
   vuoto in più rispetto al normale margin-top di .section-sub tra
   paragrafi. */
.philosophy-signature{
  margin-top: var(--space-6);
  font-size: var(--fs-lead);
  font-style: italic;
  color: var(--text);
}
.ten-years-section .visual-copy .section-sub{ margin-bottom: 0; }
.visual-section--10y .testimonial-quote{ font-size: 1.3rem; }

/* -- Sezione "10 anni" definitiva: numero+"ANNI" impilati, linea sotto il
   titolo, citazione sotto la foto, foto piccola e centrata. Nata come
   confronto di più bozze in demo-10-anni.html, portata qui a scelta
   fatta. */
.ten-years-section .anniversary-mark--labeled{
  display: flex; flex-direction: column; align-items: stretch; gap: .1em;
}
.ten-years-section .anniversary-mark-label{
  display: block; width: 100%;
  text-align: justify; text-align-last: justify; text-justify: inter-character;
  font-family: var(--font-heading); font-weight: 700;
  text-transform: uppercase; color: var(--text);
  position: relative; font-size: 2.8rem; margin-top: -25px;
}
/* Le due date per esteso accanto ad "ANNI" — allineate con un vero
   allineamento flex (align-items:flex-start sulla riga + h2/date impilati
   in un'unica colonna, .anniversary-title-wrap) invece di un offset in
   px fisso: un valore fisso sballerebbe appena il viewport cambia
   larghezza, perché il clamp() del titolo scala ma un px fisso no. */
.ten-years-section .anniversary-title-row--dated{ align-items: flex-start; }
.ten-years-section .anniversary-title-wrap{ display: flex; flex-direction: column; }
.ten-years-section .anniversary-date-range{
  display: flex; flex-wrap: wrap; align-items: center; gap: .35em; margin-top: .8rem;
  font-family: var(--font-heading); font-weight: 700; line-height: 1;
  font-size: clamp(1.6rem, 1.2rem + 1.6vw, 2.3rem);
}
.ten-years-section .anniversary-date--start{ color: var(--accent); }
.ten-years-section .anniversary-date--end{ color: var(--accent-2); }
.ten-years-section .anniversary-date-sep{ color: #fff; }
.ten-years-section .visual-media .testimonial-quote{
  margin-top: var(--space-5); font-size: 1.02rem; text-align: center;
  color: var(--text-muted);
}
.ten-years-section .visual-media .testimonial-quote cite{
  display: block; margin-top: var(--space-4);
  font-size: 1.15rem; color: var(--text);
}
.ten-years-section .visual-copy .text-link{ display: flex; width: fit-content; margin-left: auto; }
.ten-years-section .visual-media img{ max-width: 175px; display: block; margin: 0 auto; }

/* Mobile della sezione "10 anni" — va DOPO le regole desktop sopra, non
   nel media query generico più in alto nel file: a parità di specificità
   vince l'ultima regola in ordine di sorgente, quindi un mobile-override
   scritto prima di regole desktop identiche in specificità (come quelle
   qui sopra) perderebbe sempre il confronto, a qualunque larghezza —
   bug reale che teneva il layout desktop anche sotto i 900px finché non
   è stato spostato qui. */
@media (max-width: 900px){
  .visual-grid--10y{ grid-template-columns: 1fr; }
  .visual-grid--10y .visual-media{ order: -1; }
  .ten-years-section .anniversary-title-row{
    flex-direction: column; align-items: flex-start; gap: var(--space-3);
  }
  .ten-years-section .anniversary-mark-number{ font-size: 8rem; }
  .ten-years-section .anniversary-mark-label{ font-size: 4rem; margin-top: -12px; }
  .ten-years-section .anniversary-title-row h2{
    font-size: clamp(1.6rem, 1.2rem + 3vw, 2.1rem);
  }
  .ten-years-section .visual-copy .text-link{ margin-left: 0; }
}

.spotlight-features{
  display: flex; flex-direction: column; gap: var(--space-4);
  margin: var(--space-5) 0 var(--space-6);
  padding-top: var(--space-5); border-top: 1px solid var(--border);
}
/* PROVA (it/blending.html): qui le spunte sono l'unico contenuto della
   colonna (titolo/descrizione spostati sopra la griglia, vedi
   .section-heading--right) -- niente più testo sopra da "staccare" con
   un divisore, il border-top/padding-top non servono più. */
.visual-section--no-glow .spotlight-features{ border-top: none; padding-top: 0; margin-top: 0; }
/* Titolo tutto su una riga: niente più max-width:48ch (di serie su
   .section-heading, pensato per un blocco centrato più stretto) a
   limitare la larghezza disponibile, altrimenti "Sistema avanzato RAW
   linearizzato" va a capo da solo anche senza <br> nel markup. */
.visual-section--no-glow .section-heading{ max-width: none; }
.visual-section--no-glow .section-heading h2{ white-space: nowrap; }
/* Niente regola equivalente qui per .section-sub: era stata provata su
   Blending/Basic/Glow & Orton/Bianco e Nero, dove il testo segnaposto
   corto ci stava comodamente su una riga (nowrap+max-width:none diretti
   inline su quei singoli paragrafi, non qui). Su Sharpen invece il
   paragrafo è testo reale molto più lungo: forzarlo su una riga a
   livello di classe condivisa lo faceva uscire dal viewport. Meglio
   lasciare qui il default (.section-sub ha già max-width:42ch di suo,
   va a capo da solo quando serve) e gestire il caso-per-caso inline
   dove davvero serve una riga sola. */
.spotlight-features li{ display: flex; gap: var(--space-4); align-items: flex-start; }
.spotlight-check{
  flex-shrink: 0; width: 1.6rem; height: 1.6rem; margin-top: .05rem;
  color: var(--accent-2);
  display: flex; align-items: center; justify-content: center;
}
.spotlight-check svg{ width: 22px; height: 17px; }
.spotlight-features strong{ display: block; font-size: 1.05rem; color: var(--text); margin-bottom: .2rem; }
.spotlight-features span:not(.spotlight-check){ font-size: .92rem; color: var(--text-muted); }

/* -- Team (riusa il linguaggio visivo di .team-card) ---------------- */
.team-section{ padding-block: var(--space-10); border-top: 1px solid var(--border); }
/* 4 per riga, full width: è il layout di default perché il tier 'team' è
   la griglia più diffusa (vedi nota su .team-role sopra per lo stesso
   ragionamento core-come-eccezione). Griglia core (founder/business,
   sempre 2) separata e più stretta, resa esplicita dal modificatore
   .team-grid--core sotto: due file distinte invece di una sola griglia
   mista, così Gaspare/Luca restano visivamente il "motore" del progetto
   in cima, staccati dal resto del team sotto. */
.team-grid{ display: grid; gap: var(--space-6); margin-inline: auto; grid-template-columns: repeat(5, minmax(0, 1fr)); max-width: 100%; }
.team-grid--core{ grid-template-columns: repeat(2, minmax(0, 1fr)); max-width: 640px; margin-bottom: var(--space-6); }
.team-subrole{ font-size: .74rem; color: var(--text-faint); text-transform: uppercase; letter-spacing: .05em; margin-bottom: var(--space-3); }
.team-bio{ color: var(--text-muted); font-size: .86rem; margin-bottom: var(--space-4); }
.team-quote{ color: var(--text-muted); font-size: .86rem; font-style: italic; margin-bottom: var(--space-4); }
.team-media--round img{ border-radius: 50%; }
/* Dimensioni avatar duplicate per variante (--link, --grid, --compact più
   sotto) invece che su .team-media--round nudo: oggi --link e --grid
   coincidono (avatar a piena larghezza), ma sono due regole separate
   apposta, per poterne cambiare una senza toccare l'altra. */
.team-card--link .team-media--round{ aspect-ratio: 1 / 1; padding: var(--space-5) var(--space-4); }
.team-card--grid .team-media--round{ aspect-ratio: 1 / 1; padding: var(--space-5) var(--space-4); }

/* Variante compatta per il carosello di prova (tm_team_quote_card):
   stessa impostazione verticale della card grande (avatar sopra, testo
   sotto), ma avatar piccolo e allineato a sinistra invece che a piena
   larghezza — è questo, non l'affiancamento orizzontale (primo tentativo,
   troppo stretto per 5 card visibili), a lasciare abbastanza spazio al
   testo con 5 card per riga. align-items: flex-start sovrascrive il
   default di .team-card (nessuno esplicito, quindi stretch) per non far
   allargare l'avatar a tutta la card. */
.team-card--compact{ display: flex; flex-direction: column; align-items: flex-start; padding: var(--space-5); text-align: left; }
.team-card--compact .team-media--round{ max-width: 150px; aspect-ratio: 1 / 1; padding: 0; margin-bottom: var(--space-4); flex-shrink: 0; }
.team-card--compact .team-body{ padding: 0; text-align: left; }
.team-card--compact .team-body h3{ margin-bottom: .1rem; }
.team-card--compact .team-subrole{ margin-bottom: var(--space-2); }
.team-card--compact .team-quote{ margin-bottom: 0; }
.carousel--team{ max-width: 100%; }

/* Blocco statico di testimonianze mirate per pagina (prodotto/shop,
   partials/team-member-quotes.php quando la pagina non è nella lista
   $tm_team_quotes_autorotation_pages) — non un carosello: qui non gira
   js/media/carousel.js, quindi .team-card--compact (di suo senza una
   propria larghezza, gliela dà sempre il carosello via flex-basis) deve
   riceverla da questo contenitore invece. Centrate, a capo se non
   entrano tutte in una riga. */
.team-quotes-section{ padding-block: var(--space-10); border-top: 1px solid var(--border); }
.team-quotes-static{ display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-5); }
.team-quotes-static .team-card--compact{ width: 14rem; max-width: 100%; flex: 0 1 auto; }

/* -- Band iscrizione newsletter + download gratuito ------------------------- */
.signup-section{ padding-block: var(--space-10); border-top: 1px solid var(--border); }
.signup-stack{ display: flex; flex-direction: column; gap: var(--space-5); }

.signup-band{
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-5);
  padding: var(--space-6); background: var(--accent-soft); border: 1px solid var(--border); border-radius: var(--radius-lg);
}
.signup-band--free{ background: var(--accent-2-soft); }
.signup-band-copy{ max-width: 42ch; }
.signup-band-copy h3{ font-size: 1.15rem; margin-bottom: .3rem; }
.signup-band-copy p{ color: var(--text-muted); font-size: .88rem; }

.signup-form{ display: flex; gap: var(--space-3); flex-wrap: wrap; flex-shrink: 0; }
.signup-form input[type="email"]{
  min-width: 0; width: 15rem; max-width: 100%; padding: .85rem 1rem; border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong); background: var(--surface-2); color: var(--text); font-size: .92rem;
}
.signup-form input[type="email"]::placeholder{ color: var(--text-faint); }
.signup-form input[type="email"]:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; }

@media (max-width: 1080px){
  .plugin-grid{ grid-template-columns: repeat(2, 1fr); }
  .team-grid{ grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 900px){
  .visual-grid--reverse .visual-media{ order: -1; }
  /* Bio con ritratto (about-gaspare.html/about-luca.html): la regola base
     .visual-grid--narrow-media.visual-grid--reverse (grid-template-
     columns:2fr 1fr, riga ~1471, due classi = specificity più alta di
     .visual-grid qui sotto) vinceva anche su mobile, quindi la griglia non
     collassava mai a una colonna. Va forzata qui esplicitamente. Ordine
     lasciato al comportamento di default (order:-1 sopra, foto per
     prima), stesso di ogni altra .visual-grid--reverse. */
  .visual-grid--narrow-media.visual-grid--reverse{ grid-template-columns: 1fr; }
  /* Ritratto rotondo: senza colonna stretta a contenerlo (grid a 1
     colonna qui sopra), .visual-media img (width:100% di serie) si
     allargherebbe a piena larghezza del container invece che restare
     alla dimensione contenuta del desktop -- cap esplicito, centrato. */
  .visual-media--round{ max-width: 20rem; margin-inline: auto; }
  /* Eyebrow e titolo centrati sotto il ritratto, invece che allineati a
     sinistra come nella colonna stretta del desktop. text-align va sul
     contenitore, non sull'eyebrow stesso: è inline-flex (riga ~211), un
     text-align sulla propria box non la centra, va centrata come box
     inline dentro il genitore. Il testo biografico sotto (.bio-body) resta
     comunque a sinistra grazie al suo text-align:left di serie (riga
     ~1425), che vince per ereditarietà interrotta. */
  .visual-grid--narrow-media.visual-grid--reverse .visual-copy{ text-align: center; }
  /* Troppo spazio vuoto sopra la foto, tra il menu e l'inizio sezione:
     padding-top di serie (.visual-section, var(--space-10) = 7rem) pensato
     per una sezione a due colonne piene, qui invece sopra c'è solo la foto
     centrata col cap di larghezza qui sopra. Ridotto solo il top, il
     bottom resta invariato. */
  .visual-section:has(.visual-grid--narrow-media.visual-grid--reverse){ padding-top: var(--space-7); }
}
@media (max-width: 640px){
  .plugin-grid{ grid-template-columns: 1fr; }
  .signup-band{ flex-direction: column; align-items: stretch; }
  .signup-form{ flex-direction: column; align-items: stretch; }
  .signup-form input[type="email"]{ width: 100%; }
}

/* --------------------------------------------------------------------------
   21. HUB VIDEO — un'unica superficie unificata: selettore autore
   (sinistra) + player con una griglia "prossimi video" sotto (destra),
   stile piattaforma corsi. Stesso pattern dell'hub plugin (§27:
   selettore a sinistra, pannello di dettaglio a destra), ma con markup/
   CSS/JS/dati indipendenti — vedi js/media/hub-video.js e
   partials/hub-video-data.php.
-------------------------------------------------------------------------- */
.hub-video-section{ padding-block: var(--space-10); border-top: 1px solid var(--border); }
.hub-video-section > .container{ max-width: 1760px; }

.hub-video-dashboard{
  display: grid;
  grid-template-columns: 248px minmax(0, 1fr);
  background: var(--glass-bg); border: 1px solid var(--glass-border); border-radius: var(--radius-lg);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-shadow);
  overflow: hidden;
}

/* -- Sinistra: selettore autore --------------------------------------------- */
/* transform+isolation: come su .hub-video-player-frame più sotto, ma qui
   il sospetto è position:sticky dentro un antenato con backdrop-filter —
   altra combinazione nota per artefatti di compositing in Chrome, il
   layer dello sticky e quello del blur possono disallinearsi quando il
   contenuto della sidebar cambia (click su un autore, innerHTML nuovo). */
.hub-video-authors{
  padding: 0 var(--space-4) var(--space-5);
  border-right: 1px solid var(--glass-border);
  position: sticky; top: 1.5rem; align-self: start;
  transform: translateZ(0); isolation: isolate;
  max-height: calc(100vh - 3rem);
  overflow-y: auto;
}
.hub-video-authors-label{
  font-size: .74rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
  color: var(--text-faint); padding-inline: var(--space-2); margin-bottom: var(--space-3);
}
.hub-video-author-list{ display: flex; flex-direction: column; gap: var(--space-1); }
.hub-video-author-item{
  display: flex; align-items: center; gap: var(--space-3); width: 100%; text-align: left;
  padding: var(--space-2) var(--space-3); border-radius: var(--radius-md);
  border: 1px solid transparent; background: none; color: var(--text-muted); cursor: pointer;
  font-family: inherit; font-size: .88rem;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.hub-video-author-item:hover{ background: var(--glass-bg-strong); color: var(--text); }
.hub-video-author-item.is-active{ background: var(--accent-soft); border-color: var(--accent); color: var(--text); }
.hub-video-author-avatar{
  flex-shrink: 0; width: 2.35rem; height: 2.35rem; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-heading); font-weight: 700; font-size: .8rem; color: var(--text-muted);
  background: var(--accent-soft); border: 1.5px solid transparent;
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.hub-video-author-item.is-active .hub-video-author-avatar{ color: var(--accent); border-color: var(--accent); }
.hub-video-author-meta{ display: flex; flex-direction: column; min-width: 0; gap: .1rem; }
.hub-video-author-name{
  font-weight: 600; color: var(--text); line-height: 1.25;
  transition: color var(--dur-fast) var(--ease);
}
.hub-video-author-count{ font-size: .74rem; color: var(--text-faint); }

/* -- Destra: palco player + riga prossimi-video sotto -------------------------*/
.hub-video-main{ min-width: 0; padding: var(--space-5); display: flex; flex-direction: column; gap: var(--space-6); }

.hub-video-stage{ min-width: 0; }
/* transform+isolation: forza questo box su un proprio layer di compositing
   GPU, separato da quello del backdrop-filter (--glass-blur) ereditato da
   .hub-video-dashboard. Senza, Chrome a volte disallinea per un frame il
   layer del blur e quello dell'iframe quando il suo src cambia (cambio
   autore), mostrando strappi/righe fantasma ai bordi del blur — bug di
   compositing noto per backdrop-filter + iframe annidato, non una nostra
   regola di layout rotta. */
.hub-video-player-frame{
  position: relative; width: 100%; aspect-ratio: 16 / 9; overflow: hidden;
  background: #000; border: 1px solid var(--glass-border); border-radius: var(--radius-lg); box-shadow: var(--glass-shadow);
  transform: translateZ(0); isolation: isolate;
}
.hub-video-player-frame iframe{ position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.hub-video-stage-title{
  font-size: .95rem; font-weight: 600; color: var(--text); line-height: 1.4;
  margin-top: var(--space-4); margin-bottom: 0;
}

/* -- Prossimi video, singola riga scrollabile, sotto al titolo --------------------*/
.hub-video-list-panel{
  min-width: 0; background: var(--surface-2); border: 1px solid var(--glass-border);
  border-radius: var(--radius-lg); padding: var(--space-4);
}
.hub-video-list-label{
  font-size: .74rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
  color: var(--text-faint); margin-bottom: var(--space-4);
}
.hub-video-list-author{
  font-weight: 600; text-transform: none; letter-spacing: normal; color: var(--text-muted);
}
.hub-video-list{ display: flex; flex-direction: row; gap: var(--space-4); overflow-x: auto; padding-bottom: var(--space-2); }
.hub-video-list-item{
  flex: 0 0 auto; width: 12rem;
  display: flex; flex-direction: column; gap: var(--space-2); text-align: left;
  background: none; border: 1px solid transparent; border-radius: var(--radius-md); padding: var(--space-2); cursor: pointer;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.hub-video-list-item:hover{ background: var(--glass-bg); }
.hub-video-list-item.is-active{ background: var(--accent-soft); border-color: var(--accent); }
.hub-video-list-thumb-wrp{
  width: 100%; aspect-ratio: 16 / 9; border-radius: var(--radius-sm); overflow: hidden; background: var(--surface-2);
}
.hub-video-list-thumb-wrp img{ width: 100%; height: 100%; object-fit: cover; display: block; }
.hub-video-list-item-title{
  font-size: .82rem; line-height: 1.35; color: var(--text);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

/* -- Scrollbar sottili personalizzate (WebKit) ---------------------------------------*/
.hub-video-authors::-webkit-scrollbar, .hub-video-list::-webkit-scrollbar{ height: 6px; width: 6px; }
.hub-video-authors::-webkit-scrollbar-thumb, .hub-video-list::-webkit-scrollbar-thumb{ background: var(--border-strong); border-radius: var(--radius-pill); }

@media (max-width: 1080px){
  .hub-video-dashboard{ grid-template-columns: 208px minmax(0, 1fr); }
}
@media (max-width: 900px){
  .hub-video-dashboard{ grid-template-columns: minmax(0, 1fr); }
  .hub-video-authors{
    position: static; max-height: none; overflow: visible; padding: var(--space-4);
    border-right: none; border-bottom: 1px solid var(--glass-border);
  }
  .hub-video-author-list{ flex-direction: row; overflow-x: auto; padding-bottom: var(--space-1); }
  .hub-video-author-item{ flex-shrink: 0; min-width: 12rem; }
  .hub-video-main{ padding: var(--space-4); }
}
@media (max-width: 640px){
  .hub-video-list-item{ width: 9.5rem; }
}

/* --------------------------------------------------------------------------
   22. STAMPA / RECENSITO DA — card incentrate sul testo (nessun logo
   preso in prestito), che rimandano all'articolo di stampa o alla pagina
   propria del recensore. Guscio, bordo in hover e bagliore esterno sono
   quelli condivisi in 17d CARD GLOW — .press-card è stata il modello da
   cui è nato quel sistema, ora ne è solo un'applicazione come le altre.
-------------------------------------------------------------------------- */
.press-section{ padding-block: var(--space-9); border-top: 1px solid var(--border); }
.press-grid{ display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-5); margin-top: var(--space-8); }
.press-card{
  display: flex; flex-direction: column; align-items: center; gap: var(--space-2); text-align: center;
  padding: var(--space-6) var(--space-5);
}
/* Logo del recensore, opzionale: le card senza asset restano solo testo
   (.press-card centra comunque il contenuto). SVG con fill="currentColor"
   va inlineato nel markup (non <img>) per ereditare il tema chiaro/scuro,
   stesso motivo di partials/social-icons.php; i loghi raster (png/jpg)
   restano <img> normali. */
.press-card-logo{ display: flex; align-items: center; justify-content: center; height: 6rem; max-width: 100%; color: var(--text); }
/* max-height/max-width (non height:100% fisso) cala i loghi molto larghi
   (es. FixThePhoto 372x86, Fstoppers ~3.7:1) senza spezzarne l'aspect
   ratio: con height:100% fisso, il max-width li tagliava in larghezza
   lasciando l'altezza piena, effetto "stirato" in verticale. min(14rem,100%)
   invece di un 14rem fisso: con 5 card visibili invece di 3 le colonne
   sono più strette del cap fisso, che le faceva uscire lateralmente dalla
   card (l'overflow del carosello nasconde quel che eccede, non lo comprime). */
.press-card-logo img, .press-card-logo svg{ max-height: 100%; max-width: min(14rem, 100%); width: auto; height: auto; }
/* Loghi raster non su sfondo trasparente/mono (es. FixThePhoto) non possono
   ereditare currentColor come gli SVG sopra: brightness(0) appiattisce ogni
   pixel visibile a nero (l'alpha resta intatta), invert(1) lo ribalta in
   bianco pieno — così anche un logo con nero/grigio misto diventa un
   silhouette bianco uniforme invece che solo più chiaro. Funziona solo su
   PNG/SVG con sfondo trasparente: su uno sfondo pieno (GoGeek, FiD) non
   c'è alpha che separi il segno dallo sfondo, quindi tutto il riquadro
   collassa in un unico bianco pieno senza dettagli — per quei due usare
   --gray, non --mono. */
.press-card-logo--mono img{ filter: brightness(0) invert(1); }
/* Sfondo pieno, non trasparente (GoGeek, FiD): grayscale mantiene il
   contrasto chiaro/scuro del disegno invece di appiattirlo — "bianco e
   nero" ma leggibile, a differenza di --mono su questi asset. */
.press-card-logo--gray img{ filter: grayscale(1); }
/* Segnaposto rotondo per i recensori-persona senza foto propria (stesso
   $tm_team_placeholder_avatar di partials/team-member-data.php, non duplicato):
   oggi è sempre il placeholder, ma la classe è pronta per il giorno in cui
   queste card mostreranno l'avatar reale del fotografo al posto del logo
   testata. object-fit:cover ritaglia qualunque foto arrivi al quadrato. */
.press-card-logo--avatar{ width: 6rem; border-radius: 50%; overflow: hidden; }
.press-card-logo--avatar img{ width: 100%; height: 100%; object-fit: cover; }
/* Il sigillo di Viaggi Fotografici riempie il suo riquadro esattamente come
   l'avatar (96x96px misurati), ma un bordo chiaro sottile + dettagli fini
   lo fanno percepire più piccolo del disco pieno del segnaposto: scale(1.15)
   compensa otticamente, non è un bug di dimensionamento del box. */
.press-card-logo--zoom img{ transform: scale(1.15); }
.press-card-name{ font-family: var(--font-heading); font-weight: 700; font-size: 1.3rem; color: var(--text); letter-spacing: -.01em; }
.press-card-desc{ font-size: .85rem; color: var(--text-muted); }
/* Non è più un <a> a sé (tutta la .press-card è il link): statico, nessun
   hover proprio, per lo stesso motivo di .team-more sopra. */
/* margin-top:auto (non var(--space-2) fisso): .press-card è stirata da
   .carousel-track (flex, align-items:stretch di default) alla stessa
   altezza della card più alta della riga -- nomi a una riga (Fstoppers)
   vs due righe (Viaggi Fotografici) lasciavano quindi uno spazio vuoto
   diverso sopra questo link, che finiva ad altezze diverse da card a
   card. auto assorbe quello spazio e lo ancora sempre in fondo. */
.press-card-link{
  margin-top: auto; font-size: .8rem; font-weight: 600; color: var(--accent);
  display: inline-flex; align-items: center; gap: .4em;
}

@media (max-width: 900px){
  .press-grid{ grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   23. TIMELINE STORIA — un marcatore per anno, collegati da una linea verticale
-------------------------------------------------------------------------- */
.story-section{ padding-block: var(--space-9) var(--space-10); }

.timeline{ margin-inline: 0; }
.timeline-item{
  position: relative; display: grid; grid-template-columns: 9rem 2rem 1fr; column-gap: var(--space-4);
  padding-bottom: var(--space-8);
}
.timeline-item:last-child{ padding-bottom: 0; }
.timeline-year{
  font-family: var(--font-heading); font-weight: 700; font-size: 1.05rem; color: var(--text);
  text-align: right; padding-top: .32rem; white-space: nowrap; line-height: 1.1;
}
@keyframes tm-pulse-soft{ 0%, 100% { opacity: 1; } 50% { opacity: .4; } }
.timeline-year--live{ animation: tm-pulse-soft 2.4s ease-in-out infinite; }
.timeline-track{ position: relative; display: flex; justify-content: center; }
.timeline-track::before{
  content: ""; position: absolute; top: 0; bottom: calc(-1 * var(--space-8)); left: 50%;
  width: 2px; background: var(--border); transform: translateX(-50%);
}
.timeline-item:last-child .timeline-track::before{ display: none; }
.timeline-dot{
  position: relative; z-index: 1; width: .85rem; height: .85rem; border-radius: 50%;
  background: var(--accent); margin-top: .4rem; flex-shrink: 0;
}
.timeline-item--highlight .timeline-dot{
  width: 1.15rem; height: 1.15rem; margin-top: .28rem;
  box-shadow: 0 0 0 4px var(--accent-soft);
}
/* -- Pallino della release più recente: ring esterno pulsante + glow,
   a differenza degli altri highlight (es. il prototipo più vecchio)
   che restano statici. -- */
.timeline-dot--live{ box-shadow: 0 0 0 4px var(--accent-soft), 0 0 12px 3px var(--accent-soft); }
.timeline-dot--live::after{
  content: ""; position: absolute; inset: -4px; border-radius: 50%;
  border: 2px solid var(--accent-soft);
  animation: tm-dot-ping 2.4s ease-out infinite;
}
@keyframes tm-dot-ping{
  0% { transform: scale(.85); opacity: .9; }
  70% { transform: scale(1.9); opacity: 0; }
  100% { transform: scale(1.9); opacity: 0; }
}
.timeline-content h3{ font-size: 1.05rem; margin-bottom: .35rem; padding-top: .32rem; }
.timeline-tech{ font-weight: 400; font-size: .85rem; color: var(--text-muted); }
.timeline-content p{ color: var(--text-muted); font-size: .92rem; }
.timeline-image{
  display: block; width: 100%; aspect-ratio: 3 / 2; object-fit: cover;
  margin-top: var(--space-4);
}
.timeline-gallery{ display: flex; flex-wrap: wrap; gap: var(--space-3); margin-top: var(--space-4); }
.timeline-gallery .timeline-image{
  margin-top: 0; flex: 0 0 auto; width: 14rem; height: auto; aspect-ratio: auto; object-fit: contain;
}

@media (prefers-reduced-motion: reduce){
  .timeline-year--live{ animation: none; }
  .timeline-dot--live::after{ animation: none; opacity: .5; }
}

@media (max-width: 640px){
  .timeline-item{ grid-template-columns: 6.75rem 1.2rem 1fr; column-gap: var(--space-3); }
  .timeline-year{ font-size: .8rem; }
}

/* --------------------------------------------------------------------------
   24. MODULI PRODOTTO — screenshot reale per funzionalità + tagline + tag
   a pillola, riusando .visual-section/.visual-grid (lati alternati via --reverse).
-------------------------------------------------------------------------- */
/* Questi screenshot sono ritagli UI a risoluzione nativa (alcuni pannelli
   a piena altezza, altri strisce di toolbar alte 20-30px). Niente
   max-width sul contenitore: comprimeva anche gli screenshot più grandi
   dentro 22rem (352px), rendendoli illeggibili. */
.visual-media--screenshot{
  display: flex; align-items: center; justify-content: center; aspect-ratio: auto;
  margin-inline: auto;
  padding: var(--space-6); background: var(--glass-bg); border: 1px solid var(--glass-border); border-radius: var(--radius-lg);
}
.visual-media--screenshot img{
  width: 100%; max-width: 100%; height: auto; max-height: 28rem; object-fit: contain;
  border-radius: var(--radius-md);
}
.module-taglines{ display: flex; flex-direction: column; gap: var(--space-3); margin: var(--space-5) 0; }
.module-taglines p{ color: var(--text-muted); font-size: .95rem; line-height: 1.6; }
.module-taglines strong{ color: var(--text); }

/* -- HUB-FEATURE — selettore di funzionalità sulla pagina prodotto, subito
   dopo l'hero: un pannello (screenshot + copy) alla volta, cambiato dai tab
   sopra. Interamente CSS (radio-hack: input radio nascosti + label come
   tab + :checked ~ per mostrare il pannello corrispondente), NIENTE JS —
   ogni pannello resta comunque scritto per intero nell'HTML fin dal
   caricamento (nessun contenuto iniettato al click), quindi nulla di
   invisibile a un crawler. Le immagini restano "nude" (niente bordo/vetro/
   angoli smussati) a differenza di .visual-media--screenshot, di proposito:
   qui sono un riferimento rapido dentro il selettore, non lo spotlight di
   prodotto delle sezioni sotto. */
.hub-feature-input{ position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }

/* Stesso identico "vetro" di .hub-before-after-panel (hub before/after
   della home): fondo glass, bordo, radius-lg, blur, ombra. Contenitore
   unico invece di riusare quella classe per nome, per non legare
   semanticamente un componente a metà pagina al concetto di "hero". */
.hub-feature{
  background: var(--glass-bg); border: 1px solid var(--glass-border); border-radius: var(--radius-lg);
  backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px);
  overflow: hidden;
  box-shadow: 0 30px 60px -30px rgba(0,0,0,.6), var(--glass-shadow);
}
/* Stessa identica geometria di .hub-before-after-tabs/.hub-before-after-tab:
   barra separata da un bordo sotto, tab con angoli smussati (--radius-sm),
   non a pillola. */
.hub-feature-tabs{
  display: flex; flex-wrap: wrap; gap: var(--space-2);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border);
}
.hub-feature-tab{
  cursor: pointer; user-select: none;
  padding: .45rem 1rem; border-radius: var(--radius-sm);
  font-size: .82rem; font-weight: 600; color: var(--text-muted);
  border: 1px solid transparent;
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.hub-feature-tab:hover{ color: var(--text); }

/* Tutti i pannelli restano nella stessa cella di griglia (grid-area
   1/1, sovrapposti) invece di essere tolti dal layout con display:none:
   così la riga si dimensiona sul più alto di TUTTI, anche quelli nascosti
   con visibility (che continuano a occupare spazio, a differenza di
   display:none) — l'hub non cambia più altezza cambiando tab. */
.hub-feature-panels{ display: grid; padding: var(--space-7) var(--space-6); }
.hub-feature-panel{ grid-area: 1 / 1; visibility: hidden; pointer-events: none; }
/* align-items:start (non center): sia l'immagine sia il blocco di testo
   hanno altezza diversa da pannello a pannello (screenshot diversi, liste
   con un numero diverso di voci) — centrati "salterebbero" ogni volta
   nella riga alta fissa (vedi sopra). Ancorati tutti in alto restano
   sempre allo stesso bordo superiore, titolo compreso. */
.hub-feature-panel-inner{ display: grid; grid-template-columns: 1fr 2fr; gap: var(--space-9); align-items: start; }

/* Colonna anziché blocco semplice: quando un pannello ha due screenshot
   impilati (es. Blending 32bit) restano separati da uno spazio invece di
   toccarsi — con una sola immagine si comporta identica a un div normale.
   align-items:flex-start è la parte che conta: senza, lo stretch di
   default di flexbox ingrandirebbe ogni immagine a piena larghezza della
   colonna, esattamente il contrario di "mai oltre la dimensione naturale"
   che vogliamo (vedi width:auto sotto). */
.hub-feature-media{ display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-4); }
/* max-width invece di width:100% — l'immagine non deve mai essere
   ingrandita oltre la sua dimensione naturale, solo ridotta se la colonna
   è più stretta di lei. */
.hub-feature-media img{
  display: block; max-width: 100%; width: auto; height: auto;
  border-radius: 0; border: none; box-shadow: none; background: none;
}

.hub-feature-copy h3{ font-size: 1.8rem; color: var(--text-muted); margin-bottom: var(--space-3); }
.hub-feature-copy > p{ color: var(--text-muted); font-size: .95rem; line-height: 1.6; }
/* La lista con spunta è .spotlight-features/.spotlight-check già esistenti
   (stesse classi delle sezioni sotto) — nessuna classe nuova qui. */

/* Radio-hack GENERICO, non legato agli ID di un prodotto specifico:
   nth-of-type conta solo la POSIZIONE (1°, 2°, 3° input/tab/pannello),
   quindi la stessa regola vale per ogni .hub-feature del sito (Tonality
   Masks, Color Grading, futuri) purché l'ordine nel DOM di
   .hub-feature-input, .hub-feature-tab e .hub-feature-panel corrisponda
   posizione per posizione (1° input ↔ 1° tab ↔ 1° pannello, ecc.) — gli
   ID/nomi restano liberi, servono solo per il "for" delle label. Fino a
   12 posizioni, oltre le quali si aggiungono altre righe allo stesso
   modo. Stato attivo identico a .hub-before-after-tab.is-active (stesso
   hub before/after della home): fondo accent-soft, bordo accent, testo
   normale — l'arancio resta riservato al "baffo" dei punti elenco a
   destra, così i due colori non competono per lo stesso significato. */
.hub-feature-input:nth-of-type(1):checked ~ .hub-feature-tabs .hub-feature-tab:nth-of-type(1){ color: var(--text); background: var(--accent-soft); border-color: var(--accent); }
.hub-feature-input:nth-of-type(1):checked ~ .hub-feature-panels .hub-feature-panel:nth-of-type(1){ visibility: visible; pointer-events: auto; }
.hub-feature-input:nth-of-type(2):checked ~ .hub-feature-tabs .hub-feature-tab:nth-of-type(2){ color: var(--text); background: var(--accent-soft); border-color: var(--accent); }
.hub-feature-input:nth-of-type(2):checked ~ .hub-feature-panels .hub-feature-panel:nth-of-type(2){ visibility: visible; pointer-events: auto; }
.hub-feature-input:nth-of-type(3):checked ~ .hub-feature-tabs .hub-feature-tab:nth-of-type(3){ color: var(--text); background: var(--accent-soft); border-color: var(--accent); }
.hub-feature-input:nth-of-type(3):checked ~ .hub-feature-panels .hub-feature-panel:nth-of-type(3){ visibility: visible; pointer-events: auto; }
.hub-feature-input:nth-of-type(4):checked ~ .hub-feature-tabs .hub-feature-tab:nth-of-type(4){ color: var(--text); background: var(--accent-soft); border-color: var(--accent); }
.hub-feature-input:nth-of-type(4):checked ~ .hub-feature-panels .hub-feature-panel:nth-of-type(4){ visibility: visible; pointer-events: auto; }
.hub-feature-input:nth-of-type(5):checked ~ .hub-feature-tabs .hub-feature-tab:nth-of-type(5){ color: var(--text); background: var(--accent-soft); border-color: var(--accent); }
.hub-feature-input:nth-of-type(5):checked ~ .hub-feature-panels .hub-feature-panel:nth-of-type(5){ visibility: visible; pointer-events: auto; }
.hub-feature-input:nth-of-type(6):checked ~ .hub-feature-tabs .hub-feature-tab:nth-of-type(6){ color: var(--text); background: var(--accent-soft); border-color: var(--accent); }
.hub-feature-input:nth-of-type(6):checked ~ .hub-feature-panels .hub-feature-panel:nth-of-type(6){ visibility: visible; pointer-events: auto; }
.hub-feature-input:nth-of-type(7):checked ~ .hub-feature-tabs .hub-feature-tab:nth-of-type(7){ color: var(--text); background: var(--accent-soft); border-color: var(--accent); }
.hub-feature-input:nth-of-type(7):checked ~ .hub-feature-panels .hub-feature-panel:nth-of-type(7){ visibility: visible; pointer-events: auto; }
.hub-feature-input:nth-of-type(8):checked ~ .hub-feature-tabs .hub-feature-tab:nth-of-type(8){ color: var(--text); background: var(--accent-soft); border-color: var(--accent); }
.hub-feature-input:nth-of-type(8):checked ~ .hub-feature-panels .hub-feature-panel:nth-of-type(8){ visibility: visible; pointer-events: auto; }
.hub-feature-input:nth-of-type(9):checked ~ .hub-feature-tabs .hub-feature-tab:nth-of-type(9){ color: var(--text); background: var(--accent-soft); border-color: var(--accent); }
.hub-feature-input:nth-of-type(9):checked ~ .hub-feature-panels .hub-feature-panel:nth-of-type(9){ visibility: visible; pointer-events: auto; }
.hub-feature-input:nth-of-type(10):checked ~ .hub-feature-tabs .hub-feature-tab:nth-of-type(10){ color: var(--text); background: var(--accent-soft); border-color: var(--accent); }
.hub-feature-input:nth-of-type(10):checked ~ .hub-feature-panels .hub-feature-panel:nth-of-type(10){ visibility: visible; pointer-events: auto; }
.hub-feature-input:nth-of-type(11):checked ~ .hub-feature-tabs .hub-feature-tab:nth-of-type(11){ color: var(--text); background: var(--accent-soft); border-color: var(--accent); }
.hub-feature-input:nth-of-type(11):checked ~ .hub-feature-panels .hub-feature-panel:nth-of-type(11){ visibility: visible; pointer-events: auto; }
.hub-feature-input:nth-of-type(12):checked ~ .hub-feature-tabs .hub-feature-tab:nth-of-type(12){ color: var(--text); background: var(--accent-soft); border-color: var(--accent); }
.hub-feature-input:nth-of-type(12):checked ~ .hub-feature-panels .hub-feature-panel:nth-of-type(12){ visibility: visible; pointer-events: auto; }

/* In più, anteprima al passaggio del mouse: :has() risale dalla label in
   hover fino al contenitore .hub-feature per attivare il pannello
   corrispondente, senza aspettare il click (utile con mouse; su touch,
   dove :hover non esiste davvero, resta comunque il click sul radio).
   Il click continua a "fissare" la scelta tramite :checked sopra — appena
   il mouse esce, se non hai cliccato torna al tab selezionato prima.
   Anche qui nth-of-type generico, stesso principio di sopra.

   Finché un tab qualsiasi è in hover, queste due regole di reset
   nascondono TUTTI i pannelli e disattivano TUTTI i tab — altrimenti
   quello ancora :checked resterebbe visibile/attivo insieme a quello
   sotto il mouse. !important necessario per battere le regole
   :nth-of-type(N):checked sopra; le regole puntuali sotto sono più
   specifiche di questo reset generico quindi vincono comunque su di
   esso, pur avendo anche loro !important. */
.hub-feature:has(.hub-feature-tab:hover) .hub-feature-panel{ visibility: hidden !important; pointer-events: none !important; }
.hub-feature:has(.hub-feature-tab:hover) .hub-feature-tabs .hub-feature-tab{ color: var(--text-muted) !important; background: transparent !important; border-color: transparent !important; }

.hub-feature:has(.hub-feature-tab:nth-of-type(1):hover) .hub-feature-tabs .hub-feature-tab:nth-of-type(1){ color: var(--text) !important; background: var(--accent-soft) !important; border-color: var(--accent) !important; }
.hub-feature:has(.hub-feature-tab:nth-of-type(1):hover) .hub-feature-panels .hub-feature-panel:nth-of-type(1){ visibility: visible !important; pointer-events: auto !important; }
.hub-feature:has(.hub-feature-tab:nth-of-type(2):hover) .hub-feature-tabs .hub-feature-tab:nth-of-type(2){ color: var(--text) !important; background: var(--accent-soft) !important; border-color: var(--accent) !important; }
.hub-feature:has(.hub-feature-tab:nth-of-type(2):hover) .hub-feature-panels .hub-feature-panel:nth-of-type(2){ visibility: visible !important; pointer-events: auto !important; }
.hub-feature:has(.hub-feature-tab:nth-of-type(3):hover) .hub-feature-tabs .hub-feature-tab:nth-of-type(3){ color: var(--text) !important; background: var(--accent-soft) !important; border-color: var(--accent) !important; }
.hub-feature:has(.hub-feature-tab:nth-of-type(3):hover) .hub-feature-panels .hub-feature-panel:nth-of-type(3){ visibility: visible !important; pointer-events: auto !important; }
.hub-feature:has(.hub-feature-tab:nth-of-type(4):hover) .hub-feature-tabs .hub-feature-tab:nth-of-type(4){ color: var(--text) !important; background: var(--accent-soft) !important; border-color: var(--accent) !important; }
.hub-feature:has(.hub-feature-tab:nth-of-type(4):hover) .hub-feature-panels .hub-feature-panel:nth-of-type(4){ visibility: visible !important; pointer-events: auto !important; }
.hub-feature:has(.hub-feature-tab:nth-of-type(5):hover) .hub-feature-tabs .hub-feature-tab:nth-of-type(5){ color: var(--text) !important; background: var(--accent-soft) !important; border-color: var(--accent) !important; }
.hub-feature:has(.hub-feature-tab:nth-of-type(5):hover) .hub-feature-panels .hub-feature-panel:nth-of-type(5){ visibility: visible !important; pointer-events: auto !important; }
.hub-feature:has(.hub-feature-tab:nth-of-type(6):hover) .hub-feature-tabs .hub-feature-tab:nth-of-type(6){ color: var(--text) !important; background: var(--accent-soft) !important; border-color: var(--accent) !important; }
.hub-feature:has(.hub-feature-tab:nth-of-type(6):hover) .hub-feature-panels .hub-feature-panel:nth-of-type(6){ visibility: visible !important; pointer-events: auto !important; }
.hub-feature:has(.hub-feature-tab:nth-of-type(7):hover) .hub-feature-tabs .hub-feature-tab:nth-of-type(7){ color: var(--text) !important; background: var(--accent-soft) !important; border-color: var(--accent) !important; }
.hub-feature:has(.hub-feature-tab:nth-of-type(7):hover) .hub-feature-panels .hub-feature-panel:nth-of-type(7){ visibility: visible !important; pointer-events: auto !important; }
.hub-feature:has(.hub-feature-tab:nth-of-type(8):hover) .hub-feature-tabs .hub-feature-tab:nth-of-type(8){ color: var(--text) !important; background: var(--accent-soft) !important; border-color: var(--accent) !important; }
.hub-feature:has(.hub-feature-tab:nth-of-type(8):hover) .hub-feature-panels .hub-feature-panel:nth-of-type(8){ visibility: visible !important; pointer-events: auto !important; }
.hub-feature:has(.hub-feature-tab:nth-of-type(9):hover) .hub-feature-tabs .hub-feature-tab:nth-of-type(9){ color: var(--text) !important; background: var(--accent-soft) !important; border-color: var(--accent) !important; }
.hub-feature:has(.hub-feature-tab:nth-of-type(9):hover) .hub-feature-panels .hub-feature-panel:nth-of-type(9){ visibility: visible !important; pointer-events: auto !important; }
.hub-feature:has(.hub-feature-tab:nth-of-type(10):hover) .hub-feature-tabs .hub-feature-tab:nth-of-type(10){ color: var(--text) !important; background: var(--accent-soft) !important; border-color: var(--accent) !important; }
.hub-feature:has(.hub-feature-tab:nth-of-type(10):hover) .hub-feature-panels .hub-feature-panel:nth-of-type(10){ visibility: visible !important; pointer-events: auto !important; }
.hub-feature:has(.hub-feature-tab:nth-of-type(11):hover) .hub-feature-tabs .hub-feature-tab:nth-of-type(11){ color: var(--text) !important; background: var(--accent-soft) !important; border-color: var(--accent) !important; }
.hub-feature:has(.hub-feature-tab:nth-of-type(11):hover) .hub-feature-panels .hub-feature-panel:nth-of-type(11){ visibility: visible !important; pointer-events: auto !important; }
.hub-feature:has(.hub-feature-tab:nth-of-type(12):hover) .hub-feature-tabs .hub-feature-tab:nth-of-type(12){ color: var(--text) !important; background: var(--accent-soft) !important; border-color: var(--accent) !important; }
.hub-feature:has(.hub-feature-tab:nth-of-type(12):hover) .hub-feature-panels .hub-feature-panel:nth-of-type(12){ visibility: visible !important; pointer-events: auto !important; }

@media (max-width: 900px){
  .hub-feature-panel-inner{ grid-template-columns: 1fr; }
  .hub-feature-panel-inner .hub-feature-media{ order: -1; }
  /* A dimensione naturale (non più forzata a piena larghezza) l'immagine
     si allineerebbe a sinistra come ogni <img> di default — qui invece va
     centrata orizzontalmente nella colonna piena. */
  .hub-feature-media img{ margin-inline: auto; }
}

/* -- Blocco video compatto "guardalo in azione", subito dopo la hero ----- */
.product-video-section{ padding-block: var(--space-8); border-top: 1px solid var(--border); }
.product-video-frame{
  max-width: 56rem; margin-inline: auto; aspect-ratio: 16 / 9; position: relative;
  border-radius: var(--radius-lg); overflow: hidden; border: 1px solid var(--glass-border); box-shadow: var(--glass-shadow);
  background: #000;
}
.product-video-frame iframe{ position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
/* Variante a due video affiancati (pagina Tonality Masks): stessa
   .product-video-frame di prima per ognuno, solo senza il suo
   max-width/margin-inline individuale — è la griglia a centrarli insieme. */
.product-video-grid{ display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-6); max-width: 68rem; margin-inline: auto; }
.product-video-grid .product-video-frame{ max-width: none; margin-inline: 0; }
@media (max-width: 760px){
  .product-video-grid{ grid-template-columns: 1fr; }
}

/* PROVA (solo it/blending.html per ora): video affiancato a un before/
   after dentro .visual-grid (align-items:center, non stretch) — a
   differenza di .product-video-grid sopra, qui .product-video-frame non
   riceve una larghezza dalla colonna: senza contenuto in flusso normale
   al suo interno (l'iframe è position:absolute) e senza stretch,
   il browser gli risolve una larghezza dal contenuto (~0) invece che
   dalla colonna, e aspect-ratio collassa tutto a pochi px. width:100%
   esplicito forza una larghezza definita (quella della colonna), da cui
   aspect-ratio calcola poi l'altezza correttamente. */
.visual-grid .product-video-frame{ width: 100%; max-width: none; margin-inline: 0; }

/* -- Mosaico 3x3 di foto di esempio, nessun gap tra le celle ---- */
.photo-mosaic-section{ padding-block: var(--space-10); border-top: 1px solid var(--border); }
.photo-mosaic{
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 0;
  border-radius: var(--radius-lg); overflow: hidden;
}
.photo-mosaic img{ display: block; width: 100%; height: 100%; aspect-ratio: 3 / 2; object-fit: cover; }
@media (max-width: 640px){
  .photo-mosaic{ grid-template-columns: repeat(2, 1fr); }
}

/* -- Coppie di esempio before/after statiche dentro un modulo (senza slider JS) ---- */
.ba-pairs{ display: flex; flex-direction: column; gap: var(--space-3); width: 100%; }
.ba-pair{ display: grid; grid-template-columns: 1fr; gap: var(--space-3); }
.ba-pair figure{ position: relative; margin: 0; border-radius: var(--radius-md); overflow: hidden; border: 1px solid var(--glass-border); }
.ba-pair img{ width: 100%; height: 100%; object-fit: cover; display: block; aspect-ratio: 3 / 2; }
.ba-pair figcaption{
  position: absolute; top: .5rem; left: .5rem;
  background: rgba(11,11,16,.72); backdrop-filter: blur(6px);
  color: var(--text); border: 1px solid var(--border-strong);
  font-size: .62rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  padding: .25rem .55rem; border-radius: var(--radius-pill);
}

/* -- Slider before/after a foto singola trascinabile dentro un modulo
   (riusa la logica di trascinamento .compare-media/.compare-img/
   .compare-handle della homepage, con ambito locale avvolgendo ogni
   istanza nella propria card .ba-slider) --- */
.ba-slider{ position: relative; border-radius: var(--radius-md); overflow: hidden; }

/* --------------------------------------------------------------------------
   21. CART DRAWER — UI del carrello nostra (shop.html), alimentata live dal
   data-data-callback di FastSpring e pilotata dai metodi JS
   fastspring.builder.*. Solo il passaggio finale di pagamento apre
   l'iframe/popup proprio di FastSpring; tutto ciò che c'è prima
   (navigazione, quantità, coupon, totali) è nostro, stilizzato per
   abbinarsi al resto del sito invece che alla UI di checkout di FastSpring.
-------------------------------------------------------------------------- */
.cart-toggle{
  position: fixed; right: var(--space-6); bottom: var(--space-6); z-index: 200;
  width: 3.5rem; height: 3.5rem; border-radius: var(--radius-lg);
  display: flex; align-items: center; justify-content: center;
  background: var(--accent); color: #fff; border: 1px solid var(--glass-border-strong);
  box-shadow: 0 12px 28px -8px rgba(37,99,235,.55), var(--glass-shadow);
  transition: transform var(--dur-fast) var(--ease), filter var(--dur-fast) var(--ease);
}
.cart-toggle:hover{ filter: brightness(1.08); transform: translateY(-2px); }
/* L'SVG è scritto inline in ogni pagina prodotto con width/height="20" —
   sovrascritto qui invece che in 16 file diversi, dato che CSS width/
   height ha comunque priorità sugli attributi HTML su <svg>. */
.cart-toggle svg{ width: 24px; height: 24px; }
.cart-toggle-count{
  position: absolute; top: -.3rem; right: -.3rem;
  min-width: 1.35rem; height: 1.35rem; padding: 0 .3rem;
  display: flex; align-items: center; justify-content: center;
  background: var(--accent-2); color: #fff; font-size: .72rem; font-weight: 700;
  border-radius: var(--radius-sm); border: 2px solid var(--bg);
}
.cart-toggle-count:empty{ display: none; }

.cart-scrim{
  position: fixed; inset: 0; z-index: 199;
  background: rgba(6,6,9,.6); backdrop-filter: blur(2px);
  opacity: 0; pointer-events: none; transition: opacity var(--dur-base) var(--ease);
}
.cart-scrim.is-open{ opacity: 1; pointer-events: auto; }

/* -- Cart Abandonment Offer (js/cart/cart-abandonment-offer.js) ------------
   Stesso linguaggio visivo di .license-discount-tag--offer-countdown
   (nome prodotto, percentuale+countdown, coupon), qui dentro un overlay
   centrato invece che nell'angolo — logica e countdown indipendenti da
   offer-countdown.js/.offer-float, mai toccati da qui. Scrim e box sono
   fratelli diretti di body (non annidati l'uno nell'altro): il box usa
   position:fixed, e siccome lo scrim ha backdrop-filter (che crea un
   nuovo containing block per i discendenti position:fixed, vedi nota su
   .site-header più sopra), annidarlo dentro lo scrim gli farebbe perdere
   l'ancoraggio al viewport reale. Sopra al drawer carrello (z-index
   199/201), sotto la dissolvenza di cambio pagina (500). Attivo sia
   desktop che mobile, a differenza di .offer-float. */
.cart-abandonment-scrim{
  position: fixed; inset: 0; z-index: 250;
  background: rgba(6,6,9,.6); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  opacity: 0; pointer-events: none; transition: opacity var(--dur-base) var(--ease);
}
.cart-abandonment-scrim.is-open{ opacity: 1; pointer-events: auto; }
.cart-abandonment-box{
  position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 251;
  max-width: min(92vw, 22rem);
  padding: var(--space-5) var(--space-6);
  background: var(--surface-2);
  border: 1px solid var(--violet-soft);
  border-radius: var(--radius-lg);
  box-shadow: 0 10px 30px rgba(0,0,0,.25);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity var(--dur-base) var(--ease);
}
.cart-abandonment-box.is-open{ opacity: 1; visibility: visible; pointer-events: auto; }
/* Qui il verde di .license-discount-tag--offer-countdown diventa viola
   (--violet): stesso layout/tipografia riusati, solo l'accento colore
   cambia per distinguere il popup di abbandono dal badge/box dell'offerta
   di lancio (.offer-float), che restano verdi e non vengono toccati da
   questa regola più specifica. */
.cart-abandonment-box .discount-product,
.cart-abandonment-reminder .discount-product{ color: var(--text); }
.cart-abandonment-box .discount-percent,
.cart-abandonment-box .discount-line,
.cart-abandonment-reminder .discount-percent,
.cart-abandonment-reminder .discount-line{ color: var(--violet); }
.cart-abandonment-dismiss{
  align-self: center; margin-top: var(--space-4);
  background: rgba(139,92,246,.5); color: #fff; border-color: transparent;
}
.cart-abandonment-dismiss:hover{ background: rgba(139,92,246,.65); }
/* Sotto i 900px la riga percentuale+testo non resta a due colonne: la
   percentuale diventa una riga a sé sopra a testo e countdown (che sono
   già impilati tra loro via .discount-text), tre righe in tutto invece
   di due colonne strette. Solo per questo popup, non per .offer-float né
   per il badge hero della home, che riusano lo stesso .discount-row ma
   restano nella loro variante a riga singola. */
@media (max-width: 900px){
  .cart-abandonment-box .discount-row{ flex-direction: column; align-items: flex-start; }
}

/* Card di promemoria del coupon (js/cart/cart-abandonment-offer.js):
   compare in shop, sotto a .offer-float, dopo che il popup di abbandono
   è stato mostrato e chiuso (o subito al caricamento, se era già stato
   mostrato in una visita precedente entro la finestra di 7 giorni). Il
   "top" esatto è calcolato in JS leggendo l'altezza reale di .offer-float
   a runtime (sola lettura, nessuno stato condiviso), qui solo l'aspetto:
   stessa card di .offer-float ma bordo/accento viola. Solo desktop,
   stesso motivo di .offer-float: non esiste un posto per impilarla sotto
   su schermi dove .offer-float stessa non c'è. */
.cart-abandonment-reminder{ display: none; }
@media (min-width: 901px){
  .cart-abandonment-reminder{
    display: inline-flex;
    position: fixed; right: var(--space-6); z-index: 80;
    padding: var(--space-4) var(--space-5);
    background: var(--surface-2);
    border: 1px solid var(--violet-soft);
    border-radius: var(--radius-lg);
    box-shadow: 0 10px 30px rgba(0,0,0,.25);
  }
}

/* Transizione pagina al cambio lingua — una dissolvenza di cortesia così
   scambiare EN/IT non si legge mai come un ricaricamento brusco. Stesso
   look di .cart-scrim/.page-scrim. Sta sopra a tutto (header incluso)
   dato che copre l'intera pagina. Il JS attiva .is-active per farla
   comparire in dissolvenza sulla pagina in uscita subito prima di
   navigare; la pagina in arrivo non ha bisogno di un passaggio di
   "dissolvenza in uscita" corrispondente qui — il fade-in universale del
   body sopra (vedi regola body{}) dissolve già la nuova pagina in vista. */
.page-transition-scrim{
  position: fixed; inset: 0; z-index: 500;
  background: rgba(6,6,9,.6); backdrop-filter: blur(0); -webkit-backdrop-filter: blur(0);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity var(--dur-base) var(--ease), backdrop-filter var(--dur-base) var(--ease), -webkit-backdrop-filter var(--dur-base) var(--ease), visibility 0s linear var(--dur-base);
}
.page-transition-scrim.is-active{
  opacity: 1; visibility: visible; pointer-events: auto;
  backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
  transition: opacity var(--dur-base) var(--ease), backdrop-filter var(--dur-base) var(--ease), -webkit-backdrop-filter var(--dur-base) var(--ease);
}

.cart-drawer{
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 201;
  width: min(26rem, 100vw);
  display: flex; flex-direction: column;
  background: var(--bg); border-left: 1px solid var(--glass-border);
  box-shadow: -20px 0 60px rgba(0,0,0,.5);
  transform: translateX(100%); transition: transform var(--dur-base) var(--ease);
}
.cart-drawer.is-open{ transform: translateX(0); }

.cart-drawer-head{
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
  padding: var(--space-5); border-bottom: 1px solid var(--border);
}
.cart-drawer-head h3{ font-family: var(--font-heading); font-size: 1.1rem; margin-right: auto; }
.cart-drawer-head-actions{ display: flex; align-items: center; gap: var(--space-4); }
.cart-clear-btn{
  font-size: .78rem; color: var(--text-muted);
  transition: color var(--dur-fast) var(--ease);
}
.cart-clear-btn:hover{ color: var(--accent-2); }
.cart-drawer-close{
  width: 2.25rem; height: 2.25rem; border-radius: var(--radius-sm);
  display: flex; align-items: center; justify-content: center;
  color: var(--text-muted); border: 1px solid var(--glass-border);
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.cart-drawer-close:hover{ color: var(--text); border-color: var(--glass-border-strong); }

.cart-drawer-body{ flex: 1; overflow-y: auto; padding: var(--space-5); display: flex; flex-direction: column; gap: var(--space-4); }

.cart-empty{ margin: auto; text-align: center; color: var(--text-muted); font-size: .92rem; }

.cart-item{
  display: grid; grid-template-columns: auto 1fr auto; gap: var(--space-3);
  padding: var(--space-4); background: var(--glass-bg); border: 1px solid var(--glass-border); border-radius: var(--radius-md);
}
.cart-item-icon{
  width: 2.75rem; height: 2.75rem; border-radius: var(--radius-sm); object-fit: contain;
}
.cart-item-name{ font-weight: 700; font-size: .92rem; }
.cart-item-remove{ color: var(--text-faint); font-size: .78rem; transition: color var(--dur-fast) var(--ease); }
.cart-item-remove:hover{ color: var(--accent-2); }
.cart-item-bundle-icons{ display: flex; flex-wrap: wrap; gap: .5rem; margin: .6rem 0 .8rem; }
.cart-item-bundle-icons img{ width: 2.1rem; height: 2.1rem; border-radius: 5px; }
.cart-item-qty{
  display: inline-flex; align-items: center; gap: .4rem; margin-top: .3rem;
  border: 1px solid var(--glass-border); border-radius: var(--radius-sm); padding: .2rem;
}
.cart-item-qty button{
  width: 1.5rem; height: 1.5rem; border-radius: var(--radius-sm); color: var(--text-muted);
  display: flex; align-items: center; justify-content: center; font-size: .9rem;
}
.cart-item-qty button:hover{ background: var(--glass-bg-strong); color: var(--text); }
.cart-item-qty span{ min-width: 1.2rem; text-align: center; font-size: .84rem; }
.cart-item-price{ text-align: right; }
.cart-item-price-total{ font-weight: 700; }
.cart-item-price-old{ display: block; color: var(--ok); text-decoration: line-through; font-size: .78rem; }

.cart-coupon{ display: flex; gap: var(--space-2); }
.cart-coupon input{
  flex: 1; padding: .65rem .9rem; border-radius: var(--radius-sm);
  background: var(--glass-bg); border: 1px solid var(--glass-border); color: var(--text); font-size: .86rem;
}
.cart-coupon input::placeholder{ color: var(--text-faint); }
.cart-coupon button{
  padding: .65rem 1.1rem; border-radius: var(--radius-sm);
  background: var(--glass-bg-strong); border: 1px solid var(--glass-border-strong); color: var(--text);
  font-size: .82rem; font-weight: 700; white-space: nowrap;
}
.cart-coupon button:hover{ border-color: var(--accent); }
.cart-coupon-msg{ font-size: .78rem; margin: .5rem 0 var(--space-6); min-height: 1em; }
.cart-coupon-msg[data-state="ok"]{ color: var(--ok); display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }
.cart-coupon-msg[data-state="error"]{ color: var(--accent-2); }
.cart-coupon-msg-code{ font-weight: 700; }

.cart-drawer-foot{ padding: var(--space-5); border-top: 1px solid var(--border); }
.cart-summary-row{ display: flex; align-items: center; justify-content: space-between; font-size: .86rem; color: var(--text-muted); padding: .2rem 0; }
.cart-summary-row--total{ color: var(--text); font-size: 1.05rem; font-weight: 700; padding-top: .6rem; margin-top: .4rem; border-top: 1px solid var(--border); }
.cart-summary-row--discount{ color: var(--ok); }

/* --------------------------------------------------------------------------
   24. CONTENUTO LEGALE — Privacy Policy, Terms of Service, Cookie Policy, EULA
-------------------------------------------------------------------------- */
.legal-section{ padding-block: var(--space-9); }
/* -- Firma d'autore in calce agli articoli (partials/author-credit.php) --
   .legal-content a{color:accent} sopra (più specifica di .team-social
   da solo) tingerebbe di blu anche le icone social qui — le riporto al
   grigio di default/blu solo in hover, come nelle card del team in home. */
.author-credit .team-social{ color: var(--text-muted); }
.author-credit .team-social:hover{ color: var(--accent); }
.legal-content{ max-width: 68ch; margin-inline: auto; color: var(--text-muted); }
.legal-updated{ color: var(--text-faint); font-size: .85rem; margin-bottom: var(--space-7); }
.legal-content h2{ color: var(--text); font-size: 1.3rem; margin: var(--space-8) 0 var(--space-3); }
.legal-content h2:first-of-type{ margin-top: 0; }
.legal-content h3{ color: var(--text); font-size: 1.05rem; margin: var(--space-6) 0 var(--space-2); }
.legal-content p{ margin: 0 0 var(--space-4); line-height: 1.7; }
.legal-content ul, .legal-content ol{ margin: 0 0 var(--space-4); padding-left: 1.25em; }
.legal-content ul{ list-style: disc; }
.legal-content ol{ list-style: decimal; }
.legal-content li{ margin-bottom: var(--space-2); line-height: 1.7; }

/* "Torna al Supporto" — it/faq/*.html. margin-top: 3.125rem = 50px (rem
   invece di px per restare in scala con lo zoom/font-size del resto del
   sito), staccato dal testo sopra senza riga divisoria. Colorato accent-2
   per leggersi come azione di navigazione, non come un altro link nel
   corpo del testo. Selettori ".legal-content .faq-back-link(...)" (non solo
   ".faq-back-link") apposta: sia ".legal-content p" (margin) sia
   ".legal-content a" (colore), qui sotto, hanno più specificità di una
   singola classe da sola (classe+elemento batte una classe sola) e
   vincerebbero altrimenti, a prescindere dall'ordine nel foglio. */
.legal-content .faq-back-link{ margin-top: 3.125rem; }
.legal-content .faq-back-link a{ color: var(--accent-2); font-weight: 700; }
.legal-content .faq-back-link a:hover{ opacity: .75; }
.legal-content li::marker{ color: var(--accent); }
.legal-content a{ color: var(--accent); text-underline-offset: 2px; }
.legal-content a:hover{ color: var(--accent-2); }
.legal-content strong{ color: var(--text); }

/* Article variant — pairs with .legal-content (class="legal-content
   article-content") on blog/guide-style pages so their section headings
   read as accent-blue instead of the plain-white h2 that Privacy
   Policy/Terms/Cookie Policy/EULA use, and get a light dashed rule above
   each one to read as a chapter break. Scoped to this extra class instead
   of touching .legal-content h2 directly, so the legal pages are
   untouched. */
.article-content h2{
  color: var(--accent);
  padding-top: var(--space-6);
  border-top: 1px dashed var(--border);
}
.article-content h2:first-of-type{ padding-top: 0; border-top: none; }

/* Source-attribution note: boxed callout at the top of an article,
   before the content, pointing readers to Gaspare Silverii (name links
   to his homepage, accent blue like any normal site link) plus his
   blog/corsi (silent links: same color as the surrounding text, no
   accent, there purely for internal linking/SEO, not meant to read as
   a second CTA next to the name link). Box itself keeps orange
   accent-2 border/tint on purpose, distinct from the site's usual blue,
   so it reads as "outbound reference" rather than a normal in-site card. */
.article-source-note{
  background: rgba(249,115,22,.05); border: 1px solid var(--accent-2);
  border-radius: var(--radius-md); padding: var(--space-5);
  margin-bottom: var(--space-8);
  font-size: .92rem; line-height: 1.65; color: var(--text-muted);
}
.article-source-note p{ margin: 0; }
.article-source-note p + p{ margin-top: var(--space-3); }
.article-source-note a{ color: var(--accent); text-underline-offset: 2px; }
.article-source-note a:hover{ color: var(--accent-hover); }
.article-source-note a.link-muted{ color: inherit; text-decoration-color: var(--glass-border-strong); }
.article-source-note a.link-muted:hover{ color: inherit; text-decoration-color: currentColor; }
.article-source-note a.link-name{ font-weight: 500; }

/* --------------------------------------------------------------------------
   25. BANNER CONSENSO COOKIE — vedi partials/cookie-banner.php +
   js/cookie/cookie-consent.js. z-index 600, sopra a .page-transition-scrim
   (500), così è sempre raggiungibile anche a metà transizione.
-------------------------------------------------------------------------- */
/* Banner deliberatamente chiaro/bianco, in controtendenza col resto del
   sito (tema scuro): serve a farlo risaltare come elemento di sistema/
   utility distinto dal contenuto, non un pannello del sito stesso — per
   questo qui sotto sono valori letterali invece delle variabili del tema
   scuro (--surface-2, --text-muted, --glass-bg, ecc.), che darebbero testo
   chiaro su sfondo chiaro. */
.cookie-banner{
  position: fixed; left: var(--space-5); right: var(--space-5); bottom: var(--space-5);
  max-width: 34rem; margin-inline: auto;
  z-index: 600;
  background: #fff;
  border: 1px solid #e2e4ea;
  border-radius: var(--radius-lg);
  box-shadow: 0 20px 50px rgba(10,12,20,.35);
  padding: var(--space-5);
  max-height: calc(100vh - var(--space-8));
  overflow-y: auto;
  transform: translateY(calc(100% + var(--space-5)));
  opacity: 0;
  pointer-events: none;
  transition: transform var(--dur-base) var(--ease), opacity var(--dur-base) var(--ease);
}
.cookie-banner.is-visible{ transform: translateY(0); opacity: 1; pointer-events: auto; }

.cookie-banner-text{ font-size: var(--fs-small); color: #4b4d5c; line-height: 1.6; margin: 0 0 var(--space-4); }
.cookie-banner-text a{ color: var(--accent); }
.cookie-banner-text a:hover{ color: var(--accent-2); }

.cookie-banner-actions{ display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); flex-wrap: wrap; }
.cookie-banner-actions-right{ display: flex; gap: var(--space-2); flex-wrap: wrap; }
.cookie-banner-actions-right .btn{ flex: 1 1 auto; }
.cookie-banner .btn-accept{ background: #1a9c6b; }
.cookie-banner .btn-accept:hover{ background: #178a5e; }
.cookie-banner-prefs-link{
  display: inline-flex; align-items: center; gap: var(--space-2);
  background: none; border: 0; padding: 0; margin: 0;
  color: #4b4d5c; font: inherit; font-size: var(--fs-small);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease);
}
.cookie-banner-prefs-link:hover{ color: #12131a; }
.cookie-banner-prefs-link svg{ flex-shrink: 0; }

.cookie-banner-detail:not([hidden]){ display: flex; flex-direction: column; gap: var(--space-4); }
.cookie-consent-row{
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid #e6e7ec;
}
.cookie-consent-row:last-of-type{ border-bottom: 0; padding-bottom: 0; }
.cookie-consent-copy strong{ display: block; color: #12131a; font-size: var(--fs-small); margin-bottom: 2px; }
.cookie-consent-copy p{ font-size: .8rem; color: #6b6d7a; margin: 0; line-height: 1.5; }

.cookie-consent-row input[type="checkbox"]{
  flex-shrink: 0; width: 2.2rem; height: 1.3rem; margin-top: 2px;
  appearance: none; -webkit-appearance: none;
  background: #d8dae2; border-radius: var(--radius-pill);
  position: relative; cursor: pointer;
  transition: background var(--dur-fast) var(--ease);
}
.cookie-consent-row input[type="checkbox"]::before{
  content: ""; position: absolute; top: 2px; left: 2px;
  width: 1.1rem; height: 1.1rem; border-radius: 50%;
  background: #fff;
  transition: transform var(--dur-fast) var(--ease);
}
.cookie-consent-row input[type="checkbox"]:checked{ background: var(--ok); }
.cookie-consent-row input[type="checkbox"]:checked::before{ transform: translateX(.9rem); }
.cookie-consent-row input[type="checkbox"]:disabled{ opacity: .6; cursor: not-allowed; }
.cart-payment-note{ font-size: .76rem; color: var(--text-faint); text-align: center; margin-top: var(--space-5); margin-bottom: var(--space-3); }
.cart-checkout-note{ font-size: .76rem; color: var(--text-faint); text-align: center; margin-top: var(--space-3); }
.cart-checkout-note a{ color: var(--text-muted); text-decoration: none; }
.cart-checkout-note a:hover{ color: var(--text); }

/* -- Box coupon upgrade (it/en/shop.html, sopra la card bundle) — un'unica
   volta per l'intera pagina, non ripetuto per card prodotto: il coupon è
   nominale/via email, il sito statico non sa chi ha già una licenza
   precedente, quindi qui si limita a segnalarlo con abbastanza risalto da
   non passare inosservato (prima versione, una semplice riga di testo
   piccolo, era troppo debole: rischio concreto di ticket di supporto da
   chi non se ne accorgeva). Vedi anche
   it/en/faq/update-and-upgrade-policy.html. -- */
.upgrade-promo{
  max-width: 42rem; margin: 0 auto var(--space-7);
  padding: var(--space-5) var(--space-6);
  text-align: center;
  background: rgba(62,207,142,.14);
  border: 1px solid rgba(62,207,142,.3);
  border-radius: var(--radius-lg);
}
.upgrade-promo-icon{ color: var(--ok); flex-shrink: 0; }
.upgrade-promo-label{
  display: flex; align-items: center; justify-content: center; gap: .55em;
  font-size: .76rem; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--ok); margin-bottom: var(--space-3);
}
.upgrade-promo-text{ font-size: .95rem; color: var(--text-muted); line-height: 1.6; }
.upgrade-promo-text a{ color: var(--text); font-weight: 700; text-decoration: underline; text-underline-offset: 2px; }
.upgrade-promo-text a:hover{ color: var(--ok); }
.cart-payment-icons{ justify-content: center; }
.cart-payment-icons svg{ width: 2.1rem; }
.cart-checkout-btn{ width: 100%; margin-top: var(--space-4); justify-content: center; }

/* --------------------------------------------------------------------------
   26. ARTICLE CARDS — en/it/articles.html. Reuses .course-card (border,
   glass background, hover: see 17d CARD GLOW) without its .course-media
   image or .course-tag category — with topics that are almost always some
   flavor of "Photoshop technique", both added visual weight without adding
   real information. .course-card--text fixes the spacing that assumed an
   image came first, and gives the title the site's usual link color
   instead of plain white. The topic label (see partials/seo-articles.php)
   does NOT reuse the site's normal .eyebrow style (that one's a
   dash-prefixed line, meant for page heroes) — here it's its own badge,
   rounded like .btn (--radius-sm, not a pill), muted to match the card's
   own border/text instead of an accent color, no hover state of its own.
   align-self: flex-start keeps it from stretching to the card's full
   width, which it otherwise would: .course-body is a flex column, and a
   flex item's own inline-flex display blockifies to flex, so it stretches
   on the cross axis by default. Title and badge both stay static so
   nothing on the card is fighting for hover attention with the card
   border itself (17d).
-------------------------------------------------------------------------- */
.course-card--text .course-body{ padding-top: var(--space-5); }
.course-card--text .article-topic{
  display: inline-flex; align-items: center; align-self: flex-start;
  flex: none; /* it's a <p>, so it would otherwise inherit .course-body p's flex:1 and stretch by leftover space */
  border: 1px solid var(--glass-border); border-radius: var(--radius-sm);
  padding: .3em .85em; margin-bottom: var(--space-3);
  font-size: .72rem; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--text-muted);
}
.course-card--text h3{ color: var(--accent); font-size: 1.15rem; }
/* "Continue Reading" — stessa ricetta statica di .press-card-link
   (colore fisso, nessun hover proprio): tutta la card è già l'unico
   link (17d), quindi anche questa etichetta non deve competere con
   l'hover del bordo. margin-top:auto la ancora in fondo
   alla card anche quando le descrizioni hanno lunghezze diverse, dato
   che .course-body è già una colonna flex. */
.course-card--text .article-read-more{
  display: inline-flex; align-items: center; gap: .4em;
  margin-top: auto; padding-top: var(--space-3);
  font-size: .82rem; font-weight: 700; color: var(--accent);
}

/* --------------------------------------------------------------------------
   26b. SUPPORT HUB — it/en/support.html.

   .account-band: due colonne uguali (testo a sinistra, bottone a destra)
   per la sezione "Il Tuo Account" — grid invece di flex apposta, così il
   bottone ha una vera colonna larga quanto l'altra metà della riga e può
   centrarsi al suo interno (justify-self) invece di stare incollato al
   bordo destro come faceva con space-between.

   .faq-columns: le FAQ raggruppate per tema (vedi partials/seo-faqs.php,
   campo 'topic') non sono righe di card impilate ma colonne verticali
   affiancate, una per topic — rende scansionabile l'intero elenco in un
   colpo d'occhio invece di scorrere gruppo dopo gruppo. Il topic (h3) resta
   accent-2 (arancione) come unico elemento colorato della colonna, i link
   sotto sono bianchi e non-bold apposta per non competere visivamente con
   l'arancione del topic — è quello il colore che deve saltare all'occhio.
-------------------------------------------------------------------------- */
.account-band{
  display: grid; grid-template-columns: 1fr 1fr; align-items: center; gap: var(--space-6);
}
.account-band .btn{ justify-self: center; }
.account-band .btn svg{ width: 22px; height: 22px; }

.faq-columns{ display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-7); align-items: start; margin-top: var(--space-4); }
.faq-column h3{
  font-size: .95rem; margin-bottom: var(--space-4); padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--border); color: var(--accent-2);
}
.faq-link-list{ display: flex; flex-direction: column; gap: var(--space-3); }
.faq-link{ color: var(--text); font-size: .9rem; font-weight: 400; line-height: 1.4; transition: opacity var(--dur-fast) var(--ease); }
.faq-link:hover{ opacity: .72; }

/* Client-only article search (js/article-search/article-search.js) — input reuses the
   .signup-form input[type=email] recipe. Results render as a separate
   list of title-only mini-cards ABOVE #article-grid, never inside it:
   the real grid's DOM order never changes, so 17d's card-glow hover
   binding on the original cards is never at risk of breaking. */
/* Closed state: a centered circular icon button, same width as height.
   Click toggles .is-open, which widens the wrapper to the full bar —
   margin-inline:auto keeps it centered throughout, so growing the width
   alone reads as "unfolding" symmetrically left and right, no transform
   tricks needed. The toggle button itself slides from dead-center (its
   position while the wrapper is still narrow) to pinned-left (once the
   wrapper is wide), animating in step with the same width transition. */
.article-search{
  position: relative; margin-inline: auto; margin-bottom: var(--space-5);
  width: 2.75rem; max-width: 100%;
  transition: width .32s cubic-bezier(.4,0,.2,1);
}
.article-search.is-open{ width: 44rem; }

.article-search-toggle{
  position: absolute; top: 0; left: 50%; transform: translateX(-50%);
  width: 2.75rem; height: 2.75rem; padding: 0;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius-sm); border: 1px solid var(--border-strong);
  background: var(--surface-2); color: var(--text-faint); cursor: pointer;
  transition: left .32s cubic-bezier(.4,0,.2,1), transform .32s cubic-bezier(.4,0,.2,1),
              background-color .2s ease, border-color .2s ease;
}
.article-search.is-open .article-search-toggle{
  left: 0; transform: translateX(0);
  background: transparent; border-color: transparent;
}
.article-search-toggle:hover{ color: var(--text); border-color: var(--border-strong); }
.article-search-toggle:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; }

.article-search input[type="search"]{
  width: 100%; height: 2.75rem; padding: 0 1rem 0 2.5rem; border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong); background: var(--surface-2);
  color: var(--text); font-size: .92rem;
  opacity: 0; pointer-events: none; transition: opacity .18s ease .1s;
}
.article-search.is-open input[type="search"]{ opacity: 1; pointer-events: auto; }
.article-search input[type="search"]::placeholder{ color: var(--text-faint); }
.article-search input[type="search"]:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; }
.article-search input[type="search"]::-webkit-search-cancel-button{ filter: grayscale(1) opacity(.6); }

.article-search-results{
  display: flex; flex-direction: column; gap: 2px;
  max-width: 44rem; margin-inline: auto; margin-bottom: var(--space-7);
  border: 1px solid var(--border); border-radius: var(--radius-md);
  overflow: hidden;
}
.article-search-result{
  padding: .75rem 1.1rem; color: var(--text-muted); font-size: .88rem; font-weight: 400;
  background: var(--surface-2);
}
.article-search-result:hover{ background: var(--glass-bg-strong); color: var(--accent); }
.article-search-empty{ padding: .75rem 1.1rem; color: var(--text-faint); font-size: .86rem; background: var(--surface-2); }

/* --------------------------------------------------------------------------
   26b. HUB TABS — sezione gemella dell'hub plugin qui sotto (§27, stessi
   dati $tm_hub_plugin_images), ma niente JS: radio nascosti + CSS puro,
   click per aprire (non più :hover/:focus-within — cambiato su richiesta
   esplicita, l'interazione voluta è il click, non il passaggio del
   mouse). Stesso principio "puramente CSS" di .license-tooltip più sopra
   in questo file: niente JS da scrivere né mantenere per aprire/chiudere.

   Geometria validata a parte in un prototipo isolato (non uno skew su
   otto colonne affiancate, il taglio diagonale precedente) prima di
   essere portata qui:

   UN SOLO elemento per linguetta (.hub-tabs-tab, non più shape+label+
   gallery separati): l'unione trapezio+rettangolo+trapezio è
   geometricamente solo un quadrilatero — due lati obliqui paralleli
   (stessa inclinazione, 6rem di scarto fisso) e due lati orizzontali
   (lunghezza = larghezza della linguetta, quindi in percentuale).
   clip-path che mescola rem fissi (i due tagli obliqui) e percentuali (i
   bordi orizzontali, seguono la larghezza sia chiusa che aperta senza
   bisogno di due stati hardcoded):
     (6rem, 0)               -- inizio taglio in alto
     (100%, 0)               -- angolo in alto a destra
     (calc(100% - 6rem),100%)-- fine taglio in basso
     (0, 100%)               -- angolo in basso a sinistra
   margin-left:-6rem su OGNI linguetta: si sovrappone di 6rem alla
   PRECEDENTE (stessa quantità del proprio vuoto diagonale in alto) —
   z-index la fa dipingere sopra, le due diagonali combaciano senza buco.
   padding-left della riga (6rem) compensa il margin-left negativo della
   primissima linguetta, che altrimenti perderebbe 6rem a sinistra come
   tutte le altre. Non si può scendere sotto i 12rem di larghezza (2x6rem)
   per una singola linguetta, altrimenti le due diagonali si incrociano —
   qui si resta larghi (--space-10 di altezza riga più delle linguette),
   quindi non è un problema. -------------------------------------------------------------------------- */
.hub-tabs-section{ padding-block: var(--space-10); border-top: 1px solid var(--border); }
.hub-tabs-row{
  display: flex; align-items: flex-start; height: 16rem;
  padding-left: 6rem;
}
.hub-tabs-radio{ position: absolute; width: 0; height: 0; opacity: 0; pointer-events: none; }
.hub-tabs-tab{
  position: relative; z-index: 1;
  height: 100%; flex: 0 0 12rem;
  margin-left: -6rem;
  cursor: pointer; outline: none;
  background-color: var(--surface-2);
  clip-path: polygon(6rem 0, 100% 0, calc(100% - 6rem) 100%, 0 100%);
  /* sinistra fissa a 6.5rem: minimo per non far tagliare l'icona nel
     vuoto diagonale (arriva a 6rem in alto). destra minima (1rem) a
     riposo, 10.25rem da aperta (vedi :checked sotto) — deve avere una
     transition propria: senza, scatterebbe all'istante mentre flex-basis
     impiega .5s, e per una frazione di secondo la linguetta avrebbe già
     il padding "finale" con la larghezza ancora piccola (zona contenuto
     negativa, contenuto sfarfallante fuori posto). */
  padding: 1rem 1rem 1rem 6.5rem;
  display: flex; flex-direction: column; align-items: flex-start; gap: .6rem;
  overflow: hidden;
  transition: flex-basis .5s var(--ease), padding-right .5s var(--ease), background-color .4s var(--ease);
}
/* Sfumatura "a sheen" solo sulle linguette chiuse (indicatore di stato,
   l'aperta si distingue già col colore chiaro pieno): vive su un layer
   separato (::before), non su background-image diretto della linguetta
   — un background-image non può sfumare fra "presente" e "assente" (è
   un cambio secco anche con transition dichiarata), qui invece cambia
   solo la sua opacity, proprietà animabile per davvero. */
.hub-tabs-tab::before{
  content: "";
  position: absolute; inset: 0;
  background-image: linear-gradient(155deg, rgba(255,255,255,.05) 0%, rgba(255,255,255,0) 100%);
  opacity: 1;
  transition: opacity .4s var(--ease);
  pointer-events: none;
}
.hub-tabs-radio:checked + .hub-tabs-tab::before{ opacity: 0; }

/* Icona + nome sono UN SOLO blocco (.hub-tabs-tab-namecol, grande esatta-
   mente quanto l'icona): il nome è ancorato con position:absolute al
   bordo dell'icona stessa, non a una distanza fissa dalla linguetta —
   si spostano sempre insieme, qualunque sia la posizione del blocco. */
.hub-tabs-tab-namecol{
  position: relative;
  width: 3rem; height: 3rem;
  flex-shrink: 0;
}
.hub-tabs-tab-icon{ width: 3rem; height: 3rem; object-fit: contain; display: block; }
/* Nome (+ versione) in diagonale, stesso angolo del bordo obliquo della
   linguetta (6rem di scarto su 16rem di altezza, atan(16/6) ≈ 69.44°
   dall'orizzontale). Ancorato al bordo inferiore-destro dell'icona
   stessa, ruotato attorno a quel punto: si allunga all'indietro
   scendendo lungo la diagonale — la fine (attaccata all'icona) resta
   sempre nello stesso punto relativo, qualunque sia la lunghezza del
   nome, è l'inizio a scendere di più per i nomi lunghi. */
.hub-tabs-tab-name{
  position: absolute;
  right: 32px; bottom: -20px;
  transform-origin: right bottom;
  transform: rotate(-69.44deg);
  font-size: .9rem; font-weight: 700; white-space: nowrap; color: var(--text);
}
/* Corpo (screenshot): in absolute per partire dalla stessa altezza
   dell'icona (top:1rem) e stare alla sua destra, senza impilarsi sotto
   nel flusso. right lascia lo spazio della chiusura a destra (10.25rem,
   coerente col padding-right da aperta qui sotto). */
.hub-tabs-tab-body{
  position: absolute;
  top: 1rem; left: 10.5rem; right: 10.25rem;
  display: flex; gap: 1rem;
  opacity: 0;
  transition: opacity .25s var(--ease) 0s;
}
.hub-tabs-tab-shot{ width: 9rem; height: auto; border-radius: var(--radius-sm); display: block; flex-shrink: 0; }

/* Stato aperto: un solo elemento da animare invece di tre in sincrono.
   Colore più chiaro invece che un colore identificativo per plugin (era
   così nella versione a skew): qui tutte le linguette chiuse sono
   uguali, solo quella aperta si schiarisce. */
.hub-tabs-radio:checked + .hub-tabs-tab{
  flex-basis: 38rem; /* 9rem taglio + 20rem contenuto + 9rem taglio */
  padding-right: 10.25rem;
  background-color: hsl(222 19% 22% / 1);
}
/* Comparsa del contenuto ritardata a .28s (su una transizione di
   espansione di .5s): aspetta che la linguetta sia già quasi tutta larga
   prima di mostrare le foto, invece di farle comparire a metà espansione
   quando lo spazio disponibile è ancora insufficiente (schiacciate,
   fonte di uno scatto visibile). In chiusura nessun ritardo: sparisce
   subito, prima che la linguetta inizi a restringersi visibilmente. */
.hub-tabs-radio:checked + .hub-tabs-tab .hub-tabs-tab-body{
  opacity: 1;
  transition: opacity .3s var(--ease) .28s;
}

/* --------------------------------------------------------------------------
   26c. HUB TABS — VARIANTE VETRO (sperimentale, NON in produzione): stessa
   struttura dati e stesso meccanismo di apertura di §26b (radio nascosti,
   click), ma niente tagli diagonali — box dritti, contenitore unico stile
   "vetro" del progetto (var(--glass-bg)/--glass-border, var(--radius-lg)),
   nome dritto (non ruotato) visibile solo da aperta, screenshot sotto
   invece che a fianco. Tenuta in test.html come alternativa di riferimento
   nel caso si voglia riconsiderare la direzione diagonale in futuro — non
   linkata da nessuna pagina reale. -------------------------------------------------------------------------- */
.hub-tabs-glass-row{
  display: flex;
  width: fit-content; /* un div flex block-level si allargherebbe altrimenti a riempire il genitore */
  height: 20rem; /* padding-top(1rem) + icona(3rem) + gap(.75rem) + altezza foto a 9rem di larghezza (14rem, rapporto 320x498px) + padding-bottom(1rem) */
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.hub-tabs-glass-radio{ position: absolute; width: 0; height: 0; opacity: 0; pointer-events: none; }
.hub-tabs-glass-tab{
  position: relative;
  height: 100%;
  flex: 0 0 6rem;
  display: flex; flex-direction: column; align-items: flex-start;
  gap: .75rem;
  padding: 1rem;
  cursor: pointer; outline: none;
  background-color: transparent;
  overflow: hidden;
  transition: flex-basis .5s var(--ease), background-color .4s var(--ease);
  --n-shots: 2; /* quante immagini mette questa linguetta -- sovrascritto inline dove servono meno (es. Basic, una sola) */
}
/* Larghezza da aperta calcolata sul contenuto reale (N immagini da 9rem +
   gap da 1rem tra loro, + il padding): Basic (1 sola immagine) si apre più
   stretta di Color Grading (2 immagini), senza JS — --n-shots è una
   custom property, calc() la usa per calcolare un numero concreto (non
   "auto", che non si anima in modo affidabile via transition). */
.hub-tabs-glass-radio:checked + .hub-tabs-glass-tab{
  flex-basis: calc(2rem + (var(--n-shots) * 9rem) + ((var(--n-shots) - 1) * 1rem));
  background-color: var(--glass-bg-strong);
}
.hub-tabs-glass-header{ display: flex; align-items: center; gap: .75rem; flex-shrink: 0; }
.hub-tabs-glass-icon{ width: 3rem; height: 3rem; object-fit: contain; flex-shrink: 0; }
/* Nome (+ versione) visibile SOLO da aperta (opacity, stesso ritardo del
   corpo qui sotto): da chiusa resta nel DOM ma invisibile e tagliato via
   da overflow:hidden sulla linguetta. */
.hub-tabs-glass-name{
  font-size: .85rem; font-weight: 700; white-space: nowrap; color: var(--text);
  opacity: 0;
  transition: opacity .25s var(--ease) 0s;
}
.hub-tabs-glass-radio:checked + .hub-tabs-glass-tab .hub-tabs-glass-name{
  opacity: 1;
  transition: opacity .3s var(--ease) .28s;
}
.hub-tabs-glass-body{
  display: flex; gap: 1rem;
  opacity: 0;
  transition: opacity .25s var(--ease) 0s;
}
.hub-tabs-glass-radio:checked + .hub-tabs-glass-tab .hub-tabs-glass-body{
  opacity: 1;
  transition: opacity .3s var(--ease) .28s;
}
.hub-tabs-glass-shot{ width: 9rem; height: auto; border-radius: var(--radius-sm); display: block; flex-shrink: 0; }

/* --------------------------------------------------------------------------
   27. HUB PLUGIN — stesso pattern a due colonne dell'hub video (§21:
   selettore a sinistra, pannello di dettaglio a destra), ma clonato in
   markup/CSS/JS/dati tutti separati apposta: niente classi o file
   condivisi con l'hub video, così le due funzionalità restano libere di
   evolversi senza rischio di rompersi a vicenda (vedi anche
   js/media/hub-plugin.js e partials/hub-plugin-data.php). A differenza
   dell'hub video, qui non c'è nessuna riga "altri elementi" sotto: il
   pannello destro si ferma a titolo + descrizione + galleria + un bottone.
-------------------------------------------------------------------------- */
/* Niente override di max-width sul .container qui (a differenza
   dell'hub video, che ne ha uno a 1760px per la sua dashboard più larga):
   questa sezione resta alla larghezza standard --container-w (1240px)
   come le altre sezioni della home. */
.hub-plugin-section{ position: relative; overflow: hidden; padding-block: var(--space-10); border-top: 1px solid var(--border); }
.hub-plugin-section > .container{ position: relative; z-index: 1; }
/* Sfondo leggero, edge-to-edge, dietro tutta la sezione (titolo +
   dashboard) — a differenza di .visual-section-bg-img (blur+brightness,
   pensato per stare dietro una card di testo scuro) qui la dashboard ha
   già il proprio sfondo vetro (.hub-plugin-dashboard, --glass-bg), quindi
   basta un'opacità bassa: solo una texture appena percepibile, non un
   vero sfondo scuro da coprire con altro. */
.hub-plugin-section-bg{ position: absolute; inset: 0; z-index: 0; }
.hub-plugin-section-bg-img{
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: top; display: block;
  opacity: .1; filter: blur(4px);
}

/* Riga di card statiche (icona + nome + versione), quello che oggi
   sostituisce l'hub-plugin-dashboard interattivo in home — quello vive
   ora solo in test.html. Card strette (7rem) e gap piccolo
   (var(--space-4)): 8 card a 7rem + gap 1rem = 1008px, entrano tutte su
   una riga nel --container-w standard (1240px) anche tolto il padding
   del container (--container-pad, fino a 2.5rem per lato). Nessun
   link/comportamento al click per ora (<div>, non <a>) — verrà aggiunto
   in un secondo momento.

   Badge "new" DENTRO la card ora, su richiesta esplicita: non più un
   pill a sé, ma una striscia che sembra parte della card stessa, in
   cima (primo figlio nel markup, sopra icona/nome/versione — non più in
   fondo). margin-top negativo la fa debordare fino al bordo superiore
   della card (fuori dal padding), margin-inline negativo idem su
   sinistra/destra, e overflow:hidden + lo stesso border-radius sulla
   card la arrotonda automaticamente SOLO gli angoli in alto (quelli in
   basso, verso icona/nome/versione, restano vivi: il taglio netto) —
   niente border-radius duplicato da tenere sincronizzato a mano con
   quello della card.

   Card tutte della stessa altezza: .hub-plugin-static-grid è di nuovo
   align-items:stretch (il default, non più flex-end) così ogni colonna
   si allunga quanto la più alta della riga; .hub-plugin-static-card ha
   flex:1 per assorbire quello spazio in più (nomi come "Black & White"
   vanno a capo su due righe, altri no — senza flex:1 la card restava
   alta solo quanto il proprio contenuto). Le card senza flag restano
   della stessa altezza di quelle con badge (flex:1 + il resto vuoto in
   fondo, invisibile), niente riga fissa riservata da tenere allineata. */
.hub-plugin-static-grid{ display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-4); }
.hub-plugin-static-col{ display: flex; flex-direction: column; width: 7rem; }
.hub-plugin-static-card{
  /* justify-content:flex-start (non più center): icona allineata in alto,
     su richiesta esplicita -- nome/versione seguono subito sotto invece
     di restare centrati come gruppo sull'intera altezza della card. */
  position: relative; overflow: hidden;
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: flex-start; text-align: center;
  padding: var(--space-4) var(--space-2);
  /* Spazio riservato in cima SEMPRE (flag presente o no), non solo
     quando c'è: altrimenti le card senza "New" avrebbero l'icona più in
     alto di quelle con badge, disallineando la riga. Stessa idea di
     .hub-plugin-static-flag{height:2rem} di prima (riga riservata fuori
     dalla card), semplicemente ora il padding vive dentro la card dato
     che il badge stesso ci vive dentro. */
  padding-top: 2.35rem;
  background: var(--glass-bg); border: 1px solid var(--glass-border); border-radius: var(--radius-lg);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  cursor: pointer;
}
/* position:absolute invece di margini negativi: il badge non fa più
   parte del flusso flex della card (che quindi non deve "sapere" di
   lui), copre la larghezza piena in cima a prescindere dal padding-top
   sopra -- overflow:hidden + border-radius della card lo arrotonda
   comunque solo agli angoli in alto, stesso risultato di prima. */
.hub-plugin-static-flag{
  position: absolute; top: 0; left: 0; right: 0;
  padding: .4rem var(--space-2) .35rem;
  font-size: .8rem; font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
  color: var(--accent-2);
  background: rgba(249,115,22,.16); border-bottom: 2px solid rgba(249,115,22,.35);
}
.hub-plugin-static-icon{ width: 3.5rem; height: 3.5rem; object-fit: contain; margin-top: var(--space-3); margin-bottom: var(--space-3); }
/* Versione con lo stesso stile del nome (stesso peso/dimensione/colore,
   non più piccola/attenuata) — solo il testo cambia. */
.hub-plugin-static-name, .hub-plugin-static-version{ font-weight: 600; font-size: .92rem; color: var(--text); }
/* Hover "toggle": stesso trattamento "glass tinto" di
   .plugin-buy-btn--in-cart (prodotto già nel carrello, shop) e
   .hub-plugin-item.is-active — non pieno, solo un velo di accent blu
   sopra il glass neutro di base, transizione morbida invece di uno
   scatto secco. */
.hub-plugin-static-card{ transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease); }
.hub-plugin-static-card:hover{ background: var(--accent-soft); border-color: var(--accent); }

/* Popup di anteprima hover per le card statiche — ora vive DENTRO
   .section-heading (vedi index-it.html), non più come elemento a sé fuori
   da <main>: su richiesta esplicita non deve più essere un riquadro
   centrato nel viewport con dimensione propria, ma un overlay che copre
   ESATTAMENTE l'area di eyebrow+titolo+sottotitolo, sostituendoli al posto
   di sovrapporglisi. position:relative qui sotto su .section-heading
   (scoped a questa sezione) rende .section-heading il containing block:
   inset:0 sul popup lo fa combaciare perfettamente con quel box, qualunque
   sia la sua altezza reale (2 o 3 righe di sottotitolo), senza valori
   hardcoded. Restando DENTRO la sezione (non più fuori da <main>) non
   serve più lo z-index-9999 "a scavalco" di prima: un normale z-index
   basso basta per dipingere sopra ai fratelli (eyebrow/h2/sottotitolo)
   dentro lo stesso .section-heading.

   Stile vetro del progetto (var(--glass-bg)/--glass-border, stesso
   .hub-plugin-dashboard qui sotto) al posto dello sfondo scuro pieno di
   prima — su richiesta esplicita. Dentro, il contenuto (.hub-plugin-
   gallery, tutte le foto del prodotto) viene rimpicciolito via
   transform:scale() da JS per entrarci sempre senza scroll: prodotti con
   più colonne (Color Grading, 3) o meno (Basic, 1) restano nello stesso
   riquadro, solo più o meno "zoomati". */
/* z-index qui (non solo su .section-heading, position:relative) serve a
   far dipingere l'intero blocco sopra .hub-plugin-static-grid, suo
   fratello successivo nel DOM: senza, quando il popup sotto eccede in
   altezza il box del testo, la griglia (più avanti nel markup, quindi
   sopra di default a parità di stacking) lo coprirebbe invece del
   contrario. */
/* margin-bottom maggiorato solo qui (base .section-heading resta
   var(--space-8) ovunque altro): un po' più di distanza dalla griglia
   di card sotto, su richiesta esplicita. */
.hub-plugin-section .section-heading{ position: relative; z-index: 1; margin-bottom: calc(var(--space-8) + var(--space-8)); }
.hub-plugin-preview-popup{
  /* top/bottom -50% invece di inset:0: il popup deve essere alto il
     doppio del box di testo (stessa larghezza, stessa posizione
     orizzontale/verticale centrale) su richiesta esplicita -- con top e
     bottom entrambi negativi del 50% dell'altezza del contenitore,
     l'altezza risultante è containing-block + 50% + 50% = il doppio,
     centrata sullo stesso punto medio di prima. */
  position: absolute; z-index: 2; top: -50%; bottom: -50%; left: 0; right: 0;
  padding: var(--space-4);
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
  background: var(--glass-bg); border: 1px solid var(--glass-border);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  border-radius: var(--radius-md);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity var(--dur-fast) var(--ease);
}
.hub-plugin-preview-popup.is-visible{ opacity: 1; visibility: visible; pointer-events: auto; }
/* Testo (eyebrow/h2/sottotitolo) e popup non stanno mai visibili insieme:
   is-previewing (aggiunta da js/media/hub-plugin-preview.js sullo stesso
   hover che mostra il popup) porta a opacity:0 tutto tranne il popup
   stesso — :not() invece di elencare ogni figlio, resta valido anche se
   il testo del titolo cambia in futuro. */
.hub-plugin-section .section-heading > :not(.hub-plugin-preview-popup){
  transition: opacity var(--dur-fast) var(--ease);
}
/* is-previewing ora sta su .hub-plugin-section (vedi JS), non più sulla
   sola .section-heading: da mobile deve poter nascondere anche
   .hub-plugin-static-grid (vedi media query più sotto), non solo il
   testo sopra di essa. */
.hub-plugin-section.is-previewing .section-heading > :not(.hub-plugin-preview-popup){
  opacity: 0;
}

/* Bottone di chiusura: esiste sempre nel markup ma serve solo da mobile
   (sotto i 900px, vedi media query in fondo a questa sezione) -- da
   desktop il popup si chiude col mouseleave sulla card, non serve. Stile
   tema (stesso identico pattern di .cart-drawer-close più sopra in questo
   file): bordo sottile, nessuno sfondo pieno, non un cerchio -- non più
   il bottone rotondo con bordo/sfondo marcati di prima. */
.hub-plugin-preview-close{
  display: none;
  position: absolute; z-index: 3; top: var(--space-3); right: var(--space-3);
  width: 2.25rem; height: 2.25rem; border-radius: var(--radius-sm);
  align-items: center; justify-content: center;
  background: none; border: 1px solid var(--glass-border);
  color: var(--text-muted); font-size: 1.3rem; line-height: 1; cursor: pointer;
  pointer-events: auto;
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.hub-plugin-preview-close:hover{ color: var(--text); border-color: var(--glass-border-strong); }

/* Da mobile (touch, niente hover reale) l'apertura passa dal click sulla
   card (js/media/hub-plugin-preview.js) invece che dall'hover. Il popup
   viene spostato via JS (ensureParent, hub-plugin-preview.js) a essere
   figlio diretto di <body> mentre è aperto da mobile, invece di restare
   annidato dentro .section-heading come da desktop -- vive davvero fuori
   dalla sezione, niente antenati con position/transform/z-index di mezzo
   che ne condizionino containing block o stacking context. Copre tutto
   con lo stesso sfondo della sezione delle card plugin (var(--bg) + la
   STESSA foto sfocata a bassissima opacità di .hub-plugin-section-bg-img
   più sopra, riprodotta identica sul supporto::before qui sotto, non
   solo un colore piatto) -- sotto resta tutto esattamente com'era
   (griglia e testo), non serve nasconderlo, il popup ci sta sopra.

   Galleria: TUTTE le immagini in un'unica colonna (non più affiancate),
   scroll SOLO verticale (non più orizzontale) -- flex-direction:column
   sia sul contenitore che sulle singole .hub-plugin-gallery-col (già
   column di suo) fa sì che ogni colonna si impili sotto la precedente
   invece che affiancarsi, risultato: un unico flusso verticale continuo.
   flex:1 + min-height:0 sulla galleria: necessario per il classico
   pattern flexbox "scroll interno" (senza min-height:0 un figlio flex
   non si restringe mai sotto la dimensione del suo contenuto, l'overflow
   non scatterebbe mai). Scrollbar sottile verticale, stesso stile già
   usato per .hub-plugin-sidebar qui sopra (§27). Selettori scoped a
   .hub-plugin-preview-popup (non .hub-plugin-gallery da solo): quella
   classe è condivisa con l'hub interattivo di test.html, che ha già le
   sue regole mobile proprie e non va toccato da queste. */
@media (max-width: 900px){
  .hub-plugin-preview-popup{
    position: fixed; z-index: 999; inset: 0;
    width: auto; height: auto;
    border-radius: 0;
    background: var(--bg);
    flex-direction: column;
    align-items: stretch; justify-content: flex-start;
    padding: calc(var(--space-4) + 2.5rem) var(--space-4) var(--space-4);
  }
  .hub-plugin-preview-popup::before{
    content: "";
    position: absolute; inset: 0; z-index: 0;
    background-image: url('/content/Fotografia_Paesaggio_Gaspare_Silverii_Lofoten_Venenum.jpg');
    background-size: cover; background-position: top; background-repeat: no-repeat;
    opacity: .1; filter: blur(4px);
  }
  /* right: var(--container-pad) invece di var(--space-3) -- stesso
     margine destro del container dell'header (partials/header.php), così
     la X si allinea sull'asse orizzontale a dove sta l'icona hamburger
     nell'header sottostante (entrambe a filo dello stesso padding). */
  .hub-plugin-preview-close{ display: flex; right: var(--container-pad); }
  /* Centratura "safe": se il contenuto (le immagini impilate) è più basso
     dell'altezza disponibile resta centrato, ma se eccede non deve
     sparire metà sopra l'area visibile e irraggiungibile con lo scroll
     (scrollTop non può andare negativo) -- bug classico di
     justify-content:center + overflow-y:auto quando l'overflow è
     piccolo. Niente justify-content:center sul contenitore: uso invece
     due pseudo-elementi (primo/ultimo "figlio" flex) con margin:auto
     sull'asse principale (verticale, colonna) -- assorbono lo spazio
     libero e centrano il contenuto quando ce n'è, ma un margin:auto non
     può mai andare sotto zero, quindi collassano da soli quando il
     contenuto eccede, lasciando lo scroll libero di raggiungere tutto. */
  .hub-plugin-preview-popup .hub-plugin-gallery{
    position: relative; z-index: 1;
    flex: 1; min-height: 0;
    flex-direction: column; flex-wrap: nowrap;
    align-items: center; justify-content: flex-start;
    width: 100%;
    overflow-y: auto; overflow-x: hidden;
    padding-right: var(--space-2);
  }
  .hub-plugin-preview-popup .hub-plugin-gallery::before,
  .hub-plugin-preview-popup .hub-plugin-gallery::after{
    content: ""; margin: auto 0;
  }
  .hub-plugin-preview-popup .hub-plugin-gallery-col{ width: 100%; max-width: 16rem; }
  /* Stessa eccezione del desktop (renderGallery in hub-plugin-preview.js,
     colonna Glow & Orton): qui niente fitGallery() con larghezza in px
     (gira solo sul ramo desktop), ma in mobile basta togliere il tetto
     max-width -- width:100% ereditato dalla regola sopra fa già il
     resto, prendendo tutta la larghezza disponibile della galleria. */
  .hub-plugin-preview-popup .hub-plugin-gallery-col--full{ max-width: none; }
  .hub-plugin-preview-popup .hub-plugin-gallery::-webkit-scrollbar{ width: 6px; }
  .hub-plugin-preview-popup .hub-plugin-gallery::-webkit-scrollbar-thumb{ background: var(--border-strong); border-radius: var(--radius-pill); }
}

.hub-plugin-dashboard{
  display: grid;
  grid-template-columns: 288px minmax(0, 1fr);
  background: var(--glass-bg); border: 1px solid var(--glass-border); border-radius: var(--radius-lg);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-shadow);
  overflow: hidden;
}

/* -- Sinistra: selettore plugin (icona + nome, niente conteggio a
   differenza degli autori: qui non c'è un "quante cose dentro" da dire) -- */
/* Niente align-self:start: senza, la sidebar resta alta solo quanto il
   suo contenuto (8 voci), e il border-right si ferma lì invece di
   arrivare fino in fondo al pannello — ora che .hub-plugin-main ha
   un'altezza fissa (§ sopra), lasciamo la sidebar allungarsi di default
   (stretch) fino alla stessa altezza della riga della grid, così la
   linea verticale li separa per intero. position:sticky sul contenuto
   interno resta comunque valido (si applica al box a prescindere da
   quanto è alto), utile se in futuro il pannello tornasse più alto del
   viewport. */
/* background: --surface-2, stessa superficie scura già usata altrove nel
   tema (es. .hub-video-list-panel) — non un colore nuovo. padding-top:
   prima era 0, con la label "PLUGIN" attaccata al bordo superiore; ora un
   minimo di respiro (--space-3) prima del bordo. */
.hub-plugin-sidebar{
  padding: var(--space-5) var(--space-4) var(--space-5);
  background: var(--surface-2);
  border-right: 1px solid var(--glass-border);
  position: sticky; top: 1.5rem;
  max-height: calc(100vh - 3rem);
  overflow-y: auto;
}
.hub-plugin-sidebar-label{
  font-size: .74rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
  color: var(--text-faint); padding-inline: var(--space-2); margin-bottom: var(--space-3);
}
/* Conteggio dinamico "› Scopri gli N plugin" (N da $tm_hub_plugin_images,
   PHP) — nascosto di default: su desktop la lista è verticale e già
   tutta visibile, il conteggio non serve. Mostrato solo nel breakpoint
   mobile qui sotto, dove la lista diventa una riga orizzontale
   scrollabile e serve un indizio che ce ne sono altri fuori vista.
   text-transform:none/letter-spacing:normal: stessa eccezione di
   .hub-video-list-author, altrimenti erediterebbe le MAIUSCOLE di
   .hub-plugin-sidebar-label. */
.hub-plugin-count{
  display: none; font-weight: 400; text-transform: none; letter-spacing: normal; color: var(--text-muted);
}
.hub-plugin-list{ display: flex; flex-direction: column; gap: var(--space-1); }
/* position:relative: ancora .new-flag in alto a destra della riga (stesso
   badge "New" riusato dal dropdown Plugin dell'header, css riga ~495),
   qui nel suo colore di base arancio invece della variante blu accent che
   .nav-plugin-grid-item .new-flag applica in quel contesto. */
.hub-plugin-item{
  position: relative;
  display: flex; align-items: center; gap: var(--space-3); width: 100%; text-align: left;
  padding: var(--space-2) var(--space-3); border-radius: var(--radius-md);
  border: 1px solid transparent; background: none; color: var(--text-muted); cursor: pointer;
  font-family: inherit; font-size: .92rem;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.hub-plugin-item .new-flag{ position: absolute; top: .4rem; right: .4rem; margin-left: 0; }
.hub-plugin-item:hover{ background: var(--glass-bg-strong); color: var(--text); }
.hub-plugin-item.is-active{ background: var(--accent-soft); border-color: var(--accent); color: var(--text); }
/* Stessa dimensione di .plugin-icon nella griglia card sopra (§20/index):
   icona "grande come nella card", non un'iconcina segnaposto. */
.hub-plugin-icon{ flex-shrink: 0; width: 2.75rem; height: 2.75rem; object-fit: contain; }
.hub-plugin-name{ font-weight: 600; color: var(--text); line-height: 1.3; }

/* -- Destra: titolo + descrizione + galleria + CTA in basso a destra --------
   height fisso, tarato sul caso reale più alto tra gli 8 plugin: Glow &
   Orton, unico con una colonna doppia (32rem, vedi hub-plugin.js) a 2
   immagini impilate, misurato a ~807px di contenuto naturale — gli altri
   7 plugin stanno tutti in ~577px. 52rem (832px) qui sotto lascia un po'
   di margine sopra quel valore. Niente scroll interno né min-height: un
   tetto fisso scelto a mano sul contenuto REALE di oggi, così non c'è mai
   un salto d'altezza tra un plugin e l'altro (la sidebar a sinistra resta
   comunque sempre alla sua altezza naturale, sempre uguale).
   ATTENZIONE: se in futuro un plugin in partials/hub-plugin-data.php
   guadagna una galleria più alta di quella di Glow & Orton oggi, questo
   valore va rialzato a mano — non si aggiorna da solo. */
.hub-plugin-main{ min-width: 0; height: 52rem; padding: var(--space-6); display: flex; flex-direction: column; gap: var(--space-5); }
.hub-plugin-title{ font-family: var(--font-heading); font-size: 1.5rem; font-weight: 700; color: var(--text); margin: 0; flex-shrink: 0; }
.hub-plugin-desc{ color: var(--text-muted); font-size: .98rem; line-height: 1.6; margin: 0; flex-shrink: 0; }

/* Griglia a colonne, non un flex-wrap che si spezza da solo in base alla
   larghezza disponibile: ogni .hub-plugin-gallery-col è una colonna vera,
   con dentro 1+ immagini impilate (vedi $tm_hub_plugin_images in
   partials/hub-plugin-data.php, array di colonne). Larghezza standard
   fissa per colonna (non un aspect-ratio forzato con crop, stessa idea di
   .timeline-gallery/.timeline-image §23) così più colonne restano
   allineate alla stessa dimensione, e height:auto + object-fit:contain
   mostra ogni screenshot per intero invece di ritagliarlo. */
.hub-plugin-gallery{ display: flex; flex-wrap: wrap; gap: var(--space-3); align-items: flex-start; }
.hub-plugin-gallery-col{
  display: flex; flex-direction: column; gap: var(--space-3);
  flex: 0 0 auto; width: 16rem; max-width: 100%;
}
.hub-plugin-gallery-col img{ display: block; width: 100%; height: auto; object-fit: contain; }

.hub-plugin-foot{ display: flex; justify-content: flex-end; margin-top: auto; flex-shrink: 0; }
/* Stesso trattamento "glass tinto" di .plugin-buy-btn--in-cart (prodotto
   già nel carrello, shop): non pieno, solo un velo di accent blu sopra
   il .btn-ghost neutro di base — .btn-ghost fornisce lo spessore/stile
   del bordo, qui si sovrascrive solo il colore (stesso doppio-selettore
   di .plugin-buy-btn.plugin-buy-btn--in-cart per vincere in specificità). */
.btn-ghost.hub-plugin-cta{ background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.btn-ghost.hub-plugin-cta:hover{ background: rgba(0,153,255,.22); }

.hub-plugin-sidebar::-webkit-scrollbar{ width: 6px; }
.hub-plugin-sidebar::-webkit-scrollbar-thumb{ background: var(--border-strong); border-radius: var(--radius-pill); }

@media (max-width: 1080px){
  .hub-plugin-dashboard{ grid-template-columns: 240px minmax(0, 1fr); }
}
@media (max-width: 900px){
  .hub-plugin-dashboard{ grid-template-columns: minmax(0, 1fr); }
  .hub-plugin-sidebar{
    position: static; max-height: none; overflow: visible; padding: var(--space-4);
    border-right: none; border-bottom: 1px solid var(--glass-border);
  }
  .hub-plugin-list{ flex-direction: row; overflow-x: auto; padding-bottom: var(--space-1); }
  .hub-plugin-item{ flex-shrink: 0; min-width: 11rem; }
  .hub-plugin-main{ padding: var(--space-4); }
  /* Stessa scrollbar sottile del "rullino" orizzontale dell'hub video
     (.hub-video-list) invece di quella grezza di default del browser. */
  .hub-plugin-list::-webkit-scrollbar{ height: 6px; }
  .hub-plugin-list::-webkit-scrollbar-thumb{ background: var(--border-strong); border-radius: var(--radius-pill); }
  .hub-plugin-count{ display: inline; }
}
@media (max-width: 520px){
  .hub-plugin-gallery-col{ flex-basis: 100%; }
}
