/* Sistema di design delle pagine che incassano denaro.
 *
 * Nasce con la pagina biglietti di Valle Orco e vive qui perché il tesseramento
 * deve avere lo stesso aspetto: stessa scala di superfici, stessi passi
 * numerati, stesso riepilogo che segue lo scorrimento, stessa barra di acquisto
 * su telefono. Due checkout che si somigliano solo a occhio divergono al primo
 * ritocco.
 *
 * Lo usano biglietti.html e tesseramento.html. Le pagine tengono nel proprio
 * tag <style> solo ciò che è davvero loro — le tariffe e i partecipanti di un
 * evento, il modulo di affiliazione di una tessera — e prendono da qui tutto
 * il resto.
 *
 * Si applica a un antenato con class="checkout-page".
 *
 * Le pagine lo agganciano con `?v=N` (come js/admin.js): _headers tiene /css/*
 * in cache un'ora con stale-while-revalidate, e da qui dipende ormai tutta
 * l'impaginazione di due pagine — non solo qualche colore. Alzare N a ogni
 * modifica di questo file.
 */

/* --- Token ---------------------------------------------------------------
   In dark il tema globale porta --border a `transparent` e --glass-bg allo
   stesso #1a1a1a delle card: i riquadri annidati (opzioni, metodi di
   pagamento, consensi) sparivano dentro la card che li contiene. Serve una
   scala dedicata, dove la gerarchia card > riga > campo resta leggibile. */

.checkout-page {
  --tk-border: rgba(0, 0, 0, 0.09);
  --tk-border-strong: rgba(0, 0, 0, 0.18);
  --tk-surface: rgba(0, 0, 0, 0.025);
  --tk-surface-hover: rgba(0, 0, 0, 0.045);
  --tk-selected: rgba(34, 110, 247, 0.07);
  --tk-radius-card: 24px;
  --tk-radius-row: 16px;
  --tk-danger: #c62d22;
  --tk-danger-bg: rgba(255, 59, 48, 0.09);
  --tk-danger-border: rgba(255, 59, 48, 0.28);
  /* Il verde della scala: un passo concluso, una commissione che non c'è. Fa
     coppia con --tk-danger, e come quello ha una tinta più chiara in dark —
     lo stesso verde su fondo nero diventa illeggibile. */
  --tk-ok: #157f3d;
  /* Il colore di quello che ci sta sopra: in chiaro il verde è scuro e il segno
     è bianco, in scuro si scambiano. La spunta di un passo concluso deve
     leggersi in tutti e due i temi. */
  --tk-ok-ink: #fff;
}

[data-theme="dark"] .checkout-page {
  --tk-border: rgba(255, 255, 255, 0.09);
  --tk-border-strong: rgba(255, 255, 255, 0.24);
  --tk-surface: rgba(255, 255, 255, 0.045);
  --tk-surface-hover: rgba(255, 255, 255, 0.075);
  --tk-selected: rgba(64, 112, 244, 0.14);
  --tk-danger: #ff9b91;
  --tk-danger-bg: rgba(255, 59, 48, 0.14);
  --tk-danger-border: rgba(255, 59, 48, 0.32);
  --tk-ok: #5fd68a;
  --tk-ok-ink: #0b2c18;
}

/* --- Impalcatura ---------------------------------------------------------- */

/* Il checkout è l'unico posto del sito che può entrare nei margini.
   Altrove il contenuto sta nel 70% della pagina (--page-gutter, in
   css/style.css); qui a fianco del modulo c'è il riepilogo, e a 1440px il 70%
   lascerebbe al modulo poco più di 600px. Il margine diventa quindi il più
   piccolo fra il 15% della gabbia e quello che serve per avere
   --checkout-max di spazio, senza mai scendere sotto il respiro dei riquadri.
   Dove il 70% è già più largo di così (da 1660px in su) il checkout si
   allinea alla gabbia come il resto; sotto, si prende i lati.
   Le percentuali dentro `padding` si risolvono sulla larghezza della pagina,
   come il 15% della gabbia. */
.container:has(> .checkout-wrap) {
  --checkout-max: 1160px;
  padding-inline: min(var(--page-gutter),
      max(var(--container-padding), calc(50% - var(--checkout-max) / 2)));
}

.checkout-wrap {
  margin: 0 auto;
  padding-bottom: 4rem;
}

.checkout-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.25rem;
}

.checkout-back {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--text-secondary);
  text-decoration: none;
  padding: 0.45rem 0.9rem 0.45rem 0.7rem;
  border-radius: 999px;
  transition: var(--transition);
}

.checkout-back:hover {
  background: var(--tk-surface-hover);
  color: var(--text);
}

.checkout-trust {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text-muted);
}

.checkout-trust i {
  color: var(--primary-text);
}

/* Il gruppo di destra: la rassicurazione sull'acquisto e la scelta della
   lingua. Stanno insieme perché la barra si dispone con space-between, e tre
   figli diretti manderebbero uno dei due in mezzo, lontano da entrambi i
   bordi. */
.checkout-topbar-end {
  display: inline-flex;
  align-items: center;
  gap: 0.9rem;
}

/* Su schermo stretto finirebbe a capo sotto il link di ritorno, rubando una
   riga prima ancora del contenuto. Il selettore di lingua resta: su telefono
   è proprio il caso di chi ne ha bisogno, cioè chi arriva da fuori con un
   link diretto al checkout. */
@media (max-width: 640px) {
  .checkout-trust {
    display: none;
  }
}

