/* ============================================================================
   NAILS ROOM · il portale, i pulsanti glow, la barra di vetro
   ============================================================================

   Solo token. Un colore scritto a mano qui dentro è un bug: cambiare
   `--accento` in art-tokens.css deve cambiare anche il bagliore dei bottoni e
   la luce del pozzo, senza toccare altro.

   Il pozzo porta nella pagina il punto più scuro della palette (`--bg-scuro`),
   che prima viveva solo nel piè di pagina. La home fa un arco tonale — crema,
   bruno, crema — invece di restare su una nota sola.
   ========================================================================= */

:root {
  --glow-vicino: oklch(0.520 0.090 52 / .38);   /* stretta e vicina */
  --glow-lontano: oklch(0.620 0.120 55 / .26);  /* larga e tenue */
  --vetro: oklch(0.949 0.013 65 / .72);
  /* una molla, non una rampa: supera il bersaglio e ci torna. È il modo in cui
     si muove una cosa che ha una massa, e si sente anche a 600 ms. */
  --e-molla: cubic-bezier(0.16, 1.14, 0.32, 1);
  --vetro-scuro: oklch(0.245 0.048 45 / .58);
}

/* ============================================================ pulsanti glow

   Un bagliore fatto bene è una luce, non un alone: due ombre nella STESSA
   direzione della luce del progetto (dall'alto), una stretta e satura vicino
   al bordo, una larga e tenue lontano. Una sola ombra sfocata legge come
   sfocatura, non come emissione. */

.bottone--glow {
  position: relative;
  border: 0;
  color: var(--accento-fg);
  background: linear-gradient(
    180deg,
    oklch(from var(--accento) calc(l + 0.06) c h),
    var(--accento));
  box-shadow:
    0 1px 0 oklch(1 0 0 / .22) inset,
    0 2px 6px var(--glow-vicino),
    0 8px 28px var(--glow-lontano);
  transition:
    box-shadow var(--d-base) var(--e-out),
    transform var(--d-micro) var(--e-out);
}

/* Il bordo che gira. Un gradiente conico mascherato al solo perimetro: si
   accende all'avvicinarsi del puntatore e continua a girare finché resta lì.
   È il "dettaglio impossibile" in versione economica — 20 righe, nessuna
   libreria, e si vede subito che qualcuno ci ha lavorato.
   
   Sta SOPRA il bottone (`::after`, z-index positivo) e non sotto. Con
   `isolation: isolate` e uno `z-index: -1` la resa era identica, ma nello
   scatto di una transizione fra pagine il bottone si appiattiva in un
   rettangolo bianco: quel contesto di impilamento non sopravvive alla
   rasterizzazione. La maschera lascia visibile solo la cornice di 1px, quindi
   stare sopra non copre niente. */
.bottone--glow::after {
  content: '';
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  padding: 1px;
  background: conic-gradient(
    from var(--giro, 0deg),
    transparent 0deg,
    oklch(0.92 0.10 70) 40deg,
    oklch(from var(--accento) calc(l + 0.24) c h) 80deg,
    transparent 140deg,
    transparent 360deg);
  /* la maschera lascia visibile solo la cornice di 1px */
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  opacity: 0;
  transition: opacity var(--d-base) var(--e-out);
  pointer-events: none;
  z-index: 1;
}

@property --giro {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

@keyframes gira-bordo { to { --giro: 360deg; } }

.bottone--glow:hover,
.bottone--glow:focus-visible {
  transform: translateY(-1px);
  box-shadow:
    0 1px 0 oklch(1 0 0 / .28) inset,
    0 3px 10px var(--glow-vicino),
    0 14px 44px var(--glow-lontano);
}

.bottone--glow:hover::after,
.bottone--glow:focus-visible::after {
  opacity: 1;
  animation: gira-bordo 2.6s linear infinite;
}

/* L'uscita è più rapida dell'ingresso: chi smette di puntare ha già deciso. */
.bottone--glow:active {
  transform: translateY(0);
  transition-duration: var(--d-uscita);
}

/* Sul bruno il bagliore va alzato, o sparisce nel fondo. */
.su-scuro .bottone--glow {
  box-shadow:
    0 1px 0 oklch(1 0 0 / .18) inset,
    0 2px 8px oklch(0.620 0.120 55 / .50),
    0 10px 38px oklch(0.660 0.130 58 / .34);
}

/* ========================================================== barra di vetro

   Tre livelli, non un rettangolo semitrasparente: sfocatura + saturazione
   (il vetro concentra il colore di ciò che copre), un colpo di luce sul bordo
   alto, e una riga di separazione che prende la tinta del fondo, mai grigia. */

/* La barra esisteva già (`.barra--posata`): qui il vetro diventa a tre livelli
   — sfocatura più saturazione, perché il vetro vero concentra il colore di ciò
   che copre; un colpo di luce sul bordo alto; una riga che prende la tinta del
   fondo, mai grigia. */
.barra--posata {
  background: var(--vetro);
  backdrop-filter: blur(16px) saturate(1.6);
  -webkit-backdrop-filter: blur(16px) saturate(1.6);
  border-bottom: 1px solid oklch(from var(--linea) l c h / .55);
  box-shadow: var(--ombra-dentro), 0 6px 24px oklch(0.32 0.05 45 / .07);
}

/* Sopra il pozzo si inverte: resta leggibile sul bruno senza cambiare posto né
   dimensione. Il menu è il punto fermo mentre tutto il resto scende. */
.barra.su-scuro {
  background: var(--vetro-scuro);
  backdrop-filter: blur(16px) saturate(1.3);
  -webkit-backdrop-filter: blur(16px) saturate(1.3);
  border-bottom: 1px solid oklch(from var(--linea-scuro) l c h / .7);
  box-shadow: 0 1px 0 oklch(1 0 0 / .07) inset;
  color: var(--fg-su-scuro);
}

.barra.su-scuro a:not(.bottone),
.barra.su-scuro .marchio span { color: var(--fg-su-scuro); }

/* il monogramma è bruno su trasparente: sul bruno sparirebbe */
.barra.su-scuro .marchio img { filter: invert(1) brightness(1.25); }

@supports not (backdrop-filter: blur(1px)) {
  /* senza backdrop-filter un fondo semitrasparente rende il testo illeggibile:
     meglio opaco che "quasi vetro" */
  .barra--posata { background: var(--bg-1); }
  .barra.su-scuro { background: var(--bg-scuro); }
}

/* ================================================================ il pozzo */

/* La stanza chiara. Era un pozzo bruno: adesso è lo stesso viaggio con la luce
   opposta — crema, ariosa, con le foto dentro. Il punto scuro della palette
   torna a vivere nel piè di pagina, dov'era, e non si prende più mezza home. */
.portale {
  position: relative;
  background: var(--bg-1);
  color: var(--fg);
  /* la lunghezza dello scroll è ciò che dà alla scena il tempo di succedere:
     una schermata piena per trattamento, più una di coda */
  --stazioni: 4;
  min-height: calc((var(--stazioni) + 1) * 100svh);
}

.portale-tela {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  /* la tela non deve mai rubare un clic a ciò che le sta sopra */
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--d-cine) var(--e-out);
}

