/* =========================================================================
   Stubborn, nerfed, paranoid — foglio di stile principale
   -------------------------------------------------------------------------
   Come è organizzato questo file (utile se non hai esperienza di CSS):
   1. TOKEN     -> colori, font, misure riutilizzate in tutto il sito
   2. BASE      -> reset minimo + stili generali di testo
   3. LAYOUT    -> navbar, contenitori, griglie generali
   4. HOMEPAGE  -> stili specifici della home
   5. VIZ       -> l'area della visualizzazione a "pallozzi" (per ora un
                   VIEWPORT segnaposto: la visualizzazione interattiva vera
                   e propria arriva in un secondo passaggio, vedi nota nella
                   sezione 5 qui sotto)
   6. CASO      -> il pannello con citazione / user request / model behaviour
                   (pronto per quando la viz sarà collegata)
   7. ABOUT     -> pagina About con indice e sezioni
   8. UTILITY   -> classi di supporto (blob decorativi, responsive, ecc.)

   Ogni regola presa da Figma Dev Mode ha un commento con il node id
   corrispondente (es. /* Figma: 528:840 *​/) così puoi ritrovarla in Figma
   e correggere al volo qualunque valore con il numero esatto che vedi in
   Dev Mode — non serve chiedermelo, basta scrivere qui il valore giusto.
   Per cambiare un colore o un font ovunque nel sito, modifica invece solo
   la sezione 1 (TOKEN).
   ========================================================================= */

/* 1. TOKEN ---------------------------------------------------------------- */
:root {
  --color-bg: #f5f5f3;        /* sfondo pagina — verifica il valore esatto in Dev Mode se serve */
  --color-blue: #002ffa;      /* tema Sense-making / Home / About — Figma: fill di quasi tutti i testi tema blu */
  --color-pink: #ff207d;      /* tema Reacting */
  --color-grey-placeholder: #d9d9d9; /* Figma: 613:372, riquadro immagine/video segnaposto */

  --font-display: "Inter Tight", "Inter", system-ui, sans-serif;
  --font-body: "Inter", system-ui, sans-serif;

  /* Tracking (letter-spacing): in Figma quasi tutti i testi hanno lo 0.03em
     ("3%"), tranne un piccolo gruppo di testi "tagline" che hanno 0.01em
     ("1%") — sono due variabili così puoi correggerle ovunque in un colpo
     solo se noti differenze in Dev Mode. */
  --tracking-normal: 0.03em;
  --tracking-tight: 0.01em;

  --nav-height: 116px;         /* Anna: "torniamo alla versione della navbar precedente a questa" -- si ripristina la ricostruzione su Figma (h:136px, poi ridotta di 20px su richiesta di Anna: "fai la navbar piu' corta di 20px" -> 116px), annullando il precedente ritorno alla versione originale (98.67px). */
  --page-max-width: 1728px;    /* larghezza del frame Figma */
  --page-padding: 120px;       /* Figma: NavBar px-[120px] */

  /* Dimensioni testo navbar, responsive a step (vedi media query sotto).
     Rapporto Figma: titolo 24px / voci menu 16px. Su richiesta di Anna le
     voci sono state ridotte leggermente a 15px mantenendo un rapporto
     simile (24:15 ≈ 24:16). */
  --navbar-brand-size: 20px;
  --navbar-link-size: 15px;
}

/* >900px: dimensioni piene (desktop). 768–900px: un primo passo di
   riduzione. <768px ("più piccolo di un iPad"): le voci del menu si
   raccolgono in un sandwich menu, vedi sezione 3 più sotto. */
@media (max-width: 900px) {
  :root {
    --page-padding: 24px;
    --nav-height: 96px;
    --navbar-brand-size: 21px;
    --navbar-link-size: 14px;
  }
}

@media (max-width: 768px) {
  :root {
    --nav-height: 88px;
    --navbar-brand-size: 19px;
  }
}

/* Il colore "di sezione" cambia in base alla pagina: la classe .theme-blue
   o .theme-pink va messa sul <body> (vedi i template in _includes). */
.theme-blue { --color-accent: var(--color-blue); }
.theme-pink { --color-accent: var(--color-pink); }

/* 2. BASE ------------------------------------------------------------------ */
* { box-sizing: border-box; }

/* Anna: "se scrollo verso il basso il contenuto rimbalza e vedo del
   bianco sotto al footer" -- overscroll bounce nativo del browser/trackpad
   che scrolla oltre la fine reale della pagina, rivelando lo sfondo di
   <html> (bianco di default) sotto il footer. overscroll-behavior blocca
   l'effetto elastico verticale: lo scroll si ferma esattamente al bordo
   del footer. Il background su <html> resta come rete di sicurezza per i
   pochi casi (es. iOS Safari) dove un pelo di rimbalzo residuo può
   comunque intravedersi. */
html {
  scroll-behavior: smooth;
  overscroll-behavior-y: none;
  background: var(--color-bg);
}

body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-accent, var(--color-blue));
  font-family: var(--font-body);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

a { color: inherit; }

img { max-width: 100%; display: block; }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* 3. LAYOUT ----------------------------------------------------------------- */

/* NOTA: qui era stata provata la View Transitions API nativa del browser
   per evitare lo "scatto" della navbar tra una pagina e l'altra (sito
   multipagina "vero", ogni link ricarica una pagina HTML diversa). Tolta
   di nuovo su richiesta di Anna: causava piu' problemi di quanti ne
   risolvesse (il blur di sfondo della navbar si perdeva nello snapshot
   della transizione, le animazioni di comparsa del testo sembravano
   partire gia' a meta', la scrollbar appariva/spariva spostando il
   contenuto). Se in futuro si vuole riprovare, i tre ingredienti erano:
   "@view-transition { navigation: auto; }", un view-transition-name
   stabile sulla navbar, e "scrollbar-gutter: stable" su html. */

/* Velo di caricamento, su TUTTE le pagine del sito (Anna: "questa cosa
   del velo puoi farla per tutte le pagine?", dopo averlo chiesto
   inizialmente solo per la homepage: "il sito si deve prendere tot
   secondi di caricamento con uno strato sopra del colore del
   background, una volta che le immagini sono pronte, lo strato perde
   opacità e compare il titolo"). Il <div class="page-loader"> vive in
   layouts/base.njk (prima cosa nel body, prima della navbar), la logica
   in js/page-loader.js: velo a tinta unita che copre l'intera pagina
   (navbar inclusa: z-index 9999, sopra ai 40 della navbar) finche' la
   pagina non e' DAVVERO pronta E non e' passato un tempo minimo
   garantito (altrimenti su una pagina gia' in cache il velo sparirebbe
   troppo in fretta per essere percepito -- bug riscontrato da Anna:
   "non c'è nessun velo di caricamento"). Risolve anche, sulla homepage,
   il problema dei tempi di caricamento diversi delle immagini di
   sfondo: restano nascoste tutte insieme finche' non sono TUTTE pronte,
   invece di comparire una alla volta in ordine imprevedibile. */
.page-loader {
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: var(--color-bg);
  pointer-events: none;
  opacity: 1;
  transition: opacity 0.6s ease;
}

.page-loader--hidden {
  opacity: 0;
}

@media (prefers-reduced-motion: reduce) {
  .page-loader {
    transition: none;
  }
}

.page {
  position: relative;
  min-height: 100vh;
  /* La navbar e' un overlay (position:fixed, sezione qui sotto), quindi
     non occupa piu' spazio "reale" nel flusso: questo padding-top
     ricrea lo spazio per lei in cima a ogni pagina, cosi' i contenuti
     non ci finiscono sotto. L'unica eccezione e' .hero della home, che
     lo annulla apposta per estendere il suo sfondo fin sotto la navbar
     (vedi sezione 4, HOMEPAGE). */
  padding-top: var(--nav-height);
}

.container {
  max-width: var(--page-max-width);
  margin: 0 auto;
  padding-left: var(--page-padding);
  padding-right: var(--page-padding);
}

/* Figma: 528:840 "NavBar" — px-[120px] py-[48px]. Il valore di Figma per il
   blur di sfondo era 0.5px (quasi nulla); su richiesta di Anna è stato
   sostituito con un blur "progressivo": più intenso vicino al bordo
   superiore della navbar, che sfuma fino a zero verso il bordo inferiore.
   Così, quando un testo di pagina scrolla e comincia a "spuntare" da sotto
   la navbar, non si trova subito completamente sfocato (effetto brusco) —
   la sfocatura aumenta gradualmente solo mentre il testo scorre più in
   profondità sotto la navbar. Il colore/sfondo vive nello pseudo-elemento
   ::before qui sotto, separato dal contenuto (testo/link), che altrimenti
   verrebbe sfumato via insieme allo sfondo dalla maschera. */
/* Anna ("torniamo alla versione della navbar precedente a questa"):
   ripristinata la ricostruzione a 5 layer + fill sul modello Figma di
   Anna (h:136px poi ridotta a 116px, fill+blur progressivo separati in
   .navbar__blur-layer/.navbar__fill, vedi partials/navbar.njk),
   annullando il precedente ritorno alla versione con un solo pseudo-
   elemento (.navbar::before, blur fisso 14px). */
.navbar {
  /* position:fixed invece di sticky: la navbar diventa un vero overlay,
     sempre sopra al contenuto anche in cima alla pagina (prima, con
     sticky, "si attaccava" solo una volta superata la sua posizione
     naturale in-flow — che coincideva comunque con l'inizio pagina, quindi
     visivamente identico finora). Serve ora perche' lo sfondo della
     homepage deve estendersi ed essere visibile ANCHE sotto la navbar fin
     dal primo caricamento, non solo scrollando. Resta full-bleed (niente
     max-width qui) SOLO per questo: lo sfondo/blur deve coprire tutta la
     larghezza schermo, non solo la colonna centrale -- il contenuto vero
     e proprio (testi/link) e' invece dentro .navbar__inner qui sotto,
     che e' lui a allinearsi a .container. */
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: var(--nav-height); /* Anna: h:136px esatta dal suo Figma. */
  z-index: 40;
}

/* Anna: "i testi della navbar sono allineati a sx/dx in modo diverso
   rispetto al testo delle pagine [su schermi più grandi del mio Mac]...
   il margine sx/dx della navbar deve aumentare all'aumentare dello
   schermo" -- prima .navbar applicava var(--page-padding) direttamente
   al bordo VERO dello schermo (essendo lei stessa position:fixed,
   full-bleed): su schermi più larghi del frame Figma (--page-max-width,
   1728px) questo NON coincide più col bordo di .container, che invece
   smette di crescere oltre quella soglia e si centra (margin:0 auto) --
   i due margini divergevano proprio li'. Fix: .navbar__inner replica
   ESATTAMENTE la stessa ricetta di .container (max-width + margin:0
   auto + stesso padding-left/right), quindi i suoi bordi restano sempre
   identici a quelli del testo di pagina, qualunque sia la larghezza
   dello schermo -- non solo entro i 1728px del frame originale.
   display:flex/align-items/justify-content/padding verticale erano
   prima su .navbar stessa, spostati qui pari pari. */
.navbar__inner {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  max-width: var(--page-max-width);
  margin: 0 auto;
  height: 100%;
  /* Anna: "ne ho fatto uno su Figma che mi piace di piu'" -- la storia di
     aggiustamenti su padding-top/bottom (40/48 -> 26.67/32 -> 20/62,
     vari giri di "riduci", "rendi piu' spesso", "aggiungi 30px sotto")
     e' superata: ora .navbar ha un'altezza fissa (h:136px, --nav-height).
     Poi: "il padding superiore deve essere di 24px" -- allineamento
     tornato ad flex-start + padding-top esplicito (non piu'
     align-items:center) cosi' il valore e' quello dettato da Anna,
     indipendente da quanto spazio resta sotto. */
  padding: 30px var(--page-padding) 0;
}

/* Anna: "eliminiamo anche il backgroundblur della navbar e usiamo la
   stessa strategia del colore da 100 a 0 anche per quella" -- via tutto
   l'approccio a blur progressivo (5 layer con backdrop-filter impilati +
   velo piatto sopra, con l'estensione oltre il bordo per evitare la riga
   netta -- una tecnica necessaria SOLO per i backdrop-filter, che hanno
   un artefatto di rendering quando la maschera arriva a zero esattamente
   sul bordo del box). Sostituito con un unico rettangolo piatto (stessa
   ricetta di .hero__fade-overlay in sezione 4 HOMEPAGE): nessun blur,
   solo un gradiente lineare dell'ALPHA dello stesso colore #f7f7f7, dal
   100% (opaco, vicino al testo della navbar) allo 0% (trasparente, in
   fondo alla navbar) -- direzione opposta a quella della homepage (li'
   si andava da 0 a 100 scendendo, qui da 100 a 0), stessa tecnica.
   Un semplice background (nessun backdrop-filter) non ha l'artefatto di
   bordo netto: puo' quindi restare esattamente dentro i confini della
   navbar (inset:0, nessuna estensione necessaria) senza alcun rischio di
   riga visibile, ed essendo un velo di colore (non un blur) non "sfoca"
   piu' nulla sotto di se' -- il problema del testo introduttivo di
   Sense-making/Reacting blurrato dalla navbar (che aveva richiesto di
   accorciare la zona di sfumatura fino a 100px) non si pone piu': qui
   sotto la navbar non c'e' piu' nessun blur da propagare. Markup: vedi
   partials/navbar.njk (un solo div .navbar__fill, non piu' 5 layer). */
.navbar__fill {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  /* Anna: "vorrei che la sfumatura cominciasse più in basso e ci fosse
     più opacità sotto i testi della navbar, occupando però lo stesso
     spazio di adesso" -- il rettangolo resta grande esattamente quanto
     prima (inset:0, stessa altezza navbar). Due tentativi con una zona
     "piena" netta seguita da una rampa lineare (prima 65%, poi 35%)
     creavano pero' un gomito visibile nel punto esatto in cui la zona
     piatta finisce e la rampa comincia (derivata che cambia di colpo da
     zero a costante) -- Anna: "sembra solo che tu abbia spostato il
     gradino più in alto, voglio un gradino più diffuso". Fix: niente
     piu' un tratto piatto seguito da una rampa dritta, ma una curva a
     piu' tappe (stessa tecnica gia' usata altrove nel sito per le
     sfumature, es. .viz-page__intro-blur::before) che resta alta ma non
     perfettamente ferma nella prima meta', poi scende via via piu'
     ripida verso il centro e si distende di nuovo avvicinandosi allo
     zero: nessun punto in cui la pendenza cambia bruscamente, quindi
     nessun gomito percepibile, pur restando piu' opaca piu' a lungo
     (sotto il testo) di quanto non fosse la rampa dritta originale. */
  background: linear-gradient(
    to bottom,
    rgba(245, 245, 243, 1) 0%,
    rgba(245, 245, 243, 0.97) 20%,
    rgba(245, 245, 243, 0.88) 35%,
    rgba(245, 245, 243, 0.7) 50%,
    rgba(245, 245, 243, 0.46) 65%,
    rgba(245, 245, 243, 0.24) 80%,
    rgba(245, 245, 243, 0.08) 92%,
    rgba(245, 245, 243, 0) 100%
  );
}

/* Figma: 528:842 (istanza dentro NavBar) — Inter Tight Regular 24px,
   tracking 3%, line-height 24px (compatto). NB: nella navbar il wordmark è
   minuscolo ("Stubborn, nerfed, paranoid"); nell'hero della home è invece
   Title Case e molto più grande (vedi .hero__title). */
.navbar__brand {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--navbar-brand-size);
  line-height: 1;
  letter-spacing: var(--tracking-normal);
  text-decoration: none;
  /* Outline (.text-outline, sezione 5): 1px a riposo, ESPRESSO IN EM
     rispetto alla dimensione ATTUALE del font (var(--navbar-brand-size),
     24px su desktop) così scala in proporzione quando il testo si
     rimpicciolisce in responsive: 1/24 = 0.0417em. In hover sale a 2px
     (2/24 = 0.0833em), sempre con lo stesso criterio di proporzionalità —
     vedi regola :hover qui sotto. */
  --outline-width: 0.0417em;
  transition: text-shadow 0.2s ease;
}

.navbar__brand:hover {
  --outline-width: 0.0833em;
}

.navbar__links {
  display: flex;
  align-items: center;
  gap: 20px;
  list-style: none;
  margin: 0;
  padding: 0;
  /* Anna: "il titolo sembra più in alto" -- .navbar__inner usa
     align-items:flex-start, quindi titolo e link partono dallo stesso
     bordo superiore come CASELLE, ma ogni link (.navbar__links a) ha un
     padding-top di 8px (--nav-link-pad-y qui sotto, serve al calcolo
     della sottolineatura, non va toccato) che il titolo non ha: il
     testo dei link finiva quindi visibilmente più in basso. Margine
     negativo qui (sull'intera riga, non sui singoli link) compensa
     esattamente quegli 8px così i due testi tornano alla stessa
     altezza. NB: -8px è un valore fisso (non un var()) perché
     --nav-link-pad-y è dichiarata su .navbar__links a, un discendente —
     le custom property in CSS non risalgono dai figli al genitore. Se
     cambi 8px lì, aggiorna anche questo -8px. */
  margin-top: -8px;
}

/* Figma: 528:845 ecc. — Inter Medium 16px (qui leggermente ridotto a
   var(--navbar-link-size), vedi TOKEN), line-height 24px.
   --nav-link-pad-y è lo stesso valore della padding verticale: serve alla
   sottolineatura qui sotto per calcolare la distanza reale dal testo
   (senza, "bottom" si misura dal bordo del padding, non dal testo, ed è
   per questo che sembrava "lontana" nonostante il valore fosse 1px). */
