/* Costruito da tools/build-css.php — NON si modifica a mano.
   Si lavora in assets/css/sorgenti/, poi: php tools/build-css.php */

/* ======================== 00-tokens.css ======================== */

/* I numeri del sito. Qui non c'e' nessuna regola: solo i valori che tutte le
   altre chiamano per nome. Se una misura compare scritta a mano da qualche
   altra parte, o e' un caso davvero unico o e' una svista.

   Le tre voci tipografiche, e quando si usano:

     graziato corsivo   la voce editoriale: il testo, e i titoli che sono
                        contenuto — titolo del progetto, nome sulla home,
                        statement, titolo di una scheda
     lineare            l'interfaccia: menu, filtri, etichette, titoli di
                        pagina e di sezione. Dritto, neutro, non commenta
     monospaziato       solo numeri e didascalie: 01/16, l'anno, la riga
                        sotto una fotografia. Non sale mai di corpo
*/

:root {
  /* colore */
  --ink: #0c0c0c;
  --paper: #f5f3f0;
  --muted: #6b6864;          /* 5.2:1 sulla carta: e' testo, deve leggersi */
  --line: rgba(12, 12, 12, .12);
  --line-strong: rgba(12, 12, 12, .3);
  --accent: #0000ff;              /* ossido: solo il contorno del fuoco */
  --veil: rgba(12, 12, 12, .68);  /* velo sotto ai testi in overlay: 5.6:1 anche sul bianco */
  --lqip: #e6e4e0;                /* tinta d'attesa, sovrascritta per immagine */

  /* tipografia */
  --font-display: "Alice", "Iowan Old Style", Georgia, serif;
  --font-text: var(--font-display);
  --font-sans: "Archivo", -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-util: "Fragment Mono", ui-monospace, "SF Mono", Menlo, monospace;

  --t--2: .75rem;
  --t--1: .8125rem;
  --t-0: 1.1875rem;          /* il corsivo chiede un corpo piu' generoso */
  --t-1: 1.328rem;
  --t-2: 1.66rem;
  --t-3: 2.074rem;
  --t-4: 2.594rem;
  --t-5: 3.242rem;
  --t-6: 4.052rem;

  --measure: 62ch;

  /* spazi: 4 8 12 20 32 56 96 160 */
  --s1: .25rem;  --s2: .5rem;   --s3: .75rem;  --s4: 1.25rem;
  --s5: 2rem;    --s6: 3.5rem;  --s7: 6rem;    --s8: 10rem;

  --gutter: 24px;                /* respiro ai bordi: fisso, uguale su ogni schermo */

  /* movimento: le durate stanno qui, il JS legge le stesse */
  --d-link: 180ms;
  --d-menu: 240ms;
  --d-out: 260ms;
  --d-panel: 300ms;
  --d-caption: 380ms;
  --d-in: 420ms;
  --d-reveal: 700ms;
  --d-scale: 900ms;
  --d-flip: 620ms;
  --d-slow: 790ms;                          /* gli ingressi grandi */
  --e-out: cubic-bezier(.16, 1, .3, 1);
  --e-slow: cubic-bezier(.77, 0, .175, 1);  /* la curva lenta del reference */
  --e-mid: cubic-bezier(.5, 0, .5, 1);      /* quella del velo, simmetrica */

  /* profondita': tutti gli strati del sito, in un posto solo. Un numero
     scritto a mano dentro un componente e' il modo in cui due cose finiscono
     una sopra l'altra senza che nessuno sappia perche'. */
  --z-panel: 20;         /* la tendina dei figli nel menu */
  --z-wordmark: 15;
  --z-menu: 40;          /* il pannello del menu a tutto schermo */
  --z-toggle: 45;        /* il pulsante che lo apre: sempre sopra al pannello */
  --z-top: 50;           /* l'intestazione */
  --z-nome: 60;          /* il nome del progetto, grande in mezzo allo schermo */
  --z-cursor: 80;
  --z-veil: 90;
  --z-skip: 100;         /* il salto al contenuto: sopra a tutto, sempre */
}

/* I file dei caratteri stanno in assets/fonts: nessuna richiesta a terzi, e il
   sito continua a funzionare senza rete. */
/* Alice ha il solo tondo: nessun corsivo da caricare, e nessuno da fingere —
   vedi 01-base.css. */
@font-face {
  font-family: "Alice"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("../fonts/alice.woff2") format("woff2");
}
@font-face {
  font-family: "Archivo"; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url("../fonts/archivo.woff2") format("woff2");
}
@font-face {
  font-family: "Fragment Mono"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("../fonts/fragment-mono.woff2") format("woff2");
}

/* ======================== 01-base.css ======================== */

/* Il minimo perche' il documento parta dritto: azzeramenti, la pagina, gli
   elementi nudi. Niente che riguardi un componente. */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-text);
  /* Tondo, non corsivo: Alice il corsivo non ce l'ha. Chiederglielo lo stesso
     vuol dire farlo inclinare al browser — una tranciata obliqua sulle stesse
     lettere dritte, che su un graziato si vede subito e si vede male. Se il
     corsivo dev'essere corsivo, serve un carattere che ce l'abbia disegnato. */
  font-style: normal;
  font-size: var(--t-0);
  font-weight: 400;
  line-height: 1.6;
  overflow-x: hidden;             /* nessuno scorrimento laterale, mai */
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4, p, figure, blockquote, dl, dd, ol, ul { margin: 0; }
ul, ol { padding: 0; list-style: none; }

picture { display: block; }

img {
  display: block;
  max-width: 100%;
  height: auto;
  background: var(--lqip);        /* tinta piatta finche' la foto non c'e' */
}

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

button {
  font: inherit; color: inherit; background: none; border: 0;
  padding: 0; cursor: pointer; border-radius: 0;
}

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

.skip {
  position: absolute; left: -9999px;
  background: var(--ink); color: var(--paper);
  padding: var(--s3) var(--s4); z-index: var(--z-skip);
}
.skip:focus { left: var(--gutter); top: var(--s3); }

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* Chi ha chiesto meno movimento lo ottiene su tutto il sito, in un posto solo.
   Il JavaScript legge la stessa preferenza e si comporta di conseguenza. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ======================== 02-tipografia.css ======================== */

/* Le tre voci, e chi parla con quale. Questo file decide il carattere di ogni
   cosa: un componente piu' avanti puo' cambiare il corpo o il colore, ma il
   carattere si sceglie qui. */

/* --- graziato corsivo: i titoli che sono contenuto ---
   Non sono etichette: sono il nome della cosa che si sta guardando. Il nome
   dell'autore e' il primo di questi: in home sta grande in mezzo alle
   fotografie (.wordmark), nell'intestazione e' lo stesso nome scritto piccolo
   (.mark). Stessa voce, corpo diverso. */
.mark,
.project-title,
.index-title,
.card-title,
.pub-title,
.link-big,
.cat h3,
.statement p {
  font-family: var(--font-display);
  font-style: normal;             /* Alice non ha corsivo: vedi 01-base.css */
  font-weight: 400;
  letter-spacing: -.018em;        /* i corpi grandi si stringono */
  line-height: 1;
}

/* --- lineare: l'interfaccia ---
   Menu, comandi, etichette, titoli di pagina e di sezione. Dritto e neutro:
   dice dove sei, non commenta. */
.drop-cima,
.drop-panel,
.vista button,
.page-title,
.section-title,
.eyebrow,
.menu-list > li > a,
.ticks li,
.credits dt,
.project-facts dt,
.foot,
.pub-meta {
  font-family: var(--font-sans);
  font-style: normal;
  font-weight: 400;
}

/* --- monospaziato: numeri e didascalie ---
   L'unica cosa che conta caratteri. Non sale mai di corpo: se una di queste
   diventa grande, ha sbagliato posto. */
.num, figcaption, .card-meta, .yr, .pub-year,
.cursor {
  font-family: var(--font-util);
  font-style: normal;
  font-weight: 400;
  font-variant-numeric: tabular-nums;
  letter-spacing: .02em;
  font-size: var(--t--1);
  color: var(--muted);
}

/* --- le etichettine: lineare, maiuscolo, corpo minimo --- */
.eyebrow, .section-title,
.credits dt, .project-facts dt {
  font-size: var(--t--2);
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--muted);
}