.portale--vivo .portale-tela { opacity: 1; }

/* Con la tela accesa il fondo statico si toglie di mezzo: sotto c'è la scena.
   Senza (niente WebGL, movimento ridotto, dati risparmiati) resta lui, e la
   sezione continua ad avere senso — è il fondo scuro con le schede sopra. */
.portale-fondo {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(120% 70% at 50% 0%,
      oklch(from var(--accento) calc(l + 0.30) c h / .16), transparent 66%),
    var(--bg-1);
  transition: opacity var(--d-cine) var(--e-out);
}

.portale-stazioni {
  position: relative;
  z-index: 1;
  pointer-events: none;    /* i clic passano, tranne dove serve (sotto) */
}

/* Il testo sta nel terzo basso, non al centro: al centro finiva sopra la foto
   incorniciata dall'arco, e i due si mangiavano a vicenda. Sotto, il lavoro
   resta visibile dentro l'anello e la scritta ha il suo posto. (Giro 3.) */
.stazione {
  min-height: 100svh;
  display: grid;
  align-content: end;
  padding: var(--respiro) var(--s5) calc(var(--respiro) * 0.9);
  max-width: 34rem;
  margin-inline: auto;
  text-align: center;
}

/* Un velo che sale dal basso: separa la scritta dalla scena senza disegnare un
   riquadro, e prende la tinta del fondo — mai un nero neutro. Sta sulla
   sezione e non dentro le schede: dentro, con `z-index: -1`, finiva sotto il
   proprio contesto di impilamento e non si vedeva affatto. (Giro 4.) */
.portale--vivo .portale-fondo {
  opacity: 1;
  background: linear-gradient(
    to top,
    oklch(from var(--bg-1) l c h / .97) 0%,
    oklch(from var(--bg-1) l c h / .88) 20%,
    oklch(from var(--bg-1) l c h / .58) 38%,
    transparent 56%);
  position: fixed;
  pointer-events: none;
}

.stazione > * { pointer-events: auto; }

.stazione-numero {
  font-family: var(--f-display);
  font-size: var(--t-display);
  line-height: 0.8;
  color: oklch(from var(--accento) calc(l + 0.10) c h / .30);
  margin: 0 0 calc(var(--s6) * -1);
  user-select: none;
}

/* Il titolo passa sopra l'arco acceso mentre lo si attraversa, e su un telefono
   ci passa sempre. L'ombra non è un nero neutro: è il bruno dell'ambiente, come
   ogni altra ombra del progetto — due livelli, una stretta e una larga, così
   regge sia sul buio sia sul rame. */
.stazione h3 {
  font-family: var(--f-display);
  font-size: var(--t-titolo);
  line-height: var(--i-titolo);
  letter-spacing: var(--s-titolo);
  margin: 0 0 var(--s3);
  /* su fondo chiaro l'alone è chiaro: stacca il titolo dalla foto senza
     disegnarci sopra un'ombra scura, che qui sembrerebbe sporco */
  text-shadow:
    0 1px 2px oklch(from var(--bg-1) calc(l + 0.04) c h / .95),
    0 4px 20px oklch(from var(--bg-1) calc(l + 0.04) c h / .9);
}

.stazione-dati,
.stazione p { text-shadow: 0 1px 12px oklch(from var(--bg-1) calc(l + 0.04) c h / .92); }

.stazione p {
  color: var(--fg-2);
  font-size: var(--t-corpo);
  line-height: var(--i-corpo);
  margin: 0 0 var(--s5);
}

.stazione-dati {
  display: flex;
  gap: var(--s5);
  justify-content: center;
  font-size: var(--t-micro);
  letter-spacing: var(--s-micro);
  text-transform: uppercase;
  color: var(--accento);
  margin-bottom: var(--s5);
}

/* Con la scena accesa le schede entrano e escono con la camera: quella che si
   sta attraversando è nitida, le altre si ritirano. Senza scena restano tutte
   visibili, e la sezione si legge come una sequenza di schermate. */
.portale--vivo .stazione {
  opacity: 0.18;
  transform: translateY(1.2rem) scale(0.985);
  filter: blur(2px);
  transition:
    opacity var(--d-cine) var(--e-out),
    transform var(--d-cine) var(--e-out),
    filter var(--d-cine) var(--e-out);
}

.portale--vivo .stazione--attiva {
  opacity: 1;
  transform: none;
  filter: none;
}

/* ------------------------------------------------------- movimento ridotto
   Non è burocrazia: per una parte delle persone il movimento provoca nausea
   reale. Qui la scena non si accende affatto (lo decide portale.js) e tutto
   ciò che resta è fermo e leggibile. */