.navbar__links a {
  --nav-link-pad-y: 8px;
  display: inline-block;
  padding: var(--nav-link-pad-y) 0;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--navbar-link-size);
  /* Prima era 24px fisso: con il font ridotto a 15px lasciava ~4-5px di
     interlinea "invisibile" sopra e sotto il testo (24-15=9px, metà per
     lato), che si sommava al calcolo del padding qui sotto e teneva la
     sottolineatura lontana nonostante il valore fosse "1px". Con
     line-height proporzionale (1.2) l'interlinea in eccesso è minima e il
     calcolo torna preciso. */
  line-height: 1.2;
  text-decoration: none;
}

.navbar__divider {
  display: inline-block;
  padding: 8px 0;
  opacity: 0.6;
}

/* Sottolineatura animata: a riposo niente linea; in hover CRESCE da
   sinistra verso destra; quando il mouse esce SPARISCE anch'essa da
   sinistra verso destra (non semplicemente al contrario). Il trucco: due
   transform-origin diversi a seconda dello stato — "left" mentre si
   cresce (in hover), "right" quando si torna a riposo. Dato che
   transform-origin non viene animato (solo "transform" ha una
   transition), il browser usa il nuovo origin per l'intera animazione di
   rimpicciolimento, che quindi si "richiude" verso destra: il lato
   sinistro sparisce per primo, il destro per ultimo, cioè sempre da sx a
   dx. Sulla pagina attiva resta fissa, niente animazione.
   NB: il bottone "Legend" NON usa più questa regola — ha la sua versione
   dedicata (.legend-toggle__label, qualche riga più sotto) che copre solo
   la parola "Legend" e non la freccia. Tenerlo qui duplicato causava
   proprio il bug della freccia sottolineata. */
.navbar__links a {
  position: relative;
  text-decoration: none;
}

.navbar__links a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  /* Distanza dal testo: un'unica variabile, così se vuoi ancora più vicina
     (o più staccata) basta cambiare questo numero. */
  --underline-gap: 1px;
  bottom: calc(var(--nav-link-pad-y, 0px) - var(--underline-gap));
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right; /* stato di riposo/uscita: si richiude verso dx */
  transition: transform 0.25s ease;
}

.navbar__links a:hover::after,
.navbar__links a.is-clicking::after {
  transform: scaleX(1);
  transform-origin: left; /* comparsa: cresce verso dx */
}

.navbar__links a[aria-current="page"]::after {
  transform: scaleX(1);
  transform-origin: left;
  transition: none;
}

/* Bottone "sandwich" — nascosto di default, compare solo sotto i 768px
   (vedi media query più sotto). 3 barre che diventano una X quando aperto.
   Rimpicciolito su richiesta di Anna (prima 32px). */
.navbar__toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 4px;
  width: 20px;
  height: 20px;
  padding: 0;
  border: none;
  background: none;
  color: inherit;
  cursor: pointer;
}

.navbar__toggle span {
  display: block;
  width: 100%;
  height: 1.2px;
  background: currentColor;
  transition: transform 0.25s ease, opacity 0.2s ease;
}

.navbar__toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(5.2px) rotate(45deg); }
.navbar__toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.navbar__toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-5.2px) rotate(-45deg); }

/* Sotto i 768px ("più piccolo di un iPad", che in portrait è tipicamente
   768–834px) le voci del menu non hanno più spazio per stare in riga a
   una dimensione leggibile: si raccolgono in un pannello a tendina sotto
   la navbar, aperto/chiuso dal bottone sandwich qui sopra. */
@media (max-width: 768px) {
  .navbar__toggle { display: flex; }

  .navbar__links {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: flex-start;
    gap: 0; /* l'interlinea qui è tutta nel padding di ogni voce, vedi sotto */
    background: var(--color-bg);
    padding: 0 var(--page-padding);
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.3s ease, padding 0.3s ease;
  }

  .navbar__links.is-open {
    max-height: 320px;
    padding: 4px var(--page-padding) 16px;
    box-shadow: 0 12px 24px -12px rgba(0, 0, 0, 0.15);
  }

  .navbar__links a {
    width: 100%;
    font-size: 16px; /* in colonna c'è spazio: torna a una dimensione comoda da toccare */
    /* Meno interlinea tra le voci impilate (prima 8px sopra/sotto = 16px
       tra due voci consecutive, ora 5px = 10px tra due voci). */
    padding-top: 5px;
    padding-bottom: 5px;
  }

  /* Il separatore decorativo "––––" tra Reacting e About non ha senso
     impilato in verticale. */
  .navbar__divider { display: none; }
}

/* 4. HOMEPAGE ---------------------------------------------------------------- */
/* margin-top negativo = annulla il padding-top che .page (sezione 3) mette
   in cima a ogni pagina per la navbar-overlay: cosi' .hero (e il suo
   sfondo) parte da y:0, estendendosi fin sotto alla navbar trasparente.
   padding-top qui dentro ripristina poi lo spazio per il TITOLO, che deve
   comunque restare sotto alla navbar e non nasconderlesi dietro. */
.hero {
  position: relative;
  margin-top: calc(-1 * var(--nav-height));
  /* Anna: "distanza contenuto pagina-footer di 60px" -- il padding-bottom
     (18vh) si sommava al margin-top:60px del footer, producendo uno
     spazio ben piu' grande di 60px sulla homepage. Azzerato: la distanza
     dai tasti preview al footer e' ora data SOLO dal margin-top del
     footer stesso. */
  /* Anna: le didascalie non devono essere tagliate -- con il parallasse i
     pallozzi scendono sotto il fondo di .hero (overflow:hidden): 160px di
     padding-bottom compensati dal margin-bottom negativo, cosi' la distanza
     dal footer non cambia. */
  padding: calc(var(--nav-height) + 8vh) var(--page-padding) 160px;
  margin-bottom: -160px;
  text-align: center;
  overflow: hidden;
}

/* Composizione di immagini di sfondo, dati in src/_data/homepageBg.json
   (sezione 4 del README ha i dettagli). Posizionate in percentuale
   rispetto a .hero: restano proporzionate a qualunque larghezza, non c'e'
   bisogno di ricalcolare nulla nei breakpoint responsive. */
/* Anna, sequenza di apertura homepage (dopo vari tentativi con le
   immagini di sfondo animate -- dissolvenza, deblur -- che risultavano
   "a scatti" per via del peso delle immagini enormi, e poi "sgranate"
   una volta ridimensionate per alleggerire l'animazione: Anna ha deciso
   di eliminare l'animazione delle immagini e tenerle a piena
   risoluzione):
     1. navbar e 4 immagini di sfondo sono gia' presenti da subito,
        nessuna animazione (Anna: "le immagini non compaiono, sono gia'
        li' insieme alla navbar");
     2. poi titolo+sottotitolo, alla velocita' che avevano gia' (800ms
        di distanza tra i due, stessa cadenza parola-per-parola);
     3. poi, INSIEME, il testo introduttivo e i 2 tasti-preview.
   Titolo/sottotitolo e testo introduttivo/tasti-preview restano
   agganciati alla classe .hero--loaded (aggiunta via JS sull'evento
   "load" della finestra, vedi index.njk), cosi' il loro t=0 e' sempre
   "pagina davvero pronta", non il momento in cui il CSS viene
   applicato. */
.hero__bg-item {
  position: absolute;
  z-index: 0;
  pointer-events: none;
  will-change: transform;
  /* Anna: "opacità delle immagini di sfondo all'85%". */
  opacity: 0.85;
  /* Anna: "fai la navbar piu' corta di 20px, ma fai come il background
     scorresse piu' in alto di 20px senza modificare il suo aspetto" --
     .hero si sposta in giu' di 20px per via del margin-top legato a
     --nav-height (vedi .hero qui sopra), e queste immagini (posizionate
     in assoluto rispetto a .hero) si sposterebbero in giu' insieme a
     lui. Il compenso di -20px NON puo' stare qui in CSS: lo script di
     parallasse in fondo a index.njk sovrascrive style.transform su
     questi elementi ad ogni scroll (anche a scrollY=0), cancellerebbe
     subito questo valore -- il -20px e' quindi dentro la formula di
     quello script, non qui. */
}

/* Titolo e sottotitolo (.reveal-word, sezione 9): l'animazione parola-
   per-parola parte "da sola" appena reveal-text.js aggiunge gli span
   (script condiviso da altre pagine, non tocchiamo la sua logica). La
   teniamo in pausa (animation-play-state:paused blocca anche il
   conteggio del proprio animation-delay, non solo il fotogramma) finche'
   non arriva .hero--loaded -- cosi' data-reveal-delay="0"/"800" su h1/p
   (index.njk) e' contato da quel momento (pagina caricata), non da
   quando la pagina viene disegnata. */
.hero__title .reveal-word,
.hero__tagline .reveal-word {
  animation-play-state: paused;
}

.hero--loaded .hero__title .reveal-word,
.hero--loaded .hero__tagline .reveal-word {
  animation-play-state: running;
}

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

/* Segnaposto (finche' non c'e' il PNG reale in "src"): stesso ingombro e
   posizione, pattern a puntini invece dell'immagine. */
.hero__bg-item--placeholder {
  border-radius: 50%;
  background-image: radial-gradient(currentColor 2px, transparent 2.4px);
  background-size: 14px 14px;
  color: var(--color-accent);
  opacity: 0.55;
}

/* Anna: "centra il titolo nel viewport verticalmente" + "il testo
   introduttivo deve essere fuori dal viewport quando apro la homepage" --
   questo blocco centra .hero__heading al suo interno. min-height sottrae
   il padding-top che .hero (sezione qui sopra) mette GIA' sopra questo
   blocco (calc(nav-height + 8vh)): senza sottrarlo il blocco risultava
   piu' alto di un intero schermo, e centrare un elemento dentro un box
   piu' alto del previsto lo spingeva visibilmente verso il basso (bug
   segnalato da Anna: "il titolo non e' centrato, e' allineato in basso") --
   ora il box corrisponde esattamente allo spazio rimasto nel primo
   schermo, quindi il centraggio e' quello vero. */
.hero__above-fold {
  position: relative;
  z-index: 2;
  min-height: calc(100dvh - (var(--nav-height) + 8vh));
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Avvolge titolo+tagline in un blocco largo quanto la riga piu' larga del
   titolo (width: fit-content): la tagline, con max-width:100% qui sotto,
   non puo' mai superare quella larghezza e va a capo prima invece di
   allungarsi per conto suo. */
.hero__heading {
  width: fit-content;
  max-width: 100%;
  margin: 0 auto;
  /* Anna: dimensione del blocco titolo+sottotitolo al 90% -- transform
     (non un semplice cambio di font-size) cosi' tutto il blocco -- testo,
     spaziatura tra titolo e tagline, spessore dell'outline (in em, quindi
     legato al font-size) -- si riduce insieme, mantenendo le stesse
     proporzioni. "tira su il titolo di 30px" poi "spostalo un pochino piu'
     in alto" (-30px -> -50px): translateY PRIMA di scale nell'elenco,
     cosi' lo spostamento resta in px reali sullo schermo, non scalato
     insieme al resto (l'ordine conta: le funzioni si applicano da destra
     a sinistra). */
  transform: translateY(-60px) scale(0.9);
  /* Anna: "in hover su titolo e sottotitolo il cursore deve restare
     freccia" -- niente I-beam del testo selezionabile. "Sposta poco piu'
     in alto il blocco": -50px -> -70px. */
  cursor: default;
}

.hero__heading * {
  cursor: default;
}

/* Figma: 528:818 — Inter Tight Regular 140px, line-height 140px (nessuno
   spazio extra tra le righe), tracking 3%. Il peso è Regular, non Bold:
   l'effetto "pieno" viene dal doppio layer con contorno (.text-outline,
   sezione 5) che Anna ha fatto in Figma. Nota per Anna: il tool che leggo
   da Figma (conversione in Tailwind) non riesce a portare via il valore
   ESATTO dello spessore di quel contorno — Tailwind non ha una classe
   nativa per lo stroke del testo, quindi Figma Dev Mode lo scarta in fase
   di conversione. È esattamente il tipo di valore che conviene prendere tu
   da Dev Mode (proprietà "stroke" del testo, sezione destra) e incollare
   qui sotto in --outline-width. */
.hero__title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(50px, 8.5vw, 126px);
  /* Anna: "riduci leggermente l'interlinea del titolo" -- era 1 (Figma:
     140px/140px, nessuno spazio extra tra le righe), ora leggermente
     sotto per avvicinare le 3 righe. */
  line-height: 0.9;
  letter-spacing: var(--tracking-normal);
  margin: 0 0 32px;
  /* --outline-width: ...px;  <- valore esatto da Figma Dev Mode, se diverso dal default */
  /* In hover lo stroke sale a 8px (0.0571em = 8/140, riferito alla
     dimensione piena desktop): scala in proporzione col resto del testo
     a ogni breakpoint, stessa logica di .navbar__brand. */
  transition: text-shadow 0.2s ease;
  /* Comparsa parola per parola come tutti gli altri testi principali del
     body (vedi .reveal-word, sezione 9): data-reveal-text in index.njk.
     (In precedenza c'era un'animazione di blur-in a blocco unico, tolta
     su richiesta di Anna.) */
}

.hero__title:hover {
  --outline-width: 0.0571em;
}

/* Figma: 528:882 "Mapping human-AI relations..." — Inter Tight Regular
   30px, tracking 1% (nota: qui il tracking è quello "tight", diverso dal
   resto dei testi). */
.hero__tagline {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(16px, 2.07vw, 27px);
  line-height: 1.4;
  letter-spacing: var(--tracking-tight);
  margin: 0;
  /* Non puo' mai superare la larghezza del titolo: vedi .hero__heading
     (width: fit-content) qui sopra. */
  max-width: 100%;
  /* Outline 0.5px (0.0167em = 0.5/30, riferito alla dimensione piena
     desktop), proporzionale come il resto. Va aggiunta anche la classe
     .text-outline in index.njk perche' questa regola scatti. */
  --outline-width: 0.0167em;
}

/* Anna: "elimina il backgroundblur della homepage, crea un rettangolo
   largo quanto lo schermo di colore #f7f7f7 che inizia ad opacita' 0%
   appena sotto il sottotitolo e progressivamente arriva al 100%,
   raggiungendo il 100% sopra l'inizio del testo introduttivo, restando
   al 100% per il resto della homepage verso il basso" -- sostituisce
   l'intero effetto precedente (velo bianco + backdrop-filter blur,
   sfumato con una maschera) con un rettangolo PIATTO (nessun blur): un
   solo gradiente lineare dell'ALPHA del colore stesso, da trasparente a
   pieno #f7f7f7, niente piu' backdrop-filter/mask-image. Copre l'intero
   .hero (inset:0, che essendo .hero position:relative/overflow:hidden
   coincide sempre con la sua altezza reale, quindi il tratto "100% per
   il resto della homepage" e' automatico: il rettangolo si estende fino
   in fondo a .hero, che contiene tutto il contenuto della home). Sta
   SOPRA le immagini di sfondo (.hero__bg-item, z-index:0) ma SOTTO al
   testo/bottoni (.hero__above-fold/.hero__intro/.hero__previews, tutti
   z-index:2) grazie al suo z-index:1, invariato dalla versione
   precedente.
   --fade-start (bordo inferiore del sottotitolo) e --fade-end (bordo
   superiore del testo introduttivo) sono impostati via JS (vedi lo
   script in fondo a index.njk) come distanza reale in px dal bordo
   superiore di .hero, misurati con getBoundingClientRect cosi' restano
   corretti a qualunque dimensione di schermo/testo -- niente piu' stime
   a percentuale. I fallback (400px/700px) valgono solo per il
   brevissimo istante prima che lo script giri. */
.hero__fade-overlay {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    rgba(245, 245, 243, 0) 0px,
    rgba(245, 245, 243, 0) var(--fade-start, 400px),
    rgba(245, 245, 243, 1) var(--fade-end, 700px),
    rgba(245, 245, 243, 1) 100%
  );
}

/* Anna: "aggiorna il contenuto sotto il titolo con quanto e' presente nel
   Figma" (nodo 885:2). .hero__intro raggruppa ora TUTTO il contenuto
   sotto il titolo: colonna di testo larga 732px centrata (Inter Tight
   32px/40px, tracking 1%, blu), placeholder video, collage di 5 post
   Reddit, due pallozzi-bottone con didascalia. Spaziature dal Figma:
   80px tra testo e immagine/collage che lo seguono, 160px dopo. */
.hero__intro {
  position: relative;
  z-index: 2;
  /* Anna: "il rettangolo segnaposto deve essere largo quanto la colonna
     di testo" -- l'intero blocco e' largo 6 colonne reali della griglia
     (732px a 1728px), quindi testo, placeholder, collage e pallozzi
     condividono la stessa larghezza a qualunque schermo. */
  width: calc((min(100vw, var(--page-max-width)) - 2 * var(--page-padding) - 264px) / 2 + 120px);
  --col8: calc((min(100vw, var(--page-max-width)) - 2 * var(--page-padding) - 264px) * 2 / 3 + 168px);
  max-width: 100%;
  margin: calc(12vh + 140px) auto 0;
  padding: 0;
  text-align: left;
  font-family: var(--font-display);
  font-weight: 400;
  /* Anna: "il corpo del testo e' troppo grande, vai a 24px con
     interlinea 28" (nel Figma era 32/40). */
  /* Anna: "allora fai testo 18 interlinea 20" (prima 24/28, nel Figma
     32/40). */
  /* Anna: "fai corpo del testo 20px con interlinea 24" (prima 18/20). */
  font-size: 20px;
  line-height: 24px;
  letter-spacing: 0.01em;
  color: var(--color-blue);
  /* Compare in dissolvenza dopo titolo+sottotitolo (vedi il blocco
     .reveal-word, sezione 9): opacity:0 di riposo, animazione vera solo
     sotto .hero--loaded, cosi' il delay e' contato da t=0 (pagina
     caricata). */
  opacity: 0;
}