/* --- i corpi --- */
.page-title {
  font-size: clamp(2rem, 4.4vw, var(--t-5));
  letter-spacing: -.02em;
  line-height: 1.05;
  margin-bottom: var(--s6);
}
.lead { font-size: var(--t-1); line-height: 1.35; max-width: 52ch; }
.prose { max-width: var(--measure); }
.prose p + p { margin-top: var(--s4); }
.link-big { font-size: var(--t-3); }
.section-title { display: block; margin-bottom: var(--s4); }

.menu-list > li > a, .drop-panel a, .vista button {
  font-size: var(--t-0);
  letter-spacing: -.005em;
}

/* --- i tre modi di segnare un link ---
   1. il filetto: cresce da sinistra, il colore non cambia. Sta nel menu, dove
      le voci sono la navigazione del sito.
   2. il pallino: un punto prima della parola. Sta sui comandi — le voci del
      filtro, le due viste — dove una voce e' uno stato acceso e non un posto
      dove si va. E' lo stesso punto che nel menu insegue la voce puntata: qui
      non viaggia, si accende.
   3. la tinta: niente segno, il testo passa da chiaro a scuro. Sta dove il
      link e' dentro al discorso, o e' una voce sola in mezzo all'aria. */

/* 1. filetto */
.menu-list a { position: relative; }
.menu-list a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -.16em;
  height: 1px; background: currentColor;
  transform: scaleX(0); transform-origin: left;
  transition: transform var(--d-menu) var(--e-out);
}
.menu-list a:hover::after, .menu-list a:focus-visible::after,
.menu-list a[aria-current]::after {
  transform: scaleX(1);
}

/* 2. pallino: stessa misura del punto del menu, 5px, cosi' il segno e' uno
   solo in tutto il sito. Lo spazio a sinistra c'e' sempre, acceso o spento:
   se no le voci ballerebbero al passaggio del puntatore. */
.link-punto { position: relative; padding-left: 12px; }
.link-punto::before {
  content: ""; position: absolute; left: 0; top: 50%; margin-top: -2.5px;
  width: 5px; height: 5px; border-radius: 3px; background: currentColor;
  opacity: 0; transform: scale(.4);
  transition: opacity var(--d-link) var(--e-out), transform var(--d-link) var(--e-out);
}
.link-punto:hover::before, .link-punto:focus-visible::before,
.link-punto[aria-current]::before, .link-punto[aria-pressed="true"]::before {
  opacity: 1; transform: none;
}

.menu-list a[aria-current] { color: var(--ink); }

/* 3. tinta */
.prose a, .link-big, .foot a, .link-tinta {
  color: var(--muted);
  transition: color var(--d-link) var(--e-out);
}
.prose a:hover, .link-big:hover, .foot a:hover, .link-tinta:hover,
.prose a:focus-visible, .link-big:focus-visible, .foot a:focus-visible,
.link-tinta:focus-visible {
  color: var(--ink);
}

/* ======================== 03-guscio.css ======================== */

/* Il guscio: quel che c'e' su ogni pagina. Intestazione, menu, piede.
   Le regole per lo schermo stretto stanno qui sotto, non in fondo al foglio:
   un componente si legge tutto in un posto solo. */

/* Nessuna misura massima: il contenuto arriva sempre al --gutter, su ogni
   pagina e a ogni larghezza. Dove un testo lungo avrebbe bisogno di una riga
   corta ci pensa la sua misura — .prose, .lead — non un contenitore. */
main { padding: 0 var(--gutter) var(--s8); }

/* --- intestazione ---
   Il marchio sta appoggiato al margine della pagina, lo stesso di tutto il
   resto: la riga in alto comincia dove comincia il contenuto. */

.top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s5);
  /* bassa: 12px sopra e sotto al pulsante, che resta alto 44 per il dito */
  padding: var(--s3) var(--gutter);
  position: relative; z-index: var(--z-top);
}

.mark {
  /* aperto il menu il marchio resta dov'e', sopra alla carta del pannello:
     e' l'unico punto fermo mentre tutto il resto e' cambiato */
  position: relative; z-index: var(--z-toggle);
  /* lo stesso nome della home, alla misura che entra in una riga alta 44px:
     il corpo scende fino a --t-1 e la stretta resta quella dei corpi grandi,
     cosi' anche piccolo si legge come il nome e non come un'etichetta */
  font-size: var(--t-1);
  letter-spacing: -.02em;
  line-height: 1;
  white-space: nowrap;
}

/* --- due menu, un interruttore ---
   In home le voci coprirebbero le fotografie e ruberebbero l'unica cosa che
   conta: li' restano dietro al pulsante, in un pannello. Dentro alle altre
   pagine sono gia' aperte, in riga a destra della firma — chi sta leggendo un
   progetto vuole saltare a un altro senza aprire niente.
   Sullo schermo stretto vince sempre il pannello: quattro voci in riga
   starebbero strette e vicine.
   Il pannello e' la forma di partenza per chiunque abbia JavaScript; le voci
   in riga sono l'eccezione, e valgono solo dentro alle pagine (classe
   .pagina-interna, scritta da PHP) e solo sopra i 48rem. Scritto al contrario
   — riga di partenza, pannello per eccezione — in home il pannello si vedrebbe
   per un istante alla prima passata, prima che la regola lo spenga. */

/* --- il pulsante: tre righe che diventano una X ---
   Quelle di sopra e di sotto si raggiungono al centro e ruotano, quella di
   mezzo sfuma. Compare solo dove c'e' un pannello da aprire; senza
   JavaScript, mai: le voci sono gia' tutte li' e un pulsante che non apre
   niente sarebbe una porta finta. */

.menu-toggle { display: none; }
.js .menu-toggle {
  /* il bersaglio resta 44px, il segno dentro e' piu' piccolo e sta in mezzo */
  display: grid; place-items: center;
  width: 44px; height: 44px;
  position: relative;
  margin-right: calc(var(--s3) * -1);   /* il segno a filo del margine, non il bersaglio */
  margin-left: auto;
  z-index: var(--z-toggle);
}
.menu-toggle .bars, .menu-toggle .bars::before, .menu-toggle .bars::after {
  width: 1.5rem; height: 1px;
  background: var(--ink);
  transition: transform var(--d-menu) var(--e-out), background-color 160ms ease-out;
}
.menu-toggle .bars { position: relative; }
.menu-toggle .bars::before, .menu-toggle .bars::after { content: ""; position: absolute; left: 0; }
.menu-toggle .bars::before { top: -5px; }
.menu-toggle .bars::after { top: 5px; }
/* La riga di mezzo se ne va per prima, poi le altre due si incrociano: un
   gesto solo, ma in quest'ordine si legge come una X che si forma.
   Sparisce col colore e non con opacity: l'opacita' vale per l'elemento
   insieme ai suoi pseudo-elementi, e ::before e ::after sono le altre due
   righe — spegnerla qui spegnerebbe tutta la X. */
.menu-open .menu-toggle .bars { background-color: transparent; }
.menu-open .menu-toggle .bars::before { transform: translateY(5px) rotate(45deg); transition-delay: 80ms; }
.menu-open .menu-toggle .bars::after { transform: translateY(-5px) rotate(-45deg); transition-delay: 80ms; }

/* --- le voci in riga (dentro le pagine, e sempre senza JavaScript) ---
   Non si sottolineano: si accendono, e un punto solo scivola sotto a quella
   puntata. Il punto vive dentro l'elenco — e' un <li> fuori dal flusso — cosi'
   le sue coordinate sono quelle dell'elenco e non serve misurare altro. */

.menu-list {
  position: relative;
  display: flex; gap: var(--s5);
  --punto-modo: riga;          /* lo legge il JS per sapere dove posare il punto */
}
.menu-list > li > a {
  display: flex; align-items: center; min-height: 44px;
  font-size: var(--t--1);
  color: var(--muted);
  transition: color var(--d-link) var(--e-out), opacity var(--d-link) var(--e-out),
              transform var(--d-link) var(--e-out);
}
.menu-list > li > a:hover,
.menu-list > li > a:focus-visible,
.menu-list > li > a[aria-current] { color: var(--ink); }
/* il filetto qui non serve: il segno e' il punto */
.menu-list a::after { content: none; }
.menu-eti, .menu-extra, .menu-punto { display: none; }   /* roba del pannello */