@media (prefers-reduced-motion: reduce) {
  .bottone--glow:hover::after,
  .bottone--glow:focus-visible::after { animation: none; opacity: 1; }
  .bottone--glow:hover,
  .bottone--glow:focus-visible { transform: none; }
  .portale { min-height: 0; }
  .stazione { min-height: 0; padding-block: var(--s8); }
}

/* ==================================================== la transizione fra pagine

   L'arco che tocchi diventa la scheda del trattamento invece di cambiare
   pagina. È il browser a farla, non noi: si limita a interpolare gli elementi
   che di qua e di là portano lo STESSO `view-transition-name`.

   Da qui la regola che governa tutto: un nome deve essere unico nel documento.
   Se due stazioni lo portassero insieme, il browser rinuncia e resta il taglio
   secco — per questo i nomi stanno su `.stazione--in-partenza`, che è una sola
   e viene decisa al momento del clic. */

@view-transition { navigation: auto; }

.stazione--in-partenza .stazione-disco { view-transition-name: trattamento-disco; }
.stazione--in-partenza h3               { view-transition-name: trattamento-nome; }

.arrivo-disco { view-transition-name: trattamento-disco; }
.arrivo-nome  { view-transition-name: trattamento-nome; }

/* Il gemello in DOM del disco. Non si vede mai davvero: esiste perché il
   browser abbia qualcosa da fotografare al posto della tela WebGL. Resta
   caricato (stessa immagine della texture, quindi stessa voce di cache) e
   viene messo al posto giusto solo nell'istante della partenza. */
.stazione-disco {
  position: fixed;
  opacity: 0;
  object-fit: cover;
  border-radius: 50%;
  pointer-events: none;
}

.stazione--con-disco .stazione-disco { opacity: 1; }

/* Il disco atterrato, dentro il riepilogo: circolare come nella scena, con un
   filo di rame intorno che è l'ultimo resto dell'arco da cui viene. */
.arrivo-disco {
  display: block;
  width: 112px;
  height: 112px;
  object-fit: cover;
  border-radius: 50%;
  margin: 0 0 var(--s4);
  box-shadow:
    0 0 0 1px oklch(from var(--accento) l c h / .55),
    0 2px 6px var(--glow-vicino),
    0 12px 36px var(--glow-lontano);
  transition: opacity var(--d-base) var(--e-out),
              transform var(--d-base) var(--e-out);
}

/* Cambiata la scelta, il disco non racconta più il vero: se ne va. */
.arrivo-disco[hidden] { opacity: 0; transform: scale(0.9); }

/* --- la taratura del movimento ---

   Il difetto tipico di questa API è lasciare la durata di serie a tutto: 250 ms
   uguali per la pagina intera e per il disco che attraversa mezzo schermo. Il
   disco fa il viaggio più lungo, quindi ci mette di più; il resto sfuma prima e
   gli lascia la scena. */

::view-transition-group(trattamento-disco) {
  animation-duration: var(--d-cine);
  animation-timing-function: var(--e-out);
}

::view-transition-group(trattamento-nome) {
  animation-duration: var(--d-base);
  animation-timing-function: var(--e-out);
}

/* La pagina che se ne va sfuma subito: chi ha premuto ha già deciso, e
   trattenerla la fa sembrare lenta. */
::view-transition-old(root) {
  animation-duration: var(--d-uscita);
  animation-timing-function: var(--e-inout);
}

::view-transition-new(root) {
  animation-duration: var(--d-base);
  animation-timing-function: var(--e-out);
}

@media (prefers-reduced-motion: reduce) {
  /* Non si toglie la transizione: si azzera. Toglierla del tutto (con
     `navigation: none`) riporterebbe il salto secco fra due pagine, che per chi
     soffre il movimento non è meglio — è solo un movimento più brusco. */
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation-duration: 1ms !important;
  }
}

/* Il testo che cambia dimensione fra le due pagine non si può incrociare a
   metà: per un istante si leggono due volte le stesse parole, di due corpi
   diversi, sovrapposte. Si vede come una stampa mossa.

   Il rimedio è togliere il dissolvenza incrociata di serie e sfalsare i due:
   il vecchio esce subito, il nuovo entra quando il vecchio se n'è già andato.
   La posizione continua a interpolare — quella è la continuità che conta. */
::view-transition-old(trattamento-nome) {
  animation: sparisci var(--d-uscita) var(--e-inout) forwards;
}

::view-transition-new(trattamento-nome) {
  animation: comparsa var(--d-base) var(--e-out) var(--d-uscita) backwards;
}

@keyframes sparisci { to { opacity: 0; } }
@keyframes comparsa { from { opacity: 0; } }

/* Il bottone premuto se ne va con il clic, prima dello scatto.

   Non è una preferenza: nello scatto della pagina che parte il bordo conico
   mascherato si appiattisce in un **rettangolo bianco pieno** — la
   rasterizzazione della transizione non ripercorre `mask-composite`. Invece di
   inseguire il rasterizzatore, si toglie di mezzo l'elemento: è quello che hai
   appena premuto, la sua sparizione si legge come una risposta, e la
   continuità la portano il disco e il nome. */
.stazione--senza-bottone .bottone--glow { opacity: 0; }

/* ======================================================= il logo che scende

   L'apertura è il monogramma, grande e al centro: la prima cosa che si vede è
   di chi è il posto. Poi scende e si dissolve, e al suo posto resta quello
   piccolo nella barra — lo stesso segno, che ha solo preso il suo posto di
   lavoro.

   Lo fa una **animazione legata allo scroll**, non JavaScript: gira sul
   compositore, non fa scattare lo scorrimento e non costa un ascoltatore di
   eventi. È la tecnica del 2026 per questa cosa esatta; con `scroll()` il JS
   sarebbe un'approssimazione peggiore. */

