/* =============================================================================
   Modulo: servizi, scena in scroll.

   Stessa struttura a due layout della hero, e per le stesse ragioni: il default
   è la versione statica — un titolo e una lista di link — e il layout animato sta
   dentro `@media (scripting: enabled) and (prefers-reduced-motion: no-preference)`.

   Qui il JavaScript scrive ancora meno che nella hero: **un intero**, l'indice
   del servizio attivo, più tre classi di stato sugli elementi della lista. Non
   interpola niente, e quando l'indice non cambia non tocca il DOM. Il movimento
   — lo scorrimento della colonna e i crossfade — sono `transition` CSS che
   partono da quel cambio di indice. Per questo il modulo non ha bisogno di un
   `requestAnimationFrame`: non c'è niente da ridisegnare a ogni frame.

   La geometria della card viene dai token `--scene-card-*` di base.css, gli
   stessi che legge la hero: è il contratto che tiene le due card nello stesso
   punto dello schermo.
   ========================================================================== */

.services-scroll {
    /* Passo della colonna: la distanza fra un nome e il successivo, e quindi
       anche di quanto scorre la lista a ogni scatto. Un valore solo per le due
       cose, così non possono andare fuori sincrono.

       160px nel file di design — i nomi stanno a y=200, 360, 520, 680 — su un
       frame da 1512, cioè 10,58vw. Il Make ne aveva 200, ma là i nomi erano in
       display: qui sono a 40px, e il passo del design è quello che ci sta
       attorno. */
    --service-step: clamp(6rem, 10.58vw, 10rem);

    /* La freccia accanto al nome attivo: quanto il nome si sposta per farle
       posto, e quanto è larga.

       Dal design — Services, nodo 524:3358, la variante con la freccia dello
       stesso componente che nel frame accanto ne è senza. Misurato sui due
       render: il bordo destro del nome si sposta di **esattamente 24px** su un
       nome da 24, e il disegno esportato — 18 × 14,728 — sta al centro di quei
       24, cioè 3px di margine per parte. Il centraggio non è quindi un valore da
       tenere: si ricava dai due qui sotto, ed è così che lo calcola la regola.

       In `em` e non in px, così la freccia segue il corpo del nome: nel design
       sta accanto a un nome da 24, qui accanto a uno da 40. */
    --service-arrow-lead: 1em;
    --service-arrow-width: 0.75em;

    /* Lo stacco fra una casella e l'altra: nel design è 120, ed è il passo meno
       l'altezza di un nome. Scritto come quella sottrazione e non come un numero,
       perché è la stessa misura di `--service-step` e deve restare tale.

       Lo leggono in due, la colonna e la lista dentro di essa, ed è ciò che fa
       leggere le due come una pila sola. */
    --slot-gap: calc(var(--service-step) - var(--text-title));

    /* Quanto scroll vale una casella. `--slot-count` arriva dal PHP, e conta i
       paragrafi oltre ai nomi. */
    --slot-length: 100vh;

    /* Lo scorrimento della colonna, **in pixel e scritto dal JS**.

       ⚠ Prima era `calc(var(--active) * var(--service-step) * -1)`, cioè il CSS lo
       ricavava da un intero. Non si può più: da quando i paragrafi sono caselle, le
       caselle non hanno tutte la stessa altezza — un nome è alto una riga, un
       paragrafo tre — e la posizione della casella attiva non è più il suo indice
       per il passo. La misura il JS dal DOM, una volta per resize, e qui arriva già
       fatta. Vedi il JS, `measure()`. */

    position: relative;
    color: var(--color-scene-text);
}

/* Il fondo scuro sta sulla **card**, non sulla sezione: nel design la sezione
   lascia vedere il fondo pagina col pattern, e qui si vede per davvero — la card
   occupa solo la metà destra. Sul riquadro serve, per il tempo in cui il filmato
   non è arrivato. */
.services-scroll__card {
    background-color: var(--color-scene-bg);
}

/* -----------------------------------------------------------------------------
   Layout statico: nessun JavaScript, oppure movimento ridotto.
   -------------------------------------------------------------------------- */