/* Nel piè di pagina due parole grigie bastano, perché lì intorno c'è solo
   testo di servizio. In cima a un modulo d'acquisto no: la prima versione era
   la stessa coppia di parole, e chi arriva da fuori non la vedeva — guarda il
   prezzo e i pulsanti, non gli angoli.
   
   Quindi un interruttore vero e proprio: un mappamondo, che si legge senza
   sapere l'italiano né l'inglese, e la lingua attiva piena di colore invece
   che appena tinta. La differenza fra le due metà è il punto — si deve capire
   a colpo d'occhio che sono due stati di una stessa cosa, e che l'altro si
   può premere. */
.lang-switch--checkout {
  gap: 0.15rem;
  padding: 0.25rem 0.35rem 0.25rem 0.65rem;
  font-size: 0.85rem;
  color: var(--text);
  background: var(--card-bg);
  border: 1px solid var(--tk-border-strong);
  border-radius: 999px;
}

.lang-switch--checkout .fa-globe {
  margin-right: 0.15rem;
  font-size: 0.9em;
  color: var(--primary-text);
}

.lang-switch--checkout .lang-btn {
  padding: 0.25rem 0.6rem;
  border-radius: 999px;
  /* Non --text-muted: la lingua che non è attiva è comunque una scelta
     disponibile, non un'etichetta spenta. Grigio chiaro su bianco era
     esattamente il motivo per cui nessuno la premeva. */
  color: var(--text);
}

.lang-switch--checkout .lang-btn.active,
.lang-switch--checkout .lang-btn.active:hover {
  color: #fff;
  background: var(--primary);
}

/* Il richiamo, e una volta sola.
   
   Non basta essere visibile: la pagina si apre in italiano, e chi non lo legge
   sta già cercando altrove. Per tre volte l'interruttore si allarga un alone,
   poi smette e non torna più — l'attributo lo mette js/i18n.js appena qualcuno
   sceglie una lingua, e da quel momento questa regola non si applica più su
   nessuna pagina e su nessuna visita successiva. */
html:not([data-lingua-scelta]) .lang-switch--checkout {
  animation: lang-richiamo 1.9s ease-out 0.8s 3;
}

@keyframes lang-richiamo {
  from {
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--primary) 45%, transparent);
  }
  to {
    box-shadow: 0 0 0 11px color-mix(in srgb, var(--primary) 0%, transparent);
  }
}

@media (prefers-reduced-motion: reduce) {
  html:not([data-lingua-scelta]) .lang-switch--checkout {
    animation: none;
  }
}

/* La colonna del riepilogo e lo spazio fra le due colonne. Sono scritti come
   variabili perché servono anche fuori dalla griglia: la striscia degli
   sponsor, che nella griglia non ci sta, si allinea comunque alla colonna di
   sinistra e le larghezze devono restare le stesse. */
.checkout-page {
  --tk-aside: 360px;
  --tk-col-gap: 1.75rem;
}

.checkout-layout {
  display: grid;
  gap: 1.5rem;
  align-items: start;
  counter-reset: step;
}

@media (min-width: 960px) {
  .checkout-layout {
    grid-template-columns: minmax(0, 1fr) var(--tk-aside);
    gap: var(--tk-col-gap);
  }

  /* Il riepilogo segue lo scorrimento: su schermi lunghi il totale resta
     sempre visibile mentre si compila il modulo. */
  .checkout-aside {
    position: sticky;
    top: 96px;
  }

  /* I marchi in fondo alla pagina si allineano alla colonna di sinistra,
     quella delle schede. Nella griglia non ci stanno — arrivano dopo il
     modulo, e ci sono anche quando il modulo non c'è — quindi la larghezza se
     la rifanno da sé, togliendo il riepilogo e lo spazio fra le colonne.
     Centrati su tutta la pagina cadevano spostati a destra rispetto a quello
     che si stava guardando: la colonna del riepilogo allarga la pagina, non
     il modulo. Sotto i 960px la griglia ha una colonna sola e la striscia è
     già al suo posto. */
  .checkout-wrap .sponsor-strip {
    width: calc(100% - var(--tk-aside) - var(--tk-col-gap));
    margin-right: auto;
  }
}

/* Le card di queste pagine vivono dentro una griglia a due colonne: la
   larghezza e i margini automatici di .form-card le disallineerebbero
   rispetto all'intestazione. */
.checkout-layout .form-card {
  max-width: none;
  margin: 0;
  padding: 1.6rem 1.75rem;
  border-radius: var(--tk-radius-card);
  border: 1px solid var(--tk-border);
}

.checkout-col {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
  min-width: 0;
}

/* --- Intestazione --------------------------------------------------------- */

.checkout-hero {
  border-radius: var(--tk-radius-card);
  border: 1px solid var(--tk-border);
  overflow: hidden;
}

/* La locandina dell'evento. Non è più uno sfondo CSS ma una cornice che
   riceve un <img> creato da js/biglietti.js quando risponde l'API: serve un
   nodo immagine vero perché js/loading.js lo veda comparire e gli dia lo
   spinner e la dissolvenza che hanno tutte le altre foto del sito.
   Il colore di fondo resta ed è quello che si vede sotto lo spinner. */
.checkout-hero-media {
  height: clamp(150px, 22vw, 230px);
  overflow: hidden;
  background-color: var(--bg-subtle);
  border-bottom: 1px solid var(--tk-border);
}

.checkout-hero-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.checkout-hero-body {
  padding: 1.5rem 1.75rem 1.6rem;
}

.checkout-eyebrow {
  display: inline-block;
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--primary-text);
  margin-bottom: 0.6rem;
}

.checkout-hero-body h1 {
  font-size: clamp(1.5rem, 3.4vw, 2rem);
  line-height: 1.15;
  margin: 0;
  text-align: left;
}