/* `.apertura-logo` non esiste più in nessun template: era l'apertura di prima
   della soglia. Con lei se ne va anche la sua uscita in parallasse, che è
   esattamente quella che sulla soglia si è rivelata sbagliata. Regole morte
   che restano in un foglio di stile sono la ragione per cui, fra sei mesi,
   qualcuno rimette un effetto che era stato tolto apposta.

   La graffa che chiudeva il loro `@supports` era rimasta qui da sola. Una
   graffa spaiata in CSS non viene ignorata: il parser la prende come inizio di
   un selettore e **si mangia la regola dopo**. La regola dopo era
   `.stanza { display: grid }` — e senza griglia le quattro foto d'ambiente
   diventavano quattro immagini a tutta larghezza, una sotto l'altra:
   4.829 pixel invece di 1.300, un terzo di tutta la home. */

/* ==================================================== la stanza, in immagini

   «Tante immagini» non vuol dire una griglia di riquadri uguali: quella è la
   posa del template. Qui le foto hanno pesi diversi, una sborda dal bordo, e
   fra loro c'è aria — è il ritmo a far sembrare un posto un posto. */

.stanza {
  display: grid;
  gap: var(--s5);
  grid-template-columns: repeat(6, 1fr);
  align-items: end;
}

.stanza figure { margin: 0; position: relative; }
.stanza img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: var(--r3);
  display: block;
  box-shadow: var(--ombra-2);
}

.stanza figcaption {
  margin-top: var(--s3);
  font-size: var(--t-micro);
  letter-spacing: var(--s-micro);
  text-transform: uppercase;
  color: var(--fg-3);
}

.stanza .larga { grid-column: span 4; aspect-ratio: 3 / 2; }
.stanza .alta  { grid-column: span 2; aspect-ratio: 3 / 4; }
.stanza .media { grid-column: span 3; aspect-ratio: 4 / 3; }
.stanza .piccola { grid-column: span 2; aspect-ratio: 1; }

/* la rottura: una foto che esce dalla gabbia. Una per pagina basta. */
.stanza .sborda { grid-column: span 3; aspect-ratio: 4 / 3; margin-right: -6vw; }

@media (max-width: 820px) {
  .stanza { grid-template-columns: repeat(2, 1fr); }
  .stanza .larga, .stanza .media, .stanza .sborda { grid-column: span 2; }
  .stanza .alta, .stanza .piccola { grid-column: span 1; }
  .stanza .sborda { margin-right: 0; }
}

/* =================================================== foto difficili da copiare

   Onestà: impossibile non esiste. Il browser, per mostrarle, le ha già
   decodificate, e uno screenshot lo fa chiunque. Queste tre righe fermano la
   copia distratta — trascinare, tasto destro, selezionare — che è la
   stragrande maggioranza. Quella determinata la ferma solo il monogramma
   impresso nei pixel, che è il vero lavoro e sta in `filigrana.py`. */

.protetta {
  -webkit-user-drag: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;   /* niente «salva immagine» tenendo premuto su iOS */
}

/* ==================================================== con chi ti fai le unghie

   La scelta della persona non è una tendina: è la parte del modulo in cui la
   cliente guarda una faccia e decide di fidarsi. Quindi ritratti veri, grandi
   abbastanza da riconoscere qualcuno, e il primo riquadro — «Chiunque» — che
   non è un ripiego ma la scelta normale: la maggior parte vuole l'orario. */

.squadra-scelta {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(7.5rem, 1fr));
  gap: var(--s3);
}

.chi-scelta {
  display: grid;
  justify-items: center;
  gap: var(--s2);
  padding: var(--s4) var(--s3);
  border: 1px solid var(--linea);
  border-radius: var(--r3);
  background: var(--bg-2);
  color: var(--fg-2);
  font: inherit;
  cursor: pointer;
  text-align: center;
  transition: border-color var(--d-micro) var(--e-out),
              background var(--d-micro) var(--e-out),
              transform var(--d-micro) var(--e-out);
}

.chi-scelta img,
.chi-scelta__vuoto {
  width: 3.75rem;
  height: 5rem;
  object-fit: cover;
  border-radius: var(--r2);
  display: block;
}

/* «Chiunque» non ha una faccia: al suo posto un riquadro col filetto di rame,
   che lo tiene alla stessa altezza degli altri invece di farlo saltare su. */
.chi-scelta__vuoto {
  display: grid;
  place-items: center;
  background: var(--accento-velo);
  color: var(--accento);
}

.chi-scelta b { font-weight: 400; color: var(--fg); }
.chi-scelta small {
  font-size: var(--t-micro);
  color: var(--fg-3);
  line-height: 1.25;
}

.chi-scelta:hover { border-color: var(--linea-2); }

.chi-scelta--presa {
  border-color: var(--accento);
  background: var(--accento-velo);
  box-shadow: 0 0 0 1px var(--accento);
}

.chi-scelta:focus-visible {
  outline: 2px solid var(--accento);
  outline-offset: 2px;
}

/* Il nome sotto l'ora, quando in quel giorno le persone cambiano.
   `.orari-liberi label` è un `inline-flex` in riga: un figlio `block` gli si
   affianca invece di andare sotto, e si legge «09:00Giulia». Serve il ritorno
   a capo esplicito. */
.orari-liberi label {
  flex-wrap: wrap;
  row-gap: 0;
}

.orari-liberi label small {
  flex-basis: 100%;
  text-align: center;
  font-size: var(--t-micro);
  color: var(--fg-3);
  letter-spacing: 0;
  text-transform: none;
  margin-top: 2px;
}

.orari-liberi label:has(input:checked) small { color: oklch(0.86 0.02 62); }

@media (prefers-reduced-motion: no-preference) {
  .chi-scelta:active { transform: scale(0.98); }
}

/* ==================================================================== LA SOGLIA

   La prima schermata. Prima era il logo al centro della crema vuota: nessuna
   immagine, nessuna profondità, nessun estremo tonale — e senza estremi
   l'immagine è nebbia (ART.md §3, e la firma 12 delle diagnosi).

   Adesso è un piano di lino a tutto campo. Non un fondo: una superficie, con
   le pieghe che portano i neri veri e le creste che portano i bianchi. Il logo
   scende e sparisce come prima — quello funzionava — ma sparisce dentro
   qualcosa invece che nel vuoto. */