/* il punto: una macchia sola per tutto il menu, in riga come in colonna.
   Il selettore ripete l'elenco perche' nel pannello .js .menu-list > li impone
   width: 100% a tutte le voci: il punto e' un li anche lui, e senza questa
   precisazione diventerebbe una riga nera lunga quanto lo schermo. */
.js .menu-list > li.menu-punto {
  display: block;
  position: absolute; left: 0; top: 0;
  width: 5px; height: 5px; border-radius: 3px;
  background: var(--ink);
  opacity: 0; pointer-events: none;
  /* in viaggio si stira nel verso in cui va e si rimette in forma appena
     arrivato: e' la deformazione che fa leggere la traiettoria invece del
     salto. Le coordinate le scrive il JS, il movimento e' tutto qui. */
  transition: transform 280ms var(--e-out), opacity 160ms linear;
}

/* --- il pannello: carta a tutto schermo, voci in colonna ---
   Solo carta e voci: niente riquadri, niente filetti. Le voci al centro una
   sotto l'altra, i contatti in fondo, piccoli. */

.js .menu {
  position: fixed; inset: 0; z-index: var(--z-menu);
  background: var(--paper);
  padding: var(--s8) var(--gutter) var(--s6);
  display: flex; flex-direction: column; align-items: center;
  /* si scorre quando non ci sta: telefono in orizzontale, o corpo del testo
     ingrandito dal sistema. I margini automatici tengono il gruppo in mezzo
     finche' ci sta. */
  overflow-y: auto;
  overscroll-behavior: contain;
  opacity: 0; visibility: hidden;
  transform: translateY(-8px);
  transition: opacity var(--d-menu) var(--e-out),
              transform var(--d-menu) var(--e-out),
              visibility 0s linear var(--d-menu);
}
.js.menu-open .menu {
  opacity: 1; visibility: visible; transform: none; transition-delay: 0s;
}
.js.menu-open { overflow: hidden; }

.js .menu-list {
  --punto-modo: colonna;       /* il punto va a sinistra della voce, non sotto */
  flex-direction: column; align-items: center;
  gap: var(--s3); text-align: center;
  width: 100%; margin: auto 0 0;
}
.js .menu-list > li:not(.menu-punto) { width: 100%; }
.js .menu-list > li > a {
  justify-content: center;
  font-size: clamp(1.75rem, 5vw, var(--t-4));
  /* una parola sola per riga: l'interlinea del corpo del testo qui non serve,
     e conta perche' il punto si centra sulla riga di testo — con 1.6 la riga
     e' mezza em piu' alta delle lettere, e ogni motore la misura a modo suo.
     A 1 la riga e' le lettere: il punto sta in mezzo alla parola davvero.
     L'aria fra le voci la da' il gap dell'elenco. */
  line-height: 1;
  letter-spacing: -.02em;
  color: var(--ink);
}

/* col puntatore la voce puntata si fa avanti e le altre arretrano: e' l'unico
   modo che ha una fila di sole parole per dire quale sta rispondendo */
@media (hover: hover) {
  .js .menu-list:hover > li > a { opacity: .35; }
  .js .menu-list > li > a:hover { opacity: 1; transform: translateX(.35em); }
}

/* i contatti in fondo al pannello: piccoli, in riga, non chiamano l'attenzione */
.js .menu-eti {
  display: block; margin: var(--s6) auto var(--s4);
  width: 2rem; height: 0;
  border: 0; border-top: 1px solid var(--line-strong);
}
.js .menu-extra {
  display: flex; flex-wrap: wrap; justify-content: center; gap: var(--s2) var(--s4);
  margin-bottom: auto;
  font-family: var(--font-sans); font-style: normal; font-size: var(--t--1);
  color: var(--muted);
}
.js .menu-extra a { color: var(--muted); }
.js .menu-extra a:hover,
.js .menu-extra a:focus-visible { color: var(--ink); }

/* le voci entrano una dopo l'altra, 30ms l'una. Solo in dissolvenza: se
   scorressero anche di qualche pixel, il punto — che si posa misurando dove
   sta la parola — le troverebbe per strada e si fermerebbe dove non sono
   ancora arrivate. Il pannello intero scorre, e quello non da' fastidio:
   porta con se' elenco e voci insieme. */
.js.menu-open .menu-item {
  animation: voce-in 320ms var(--e-out) both;
  animation-delay: calc(var(--i, 0) * 30ms);
}
@keyframes voce-in { from { opacity: 0; } }

/* --- l'eccezione: le voci gia' aperte, dentro alle pagine ---
   Chi sta leggendo un progetto vuole saltare a un altro senza aprire niente.
   Qui il pannello si smonta e torna una riga: il punto scivola sotto alla voce
   invece che di fianco, e i contatti restano al piede, dove uno li cerca. */

@media (min-width: 48.0625rem) {
  .js.pagina-interna .menu-toggle { display: none; }

  .js.pagina-interna .menu {
    position: static; z-index: auto;
    padding: 0; background: none; overflow: visible;
    display: block;
    opacity: 1; visibility: visible; transform: none;
  }
  .js.pagina-interna .menu-list {
    --punto-modo: riga;
    flex-direction: row; align-items: center; justify-content: flex-end;
    gap: var(--s5); width: auto; margin: 0; text-align: left;
  }
  .js.pagina-interna .menu-list > li:not(.menu-punto) { width: auto; }
  .js.pagina-interna .menu-list > li > a {
    justify-content: flex-start;
    min-height: 44px;
    font-size: var(--t--1);
    line-height: 1.6;   /* qui il punto va sotto alla parola: la riga resta com'era */
    letter-spacing: 0;
    color: var(--muted);
  }
  .js.pagina-interna .menu-list > li > a:hover,
  .js.pagina-interna .menu-list > li > a:focus-visible,
  .js.pagina-interna .menu-list > li > a[aria-current] { color: var(--ink); }
  /* in riga le voci sono piccole e vicine: spegnere le altre sarebbe un
     tremolio continuo mentre il puntatore attraversa la barra */
  .js.pagina-interna .menu-list:hover > li > a { opacity: 1; }
  .js.pagina-interna .menu-list > li > a:hover { transform: none; }
  .js.pagina-interna .menu-eti,
  .js.pagina-interna .menu-extra { display: none; }
}

/* --- piede ---
   Una riga sola, allineata alla linea di base. I link qui vanno a tinta e non
   a filetto: sono parole corte in fila fitta, e cinque filetti che crescono
   uno accanto all'altro sono piu' rumore che segno. */

.foot {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: var(--s2) var(--s4);
  padding: var(--s5) var(--gutter) var(--s5);
  /* border-top: 1px solid var(--line); */
  color: var(--muted);
  font-size: var(--t--1);
}
.foot-name { color: var(--ink); }
.foot-sep { opacity: .4; }
/* l'anno si stacca dal gruppo dei contatti e va a fondo riga */
.foot-coda { margin-left: auto; font-family: var(--font-util); font-size: var(--t--2); }

/* --- schermo stretto --- */

@media (max-width: 48rem) {
  /* senza JavaScript le voci vanno a capo invece di uscire dallo schermo */
  html:not(.js) .top { flex-wrap: wrap; }
  html:not(.js) .menu { width: 100%; }
  .menu-list { flex-wrap: wrap; gap: var(--s4); }

  /* al piede l'allineamento a base non regge piu': qui sotto i link diventano
     alti 44px per il dito, e accanto a parole senza riquadro le due cose si
     posano a quote diverse. Allora il piede va a righe: il nome sulla sua,
     i contatti tutti in fila, l'anno in fondo. Il punto fra nome e posta non
     serve piu' — a separarli c'e' l'andare a capo. */
  .foot { align-items: center; justify-content: center; gap: 0 var(--s4); }
  .foot-name, .foot-coda { flex-basis: 100%; text-align: center; }
  .foot-sep { display: none; }
  .foot-coda { margin: var(--s3) 0 0; }

  /* col dito serve superficie: nessun bersaglio sotto i 44px */
  .menu-list > li > a, .drop-panel a, .foot a {
    min-height: 44px; display: flex; align-items: center;
  }
  .foot a { justify-content: center; }
}