.checkout-hero-body > p {
  margin: 0.85rem 0 0;
  font-size: 0.95rem;
  line-height: 1.6;
  color: var(--text-secondary);
}

.checkout-meta-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 1rem;
}

.checkout-meta-row {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--text-secondary);
  background: var(--tk-surface);
  border: 1px solid var(--tk-border);
  border-radius: 999px;
  padding: 0.4rem 0.8rem;
}

.checkout-meta-row i {
  color: var(--primary-text);
  font-size: 0.85rem;
}

.checkout-meta-row a {
  text-decoration: none;
  color: inherit;
}

.checkout-meta-row a:hover {
  text-decoration: underline;
}

/* --- Programma ------------------------------------------------------------
   Il programma dell'evento, sotto l'intestazione e chiuso di default: aperto
   sarebbe una schermata di testo fra chi arriva e i biglietti. Chiuso è una
   riga sola, che chi vuole sapere cosa si fa in quattro giorni apre. */

.program-details {
  border: 1px solid var(--tk-border);
  border-radius: var(--tk-radius-card);
  overflow: hidden;
}

.program-details > summary {
  cursor: pointer;
  list-style: none;
  padding: 0.95rem 1.25rem;
  font-size: 0.95rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.program-details > summary::-webkit-details-marker {
  display: none;
}

.program-details > summary i {
  color: var(--primary-text);
  font-size: 0.8rem;
  transition: transform 0.2s ease;
}

.program-details[open] > summary i {
  transform: rotate(90deg);
}

.program-body {
  padding: 0 1.25rem 1.25rem;
  font-size: 0.9rem;
  line-height: 1.65;
  color: var(--text-secondary);
}

.program-body b {
  color: var(--text);
}

/* --- Passi ----------------------------------------------------------------
   Numerazione automatica: i passi che restano nascosti non devono lasciare
   buchi nella sequenza. Un contatore CSS non conta gli elementi con
   display:none, quindi i passi visibili sono sempre consecutivi. */

.step-title {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  font-size: 1.05rem;
  font-weight: 700;
  margin-bottom: 0.35rem;
}

.step-title::before {
  counter-increment: step;
  content: counter(step);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--primary);
  color: #fff;
  font-size: 0.82rem;
  font-weight: 800;
  flex-shrink: 0;
}

.step-hint {
  font-size: 0.87rem;
  line-height: 1.5;
  color: var(--text-muted);
  margin: 0 0 1.1rem;
}

/* Il testo corrente del modulo va a capo alla misura di riga del sito: con
   il checkout a 1160px la colonna del modulo ne dava 105 caratteri. Le note
   dei passi e dei campi, l'introduzione in testa, i testi dei consensi. */
.step-hint,
.checkout-page .field-note,
.checkout-hero-body p,
.consent-body p {
  max-width: var(--measure);
}

.step-title:last-child,
.step-title:not(:has(+ .step-hint)) {
  margin-bottom: 1.1rem;
}

/* --- Un passo alla volta ---------------------------------------------------
   Il modulo si apre a fisarmonica: un blocco aperto, quelli già fatti chiusi
   sopra con il loro riepilogo in testa, quelli che seguono non ancora visibili.
   Il motivo è che il checkout intero, aperto tutto insieme, è una pagina lunga
   quattro schermate in cui non si capisce dove si è: chi la vede su un telefono
   scorre cercando la fine invece di compilare.

   `data-state` lo scrive js/biglietti.js e vale solo quando il JavaScript
   parte. Senza, nessun blocco ha lo stato, nessuna di queste regole si applica
   e resta il modulo di sempre — lungo, ma tutto compilabile e inviabile. È il
   motivo per cui a chiudere è lo stato e non una classe messa sui blocchi da
   nascondere: la mancanza di JavaScript non deve poter nascondere un campo
   obbligatorio. */

/* Il blocco che si apre arriva sotto alla barra di navigazione, non sotto al
   bordo dello schermo: senza questo, "Continua" porterebbe il titolo del passo
   nuovo esattamente dove la barra lo copre. */
.step {
  scroll-margin-top: 90px;
}

.step[data-state="locked"] {
  display: none;
}

.step[data-state="done"] > *:not(.step-title),
.step[data-state="todo"] > *:not(.step-title) {
  display: none;
}

.step[data-state="done"],
.step[data-state="todo"] {
  padding-top: 1.1rem;
  padding-bottom: 1.1rem;
  cursor: pointer;
}

.step[data-state="done"] .step-title,
.step[data-state="todo"] .step-title {
  margin-bottom: 0;
}

.step[data-state="done"]:hover,
.step[data-state="todo"]:hover {
  border-color: var(--tk-border-strong);
}

/* Il passo aperto è quello che si sta compilando: il bordo lo dice senza
   aggiungere una parola, e in una colonna di blocchi chiusi tutti uguali serve
   a ritrovare il punto dopo aver guardato altrove. */
.step[data-state="open"] {
  border-color: var(--primary);
}

/* Fatto: al posto del numero, la spunta. Il numero l'ha già consumato
   counter-increment nella regola sopra, quindi la sequenza non si scompone. */
.step[data-state="done"] .step-title::before {
  content: "\f00c";
  font-family: "Font Awesome 6 Free";
  font-weight: 900;
  font-size: 0.72rem;
  background: var(--tk-ok);
  color: var(--tk-ok-ink);
}

/* Cosa c'era scritto dentro, in una riga: due biglietti, il nome, il metodo di
   pagamento. Un blocco chiuso senza riepilogo è una scatola con su un'etichetta
   e niente dentro — per ricontrollare bisognerebbe riaprirlo, e a quel punto
   tanto valeva lasciarlo aperto. */
