/* RingVision — sito pubblico statico.
   Foglio di stile unico condiviso da landing, pagine legali, help, deletion,
   stripe callback e portale /gym. Zero dipendenze esterne, zero JS, zero tracker.
   Palette allineata all'app (lib/ui/theme/app_colors.dart, rebrand carbon). */

/* ── Tipografia self-hosted ──────────────────────────────────────────────────
   Poppins e JetBrains Mono sono gli stessi font dell'app (AppTextStyles):
   Poppins per il testo, il mono SOLO per numeri e metriche — è il segnale
   "strumento tecnico" più forte della grammatica Fight Analysis Console.

   SELF-HOSTED, non da Google Fonts CDN: la CSP della landing dichiara
   `font-src 'self'` (firebase.json) e un CDN la violerebbe; in più il fetch a
   fonts.gstatic.com espone l'IP del visitatore a un terzo, che sul sito di
   un'app europea è un problema GDPR che oggi non esiste.

   I .woff2 derivano dai .ttf in assets/google_fonts/ (bundle Flutter), tagliati
   al subset latin con `python -m fontTools.subset`: 4 file per 32 KB totali
   invece di 750 KB di TTF. Licenza OFL in fonts/OFL-*.txt, che DEVE restare
   accanto ai font — la ridistribuzione la richiede. */