.hero--loaded .hero__intro {
  animation: hero-fade-in 0.5s ease forwards;
  animation-delay: 1600ms;
}

@keyframes hero-fade-in {
  to {
    opacity: 1;
  }
}

/* Anna: "riduci la larghezza della colonna di testo a 2 colonne di layout
   in meno, una a dx e una a sx" -- 6 colonne della griglia a 12 (Anna: "vai a 6"; 4 era troppo stretto)
   (colonna = (contenuto - 11*24px)/12): 4 colonne + 3 gap. Il testo resta
   centrato nella colonna da 732px che contiene anche video/collage/
   pallozzi, che non cambiano. */
.hero__text {
  /* NB: il 100% di una percentuale qui sarebbe la larghezza di
     .hero__intro (732px), non del contenuto di pagina -- per questo si
     parte dalla larghezza reale del contenuto (viewport - padding). */
  width: calc((min(100vw, var(--page-max-width)) - 2 * var(--page-padding) - 264px) / 2 + 120px);
  margin: 0 auto;
  cursor: default;
}

.hero__text b {
  font-weight: 700;
}


/* Spaziature verticali tra i blocchi (Figma, frame 1728px): 80px da un
   testo all'elemento visivo che lo segue, 160px dall'elemento visivo al
   testo successivo. */
/* Anna: "riduci il padding sopra e sotto un pochino" -- 80px -> 64px e
   160px -> 120px. */
/* Anna: 3 blocchi testo+immagine; distanza x tra testo e la sua immagine,
   2x tra l'immagine e il testo del blocco successivo (x = 80px). */
.hero__intro { --gap-x: 80px; }

.hero__text + .hero__video-placeholder,
.hero__text + .hero__posts,
.hero__text + .hero__previews {
  margin-top: var(--gap-x);
}

.hero__video-placeholder + .hero__text,
.hero__posts + .hero__text {
  margin-top: calc(2 * var(--gap-x));
}

/* Placeholder del video (Figma: 731x451, #d9d9d9) -- da sostituire. */
.hero__video-placeholder {
  /* 8 colonne, centrato rispetto alla colonna di testo (6) */
  width: var(--col8);
  max-width: 100vw;
  margin-left: calc((100% - var(--col8)) / 2);
  aspect-ratio: 731 / 451;
  background: var(--color-grey-placeholder);
}

/* Collage dei 5 post Reddit (Figma 894:97): box di 1093x435 centrato (piu'
   largo della colonna di testo), ogni post e' posizionato in % rispetto
   al box cosi' scala in proporzione. Contorno nero 1px come nel Figma. */
.hero__posts {
  position: relative;
  /* Anna: "le immagini devono occupare solo 6 colonne, non essere piu'
     larghe del testo" -- era 1093px (piu' larga della colonna): ora
     occupa la larghezza della colonna (6 colonne), stesse proporzioni. */
  width: var(--col8);
  margin-left: calc((100% - var(--col8)) / 2);
  aspect-ratio: 1093 / 435;
}

.hero__post {
  position: absolute;
  display: block;
  border: 1px solid #000;
  box-sizing: border-box;
  max-width: none;
}

.hero__post--1 { left: 13.17%; top: 40.92%; width: 48.03%; height: 59.08%; }
.hero__post--2 { left: 34.58%; top: 0;      width: 51.97%; height: 22.07%; }
.hero__post--3 { left: 0;      top: 16.55%; width: 51.97%; height: 21.15%; }
.hero__post--4 { left: 48.03%; top: 36.55%; width: 51.97%; height: 16.78%; }
.hero__post--5 { left: 60.38%; top: 58.39%; width: 32.48%; height: 23.68%; }

/* Pallozzi-bottone alle due visualizzazioni (Figma: cerchi di 250px con
   didascalia 18px/24px sotto, due colonne di 354px a 24px di distanza). */
.hero__previews {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 24px;
}

.hero__preview-col {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.hero__preview-col--blue { color: var(--color-blue); }
.hero__preview-col--pink { color: var(--color-pink); }

/* Anna: "applica ai pallozzi la stessa interazione delle viz" poi
   "voglio che solo il testo sopra si ingrandisca, il pallozzo resta
   fermo" -- in hover cresce solo la label (1.2x, come nelle viz). */
.hero__preview {
  position: relative;
  display: block;
  align-self: center;
  width: min(250px, 100%);
  aspect-ratio: 1;
  margin-bottom: 48px;
  border-radius: 50%;
  text-decoration: none;
  transition: transform 0.2s ease;
}


.hero__preview-image {
  position: absolute;
  inset: 0;
  display: block;
}

.hero__preview-image img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}

.hero__preview-label {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-size: 28px;
  letter-spacing: 0.03em;
  --outline-width: 0.03em;
  transition: transform 0.2s ease;
}

.hero__preview:hover .hero__preview-label {
  transform: scale(1.2);
}

.hero__preview-label__text {
  position: relative;
  display: inline-block;
  isolation: isolate;
}

/* Anna: "outline bianco intorno ai testi dei pallozzi" -- copia del testo
   (data-text) dietro all'originale, con solo il contorno bianco. */
.hero__preview-label__text::before {
  content: attr(data-text);
  position: absolute;
  inset: 0;
  z-index: -1;
  color: #fff;
  -webkit-text-stroke: 10px #fff;
  paint-order: stroke fill;
  text-shadow: none;
  stroke-linejoin: round;
  pointer-events: none;
}

.hero__preview-caption {
  margin: 0;
  width: 100%;
  font-size: 16px;
  line-height: 20px;
  letter-spacing: 0.01em;
  cursor: default;
}

@media (max-width: 900px) {
  .hero__intro { padding: 0; width: 100%; --col8: 100%; }
  .hero__text { width: 100%; }
  .hero__previews { grid-template-columns: 1fr; }
  .hero__preview-col { align-items: center; }
  .hero__preview-caption { width: 100%; }
}

/* 5. VIZ (area della visualizzazione) ----------------------------------------- */

/* L'intera pagina viz (titolo + diagramma + pannello caso) è vincolata
   all'altezza dello schermo: niente scroll per vedere il diagramma intero.
   Il footer resta subito sotto, raggiungibile scrollando se serve. */
.viz-page {
  position: relative;
  display: flex;
  flex-direction: column;
  height: calc(100dvh - var(--nav-height));
  padding-top: 6px; /* più vicino alla navbar (era 20px) */
  padding-bottom: 20px;
}

.viz-page__header {
  flex: 0 0 auto;
  /* Griglia a 12 colonne: il testo introduttivo occupa le colonne 1-9
     (comprese), la definizione le colonne 1-6 (comprese, riga 2), il
     bottone Legend le colonne 10-12 allineato a destra sulla riga 1 con
     la STESSA BASELINE del titolo (align-items:baseline).

     row-gap: 16px e' la distanza REALE, costante, tra il testo del
     titolo e quello della definizione -- misurata dal testo vero, non da
     uno spazio riservato dentro il titolo (quello era l'errore prima:
     Anna, "quello che ti ho chiesto di dimezzare non era un padding, era
     lo spazio lasciato per la riga di testo quando il testo occupa più
     righe" -- riservare spazio DENTRO al titolo/definizione per il caso
     peggiore creava un vuoto visibile sotto un testo più corto).

     Il pezzo che DEVE restare fisso e' invece dove comincia il
     diagramma sotto: min-height (impostato via JS, reserveHeaderHeight
     in viz.js) blocca l'altezza di QUESTO contenitore al caso peggiore
     assoluto -- 3 righe di titolo + 16px + 3 righe di definizione, Anna:
     "la viz dovra' stare alla massima distanza possibile" -- cosi' il
     diagramma non si sposta mai, qualunque sia la lunghezza REALE del
     testo in un dato momento. align-content:start (invece del default,
     che su alcuni motori CSS puo' STIRARE le righe della grid per
     riempire lo spazio in piu' anziche' lasciarlo vuoto in fondo) fa si'
     che l'eventuale spazio avanzato tra la fine della definizione vera e
     il bordo inferiore riservato resti vuoto DOPO la definizione, senza
     allargare né il titolo né la definizione stessi. */
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: 24px;
  row-gap: 16px;
  align-items: baseline;
  align-content: start;
}

/* Applicata a ENTRAMBE le pagine (vedi il tag <section> in reacting.njk
   E in sense-making.njk -- Anna: "fai che anche introtext di
   sense-making si comporta in questo stesso modo", quindi non e' piu'
   "Reacting soltanto" come nel giro precedente).
   Anna (rifinitura finale, testo originale riferito a Reacting ma ora
   valido per entrambe): "Tratta il blocco introblock come uno sticky,
   ma non legend. Solo il testo introduttivo e la definizione sono
   sticky." -- .legend-wrap e' un fratello INDIPENDENTE (vedi
   reacting.njk / sense-making.njk), fuori da .viz-page__header, quindi
   resta in flusso normale e scorre via con la pagina.

   Perche' .viz-page diventa una griglia (non piu' flex) per queste
   pagine: .viz-page__header e .legend-wrap devono restare AFFIANCATI
   sulla stessa riga/baseline (come a riposo, invariato visivamente), ma
   .viz-page__header deve anche avere un contenitore "alto" per la sua
   posizione sticky -- per funzionare per TUTTA la lunghezza della
   pagina (il diagramma puo' essere molto piu' alto di uno schermo), il
   "contenitore" giusto e' .viz-page stesso (che con .viz-page--scroll
   ha height:auto, quindi alto quanto header+diagramma insieme), NON un
   header-wrapper piu' corto -- per questo header/legend/body sono ora
   TUTTI e tre item diretti della STESSA griglia (righe diverse: riga 1
   header+legend, riga 2 il diagramma), invece di stare header dentro
   un altro contenitore. */
.viz-page--sticky-header {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: 24px;
  align-items: baseline;
}

/* .viz-page__header (Reacting): ora contiene SOLO .viz-page__intro-blur
   (titolo+definizione, vedi reacting.njk) -- non piu' un proprio grid a
   12 colonne al suo interno (non serve piu': ha un solo figlio, che
   occupa tutta la sua larghezza). E' LUI ad avere min-height (via JS,
   reserveHeaderHeight in viz.js, invariato) e a diventare sticky:

   - grid-column:1/10 nella griglia ESTERNA di .viz-page-sticky-header
     (le stesse 9 tracce che aveva il solo titolo prima).
   - top: calc(--nav-height + 6px) = la stessa identica distanza a cui
     si trova già oggi A RIPOSO (.viz-page ha padding-top:6px) -- lo
     sticky si "aggancia" dal primissimo pixel di scroll, senza scatti,
     e la mantiene COSTANTE per sempre dopo.
   - z-index:35, tra .navbar (40) e tutto il resto del contenuto di
     pagina (diagramma, pallozzi, z-index:auto) -- risultato: lo sfondo
     sfocato di .viz-page__intro-blur qui dentro (sotto) sta SOTTO la
     navbar, SOPRA tutto il resto, esattamente come richiesto. */
.viz-page--sticky-header .viz-page__header {
  /* display:block sovrascrive il grid a 12 colonne della regola base
     (quella regola resta come fallback per qualunque pagina viz senza
     .viz-page--sticky-header) -- qui header ha un solo figlio
     (.viz-page__intro-blur, vedi sotto), che riempie semplicemente il
     100% della sua larghezza in flusso normale, senza bisogno di alcun
     grid interno. */
  display: block;
  grid-column: 1 / 10;
  grid-row: 1;
  align-self: start;
  position: sticky;
  top: calc(var(--nav-height) + 6px);
  z-index: 35;
}

/* .legend-wrap: item DIRETTO della stessa griglia esterna di
   .viz-page__header (non piu' figlio suo) -- stessa colonna/riga di
   sempre (10/13, riga 1), quindi a riposo appare ESATTAMENTE come
   prima. Anna (rifinitura successiva): "rendi anche legend sticky con
   introtext" -- ora e' sticky ANCH'ESSO, con lo stesso top di
   .viz-page__header cosi' restano affiancati sulla stessa riga per
   tutta la durata dello scroll, esattamente come lo sono a riposo
   (prima invece scorreva via con la pagina, vedi commento superato qui
   sotto).

   z-index qui sotto: il rettangolo di sfondo/blur di
   .viz-page__intro-blur (vedi piu' sotto) e' largo quanto tutto lo
   schermo e passa ANCHE sotto quest'area (Anna: "passa sotto legend"),
   quindi .legend-wrap ha bisogno di un proprio z-index piu' alto di
   quello di .viz-page__header (35), altrimenti -- essendo ora anche lui
   positioned (sticky) ma dichiarato DOPO nel DOM -- il suo ordine di
   stacking non e' garantito sopra quello dell'header. 36 = un solo
   passo sopra i 35 dell'header, cosi' il bottone Legend resta sempre
   leggibile sopra al blur. */
.viz-page--sticky-header .legend-wrap {
  grid-column: 10 / 13;
  grid-row: 1;
  align-self: start;
  position: sticky;
  top: calc(var(--nav-height) + 6px);
  z-index: 36;
}

.viz-page--sticky-header .viz-page__body {
  grid-column: 1 / 13;
  grid-row: 2;
}

/* viz-page__intro-blur: wrapper interno a .viz-page__header, attorno a
   SOLO titolo+definizione. Ha una sua griglia interna a 9 colonne (non
   12) per titolo(riga1)/definizione(riga2) -- 9 tracce con lo stesso
   column-gap (24px) di quella esterna a 12 producono tracce della
   STESSA identica larghezza in pixel (N*W + (N-1)*G = larghezza totale;
   qui la larghezza totale e' la stessa, 9 colonne esterne, quindi W
   torna identico), percio' le regole .viz-page__title (grid-column:1/10
   = tutte e 9 le tracce) e .viz-page__definition (grid-column:1/7 = 6 di
   9 tracce) NON hanno bisogno di essere toccate.

   ALTEZZA REALE (auto, non min-height, a differenza di .viz-page__header
   che invece riserva SEMPRE il caso peggiore): quando la definizione e'
   vuota/nascosta resta alta solo quanto il titolo; quando compare una
   definizione vera, si allarga -- Anna: "quando compare la definizione,
   questo rettangolo di background blur diventa più alto, andando in
   basso 36px oltre la fine del testo della definizione." Per questo lo
   sfondo/blur (sotto, ::before) vive su QUESTO elemento e non
   direttamente su .viz-page__header. */
.viz-page--sticky-header .viz-page__intro-blur {
  position: relative;
  display: grid;
  grid-template-columns: repeat(9, 1fr);
  column-gap: 24px;
  row-gap: 16px;
  align-content: start;
}

/* Lo sfondo/blur vero e proprio vive in questo pseudo-elemento (stessa
   tecnica di .navbar::before e di .legend-panel::before), NON sul
   contenitore stesso, per poterlo estendere OLTRE il testo senza
   spostarlo (titolo/definizione restano allineati pixel-per-pixel alle
   colonne) e per applicare una maschera sfumata senza tagliare via il
   testo vero (che e' un fratello di questo pseudo, non dentro).

   Anna (rifinitura finale), i quattro lati:
   - top: sborda verso l'alto fino a "finire sotto la navbar" -- risale
     di (nav-height + 6px), che porta esattamente a y:0 (il bordo
     superiore della navbar), + un margine extra cosi' da finire
     davvero SOTTO di lei, non solo toccarne il bordo. Invisibile
     comunque sotto la navbar stessa (z-index 40, piu' alto).
   - left E right: Anna (rifinitura successiva): "fai che il rettangolo
     backgroundblur di introtext e' largo quanto tutto lo schermo e
     passa sotto legend" -- entrambi i lati ora usano lo stesso
     overshoot enorme (-100vw), tagliato via al vero bordo dello
     schermo da overflow-x:hidden su <body> (sezione 2 BASE), che e' un
     ancestor. Il rettangolo passa quindi anche sotto .legend-wrap (che
     per restare leggibile sopra ha un proprio z-index piu' alto, vedi
     il commento su .viz-page--sticky-header .legend-wrap qui sopra).
   - bottom: eccede oltre il bordo REALE (non il caso peggiore) del
     testo attualmente visibile, della stessa misura della zona di
     sfumatura qui sotto (64px) -- vedi il commento su
     .viz-page__intro-blur sopra per il perche' funziona automaticamente
     quando compare la definizione.

   mask-image a DUE livelli (verticale + orizzontale) con
   mask-composite:intersect, stessa tecnica di .legend-panel::before:
   sfuma su TUTTI E QUATTRO i lati (Anna: "tutti i lati che diminuiscono
   progressivamente di opacità... come fa la navbar... come anche fa la
   legenda").
   Anna (rifinitura successiva): "vorrei che lo stacco... fosse ancora
   più sfumato e meno netto" -- due cambi rispetto a prima (era un
   singolo gradiente lineare 0->36px):
   1. zona di sfumatura allargata, 36px -> 64px per lato;
   2. curva ad accelerazione variabile (5 stop intermedi, non piu' un
      solo gradiente lineare) che replica esattamente la stessa "forma"
      della sfumatura di .navbar::before (stessa progressione di
      opacità 0/0.14/0.36/0.62/0.88/1, qui semplicemente percorsa in
      64px invece che sull'intera altezza navbar) -- risultato uno
      stacco molto più morbido, coerente con l'altra sfumatura di
      pagina invece di un semplice fade lineare.
   A sinistra/destra la sfumatura non si vede mai in pratica (tagliata
   via dal clip dello schermo), in alto e' coperta dalla navbar: resta
   comunque la stessa regola per tutti e quattro i lati, senza casi
   speciali.

   Anna ("si crea un leggero gradino tra i due backgroundblur, come se
   la fine della navbar si sovrapponesse al bg blur... quando dovrebbero
   essere in continuità"): la sfumatura d'apertura in ALTO (verso la
   navbar) arrivava a piena intensità già 64px dopo l'inizio del box --
   cioe' a y≈44px nella viewport (box inizia a y≈-20, nascosto sotto la
   navbar) -- mentre il blur della navbar (col vecchio approccio a 5
   layer) era ancora forte fino a y≈90-100px: tra 44 e 100 i due si
   sommavano (doppio blur), poi sparendo di colpo la navbar a 100
   l'intensita' totale scendeva bruscamente. Fix di allora: la sfumatura
   restava a zero fino a y≈70, poi saliva a piena intensita' proprio
   nella finestra in cui la navbar sfumava via (70->100), passandosi il
   testimone nello stesso intervallo.
   Anna (dopo aver eliminato il blur della navbar, sostituendolo con un
   velo piatto a gradiente di opacita'): "adesso abbiamo un altro
   problema: gestire lo stacco tra navbar aggiornata e background blur
   dei testi introduttivi... estendere il bg blur più in su rispetto al
   testo introduttivo, mandandolo sotto la navbar fino a Y=0 dello
   schermo". Motivo: la navbar non blurra piu' nulla, e' solo un velo di
   colore che diventa via via piu' trasparente scendendo -- nella zona in
   cui e' trasparente, prima si vedeva il contenuto SOTTO di lei ancora
   nitido (il blocco sticky era trasparente li', partiva a piena
   intensita' solo a y≈70-100), creando uno stacco di nitidezza (nitido
   sotto la navbar che sfuma -> sfocato sotto). Fix: il blocco sticky e'
   ormai OPACO fin dall'inizio del suo box (gia' oltre lo schermo, a
   y≈-20, quindi invisibile prima di y=0) -- niente piu' tratto
   trasparente ne' rampa iniziale: da y=0 in giu' il contenuto dietro la
   navbar e' SEMPRE gia' sfocato, quindi qualunque punto in cui la navbar
   diventa trasparente rivela sfondo gia' sfocato, non piu' nitido. La
   sfumatura in BASSO (verso il diagramma sotto, non legata alla navbar)
   resta invariata (64px), quindi il gradiente verticale non e'
   simmetrico: i due lati sono scritti con valori distinti. */