/* Il nome del passo non si stringe: a cedere è il riepilogo qui sotto, che i
   puntini di sospensione ce li ha apposta. Senza, su un telefono stretto
   «I tuoi dati» si spezzava una parola per riga — «I / tuoi / dati» — mentre
   di fianco restava spazio vuoto. */
.step-title > span:first-of-type {
  flex-shrink: 0;
  white-space: nowrap;
}

.step-recap {
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

.step[data-state="open"] .step-recap,
.step[data-state="open"] .step-edit {
  display: none;
}

.step-edit {
  margin-left: auto;
  flex-shrink: 0;
  background: none;
  border: none;
  padding: 0.2rem 0.35rem;
  font: inherit;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--primary-text);
  cursor: pointer;
}

.step-edit:hover {
  text-decoration: underline;
}

/* Il pulsante che manda avanti. A destra su schermo largo, dove finisce la
   lettura; a tutta larghezza sul telefono, dove il pollice arriva ovunque tranne
   che in un angolo. */
.step-actions {
  display: flex;
  justify-content: flex-end;
  margin-top: 1.5rem;
}

.step-actions .step-next {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
}

@media (max-width: 640px) {
  .step-actions .step-next {
    width: 100%;
    justify-content: center;
  }
}

/* --- La traccia dei passi --------------------------------------------------
   Con un blocco alla volta la domanda immediata è «quanto manca?». Senza
   risposta, chi compra la ricava scorrendo — e non può, perché sotto non c'è
   ancora niente. */

.step-track {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem 0.5rem;
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: track;
}

.step-track-item {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-muted);
  padding: 0.3rem 0.6rem 0.3rem 0.3rem;
  border-radius: 999px;
  border: 1px solid transparent;
}

.step-track-item.is-current {
  color: var(--text);
  border-color: var(--tk-border);
  background: var(--tk-surface);
}

.step-track-dot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 1px solid var(--tk-border-strong);
  font-size: 0.68rem;
  font-weight: 800;
  flex-shrink: 0;
}

.step-track-item.is-current .step-track-dot {
  background: var(--primary);
  border-color: var(--primary);
  color: #fff;
}

.step-track-item.is-done .step-track-dot {
  background: var(--tk-ok);
  border-color: var(--tk-ok);
  color: var(--tk-ok-ink);
}

/* Sul telefono le etichette non ci stanno in riga: restano i pallini, e il nome
   solo su quello dove si è. Andare a capo con sei voci vorrebbe dire tre righe
   di indice sopra il modulo. */
@media (max-width: 640px) {
  .step-track-item:not(.is-current) .step-track-label {
    display: none;
  }

  .step-track-item:not(.is-current) {
    padding-right: 0.3rem;
  }
}

/* --- Tesseramento comprato con un biglietto -------------------------------
   Chi arriva dal link personale non sta scegliendo una tessera: ne ha già
   pagata una dentro il prezzo del biglietto, e sta finendo un modulo. Questo
   riquadro prende il posto della scelta e dice le due cose che servono a
   fidarsi — da quale biglietto arriva, e che non c'è altro da pagare. */
.invite-from {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 0.6rem;
}

.invite-fee {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.7rem 0.9rem;
  border-radius: var(--tk-radius-row, 14px);
  background: rgba(36, 138, 61, 0.12);
  border: 1px solid rgba(36, 138, 61, 0.32);
  color: var(--text);
  font-size: 0.92rem;
  font-weight: 700;
}

.invite-fee > i {
  color: #248a3d;
  font-size: 1.05rem;
}

/* Quando invece la quota è ancora da pagare — chi ha comprato a tariffa socio
   senza esserlo — il verde direbbe il contrario di quello che c'è scritto. */
.invite-fee:has(.fa-euro-sign) {
  background: rgba(180, 83, 9, 0.12);
  border-color: rgba(180, 83, 9, 0.34);
}

.invite-fee:has(.fa-euro-sign) > i {
  color: #b45309;
}

/* Sottotitolo dentro un passo, quando il passo raccoglie più gruppi di campi
   (anagrafica, residenza) e servono delle intestazioni minori. */
.step-group-title {
  display: block;
  font-size: 0.75rem;
  font-weight: 800;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 1.6rem 0 0.85rem;
}

.step-group-title:first-of-type {
  margin-top: 0;
}

/* --- Righe selezionabili --------------------------------------------------
   Una tariffa, una taglia, un'opzione: la riga è tutta cliccabile e lo stato
   selezionato si vede dal bordo, dal fondo e dalla barretta a sinistra. */

.option-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.35rem 1.25rem;
  padding: 1.05rem 1.2rem;
  border-radius: var(--tk-radius-row);
  border: 1px solid var(--tk-border);
  background: var(--tk-surface);
  cursor: pointer;
  transition: var(--transition);
}

.option-row + .option-row {
  margin-top: 0.7rem;
}

.option-row:hover {
  border-color: var(--tk-border-strong);
}

.option-row:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

.option-row.selected {
  border-color: var(--primary);
  background: var(--tk-selected);
  box-shadow: inset 3px 0 0 0 var(--primary);
}

.option-name {
  font-weight: 700;
  margin-bottom: 0.2rem;
}

.option-desc {
  font-size: 0.85rem;
  color: var(--text-muted);
  line-height: 1.45;
}