@font-face {
  font-family: 'Poppins';
  src: url('/assets/fonts/Poppins-Regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Poppins';
  src: url('/assets/fonts/Poppins-SemiBold.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Poppins';
  src: url('/assets/fonts/Poppins-Bold.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
/* Un solo peso del mono: vive esclusivamente sui valori numerici (700, come
   AppTextStyles.metricMedium). Subset ancora più stretto — ASCII + simboli di
   misura — perché non deve mai comporre testo lungo. */
@font-face {
  font-family: 'JetBrains Mono';
  src: url('/assets/fonts/JetBrainsMono-Bold.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

:root {
  --bg: #05070B;          /* Ring Black — sfondo base */
  --surface: #111722;     /* Carbon — superfici/card */
  --surface-high: #18212F;/* Carbon sopraelevato */
  --line: #263244;        /* linea/divider tecnico */
  --accent: #2979FF;      /* Electric Blue — accento/interattivo */
  --accent-soft: #4DA3FF; /* Electric Blue chiaro — link/hover */
  /* Sfondo dei soli controlli che portano TESTO BIANCO sopra: il bottone
     primario e il toggle di fatturazione del portale. È `--accent` scurito
     quel tanto che basta a superare il 4,5:1 richiesto da WCAG AA per il testo
     non "large" — misurato: bianco su #2979FF dà 3,98:1, su #1F6BFF dà 4,56:1.
     Il bottone è 16px bold, sotto la soglia dei 18,66px bold oltre la quale
     basterebbe 3:1, quindi la deroga per il testo grande non si applica.

     È un token a sé e NON un cambio di `--accent`: quel colore è l'Electric
     Blue di `lib/ui/theme/app_colors.dart` e vale per link, bordi, icone e
     barre, dove il contrasto si misura su tutt'altro accostamento. Cambiarlo
     alla radice disallineerebbe il sito dall'app per risolvere il problema di
     due sole regole. */
  --btn-primary-bg: #1F6BFF;
  /* Hover del primario come COLORE, non come `filter`. Il motivo è misurato e
     controintuitivo: `filter: brightness()` si applica all'intero elemento, il
     testo compreso, quindi scurendo dell'8% il bianco diventa #EBEBEB e il
     contrasto scende a 4,38:1 — sotto l'AA — anche partendo da un fondo che da
     fermo dà 4,56:1. Scurire il solo sfondo lascia il bianco puro e porta a
     5,75:1. Vale la pena saperlo prima di reintrodurre un filtro su un
     controllo con testo sopra. */
  --btn-primary-bg-hover: #1A5CE0;
  /* Corner Red — `AppColors.cornerRed`, l'unico rosso del design system
     dell'app. Ha UN solo uso sul sito, il pallino di round attivo del mock, e
     il token esiste per togliere quel letterale dalla regola, non per aprire la
     strada ad altri usi: l'estensione a un filetto sui titoli di sezione e a
     una linguetta sulla card in evidenza è stata implementata e poi RIMOSSA su
     giudizio di design (08/08/2026). Chi la riproponesse tenga presente che è
     già stata vista.
     Due vincoli restano comunque veri e sono la ragione per cui questa nota sta
     qui. Primo, misurato: **non è mai un colore di testo su questo sito** —
     4,03:1 su `--bg`, 3,58:1 su `--surface`, 3,23:1 su `--surface-high`, tutti
     sotto il 4,5:1 dell'AA; sopra il 3:1 di WCAG 1.4.11, quindi ammesso solo
     per pallini, filetti e bordi, e con testo bianco SOPRA di sé dà 5,01:1.
     È per questo che la proposta dell'audit di accentare i numeri delle
     metriche demo non è implementabile: quei numeri sono testo.
     Secondo: mai su una CTA. Il colore interattivo del brand è l'Electric
     Blue, e cambiarlo sul web romperebbe la coerenza con l'app. */
  --corner-red: #D62828;
  --text: #F0F4FF;        /* testo primario */
  --text-dim: #D5E0EF;    /* testo secondario */
  /* ── Spacing — scala 4pt di AppSpacing (lib/constants/app_design_tokens.dart) */
  --sp-xs: 4px;
  --sp-sm: 8px;
  --sp-md: 12px;
  --sp-lg: 16px;
  --sp-xl: 20px;
  --sp-2xl: 24px;
  --sp-3xl: 32px;
  /* Ritmo verticale delle sezioni. Era 72px fissi: troppi su mobile (spingono
     il contenuto oltre la piega), pochi su desktop grande. */
  --sp-section: clamp(56px, 8vw, 112px);

  /* ── Radius — scala di AppRadius (stesso file) */
  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 14px;
  --r-xl: 16px;
  --r-2xl: 20px;
  --r-3xl: 24px;
  --r-full: 999px;
  --radius: var(--r-xl);  /* alias storico: usato anche da gym.css */

  /* ── Elevation — l'app ha card sopraelevate con ombra, il sito aveva solo
     bordi 1px: è la ragione tecnica per cui sembrava "piatto" rispetto all'app. */
  --elev-1: 0 1px 2px rgba(0, 0, 0, 0.4);
  --elev-2: 0 8px 24px rgba(0, 0, 0, 0.45);

  --maxw: 1080px;
  --maxw-read: 760px;     /* colonna di lettura pagine legali */

  /* Il fallback di sistema resta in coda: se il woff2 non arriva la pagina è
     identica a prima del self-host, non senza font. */
  --font: 'Poppins', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

* { box-sizing: border-box; }

/* Rinforzo dell'attributo nativo `hidden`, che è il modo in cui questo sito
   nasconde e rivela: `el.hidden = …` in assets/deeplink.js e nei moduli di
   /gym. La regola UA `[hidden]{display:none}` NON basta: sta nell'origine
   User-Agent e perde contro QUALSIASI dichiarazione d'autore, specificità
   irrilevante. Su questo sito quelle dichiarazioni esistono — `.btn` è
   `display:inline-flex`, e gym.css (caricato DOPO) ha sei componenti flex.
   Senza `!important`, i tre CTA condizionali di /gym/account (`#activate-btn`,
   `#sca-btn`, `#portal-btn`) sarebbero visibili a chiunque: "Gestisci
   fatturazione" mostrato a chi non ha un customer Stripe porta a un
   `failed-precondition` su un account sano. L'`!important` rende anche
   irrilevante la posizione di questa riga nei due fogli, che è l'altra metà
   del motivo per cui c'è.
   Presidiato da test/services/guards/hosting_inline_style_guard.dart. */
[hidden] { display: none !important; }

html {
  background: var(--bg);
}

/* Lo scroll animato è un'aggiunta, non un requisito: si accende solo se il
   sistema non chiede di ridurre il movimento. */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

body {
  margin: 0;
  min-height: 100vh;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 17px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--accent-soft); text-decoration: none; }
a:hover { text-decoration: underline; }

/* Focus da tastiera: l'outline di default del browser è quasi invisibile su #05070B. */
:where(a, button, [tabindex]):focus-visible {
  outline: 2px solid var(--accent-soft);
  outline-offset: 3px;
  border-radius: 4px;
}

/* Skip-link: fuori schermo finché non riceve il focus. */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--surface-high);
  color: var(--text);
  padding: var(--sp-md) var(--sp-xl);
  border-radius: 0 0 var(--r-md) 0;
  z-index: 10;
}
.skip-link:focus { left: 0; }

img { max-width: 100%; height: auto; }

/* I token coprono spaziature di LAYOUT (container, card, gap, sezioni) e i
   raggi. Restano volutamente letterali: le dimensioni proprie di un componente
   (84px del logo, 220px della device frame), i font-size e i margini di prosa
   delle pagine legali — non sono passi di una scala, e tokenizzarli avrebbe
   dato nomi a valori che nessun altro riuserà. */
.container {
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 var(--sp-2xl);
}

/* ── Top bar ─────────────────────────────────────────────────────────────── */
/* `flex-wrap` non è decorativo: a 320px il contenitore lascia 272px utili
   (320 − 24×2 di padding) e logo 34px + brand 20px/700 + back-link testuale non
   ci stanno — il foglio non ha alcuna media query di larghezza, quindi senza
   wrap la riga sfonda e con lei tutta la pagina scrolla in orizzontale. Poppins
   peggiora il conto rispetto al font di sistema di prima. */
.topbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-md);
  padding: var(--sp-xl) 0;
  border-bottom: 1px solid var(--line);
}
.topbar .brand-name {
  font-weight: 700;
  font-size: 20px;
  letter-spacing: 0.3px;
}
.topbar img { width: 34px; height: 34px; border-radius: var(--r-sm); }
/* Vale per il back-link e per i link di sezione che /gym/index.html mette qui:
   sono navigazione, non azioni, e a corpo pieno competerebbero con la CTA
   dell'hero che sta due righe sotto. Le altre pagine hanno un solo `<a>` nella
   topbar e vedono esattamente i valori di prima. */
.topbar a { font-size: 15px; color: var(--text-dim); }
.topbar .back-link { margin-left: auto; }
/* Solo il PRIMO back-link spinge a destra. Le 6 pagine legali ne hanno tre
   (due switch di lingua + Home) e portavano un `margin-left:0` inline sul secondo:
   con due `margin-left:auto` lo spazio libero si distribuisce FRA i due e il
   gruppo si spacca in mezzo alla topbar. `~` e non `+`: regge anche se un
   giorno fra i due finisse dell'altro. */
.topbar .back-link ~ .back-link { margin-left: 0; }
/* Gruppo di link di sezione (oggi solo /gym/index.html). È `margin-left: auto`
   il gruppo, non i singoli link: così su desktop la fila è allineata a destra e
   a 320px scende in blocco sotto il brand, invece di distribuirsi una voce per
   riga con l'ultima spinta a destra dal suo stesso auto-margin. */
.topbar-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-sm) var(--sp-xl);
  margin-left: auto;
}

