/* ===========================================================================
   Gallery il cui contenuto sta su R2 — caricato SOLO dalle pagine con `r2Prefix`
   nel front matter (vedi partials/r2-gallery.html), quali che siano, quindi qui
   dentro nient'altro. L'elenco autorevole delle sezioni è SECTIONS in
   functions/api/gallery/[section].js: qui non se ne nomina nessuna, e le pagine
   condividono layout e animazioni per costruzione — dal front matter arrivano
   solo il titolo e la cartella del bucket.
   Il body ha `is-rgallery`; le classi sono prefissate `rg-` per non incrociare
   site.css.

   DUE viste, stesso DOM (le celle NON vengono ricostruite: cambia solo la classe sul
   contenitore, e il JS anima il passaggio con una FLIP):
     .is-pair   stato iniziale: tre colonne desktop, due mobile
     .is-zoom   una colonna, un'immagine quasi a tutto viewport
   Un click passa dalla vista iniziale alla singola e un altro torna indietro.
   =========================================================================== */

/* Profondità del warp: `perspective` è la distanza dell'osservatore dal piano, il
   translateZ degli elementi non deve mai avvicinarsi troppo a questo valore (a z =
   perspective la scala è infinita). Sta sul <body> perché la condividono la griglia e il
   monogramma, che sono su due rami diversi (vedi .rg). Il JS la rilegge da .rg (cssNum),
   dove arriva per eredità. */
body.is-rgallery { --rg-persp: 1100; background: var(--rg-bg); }
/* Il warp scaglia le celle ben oltre i bordi del viewport: senza questo comparirebbero
   le barre di scorrimento del documento mentre passano. */
body.is-rgallery { overflow: hidden; }

.rg {
  --rg-bg: #E7E7E7;
  --rg-ink: #111;
  --rg-pad: 3.2vw;          /* margine del contenuto */
  /* Margine SINISTRO dello scroller. È una variabile perché la sezione delle porte in fondo
     esce dai margini con un valore negativo e deve sapere di quanto. */
  --rg-padl: var(--rg-pad);
  /* Base mobile: due colonne e gap 3vw. Su desktop si passa a tre colonne e il solo gap
     orizzontale viene stretto a 1,5vw nel blocco responsive in fondo. */
  --rg-cols: 2;
  --rg-gap-x: 3vw;
  --rg-gap-y: 3vw;
  /* Tetti in ALTEZZA (le larghezze le dà la colonna): impediscono che una verticale molto
     stretta si mangi lo schermo. Non sono l'altezza delle celle, che segue le proporzioni. */
  --rg-top: 8vh;            /* da dove parte il contenuto: prima riga di foto E titolo */
  --rg-row-max-pair: 84vh;  /* vista ravvicinata */
  --rg-row-max-zoom: 98vh;  /* vista singola: occupa quasi l'intera altezza del viewport */
  /* La vista singola è disponibile a tutte le larghezze: il JS legge questo flag per
     mantenere il comportamento allineato al CSS. */
  --rg-zoom: 1;
  /* --rg-persp (profondità del warp) sta sul <body>, non qui: la usano due elementi che
     NON sono uno dentro l'altro (.rg-scroll e #brand-mark, fratelli dentro <main>), e una
     custom property non risale l'albero. Le altre manopole del warp restano qui. */
  --rg-warp-z: 620;         /* quanto "esce dallo schermo" una cella che se ne va */
  --rg-warp-in: -900;       /* … e da quanto lontano arriva quando entra */
  --rg-warp-dur: 900;       /* ms */
  --rg-warp-step: 26;       /* stagger fra una cella e l'altra, ms */
  /* Passaggio coppia <-> singola. Non è un movimento uniforme: l'immagine su cui si è
     cliccato è la più veloce e le altre la seguono tanto più lente quanto più sono
     lontane, come se le trascinasse.
       --rg-morph        ms dell'immagine cliccata (la più rapida)
       --rg-morph-drag   quanto si allunga la durata a distanza massima (0.5 = +50%)
       --rg-morph-lag    ritardo massimo di partenza, ms (il "ritardo della scia") */
  --rg-morph: 1050;
  --rg-morph-drag: 0.85;
  --rg-morph-lag: 240;

  position: fixed; inset: 0;
  background: var(--rg-bg); color: var(--rg-ink);
}