.option-price {
  justify-self: end;
  font-size: 1.15rem;
  font-weight: 800;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

@media (max-width: 480px) {
  .option-row {
    grid-template-columns: minmax(0, 1fr);
  }

  .option-price {
    justify-self: start;
  }
}

/* --- Metodi di pagamento -------------------------------------------------- */

.method-option {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  padding: 1rem 1.2rem;
  border-radius: var(--tk-radius-row);
  border: 1px solid var(--tk-border);
  background: var(--tk-surface);
  cursor: pointer;
  transition: var(--transition);
}

.method-option + .method-option {
  margin-top: 0.7rem;
}

.method-option:hover {
  border-color: var(--tk-border-strong);
}

.method-option:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

.method-option.selected {
  border-color: var(--primary);
  background: var(--tk-selected);
}

.method-option > i {
  font-size: 1.3rem;
  color: var(--primary-text);
  width: 28px;
  text-align: center;
  flex-shrink: 0;
}

/* Indicatore di selezione: il solo cambio di bordo non basta a dire "questa è
   la scelta attiva", soprattutto in dark. */
.method-option::after {
  content: "";
  margin-left: auto;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 2px solid var(--tk-border-strong);
  flex-shrink: 0;
  transition: var(--transition);
}

.method-option.selected::after {
  border-color: var(--primary);
  background: var(--primary);
  box-shadow: inset 0 0 0 3px var(--card-bg);
}

.method-label {
  font-weight: 600;
}

.method-note {
  font-size: 0.82rem;
  color: var(--text-muted);
  line-height: 1.45;
  margin-top: 0.15rem;
}

/* Il corpo della riga sta in mezzo fra l'icona e la commissione. `flex: 1 1 0`
   e non la base automatica: dove la riga può andare a capo (vedi più sotto), un
   elemento largo quanto il suo testo si porterebbe dietro un'interruzione e il
   nome del metodo finirebbe sotto alla sua icona. */
.method-body {
  flex: 1 1 0;
  min-width: 0;
}

/* --- Consensi ------------------------------------------------------------- */

.consent-row {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  padding: 0.9rem 1rem;
  border-radius: var(--tk-radius-row);
  border: 1px solid var(--tk-border);
  background: var(--tk-surface);
  transition: var(--transition);
}

.consent-row + .consent-row {
  margin-top: 0.7rem;
}

.consent-row:hover {
  border-color: var(--tk-border-strong);
}

.consent-row input {
  width: auto;
  margin: 0.15rem 0 0;
  flex-shrink: 0;
}

.consent-row label {
  font-size: 0.88rem;
  line-height: 1.55;
  margin: 0;
  cursor: pointer;
}

/* Informativa da leggere prima di acconsentire: chiusa di default, perché
   aperta sposterebbe il consenso due schermate più in basso. */
.consent-details {
  border: 1px solid var(--tk-border);
  border-radius: var(--tk-radius-row);
  background: var(--tk-surface);
  margin-bottom: 0.7rem;
}

.consent-details > summary {
  cursor: pointer;
  list-style: none;
  padding: 0.75rem 1rem;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  gap: 0.55rem;
}

.consent-details > summary::-webkit-details-marker {
  display: none;
}

.consent-details > summary i {
  color: var(--primary-text);
  font-size: 0.75rem;
  transition: transform 0.2s ease;
}

.consent-details[open] > summary i {
  transform: rotate(90deg);
}

.consent-details .consent-body {
  padding: 0 1rem 1rem;
  font-size: 0.84rem;
  line-height: 1.6;
  color: var(--text-muted);
}

/* --- Riepilogo ------------------------------------------------------------ */

.summary-title {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  font-size: 1.05rem;
  margin: 0 0 1.1rem;
}

.summary-title i {
  color: var(--primary-text);
  font-size: 0.95rem;
}

.summary-empty {
  font-size: 0.9rem;
  color: var(--text-muted);
  line-height: 1.5;
  margin: 0;
}

.summary-line {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  font-size: 0.92rem;
  color: var(--text-secondary);
}

.summary-line + .summary-line {
  margin-top: 0.6rem;
}

.summary-line-name {
  display: flex;
  align-items: baseline;
  gap: 0.45rem;
  min-width: 0;
}

/* Quante volte quella riga: usata dove il riepilogo conta pezzi (biglietti),
   assente dove la riga è una sola per definizione (la tessera). */
.summary-qty {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  height: 20px;
  padding: 0 0.35rem;
  border-radius: 6px;
  background: var(--primary-glow);
  color: var(--primary-text);
  font-size: 0.75rem;
  font-weight: 800;
  flex-shrink: 0;
}

.summary-line-amount {
  font-weight: 600;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* Gli extra si aggiungono ai biglietti e non sono biglietti: un filo di stacco
   li separa dalle righe sopra senza farne una sezione a parte, che per una riga
   sola sarebbe più ingombro che informazione. */
.summary-line-addon {
  padding-top: 0.6rem;
  border-top: 1px dashed var(--tk-border);
}

.summary-line-addon + .summary-line-addon {
  border-top: 0;
  padding-top: 0;
}

/* La protezione acquisti. Stesso stacco degli extra, perché è la stessa cosa
   dal punto di vista di chi legge: una riga che si somma ai biglietti e non è un
   biglietto. Il colore resta quello del testo — è una cifra che si paga, e
   dipingerla di verde la farebbe leggere come uno sconto. */
.summary-line-protection {
  padding-top: 0.6rem;
  border-top: 1px dashed var(--tk-border);
}

/* La percentuale accanto al nome della voce, in piccolo: «3% dell'ordine»
   risponde da sola alla domanda che una cifra da sola fa venire. */
.summary-line-note {
  display: block;
  font-size: 0.75rem;
  color: var(--text-muted);
  margin-top: 0.1rem;
}

.summary-total {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  padding-top: 1rem;
  margin-top: 1rem;
  border-top: 1px solid var(--tk-border);
  font-weight: 800;
  font-size: 1.35rem;
  font-variant-numeric: tabular-nums;
}

.summary-total > span:first-child {
  font-size: 1rem;
}

.checkout-submit {
  margin-top: 1.25rem;
  padding-top: 1rem;
  padding-bottom: 1rem;
  font-size: 1rem;
}

.summary-perks {
  list-style: none;
  margin: 1.1rem 0 0;
  padding: 1rem 0 0;
  border-top: 1px solid var(--tk-border);
  font-size: 0.82rem;
  color: var(--text-muted);
  line-height: 1.45;
}

.summary-perks li {
  display: flex;
  align-items: flex-start;
  gap: 0.55rem;
}

.summary-perks li + li {
  margin-top: 0.5rem;
}

.summary-perks i {
  color: var(--primary-text);
  margin-top: 0.15rem;
  width: 14px;
  text-align: center;
  flex-shrink: 0;
}

/* --- Barra di acquisto su mobile ------------------------------------------
   Sotto i 960px il riepilogo finisce in fondo alla pagina, lontanissimo da
   quello che si sta compilando: il totale e il pulsante restano invece sempre
   a portata di pollice. */

.checkout-bar {
  display: none;
}

@media (max-width: 959px) {
  .checkout-bar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 900;
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 0.85rem var(--container-padding);
    padding-bottom: calc(0.85rem + env(safe-area-inset-bottom));
    /* Fondo pieno, non il 94% di --glass-bg-solid.
       Quel 94% è pensato per i riquadri fermi sopra al gradiente quasi
       uniforme del body, dove la differenza non si vede (style.css). Qui sotto
       invece ci scorre il testo della pagina, e in trasparenza si leggeva il
       titolo del passo dietro al prezzo: sembrava che il totale e il pulsante
       ci finissero sopra. */
    background: var(--bg-base);
    border-top: 1px solid var(--tk-border);
    box-shadow: 0 -8px 30px rgba(0, 0, 0, 0.12);
    transform: translateY(110%);
    transition: transform 0.35s var(--spring-easing);
  }

  .checkout-bar.visible {
    transform: none;
  }

  /* Il totale non si stringe e non va a capo: è la ragione per cui la barra
     esiste. Con `min-width: 0` a cedere era lui — i bottoni su telefono sono
     larghi quanto lo schermo (`.btn { width: 100% }` in style.css) e quello
     non si spezza — e «51,50 €» finiva su due righe, con l'euro da solo sotto
     la cifra. */
  .checkout-bar-info {
    display: flex;
    flex-direction: column;
    line-height: 1.2;
    flex-shrink: 0;
  }

  .checkout-bar-label {
    font-size: 0.75rem;
    color: var(--text-muted);
    font-weight: 600;
  }

  .checkout-bar-total {
    font-size: 1.25rem;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }

  .checkout-bar .btn {
    margin-left: auto;
    white-space: nowrap;
    /* Fuori di qui, su telefono, i bottoni sono larghi quanto lo schermo e si
       staccano da quello dopo con un margine sotto. In barra stanno in riga
       col totale: il bottone prende lo spazio che gli serve, e se manca cede
       lui — non il prezzo. */
    width: auto;
    min-width: 0;
    margin-bottom: 0;
  }

  /* Il pulsante compare solo all'ultimo passo.
     Prima c'era sempre, e diceva «Continua» mentre lo diceva anche il pulsante
     dentro al blocco aperto e quello del riepilogo qui sotto: tre volte la
     stessa parola nella stessa schermata, per la stessa azione. Adesso i ruoli
     sono due e non si sovrappongono — dentro al blocco si va avanti, quaggiù
     si guarda quanto si sta spendendo — finché non resta solo da pagare, che è
     l'unico momento in cui il blocco aperto un pulsante suo non ce l'ha.
     Lo accende updateSubmitState(). */
  .checkout-bar:not(.is-final) .btn {
    display: none;
  }

  /* Il pulsante del riepilogo, che su desktop sta nella colonna di fianco, qui
     finisce in fondo alla pagina: stessa etichetta, stesso totale scritto
     sopra, e la barra fissa che glielo ripete addosso. Ne resta uno. */
  .checkout-submit {
    display: none;
  }

  /* Spazio in fondo alla pagina solo quando la barra c'è davvero, altrimenti
     resterebbe un vuoto sotto al footer. */
  body:has(.checkout-bar.visible) {
    padding-bottom: 6rem;
  }
}

/* --- Avvisi --------------------------------------------------------------- */

.alert {
  padding: 1rem 1.15rem;
  border-radius: var(--tk-radius-row);
  font-size: 0.92rem;
  line-height: 1.5;
  display: flex;
  gap: 0.7rem;
  align-items: flex-start;
}

.alert i {
  margin-top: 0.15rem;
  flex-shrink: 0;
}

.alert-error {
  background: var(--tk-danger-bg);
  border: 1px solid var(--tk-danger-border);
  color: var(--tk-danger);
}

.alert-info {
  background: var(--primary-glow);
  border: 1px solid rgba(34, 110, 247, 0.2);
}

.alert-warning {
  background: var(--accent-glow);
  border: 1px solid rgba(255, 122, 48, 0.28);
}

/* --- Esito ----------------------------------------------------------------
   Pagamento avviato, ordine confermato, errore: tutte le pagine di esito hanno
   la stessa forma, così l'utente riconosce dove si trova. */

/* Stava ripetuta identica nel <style> di quattro pagine, con la solita copia
   rimasta indietro: la forma dell'esito è una sola e sta qui. */
.state-shell {
  min-height: 60vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem 0 3rem;
}

.state-card {
  max-width: 620px;
  margin: 1rem auto 0;
  padding: 2.5rem 2rem;
  border-radius: var(--tk-radius-card);
  border: 1px solid var(--tk-border);
  text-align: center;
}

.state-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 68px;
  height: 68px;
  border-radius: 50%;
  font-size: 1.9rem;
  margin-bottom: 1.25rem;
}