.soglia {
  position: relative;
  /* Il lino passa SOTTO la barra. Senza, la barra restava una fascia crema con
     un bordo netto che tagliava la fotografia in due — non un menu sopra
     un'immagine, ma due immagini impilate. Alla barra ci pensa `.barra--posata`:
     è trasparente finché non si scorre, e da lì diventa vetro. */
  margin-top: calc(-1 * var(--barra-h));

  /* ESATTAMENTE uno schermo, non «almeno uno schermo».
     Con `min-height` più il padding la sezione veniva più alta della finestra,
     e scorrendo restava una fascia morta: il logo mezzo sotto la barra, la
     polaroid affettata, il «Scendi» sospeso in mezzo al nulla. Non era un
     difetto dell'animazione (quella l'ho tolta): era questo. Alta uno schermo,
     esce tutta insieme — e quello che si vede a metà corsa è una pagina che
     scorre, non tre pezzi rotti che si guardano.

     `svh` e non `vh`: sul telefono `vh` conta la barra del browser come se non
     ci fosse, e la prima schermata nasce più alta della finestra proprio dove
     il difetto si vede peggio. */
  height: 100svh;
  min-height: 32rem;              /* su una finestra bassissima il logo respira */
  display: grid;
  place-items: center;
  padding: calc(var(--barra-h) + var(--s6)) var(--s5) var(--s8);
  overflow: clip;                 /* la polaroid sborda, ma non allarga la pagina */
  isolation: isolate;
}

.soglia__piano { position: absolute; inset: 0; z-index: -2; }
.soglia__lino {
  width: 100%; height: 100%; object-fit: cover; display: block;
  /* Il lino è quasi bianco: alzarne il contrasto è ciò che fa comparire le
     pieghe, e le pieghe sono l'unico nero vero di questa schermata. */
  filter: contrast(1.14) saturate(0.92) brightness(0.99);
}

/* Vignettatura calda, non nera: l'ombra prende il colore dell'ambiente
   (ART.md §3 — una sola luce, ombre brune). Il centro resta il punto più
   chiaro della pagina, ed è lì che sta il logo. */
.soglia::after {
  content: '';
  position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background:
    radial-gradient(120% 90% at 50% 38%,
      oklch(1 0 0 / .34) 0%, oklch(1 0 0 / .10) 42%, transparent 66%),
    radial-gradient(130% 110% at 50% 46%,
      transparent 48%, oklch(0.42 0.045 45 / .13) 86%, oklch(0.32 0.05 45 / .22) 100%);
}

/* Il contenitore: sul desktop è trasparente al layout (la polaroid dentro
   resta assoluta e va nell'angolo), sul telefono diventa la colonna. */
.soglia__dentro { display: contents; }
@media (max-width: 720px) {
  .soglia__dentro { display: block; text-align: center; }
}

.soglia__logo { text-align: center; position: relative; z-index: 1; }
.soglia__logo img {
  width: min(46vw, 360px); height: auto; display: block; margin-inline: auto;
  /* il logo è nero su trasparente: sul lino vuole la sua ombra, o galleggia */
  filter: drop-shadow(0 1px 0 oklch(1 0 0 / .6))
          drop-shadow(0 12px 26px oklch(0.32 0.05 45 / .16));
}
.soglia__logo p {
  margin: var(--s5) 0 0;
  font-size: var(--t-piccolo); letter-spacing: var(--s-micro);
  /* Era `--t-micro` in `--fg-2` sopra una fotografia chiara: sulla carta del
     progetto passava, sul lino vero non si leggeva. Su un'immagine il testo
     non basta che sia «scuro abbastanza» in astratto — deve staccare da
     QUELLA immagine, e il lino è quasi bianco. */
  text-transform: uppercase; color: var(--fg);
  text-shadow: 0 1px 0 oklch(1 0 0 / .55);
}

/* La rottura: una foto vera, posata di sbieco come un attimo fa, che esce dal
   bordo. Una sola per pagina basta a far sembrare che ci sia stato qualcuno a
   decidere (firma 8). */
.soglia__polaroid {
  position: absolute; z-index: 2; margin: 0;
  /* Una rottura si legge come rottura solo se si vede COSA si rompe: con la
     foto per due terzi fuori schermo sembrava un traboccamento, non una
     decisione. Adesso esce di un angolo, e il resto sta dentro. */
  right: clamp(1.5rem, 4vw, 5rem); bottom: clamp(5rem, 14svh, 9rem);
  width: clamp(9rem, 22vw, 15rem);
  transform: rotate(3.2deg);
  background: var(--bianco);
  padding: var(--s3) var(--s3) var(--s5);
  border-radius: var(--r2);
  box-shadow: 0 2px 4px oklch(0.32 0.05 45 / .14),
              0 34px 60px -26px oklch(0.32 0.05 45 / .46);
  transition: transform var(--d-base) var(--e-out);
}
.soglia__polaroid img { width: 100%; height: auto; display: block; border-radius: 1px; }
.soglia__polaroid figcaption {
  margin-top: var(--s3); text-align: center;
  font-size: var(--t-micro); letter-spacing: var(--s-micro);
  text-transform: uppercase; color: var(--fg-3);
}
.soglia__polaroid:hover { transform: rotate(1.4deg) translateY(-4px); }

/* «Scendi»: una riga che si allunga, non una freccia che rimbalza. */
.soglia__giu {
  position: absolute; z-index: 2; left: var(--s6); bottom: var(--s6);
  display: flex; align-items: center; gap: var(--s3);
  text-decoration: none; color: var(--fg);
  text-shadow: 0 1px 0 oklch(1 0 0 / .55);
  font-size: var(--t-micro); letter-spacing: var(--s-micro); text-transform: uppercase;
}
.soglia__giu i {
  display: block; width: 3.5rem; height: 1px; background: var(--linea-2);
  transform-origin: left; position: relative;
}
.soglia__giu i::after {
  content: ''; position: absolute; inset: 0 auto 0 0; width: 40%;
  background: var(--accento);
  animation: filo-scorre 2.6s var(--e-inout) infinite;
}
@keyframes filo-scorre {
  0%   { transform: translateX(-100%); }
  60%, 100% { transform: translateX(250%); }
}
.soglia__giu:hover { color: var(--fg); }