.services-scroll__viewport {
    padding-block: var(--space-section);
}

.services-scroll__scene {
    position: relative;
    display: grid;
    gap: var(--space-2xl);
    width: 100%;
    max-width: var(--container-max);
    margin-inline: auto;
    padding-inline: var(--gutter);
    overflow: hidden;
}

.services-scroll__card {
    position: relative;
    aspect-ratio: 16 / 9;
    overflow: hidden;
    border-radius: var(--radius-l);
}

/* In statico la card mostra il primo servizio e basta: sono tutti sovrapposti,
   e senza questa regola si vedrebbe solo l'ultimo. I video hanno
   `preload="none"`, quindi restano sui rispettivi poster senza scaricare nulla. */
.services-scroll__media:not(:first-child) {
    display: none;
}

.services-scroll__media {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
}

/* Il gap della scena separa la card dal pannello, non il titolo dalla lista: sono
   entrambi dentro il pannello, che quindi ha la propria spaziatura. */
.services-scroll__panel {
    display: grid;
    gap: var(--space-l);
}

/* In statico la colonna è una pila normale, e i due stacchi **non** sono quelli
   del design: 120px fra un nome e l'altro sono il passo di una scena in pin, non
   di una lista che si legge tutta insieme. Qui valgono i token di spaziatura. */
.services-scroll__column {
    display: grid;
    gap: var(--space-l);
}

/* I paragrafi d'apertura, che nel design (nodo 436:3126) sono testo corrente e
   non un titolo: body-lg — 24px su 40 di interlinea, peso 500, largo 666 su una
   colonna da 716.

   ⚠ **In statico non prendono la rampa**, e questo è un difetto corretto: il 16%
   di bianco è un gradino del layout animato, dove il paragrafo lontano da quello
   attivo è appena accennato. Senza rampa quel valore è solo testo illeggibile —
   con un titolo breve passava, con due paragrafi no. L'opacità sta quindi nel
   blocco animato, dove c'è la rampa che le dà senso. */
.services-scroll__paragraph {
    max-width: 42rem;
    margin: 0;
    font-size: var(--text-body-l);
    line-height: var(--leading-body-l);
    font-weight: var(--weight-medium);
    font-feature-settings: "lnum", "pnum";
}

.services-scroll__list {
    display: grid;
    gap: var(--space-m);
    list-style: none;
    margin: 0;
    padding: 0;
}

.services-scroll__link {
    display: block;
    font-size: var(--text-title);
    line-height: var(--leading-solid);
    font-weight: var(--weight-black);
    text-transform: uppercase;
    text-decoration: none;
    font-feature-settings: "lnum", "pnum";
}

/* -----------------------------------------------------------------------------
   Layout animato.
   -------------------------------------------------------------------------- */