.state-icon.success {
  color: var(--primary-text);
  background: var(--primary-glow);
  box-shadow: 0 0 0 8px var(--primary-glow);
}

.state-icon.done {
  color: #34c759;
  background: rgba(52, 199, 89, 0.12);
  box-shadow: 0 0 0 8px rgba(52, 199, 89, 0.08);
}

/* Un ordine registrato che aspetta i soldi. Non è né un successo né un errore,
   ed è il caso in cui prima si metteva la spunta blu del successo: una conferma
   grande quanto un pollice sopra la parola «Paga», che chi la legge prende per
   una ricevuta. L'arancione degli avvisi, e una clessidra. */
.state-icon.pending {
  color: var(--accent);
  background: var(--accent-glow);
  box-shadow: 0 0 0 8px var(--accent-glow);
}

.state-icon.error {
  color: var(--tk-danger);
  background: var(--tk-danger-bg);
  box-shadow: 0 0 0 8px var(--tk-danger-bg);
}

.state-card h1 {
  font-size: 1.5rem;
  margin: 0 0 0.6rem;
}

/* --- La schermata del pagamento -------------------------------------------
   Su un telefono ci si entra e si deve capire in un colpo che cosa fare. Prima
   veniva il codice ordine e tutto il dettaglio, e il QR cominciava sotto al
   bordo dello schermo: la prima cosa che si toccava era il pulsante del
   tesseramento, che era l'unico visibile. Adesso l'ordine è cifra, pulsante,
   QR; la ricevuta sta in fondo. */