.viz-page--sticky-header .viz-page__intro-blur::before {
  content: "";
  position: absolute;
  top: calc(-1 * (var(--nav-height) + 6px + 20px));
  left: -100vw;
  right: -100vw;
  bottom: -64px;
  z-index: -1;
  background: rgba(247, 247, 247, 0.45);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  -webkit-mask-image:
    linear-gradient(to bottom,
      #000 0,
      #000 calc(100% - 64px),
      rgba(0, 0, 0, 0.88) calc(100% - 48px),
      rgba(0, 0, 0, 0.62) calc(100% - 34px),
      rgba(0, 0, 0, 0.36) calc(100% - 21px),
      rgba(0, 0, 0, 0.14) calc(100% - 10px),
      transparent 100%),
    linear-gradient(to right,
      transparent 0,
      rgba(0, 0, 0, 0.14) 10px,
      rgba(0, 0, 0, 0.36) 21px,
      rgba(0, 0, 0, 0.62) 34px,
      rgba(0, 0, 0, 0.88) 48px,
      #000 64px,
      #000 calc(100% - 64px),
      rgba(0, 0, 0, 0.88) calc(100% - 48px),
      rgba(0, 0, 0, 0.62) calc(100% - 34px),
      rgba(0, 0, 0, 0.36) calc(100% - 21px),
      rgba(0, 0, 0, 0.14) calc(100% - 10px),
      transparent 100%);
  -webkit-mask-composite: source-in;
  mask-image:
    linear-gradient(to bottom,
      #000 0,
      #000 calc(100% - 64px),
      rgba(0, 0, 0, 0.88) calc(100% - 48px),
      rgba(0, 0, 0, 0.62) calc(100% - 34px),
      rgba(0, 0, 0, 0.36) calc(100% - 21px),
      rgba(0, 0, 0, 0.14) calc(100% - 10px),
      transparent 100%),
    linear-gradient(to right,
      transparent 0,
      rgba(0, 0, 0, 0.14) 10px,
      rgba(0, 0, 0, 0.36) 21px,
      rgba(0, 0, 0, 0.62) 34px,
      rgba(0, 0, 0, 0.88) 48px,
      #000 64px,
      #000 calc(100% - 64px),
      rgba(0, 0, 0, 0.88) calc(100% - 48px),
      rgba(0, 0, 0, 0.62) calc(100% - 34px),
      rgba(0, 0, 0, 0.36) calc(100% - 21px),
      rgba(0, 0, 0, 0.14) calc(100% - 10px),
      transparent 100%);
  mask-composite: intersect;
}

/* Rimpicciolito su richiesta di Anna rispetto al valore Figma (era 36px)
   e vincolato alle colonne 1-9 (vedi .viz-page__header sopra) invece che
   a un max-width fisso. */
.viz-page__title {
  /* Colonne 1-9 comprese (Anna) = 9 track = dalla linea di griglia 1 alla
     10, non alla 9 (che fermerebbe la colonna 9 esclusa, solo 8 track). */
  grid-column: 1 / 10;
  grid-row: 1;
  font-family: var(--font-display);
  font-weight: 300;
  /* Anna: "fallo di corpo 28 con interlinea 32" -- valori fissi, non più
     clamp responsive (era clamp(20px, 1.7vw, 28px) / line-height 1.3). */
  font-size: 28px;
  line-height: 32px;
  letter-spacing: var(--tracking-normal);
  margin: 0;
}

.viz-page__title b,
.viz-page__title strong {
  font-weight: 500;
}

.viz-page__title .is-adjective {
  font-weight: 500;
  /* Anna: "la frase introduttiva mantiene l'outline stroke
     nell'underline, non voglio che l'underline sia più spesso, deve
     rimanere senza stroke". text-decoration:underline was NOT enough
     even after nesting the outline (.text-outline) onto an INNER span
     (see buildTitle in viz.js): Chrome still let that inner span's
     text-shadow (the outline halo, see .text-outline below) bleed into
     the outer decoration line. border-bottom is a completely different
     paint mechanism (box model, not text-decoration/text-shadow), so it
     structurally cannot pick up a descendant's shadow, whatever Chrome's
     exact propagation behaviour is for the other technique.
     display:inline-block makes the border-box (and its padding-bottom,
     the 4px gap Anna asked for) actually count towards this line's
     height, instead of just overflowing invisibly the way inline
     elements' vertical padding does. */
  display: inline-block;
  text-decoration: none;
  /* Anna: 0.5px -> 1px (probabilmente arrotondato a invisibile da
     Chrome, i border sub-pixel non renderizzano in modo affidabile a
     tutti gli zoom/DPI) -> 2px. */
  border-bottom: 2px solid currentColor;
  padding-bottom: 2px;
}

.viz-page__definition {
  /* Colonne 1-6 comprese (Anna) = 6 track = dalla linea di griglia 1 alla
     7. La distanza dal titolo sopra e' il row-gap: 16px di
     .viz-page__header, non un margin qui. */
  grid-column: 1 / 7;
  grid-row: 2;
  margin: 0;
  /* Anna: "corpo 16 e interlinea 20" per il testo della definizione
     (ridotto da un iniziale 20/24). */
  font-size: 16px;
  line-height: 20px;
  /* Nessun min-height qui apposta: l'elemento e' alto quanto il suo testo
     VERO (Anna sta accorciando ogni definizione per stare entro 3 righe
     a questa impaginazione) -- lo spazio per il caso peggiore (3 righe)
     e' riservato dal CONTENITORE (.viz-page__header, min-height via JS),
     non da questo elemento, altrimenti un testo più corto lascerebbe di
     nuovo un vuoto sotto se stesso invece che comprimersi naturalmente.
     display:-webkit-box + line-clamp resta comunque una rete di
     sicurezza: un testo più lungo di 3 righe viene troncato, non
     trabocca oltre lo spazio riservato dal contenitore. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
  /* Niente attributo HTML "hidden" (display:none, che lo toglierebbe dal
     flusso): resta sempre nel layout, cambia solo visibility, cosi'
     l'aggiunta della definizione al click non sposta mai il testo sotto
     di lei (che qui non c'e' comunque altro, ma per coerenza col
     titolo). */
  visibility: hidden;
}

/* Occupa le 3 colonne intere (10-12) — non solo la larghezza del bottone
   — cosi' il pannello della legenda qui sotto puo' essere largo esattamente
   3 colonne. Il bottone dentro si allinea a destra da solo via flexbox
   (justify-content:flex-end), visivamente identico a prima. */
.legend-wrap {
  grid-column: 10 / 13;
  grid-row: 1;
  display: flex;
  justify-content: flex-end;
  position: relative;
}

/* Figma: 569:292 (chiuso, freccia ↖) / 611:295 (aperto, freccia ↙) — Inter
   Tight BOLD 16px, line-height 20px, tracking 3%, uppercase. */
.legend-toggle {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  background: none;
  border: none;
  padding: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 14px;
  line-height: 18px;
  letter-spacing: var(--tracking-normal);
  text-transform: uppercase;
  color: inherit;
  cursor: pointer;
}

.legend-toggle__label {
  position: relative;
}

.legend-toggle__label::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0; /* praticamente a contatto col testo (prima -1px) */
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right; /* riposo/uscita: si richiude verso dx, stessa logica della navbar */
  transition: transform 0.25s ease;
}

.legend-toggle:hover .legend-toggle__label::after,
.legend-toggle.is-clicking .legend-toggle__label::after {
  transform: scaleX(1);
  transform-origin: left; /* comparsa: cresce verso dx */
}

.legend-toggle[aria-expanded="true"] .legend-toggle__label::after {
  transform: scaleX(1);
  transform-origin: left;
  transition: none;
}

/* Freccia della legenda: stesso trattamento delle frecce dell'indice about
   (nessun outline a riposo, cresce in hover della stessa quantita', 1.5px). */
.legend-toggle [data-legend-arrow] {
  --outline-width: 0;
  transition: text-shadow 0.2s ease;
}
.legend-toggle:hover [data-legend-arrow] {
  --outline-width: 1.5px;
}

/* Ancorata a .legend-wrap (non più a .viz-page): si riposiziona da sola
   qualunque sia la posizione del bottone. Il CONTENUTO (i .legend-item,
   vedi padding sotto) è largo esattamente le 3 colonne di .legend-wrap
   (10-12); il pannello stesso si estende OLTRE quelle 3 colonne di
   --legend-air su ogni lato — margine negativo bilanciato da un padding
   della stessa misura — per dare respiro al contenuto senza restringerlo.
   z-index sotto quello della navbar (40): scorrendo la pagina il pannello
   deve passarci SOTTO, non sopra. Niente bordo: solo sfondo semi-trasparente
   + blur, come la navbar. */
.legend-panel {
  --legend-air: 24px;
  --legend-air-bottom: 40px; /* più aria sotto l'ultimo blocco che sugli altri lati */
  position: absolute;
  top: calc(100% + 10px);
  left: calc(-1 * var(--legend-air));
  right: calc(-1 * var(--legend-air));
  font-family: var(--font-display);
  font-size: 15px;
  border-radius: 8px;
  padding: var(--legend-air) var(--legend-air) var(--legend-air-bottom) var(--legend-air);
  z-index: 30;
}

/* Sfondo + blur separati in uno pseudo-elemento (stessa tecnica di
   .navbar::before) cosi' possiamo sfumarne i bordi con una maschera SENZA
   sfumare anche il testo sopra: niente piu' rettangolo netto, il pannello
   si dissolve gradualmente verso il blur su tutti e 4 i lati. */