/* ── Hero ────────────────────────────────────────────────────────────────── */
.hero {
  /* appGradient: Ring Black → Carbon → Electric Blue (top-left → bottom-right) */
  background: linear-gradient(135deg, #05070B 0%, #111722 60%, #2979FF 100%);
  /* Asimmetrico di proposito: il fondo respira di più per staccare l'hero
     dalla prima sezione, che ha lo stesso colore di sfondo. */
  padding: var(--sp-section) 0 calc(var(--sp-section) + var(--sp-lg));
  text-align: center;
}
.hero img.logo {
  width: 84px;
  height: 84px;
  border-radius: var(--r-2xl);
  margin-bottom: var(--sp-2xl);
}
.hero h1 {
  font-size: clamp(34px, 6vw, 56px);
  line-height: 1.05;
  margin: 0 0 var(--sp-lg);
  letter-spacing: -0.5px;
}
.hero .tagline {
  font-size: clamp(18px, 2.6vw, 22px);
  color: var(--text-dim);
  max-width: 640px;
  margin: 0 auto var(--sp-3xl);
}

/* ── Store badge ─────────────────────────────────────────────────────────── */
.store-badges {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-lg);
  justify-content: center;
}
.store-badge {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding: var(--sp-md) var(--sp-2xl);
  border: 1px solid rgba(240, 244, 255, 0.25);
  border-radius: var(--r-md);
  background: rgba(5, 7, 11, 0.35);
  color: var(--text);
  min-width: 190px;
  text-align: left;
  cursor: default;
}
.store-badge .badge-top { font-size: 12px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 1px; }
.store-badge .badge-main { font-size: 18px; font-weight: 700; }
/* REGOLA: il testo sotto i 18px non va MAI direttamente sul gradiente. Qui il
   fondo semi-trasparente del badge lasciava passare l'estremità blu e questo
   chip cadeva a ~2,8:1, sotto AA. Su carbon pieno è 7,7:1. La regola vale anche
   per i badge store veri, quando arriveranno: cambia il testo, non il fondo. */
.store-badge .badge-soon {
  font-size: 12px;
  font-weight: 600;
  color: var(--accent-soft);
  background: var(--bg);
  border-radius: var(--r-sm);
  padding: 2px var(--sp-sm);
  margin-top: var(--sp-xs);
}

/* ── Vetrina prodotto ─────────────────────────────────────────────────────── */
.showcase { margin-top: 48px; }

/* Cornice device RIUSABILE — sostituisce il vecchio `.shot`, che era un
   rettangolo tratteggiato monouso con dentro "Screenshot app (da inserire)".
   Accetta un <img>/<video> (screenshot e demo, quando ci saranno) oppure un
   contenuto HTML, come il mock console qui sotto. Serve anche alla futura
   pagina referral e a /gym.
   9/19.5 e non 9/19: i device target di oggi sono più stretti. */
.device-frame {
  width: 260px;
  max-width: 100%;
  aspect-ratio: 9 / 19.5;
  margin: 0 auto;
  border: 1px solid var(--line);
  border-radius: var(--r-3xl);
  background: var(--surface);
  box-shadow: var(--elev-2);
  overflow: hidden;
}
.device-frame > img,
.device-frame > video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Mock console: replica in HTML della grammatica "Fight Analysis Console" del
   kit fight_lab. Esiste per mostrare il prodotto senza dipendere da screenshot
   reali; quando ci saranno, si sostituisce con un <img> dentro la stessa
   cornice e questo blocco può sparire. */
.app-mock {
  height: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--sp-md);
  padding: var(--sp-lg);
  text-align: left;
  /* gridLine #263244 a bassa opacità: la texture "analisi tecnica" dell'app.
     A piena tinta le righe si leggevano come voci di lista vuote — cioè come
     una schermata non caricata, che è l'opposto di ciò che il mock deve dire. */
  background:
    linear-gradient(rgba(38, 50, 68, 0.45) 1px, transparent 1px) 0 0 / 100% 44px,
    var(--surface);
}
.mock-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-sm);
}
.mock-round {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--text-dim);
}
/* Unico uso del Corner Red in tutto il sito — vedi il token in `:root` per la
   regola e i contrasti misurati. È l'indicatore di round attivo: un pallino,
   cioè un elemento non testuale, che è l'unica categoria in cui il rosso è
   ammesso. */
.mock-round::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: var(--r-full);
  background: var(--corner-red);
}
.mock-time {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.5px;
  color: var(--text);
}

/* Metric strip — i numeri in mono sono il segnale "strumento di misura" più
   forte del design system (AppTextStyles.metricMedium / metricCaption). */
.metric-strip {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-sm);
  background: var(--surface-high);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--sp-md) var(--sp-sm);
}
.metric { text-align: center; }
.metric-value {
  display: block;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 18px;
  line-height: 1.1;
  color: var(--text);
}
.metric-caption {
  display: block;
  margin-top: 2px;
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--text-dim);
}