/* «Scendi» sparisce al primo pixel di scorrimento.
   È un invito: appena l'invito è stato accolto non ha più niente da dire, e
   restando attaccato al fondo dell'apertura finiva a mezzo schermo, sospeso
   nel vuoto, a somigliare a un pezzo staccato. Lo fa una classe su <html>
   messa da `cinetica.js`, non una `animation-timeline`: un interruttore ha due
   stati e nessun fotogramma intermedio in cui restare impigliato. */
.soglia__giu {
  transition: opacity var(--d-media) var(--e-out),
              transform var(--d-media) var(--e-out);
}
html.scorso .soglia__giu {
  opacity: 0; transform: translateY(0.75rem); pointer-events: none;
}

@media (max-width: 720px) {
  /* Su un telefono la polaroid finiva SOPRA la riga sotto il logo, e quella
     riga risultava tagliata a metà parola: «STIAMO PER AP». Una foto messa di
     sbieco è una rottura voluta, una foto che copre una frase è un errore — e
     si distinguono solo guardando la pagina su uno schermo stretto.
     Quindi: più piccola, più in basso, e il blocco del logo si stringe in modo
     che le due cose non si incontrino mai. */
  /* Su un telefono la polaroid smette di essere assoluta e diventa il terzo
     pezzo di una colonna sola: logo, riga, foto.

     Prima era in un angolo, e la riga sotto il logo le finiva dentro
     («PRENOTAZIONI SON▮ ATTIVE»). Allontanandole restava mezza schermata di
     lino vuoto in mezzo — il difetto peggiore dei due, perché una schermata
     vuota non sembra sobria: sembra che manchi qualcosa. In colonna si
     toccano senza sovrapporsi e riempiono l'altezza, e lo sbieco resta: è la
     rottura, e va tenuta. */
  .soglia { padding-inline: var(--s4); }
  .soglia__logo { max-width: 82vw; margin-inline: auto; }
  .soglia__logo img { width: min(56vw, 250px); }

  .soglia__polaroid {
    position: static;
    /* La didascalia deve restare DENTRO la prima schermata: tagliata a metà
       è peggio che assente, e su 664 pixel di finestra ogni rem conta. */
    width: min(34vw, 9rem);
    margin: var(--s5) auto 0;
    /* spostata di poco a destra: in asse perfetto sembrerebbe un terzo
       elemento del logo, non una foto posata lì */
    transform: rotate(2.6deg) translateX(6%);
  }
  .soglia__polaroid figcaption { font-size: 0.625rem; letter-spacing: .06em; }
  .soglia__giu { left: var(--s5); bottom: var(--s5); }
  .soglia__giu i { width: 2.25rem; }
}

/* L'USCITA DELL'APERTURA — quello che c'era prima, e perché non c'è più.
   ----------------------------------------------------------------------
   C'era una parallasse in uscita: il logo e la polaroid scendevano, il lino
   scendeva più piano. Fermo sembrava un'idea; scorrendo era un disastro. Per
   un intero schermo di scorrimento restavano a schermo un logo tagliato a metà
   dalla barra, una polaroid mozzata e un «Scendi» sospeso nel vuoto — e sotto
   cominciava già la sezione dopo. Non una transizione: tre pezzi rotti che si
   guardano.

   Aggiungeva anche un difetto peggiore: `animation-range: exit` lascia
   l'elemento a metà corsa finché la sezione non è uscita del tutto. Risalendo
   di scatto, quel fotogramma intermedio resta lì impresso — e sembra un sito
   rotto, perché in quel momento lo è.

   Adesso l'apertura esce come esce una pagina: sale e basta. Il movimento sta
   dove non può rompersi (la luce che segue il puntatore, il titolo che ha un
   peso), e la giunzione con la sezione dopo la fa una sfumatura invece di un
   taglio netto. Meno macchina, zero stati a metà. */

/* La giunzione: il lino non finisce con una riga orizzontale netta contro il
   rosa della sezione dopo — sfuma dentro. Era l'altra cosa che si vedeva
   subito, e faceva sembrare la pagina montata con due ritagli. */
.soglia::before {
  content: '';
  position: absolute; left: 0; right: 0; bottom: 0; height: clamp(7rem, 20svh, 14rem);
  /* z-index 0, non -1: sotto ci sta la vignettatura, che in fondo scurisce e si
     interrompeva di netto sul bordo della sezione. Era LEI il taglio che si
     vedeva — una riga orizzontale scura fra il lino e il rosa, che faceva
     sembrare la pagina montata con due ritagli. La sfumatura deve passarle
     sopra. Resta sotto al logo (1) e alla polaroid (2), che non si toccano. */
  z-index: 0; pointer-events: none;
  background: linear-gradient(to bottom,
    transparent 0%, oklch(from var(--bg-0) l c h / .55) 55%, var(--bg-0) 100%);
}

/* ---------------------------------------------------- la luce che ti segue

   Il dettaglio che costa fatica, e che si vede solo muovendo il mouse. Non è
   un cursore disegnato: è una lampada che scalda il lino dove stai guardando,
   e ci arriva mezzo secondo dopo di te (l'interpolazione sta in cinetica.js).
   A gesto concluso non succede niente: succede DURANTE, ed è quella la
   differenza fra un frontend di adesso e uno di sette anni fa. */
.soglia {
  --pt-x: .5;
  --pt-y: .42;
}
@media (hover: hover) and (pointer: fine) {
  .soglia__piano::after {
    content: '';
    position: absolute; inset: 0; pointer-events: none;
    background: radial-gradient(
      26rem 26rem at calc(var(--pt-x) * 100%) calc(var(--pt-y) * 100%),
      oklch(0.98 0.035 78 / .40) 0%,
      oklch(0.96 0.030 72 / .16) 38%,
      transparent 70%);
    mix-blend-mode: soft-light;
  }
}