.legend-panel::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: 8px;
  background: rgba(247, 247, 247, 0.55);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  -webkit-mask-image:
    linear-gradient(to bottom, transparent 0, #000 22px, #000 calc(100% - 22px), transparent 100%),
    linear-gradient(to right, transparent 0, #000 22px, #000 calc(100% - 22px), transparent 100%);
  -webkit-mask-composite: source-in;
  mask-image:
    linear-gradient(to bottom, transparent 0, #000 22px, #000 calc(100% - 22px), transparent 100%),
    linear-gradient(to right, transparent 0, #000 22px, #000 calc(100% - 22px), transparent 100%);
  mask-composite: intersect;
}

/* Ogni voce (adjective/cluster ecc.) è un blocco immagine+testo: immagine
   a sx larga 1/3, definizione a dx larga 2/3, più blocchi impilati uno
   sotto l'altro. L'immagine è centrata nel suo terzo, il testo allineato
   a sx (come richiesto). */
.legend-item {
  display: grid;
  grid-template-columns: 1fr 2fr;
  gap: 12px;
  align-items: start;
  text-align: left;
}
.legend-item + .legend-item {
  margin-top: 16px;
}

/* Anna: immagini reali in src/images/legend-images/ (sensemaking-1/2,
   reacting-1/2/3 -- gia' texture/outline/blob dei pallozzi/cluster/aspetti
   reali, sfondo trasparente) al posto del cerchio placeholder colorato di
   prima. Niente crop/border-radius: le immagini hanno gia' la propria
   forma (alcune sono un cerchio pieno, altre un contorno tratteggiato,
   altre una macchia irregolare per "system aspect") -- object-fit:contain
   le mostra intere, senza tagliarle né deformarle. */
.legend-item__media {
  width: 60%;
  aspect-ratio: 1 / 1;
  margin: 0 auto; /* centrata nel suo terzo di colonna */
}
.legend-item__media img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}

.legend-item__text {
  margin: 0;
}
/* Il <p> generato in JS (vedi viz.js) ha un margine utente-agent di default
   che spingeva il testo piu' in basso del bordo superiore della cella,
   facendo sembrare l'immagine "piu' in alto": azzerandolo i due bordi
   superiori (immagine e testo) coincidono davvero. */
.legend-item__text p {
  margin: 0;
}

/* Fuori da .viz-page (che e' vincolata all'altezza dello schermo): sta
   nel normale flusso della pagina, quindi non e' visibile all'apertura e
   richiede uno scroll verso il basso per essere raggiunta, come il footer
   subito sotto. Allineata a destra, testo piu' piccolo delle voci navbar. */
.viz-source-wrap {
  padding-top: 8px;
  /* Anna: "distanza contenuto pagina-footer di 60px" -- questo padding si
     sommava al margin-top:60px del footer stesso (vedi footer.site-footer
     piu' sotto), producendo uno spazio reale ben piu' grande di 60px.
     Azzerato: ora l'UNICA fonte dello spacing prima del footer e' il
     margin-top del footer, quindi la distanza reale e' davvero 60px. */
  padding-bottom: 0;
  text-align: right;
}
/* Anna: sostituita la placeholder "Source sentence" con la frase vera
   (vedi sense-making.njk / reacting.njk) -- .viz-source-text e' ora un
   paragrafo con testo semplice + 1 o 2 link inline, non piu' un intero
   <a>. I link (.viz-source-link) usano lo STESSO trattamento di
   .about-citation/.panel-quote__id altrove nel sito: nessun underline
   sull'intero link, freccia (data-arrow, gia' .text-outline nel markup)
   senza stroke a riposo che si ispessisce in hover, underline solo sul
   testo (.viz-source-link__label). */
.viz-source-text {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 16px;
}

/* Anna: "in sense-making lo fai tornare come prima" (via il margin-top
   condiviso rimosso sopra) "mentre in reacting fai un padding top di
   32px" -- .theme-pink e' l'unica pagina con quel tema (reacting.njk),
   stesso selettore-scope gia' usato altrove nel file per "solo
   Reacting". Padding (non margin, come richiesto testualmente) sul
   paragrafo stesso. */
.theme-pink .viz-source-text {
  padding-top: 32px;
}
.viz-source-link {
  text-decoration: none;
  color: inherit;
}
.viz-source-link [data-arrow] {
  --outline-width: 0;
  transition: text-shadow 0.2s ease;
}
.viz-source-link:hover [data-arrow] {
  --outline-width: 1.5px;
}
.viz-source-link__label {
  text-decoration: underline;
}

/* Corpo della pagina: griglia a 12 colonne (come in Figma). Il diagramma
   occupa sempre tutto lo spazio, alla sua dimensione piena — non si
   restringe mai per fare posto al pannello del caso, che compare invece IN
   OVERLAY sopra di esso, posizionato con grid-column (vedi sezione 6). */
.viz-page__body {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  /* Anna: "la distanza tra definizione e viz non ha senso definirla,
     dato che la viz deve stare sempre fissa" -- niente margin-top
     POSITIVO qui: la posizione fissa del diagramma resta TUTTA
     responsabilita' del min-height su .viz-page__header (vedi li' e
     reserveHeaderHeight in viz.js), che gia' include il caso peggiore di
     titolo + gap + 3 righe di definizione. Un margin positivo
     aggiungerebbe ancora altro spazio oltre a quel massimo, invece di
     essere il massimo stesso. Un margin NEGATIVO invece va bene: Anna
     l'ha chiesto esplicitamente ("porta più in alto tutta la viewport
     della viz di 16px") come scostamento fisso indipendente da tutto il
     resto -- l'header sopra mantiene il suo spazio riservato invariato,
     il corpo della viz si limita a scivolarci dentro di 16px. */
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: 24px;
  margin-top: -16px;
}

.viz-wrap {
  position: absolute;
  inset: 0;
  min-width: 0;
  min-height: 0;
}

/* -------------------------------------------------------------------------
   VIEWPORT SEGNAPOSTO: la visualizzazione interattiva vera (SVG dei
   pallozzi, cluster, maschere immagine ecc.) arriva in un passaggio
   successivo — qui c'è solo un riquadro che riserva lo spazio corretto
   (stesse proporzioni del frame Figma) così il layout della pagina è già
   quello definitivo. Sostituire .viz-placeholder con il markup della viz
   reale non richiederà toccare il resto della pagina.
   ------------------------------------------------------------------------- */
.viz-placeholder {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px dashed color-mix(in srgb, var(--color-accent) 45%, transparent);
  border-radius: 8px;
  background: color-mix(in srgb, var(--color-accent) 4%, transparent);
}

.viz-placeholder__label {
  font-family: var(--font-display);
  font-size: 14px;
  letter-spacing: var(--tracking-normal);
  text-transform: uppercase;
  opacity: 0.5;
  text-align: center;
  max-width: 320px;
}

/* -------------------------------------------------------------------------
   VIZ REALE: SVG dei pallozzi/cluster (sostituisce .viz-placeholder qui
   sopra, che resta nel foglio come fallback ma non viene più renderizzato
   da sense-making.njk / reacting.njk).

   Ogni pallozzo è un <rect> grande quanto l'intero cluster, riempito con
   il colore del tema (currentColor, vedi --color-accent su body) e
   MASCHERATO dalla texture puntinata di Anna via mask-image (il PNG è
   nero+alpha: l'alpha decide dove il colore compare, il nero stesso non
   conta — per questo serve mask-image e non un semplice <image>), poi
   RITAGLIATO al cerchio di quel pallozzo (clip-path, vedi <clipPath> per
   pallozzo in viz.njk). mask-size:contain evita qualunque deformazione
   (a differenza del vecchio preserveAspectRatio="none" che schiacciava
   l'immagine dentro un riquadro di proporzioni diverse dalle sue).
   ------------------------------------------------------------------------- */
.viz-desktop {
  width: 100%;
  height: 100%;
  display: block;
}

/* Il contorno tratteggiato (cluster E macro-cluster) non ha più una
   regola di presentazione propria: e' markup REALE dell'SVG di Anna,
   ricopiato di peso in un <g data-cluster-label="..."> / <g
   data-macro-cluster-label="..."> da renderViz (vedi il commento li',
   partials/viz.njk) -- porta gia' il proprio fill/stroke/stroke-width/
   dasharray corretti (con l'accento sostituito da currentColor). Quel
   <g> condivide lo stesso attributo data-* della label del suo
   cluster/macro-cluster, quindi eredita transizione e sfocatura "gratis"
   dalle regole [data-cluster-label]/[data-macro-cluster-label] piu'
   sotto, senza bisogno di una classe .cluster-outline separata. */

/* Percorso invisibile: esiste solo perché <textPath> (nome del cluster,
   vedi renderViz) possa "camminarci sopra". */
.cluster-label-arc {
  fill: none;
  stroke: none;
}

.cluster-label {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 14px;
  letter-spacing: var(--tracking-normal);
  pointer-events: none;
  transition: filter 0.35s ease, opacity 0.35s ease;
}

.category-label {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: var(--tracking-normal);
  pointer-events: none;
}

.bubble-cluster {
  cursor: pointer;
  /* Anna: il clic mette a fuoco (focus) il <g tabindex="0">, e il default
     del browser disegna un focus ring intorno al suo BOUNDING BOX -- che
     è quello del <rect> texture, grande quanto l'intero cluster (il
     cerchio visibile è solo un clip-path, il rect sotto resta a piena
     dimensione), non quello del solo pallozzo. Risultato: "si crea un
     outline intorno a tutta l'immagine png del cluster", che Anna non
     vuole. La crescita del testo in hover/focus-visible qui sotto è già
     un indicatore di stato sufficiente, quindi il ring va tolto. */
  outline: none;
  /* Il blur/opacità dello stato "isolato" (is-blurred / is-blurred-sibling
     più sotto) va applicato QUI, al <g>, e non al <rect> texture al suo
     interno -- che invece ha un clip-path (vedi renderBubbleShape in
     partials/viz.njk). Un filter su un elemento CON clip-path viene
     tagliato via esattamente al bordo del ritaglio: lo sfocamento si
     vede solo come un lieve ammorbidimento interno, con un bordo ancora
     netto -- esattamente quello che Anna descriveva come "sembra che
     siano solo in opacità ridotta invece che blurrati". Applicato al <g>
     invece, il browser disegna prima il rect già ritagliato al cerchio
     e SOLO DOPO sfoca quel risultato, lasciando lo sfocato spandersi
     oltre il bordo come ci si aspetta. */
  transition: filter 0.35s ease, opacity 0.35s ease;
}

.bubble-cluster__texture {
  fill: currentColor;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
  pointer-events: none;
}

/* Bersaglio cliccabile/hover: un cerchio invisibile della stessa identica
   geometria del pallozzo, sopra alla texture -- serve perché la texture
   (puntinata, non piena) da sola non intercetterebbe il clic su tutta
   l'area. Resta SEMPRE invisibile, anche in hover/attivo (Anna non vuole
   il cerchio): il riscontro visivo viene solo dall'ingrandimento del
   testo qui sotto e dalla sfocatura del resto del diagramma. */
.bubble-cluster__hit {
  fill: transparent;
  stroke: transparent;
}

/* Label del pallozzo: DUE <text> separati (vedi renderBubbleLabel in
   partials/viz.njk), uno per lo stroke e uno per il fill. Non bastava un
   solo elemento con paint-order né uno z-index CSS (provato e rimosso:
   in pratica non riordinava il disegno come previsto) -- la garanzia ora
   viene dall'ORDINE DI DISEGNO stesso: renderViz fa passate separate,
   prima TUTTE le forme (texture) di ogni pallozzo/cluster, poi TUTTI gli
   stroke, poi TUTTI i fill, quindi ogni stroke è sempre sopra OGNI
   texture e ogni fill è sempre sopra OGNI stroke, indipendentemente da
   quale pallozzo/cluster siano (Anna: "lo stroke bianco deve stare sopra
   TUTTI i pallozzi di quel cluster, non solo sopra il pallozzo a cui
   appartiene"). */
.bubble-cluster__label {
  font-family: var(--font-body);
  font-weight: 500;
  pointer-events: none;
  transform-box: fill-box;
  transform-origin: center;
  transition: opacity 0.2s ease, transform 0.2s ease, filter 0.3s ease;
}

.bubble-cluster__label--stroke {
  /* Grigio dello sfondo (Anna: "outline stroke grigio dello stesso
     colore dello sfondo"), stondato, spesso il DOPPIO di quello
     originale: era 0.2em (4px a corpo 20), ora 0.4em (8px a corpo 20). */
  fill: none;
  stroke: var(--color-bg);
  stroke-width: 0.4em;
  stroke-linejoin: round;
  paint-order: stroke;
}

.bubble-cluster__label--fill {
  fill: currentColor;
  stroke: none;
}

/* Hover/focus: JS (wireBubbleHover in viz.js) sincronizza .is-hovered fra
   la forma (unica a poter davvero ricevere hover/focus, la label è
   pointer-events:none) e le sue due label -- non più puro CSS perché non
   sono più annidate una dentro l'altra (vedi il commento sopra). A
   riposo, solo la label cresce: corpo 20 -> 24 (Anna), 24/20 = 1.2. */
.bubble-cluster__label.is-hovered {
  transform: scale(1.2);
}

/* Stato "isolato": un pallozzo attivo (scelto con un clic). Applicato via
   JS in viz.js (selectBubble/resetSelection), che alterna
   is-active/is-blurred-sibling/is-blurred su .bubble-cluster E su
   .bubble-cluster__label (sono siblings adesso, ognuno riceve la classe
   per conto proprio in base al proprio data-group/data-bubble). */

/* Pallozzi dello STESSO cluster del pallozzo attivo ("fratelli"): allo
   stesso livello che prima era quello di "tutto il resto" -- Anna:
   "rendi l'attuale livello di blur quello dei pallozzi all'interno del
   cluster selezionato". */
.bubble-cluster.is-blurred-sibling,
.bubble-cluster__label.is-blurred-sibling {
  filter: blur(6px);
  opacity: 0.5;
}

/* Pallozzi FUORI dal cluster attivo: più sfocati dei "fratelli" -- Anna:
   "aumenta quello sugli altri" (e comunque, una volta risolto il bug del
   clip-path qui sopra, questo valore rende visivamente ALMENO quanto lo
   sfocamento di outline/label del cluster qui sotto, 9px). Non sono più
   cliccabili mentre una selezione è attiva (vedi il click handler in
   viz.js, Anna: "gli altri cluster non devono essere cliccabili") --
   cursor:default lo comunica anche visivamente, invece del pointer che
   suggerirebbe il contrario.
   Anna (rifinitura successiva): "vorrei che il blur sui cluster non
   selezionati... fosse ancora più forte" -- 10px -> 15px (+50%, stesso
   rapporto di prima rispetto al blur di outline/nome cluster qui sotto,
   6px -> 9px). Solo il RAGGIO di blur e' cambiato, non l'opacita': la
   richiesta era specificamente sul blur. */
.bubble-cluster.is-blurred,
.bubble-cluster__label.is-blurred {
  filter: blur(15px);
  opacity: 0.35;
}

.bubble-cluster.is-blurred {
  cursor: default;
}

/* Anna: "in hover sul pallozzo, oltre a rendersi più leggibile il testo,
   anche il pallozzo stesso diventi visibile riducendo il blur" -- ora
   entrambi (non più solo la label) tornano nitidi in hover/focus, ma
   solo per il livello "fratelli" (stesso cluster del pallozzo attivo):
   quelli fuori cluster restano sfocati anche in hover. */
.bubble-cluster.is-blurred-sibling.is-hovered,
.bubble-cluster__label.is-blurred-sibling.is-hovered {
  filter: none;
  opacity: 1;
}

/* Contorno/nome del cluster (il <g> del contorno + il <g>/<text> del
   nome, qualunque forma prendano: importati pari pari dall'svg di Anna
   per entrambe le pagine, o calcolati via textPath come rete di
   sicurezza se un export non avesse ancora la label. [data-cluster-label]
   è l'attributo che TUTTI questi elementi hanno sempre, vedi renderViz
   in partials/viz.njk). Un solo livello: un
   cluster o è quello attivo (nitido) o non lo è (sfocato), niente
   "fratelli" per i cluster. Nessun clip-path qui, quindi nessun bug di
   sfocamento tagliato via come sopra.

   [data-macro-cluster-label] è lo stesso identico meccanismo un livello
   più in su (contorno + nome del MACRO-cluster, solo Reacting): stessa
   transizione/sfocatura, ma tenuto sfocato/nitido in base al macro-cluster
   attivo (non al cluster), vedi selectBubble in viz.js. */
[data-cluster-label],
[data-macro-cluster-label] {
  transition: filter 0.35s ease, opacity 0.35s ease;
}
[data-cluster-label].is-blurred,
[data-macro-cluster-label].is-blurred {
  /* Anna: "vorrei che il blur sui cluster non selezionati... fosse
     ancora più forte" -- 6px -> 9px (+50%, stessa proporzione della
     bump sui pallozzi qui sopra). Vale per entrambe le pagine (Reacting
     e Sense-making condividono questa stessa regola, nessun override
     per tema/pagina serve). */
  filter: blur(9px);
  opacity: 0.5;
}

/* Il nome del cluster/macro-cluster e' importato "di peso" dall'SVG di
   Anna (vedi commento su renderViz in viz.njk): fill/stroke/stroke-width/
   dasharray restano quelli VERI del file (solo il colore-tema hardcoded
   e' sostituito con currentColor) -- niente regola CSS che li
   riscriva. Un vero stroke-width="10" a questa scala chiuderebbe i
   "buchi" delle lettere (il centro di O/D/G/A...) se disegnato nello
   stesso elemento del fill: il motivo per cui resta leggibile e' che
   renderViz ora disegna alone e riempimento come DUE elementi separati,
   tutti gli aloni di ogni cluster/macro-cluster PRIMA, tutti i
   riempimenti DOPO (stessa tecnica della label dei pallozzi) -- il
   riempimento vince sempre sopra, qualunque sia lo spessore del suo
   alone. Vedi il commento sopra renderViz in partials/viz.njk per il
   dettaglio completo. */

/* Reacting è più "alto" di uno schermo (12 cluster su una griglia molto
   più fitta di Sense-making): invece di schiacciarlo per farlo stare in
   un'unica schermata, occupa tutta la larghezza del viewport e la pagina
   scorre in verticale per vederlo tutto (Anna: "va bene se scrollo per
   vedere la porzione in basso"). Stessa identica tecnica già usata sotto
   i 900px per ENTRAMBE le pagine (vedi media query qui sotto): qui viene
   applicata SEMPRE, non solo su schermi stretti, alla pagina che ha la
   classe .viz-page--scroll (vedi il tag <section> in reacting.njk e
   sense-making.njk).

   Sense-making usa la stessa classe anche se il suo diagramma di norma
   STA in un'unica schermata (era "senza scroll" prima): senza, la sua
   larghezza al 100% competeva con l'ALTEZZA disponibile (.viz-wrap era
   position:absolute; inset:0 dentro .viz-page__body, che si restringe
   ogni volta che l'header sopra (titolo/definizione) cresce) -- quando
   l'header prendeva più spazio, preserveAspectRatio sull'SVG lo
   rimpiccioliva per stare nell'altezza rimasta, invece di restare largo
   quanto il viewport (Anna: "hai ridotto la dimensione della
   visualizzazione... tieni la viz sempre larga quanto la viewport").
   Con .viz-page--scroll la larghezza è SEMPRE il 100%, l'altezza segue
   l'aspect-ratio: nella stragrande maggioranza dei casi il risultato sta
   comunque tutto in una schermata (Sense-making è molto più "largo" che
   "alto", 1495 × 636 ≈ 2.35 : 1), e scorre solo nei casi limite in cui
   non ci stesse. */
.viz-page--scroll {
  height: auto;
  min-height: calc(100dvh - var(--nav-height));
}
.viz-page--scroll .viz-page__body {
  position: static;
  display: block;
}

/* Reacting soltanto (Anna: "porta più in basso di 16px tutta la viewport
   della viz" -- riferito solo a questa pagina, non a Sense-making, che
   tiene il proprio -16px qui sopra invariato): annulla lo scostamento
   verso l'alto di .viz-page__body per questa pagina, riportandola alla
   posizione base. .theme-pink e' un selettore sicuro per "solo Reacting"
   (l'unica pagina con quel tema, vedi il front matter "theme: pink" in
   reacting.njk). */
.theme-pink .viz-page__body {
  margin-top: 0;
}
.viz-page--scroll .viz-wrap {
  position: static;
  width: 100%;
  aspect-ratio: var(--viz-aspect, 1 / 1);
}

@media (max-width: 900px) {
  .viz-page { height: auto; min-height: calc(100dvh - var(--nav-height)); }
  .viz-page__body { position: static; display: block; margin-top: 24px; }
  .viz-wrap { position: static; aspect-ratio: var(--viz-aspect, 1 / 1); }
}

/* 6. CASO (pannello esempio, IN OVERLAY sopra al diagramma) --------------------- */
/* Pronto per quando la viz interattiva sarà collegata. Posizione sulla
   griglia a 12 colonne definita in .viz-page__body qui sopra: per spostare
   il pannello basta cambiare questi due numeri — es. "4 / 7" mette il
   pannello nelle colonne 4, 5, 6 allineato a sinistra. La seconda cifra è
   la colonna DOPO l'ultima occupata (quindi "fino alla colonna 6" = 7). */
.case-panel {
  --panel-col-start: 8;
  --panel-col-end: 13;
  display: none;
  position: absolute;
  inset: 0;
  grid-column: var(--panel-col-start) / var(--panel-col-end);
  max-width: 560px;
  overflow-y: auto;
  flex-direction: column;
  justify-content: center;
  gap: 16px;
  padding: 24px 0 24px 32px;
  background: linear-gradient(to right, transparent, var(--color-bg) 48px);
  z-index: 10;
}

.case-panel.is-visible {
  display: flex;
}

.case-panel__image {
  width: 100%;
  max-height: 220px;
  object-fit: cover;
  background: color-mix(in srgb, var(--color-accent) 8%, #fff);
  border-radius: 4px;
}

.case-panel__text dt {
  font-weight: 700;
  margin-top: 12px;
  font-size: 14px;
}
.case-panel__text dt:first-child { margin-top: 0; }
.case-panel__text dd {
  margin: 0 0 4px;
  font-size: 14px;
}

/* .case-panel__quote / .case-nav* (citazione + ID + "next case" del
   vecchio pannello per-pallozzo di Reacting) rimosse insieme al markup
   che le usava (renderFixedPanelsLegacy in viz.js) quando Reacting e'
   passato alla stessa griglia di Sense-making -- vedi il blocco
   "PANNELLI A COMPARSA" piu' sotto, che ora e' l'UNICO sistema per
   entrambe le pagine.

   .case-panel__empty resta: e' ancora il messaggio "nessun caso
   raccolto" del sistema a griglia (renderFixedPanelsGrid), invariato. */
.case-panel__empty {
  font-style: italic;
  opacity: 0.7;
  max-width: 480px;
  font-size: 14px;
}

@media (max-width: 900px) {
  .case-panel {
    position: static;
    grid-column: auto;
    width: auto;
    max-width: none;
    background: none;
    padding: 24px 0 0;
    margin-top: 8px;
  }
}

/* -------------------------------------------------------------------------
   PANNELLI A COMPARSA (ancorati al punto della PAGINA in cui compaiono) --
   Sostituiscono .case-panel qui sopra (rimasto nel foglio ma non più
   usato da nessun markup: era il vecchio pannello singolo impilato,
   pensato prima di avere le 13 disposizioni reali da Figma). Ogni
   pallozzo cliccato apre fino a 4 di questi elementi, ognuno posizionato
   via JS (viz.js, renderFixedPanels) con coordinate calcolate a partire
   dalla posizione reale del pallozzo sullo schermo AL MOMENTO DEL CLIC,
   convertita in coordinate di DOCUMENTO (+ window.scrollX/scrollY, vedi
   viz.js). Anna: "voglio poter scrollare mentre sono onclick su un
   pallozzo. Semplicemente, se scrollo il testo rimane dove è comparso"
   -- position:absolute (non fixed) e' esattamente questo: nessun
   ancestor posizionato tra questi elementi (appesi direttamente a
   document.body) e la initial containing block, quindi la loro
   posizione e' relativa al DOCUMENTO intero, non al viewport -- scorrono
   naturalmente insieme al resto della pagina, restando ancorati al punto
   esatto in cui sono comparsi, senza bisogno di alcun listener di
   scroll. Il posizionamento (left/top/width) viene scritto come stile
   inline da viz.js; qui c'è solo l'aspetto.
   ------------------------------------------------------------------------- */
.viz-fixed-panel {
  position: absolute;
  /* z-index piu' basso di .navbar (40, vedi sezione 3 LAYOUT, che e'
     invece position:fixed): scorrendo la pagina, un pannello puo' finire
     "sotto" alla navbar in coordinate di documento -- con uno z-index
     piu' basso resta visivamente dietro ed effettivamente sfocato dal
     backdrop-filter di .navbar::before, esattamente come qualunque altro
     contenuto di pagina (Anna: "scrollando il contenuto si posiziona
     sopra la navbar, non va bene deve rimanere sotto e subire il
     blur"). */
  z-index: 30;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.25s ease;
}

.viz-fixed-panel.is-visible {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}

/* Anna: cluster/macro-cluster "vicini all'inizio" del diagramma (Person,
   Commercial product, Multimodal prompting -- vedi SCROLL_AWARE_IMAGE_GROUPS
   in viz.js) le cui immagini popup finiscono spesso proprio sotto la
   fascia sticky di introtext ("troppo in alto, non avrò mai modo di
   scrollare e vederle per intero"). z-index qui sopra a
   .viz-page__header/.viz-page__intro-blur::before (35) -- ma sotto
   .navbar (40) -- cosi' l'immagine non viene MAI davvero coperta dal
   backdrop-filter reale della fascia sticky; "va sotto" e' invece
   SIMULATO qui sotto con .is-scrolled (stessi valori di
   .bubble-cluster.is-blurred altrove nel sito, per coerenza visiva),
   che a differenza di z-index si anima con una transition fluida
   (toggle via scroll listener in viz.js, updateScrollAwareImage). */
.viz-fixed-panel--image.is-scroll-aware {
  z-index: 37;
  transition: opacity 0.25s ease, filter 0.35s ease;
}
.viz-fixed-panel--image.is-scroll-aware.is-scrolled {
  filter: blur(15px);
  opacity: 0.35;
}

/* La coppia di "eco" ingrandite (nome cluster + parola pallozzo) che
   comparivano qui come pannello fisso separato è stata rimossa (Anna:
   duplicava le stesse due parole già completate nella frase del titolo,
   "non li voglio"). Il titolo (.viz-page__title, sezione qui sopra) resta
   l'unico posto dove compaiono, con lo stesso corpo del resto della
   frase più l'outline blu (.text-outline) e, per la parola del pallozzo,
   la sottolineatura.

   --info (blocco "User request" / "Model behaviour") e --quote
   (citazione + ID caso + Case X of N) hanno il loro aspetto vero piu'
   sotto, nella sezione "griglia 12 colonne" (.viz-fixed-panel--info p,
   .viz-fixed-panel--quote blockquote...) -- lo stesso identico markup e
   le stesse classi su ENTRAMBE le pagine, solo il colore cambia via
   tema (.theme-pink / .theme-blue sul <body>). */

/* --image: screenshot del caso, solo dove presente. */
/* Anna: sequenze di più immagini per lo stesso caso (colonna display_img
   del CSV con più valori) si alternano ogni 1s (vedi positionImagePanelGrid
   in viz.js). Anna (rifinitura): "non devono avere per forza la stessa
   forma... l'importante è che sia centrato... usa gli stessi criteri per
   la dimensione già in vigore" -- ogni <img> ha quindi la SUA PROPRIA
   dimensione reale (calcolata da viz.js con lo stesso sizeForAspect di
   sempre sulla sua vera aspect ratio, non forzata in un unico box), e
   left/top/width/height vengono scritti come inline style DIRETTAMENTE
   da viz.js per ciascuna immagine (coordinate locali al contenitore,
   centrate sul centro del riquadro-ancora determinato dalla prima
   immagine) -- qui c'è quindi solo l'aspetto, nessuna dimensione/
   posizione di default. object-fit:contain (non più cover): le
   dimensioni scritte da viz.js sono già la vera aspect ratio
   dell'immagine, contain evita solo un ritaglio per arrotondamenti
   dell'ultimo pixel. Una sola alla volta e' visibile (.is-current,
   opacity:1), le altre restano a opacity:0 -- il cambio è una
   dissolvenza (transition), non uno scatto. Nel caso comune (una sola
   immagine) c'è un solo <img>, sempre .is-current. */
.viz-fixed-panel--image img {
  display: block;
  position: absolute;
  object-fit: contain;
  border-radius: 4px;
  opacity: 0;
  transition: opacity 0.6s ease;
}
.viz-fixed-panel--image img.is-current {
  opacity: 1;
}

/* -------------------------------------------------------------------------
   Blocchi a comparsa: un layout per CLUSTER (Sense-making) / per
   MACRO-CLUSTER (Reacting, 7 configurazioni condivise dai cluster al suo
   interno) su una griglia 12 colonne x GRID_ROWS righe che copre il solo
   diagramma (vedi panelLayouts.js/viz.js: renderFixedPanelsGrid per come
   i numeri "col"/"row" diventano queste posizioni via JS -- GRID_ROWS e'
   6 per Sense-making, 12 per Reacting, unica differenza reale fra le due
   pagine). STESSO markup, STESSE classi, STESSO aspetto su entrambe --
   solo il colore cambia (currentColor, guidato da --color-accent /
   .theme-pink-.theme-blue sul <body>).
   ------------------------------------------------------------------------- */

/* Blocco 1 (User request / Model behaviour): testo che scorre come un
   paragrafo normale -- Anna: "dopo i : il testo non deve andare a capo,
   ma proseguire sulla stessa riga" (cioe' non piu' un <dl> con <dd>
   forzato su riga propria). Solo le due etichette hanno l'outline
   (0.5px, via --outline-width inline su ciascuna, vedi .text-outline). */
.viz-fixed-panel--info p {
  font-family: var(--font-display); /* "Inter Tight" */
  font-weight: 400; /* regular */
  font-size: 16px;
  line-height: 20px;
  margin: 0;
}
/* 4px tra "User request" e "Model behaviour" -- solo sopra il secondo
   paragrafo, non sopra il primo. */
.viz-fixed-panel--info p + p {
  margin-top: 4px;
}

/* Blocco 2 (citazione): Inter Tight LIGHT, corpo 24/interlinea 28,
   outline 0.5px su tutto il testo (impostato inline in viz.js, non solo
   sulle etichette come nel blocco 1). */
.viz-fixed-panel--quote blockquote {
  font-family: var(--font-display);
  font-weight: 300; /* light */
  font-size: 24px;
  line-height: 28px;
  margin: 0;
}

/* Riga previous/next: e' anche il punto di ancoraggio dell'intero
   gruppo (block1.col/row in panelLayouts.js -- vedi il commento in
   renderFixedPanelsGrid, viz.js). Font Inter (non Inter Tight),
   semi-bold, maiuscolo, corpo 12. */
.panel-nav {
  display: flex;
  align-items: baseline;
  gap: 24px;
  font-family: var(--font-body); /* "Inter" */
  font-weight: 600; /* semi-bold */
  font-size: 12px;
  letter-spacing: var(--tracking-normal);
  text-transform: uppercase;
}
/* Anna: "i tasti sono un po' difficili da cliccare, vorrei che
   diventassero dei veri e propri tasti" -- prima le "[ ]" erano solo
   caratteri di testo attorno alla freccia (vedi viz.js), non un vero
   riquadro cliccabile: ora un bordo reale di 1px racchiude l'intero
   <button>, quindi cliccare OVUNQUE dentro il rettangolo funziona (è
   comunque sempre stato un <button> vero, solo che non si vedeva dove
   finisse l'area cliccabile). currentColor invece di un blu fisso: così
   il bordo segue automaticamente il tema di pagina, blu su Sense-making
   e rosa su Reacting, come tutto il resto (--color-accent). */
.panel-nav__btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: none;
  border: 1px solid currentColor;
  padding: 3px 6px;
  font: inherit;
  color: inherit;
  cursor: pointer;
  /* text-transform NON e' ereditato qui nonostante .panel-nav (il
     genitore) lo imposti: il foglio di stile di default del browser per
     <button> lo azzera esplicitamente (text-transform: none), e una
     dichiarazione diretta sull'elemento batte sempre l'ereditarietà dal
     genitore, qualunque sia la specificità. Va quindi ripetuto qui. */
  text-transform: uppercase;
}
.panel-nav__btn:disabled {
  opacity: 0.35;
  cursor: default;
  /* Anna: "non voglio nemmeno l'animazione in hover" sul bottone non
     cliccabile -- pointer-events:none e' l'unico modo per impedire
     davvero al browser di far scattare :hover, non basterebbe provare a
     sovrascrivere ogni regola :hover con :not(:disabled). */
  pointer-events: none;
}

/* Id del caso (con link a reddit) + conteggio "Case X of N", 24px sotto
   la citazione, sulla stessa riga con 24px di distanza reale tra loro
   (non più agganciato a una colonna della griglia come il vecchio
   bottone "next case" -- qui è solo testo informativo, non un bersaglio
   di clic da allineare). Maiuscolo, corpo 12. */
.panel-quote__nav {
  display: flex;
  align-items: baseline;
  gap: 24px;
  margin-top: 24px;
  font-family: var(--font-body); /* "Inter" */
  font-weight: 600; /* semi-bold, stessa famiglia/peso dei bottoni sopra */
  font-size: 12px;
  text-transform: uppercase;
}

/* L'intero link non ha underline di default (coprirebbe anche parentesi
   e freccia) -- Anna vuole l'underline SOLO sul testo "ID...", non sulla
   freccia: vedi .panel-quote__id-label qui sotto, che lo aggiunge solo
   a quello span. */
.panel-quote__id {
  text-decoration: none;
  color: inherit;
}
.panel-quote__id-label {
  text-decoration: underline;
}

/* Le frecce (previous/next + il link id caso): stesso trattamento della
   freccia del link dell'about (.about-citation [data-citation-arrow])
   -- nessun outline a riposo, si ispessisce in hover, stessa misura
   (1.5px) usata per la freccia di .legend-toggle. NB: qui bastano queste
   due regole solo perche' lo span della freccia ha ANCHE class="text-outline"
   nel markup (viz.js) -- e' quella classe a disegnare davvero lo stroke,
   --outline-width da sola e' solo il "quanto". */
.panel-quote__id [data-arrow],
.panel-nav__btn [data-arrow] {
  --outline-width: 0;
  transition: text-shadow 0.2s ease;
}
.panel-quote__id:hover [data-arrow] {
  --outline-width: 1.5px;
}
.panel-nav__btn:hover [data-arrow] {
  --outline-width: 1.5px;
}

/* "Previous"/"Next" hanno anche una sottolineatura in hover sulla sola
   parola (mai sulla freccia), stessa tecnica/valori di
   .legend-toggle__label e .navbar__links a::after (cresce da sx a dx,
   sparisce sempre da sx a dx). */
.panel-nav__label {
  position: relative;
}
.panel-nav__label::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 0.25s ease;
}
/* Anna: "in hover non si sottolinea più la parola" -- .panel-nav__label::after
   resta definito sopra (scaleX(0) di riposo) ma non viene più portato a
   scaleX(1) in hover: la sottolineatura non compare mai più sui bottoni
   previous/next (l'effetto hover ora è solo la freccia più spessa). */

@media (max-width: 900px) {
  /* La disposizione a posizione fissa è pensata per desktop (dove il
     diagramma sta tutto a schermo o richiede uno scroll verticale
     "controllato"): sotto i 900px i pannelli restano nascosti finché non
     arriva la versione mobile della viz (vedi renderVizMobile in
     partials/viz.njk, ancora rimandata su richiesta di Anna). */
  .viz-fixed-panel {
    display: none;
  }
}

/* "doppio layer con outline esterno": lo stroke viene disegnato dietro al
   riempimento, dello stesso colore, dando un effetto più marcato/compresso
   senza dover cambiare peso del font. Applicala a qualunque testo con
   class="text-outline" (in SVG o in HTML).
   Lo spessore è una custom property (--outline-width) con un default
   ragionevole: se in Figma Dev Mode vedi un valore diverso per un elemento
   specifico, non serve toccare questa regola — basta impostare
   `--outline-width: ...;` (in px o em) direttamente su quell'elemento. */
.text-outline {
  /* Per testo dentro un <svg> (es. le etichette dei cluster nel diagramma):
     stroke/paint-order sono proprietà SVG, disegnano il contorno dietro al
     riempimento, con spigoli arrotondati via stroke-linejoin. */
  paint-order: stroke fill;
  stroke: currentColor;
  stroke-width: var(--outline-width, 0.0286em);
  stroke-linejoin: round;
  /* Per testo HTML normale (h1, span, ecc. — es. il titolo della home):
     non esiste un vero "stroke-linejoin" per il testo HTML (neanche con
     -webkit-text-stroke, che dà sempre spigoli vivi). Per ottenere angoli
     arrotondati si simula il contorno con 240 ombre disposte in cerchio
     attorno al testo (una ogni 1.5°: più ombre = risultato più smooth,
     240 elimina i "gradini" visibili con poche ombre), alla distanza di
     --outline-width: il risultato è visivamente equivalente a uno stroke
     con line-join arrotondato. */
  text-shadow:
    calc(var(--outline-width, 0.0286em) * 1.0) 0 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9997) calc(var(--outline-width, 0.0286em) * 0.0262) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9986) calc(var(--outline-width, 0.0286em) * 0.0523) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9969) calc(var(--outline-width, 0.0286em) * 0.0785) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9945) calc(var(--outline-width, 0.0286em) * 0.1045) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9914) calc(var(--outline-width, 0.0286em) * 0.1305) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9877) calc(var(--outline-width, 0.0286em) * 0.1564) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9833) calc(var(--outline-width, 0.0286em) * 0.1822) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9781) calc(var(--outline-width, 0.0286em) * 0.2079) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9724) calc(var(--outline-width, 0.0286em) * 0.2334) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9659) calc(var(--outline-width, 0.0286em) * 0.2588) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9588) calc(var(--outline-width, 0.0286em) * 0.284) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9511) calc(var(--outline-width, 0.0286em) * 0.309) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9426) calc(var(--outline-width, 0.0286em) * 0.3338) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9336) calc(var(--outline-width, 0.0286em) * 0.3584) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9239) calc(var(--outline-width, 0.0286em) * 0.3827) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9135) calc(var(--outline-width, 0.0286em) * 0.4067) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9026) calc(var(--outline-width, 0.0286em) * 0.4305) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.891) calc(var(--outline-width, 0.0286em) * 0.454) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.8788) calc(var(--outline-width, 0.0286em) * 0.4772) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.866) calc(var(--outline-width, 0.0286em) * 0.5) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.8526) calc(var(--outline-width, 0.0286em) * 0.5225) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.8387) calc(var(--outline-width, 0.0286em) * 0.5446) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.8241) calc(var(--outline-width, 0.0286em) * 0.5664) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.809) calc(var(--outline-width, 0.0286em) * 0.5878) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.7934) calc(var(--outline-width, 0.0286em) * 0.6088) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.7771) calc(var(--outline-width, 0.0286em) * 0.6293) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.7604) calc(var(--outline-width, 0.0286em) * 0.6494) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.7431) calc(var(--outline-width, 0.0286em) * 0.6691) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.7254) calc(var(--outline-width, 0.0286em) * 0.6884) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.7071) calc(var(--outline-width, 0.0286em) * 0.7071) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.6884) calc(var(--outline-width, 0.0286em) * 0.7254) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.6691) calc(var(--outline-width, 0.0286em) * 0.7431) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.6494) calc(var(--outline-width, 0.0286em) * 0.7604) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.6293) calc(var(--outline-width, 0.0286em) * 0.7771) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.6088) calc(var(--outline-width, 0.0286em) * 0.7934) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.5878) calc(var(--outline-width, 0.0286em) * 0.809) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.5664) calc(var(--outline-width, 0.0286em) * 0.8241) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.5446) calc(var(--outline-width, 0.0286em) * 0.8387) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.5225) calc(var(--outline-width, 0.0286em) * 0.8526) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.5) calc(var(--outline-width, 0.0286em) * 0.866) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.4772) calc(var(--outline-width, 0.0286em) * 0.8788) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.454) calc(var(--outline-width, 0.0286em) * 0.891) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.4305) calc(var(--outline-width, 0.0286em) * 0.9026) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.4067) calc(var(--outline-width, 0.0286em) * 0.9135) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.3827) calc(var(--outline-width, 0.0286em) * 0.9239) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.3584) calc(var(--outline-width, 0.0286em) * 0.9336) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.3338) calc(var(--outline-width, 0.0286em) * 0.9426) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.309) calc(var(--outline-width, 0.0286em) * 0.9511) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.284) calc(var(--outline-width, 0.0286em) * 0.9588) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.2588) calc(var(--outline-width, 0.0286em) * 0.9659) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.2334) calc(var(--outline-width, 0.0286em) * 0.9724) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.2079) calc(var(--outline-width, 0.0286em) * 0.9781) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.1822) calc(var(--outline-width, 0.0286em) * 0.9833) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.1564) calc(var(--outline-width, 0.0286em) * 0.9877) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.1305) calc(var(--outline-width, 0.0286em) * 0.9914) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.1045) calc(var(--outline-width, 0.0286em) * 0.9945) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.0785) calc(var(--outline-width, 0.0286em) * 0.9969) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.0523) calc(var(--outline-width, 0.0286em) * 0.9986) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.0262) calc(var(--outline-width, 0.0286em) * 0.9997) 0 currentColor,
    0 calc(var(--outline-width, 0.0286em) * 1.0) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.0262) calc(var(--outline-width, 0.0286em) * 0.9997) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.0523) calc(var(--outline-width, 0.0286em) * 0.9986) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.0785) calc(var(--outline-width, 0.0286em) * 0.9969) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.1045) calc(var(--outline-width, 0.0286em) * 0.9945) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.1305) calc(var(--outline-width, 0.0286em) * 0.9914) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.1564) calc(var(--outline-width, 0.0286em) * 0.9877) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.1822) calc(var(--outline-width, 0.0286em) * 0.9833) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.2079) calc(var(--outline-width, 0.0286em) * 0.9781) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.2334) calc(var(--outline-width, 0.0286em) * 0.9724) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.2588) calc(var(--outline-width, 0.0286em) * 0.9659) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.284) calc(var(--outline-width, 0.0286em) * 0.9588) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.309) calc(var(--outline-width, 0.0286em) * 0.9511) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.3338) calc(var(--outline-width, 0.0286em) * 0.9426) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.3584) calc(var(--outline-width, 0.0286em) * 0.9336) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.3827) calc(var(--outline-width, 0.0286em) * 0.9239) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.4067) calc(var(--outline-width, 0.0286em) * 0.9135) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.4305) calc(var(--outline-width, 0.0286em) * 0.9026) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.454) calc(var(--outline-width, 0.0286em) * 0.891) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.4772) calc(var(--outline-width, 0.0286em) * 0.8788) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.5) calc(var(--outline-width, 0.0286em) * 0.866) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.5225) calc(var(--outline-width, 0.0286em) * 0.8526) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.5446) calc(var(--outline-width, 0.0286em) * 0.8387) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.5664) calc(var(--outline-width, 0.0286em) * 0.8241) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.5878) calc(var(--outline-width, 0.0286em) * 0.809) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.6088) calc(var(--outline-width, 0.0286em) * 0.7934) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.6293) calc(var(--outline-width, 0.0286em) * 0.7771) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.6494) calc(var(--outline-width, 0.0286em) * 0.7604) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.6691) calc(var(--outline-width, 0.0286em) * 0.7431) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.6884) calc(var(--outline-width, 0.0286em) * 0.7254) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.7071) calc(var(--outline-width, 0.0286em) * 0.7071) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.7254) calc(var(--outline-width, 0.0286em) * 0.6884) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.7431) calc(var(--outline-width, 0.0286em) * 0.6691) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.7604) calc(var(--outline-width, 0.0286em) * 0.6494) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.7771) calc(var(--outline-width, 0.0286em) * 0.6293) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.7934) calc(var(--outline-width, 0.0286em) * 0.6088) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.809) calc(var(--outline-width, 0.0286em) * 0.5878) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.8241) calc(var(--outline-width, 0.0286em) * 0.5664) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.8387) calc(var(--outline-width, 0.0286em) * 0.5446) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.8526) calc(var(--outline-width, 0.0286em) * 0.5225) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.866) calc(var(--outline-width, 0.0286em) * 0.5) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.8788) calc(var(--outline-width, 0.0286em) * 0.4772) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.891) calc(var(--outline-width, 0.0286em) * 0.454) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9026) calc(var(--outline-width, 0.0286em) * 0.4305) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9135) calc(var(--outline-width, 0.0286em) * 0.4067) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9239) calc(var(--outline-width, 0.0286em) * 0.3827) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9336) calc(var(--outline-width, 0.0286em) * 0.3584) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9426) calc(var(--outline-width, 0.0286em) * 0.3338) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9511) calc(var(--outline-width, 0.0286em) * 0.309) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9588) calc(var(--outline-width, 0.0286em) * 0.284) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9659) calc(var(--outline-width, 0.0286em) * 0.2588) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9724) calc(var(--outline-width, 0.0286em) * 0.2334) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9781) calc(var(--outline-width, 0.0286em) * 0.2079) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9833) calc(var(--outline-width, 0.0286em) * 0.1822) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9877) calc(var(--outline-width, 0.0286em) * 0.1564) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9914) calc(var(--outline-width, 0.0286em) * 0.1305) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9945) calc(var(--outline-width, 0.0286em) * 0.1045) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9969) calc(var(--outline-width, 0.0286em) * 0.0785) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9986) calc(var(--outline-width, 0.0286em) * 0.0523) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9997) calc(var(--outline-width, 0.0286em) * 0.0262) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -1.0) 0 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9997) calc(var(--outline-width, 0.0286em) * -0.0262) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9986) calc(var(--outline-width, 0.0286em) * -0.0523) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9969) calc(var(--outline-width, 0.0286em) * -0.0785) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9945) calc(var(--outline-width, 0.0286em) * -0.1045) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9914) calc(var(--outline-width, 0.0286em) * -0.1305) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9877) calc(var(--outline-width, 0.0286em) * -0.1564) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9833) calc(var(--outline-width, 0.0286em) * -0.1822) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9781) calc(var(--outline-width, 0.0286em) * -0.2079) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9724) calc(var(--outline-width, 0.0286em) * -0.2334) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9659) calc(var(--outline-width, 0.0286em) * -0.2588) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9588) calc(var(--outline-width, 0.0286em) * -0.284) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9511) calc(var(--outline-width, 0.0286em) * -0.309) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9426) calc(var(--outline-width, 0.0286em) * -0.3338) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9336) calc(var(--outline-width, 0.0286em) * -0.3584) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9239) calc(var(--outline-width, 0.0286em) * -0.3827) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9135) calc(var(--outline-width, 0.0286em) * -0.4067) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.9026) calc(var(--outline-width, 0.0286em) * -0.4305) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.891) calc(var(--outline-width, 0.0286em) * -0.454) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.8788) calc(var(--outline-width, 0.0286em) * -0.4772) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.866) calc(var(--outline-width, 0.0286em) * -0.5) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.8526) calc(var(--outline-width, 0.0286em) * -0.5225) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.8387) calc(var(--outline-width, 0.0286em) * -0.5446) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.8241) calc(var(--outline-width, 0.0286em) * -0.5664) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.809) calc(var(--outline-width, 0.0286em) * -0.5878) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.7934) calc(var(--outline-width, 0.0286em) * -0.6088) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.7771) calc(var(--outline-width, 0.0286em) * -0.6293) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.7604) calc(var(--outline-width, 0.0286em) * -0.6494) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.7431) calc(var(--outline-width, 0.0286em) * -0.6691) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.7254) calc(var(--outline-width, 0.0286em) * -0.6884) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.7071) calc(var(--outline-width, 0.0286em) * -0.7071) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.6884) calc(var(--outline-width, 0.0286em) * -0.7254) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.6691) calc(var(--outline-width, 0.0286em) * -0.7431) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.6494) calc(var(--outline-width, 0.0286em) * -0.7604) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.6293) calc(var(--outline-width, 0.0286em) * -0.7771) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.6088) calc(var(--outline-width, 0.0286em) * -0.7934) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.5878) calc(var(--outline-width, 0.0286em) * -0.809) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.5664) calc(var(--outline-width, 0.0286em) * -0.8241) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.5446) calc(var(--outline-width, 0.0286em) * -0.8387) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.5225) calc(var(--outline-width, 0.0286em) * -0.8526) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.5) calc(var(--outline-width, 0.0286em) * -0.866) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.4772) calc(var(--outline-width, 0.0286em) * -0.8788) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.454) calc(var(--outline-width, 0.0286em) * -0.891) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.4305) calc(var(--outline-width, 0.0286em) * -0.9026) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.4067) calc(var(--outline-width, 0.0286em) * -0.9135) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.3827) calc(var(--outline-width, 0.0286em) * -0.9239) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.3584) calc(var(--outline-width, 0.0286em) * -0.9336) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.3338) calc(var(--outline-width, 0.0286em) * -0.9426) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.309) calc(var(--outline-width, 0.0286em) * -0.9511) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.284) calc(var(--outline-width, 0.0286em) * -0.9588) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.2588) calc(var(--outline-width, 0.0286em) * -0.9659) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.2334) calc(var(--outline-width, 0.0286em) * -0.9724) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.2079) calc(var(--outline-width, 0.0286em) * -0.9781) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.1822) calc(var(--outline-width, 0.0286em) * -0.9833) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.1564) calc(var(--outline-width, 0.0286em) * -0.9877) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.1305) calc(var(--outline-width, 0.0286em) * -0.9914) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.1045) calc(var(--outline-width, 0.0286em) * -0.9945) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.0785) calc(var(--outline-width, 0.0286em) * -0.9969) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.0523) calc(var(--outline-width, 0.0286em) * -0.9986) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * -0.0262) calc(var(--outline-width, 0.0286em) * -0.9997) 0 currentColor,
    0 calc(var(--outline-width, 0.0286em) * -1.0) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.0262) calc(var(--outline-width, 0.0286em) * -0.9997) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.0523) calc(var(--outline-width, 0.0286em) * -0.9986) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.0785) calc(var(--outline-width, 0.0286em) * -0.9969) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.1045) calc(var(--outline-width, 0.0286em) * -0.9945) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.1305) calc(var(--outline-width, 0.0286em) * -0.9914) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.1564) calc(var(--outline-width, 0.0286em) * -0.9877) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.1822) calc(var(--outline-width, 0.0286em) * -0.9833) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.2079) calc(var(--outline-width, 0.0286em) * -0.9781) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.2334) calc(var(--outline-width, 0.0286em) * -0.9724) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.2588) calc(var(--outline-width, 0.0286em) * -0.9659) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.284) calc(var(--outline-width, 0.0286em) * -0.9588) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.309) calc(var(--outline-width, 0.0286em) * -0.9511) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.3338) calc(var(--outline-width, 0.0286em) * -0.9426) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.3584) calc(var(--outline-width, 0.0286em) * -0.9336) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.3827) calc(var(--outline-width, 0.0286em) * -0.9239) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.4067) calc(var(--outline-width, 0.0286em) * -0.9135) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.4305) calc(var(--outline-width, 0.0286em) * -0.9026) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.454) calc(var(--outline-width, 0.0286em) * -0.891) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.4772) calc(var(--outline-width, 0.0286em) * -0.8788) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.5) calc(var(--outline-width, 0.0286em) * -0.866) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.5225) calc(var(--outline-width, 0.0286em) * -0.8526) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.5446) calc(var(--outline-width, 0.0286em) * -0.8387) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.5664) calc(var(--outline-width, 0.0286em) * -0.8241) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.5878) calc(var(--outline-width, 0.0286em) * -0.809) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.6088) calc(var(--outline-width, 0.0286em) * -0.7934) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.6293) calc(var(--outline-width, 0.0286em) * -0.7771) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.6494) calc(var(--outline-width, 0.0286em) * -0.7604) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.6691) calc(var(--outline-width, 0.0286em) * -0.7431) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.6884) calc(var(--outline-width, 0.0286em) * -0.7254) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.7071) calc(var(--outline-width, 0.0286em) * -0.7071) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.7254) calc(var(--outline-width, 0.0286em) * -0.6884) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.7431) calc(var(--outline-width, 0.0286em) * -0.6691) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.7604) calc(var(--outline-width, 0.0286em) * -0.6494) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.7771) calc(var(--outline-width, 0.0286em) * -0.6293) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.7934) calc(var(--outline-width, 0.0286em) * -0.6088) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.809) calc(var(--outline-width, 0.0286em) * -0.5878) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.8241) calc(var(--outline-width, 0.0286em) * -0.5664) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.8387) calc(var(--outline-width, 0.0286em) * -0.5446) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.8526) calc(var(--outline-width, 0.0286em) * -0.5225) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.866) calc(var(--outline-width, 0.0286em) * -0.5) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.8788) calc(var(--outline-width, 0.0286em) * -0.4772) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.891) calc(var(--outline-width, 0.0286em) * -0.454) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9026) calc(var(--outline-width, 0.0286em) * -0.4305) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9135) calc(var(--outline-width, 0.0286em) * -0.4067) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9239) calc(var(--outline-width, 0.0286em) * -0.3827) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9336) calc(var(--outline-width, 0.0286em) * -0.3584) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9426) calc(var(--outline-width, 0.0286em) * -0.3338) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9511) calc(var(--outline-width, 0.0286em) * -0.309) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9588) calc(var(--outline-width, 0.0286em) * -0.284) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9659) calc(var(--outline-width, 0.0286em) * -0.2588) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9724) calc(var(--outline-width, 0.0286em) * -0.2334) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9781) calc(var(--outline-width, 0.0286em) * -0.2079) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9833) calc(var(--outline-width, 0.0286em) * -0.1822) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9877) calc(var(--outline-width, 0.0286em) * -0.1564) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9914) calc(var(--outline-width, 0.0286em) * -0.1305) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9945) calc(var(--outline-width, 0.0286em) * -0.1045) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9969) calc(var(--outline-width, 0.0286em) * -0.0785) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9986) calc(var(--outline-width, 0.0286em) * -0.0523) 0 currentColor,
    calc(var(--outline-width, 0.0286em) * 0.9997) calc(var(--outline-width, 0.0286em) * -0.0262) 0 currentColor;
}