/* Cresce per occupare l'altezza della cornice: una schermata di telefono con il
   contenuto tutto schiacciato in alto non sembra una schermata. */
.mock-breakdown { display: flex; flex-direction: column; flex: 1; justify-content: space-evenly; gap: var(--sp-sm); }
.mock-row { display: grid; grid-template-columns: 1fr auto; gap: 2px var(--sp-sm); align-items: center; }
.mock-row .k { font-size: 11px; color: var(--text-dim); }
.mock-row .v { font-family: var(--font-mono); font-weight: 700; font-size: 11px; color: var(--text); }
.mock-row .track {
  grid-column: 1 / -1;
  display: block;
  height: 4px;
  border-radius: var(--r-full);
  background: rgba(240, 244, 255, 0.08);
  overflow: hidden;
}
.mock-row .bar {
  display: block;
  height: 100%;
  border-radius: var(--r-full);
  background: var(--accent);
}
/* Palette colpi Wong (2011), copiata da PunchTypeColors in
   lib/constants/app_constants.dart:131 — leggibile in deuteranopia. Classi e
   non attributi di stile inline: da W-33 (13/08/2026) in tutto hosting/ non ce
   n'è più nessuno, e `style-src` è sceso a `'self'` su /, sulle pagine statiche
   e su /legal/**. Resta 'unsafe-inline' sulle due CSP di /gym, dove lo impone
   il badge reCAPTCHA di App Check: è l'unica deroga, ed è di reCAPTCHA, non
   nostra. Le larghezze pure sono classi, per lo stesso motivo.
   Presidiato da test/services/guards/hosting_inline_style_guard.dart. */
.mock-row .bar.jab      { background: #0072B2; }
.mock-row .bar.cross    { background: #D55E00; }
.mock-row .bar.hook     { background: #CC79A7; }
.mock-row .bar.uppercut { background: #E69F00; }
.mock-row .bar.w44 { width: 44%; }
.mock-row .bar.w26 { width: 26%; }
.mock-row .bar.w18 { width: 18%; }
.mock-row .bar.w12 { width: 12%; }

.mock-rounds {
  display: flex;
  flex-direction: column;
  gap: var(--sp-sm);
  border-top: 1px solid var(--line);
  padding-top: var(--sp-md);
}
.mock-rounds .r { display: flex; justify-content: space-between; align-items: baseline; font-size: 11px; color: var(--text-dim); }
.mock-rounds .r b { font-family: var(--font-mono); font-weight: 700; font-size: 11px; color: var(--text); }

.showcase-note {
  margin: var(--sp-lg) auto 0;
  font-size: 13px;
  color: var(--text-dim);
}

/* ── Sezione generica ────────────────────────────────────────────────────── */
.section { padding: var(--sp-section) 0; }
.section h2 {
  font-size: clamp(26px, 4vw, 34px);
  text-align: center;
  margin: 0 0 48px;
}
/* Quando sotto al titolo c'è un occhiello, i 48px vanno DOPO l'occhiello, non
   fra titolo e occhiello: altrimenti i due si leggono come blocchi separati. */
.section h2.tight { margin-bottom: var(--sp-lg); }
.section-lead {
  max-width: var(--maxw-read);
  margin: 0 auto 48px;
  text-align: center;
  color: var(--text-dim);
  font-size: 17px;
}

/* Alternanza di superficie. Prima della Fase 3 la pagina aveva due sole
   superfici in tutto (gradiente dell'hero + fondo base) e lo scroll risultava
   monotono; con nove sezioni serve un ritmo, e questa è la leva più economica —
   niente bordi, niente ombre, solo il carbon già in palette. */
.section.alt { background: var(--surface); }

/* Colonna di lettura, stessa larghezza delle pagine legali e della FAQ: la
   prosa su 1080px non si legge. */
.narrow { max-width: var(--maxw-read); margin-inline: auto; }

/* Barra di fiducia fra hero e corpo. NON è una `.section`: con il suo ritmo
   verticale (fino a 112px sopra e sotto) diventerebbe un blocco, mentre deve
   leggersi come una riga sola. */
.trustbar {
  background: var(--surface);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  padding: var(--sp-lg) 0;
}
.trustbar ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-sm) var(--sp-2xl);
  justify-content: center;
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 14px;
  color: var(--text-dim);
}

/* ── Feature cards ───────────────────────────────────────────────────────── */
.features {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--sp-xl);
}
.feature {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--sp-3xl) var(--sp-2xl);
  box-shadow: var(--elev-2);
  transition: border-color 0.15s ease;
}
/* La card NON è un link: il link sta in fondo, dentro di lei (index.html:282,
   290, 298). L'hover è quindi un rinforzo di ciò che quel link già dice, non la
   promessa che tutta la card sia premibile — per questo cambia il bordo e NON
   solleva la card con un `translateY`, che è l'affordance di un elemento
   cliccabile e qui mentirebbe.
   Lo stesso vale, al contrario, per gli elenchi: `.chips li`, `.perk-list li` e
   `.step` restano fermi perché non c'è niente da premere.
   Le due regole sono separate di proposito. `:focus-within` sta fuori dalla
   media query: è il segnale per chi arriva con Tab, e senza di lui l'effetto
   sarebbe una decorazione riservata a chi ha un mouse. */