/* ------------------------------------------------- tipografia con un peso

   Le parole del titolo non stanno ferme: si piegano con la velocità dello
   scorrimento e si riassestano dopo, ognuna con un ritardo suo. Non è un
   fade-in-up (firma 9): è massa. Il valore arriva da `--scorsa`, che a riposo
   vale 0 — quindi senza JS, o con «riduci il movimento», questa regola è
   l'identità e il titolo è semplicemente un titolo. */
/* Il valore di riposo sta su :root, NON su `.cinetico`.
   Dichiararlo qui sembrava più ordinato e invece spegneva tutto: `cinetica.js`
   scrive `--scorsa` come stile in linea su <html>, e una dichiarazione su
   `.cinetico` è più vicina all'elemento — quindi vinceva sempre lei, e la
   trasformazione restava l'identità. Il valore c'era, il movimento no, e dal
   codice sembrava giusto. */
:root { --scorsa: 0; }
.cinetico span {
  display: inline-block;
  transform:
    translateY(calc(var(--scorsa) * var(--peso, 1) * -0.95rem))
    skewY(calc(var(--scorsa) * var(--peso, 1) * -1.75deg));
  will-change: transform;
}
/* i pesi scalano: la prima parola si muove di più, l'ultima quasi niente.
   Uguali, si muoverebbero come un blocco solo — e un blocco solo non ha peso. */
.cinetico span:nth-child(1) { --peso: 1; }
.cinetico span:nth-child(2) { --peso: .74; }
.cinetico span:nth-child(3) { --peso: .48; }
.cinetico span:nth-child(4) { --peso: .26; }

/* ============================================================ L'INVITO ALL'APP

   Compare una volta sola, al primo ingresso. Sta in basso, non in mezzo: un
   riquadro che copre la pagina appena si apre è la cosa che fa chiudere la
   scheda, e chiedere di installare qualcosa a chi non ha ancora visto niente
   è chiedere troppo presto.

   È vetro come la barra — stessa famiglia, non un componente venuto da fuori. */
.app-invito {
  position: fixed; z-index: 500;
  left: 50%;
  /* su iPhone in basso ci sono la barra di Safari e la tacca del gesto: senza
     `safe-area` la scheda finisce mezza sotto, e proprio lì c'è il bottone */
  bottom: calc(var(--s5) + env(safe-area-inset-bottom, 0px));
  width: min(30rem, calc(100vw - 2 * var(--s5)));
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--s3) var(--s4);
  align-items: start;
  padding: var(--s4) var(--s5);
  background: var(--vetro);
  backdrop-filter: blur(18px) saturate(1.5);
  -webkit-backdrop-filter: blur(18px) saturate(1.5);
  border: 1px solid oklch(from var(--linea) l c h / .6);
  border-radius: var(--r3);
  box-shadow: var(--ombra-dentro), var(--ombra-3);
  /* entra da sotto, e ci arriva con una molla invece che con una rampa */
  transform: translate(-50%, calc(100% + var(--s6)));
  opacity: 0;
  transition: transform 620ms var(--e-molla, cubic-bezier(0.16, 1.14, 0.32, 1)),
              opacity 300ms var(--e-out);
}
.app-invito--dentro { transform: translate(-50%, 0); opacity: 1; }

.app-invito > img {
  width: 52px; height: 52px; border-radius: var(--r2);
  /* il monogramma è crema su crema: sul vetro sparirebbe. Il bordo gli dà il
     contorno che ha un'icona sulla schermata iniziale — che è esattamente
     quello che la scheda sta promettendo. */
  border: 1px solid oklch(from var(--linea) l c h / .8);
  box-shadow: var(--ombra-2);
}
.app-invito__testo b { font-size: var(--t-corpo); font-weight: 500; }
.app-invito__testo p {
  margin: var(--s2) 0 0; font-size: var(--t-piccolo); color: var(--fg-2);
}
.app-invito__ios b { font-weight: 500; color: var(--fg); }
.app-invito__tasti {
  grid-column: 2; display: flex; gap: var(--s4); align-items: center;
  margin-top: var(--s2);
}
.app-invito__tasti .bottone { padding-block: .55rem; }

/* Sul telefono diventa una striscia, non una scheda.
   Com'era, occupava il quaranta per cento della prima schermata. Il primo
   tentativo di stringerla è andato peggio: tre colonne con «No, grazie» per
   esteso nell'ultima, e al testo restava una colonna così stretta che
   «Aggiungi a Home» finiva su tre righe. Su 390 pixel non ci stanno una
   spiegazione e due bottoni: ci stanno una riga e una ×. */
@media (max-width: 560px) {
  .app-invito {
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--s3);
    padding: var(--s3) var(--s4);
    border-radius: var(--r2);
    width: calc(100vw - 2 * var(--s4));
    bottom: calc(var(--s4) + env(safe-area-inset-bottom, 0px));
  }
  .app-invito > img { width: 32px; height: 32px; align-self: center; }
  .app-invito__lunga { display: none; }
  .app-invito__testo { min-width: 0; }
  /* `> b`, non `b`: il selettore discendente prendeva anche i «Condividi» e
     «Aggiungi a Home» dentro la frase e li faceva diventare blocchi, cioè la
     riga di istruzioni andava a capo quattro volte. Un `display: block` messo
     largo è il modo più rapido di rompere un testo scritto bene. */
  .app-invito__testo > b { font-size: var(--t-piccolo); font-weight: 500;
                           line-height: 1.2; display: block; }
  .app-invito__ios {
    margin-top: 2px; font-size: var(--t-micro); line-height: 1.35;
    /* Niente `nowrap` con i puntini: tagliava «Aggiu…», cioè proprio la parola
       che dice cosa fare. Un invito a installare che non si può leggere fino
       in fondo non è più corto — è inutile. Due righe e si legge tutto. */
    text-wrap: pretty;
  }
  .app-invito__ios b { font-weight: 500; }

  /* i tasti: solo quello che serve, e «No grazie» diventa una × */
  .app-invito__tasti { grid-column: auto; margin-top: 0; gap: var(--s3); }
  .app-invito__tasti .bottone { padding: .4rem .75rem; font-size: var(--t-micro);
                                white-space: nowrap; }
  .app-invito__tasti [data-app-chiudi] {
    font-size: 0; line-height: 0; padding: .5rem; border: 0; background: none;
    text-decoration: none; box-shadow: none;
  }
  .app-invito__tasti [data-app-chiudi]::before {
    content: '\00d7'; font-size: 1.5rem; line-height: 1; color: var(--fg-2);
  }
}