/* ======================== 04-componenti.css ======================== */

/* I pezzi che si ripetono in piu' pagine. Se una cosa la usa una pagina sola,
   sta nel file di quella pagina, non qui. */

/* --- le griglie ---
   Tutte e tre sono dodici colonne che partono in alto: cambia solo quanto
   respirano fra una riga e l'altra. Erano tre blocchi identici copiati. */

.grid, .grid-loose, .canvas-plane {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  align-items: start;
}
.grid { gap: var(--s7) var(--s5); }
.grid-loose { gap: var(--s6) var(--s5); }
.canvas-plane { gap: var(--s8) var(--s5); }

/* le campate seguono il formato della fotografia, e i buchi che restano sono
   il ritmo della pagina: nessun riempimento forzato. Nell'indice le
   fotografie sono grandi — due per riga al massimo, spesso una sola: e' il
   lavoro che si guarda, non l'elenco che si scorre. */
.grid .card { grid-column: span 7; }
.grid .card-landscape { grid-column: span 8; }
.grid .card-portrait  { grid-column: span 5; }
.grid .card-square    { grid-column: span 6; }
.grid .card:nth-child(even) { grid-column-end: -1; }   /* le pari a destra: la riga respira in mezzo */
.grid-item { grid-column: span 4; }
.grid-item.is-landscape { grid-column: span 6; }
.grid-item.is-portrait { grid-column: span 3; }

/* sfalsature: rompono la riga senza spostare niente in orizzontale */
.grid > :nth-child(3n + 2) { margin-top: var(--s7); }
.grid > :nth-child(4n + 3) { margin-top: var(--s5); }
.grid > :nth-child(6n + 5) { margin-top: var(--s8); }
.grid-loose > :nth-child(2n) { margin-top: var(--s6); }

.empty { color: var(--muted); grid-column: 1 / -1; }

/* --- la scheda di un progetto ---
   La fotografia e' tutta la scheda: nell'indice non c'e' altro, ne' anno ne'
   committente. Passando sopra a una fotografia il nome del progetto compare
   in mezzo allo schermo — non sopra alla sua fotografia: al centro, sempre
   nello stesso punto, come il nome in home. Sono due gesti gia' del sito
   messi insieme: la parola grande al centro (.wordmark) e la cosa che si
   rivela passandoci sopra (.rivela). */

.card a { display: block; }

.card-over {
  position: fixed; inset: 0;
  display: grid; place-content: center;
  padding: var(--gutter);
  z-index: var(--z-nome);
  pointer-events: none;      /* il nome non deve mai rubare il puntatore alla fotografia */
  text-align: center;
}
.card-title {
  /* la misura del nome in home: e' la stessa parola grande, in un altro momento */
  font-size: clamp(2.25rem, 5.5vw, 5.125rem);
  line-height: 1; letter-spacing: -.02em;
}
.card.is-off, .pub.is-off { display: none; }

/* --- il cambio di vista ---
   Griglia ed elenco sono la stessa marcatura disposta in due modi, e il cambio
   e' istantaneo: la disposizione nuova c'e' al fotogramma dopo il clic. Il
   movimento di questo gesto sta tutto nel comando — la casella che scivola
   sotto ai due segni — che e' quel che si stava guardando quando si e' premuto.

   Istantaneo va detto, pero'. Non basta non animare niente: passando a elenco
   le copertine vanno a opacita' zero e i nomi a opacita' uno, e quei due
   valori hanno gia' la loro transizione addosso perche' servono all'hover
   della riga. Nessuno l'aveva chiesta al cambio vista, ma il browser non sa
   distinguere: cambia il valore, parte la transizione. Erano quelle le due
   dissolvenze da 380ms che si vedevano.
   Si spengono per il tempo di un ricalcolo, e basta: la classe la mette e la
   toglie il JavaScript intorno al cambio, dentro allo stesso fotogramma. */
.grid.is-fermo, .grid.is-fermo *, .grid.is-fermo *::before, .grid.is-fermo *::after {
  transition: none !important;
  animation: none !important;
}

/* --- la vista a elenco ---
   Una riga per progetto: anno, nome, categoria. Il nome e' il nome, non
   un'etichetta, quindi resta nella voce del graziato. Passando su una riga le
   altre arretrano — e' lo stesso gesto delle voci del menu — e la copertina
   compare accanto al nome.

   Lo stesso elenco lo usano le pubblicazioni (07-pagine.css): cambiano le
   informazioni — anno, lavoro, testata e tipo — non la forma. Le regole sono
   qui una volta sola, coi due selettori appaiati: se si sposta una colonna si
   spostano tutte e due le pagine, che e' il punto. */

/* anno e categoria vivono solo nell'elenco */
.card-year, .card-cat { display: none; }
.grid.is-list .card-year, .grid.is-list .card-cat { display: block; }

.grid.is-list { display: block; border-top: 1px solid var(--line); }
.grid.is-list .card,
.grid.is-list .card-landscape,
.grid.is-list .card-portrait,
.grid.is-list .card-square { grid-column: auto; margin-top: 0; border-bottom: 1px solid var(--line); }

.grid.is-list .card a,
.pub > :first-child {
  display: grid;
  /* il nome sta al centro della pagina, sempre nello stesso punto — come nella
     vista a fotografie. Le due colonne ai lati sono uguali, quindi quella di
     mezzo cade in mezzo: l'anno si appoggia al margine sinistro, la categoria
     a quello destro, e scorrendo l'elenco solo i nomi restano incolonnati. */
  grid-template-columns: 1fr auto 1fr;
  align-items: center; gap: var(--s4);
  min-height: 68px; padding: var(--s4) 0;
  position: relative;
  transition: opacity var(--d-link) var(--e-out), transform var(--d-menu) var(--e-out);
}
.grid.is-list .card-over { position: static; display: contents; opacity: 1; transform: none; }
/* tutti e tre sulla prima riga, detto a mano: nella marcatura il nome viene
   prima dell'anno, e la disposizione automatica non torna mai indietro — senza
   questo, il nome si prende la riga uno e anno e categoria finiscono in una
   riga sotto. E' quello che li faceva stare piu' in basso del nome. */
.grid.is-list .card-year,
.grid.is-list .card-title,
.grid.is-list .card-cat,
.pub-year, .pub-title, .pub-meta { grid-row: 1; }

.grid.is-list .card-year,
.pub-year { grid-column: 1; justify-self: start; font-family: var(--font-util); font-style: normal; font-size: var(--t--2); color: var(--muted); }
.grid.is-list .card-title,
.pub-title { grid-column: 2; text-align: center; font-size: clamp(var(--t-2), 2.6vw, var(--t-4)); }
.grid.is-list .card-cat,
.pub-meta { grid-column: 3; justify-self: end; font-family: var(--font-sans); font-style: normal; font-size: var(--t--1); color: var(--muted); }

/* la copertina compare accanto al nome, non in un angolo lontano: e' assoluta
   dentro alla terza colonna, quindi sta dove finisce il nome di quella riga e
   segue riga per riga. Assoluta dentro a una griglia significa dentro alla sua
   campata: non alza la riga e non sposta niente. E' la stessa immagine della
   griglia, non una in piu' da scaricare. */
.grid.is-list .card picture,
.pub-thumb {
  position: absolute; grid-column: 3; grid-row: 1;
  left: 0; top: 50%;   /* subito dopo il nome, dentro alla campata di destra */
  width: 12rem; z-index: 5; pointer-events: none;
  opacity: 0; transform: translate(-6px, -50%);
  transition: opacity var(--d-caption) var(--e-out), transform var(--d-caption) var(--e-out);
}

@media (hover: hover) {
  /* le altre righe arretrano, quella puntata resta e si fa avanti di mezza
     lettera: il movimento dice quale riga risponderebbe al clic */
  .grid.is-list:hover .card a,
  .pub-list:hover .pub > :first-child { opacity: .35; }
  /* .pub-list davanti non e' decorazione: senza, la regola che spegne le
     righe pesa piu' di questa e si spegne anche quella puntata */
  .grid.is-list .card a:hover,
  .pub-list .pub > :first-child:hover { opacity: 1; transform: translateX(.35em); }
  .grid.is-list .card a:hover picture,
  .grid.is-list .card a:focus-visible picture,
  /* :first-child e non a: le pubblicazioni senza indirizzo sono uno <span>,
     e anche loro hanno una copertina da mostrare */
  .pub-list .pub > :first-child:hover .pub-thumb,
  .pub-list .pub > :first-child:focus-visible .pub-thumb { opacity: 1; transform: translate(0, -50%); }
}