.feature:focus-within { border-color: var(--accent-soft); }
@media (hover: hover) {
  .feature:hover { border-color: var(--accent-soft); }
}
.feature .ico { color: var(--accent-soft); line-height: 1; margin-bottom: var(--sp-lg); }
.feature .ico svg { display: block; width: 28px; height: 28px; }
.feature h3 { margin: 0 0 10px; font-size: 20px; }
.feature p { margin: 0; color: var(--text-dim); font-size: 16px; }

/* ── Elenchi non-card ────────────────────────────────────────────────────────
   Il rischio della Fase 3 non era avere poche card, era averne dodici uguali:
   tre griglie di `.feature` consecutive (metriche, percorsi, funzioni) sono lo
   stesso difetto delle tre card di partenza, moltiplicato. Da qui due registri
   visivi in più, ciascuno usato una volta sola: chip per gli elenchi brevi di
   nomi, righe per gli elenchi lunghi con descrizione. */
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-md);
  justify-content: center;
  list-style: none;
  margin: 0 auto;
  padding: 0;
  max-width: var(--maxw-read);
}
.chips li {
  background: var(--surface-high);
  border: 1px solid var(--line);
  border-radius: var(--r-full);
  padding: var(--sp-sm) var(--sp-lg);
  font-size: 15px;
}

/* 260px come `.pricing` e `.features`, e per la stessa ragione: `.narrow` non
   aggiunge padding proprio, quindi lo spazio utile a 320px resta 272px e un
   pavimento a 280 sfonderebbe qui esattamente come là. */
.perk-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--sp-xl) var(--sp-3xl);
  max-width: 900px;
  margin: 0 auto;
  padding: 0;
  list-style: none;
}
.perk-list li { border-top: 1px solid var(--line); padding-top: var(--sp-lg); }
.perk-list b { display: block; font-size: 16px; margin-bottom: var(--sp-xs); }
.perk-list span { color: var(--text-dim); font-size: 15px; }

/* ── Componenti condivisi ────────────────────────────────────────────────────
   Vivevano in gym/gym.css, dove li aveva scritti il portale B2B — costruito
   dopo la landing e, paradossalmente, più curato: aveva una CTA nell'hero, una
   a metà pagina e un microcopy che rimuove l'obiezione, mentre la home non
   aveva un solo elemento cliccabile.
   Sono stati SPOSTATI, non copiati: due `.btn` divergenti fra due mesi sono
   esattamente il debito che questo spostamento previene. Le pagine di /gym
   caricano styles.css PRIMA di gym.css, quindi per loro non cambia nulla. */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-sm);
  /* Touch target WCAG 2.5.5. `min-height` e non più padding: dopo il passaggio
     a Poppins il padding verticale non basta a predire l'altezza della riga. */
  min-height: 48px;
  padding: var(--sp-md) var(--sp-3xl);
  border-radius: var(--r-md);
  border: 1px solid transparent;
  background: var(--btn-primary-bg);
  color: #fff;
  font-size: 16px;
  font-weight: 700;
  font-family: var(--font);
  cursor: pointer;
  text-decoration: none;
  /* `background-color` è nell'elenco perché è ciò che l'hover del primario
     cambia davvero: la riga dichiarava ancora solo `filter`/`opacity`, rimasti
     dal tempo in cui l'hover era `brightness()`, quindi il bottone scattava
     invece di sfumare. Una transizione che nomina una proprietà che nessuno
     tocca non fallisce e non si vede: si nota solo mettendo le due righe
     vicine. */
  transition: background-color 0.15s ease, filter 0.15s ease, opacity 0.15s ease;
}
.btn:disabled { opacity: 0.55; cursor: not-allowed; }
.btn.block { width: 100%; }
.btn.secondary {
  background: transparent;
  border-color: var(--line);
  color: var(--text);
}
.btn.subtle {
  background: transparent;
  border: none;
  color: var(--text-dim);
  font-weight: 600;
  padding: var(--sp-sm) var(--sp-md);
}

/* ── Stati interattivi ───────────────────────────────────────────────────────
   Gli `:hover` stanno tutti dentro `@media (hover: hover)`. Su un touchscreen
   il browser applica comunque `:hover` al tap e lo lascia attaccato finché non
   si tocca altrove: il bottone appena premuto resta illuminato mentre la pagina
   scorre, e sembra selezionato invece che premuto. La media query non è una
   rifinitura per desktop, è ciò che toglie un artefatto dal mobile.
   `:active` invece sta FUORI, ed è il punto della faccenda: su touch è l'unico
   feedback che un dito riceve, perché l'hover lì non esiste. */
@media (hover: hover) {
  /* Il primario cambia SFONDO invece di applicare un filtro: così il testo
     resta bianco puro e il contrasto sale a 5,75:1 (misurato) invece di
     scendere. Con `brightness(1.12)` — la regola precedente — arrivava a
     4,04:1, cioè sotto l'AA proprio nell'istante in cui l'utente sta per
     premere. Le due varianti trasparenti hanno la loro regola sotto: il loro
     testo sta su fondo scuro, dove è schiarire che aumenta il contrasto. */
  .btn:hover { background: var(--btn-primary-bg-hover); text-decoration: none; }
  /* `background: transparent` esplicito: è la regola generale a impostare uno
     sfondo pieno sull'hover, e senza questa riga la secondary lo erediterebbe. */
  .btn.secondary:hover { background: transparent; border-color: var(--accent-soft); filter: none; }
  .btn.subtle:hover { background: transparent; filter: brightness(1.12); }
}