/* La cifra è l'ancora della schermata: si legge prima del titolo. */
.state-card .payment-amount {
  margin: 0.25rem 0 1.25rem;
}

.btn-pay {
  width: 100%;
  max-width: 360px;
  justify-content: center;
  margin-bottom: 1.5rem;
}

.payment-qr-block {
  margin-bottom: 1.5rem;
}

/* Da telefono il QR non si inquadra: si sta guardando lo schermo su cui
   sarebbe. La didascalia dice a chi serve davvero, e toglie la sensazione di
   essersi persi un passaggio. */
.payment-qr-caption {
  font-size: 0.82rem;
  color: var(--text-muted);
  margin: 0;
}

.payment-qr-block .payment-qr {
  margin-top: 0.6rem;
}

/* Il codice ordine: sceso in fondo con la ricevuta, perché i passi qui sopra lo
   dettano già — è la causale — e in cima rubava la riga al pulsante. */
.instructions-code {
  color: var(--text-muted);
  margin: 0 0 0.9rem;
}

.state-message {
  font-size: 1rem;
  line-height: 1.65;
  color: var(--text-secondary);
  margin: 0 0 1.75rem;
}

.state-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  justify-content: center;
}

/* Su telefono style.css dà a ogni .btn un margine sotto, per staccarlo dal
   pulsante che segue. Qui la distanza la dà il `gap`, e quel margine restava
   in fondo alla scheda come 12px di vuoto in più del padding. */
.state-actions .btn {
  margin-bottom: 0;
}

/* Il codice della pratica: risalito sotto al messaggio, da cui il margine
   negativo, perché è parte della frase appena letta e non un blocco a sé. */
.state-code {
  color: var(--text-muted);
  margin: -0.75rem 0 1.75rem;
}

/* --- Come si ritrova un ordine ----------------------------------------------
   Senza account, la pagina di esito è tutto quello che si ha in mano — e si
   chiude. Questo riquadro risponde alla domanda che viene subito dopo: dove sta
   la copia. Sta qui e non nella pagina d'acquisto perché lo mostrano in due,
   con le stesse parole: la fine del checkout e il ritorno dal pagamento.
   Neutro e non allarmato: non è un avviso, è un indirizzo. */
.recovery-box {
  margin: 0 0 1.5rem;
  padding: 1rem 1.15rem;
  border: 1px solid var(--tk-border);
  border-radius: var(--tk-radius-row);
  background: var(--tk-surface);
  text-align: left;
}

.recovery-box:empty {
  display: none;
}

.recovery-title {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 0.5rem;
  font-size: 0.95rem;
  font-weight: 700;
}

.recovery-title i {
  color: var(--primary-text);
}

.recovery-box p {
  margin: 0 0 0.6rem;
  font-size: 0.87rem;
  line-height: 1.55;
  color: var(--text-muted);
}

.recovery-box p:last-child {
  margin-bottom: 0;
}

.recovery-box b {
  color: var(--text);
}

.recovery-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.85rem;
}

/* I due pulsanti si dimensionano sul testo, non su una base fissa: con
   `flex-basis` a 12rem l'etichetta più lunga cresceva oltre la sua scatola e
   andava a capo in mezzo alla frase. Sotto i 380 px vanno a capo per intero,
   che è un'altra cosa. */
.recovery-actions .btn {
  flex: 1 1 auto;
  /* Più piccoli dei pulsanti della scheda: sono azioni secondarie dentro un
     riquadro dentro la scheda, e su un telefono da 375 le tre cornici lasciano
     250px. Con il padding pieno «Email sbagliata? Scrivici» andava su due
     righe. */
  padding: 0.65rem 1rem;
  font-size: 0.92rem;
}