/* 7. ABOUT -------------------------------------------------------------------- */

/* Figma: 613:520 — Inter Tight Regular 80px, line-height 80px, tracking 1%.
   .about-hero e' anch'esso griglia a 12 colonne (come .viz-page__header)
   cosi' il blocco titolo+sottotitolo puo' agganciarsi alle colonne 1-8 con
   un grid-column diretto invece che con un max-width calcolato a mano. */
.about-hero {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: 24px;
  max-width: var(--page-max-width);
  margin: 0 auto;
  padding: 64px var(--page-padding) 0;
}

/* Largo quanto la riga piu' larga del TITOLO (width: fit-content), stessa
   tecnica di .hero__heading in home: il sottotitolo dentro non puo' mai
   essere piu' largo del titolo, in nessun caso, nemmeno responsive — va a
   capo prima invece di allungarsi per conto suo. E' questo wrapper (non
   piu' h1/p singolarmente) ad agganciarsi alle colonne 1-8. */
.about-hero__heading {
  grid-column: 1 / 9;
  width: fit-content;
  max-width: 100%;
}

.about-hero h1 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(40px, 6.5vw, 68px);
  line-height: 1;
  letter-spacing: var(--tracking-tight);
  margin: 0 0 12px;
  /* Outline 1.5px (0.02206em = 1.5/68, riferito alla dimensione piena
     desktop), stessa tecnica arrotondata di navbar/hero title
     (.text-outline, sezione 5). */
  --outline-width: 0.02206em;
}