/* Stessa scelta dell'hover: sul primario si cambia lo sfondo, mai `filter`, che
   agirebbe anche sul testo bianco e abbasserebbe il contrasto proprio mentre il
   dito è sul bottone. Le trasparenti prendono un velo chiarissimo — su fondo
   scuro è l'equivalente visivo dell'affondamento.
   Vengono DOPO il blocco hover di proposito: con un mouse premere significa
   hover + active insieme, e a parità di specificità decide l'ordine. */
.btn:active { background: var(--btn-primary-bg-hover); }
.btn.secondary:active,
.btn.subtle:active { background: rgba(240, 244, 255, 0.07); }

/* Piano / prezzi — serve al portale e alla futura sezione "Free vs Pro". */
.plan-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg);
  padding: var(--sp-xl);
  margin-bottom: var(--sp-md);
  cursor: pointer;
  position: relative;
  box-shadow: var(--elev-1);
  transition: border-color 0.15s ease;
}
@media (hover: hover) {
  .plan-card:hover { border-color: var(--accent-soft); }
}
/* L'anello di selezione va COMPOSTO con l'elevation, non sostituito:
   sovrascriverlo toglierebbe al checkout il feedback di scelta del piano. */
.plan-card.selected {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(41, 121, 255, 0.15), var(--elev-1);
}
.plan-card .plan-head { display: flex; align-items: baseline; gap: var(--sp-sm); }
.plan-card h3 { margin: 0; font-size: 19px; }
.plan-card .plan-badge {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  color: var(--accent-soft);
  border: 1px solid rgba(41, 121, 255, 0.4);
  border-radius: var(--r-sm);
  padding: 2px var(--sp-sm);
}
.plan-card .plan-price { margin: var(--sp-sm) 0 2px; font-size: 26px; font-weight: 800; }
.plan-card .plan-price small { font-size: 14px; font-weight: 400; color: var(--text-dim); }
.plan-card .plan-eq { font-size: 13px; color: var(--text-dim); margin: 0 0 10px; }
.plan-card ul { margin: 0; padding-left: 18px; color: var(--text-dim); font-size: 14px; }
.plan-card li { margin-bottom: 3px; }

/* Griglia di card prezzo statiche (non selezionabili).
   260px e non 280: il minimo di `minmax` è un PAVIMENTO, non una preferenza —
   se la traccia non ci sta, la griglia sfonda invece di restringersi. A 320px
   lo spazio utile è 272px, quindi 280 era 8px oltre e la sezione prezzi
   scrollava in orizzontale. 260 è lo stesso valore di `.features`, che con la
   stessa struttura non ha mai avuto il problema. */
.pricing {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--sp-xl);
  max-width: 760px;
  margin: 0 auto;
}
/* Tre piani non stanno in 760px: 3×260 + 2 gap ne chiedono 820, quindi
   `auto-fit` ne manderebbe uno a capo da solo. Modificatore invece di alzare il
   max-width del componente: su /gym il listino è di una card sola (Sprint 4) e
   760px è la misura che la tiene larga quanto la colonna di testo sopra. */
.pricing.three { max-width: 1000px; }
/* `cursor: default` perché queste card non si selezionano: sono un listino, non
   il selettore di piano del checkout. L'hover del componente era quindi
   ANNULLATO (`border-color: var(--line)`, cioè il valore a riposo).
   Ora c'è, ed è deliberatamente un'EVIDENZIAZIONE e non un'affordance di
   click: si alza e accende il bordo, ma il cursore resta la freccia e nessun
   elemento diventa premibile. La differenza la fa il cursore, che è il segnale
   su cui l'utente decide se provare a cliccare.
   Vale anche per /gym/index.html, che usa lo stesso `.pricing` per la sua card
   di listino — una sola dallo Sprint 4 (11/08/2026), che ha collassato Starter
   e Pro Gym in "RingVision Gym": è lo stesso componente nello stesso ruolo, ed
   è esattamente ciò che la promozione dei componenti in questo foglio doveva
   impedire che divergesse. NON tocca la `.plan-card` di /gym/checkout.html, che
   non è dentro `.pricing` — sta in `#plan-section` — e che non è premibile: da
   Sprint 4 il piano è uno e non c'è più un selettore. Ciò che si sceglie lì è
   la periodicità, ed è il `role="radiogroup"` accanto (W-18, 13/08/2026). */
.pricing .plan-card {
  cursor: default;
  transition: border-color 0.15s ease, box-shadow 0.2s ease, transform 0.2s ease;
}
@media (hover: hover) {
  .pricing .plan-card:hover {
    border-color: var(--accent-soft);
    box-shadow: var(--elev-2);
    transform: translateY(-4px);
  }
}
/* Il blocco globale `prefers-reduced-motion` azzera le DURATE, non gli stati:
   senza questa riga la card salterebbe comunque di 4px, istantaneamente, che
   per chi ha chiesto meno movimento è il movimento peggiore. Il bordo e
   l'ombra restano — non sono moto. */
@media (prefers-reduced-motion: reduce) {
  .pricing .plan-card:hover { transform: none; }
}
.pricing .plan-card.featured { border-color: var(--accent); }