/* in elenco, su schermo stretto: niente copertina ferma (non c'e' il lato
   dove metterla) e niente categoria, che e' la colonna piu' rinunciabile */
@media (max-width: 64rem) {
  .grid.is-list .card picture,
  .pub-thumb { display: none; }
}
/* stretto: la colonna di mezzo non ha piu' aria ai lati da spartire, e il
   nome centrato diventa un titolo che balla. Torna in fila a sinistra. */
@media (max-width: 48rem) {
  .grid.is-list .card a,
  .pub > :first-child { grid-template-columns: 3.5rem 1fr; gap: var(--s3); }
  .grid.is-list .card-title,
  .pub-title { grid-column: 2; text-align: left; }
  .grid.is-list .card-cat { display: none; }
  /* nelle pubblicazioni testata e tipo non si buttano via: vanno sotto al
     lavoro, nella stessa colonna */
  .pub-meta { grid-column: 2; grid-row: 2; justify-self: start; }
}


/* col dito l'hover non esiste, e un nome fisso al centro dello schermo non
   avrebbe modo di comparire: torna sotto alla sua fotografia, e ci resta */
/* Sullo schermo stretto non si passa sopra a niente, ma si scorre: il nome lo
   accende la fotografia piu' vicina alla mezzeria. Resta al centro, dov'e' col
   puntatore — stesso posto, acceso da un altro gesto.

   La soglia e' la larghezza e non (hover: none): un portatile con la finestra
   stretta dichiara comunque di avere un puntatore, e li' non si vedeva niente.
   La mancanza di hover resta come seconda porta, per i telefoni larghi.

   .card-over.rivela e non .card-over: il nome indossa anche .rivela, che piu'
   avanti nel foglio lo mette a opacita' zero, e a parita' di peso vince chi
   viene dopo. */
@media (max-width: 48rem), (hover: none) {
  /* .nome-acceso lo mette e lo toglie il JavaScript: acceso mentre si scorre,
     spento dopo un momento di quiete. Un nome che resta a pagina ferma diventa
     una scritta sopra a una fotografia, e questa pagina e' fatta per non
     averne. */
  .grid.nome-acceso:not(.is-list) .card.is-mid .card-over.rivela {
    opacity: 1; transform: none;
  }

  /* senza JavaScript nessuno accende niente: il nome torna sotto alla sua
     fotografia e ci resta */
  html:not(.js) .card-over.rivela {
    position: static; opacity: 1; transform: none;
    display: block; padding: var(--s3) 0 0; text-align: left;
  }
  html:not(.js) .card-title { font-size: var(--t-2); }
}

/* --- si rivela ---
   Tre punti del sito mostrano una fotografia quando ci passi sopra o quando
   scegli una voce: l'anteprima negli indici, la copertina nell'elenco delle
   pubblicazioni, il progetto che viene dopo. Erano tre regole gemelle con tre
   nomi diversi. Il movimento sta qui; dove si posiziona lo dice il
   componente. */

.rivela {
  opacity: 0;
  transform: translateY(6px);
  transition: opacity var(--d-caption) var(--e-out), transform var(--d-caption) var(--e-out);
}
.rivela.is-on,
.rivela-qui:focus-within .rivela {
  opacity: 1;
  transform: none;
}
/* l'hover sta dentro alla sua condizione: col dito non esiste un "passarci
   sopra", ma il tocco lascia lo stato hover appiccicato alla cosa toccata —
   e senza questa condizione bastava sfiorare una fotografia per accendere il
   nome e lasciarcelo finche' non si toccava altrove */
@media (hover: hover) {
  .rivela-qui:hover .rivela { opacity: 1; transform: none; }
}

/* --- il puntatore che diventa didascalia (elemento firma) --- */

.cursor {
  position: fixed; top: 0; left: 0; z-index: var(--z-cursor);
  margin: 14px 0 0 16px;
  padding: .3rem .5rem;
  background: var(--veil); color: var(--paper);
  font-size: var(--t--2);
  white-space: nowrap; pointer-events: none;
  opacity: 0;
  transition: opacity 200ms var(--e-out);
}
.cursor.is-on { opacity: 1; will-change: transform; }
.cursor-on [data-cursor], .cursor-on [data-cursor] * { cursor: none; }

/* --- il velo: ingresso e uscita di pagina ---
   Il nodo lo crea il JavaScript. Senza, non c'e' nessun velo da togliere e la
   pagina e' semplicemente li'. */

.veil {
  position: fixed; inset: 0; z-index: var(--z-veil);
  pointer-events: none;
  background: var(--paper);
  backdrop-filter: blur(12px);
  opacity: 1;
  transition: opacity var(--d-in) var(--e-mid), backdrop-filter var(--d-in) var(--e-mid);
}
.veil.is-off { opacity: 0; backdrop-filter: blur(0px); }

/* --- il passaggio fra due pagine ---
   Lo fa il browser: fotografa quella che si lascia e la dissolve su quella che
   arriva, dentro alla navigazione vera. Non c'e' niente da sincronizzare a
   mano, ed e' per questo che e' pulito: i due fotogrammi sono quelli veri.
   Dove non c'e' (browser vecchi) resta il velo di transitions.js, che non
   ha niente da dissolvere e quindi si stende e basta.
   Dura come il velo di prima — 260ms, la stessa curva simmetrica: il passaggio
   e' cambiato, il tempo del sito no. */

@view-transition { navigation: auto; }

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 260ms;
  animation-timing-function: var(--e-mid);
}

/* Nessun elemento ha un nome suo: passa tutta la pagina insieme. Dare un nome
   al marchio o al piede li farebbe riconoscere e spostare da una posizione
   all'altra — ma si clicca anche stando a meta' pagina, e li' quei due sono
   fuori dallo schermo: si vedrebbero arrivare da fuori. Una dissolvenza sola
   e' piu' quieta e non ha casi storti. */

/* Chi ha chiesto meno movimento cambia pagina e basta. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*) { animation: none; }
}


/* --- il testo che entra una riga per volta ---
   Le righe le costruisce il JS misurando quelle vere. Finche' non l'ha fatto
   il paragrafo e' un paragrafo normale, e se il modulo non parte resta tale. */

.lines .line { display: block; perspective: 1000px; }
.lines .line__inner {
  display: block;
  transform-style: preserve-3d;
  transform-origin: center center;
  backface-visibility: hidden;
  transform: translateY(50%) rotateX(-40deg);
  opacity: 0;
  transition: transform var(--d-slow) var(--e-slow), opacity var(--d-slow) var(--e-slow);
  transition-delay: calc(var(--i, 0) * 15ms);
}
.lines.is-on .line__inner { transform: none; opacity: 1; }

/* --- fotografie che entrano in campo ---
   La classe la mette il JS, mai il CSS: se il modulo non partisse, le foto
   devono restare visibili invece di sparire. */

img.to-reveal {
  opacity: 0; transform: scale(1.03);
  transition: opacity var(--d-reveal) var(--e-out), transform var(--d-scale) var(--e-out);
}
img.to-reveal.is-revealed { opacity: 1; transform: none; }

/* --- schermo stretto --- */

@media (max-width: 64rem) {
  .grid .card, .grid .card-landscape, .grid .card-portrait, .grid .card-square { grid-column: span 6; }
  .grid > * { margin-top: 0 !important; }
  .grid-loose { grid-template-columns: 1fr; }
  .grid-item, .grid-item.is-landscape, .grid-item.is-portrait { grid-column: 1 / -1; margin-top: 0; }
}

@media (max-width: 48rem) {
  .grid { grid-template-columns: 1fr; gap: var(--s6); }
  .grid .card, .grid .card-landscape, .grid .card-portrait, .grid .card-square { grid-column: 1 / -1; }
}