/* Figma: 611:363 — Inter Tight Regular 28px, line-height 32px, tracking 1%. */
.about-hero p {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(18px, 2.2vw, 28px);
  line-height: 32px;
  letter-spacing: var(--tracking-tight);
  margin: 0;
  /* Non puo' mai superare la larghezza del titolo: vedi .about-hero__heading
     (width: fit-content) qui sopra. */
  max-width: 100%;
  /* Outline 0.3px (0.0107em = 0.3/28), stessa tecnica del titolo sopra. */
  --outline-width: 0.0107em;
}

/* Griglia a 12 colonne (stessa di tutte le altre pagine). Indice colonne
   1-2, gutter di 1 colonna (col.3), blocco di testo colonne 4-12: il
   bordo destro del blocco tocca la fine della colonna 12, che e' anche
   dove finisce (circa) la parola "About" nella navbar — stesso
   --page-padding, quindi stesso margine destro. .about-sections e ogni
   .about-section (sotto) sono "subgrid": ereditano le STESSE tracce di
   colonna di .about-layout, cosi' un elemento dentro una sezione puo'
   scrivere "grid-column: 1/9" (locale) e finire ESATTAMENTE sulle colonne
   di pagina corrispondenti, senza calcoli in px. */
.about-layout {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: 24px;
  /* Anna: "porta indice e testi più in alto, all'altezza in cui si fissa
     l'indice quando scrollo" -- prima partivano a 12vh (uno spazio
     "a occhio" sotto la navbar), diverso dal punto in cui l'indice si
     blocca durante lo scroll (position:sticky, top:calc(--nav-height +
     24px) su .about-index qui sotto). NB: il primo tentativo (padding-top:
     calc(--nav-height + 24px) anche qui) sommava DUE VOLTE --nav-height,
     perche' .page (base.njk) ha gia' il suo padding-top:var(--nav-height)
     per riservare lo spazio della navbar fissa -- risultato: 116+140=256px
     invece di 140px, contenuti ancora troppo in basso. Il padding-top qui
     deve essere SOLO il resto (24px), dato che i --nav-height di margine
     sono gia' garantiti da .page. */
  /* Anna: "distanza contenuto pagina-footer di 60px" -- i 120px di
     bottom qui si sommavano al margin-top:60px del footer, dando una
     distanza reale di 180px sull'About. Azzerato: resta solo il
     margin-top del footer a creare lo spazio, quindi la distanza reale
     e' 60px. */
  padding: 24px var(--page-padding) 0;
  max-width: var(--page-max-width);
  margin: 0 auto;
  align-items: start;
}

/* Figma: 614:537 — Inter Tight Regular 16px, line-height 20px, tracking
   3%. Ogni voce: freccia + testo (a volte su 2 righe). La voce "attiva"
   (sezione attualmente visibile, aggiornata via scroll o click) è
   sottolineata e usa la freccia ↘, le altre usano ↗ — vedi lo script in
   about.njk. */
.about-index {
  grid-column: 1 / 3;
  position: sticky;
  top: calc(var(--nav-height) + 24px);
  font-family: var(--font-display);
  font-size: 16px;
  line-height: 20px;
  letter-spacing: var(--tracking-normal);
}