/* Stepper "come funziona" — numerazione via counter CSS, così l'ordine non è
   duplicato nel markup. */
.steps { max-width: 640px; margin: 0 auto; counter-reset: step; }
.step {
  display: flex;
  gap: var(--sp-lg);
  padding: var(--sp-lg) 0;
  border-bottom: 1px solid var(--line);
}
.step:last-child { border-bottom: none; }
.step .n {
  counter-increment: step;
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: var(--r-full);
  border: 1px solid var(--accent);
  color: var(--accent-soft);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 800;
  font-size: 15px;
}
.step .n::before { content: counter(step); }
.step h3 { margin: var(--sp-xs) 0 var(--sp-xs); font-size: 17px; }
.step p { margin: 0; color: var(--text-dim); font-size: 15px; }

/* Utility */
.muted { color: var(--text-dim); font-size: 14px; }
/* Un link dentro una riga di prosa secondaria è distinguibile SOLO per colore, e
   `--accent-soft` contro `--text-dim` dà 1,96:1 contro il 3:1 che WCAG 1.4.1
   richiede quando il colore è l'unico segnale (misurato da Lighthouse su
   `p.muted > a`, non dedotto: due occorrenze per pagina, la CTA verso /gym sotto
   i piani e l'indirizzo di supporto). La sottolineatura è il segnale non
   cromatico, ed è anche il motivo per cui non basta scurire il testo attorno.
   Vale solo dentro `.muted`: altrove i link sono isolati in una riga propria o
   sono `.btn`, dove il colore non è l'unico segnale. */
.muted a { text-decoration: underline; text-underline-offset: 2px; }
/* W-18: un'AZIONE che sta dentro una riga di prosa. Era un `<a href="#">` con
   `preventDefault()` — "Esci" su /gym/verify e "Password dimenticata?" su
   /gym/account: se il JS non parte (lo scenario per cui esiste boot-guard.js)
   il click salta in cima alla pagina senza dire nulla, e uno screen reader lo
   annuncia come collegamento verso il nulla. È un `<button>`, e questa classe
   gli toglie l'aspetto di bottone senza toccarne la semantica. `font: inherit`
   perché i controlli di form non ereditano il font: senza, in mezzo a una
   frase in Poppins comparirebbe il font di sistema. */
.link-btn {
  background: none;
  border: 0;
  padding: 0;
  color: inherit;
  font: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.center { text-align: center; }
.mt-8  { margin-top: var(--sp-sm); }
.mt-16 { margin-top: var(--sp-lg); }
.mt-24 { margin-top: var(--sp-2xl); }

/* ── Footer ──────────────────────────────────────────────────────────────── */
.site-footer {
  border-top: 1px solid var(--line);
  background: var(--surface);
  padding: 48px 0 40px;
}
/* Touch target (WCAG 2.5.5): con font-size 15px e nessun padding l'area
   cliccabile era ~24px. Il padding verticale la porta a 48px; il row-gap scende
   da 20 a 8 per compensarlo, così il ritmo verticale del footer resta identico
   a prima e cambia solo ciò che il dito colpisce. */
.site-footer .foot-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-sm) var(--sp-xl);
  margin-bottom: var(--sp-md);
}
.site-footer .foot-links a {
  color: var(--text-dim);
  font-size: 15px;
  display: inline-flex;
  align-items: center;
  min-height: 48px;
  transition: color 0.15s ease;
}
/* Questi link nascono `--text-dim` per non competere con il contenuto: senza
   una regola propria, l'unico segnale al passaggio del mouse sarebbe la
   sottolineatura di `a:hover`, che su un elenco di dieci voci ravvicinate non
   dice quale delle dieci si sta per premere. */
@media (hover: hover) {
  .site-footer .foot-links a:hover { color: var(--accent-soft); }
}
.site-footer .foot-meta { color: var(--text-dim); font-size: 14px; line-height: 1.7; }
.site-footer .foot-brand { font-weight: 700; letter-spacing: 1px; text-transform: uppercase; color: var(--accent-soft); margin-bottom: 10px; }

/* Placeholder da compilare prima del rilascio (nome titolare, indirizzo…) */
mark.placeholder {
  background: rgba(41, 121, 255, 0.18);
  color: var(--accent-soft);
  padding: 1px 6px;
  border-radius: 5px;
  font-family: monospace;
  font-size: 0.92em;
}

/* ── Documento legale / lettura ──────────────────────────────────────────── */
.legal {
  max-width: var(--maxw-read);
  margin: 0 auto;
  padding: 48px var(--sp-2xl) 96px;
}
.legal h1 { font-size: 30px; margin: 0 0 8px; }
.legal .updated { color: var(--text-dim); font-size: 14px; margin: 0 0 40px; }
.legal h2 { font-size: 22px; margin: 40px 0 12px; border-top: 1px solid var(--line); padding-top: 28px; }
.legal h3 { font-size: 18px; margin: 28px 0 10px; }
.legal p { color: var(--text-dim); margin: 0 0 14px; }
.legal ul { color: var(--text-dim); padding-left: 22px; margin: 0 0 16px; }
.legal li { margin-bottom: 6px; }
.legal .note {
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: var(--r-md);
  padding: var(--sp-lg) var(--sp-xl);
  margin: var(--sp-2xl) 0;
}
.legal table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
  margin: 16px 0 24px;
  display: block;
  overflow-x: auto;
  white-space: nowrap;
}
.legal th, .legal td {
  border: 1px solid var(--line);
  padding: var(--sp-sm) var(--sp-md);
  text-align: left;
  color: var(--text-dim);
}
.legal th { background: var(--surface-high); color: var(--text); }