/* soglia scene: 901px — vedi base.css → "Passaggio fra scene" */
@media (scripting: enabled) and (prefers-reduced-motion: no-preference) and (min-width: 901px) {

    /* Una schermata per servizio, più quella che resta in pin, più i due tratti
       del passaggio fra scene. Vedi base.css → "Passaggio fra scene". */
    .services-scroll {
        height: calc(
            100vh
            + var(--scene-enter, 0px)
            + var(--slot-count, 1) * var(--slot-length)
            + var(--scene-hold, 0px)
        );
    }

    /* La dissolvenza d'ingresso. `--enter` la scrive il JS sulla finestra di
       sovrapposizione con la scena precedente; senza scena sopra vale 1 e non
       succede niente.

       Sta sulla sezione e non sul viewport perché il fondo scuro è dichiarato
       sulla sezione: mettendola sul figlio, lo sfondo resterebbe opaco e
       coprirebbe la scena sottostante proprio durante la dissolvenza.

       `pointer-events` è la parte che non si vede e che va ricordata: prima di
       inchiodarsi questa sezione sale dal basso per una schermata, invisibile ma
       ancora cliccabile, e si mangerebbe i click su quello che le sta sotto. */
    .services-scroll {
        opacity: var(--enter, 1);
    }

    .services-scroll.is-entering {
        pointer-events: none;
    }

    .services-scroll__viewport {
        position: sticky;
        inset-block-start: 0;
        height: 100vh;
        padding-block: 0;
        overflow: hidden;
    }

    .services-scroll__scene {
        display: block;
        height: 100%;
        max-width: none;
        margin-inline: 0;
        padding-inline: 0;
    }

    /* La card, ferma per tutta la scena: qui non si trasforma, si trasforma nella
       hero. Questi sono gli stessi quattro token, quindi finisce nello stesso
       punto in cui la hero l'ha portata. */
    .services-scroll__card {
        position: absolute;
        inset-inline-start: var(--scene-card-x);
        inset-block-start: var(--scene-card-y);
        width: var(--scene-card-w);
        height: var(--scene-card-h);
        aspect-ratio: auto;
        z-index: 1;
    }

    /* Tutti i media sovrapposti e trasparenti: il crossfade è la transizione di
       opacità di quello che diventa attivo. */
    .services-scroll__media {
        position: absolute;
        inset: 0;
        opacity: 0;
        transition: opacity var(--duration-slow) var(--ease-scene);
    }

    .services-scroll__media:not(:first-child) {
        display: block;
    }

    .services-scroll__media.is-active {
        opacity: 1;
    }

    /* La colonna sinistra. Larga come la card e incassata del gutter: nel design
       le due colonne sono entrambe 716 su 1512, la sinistra attaccata al margine
       di pagina e la destra al margine opposto.

       ⚠ Il margine è `inset-inline-start` e **non un padding**, ed è una
       correzione: il padding qui non serviva a niente, perché il blocco
       contenitore di un figlio in assoluto è il *padding box* del pannello — i
       nomi finivano incollati al bordo dello schermo, non al margine di pagina.
       Con l'inset il pannello si sposta per intero, e i figli con `inset-inline: 0`
       cadono dove devono. */
    .services-scroll__panel {
        position: absolute;
        inset-block: 0;
        inset-inline-start: var(--gutter);
        width: var(--scene-card-w);
        z-index: 2;
    }

    /* **Il paragrafo si vede**, e questa è la differenza col Make: là non c'era e
       qui era nascosto agli occhi come un titolo di servizio. Nel design è la
       riga sopra il primo nome, e scorre con la colonna.

       Il suo posto è espresso rispetto alla lista: il suo fondo sta mezza riga di
       nome sopra il bordo alto della lista, che è quello che nel design mette 120px
       fra il paragrafo e "Digital signage" — cioè il passo meno l'altezza del
       nome. La formula è quella, non due valori da tenere d'accordo. */
    /* -------------------------------------------------------------------------
       La colonna.

       Una pila sola di caselle — i paragrafi e poi i nomi — ancorata a metà
       schermo e traslata perché la casella attiva finisca **col proprio centro**
       su quella riga. È il JS a dire di quanto, in pixel, perché le caselle non
       sono alte uguale e la posizione della attiva non si ricava dal suo indice.

       ⚠ `inset-block-start: 50%` e non `inset-block-end`: la colonna cresce verso
       il basso a partire dalla riga di mezzo, e il JS la tira su. Con l'ancoraggio
       in fondo il conto del JS avrebbe il segno opposto per metà delle caselle.

       Tutti e due gli inset inline, non solo uno: con `inset-inline-end` da solo un
       elemento in assoluto si restringe al contenuto e si appoggia a destra.

       Il fallback `0px` è il primo fotogramma prima che il JS scriva: la colonna
       parte da metà schermo invece che centrata sulla prima casella. Non è la posa
       giusta, ma è composta — e dura al più un frame, perché il modulo è un ES
       module differito che gira prima del primo paint. Un default esatto non è
       esprimibile in CSS: dipende dall'altezza della prima casella, che è il
       numero di righe di un paragrafo. */
    .services-scroll__column {
        position: absolute;
        inset-block-start: 50%;
        inset-inline: 0;
        display: flex;
        flex-direction: column;
        gap: var(--slot-gap);
        margin: 0;
        translate: 0 var(--wheel-y, 0px);
        transition: translate var(--duration-slower) var(--ease-scene);
    }

    /* La lista non si posiziona più da sé: è una casella della colonna come i
       paragrafi, e porta lo **stesso** `gap`, che è ciò che fa leggere le due come
       una pila unica.

       ⚠ Niente altezza fissa sulle righe, e prima c'era. Con caselle alte uguali il
       passo si otteneva dando a ogni riga l'altezza del passo; adesso l'altezza è
       quella del contenuto — una riga per un nome, tre per un paragrafo — e a fare
       il passo è il `gap`. Rimettere un'altezza fissa qui romperebbe la misura che
       il JS fa dei centri. */
    .services-scroll__list {
        display: flex;
        flex-direction: column;
        gap: var(--slot-gap);
        margin: 0;
        padding: 0;
    }

    /* La rampa, una sola regola per tutte le caselle: attiva piena, vicina a metà,
       lontana appena accennata. Sono i tre valori del design (nodo 436:3138), e
       valgono tanto per un nome quanto per un paragrafo — è quello che li fa
       leggere come la stessa pila invece che come due cose diverse messe vicine. */
    .services-scroll__slot {
        opacity: 0.16;
        transition: opacity var(--duration-slow) var(--ease-scene);
    }

    .services-scroll__slot.is-near {
        opacity: 0.5;
    }

    .services-scroll__slot.is-active {
        opacity: 1;
    }

    /* Il nome attivo prende il riempimento sfumato del design; gli altri restano
       bianchi e ci pensa l'`opacity` della riga a portarli a 0,5 e 0,16, che sul
       bianco è esattamente il rgba del design. Sull'attivo la sfumatura è invece
       una cosa in più, e sta qui e non in `.display-gradient` sul markup perché
       dipende dallo stato, non dall'elemento.

       Il corpo resta quello del layout statico — 40px, `--text-title` — e non
       `--text-display`: nel design i nomi sono `title-upper-md`, cioè 40, non i
       72 che il Make usava.

       ⚠ Il padding e il margine negativo sono la **stessa correzione** di
       `.display-gradient` in base.css, e vanno insieme al riempimento: senza,
       l'accento di un nome di servizio accentato — su sei lingue arriva — resta
       fuori dall'area che il riempimento può dipingere e si vede tagliato. Misurato:
       2,5px a 40px con interlinea 1. Il perché per esteso è nel commento su
       `.display-gradient`; qui non si può usare la classe perché il riempimento
       dipende dallo stato. */
    .services-scroll__item.is-active .services-scroll__link {
        padding-block: var(--fill-display-bleed);
        margin-block: calc(-1 * var(--fill-display-bleed));
        background-image: var(--fill-display);
        -webkit-background-clip: text;
        background-clip: text;
        color: transparent;
    }

    /* -------------------------------------------------------------------------
       Il nome attivo porta a una pagina, gli altri portano a sé.

       Dentro la scena un click su un nome non attivo non naviga: porta a fuoco
       quel servizio, cioè scorre la pagina fino alla sua schermata. È il JS a
       farlo. Qui c'è solo il segno della differenza — la freccia — perché senza
       due nomi identici farebbero due cose diverse.
       ---------------------------------------------------------------------- */

    /* Il cursore anche sui nomi senza `link`, che sono uno `span`: nella scena
       sono cliccabili comunque, perché l'azione è lo scroll e non la navigazione. */
    .services-scroll__item:not(.is-active) .services-scroll__link {
        cursor: pointer;
    }

    /* Solo su un `a`: un nome senza pagina non ha dove andare, quindi non prende
       né la freccia né lo spostamento.

       ⚠ **Lo spostamento è un padding, non un `translate`.** Il bersaglio dello
       hover è la scatola del link, che è larga quanto il nome: traslandola verso
       destra il puntatore che sta sul bordo sinistro ci resta fuori, lo hover
       cade, il nome torna indietro — e ricomincia. Con il padding la scatola
       **cresce solo a destra** e il bordo sinistro non si muove, quindi il
       puntatore che ha aperto lo stato è sempre ancora dentro. È un'animazione di
       layout su un elemento solo, allo hover: il prezzo è quello, il tremolio no. */
    a.services-scroll__link {
        position: relative;
        transition: padding-inline-start var(--duration-base) var(--ease-out);
    }

    /* Il disegno è `assets/icons/arrow-right.svg` in maschera invece che inline:
       è la decorazione di uno stato, e in maschera esiste una volta sola invece
       che in una copia per servizio.

       ⚠ Il colore **non può essere `currentColor`**: dentro il nome attivo
       `color` è `transparent`, perché è da lì che si vede il riempimento sfumato.
       Prende quindi lo stesso riempimento, non una tinta: la freccia compare solo
       accanto al nome attivo, che è sfumato, e in bianco pieno si staccava.

       ⚠ **Il riempimento va dimensionato sulla scatola del nome, non su quella
       della freccia**, ed è la parte che non si indovina. Le fermate di
       `--fill-display` sono in percentuale (−192% e 115,5%), quindi si riscalano
       con la scatola: sulla freccia, che è alta meno della metà, la stessa rampa
       si comprime e la freccia esce **più contrastata delle lettere che ha
       accanto**. Misurato a 40px: nella fascia occupata dalla freccia le lettere
       vanno da rgb(203,221,242) a rgb(230,238,248), mentre il riempimento sulla
       scatola della freccia darebbe da rgb(187,210,237) a rgb(246,249,253) — il
       doppio di escursione.

       Da qui `background-size`: si dà al riempimento l'altezza della scatola
       dipinta del nome — il suo `1em` più i due sbordi — e lo si centra. La
       freccia è già centrata sulla stessa scatola, quindi i due riempimenti
       cadono allineati e alla stessa altezza c'è lo stesso inchiostro.

       `no-repeat` perché la piastrella è più alta della scatola: senza, il
       motore ne accosterebbe altre sopra e sotto: fuori dalla vista, ma inutili.

       La freccia è ancorata al bordo sinistro della scatola, che non si muove:
       resta ferma mentre il nome le scorre via da sotto. */
    a.services-scroll__link::before {
        content: "";
        position: absolute;
        inset-inline-start: calc((var(--service-arrow-lead) - var(--service-arrow-width)) / 2);
        inset-block-start: 50%;
        width: var(--service-arrow-width);
        aspect-ratio: 18 / 14.7279;
        background-image: var(--fill-display);
        background-size: 100% calc(1em + 2 * var(--fill-display-bleed));
        background-position: center;
        background-repeat: no-repeat;
        -webkit-mask: url("../../../icons/arrow-right.svg") center / contain no-repeat;
        mask: url("../../../icons/arrow-right.svg") center / contain no-repeat;
        opacity: 0;
        translate: 0 -50%;
        transition: opacity var(--duration-base) var(--ease-out);
    }

    /* `:focus-visible` accanto a `:hover` perché da tastiera la differenza è la
       stessa: il primo Invio porta il servizio a fuoco, il secondo apre la
       pagina. La freccia che compare sul nome appena messo a fuoco è ciò che
       annuncia il secondo.

       ⚠ **Non mentre la pagina scorre.** `is-scrolling` la scrive il JS a ogni
       raffica di scroll e la toglie a scroll fermo. Senza, cliccare un nome per
       portarlo a fuoco lo fa diventare attivo *mentre sta ancora salendo*: sotto un
       puntatore che non si è mosso la freccia entra e il nome si sposta a metà
       corsa, e le due animazioni si sommano. E scorrendo a mano con il puntatore
       appoggiato sulla colonna, ogni nome che passa sotto si accenderebbe per un
       istante.

       Lo hover è un puntatore fermo su un nome fermo: quando è il nome a muoversi,
       hover non è. */
    .services-scroll:not(.is-scrolling) .services-scroll__item.is-active a.services-scroll__link:is(:hover, :focus-visible) {
        padding-inline-start: var(--service-arrow-lead);
    }

    .services-scroll:not(.is-scrolling) .services-scroll__item.is-active a.services-scroll__link:is(:hover, :focus-visible)::before {
        opacity: 1;
    }
}