@media (prefers-reduced-motion: reduce) {
  .app-invito { transition: opacity 200ms linear; transform: translate(-50%, 0); }
}

/* ==================================================== LA SALA DI LUCE

   La tela sta SOPRA il lino e sotto il logo, e comincia invisibile. Si accende
   solo quando `sala.js` ha davvero disegnato un fotogramma (classe `sala-viva`
   sull'html), e in quel momento il lino si dissolve sotto di lei.

   L'ordine conta: prima appare la sala, POI sparisce il lino. Al contrario ci
   sarebbe un istante di nero fra le due — ed è esattamente l'istante che fa
   sembrare un sito rotto. */
.soglia__sala {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  display: block;
  opacity: 0;
  pointer-events: none;
  transition: opacity 1100ms var(--e-out);
}
.sala-viva .soglia__sala { opacity: 1; }
.sala-viva .soglia__lino { opacity: 0; transition: opacity 900ms var(--e-out) 380ms; }

/* ------------------------------------- il poster: quello che si vede PRIMA
   «Mi appare sempre il vecchio sfondo e poi quello nuovo animato». Erano due
   immagini diverse, e fra le due passava il tempo di scaricare e accendere
   seicento kilobyte. Adesso quello che si vede prima È la scena: un suo
   fotogramma, dieci kilobyte, dipinto subito.

   La tela poi si accende SOPRA il poster, non al posto suo, e il poster non
   si toglie: sono la stessa immagine, una ferma e una viva, e togliere quella
   sotto non cambierebbe niente se non il rischio di un lampo nel mezzo. */
.sala-in-arrivo .soglia__piano {
  background-image: url("../img/sala/soglia-alto.webp");
  background-size: cover;
  background-position: 50% 42%;
  /* il fondo della sala, per il primo istante e per i bordi che il ritaglio
     non copre: un nero freddo qui sarebbe una porta su un'altra stanza */
  background-color: oklch(0.17 0.014 40);
}
@media (min-width: 721px) {
  .sala-in-arrivo .soglia__piano {
    background-image: url("../img/sala/soglia-largo.webp");
  }
}
/* Il lino non si dipinge nemmeno: sotto al poster non si vedrebbe, e
   scaricarlo per nasconderlo sarebbe una foto grande chiesta per niente. */
.sala-in-arrivo .soglia__lino { display: none; }

/* Sulla sala il testo sta su un fondo scuro e mosso: le due righe che prima
   erano brune su lino diventano chiare, e si portano dietro l'ombra che le
   stacca dalla polvere. */
/* `invert(1)` su un logo bruno restituisce un ciano, non un bianco: lo
   scambio dei canali non è una schiaritura. `brightness(0)` lo appiattisce a
   nero pieno, `invert(1)` lo ribalta a bianco pieno, e la forma resta intatta
   perché l'alfa non viene toccata. */
.sala-viva .soglia__logo img,
.sala-in-arrivo .soglia__logo img { filter: brightness(0) invert(1) opacity(0.94); }
.sala-viva .soglia__logo p,
.sala-viva .soglia__giu,
.sala-in-arrivo .soglia__logo p,
.sala-in-arrivo .soglia__giu { color: var(--fg-su-scuro); text-shadow: 0 1px 14px var(--nero); }

/* Il velo sotto il titolo.
   Dentro la sala il logo cade nel punto più affollato dell'inquadratura: le
   lame gli passano dietro e «NAILS ROOM» si legge a pezzi. È il problema che
   ha ogni titolo di testa su una ripresa, e si risolve come al cinema — con un
   velo morbido dietro le lettere, non spostando la ripresa. Sfuma a zero prima
   dei bordi: non è un riquadro, è un abbassamento di luce. */
.sala-viva .soglia__logo,
.sala-in-arrivo .soglia__logo { position: relative; isolation: isolate; }
.sala-viva .soglia__logo::before,
.sala-in-arrivo .soglia__logo::before {
  content: ''; position: absolute; z-index: -1;
  inset: -18% -26%;
  background: radial-gradient(60% 52% at 50% 46%,
    oklch(from var(--nero) l c h / .62) 0%,
    oklch(from var(--nero) l c h / .34) 46%,
    transparent 74%);
  pointer-events: none;
}
.sala-viva .soglia__giu i,
.sala-in-arrivo .soglia__giu i { background: var(--accento-su-scuro); }
/* la polaroid esce: dentro la sala i lavori ci sono già, appesi nell'aria, e
   averli due volte nella stessa schermata li fa contare zero volte */
/* La polaroid esce già col poster, non quando la tela si accende: nel
   poster i lavori sono già appesi nell’aria, e farla comparire per poi
   toglierla un secondo dopo è un altro lampeggio — lo stesso che stiamo
   togliendo, in piccolo. */
.sala-viva .soglia__polaroid,
.sala-in-arrivo .soglia__polaroid { opacity: 0; transition: opacity 700ms var(--e-out); }
/* e il velo che cuciva il lino alla sezione dopo non serve più: la sala
   finisce nel suo bruno, che è già il colore della fascia successiva */
.sala-viva .soglia::before,
.sala-in-arrivo .soglia::before { opacity: 0; }