/* ---- Contenitore di scroll ----------------------------------------------------------
   `perspective` sta QUI, non sulla griglia: è il punto di vista dello spettatore, e deve
   restare fermo mentre il contenuto scorre. Il punto di fuga lo sposta il JS
   (--rg-ox/--rg-oy) sul punto in cui l'utente ha cliccato, così il warp parte da lì. */
.rg-scroll {
  position: absolute; inset: 0;
  overflow-y: auto; overflow-x: hidden;
  padding: var(--rg-top) var(--rg-pad) 12vh var(--rg-padl);
  perspective: calc(var(--rg-persp) * 1px);
  perspective-origin: var(--rg-ox, 50%) var(--rg-oy, 50%);
  scrollbar-width: none; -ms-overflow-style: none;
}
.rg-scroll::-webkit-scrollbar { width: 0; height: 0; display: none; }
/* Mentre l'immagine su cui si è cliccato è in volo lo scroll è bloccato: si sta muovendo in
   profondità e trascinarla nel frattempo mostrerebbe celle che rientrano da sole.
   Dura quanto l'ANCORA (--rg-morph), non quanto l'ultima cella della scia: le celle in coda
   continuano a scivolare al loro posto anche se nel frattempo si scorre (il loro transform è
   una traslazione che va a zero, non c'è niente che possa saltare), e tenerlo chiuso fino
   all'ultima rendeva la pagina sorda per oltre due secondi. Chi lo toglie è morph(). */
.rg.is-warping .rg-scroll { overflow: hidden; }

.rg-status {
  margin: 0; padding: 2vh 0; max-width: 46em;
  font: 500 11px/1.8 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: 0.1em; opacity: 0.55;
}
.rg-status[hidden] { display: none; }

/* ---- Sezioni introduttive -------------------------------------------------------------
   Vivono nello stesso scroller della gallery ma non nella griglia animata. I tre moduli
   condividono una grammatica precisa: titolo editoriale, testo leggibile, immagine intera.
   Niente opzioni arbitrarie dal pannello: il template resta responsabile del design. */