/* display:flow-root (non flex): un box block-level "normale" che contiene
   correttamente la freccia in float senza farla influenzare la larghezza
   della label. Con display:flex la label, per restringersi quando il
   testo va a capo su 2 righe, veniva ridimensionata dall'algoritmo dei
   flex-item alla larghezza DISPONIBILE nella riga (non a quella del testo
   effettivo) — la sottolineatura sotto (ancorata a quella larghezza)
   risultava quindi piu' larga del testo vero. Fuori da flex il problema
   non si pone piu': vedi .about-index__label sotto per la soluzione
   definitiva (sottolineatura per-riga via background, non per-box). */
.about-index a {
  display: flow-root;
  text-decoration: none;
  margin-bottom: 12px;
}
.about-index a:last-child { margin-bottom: 0; }

/* Freccia in float: esce dal flusso del testo, la label le scorre
   accanto/sotto normalmente. Nessun outline a riposo, cresce a 1.5px
   (0.09375em = 1.5/16) quando si passa sopra la voce (seconda animazione
   in hover, oltre alla sottolineatura). */
.about-index [data-index-arrow] {
  float: left;
  margin-right: 6px;
  --outline-width: 0;
  transition: text-shadow 0.2s ease;
}
.about-index a:hover [data-index-arrow] {
  --outline-width: 0.09375em;
}

/* Sottolineatura solo sul testo (mai sulla freccia, che e' fuori da questo
   span). Tecnica: background-image lineare invece di un ::after
   assolutamente posizionato — con box-decoration-break:clone e la label
   rimasta un elemento INLINE "vero" (non un flex/grid item, che verrebbe
   "blockificato" perdendo la fedelta' per-riga), il browser ridisegna lo
   sfondo SEPARATAMENTE per ogni riga su cui il testo va a capo, usando la
   larghezza REALE di quella riga come riferimento per il %: niente piu'
   sottolineatura piu' larga del testo quando una voce va a capo. Stessa
   logica direzionale di .legend-toggle__label: compare da sx verso dx
   (si ancora a sx e cresce), sparisce sempre da sx verso dx (resta
   ancorata a dx e si restringe, "mangiandosi" da sx). Attiva (scroll o
   click) = stesso trattamento dell'hover, ma persistente. */
.about-index__label {
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-position: right bottom;
  background-size: 0% 1px;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
  transition: background-size 0.25s ease;
}
.about-index a:hover .about-index__label,
.about-index a[data-active="true"] .about-index__label {
  background-position: left bottom;
  background-size: 100% 1px;
}

.about-sections {
  grid-column: 4 / 13;
  display: grid;
  grid-template-columns: subgrid;
}

.about-section {
  display: grid;
  grid-template-columns: subgrid;
  grid-column: 1 / 10;
  margin-bottom: 96px;
  scroll-margin-top: calc(var(--nav-height) + 16px);
}
.about-section:last-child { margin-bottom: 0; }

/* Figma: 569:263 / 614:563 — Inter Tight Regular 28px, line-height 30px,
   tracking 3% (diverso dalla tagline sopra, che è 1%: Figma li ha
   effettivamente impostati così). Colonne locali 1-8 = colonne di pagina
   4-11, piena larghezza del blocco (una colonna piu' larga del corpo
   testo sotto, che e' 4-10). */
.about-section h2 {
  grid-column: 1 / 9;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(20px, 2.4vw, 28px);
  line-height: 30px;
  letter-spacing: var(--tracking-normal);
  margin: 0 0 24px;
  /* Outline 0.5px (0.0179em = 0.5/28), stessa tecnica del sottotitolo. */
  --outline-width: 0.0179em;
}

/* Figma: 613:371 — la "domanda di ricerca" in evidenza dentro la sezione
   "Research question", SOTTO al titolo piccolo "Research question": Inter
   Tight Regular 40px, line-height 48px, tracking 3%. È un paragrafo a sé
   (.about-section__highlight), non il titolo h2 della sezione. Colonne
   locali 1-9 (tutte quelle ereditate) = colonne di pagina 4-12, piena
   larghezza del blocco, un filo piu' larga del corpo testo sotto. */
.about-section .about-section__highlight {
  grid-column: 1 / 10;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(26px, 3.6vw, 40px);
  line-height: 48px;
  letter-spacing: var(--tracking-normal);
  text-indent: 0;
  margin: 0 0 64px;
  /* Outline 1.5px (0.0375em = 1.5/40), stessa tecnica del titolo. */
  --outline-width: 0.0375em;
}

/* Figma: 613:366 ecc. — Inter Tight Regular 24px (ridotto a 22px su
   richiesta di Anna), line-height 30px, tracking 3%, prima riga di ogni
   paragrafo rientrata di 64px. Colonne locali 1-8 = colonne di pagina
   4-11. */
.about-section p {
  grid-column: 1 / 8;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(15px, 1.5vw, 20px);
  line-height: 24px;
  letter-spacing: var(--tracking-normal);
  text-indent: 64px;
  margin: 0 0 48px;
}
.about-section p:last-child { margin-bottom: 0; }

.about-section p strong { font-weight: 700; }

.about-section .about-section__note {
  grid-column: 1 / 8;
  font-size: 13px;
  font-style: italic;
  opacity: 0.7;
  border-left: 3px solid currentColor;
  padding-left: 12px;
  margin: 0 0 24px;
  text-indent: 0;
}

/* Figma: 613:372 — riquadro video/immagine, colonne locali 1-7 = colonne
   di pagina 4-10, stessa occupazione del corpo testo sotto (prima era
   1/10 = 4-12, piena larghezza). Larghezza invariata rispetto al vecchio
   rettangolo segnaposto (921×485, ~19:10); l'altezza pero' ora segue la
   proporzione VERA dell'immagine caricata (screenshot 1920×1080, 16:9,
   piu' "alta" del vecchio rettangolo a parita' di larghezza) invece di
   quella del rettangolo grigio placeholder -- Anna: "mantieni la stessa
   larghezza ma fai in modo che sia proporzionata alla dimensione vera
   dell'immagine". */
.about-figure {
  grid-column: 1 / 8;
  background: var(--color-grey-placeholder);
  aspect-ratio: 1920 / 1080;
  border-radius: 4px;
  margin-bottom: 8px;
  overflow: hidden;
}
/* Immagine reale (es. screenshot del video Sora) al posto del rettangolo
   segnaposto: riempie il riquadro senza deformarsi (object-fit:cover),
   angoli arrotondati ereditati dall'overflow:hidden del contenitore. */
.about-figure__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Citazioni/riferimenti linkati (sia inline dentro un paragrafo, es.
   "[Laba, 2024]", sia come didascalia isolata sotto un'immagine): freccia
   + testo, mai flex/gap — sono testo normale in linea, uno spazio vero
   (non un gap css) separa la freccia dal nome, esattamente come una
   parola qualunque. Sottolineatura nativa solo sul testo (mai sulla
   freccia). In hover la freccia guadagna un outline di 1.5px, stessa
   quantita' delle frecce dell'indice about. */
.about-citation {
  text-decoration: none;
  color: inherit;
}
.about-citation [data-citation-arrow] {
  --outline-width: 0;
  transition: text-shadow 0.2s ease;
}
.about-citation:hover [data-citation-arrow] {
  --outline-width: 1.5px;
}
.about-citation__label {
  text-decoration: underline;
}

/* Variante "didascalia": wrapper (un <p>, non piu' l'<a> stesso) su riga
   propria sotto un'immagine invece che dentro il flusso di un paragrafo
   (es. il riferimento sotto .about-figure). Le parentesi quadre [ ] e il
   link stanno DENTRO questo <p> (mai come testo nudo diretto figlio di
   .about-section, che essendo grid genererebbe per ognuna un item-grid
   anonimo a se' — bug gia' visto: la "]" finiva isolata altrove). */
/* Stessa specificita' del bug gia' visto su research question/note: essendo
   anche lei un <p> dentro .about-section, ".about-section p" (0,1,1, ha
   anche il selettore di tag) vinceva sempre su ".about-citation--caption"
   (0,1,0) per le proprieta' in comune — in particolare il text-indent:64px
   del corpo, che la spostava a destra impedendole di iniziare allineata
   all'immagine sopra. Fix: stesso trucco, secondo selettore di classe per
   alzare la specificita'. Corpo ridotto a 14px (era 16px). */
/* Lista puntata dentro un .about-section (es. "Methods and data" -- i 4
   elementi estratti da ogni caso): stessa tipografia di .about-section p
   (stesse colonne/corpo/interlinea) ma senza il text-indent della prima
   riga, e senza bullet nativi -- il puntino e' letteralmente il
   carattere "↘" scritto nel testo (about.json), coerente con lo stesso
   simbolo usato per i link altrove nella pagina, qui pero' come marcatore
   fisso di elenco, non come freccia interattiva (nessun hover, nessun
   link). Resa SENZA il wrapper <p> (vedi about.njk: i paragrafi che
   iniziano con "<ul" saltano il wrapper) perche' <ul> è un elemento di
   blocco e non può stare dentro un <p>. */
.about-section .about-list {
  grid-column: 1 / 8;
  list-style: none;
  margin: 0 0 48px;
  padding: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(15px, 1.5vw, 20px);
  line-height: 24px;
  letter-spacing: var(--tracking-normal);
  /* Anna (rifinitura): "non solo la prima riga -- tutto quello che è
     dentro l'elenco sta 64px più a dx del resto del testo, senza
     indentation" -- text-indent (tentativo precedente) sposta SOLO la
     prima riga di ognuno; qui invece serve uno spostamento uniforme di
     TUTTO il blocco, comprese eventuali righe successive di un <li> che
     va a capo -- margin-left positivo, non text-indent. */
  margin-left: 64px;
}
.about-list li {
  margin: 0 0 8px;
}
.about-list li:last-child {
  margin-bottom: 0;
}

.about-section .about-citation--caption {
  display: block;
  grid-column: 1 / 8;
  font-family: var(--font-display);
  font-size: 14px;
  line-height: 1.3;
  letter-spacing: var(--tracking-normal);
  text-indent: 0;
  margin-bottom: 64px;
}

@media (max-width: 860px) {
  /* Sotto gli 860px la griglia a 12 colonne (e le due subgrid annidate)
     lascia il posto a un semplice impaginamento a blocco: i grid-column
     diretti sui figli diventano inerti automaticamente, non serve
     azzerarli uno per uno. */
  .about-hero { display: block; }
  .about-layout { display: block; }
  .about-index { position: static; margin-bottom: 32px; }
  .about-sections { display: block; }
  .about-section { display: block; }
  .about-section p { text-indent: 0; }
  .about-section .about-list { margin-left: 0; }
}

/* 8. UTILITY -------------------------------------------------------------------- */
.blob {
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
  z-index: 0;
}

.blob--dots {
  background-image: radial-gradient(currentColor 2px, transparent 2.4px);
  background-size: 14px 14px;
  color: var(--color-accent);
  opacity: 0.55;
}

.blob--soft {
  background: var(--color-accent);
  filter: blur(50px);
  opacity: 0.18;
}

/* Footer sitewide (Anna: "aggiungi un footer per tutto il sito"): altezza
   fissa 136px, sfondo colorato in base al tema della pagina (stessa
   variabile --color-accent già usata per i testi, letta pero' qui
   direttamente da --color-blue/--color-pink per non dipendere da dove
   .theme-blue/.theme-pink viene applicato altrove). Nessun footer è
   presente nel file Figma originale: il layout qui sotto (2 blocchi sulla
   stessa riga, loghi a sx / credito a dx) è una scelta editoriale di
   Anna, non un frame Figma esistente. */
footer.site-footer {
  /* Anna: "riduci l'altezza del footer a altezza contenuti + padding 12px
     sopra e sotto" -- niente piu' altezza fissa (era 100px, prima ancora
     136px): l'altezza segue ora quella reale dei contenuti (i loghi, 40px,
     sono l'elemento piu' alto) piu' il padding verticale qui sotto,
     invece di uno spazio fisso con contenuto centrato dentro.
     Anna (rifinitura successiva): "fai il padding dei contenuti del
     footer sopra e sotto di 24px" -- era 12px. */
  /* Anna: "aggiorna il footer come nel Figma" (nodo 885:2): barra alta
     94px, credito a SINISTRA, loghi a DESTRA. */
  height: 80px;
  padding: 0;
  margin-top: 240px; /* Anna: "riduci lo spazio a 240px" (era 320px, prima 400px) */
  display: flex;
  align-items: center;
}
.theme-blue .site-footer { background: var(--color-blue); }
.theme-pink .site-footer { background: var(--color-pink); }

/* Stessa ricetta di .container/.navbar__inner (max-width + margin:0 auto
   + padding laterale = --page-padding), cosi' i due blocchi si allineano
   esattamente come il titolo/i link della navbar. */
.site-footer__inner {
  width: 100%;
  max-width: var(--page-max-width);
  margin: 0 auto;
  padding: 0 var(--page-padding);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}

/* Blocco 1: i due loghi, allineati al centro sulla stessa riga, 80px di
   distanza tra Politecnico (primo, a sx) e DensityDesign (secondo). */
.site-footer__logos {
  display: flex;
  align-items: center;
  gap: 96px;
  flex-shrink: 0;
}

/* Figma: logo Politecnico 276x47, logo DensityDesign 51x62. */
.site-footer__logo {
  display: block;
  width: auto;
}
.site-footer__logo--polimi { height: 40px; }
.site-footer__logo--densitydesign { height: 34px; } /* Anna: "fai 40 di altezza" (era 62px, poi 52px) */

/* Blocco 2: la frase di credito, sempre bianca (indipendentemente dal
   tema/sfondo sottostante, blu o rosa che sia). */
.site-footer__credit {
  margin: 0;
  width: 354px;
  max-width: 100%;
  color: #f7f7f7;
  font-family: var(--font-display);
  font-size: 14px;
  line-height: 20px;
  letter-spacing: 0.03em;
  text-align: left;
}

@media (max-width: 700px) {
  .navbar__inner { flex-wrap: wrap; gap: 12px; }
  .navbar__links { gap: 12px; font-size: 14px; }

  /* Sotto i 700px il footer passa da riga fissa a due righe impilate
     (stessa logica di .navbar__inner qui sopra): altezza fissa 136px non
     basterebbe piu' a contenere loghi + testo senza sovrapporsi. */
  footer.site-footer { height: auto; }
  .site-footer__inner {
    flex-wrap: wrap;
    justify-content: flex-start;
    padding-top: 24px;
    padding-bottom: 24px;
    gap: 16px;
  }
  .site-footer__credit { text-align: left; }
}

/* 9. TEXT REVEAL (comparsa parola per parola) ----------------------------------
   I testi principali del BODY (mai la navbar, che resta statica) compaiono
   una parola alla volta a ogni caricamento di pagina/refresh: titolo e
   sottotitolo in home e about, frase introduttiva nelle pagine viz. Il
   wrapping delle parole in <span class="reveal-word"> avviene in JS (vedi
   src/js/reveal-text.js), che assegna a ognuna un indice --i usato qui sotto
   per lo sfasamento nel tempo. */
.reveal-word {
  display: inline-block;
  opacity: 0;
  transform: translateY(0.4em);
  filter: blur(var(--reveal-blur, 3px));
  animation: reveal-word var(--reveal-duration, 1s) cubic-bezier(0.22, 0.68, 0.18, 1) forwards;
  animation-delay: calc(var(--i, 0) * 65ms + 60ms + var(--group-delay, 0ms));
}

/* Titolo homepage: leggermente piu' lento e con un pelino di blur in piu'
   rispetto agli altri testi a comparsa parola-per-parola. */
.hero__title {
  --reveal-duration: 1.25s;
  --reveal-blur: 4.5px;
}

/* Anna: "il velo nasconde le animazioni sui testi nelle altre pagine.
   [...] fai in modo che le altre animazioni partano dopo che il velo e'
   andato via. Le animazioni dovrebbero riguardare solo i testi
   introduttivi di sense-making e reacting e il titolo+sottotitolo di
   about" -- senza questo, .reveal-word partiva appena la pagina veniva
   disegnata (ben prima che js/page-loader.js facesse sparire il velo),
   quindi l'animazione finiva gia' mentre era ancora coperta e l'utente
   vedeva solo il testo fermo. Stessa tecnica gia' usata per il titolo
   homepage (sezione 4): animation-play-state:paused blocca anche il
   conteggio del proprio animation-delay, non solo il fotogramma, e
   riparte solo quando <html> riceve "is-loaded" (aggiunta da
   page-loader.js quando il velo sta per sparire). Scelta di proposito
   SOLO per questi elementi, non per .reveal-word in generale, per non
   toccare altri usi futuri del wrapping parola-per-parola. */
.viz-page__title .reveal-word,
.about-hero .reveal-word {
  animation-play-state: paused;
}

html.is-loaded .viz-page__title .reveal-word,
html.is-loaded .about-hero .reveal-word {
  animation-play-state: running;
}

@keyframes reveal-word {
  to {
    opacity: 1;
    transform: translateY(0);
    filter: blur(0);
  }
}

@media (prefers-reduced-motion: reduce) {
  .reveal-word {
    animation: none;
    opacity: 1;
    transform: none;
    filter: none;
  }

  .hero__title .reveal-word,
  .hero__tagline .reveal-word,
  .viz-page__title .reveal-word,
  .about-hero .reveal-word {
    /* Sovrascrive il paused di riposo (vedi sezione 4 per il titolo
       homepage, sezione 9 qui sopra per gli altri): con animazioni
       ridotte niente attesa di .hero--loaded/is-loaded, il testo e'
       subito fermo e visibile (coerente con .reveal-word qui sopra). */
    animation-play-state: paused !important;
  }

  .hero__intro,
  .hero__previews {
    animation: none !important;
    opacity: 1 !important;
  }
}