/* ======================== 05-home.css ======================== */

/* La home, nei suoi due stati:
     senza JavaScript   una griglia di fotografie, leggibile e indicizzabile
     con JavaScript     un piano piu' grande della finestra, che si trascina
   Il primo e' la marcatura che arriva da PHP; il secondo lo accende
   gallery-canvas.js aggiungendo una classe. Vale uguale col mouse e col dito:
   i pointer event non fanno differenza, quindi non la fa neanche la home. */

.canvas { margin-top: var(--s6); }
.tile { grid-column: span 6; position: relative; display: block; }
.tile:nth-child(3n + 1) { grid-column: span 7; }
.tile:nth-child(3n + 2) { grid-column: 9 / span 4; margin-top: var(--s7); }
.tile:nth-child(3n + 3) { grid-column: 2 / span 5; }

/* --- il piano da trascinare (scrivania) ---
   Il piano si ripete su tutti e due gli assi; le posizioni le decide il JS,
   che possiede solo transform e opacity. */

/* Il piano si prende tutta la finestra e l'intestazione gli sta sopra, senza
   rubargli altezza: restano le fotografie e il pulsante del menu. */
.canvas-immersive, .canvas-immersive body { height: 100%; overflow: hidden; }
.canvas-immersive main { padding: 0; }
.canvas-immersive .canvas.canvas-on { height: 100svh; margin: 0; }
.canvas-immersive .top {
  position: fixed; top: 0; left: 0; right: 0; z-index: var(--z-top);
}
/* il piede sparisce solo quando c'e' il piano: i contatti restano a un clic,
   nella voce Info del menu */
.canvas-immersive .foot { display: none; }

.canvas.canvas-on {
  position: relative;
  background: var(--paper);
  height: calc(100svh - 8rem);
  min-height: 30rem;
  margin: 0 calc(var(--gutter) * -1);
  overflow: hidden;
  cursor: grab;
  touch-action: none;
  user-select: none;
}
.canvas.canvas-on:active { cursor: grabbing; }
.canvas.canvas-on .canvas-plane { display: block; position: absolute; inset: 0; }
.canvas.canvas-on .tile {
  position: absolute; top: 0; left: 0;
  grid-column: auto;
  margin: 0;
  /* will-change lo accende e lo spegne il JS: acceso mentre il piano si
     muove, spento appena si ferma. Permanente sarebbe memoria buttata via. */
}
/* L'ingresso lo guida il JS sulla <picture>: le fotografie partono raccolte al
   centro e raggiungono il loro posto, ognuna col suo tempo. */
.canvas.canvas-on .tile picture { backface-visibility: hidden; }
/* la larghezza della tessera la scrive il JS: l'immagine la riempie esatta,
   se no un ritaglio piu' stretto lascia un margine di lato */
.canvas.canvas-on .tile img {
  width: 100%;
  /* Il gesto sulla home e' trascinare il piano, non la fotografia. Il
     trascinamento nativo dell'immagine parte da solo e si porta via il gesto:
     pointer-events lo spegne alla radice (gli ascolti stanno sul piano, non
     qui), -webkit-user-drag copre Safari, dove il primo non basta. */
  pointer-events: none;
  -webkit-user-drag: none;
  user-select: none;
}

/* --- il nome, fermo al centro del piano ---
   Inchiostro pieno, senza niente dietro. Niente mix-blend-mode: su Chromium,
   appena le tessere finiscono su livelli compositi propri, la differenza si
   calcola contro il vuoto e il nome sparisce. */
.wordmark {
  display: none;
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  z-index: var(--z-wordmark); pointer-events: none;
  width: max-content; max-width: 72%;
  margin: 0;
  font-size: clamp(2.25rem, 5.5vw, 5.125rem);
  line-height: 1; letter-spacing: -.02em; text-align: center;
  perspective: 1000px;
  transition: opacity var(--d-caption) var(--e-out);
}
.canvas-on .wordmark { display: block; }
.canvas-immersive .mark { visibility: hidden; }   /* il nome grande al centro basta */
.wordmark span {
  display: block;
  color: var(--ink);
  transform-style: preserve-3d;
  transform: translateY(50%) rotateX(-40deg);
  filter: blur(12px);
  opacity: 0;
  transition: transform var(--d-slow) var(--e-slow) .3s,
              filter var(--d-slow) var(--e-slow) .3s,
              opacity var(--d-slow) var(--e-slow) .3s;
}
.canvas-on.is-in .wordmark span { transform: none; filter: blur(0); opacity: 1; }

/* --- schermo stretto: la griglia senza JavaScript diventa una colonna --- */

@media (max-width: 64rem) {
  .canvas-plane { gap: var(--s7); }
  .tile, .tile:nth-child(3n + 1), .tile:nth-child(3n + 2), .tile:nth-child(3n + 3) {
    grid-column: 1 / -1; margin-top: 0;
  }
}

/* ======================== 06-progetto.css ======================== */

/* La pagina di un progetto: la galleria e basta.

   Nessun testo oltre all'intestazione e al piede, e l'altezza bloccata a
   quella dello schermo: la pagina non scorre, mai. Una fotografia per volta al
   centro, e a lato la minimappa — la fila delle miniature che scorre e si
   incurva, con l'onda centrata su quella che si sta guardando.

   Le tre fasce le tiene il <body>: intestazione, contenuto, piede. Solo il
   contenuto si allarga. */

.page-progetto {
  /* dvh e non vh: sul telefono la barra del browser entra ed esce, e con vh
     il piede finirebbe sotto di essa */
  height: 100dvh;
  overflow: hidden;
  display: flex; flex-direction: column;
}
.page-progetto main {
  flex: 1; min-height: 0;
  display: flex;
  padding-bottom: 0;
}
.page-progetto .foot {
  /* il piede resta, ma qui non puo' prendersi un terzo di schermo. Il filetto
     non si rimette: se il piede non ce l'ha nel resto del sito, non ce l'ha
     nemmeno qui. */
  padding-block: var(--s3);
}

.project { flex: 1; min-height: 0; display: flex; }

.viewer {
  flex: 1; min-height: 0;
  display: grid;
  grid-template-columns: 150px minmax(0, 1fr);
  gap: var(--s6);
  /* niente align-items: le due colonne devono tirarsi su tutta la fascia. Con
     center prenderebbero l'altezza del contenuto, e l'altezza in percentuale
     di quel che sta dentro non avrebbe piu' niente a cui riferirsi. */
}

/* --- la minimappa ---
   Una colonna alta quanto la fascia, che non scorre col dito: la muove l'onda.
   Le miniature stanno in fila dentro, ancorate al centro, e la fila si sposta
   di un passo per fotografia. Quel che esce dai bordi e' tagliato: e' una
   finestra su una fila piu' lunga, ed e' quello che le da' il senso di mappa. */