/* ── Card centrata (stripe callback / help hero) ─────────────────────────── */
.center-card {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 40px var(--sp-2xl);
}
.center-card .card { max-width: 460px; }
.center-card .logo { width: 52px; height: 52px; border-radius: var(--r-lg); margin-bottom: var(--sp-xs); }
.center-card .icon { color: var(--accent-soft); line-height: 1; margin-top: var(--sp-lg); }
.center-card .icon svg { display: block; width: 48px; height: 48px; margin: 0 auto; }
/* Stessa semantica di colore delle .status-pill del portale: esito positivo in
   verde, non nell'accento del brand — su una pagina di esito il colore è
   informazione, non decorazione. */
.center-card .icon.ok { color: #9AE6A0; }
.center-card h1 { font-size: 26px; margin: var(--sp-xl) 0 var(--sp-md); }
.center-card p { color: var(--text-dim); margin: 0 0 var(--sp-sm); }
.center-card .brand {
  margin-top: var(--sp-3xl);
  font-size: 13px;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--accent-soft);
  font-weight: 700;
}

/* ── FAQ ─────────────────────────────────────────────────────────────────── */
.faq { max-width: var(--maxw-read); margin: 0 auto; padding: 0 var(--sp-2xl) 80px; }
/* Dentro una `.section` gli 80px si sommano al padding della sezione (fino a
   112px) e aprono un buco di 192px. Fuori — come in help.html, dove `.faq` è
   figlia diretta di <main> — la regola non si applica e il padding resta. */
.section > .faq { padding-bottom: 0; }
/* Solo help.html: è l'unica pagina che mette l'h2 DENTRO `.faq`. Altrove
   (index.html, en.html, gym/index.html) il titolo è FRATELLO del blocco e sta
   dentro una `.section`, quindi lo veste `.section h2` — che qui non arriva, ed
   è il motivo per cui su help c'era uno stile inline. Riproduce esatti i valori
   dell'attributo che ha sostituito. */
.faq > h2 { text-align: center; margin: 40px 0 24px; }
.faq .qa { border-bottom: 1px solid var(--line); padding: var(--sp-xl) 0; }
.faq .qa h3 { margin: 0 0 var(--sp-sm); font-size: 18px; }
.faq .qa p { margin: 0; color: var(--text-dim); }
.support-box {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--sp-2xl);
  text-align: center;
  margin: var(--sp-3xl) auto var(--sp-sm);
  max-width: var(--maxw-read);
}
.support-box .email { font-size: 20px; font-weight: 700; color: var(--accent-soft); }

/* ── Atterraggio deep link (/referral, /join) ────────────────────────────── */
/* Modificatore, non un cambio di `.center-card .card`: le pagine Stripe e la 404
   sono una card d'esito e i loro 460px vanno bene. Queste due portano anche i
   passi e i badge store. */
.center-card .card.wide { max-width: 560px; }
/* `.center-card` non aveva un h2: la card d'esito ha un titolo solo. Qui i passi
   ne hanno bisogno, e serve anche a non saltare da h1 a h3. */
.center-card h2 { font-size: 18px; margin: var(--sp-3xl) 0 var(--sp-md); }
/* `.center-card` centra tutto, ma `.step` è una riga numero + testo: centrata
   perde l'allineamento della colonna di sinistra. */
.center-card .steps { text-align: left; }

.code-label {
  text-transform: uppercase;
  letter-spacing: 1px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-dim);
  margin: var(--sp-2xl) 0 var(--sp-sm);
}
/* Il mono è quello dei numeri dell'app, ma qui la ragione è funzionale prima che
   estetica: questo codice va ricopiato a mano al primo avvio (l'Install Referrer
   non lo consegna, vedi referral.html), e a 22px in JetBrains Mono 0/O e 1/I
   restano distinguibili. `user-select: all` lo fa selezionare tutto con un tap. */
.code-box {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 22px;
  font-weight: 700;
  letter-spacing: 2px;
  color: var(--accent-soft);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--sp-md) var(--sp-xl);
  margin: 0 0 var(--sp-lg);
  user-select: all;
  -webkit-user-select: all;
  word-break: break-all;
}
/* Riga di link di chiusura. Due difetti misurati su Chrome, non dedotti:
   - senza `min-height` i link sono alti quanto la riga di testo, 24px: metà del
     minimo WCAG 2.5.5. È lo stesso rimedio già applicato al footer, che però
     vive sotto `.site-footer .foot-links a` e qui non arriva — e `.btn` non
     c'entra, questi non sono `.btn`;
   - i separatori erano `·` scritti nel markup, e a 390px la riga di `/join` va
     a capo lasciandone uno appeso a fine riga. Con flex + `gap` non esistono
     separatori da lasciare appesi, a qualunque larghezza. */
.center-card .link-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0 var(--sp-xl);
}
.center-card .link-row a {
  display: inline-flex;
  align-items: center;
  min-height: 48px;
}
.reward {
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: var(--r-md);
  padding: var(--sp-lg) var(--sp-xl);
  text-align: left;
  margin: var(--sp-lg) 0 0;
}