/* L'invito alla community compare solo a tesseramento riuscito: è il momento
   in cui entrarci ha un senso, ed è l'unico in cui qualcuno lo fa. */
.community-box {
  margin: 0 0 1.75rem;
  padding: 1.25rem;
  border-radius: var(--tk-radius-row);
  border: 1px solid var(--tk-border);
  background: var(--tk-surface);
}

.community-box p {
  font-weight: 800;
  margin-bottom: 1rem;
}

.community-box .btn {
  width: 100%;
  justify-content: center;
  gap: 0.75rem;
}

@media (max-width: 520px) {
  .state-shell {
    align-items: flex-start;
  }

  .state-card {
    padding: 2rem 1.35rem;
  }

  .state-actions {
    flex-direction: column;
  }

  .state-actions .btn {
    width: 100%;
    justify-content: center;
  }
}

/* --- Istruzioni di pagamento -----------------------------------------------
   QR, importo, passi e coordinate del bonifico stanno in
   css/pagamento-istruzioni.css: le stesse righe compaiono anche su
   biglietto.html, che di questo foglio non carica niente, e due copie
   divergerebbero al primo ritocco. */

/* Sei caratteri che qualcuno ricopierà a mano sul bonifico: la spaziatura larga
   li stacca uno dall'altro, che è tutto quello che serve per non leggerne due
   attaccati come se fossero un carattere solo. */
.order-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  background: var(--primary-glow);
  color: var(--primary-text);
  padding: 0.35rem 0.7rem;
  border-radius: 8px;
  font-weight: 700;
  letter-spacing: 0.12em;
  /* Un codice non si spezza: se non ci sta, scende intero alla riga sotto.
     A 375px il numero di tessera andava a capo sul trattino, e metà codice
     su una riga e metà sull'altra non si ricopia. */
  display: inline-block;
  white-space: nowrap;
}

/* --- Scheletro di caricamento ---------------------------------------------
   Ricalca l'impaginazione reale, così all'arrivo dei dati il contenuto non fa
   saltare la pagina. */

.skeleton-block {
  border-radius: var(--tk-radius-card);
  background: var(--tk-surface);
  border: 1px solid var(--tk-border);
  animation: checkoutPulse 1.4s ease-in-out infinite;
}

/* Il blocco grande ricalca una card, la riga una delle righe che stanno
   dentro: stesso spigolo dell'elemento che sostituiscono, altrimenti
   all'arrivo dei dati cambia anche la forma e non solo il contenuto. */
.skeleton-row {
  height: 78px;
  border-radius: var(--tk-radius-row);
}

.skeleton-row + .skeleton-row {
  margin-top: 0.7rem;
}

@keyframes checkoutPulse {
  0%,
  100% {
    opacity: 0.5;
  }

  50% {
    opacity: 0.85;
  }
}

@media (prefers-reduced-motion: reduce) {
  .skeleton-block,
  .checkout-bar,
  .consent-details > summary i {
    animation: none;
    transition: none;
  }
}

/* --- Campi da correggere ----------------------------------------------------
   Il segnale è il campo, non una frase sotto il campo. Prima `.invalid` veniva
   messo dal JavaScript e non era disegnato da nessuna parte: l'unica traccia di
   un modulo incompleto era «Manca questo pezzo!» ripetuto sotto ogni riga
   rimasta vuota, che riempie la pagina di rosso senza dire dove guardare.
   Adesso il campo si accende, la pagina ci porta sopra e il perché sta scritto
   una volta sola, nell'avviso in cima. */
/* I selettori ricalcano quelli dei campi in css/style.css, `:not()` compresi:
   quella regola pesa più di un semplice `.invalid`, e un rosso che perde contro
   il grigio di sempre non segnala niente. */
input:not([type="checkbox"]):not([type="radio"]).invalid,
select.invalid,
textarea.invalid {
  border-color: #ff3b30;
  box-shadow: 0 0 0 3px rgba(255, 59, 48, 0.16);
}

/* Il fuoco arriva subito dopo lo scorrimento, e il campo su cui arriva è proprio
   quello segnalato: senza questa regola l'anello azzurro del focus coprirebbe il
   rosso esattamente dove serviva vederlo. */
input:not([type="checkbox"]):not([type="radio"]).invalid:focus,
select.invalid:focus,
textarea.invalid:focus {
  border-color: #ff3b30;
  background: var(--bg-base);
  box-shadow: 0 0 0 4px rgba(255, 59, 48, 0.28);
}

/* Quello che non è un campo di testo si accende lo stesso, con lo stesso rosso:
   il consenso (una casella grande come un carattere non si vede diventare
   rossa), il riquadro della firma e il blocco dei metodi di pagamento. */
.consent-row.invalid,
.signature-pad-container.invalid,
.invalid-group {
  border-radius: var(--tk-radius-row);
  outline: 2px solid rgba(255, 59, 48, 0.55);
  outline-offset: 6px;
}

.consent-row.invalid label {
  color: #ff3b30;
}

@media (prefers-reduced-motion: no-preference) {
  .invalid {
    transition: box-shadow 0.18s ease, border-color 0.18s ease;
  }
}

/* Un messaggio d'errore vuoto non occupa spazio.
   Ogni campo dei due moduli porta uno <span class="error"> sotto, pronto a
   ricevere il testo: da `inline` occupava comunque una riga più il suo margine,
   cioè una ventina di pixel di aria sotto ogni campo anche quando non c'è niente
   da dire. Sommati su nome, cognome, email e telefono diventano il doppio dello
   spazio previsto fra un campo e il successivo. */
.error:empty {
  display: none;
}

.hidden {
  display: none !important;
}