.rg-sections {
  width: 100%; display: flex; flex-direction: column;
  gap: clamp(4rem, 10vh, 8rem); margin: 0 0 clamp(5rem, 13vh, 10rem);
}
.rg-section { margin: 0; }
.rg-section-header { max-width: min(74rem, 88%); }
.rg-section-eyebrow {
  margin: 0 0 1.2rem; opacity: .58;
  font: 600 clamp(10px, .72vw, 13px)/1.2 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .14em; text-transform: uppercase;
}
.rg-section-header h2 {
  max-width: 13ch; margin: 0;
  font: 700 clamp(42px, 6.2vw, 112px)/.9 "Helvetica Neue", Arial, sans-serif;
  letter-spacing: -.045em; text-transform: uppercase;
}
.rg-section-lead {
  max-width: 48rem; margin-top: clamp(1.5rem, 3.5vh, 3rem);
  font: 400 clamp(17px, 1.45vw, 25px)/1.42 "Helvetica Neue", Arial, sans-serif;
}
.rg-section-lead p { margin: 0; }
.rg-section-text {
  max-width: 52rem;
  font: 400 clamp(17px, 1.25vw, 22px)/1.62 "Helvetica Neue", Arial, sans-serif;
}
.rg-section-text p { margin: 0 0 1.25em; }
.rg-section-text p:last-child { margin-bottom: 0; }
.rg-section-text a,
.rg-section-lead a { color: inherit; text-decoration-thickness: 1px; text-underline-offset: .16em; }
.rg-section-image { width: 100%; }
.rg-section-image.is-contained { width: min(72%, 860px); }
.rg-section-image img {
  display: block; width: auto; height: auto; max-width: 100%; max-height: 82vh;
  object-fit: contain; object-position: left center;
}
.rg-section-image figcaption {
  max-width: 48rem; margin-top: .8rem; opacity: .58;
  font: 400 11px/1.45 ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* ---- Coppia iniziale ------------------------------------------------------------------
   Ordine per RIGHE (è l'ordine naturale di CSS Grid: si riempie una riga alla volta) e
   celle allineate in ALTO, così le immagini più basse lasciano aria sotto invece di
   essere stirate — è la disposizione della reference. Niente masonry a incastro: le
   righe restano leggibili come righe. */
.rg-grid {
  display: grid;
  grid-template-columns: repeat(var(--rg-cols), 1fr);
  column-gap: var(--rg-gap-x);
  row-gap: var(--rg-gap-y);
  align-items: start;
  /* Le celle si muovono in Z: senza questo la griglia appiattirebbe i figli sul proprio
     piano e la prospettiva di .rg-scroll non si vedrebbe. */
  transform-style: preserve-3d;
}

.rg-cell {
  margin: 0; padding: 0;
  display: flex; justify-content: center;
  cursor: zoom-in;
  /* Il transform lo scrive il JS (warp e FLIP).
     Qui NON ci sono né `will-change` né `backface-visibility`, e non è una dimenticanza:
     promuovono la cella a layer composito, e di celle ce ne sono ~100. Promosse tutte,
     ognuna si tiene la propria texture — in vista ravvicinata una cella è 1160x1210 px
     reali, ~5,6 MB l'una — e al cambio di vista, che le fa crescere di 3,5x in linea (12x
     in pixel), vengono invalidate e ri-rasterizzate TUTTE insieme: comprese le decine fuori
     campo, che non si muovono nemmeno. Non è tempo di main thread (il cambio classe costa
     2-3 ms) e infatti non si vede in un profilo JS: è il compositor, ed era la causa dello
     scatto all'apertura della coppia. Senza, il browser torna a rasterizzare solo ciò che è
     vicino al viewport.
     La promozione serve davvero solo alle celle che si stanno muovendo, e per il tempo in
     cui si muovono: gliela mette il JS inline (`fly()` in gallery.js), insieme a
     `backface-visibility` — che evita il flicker di WebKit sugli elementi che attraversano
     il piano dell'osservatore, ma promuove anch'esso, quindi vale la stessa regola. */
}
.rg-cell img {
  display: block;
  /* L'immagine è larga quanto la colonna e alta secondo le proporzioni native, MA quando è
     il tetto in altezza a mordere (verticali molto slanciate) è la larghezza a cedere:
     `width: auto` + `max-width` fa restringere il BOX insieme all'immagine. Con
     `width: 100%` il box resterebbe largo quanto la colonna e l'immagine, contenuta dentro,
     lascerebbe due bande vuote ai lati — il "riquadro" attorno alle verticali.
     Mai `height` fissa: ritaglierebbe o deformerebbe. */
  width: auto; height: auto;
  max-width: 100%; max-height: var(--rg-row-max-pair);
}
/* Cella ancora senza immagine: qui la larghezza serve, altrimenti il segnaposto (che non ha
   dimensioni intrinseche) collasserebbe a zero e il lazy loading tornerebbe a non mordere. */
.rg-cell.is-loading img { width: 100%; background: #dcdcdc; }

/* `aspect-ratio` qui NON è estetica, è ciò che fa funzionare il lazy loading: un'immagine
   non ancora caricata non ha dimensioni intrinseche, quindi senza questo le celle sarebbero
   alte quasi zero e l'intera griglia starebbe dentro la finestra di prefetch del browser —
   che le scaricherebbe TUTTE (~22 MB per un centinaio di foto) invece delle poche in vista.
   Con un'altezza riservata plausibile la griglia ha la sua lunghezza vera fin da subito e il
   lazy morde. Il valore è il formato verticale tipico di queste foto (2:3); appena
   l'immagine arriva la classe cade e l'ingombro passa alle proporzioni reali. */
.rg-cell.is-loading img { opacity: 0; aspect-ratio: var(--ar, 2 / 3); }

/* ---- Vista iniziale --------------------------------------------------------------------
   Due colonne come base mobile, tre su desktop; immagini fino a 84vh. */
.rg.is-pair { --rg-cols: 2; --rg-gap-x: 3vw; --rg-gap-y: 3vw; }
.rg.is-pair .rg-cell img { max-height: var(--rg-row-max-pair); }
.rg.is-pair .rg-cell { cursor: default; }

/* ---- Vista singola (una colonna, quasi a tutto viewport) -----------------------------
   L'ultimo scalino: una foto alla volta, e si continua a scorrere verticalmente lungo
   tutta la selezione — non è un overlay che isola una foto, è la stessa griglia con una
   colonna sola. Un click sull'immagine torna alla coppia. Il gap mantiene il ritmo storico
   della vista vicina. */
.rg.is-zoom { --rg-cols: 1; --rg-gap-x: 3vw; --rg-gap-y: 3vw; }
.rg.is-zoom .rg-cell img { max-height: var(--rg-row-max-zoom); }
.rg.is-zoom .rg-cell { cursor: zoom-out; }

/* ---- Le altre porte, in fondo allo scroll ---------------------------------------------
   Dopo l'ultima foto: la STESSA sezione che chiude la home (partials/genres.html, regole
   .genres/.genre-* in site.css, gesto in js/warp-out.js). Qui non si ridichiara niente di
   come si vede — solo ciò che là viene dall'essere una schermata intera dentro uno scroll
   guidato, e qui non ha senso.
   Sta dentro .rg-scroll perché è l'unica cosa che scorre (.rg è fissa e il documento ha
   `overflow: hidden`): "dopo la gallery" vuol dire dopo l'ultima foto. */
.genres.rg-more {
  /* Esce dai margini dello scroller: le immagini vanno a filo come nella home, e --pad-l
     torna a essere misurato dal bordo dello schermo. A sinistra il margine da disfare è
     --rg-padl. */
  margin: 12vh calc(-1 * var(--rg-pad)) 0 calc(-1 * var(--rg-padl));
  height: auto;              /* in home è alta una schermata; qui è in coda a uno scroll */
  scroll-snap-align: none;   /* e qui non c'è nessuno snap a cui agganciarsi */
  background: transparent;   /* il fondo è già quello della gallery, di un tono diverso */
}
.genres.rg-more .genres-grid {
  height: auto;
  /* Il monogramma resta l'unica corsia da scansare; --rg-padl mantiene inoltre le colonne
     allineate al margine delle foto. */
  --pad-l: max(var(--pad-r), var(--mono-lane), var(--rg-padl));
  /* In home il tetto serve a far stare le tre immagini in una schermata: 100vh meno la fetta
     --peek, che è una manopola di .scroll-root e qui NON esiste — un `calc` con una variabile
     vuota non è un errore visibile, è un max-width che sparisce in silenzio. Qui la sezione
     non deve stare in una schermata, ma il tetto serve lo stesso: su uno schermo largo e
     basso tre immagini 2:3 diventerebbero altissime. */
  --img-h-max: 62vh;
}

/* ---- Monogramma ----------------------------------------------------------------------
   Il monogramma vero e proprio (dimensioni, colori, blend, segno) è in site.css: è lo
   stesso di tutto il sito e qui non va duplicato. Serve solo dargli la PROSPETTIVA, così
   entra dal fondo come le celle: stessa distanza dell'osservatore e — importante — stesso
   punto di fuga della griglia, altrimenti arriverebbe lungo un'altra diagonale.
   Il punto di fuga lo scrive il JS su :root proprio per questo (setOrigin): .rg-scroll e
   #brand-mark non sono uno dentro l'altro. */
body.is-rgallery #brand-mark {
  perspective: calc(var(--rg-persp) * 1px);
  perspective-origin: var(--rg-ox, 50%) var(--rg-oy, 50%);
}
/* Come per le celle: evita il flicker di WebKit sugli elementi che si muovono in Z (e
   tiene vivo il layer, senza il quale su iOS il blend degli elementi fermi salta). */
body.is-rgallery .bm { -webkit-backface-visibility: hidden; backface-visibility: hidden; }

/* ---- Titolo della sezione --------------------------------------------------------------
   Su mobile resta nel flusso sopra alle foto. Su desktop la vista ravvicinata storica lo
   nasconde, come faceva già dopo il primo avvicinamento. */
.rg-title {
  margin: 0 0 var(--rg-gap-y);
  font-family: "Helvetica Neue", Arial, sans-serif; font-weight: 700; line-height: 1;
  /* Il corpo lo scrive `fitTitle()` in --fs; il clamp è il valore di partenza (e quello che
     resta se il JS non gira). È lo STESSO nei due stati: cambiarlo all'hover si vedeva come
     uno scatto. Lo sceglie il più largo fra titolo e alternativa — vedi buildTitle. */
  font-size: var(--fs, clamp(28px, 10vw, 46px));
  letter-spacing: 0.01em; text-transform: uppercase; color: var(--rg-ink);
}
/* Lo scambio di lettere in "<HOME" (le finestrelle, `.is-back`) è il MECCANISMO condiviso
   col portfolio: vive in title-swap.css/.js, classi `.tsw-*`, applicate allo stesso `<h1>`
   insieme a `.rg-title` (vedi partials/gallery.html). Qui restano solo posizione e corpo,
   che sono specifici di questa pagina. */

/* ---- Pulsante di zoom-out ------------------------------------------------------------
   Circolare, in alto a destra: quattro angoli che rientrano verso il centro. */
.rg-out {
  position: fixed; top: 3vh; left: 3vh; z-index: 5;
  width: 48px; height: 48px; padding: 0;
  border-radius: 999px; border: none;
  background: rgba(255,255,255,.72); color: var(--rg-ink);
  box-shadow: 0 8px 20px -6px rgba(0,0,0,.35);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  display: grid; place-items: center; cursor: pointer;
  opacity: 0; transform: scale(0.85);
  /* Invisibile nella vista iniziale, e anche INTOCCABILE: senza questo resterebbe un bersaglio
     trasparente che si mangia i click nell'angolo in alto a sinistra. */
  pointer-events: none;
  transition: opacity 300ms ease, transform 300ms cubic-bezier(0.16,1,0.3,1), background 200ms ease;
}
.rg-out[hidden] { display: none; }
/* Visibile soltanto nella vista singola: torna alla coppia iniziale. */
.rg.is-zoom .rg-out { opacity: 1; transform: none; pointer-events: auto; }
.rg-out:hover { background: #fff; }
.rg-out svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }

/* ---- Mobile ---------------------------------------------------------------------------
   La vista iniziale mantiene due colonne; il click passa alla singola colonna. */
@media (max-width: 900px) {
  .rg { --rg-pad: 5vw; }
  .rg.is-pair { --rg-cols: 2; }
  .rg-sections { gap: 3.5rem; margin-bottom: 5rem; }
  .rg-section-header { max-width: 100%; }
  .rg-section-header h2 { font-size: clamp(38px, 13vw, 68px); }
  .rg-section-text,
  .rg-section-lead { font-size: 17px; }
  .rg-section-image.is-contained { width: 86%; }
}

/* ---- Motion ridotto -------------------------------------------------------------------
   Niente warp né FLIP: il JS salta le animazioni e le celle compaiono al loro posto. */
@media (prefers-reduced-motion: reduce) {
  .rg-cell, .rg-cell img { transition: none !important; }
  /* La sostituzione delle lettere (transizione propria in title-swap.css) resta secca:
     niente scorrimento — qui solo la dissolvenza del titolo che è specifica di questa
     pagina (svanisce in vista ravvicinata). */
  .rg-title { transition: none !important; }
}

/* ---- Desktop -------------------------------------------------------------------------- */
@media (min-width: 901px) {
  .rg {
    --rg-col: calc((100vw - 2 * var(--rg-pad) - 2 * var(--rg-gap-x)) / 3);
  }
  .rg.is-pair { --rg-cols: 3; --rg-gap-x: 1.5vw; --rg-gap-y: 1.5vw; }
  .rg.is-pair .rg-cell img { max-height: none; }
  /* Il JS compone righe complete senza cambiare il DOM: una orizzontale (due tracce) seguita
     dalla verticale abbinata. `order` vale solo in questa vista; in singola torna l'ordine
     editoriale originale. Se, per dati anomali, le verticali non bastano, l'orizzontale
     residua prende tutte e tre le tracce invece di lasciare una colonna rotta. */
  .rg.is-pair .rg-cell { order: var(--rg-pair-order, 0); }
  .rg.is-pair .rg-cell.is-landscape { grid-column: span 2; }
  .rg.is-pair .rg-cell.is-landscape.is-unpaired-landscape { grid-column: span 3; }
  .rg.is-pair .rg-cell { cursor: zoom-in; }

  .rg-title {
    position: fixed; z-index: 3;
    margin: 0;
    left: var(--rg-pad);
    /* A filo della prima riga di foto. Con `line-height: 1` il top della "F" NON è il top
       della riga di testo: fra i due c'è il gap che il font si tiene sopra le maiuscole
       (Helvetica Neue ~0.17em). Senza toglierlo il titolo cadrebbe qualche px più in basso
       delle immagini — vedi la nota sulla cap band in CLAUDE.md. */
    top: calc(var(--rg-top) - 0.17em);
    width: var(--rg-col);
    /* Le sezioni dal nome lungo ("HOSPITALITY & INTERIORS") vanno a capo dentro la corsia
       invece di sbordare sulle foto. */
    margin: 0; overflow-wrap: break-word;
    /* Il corpo lo decide `fitTitle()` in gallery.js, che lo scala finché la parola più
       lunga è larga quanto la colonna: qui c'è solo il valore di partenza, che è anche il
       riferimento su cui il JS misura (e ciò che resta se il JS non gira). */
    font-size: var(--fs, clamp(17px, 1.6vw, 28px)); line-height: 1.02;
    color: var(--rg-ink);
    /* annulla il nascondimento di sopra */
    height: auto; clip: auto; overflow: visible; white-space: normal;
    transition: opacity 300ms ease;
  }
  .rg-title { opacity: 0; }
}

/* ---- Layout masonry (gallery con `galleryLayout: masonry`) ----------------------------
   Oggi le quattro visibili: food, drink, people, hospitality & interiors. Niente qui dipende
   da quale gallery sia — la regola guarda solo la classe, che baseof mette dal front matter.
   È la stessa gallery e lo stesso DOM: cambia soltanto la disposizione della vista iniziale.
   Le CSS columns danno tre pile indipendenti e riempiono ogni vuoto verticale senza crop.
   La regola è legata a `.is-pair`: al click la classe cade e la normale `.is-zoom` a una
   colonna torna automaticamente a essere CSS Grid, quindi il warp non ha un secondo codice. */
body.is-masonry .rg.is-pair .rg-grid {
  display: block;
  column-count: 3;
  column-gap: 1.5vw;
}
body.is-masonry .rg.is-pair .rg-cell {
  display: block;
  width: 100%;
  margin: 0 0 1.5vw;
  break-inside: avoid;
  -webkit-column-break-inside: avoid;
}
body.is-masonry .rg.is-pair .rg-cell img {
  width: 100%;
  max-width: 100%;
  max-height: none;
}
@media (max-width: 900px) {
  body.is-masonry .rg.is-pair .rg-grid {
    column-count: 2;
    column-gap: 3vw;
  }
  body.is-masonry .rg.is-pair .rg-cell { margin-bottom: 3vw; }
}