.mini {
  position: relative; height: 100%;
  overflow: hidden;
  /* ai due capi la fila sfuma invece di essere tranciata di netto */
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 12%, #000 88%, transparent);
          mask-image: linear-gradient(to bottom, transparent, #000 12%, #000 88%, transparent);
}
.mini ol {
  position: absolute; top: 50%; left: 0;
  display: flex; flex-direction: column;
  /* respiro fra le miniature: al colmo dell'arco crescono, e con un respiro
     stretto la piu' grande finirebbe sopra alle vicine */
  gap: var(--s4);
  /* il centro della miniatura attiva lo porta a meta' fascia il JavaScript:
     qui l'ancora e' il bordo, e il mezzo passo non si puo' piu' scrivere a
     mano perche' ogni miniatura ha la sua altezza */
  will-change: transform;
}
.mini li {
  /* mai schiacciate: in riga sedici miniature non ci stanno, e senza questo il
     flex le assottiglia invece di lasciarle uscire dalla finestra */
  flex: 0 0 auto;
  will-change: transform, opacity;
  transform-origin: left center;
}
.mini a { display: block; }
/* ogni miniatura tiene il suo formato: una verticale e' alta, una orizzontale
   e' bassa, e la fila ne racconta il ritmo. Il conto dell'onda non ha piu' un
   passo solo, e infatti il JavaScript misura ogni centro. */
.mini img { width: 88px; height: auto; }

/* --- la fotografia ---
   Una per volta, tutte impilate nello stesso posto. Cambia subito: chi scorre
   sta cercando, e cercare vuol dire vedere adesso quel che si e' scelto. Il
   solo velo e' sull'uscita, 90ms, che basta perche' il cambio non sia uno
   strappo e non tanto da farlo aspettare. */

.stage { position: relative; height: 100%; min-width: 0; }
.frame {
  position: absolute; inset: 0; margin: 0;
  display: flex; align-items: center; justify-content: center;
}
.frame picture { display: block; width: 100%; height: 100%; }
.frame img {
  /* il riquadro e' tutta la fascia, la fotografia ci sta dentro in proporzione:
     non esce mai sopra l'intestazione ne' sotto il piede, e non viene mai
     tagliata. Con width/height automatici il browser si fermerebbe invece alla
     misura del ritaglio scelto. */
  width: 100%; height: 100%;
  object-fit: contain;
  /* il riquadro e' piu' grande della fotografia quasi sempre: la tinta
     d'attesa dipingerebbe due bande grigie ai suoi lati */
  background: none;
}

/* senza JavaScript le fotografie stanno in colonna, una dopo l'altra: la
   pagina resta una pagina */
.viewer-on .frame { opacity: 0; pointer-events: none; transition: opacity 90ms linear; }
.viewer-on .frame.is-on { opacity: 1; pointer-events: auto; }
/* html:not(.js) e non :not(.js): la classe la porta <html>, e un :not() nudo
   combacerebbe con qualunque antenato che non ce l'ha — body, main, l'articolo
   — cioe' sempre. */
html:not(.js) .page-progetto,
html:not(.js) .page-progetto main { height: auto; overflow: visible; }
html:not(.js) .stage { height: auto; }
html:not(.js) .frame { position: static; margin-bottom: var(--s6); }

/* la fotografia si sfoglia trascinando: col dito il gesto e' nostro (la pagina
   non scorre, non c'e' niente da lasciare al sistema), col puntatore il cursore
   lo dice prima che si provi */
.viewer-on .stage { touch-action: none; }
@media (hover: hover) {
  .viewer-on .stage { cursor: grab; }
  .viewer-on .stage:active { cursor: grabbing; }
}

/* --- schermo stretto ---
   Uguale, girato: la minimappa passa in basso e diventa orizzontale, e l'onda
   sporge verso l'alto — verso la fotografia, non fuori dallo schermo. */

@media (max-width: 48rem) {
  .viewer {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr) auto;
    gap: var(--s4);
  }
  /* nella marcatura la minimappa viene prima — le e' utile a chi legge con
     altro, che la incontra subito. Qui va sotto: si dice a mano, senza
     toccare l'ordine vero. */
  .stage { grid-row: 1; }
  .mini { grid-row: 2; }
  .mini {
    height: 128px; width: 100%;   /* la piu' alta e' una verticale: 90 di miniatura, piu' la sporgenza */
    -webkit-mask-image: linear-gradient(to right, transparent, #000 10%, #000 90%, transparent);
            mask-image: linear-gradient(to right, transparent, #000 10%, #000 90%, transparent);
  }
  .mini ol {
    top: auto; bottom: 0; left: 50%;
    flex-direction: row; align-items: flex-end; gap: var(--s3);
    margin-top: 0;
  }
  .mini li { transform-origin: center bottom; }
  .mini img { width: 60px; height: auto; }

  /* su uno schermo stretto il piede andava a capo tre volte e si mangiava la
     fascia della fotografia: qui sta su una riga fitta, e l'altezza torna
     dov'era destinata */
  .page-progetto .foot {
    font-size: var(--t--2);
    gap: var(--s1) var(--s3);
    padding-block: var(--s2);
  }
}

/* ======================== 07-pagine.css ======================== */

/* Le pagine che hanno pezzi tutti loro: servizi, indice progetti,
   pubblicazioni, info, 404. Ognuna con le sue regole per lo schermo stretto,
   in fondo alla propria sezione. */

/* --- l'attacco di ogni pagina ---
   Su progetti e pubblicazioni la distanza fra la barra in alto e il contenuto
   la tiene la riga dei comandi (.index-head, --s7 sotto). Dove quella riga non
   c'e' — servizi, la scheda di un servizio, info, 404 — la stessa aria la mette
   la pagina, cosi' l'attacco e' lo stesso dappertutto. */
.servizi, .service, .info, .gone { padding-top: var(--s7); }

/* ============================== servizi ============================== */

.service-head { margin-bottom: var(--s6); }
.eyebrow { display: block; margin-bottom: var(--s4); }

.cat {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--s6);
  align-items: start;
  padding: var(--s6) 0;
  border-top: 1px solid var(--line);
}
.cat h3 { font-size: var(--t-3); letter-spacing: -.02em; }
.cat p { margin-top: var(--s3); color: var(--muted); max-width: 46ch; }
.ticks { margin-top: var(--s4); display: grid; gap: var(--s2); }
.ticks li { padding-left: var(--s4); position: relative; font-size: var(--t--1); }
.ticks li::before { content: "—"; position: absolute; left: 0; color: var(--muted); }

.service-cats, .service-gallery { margin-top: var(--s7); }

@media (max-width: 48rem) {
  .cat { grid-template-columns: 1fr; gap: var(--s4); }
}

/* ========================= indice dei servizi =========================
   A sinistra il servizio col suo testo, a destra i nomi delle categorie
   appoggiati in fondo al blocco, uno sotto l'altro con la freccia. Nessuna
   fotografia e nessun JavaScript: e' un indice, e un indice si legge.

*/

.index-head { margin-bottom: var(--s7); }
/* in una variabile perche' la colonna delle categorie ci si allinea sopra */
:root { --t-index: clamp(2rem, 4.5vw, var(--t-5)); }
.index-title { display: block; font-size: var(--t-index); }

.serv {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--s5);
  padding: var(--s6) 0; border-top: 1px solid var(--line);
}
.serv:first-child { padding-top: 0; border-top: 0; }
.serv-testo { max-width: 46ch; margin-top: var(--s4); font-size: var(--t-0); color: var(--muted); }
/* .plain porta con se' --t--1, che va bene per un elenco di note: queste
   invece sono le categorie del servizio, la seconda colonna della pagina.
   Corpo del testo, come il nome che le raccoglie. */
/* Il primo filetto cade sulla base del titolo, non in cima alla riga: e'
   quella la linea che l'occhio segue attraverso la pagina.
   Alice ha ascendente .909 e discendente .234; con line-height 1 il
   mezzo-interlinea e' (1 - 1.143) / 2 = -.0715, quindi la base sta a .8375 del
   corpo sotto al bordo alto della riga — .84, come veniva con Instrument
   Serif: due caratteri diversi, stessa base per un pelo.
   ponytail: fattore scritto a mano — cambiando carattere del titolo va
   rimisurato (hhea ascent/descent dal woff2), non c'e' un modo in CSS di
   leggere le metriche. */
.serv-voci.plain {
  align-self: start;
  margin-top: calc(var(--t-index) * .84);
  font-size: var(--t-0);
}
.serv-voci li { border-top: 1px solid var(--line); }
.serv-voci a {
  display: flex; justify-content: space-between; gap: var(--s3); align-items: baseline;
  padding: var(--s3) 0;
  transition: padding-left var(--d-link) var(--e-out);
}
.serv-voci .freccia { font-family: var(--font-util); font-style: normal; color: var(--muted); }
/* senza pagina di dettaglio: stessa riga, stessa altezza, ma spenta */
.serv-voce-ferma { display: block; padding: var(--s3) 0; color: var(--muted); }
/* la voce puntata si scosta di mezza lettera: e' il gesto degli elenchi del
   sito, qui su una riga che non ha una copertina da mostrare */
@media (hover: hover) { .serv-voci a:hover { padding-left: .35em; } }

@media (max-width: 64rem) {
  .serv { grid-template-columns: 1fr; gap: var(--s4); }
  /* una colonna sola: non c'e' piu' nessun titolo di fianco a cui allinearsi */
  .serv-voci.plain { margin-top: 0; }
}

/* --- la testa dell'indice progetti: filtro a sinistra, viste a destra --- */

.index-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s4); flex-wrap: wrap;
}

/* --- il filtro: una tendina ---
   Chiusa e' due parole: che cosa fa ("Categoria") e a che punto sta ("Tutti").
   La seconda e' quella che si legge, quindi e' quella in --ink. */

.filtro { position: relative; }
.drop { position: relative; }
.drop-cima {
  display: inline-flex; align-items: center; gap: .55em;
  min-height: 44px;                      /* il dito ha di che prendere */
  cursor: pointer; list-style: none; user-select: none;
  font-size: var(--t--1);
}
.drop-cima::-webkit-details-marker { display: none; }
.drop-eti { color: var(--muted); }
.drop-scelta { color: var(--ink); transition: opacity var(--d-link) var(--e-out); }
.drop-cima:hover .drop-scelta { opacity: .55; }

/* la punta: due lati di un quadrato girato. Chiusa guarda in giu', aperta in
   su, e ci mette lo stesso tempo del pannello a girarsi */
.drop-punta {
  width: 6px; height: 6px; margin-left: .1em;
  border-right: 1px solid var(--ink); border-bottom: 1px solid var(--ink);
  transform: translateY(-2px) rotate(45deg);
  transition: transform var(--d-menu) var(--e-out);
}
.drop[open] .drop-punta { transform: translateY(1px) rotate(-135deg); }

.drop-panel {
  position: absolute; left: 0; top: calc(100% + var(--s2));
  z-index: var(--z-panel);
  display: grid; min-width: 14rem;
  padding: var(--s3) var(--s4) var(--s3) var(--s3);
  background: var(--paper);
  border: 1px solid var(--line);
  font-size: var(--t--1);
}
.drop-panel a {
  display: flex; align-items: center;
  min-height: 40px;
  color: var(--muted);
  transition: color var(--d-link) var(--e-out);
  white-space: nowrap;
}
.drop-panel a:hover, .drop-panel a:focus-visible { color: var(--ink); }
.drop-panel a[aria-current] { color: var(--ink); }

/* il pannello entra dall'alto, le voci una dopo l'altra: 24ms l'una, che e'
   abbastanza per leggere il verso e troppo poco per aspettare. In uscita
   nessuna scala: chi chiude ha gia' deciso, e va accompagnato fuori in fretta. */
.drop[open] .drop-panel { animation: drop-giu var(--d-menu) var(--e-out) both; }
.drop[open] .drop-panel a { animation: drop-voce 220ms var(--e-out) both; animation-delay: calc(var(--i, 0) * 24ms + 30ms); }
.drop.is-closing .drop-panel { animation: drop-su 140ms var(--e-mid) both; }
.drop.is-closing .drop-panel a { animation: none; }

@keyframes drop-giu { from { opacity: 0; transform: translateY(-6px); } }
@keyframes drop-voce { from { opacity: 0; transform: translateY(-4px); } }
@keyframes drop-su { to { opacity: 0; transform: translateY(-4px); } }

/* --- le due viste: due segni e una casella che scivola ---
   Senza JavaScript il gruppo non compare (l'attributo hidden lo toglie il JS):
   non c'e' niente da scambiare.

   La coreografia, in ordine di quel che si vede:

     0ms      il segno premuto si stringe (:active). E' l'unica risposta che
              deve arrivare prima del pensiero, e infatti non passa da niente.
     0-140ms  i due segni si scambiano il colore. Il colore dice lo stato: va
              detto per primo, e in fretta.
     0-240ms  la casella scivola. Non dice lo stato, accompagna l'occhio da una
              parte all'altra — quindi puo' metterci di piu' e deve mettercelo.
     120ms    la griglia, spenta al clic, e' finita di spegnersi: si rimette a
              posto e si riaccende in 200. Il comando ha gia' risposto da un
              pezzo, e va bene cosi': prima il comando, poi il contenuto.

   --i (0 o 1) e' quale delle due e' accesa. La scrive il JavaScript, che quello
   stato ce l'ha gia': con :has() sarebbe il foglio a leggerlo da aria-pressed,
   ma allora un motore senza :has() lascerebbe la casella ferma sul primo. */

.vista {
  position: relative;
  display: inline-flex; align-items: center;
  --i: 0;
  --casella: 34px;
  /* il ritardo di 300ms del tocco su iOS non serve: qui non si fa doppio tocco */
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  /* il bersaglio del dito sborda di 3px oltre la casella che si vede: senza
     questo il bordo resterebbe 3px dentro al margine della pagina, e in una
     riga fatta di filetti allineati si nota */
  margin-right: -3px;
}

.vista-b {
  display: grid; place-items: center;
  /* il bersaglio e' 44 in altezza e 40 in larghezza, la casella che si vede e'
     34: l'aria in mezzo la prende il dito, non l'occhio */
  width: 40px; height: 44px;
  color: var(--muted);
  transition: color 140ms var(--e-out);
}
.vista-b:hover { color: var(--ink); }
.vista-b[aria-pressed="true"] { color: var(--ink); }

/* Il segno si stringe sotto al dito e torna piano: veloce a scendere, lento a
   risalire — e' come si comporta una cosa che si schiaccia. Il -webkit-
   tap-highlight tolto sopra lascia scoperta la risposta al tocco; questa la
   rimette, ma fatta come si deve. */
.vista-segno { display: block; transition: transform 240ms var(--e-out); }
.vista-b:active .vista-segno { transform: scale(.86); transition-duration: 80ms; }

/* La casella sta dietro ai pulsanti: sono fratelli, e viene prima nella
   marcatura, quindi si sovrappone senza dover alzare nessuno con z-index. */
.vista-casella {
  position: absolute; left: 3px; top: 50%;
  width: var(--casella); height: var(--casella);
  margin-top: calc(var(--casella) / -2);
  border: 1px solid var(--line);
  transform: translateX(calc(var(--i) * 40px));
  transition: transform var(--d-menu) var(--e-out);
  pointer-events: none;
}

/* =========================== pubblicazioni ===========================
   E' l'elenco dell'indice progetti, con altre informazioni: anno, lavoro,
   testata e tipo. La forma sta in 04-componenti.css insieme a quella dei
   progetti; qui restano solo i filetti e cio' che l'elenco progetti non ha. */

.pub-list { border-top: 1px solid var(--line); }
.pub { border-bottom: 1px solid var(--line); }
.pub-desc { display: none; }   /* c'e' nei dati, in riga non entra: la riga e' una riga */

/* ================================ info ===============================
   La scheda di un progetto applicata a una persona: il ritratto sta fermo a
   sinistra mentre a destra scorrono i blocchi, e l'etichetta di ogni blocco
   sta a margine, incolonnata con le altre. Le etichette sono .section-title,
   le stesse del resto del sito: qui cambia dove stanno, non come parlano. */

.info { display: grid; grid-template-columns: 26rem 1fr; gap: var(--s7); align-items: start; }
.portrait { position: sticky; top: var(--s5); }
.portrait figcaption { margin-top: var(--s3); }

.info-blocco {
  display: grid; grid-template-columns: 7rem 1fr; gap: var(--s4);
  padding: var(--s5) 0; border-top: 1px solid var(--line);
}
.info-blocco:first-child { padding-top: 0; border-top: 0; }
.info-attacco { grid-template-columns: 1fr; }   /* l'attacco non ha etichetta */

.statement { color: var(--muted); }
.statement p { font-size: clamp(1.5rem, 3vw, var(--t-4)); line-height: 1.15; }

.plain { display: grid; gap: var(--s2); font-size: var(--t--1); }
.plain.cols { grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr)); }
.rows-2 li { display: grid; grid-template-columns: 3.5rem 1fr; gap: var(--s3); }

/* stretto: il ritratto non ha piu' un fianco dove stare fermo, e l'etichetta
   non ha piu' un margine: torna sopra al suo blocco */
@media (max-width: 64rem) {
  .info { grid-template-columns: 1fr; gap: var(--s5); }
  .portrait { position: static; max-width: 20rem; }
  .info-blocco { grid-template-columns: 1fr; gap: var(--s2); }
}

/* ================================= 404 =============================== */

.gone .lead { margin-bottom: var(--s5); }

/* il piano si prende tutta l'altezza: l'intestazione e' gia' bassa di suo */
.page-home main { padding-top: 0; }
