@charset "UTF-8";
/*!
 * Riviera — design system per la Telegram Mini App «Наши в Сан-Ремо»
 * File GENERATO da scripts/build.mjs a partire da src/riviera.css.
 * Non modificarlo a mano: modifica i sorgenti in src/ e rilancia la build.
 * Build: 2026-09-20T11:05:29.274Z
 */
/* ==========================================================================
   Riviera · riviera.css — indice del design system
   Da collegare nelle pagine durante lo sviluppo (…/src/riviera.css).
   In produzione usa dist/riviera.css (node scripts/build.mjs).
   L'ordine conta: caratteri → token → base → icone → componenti.
   Ogni gruppo di componenti aggiunge qui la propria riga se manca.
   ========================================================================== */


/* ─── src/fonts.css ─────────────────────────────────────────────── */
/* ==========================================================================
   Riviera · fonts.css
   Caratteri self-hosted (SIL OFL 1.1, licenze in ../fonts/LICENSE-*.txt).
   Fonte: pacchetti @fontsource 5.3.x, sottoinsiemi cirillico + latino.
   Gli unicode-range sono copiati esattamente dai CSS di fontsource: il browser
   scarica solo il sottoinsieme che serve al testo della pagina.
   Nota: simboli come ♡ ★ non sono in nessuno dei tre caratteri (cadrebbero sul
   font di sistema, diverso su ogni telefono): usare le icone .rv-icon--heart,
   .rv-icon--heart-fill, .rv-icon--star-fill, anche accanto alla firma a mano.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Playfair Display — titoli, prezzi grandi, citazioni. 400/500/600 normale + 400/500 corsivo.
   -------------------------------------------------------------------------- */

@font-face {
  font-family: "Playfair Display";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/playfair-display-cyrillic-400-normal.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: "Playfair Display";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/playfair-display-latin-400-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Playfair Display";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/playfair-display-cyrillic-500-normal.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: "Playfair Display";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/playfair-display-latin-500-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Playfair Display";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/playfair-display-cyrillic-600-normal.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: "Playfair Display";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/playfair-display-latin-600-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Playfair Display";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/playfair-display-cyrillic-400-italic.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: "Playfair Display";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/playfair-display-latin-400-italic.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Playfair Display";
  font-style: italic;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/playfair-display-cyrillic-500-italic.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: "Playfair Display";
  font-style: italic;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/playfair-display-latin-500-italic.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* --------------------------------------------------------------------------
   Onest — interfaccia e testo. 400/500/600/700.
   -------------------------------------------------------------------------- */

@font-face {
  font-family: "Onest";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/onest-cyrillic-400-normal.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: "Onest";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/onest-latin-400-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Onest";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/onest-cyrillic-500-normal.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: "Onest";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/onest-latin-500-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Onest";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/onest-cyrillic-600-normal.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: "Onest";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/onest-latin-600-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Onest";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../fonts/onest-cyrillic-700-normal.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: "Onest";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../fonts/onest-latin-700-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* --------------------------------------------------------------------------
   Bad Script — SOLO note a mano decorative (mai sotto 20px, una per schermata).
   -------------------------------------------------------------------------- */

@font-face {
  font-family: "Bad Script";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/bad-script-cyrillic-400-normal.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: "Bad Script";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/bad-script-latin-400-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}


/* ─── src/tokens.css ────────────────────────────────────────────── */
/* ==========================================================================
   Riviera · tokens.css
   Primitive (scale di colore con nomi fissi) + token semantici + scale di
   spazio, raggio, tipo, moto, livelli, tocco, layout, aree sicure.

   REGOLE
   - I componenti usano SOLO i token semantici (--rv-bg, --rv-text, …),
     mai le primitive (eccezione: illustrazioni).
   - Tema chiaro «Giorno» = base. Tema scuro «Notte a Sanremo» = progetto a sé.
   - Struttura a tre stati:
       1. :root                                   → palette chiara completa
       2. @media (prefers-color-scheme: dark)
            :root:not([data-theme="light"])       → ridefinizioni scure
       3. :root[data-theme="dark"]                → stesse ridefinizioni
   - Temi annidati: qualsiasi elemento con [data-theme="light"|"dark"]
     ridefinisce i token semantici per il proprio sottoalbero.
   - I blocchi 2 e 3 devono restare IDENTICI e contenere le stesse chiavi del
     blocco semantico chiaro: lo verifica scripts/check-contrast.mjs.
   - Un token che ne cita un altro (es. --rv-image-fade: var(--rv-bg)) va
     ripetuto in ogni tema: var() si risolve dove il token è dichiarato.
   - I token di colore sono solo hex/rgba (niente color-mix(), oklch()…): li
     leggono così come sono scripts/check-contrast.mjs, tokens/tokens.json e,
     tramite js/riviera-telegram.js, i colori nativi del client Telegram.

   SUPPORTO WEBVIEW
   - Baseline: iOS 16.2+ (Safari e WKWebView) e Android System WebView /
     Chrome 111+. Lì esistono color-mix(), :has() e le proprietà logiche
     usate nel sistema: la resa è quella progettata e verificata.
   - Sotto la baseline la resa perfetta non è garantita, ma nulla di
     essenziale deve sparire. Regola per chi scrive i componenti: segmento
     scelto, spinner, stato scelto di chip e tab, bordi dei controlli hanno
     prima un valore pieno a token, senza color-mix() né :has(); la versione
     moderna viene dopo (la cascata fa da ripiego) oppure sta in @supports.
     Attenzione: una dichiarazione con var() e color-mix() NON ricade su
     quella precedente in un motore che non conosce color-mix() (vale
     «unset»): in quel caso serve @supports (color: color-mix(in srgb, red, blue)).
     Un selettore con :has() invalida tutta la sua lista: va in una regola a
     parte, accanto al ripiego con le classi che mette riviera-telegram.js.

   TOKEN PER CASI PARTICOLARI
   - --rv-on-image / --rv-on-image-scrim: testo e icone sopra una foto. Il
     velo, nel suo punto più denso, garantisce 4,5:1 anche su una foto bianca;
     il componente lo sfuma (gradiente) dove il testo non c'è.
   - --rv-border-control: bordo a riposo di bottone outline e tessera upload,
     a metà fra border e border-strong. Sta sotto 3:1 di proposito: il
     controllo si riconosce dal testo/icona. Input, checkbox e radio usano
     --rv-border-strong (≥3:1), perché il bordo è l'unico segno del campo.
   - --rv-skeleton / --rv-skeleton-glint: osso e riflesso dello shimmer; il
     riflesso è sempre PIÙ CHIARO dell'osso (anche di notte).
   - --rv-image-muted: filtro completo per immagini spente (annuncio venduto).
     Sostituisce --rv-image-dim, non va concatenato: `none` + altro non è valido.
   - --rv-surface-hover è sempre distinto da --rv-surface-raised (lo verifica
     check-contrast.mjs), così l'hover si vede anche su sheet e dialog.
   - --rv-link / --rv-link-hover: il verde quando è TESTO (link, azione del
     toast, azioni dei titoli di sezione, valore della riga scelta). AA su
     bg, surface, surface-raised, surface-hover e bg-muted in entrambi i temi.
     --rv-primary resta per i pieni: di notte (pino-400) come testo scende a
     4,0:1 su surface-raised e 3,6:1 su surface-hover.
   - --rv-sticky-h: altezza della barra .rv-sticky, usata da navigation.css
     per lo scroll-padding (il focus non finisce sotto la barra). Una pagina
     con una barra più alta lo ridefinisce su :root (non su html: :root vince).
     Esempio: ricerca + fila di chip = 8 + 44 + 44 + 8 → :root { --rv-sticky-h: 104px; }
   - --rv-chat-bar-h: altezza a riposo della barra .rv-chat-bar (campo a una
     riga). La usano .rv-app--chat-bar per lo spazio in fondo al contenuto e
     lo scroll-padding di navigation.css. Il campo che cresce NON la cambia:
     sotto il campo alto c'è solo la fine della conversazione, e il focus è
     nel campo stesso.
   ========================================================================== */


/* --------------------------------------------------------------------------
   1a. PRIMITIVE + SCALE (non dipendono dal tema)
   -------------------------------------------------------------------------- */
:root {
  /* Latte — fondi crema caldi (0 e 25 = bianchi per superfici) */
  --rv-latte-0: #FFFFFF;
  --rv-latte-25: #FFFDF8;
  --rv-latte-50: #FBF8F3;
  --rv-latte-100: #F7F3EC;
  --rv-latte-150: #F3EEE5;
  --rv-latte-200: #EFE8DC;
  --rv-latte-250: #E9E1D3;
  --rv-latte-300: #E3D9C8;
  --rv-latte-400: #CFC3AE;
  --rv-latte-450: #BCB29F;
  --rv-latte-500: #958A75;

  /* Pino — verde primario */
  --rv-pino-50: #EDF3EF;
  --rv-pino-100: #DCE8E1;
  --rv-pino-200: #BCD4C8;
  --rv-pino-300: #8DB8A6;
  --rv-pino-350: #72A790;
  --rv-pino-400: #5E9781;
  --rv-pino-450: #4F8A73;
  --rv-pino-500: #2E6A5A;
  --rv-pino-600: #24594C;
  --rv-pino-700: #1C473D;
  --rv-pino-800: #15362F;
  --rv-pino-900: #0E241F;

  /* Ottone — dettaglio prezioso (≤400 decorativo, ≥600 per testo) */
  --rv-ottone-200: #E8D6B5;
  --rv-ottone-300: #D8BB8C;
  --rv-ottone-400: #C4A06E;
  --rv-ottone-500: #AB8555;
  --rv-ottone-600: #9A7548;
  --rv-ottone-700: #77572F;
  --rv-ottone-900: #372B1B;

  /* Cipria — rosa */
  --rv-cipria-100: #F6E3DE;
  --rv-cipria-200: #EFCFC7;
  --rv-cipria-400: #D98E7F;
  --rv-cipria-600: #A5584B;
  --rv-cipria-700: #86463B;
  --rv-cipria-900: #3C2723;

  /* Salvia — olivo/greige */
  --rv-salvia-100: #ECEBE2;
  --rv-salvia-200: #DFE0D2;
  --rv-salvia-400: #A7AE91;
  --rv-salvia-600: #6E7A5B;
  --rv-salvia-700: #505B40;
  --rv-salvia-800: #2E3527;
  --rv-salvia-900: #252C23;

  /* Mare — blu riviera, uso raro (info, immobili) */
  --rv-mare-100: #DFEBEE;
  --rv-mare-300: #8EC1CE;
  --rv-mare-600: #336D7C;
  --rv-mare-700: #2A5A67;
  --rv-mare-900: #13303A;

  /* Sole — stelle e valutazioni */
  --rv-sole-400: #E5A33A;
  --rv-sole-500: #C07C16;

  /* Rosso — errore */
  --rv-rosso-100: #F6E0DA;
  --rv-rosso-300: #EE9480;
  --rv-rosso-600: #B03F2B;
  --rv-rosso-900: #3A1D17;

  /* Ambra — avviso */
  --rv-ambra-100: #F6E8CF;
  --rv-ambra-300: #E0B062;
  --rv-ambra-600: #8F5B0E;
  --rv-ambra-900: #33270F;

  /* Inchiostro — neutri caldi che virano al verde (tema chiaro) */
  --rv-ink-300: #B5B8B0;
  --rv-ink-400: #8C918A;
  --rv-ink-500: #626963;
  --rv-ink-600: #4E5651;
  --rv-ink-700: #3A423E;
  --rv-ink-800: #2A322E;
  --rv-ink-900: #1C2420;

  /* Notte — neutri verde-carbone (tema scuro); 200–400 = testi e bordi */
  --rv-notte-200: #C8C6BA;
  --rv-notte-300: #9DA298;
  --rv-notte-400: #6F7C73;
  --rv-notte-500: #515E57;
  --rv-notte-550: #3A4841;
  --rv-notte-600: #33403A;
  --rv-notte-650: #2C3832;
  --rv-notte-700: #26302B;
  --rv-notte-800: #1C2420;
  --rv-notte-850: #171E1A;
  --rv-notte-900: #121815;
  --rv-notte-950: #0D1210;

  /* Famiglie */
  --rv-font-display: "Playfair Display", "Noto Serif", Georgia, "Times New Roman", serif;
  --rv-font-sans: "Onest", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --rv-font-script: "Bad Script", "Segoe Script", "Apple Chancery", cursive;

  /* Pesi */
  --rv-fw-regular: 400;
  --rv-fw-medium: 500;
  --rv-fw-semibold: 600;
  --rv-fw-bold: 700;

  /* Scala tipografica (mobile) */
  --rv-fs-display: 34px;
  --rv-fs-title-1: 28px;
  --rv-fs-title-2: 22px;
  --rv-fs-title-3: 19px;
  --rv-fs-headline: 17px;
  --rv-fs-body-lg: 17px;
  --rv-fs-body: 15px;
  --rv-fs-callout: 14px;
  --rv-fs-caption: 13px;
  --rv-fs-overline: 11px;
  --rv-fs-tag: 10.5px;
  --rv-fs-script: 24px;

  --rv-lh-display: 1.08;
  --rv-lh-title-1: 1.15;
  --rv-lh-title-2: 1.2;
  --rv-lh-title-3: 1.25;
  --rv-lh-headline: 1.3;
  --rv-lh-body-lg: 1.5;
  --rv-lh-body: 1.5;
  --rv-lh-callout: 1.45;
  --rv-lh-caption: 1.4;
  --rv-lh-overline: 1.3;
  --rv-lh-tag: 1.2;
  --rv-lh-script: 1.2;

  --rv-tracking-display: -0.015em;  /* il serif grande respira meglio un filo stretto */
  --rv-tracking-title: -0.005em;
  --rv-tracking-overline: 0.12em;
  --rv-tracking-tag: 0.08em;

  /* Spazi (base 4) */
  --rv-space-0-5: 2px;
  --rv-space-1: 4px;
  --rv-space-1-5: 6px;
  --rv-space-2: 8px;
  --rv-space-3: 12px;
  --rv-space-4: 16px;
  --rv-space-5: 20px;
  --rv-space-6: 24px;
  --rv-space-8: 32px;
  --rv-space-10: 40px;
  --rv-space-12: 48px;
  --rv-space-16: 64px;

  /* Raggi: seguono il ruolo, non sono tutti uguali */
  --rv-radius-xs: 6px;     /* tag piccoli */
  --rv-radius-sm: 10px;    /* bottone interno ricerca, input piccoli */
  --rv-radius-md: 14px;    /* input, tessere */
  --rv-radius-lg: 20px;    /* card, barra ricerca */
  --rv-radius-xl: 28px;    /* sheet, banner, tab bar */
  --rv-radius-pill: 999px;

  /* Linee e focus */
  --rv-border-width: 1px;
  --rv-focus-width: 2px;
  --rv-focus-offset: 2px;

  /* Moto */
  --rv-ease-out: cubic-bezier(0.2, 0.7, 0.2, 1);
  --rv-ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --rv-dur-fast: 120ms;
  --rv-dur-base: 200ms;
  --rv-dur-slow: 320ms;
  --rv-press-scale: 0.97;  /* transform: scale(var(--rv-press-scale)) alla pressione */

  /* Livelli */
  --rv-z-sticky: 20;
  --rv-z-tabbar: 40;
  --rv-z-sheet: 60;
  --rv-z-toast: 80;

  /* Tocco e layout app */
  --rv-tap: 44px;
  --rv-gutter: 16px;
  --rv-tabbar-h: 64px;
  --rv-appbar-h: 56px;
  --rv-sticky-h: 60px;     /* barra .rv-sticky tipica: riga da 44px + 8px sopra e sotto; una barra più alta lo ridefinisce su :root */
  --rv-chat-bar-h: 60px;   /* barra .rv-chat-bar a riposo: campo da 44px + 8px sopra e sotto (l'area sicura si somma a parte) */
  --rv-content-max: 560px;

  /* Aree sicure: il massimo fra quelle del sistema e quelle di Telegram
     (safe area del dispositivo + area occupata dai controlli del client) */
  --rv-safe-top: max(env(safe-area-inset-top, 0px), calc(var(--tg-safe-area-inset-top, 0px) + var(--tg-content-safe-area-inset-top, 0px)));
  --rv-safe-right: max(env(safe-area-inset-right, 0px), calc(var(--tg-safe-area-inset-right, 0px) + var(--tg-content-safe-area-inset-right, 0px)));
  --rv-safe-bottom: max(env(safe-area-inset-bottom, 0px), calc(var(--tg-safe-area-inset-bottom, 0px) + var(--tg-content-safe-area-inset-bottom, 0px)));
  --rv-safe-left: max(env(safe-area-inset-left, 0px), calc(var(--tg-safe-area-inset-left, 0px) + var(--tg-content-safe-area-inset-left, 0px)));
}


/* --------------------------------------------------------------------------
   1b. TEMA CHIARO «Giorno» — token semantici
   Vale per :root e per ogni sottoalbero [data-theme="light"].
   -------------------------------------------------------------------------- */
:root,
[data-theme="light"] {
  color-scheme: light;

  /* Fondi e superfici */
  --rv-bg: var(--rv-latte-100);                 /* fondo pagina */
  --rv-bg-muted: var(--rv-salvia-100);          /* pannelli salvia (banner CTA) */
  --rv-surface: var(--rv-latte-25);             /* card, tessere, tab bar */
  --rv-surface-raised: var(--rv-latte-0);       /* barra ricerca, sheet, dialog, toast */
  --rv-surface-sunken: var(--rv-latte-200);     /* fondo input, aree incassate */
  --rv-surface-hover: var(--rv-latte-150);      /* hover/pressed su superfici (distinto da surface-raised) */
  --rv-scrim: rgba(28, 36, 32, 0.4);            /* velo dietro sheet/dialog */

  /* Linee */
  --rv-border: var(--rv-latte-300);             /* linee sottili */
  --rv-border-strong: var(--rv-latte-500);      /* bordo di input, checkbox, radio (≥3:1: il campo si riconosce) */
  --rv-border-control: var(--rv-latte-450);     /* bordo a riposo di bottone outline e tessera upload; NON per input */

  /* Testo */
  --rv-text: var(--rv-ink-900);                 /* testo principale */
  --rv-text-secondary: var(--rv-ink-700);       /* lede, testo di supporto */
  --rv-text-muted: var(--rv-ink-500);           /* meta, placeholder, didascalie (AA) */
  --rv-text-on-primary: var(--rv-latte-50);     /* testo su pieno primario */
  --rv-heading: var(--rv-ink-900);              /* titoli serif */
  --rv-heading-accent: var(--rv-pino-500);      /* corsivo verde dei titoli */

  /* Azione */
  --rv-primary: var(--rv-pino-500);             /* verde pino: azioni */
  --rv-primary-hover: var(--rv-pino-600);
  --rv-primary-pressed: var(--rv-pino-700);
  --rv-primary-soft: var(--rv-pino-100);        /* tab attiva, chip selezionati */
  --rv-primary-soft-text: var(--rv-pino-700);   /* testo/icona su primary-soft */
  --rv-focus-ring: var(--rv-pino-500);          /* anello di focus */
  --rv-link: var(--rv-pino-500);                /* verde come TESTO: link, azioni testuali, valore scelto (AA su ogni fondo) */
  --rv-link-hover: var(--rv-pino-600);          /* hover/pressione del link: più scuro di giorno */

  /* Accenti */
  --rv-accent: var(--rv-ottone-500);            /* ottone decorativo: tratti, logo, linee */
  --rv-accent-text: var(--rv-ottone-700);       /* ottone leggibile: eyebrow, testo piccolo */
  --rv-star: var(--rv-sole-500);                /* stelle valutazione */
  --rv-favorite: var(--rv-cipria-600);          /* cuore attivo */

  /* Stati */
  --rv-success: var(--rv-pino-600);
  --rv-success-soft: var(--rv-pino-50);
  --rv-warning: var(--rv-ambra-600);
  --rv-warning-soft: var(--rv-ambra-100);
  --rv-danger: var(--rv-rosso-600);
  --rv-danger-soft: var(--rv-rosso-100);
  --rv-info: var(--rv-mare-600);
  --rv-info-soft: var(--rv-mare-100);

  /* Tag (fondo / testo) */
  --rv-tag-pino-bg: var(--rv-pino-500);
  --rv-tag-pino-fg: var(--rv-latte-50);
  --rv-tag-cipria-bg: var(--rv-cipria-200);
  --rv-tag-cipria-fg: var(--rv-cipria-700);
  --rv-tag-sabbia-bg: var(--rv-ottone-200);
  --rv-tag-sabbia-fg: var(--rv-ottone-700);
  --rv-tag-mare-bg: var(--rv-mare-100);
  --rv-tag-mare-fg: var(--rv-mare-700);
  --rv-tag-salvia-bg: var(--rv-salvia-200);
  --rv-tag-salvia-fg: var(--rv-salvia-700);

  /* Ombre calde (marrone scuro, mai nero). xs/sm includono un filo quasi
     invisibile che definisce il bordo delle superfici bianche sul crema. */
  --rv-shadow-xs: 0 0 0 1px rgba(58, 46, 30, 0.04), 0 1px 2px rgba(58, 46, 30, 0.06);
  --rv-shadow-sm: 0 0 0 1px rgba(58, 46, 30, 0.03), 0 1px 2px rgba(58, 46, 30, 0.05), 0 4px 14px rgba(58, 46, 30, 0.07);
  --rv-shadow-md: 0 2px 6px rgba(58, 46, 30, 0.05), 0 10px 28px rgba(58, 46, 30, 0.09);
  --rv-shadow-lg: 0 4px 12px rgba(58, 46, 30, 0.06), 0 22px 56px rgba(58, 46, 30, 0.13);

  /* Immagini */
  --rv-image-fade: var(--rv-bg);                /* colore verso cui sfumano le foto hero */
  --rv-image-dim: none;                         /* filtro CSS sulle immagini: nessuno di giorno, abbassate di notte */
  --rv-image-muted: grayscale(0.9) opacity(0.7); /* immagine spenta (annuncio venduto): sostituisce image-dim, non si somma */
  --rv-on-image: var(--rv-latte-25);            /* testo e icone sopra una foto: crema chiaro in entrambi i temi */
  --rv-on-image-scrim: rgba(13, 18, 16, 0.6);   /* velo scuro dietro testo su foto: ≥4,5:1 anche su una foto bianca */

  /* Caricamento */
  --rv-skeleton: var(--rv-latte-250);           /* osso dello skeleton */
  --rv-skeleton-glint: var(--rv-latte-50);      /* riflesso che scorre: più chiaro dell'osso */
}


/* --------------------------------------------------------------------------
   2. TEMA SCURO «Notte a Sanremo» — preferenza di sistema
   Fondi verde-carbone, testo crema, pino luminoso con testo scuro,
   ottone più chiaro, ombre profonde con filo di luce di 1px.
   -------------------------------------------------------------------------- */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;

    --rv-bg: var(--rv-notte-900);
    --rv-bg-muted: var(--rv-salvia-900);
    --rv-surface: var(--rv-notte-800);
    --rv-surface-raised: var(--rv-notte-700);
    --rv-surface-sunken: var(--rv-notte-950);
    --rv-surface-hover: var(--rv-notte-650);
    --rv-scrim: rgba(5, 8, 7, 0.64);

    --rv-border: var(--rv-notte-600);
    --rv-border-strong: var(--rv-notte-400);
    --rv-border-control: var(--rv-notte-500);

    --rv-text: var(--rv-latte-200);
    --rv-text-secondary: var(--rv-notte-200);
    --rv-text-muted: var(--rv-notte-300);
    --rv-text-on-primary: var(--rv-notte-950);
    --rv-heading: var(--rv-latte-100);
    --rv-heading-accent: var(--rv-pino-300);

    --rv-primary: var(--rv-pino-400);
    --rv-primary-hover: var(--rv-pino-350);
    --rv-primary-pressed: var(--rv-pino-450);
    --rv-primary-soft: var(--rv-pino-800);
    --rv-primary-soft-text: var(--rv-pino-200);
    --rv-focus-ring: var(--rv-pino-300);
    --rv-link: var(--rv-pino-300);
    --rv-link-hover: var(--rv-pino-200);

    --rv-accent: var(--rv-ottone-400);
    --rv-accent-text: var(--rv-ottone-300);
    --rv-star: var(--rv-sole-400);
    --rv-favorite: var(--rv-cipria-400);

    --rv-success: var(--rv-pino-300);
    --rv-success-soft: var(--rv-pino-800);
    --rv-warning: var(--rv-ambra-300);
    --rv-warning-soft: var(--rv-ambra-900);
    --rv-danger: var(--rv-rosso-300);
    --rv-danger-soft: var(--rv-rosso-900);
    --rv-info: var(--rv-mare-300);
    --rv-info-soft: var(--rv-mare-900);

    --rv-tag-pino-bg: var(--rv-pino-600);
    --rv-tag-pino-fg: var(--rv-pino-100);
    --rv-tag-cipria-bg: var(--rv-cipria-900);
    --rv-tag-cipria-fg: var(--rv-cipria-200);
    --rv-tag-sabbia-bg: var(--rv-ottone-900);
    --rv-tag-sabbia-fg: var(--rv-ottone-200);
    --rv-tag-mare-bg: var(--rv-mare-900);
    --rv-tag-mare-fg: var(--rv-mare-100);
    --rv-tag-salvia-bg: var(--rv-salvia-800);
    --rv-tag-salvia-fg: var(--rv-salvia-200);

    --rv-shadow-xs: 0 0 0 1px rgba(239, 232, 220, 0.05), 0 1px 2px rgba(3, 6, 5, 0.4);
    --rv-shadow-sm: 0 0 0 1px rgba(239, 232, 220, 0.06), 0 4px 12px rgba(3, 6, 5, 0.45);
    --rv-shadow-md: 0 0 0 1px rgba(239, 232, 220, 0.07), 0 10px 28px rgba(3, 6, 5, 0.55);
    --rv-shadow-lg: 0 0 0 1px rgba(239, 232, 220, 0.08), 0 22px 56px rgba(3, 6, 5, 0.65);

    --rv-image-fade: var(--rv-bg);
    --rv-image-dim: brightness(0.86) saturate(0.92);
    --rv-image-muted: grayscale(0.9) brightness(0.8) opacity(0.8);
    --rv-on-image: var(--rv-latte-25);
    --rv-on-image-scrim: rgba(13, 18, 16, 0.6);

    --rv-skeleton: var(--rv-notte-650);
    --rv-skeleton-glint: var(--rv-notte-550);
  }
}


/* --------------------------------------------------------------------------
   3. TEMA SCURO forzato (data-theme="dark" su :root, impostato da Telegram)
   e sottoalberi scuri annidati. Copia IDENTICA del blocco 2.
   -------------------------------------------------------------------------- */
:root[data-theme="dark"],
[data-theme="dark"] {
  color-scheme: dark;

  --rv-bg: var(--rv-notte-900);
  --rv-bg-muted: var(--rv-salvia-900);
  --rv-surface: var(--rv-notte-800);
  --rv-surface-raised: var(--rv-notte-700);
  --rv-surface-sunken: var(--rv-notte-950);
  --rv-surface-hover: var(--rv-notte-650);
  --rv-scrim: rgba(5, 8, 7, 0.64);

  --rv-border: var(--rv-notte-600);
  --rv-border-strong: var(--rv-notte-400);
  --rv-border-control: var(--rv-notte-500);

  --rv-text: var(--rv-latte-200);
  --rv-text-secondary: var(--rv-notte-200);
  --rv-text-muted: var(--rv-notte-300);
  --rv-text-on-primary: var(--rv-notte-950);
  --rv-heading: var(--rv-latte-100);
  --rv-heading-accent: var(--rv-pino-300);

  --rv-primary: var(--rv-pino-400);
  --rv-primary-hover: var(--rv-pino-350);
  --rv-primary-pressed: var(--rv-pino-450);
  --rv-primary-soft: var(--rv-pino-800);
  --rv-primary-soft-text: var(--rv-pino-200);
  --rv-focus-ring: var(--rv-pino-300);
  --rv-link: var(--rv-pino-300);
  --rv-link-hover: var(--rv-pino-200);

  --rv-accent: var(--rv-ottone-400);
  --rv-accent-text: var(--rv-ottone-300);
  --rv-star: var(--rv-sole-400);
  --rv-favorite: var(--rv-cipria-400);

  --rv-success: var(--rv-pino-300);
  --rv-success-soft: var(--rv-pino-800);
  --rv-warning: var(--rv-ambra-300);
  --rv-warning-soft: var(--rv-ambra-900);
  --rv-danger: var(--rv-rosso-300);
  --rv-danger-soft: var(--rv-rosso-900);
  --rv-info: var(--rv-mare-300);
  --rv-info-soft: var(--rv-mare-900);

  --rv-tag-pino-bg: var(--rv-pino-600);
  --rv-tag-pino-fg: var(--rv-pino-100);
  --rv-tag-cipria-bg: var(--rv-cipria-900);
  --rv-tag-cipria-fg: var(--rv-cipria-200);
  --rv-tag-sabbia-bg: var(--rv-ottone-900);
  --rv-tag-sabbia-fg: var(--rv-ottone-200);
  --rv-tag-mare-bg: var(--rv-mare-900);
  --rv-tag-mare-fg: var(--rv-mare-100);
  --rv-tag-salvia-bg: var(--rv-salvia-800);
  --rv-tag-salvia-fg: var(--rv-salvia-200);

  --rv-shadow-xs: 0 0 0 1px rgba(239, 232, 220, 0.05), 0 1px 2px rgba(3, 6, 5, 0.4);
  --rv-shadow-sm: 0 0 0 1px rgba(239, 232, 220, 0.06), 0 4px 12px rgba(3, 6, 5, 0.45);
  --rv-shadow-md: 0 0 0 1px rgba(239, 232, 220, 0.07), 0 10px 28px rgba(3, 6, 5, 0.55);
  --rv-shadow-lg: 0 0 0 1px rgba(239, 232, 220, 0.08), 0 22px 56px rgba(3, 6, 5, 0.65);

  --rv-image-fade: var(--rv-bg);
  --rv-image-dim: brightness(0.86) saturate(0.92);
  --rv-image-muted: grayscale(0.9) brightness(0.8) opacity(0.8);
  --rv-on-image: var(--rv-latte-25);
  --rv-on-image-scrim: rgba(13, 18, 16, 0.6);

  --rv-skeleton: var(--rv-notte-650);
  --rv-skeleton-glint: var(--rv-notte-550);
}


/* --------------------------------------------------------------------------
   4. Sottoalberi a tema: colore e fondo propri, così un pannello annidato
   (es. anteprima chiaro/scuro nella styleguide) è autosufficiente.
   -------------------------------------------------------------------------- */
[data-theme="light"],
[data-theme="dark"] {
  color: var(--rv-text);
  background-color: var(--rv-bg);
}


/* ─── src/base.css ──────────────────────────────────────────────── */
/* ==========================================================================
   Riviera · base.css
   Reset moderno, documento, tipografia, focus, selezione, moto ridotto e
   poche utility di layout. Nessun componente qui: solo fondamenta.
   ========================================================================== */


/* --------------------------------------------------------------------------
   Reset (selettori di elemento o :where(): non battono mai una classe .rv-*)
   -------------------------------------------------------------------------- */
*,
*::before,
*::after {
  box-sizing: border-box;
}

* {
  margin: 0;
}

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  -moz-tab-size: 4;
  tab-size: 4;
}

/* I titoli ereditano: la gerarchia visiva la danno le classi .rv-* */
h1, h2, h3, h4, h5, h6 {
  font-size: inherit;
  font-weight: inherit;
  text-wrap: balance;
  overflow-wrap: break-word;
}

p, li, figcaption, blockquote, dd {
  text-wrap: pretty;
  overflow-wrap: break-word;
}

img, picture, video, canvas, svg {
  display: block;
  max-width: 100%;
}

img, video {
  height: auto;
}

input, button, textarea, select, optgroup {
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
}

button {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  text-align: inherit;
  touch-action: manipulation;  /* niente ritardo del doppio tocco */
}

:where(button:disabled, [aria-disabled="true"]) {
  cursor: default;
}

textarea {
  resize: vertical;
}

/* Liste con classe o ruolo list: senza puntini né rientro */
:where(ul, ol):where([class], [role="list"]) {
  list-style: none;
  padding: 0;
}

table {
  border-collapse: collapse;
}

hr {
  height: 0;
  border: 0;
  border-top: var(--rv-border-width) solid var(--rv-border);
  color: inherit;
}

[hidden] {
  display: none !important;
}


/* --------------------------------------------------------------------------
   Documento
   -------------------------------------------------------------------------- */
html {
  background-color: var(--rv-bg);
  /* Niente rimbalzo elastico che scopre il client Telegram dietro la pagina.
     Va su html e non su body: overscroll-behavior arriva alla finestra
     dall'elemento RADICE — è l'opposto di overflow, che invece si propaga dal
     body — e Chromium ha tolto apposta la propagazione dal body. Scritta lì
     era una dichiarazione senza effetto. */
  overscroll-behavior-y: none;
}

/* Focus mai coperto (WCAG 2.4.11). Quando Tab, un'ancora o scrollIntoView()
   portano un elemento in vista, il browser lascia libere queste due fasce.
   Di base: appbar + area sicura in alto, area sicura + respiro in basso.
   navigation.css alza i valori quando c'è qualcosa di fisso o appiccicoso,
   dichiarando le variabili su html:
     --rv-scroll-pad-bottom   tab bar o MainButton simulato visibili
     --rv-scroll-pad-top      barra .rv-sticky (altezza --rv-sticky-h)
   con html:has(…) e, per le WebView senza :has(), con le classi
   html.rv-has-tabbar / .rv-has-mainbutton-sim / .rv-has-sticky messe da
   js/riviera-telegram.js (RivieraTelegram.refreshLayout()). */
html {
  scroll-padding-block-start: var(--rv-scroll-pad-top, calc(var(--rv-safe-top) + var(--rv-appbar-h) + var(--rv-space-3)));
  scroll-padding-block-end: var(--rv-scroll-pad-bottom, calc(var(--rv-safe-bottom) + var(--rv-space-6)));
}

body {
  min-height: 100vh;
  min-height: 100dvh;
  font-family: var(--rv-font-sans);
  font-size: var(--rv-fs-body);
  font-weight: var(--rv-fw-regular);
  line-height: var(--rv-lh-body);
  color: var(--rv-text);
  background-color: var(--rv-bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  font-synthesis: none;               /* niente grassetti/corsivi finti */
  font-kerning: normal;
  -webkit-tap-highlight-color: transparent;
}

/* Link nel testo corrente: i link-componente hanno sempre una classe.
   Verde come testo = --rv-link (AA anche su sheet, hover e banner).
   Sottolineatura: piena dove color-mix() manca (dichiarata prima: senza var()
   la dichiarazione moderna viene scartata e resta questa), velata altrove. */
a:not([class]) {
  color: var(--rv-link);
  text-decoration-line: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  text-decoration-color: currentColor;
  text-decoration-color: color-mix(in srgb, currentColor 45%, transparent);
}

@media (hover: hover) {
  a:not([class]):hover {
    color: var(--rv-link-hover);
    text-decoration-color: currentColor;
  }
}

a:not([class]):active {
  color: var(--rv-link-hover);
  text-decoration-color: currentColor;
}

/* :where() = specificità zero, così .rv-link & co. vincono sempre */
:where(a[class]) {
  color: inherit;
  text-decoration: none;
}

strong, b {
  font-weight: var(--rv-fw-semibold);
}


/* --------------------------------------------------------------------------
   Focus e selezione
   -------------------------------------------------------------------------- */
:focus-visible {
  outline: var(--rv-focus-width) solid var(--rv-focus-ring);
  outline-offset: var(--rv-focus-offset);
}

:where(:focus:not(:focus-visible)) {
  outline: none;
}

::selection {
  background-color: var(--rv-primary-soft);
  color: var(--rv-primary-soft-text);
}


/* --------------------------------------------------------------------------
   Tipografia
   Serif (Playfair): display, title-1/2/3 — colore titolo.
   Sans (Onest): tutto il resto — colore ereditato.
   -------------------------------------------------------------------------- */
.rv-display,
.rv-title-1,
.rv-title-2,
.rv-title-3 {
  font-family: var(--rv-font-display);
  font-weight: var(--rv-fw-medium);
  color: var(--rv-heading);
  text-wrap: balance;
}

.rv-display {
  font-size: var(--rv-fs-display);
  line-height: var(--rv-lh-display);
  letter-spacing: var(--rv-tracking-display);
}

.rv-title-1 {
  font-size: var(--rv-fs-title-1);
  line-height: var(--rv-lh-title-1);
  letter-spacing: var(--rv-tracking-title);
}

.rv-title-2 {
  font-size: var(--rv-fs-title-2);
  line-height: var(--rv-lh-title-2);
  letter-spacing: var(--rv-tracking-title);
}

.rv-title-3 {
  font-size: var(--rv-fs-title-3);
  line-height: var(--rv-lh-title-3);
}

.rv-headline {
  font-family: var(--rv-font-sans);
  font-size: var(--rv-fs-headline);
  font-weight: var(--rv-fw-semibold);
  line-height: var(--rv-lh-headline);
  letter-spacing: -0.005em;
}

.rv-body-lg {
  font-size: var(--rv-fs-body-lg);
  line-height: var(--rv-lh-body-lg);
}

.rv-body {
  font-size: var(--rv-fs-body);
  line-height: var(--rv-lh-body);
}

.rv-callout {
  font-size: var(--rv-fs-callout);
  line-height: var(--rv-lh-callout);
}

.rv-caption {
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
}

.rv-overline {
  font-family: var(--rv-font-sans);
  font-size: var(--rv-fs-overline);
  font-weight: var(--rv-fw-semibold);
  line-height: var(--rv-lh-overline);
  letter-spacing: var(--rv-tracking-overline);
  text-transform: uppercase;
}

/* Nota a mano: solo decorativa, mai sotto 20px, al massimo una per vista
   (una pagina lunga può averne due, se non stanno mai sullo stesso schermo).
   Niente ♡ ★ come caratteri: non sono nei font, si usano le icone. */
.rv-script {
  font-family: var(--rv-font-script);
  font-size: max(var(--rv-fs-script), 20px);
  font-weight: var(--rv-fw-regular);
  font-style: normal;
  line-height: var(--rv-lh-script);
  letter-spacing: 0;
}

/* Il corsivo verde: una parola o frase chiave per schermata */
.rv-accent-italic {
  font-family: var(--rv-font-display);
  font-style: italic;
  color: var(--rv-heading-accent);
  letter-spacing: 0;
}

/* Numeri che si allineano (prezzi, contatori, orari) */
.rv-num {
  font-variant-numeric: tabular-nums lining-nums;
}

/* Colori di testo ricorrenti */
.rv-text-secondary { color: var(--rv-text-secondary); }
.rv-text-muted { color: var(--rv-text-muted); }
.rv-text-accent { color: var(--rv-accent-text); }


/* --------------------------------------------------------------------------
   Utility di layout (minime)
   Distanza fra figli: default 16px (stack) / 8px (cluster), oppure
   .rv-gap-xs 4 · -sm 8 · -md 16 · -lg 24 · -xl 40. Il gap è scritto
   direttamente (niente variabile ereditata: gli stack annidati non si
   contagiano a vicenda).
   -------------------------------------------------------------------------- */
.rv-stack {
  display: flex;
  flex-direction: column;
  gap: var(--rv-space-4);
}

.rv-cluster {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--rv-space-2);
}

:is(.rv-stack, .rv-cluster, .rv-scroll-x).rv-gap-xs { gap: var(--rv-space-1); }
:is(.rv-stack, .rv-cluster, .rv-scroll-x).rv-gap-sm { gap: var(--rv-space-2); }
:is(.rv-stack, .rv-cluster, .rv-scroll-x).rv-gap-md { gap: var(--rv-space-4); }
:is(.rv-stack, .rv-cluster, .rv-scroll-x).rv-gap-lg { gap: var(--rv-space-6); }
:is(.rv-stack, .rv-cluster, .rv-scroll-x).rv-gap-xl { gap: var(--rv-space-10); }

/* Colonna di contenuto: gutter laterale e larghezza massima */
.rv-container {
  width: 100%;
  max-width: calc(var(--rv-content-max) + 2 * var(--rv-gutter));
  margin-inline: auto;
  padding-inline: var(--rv-gutter);
}

/* Rail orizzontale con snap: contenuto che arriva al bordo dello schermo.
   Dentro .rv-container usa .rv-scroll-x--bleed per sconfinare.
   Scrollbar: nascosta al tocco (si scorre col dito); con un puntatore fine
   (Telegram Desktop, Web, macOS) sottile e visibile, perché con un mouse
   senza rotella orizzontale la fila altrimenti non si scorre. */
.rv-scroll-x {
  display: flex;
  gap: var(--rv-space-3);
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--rv-gutter);
  padding-inline: var(--rv-gutter);
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}

.rv-scroll-x::-webkit-scrollbar {
  display: none;
}

/* Due strade per la stessa barra (6px, cursore arrotondato nei colori del sistema):
   - Chromium e Safari: ::-webkit-scrollbar. Chromium dalla 121 lo ignora se
     scrollbar-width o scrollbar-color non sono «auto» (disegnerebbe la barra nativa,
     con le frecce su Windows): per questo lì scrollbar-width torna auto;
   - Firefox: scrollbar-width thin + scrollbar-color, solo dove ::-webkit-scrollbar
     non esiste. */
@media (hover: hover) and (pointer: fine) {
  .rv-scroll-x {
    padding-block-end: var(--rv-space-2);   /* la barra non tocca card e ombre */
    scrollbar-width: auto;
  }

  @supports not selector(::-webkit-scrollbar) {
    .rv-scroll-x {
      scrollbar-width: thin;
      scrollbar-color: var(--rv-border-strong) transparent;
    }
  }

  .rv-scroll-x::-webkit-scrollbar {
    display: block;
    height: 6px;
  }

  .rv-scroll-x::-webkit-scrollbar-track {
    margin: 0 var(--rv-gutter);             /* la barra si allinea al contenuto, non al bordo dello schermo */
    background-color: transparent;
  }

  .rv-scroll-x::-webkit-scrollbar-thumb {
    border-radius: var(--rv-radius-pill);
    background-color: var(--rv-border-strong);
  }

  .rv-scroll-x::-webkit-scrollbar-thumb:hover {
    background-color: var(--rv-text-muted);
  }
}

.rv-scroll-x > * {
  flex: none;
  scroll-snap-align: start;
}

.rv-scroll-x--bleed {
  margin-inline: calc(-1 * var(--rv-gutter));
}

/* Nascosto alla vista, leggibile dagli screen reader */
.rv-visually-hidden {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  padding: 0 !important;
  margin: -1px !important;
  overflow: hidden !important;
  clip: rect(0 0 0 0) !important;
  clip-path: inset(50%) !important;
  white-space: nowrap !important;
  border: 0 !important;
}

/* Parole che non vanno spezzate: «Сан-Ремо», «1 250 €» */
.rv-nowrap {
  white-space: nowrap;
}

.rv-truncate {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  min-width: 0;
}

.rv-clamp-2 {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}


/* --------------------------------------------------------------------------
   Moto ridotto
   Azzera transizioni e animazioni. Resta attivo solo ciò che comunica uno
   stato in corso:
   - .rv-spinner (e i suoi figli);
   - lo spinner dei controlli occupati, cioè ::before/::after di qualsiasi
     elemento [aria-busy="true"]: .rv-btn (rv-btn-spin), .rv-search__submit,
     .rv-sim-mainbutton__button;
   - qualsiasi elemento con .rv-motion-essential.
   Durata ~0 e non `animation: none`: così animationend scatta comunque e
   sheet, dialog e toast che aspettano la fine dell'uscita si chiudono.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  :root {
    --rv-dur-fast: 0ms;
    --rv-dur-base: 0ms;
    --rv-dur-slow: 0ms;
    --rv-press-scale: 1;
  }

  html:focus-within {
    scroll-behavior: auto;
  }

  *:not(.rv-spinner, .rv-spinner *, .rv-motion-essential, .rv-motion-essential *),
  *:not(.rv-spinner, .rv-motion-essential, [aria-busy="true"])::before,
  *:not(.rv-spinner, .rv-motion-essential, [aria-busy="true"])::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}


/* --------------------------------------------------------------------------
   Dispositivi lenti
   html[data-tg-performance="low"] lo imposta js/riviera-telegram.js quando
   Telegram per Android dichiara la classe LOW (fuori da Telegram: ?performance=low).
   Linee guida Telegram: meno animazioni ed effetti. Si fermano le animazioni
   decorative (shimmer dello skeleton, entrate di sheet/dialog/toast, cuore) e
   le sfocature di sfondo; restano le transizioni brevi di tocco e gli stessi
   spinner esentati sopra.
   -------------------------------------------------------------------------- */
html[data-tg-performance="low"] :not(.rv-spinner, .rv-spinner *, .rv-motion-essential, .rv-motion-essential *),
html[data-tg-performance="low"] :not(.rv-spinner, .rv-motion-essential, [aria-busy="true"])::before,
html[data-tg-performance="low"] :not(.rv-spinner, .rv-motion-essential, [aria-busy="true"])::after {
  animation-duration: 0.01ms !important;
  animation-iteration-count: 1 !important;
  -webkit-backdrop-filter: none !important;
  backdrop-filter: none !important;
}


/* ─── src/icons.css ─────────────────────────────────────────────── */
/* ==========================================================================
   Riviera — icone
   FILE GENERATO da scripts/build-icons.mjs a partire da icons/svg/: non modificarlo a mano.
   78 icone · griglia 24×24 · tratto 1.75 · estremità e giunzioni arrotondate.

   Uso:   <span class="rv-icon rv-icon--home" aria-hidden="true"></span>
   Colore: eredita currentColor (è il background della maschera).
   Misura: --rv-icon-size (default 24px) oppure .rv-icon--sm 20 · --lg 28 · --xl 32.
   Icona con significato proprio (senza testo accanto): togli aria-hidden e aggiungi
   role="img" e aria-label sull'elemento, oppure un testo nascosto nel controllo che la contiene.
   ========================================================================== */

.rv-icon {
  /* sorgente di riserva trasparente: un id inesistente non disegna un quadrato pieno */
  --rv-icon-src: linear-gradient(transparent, transparent);
  display: inline-block;
  inline-size: var(--rv-icon-size, 24px);
  block-size: var(--rv-icon-size, 24px);
  flex: none;
  background-color: currentColor;
  -webkit-mask: var(--rv-icon-src) center / contain no-repeat;
  mask: var(--rv-icon-src) center / contain no-repeat;
  vertical-align: middle;
}

.rv-icon--sm { --rv-icon-size: 20px; }
.rv-icon--lg { --rv-icon-size: 28px; }
.rv-icon--xl { --rv-icon-size: 32px; }

/* Contrasto elevato di Windows: senza questo il fondo verrebbe forzato e l'icona sparirebbe */
@media (forced-colors: active) {
  .rv-icon { forced-color-adjust: none; }
}

.rv-icon--alert-circle { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 7.5v5.25'/%3E%3Ccircle cx='12' cy='16.25' r='1.1' fill='%23000' stroke='none'/%3E%3C/svg%3E"); }
.rv-icon--arrow-down { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 4.5v15M18 13.5l-6 6-6-6'/%3E%3C/svg%3E"); }
.rv-icon--arrow-left { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M19.5 12h-15M10.5 6l-6 6 6 6'/%3E%3C/svg%3E"); }
.rv-icon--arrow-right { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4.5 12h15M13.5 6l6 6-6 6'/%3E%3C/svg%3E"); }
.rv-icon--arrow-up { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 19.5v-15M6 10.5l6-6 6 6'/%3E%3C/svg%3E"); }
.rv-icon--article { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='5' y='3' width='14' height='18' rx='2.5'/%3E%3Cpath d='M8.5 8h7M8.5 12h7M8.5 16h4'/%3E%3C/svg%3E"); }
.rv-icon--baby { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M13.25 11V3.5A7.5 7.5 0 0 1 20.75 11Z'/%3E%3Cpath d='M20.75 11A7.5 5 0 0 1 5.75 11h15M5.75 11L4.5 7H3.25'/%3E%3Ccircle cx='7.75' cy='19' r='1.75'/%3E%3Ccircle cx='18.75' cy='19' r='1.75'/%3E%3C/svg%3E"); }
.rv-icon--bell-off { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6.9 6.9C6.33 7.91 6 9.06 6 10.25v3.75l-1.5 2.75h12.25'/%3E%3Cpath d='M9.17 4.56C10.01 4.05 10.98 3.75 12 3.75c3.3 0 6 3.1 6 6.5v3.15'/%3E%3Cpath d='M10 19.5q2 1.75 4 0'/%3E%3Cpath d='M3.25 3.25l17.5 17.5'/%3E%3C/svg%3E"); }
.rv-icon--bell { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 10.25c0-3.4-2.7-6.5-6-6.5s-6 3.1-6 6.5v3.75l-1.5 2.75h15L18 14Z'/%3E%3Cpath d='M10 19.5q2 1.75 4 0'/%3E%3C/svg%3E"); }
.rv-icon--bike { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='6.5' cy='14.25' r='3.5'/%3E%3Ccircle cx='17.5' cy='14.25' r='3.5'/%3E%3Cpath d='M6.5 14.25l3.25-6h5.5M11.75 14.25 8.9 6.5M7.75 6.5h2.5M11.75 14.25l3.5-6M17.5 14.25l-2.1-8h1.6'/%3E%3C/svg%3E"); }
.rv-icon--book { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 6.5C10.1 4.9 7.4 4.25 3.75 4.25v13.5c3.65 0 6.35.65 8.25 2.25 1.9-1.6 4.6-2.25 8.25-2.25V4.25C16.6 4.25 13.9 4.9 12 6.5Z'/%3E%3Cpath d='M12 6.5V20'/%3E%3C/svg%3E"); }
.rv-icon--bookmark { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7.5 3.5h9a2 2 0 0 1 2 2v15L12 16.25 5.5 20.5v-15a2 2 0 0 1 2-2Z'/%3E%3C/svg%3E"); }
.rv-icon--broom { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20.58 3.42L14.59 9.41'/%3E%3Cpath d='M12.71 7.54L16.46 11.29C16.84 11.66 16.91 12.33 16.61 12.78L12.15 20.09C11.67 20.73 10.92 20.73 10.39 20.2L3.8 13.61C3.27 13.08 3.27 12.33 3.91 11.85L11.22 7.39C11.67 7.09 12.34 7.16 12.71 7.54Z'/%3E%3Cpath d='M9.23 11.92L5.48 15.3M12.08 14.77L8.7 18.52'/%3E%3C/svg%3E"); }
.rv-icon--calendar { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.5' y='5' width='17' height='15.5' rx='2.5'/%3E%3Cpath d='M8 3.25v3.5M16 3.25v3.5M3.5 10h17'/%3E%3C/svg%3E"); }
.rv-icon--camera { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5.5 6.75h3l1.2-1.8c.4-.45.8-.7 1.3-.7h2c.5 0 .9.25 1.3.7l1.2 1.8h3A2.5 2.5 0 0 1 21 9.25v8a2.5 2.5 0 0 1-2.5 2.5h-13A2.5 2.5 0 0 1 3 17.25v-8a2.5 2.5 0 0 1 2.5-2.5Z'/%3E%3Ccircle cx='12' cy='13' r='3.5'/%3E%3C/svg%3E"); }
.rv-icon--car { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 16h-.5A1.5 1.5 0 0 1 3 14.5v-2a2 2 0 0 1 1.6-1.95l2.15-.45 1.85-2.9c.5-.8 1.1-1.2 2-1.2h3.9c.9 0 1.5.3 2.05 1l2.7 3.2 1.15.3A2 2 0 0 1 21 12.5v2a1.5 1.5 0 0 1-1.5 1.5H19M9 16h6M6.75 10.1h11.5M12.5 6v4.1'/%3E%3Ccircle cx='7' cy='16' r='2'/%3E%3Ccircle cx='17' cy='16' r='2'/%3E%3C/svg%3E"); }
.rv-icon--card { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2.75' y='5.25' width='18.5' height='13.5' rx='2.5'/%3E%3Cpath d='M2.75 9.75h18.5M6.5 14.5h3.5'/%3E%3C/svg%3E"); }
.rv-icon--chat-translate { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7.25 11.5H6.25L3 14.25V6a2.5 2.5 0 0 1 2.5-2.5h6A2.5 2.5 0 0 1 14 6v1.25'/%3E%3Cpath d='M12.5 10h6A2.5 2.5 0 0 1 21 12.5v8L18 18h-5.5A2.5 2.5 0 0 1 10 15.5v-3a2.5 2.5 0 0 1 2.5-2.5Z'/%3E%3C/svg%3E"); }
.rv-icon--chats { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 20.25v-13A3.5 3.5 0 0 1 6.5 3.75h11A3.5 3.5 0 0 1 21 7.25v6a3.5 3.5 0 0 1-3.5 3.5H7Z'/%3E%3C/svg%3E"); }
.rv-icon--check-circle { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M8 12.25l2.75 2.75L16 9.5'/%3E%3C/svg%3E"); }
.rv-icon--check { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4.5 12.5l5 5 10-11'/%3E%3C/svg%3E"); }
.rv-icon--chevron-down { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5.5 8.75l6.5 6.5 6.5-6.5'/%3E%3C/svg%3E"); }
.rv-icon--chevron-left { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15.25 5.5L8.75 12l6.5 6.5'/%3E%3C/svg%3E"); }
.rv-icon--chevron-right { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8.75 5.5l6.5 6.5-6.5 6.5'/%3E%3C/svg%3E"); }
.rv-icon--child-seat { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7.25 3.75h3.5A1.75 1.75 0 0 1 12.5 5.5v7.75h5.5A1.75 1.75 0 0 1 19.75 15v1.5A1.75 1.75 0 0 1 18 18.25H7.25A1.75 1.75 0 0 1 5.5 16.5V5.5A1.75 1.75 0 0 1 7.25 3.75Z'/%3E%3Cpath d='M5.5 8.25h7'/%3E%3Cpath d='M9.25 10.75v4a1.25 1.25 0 0 0 1.25 1.25h4.75'/%3E%3C/svg%3E"); }
.rv-icon--clock { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 7.25V12l3.25 2'/%3E%3C/svg%3E"); }
.rv-icon--close { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 6l12 12M18 6L6 18'/%3E%3C/svg%3E"); }
.rv-icon--dots { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='5' cy='12' r='1.5' fill='%23000' stroke='none'/%3E%3Ccircle cx='12' cy='12' r='1.5' fill='%23000' stroke='none'/%3E%3Ccircle cx='19' cy='12' r='1.5' fill='%23000' stroke='none'/%3E%3C/svg%3E"); }
.rv-icon--edit { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5.3 14.88L15.23 4.95A2.7 2.7 0 0 1 19.05 8.77L9.12 18.7L4.16 19.84ZM12.94 7.24L16.76 11.06'/%3E%3C/svg%3E"); }
.rv-icon--euro { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18.95 7.05a7 7 0 1 0 0 9.9M5 10.25h8.5M5 13.75h8.5'/%3E%3C/svg%3E"); }
.rv-icon--eye { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 12c2-4.25 5.25-6.5 9-6.5s7 2.25 9 6.5c-2 4.25-5.25 6.5-9 6.5S5 16.25 3 12Z'/%3E%3Ccircle cx='12' cy='12' r='3'/%3E%3C/svg%3E"); }
.rv-icon--flag { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 20.75V3.25'/%3E%3Cpath d='M6 4.5h11.75l-2.75 4 2.75 4H6Z'/%3E%3C/svg%3E"); }
.rv-icon--gift { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.25' y='7.75' width='17.5' height='4' rx='1.25'/%3E%3Cpath d='M5 11.75v7a1.75 1.75 0 0 0 1.75 1.75h10.5A1.75 1.75 0 0 0 19 18.75v-7M12 7.75V20.5'/%3E%3Cpath d='M12 7.75C11.1 5 9.3 3.6 7.9 4.2c-1.45.6-1.1 3.55 4.1 3.55ZM12 7.75c.9-2.75 2.7-4.15 4.1-3.55 1.45.6 1.1 3.55-4.1 3.55Z'/%3E%3C/svg%3E"); }
.rv-icon--globe { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cellipse cx='12' cy='12' rx='4' ry='9'/%3E%3Cpath d='M3 12h18'/%3E%3C/svg%3E"); }
.rv-icon--graduation-cap { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 5l9 4.5-9 4.5-9-4.5Z'/%3E%3Cpath d='M6.75 11.4v4.35c0 1.7 2.35 3 5.25 3s5.25-1.3 5.25-3V11.4'/%3E%3Cpath d='M21 9.5V15'/%3E%3C/svg%3E"); }
.rv-icon--grid { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.5' y='3.5' width='6.75' height='6.75' rx='1.75'/%3E%3Crect x='13.75' y='3.5' width='6.75' height='6.75' rx='1.75'/%3E%3Crect x='3.5' y='13.75' width='6.75' height='6.75' rx='1.75'/%3E%3Crect x='13.75' y='13.75' width='6.75' height='6.75' rx='1.75'/%3E%3C/svg%3E"); }
.rv-icon--hammer { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M13.25 9.25L10.33 6.33Q9.63 5.63 10.33 4.92L11.57 3.68Q12.28 2.98 12.99 3.68L18.2 8.9C19.88 10.58 20.23 13.05 18.29 16.06C18.29 13.94 16.87 12.87 15.55 11.55L7.06 20.03A1.63 1.63 0 0 1 4.77 17.74Z'/%3E%3C/svg%3E"); }
.rv-icon--heart-fill { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23000' stroke='none'%3E%3Cpath d='M11.39 20.38L3.83 13.03A5.63 5.63 0 1 1 12 5.32A5.63 5.63 0 1 1 20.17 13.03L12.61 20.38A.875 .875 0 0 1 11.39 20.38Z'/%3E%3C/svg%3E"); }
.rv-icon--heart { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 19.75L4.44 12.41A4.75 4.75 0 1 1 12 6.88A4.75 4.75 0 1 1 19.56 12.41Z'/%3E%3C/svg%3E"); }
.rv-icon--home { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 11.25l9-7.5 9 7.5'/%3E%3Cpath d='M5 9.75v9a1.75 1.75 0 0 0 1.75 1.75h10.5A1.75 1.75 0 0 0 19 18.75v-9'/%3E%3Cpath d='M9.75 20.5v-5a2.25 2.25 0 0 1 4.5 0v5'/%3E%3C/svg%3E"); }
.rv-icon--image { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='16' rx='2.5'/%3E%3Ccircle cx='8.5' cy='8.75' r='1.75'/%3E%3Cpath d='M3.25 17l4.65-4.35q1.1-1 2.2 0l3.3 3.1 2-1.85q1.1-1 2.2 0l3.15 2.85'/%3E%3C/svg%3E"); }
.rv-icon--info { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 11v5.25'/%3E%3Ccircle cx='12' cy='7.75' r='1.1' fill='%23000' stroke='none'/%3E%3C/svg%3E"); }
.rv-icon--key { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='8' cy='16' r='4.25'/%3E%3Cpath d='M11 13l9.25-9.25M17.5 6.5l2.25 2.25M15 9l1.75 1.75'/%3E%3C/svg%3E"); }
.rv-icon--lamp { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8.6 3.5h6.8q.75 0 1.05.7l2.8 6.8H4.75l2.8-6.8q.3-.7 1.05-.7Z'/%3E%3Cpath d='M12 11v8.25M8.25 20.25h7.5M15.25 11v2.5'/%3E%3C/svg%3E"); }
.rv-icon--list { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8.75 6H20M8.75 12H20M8.75 18H20'/%3E%3Ccircle cx='4.75' cy='6' r='1.1' fill='%23000' stroke='none'/%3E%3Ccircle cx='4.75' cy='12' r='1.1' fill='%23000' stroke='none'/%3E%3Ccircle cx='4.75' cy='18' r='1.1' fill='%23000' stroke='none'/%3E%3C/svg%3E"); }
.rv-icon--lock { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='5' y='10.5' width='14' height='10' rx='2.5'/%3E%3Cpath d='M8 10.5v-3a4 4 0 0 1 8 0v3M12 14.5v2'/%3E%3C/svg%3E"); }
.rv-icon--logout { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9.5 20.5H6A2.5 2.5 0 0 1 3.5 18V6A2.5 2.5 0 0 1 6 3.5h3.5M10 12h10.5M16.5 8l4 4-4 4'/%3E%3C/svg%3E"); }
.rv-icon--lotus { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 19.56C9.1 16.93 8.85 9.48 12 4.44c3.15 5.04 2.9 12.49 0 15.12Z'/%3E%3Cpath d='M9.73 12.9C8.1 10.88 5.9 9.65 3.25 9.48 3.1 15.36 6.6 19.56 12 19.56'/%3E%3Cpath d='M14.27 12.9c1.63-2.02 3.83-3.25 6.48-3.42.15 5.88-3.35 10.08-8.75 10.08'/%3E%3C/svg%3E"); }
.rv-icon--map-pin { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 21C9.3 18.45 5.25 14.55 5.25 9.75a6.75 6.75 0 0 1 13.5 0c0 4.8-4.05 8.7-6.75 11.25Z'/%3E%3Ccircle cx='12' cy='9.75' r='2.5'/%3E%3C/svg%3E"); }
.rv-icon--minus { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14'/%3E%3C/svg%3E"); }
.rv-icon--monitor { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='12.5' rx='2.5'/%3E%3Cpath d='M12 16.5v3.75M8.25 20.25h7.5'/%3E%3C/svg%3E"); }
.rv-icon--moon { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20.5 12.18A8.5 8.5 0 1 1 11.82 3.5a6.25 6.25 0 0 0 8.68 8.68Z'/%3E%3C/svg%3E"); }
.rv-icon--navigation { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3.75l7.75 16.5L12 16.25l-7.75 4Z'/%3E%3C/svg%3E"); }
.rv-icon--paw { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='6.5' cy='10.9' r='1.85'/%3E%3Ccircle cx='10' cy='7.2' r='1.85'/%3E%3Ccircle cx='14' cy='7.2' r='1.85'/%3E%3Ccircle cx='17.5' cy='10.9' r='1.85'/%3E%3Cpath d='M12 13.25c3.2 0 5.25 2.1 5.25 4.3 0 1.9-1.75 3.2-5.25 3.2s-5.25-1.3-5.25-3.2c0-2.2 2.05-4.3 5.25-4.3Z'/%3E%3C/svg%3E"); }
.rv-icon--phone { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.25 5.5v-.6Q3.25 3.25 4.9 3.25H7.4Q8.9 3.25 9.05 4.75L9.4 8.1 7.91 9.99A11.5 11.5 0 0 0 14.01 16.09L15.9 14.6 19.25 14.95Q20.75 15.1 20.75 16.6V19.1Q20.75 20.75 19.1 20.75H18.5A15.25 15.25 0 0 1 3.25 5.5Z'/%3E%3C/svg%3E"); }
.rv-icon--plus { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 5v14M5 12h14'/%3E%3C/svg%3E"); }
.rv-icon--route { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='6' cy='18' r='2.75'/%3E%3Ccircle cx='18' cy='6' r='2.75'/%3E%3Cpath d='M6 15.25c0-3 2.5-3.75 6-3.75s6-.75 6-3.75'/%3E%3C/svg%3E"); }
.rv-icon--seal-check { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 2.95C13.27 2.95 13.75 4.45 14.93 4.93C16.1 5.42 17.5 4.7 18.4 5.6C19.3 6.5 18.58 7.9 19.07 9.07C19.55 10.25 21.05 10.73 21.05 12C21.05 13.27 19.55 13.75 19.07 14.93C18.58 16.1 19.3 17.5 18.4 18.4C17.5 19.3 16.1 18.58 14.93 19.07C13.75 19.55 13.27 21.05 12 21.05C10.73 21.05 10.25 19.55 9.07 19.07C7.9 18.58 6.5 19.3 5.6 18.4C4.7 17.5 5.42 16.1 4.93 14.93C4.45 13.75 2.95 13.27 2.95 12C2.95 10.73 4.45 10.25 4.93 9.07C5.42 7.9 4.7 6.5 5.6 5.6C6.5 4.7 7.9 5.42 9.07 4.93C10.25 4.45 10.73 2.95 12 2.95Z'/%3E%3Cpath d='M8.25 12.25l2.5 2.5 5-5'/%3E%3C/svg%3E"); }
.rv-icon--search { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='10.75' cy='10.75' r='7'/%3E%3Cpath d='M15.9 15.9 20.5 20.5'/%3E%3C/svg%3E"); }
.rv-icon--send { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20.5 3.5L3.75 9.9l7 3.35 3.35 7Z'/%3E%3Cpath d='M10.75 13.25L20.5 3.5'/%3E%3C/svg%3E"); }
.rv-icon--settings { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9.97 4.85L10.14 3.95Q10.29 3.06 11.19 3.06L12.81 3.06Q13.71 3.06 13.86 3.95L14.03 4.85Q14.13 5.44 14.65 5.74L16.1 6.57Q16.62 6.87 17.18 6.67L18.04 6.36Q18.89 6.05 19.34 6.83L20.14 8.23Q20.59 9.01 19.91 9.59L19.21 10.18Q18.75 10.57 18.75 11.17L18.75 12.83Q18.75 13.43 19.21 13.82L19.91 14.41Q20.59 14.99 20.14 15.77L19.34 17.17Q18.89 17.95 18.04 17.64L17.18 17.33Q16.62 17.13 16.1 17.43L14.65 18.26Q14.13 18.56 14.03 19.15L13.86 20.05Q13.71 20.94 12.81 20.94L11.19 20.94Q10.29 20.94 10.14 20.05L9.97 19.15Q9.87 18.56 9.35 18.26L7.9 17.43Q7.38 17.13 6.82 17.33L5.96 17.64Q5.11 17.95 4.66 17.17L3.86 15.77Q3.41 14.99 4.09 14.41L4.79 13.82Q5.25 13.43 5.25 12.83L5.25 11.17Q5.25 10.57 4.79 10.18L4.09 9.59Q3.41 9.01 3.86 8.23L4.66 6.83Q5.11 6.05 5.96 6.36L6.82 6.67Q7.38 6.87 7.9 6.57L9.35 5.74Q9.87 5.44 9.97 4.85Z'/%3E%3Ccircle cx='12' cy='12' r='3'/%3E%3C/svg%3E"); }
.rv-icon--share { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='17.5' cy='6' r='2.5'/%3E%3Ccircle cx='6.5' cy='12' r='2.5'/%3E%3Ccircle cx='17.5' cy='18' r='2.5'/%3E%3Cpath d='M8.7 10.8l6.6-3.6M8.7 13.2l6.6 3.6'/%3E%3C/svg%3E"); }
.rv-icon--shield-check { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3.1c2.2 1.5 4.6 2.3 7 2.4v5.75c0 4.65-2.9 7.95-7 9.65-4.1-1.7-7-5-7-9.65V5.5c2.4-.1 4.8-.9 7-2.4Z'/%3E%3Cpath d='M8.75 12l2.25 2.25 4.25-4.25'/%3E%3C/svg%3E"); }
.rv-icon--shirt { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 4.25a3 3 0 0 0 6 0l3.9 1.4q.85.3 1.2 1.15l1.15 2.9-3.25 1.3v8a1.75 1.75 0 0 1-1.75 1.75h-8.5A1.75 1.75 0 0 1 6 19v-8L2.75 9.7 3.9 6.8q.35-.85 1.2-1.15Z'/%3E%3C/svg%3E"); }
.rv-icon--sliders { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 7h9.25M17.25 7h3.25M3.5 17h3.25M11.25 17h9.25'/%3E%3Ccircle cx='15' cy='7' r='2.25'/%3E%3Ccircle cx='9' cy='17' r='2.25'/%3E%3C/svg%3E"); }
.rv-icon--sofa { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5.25 10V7.75A2.75 2.75 0 0 1 8 5h8a2.75 2.75 0 0 1 2.75 2.75V10'/%3E%3Cpath d='M3 11.75a1.875 1.875 0 0 1 3.75 0v1.5h10.5v-1.5a1.875 1.875 0 0 1 3.75 0v4A1.5 1.5 0 0 1 19.5 17.25h-15A1.5 1.5 0 0 1 3 15.75Z'/%3E%3Cpath d='M6 17.25v1.5M18 17.25v1.5'/%3E%3C/svg%3E"); }
.rv-icon--sparkle { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10.5 6.25C10.5 11.33 12.68 13.5 17.75 13.5C12.68 13.5 10.5 15.68 10.5 20.75C10.5 15.68 8.32 13.5 3.25 13.5C8.32 13.5 10.5 11.33 10.5 6.25Z'/%3E%3Cpath d='M18 3.25C18 5.51 18.5 6 20.75 6C18.5 6 18 6.5 18 8.75C18 6.5 17.51 6 15.25 6C17.51 6 18 5.51 18 3.25Z'/%3E%3C/svg%3E"); }
.rv-icon--star-fill { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23000' stroke='none'%3E%3Cpath d='M12.75 3.82L14.79 7.95Q15.1 8.58 15.79 8.68L20.35 9.35Q22.03 9.59 20.82 10.78L17.52 13.99Q17.02 14.48 17.14 15.17L17.91 19.71Q18.2 21.39 16.7 20.59L12.62 18.45Q12 18.13 11.38 18.45L7.3 20.59Q5.8 21.39 6.09 19.71L6.86 15.17Q6.98 14.48 6.48 13.99L3.18 10.78Q1.97 9.59 3.65 9.35L8.21 8.68Q8.9 8.58 9.21 7.95L11.25 3.82Q12 2.3 12.75 3.82Z'/%3E%3C/svg%3E"); }
.rv-icon--star { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12.46 4.35L14.43 8.62Q14.68 9.16 15.28 9.23L19.94 9.78Q21.04 9.91 20.23 10.66L16.78 13.85Q16.34 14.26 16.45 14.85L17.37 19.46Q17.58 20.54 16.62 20L12.52 17.7Q12 17.41 11.48 17.7L7.38 20Q6.42 20.54 6.63 19.46L7.55 14.85Q7.66 14.26 7.22 13.85L3.77 10.66Q2.96 9.91 4.06 9.78L8.72 9.23Q9.32 9.16 9.57 8.62L11.54 4.35Q12 3.35 12.46 4.35Z'/%3E%3C/svg%3E"); }
.rv-icon--stethoscope { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 4.5h1.25v5.25a3.75 3.75 0 0 0 7.5 0V4.5H14'/%3E%3Cpath d='M9 13.5v1.75a4.375 4.375 0 0 0 8.75 0V13.5'/%3E%3Ccircle cx='17.75' cy='11.25' r='2.25'/%3E%3C/svg%3E"); }
.rv-icon--suitcase { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2.75' y='7.25' width='18.5' height='12.5' rx='2.5'/%3E%3Cpath d='M8.75 7.25V5.25A1.75 1.75 0 0 1 10.5 3.5h3A1.75 1.75 0 0 1 15.25 5.25v2'/%3E%3Cpath d='M9.25 11.5v4M14.75 11.5v4'/%3E%3C/svg%3E"); }
.rv-icon--sun { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='4'/%3E%3Cpath d='M12 3v2M12 19v2M3 12h2M19 12h2M5.64 5.64l1.41 1.41M16.95 16.95l1.41 1.41M18.36 5.64l-1.41 1.41M7.05 16.95l-1.41 1.41'/%3E%3C/svg%3E"); }
.rv-icon--tag { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 5.25V11c0 .5.2 1 .55 1.35l7.6 7.6a1.75 1.75 0 0 0 2.5 0l5.8-5.8a1.75 1.75 0 0 0 0-2.5l-7.6-7.6A1.9 1.9 0 0 0 11 3.5H5.25A1.75 1.75 0 0 0 3.5 5.25Z'/%3E%3Ccircle cx='8' cy='8' r='1.5'/%3E%3C/svg%3E"); }
.rv-icon--taxi { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10.45 7.25V5.2a.85.85 0 0 1 .85-.85h2.5a.85.85 0 0 1 .85.85v2.05'/%3E%3Cpath d='M5 17.25h-.5A1.5 1.5 0 0 1 3 15.75v-2a2 2 0 0 1 1.6-1.95l2.15-.45 1.85-2.9c.5-.8 1.1-1.2 2-1.2h3.9c.9 0 1.5.3 2.05 1l2.7 3.2 1.15.3A2 2 0 0 1 21 13.75v2a1.5 1.5 0 0 1-1.5 1.5H19M9 17.25h6M6.75 11.35h11.5'/%3E%3Ccircle cx='7' cy='17.25' r='2'/%3E%3Ccircle cx='17' cy='17.25' r='2'/%3E%3C/svg%3E"); }
.rv-icon--trash { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.5h16M9 6.5V5a1.5 1.5 0 0 1 1.5-1.5h3A1.5 1.5 0 0 1 15 5v1.5'/%3E%3Cpath d='M6 6.5l.85 12.1c.1 1.1.95 1.9 2 1.9h6.3c1.05 0 1.9-.8 2-1.9L18 6.5'/%3E%3Cpath d='M10 10.5v6M14 10.5v6'/%3E%3C/svg%3E"); }
.rv-icon--upload { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 15.5V18a2.5 2.5 0 0 0 2.5 2.5h11A2.5 2.5 0 0 0 20 18v-2.5M12 15V3.75M7.5 8.25l4.5-4.5 4.5 4.5'/%3E%3C/svg%3E"); }
.rv-icon--user { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='7.75' r='4'/%3E%3Cpath d='M4.75 20.25c0-3.65 3.25-6 7.25-6s7.25 2.35 7.25 6'/%3E%3C/svg%3E"); }
.rv-icon--users { --rv-icon-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='9' cy='8' r='3.75'/%3E%3Cpath d='M3 19.75c0-3.25 2.7-5.5 6-5.5s6 2.25 6 5.5'/%3E%3Cpath d='M15.25 4.4a3.75 3.75 0 0 1 0 7.2'/%3E%3Cpath d='M16.5 14.5c2.6.5 4.5 2.45 4.5 5.25'/%3E%3C/svg%3E"); }
                 /* generato da scripts/build-icons.mjs */

/* ─── src/components/actions.css ────────────────────────────────── */
/* ==========================================================================
   Riviera · components/actions.css
   Azioni: .rv-btn, .rv-icon-btn, .rv-link, .rv-tag, .rv-badge, .rv-chip, .rv-rating, .rv-avatar
   Usa SOLO token semantici (--rv-bg, --rv-text, --rv-primary, …) e le scale
   di spazio/raggio/tipo/moto di tokens.css. Niente colori nei blocchi tema.

   CONVENZIONI DEL FILE
   - Ogni componente dichiara poche variabili locali (--rv-btn-bg, --rv-chip-fg…).
     I modificatori ridefiniscono solo quelle: hover, pressione e disabilitato
     si scrivono una volta sola e valgono per tutte le varianti.
   - Le variabili locali si risolvono sull'elemento stesso, quindi seguono il
     tema del sottoalbero ([data-theme] annidati compresi).
   - Specificità piatta: una classe, al massimo classe + stato/modificatore.
     Stati da attributi ARIA/HTML: [disabled], [aria-disabled], [aria-busy],
     [aria-pressed], [aria-selected].
   - Hover solo dentro @media (hover: hover): sul telefono non resta «appiccicato».
   - Bersagli sotto i 44px (bottone 36, chip 36, link) allargano l'area di
     tocco con un ::before trasparente, senza cambiare l'aspetto. Il ::before
     assoluto si misura dal padding box: il bordo di 1px va sottratto
     (--rv-border-width), altrimenti l'area arriva a 42px.
   - Le icone dentro i componenti prendono la misura da --rv-icon-size, che il
     componente imposta su di sé (un .rv-icon--sm/lg sull'icona vince comunque).
   - Bordi a riposo e fondi di hover usano i token dedicati: --rv-border-control
     (controlli che si riconoscono già da testo o icona), --rv-border-strong
     (quando il bordo è l'unico segno), --rv-surface-hover (distinto da
     --rv-surface-raised in entrambi i temi). color-mix() resta solo per le
     tinte derivate senza token proprio: hover di secondary, danger e chip
     scelta, bordo della chip scelta, fondo del tag neutro, anello dello spinner.
     Ogni color-mix() ha un ripiego a token pieno e vive in un blocco
     @supports (color: color-mix(in srgb, red, blue)): una dichiarazione che
     contiene var() non ricade su quella precedente se il browser non conosce
     color-mix(), diventa il valore iniziale (trasparente, bordo assente).
   - Verde come TESTO (link, azioni testuali): --rv-link e --rv-link-hover,
     mai --rv-primary, che resta per i pieni. --rv-link è AA su bg, surface,
     surface-raised, surface-hover e bg-muted in entrambi i temi (coppie in
     scripts/check-contrast.mjs); di notte --rv-primary come testo scende a
     4,0:1 su surface-raised e 3,6:1 su surface-hover.
   ========================================================================== */


/* ==========================================================================
   1. BOTTONE · .rv-btn
   Pill. Varianti: --primary (pieno pino: UNA per schermata), --secondary
   (pieno tenue), --outline (bordo beige), --ghost, --danger (tenue rosso).
   Misure: --sm 36px · default 44px · --lg 52px. --block = tutta la larghezza.
   Stati: :hover, :active (pressione), :focus-visible (da base.css),
   [disabled] / [aria-disabled="true"], [aria-busy="true"] (spinner).
   Il testo lungo va a capo bilanciato: nessun troncamento dei testi russi.

   <button class="rv-btn rv-btn--primary" type="button">Написать</button>

   <a class="rv-btn rv-btn--outline rv-btn--block" href="…">
     <span class="rv-btn__label">Смотреть профиль</span>
     <span class="rv-icon rv-icon--arrow-right rv-btn__icon" aria-hidden="true"></span>
   </a>

   Caricamento: aggiungi aria-busy="true" (l'etichetta resta nel DOM, quindi la
   larghezza non salta; il bottone non riceve tocchi finché è occupato).
   ========================================================================== */
.rv-btn {
  --rv-btn-h: var(--rv-tap);
  --rv-btn-px: var(--rv-space-5);
  --rv-btn-py: var(--rv-space-2);
  --rv-btn-bg: transparent;
  --rv-btn-bg-hover: var(--rv-surface-hover);
  --rv-btn-bg-active: var(--rv-surface-hover);
  --rv-btn-fg: var(--rv-text);
  --rv-btn-fg-hover: var(--rv-btn-fg);
  --rv-btn-border: transparent;
  --rv-btn-border-hover: var(--rv-btn-border);
  --rv-icon-size: 20px;

  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--rv-space-2);
  min-height: var(--rv-btn-h);
  max-width: 100%;
  padding: var(--rv-btn-py) var(--rv-btn-px);
  /* bordo sempre presente (trasparente sui pieni): tutte le varianti hanno la
     stessa misura esterna e in «contrasto elevato» il bordo diventa visibile */
  border: var(--rv-border-width) solid var(--rv-btn-border);
  /* metà dell'altezza minima: su una riga è una pill perfetta; se il testo va
     a capo diventa un rettangolo morbido invece di un ovale */
  border-radius: calc(var(--rv-btn-h) / 2);
  background-color: var(--rv-btn-bg);
  color: var(--rv-btn-fg);
  font-family: var(--rv-font-sans);
  font-size: var(--rv-fs-body);
  font-weight: var(--rv-fw-semibold);
  line-height: 1.2;
  letter-spacing: -0.005em;
  text-align: center;
  text-decoration: none;
  text-wrap: balance;
  vertical-align: middle;
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
  appearance: none;
  transition:
    background-color var(--rv-dur-fast) var(--rv-ease-out),
    border-color var(--rv-dur-fast) var(--rv-ease-out),
    color var(--rv-dur-fast) var(--rv-ease-out),
    transform var(--rv-dur-fast) var(--rv-ease-out);
}

@media (hover: hover) {
  .rv-btn:hover:not(:disabled, [aria-disabled="true"], [aria-busy="true"]) {
    background-color: var(--rv-btn-bg-hover);
    border-color: var(--rv-btn-border-hover);
    color: var(--rv-btn-fg-hover);
  }

  /* la freccia accenna la direzione, di 2px */
  .rv-btn:hover:not(:disabled, [aria-disabled="true"]) > .rv-icon--arrow-right {
    transform: translateX(2px);
  }
}

.rv-btn:active:not(:disabled, [aria-disabled="true"], [aria-busy="true"]) {
  background-color: var(--rv-btn-bg-active);
  border-color: var(--rv-btn-border-hover);
  color: var(--rv-btn-fg-hover);
  transform: scale(var(--rv-press-scale));
}

/* Icona: il margine negativo compensa l'aria interna della griglia 24,
   così il bordo del bottone sembra uguale dal lato del testo e dell'icona */
.rv-btn__icon {
  margin-inline: calc(-1 * var(--rv-space-0-5));
  transition: transform var(--rv-dur-fast) var(--rv-ease-out);
}

.rv-btn__label {
  min-width: 0;
}

/* --- Varianti --- */
.rv-btn--primary {
  --rv-btn-bg: var(--rv-primary);
  --rv-btn-bg-hover: var(--rv-primary-hover);
  --rv-btn-bg-active: var(--rv-primary-pressed);
  --rv-btn-fg: var(--rv-text-on-primary);
}

.rv-btn--secondary {
  --rv-btn-bg: var(--rv-primary-soft);
  --rv-btn-bg-hover: var(--rv-primary-soft);     /* ripiego: resta pieno, la pressione la dice scale() */
  --rv-btn-bg-active: var(--rv-primary-soft);
  --rv-btn-fg: var(--rv-primary-soft-text);
}

@supports (color: color-mix(in srgb, red, blue)) {
  .rv-btn--secondary {
    --rv-btn-bg-hover: color-mix(in srgb, var(--rv-primary-soft), var(--rv-primary) 10%);
    --rv-btn-bg-active: color-mix(in srgb, var(--rv-primary-soft), var(--rv-primary) 18%);
  }
}

/* Bordo «beige» --rv-border-control, a metà fra border e border-strong: il
   testo identifica già il bottone, un bordo border-strong ripetuto su ogni card
   pesa troppo. All'hover e alla pressione il bordo sale a border-strong. */
.rv-btn--outline {
  --rv-btn-border: var(--rv-border-control);
  --rv-btn-border-hover: var(--rv-border-strong);
}

.rv-btn--ghost {
  --rv-btn-fg: var(--rv-text);
}

/* Distruttivo tenue: forte abbastanza accanto a «Отмена», senza gridare.
   Quando il fondo si scurisce, il testo si avvicina a --rv-text: resta AA
   in entrambi i temi (di giorno scurisce, di notte schiarisce). */
.rv-btn--danger {
  --rv-btn-bg: var(--rv-danger-soft);
  --rv-btn-bg-hover: var(--rv-danger-soft);      /* ripieghi senza color-mix() */
  --rv-btn-bg-active: var(--rv-danger-soft);
  --rv-btn-fg: var(--rv-danger);
  --rv-btn-fg-hover: var(--rv-danger);
}

@supports (color: color-mix(in srgb, red, blue)) {
  .rv-btn--danger {
    --rv-btn-bg-hover: color-mix(in srgb, var(--rv-danger-soft), var(--rv-danger) 8%);
    --rv-btn-bg-active: color-mix(in srgb, var(--rv-danger-soft), var(--rv-danger) 14%);
    --rv-btn-fg-hover: color-mix(in srgb, var(--rv-danger), var(--rv-text) 30%);
  }
}

/* --- Misure --- */
.rv-btn--sm {
  --rv-btn-h: 36px;
  --rv-btn-px: var(--rv-space-4);
  --rv-btn-py: var(--rv-space-1-5);
  --rv-icon-size: 18px;
  gap: var(--rv-space-1-5);
  font-size: var(--rv-fs-callout);
}

/* area di tocco 44px intorno al bottone da 36 (4px + il bordo, sopra e sotto) */
.rv-btn--sm::before {
  content: "";
  position: absolute;
  inset: calc((var(--rv-tap) - 36px) / -2 - var(--rv-border-width)) 0;
}

.rv-btn--lg {
  --rv-btn-h: 52px;
  --rv-btn-px: var(--rv-space-6);
  --rv-btn-py: var(--rv-space-3);
  font-size: var(--rv-fs-headline);
}

.rv-btn--block {
  display: flex;
  width: 100%;
}

/* --- Disabilitato: fondo incassato e testo muted (sempre leggibile, AA) --- */
.rv-btn:disabled,
.rv-btn[aria-disabled="true"] {
  --rv-btn-bg: var(--rv-surface-sunken);
  --rv-btn-fg: var(--rv-text-muted);
  --rv-btn-border: transparent;
  cursor: default;
}

.rv-btn--outline:disabled,
.rv-btn--outline[aria-disabled="true"] {
  --rv-btn-bg: transparent;
  --rv-btn-border: var(--rv-border);
}

.rv-btn--ghost:disabled,
.rv-btn--ghost[aria-disabled="true"] {
  --rv-btn-bg: transparent;
}

/* un link non ha [disabled]: blocchiamo il tocco */
a.rv-btn[aria-disabled="true"] {
  pointer-events: none;
}

/* --- Caricamento --- */
.rv-btn[aria-busy="true"] {
  color: transparent;
  cursor: progress;
  pointer-events: none;
}

/* Arco pieno + anello tenue. Senza color-mix() resta l'arco da solo (binario
   trasparente): lo spinner si vede comunque girare. */
.rv-btn[aria-busy="true"]::after {
  content: "";
  position: absolute;
  inset: 0;
  width: 18px;
  height: 18px;
  margin: auto;
  border: 2px solid transparent;
  border-top-color: var(--rv-btn-fg);
  border-radius: 50%;
  animation: rv-btn-spin 700ms linear infinite;
}

@supports (color: color-mix(in srgb, red, blue)) {
  .rv-btn[aria-busy="true"]::after {
    border-color: color-mix(in srgb, var(--rv-btn-fg) 28%, transparent);
    border-top-color: var(--rv-btn-fg);
  }
}

.rv-btn--sm[aria-busy="true"]::after {
  width: 16px;
  height: 16px;
}

@keyframes rv-btn-spin {
  to { transform: rotate(360deg); }
}


/* ==========================================================================
   2. BOTTONE ICONA · .rv-icon-btn
   Rotondo, 44px (--sm: 36px visivi, 44px di tocco). Senza modificatore è
   trasparente; --ghost è lo stesso aspetto scritto in modo esplicito.
   --surface: cerchio chiaro con ombra, per stare sopra le foto (cuore dei
   preferiti) o accanto a un carosello.
   Toggle: aria-pressed. Con due icone __off/__on si scambia la forma; il
   cuore pieno prende --rv-favorite, gli altri toggle --rv-primary.
   Sempre un nome accessibile: aria-label o testo .rv-visually-hidden.

   <button class="rv-icon-btn rv-icon-btn--surface rv-icon-btn--sm" type="button"
           aria-pressed="false" aria-label="В избранное">
     <span class="rv-icon rv-icon--heart rv-icon-btn__off" aria-hidden="true"></span>
     <span class="rv-icon rv-icon--heart-fill rv-icon-btn__on" aria-hidden="true"></span>
   </button>

   <button class="rv-icon-btn" type="button" aria-label="Чаты, 3 непрочитанных">
     <span class="rv-icon rv-icon--chats" aria-hidden="true"></span>
     <span class="rv-badge rv-badge--corner" aria-hidden="true">3</span>
   </button>
   ========================================================================== */
.rv-icon-btn {
  --rv-icon-btn-size: var(--rv-tap);
  --rv-icon-btn-bg: transparent;
  --rv-icon-btn-bg-hover: var(--rv-surface-hover);
  --rv-icon-btn-fg: var(--rv-text);
  --rv-icon-btn-shadow: none;
  --rv-icon-size: 24px;

  position: relative;
  display: inline-grid;
  place-items: center;
  flex: none;
  width: var(--rv-icon-btn-size);
  height: var(--rv-icon-btn-size);
  padding: 0;
  border: var(--rv-border-width) solid transparent;
  border-radius: 50%;
  background-color: var(--rv-icon-btn-bg);
  box-shadow: var(--rv-icon-btn-shadow);
  color: var(--rv-icon-btn-fg);
  vertical-align: middle;
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
  appearance: none;
  transition:
    background-color var(--rv-dur-fast) var(--rv-ease-out),
    color var(--rv-dur-fast) var(--rv-ease-out),
    transform var(--rv-dur-fast) var(--rv-ease-out);
}

@media (hover: hover) {
  .rv-icon-btn:hover:not(:disabled, [aria-disabled="true"]) {
    background-color: var(--rv-icon-btn-bg-hover);
  }
}

.rv-icon-btn:active:not(:disabled, [aria-disabled="true"]) {
  background-color: var(--rv-icon-btn-bg-hover);
  transform: scale(var(--rv-press-scale));
}

.rv-icon-btn--ghost {
  --rv-icon-btn-bg: transparent;
}

/* cerchio raised; all'hover il fondo comune --rv-surface-hover, che in
   entrambi i temi si distingue da raised */
.rv-icon-btn--surface {
  --rv-icon-btn-bg: var(--rv-surface-raised);
  --rv-icon-btn-bg-hover: var(--rv-surface-hover);
  --rv-icon-btn-shadow: var(--rv-shadow-sm);
  --rv-badge-cutout: var(--rv-surface-raised);
}

.rv-icon-btn--sm {
  --rv-icon-btn-size: 36px;
  --rv-icon-size: 20px;
}

/* area di tocco 44×44: 4px + il bordo trasparente di 1px, su tutti i lati */
.rv-icon-btn--sm::before {
  content: "";
  position: absolute;
  inset: calc((var(--rv-tap) - 36px) / -2 - var(--rv-border-width));
  border-radius: 50%;
}

/* --- Toggle --- */
.rv-icon-btn[aria-pressed="true"] {
  --rv-icon-btn-fg: var(--rv-primary);
}

.rv-icon-btn__on {
  display: none;
}

[aria-pressed="true"] > .rv-icon-btn__on {
  display: inline-block;
  animation: rv-heart-pop var(--rv-dur-slow) var(--rv-ease-out);
}

[aria-pressed="true"] > .rv-icon-btn__off {
  display: none;
}

[aria-pressed="true"] > .rv-icon--heart-fill {
  color: var(--rv-favorite);
}

@keyframes rv-heart-pop {
  0% { transform: scale(0.6); }
  55% { transform: scale(1.14); }
  100% { transform: scale(1); }
}

/* --- Disabilitato --- */
.rv-icon-btn:disabled,
.rv-icon-btn[aria-disabled="true"] {
  --rv-icon-btn-fg: var(--rv-text-muted);
  --rv-icon-btn-shadow: none;
  cursor: default;
}


/* ==========================================================================
   3. LINK CON FRECCIA · .rv-link
   Azione testuale verde (sezioni «Все →», «Назад»). Niente sottolineatura a
   riposo, compare all'hover. L'area di tocco arriva a 44px senza spostare
   l'impaginazione. --muted: link di servizio in grigio («Пожаловаться»).
   Colore --rv-link (hover e pressione --rv-link-hover): AA anche su sheet,
   dialog, toast, banner e fondo di hover, in entrambi i temi. Link su più
   righe vicine (indici, elenchi che vanno a capo):
   almeno 24px fra una riga e l'altra (es. row-gap: var(--rv-space-6)),
   altrimenti le aree di tocco estese (12px sopra e sotto) si sovrappongono e
   un tocco appena sotto un link apre quello della riga successiva.

   <a class="rv-link" href="…">
     Все специалисты
     <span class="rv-icon rv-icon--arrow-right rv-link__icon" aria-hidden="true"></span>
   </a>
   ========================================================================== */
.rv-link {
  --rv-link-fg: var(--rv-link);
  --rv-link-fg-hover: var(--rv-link-hover);
  --rv-icon-size: 18px;

  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--rv-space-1);
  max-width: 100%;
  border-radius: var(--rv-radius-xs);
  color: var(--rv-link-fg);
  font-family: var(--rv-font-sans);
  font-size: var(--rv-fs-callout);
  font-weight: var(--rv-fw-semibold);
  line-height: var(--rv-lh-callout);
  text-decoration-line: none;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
  cursor: pointer;
  transition: color var(--rv-dur-fast) var(--rv-ease-out);
}

/* area di tocco: 20px di riga + 12 sopra e sotto */
.rv-link::before {
  content: "";
  position: absolute;
  inset: calc((var(--rv-tap) - 1em * 1.45) / -2) calc(-1 * var(--rv-space-1));
}

.rv-link__icon {
  transition: transform var(--rv-dur-fast) var(--rv-ease-out);
}

@media (hover: hover) {
  .rv-link:hover {
    color: var(--rv-link-fg-hover);
    text-decoration-line: underline;
  }

  .rv-link:hover > .rv-icon--arrow-right { transform: translateX(2px); }
  .rv-link:hover > .rv-icon--arrow-left { transform: translateX(-2px); }
}

.rv-link:active {
  color: var(--rv-link-fg-hover);
  opacity: 0.8;
}

.rv-link--muted {
  --rv-link-fg: var(--rv-text-secondary);
  --rv-link-fg-hover: var(--rv-text);
  font-weight: var(--rv-fw-medium);
}


/* ==========================================================================
   4. TAG · .rv-tag
   Pill maiuscola piccola per categorie. Colori: --pino (pieno, forte),
   --cipria --sabbia --mare --salvia (tenui). Senza modificatore: neutro.
   Non è un bottone: se deve filtrare, usa .rv-chip.
   Sopra un'immagine «a cavallo» del bordo: il posizionamento lo dà la card;
   il tag porta con sé un contorno del colore della card (--rv-tag-cutout).

   <span class="rv-tag rv-tag--pino">Ремонт</span>
   <span class="rv-tag rv-tag--salvia">
     <span class="rv-icon rv-icon--seal-check rv-tag__icon" aria-hidden="true"></span>Проверен
   </span>
   ========================================================================== */
.rv-tag {
  --rv-tag-bg: var(--rv-surface-hover);   /* ripiego senza color-mix() */
  --rv-tag-fg: var(--rv-text-secondary);
  --rv-icon-size: 14px;

  display: inline-flex;
  align-items: center;
  gap: var(--rv-space-1);
  max-width: 100%;
  min-height: 22px;
  /* il tracking aggiunge aria dopo l'ultima lettera: la togliamo a destra */
  padding-block: var(--rv-space-0-5);
  padding-inline: calc(var(--rv-space-2) + var(--rv-space-0-5)) calc(var(--rv-space-2) + var(--rv-space-0-5) - var(--rv-tracking-tag));
  border-radius: var(--rv-radius-pill);
  background-color: var(--rv-tag-bg);
  color: var(--rv-tag-fg);
  font-family: var(--rv-font-sans);
  font-size: var(--rv-fs-tag);
  font-weight: var(--rv-fw-semibold);
  line-height: var(--rv-lh-tag);
  letter-spacing: var(--rv-tracking-tag);
  text-transform: uppercase;
  white-space: nowrap;
  vertical-align: middle;
}

@supports (color: color-mix(in srgb, red, blue)) {
  .rv-tag {
    --rv-tag-bg: color-mix(in srgb, var(--rv-surface), var(--rv-text) 9%);
  }
}

.rv-tag__icon {
  margin-inline-start: calc(-1 * var(--rv-space-0-5));
}

.rv-tag--pino { --rv-tag-bg: var(--rv-tag-pino-bg); --rv-tag-fg: var(--rv-tag-pino-fg); }
.rv-tag--cipria { --rv-tag-bg: var(--rv-tag-cipria-bg); --rv-tag-fg: var(--rv-tag-cipria-fg); }
.rv-tag--sabbia { --rv-tag-bg: var(--rv-tag-sabbia-bg); --rv-tag-fg: var(--rv-tag-sabbia-fg); }
.rv-tag--mare { --rv-tag-bg: var(--rv-tag-mare-bg); --rv-tag-fg: var(--rv-tag-mare-fg); }
.rv-tag--salvia { --rv-tag-bg: var(--rv-tag-salvia-bg); --rv-tag-fg: var(--rv-tag-salvia-fg); }

/* Sopra una foto: contorno nel colore della superficie che lo ospita */
.rv-tag--cutout {
  box-shadow: 0 0 0 3px var(--rv-tag-cutout, var(--rv-surface));
}


/* ==========================================================================
   5. BADGE · .rv-badge
   Contatore di non letti (pill 20px, cifre tabellari) o punto (--dot).
   --muted: chat silenziate; il grigio da solo non basta a dirlo, accanto va
   il segno .rv-row__muted (campanella barrata + «без звука» nascosto,
   content.css). --corner: nell'angolo in alto a destra
   dell'icona di un contenitore posizionato (.rv-icon-btn lo è già); il
   contorno prende --rv-badge-cutout (default --rv-bg).

   ACCESSIBILITÀ — un «12» letto da solo non dice nulla: il numero va sempre
   insieme alla parola che lo spiega. Schema consigliato: badge
   aria-hidden="true" e conteggio completo altrove:
   - dentro un controllo (bottone, link, tab): nel suo nome accessibile
     (aria-label del bottone oppure testo .rv-visually-hidden al suo interno);
   - in una riga o in un testo: in un .rv-visually-hidden accanto al badge.
   Ammesso anche il testo nascosto DENTRO il badge, che allora resta leggibile:
   <span class="rv-badge">17<span class="rv-visually-hidden"> непрочитанных</span></span>
   Niente aria-label su uno <span> senza ruolo: molti lettori di schermo lo
   ignorano. Il punto (--dot) non ha numero: la novità va detta a parole
   («есть новые») nel nome del controllo.

   Riga di un elenco (chat):
   <span class="rv-visually-hidden">12 непрочитанных</span>
   <span class="rv-badge" aria-hidden="true">12</span>

   Dentro un bottone icona:
   <button class="rv-icon-btn" type="button" aria-label="Чаты, 17 непрочитанных">
     <span class="rv-icon rv-icon--chats" aria-hidden="true"></span>
     <span class="rv-badge rv-badge--corner" aria-hidden="true">17</span>
   </button>

   Punto:
   <button class="rv-icon-btn" type="button" aria-label="Уведомления, есть новые">
     <span class="rv-icon rv-icon--bell" aria-hidden="true"></span>
     <span class="rv-badge rv-badge--dot rv-badge--corner" aria-hidden="true"></span>
   </button>
   ========================================================================== */
.rv-badge {
  --rv-badge-bg: var(--rv-primary);
  --rv-badge-fg: var(--rv-text-on-primary);

  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  min-width: 20px;
  height: 20px;
  padding-inline: var(--rv-space-1-5);
  border-radius: var(--rv-radius-pill);
  background-color: var(--rv-badge-bg);
  color: var(--rv-badge-fg);
  font-family: var(--rv-font-sans);
  font-size: var(--rv-fs-overline);
  font-weight: var(--rv-fw-bold);
  line-height: 1;
  letter-spacing: 0;
  font-variant-numeric: tabular-nums lining-nums;
  white-space: nowrap;
  vertical-align: middle;
}

/* grigio pieno con testo chiaro: stesso rapporto di text-muted su surface */
.rv-badge--muted {
  --rv-badge-bg: var(--rv-text-muted);
  --rv-badge-fg: var(--rv-surface);
}

.rv-badge--dot {
  min-width: 0;
  width: 10px;
  height: 10px;
  padding: 0;
}

.rv-badge--corner {
  position: absolute;
  top: var(--rv-space-0-5);
  inset-inline-start: calc(50% + var(--rv-space-0-5));
  box-shadow: 0 0 0 2px var(--rv-badge-cutout, var(--rv-bg));
}

.rv-badge--dot.rv-badge--corner {
  top: var(--rv-space-2);
  inset-inline-start: calc(50% + var(--rv-space-1-5));
}


/* ==========================================================================
   6. CHIP · .rv-chip
   Filtro selezionabile, pill 36px (tocco 44). Stato con aria-pressed (filtri
   indipendenti) o aria-selected (dentro role="tablist"/"listbox").
   Selezionato = fondo tenue pino + bordo pino + (facoltativa) spunta __check:
   il significato non è affidato al solo colore.
   Parti: __icon (categoria o chiudi), __count (numero), __check, __label
   (serve solo per troncare testi molto lunghi con i puntini).

   <button class="rv-chip" type="button" aria-pressed="true">
     <span class="rv-icon rv-icon--check rv-chip__check" aria-hidden="true"></span>
     Рядом со мной
   </button>
   <button class="rv-chip" type="button" aria-pressed="false">
     <span class="rv-icon rv-icon--hammer rv-chip__icon" aria-hidden="true"></span>
     Ремонт <span class="rv-chip__count">24</span>
   </button>
   ========================================================================== */
.rv-chip {
  --rv-chip-bg: var(--rv-surface);
  --rv-chip-bg-hover: var(--rv-surface-hover);
  --rv-chip-fg: var(--rv-text);
  --rv-chip-border: var(--rv-border);
  --rv-icon-size: 18px;

  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--rv-space-2);
  max-width: 100%;
  min-height: 36px;
  padding: 0 var(--rv-space-4);
  border: var(--rv-border-width) solid var(--rv-chip-border);
  border-radius: var(--rv-radius-pill);
  background-color: var(--rv-chip-bg);
  color: var(--rv-chip-fg);
  font-family: var(--rv-font-sans);
  font-size: var(--rv-fs-callout);
  font-weight: var(--rv-fw-medium);
  line-height: 1.2;
  white-space: nowrap;
  vertical-align: middle;
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
  appearance: none;
  transition:
    background-color var(--rv-dur-fast) var(--rv-ease-out),
    border-color var(--rv-dur-fast) var(--rv-ease-out),
    color var(--rv-dur-fast) var(--rv-ease-out),
    transform var(--rv-dur-fast) var(--rv-ease-out);
}

/* area di tocco 44px: 4px + il bordo di 1px, sopra e sotto */
.rv-chip::before {
  content: "";
  position: absolute;
  inset: calc((var(--rv-tap) - 36px) / -2 - var(--rv-border-width)) 0;
}

@media (hover: hover) {
  .rv-chip:hover:not(:disabled, [aria-disabled="true"]) {
    background-color: var(--rv-chip-bg-hover);
  }
}

.rv-chip:active:not(:disabled, [aria-disabled="true"]) {
  background-color: var(--rv-chip-bg-hover);
  transform: scale(var(--rv-press-scale));
}

.rv-chip__icon,
.rv-chip__check {
  margin-inline: calc(-1 * var(--rv-space-0-5));
}

.rv-chip__label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

.rv-chip__count {
  color: var(--rv-text-muted);
  font-weight: var(--rv-fw-regular);
  font-variant-numeric: tabular-nums lining-nums;
}

.rv-chip__check {
  display: none;
}

/* --- Selezionato --- */
.rv-chip[aria-pressed="true"],
.rv-chip[aria-selected="true"] {
  --rv-chip-bg: var(--rv-primary-soft);
  --rv-chip-bg-hover: var(--rv-primary-soft);   /* ripieghi senza color-mix() */
  --rv-chip-fg: var(--rv-primary-soft-text);
  --rv-chip-border: var(--rv-primary);
}

@supports (color: color-mix(in srgb, red, blue)) {
  .rv-chip[aria-pressed="true"],
  .rv-chip[aria-selected="true"] {
    --rv-chip-bg-hover: color-mix(in srgb, var(--rv-primary-soft), var(--rv-primary) 10%);
    --rv-chip-border: color-mix(in srgb, var(--rv-primary) 55%, transparent);
  }
}

:is([aria-pressed="true"], [aria-selected="true"]) > .rv-chip__check {
  display: inline-block;
}

:is([aria-pressed="true"], [aria-selected="true"]) > .rv-chip__count {
  color: inherit;
}

/* --- Disabilitato --- */
.rv-chip:disabled,
.rv-chip[aria-disabled="true"] {
  --rv-chip-bg: transparent;
  --rv-chip-fg: var(--rv-text-muted);
  --rv-chip-border: var(--rv-border);
  cursor: default;
}


/* ==========================================================================
   7. VALUTAZIONE · .rv-rating
   Stella + valore + recensioni: «★ 4,9 · 12 отзывов». Il punto separatore è
   generato dal CSS (muto per gli screen reader), solo fra due testi (valore e
   conteggio), con lo stesso spazio ai due lati. Dopo la fila __stars niente
   punto: le stelle separano già («4,9 ★★★★★ 12 отзывов»). Decimali con la virgola.
   Misure: --sm (meta nelle card), default, --lg (profilo, valore in serif).
   __stars: fila di 5 stelle. La stella vuota cambia FORMA, non solo colore:
   icona a contorno rv-icon--star + .rv-rating__star--empty (bordo ≥3:1).
   Le piene restano rv-icon--star-fill.
   --none: nessuna recensione ancora (stella a contorno, tenue).

   <span class="rv-rating">
     <span class="rv-icon rv-icon--star-fill rv-rating__star" aria-hidden="true"></span>
     <span class="rv-visually-hidden">Рейтинг</span>
     <span class="rv-rating__value">4,9</span>
     <span class="rv-rating__count">12 отзывов</span>
   </span>

   Quattro su cinque (la fila ha un nome: ha un ruolo):
   <span class="rv-rating__stars" role="img" aria-label="4 из 5">
     <span class="rv-icon rv-icon--star-fill rv-rating__star"></span> ×4
     <span class="rv-icon rv-icon--star rv-rating__star rv-rating__star--empty"></span>
   </span>

   Nessuna recensione:
   <span class="rv-rating rv-rating--none">
     <span class="rv-icon rv-icon--star rv-rating__star" aria-hidden="true"></span>
     <span class="rv-rating__count">Отзывов пока нет</span>
   </span>
   ========================================================================== */
.rv-rating {
  --rv-icon-size: 16px;
  --rv-rating-gap: var(--rv-space-1);

  display: inline-flex;
  align-items: center;
  gap: var(--rv-rating-gap);
  max-width: 100%;
  color: var(--rv-text-muted);
  font-family: var(--rv-font-sans);
  font-size: var(--rv-fs-callout);
  font-weight: var(--rv-fw-regular);
  line-height: var(--rv-lh-callout);
  font-variant-numeric: tabular-nums lining-nums;
  white-space: nowrap;
  vertical-align: middle;
}

.rv-rating__star {
  color: var(--rv-star);
  /* la stella piena ha il baricentro basso: la alziamo di un pixel */
  translate: 0 -1px;
}

/* stella vuota: contorno (rv-icon--star) in border-strong. Porta
   un'informazione (4 su 5), quindi il tratto resta ≥3:1 sul fondo. */
.rv-rating__star--empty {
  color: var(--rv-border-strong);
}

.rv-rating__stars {
  display: inline-flex;
  gap: 1px;
}

/* Un po' d'aria in più fra la fila di stelle e il testo che la segue o la
   precede (un testo nascosto «Рейтинг» in mezzo non conta) */
.rv-rating__stars + :is(.rv-rating__value, .rv-rating__count),
.rv-rating__stars + .rv-visually-hidden + :is(.rv-rating__value, .rv-rating__count),
.rv-rating__value + .rv-rating__stars {
  margin-inline-start: var(--rv-space-1);
}

.rv-rating__value {
  color: var(--rv-text);
  font-weight: var(--rv-fw-semibold);
}

.rv-rating__count {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* il separatore compare solo fra valore e conteggio («Пока нет отзывов» no),
   con a destra lo stesso spazio del gap che ha a sinistra */
.rv-rating__value + .rv-rating__count::before {
  content: "·";
  content: "·" / "";
  margin-inline-end: var(--rv-rating-gap);
}

.rv-rating--sm {
  --rv-icon-size: 14px;
  --rv-rating-gap: var(--rv-space-1);
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
}

.rv-rating--lg {
  --rv-icon-size: 20px;
  --rv-rating-gap: var(--rv-space-1-5);
}

.rv-rating--lg .rv-rating__value {
  color: var(--rv-heading);
  font-family: var(--rv-font-display);
  font-size: var(--rv-fs-title-2);
  font-weight: var(--rv-fw-medium);
  line-height: 1;
}

/* nessuna recensione: la stella è decorativa (il testo dice già tutto),
   quindi basta il beige dei controlli a riposo */
.rv-rating--none .rv-rating__star {
  color: var(--rv-border-control);
}


/* ==========================================================================
   8. AVATAR · .rv-avatar  (+ .rv-avatar-group)
   Iniziali in serif su tinta tenue, oppure <img>. Misure: --sm 32 · default 40
   · --lg 56 · --xl 88. Tinte: --pino --cipria --sabbia --mare --salvia
   (default salvia; sceglile in modo stabile dal nome, non a caso).
   --verified: anello ottone staccato dal bordo, con sigillo __seal facoltativo
   (da 56px in su). --more: «+12» in un gruppo.
   Gruppo: sovrapposizione del 18% e contorno del colore di fondo
   (--rv-avatar-cutout, default --rv-bg; sulle card usa --rv-surface).
   Attenzione al tema scuro: le tinte (toni 900) hanno quasi la luminanza di
   --rv-bg-muted e --rv-surface-raised (1,03:1). Su quei pannelli il contorno
   NON deve essere il colore del pannello: lascia --rv-bg e aggiungi un filo
   sottile fra disco e contorno, --rv-avatar-edge: var(--rv-border-control)
   (1px; ~1,9:1 sui dischi e ~2,9:1 sul contorno di notte, appena percettibile
   di giorno). Così fa .rv-banner.

   <span class="rv-avatar rv-avatar--lg rv-avatar--cipria rv-avatar--verified" role="img" aria-label="Ольга Смирнова, проверенный специалист">
     ОС
     <span class="rv-avatar__seal" aria-hidden="true"><span class="rv-icon rv-icon--seal-check"></span></span>
   </span>

   <div class="rv-avatar-group">
     <span class="rv-avatar rv-avatar--sm" aria-hidden="true">ИП</span>
     <span class="rv-avatar rv-avatar--sm rv-avatar--more" aria-hidden="true">+12</span>
   </div>
   ========================================================================== */
.rv-avatar {
  --rv-avatar-size: 40px;
  --rv-avatar-bg: var(--rv-tag-salvia-bg);
  --rv-avatar-fg: var(--rv-tag-salvia-fg);
  --rv-avatar-ring: 1.5px;

  position: relative;
  display: inline-grid;
  place-items: center;
  flex: none;
  width: var(--rv-avatar-size);
  height: var(--rv-avatar-size);
  border-radius: 50%;
  background-color: var(--rv-avatar-bg);
  color: var(--rv-avatar-fg);
  font-family: var(--rv-font-display);
  font-size: calc(var(--rv-avatar-size) * 0.4);
  font-weight: var(--rv-fw-medium);
  line-height: 1;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  white-space: nowrap;
  vertical-align: middle;
  -webkit-user-select: none;
  user-select: none;
}

.rv-avatar > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border-radius: inherit;
  object-fit: cover;
  filter: var(--rv-image-dim);
}

/* --- Misure --- */
.rv-avatar--sm { --rv-avatar-size: 32px; }
.rv-avatar--lg { --rv-avatar-size: 56px; }
.rv-avatar--xl { --rv-avatar-size: 88px; --rv-avatar-ring: 2px; }

/* --- Tinte --- */
.rv-avatar--pino { --rv-avatar-bg: var(--rv-primary-soft); --rv-avatar-fg: var(--rv-primary-soft-text); }
.rv-avatar--cipria { --rv-avatar-bg: var(--rv-tag-cipria-bg); --rv-avatar-fg: var(--rv-tag-cipria-fg); }
.rv-avatar--sabbia { --rv-avatar-bg: var(--rv-tag-sabbia-bg); --rv-avatar-fg: var(--rv-tag-sabbia-fg); }
.rv-avatar--mare { --rv-avatar-bg: var(--rv-tag-mare-bg); --rv-avatar-fg: var(--rv-tag-mare-fg); }
.rv-avatar--salvia { --rv-avatar-bg: var(--rv-tag-salvia-bg); --rv-avatar-fg: var(--rv-tag-salvia-fg); }

/* --- Verificato: anello ottone con uno spiraglio trasparente --- */
.rv-avatar--verified {
  outline: var(--rv-avatar-ring) solid var(--rv-accent);
  outline-offset: calc(var(--rv-avatar-ring) + 0.5px);
}

.rv-avatar__seal {
  --rv-icon-size: calc(var(--rv-avatar-size) * 0.27);
  position: absolute;
  inset-inline-end: calc(var(--rv-avatar-size) * -0.05);
  bottom: calc(var(--rv-avatar-size) * -0.05);
  display: grid;
  place-items: center;
  width: calc(var(--rv-avatar-size) * 0.38);
  height: calc(var(--rv-avatar-size) * 0.38);
  border-radius: 50%;
  background-color: var(--rv-surface-raised);
  box-shadow: var(--rv-shadow-xs);
  color: var(--rv-accent);
}

/* --- «+12» --- */
/* Corpo proporzionale al cerchio, mai sotto il gradino minimo della scala
   (--rv-fs-tag, 10,5px). Nel cerchio da 32px stanno due cifre («+14»):
   oltre, scrivi «99+». */
.rv-avatar--more {
  --rv-avatar-bg: var(--rv-surface-sunken);
  --rv-avatar-fg: var(--rv-text-secondary);
  font-family: var(--rv-font-sans);
  font-size: max(var(--rv-fs-tag), calc(var(--rv-avatar-size) * 0.32));
  font-weight: var(--rv-fw-semibold);
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums lining-nums;
}

/* --- Gruppo sovrapposto ---
   Ogni cerchio copre il 18% del precedente, con un contorno del colore di
   fondo che li stacca. Le iniziali dei cerchi coperti si centrano nella parte
   che resta visibile, così la seconda lettera non finisce sotto il vicino. */
.rv-avatar-group {
  display: inline-flex;
  align-items: center;
  vertical-align: middle;
}

.rv-avatar-group > .rv-avatar {
  font-size: calc(var(--rv-avatar-size) * 0.34);
  /* filo facoltativo (--rv-avatar-edge, 1px) attaccato al disco, poi il contorno da 2px */
  box-shadow:
    0 0 0 var(--rv-avatar-edge-w, 0px) var(--rv-avatar-edge, transparent),
    0 0 0 calc(2px + var(--rv-avatar-edge-w, 0px)) var(--rv-avatar-cutout, var(--rv-bg));
}

.rv-avatar-group > .rv-avatar:not(:last-child) {
  padding-inline-end: calc(var(--rv-avatar-size) * 0.18 + 2px);
}

.rv-avatar-group > .rv-avatar--more {
  font-size: max(var(--rv-fs-tag), calc(var(--rv-avatar-size) * 0.3));
}

.rv-avatar-group > .rv-avatar + .rv-avatar {
  margin-inline-start: calc(var(--rv-avatar-size) * -0.18);
}


/* ─── src/components/forms.css ──────────────────────────────────── */
/* ==========================================================================
   Riviera · components/forms.css
   Moduli: .rv-search, .rv-search-hint, .rv-interpretation, .rv-field, .rv-input (+ --date), .rv-textarea, .rv-select, .rv-check, .rv-radio, .rv-switch, .rv-segmented, .rv-stepper, .rv-rating-input, .rv-upload (+ __cover), .rv-price
   Usa SOLO token semantici (--rv-bg, --rv-text, --rv-primary, …) e le scale
   di spazio/raggio/tipo/moto di tokens.css. Niente colori nei blocchi tema.

   CONVENZIONI DEL GRUPPO
   - Campo di testo: alto 48px (tocco 44 + respiro), testo 16px (sotto i 16px
     Safari iOS ingrandisce la pagina al focus), raggio md, fondo surface.
   - Bordi a riposo, due livelli (decisi in tokens.css):
     · --rv-border-strong (≥3:1) dove il bordo è l'unico segno del controllo:
       input, textarea, select, prezzo, checkbox, radio;
     · --rv-border-control dove testo o icona identificano già il controllo:
       tessera «Добавить» dell'upload, bottoni − / + dello stepper.
     All'hover entrambi salgono di un gradino (text-muted / border-strong).
   - Disabilitato e sola lettura: fondo surface-sunken, bordo tenue.
   - Focus: bordo focus-ring da 2px + alone primary-soft. Solo al focus
     l'outline diventa trasparente: la modalità a contrasto elevato di Windows
     lo ricolora e lo mostra (le ombre lì spariscono).
   - Errore: aria-invalid="true" sul controllo + .rv-field__error con icona
     e testo che spiega cosa fare (mai solo il rosso).
   - Label SEMPRE visibile: il placeholder è solo un esempio. Unica eccezione
     documentata: la ricerca compatta (§3), con nome accessibile esplicito.
   - Checkbox, radio, switch e segmenti usano l'input nativo trasparente sopra
     il disegno: tastiera, lettori di schermo e moduli funzionano da soli.
   - .is-focus riproduce il focus nelle pagine della styleguide: non usarlo
     nel prodotto.
   - Supporto (tokens.css, «SUPPORTO WEBVIEW»): baseline iOS 16.2+ e Chrome
     111+. Sotto, nulla di essenziale sparisce: stato scelto, disattivo e bordi
     dei controlli si scrivono SENZA :has(), con l'input nativo prima del
     disegno e il selettore fratello (input:checked + box, input:checked ~
     label). :has() resta solo per rifiniture e per il focus di barre e
     riquadri che contengono l'input: sotto la baseline il riquadro .rv-price
     non si colora di rosso né si spegne (l'errore resta detto da
     .rv-field__error, il campo resta disabilitato), la tessera --card scelta
     perde il filo verde (resta il pallino pieno), «очистить» resta visibile
     anche a campo vuoto.
   - :has() mai nella stessa lista di selettori di attributi e classi: dove
     :has() non esiste il browser scarta l'intera regola. Le regole con :has()
     stanno da sole; in fondo al file un blocco @supports not selector(:has(> *))
     dà il focus con :focus-within.
   ========================================================================== */


/* --------------------------------------------------------------------------
   1. .rv-field — contenitore di un campo: label, controllo, suggerimento,
   errore, contatore. Funziona anche come <fieldset> con <legend>.

   <div class="rv-field">
     <label class="rv-field__label" for="name">Имя <span class="rv-field__optional">необязательно</span></label>
     <input class="rv-input" id="name" aria-describedby="name-hint">
     <p class="rv-field__hint" id="name-hint">Так вас увидят в каталоге</p>
   </div>

   Errore:  <input class="rv-input" aria-invalid="true" aria-describedby="x-err">
            <p class="rv-field__error" id="x-err"><span class="rv-icon rv-icon--alert-circle" aria-hidden="true"></span>Введите цену цифрами</p>
   Contatore: <div class="rv-field__foot"><p class="rv-field__hint">…</p><span class="rv-field__counter">186 / 600</span></div>
   Riga (stepper, switch a lato): .rv-field--row con testi in .rv-field__text
   -------------------------------------------------------------------------- */
.rv-field {
  display: grid;
  gap: var(--rv-space-2);
  min-inline-size: 0;              /* <fieldset>: niente larghezza minima implicita */
  margin: 0;
  padding: 0;
  border: 0;
}

.rv-field__label {
  float: left;                     /* <legend> flottante = elemento normale della griglia */
  inline-size: 100%;
  padding: 0;
  font-size: var(--rv-fs-callout);
  font-weight: var(--rv-fw-medium);
  line-height: var(--rv-lh-callout);
  color: var(--rv-text);
  overflow-wrap: break-word;
}

.rv-field__optional {
  margin-inline-start: var(--rv-space-1);
  font-weight: var(--rv-fw-regular);
  color: var(--rv-text-muted);
}

.rv-field__hint,
.rv-field__error,
.rv-field__counter {
  margin: 0;
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
}

.rv-field__hint {
  min-inline-size: 0;
  color: var(--rv-text-muted);
}

.rv-field__error {
  display: flex;
  align-items: flex-start;
  gap: var(--rv-space-1-5);
  min-inline-size: 0;
  font-weight: var(--rv-fw-medium);
  color: var(--rv-danger);
}

.rv-field__error > .rv-icon {
  --rv-icon-size: 16px;
  margin-block-start: 1px;         /* centrata sulla prima riga (13 × 1,4 ≈ 18px) */
}

/* Suggerimento/errore a sinistra, contatore a destra */
.rv-field__foot {
  display: flex;
  align-items: flex-start;
  gap: var(--rv-space-3);
}

.rv-field__counter {
  flex: none;
  margin-inline-start: auto;
  color: var(--rv-text-muted);
  font-variant-numeric: tabular-nums lining-nums;
  white-space: nowrap;
}

.rv-field__counter.is-over {
  font-weight: var(--rv-fw-medium);
  color: var(--rv-danger);
}

/* Gruppo di opzioni (checkbox/radio) dentro un campo */
.rv-field__options {
  display: grid;
}

/* Variante a riga: testi a sinistra, controllo compatto a destra */
.rv-field--row {
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  column-gap: var(--rv-space-4);
}

.rv-field__text {
  display: grid;
  gap: var(--rv-space-0-5);
  min-inline-size: 0;
}


/* --------------------------------------------------------------------------
   2. Controlli di testo — .rv-input, .rv-textarea, .rv-select, .rv-price

   <input class="rv-input" id="name" type="text">
   <textarea class="rv-textarea" id="about" rows="4"></textarea>

   <span class="rv-select">
     <select class="rv-select__control" id="cat" required>
       <option value="" disabled selected>Выберите категорию</option> …
     </select>
     <span class="rv-icon rv-icon--chevron-down rv-select__icon" aria-hidden="true"></span>
   </span>

   <div class="rv-price">
     <span class="rv-price__prefix" aria-hidden="true">от</span>        (facoltativo)
     <input class="rv-price__input" id="price" type="text" inputmode="decimal">
     <span class="rv-price__suffix" aria-hidden="true">€</span>
   </div>
   (prefisso e suffisso sono nascosti ai lettori: ripetili nella label,
   es. «Цена от<span class="rv-visually-hidden">, в евро</span>»)
   -------------------------------------------------------------------------- */
.rv-input,
.rv-textarea,
.rv-select__control,
.rv-price {
  display: block;
  inline-size: 100%;
  min-block-size: calc(var(--rv-tap) + var(--rv-space-1));
  margin: 0;
  padding-inline: var(--rv-space-4);
  border: var(--rv-border-width) solid var(--rv-border-strong);
  border-radius: var(--rv-radius-md);
  background-color: var(--rv-surface);
  color: var(--rv-text);
  font-family: var(--rv-font-sans);
  font-size: 16px;                 /* soglia iOS: sotto i 16px la pagina si ingrandisce al focus */
  font-weight: var(--rv-fw-regular);
  line-height: var(--rv-lh-body);
  -webkit-appearance: none;
  appearance: none;
  transition:
    border-color var(--rv-dur-fast) var(--rv-ease-out),
    box-shadow var(--rv-dur-fast) var(--rv-ease-out),
    background-color var(--rv-dur-fast) var(--rv-ease-out);
}

/* Valore più lungo del campo: puntini invece del taglio netto (a riposo) */
.rv-input,
.rv-price__input {
  text-overflow: ellipsis;
}

.rv-input::placeholder,
.rv-textarea::placeholder,
.rv-price__input::placeholder,
.rv-search__input::placeholder {
  color: var(--rv-text-muted);
  opacity: 1;                      /* Firefox lo schiarisce di suo */
}

/* Data e ora — .rv-input--date
   <input class="rv-input rv-input--date" id="when" type="date">
   <input class="rv-input rv-input--date" id="at" type="time">
   In WebKit (Safari, tutte le webview iOS) <input type="date"> e type="time"
   non sono campi di testo normali: hanno una larghezza intrinseca propria,
   non si allargano nel contenitore, l'altezza la decide il contenuto e il
   valore viene centrato in un elemento interno. Senza queste righe, su iPhone
   i campi sembrano rotti: stretti, bassi e con la data che balla al centro.
   - -webkit-appearance: none toglie la resa nativa (bordo e ombra interna);
   - min-inline-size: 0 disinnesca la larghezza intrinseca, così inline-size:
     100% (ereditato dalla regola di .rv-input) vale davvero anche dentro flex
     e grid;
   - min-block-size tiene i 48px del campo di testo (bersaglio 44 + respiro):
     WebKit non lo ricava dal contenuto;
   - ::-webkit-date-and-time-value allinea il valore al testo, non al centro;
     ::-webkit-datetime-edit toglie l'aria interna che sfalsa la linea di base.
   L'icona del calendario resta quella nativa: segue color-scheme, che i temi
   di tokens.css impostano su :root. */
.rv-input--date {
  min-inline-size: 0;
  min-block-size: calc(var(--rv-tap) + var(--rv-space-1));
  -webkit-appearance: none;
  appearance: none;
}

.rv-input--date::-webkit-date-and-time-value {
  text-align: start;
  min-block-size: 1.5em;
}

.rv-input--date::-webkit-datetime-edit {
  padding: 0;
}

/* Il campo si allunga col testo dove il browser lo sa fare */
.rv-textarea {
  min-block-size: 128px;
  max-block-size: 320px;
  padding-block: var(--rv-space-3);
  resize: vertical;
  field-sizing: content;
}

/* Select: altezza costruita col padding, così il testo è centrato anche in Safari */
.rv-select {
  position: relative;
  display: block;
}

.rv-select__control {
  min-block-size: 0;
  padding-block: 11px;             /* 11 + 24 + 11 + bordi = 48px */
  padding-inline-end: calc(var(--rv-space-4) + 20px + var(--rv-space-2));
  line-height: 24px;
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
  cursor: pointer;
}

/* Nessuna scelta ancora: <option value="" disabled selected> + required */
.rv-select__control:invalid {
  color: var(--rv-text-muted);
}

.rv-select__control option {
  color: var(--rv-text);
  background-color: var(--rv-surface-raised);
}

.rv-select__icon {
  --rv-icon-size: 20px;
  position: absolute;
  inset-block-start: 50%;
  inset-inline-end: var(--rv-space-3);
  translate: 0 -50%;
  color: var(--rv-text-secondary);
  pointer-events: none;
}

/* Prezzo: il riquadro è il contenitore, l'input dentro è nudo */
.rv-price {
  display: flex;
  align-items: center;
  gap: var(--rv-space-2);
}

.rv-price__input {
  flex: 1 1 auto;
  min-inline-size: 0;
  inline-size: 100%;
  block-size: calc(var(--rv-tap) + var(--rv-space-1) - 2 * var(--rv-border-width));
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  color: inherit;
  font-size: var(--rv-fs-body-lg);
  font-weight: var(--rv-fw-semibold);
  font-variant-numeric: tabular-nums lining-nums;
  -webkit-appearance: none;
  appearance: textfield;
}

/* L'anello lo disegna il riquadro .rv-price; qui resta solo per il contrasto elevato */
.rv-price__input:focus-visible {
  outline: var(--rv-focus-width) solid transparent;
}

.rv-price__input::-webkit-inner-spin-button,
.rv-price__input::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.rv-price__prefix,
.rv-price__suffix {
  flex: none;
  font-size: var(--rv-fs-body-lg);
  color: var(--rv-text-muted);
  white-space: nowrap;
  user-select: none;
}

/* Hover: il bordo si scurisce appena */
@media (hover: hover) {
  .rv-input:hover,
  .rv-textarea:hover,
  .rv-select__control:hover,
  .rv-price:hover {
    border-color: var(--rv-text-muted);
  }
}

/* Focus (la variante con :has() è una regola a sé, vedi convenzioni) */
.rv-input:focus-visible,
.rv-textarea:focus-visible,
.rv-select__control:focus-visible,
.rv-input.is-focus,
.rv-textarea.is-focus,
.rv-select__control.is-focus,
.rv-price.is-focus {
  border-color: var(--rv-focus-ring);
  box-shadow:
    0 0 0 var(--rv-border-width) var(--rv-focus-ring),
    0 0 0 4px var(--rv-primary-soft);
  outline: var(--rv-focus-width) solid transparent;
}

.rv-price:has(> .rv-price__input:focus-visible) {
  border-color: var(--rv-focus-ring);
  box-shadow:
    0 0 0 var(--rv-border-width) var(--rv-focus-ring),
    0 0 0 4px var(--rv-primary-soft);
  outline: var(--rv-focus-width) solid transparent;
}

/* Errore */
.rv-input[aria-invalid="true"],
.rv-textarea[aria-invalid="true"],
.rv-select__control[aria-invalid="true"] {
  border-color: var(--rv-danger);
}

.rv-price:has(> [aria-invalid="true"]) {
  border-color: var(--rv-danger);
}

.rv-input[aria-invalid="true"]:focus-visible,
.rv-textarea[aria-invalid="true"]:focus-visible,
.rv-select__control[aria-invalid="true"]:focus-visible,
.rv-input[aria-invalid="true"].is-focus {
  border-color: var(--rv-danger);
  box-shadow:
    0 0 0 var(--rv-border-width) var(--rv-danger),
    0 0 0 4px var(--rv-danger-soft);
}

.rv-price:has(> [aria-invalid="true"]:focus-visible),
.rv-price.is-focus:has(> [aria-invalid="true"]) {
  border-color: var(--rv-danger);
  box-shadow:
    0 0 0 var(--rv-border-width) var(--rv-danger),
    0 0 0 4px var(--rv-danger-soft);
}

/* Sola lettura: incassato, testo pieno */
.rv-input[readonly],
.rv-textarea[readonly] {
  border-color: var(--rv-border);
  background-color: var(--rv-surface-sunken);
}

/* Disabilitato: incassato, testo tenue */
.rv-input:disabled,
.rv-textarea:disabled,
.rv-select__control:disabled {
  border-color: var(--rv-border);
  background-color: var(--rv-surface-sunken);
  color: var(--rv-text-muted);
  box-shadow: none;
  cursor: default;
}

.rv-price:has(> :disabled) {
  border-color: var(--rv-border);
  background-color: var(--rv-surface-sunken);
  color: var(--rv-text-muted);
  box-shadow: none;
  cursor: default;
}

.rv-input:disabled,
.rv-textarea:disabled,
.rv-price__input:disabled {
  -webkit-text-fill-color: var(--rv-text-muted);  /* Safari schiarisce di suo il testo */
  opacity: 1;
}

.rv-select__control:disabled + .rv-select__icon {
  color: var(--rv-text-muted);
}

/* Compilazione automatica: niente fondo giallo/azzurro del browser */
.rv-input:-webkit-autofill,
.rv-price__input:-webkit-autofill {
  -webkit-text-fill-color: var(--rv-text);
  box-shadow: inset 0 0 0 100px var(--rv-surface);
}

.rv-input:-webkit-autofill:focus-visible {
  box-shadow:
    inset 0 0 0 100px var(--rv-surface),
    0 0 0 var(--rv-border-width) var(--rv-focus-ring),
    0 0 0 4px var(--rv-primary-soft);
}


/* --------------------------------------------------------------------------
   3. .rv-search — barra di ricerca
   Grande («hero»): sollevata, label breve visibile sopra l'esempio,
   bottone interno «Найти →».

   <form class="rv-search" role="search" aria-label="Специалисты" action="…">
     <label class="rv-search__field">
       <span class="rv-search__label"><span class="rv-icon rv-icon--search" aria-hidden="true"></span>Что вам нужно?</span>
       <input class="rv-search__input" type="search" name="q" placeholder="Например, сантехник" enterkeyhint="search">
     </label>
     <button class="rv-search__clear" type="button" aria-label="Очистить"><span class="rv-icon rv-icon--close rv-icon--sm" aria-hidden="true"></span></button>
     <button class="rv-search__submit" type="submit">Найти<span class="rv-icon rv-icon--arrow-right" aria-hidden="true"></span></button>
   </form>
   <p class="rv-search-hint"><span class="rv-icon rv-icon--sparkle" aria-hidden="true"></span>Умный поиск понимает ваш запрос своими словами</p>

   Compatta (barre sticky, liste): .rv-search--compact, lente all'inizio.
   <form class="rv-search rv-search--compact" role="search" aria-label="Барахолка" action="…">
     <span class="rv-icon rv-icon--search rv-search__icon" aria-hidden="true"></span>
     <input class="rv-search__input" type="search" name="q" aria-label="Поиск по барахолке"
            placeholder="Поиск по барахолке" enterkeyhint="search">
     <button class="rv-search__clear" …></button>
     <button class="rv-search__action" type="button" aria-label="Фильтры"><span class="rv-icon rv-icon--sliders rv-icon--sm" aria-hidden="true"></span></button>
   </form>

   ECCEZIONE DOCUMENTATA alla regola «label sempre visibile» (solo qui).
   La ricerca compatta non ha un'etichetta visibile né un prefisso «Поиск»:
   si riconosce dalla lente, dal campo incassato e dal contesto (il titolo
   della schermata subito sopra, la barra sticky). Il placeholder dice DOVE
   si cerca; quando il campo è pieno, la lente e il titolo restano. In cambio:
   - l'input ha un nome accessibile esplicito e univoco nella pagina
     (aria-label="Поиск по барахолке"), mai affidato al solo placeholder;
   - ogni <form role="search"> ha un aria-label univoco (la sezione:
     «Барахолка», «Чаты»), così i punti di riferimento non si confondono;
   - vale solo per le ricerche: ogni altro campo ha la sua label visibile.

   Il bottone «очистить» compare da solo quando il campo non è vuoto (serve
   l'attributo placeholder). Ricerca in corso: aria-busy="true" sul submit.

   Esempio nel placeholder: BREVE, una o due parole dopo «Например,» (≤ ~24
   caratteri). A 360px il campo della ricerca grande è largo ~210px: l'esempio
   è testo secondario e usa il corpo 15 (il valore digitato resta a 16, soglia
   iOS); sotto i 375px la barra stringe gli spazi interni. Se non basta, il
   testo finisce con i puntini, mai tagliato a metà lettera.
   -------------------------------------------------------------------------- */
.rv-search {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--rv-space-3);
  min-block-size: calc(var(--rv-tap) + var(--rv-space-5));
  margin: 0;
  padding: var(--rv-space-2);
  padding-inline-start: var(--rv-space-4);
  border-radius: var(--rv-radius-lg);
  background-color: var(--rv-surface-raised);
  color: var(--rv-text);
  box-shadow: var(--rv-shadow-lg);
  transition: box-shadow var(--rv-dur-fast) var(--rv-ease-out);
}

.rv-search__field {
  flex: 1 1 auto;
  align-self: stretch;
  display: grid;
  align-content: center;
  gap: var(--rv-space-0-5);
  min-inline-size: 0;
  cursor: text;
}

.rv-search__label {
  display: flex;
  align-items: center;
  gap: var(--rv-space-1-5);
  min-inline-size: 0;
  font-size: var(--rv-fs-caption);
  font-weight: var(--rv-fw-semibold);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text);
}

.rv-search__label > .rv-icon {
  --rv-icon-size: 16px;
  color: var(--rv-text-secondary);
}

.rv-search__icon {
  color: var(--rv-text-secondary);
}

.rv-search__input {
  flex: 1 1 auto;
  inline-size: 100%;
  min-inline-size: 0;
  block-size: 24px;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  color: var(--rv-text);
  font-family: var(--rv-font-sans);
  font-size: 16px;                 /* soglia iOS */
  line-height: 24px;
  text-overflow: ellipsis;
  outline: none;                   /* il focus lo disegna la barra intera */
  -webkit-appearance: none;
  appearance: none;
}

/* Grande: l'esempio sotto la label è secondario, un gradino sotto il testo
   digitato. Compatta: il placeholder fa da nome visibile, resta a 16px. */
.rv-search__input::placeholder {
  font-size: var(--rv-fs-body);
}

.rv-search--compact > .rv-search__input::placeholder {
  font-size: inherit;
}

.rv-search__input::-webkit-search-decoration,
.rv-search__input::-webkit-search-cancel-button {
  -webkit-appearance: none;
  appearance: none;
}

.rv-search__submit {
  position: relative;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--rv-space-1);
  min-block-size: calc(var(--rv-tap) + var(--rv-space-1));
  padding-inline: var(--rv-space-4) var(--rv-space-3);  /* la freccia ha già aria a destra */
  border-radius: var(--rv-radius-sm);
  background-color: var(--rv-primary);
  color: var(--rv-text-on-primary);
  font-size: var(--rv-fs-body);
  font-weight: var(--rv-fw-semibold);
  line-height: 1.2;
  white-space: nowrap;
  transition:
    background-color var(--rv-dur-fast) var(--rv-ease-out),
    transform var(--rv-dur-fast) var(--rv-ease-out);
}

.rv-search__submit > .rv-icon {
  --rv-icon-size: 18px;
  transition: translate var(--rv-dur-base) var(--rv-ease-out);
}

.rv-search__clear,
.rv-search__action {
  position: relative;
  flex: none;
  display: grid;
  place-items: center;
  inline-size: var(--rv-tap);
  block-size: var(--rv-tap);
  border-radius: var(--rv-radius-pill);
  color: var(--rv-text-muted);
  transition: color var(--rv-dur-fast) var(--rv-ease-out), background-color var(--rv-dur-fast) var(--rv-ease-out);
}

.rv-search__clear:focus-visible,
.rv-search__action:focus-visible {
  outline-offset: -4px;            /* l'anello resta dentro la barra */
}

.rv-search:not(.rv-search--compact) > .rv-search__clear {
  margin-inline-end: calc(-1 * var(--rv-space-2));
}

/* Campo vuoto: niente «очистить» */
.rv-search:has(.rv-search__input:placeholder-shown) > .rv-search__clear {
  display: none;
}

@media (hover: hover) {
  .rv-search__submit:hover {
    background-color: var(--rv-primary-hover);
  }

  .rv-search__submit:hover > .rv-icon {
    translate: 2px 0;
  }

  .rv-search__clear:hover,
  .rv-search__action:hover {
    color: var(--rv-text);
  }
}

.rv-search__submit:active {
  background-color: var(--rv-primary-pressed);
  transform: scale(var(--rv-press-scale));
}

.rv-search__clear:active,
.rv-search__action:active {
  color: var(--rv-text);
  transform: scale(var(--rv-press-scale));
}

/* Ricerca in corso: il testo resta (tiene la larghezza) ma sparisce */
.rv-search__submit[aria-busy="true"] {
  color: transparent;
  pointer-events: none;
}

/* Stesso disegno dello spinner di .rv-btn[aria-busy]: arco pieno su anello
   tenue. Il ripiego senza color-mix() è l'arco da solo (binario trasparente),
   dichiarato fuori da @supports: si vede sempre girare. */
.rv-search__submit[aria-busy="true"]::after {
  content: "";
  position: absolute;
  inset: 0;
  inline-size: 18px;
  block-size: 18px;
  margin: auto;
  border: 2px solid transparent;
  border-block-start-color: var(--rv-text-on-primary);
  border-radius: var(--rv-radius-pill);
  animation: rv-forms-spin 700ms linear infinite;
}

@supports (color: color-mix(in srgb, red, blue)) {
  .rv-search__submit[aria-busy="true"]::after {
    border-color: color-mix(in srgb, var(--rv-text-on-primary) 28%, transparent);
    border-block-start-color: var(--rv-text-on-primary);
  }
}

/* Schermi stretti (360 e 320): spazi interni più corti, ~16px in più al campo */
@media (max-width: 374px) {
  .rv-search {
    gap: var(--rv-space-2);
    padding-inline-start: var(--rv-space-3);
  }

  .rv-search__submit {
    padding-inline: var(--rv-space-3) var(--rv-space-2);
  }
}

/* Focus sulla barra intera */
.rv-search.is-focus {
  box-shadow:
    0 0 0 var(--rv-focus-width) var(--rv-focus-ring),
    0 0 0 5px var(--rv-primary-soft),
    var(--rv-shadow-lg);
}

.rv-search:has(.rv-search__input:focus-visible) {
  box-shadow:
    0 0 0 var(--rv-focus-width) var(--rv-focus-ring),
    0 0 0 5px var(--rv-primary-soft),
    var(--rv-shadow-lg);
}

/* Compatta: incassata, alta 44px, per barre sticky e liste */
.rv-search--compact {
  gap: var(--rv-space-2);
  min-block-size: var(--rv-tap);
  padding: 0;
  padding-inline-start: var(--rv-space-3);
  border-radius: var(--rv-radius-md);
  background-color: var(--rv-surface-sunken);
  box-shadow: inset 0 0 0 var(--rv-border-width) var(--rv-border);
}

.rv-search--compact > .rv-search__icon {
  --rv-icon-size: 20px;
  color: var(--rv-text-muted);
}

.rv-search--compact > .rv-search__input {
  block-size: var(--rv-tap);
}

.rv-search--compact > .rv-search__clear + .rv-search__action,
.rv-search--compact > .rv-search__input + .rv-search__action {
  margin-inline-start: calc(-1 * var(--rv-space-2));
}

/* Filo verticale prima dell'azione (es. filtri) */
.rv-search__action::before {
  content: "";
  position: absolute;
  inset-block: var(--rv-space-3);
  inset-inline-start: 0;
  border-inline-start: var(--rv-border-width) solid var(--rv-border);
}

.rv-search__action[aria-pressed="true"],
.rv-search__action[aria-expanded="true"] {
  color: var(--rv-primary);
}

/* Filtri applicati: oltre al verde, un punto sull'icona (forma, non solo colore) */
.rv-search__action[aria-pressed="true"]::after {
  content: "";
  position: absolute;
  inset-block-start: 9px;
  inset-inline-end: 8px;
  inline-size: 7px;
  block-size: 7px;
  border-radius: var(--rv-radius-pill);
  background-color: var(--rv-primary);
  box-shadow: 0 0 0 2px var(--rv-surface-sunken);
}

.rv-search--compact.is-focus {
  background-color: var(--rv-surface);
  box-shadow:
    inset 0 0 0 var(--rv-border-width) var(--rv-focus-ring),
    0 0 0 var(--rv-border-width) var(--rv-focus-ring),
    0 0 0 4px var(--rv-primary-soft);
}

.rv-search--compact:has(.rv-search__input:focus-visible) {
  background-color: var(--rv-surface);
  box-shadow:
    inset 0 0 0 var(--rv-border-width) var(--rv-focus-ring),
    0 0 0 var(--rv-border-width) var(--rv-focus-ring),
    0 0 0 4px var(--rv-primary-soft);
}


/* --------------------------------------------------------------------------
   .rv-search-hint — riga di aiuto sotto la ricerca, centrata, con scintilla

   <p class="rv-search-hint"><span class="rv-icon rv-icon--sparkle" aria-hidden="true"></span>Умный поиск понимает ваш запрос своими словами</p>
   -------------------------------------------------------------------------- */
.rv-search-hint {
  margin: 0;
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text-muted);
  text-align: center;
  text-wrap: balance;
}

.rv-search-hint > .rv-icon {
  --rv-icon-size: 16px;
  margin-inline-end: var(--rv-space-1-5);
  vertical-align: -3px;
  color: var(--rv-accent);         /* dettaglio ottone: decorativo, il testo è accanto */
}


/* --------------------------------------------------------------------------
   .rv-interpretation — «Понял так»: cosa ha capito l'умный поиск
   Sopra i risultati di una ricerca interpretata: scintilla, frase di
   riepilogo (quella del server) e i filtri capiti come chip rimovibili, così
   chi cerca vede e corregge l'interpretazione.

   <div class="rv-interpretation">
     <span class="rv-interpretation__icon"><span class="rv-icon rv-icon--sparkle" aria-hidden="true"></span></span>
     <div class="rv-interpretation__body">
       <p class="rv-interpretation__text"><strong class="rv-interpretation__lead">Понял так:</strong> ищу маникюр на&nbsp;дому · Бордигера</p>
       <ul class="rv-interpretation__chips" aria-label="Условия поиска">
         <li><button class="rv-chip" type="button" aria-label="Убрать условие: Красота">
           <span class="rv-icon rv-icon--lotus rv-chip__icon" aria-hidden="true"></span>Красота
           <span class="rv-icon rv-icon--close rv-chip__icon" aria-hidden="true"></span></button></li>
         <li><button class="rv-chip" …>…map-pin… Бордигера …close…</button></li>
         <li><button class="rv-chip" …>маникюр на дому …close…</button></li>
       </ul>
     </div>
   </div>

   Condizione allargata dal server, nello stesso riquadro (sopra le chip):
     <p class="rv-interpretation__note"><span class="rv-icon rv-icon--info" aria-hidden="true"></span>В&nbsp;Бордигере никого не&nbsp;нашли&nbsp;— показываю всех по&nbsp;всей Ривьере</p>
   Solo l'avviso, senza interpretazione (ricerca per parole con zona vuota):
   .rv-interpretation--relaxed, icona info al posto della scintilla, niente chip
   o solo quelle ancora attive:
     <div class="rv-interpretation rv-interpretation--relaxed" role="status">
       <span class="rv-interpretation__icon"><span class="rv-icon rv-icon--info" aria-hidden="true"></span></span>
       <div class="rv-interpretation__body">
         <p class="rv-interpretation__text"><strong class="rv-interpretation__lead">В&nbsp;Бордигере никого не&nbsp;нашли.</strong> Показываю сантехников из&nbsp;соседних городов.</p>
       </div>
     </div>
   Regole:
   - la chip è un <button> che toglie il filtro: aria-label «Убрать условие: …»,
     croce finale come __icon; valori lunghi in <span class="rv-chip__label">;
   - dopo la rimozione il focus va alla chip successiva (o al campo di ricerca);
   - significato non solo nel colore: l'avviso allargato ha icona e frase;
   - role="status" solo se il riquadro compare o cambia dopo una ricerca
     (una volta, non a ogni tasto: la ricerca aspetta la pausa di chi scrive).
   Colori: scintilla in ottone decorativo (--rv-accent, ≥3:1), testo
   --rv-text-secondary con l'attacco in --rv-text; avviso su --rv-warning-soft
   con icona --rv-warning (AA).
   -------------------------------------------------------------------------- */
.rv-interpretation {
  --rv-interpretation-tone: var(--rv-accent);
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  column-gap: var(--rv-space-3);
  padding: var(--rv-space-3) var(--rv-space-4);
  border-radius: var(--rv-radius-lg);
  background-color: var(--rv-surface);
  box-shadow: var(--rv-shadow-xs);
  color: var(--rv-text);
}

/* Icona da 20px sulla prima riga del testo (14 × 1,45 ≈ 20px) */
.rv-interpretation__icon {
  --rv-icon-size: 20px;
  display: flex;
  color: var(--rv-interpretation-tone);
}

.rv-interpretation__body {
  display: grid;
  gap: var(--rv-space-3);
  min-inline-size: 0;
}

.rv-interpretation__text {
  margin: 0;
  font-size: var(--rv-fs-callout);
  line-height: var(--rv-lh-callout);
  color: var(--rv-text-secondary);
  text-wrap: pretty;
  overflow-wrap: break-word;
}

.rv-interpretation__lead {
  font-weight: var(--rv-fw-semibold);
  color: var(--rv-text);
}

/* Avviso dentro il riquadro: la ricerca è stata allargata */
.rv-interpretation__note {
  --rv-icon-size: 18px;
  display: flex;
  align-items: flex-start;
  gap: var(--rv-space-2);
  margin: 0;
  padding: var(--rv-space-2) var(--rv-space-3);
  border-radius: var(--rv-radius-sm);
  background-color: var(--rv-warning-soft);
  font-size: var(--rv-fs-callout);
  line-height: var(--rv-lh-callout);
  color: var(--rv-text);
  text-wrap: pretty;
}

.rv-interpretation__note > .rv-icon {
  margin-block-start: 1px;          /* centrata sulla prima riga (≈20px) */
  color: var(--rv-warning);
}

/* Chip dei filtri capiti: vanno a capo; 8px fra le righe = aree di tocco
   da 44px che si toccano senza sovrapporsi */
.rv-interpretation__chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--rv-space-2);
  min-inline-size: 0;
  margin: 0;
  padding: 0;
  list-style: none;
}

.rv-interpretation__chips > li {
  display: flex;
  min-inline-size: 0;
  max-inline-size: 100%;
}

/* Solo l'avviso: tutto il riquadro sul tono tenue, senza ombra */
.rv-interpretation--relaxed {
  --rv-interpretation-tone: var(--rv-warning);
  background-color: var(--rv-warning-soft);
  box-shadow: none;
}

.rv-interpretation--relaxed .rv-interpretation__text {
  color: var(--rv-text);
}


/* --------------------------------------------------------------------------
   4. .rv-check e .rv-radio — casella e scelta singola
   Riga alta almeno 44px; l'input nativo, trasparente, copre 44×44 attorno
   al quadratino. Variante --card: tessera con bordo, tutta toccabile.

   <label class="rv-check">
     <input class="rv-check__input" type="checkbox" name="lang" value="ru">
     <span class="rv-check__box" aria-hidden="true"></span>
     <span class="rv-check__label">Русский<span class="rv-check__hint">Родной язык</span></span>
   </label>

   <label class="rv-radio rv-radio--card">
     <input class="rv-radio__input" type="radio" name="contact" value="tg" checked>
     <span class="rv-radio__box" aria-hidden="true"></span>
     <span class="rv-radio__label">Telegram<span class="rv-radio__hint">Отвечаю в течение часа</span></span>
   </label>

   Gruppo: <fieldset class="rv-field"><legend class="rv-field__label">…</legend><div class="rv-field__options">…</div></fieldset>
   Stati: :checked, :indeterminate (solo check, da JS), :disabled, aria-invalid="true".
   Con un suggerimento lungo, meglio aria-labelledby + aria-describedby sull'input.
   -------------------------------------------------------------------------- */
.rv-check,
.rv-radio {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  column-gap: var(--rv-space-3);
  min-block-size: var(--rv-tap);
  padding-block: calc((var(--rv-tap) - 22px) / 2);
  font-size: var(--rv-fs-body);
  line-height: 22px;
  color: var(--rv-text);
  cursor: pointer;
  touch-action: manipulation;
}

.rv-check__input,
.rv-radio__input {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: calc((22px - var(--rv-tap)) / 2);
  inline-size: var(--rv-tap);
  block-size: var(--rv-tap);
  margin: 0;
  opacity: 0;
  cursor: inherit;
}

.rv-check__box,
.rv-radio__box {
  grid-column: 1;
  grid-row: 1;
  display: grid;
  place-items: center;
  inline-size: 22px;
  block-size: 22px;
  border: 1.5px solid var(--rv-border-strong);
  background-color: var(--rv-surface);
  color: var(--rv-text-on-primary);
  transition:
    background-color var(--rv-dur-fast) var(--rv-ease-out),
    border-color var(--rv-dur-fast) var(--rv-ease-out),
    transform var(--rv-dur-fast) var(--rv-ease-out);
}

.rv-check__box {
  border-radius: var(--rv-radius-xs);
}

.rv-radio__box {
  border-radius: var(--rv-radius-pill);
}

/* Segno di spunta: tratto spesso e arrotondato come le icone del set */
.rv-check__box::after {
  content: "";
  inline-size: 16px;
  block-size: 16px;
  background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5.5 12.5l4.25 4.25L18.5 7.5'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5.5 12.5l4.25 4.25L18.5 7.5'/%3E%3C/svg%3E") center / contain no-repeat;
  opacity: 0;
  transform: scale(0.6);
  transition:
    opacity var(--rv-dur-fast) var(--rv-ease-out),
    transform var(--rv-dur-base) var(--rv-ease-out);
}

.rv-radio__box::after {
  content: "";
  inline-size: 8px;
  block-size: 8px;
  border-radius: var(--rv-radius-pill);
  background-color: currentColor;
  opacity: 0;
  transform: scale(0.4);
  transition:
    opacity var(--rv-dur-fast) var(--rv-ease-out),
    transform var(--rv-dur-base) var(--rv-ease-out);
}

.rv-check__label,
.rv-radio__label {
  grid-column: 2;
  grid-row: 1;
  min-inline-size: 0;
  overflow-wrap: break-word;
}

.rv-check__hint,
.rv-radio__hint {
  display: block;
  margin-block-start: var(--rv-space-0-5);
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text-muted);
}

@media (hover: hover) {
  .rv-check:hover > .rv-check__box,
  .rv-radio:hover > .rv-radio__box {
    border-color: var(--rv-text-muted);
  }
}

.rv-check:active > .rv-check__box,
.rv-radio:active > .rv-radio__box {
  transform: scale(var(--rv-press-scale));
}

/* Selezionato: pieno pino, segno nel colore «su primario» */
.rv-check__input:checked + .rv-check__box,
.rv-check__input:indeterminate + .rv-check__box,
.rv-radio__input:checked + .rv-radio__box {
  border-color: var(--rv-primary);
  background-color: var(--rv-primary);
}

.rv-check__input:checked + .rv-check__box::after,
.rv-check__input:indeterminate + .rv-check__box::after,
.rv-radio__input:checked + .rv-radio__box::after {
  opacity: 1;
  transform: none;
}

/* Indeterminato: una lineetta al posto della spunta */
.rv-check__input:indeterminate + .rv-check__box::after {
  inline-size: 10px;
  block-size: 2px;
  border-radius: 1px;
  -webkit-mask: none;
  mask: none;
}

.rv-check__input:focus-visible + .rv-check__box,
.rv-radio__input:focus-visible + .rv-radio__box {
  outline: var(--rv-focus-width) solid var(--rv-focus-ring);
  outline-offset: var(--rv-focus-offset);
}

.rv-check__input[aria-invalid="true"]:not(:checked) + .rv-check__box,
.rv-radio__input[aria-invalid="true"]:not(:checked) + .rv-radio__box {
  border-color: var(--rv-danger);
  background-color: var(--rv-danger-soft);
}

.rv-check__input:disabled,
.rv-radio__input:disabled {
  cursor: default;
}

.rv-check__input:disabled ~ .rv-check__label,
.rv-radio__input:disabled ~ .rv-radio__label {
  color: var(--rv-text-muted);
  cursor: default;
}

.rv-check__input:disabled + .rv-check__box,
.rv-radio__input:disabled + .rv-radio__box {
  opacity: 0.45;
  transform: none;
}

/* Tessera: tutta la superficie è il bersaglio */
.rv-check--card,
.rv-radio--card {
  padding: var(--rv-space-3) var(--rv-space-4);
  border-radius: var(--rv-radius-md);
  background-color: var(--rv-surface);
  box-shadow: inset 0 0 0 var(--rv-border-width) var(--rv-border);
  transition:
    box-shadow var(--rv-dur-fast) var(--rv-ease-out),
    background-color var(--rv-dur-fast) var(--rv-ease-out);
}

.rv-check--card > .rv-check__input,
.rv-radio--card > .rv-radio__input {
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
}

.rv-check--card + .rv-check--card,
.rv-radio--card + .rv-radio--card {
  margin-block-start: var(--rv-space-2);
}

@media (hover: hover) {
  .rv-check--card:hover,
  .rv-radio--card:hover {
    box-shadow: inset 0 0 0 var(--rv-border-width) var(--rv-border-strong);
  }
}

.rv-check--card:has(> :checked),
.rv-radio--card:has(> :checked) {
  box-shadow: inset 0 0 0 2px var(--rv-primary);
}

.rv-check--card:has(> :disabled),
.rv-radio--card:has(> :disabled) {
  background-color: var(--rv-surface-sunken);
  box-shadow: inset 0 0 0 var(--rv-border-width) var(--rv-border);
}


/* --------------------------------------------------------------------------
   5. .rv-switch — interruttore in stile iOS nei colori Riviera
   Testo a sinistra, binario a destra; tutta la riga è toccabile.
   Spento: binario border-strong. Acceso: pino. Il pomello usa il colore
   «su primario» (crema di giorno, carbone di notte): contrasto ≥3:1 su
   entrambi i binari in entrambi i temi. Lo stato si legge anche dalla posizione.

   <label class="rv-switch">
     <input class="rv-switch__input" type="checkbox" role="switch" checked aria-labelledby="ph-l" aria-describedby="ph-h">
     <span class="rv-switch__track" aria-hidden="true"></span>
     <span class="rv-switch__label"><span id="ph-l">Показывать телефон</span><span class="rv-switch__hint" id="ph-h">Номер увидят только участники сообщества</span></span>
   </label>
   -------------------------------------------------------------------------- */
.rv-switch {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  column-gap: var(--rv-space-4);
  min-block-size: var(--rv-tap);
  padding-block: var(--rv-space-2);
  font-size: var(--rv-fs-body);
  line-height: var(--rv-lh-body);
  color: var(--rv-text);
  cursor: pointer;
  touch-action: manipulation;
}

.rv-switch__input {
  position: absolute;
  inset-block-start: 50%;
  inset-inline-end: -2px;
  inline-size: 52px;
  block-size: var(--rv-tap);
  margin: 0;
  opacity: 0;
  translate: 0 -50%;
  cursor: inherit;
}

.rv-switch__track {
  --_thumb: 22px;                  /* si allarga alla pressione, come su iOS */
  grid-column: 2;
  grid-row: 1;
  position: relative;
  inline-size: 48px;
  block-size: 28px;
  border-radius: var(--rv-radius-pill);
  background-color: var(--rv-border-strong);
  transition: background-color var(--rv-dur-base) var(--rv-ease-out);
}

.rv-switch__track::after {
  content: "";
  position: absolute;
  inset-block-start: 3px;
  inset-inline-start: 3px;
  inline-size: var(--_thumb);
  block-size: 22px;
  border-radius: var(--rv-radius-pill);
  background-color: var(--rv-text-on-primary);
  box-shadow: var(--rv-shadow-xs);
  transition:
    transform var(--rv-dur-base) var(--rv-ease-out),
    inline-size var(--rv-dur-fast) var(--rv-ease-out);
}

.rv-switch__label {
  grid-column: 1;
  grid-row: 1;
  min-inline-size: 0;
  overflow-wrap: break-word;
}

.rv-switch__hint {
  display: block;
  margin-block-start: var(--rv-space-0-5);
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text-muted);
}

@media (hover: hover) {
  .rv-switch:hover > .rv-switch__track {
    background-color: var(--rv-text-muted);
  }

  .rv-switch:hover > .rv-switch__input:checked + .rv-switch__track {
    background-color: var(--rv-primary-hover);
  }
}

.rv-switch:active > .rv-switch__track {
  --_thumb: 26px;
}

.rv-switch__input:checked + .rv-switch__track {
  background-color: var(--rv-primary);
}

.rv-switch__input:checked + .rv-switch__track::after {
  transform: translateX(calc(48px - 6px - var(--_thumb)));
}

.rv-switch__input:focus-visible + .rv-switch__track {
  outline: var(--rv-focus-width) solid var(--rv-focus-ring);
  outline-offset: var(--rv-focus-offset);
}

.rv-switch__input:disabled {
  cursor: default;
}

.rv-switch__input:disabled ~ .rv-switch__label {
  color: var(--rv-text-muted);
  cursor: default;
}

.rv-switch__input:disabled + .rv-switch__track {
  --_thumb: 22px;
  opacity: 0.45;
}


/* --------------------------------------------------------------------------
   6. .rv-segmented — controllo segmentato (2–4 voci brevi, una sola scelta)
   Binario incassato, voce scelta sollevata. Per filtri numerosi o lunghi
   usa invece le chip (.rv-chip).

   <div class="rv-segmented" role="radiogroup" aria-label="Раздел">
     <label class="rv-segmented__item">
       <input class="rv-segmented__input" type="radio" name="sec" value="all" checked>
       <span class="rv-segmented__label">Все</span>
     </label> …
   </div>

   Come schede: <button class="rv-segmented__item" role="tab" aria-selected="true">…</button>
   Con icona: <span class="rv-icon rv-icon--grid" aria-hidden="true"></span> prima della label.
   -------------------------------------------------------------------------- */
.rv-segmented {
  display: flex;
  gap: var(--rv-space-0-5);
  padding: var(--rv-space-1);
  border-radius: var(--rv-radius-md);
  background-color: var(--rv-surface-sunken);
  box-shadow: inset 0 0 0 var(--rv-border-width) var(--rv-border);
}

.rv-segmented__item {
  position: relative;
  isolation: isolate;                  /* la pill (z-index -1) resta dentro la voce */
  flex: 1 1 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--rv-space-1-5);
  min-inline-size: 0;
  min-block-size: calc(var(--rv-tap) - var(--rv-space-1));
  padding-inline: var(--rv-space-2);   /* 4 voci stanno intere anche a 360px */
  border-radius: var(--rv-radius-sm);   /* md del binario − 4px di padding: raggi concentrici */
  color: var(--rv-text-secondary);
  font-size: var(--rv-fs-callout);
  font-weight: var(--rv-fw-medium);
  line-height: 1.2;
  text-align: center;
  cursor: pointer;
  touch-action: manipulation;
  transition:
    background-color var(--rv-dur-fast) var(--rv-ease-out),
    color var(--rv-dur-fast) var(--rv-ease-out),
    box-shadow var(--rv-dur-fast) var(--rv-ease-out);
}

/* Bersaglio di 48px: si estende sul padding del binario */
.rv-segmented__item::before {
  content: "";
  position: absolute;
  inset-block: calc(-1 * var(--rv-space-1));
  inset-inline: 0;
}

.rv-segmented__input {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  margin: 0;
  opacity: 0;
  cursor: inherit;
}

.rv-segmented__label {
  min-inline-size: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rv-segmented__item > .rv-icon {
  --rv-icon-size: 18px;
}

@media (hover: hover) {
  .rv-segmented__item:hover {
    color: var(--rv-text);
  }
}

/* Voce scelta, due forme di markup e nessun :has() (vale in ogni WebView):
   - schede (role="tab") o role="radio": attributo sulla voce stessa;
   - radio nativi: l'input viene prima dell'etichetta, quindi basta il
     selettore fratello. La pill è il ::before dell'etichetta, posizionato
     sulla voce intera (la voce è il suo contenitore posizionato) e dietro al
     testo; esiste sempre e compare con l'opacità, così il cambio sfuma. */
.rv-segmented__item[aria-selected="true"],
.rv-segmented__item[aria-checked="true"] {
  background-color: var(--rv-surface-raised);
  color: var(--rv-text);
  font-weight: var(--rv-fw-semibold);
  box-shadow: var(--rv-shadow-xs);
}

.rv-segmented__input ~ .rv-segmented__label::before,
.rv-segmented__input ~ .rv-segmented__label::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--rv-radius-sm);
  pointer-events: none;
}

.rv-segmented__input ~ .rv-segmented__label::before {
  z-index: -1;
  background-color: var(--rv-surface-raised);
  box-shadow: var(--rv-shadow-xs);
  opacity: 0;
  transition: opacity var(--rv-dur-fast) var(--rv-ease-out);
}

.rv-segmented__input:checked ~ .rv-segmented__label::before {
  opacity: 1;
}

.rv-segmented__input:checked ~ * {
  color: var(--rv-text);
}

.rv-segmented__input:checked ~ .rv-segmented__label {
  font-weight: var(--rv-fw-semibold);
}

/* Focus: sulla voce (schede) o sull'::after dell'etichetta (radio) */
.rv-segmented__item:focus-visible,
.rv-segmented__input:focus-visible ~ .rv-segmented__label::after {
  outline: var(--rv-focus-width) solid var(--rv-focus-ring);
  outline-offset: 0;
}

.rv-segmented__item[aria-disabled="true"] {
  color: var(--rv-text-muted);
  opacity: 0.6;
  cursor: default;
}

.rv-segmented__input:disabled {
  cursor: default;
}

.rv-segmented__input:disabled ~ * {
  color: var(--rv-text-muted);
  opacity: 0.6;
}


/* --------------------------------------------------------------------------
   7. .rv-stepper — quantità con − e +
   Bottoni rotondi a filo (36px visibili, 44px di bersaglio), valore al centro
   in cifre tabellari. Al minimo/massimo il bottone relativo è disabled.

   <div class="rv-field rv-field--row">
     <div class="rv-field__text">
       <label class="rv-field__label" for="rooms">Комнаты</label>
       <p class="rv-field__hint">Без учёта кухни</p>
     </div>
     <div class="rv-stepper">
       <button class="rv-stepper__btn" type="button" aria-label="Меньше" aria-controls="rooms"><span class="rv-icon rv-icon--minus" aria-hidden="true"></span></button>
       <input class="rv-stepper__value" id="rooms" type="text" inputmode="numeric" value="2">
       <button class="rv-stepper__btn" type="button" aria-label="Больше" aria-controls="rooms"><span class="rv-icon rv-icon--plus" aria-hidden="true"></span></button>
     </div>
   </div>
   (il valore può essere anche <output class="rv-stepper__value">)
   -------------------------------------------------------------------------- */
.rv-stepper {
  display: inline-flex;
  align-items: center;
  gap: var(--rv-space-1);
}

.rv-stepper__btn {
  position: relative;
  flex: none;
  display: grid;
  place-items: center;
  inline-size: 36px;
  block-size: 36px;
  border-radius: var(--rv-radius-pill);
  background-color: var(--rv-surface);
  color: var(--rv-text);
  box-shadow: inset 0 0 0 var(--rv-border-width) var(--rv-border-control);  /* − e + identificano già il bottone */
  transition:
    background-color var(--rv-dur-fast) var(--rv-ease-out),
    box-shadow var(--rv-dur-fast) var(--rv-ease-out),
    transform var(--rv-dur-fast) var(--rv-ease-out);
}

.rv-stepper__btn::before {
  content: "";
  position: absolute;
  inset: calc((36px - var(--rv-tap)) / 2);
  border-radius: inherit;
}

.rv-stepper__btn > .rv-icon {
  --rv-icon-size: 20px;
}

@media (hover: hover) {
  .rv-stepper__btn:hover {
    background-color: var(--rv-surface-hover);
    box-shadow: inset 0 0 0 var(--rv-border-width) var(--rv-border-strong);
  }
}

.rv-stepper__btn:active {
  background-color: var(--rv-surface-hover);
  transform: scale(var(--rv-press-scale));
}

.rv-stepper__btn:disabled {
  background-color: transparent;
  color: var(--rv-text-muted);
  box-shadow: inset 0 0 0 var(--rv-border-width) var(--rv-border);
  opacity: 0.6;
  transform: none;
}

.rv-stepper__value {
  display: inline-block;
  inline-size: 48px;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: var(--rv-radius-sm);
  background: none;
  color: var(--rv-text);
  font-family: var(--rv-font-sans);
  font-size: var(--rv-fs-body-lg);
  font-weight: var(--rv-fw-semibold);
  font-variant-numeric: lining-nums proportional-nums;  /* numero singolo e centrato: le tabellari lo allargano */
  line-height: var(--rv-tap);
  text-align: center;
  -webkit-appearance: none;
  appearance: textfield;
}

.rv-stepper__value:focus-visible {
  outline-offset: 0;
}

/* Valore lungo: tre cifre, o un numero con l'unita' accanto («100 км»). I 48px del contatore normale
   bastano per «2» e per «12», non per questo: il testo in eccesso viene tagliato dentro l'input e sul
   telefono non c'e' modo di accorgersene. Modificatore e non larghezza nuova per tutti, perche' un
   contatore di stanze piu' largo e' un contatore peggiore: il numero smette di stare sotto il pollice. */
.rv-stepper--wide .rv-stepper__value {
  inline-size: 5.5ch;
  min-inline-size: 72px;
}


/* --------------------------------------------------------------------------
   7b. .rv-rating-input — voto a stelle (radio nativi)
   Cinque radio veri sotto le stelle: Tab entra nel gruppo (sulla stella
   scelta o sulla prima), ←/→ e ↑/↓ cambiano il voto, Spazio sceglie, il
   modulo invia il valore; nessuno script. Stelle da 28px in bersagli da 44px
   affiancati; la prima stella è a filo con l'etichetta. A destra la parola
   del voto scelto («Хорошо»): ogni stella ha la sua, si vede solo quella scelta.

   <fieldset class="rv-field">
     <legend class="rv-field__label">Оценка</legend>
     <div class="rv-rating-input">
       <label class="rv-rating-input__item">
         <input class="rv-rating-input__input" type="radio" name="rating" value="1" required aria-describedby="rating-hint">
         <span class="rv-rating-input__star" aria-hidden="true">
           <span class="rv-icon rv-icon--star rv-rating-input__off"></span>
           <span class="rv-icon rv-icon--star-fill rv-rating-input__on"></span>
         </span>
         <span class="rv-visually-hidden">1 из 5:</span>
         <span class="rv-rating-input__word">Плохо</span>
       </label>
       … value 2 «Так себе», 3 «Нормально», 4 «Хорошо», 5 «Отлично»
     </div>
     <p class="rv-field__hint" id="rating-hint">Нажмите на&nbsp;звезду: от&nbsp;одной до&nbsp;пяти</p>
   </fieldset>

   Nome di ogni radio = «4 из 5: Хорошо» (testo nascosto + parola). La parola
   delle stelle non scelte è trasparente e non riceve tocchi, ma resta nel
   nome accessibile: niente testo duplicato per i lettori di schermo.
   Stato scelto: la stella scelta è piena anche senza :has() (WebView sotto la
   baseline); dove :has() esiste si riempiono anche quelle prima di lei. La
   forma cambia (contorno → piena), non solo il colore; la parola lo ripete.
   Errore: aria-invalid="true" su ogni input + .rv-field__error collegato con
   aria-describedby: le stelle vuote diventano rosse, il testo dice cosa fare.
   Sola lettura: disabled su ogni input.
   Numero di stelle diverso da 5: --rv-rating-input-count sul contenitore.
   -------------------------------------------------------------------------- */
.rv-rating-input {
  --rv-rating-input-count: 5;
  --rv-rating-input-star: 28px;
  position: relative;               /* riferimento della parola del voto */
  display: flex;
  align-items: center;
  min-block-size: var(--rv-tap);
  /* la prima stella a filo con l'etichetta: il bersaglio ha 8px d'aria per lato */
  margin-inline-start: calc((var(--rv-rating-input-star) - var(--rv-tap)) / 2);
}

.rv-rating-input__item {
  display: grid;
  flex: none;
  place-items: center;
  inline-size: var(--rv-tap);
  block-size: var(--rv-tap);
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

/* Input e stella nella stessa cella della griglia: niente posizionamento,
   così la parola si posiziona sul contenitore */
.rv-rating-input__input,
.rv-rating-input__star {
  grid-area: 1 / 1;
}

.rv-rating-input__input {
  inline-size: 100%;
  block-size: 100%;
  margin: 0;
  opacity: 0;
  cursor: inherit;
}

.rv-rating-input__star {
  display: grid;
  place-items: center;
  inline-size: 100%;
  block-size: 100%;
  border-radius: var(--rv-radius-pill);
  color: var(--rv-border-strong);   /* contorno ≥3:1: la stella vuota porta un'informazione */
  pointer-events: none;
  transition:
    color var(--rv-dur-fast) var(--rv-ease-out),
    transform var(--rv-dur-fast) var(--rv-ease-out);
}

.rv-rating-input__star > .rv-icon {
  --rv-icon-size: var(--rv-rating-input-star);
  grid-area: 1 / 1;
}

.rv-rating-input__on {
  display: none;
}

/* Parola del voto: tutte nello stesso punto a destra delle stelle, visibile
   solo quella scelta */
.rv-rating-input__word {
  position: absolute;
  inset-block: 0;
  inset-inline-start: calc(var(--rv-rating-input-count) * var(--rv-tap) + var(--rv-space-1));
  display: flex;
  align-items: center;
  font-size: var(--rv-fs-body);
  font-weight: var(--rv-fw-medium);
  line-height: var(--rv-lh-body);
  color: var(--rv-text);
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
}

/* Hover: il contorno di una stella vuota sale di un gradino, come i bordi dei
   controlli. Niente fondo di hover: su --rv-surface-hover il contorno
   scenderebbe sotto 3:1 */
@media (hover: hover) {
  .rv-rating-input__item:hover > .rv-rating-input__input:not(:checked, :disabled) + .rv-rating-input__star {
    color: var(--rv-text-muted);
  }
}

.rv-rating-input__item:active > .rv-rating-input__star {
  transform: scale(var(--rv-press-scale));
}

/* Scelta (senza :has): la stella scelta piena e la sua parola */
.rv-rating-input__input:checked + .rv-rating-input__star {
  color: var(--rv-star);
}

.rv-rating-input__input:checked + .rv-rating-input__star > .rv-rating-input__off {
  display: none;
}

.rv-rating-input__input:checked + .rv-rating-input__star > .rv-rating-input__on {
  display: inline-block;
}

.rv-rating-input__input:checked ~ .rv-rating-input__word {
  opacity: 1;
}

/* Le stelle prima della scelta (regole a sé: :has() invalida la lista) */
.rv-rating-input__item:has(~ .rv-rating-input__item > .rv-rating-input__input:checked) > .rv-rating-input__star {
  color: var(--rv-star);
}

.rv-rating-input__item:has(~ .rv-rating-input__item > .rv-rating-input__input:checked) > .rv-rating-input__star > .rv-rating-input__off {
  display: none;
}

.rv-rating-input__item:has(~ .rv-rating-input__item > .rv-rating-input__input:checked) > .rv-rating-input__star > .rv-rating-input__on {
  display: inline-block;
}

/* Focus: anello dentro il bersaglio da 44px (i bersagli sono affiancati) */
.rv-rating-input__input:focus-visible + .rv-rating-input__star {
  outline: var(--rv-focus-width) solid var(--rv-focus-ring);
  outline-offset: calc(-1 * var(--rv-focus-width));
}

/* Errore: stelle vuote in rosso anche all'hover (il testo dell'errore dice cosa fare) */
.rv-rating-input__item > .rv-rating-input__input[aria-invalid="true"]:not(:checked) + .rv-rating-input__star {
  color: var(--rv-danger);
}

.rv-rating-input__input:disabled {
  cursor: default;
}

.rv-rating-input__input:disabled + .rv-rating-input__star {
  opacity: 0.45;
  transform: none;
}

.rv-rating-input__input:disabled ~ .rv-rating-input__word {
  color: var(--rv-text-muted);
}


/* --------------------------------------------------------------------------
   8. .rv-upload — aggiungi foto: griglia di anteprime quadrate + tessera
   tratteggiata. La prima foto può portare l'etichetta «Обложка».

   <div class="rv-upload" role="group" aria-labelledby="photos-l">
     <div class="rv-upload__item">
       <img class="rv-upload__img" src="…" alt="Кухня после ремонта">
       <span class="rv-upload__badge">Обложка</span>
       <button class="rv-upload__remove" type="button" aria-label="Удалить фото"><span class="rv-icon rv-icon--close" aria-hidden="true"></span></button>
     </div>
     <label class="rv-upload__add">
       <input class="rv-upload__input" type="file" accept="image/*" multiple>
       <span class="rv-icon rv-icon--camera" aria-hidden="true"></span>
       <span class="rv-upload__add-label">Добавить</span>
       <span class="rv-upload__add-meta">2 из 6</span>
     </label>
   </div>

   Caricamento: aria-busy="true" sull'item + <span class="rv-upload__progress" role="progressbar" aria-valuenow="64" aria-label="Загрузка" style="--rv-upload-progress: 64%"></span>
   Errore: .is-error sull'item + <button class="rv-upload__retry" type="button">Повторить</button>
   Nessuna foto: .rv-upload__add--wide (tessera larga). Trascinamento: .is-dragover.
   -------------------------------------------------------------------------- */
.rv-upload {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: var(--rv-space-2);
}

.rv-upload__item {
  position: relative;
  aspect-ratio: 1;
  border-radius: var(--rv-radius-md);
  background-color: var(--rv-surface-sunken);
}

.rv-upload__img {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  border-radius: inherit;
  object-fit: cover;
  filter: var(--rv-image-dim);
  transition: opacity var(--rv-dur-base) var(--rv-ease-out);
}

.rv-upload__badge {
  position: absolute;
  inset-block-end: var(--rv-space-1-5);
  inset-inline-start: var(--rv-space-1-5);
  padding: var(--rv-space-0-5) var(--rv-space-2);
  border-radius: var(--rv-radius-pill);
  background-color: var(--rv-surface-raised);
  color: var(--rv-text);
  font-size: var(--rv-fs-overline);
  font-weight: var(--rv-fw-semibold);
  line-height: 16px;
  box-shadow: var(--rv-shadow-xs);
}

/* --- .rv-upload__cover: scegliere la copertina ---
   Al posto di __badge quando la copertina si può cambiare: su ogni foto
   caricata una pill «Обложка» che è un radio (una copertina sola). Scelta =
   pill piena pino con spunta; le altre = pill chiara con cerchio vuoto
   (forma, non solo colore). Tutte le pill hanno la stessa misura: niente salti.

   <div class="rv-upload__item">
     <img class="rv-upload__img" src="…" alt="Ванная после ремонта">
     <label class="rv-upload__cover">
       <input class="rv-upload__cover-input" type="radio" name="cover" value="photo-1" checked>
       <span class="rv-upload__cover-mark" aria-hidden="true"><span class="rv-icon rv-icon--check"></span></span>
       <span class="rv-upload__cover-label">Обложка<span class="rv-visually-hidden">: фото 1</span></span>
     </label>
     <button class="rv-upload__remove" type="button" aria-label="Удалить фото 1">…</button>
   </div>

   Tutte le pill con lo stesso name: ←/→ spostano la copertina fra le foto, Tab
   entra nel gruppo una volta sola. Il nome accessibile dice quale foto
   («Обложка: фото 2»). Se l'ordine delle foto conta (la copertina va prima),
   al change la pagina sposta la foto in testa. Durante il caricamento e con
   errore la pill si nasconde: una foto non caricata non può essere copertina. */
.rv-upload__cover {
  position: absolute;
  inset-block-end: var(--rv-space-1-5);
  inset-inline-start: var(--rv-space-1-5);
  isolation: isolate;               /* il fondo della pill (z-index -1) resta dentro */
  display: inline-flex;
  align-items: center;
  gap: var(--rv-space-1);
  max-inline-size: calc(100% - 2 * var(--rv-space-1-5));
  min-block-size: 24px;
  padding-inline: 5px var(--rv-space-2);
  color: var(--rv-text);
  font-size: var(--rv-fs-overline);
  font-weight: var(--rv-fw-semibold);
  line-height: 16px;
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  transition: transform var(--rv-dur-fast) var(--rv-ease-out);
}

/* Radio nativo trasparente: la pill più 10px sopra e sotto (bersaglio 44px) */
.rv-upload__cover-input {
  position: absolute;
  inset: calc((24px - var(--rv-tap)) / 2) 0;
  inline-size: 100%;
  block-size: var(--rv-tap);
  margin: 0;
  opacity: 0;
  cursor: inherit;
}

/* Cerchio: vuoto con bordo ≥3:1, pieno con spunta quando scelta */
.rv-upload__cover-mark {
  --rv-icon-size: 10px;
  display: grid;
  flex: none;
  place-items: center;
  inline-size: 14px;
  block-size: 14px;
  border-radius: var(--rv-radius-pill);
  box-shadow: inset 0 0 0 1.5px var(--rv-border-strong);
  color: var(--rv-primary);
  pointer-events: none;
}

.rv-upload__cover-mark > .rv-icon {
  opacity: 0;
}

/* Fondo della pill: pseudo-elemento del cerchio, esteso a tutta la label,
   così segue lo stato del radio senza :has() */
.rv-upload__cover-mark::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: var(--rv-radius-pill);
  background-color: var(--rv-surface-raised);
  box-shadow: var(--rv-shadow-xs);
}

.rv-upload__cover-label {
  min-inline-size: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  pointer-events: none;
}

/* Anello di focus attorno alla pill (sul pseudo-elemento dell'etichetta) */
.rv-upload__cover-label::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--rv-radius-pill);
  pointer-events: none;
}

/* Hover: il cerchio vuoto sale di un gradino (su un fondo di hover il bordo
   --rv-border-strong scenderebbe sotto 3:1) */
@media (hover: hover) {
  .rv-upload__cover:hover > .rv-upload__cover-input:not(:checked) + .rv-upload__cover-mark {
    box-shadow: inset 0 0 0 1.5px var(--rv-text-muted);
  }
}

.rv-upload__cover:active {
  transform: scale(var(--rv-press-scale));
}

.rv-upload__cover-input:checked + .rv-upload__cover-mark {
  background-color: var(--rv-text-on-primary);
  box-shadow: none;
}

.rv-upload__cover-input:checked + .rv-upload__cover-mark > .rv-icon {
  opacity: 1;
}

.rv-upload__cover-input:checked + .rv-upload__cover-mark::before {
  background-color: var(--rv-primary);
  box-shadow: none;
}

.rv-upload__cover-input:checked ~ .rv-upload__cover-label {
  color: var(--rv-text-on-primary);
}

.rv-upload__cover-input:focus-visible ~ .rv-upload__cover-label::after {
  outline: var(--rv-focus-width) solid var(--rv-focus-ring);
  outline-offset: var(--rv-focus-offset);
}

.rv-upload__cover-input:disabled {
  cursor: default;
}

.rv-upload__cover-input:disabled ~ * {
  opacity: 0.6;
}

.rv-upload__item[aria-busy="true"] > .rv-upload__cover,
.rv-upload__item.is-error > .rv-upload__cover {
  display: none;
}

.rv-upload__remove {
  position: absolute;
  inset-block-start: var(--rv-space-1-5);
  inset-inline-end: var(--rv-space-1-5);
  display: grid;
  place-items: center;
  inline-size: 28px;
  block-size: 28px;
  border-radius: var(--rv-radius-pill);
  background-color: var(--rv-surface-raised);
  color: var(--rv-text);
  box-shadow: var(--rv-shadow-sm);
  transition: transform var(--rv-dur-fast) var(--rv-ease-out), color var(--rv-dur-fast) var(--rv-ease-out);
}

.rv-upload__remove::before {
  content: "";
  position: absolute;
  inset: calc((28px - var(--rv-tap)) / 2);
  border-radius: inherit;
}

.rv-upload__remove > .rv-icon {
  --rv-icon-size: 16px;
}

@media (hover: hover) {
  .rv-upload__remove:hover {
    color: var(--rv-danger);
  }
}

.rv-upload__remove:active {
  transform: scale(var(--rv-press-scale));
}

/* Caricamento in corso */
.rv-upload__item[aria-busy="true"] > .rv-upload__img {
  opacity: 0.45;
}

.rv-upload__progress {
  position: absolute;
  inset-inline: var(--rv-space-3);
  inset-block-end: var(--rv-space-3);
  block-size: 4px;
  border-radius: var(--rv-radius-pill);
  background-color: var(--rv-surface-raised);
  overflow: hidden;
}

.rv-upload__progress::before {
  content: "";
  display: block;
  inline-size: var(--rv-upload-progress, 0%);
  block-size: 100%;
  border-radius: inherit;
  background-color: var(--rv-primary);
  transition: inline-size var(--rv-dur-slow) var(--rv-ease-out);
}

/* Errore: filo rosso + «Повторить» (il testo porta il significato) */
.rv-upload__item.is-error > .rv-upload__img {
  opacity: 0.35;
}

.rv-upload__item.is-error::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 2px var(--rv-danger);
  pointer-events: none;
}

.rv-upload__retry {
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  display: inline-flex;
  align-items: center;
  min-block-size: 32px;
  padding-inline: var(--rv-space-3);
  border-radius: var(--rv-radius-pill);
  background-color: var(--rv-surface-raised);
  color: var(--rv-danger);
  font-size: var(--rv-fs-caption);
  font-weight: var(--rv-fw-semibold);
  line-height: 1.2;
  white-space: nowrap;
  box-shadow: var(--rv-shadow-sm);
  translate: -50% -50%;
  transition: transform var(--rv-dur-fast) var(--rv-ease-out);
}

/* Bersaglio di 44px attorno alla pill da 32px */
.rv-upload__retry::before {
  content: "";
  position: absolute;
  inset: calc((32px - var(--rv-tap)) / 2) 0;
}

.rv-upload__retry:active {
  transform: scale(var(--rv-press-scale));
}

/* Tessera «aggiungi»: tratteggio --rv-border-control (icona e «Добавить»
   la identificano già); all'hover il tratto sale a border-strong */
.rv-upload__add {
  position: relative;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: var(--rv-space-0-5);
  aspect-ratio: 1;
  padding: var(--rv-space-2);
  border: 1.5px dashed var(--rv-border-control);
  border-radius: var(--rv-radius-md);
  color: var(--rv-text-secondary);
  text-align: center;
  cursor: pointer;
  touch-action: manipulation;
  transition:
    background-color var(--rv-dur-fast) var(--rv-ease-out),
    border-color var(--rv-dur-fast) var(--rv-ease-out),
    transform var(--rv-dur-fast) var(--rv-ease-out);
}

.rv-upload__add > .rv-icon {
  --rv-icon-size: 28px;
  margin-block-end: var(--rv-space-1);
}

.rv-upload__input {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  margin: 0;
  opacity: 0;
  cursor: inherit;
}

.rv-upload__add-label {
  font-size: var(--rv-fs-caption);
  font-weight: var(--rv-fw-semibold);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text);
}

.rv-upload__add-meta {
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text-muted);
  font-variant-numeric: tabular-nums lining-nums;
}

@media (hover: hover) {
  .rv-upload__add:hover {
    border-color: var(--rv-border-strong);
    background-color: var(--rv-surface-hover);
  }
}

.rv-upload__add:active {
  background-color: var(--rv-surface-hover);
  transform: scale(var(--rv-press-scale));
}

.rv-upload__add:has(> :focus-visible) {
  outline: var(--rv-focus-width) solid var(--rv-focus-ring);
  outline-offset: var(--rv-focus-offset);
}

.rv-upload__add.is-dragover {
  border-style: solid;
  border-color: var(--rv-primary);
  background-color: var(--rv-primary-soft);
  color: var(--rv-primary-soft-text);
}

.rv-upload__add.is-dragover > .rv-upload__add-label,
.rv-upload__add.is-dragover > .rv-upload__add-meta {
  color: var(--rv-primary-soft-text);
}

.rv-upload__add:has(> :disabled) {
  opacity: 0.5;
  cursor: default;
  transform: none;
}

/* Nessuna foto ancora: tessera larga quanto la griglia */
.rv-upload__add--wide {
  grid-column: 1 / -1;
  aspect-ratio: auto;
  min-block-size: 132px;
  padding: var(--rv-space-5) var(--rv-space-4);
}


/* --------------------------------------------------------------------------
   9. Animazioni e contrasto elevato
   -------------------------------------------------------------------------- */
@keyframes rv-forms-spin {
  to { rotate: 1turn; }
}

/* Windows ad alto contrasto: i fondi vengono forzati, i segni no */
@media (forced-colors: active) {
  .rv-check__box::after,
  .rv-radio__box::after,
  .rv-switch__track::after,
  .rv-upload__progress::before {
    forced-color-adjust: none;
    background-color: CanvasText;
  }

  .rv-switch__track {
    border: 1px solid CanvasText;
  }

  .rv-search:has(.rv-search__input:focus-visible) {
    outline: 2px solid Highlight;
  }

  .rv-segmented__item[aria-selected="true"],
  .rv-segmented__item[aria-checked="true"],
  .rv-segmented__input:checked ~ .rv-segmented__label::before {
    outline: 2px solid Highlight;
  }

  /* Riquadri senza bordo proprio e pill della copertina: i fondi spariscono */
  .rv-interpretation,
  .rv-interpretation__note,
  .rv-upload__cover-mark::before {
    border: 1px solid CanvasText;
  }

  .rv-upload__cover-mark {
    border: 1.5px solid CanvasText;
  }
}


/* --------------------------------------------------------------------------
   10. Ripiego per WebView senza :has() (iOS < 15.4, Chromium < 105)
   Solo il focus di barre e riquadri che hanno l'input dentro (:focus-within,
   che si accende anche al tocco: accettabile qui). Stato scelto e disattivo
   non ne hanno bisogno: sono scritti senza :has() nei loro blocchi.
   -------------------------------------------------------------------------- */
@supports not selector(:has(> *)) {
  .rv-price:focus-within {
    border-color: var(--rv-focus-ring);
    box-shadow:
      0 0 0 var(--rv-border-width) var(--rv-focus-ring),
      0 0 0 4px var(--rv-primary-soft);
  }

  .rv-search:focus-within {
    box-shadow:
      0 0 0 var(--rv-focus-width) var(--rv-focus-ring),
      0 0 0 5px var(--rv-primary-soft),
      var(--rv-shadow-lg);
  }

  .rv-search--compact:focus-within {
    background-color: var(--rv-surface);
    box-shadow:
      inset 0 0 0 var(--rv-border-width) var(--rv-focus-ring),
      0 0 0 var(--rv-border-width) var(--rv-focus-ring),
      0 0 0 4px var(--rv-primary-soft);
  }

  .rv-upload__add:focus-within {
    outline: var(--rv-focus-width) solid var(--rv-focus-ring);
    outline-offset: var(--rv-focus-offset);
  }
}


/* ─── src/components/content.css ────────────────────────────────── */
/* ==========================================================================
   Riviera · components/content.css
   Contenuti: .rv-section-head (__icon), .rv-tile(-rail/-grid, __count), .rv-card(--specialist, --row, --skeleton), .rv-card-rail, .rv-listing(--row, --skeleton, __status, __negotiable), .rv-listing-head, .rv-property, .rv-article, .rv-list/.rv-row, .rv-trust, .rv-banner, .rv-review, .rv-profile-head, .rv-service, .rv-gallery, .rv-stat (--accent), .rv-divider, .rv-note, .rv-route (--readonly), .rv-calendar (--readonly), .rv-offer (--chosen, --muted, --new, --skeleton), .rv-map, .rv-chat (__message--mine/--them/--stacked, __day, __system)
   Usa SOLO token semantici (--rv-bg, --rv-text, --rv-primary, …) e le scale
   di spazio/raggio/tipo/moto di tokens.css. Niente colori nei blocchi tema.

   CONVENZIONI DI QUESTO FILE
   - Specificità piatta: una classe, al massimo blocco + elemento/modificatore.
   - Hover solo dentro @media (hover: hover); pressione = scale(--rv-press-scale)
     sugli oggetti compatti (tessere, card), fondo --rv-surface-hover sulle righe.
   - Rail orizzontali (.rv-tile-rail, .rv-card-rail, .rv-gallery--rail): come
     .rv-scroll-x di base.css hanno già il gutter come padding interno. Dentro
     una colonna con gutter (.rv-container) aggiungi .rv-scroll-x--bleed per
     farli arrivare ai bordi dello schermo. Al tocco la scrollbar è nascosta;
     con un puntatore fine (Telegram Desktop, mouse senza rotella
     orizzontale) compare una scrollbar sottile: è l'unico modo di arrivare
     alle voci fuori schermo (vedi fondo del file).
   - Card cliccabili (.rv-card, .rv-listing, .rv-property, .rv-article): il
     link è sul titolo (__link) e si allarga a tutta la card con ::after; i
     controlli annidati (__fav) stanno sopra. Un solo link per card, niente
     link dentro link, lettura naturale per gli screen reader.
   - Immagini: filter: var(--rv-image-dim) (di notte più basse). Immagini
     spente (annuncio venduto, .is-muted sul media): filter: var(--rv-image-muted),
     che SOSTITUISCE image-dim (non si concatenano). Se un'immagine manca, il
     contenitore mostra comunque un fondo tenue (--rv-bg-muted) e, se c'è,
     un'icona segnaposto centrata.
   - Testo sopra una foto (numero «+10» della galleria): --rv-on-image su
     --rv-on-image-scrim, uguali nei due temi perché la foto non cambia.
   ========================================================================== */


/* ==========================================================================
   1. SECTION HEAD — titolo serif di sezione + etichetta o azione a destra
   --------------------------------------------------------------------------
   <div class="rv-section-head">
     <h2 class="rv-section-head__title">Специалисты сообщества</h2>
     <p class="rv-section-head__meta">
       <span class="rv-icon rv-icon--map-pin" aria-hidden="true"></span>Сан-Ремо и окрестности
     </p>
     <!-- oppure: <a class="rv-link rv-section-head__action" href="…">Все</a> -->
   </div>

   Con un'icona davanti al titolo, quando la sezione e' una fra piu' sezioni
   vicine e l'icona dice a colpo d'occhio di che cosa parla («Пассажиры» /
   «Багаж» dentro lo stesso passo di un modulo). Sta DENTRO il titolo, non
   accanto: cosi' resta attaccata alla prima riga anche quando il titolo va a
   capo, mentre da fratello scivolerebbe via per conto suo.

   <h2 class="rv-section-head__title">
     <span class="rv-icon rv-icon--users rv-section-head__icon" aria-hidden="true"></span>Пассажиры
   </h2>
   Se non c'è spazio, l'etichetta va a capo sotto il titolo (allineata a sinistra);
   un'azione corta resta a destra, sulla linea di base del titolo.
   ========================================================================== */
.rv-section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  column-gap: var(--rv-space-3);
  row-gap: var(--rv-space-1);
}

.rv-section-head__title {
  min-width: 0;
  font-family: var(--rv-font-display);
  font-size: var(--rv-fs-title-2);
  font-weight: var(--rv-fw-medium);
  line-height: var(--rv-lh-title-2);
  letter-spacing: var(--rv-tracking-title);
  color: var(--rv-heading);
  text-wrap: balance;
}

.rv-section-head__meta {
  display: inline-flex;
  align-items: center;
  gap: var(--rv-space-1);
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text-muted);
  --rv-icon-size: 16px;
}

/* Icona dentro il titolo. Allineata al testo e non alla riga: la lettera russa
   maiuscola e' alta quanto l'icona, e senza questo l'icona galleggerebbe. */
.rv-section-head__icon {
  --rv-icon-size: 1em;
  margin-inline-end: var(--rv-space-2);
  vertical-align: -0.125em;
  color: var(--rv-text-muted);
}

/* Azione (un .rv-link «Все»): resta a destra sulla prima riga del titolo, anche
   quando il titolo va a capo; l'area di tocco di 44px la dà .rv-link */
.rv-section-head__action {
  flex: none;
  margin-inline-start: auto;
}

.rv-section-head:where(:has(> .rv-section-head__action)) {
  flex-wrap: nowrap;
}

/* Variante piccola: dentro una schermata di dettaglio (profilo, annuncio) */
.rv-section-head--sm .rv-section-head__title {
  font-size: var(--rv-fs-title-3);
  line-height: var(--rv-lh-title-3);
  letter-spacing: 0;
}


/* ==========================================================================
   2. TILE — tessera categoria (icona grande + etichetta)
   --------------------------------------------------------------------------
   <a class="rv-tile" href="…">
     <span class="rv-icon rv-icon--hammer" aria-hidden="true"></span>
     <span class="rv-tile__label">Ремонт</span>
   </a>
   Stati: [aria-pressed="true"] / [aria-current="page"] / .is-selected = scelta
   attiva; [aria-disabled="true"] o :disabled = non disponibile.

   .rv-tile-rail — rail orizzontale con snap (≈3,4 tessere visibili a 360, 390 e 430)
   <div class="rv-tile-rail rv-scroll-x--bleed" role="list"> … .rv-tile (role="listitem" sul wrapper se serve) … </div>

   .rv-tile-grid — griglia 4×2, righe tutte alte uguali
   <div class="rv-tile-grid"> … 8 × .rv-tile … </div>
   Nella griglia l'etichetta sta SEMPRE su una riga, senza sillabazione (a
   10–12px un «Недвижи-/мость» non si legge): il corpo scala con la larghezza
   della griglia, fra 10,5 e 12px, misurato sulla parola più lunga del set.
   Sotto i 350px di griglia (telefoni da 360) quattro colonne non bastano
   nemmeno a 10,5px: la griglia diventa 2×4 con tessere orizzontali (icona a
   sinistra, etichetta a destra). Niente &shy; nel markup.
   ========================================================================== */
.rv-tile {
  --rv-icon-size: 28px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: var(--rv-space-2);
  min-width: 0;
  min-height: 88px;
  padding: 15px var(--rv-space-1) var(--rv-space-3);   /* 4px ai lati: «Недвижимость» sta su una riga da 96px */
  border-radius: var(--rv-radius-md);
  background-color: var(--rv-surface);
  box-shadow: var(--rv-shadow-xs);
  color: var(--rv-text);
  text-align: center;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  transition:
    transform var(--rv-dur-fast) var(--rv-ease-out),
    background-color var(--rv-dur-fast) var(--rv-ease-out),
    color var(--rv-dur-fast) var(--rv-ease-out);
}

.rv-tile__label {
  max-width: 100%;
  font-size: 12px;   /* Onest è largo: a 13px «Недвижимость» chiede tessere da 104px e il rail non «sbircia» più a 360 */
  font-weight: var(--rv-fw-medium);
  line-height: 1.25;
  overflow-wrap: break-word;
  hyphens: auto;
}

@media (hover: hover) {
  .rv-tile:hover {
    background-color: var(--rv-surface-hover);
  }
}

.rv-tile:active {
  transform: scale(var(--rv-press-scale));
}

.rv-tile[aria-pressed="true"],
.rv-tile[aria-current="page"],
.rv-tile.is-selected {
  background-color: var(--rv-primary-soft);
  color: var(--rv-primary-soft-text);
  box-shadow: none;
}

.rv-tile:disabled,
.rv-tile[aria-disabled="true"] {
  opacity: 0.45;
  pointer-events: none;
}

/* Rail: il container misura la propria larghezza e dimensiona le tessere */
.rv-tile-rail {
  --rv-tile-w: 96px;
  display: flex;
  gap: var(--rv-space-2);
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--rv-gutter);
  padding-inline: var(--rv-gutter);
  /* spazio per l'ombra, compensato dal margine: il ritmo verticale non cambia */
  padding-block: var(--rv-space-1) var(--rv-space-3);
  margin-block: calc(-1 * var(--rv-space-1)) calc(-1 * var(--rv-space-3));
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

@supports (width: 1cqi) {
  .rv-tile-rail {
    container-type: inline-size;
    /* 100cqi = larghezza utile (senza i gutter). Dal bordo sinistro della prima
       tessera al bordo dello schermo ci stanno ~3,4 tessere: la quarta tagliata
       dice «scorri» (≈30–45px visibili a 360, 390 e 430). */
    --rv-tile-w: clamp(96px, calc((100cqi + var(--rv-gutter)) / 3.4 - var(--rv-space-2)), 120px);
  }
}

.rv-tile-rail::-webkit-scrollbar {
  display: none;
}

.rv-tile-rail > * {
  flex: none;
  width: var(--rv-tile-w);
  scroll-snap-align: start;
}

.rv-tile-grid {
  container: rv-tile-grid / inline-size;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  grid-auto-rows: 1fr;
  gap: var(--rv-space-2);
}

/* Griglia da 350px in su: 4×2, etichetta su una riga. Il corpo segue la
   larghezza utile della tessera, (100cqi − 3 gap) / 4 − 4px di padding,
   diviso 7,5 (larghezza di «Недвижимость» in Onest 500 con tracking −0,01em
   = 7,17 volte il corpo, più ~2px d'aria per lato): 10,6px a 390, 12px
   da 400px di griglia. */
@container rv-tile-grid (min-width: 350px) {
  .rv-tile {
    padding-inline: var(--rv-space-0-5);
  }

  .rv-tile__label {
    font-size: clamp(var(--rv-fs-tag), calc((25cqi - 10px) / 7.5), 12px);
    letter-spacing: -0.01em;
    white-space: nowrap;
    hyphens: manual;
  }
}

/* Griglia stretta (sotto i 350px): 2×4, ogni tessera occupa due colonne ed è
   orizzontale; l'etichetta ha ~100px e il corpo della didascalia */
@container rv-tile-grid (max-width: 349.98px) {
  .rv-tile-grid > * {
    grid-column: span 2;
  }

  .rv-tile {
    --rv-icon-size: 24px;
    flex-direction: row;
    align-items: center;
    gap: var(--rv-space-2);
    min-height: 52px;
    padding: var(--rv-space-2) var(--rv-space-3);
    text-align: start;
  }

  .rv-tile__label {
    font-size: var(--rv-fs-caption);
    hyphens: manual;
  }
}


/* --------------------------------------------------------------------------
   2b. .rv-tile__count — conteggio sotto l'etichetta della tessera
   --------------------------------------------------------------------------
   <a class="rv-tile" href="…">
     <span class="rv-icon rv-icon--hammer" aria-hidden="true"></span>
     <span class="rv-tile__label">Ремонт</span>
     <span class="rv-tile__count">24<span class="rv-visually-hidden"> специалиста</span></span>
   </a>
   Il numero da solo non dice nulla a chi ascolta: la parola va in un
   .rv-visually-hidden dentro il conteggio («Ремонт 24 специалиста»).
   La riga è riservata anche a conteggio vuoto (<span class="rv-tile__count"></span>
   finché i numeri non arrivano): le tessere non cambiano altezza all'arrivo.
   Tessera scelta: il numero prende il colore dell'etichetta (text-muted su
   primary-soft scende sotto 4,5:1 di giorno), la differenza resta nel corpo.
   Nella griglia stretta (tessere orizzontali, sotto i 350px) il conteggio va
   sotto l'etichetta, a destra dell'icona: la tessera diventa una griglia a
   due colonne che, senza conteggio, disegna esattamente la riga flex di prima.
   -------------------------------------------------------------------------- */
.rv-tile__count {
  display: block;
  max-width: 100%;
  min-block-size: 1.25em;          /* riga riservata anche vuota (= interlinea) */
  margin-block-start: calc(-1 * var(--rv-space-1));   /* 4px dall'etichetta invece del gap di 8 */
  font-size: 12px;                 /* come l'etichetta del rail */
  font-weight: var(--rv-fw-regular);
  line-height: 1.25;
  color: var(--rv-text-muted);
  font-variant-numeric: tabular-nums lining-nums;
  white-space: nowrap;
}

.rv-tile:is([aria-pressed="true"], [aria-current="page"], .is-selected) > .rv-tile__count {
  color: inherit;
}

@container rv-tile-grid (min-width: 350px) {
  .rv-tile__count {
    font-size: clamp(var(--rv-fs-tag), calc((25cqi - 10px) / 7.5), 12px);
  }
}

@container rv-tile-grid (max-width: 349.98px) {
  /* Colonna dell'etichetta mai più stretta della parola più lunga e voci
     allineate all'inizio: stesse misure della riga flex (anche «Недвижимость»
     resta intera e sconfina nel padding come prima) */
  .rv-tile {
    display: grid;
    grid-template-columns: auto minmax(auto, 1fr);
    align-content: center;
    justify-items: start;
    row-gap: 0;
  }

  .rv-tile__count {
    grid-column: 2;
    margin-block-start: 0;
    font-size: var(--rv-fs-caption);
  }
}


/* ==========================================================================
   3. CARD — base comune della famiglia (card, listing, property, article)
   Superficie, raggio lg, ombra sm, link esteso, preferiti, media con ripiego.
   ========================================================================== */
.rv-card,
.rv-listing,
.rv-property,
.rv-article {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  min-width: 0;
  border-radius: var(--rv-radius-lg);
  background-color: var(--rv-surface);
  box-shadow: var(--rv-shadow-sm);
  color: var(--rv-text);
  -webkit-tap-highlight-color: transparent;
  transition:
    transform var(--rv-dur-fast) var(--rv-ease-out),
    box-shadow var(--rv-dur-base) var(--rv-ease-out);
}

/* Media: fondo tenue anche senza immagine; icona segnaposto centrata */
.rv-card__media,
.rv-listing__media,
.rv-property__media,
.rv-article__media {
  --rv-icon-size: 32px;
  position: relative;
  display: grid;
  place-items: center;
  flex: none;
  aspect-ratio: 4 / 3;
  border-radius: var(--rv-radius-lg) var(--rv-radius-lg) 0 0;
  background-color: var(--rv-bg-muted);   /* segnaposto tenue in entrambi i temi (sunken di notte sembra un buco) */
  color: var(--rv-text-muted);
}

:is(.rv-card__media, .rv-listing__media, .rv-property__media, .rv-article__media) > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: inherit;
  filter: var(--rv-image-dim);
}

/* Immagine spenta (non disponibile, archiviata): il filtro completo del tema */
:is(.rv-card__media, .rv-listing__media, .rv-property__media, .rv-article__media, .rv-gallery__item).is-muted > img {
  filter: var(--rv-image-muted);
}

/* Link esteso: il titolo è il link, ::after copre tutta la card */
.rv-card__link,
.rv-listing__link,
.rv-property__link,
.rv-article__link {
  color: inherit;
  text-decoration: none;
  outline: none;
}

:is(.rv-card__link, .rv-listing__link, .rv-property__link, .rv-article__link)::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: var(--rv-radius-lg);
}

:is(.rv-card__link, .rv-listing__link, .rv-property__link, .rv-article__link):focus-visible::after {
  outline: var(--rv-focus-width) solid var(--rv-focus-ring);
  outline-offset: var(--rv-focus-offset);
}

/* Pressione e hover sull'intera card (specificità tenuta a una classe) */
.rv-card:where(:has(.rv-card__link:active)),
.rv-listing:where(:has(.rv-listing__link:active)),
.rv-property:where(:has(.rv-property__link:active)),
.rv-article:where(:has(.rv-article__link:active)) {
  transform: scale(var(--rv-press-scale));
}

@media (hover: hover) {
  .rv-card:where(:has(.rv-card__link:hover)),
  .rv-listing:where(:has(.rv-listing__link:hover)),
  .rv-property:where(:has(.rv-property__link:hover)),
  .rv-article:where(:has(.rv-article__link:hover)) {
    box-shadow: var(--rv-shadow-md);
  }
}

/* Preferiti (un .rv-icon-btn--surface) in alto a destra sull'immagine */
.rv-card__fav,
.rv-listing__fav,
.rv-property__fav {
  position: absolute;
  z-index: 2;
  inset-block-start: var(--rv-space-2);
  inset-inline-end: var(--rv-space-2);
}

/* Bandierina di stato (un .rv-tag) in alto a sinistra sull'immagine */
.rv-card__flag,
.rv-listing__flag,
.rv-property__flag {
  position: absolute;
  inset-block-start: var(--rv-space-3);
  inset-inline-start: var(--rv-space-3);
}


/* --------------------------------------------------------------------------
   3a. .rv-card (generica) e .rv-card--specialist
   --------------------------------------------------------------------------
   <article class="rv-card rv-card--specialist">
     <div class="rv-card__media">
       <img src="…/interno.svg" alt="" loading="lazy">
       <button class="rv-icon-btn rv-icon-btn--surface rv-icon-btn--sm rv-card__fav" type="button"
               aria-pressed="false" aria-label="В избранное">…heart / heart-fill…</button>
     </div>
     <div class="rv-card__body">
       <span class="rv-tag rv-tag--pino rv-tag--cutout rv-card__tag">Ремонт</span>   ← a cavallo fra foto e testo
       <h3 class="rv-card__title"><a class="rv-card__link" href="…">Дизайн и ремонт квартир</a></h3>
       <p class="rv-card__meta"><span>Иван</span><span class="rv-rating rv-rating--sm">…★ 4,9 · 12 отзывов…</span></p>
       <span class="rv-btn rv-btn--outline rv-btn--block rv-card__action" aria-hidden="true">Смотреть профиль …</span>
     </div>
   </article>
   Il bottone in fondo è solo visivo (il link esteso copre già la card).
   Nel .rv-card-rail le card hanno la stessa altezza; nella --specialist meta e
   bottone scendono insieme in fondo al corpo, così restano allineati fra card
   vicine. Lo spazio sopra la meta compare solo se una card del carosello ha
   davvero il titolo su due righe (nessuna riga riservata a vuoto).
   -------------------------------------------------------------------------- */
.rv-card__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--rv-space-1);
  padding: var(--rv-space-4);
}

.rv-card__title {
  font-size: var(--rv-fs-headline);
  font-weight: var(--rv-fw-semibold);
  line-height: var(--rv-lh-headline);
  letter-spacing: -0.005em;
  color: var(--rv-text);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

/* Meta con separatori «·» generati. Ogni voce resta intera quando va a capo e
   il punto che finirebbe a inizio riga viene tagliato: ogni voce ha il suo «·»
   in una fascia a sinistra, il contenitore rientra di quella fascia e la
   ritaglia con clip-path. Nessun punto orfano, nessuno spazio da gestire nell'HTML. */
.rv-card__meta,
.rv-listing__meta,
.rv-listing-head__meta,
.rv-property__facts,
.rv-article__meta {
  --rv-meta-sep: 13px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;   /* non baseline: una voce con icona (stella, orologio) si allineerebbe al fondo dell'icona */
  margin-inline-start: calc(-1 * var(--rv-meta-sep));
  clip-path: inset(-8px -8px -8px var(--rv-meta-sep));
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text-muted);
  font-variant-numeric: tabular-nums lining-nums;
}

:is(.rv-card__meta, .rv-listing__meta, .rv-listing-head__meta, .rv-property__facts, .rv-article__meta) > * {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--rv-space-1);
  padding-inline-start: var(--rv-meta-sep);
  white-space: nowrap;
}

:is(.rv-card__meta, .rv-listing__meta, .rv-listing-head__meta, .rv-property__facts, .rv-article__meta) > *::before {
  content: "·";
  content: "·" / "";
  position: absolute;
  inset-inline-start: 0;
  width: var(--rv-meta-sep);
  text-align: center;
  color: var(--rv-text-muted);
}

/* Tag a cavallo: parte dal bordo della foto e sale di metà della propria altezza */
.rv-card__tag {
  position: relative;
  z-index: 0;
  margin-block: calc(-1 * var(--rv-space-4)) var(--rv-space-0-5);
  transform: translateY(-50%);
}

/* Bottone finale: sempre in fondo, allineato fra card vicine */
.rv-card__action {
  align-self: stretch;
  margin-block-start: auto;
}

.rv-card--specialist .rv-card__body {
  gap: var(--rv-space-1);
  padding-block-end: var(--rv-space-4);
}

/* Meta e bottone in fondo, insieme: il margine automatico sta sulla meta
   (quello del bottone torna a 0, altrimenti lo spazio libero si dividerebbe
   fra i due) */
.rv-card--specialist .rv-card__meta {
  margin-block: auto var(--rv-space-4);
}

.rv-card--specialist .rv-card__action {
  margin-block-start: 0;
}


/* --------------------------------------------------------------------------
   3b. .rv-card-rail — carosello orizzontale con snap e «peek»
   --------------------------------------------------------------------------
   <div class="rv-card-rail rv-scroll-x--bleed">
     <article class="rv-card rv-card--specialist">…</article> × n
   </div>
   Card larghe l'80% (max 320px): la successiva si vede e invita a scorrere.
   Personalizzabile con --rv-card-rail-item (es. 64% per card più strette).
   -------------------------------------------------------------------------- */
.rv-card-rail {
  display: flex;
  gap: var(--rv-space-3);
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--rv-gutter);
  padding-inline: var(--rv-gutter);
  padding-block: var(--rv-space-1) var(--rv-space-6);
  margin-block: calc(-1 * var(--rv-space-1)) calc(-1 * var(--rv-space-6));
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

.rv-card-rail::-webkit-scrollbar {
  display: none;
}

.rv-card-rail > * {
  flex: 0 0 var(--rv-card-rail-item, min(80%, 320px));
  scroll-snap-align: start;
}


/* --------------------------------------------------------------------------
   3c. .rv-listing — annuncio della Барахолка (griglia a 2 colonne)
   --------------------------------------------------------------------------
   <div class="rv-listing-grid">
     <article class="rv-listing">
       <div class="rv-listing__media"><img src="…/poltrona.svg" alt="" loading="lazy"></div>
       <div class="rv-listing__body">
         <p class="rv-listing__price">120&#8239;€</p>
         <h3 class="rv-listing__title"><a class="rv-listing__link" href="…">Кресло 60-х</a></h3>
         <p class="rv-listing__meta"><span>Сан-Ремо</span><span>сегодня</span></p>
       </div>
     </article>
   </div>
   Prezzo:
   - «Даром» evidenziato: .rv-listing__price--free, verde in sans (il verde è
     del testo, non un pieno), con l'icona gift facoltativa:
       <p class="rv-listing__price rv-listing__price--free"><span class="rv-icon rv-icon--gift" aria-hidden="true"></span>Даром</p>
   - «торг» discreto accanto all'importo (corpo piccolo, muted, sulla linea di
     base; con importi lunghi va a capo sotto, l'importo resta intero):
       <p class="rv-listing__price">1&#8239;200&#8239;€<span class="rv-listing__negotiable">торг</span></p>

   Stato sulla foto (__status dentro __media, in basso a sinistra): testo
   --rv-on-image su velo --rv-on-image-scrim, leggibile su qualsiasi foto e in
   entrambi i temi; lo dicono la parola e l'icona, non il colore. Etichette
   brevi nella griglia (a 360 la colonna ha ~140px utili):
     <span class="rv-listing__status"><span class="rv-icon rv-icon--bookmark" aria-hidden="true"></span>Забронировано</span>
   - «Забронировано» (reserved): solo l'etichetta; foto e prezzo restano vivi,
     perché l'oggetto è ancora in catalogo;
   - «Продано»: .rv-listing.is-sold + __status (icona check-circle): foto spenta
     (--rv-image-muted), prezzo muted e barrato, «торг» nascosto. Per chi legge, nel prezzo:
     <span class="rv-visually-hidden">Цена была </span>;
   - «Снято» (scaduto, nascosto): .rv-listing.is-muted + __status (icona clock,
     o lock se l'ha nascosto un moderatore): foto spenta, prezzo muted NON barrato.
   Il vecchio markup di «Продано» con un .rv-tag in __flag resta valido, ma il
   tag in alto a sinistra finisce sotto il cuore con parole lunghe: usa __status.
   Senza foto: nel media metti un'icona (rv-icon--image) al posto di <img>.
   __meta su DUE righe: luogo sopra, tempo sotto (i «·» generati restano
   nascosti nella fascia ritagliata). In una colonna da ~140px il luogo è un
   dato chiave del mercatino e non si tronca: «Оспедалетти», «Вентимилья»
   restano interi; un nome composto molto lungo va a capo sui trattini.
   Le due righe sono sempre riservate: le meta delle card affiancate partono
   alla stessa altezza anche se una voce manca.
   -------------------------------------------------------------------------- */
.rv-listing-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--rv-space-4) var(--rv-space-3);
}

.rv-listing__media {
  aspect-ratio: 1 / 1;
}

.rv-listing__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: var(--rv-space-0-5);
  padding: var(--rv-space-3) var(--rv-space-3) 14px;
}

/* Riga flessibile: l'importo (testo diretto, nowrap) resta intero e «торг» va
   a capo sotto solo se non ci sta. Nessun gap verticale fra le due righe. */
.rv-listing__price {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: var(--rv-space-1-5);
  font-family: var(--rv-font-display);
  font-size: 20px;
  font-weight: var(--rv-fw-medium);
  line-height: 1.2;
  font-variant-numeric: lining-nums tabular-nums;
  color: var(--rv-heading);
  white-space: nowrap;
}

.rv-listing__price--free {
  --rv-icon-size: 18px;
  align-items: center;
  column-gap: var(--rv-space-1);
  font-family: var(--rv-font-sans);
  font-size: var(--rv-fs-body);
  font-weight: var(--rv-fw-semibold);
  line-height: calc(20px * 1.2);   /* stessa riga del prezzo in serif: i titoli restano allineati */
  color: var(--rv-success);
}

/* «торг»: nota discreta dopo l'importo, in sans. Interlinea 1: allineata sulla
   linea di base del serif, una riga più alta sporgerebbe sotto e allungherebbe
   la riga del prezzo (titoli non più allineati fra card vicine) */
.rv-listing__negotiable,
.rv-listing-head__negotiable {
  font-family: var(--rv-font-sans);
  font-size: var(--rv-fs-caption);
  font-style: normal;
  font-weight: var(--rv-fw-regular);
  line-height: 1;
  letter-spacing: 0;
  color: var(--rv-text-muted);
}

/* Etichetta su foto: stato dell'annuncio (griglia e testata del dettaglio) e
   contatore delle foto della testata. Velo pieno (nessuna sfumatura): il
   rapporto --rv-on-image / --rv-on-image-scrim resta ≥4,5:1 anche su una foto
   bianca. Non intercetta i tocchi: sotto c'è il link esteso o la foto. */
.rv-listing__media > .rv-listing__status,
.rv-listing-head__status,
.rv-listing-head__count {
  --rv-icon-size: 14px;
  position: absolute;
  z-index: 1;
  inset-block-end: var(--rv-space-2);
  inset-inline-start: var(--rv-space-2);
  display: inline-flex;
  align-items: center;
  gap: var(--rv-space-1);
  max-inline-size: calc(100% - 2 * var(--rv-space-2));
  min-block-size: 24px;
  padding: 3px var(--rv-space-2);
  border-radius: var(--rv-radius-pill);
  background-color: var(--rv-on-image-scrim);
  color: var(--rv-on-image);
  font-family: var(--rv-font-sans);
  font-size: 12px;
  font-weight: var(--rv-fw-semibold);
  line-height: 1.5;   /* 18px di riga: con 3 + 3 di padding la pill è alta 24 */
  letter-spacing: 0;
  white-space: nowrap;
  pointer-events: none;
}

/* Con l'icona il lato sinistro si stringe: l'icona ha già aria sua */
.rv-listing__media > .rv-listing__status:has(> .rv-icon:first-child),
.rv-listing-head__status:has(> .rv-icon:first-child) {
  padding-inline-start: var(--rv-space-1-5);
}

.rv-listing__title {
  font-size: var(--rv-fs-callout);
  font-weight: var(--rv-fw-regular);
  line-height: 1.35;
  color: var(--rv-text);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

/* Colonna: ogni voce su una riga propria, due righe riservate (13 × 1,4 × 2,
   più il padding superiore: box-sizing è border-box) */
.rv-listing__meta {
  flex-direction: column;
  flex-wrap: nowrap;
  align-items: flex-start;
  min-block-size: calc(2 * var(--rv-fs-caption) * var(--rv-lh-caption) + var(--rv-space-1-5));
  margin-block-start: auto;
  padding-block-start: var(--rv-space-1-5);
}

/* Voci a blocco: il luogo va a capo solo se è più largo della card (mai
   puntini); il tempo resta su una riga e, in una card strettissima, si
   accorcia con i puntini come ultima risorsa */
.rv-listing__meta > * {
  display: block;
  max-width: 100%;
}

.rv-listing__meta > :first-child {
  white-space: normal;
  overflow-wrap: break-word;
}

.rv-listing__meta > :not(:first-child) {
  overflow: hidden;
  text-overflow: ellipsis;
}

/* «Продано» (.is-sold) e «Снято» (.is-muted): foto spenta e prezzo muted;
   solo il venduto barra il prezzo (lo scaduto non ha cambiato prezzo) */
.rv-listing:is(.is-sold, .is-muted) .rv-listing__media > img {
  filter: var(--rv-image-muted);   /* filtro completo del tema: sostituisce image-dim */
}

.rv-listing:is(.is-sold, .is-muted) .rv-listing__price {
  color: var(--rv-text-muted);
}

.rv-listing.is-sold .rv-listing__price {
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}

/* A cosa venduta «торг» non dice più nulla (e la barra si estenderebbe a lui) */
.rv-listing.is-sold .rv-listing__negotiable,
.rv-listing-head.is-sold .rv-listing-head__negotiable {
  display: none;
}


/* --------------------------------------------------------------------------
   3c2. .rv-listing--skeleton — annuncio in caricamento (griglia)
   --------------------------------------------------------------------------
   <div class="rv-listing-grid" aria-busy="true">
     <span class="rv-visually-hidden">Загружаем объявления…</span>
     <div class="rv-listing rv-listing--skeleton" aria-hidden="true">
       <div class="rv-listing__media"><span class="rv-skeleton rv-skeleton--fill"></span></div>
       <div class="rv-listing__body">
         <span class="rv-skeleton rv-skeleton--heading"></span>   prezzo
         <span class="rv-skeleton rv-skeleton--text"></span>      titolo, 1ª riga
         <span class="rv-skeleton rv-skeleton--text"></span>      titolo, 2ª riga
         <span class="rv-skeleton rv-skeleton--text"></span>      luogo
         <span class="rv-skeleton rv-skeleton--text"></span>      tempo
       </div>
     </div> × 2 (o 4)
   </div>
   Con «Показать ещё» le ossa si aggiungono in fondo alla griglia e il testo
   nascosto va in un role="status" accanto. Niente misure in linea: le
   larghezze cambiano da sole fra colonna sinistra e destra; le altezze
   ricalcano un annuncio con il titolo su due righe (prezzo 24, titolo 2 × 19,
   meta 6 + 2 × 18), così all'arrivo dei dati la griglia non salta.
   -------------------------------------------------------------------------- */
.rv-listing--skeleton .rv-listing__body {
  gap: 0;
}

/* Prezzo: osso da 16 in una riga da 24, più i 2px del gap vero */
.rv-listing--skeleton .rv-skeleton--heading {
  inline-size: 44%;
  margin-block: 4px 6px;
}

/* Titolo: righe da 19px (osso 12 + 3,5 sopra e sotto), più i 2px del gap
   prima della meta. Meta: righe da 18,2px (osso 12 + 3,1), con i 6px di
   padding della meta vera sopra il luogo. */
.rv-listing--skeleton .rv-skeleton--text {
  margin-block: 3.5px;
}

.rv-listing--skeleton .rv-skeleton--text:nth-child(2) { inline-size: 92%; }
.rv-listing--skeleton .rv-skeleton--text:nth-child(3) { inline-size: 70%; margin-block-end: 5.5px; }
.rv-listing--skeleton .rv-skeleton--text:nth-child(4) { inline-size: 52%; margin-block: 9.1px 3.1px; }
.rv-listing--skeleton .rv-skeleton--text:nth-child(5) { inline-size: 34%; margin-block: 3.1px; }

.rv-listing--skeleton:nth-child(even) .rv-skeleton--heading { inline-size: 36%; }
.rv-listing--skeleton:nth-child(even) .rv-skeleton--text:nth-child(2) { inline-size: 84%; }
.rv-listing--skeleton:nth-child(even) .rv-skeleton--text:nth-child(3) { inline-size: 58%; }
.rv-listing--skeleton:nth-child(even) .rv-skeleton--text:nth-child(4) { inline-size: 64%; }


/* --------------------------------------------------------------------------
   3d. .rv-property — card immobile
   --------------------------------------------------------------------------
   <article class="rv-property">
     <div class="rv-property__media">
       <img src="…/appartamento.svg" alt="" loading="lazy">
       <span class="rv-tag rv-tag--mare rv-property__flag">Аренда</span>
       <button class="rv-icon-btn rv-icon-btn--surface rv-property__fav" …>…</button>
     </div>
     <div class="rv-property__body">
       <p class="rv-property__price">950&#8239;€<span class="rv-property__period">/мес</span></p>
       <h3 class="rv-property__title"><a class="rv-property__link" href="…">2-комнатная квартира у моря</a></h3>
       <p class="rv-property__facts"><span>2 комнаты</span><span>64 м²</span><span>3 этаж</span></p>
       <div class="rv-property__tags">                                     (facoltativo)
         <span class="rv-tag rv-tag--salvia">Терраса</span><span class="rv-tag rv-tag--mare">Вид на море</span>
       </div>
       <p class="rv-property__place"><span class="rv-icon rv-icon--map-pin" aria-hidden="true"></span>Сан-Ремо, Корсо Императриче</p>
     </div>
   </article>
   __tags: poche caratteristiche distintive (2–3 .rv-tag tenui), non un elenco
   di tutto: i dati già in __facts non si ripetono.
   PREZZI, grafia unica del sistema: «980 €/мес», «от 40 €», «120 €», «1 250 €».
   Spazio stretto U+202F (&#8239;) fra le cifre e prima di €, &nbsp; dopo «от»,
   nessuno spazio attorno a «/»: __period segue € senza stacco (niente gap).
   -------------------------------------------------------------------------- */
.rv-property__media {
  aspect-ratio: 16 / 10;
}

.rv-property__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: var(--rv-space-1);
  padding: 14px var(--rv-space-4) var(--rv-space-4);
}

.rv-property__price {
  display: flex;
  align-items: baseline;
  gap: 0;                            /* «€/мес» attaccati: un gap darebbe «€ /мес» */
  font-family: var(--rv-font-display);
  font-size: var(--rv-fs-title-2);
  font-weight: var(--rv-fw-medium);
  line-height: var(--rv-lh-title-2);
  font-variant-numeric: lining-nums tabular-nums;
  color: var(--rv-heading);
  white-space: nowrap;
}

.rv-property__period {
  font-family: var(--rv-font-sans);
  font-size: var(--rv-fs-callout);
  font-weight: var(--rv-fw-regular);
  color: var(--rv-text-muted);
}

.rv-property__title {
  font-size: var(--rv-fs-body);
  font-weight: var(--rv-fw-semibold);
  line-height: 1.35;
  color: var(--rv-text);
}

.rv-property__facts {
  font-size: var(--rv-fs-callout);
  color: var(--rv-text-secondary);
}

/* Gruppo di tag nel corpo: vanno a capo, 6px fra un tag e l'altro */
.rv-property__tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--rv-space-1-5);
  margin-block-start: var(--rv-space-2);
}

.rv-property__place {
  --rv-icon-size: 16px;
  display: flex;
  align-items: flex-start;
  gap: var(--rv-space-1);
  margin-block-start: var(--rv-space-2);
  padding-block-start: var(--rv-space-3);
  border-block-start: var(--rv-border-width) solid var(--rv-border);
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text-muted);
}

.rv-property__place > .rv-icon {
  margin-block-start: 1px;
}


/* --------------------------------------------------------------------------
   3e. .rv-article — card articolo / guida
   --------------------------------------------------------------------------
   <article class="rv-article">
     <div class="rv-article__media"><img src="…/articolo-documenti.svg" alt="" loading="lazy"></div>
     <div class="rv-article__body">
       <p class="rv-article__overline">Документы</p>
       <h3 class="rv-article__title"><a class="rv-article__link" href="…">Как получить ВНЖ…</a></h3>
       <p class="rv-article__excerpt">…</p>                          (facoltativo)
       <p class="rv-article__meta"><span><span class="rv-icon rv-icon--clock" aria-hidden="true"></span>8 мин чтения</span><span>12 сентября</span></p>
     </div>
   </article>
   .rv-article--row: versione compatta per elenchi (miniatura quadrata a sinistra).
   -------------------------------------------------------------------------- */
.rv-article__media {
  aspect-ratio: 16 / 9;
}

.rv-article__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: var(--rv-space-1-5);
  padding: var(--rv-space-4) var(--rv-space-4) 18px;
}

.rv-article__overline {
  font-size: var(--rv-fs-overline);
  font-weight: var(--rv-fw-semibold);
  line-height: var(--rv-lh-overline);
  letter-spacing: var(--rv-tracking-overline);
  text-transform: uppercase;
  color: var(--rv-accent-text);
}

.rv-article__title {
  font-family: var(--rv-font-display);
  font-size: var(--rv-fs-title-3);
  font-weight: var(--rv-fw-medium);
  line-height: var(--rv-lh-title-3);
  color: var(--rv-heading);
  text-wrap: balance;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
}

.rv-article__excerpt {
  font-size: var(--rv-fs-callout);
  line-height: var(--rv-lh-callout);
  color: var(--rv-text-secondary);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

.rv-article__meta {
  --rv-icon-size: 16px;
  margin-block-start: auto;
  padding-block-start: var(--rv-space-1-5);
}

/* Riga compatta: miniatura 88px, testo a destra */
.rv-article--row {
  flex-direction: row;
  align-items: center;
  gap: 14px;
  padding: var(--rv-space-3);
}

.rv-article--row .rv-article__media {
  --rv-icon-size: 24px;
  width: 88px;
  aspect-ratio: 1 / 1;
  border-radius: var(--rv-radius-md);
}

.rv-article--row .rv-article__body {
  gap: var(--rv-space-1);
  min-width: 0;
  padding: 0;
}

.rv-article--row .rv-article__title {
  font-size: 17px;
  line-height: 1.25;
}

.rv-article--row .rv-article__meta {
  padding-block-start: var(--rv-space-0-5);
}


/* --------------------------------------------------------------------------
   3f. .rv-card--row — card specialista compatta per ELENCHI
   --------------------------------------------------------------------------
   Risultati della ricerca, categoria, preferiti: una card per riga, miniatura
   quadrata da 88px a sinistra. Il carosello della home resta .rv-card--specialist.

   <article class="rv-card rv-card--row">
     <div class="rv-card__media">
       <img src="…/cover.jpg" alt="" loading="lazy">
     </div>
     <div class="rv-card__body">
       <span class="rv-tag rv-tag--pino rv-card__tag">Ремонт</span>
       <h3 class="rv-card__title"><a class="rv-card__link" href="…">Дизайн и ремонт квартир</a></h3>
       <p class="rv-card__meta">
         <span>Иван Петров</span>
         <span class="rv-rating rv-rating--sm">…★ 4,9 · 12 отзывов…</span>   (senza voti: .rv-rating--none)
       </p>
       <p class="rv-card__place"><span class="rv-icon rv-icon--map-pin" aria-hidden="true"></span>Сан-Ремо, Оспедалетти, Бордигера</p>
     </div>
     <button class="rv-icon-btn rv-icon-btn--sm rv-card__fav" type="button"
             aria-pressed="false" aria-label="Иван Петров — в избранное">
       <span class="rv-icon rv-icon--heart rv-icon-btn__off" aria-hidden="true"></span>
       <span class="rv-icon rv-icon--heart-fill rv-icon-btn__on" aria-hidden="true"></span>
     </button>
   </article>

   Senza foto: nel media l'icona della categoria su tinta (niente illustrazioni
   a caso), con la tinta del tag:
     <div class="rv-card__media rv-card__media--cipria"><span class="rv-icon rv-icon--lotus" aria-hidden="true"></span></div>
   Tinte __media--pino --cipria --sabbia --mare --salvia: stesse coppie degli
   avatar (pino = primary-soft), AA fra icona e fondo; valgono anche per il
   media 4:3 di .rv-card--specialist.
   Regole:
   - un solo link (il titolo) esteso a tutta la card; il cuore è un bottone a
     parte, sopra il link, in alto a destra (bersaglio 44px dentro la card);
   - il tag non è «a cavallo» (niente --cutout) e sta fuori dalla fascia del
     cuore; il titolo va su 2 righe al massimo (senza tag lascia la fascia libera);
   - __place: zone su UNA riga con i puntini (l'elenco intero sta nel profilo;
     i lettori di schermo leggono comunque tutto il testo);
   - il corpo si centra sulla miniatura quando è più basso di lei.
   -------------------------------------------------------------------------- */
.rv-card--row {
  flex-direction: row;
  align-items: flex-start;
  gap: var(--rv-space-3);
  padding: var(--rv-space-3);
}

.rv-card--row .rv-card__media {
  inline-size: 88px;
  aspect-ratio: 1 / 1;
  border-radius: var(--rv-radius-md);
}

.rv-card--row .rv-card__body {
  align-self: stretch;
  justify-content: center;
  gap: var(--rv-space-1);
  min-inline-size: 0;
  min-block-size: 88px;
  padding: 0;
}

/* Tag piccolo sulla prima riga, non a cavallo della foto; resta fuori dalla
   fascia del cuore (44px di bersaglio meno i 12px di padding della card) */
.rv-card--row .rv-card__tag {
  max-inline-size: calc(100% - var(--rv-tap) + var(--rv-space-3));
  margin: 0 0 var(--rv-space-1);
  transform: none;
}

/* Titolo compatto (come .rv-property__title). Sotto il tag parte già più in
   basso del cuore e usa tutta la larghezza; senza tag lascia libera la fascia */
.rv-card--row .rv-card__title {
  font-size: var(--rv-fs-body);
  line-height: 1.35;
  letter-spacing: 0;
}

.rv-card--row .rv-card__title:first-child {
  padding-inline-end: calc(var(--rv-tap) - var(--rv-space-3));
}

/* Cuore in alto a destra, sulla superficie della card (senza --surface):
   cerchio da 36px a 4px dai bordi, bersaglio da 44px a filo della card */
.rv-card--row .rv-card__fav {
  inset-block-start: var(--rv-space-1);
  inset-inline-end: var(--rv-space-1);
}

/* Zone: una riga, puntini, icona facoltativa allineata al testo.
   Larga 0 per il calcolo delle misure intrinseche e poi quanto il corpo
   (min-inline-size 100%): una riga lunga che non va a capo non allarga la
   card dentro una griglia o un contenitore che si misura sul contenuto. */
.rv-card__place {
  --rv-icon-size: 14px;
  inline-size: 0;
  min-inline-size: 100%;
  overflow: hidden;
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text-muted);
  white-space: nowrap;
  text-overflow: ellipsis;
}

.rv-card__place > .rv-icon {
  margin-inline-end: var(--rv-space-1);
  vertical-align: -2px;
}

/* Media senza foto: tinta della categoria, icona nello stesso tono */
.rv-card__media--pino { background-color: var(--rv-primary-soft); color: var(--rv-primary-soft-text); }
.rv-card__media--cipria { background-color: var(--rv-tag-cipria-bg); color: var(--rv-tag-cipria-fg); }
.rv-card__media--sabbia { background-color: var(--rv-tag-sabbia-bg); color: var(--rv-tag-sabbia-fg); }
.rv-card__media--mare { background-color: var(--rv-tag-mare-bg); color: var(--rv-tag-mare-fg); }
.rv-card__media--salvia { background-color: var(--rv-tag-salvia-bg); color: var(--rv-tag-salvia-fg); }


/* --------------------------------------------------------------------------
   3g. .rv-card--skeleton — card specialista in caricamento
   --------------------------------------------------------------------------
   Stessa struttura della card vera, ossa di .rv-skeleton (navigation.css) al
   posto dei contenuti, nessun link. Il contenitore annuncia l'attesa, le card
   sono nascoste ai lettori:

   <div class="rv-card-rail rv-scroll-x--bleed" aria-busy="true">
     <span class="rv-visually-hidden">Загружаем специалистов…</span>
     <div class="rv-card rv-card--specialist rv-card--skeleton" aria-hidden="true">
       <div class="rv-card__media"><span class="rv-skeleton rv-skeleton--fill"></span></div>
       <div class="rv-card__body">
         <span class="rv-skeleton rv-card__tag"></span>
         <span class="rv-skeleton rv-skeleton--heading"></span>
         <span class="rv-skeleton rv-skeleton--text"></span>
         <span class="rv-skeleton rv-skeleton--pill"></span>
       </div>
     </div> × 2–3
   </div>

   In elenco (.rv-card--row), due righe di testo (meta e zone), niente pill:
   <div class="rv-card rv-card--row rv-card--skeleton" aria-hidden="true">
     <div class="rv-card__media"><span class="rv-skeleton rv-skeleton--fill"></span></div>
     <div class="rv-card__body">
       <span class="rv-skeleton rv-card__tag"></span>
       <span class="rv-skeleton rv-skeleton--heading"></span>
       <span class="rv-skeleton rv-skeleton--text"></span>
       <span class="rv-skeleton rv-skeleton--text"></span>
     </div>
   </div>

   Niente misure in linea: l'osso del tag ha la forma del tag (col contorno
   ritagliato quando sta a cavallo della foto), le larghezze delle righe
   cambiano da sole fra card vicine (1ª, 2ª, 3ª di ogni terna). Le altezze
   ricalcano la card vera, così all'arrivo dei dati la pagina non salta.
   -------------------------------------------------------------------------- */
.rv-card--skeleton .rv-card__tag {
  inline-size: 84px;
  block-size: 22px;
  border-radius: var(--rv-radius-pill);
  box-shadow: 0 0 0 3px var(--rv-surface);   /* contorno come .rv-tag--cutout */
}

.rv-card--skeleton .rv-skeleton--heading { inline-size: 80%; }
.rv-card--skeleton .rv-skeleton--text { inline-size: 54%; }
.rv-card--skeleton .rv-skeleton--text + .rv-skeleton--text { inline-size: 72%; }

.rv-card--skeleton:nth-child(3n + 2) .rv-skeleton--heading { inline-size: 62%; }
.rv-card--skeleton:nth-child(3n + 2) .rv-skeleton--heading + .rv-skeleton--text { inline-size: 46%; }
.rv-card--skeleton:nth-child(3n) .rv-skeleton--heading { inline-size: 88%; }
.rv-card--skeleton:nth-child(3n) .rv-skeleton--text + .rv-skeleton--text { inline-size: 58%; }

/* Colonna: la riga della meta scende in fondo insieme al bottone, come nella
   card vera (18px di meta = osso da 12 + 3 sopra e sotto; i 3px sopra stanno
   sotto il titolo, perché il margine automatico della meta vale 0 quando la
   card non è stirata) */
.rv-card--specialist.rv-card--skeleton .rv-skeleton--heading {
  margin-block-end: 6px;
}

.rv-card--specialist.rv-card--skeleton .rv-skeleton--text {
  margin-block: auto calc(var(--rv-space-4) + 3px);
}

/* Riga: ossa alte quanto le righe vere (titolo 20px, meta e zone 18px) */
.rv-card--row.rv-card--skeleton .rv-card__tag {
  inline-size: 72px;
  margin-block-end: var(--rv-space-1);
  box-shadow: none;
}

.rv-card--row.rv-card--skeleton .rv-skeleton--heading {
  margin-block: 2px;
}

.rv-card--row.rv-card--skeleton .rv-skeleton--text {
  margin-block: 3px;
}


/* --------------------------------------------------------------------------
   3h. .rv-listing--row — annuncio in ELENCO («Мои объявления», preferiti)
   --------------------------------------------------------------------------
   Stesse parti di .rv-listing (prezzo, «Даром», «торг», .is-sold, .is-muted),
   miniatura quadrata da 88px a sinistra come .rv-card--row. Lo stato qui non
   sta sulla foto (88px non bastano a «Забронировано»): va nella meta, come
   voce con tono e icona. Sotto, facoltative, le azioni del proprietario.

   Annuncio proprio («Мои объявления»):
   <article class="rv-listing rv-listing--row">
     <div class="rv-listing__media"><img src="…" alt="" loading="lazy"></div>
     <div class="rv-listing__body">
       <p class="rv-listing__price">120&#8239;€<span class="rv-listing__negotiable">торг</span></p>
       <h3 class="rv-listing__title"><a class="rv-listing__link" href="…">Кресло 60-х, зелёный велюр</a></h3>
       <p class="rv-listing__meta">
         <span class="rv-listing__status rv-listing__status--success"><span class="rv-icon rv-icon--eye" aria-hidden="true"></span>Активно</span>
         <span>ещё 12&nbsp;дней</span>
       </p>
     </div>
     <div class="rv-listing__actions">
       <button class="rv-btn rv-btn--secondary rv-btn--sm" type="button">Продлить</button>   (solo se si può rinnovare)
       <button class="rv-btn rv-btn--outline rv-btn--sm" type="button">Продано</button>
       <a class="rv-icon-btn rv-icon-btn--sm" href="…" aria-label="Редактировать: Кресло 60-х">
         <span class="rv-icon rv-icon--edit" aria-hidden="true"></span>
       </a>
     </div>
   </article>

   Preferito (chi guarda): meta = luogo e tempo; il cuore è un figlio diretto
   dell'article, in alto a destra, .rv-icon-btn--sm senza --surface:
   <button class="rv-icon-btn rv-icon-btn--sm rv-listing__fav" type="button" aria-pressed="true"
           aria-label="Кресло 60-х — в избранное">…heart / heart-fill…</button>

   Stato (__status nella meta): parola + icona + tono, mai solo colore.
     Активно                 --success  eye
     Забронировано           --info     bookmark
     Истекает через 2 дня    --warning  clock   (sostituisce «Активно» a ridosso della scadenza)
     Продано                 (neutro)   check-circle   + .rv-listing.is-sold
     Срок истёк              --warning  clock          + .rv-listing.is-muted
     Скрыто модератором      --danger   lock           + .rv-listing.is-muted
   La seconda voce dice il tempo: «ещё 12 дней», «снято 3 дня назад». Una
   voce di tempo urgente senza icona («истекает через 2 дня» accanto a
   «Забронировано») prende il tono con __status--warning: la frase dice già
   l'urgenza, il colore la rinforza. I colori --rv-success/-info/-warning/
   -danger sono AA come testo su --rv-surface nei due temi.
   Azioni (__actions): sotto un filetto, a tutta larghezza; bottoni --sm di
   testo a sinistra, un .rv-icon-btn (modifica) va da solo in fondo a destra.
   Stanno sopra il link esteso: il tocco fra un bottone e l'altro apre
   l'annuncio. Una sola azione piena tenue (Продлить) quando serve davvero.
   -------------------------------------------------------------------------- */
.rv-listing--row {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--rv-space-3);
  padding: var(--rv-space-3);
}

.rv-listing--row .rv-listing__media {
  --rv-icon-size: 24px;
  inline-size: 88px;
  border-radius: var(--rv-radius-md);
}

/* Corpo accanto alla miniatura: centrato su di lei quando è più basso */
.rv-listing--row .rv-listing__body {
  flex: 1 1 0;
  align-self: stretch;
  justify-content: center;
  min-inline-size: 0;
  min-block-size: 88px;
  padding: 0;
}

/* Il prezzo è la prima riga: lascia libera la fascia del cuore (44px di
   bersaglio meno i 12px di padding della card), come il titolo di .rv-card--row */
.rv-listing--row .rv-listing__price {
  padding-inline-end: calc(var(--rv-tap) - var(--rv-space-3));
}

.rv-listing--row .rv-listing__title {
  font-size: var(--rv-fs-body);
}

/* Meta in riga, con i «·» generati (niente righe riservate: la riga non si
   allinea a una vicina) */
.rv-listing--row .rv-listing__meta {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  min-block-size: 0;
  margin-block-start: var(--rv-space-0-5);
  padding-block-start: 0;
}

.rv-listing--row .rv-listing__meta > * {
  display: inline-flex;
  max-width: 100%;
  overflow: visible;
  white-space: nowrap;
}

/* Cuore in alto a destra, sulla superficie della card */
.rv-listing--row > .rv-listing__fav {
  inset-block-start: var(--rv-space-1);
  inset-inline-end: var(--rv-space-1);
}

/* Stato come voce della meta: icona da 16 e tono */
.rv-listing__meta > .rv-listing__status {
  --rv-icon-size: 16px;
  font-weight: var(--rv-fw-medium);
  color: var(--rv-text-secondary);
}

.rv-listing__meta > .rv-listing__status--success { color: var(--rv-success); }
.rv-listing__meta > .rv-listing__status--info { color: var(--rv-info); }
.rv-listing__meta > .rv-listing__status--warning { color: var(--rv-warning); }
.rv-listing__meta > .rv-listing__status--danger { color: var(--rv-danger); }

/* Azioni del proprietario: seconda riga della card, sotto un filetto. Il
   contenitore lascia passare i tocchi al link esteso; i controlli no. */
.rv-listing__actions {
  position: relative;
  z-index: 2;
  display: flex;
  flex: 1 0 100%;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--rv-space-2);
  padding-block-start: var(--rv-space-3);
  border-block-start: var(--rv-border-width) solid var(--rv-border);
  pointer-events: none;
}

.rv-listing__actions > * {
  pointer-events: auto;
}

/* La modifica (bottone icona) va in fondo a destra; il cerchio da 36 si
   allinea al bordo del contenuto come i bottoni --sm a sinistra */
.rv-listing__actions > .rv-icon-btn {
  margin-inline: auto calc(-1 * var(--rv-space-1));
}


/* --------------------------------------------------------------------------
   3i. .rv-listing-head — testata del dettaglio annuncio
   --------------------------------------------------------------------------
   <header class="rv-listing-head rv-listing-head--bleed rv-listing-head--top">
     <div class="rv-listing-head__media">
       <div class="rv-listing-head__track">
         <button class="rv-listing-head__slide" type="button" aria-haspopup="dialog" aria-label="Фото 1 из 3, открыть">
           <img src="…" alt="" width="1280" height="1280">
         </button> …
       </div>
       <div class="rv-listing-head__actions">
         <button class="rv-icon-btn rv-icon-btn--surface" type="button" aria-label="Поделиться объявлением">…share…</button>
         <button class="rv-icon-btn rv-icon-btn--surface" type="button" aria-pressed="false" aria-label="В избранное">…heart / heart-fill…</button>
       </div>
       <span class="rv-listing-head__status"><span class="rv-icon rv-icon--bookmark" aria-hidden="true"></span>Забронировано</span>   (facoltativo)
       <p class="rv-listing-head__count rv-num" aria-hidden="true">1 / 3</p>                                                            (con 2+ foto)
     </div>
     <div class="rv-listing-head__body">
       <span class="rv-tag rv-tag--sabbia rv-listing-head__tag">Мебель</span>
       <p class="rv-listing-head__price">120&#8239;€<span class="rv-listing-head__negotiable">торг уместен</span></p>
       <h1 class="rv-listing-head__title">Кресло 60-х, зелёный велюр</h1>
       <p class="rv-listing-head__meta"><span>Сан-Ремо</span><span>сегодня</span></p>
     </div>
   </header>

   - Foto quadrata (al massimo 440px d'altezza), una per schermata, scorrimento
     orizzontale con snap: ogni foto è un bottone che apre .rv-viewer
     (navigation.css) sulla stessa foto. Con il mouse si apre il visore, che
     ha le frecce. Lo script aggiorna __count («2 / 3») a fine scorrimento:
     indice = Math.round(track.scrollLeft / track.clientWidth). Una foto sola:
     niente __count. Foto che non si carica: al posto di <img> l'icona image.
   - __actions: condividi e preferiti sulla foto (bottoni --surface); l'azione
     principale resta il MainButton.
   - __status: stato sulla foto, come .rv-listing__status (bookmark
     «Забронировано», check-circle «Продано», clock «Снято с публикации»).
     .is-sold (foto spente, prezzo barrato) e .is-muted (foto spente) come
     nella card; il visore mostra le foto vere.
   - __price: Playfair grande; «Даром» con __price--free, corsivo verde
     (--rv-heading-accent): è il corsivo della schermata, non aggiungerne altri.
   - --bleed porta la foto ai bordi dello schermo; --top la fa salire sotto
     l'area sicura (barra di stato, controlli di Telegram a schermo intero),
     come .rv-profile-head--top: la parte visibile resta quadrata e le azioni
     scendono sotto l'area sicura. Va dentro .rv-app e .rv-container.
   -------------------------------------------------------------------------- */
.rv-listing-head {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.rv-listing-head__media {
  position: relative;
  isolation: isolate;
  max-block-size: 440px;
  overflow: hidden;
  border-radius: var(--rv-radius-lg);
  background-color: var(--rv-bg-muted);   /* segnaposto tenue, come i media delle card */
  color: var(--rv-text-muted);
}

/* Altezza col padding in % (riferito alla larghezza): quadrata, anche dove
   aspect-ratio su un contenitore con figli assoluti non basterebbe */
.rv-listing-head__media::before {
  content: "";
  display: block;
  padding-block-start: 100%;
}

.rv-listing-head__track {
  position: absolute;
  inset: 0;
  display: flex;
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

.rv-listing-head__track::-webkit-scrollbar {
  display: none;
}

.rv-listing-head__slide {
  --rv-icon-size: 40px;
  position: relative;
  display: grid;
  flex: 0 0 100%;
  place-items: center;
  min-inline-size: 0;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  cursor: zoom-in;
  scroll-snap-align: center;
  scroll-snap-stop: always;
  -webkit-tap-highlight-color: transparent;
}

.rv-listing-head__slide > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: var(--rv-image-dim);
  -webkit-user-drag: none;
}

/* Anello dentro la foto, su un livello sopra l'immagine: l'outline del bottone
   finirebbe sotto la <img> assoluta e fuori lo taglierebbe la fila che scorre.
   Sotto l'anello una fascia --rv-surface: resta ≥3:1 su qualsiasi foto. */
.rv-listing-head__slide:focus-visible {
  outline: none;
}

.rv-listing-head__slide::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
}

.rv-listing-head__slide:focus-visible::after {
  outline: var(--rv-focus-width) solid var(--rv-focus-ring);
  outline-offset: calc(-2 * var(--rv-focus-width));
  box-shadow: inset 0 0 0 calc(3 * var(--rv-focus-width)) var(--rv-surface);
}

/* Condividi e preferiti in alto a destra, sopra la foto */
.rv-listing-head__actions {
  position: absolute;
  z-index: 1;
  inset-block-start: var(--rv-space-3);
  inset-inline-end: var(--rv-space-3);
  display: flex;
  gap: var(--rv-space-2);
}

/* Stato in basso a sinistra, contatore in basso a destra (stile nel blocco
   delle etichette su foto, sezione 3c) */
.rv-listing-head__status,
.rv-listing-head__count {
  inset-block-end: var(--rv-space-3);
  inset-inline-start: var(--rv-space-3);
}

.rv-listing-head__count {
  inset-inline: auto var(--rv-space-3);
  font-variant-numeric: tabular-nums lining-nums;
}

.rv-listing-head--bleed .rv-listing-head__media {
  margin-inline: calc(-1 * var(--rv-gutter));
  border-radius: 0;
}

/* Sui bordi dello schermo le etichette e le azioni si allineano alla colonna */
.rv-listing-head--bleed .rv-listing-head__actions { inset-inline-end: var(--rv-gutter); }
.rv-listing-head--bleed .rv-listing-head__status { inset-inline-start: var(--rv-gutter); }
.rv-listing-head--bleed .rv-listing-head__count { inset-inline-end: var(--rv-gutter); }

.rv-listing-head--top {
  margin-block-start: calc(-1 * var(--rv-safe-top));
}

.rv-listing-head--top .rv-listing-head__media {
  max-block-size: calc(440px + var(--rv-safe-top));
  border-start-start-radius: 0;
  border-start-end-radius: 0;
}

.rv-listing-head--top .rv-listing-head__media::before {
  padding-block-start: calc(100% + var(--rv-safe-top));
}

.rv-listing-head--top .rv-listing-head__actions {
  inset-block-start: calc(var(--rv-safe-top) + var(--rv-space-3));
}

.rv-listing-head__body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--rv-space-1);
  padding-block-start: var(--rv-space-4);
}

.rv-listing-head__tag {
  margin-block-end: var(--rv-space-1);
}

.rv-listing-head__price {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: var(--rv-space-2);
  font-family: var(--rv-font-display);
  font-size: 30px;
  font-weight: var(--rv-fw-medium);
  line-height: var(--rv-lh-title-1);
  letter-spacing: var(--rv-tracking-title);
  font-variant-numeric: lining-nums tabular-nums;
  color: var(--rv-heading);
  white-space: nowrap;
}

/* La «Д» corsiva sporge a sinistra del proprio box: 0,1em la riporta sul gutter */
.rv-listing-head__price--free {
  padding-inline-start: 0.1em;
  font-style: italic;
  color: var(--rv-heading-accent);
}

.rv-listing-head__negotiable {
  font-size: var(--rv-fs-callout);
}

/* Titolo scritto dall'utente: sans, due pesi sotto il prezzo, va a capo
   anche su parole lunghissime */
.rv-listing-head__title {
  font-family: var(--rv-font-sans);
  font-size: var(--rv-fs-title-3);
  font-weight: var(--rv-fw-semibold);
  line-height: 1.3;
  letter-spacing: -0.005em;
  color: var(--rv-text);
  text-wrap: pretty;
  overflow-wrap: anywhere;
}

.rv-listing-head__meta {
  --rv-icon-size: 16px;
  margin-block-start: var(--rv-space-0-5);
  font-size: var(--rv-fs-callout);
  line-height: var(--rv-lh-callout);
}

/* Venduto e ritirato: foto spente, prezzo muted (barrato solo il venduto) */
.rv-listing-head:is(.is-sold, .is-muted) .rv-listing-head__slide > img {
  filter: var(--rv-image-muted);
}

.rv-listing-head:is(.is-sold, .is-muted) .rv-listing-head__price {
  color: var(--rv-text-muted);
}

.rv-listing-head.is-sold .rv-listing-head__price {
  text-decoration: line-through;
  text-decoration-thickness: 1.5px;
}


/* ==========================================================================
   4. LIST + ROW — elenchi raggruppati (impostazioni, chat, contatti)
   --------------------------------------------------------------------------
   <ul class="rv-list">
     <li>
       <a class="rv-row" href="…">
         <span class="rv-row__icon"><span class="rv-icon rv-icon--globe" aria-hidden="true"></span></span>
         <span class="rv-row__body">
           <span class="rv-row__text">
             <span class="rv-row__title">Язык</span>
             <span class="rv-row__subtitle">Интерфейс и уведомления</span>   (facoltativo)
           </span>
           <span class="rv-row__value">Русский</span>                         (facoltativo)
           <span class="rv-icon rv-icon--chevron-right rv-row__chevron" aria-hidden="true"></span>
         </span>
       </a>
     </li>
   </ul>
   Al posto di __icon si può usare un .rv-avatar. Il separatore parte dal testo
   (non dall'icona) e arriva al bordo destro, come negli elenchi di sistema.

   Variante chat: .rv-row--chat, con __aside (ora + badge) al posto di __value.
   <a class="rv-row rv-row--chat" href="…">
     <span class="rv-avatar rv-avatar--sabbia" aria-hidden="true">БС</span>   (oppure .rv-row__icon)
     <span class="rv-row__body">
       <span class="rv-row__text">
         <span class="rv-row__title">Барахолка Сан-Ремо</span>
         <span class="rv-row__subtitle"><span class="rv-row__sender">Мария:</span> Отдам детское автокресло…</span>
       </span>
       <span class="rv-row__aside">
         <time class="rv-row__time">12:40</time>
         <span class="rv-badge">3</span>
       </span>
     </span>
   </a>
   Nella chat l'avatar è sempre da 48px (qualunque misura abbia la classe), come
   il cerchio di __icon; __sender (autore) è nel colore del testo, il resto
   dell'anteprima in muted, tutto su una riga con i puntini.

   Chat silenziata: il contatore grigio (.rv-badge--muted) da solo affida il
   significato al colore. Sulla riga del badge, prima di lui, va __muted:
   campanella barrata piccola in --rv-text-muted + «без звука» nascosto.
   <span class="rv-row__aside">
     <time class="rv-row__time">пн</time>
     <span class="rv-row__muted"><span class="rv-icon rv-icon--bell-off" aria-hidden="true"></span><span class="rv-visually-hidden">без звука,</span></span>
     <span class="rv-visually-hidden">2 непрочитанных</span>
     <span class="rv-badge rv-badge--muted" aria-hidden="true">2</span>
   </span>
   Senza messaggi non letti __muted resta da solo sulla stessa riga, a destra.
   Stati: hover/pressione = fondo --rv-surface-hover; [aria-disabled="true"];
   [aria-current="page"] o .is-selected = riga scelta (spunta in __value).
   ========================================================================== */
.rv-list {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
  list-style: none;
  border-radius: var(--rv-radius-lg);
  background-color: var(--rv-surface);
  box-shadow: var(--rv-shadow-xs);
  overflow: hidden;
}

.rv-row {
  display: flex;
  align-items: center;
  gap: var(--rv-space-3);
  width: 100%;
  min-height: 56px;
  padding-inline-start: var(--rv-space-4);
  color: var(--rv-text);
  text-align: start;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  transition: background-color var(--rv-dur-fast) var(--rv-ease-out);
}

/* Anello interno (la lista taglia ciò che sporge), con gli angoli della lista */
.rv-row:focus-visible {
  outline-offset: calc(-1 * var(--rv-focus-width));
  border-radius: calc(var(--rv-radius-lg) - var(--rv-focus-width));
}

@media (hover: hover) {
  :is(a, button, label).rv-row:hover {
    background-color: var(--rv-surface-hover);
  }
}

:is(a, button, label).rv-row:active {
  background-color: var(--rv-surface-hover);
}

.rv-row[aria-disabled="true"],
.rv-row:disabled {
  opacity: 0.45;
  pointer-events: none;
}

/* Icona in cerchio chiaro (stesso linguaggio di .rv-trust) */
.rv-row__icon {
  --rv-icon-size: 20px;
  display: grid;
  place-items: center;
  flex: none;
  width: 40px;
  height: 40px;
  border-radius: var(--rv-radius-pill);
  background-color: var(--rv-bg-muted);
  color: var(--rv-text-secondary);
}

.rv-row__body {
  display: flex;
  flex: 1;
  align-items: center;
  align-self: stretch;
  gap: var(--rv-space-3);
  min-width: 0;
  padding-block: var(--rv-space-3);
  padding-inline-end: var(--rv-space-4);
}

/* Separatore fra le righe di una lista (niente sopra la prima) */
:where(.rv-list > * + *) .rv-row__body {
  box-shadow: inset 0 var(--rv-border-width) 0 var(--rv-border);
}

.rv-row__text {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

.rv-row__title {
  font-size: var(--rv-fs-body);
  font-weight: var(--rv-fw-medium);
  line-height: 1.35;
  color: var(--rv-text);
  overflow-wrap: break-word;
}

.rv-row__subtitle {
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text-muted);
  overflow-wrap: break-word;
}

.rv-row__value {
  --rv-icon-size: 20px;
  display: inline-flex;
  align-items: center;
  flex: none;
  max-width: 45%;
  font-size: var(--rv-fs-callout);
  line-height: var(--rv-lh-callout);
  color: var(--rv-text-muted);
  font-variant-numeric: tabular-nums lining-nums;
  text-align: end;
}

.rv-row__chevron {
  --rv-icon-size: 20px;
  flex: none;
  margin-inline: calc(-1 * var(--rv-space-1)) calc(-1 * var(--rv-space-1));
  color: var(--rv-text-muted);
}

/* Riga scelta (es. lingua attiva): valore/spunta in verde. È testo, non un
   pieno: --rv-link, AA anche sul fondo di hover (dove di notte --rv-primary
   scende a 3,6:1). La spunta accanto al valore dice la scelta anche senza colore. */
.rv-row[aria-current="page"] .rv-row__value,
.rv-row.is-selected .rv-row__value {
  color: var(--rv-link);
}

/* Riga «pericolosa» (Выйти, Удалить профиль) */
.rv-row--danger .rv-row__title,
.rv-row--danger .rv-row__icon {
  color: var(--rv-danger);
}

.rv-row--danger .rv-row__icon {
  background-color: var(--rv-danger-soft);
}

/* Chat: cerchio più grande, anteprima su una riga, ora + badge a destra */
.rv-row--chat {
  min-height: 72px;
}

.rv-row--chat .rv-row__icon {
  --rv-icon-size: 24px;
  width: 48px;
  height: 48px;
}

/* Avatar della chat: 48px, fra --default (40) e --lg (56) di actions.css.
   Vale anche se nel markup c'è .rv-avatar--lg: la riga decide la misura. */
.rv-row--chat > .rv-avatar {
  --rv-avatar-size: 48px;
}

/* Autore dell'ultimo messaggio: stesso corpo dell'anteprima, colore del testo */
.rv-row__sender {
  color: var(--rv-text);
}

.rv-row--chat .rv-row__title,
.rv-row--chat .rv-row__subtitle {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.rv-row--chat .rv-row__title {
  line-height: 20px;
}

.rv-row--chat .rv-row__subtitle {
  font-size: var(--rv-fs-callout);
  line-height: 20px;
}

/* Sempre alto due righe (come __text, 20 + 1 + 20): l'ora sta sulla riga del
   titolo, badge e __muted su quella dell'anteprima, anche quando manca
   qualcosa. Griglia a due colonne solo per la seconda riga: [__muted][badge]. */
.rv-row__aside {
  display: grid;
  flex: none;
  grid-template-columns: auto auto;
  grid-template-rows: 20px 20px;
  align-items: center;
  justify-content: end;
  justify-items: end;
  gap: 1px var(--rv-space-1);
  min-width: 20px;
}

.rv-row__time {
  font-size: 12px;
  line-height: 20px;   /* = interlinea del titolo: ora e titolo sulla stessa riga */
  color: var(--rv-text-muted);
  font-variant-numeric: tabular-nums lining-nums;
  white-space: nowrap;
}

/* L'ora occupa la prima riga intera */
.rv-row__aside > .rv-row__time {
  grid-column: 1 / -1;
  grid-row: 1;
}

/* Il badge (o un'altra voce) siede sulla riga dell'anteprima, anche senza ora */
.rv-row__aside > :not(.rv-row__time, .rv-row__muted) {
  grid-column: 2;
  grid-row: 2;
}

/* Chat silenziata: campanella barrata accanto al badge; senza badge sta a destra */
.rv-row__muted {
  --rv-icon-size: 16px;
  display: inline-flex;
  grid-column: 1;
  grid-row: 2;
  color: var(--rv-text-muted);
}

.rv-row__muted:last-child {
  grid-column: 1 / -1;
}


/* ==========================================================================
   5. TRUST — fila di garanzie (icona in cerchio + testo breve)
   --------------------------------------------------------------------------
   <ul class="rv-trust">
     <li class="rv-trust__item">
       <span class="rv-trust__icon"><span class="rv-icon rv-icon--shield-check" aria-hidden="true"></span></span>
       <span class="rv-trust__text">Проверенные специалисты</span>
     </li>
     … × 3
   </ul>
   Default: colonne strette affiancate, testo centrato su 2 righe (mobile).
   .rv-trust--list: una garanzia per riga, icona a sinistra (con __desc facoltativo).
   Senza __desc l'icona è centrata sulla riga; con __desc sale in alto e il
   titolo scende quanto basta per stare a metà del cerchio.
   ========================================================================== */
.rv-trust {
  display: grid;
  grid-auto-columns: minmax(0, 1fr);
  grid-auto-flow: column;
  gap: var(--rv-space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.rv-trust__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--rv-space-2);
  min-width: 0;
  text-align: center;
}

.rv-trust__icon {
  --rv-icon-size: 22px;
  display: grid;
  place-items: center;
  flex: none;
  width: var(--rv-tap);
  height: var(--rv-tap);
  border-radius: var(--rv-radius-pill);
  background-color: var(--rv-surface);
  box-shadow: var(--rv-shadow-xs);
  color: var(--rv-primary-soft-text);
}

.rv-trust__text {
  font-size: var(--rv-fs-caption);
  font-weight: var(--rv-fw-medium);
  line-height: 1.3;
  color: var(--rv-text-secondary);
  text-wrap: balance;
  overflow-wrap: break-word;
}

.rv-trust__desc {
  display: block;
  margin-block-start: 2px;
  font-weight: var(--rv-fw-regular);
  color: var(--rv-text-muted);
}

.rv-trust--list {
  grid-auto-flow: row;
  gap: var(--rv-space-4);
}

.rv-trust--list .rv-trust__item {
  flex-direction: row;
  gap: var(--rv-space-3);
  text-align: start;
}

.rv-trust--list .rv-trust__text {
  font-size: var(--rv-fs-callout);
  text-wrap: pretty;
}

/* Con descrizione su più righe: icona in alto, prima riga del titolo centrata
   sul cerchio da 44px (interlinea 1,3em) */
.rv-trust--list .rv-trust__item:where(:has(.rv-trust__desc)) {
  align-items: flex-start;
}

.rv-trust--list .rv-trust__text:where(:has(.rv-trust__desc)) {
  padding-block-start: calc((var(--rv-tap) - 1.3em) / 2);
}


/* ==========================================================================
   6. BANNER — invito all'azione su pannello salvia con ramo d'ulivo
   --------------------------------------------------------------------------
   <section class="rv-banner" aria-labelledby="…">
     <img class="rv-banner__art" src="…/assets/ramo-ulivo.svg" alt="">
     <div class="rv-avatar-group rv-banner__people" aria-hidden="true">          (facoltativo)
       <span class="rv-avatar rv-avatar--sm rv-avatar--sabbia">ИП</span> …
     </div>
     <h2 class="rv-banner__title">Умеете делать что-то хорошо?</h2>
     <p class="rv-banner__text">Расскажите об этом нашим…</p>
     <a class="rv-btn rv-btn--primary rv-banner__action" href="…">Присоединиться …</a>
     <p class="rv-banner__note"><span class="rv-icon rv-icon--user" aria-hidden="true"></span>Быстро, бесплатно, для своих</p>
   </section>
   Il ramo entra dall'angolo in basso a destra, dietro al testo; il testo lascia
   libera una fascia a destra solo nella parte bassa (bottone e microcopy).
   ========================================================================== */
.rv-banner {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--rv-space-2);
  padding: var(--rv-space-6) var(--rv-space-5) var(--rv-space-5);
  overflow: hidden;
  border-radius: var(--rv-radius-xl);
  background-color: var(--rv-bg-muted);
  color: var(--rv-text);
}

.rv-banner__art {
  position: absolute;
  z-index: -1;
  inset-block-end: -18px;
  inset-inline-end: -14px;
  width: 112px;
  height: auto;
  transform: scaleX(-1) rotate(-6deg);
  transform-origin: 50% 100%;
  filter: var(--rv-image-dim);
  pointer-events: none;
  user-select: none;
}

/* Gruppo di avatar in apertura («qui ci sono già i nostri»): il contorno
   che stacca i cerchi è il colore della PAGINA, non del pannello, più un filo
   --rv-border-control attaccato al disco. Di notte le tinte degli avatar
   (toni 900) hanno la luminanza del pannello salvia (1,03:1) e anche il
   contorno scuro da solo resta a 1,3:1: il filo (~1,9:1 sui dischi, ~2,9:1
   sul contorno) disegna il bordo di ogni cerchio e della sovrapposizione.
   Di giorno il filo beige si legge appena, il contorno crema fa il resto. */
.rv-banner__people {
  --rv-avatar-cutout: var(--rv-bg);
  --rv-avatar-edge: var(--rv-border-control);
  --rv-avatar-edge-w: 1px;
  margin-block-end: var(--rv-space-2);
}

/* «+183»: il fondo incassato sparirebbe nel pannello (di notte sembra un buco) */
.rv-banner__people > .rv-avatar--more {
  --rv-avatar-bg: var(--rv-surface);
}

.rv-banner__title {
  max-width: 18em;
  font-family: var(--rv-font-display);
  font-size: var(--rv-fs-title-2);
  font-weight: var(--rv-fw-medium);
  line-height: var(--rv-lh-title-2);
  letter-spacing: var(--rv-tracking-title);
  color: var(--rv-heading);
  text-wrap: balance;
}

.rv-banner__text {
  max-width: 34em;
  font-size: var(--rv-fs-body);
  line-height: var(--rv-lh-body);
  color: var(--rv-text-secondary);
  text-wrap: pretty;
}

.rv-banner__action {
  margin-block-start: var(--rv-space-2);
}

.rv-banner__note > .rv-icon {
  margin-block-start: 1px;
}

.rv-banner__note {
  --rv-icon-size: 16px;
  display: inline-flex;
  align-items: flex-start;
  gap: var(--rv-space-1-5);
  padding-inline-end: 56px;   /* lascia respirare il ramo */
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text-muted);
}


/* ==========================================================================
   7. REVIEW — recensione
   --------------------------------------------------------------------------
   <article class="rv-review">
     <header class="rv-review__head">
       <span class="rv-avatar …">МК</span>
       <div class="rv-review__who">
         <p class="rv-review__name">Марина К.</p>
         <p class="rv-review__date"><time datetime="2026-08-12">12 августа</time></p>
       </div>
       <span class="rv-rating rv-rating--sm rv-review__stars" role="img" aria-label="Оценка 5 из 5">
         <span class="rv-rating__stars">5 × .rv-icon--star-fill.rv-rating__star
           (vuote: .rv-icon--star a contorno + __star--empty: forma diversa, non solo colore)</span>
       </span>
     </header>
     <p class="rv-review__text">…</p>
     <p class="rv-review__service">Услуга: лазерная эпиляция</p>   (facoltativo)
   </article>
   Le stelle sono quelle di .rv-rating (actions.css): il voto è anche nell'aria-label.
   .rv-review--card: su superficie propria (per rail o griglie).
   .rv-review__text.is-clamped: 4 righe al massimo (con link «Читать полностью»).
   ========================================================================== */
.rv-review {
  display: flex;
  flex-direction: column;
  gap: var(--rv-space-3);
  min-width: 0;
}

.rv-review__head {
  display: flex;
  align-items: center;
  gap: var(--rv-space-3);
}

.rv-review__who {
  flex: 1;
  min-width: 0;
}

.rv-review__name {
  font-size: var(--rv-fs-body);
  font-weight: var(--rv-fw-semibold);
  line-height: 1.35;
  color: var(--rv-text);
  overflow-wrap: break-word;
}

.rv-review__date {
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text-muted);
}

.rv-review__stars {
  flex: none;
  align-self: flex-start;
  margin-block-start: 3px;   /* sulla riga del nome */
}

.rv-review__text {
  font-size: var(--rv-fs-body);
  line-height: var(--rv-lh-body);
  color: var(--rv-text);
  text-wrap: pretty;
  overflow-wrap: break-word;
}

.rv-review__text.is-clamped {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 4;
  line-clamp: 4;
  overflow: hidden;
}

.rv-review__service {
  --rv-icon-size: 16px;
  display: flex;
  align-items: center;
  gap: var(--rv-space-1-5);
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text-muted);
}

.rv-review--card {
  padding: var(--rv-space-4);
  border-radius: var(--rv-radius-lg);
  background-color: var(--rv-surface);
  box-shadow: var(--rv-shadow-sm);
}


/* ==========================================================================
   8. PROFILE HEAD — testata del profilo specialista
   --------------------------------------------------------------------------
   <header class="rv-profile-head">
     <div class="rv-profile-head__cover">
       <img src="…/cura.svg" alt="">
       <div class="rv-profile-head__actions">                                    (facoltativo)
         <button class="rv-icon-btn rv-icon-btn--surface" type="button" aria-label="Поделиться профилем">…share…</button>
         <button class="rv-icon-btn rv-icon-btn--surface" type="button" aria-pressed="false" aria-label="В избранное">…heart / heart-fill…</button>
       </div>
     </div>
     <div class="rv-profile-head__avatar"><span class="rv-avatar rv-avatar--xl rv-avatar--verified …">ЮЛ</span></div>
     <span class="rv-tag rv-tag--cipria rv-profile-head__tag">Красота</span>          (facoltativo)
     <h1 class="rv-profile-head__name">Юлия Лебедева</h1>
     <p class="rv-profile-head__role">Косметолог, лазерная эпиляция</p>
     <div class="rv-profile-head__meta">
       <span class="rv-rating">…★ 5,0 · 28 отзывов…</span>
       <span class="rv-profile-head__verified"><span class="rv-icon rv-icon--seal-check" aria-hidden="true"></span>Проверенный</span>
     </div>
   </header>
   La copertina sfuma nel fondo (come l'hero). Dentro .rv-container resta nel
   gutter con gli angoli arrotondati; .rv-profile-head--bleed la porta ai bordi.
   .rv-profile-head--top: la copertina è la prima cosa dello schermo e sale
   sotto l'area sicura superiore (barra di stato, controlli di Telegram a
   schermo intero); la parte visibile resta 2:1 e le __actions scendono sotto
   l'area sicura. Va dentro .rv-app (che ha già padding-top = --rv-safe-top).
   __actions: azioni secondarie (condividi, preferiti) sopra la sfumatura;
   l'azione principale resta il MainButton.
   ========================================================================== */
.rv-profile-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 0;
  text-align: center;
}

.rv-profile-head__cover {
  position: relative;
  align-self: stretch;
  aspect-ratio: 2 / 1;
  max-height: 240px;
  border-radius: var(--rv-radius-lg) var(--rv-radius-lg) 0 0;
  /* segnaposto tenue che in fondo è già il colore della pagina: la riga
     frazionaria del bordo inferiore non lascia un filo di bg-muted */
  background-color: var(--rv-image-fade);
  background-image: linear-gradient(to bottom, var(--rv-bg-muted) 38%, var(--rv-image-fade) 100%);
  overflow: hidden;
}

/* Immagine in assoluto: l'altezza la decide la copertina (2:1, o 2:1 + area
   sicura nella variante --top). Finisce 1px sopra il fondo della copertina:
   con altezze frazionarie (padding in %) e DPR non interi l'immagine (livello
   a parte per via del filter) si arrotonda verso l'esterno, la sfumatura
   verso l'interno, e l'ultima riga di pixel della foto restava scoperta
   come un filo netto largo quanto lo schermo. */
.rv-profile-head__cover > img {
  position: absolute;
  inset: 0 0 1px;
  width: 100%;
  height: calc(100% - 1px);
  object-fit: cover;
  filter: var(--rv-image-dim);
}

/* Sfumatura verso il fondo pagina: nessun bordo netto in basso. Sborda di
   1px sotto la copertina (tagliato dall'overflow): copre sempre l'ultima riga. */
.rv-profile-head__cover::after {
  content: "";
  position: absolute;
  inset: 0 0 -1px;
  background-image: linear-gradient(to bottom, transparent 38%, var(--rv-image-fade) 100%);
}

.rv-profile-head--bleed .rv-profile-head__cover {
  margin-inline: calc(-1 * var(--rv-gutter));
  border-radius: 0;
}

/* Azioni secondarie sulla copertina, in alto a destra, sopra la sfumatura
   (bottoni .rv-icon-btn--surface: si leggono su qualsiasi foto) */
.rv-profile-head__actions {
  position: absolute;
  z-index: 1;
  inset-block-start: var(--rv-space-3);
  inset-inline-end: var(--rv-space-3);
  display: flex;
  gap: var(--rv-space-2);
}

.rv-profile-head--bleed .rv-profile-head__actions {
  inset-inline-end: var(--rv-gutter);   /* a filo con la colonna del contenuto */
}

/* --- Copertina che tocca il bordo alto dello schermo ---
   La testata risale dell'area sicura (che .rv-app ha messo come padding) e la
   copertina si allunga della stessa misura: sotto la barra di stato c'è
   ancora foto, e la parte visibile resta 2:1. Altezza col padding in % del
   ::before (riferito alla larghezza della copertina stessa, anche con --bleed). */
.rv-profile-head--top {
  margin-block-start: calc(-1 * var(--rv-safe-top));
}

.rv-profile-head--top .rv-profile-head__cover {
  aspect-ratio: auto;
  max-height: calc(240px + var(--rv-safe-top));
  border-start-start-radius: 0;
  border-start-end-radius: 0;
}

.rv-profile-head--top .rv-profile-head__cover::before {
  content: "";
  display: block;
  padding-block-start: calc(50% + var(--rv-safe-top));
}

.rv-profile-head--top .rv-profile-head__actions {
  inset-block-start: calc(var(--rv-safe-top) + var(--rv-space-3));
}

/* Anello del colore del fondo attorno all'avatar, sopra la copertina */
.rv-profile-head__avatar {
  position: relative;
  display: inline-flex;
  margin-block-start: -54px;
  padding: var(--rv-space-1-5);   /* contiene anche l'anello ottone di --verified */
  border-radius: var(--rv-radius-pill);
  background-color: var(--rv-bg);
}

.rv-profile-head__name {
  margin-block-start: var(--rv-space-3);
  font-family: var(--rv-font-display);
  font-size: var(--rv-fs-title-1);
  font-weight: var(--rv-fw-medium);
  line-height: var(--rv-lh-title-1);
  letter-spacing: var(--rv-tracking-title);
  color: var(--rv-heading);
  text-wrap: balance;
  overflow-wrap: break-word;
}

/* Categoria sopra il nome (un .rv-tag), come un occhiello: il nome si
   avvicina (dopo __name nel file, così vince a parità di specificità) */
.rv-profile-head__tag {
  margin-block-start: var(--rv-space-3);
}

.rv-profile-head__tag + :where(.rv-profile-head__name) {
  margin-block-start: var(--rv-space-2);
}

.rv-profile-head__role {
  margin-block-start: var(--rv-space-1);
  font-size: var(--rv-fs-body);
  line-height: var(--rv-lh-body);
  color: var(--rv-text-secondary);
  text-wrap: balance;
}

.rv-profile-head__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--rv-space-1) var(--rv-space-4);
  margin-block-start: var(--rv-space-3);
}

.rv-profile-head__verified {
  --rv-icon-size: 18px;
  display: inline-flex;
  align-items: center;
  gap: var(--rv-space-1);
  font-size: var(--rv-fs-caption);
  font-weight: var(--rv-fw-medium);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text-secondary);
}

.rv-profile-head__verified > .rv-icon {
  color: var(--rv-primary);
}


/* ==========================================================================
   9. SERVICE — riga servizio con prezzo
   --------------------------------------------------------------------------
   <div class="rv-service">
     <div class="rv-service__main">
       <h3 class="rv-service__name">Лазерная эпиляция голеней</h3>
       <p class="rv-service__desc"><span>Диодный лазер</span><span>40&nbsp;минут</span></p>   (facoltativo)
     </div>
     <p class="rv-service__price"><span class="rv-service__from">от</span>&nbsp;40&#8239;€</p>
   </div>
   Più servizi di seguito sono separati da una linea sottile. Il prezzo resta
   su una riga e si allinea alla prima riga del nome anche se il nome va a capo.
   __desc: una voce per figlio, il «·» lo genera il CSS come in .rv-card__meta
   (mai orfano a inizio riga). Una voce più lunga della riga va a capo al suo
   interno. Testo semplice senza figli resta ammesso (nessun separatore).
   ========================================================================== */
.rv-service {
  display: flex;
  align-items: baseline;
  gap: var(--rv-space-4);
  padding-block: 14px;
}

.rv-service + .rv-service {
  box-shadow: inset 0 var(--rv-border-width) 0 var(--rv-border);
}

.rv-service__main {
  flex: 1;
  min-width: 0;
}

.rv-service__name {
  font-size: var(--rv-fs-body);
  font-weight: var(--rv-fw-medium);
  line-height: 1.4;
  color: var(--rv-text);
  overflow-wrap: break-word;
}

.rv-service__desc {
  --rv-meta-sep: 13px;
  margin-block-start: 2px;
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text-muted);
}

/* Con voci figlie: stessa tecnica di .rv-card__meta. Ogni voce ha il suo «·»
   in una fascia a sinistra; il contenitore rientra della fascia e la ritaglia,
   così il punto che finirebbe a inizio riga non si vede. Solo con figli
   (:has): il testo semplice non va rientrato né ritagliato. */
.rv-service__desc:where(:has(> *)) {
  display: flex;
  flex-wrap: wrap;
  margin-inline-start: calc(-1 * var(--rv-meta-sep));
  clip-path: inset(-8px -8px -8px var(--rv-meta-sep));
}

.rv-service__desc > * {
  position: relative;
  min-width: 0;
  padding-inline-start: var(--rv-meta-sep);
}

.rv-service__desc > *::before {
  content: "·";
  content: "·" / "";
  position: absolute;
  inset-inline-start: 0;
  width: var(--rv-meta-sep);
  text-align: center;
}

/* Senza :has (WebView vecchie): niente rientro, quindi niente punto sulla prima voce */
@supports not selector(:has(> *)) {
  .rv-service__desc > :first-child {
    padding-inline-start: 0;
  }

  .rv-service__desc > :first-child::before {
    content: none;
  }
}

.rv-service__price {
  flex: none;
  font-size: var(--rv-fs-body);
  font-weight: var(--rv-fw-semibold);
  line-height: 1.4;
  color: var(--rv-text);
  font-variant-numeric: tabular-nums lining-nums;
  text-align: end;
  white-space: nowrap;
}

.rv-service__from {
  font-weight: var(--rv-fw-regular);
  color: var(--rv-text-muted);
}


/* ==========================================================================
   10. GALLERY — miniature (griglia 3 colonne o rail)
   --------------------------------------------------------------------------
   <div class="rv-gallery">
     <button class="rv-gallery__item" type="button" aria-label="Фото 1 из 9"><img src="…" alt=""></button>
     …
     <button class="rv-gallery__item" type="button" aria-label="Ещё 6 фото">
       <img src="…" alt=""><span class="rv-gallery__more" aria-hidden="true">+6</span>
     </button>
   </div>
   .rv-gallery--rail: una fila scorrevole di miniature 104px (va con .rv-scroll-x--bleed).
   Miniatura spenta: .is-muted sul __item.
   ========================================================================== */
.rv-gallery {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--rv-space-1-5);
}

.rv-gallery__item {
  --rv-icon-size: 24px;
  position: relative;
  display: grid;
  place-items: center;
  min-width: 0;
  aspect-ratio: 1 / 1;
  border-radius: var(--rv-radius-sm);
  background-color: var(--rv-bg-muted);
  color: var(--rv-text-muted);
  overflow: hidden;
  -webkit-tap-highlight-color: transparent;
  transition: transform var(--rv-dur-fast) var(--rv-ease-out);
}

.rv-gallery__item > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: var(--rv-image-dim);
}

.rv-gallery__item:active {
  transform: scale(var(--rv-press-scale));
}

/* «+10»: la foto sotto deve ancora intravedersi. Velo scuro morbido
   (--rv-on-image-scrim, uguale nei due temi: la foto non cambia) pieno solo
   al centro, dove sta il numero, e appena più leggero verso i bordi (un salto
   maggiore si leggerebbe come una macchia). Il centro pieno
   garantisce ≥4,5:1 al crema --rv-on-image anche su una foto bianca; il
   numero («+10», ~40×16px) sta nel disco pieno: ~44px di diametro anche in
   una miniatura da 96px. */
.rv-gallery__more {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background-color: var(--rv-on-image-scrim);   /* ripiego uniforme (WebView senza color-mix) */
  font-family: var(--rv-font-display);
  font-size: var(--rv-fs-title-2);
  font-weight: var(--rv-fw-medium);
  font-variant-numeric: lining-nums;
  color: var(--rv-on-image);
}

/* Il velo sfumato solo dove color-mix() esiste: una seconda dichiarazione
   con var() non «ricade» su quella di prima, diventerebbe trasparente */
@supports (color: color-mix(in srgb, red, blue)) {
  .rv-gallery__more {
    background-color: transparent;
    background-image: radial-gradient(
      closest-side,
      var(--rv-on-image-scrim) 46%,
      color-mix(in srgb, var(--rv-on-image-scrim) 78%, transparent) 100%
    );   /* oltre il cerchio (angoli) il gradiente continua con l'ultimo colore */
  }
}

.rv-gallery--rail {
  display: flex;
  gap: var(--rv-space-2);
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--rv-gutter);
  padding-inline: var(--rv-gutter);
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

.rv-gallery--rail::-webkit-scrollbar {
  display: none;
}

.rv-gallery--rail > .rv-gallery__item {
  flex: none;
  width: 104px;
  scroll-snap-align: start;
}


/* ==========================================================================
   11. STAT — numero + etichetta
   --------------------------------------------------------------------------
   <div class="rv-stat">
     <p class="rv-stat__value">12</p>
     <p class="rv-stat__label">лет в Сан-Ремо</p>
   </div>
   .rv-stat-row: più numeri affiancati, colonne uguali con linee sottili fra loro.
   <div class="rv-stat-row"> .rv-stat × 3 </div>
   ========================================================================== */
.rv-stat {
  display: flex;
  flex-direction: column;
  gap: var(--rv-space-0-5);
  min-width: 0;
}

.rv-stat__value {
  font-family: var(--rv-font-display);
  font-size: var(--rv-fs-title-1);
  font-weight: var(--rv-fw-medium);
  line-height: var(--rv-lh-title-1);
  font-variant-numeric: lining-nums tabular-nums;
  color: var(--rv-heading);
  white-space: nowrap;
}

.rv-stat__unit {
  margin-inline-start: 0.12em;
  font-size: 0.64em;
}

.rv-stat__label {
  font-size: var(--rv-fs-caption);
  line-height: 1.3;
  color: var(--rv-text-muted);
  text-wrap: balance;
  overflow-wrap: break-word;
}

.rv-stat-row {
  display: grid;
  grid-auto-columns: minmax(0, 1fr);
  grid-auto-flow: column;
  align-items: start;
}

.rv-stat-row > .rv-stat {
  align-items: center;
  padding-inline: var(--rv-space-2);
  text-align: center;
}

.rv-stat-row > .rv-stat + .rv-stat {
  box-shadow: inset var(--rv-border-width) 0 0 var(--rv-border);
}

/* Un solo numero della fila in verde (il netto dell'autista, «Вы получите»):
   serve a far trovare subito la cifra che conta in una fila di tre. Il colore
   NON porta significato da solo: l'etichetta sotto dice già di che numero si
   tratta, e senza colore la fila resta leggibile. Verde come TESTO = --rv-link
   (AA su bg, surface, surface-raised, surface-hover e bg-muted nei due temi),
   mai --rv-primary, che di notte su sheet e toast scende sotto AA.
   <div class="rv-stat rv-stat--accent">
     <p class="rv-stat__value">24&#8239;€</p>
     <p class="rv-stat__label">вы получите</p>
   </div> */
.rv-stat--accent .rv-stat__value {
  color: var(--rv-link);
}


/* ==========================================================================
   12. DIVIDER — linea sottile, con etichetta o con segno d'ottone
   --------------------------------------------------------------------------
   <hr class="rv-divider">
   <div class="rv-divider rv-divider--label" role="separator">Вчера</div>
   <hr class="rv-divider rv-divider--accent">   (piccolo tratto d'ottone centrato: raro, per chiudere una storia)
   ========================================================================== */
.rv-divider {
  display: block;
  flex: none;
  height: 0;
  margin: 0;
  border: 0;
  border-block-start: var(--rv-border-width) solid var(--rv-border);
  color: var(--rv-text-muted);
}

.rv-divider--label {
  display: flex;
  align-items: center;
  gap: var(--rv-space-3);
  height: auto;
  border: 0;
  font-size: var(--rv-fs-caption);
  font-weight: var(--rv-fw-medium);
  line-height: var(--rv-lh-caption);
  white-space: nowrap;
}

.rv-divider--label::before,
.rv-divider--label::after {
  content: "";
  flex: 1;
  border-block-start: var(--rv-border-width) solid var(--rv-border);
}

.rv-divider--accent {
  width: 40px;
  height: 2px;
  margin-inline: auto;
  border: 0;
  border-radius: var(--rv-radius-pill);
  background-color: var(--rv-accent);
}


/* ==========================================================================
   13. NOTE — nota scritta a mano (Bad Script)
   --------------------------------------------------------------------------
   <p class="rv-note">Люди рядом. Помощь рядом.<span class="rv-icon rv-icon--heart rv-note__heart" aria-hidden="true"></span></p>
   SOLO decorativa: mai informazioni necessarie, mai sotto 20px, al massimo
   una per vista (due note lontane nella stessa pagina lunga vanno bene).
   Il cuore è un'icona: il glifo ♡ non esiste nei sottoinsiemi dei caratteri.
   ========================================================================== */
.rv-note {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  column-gap: 0.3em;
  font-family: var(--rv-font-script);
  font-size: 22px;   /* mai sotto 20px */
  font-weight: var(--rv-fw-regular);
  font-style: normal;
  line-height: var(--rv-lh-script);
  letter-spacing: 0;
  color: var(--rv-text-muted);
  text-align: center;
  transform: rotate(-2deg);
}

.rv-note__heart {
  --rv-icon-size: 0.8em;
  color: var(--rv-favorite);
  transform: translateY(0.05em) rotate(8deg);
}

.rv-note--start {
  justify-content: flex-start;
  text-align: start;
  transform-origin: 0 50%;
}


/* ==========================================================================
   14. ROUTE — le tappe di una corsa (Такси)
   --------------------------------------------------------------------------
   Pallini A, B, C… uniti da una linea verticale, l'indirizzo accanto a
   ciascuno e, dove serve, la riga dell'attesa fra una tappa e la successiva.
   Due usi con lo stesso markup:
   · MODIFICABILE, nel modulo dell'ordine: ogni tappa ha i comandi per
     toglierla e riordinarla, e in fondo il bottone «Добавить остановку»;
   · SOLA LETTURA (.rv-route--readonly), nella scheda dell'ordine e nel
     riepilogo: nessun comando, righe più vicine.

   <div class="rv-route">
     <ol class="rv-route__stops">
       <li class="rv-route__stop">
         <span class="rv-route__dot rv-route__dot--start">A</span>
         <div class="rv-route__body">
           <p class="rv-route__label">Откуда</p>
           <p class="rv-route__address">Виа Маттеотти, 12</p>
         </div>
         <div class="rv-route__actions">
           <button class="rv-icon-btn rv-icon-btn--sm" type="button" aria-label="Переместить остановку А вниз">
             <span class="rv-icon rv-icon--arrow-down" aria-hidden="true"></span>
           </button>
           <button class="rv-icon-btn rv-icon-btn--sm" type="button" aria-label="Убрать остановку А">
             <span class="rv-icon rv-icon--trash" aria-hidden="true"></span>
           </button>
         </div>
       </li>
       <li class="rv-route__stop">
         <span class="rv-route__dot">Б</span>
         <div class="rv-route__body">
           <p class="rv-route__label">Остановка</p>
           <p class="rv-route__address">Виа Рома, 4</p>
           <p class="rv-route__wait"><span class="rv-icon rv-icon--clock" aria-hidden="true"></span>Ожидание 90&nbsp;минут</p>
         </div>
         <div class="rv-route__actions">…</div>
       </li>
       <li class="rv-route__stop">
         <span class="rv-route__dot rv-route__dot--end">В</span>
         <div class="rv-route__body">
           <p class="rv-route__label">Куда</p>
           <p class="rv-route__address">Аэропорт Ниццы, терминал&nbsp;2</p>
         </div>
       </li>
     </ol>
     <button class="rv-btn rv-btn--ghost rv-btn--sm rv-route__add" type="button">
       <span class="rv-icon rv-icon--plus rv-btn__icon" aria-hidden="true"></span>
       <span class="rv-btn__label">Добавить остановку</span>
     </button>
   </div>

   Regole
   - L'attesa appartiene alla tappa in cui si aspetta: sta DENTRO il suo <li>,
     dopo l'indirizzo, e cade così esattamente fra due pallini.
   - Partenza e arrivo si distinguono con il colore (--start in --rv-primary,
     --end in --rv-accent), ma MAI solo con quello: il pallino porta sempre la
     lettera, e __label dice a parole «Откуда» / «Остановка» / «Куда». Il
     colore sta nell'anello (elemento grafico, ≥3:1); la lettera resta in
     --rv-text, leggibile su qualunque fondo.
   - I comandi sono .rv-icon-btn--sm: 36px visibili, 44px di bersaglio.
     L'aria-label nomina la tappa («Убрать остановку Б»), non solo l'azione.
   - In sola lettura non si rendono __actions né __add; il modificatore li
     nasconde comunque, così una vista costruita da un template non li mostra
     per sbaglio.
   ========================================================================== */
.rv-route {
  --rv-route-dot: 24px;
  --rv-route-line: 2px;
  display: flex;
  flex-direction: column;
  gap: var(--rv-space-3);
  min-inline-size: 0;
}

.rv-route__stops {
  display: flex;
  flex-direction: column;
  margin: 0;   /* il margine dell'<ol> non si fonde dentro un flex: va tolto */
  padding: 0;
  list-style: none;
}

.rv-route__stop {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: var(--rv-space-3);
  padding-block-end: var(--rv-space-4);
}

.rv-route__stop:last-child {
  padding-block-end: 0;
}

/* Il filo verticale parte sotto il pallino e arriva in fondo alla tappa,
   passando accanto all'eventuale riga di attesa. L'ultima tappa non ce l'ha. */
.rv-route__stop::before {
  content: "";
  position: absolute;
  inset-block: calc(var(--rv-route-dot) + var(--rv-space-1)) 0;
  inset-inline-start: calc((var(--rv-route-dot) - var(--rv-route-line)) / 2);
  inline-size: var(--rv-route-line);
  border-radius: var(--rv-radius-pill);
  background-color: var(--rv-border);
}

.rv-route__stop:last-child::before {
  content: none;
}

/* Pallino: anello colorato, interno trasparente (il componente sta sia sulla
   pagina sia su una card) e la lettera in --rv-text */
.rv-route__dot {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  flex: none;
  inline-size: var(--rv-route-dot);
  block-size: var(--rv-route-dot);
  margin-block-start: 2px;
  border-radius: var(--rv-radius-pill);
  box-shadow: inset 0 0 0 2px var(--rv-border-strong);
  color: var(--rv-text);
  font-size: var(--rv-fs-caption);
  font-weight: var(--rv-fw-semibold);
  line-height: 1;
}

.rv-route__dot--start {
  box-shadow: inset 0 0 0 2px var(--rv-primary);
}

.rv-route__dot--end {
  box-shadow: inset 0 0 0 2px var(--rv-accent);
}

.rv-route__body {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  gap: var(--rv-space-0-5);
  min-inline-size: 0;
}

.rv-route__label {
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text-muted);
}

/* --------------------------------------------------------------------------
   L'indirizzo che si puo' ancora scegliere: ha l'aspetto di un campo.

   Nel riepilogo di una corsa l'indirizzo e' un dato e si legge come testo
   (`.rv-route__address` da solo, dentro `.rv-route--readonly`). Mentre si
   COMPILA un percorso non e' un dato: e' un posto dove si deve scrivere, e
   scritto come testo semplice non lo sembrava — chi apriva l'ordine leggeva
   «Укажите адрес» e non capiva che si toccava.

   L'elemento resta un <button>, non diventa un <input>: l'indirizzo si sceglie
   da un elenco di suggerimenti (al server serve l'id del posto, non la stringa)
   e aprire la tastiera qui sarebbe una promessa che il campo non mantiene. Ma
   prende bordo, fondo e altezza di un campo, perche' l'aspetto deve dire che
   cosa si puo' fare, non come e' implementato.
   -------------------------------------------------------------------------- */
.rv-route__address--field {
  inline-size: 100%;
  min-block-size: calc(var(--rv-tap) + var(--rv-space-1));
  margin: 0;
  padding: var(--rv-space-2) var(--rv-space-4);
  border: var(--rv-border-width) solid var(--rv-border-strong);
  border-radius: var(--rv-radius-md);
  background-color: var(--rv-surface);
  font-family: var(--rv-font-sans);
  font-size: 16px;                  /* stessa soglia iOS dei campi veri: sotto, la pagina si ingrandisce */
  font-weight: var(--rv-fw-regular);
  text-align: start;                /* un <button> centrerebbe: un campo non centra mai */
  -webkit-appearance: none;
  appearance: none;
  cursor: pointer;
  transition: border-color var(--rv-dur-fast) var(--rv-ease-out), background-color var(--rv-dur-fast) var(--rv-ease-out);
}

.rv-route__address--field:hover {
  border-color: var(--rv-primary);
}

/* Il fuoco lo disegna `:focus-visible` di base.css: qui non si tocca, si lascia passare. */

.rv-route__address {
  font-size: var(--rv-fs-body);
  line-height: var(--rv-lh-body);
  color: var(--rv-text);
  overflow-wrap: break-word;
}

/* Attesa fra due tappe: riga breve con l'orologio, sotto l'indirizzo */
.rv-route__wait {
  --rv-icon-size: 16px;
  display: flex;
  align-items: center;
  gap: var(--rv-space-1);
  margin-block-start: var(--rv-space-2);
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text-secondary);
}

.rv-route__actions {
  display: flex;
  flex: none;
  align-items: center;
  gap: var(--rv-space-1);
  margin-inline-start: auto;
}

.rv-route__add {
  align-self: flex-start;
}

.rv-route--readonly .rv-route__stop {
  padding-block-end: var(--rv-space-3);
}

.rv-route--readonly .rv-route__stop:last-child {
  padding-block-end: 0;
}

.rv-route--readonly .rv-route__actions,
.rv-route--readonly .rv-route__add {
  display: none;
}


/* ==========================================================================
   15. OFFER — la proposta di un autista (Такси)
   --------------------------------------------------------------------------
   Avatar a sinistra, corpo al centro, prezzo e minuti d'arrivo a destra; poi
   un filetto e la fila delle azioni. Nel corpo il numero di corse concluse è
   la riga in evidenza: è la moneta del sistema, quello che fa scegliere, e
   dev'essere la prima cosa che si vede (unica riga in semibold con l'icona
   verde, fra il nome e l'auto, che restano più quieti).

   NON è una card con link esteso: qui le azioni sono due e distinte, quindi
   niente ::after che copre tutto (coprirebbe i bottoni o li renderebbe
   ambigui). Chi vuole aprire il profilo usa il bottone che lo dice.

   <article class="rv-offer">
     <span class="rv-avatar rv-avatar--mare rv-offer__avatar" aria-hidden="true">СК</span>
     <div class="rv-offer__body">
       <h4 class="rv-offer__name">Сергей К.</h4>
       <p class="rv-offer__rides"><span class="rv-icon rv-icon--seal-check" aria-hidden="true"></span>134 поездки</p>
       <p class="rv-offer__car">Fiat Tipo, серый</p>
     </div>
     <div class="rv-offer__aside">
       <p class="rv-offer__price">28&#8239;€</p>
       <p class="rv-offer__eta">подъедет за&nbsp;7&nbsp;минут</p>
     </div>
     <div class="rv-offer__actions">
       <a class="rv-btn rv-btn--outline rv-btn--sm" href="…">Профиль</a>
       <button class="rv-btn rv-btn--secondary rv-btn--sm" type="button">Выбрать</button>
     </div>
   </article>

   Modificatori
   --chosen    proposta scelta: fondo --rv-success-soft e targhetta
               «Выбрано» con la spunta. Mai il solo colore.
   --muted     annullata o scaduta: fondo incassato, niente ombra, prezzo
               spento. Sempre insieme alla targhetta che dice la parola
               («Отменено», «Предложение истекло») con la sua icona.
   --new       autista senza corse alle spalle: targhetta «Новый водитель» in
               ottone leggibile, al posto della riga delle corse.
   --skeleton  in attesa delle proposte (markup in fondo al blocco).

   La targhetta è sempre .rv-offer__badge: una parte sola, il tono lo dà il
   modificatore. Un solo .rv-btn--primary per vista: in un elenco di proposte
   l'azione di scelta è --secondary, e la principale resta il MainButton.

   Sotto i 380px il prezzo scende sotto il nome (stessa colonna del corpo,
   in riga con i minuti) e le due azioni diventano due bottoni a metà
   larghezza: a 360 «Профиль» e «Выбрать» non stanno più affiancate ai
   margini naturali.

   In caricamento:
   <div aria-busy="true">
     <span class="rv-visually-hidden">Ищем водителей…</span>
     <article class="rv-offer rv-offer--skeleton" aria-hidden="true">
       <span class="rv-skeleton rv-skeleton--circle rv-offer__avatar"></span>
       <div class="rv-offer__body">
         <span class="rv-skeleton rv-skeleton--heading"></span>
         <span class="rv-skeleton rv-skeleton--text"></span>
         <span class="rv-skeleton rv-skeleton--text"></span>
       </div>
       <div class="rv-offer__aside">
         <span class="rv-skeleton rv-skeleton--heading"></span>
         <span class="rv-skeleton rv-skeleton--text"></span>
       </div>
       <div class="rv-offer__actions">
         <span class="rv-skeleton rv-skeleton--pill"></span>
         <span class="rv-skeleton rv-skeleton--pill"></span>
       </div>
     </article> × 2–3
   </div>
   ========================================================================== */
.rv-offer {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: start;
  gap: var(--rv-space-1) var(--rv-space-3);
  padding: var(--rv-space-3);
  border-radius: var(--rv-radius-lg);
  background-color: var(--rv-surface);
  box-shadow: var(--rv-shadow-sm);
  color: var(--rv-text);
}

.rv-offer__avatar {
  --rv-avatar-size: 48px;
  grid-column: 1;
  grid-row: 1;
}

.rv-offer__body {
  display: flex;
  grid-column: 2;
  grid-row: 1;
  flex-direction: column;
  gap: var(--rv-space-0-5);
  min-inline-size: 0;
}

.rv-offer__name {
  font-size: var(--rv-fs-headline);
  font-weight: var(--rv-fw-semibold);
  line-height: var(--rv-lh-headline);
  color: var(--rv-text);
  overflow-wrap: break-word;
}

/* Le corse concluse: la riga che si legge per prima */
.rv-offer__rides {
  --rv-icon-size: 16px;
  display: flex;
  align-items: center;
  gap: var(--rv-space-1);
  font-size: var(--rv-fs-body);
  font-weight: var(--rv-fw-semibold);
  line-height: var(--rv-lh-body);
  color: var(--rv-text);
  font-variant-numeric: lining-nums tabular-nums;
}

.rv-offer__rides > .rv-icon {
  color: var(--rv-success);
}

.rv-offer__car {
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text-muted);
  overflow-wrap: break-word;
}

/* Targhetta: parola + icona. Il tono lo dà il modificatore della scheda. */
.rv-offer__badge {
  --rv-icon-size: 16px;
  display: flex;
  align-items: center;
  gap: var(--rv-space-1);
  font-size: var(--rv-fs-caption);
  font-weight: var(--rv-fw-medium);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text-secondary);
}

.rv-offer__aside {
  display: flex;
  grid-column: 3;
  grid-row: 1;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--rv-space-0-5);
  text-align: end;
}

.rv-offer__price {
  font-family: var(--rv-font-display);
  font-size: var(--rv-fs-title-2);
  font-weight: var(--rv-fw-medium);
  line-height: var(--rv-lh-title-2);
  font-variant-numeric: lining-nums tabular-nums;
  color: var(--rv-heading);
  white-space: nowrap;
}

.rv-offer__eta {
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text-muted);
}

.rv-offer__actions {
  display: flex;
  grid-column: 1 / -1;
  grid-row: 2;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--rv-space-2);
  margin-block-start: var(--rv-space-1);
  padding-block-start: var(--rv-space-3);
  border-block-start: var(--rv-border-width) solid var(--rv-border);
}

/* Scelta: fondo verde tenue e targhetta con la parola. Il verde da solo non
   basta, e su --rv-success-soft testo, meta e titolo restano AA nei due temi. */
.rv-offer--chosen {
  background-color: var(--rv-success-soft);
}

.rv-offer--chosen .rv-offer__badge {
  color: var(--rv-success);
}

/* Nuovo autista: ancora nessuna corsa da mostrare, lo si dice con parole */
.rv-offer--new .rv-offer__badge {
  color: var(--rv-accent-text);
}

/* Annullata o scaduta: la scheda si spegne ma resta leggibile (niente
   opacity sul testo), e la targhetta dice che cosa è successo. Il filetto al
   posto dell'ombra: di notte --rv-surface-sunken è più scuro della pagina e
   senza bordo la scheda sembrerebbe un buco, non una scheda spenta. */
.rv-offer--muted {
  background-color: var(--rv-surface-sunken);
  box-shadow: inset 0 0 0 var(--rv-border-width) var(--rv-border);
}

.rv-offer--muted .rv-offer__price {
  color: var(--rv-text-secondary);
}

/* Anche il sigillo verde si spegne: l'offerta non è più in gioco */
.rv-offer--muted .rv-offer__rides > .rv-icon {
  color: var(--rv-text-muted);
}

/* Caricamento: ossa alte come le righe vere, nessuna misura in linea */
.rv-offer--skeleton .rv-offer__avatar {
  inline-size: 48px;
}

.rv-offer--skeleton .rv-offer__body .rv-skeleton--heading { inline-size: 56%; }
.rv-offer--skeleton .rv-offer__body .rv-skeleton--text { inline-size: 40%; }
.rv-offer--skeleton .rv-offer__body .rv-skeleton--text + .rv-skeleton--text { inline-size: 62%; }
.rv-offer--skeleton .rv-offer__aside .rv-skeleton--heading { inline-size: 56px; }
.rv-offer--skeleton .rv-offer__aside .rv-skeleton--text { inline-size: 84px; }

.rv-offer--skeleton .rv-offer__actions .rv-skeleton--pill {
  inline-size: 112px;
  block-size: 36px;
}

/* Schermi stretti (360 e 320): il prezzo scende sotto il nome, in riga con i
   minuti; le due azioni si dividono la larghezza a metà */
@media (max-width: 379.98px) {
  .rv-offer {
    grid-template-columns: auto minmax(0, 1fr);
  }

  .rv-offer__aside {
    grid-column: 2;
    grid-row: 2;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--rv-space-1) var(--rv-space-2);
    text-align: start;
  }

  .rv-offer__actions {
    grid-row: 3;
  }

  .rv-offer__actions > .rv-btn {
    flex: 1 1 0;
    min-inline-size: 0;
  }
}


/* ==========================================================================
   16. MAP — riquadro dell'immagine di un percorso (Такси)
   --------------------------------------------------------------------------
   L'immagine del percorso la genera il server (una mappa statica). Qui è
   SEMPRE decorativa (alt=""): tutte le informazioni stanno nella didascalia,
   che è obbligatoria. Così la scheda funziona anche quando la mappa non
   arriva — caso previsto e normale, non un errore: resta il fondo tenue con
   l'icona route al centro e la didascalia dice comunque il percorso.

   <figure class="rv-map">
     <div class="rv-map__frame">
       <span class="rv-icon rv-icon--route" aria-hidden="true"></span>
       <img src="…" alt="" loading="lazy" width="640" height="360">
     </div>
     <figcaption class="rv-map__caption">Виа Маттеотти, 12 → аэропорт Ниццы · 42&nbsp;км, около 55&nbsp;минут</figcaption>
   </figure>

   Senza immagine si toglie solo <img>: l'icona è già al centro del riquadro.
   Se l'immagine c'è ma non si carica, con alt="" il browser non disegna nulla
   sopra il fondo e si vede lo stesso segnaposto.
   ========================================================================== */
.rv-map {
  display: flex;
  flex-direction: column;
  gap: var(--rv-space-2);
  min-inline-size: 0;
  margin: 0;
}

.rv-map__frame {
  --rv-icon-size: 32px;
  position: relative;
  display: grid;
  place-items: center;
  aspect-ratio: 16 / 9;
  max-inline-size: 100%;
  overflow: hidden;
  border-radius: var(--rv-radius-lg);
  background-color: var(--rv-bg-muted);   /* segnaposto tenue in entrambi i temi */
  color: var(--rv-text-muted);
}

.rv-map__frame > img {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  border-radius: inherit;
  filter: var(--rv-image-dim);
}

.rv-map__caption {
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text-secondary);
  overflow-wrap: break-word;
}


/* ==========================================================================
   17. CHAT — la conversazione fra passeggero e autista (Такси)
   --------------------------------------------------------------------------
   Il primo canale privato fra due utenti dell'app: bolle mie a destra sul
   pieno --rv-primary, bolle dell'altro a sinistra su --rv-surface, con in
   mezzo i separatori di giornata e i messaggi di sistema. La barra di
   scrittura NON sta qui: è .rv-chat-bar (navigation.css), fissa in fondo.

   <div class="rv-chat" role="log" aria-label="Переписка с водителем">
     <div class="rv-divider rv-divider--label rv-chat__day" role="separator">сегодня</div>

     <p class="rv-chat__system">Водитель выбран: Сергей К.</p>

     <article class="rv-chat__message rv-chat__message--them">
       <div class="rv-chat__bubble">
         <p class="rv-chat__text">Подъезжаю, серый Fiat Tipo.</p>
         <p class="rv-chat__meta"><time class="rv-chat__time" datetime="2025-09-19T14:32">14:32</time></p>
       </div>
     </article>

     <article class="rv-chat__message rv-chat__message--them rv-chat__message--stacked">
       <div class="rv-chat__bubble">…</div>
     </article>

     <article class="rv-chat__message rv-chat__message--mine">
       <div class="rv-chat__bubble">
         <p class="rv-chat__text">Вижу, выхожу.</p>
         <p class="rv-chat__meta">
           <time class="rv-chat__time" datetime="2025-09-19T14:33">14:33</time>
           <span class="rv-chat__status"><span class="rv-icon rv-icon--check" aria-hidden="true"></span>отправлено</span>
         </p>
       </div>
     </article>

     <article class="rv-chat__message rv-chat__message--mine" aria-busy="true">
       <div class="rv-chat__bubble">
         <p class="rv-chat__text">Беру чемодан.</p>
         <p class="rv-chat__meta">
           <time class="rv-chat__time" datetime="2025-09-19T14:34">14:34</time>
           <span class="rv-chat__status"><span class="rv-icon rv-icon--clock" aria-hidden="true"></span>отправляется</span>
         </p>
       </div>
     </article>
   </div>

   Parti
   __day      separatore di giornata: è un .rv-divider--label («сегодня»,
              «19 сентября»), qui solo per il respiro che lo circonda.
   __system   messaggio di sistema al centro («водитель выбран», «чат
              закрыт»): pillola tenue, non è di nessuno dei due.
   __message  la riga; il lato lo dà --mine o --them, mai l'ordine nel DOM.
   __bubble   la bolla; __text il testo; __meta l'ora e, sulle mie, lo stato.

   Regole
   - INVIO OTTIMISTICO: il messaggio compare subito con
     .rv-chat__message[aria-busy="true"] e __status «отправляется» (icona
     clock); quando il server conferma, aria-busy sparisce e lo stato diventa
     «отправлено» (icona check). Lo stato è una PAROLA, non un colore né una
     sola spunta: l'icona accompagna, non sostituisce.
   - IL BECCO è l'angolo vivo in alto, dal lato di chi parla, e lo ha solo il
     PRIMO messaggio del gruppo: i successivi prendono --stacked, che chiude
     tutti gli angoli e li avvicina (4px invece di 12px). Un becco disegnato
     con uno pseudo-elemento triangolare andrebbe ridipinto a ogni tema e a
     ogni fondo: l'angolo è la stessa informazione senza quel debito.
   - TESTI SENZA SPAZI (IBAN, link, codici): __text ha overflow-wrap anywhere,
     che entra anche nel calcolo della larghezza minima. Senza, un IBAN
     allargherebbe la bolla, poi la colonna, poi la pagina. La bolla ha
     min-inline-size: 0 perché il figlio flex possa davvero restringersi.
   - COLORI: la bolla mia è --rv-primary con --rv-text-on-primary (coppia già
     verificata da check-contrast in entrambi i temi); ora e stato usano lo
     stesso token pieno, mai una versione sbiadita. La bolla dell'altro è
     --rv-surface con --rv-text e --rv-text-muted per la meta, come una card.
   - Un link dentro la bolla mia non può restare verde su verde: prende il
     colore della bolla e si riconosce dalla sottolineatura di base.css.
   - role="log" (che implica aria-live polite e aria-relevant additions) va
     sul contenitore, così i messaggi nuovi si annunciano da soli. Il nome
     («Переписка с водителем») dice con chi si sta parlando.
   ========================================================================== */
.rv-chat {
  --rv-chat-bubble-max: 84%;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--rv-space-1);   /* messaggi consecutivi dello stesso autore */
  min-inline-size: 0;
}

/* Fra un gruppo e l'altro: 4 di gap + 8 = 12px. Il combinatore + esclude il
   primo figlio, che non deve mai staccarsi dal bordo di sopra. */
.rv-chat > * + *:not(.rv-chat__message--stacked) {
  margin-block-start: var(--rv-space-2);
}

/* Giornata e messaggi di sistema tagliano il flusso: 4 + 20 = 24px attorno */
.rv-chat > * + .rv-chat__day,
.rv-chat > * + .rv-chat__system,
.rv-chat > .rv-chat__day + *,
.rv-chat > .rv-chat__system + * {
  margin-block-start: var(--rv-space-5);
}

.rv-chat__system {
  align-self: center;
  max-inline-size: var(--rv-chat-bubble-max);
  padding-block: var(--rv-space-1);
  padding-inline: var(--rv-space-3);
  border-radius: var(--rv-radius-pill);
  background-color: var(--rv-bg-muted);
  color: var(--rv-text-secondary);
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
  text-align: center;
  overflow-wrap: break-word;
}

.rv-chat__message {
  display: flex;
  min-inline-size: 0;
}

.rv-chat__message--them {
  justify-content: flex-start;
}

.rv-chat__message--mine {
  justify-content: flex-end;
}

.rv-chat__bubble {
  display: flex;
  flex-direction: column;
  gap: var(--rv-space-0-5);
  max-inline-size: var(--rv-chat-bubble-max);
  min-inline-size: 0;   /* senza, un IBAN terrebbe la bolla larga quanto lui */
  padding-block: var(--rv-space-2);
  padding-inline: var(--rv-space-3);
  border-radius: var(--rv-radius-lg);
  background-color: var(--rv-surface);
  box-shadow: var(--rv-shadow-xs);
  color: var(--rv-text);
}

/* Il becco: angolo vivo in alto dal lato di chi parla */
.rv-chat__message--them .rv-chat__bubble {
  border-start-start-radius: var(--rv-radius-xs);
}

.rv-chat__message--mine .rv-chat__bubble {
  border-start-end-radius: var(--rv-radius-xs);
  background-color: var(--rv-primary);
  box-shadow: none;   /* il pieno verde si stacca già dal fondo pagina */
  color: var(--rv-text-on-primary);
}

/* Messaggio successivo dello stesso autore: niente becco, tutti gli angoli
   pieni. Viene dopo le due regole di sopra: stessa specificità, vince l'ordine. */
.rv-chat__message--stacked .rv-chat__bubble {
  border-radius: var(--rv-radius-lg);
}

.rv-chat__text {
  font-size: var(--rv-fs-body);
  line-height: var(--rv-lh-body);
  overflow-wrap: break-word;
  overflow-wrap: anywhere;   /* motori vecchi scartano il valore e restano su break-word */
}

/* Verde su verde non si legge: nella bolla mia il link prende il colore della
   bolla e resta riconoscibile dalla sottolineatura di base.css */
.rv-chat__message--mine .rv-chat__bubble a {
  color: inherit;
}

.rv-chat__meta {
  --rv-icon-size: 14px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--rv-space-0-5) var(--rv-space-2);
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text-muted);
}

.rv-chat__message--mine .rv-chat__meta {
  justify-content: flex-end;
  color: var(--rv-text-on-primary);   /* pieno: sul verde una versione sbiadita non farebbe AA */
}

.rv-chat__time {
  font-variant-numeric: lining-nums tabular-nums;
}

.rv-chat__status {
  display: inline-flex;
  align-items: center;
  gap: var(--rv-space-1);
}

/* Contrasto elevato di Windows: i fondi spariscono e verde e superficie
   diventano lo stesso colore. Il lato distingue ancora chi parla, ma senza
   un bordo le bolle non si vedrebbero affatto: resta il contorno. */
@media (forced-colors: active) {
  .rv-chat__bubble,
  .rv-chat__system {
    border: 1px solid CanvasText;
  }
}


/* ==========================================================================
   18. CALENDAR — il mese delle notti libere e occupate (Недвижимость)
   --------------------------------------------------------------------------
   Una griglia di mese: sette colonne, i nomi dei giorni in testa, le celle
   vuote prima del primo del mese e una cella per ogni notte. Due usi con lo
   STESSO markup, come .rv-route:
   · INTERATTIVO, per chi pubblica l'annuncio: le celle sono <button>, si
     toccano per chiudere o riaprire un periodo e per dare un prezzo a una
     stagione (griglia da toccare, non due campi «da» e «a»);
   · SOLA LETTURA (.rv-calendar--readonly), nella scheda dell'annuncio: le
     stesse celle sono <span>, dicono soltanto quali notti sono libere.

   Le stringhe russe (mese, giorni, stati) NON stanno qui: le compone la
   pagina. Il componente dà la geometria, gli stati e i posti dove metterle.

   <section class="rv-calendar" aria-labelledby="cal-9">
     <div class="rv-calendar__head">
       <button class="rv-icon-btn rv-icon-btn--sm rv-calendar__nav" type="button" aria-label="Предыдущий месяц">
         <span class="rv-icon rv-icon--chevron-left" aria-hidden="true"></span>
       </button>
       <p class="rv-calendar__month" id="cal-9" aria-live="polite" aria-atomic="true">Сентябрь 2026</p>
       <button class="rv-icon-btn rv-icon-btn--sm rv-calendar__nav" type="button" aria-label="Следующий месяц">
         <span class="rv-icon rv-icon--chevron-right" aria-hidden="true"></span>
       </button>
     </div>
     <table class="rv-calendar__grid" role="grid" aria-multiselectable="true" aria-labelledby="cal-9">
       <thead>
         <tr>
           <th class="rv-calendar__weekday" scope="col">
             <span aria-hidden="true">Пн</span><span class="rv-visually-hidden">понедельник</span>
           </th>
           … Вт Ср Чт Пт Сб Вс
         </tr>
       </thead>
       <tbody>
         <tr>
           <td class="rv-calendar__cell"></td>                       (celle vuote di testa: vuote davvero)
           <td class="rv-calendar__cell">
             <button class="rv-calendar__day rv-calendar__day--past" type="button" tabindex="-1" aria-disabled="true">
               <span class="rv-calendar__num rv-num" aria-hidden="true">1</span>
               <span class="rv-visually-hidden">1 сентября, прошедшая дата</span>
             </button>
           </td>
           <td class="rv-calendar__cell" aria-selected="true">
             <button class="rv-calendar__day rv-calendar__day--chosen rv-calendar__day--today" type="button" tabindex="0">
               <span class="rv-calendar__num rv-num" aria-hidden="true">20</span>
               <span class="rv-calendar__price rv-num" aria-hidden="true">90&#8239;€</span>
               <span class="rv-visually-hidden">20 сентября, 90 евро за ночь, сегодня, выбрано</span>
             </button>
           </td> …
         </tr>
       </tbody>
     </table>
     <ul class="rv-calendar__legend">
       <li class="rv-calendar__key"><span class="rv-calendar__swatch" aria-hidden="true"></span>Свободно</li>
       <li class="rv-calendar__key"><span class="rv-calendar__swatch rv-calendar__swatch--busy" aria-hidden="true"></span>Занято</li>
       <li class="rv-calendar__key"><span class="rv-calendar__swatch rv-calendar__swatch--half" aria-hidden="true"></span>Заезд или выезд</li>
     </ul>
     <p class="rv-calendar__hint">Нажмите на даты, чтобы закрыть или открыть период.</p>
   </section>

   In SOLA LETTURA cambia una cosa sola: la cella è
   <span class="rv-calendar__day rv-calendar__day--busy"> invece del <button>,
   senza tabindex né aria-*; la <table> non ha role="grid" e non ha __hint.
   Geometria, nomi dei giorni, celle vuote e stati restano identici — per
   questo è un blocco solo con un modificatore. Il modificatore toglie anche
   l'interazione (pointer-events), così una vista costruita da un template che
   ha lasciato i <button> non promette un tocco che non c'è.

   Stati della cella (modificatori di __day, cumulabili)
   --busy       notte occupata        --chosen     notte scelta ora
   --arrival    sera occupata         --in-range   dentro l'intervallo scelto
   --departure  mattina occupata      --today      oggi
   --past       passata, spenta

   Regole
   - IL COLORE NON BASTA MAI. «Занято» ha il numero barrato, «заезд/выезд» la
     diagonale piena, «сегодня» il punto sotto il numero, e OGNI cella porta
     la sua parola in .rv-visually-hidden. Il numero visibile è aria-hidden:
     il nome della cella è la riga nascosta, che dice data, prezzo e stato per
     intero («12 сентября, занято») invece del solo «12».
   - LA CELLA PASSATA USA aria-disabled="true", MAI disabled: un <button>
     disabled non prende il fuoco e i lettori di schermo lo saltano, quindi
     chi naviga da tastiera non saprebbe nemmeno che quel giorno esiste. Con
     aria-disabled la cella resta nella griglia, si annuncia e non si attiva.
   - UNA SOLA CELLA NEL GIRO DEL TAB: tabindex="0" sulla cella attiva (oggi,
     o l'inizio dell'intervallo scelto), tabindex="-1" su tutte le altre. Il
     Tab entra ed esce dalla griglia in un colpo; dentro ci si muove con le
     frecce (←/→ giorno, ↑/↓ settimana, Home/End inizio e fine settimana,
     PageUp/PageDown mese), come nella griglia di date WAI-ARIA e come le
     schede di .rv-segmented.
   - IL MESE CAMBIA SENZA PERDERE IL FUOCO: quando una freccia esce dal mese,
     la pagina ridisegna la griglia, mette tabindex="0" sulla cella della
     nuova data e mette a fuoco lei; __month ha aria-live="polite" e
     aria-atomic="true", così il nome del mese nuovo si sente. I due __nav
     restano dove sono e il fuoco non si muove da loro.
   - La scelta si dichiara due volte, e sono due cose diverse: le classi
     --chosen / --in-range disegnano, aria-selected sul <td> (che dentro
     role="grid" è una gridcell) dice alla griglia che cosa è scelto. Il CSS
     legge solo le classi: la specificità resta piatta.
   - LARGHEZZA. Sette bersagli da 44px non stanno nei 328px che .rv-container
     lascia a 360: con 8px di padding si arriva a 44,57 e con in più 2px di
     gap si scende a 42,86. Quindi il calendario sconfina come
     .rv-scroll-x--bleed (margin-inline: calc(-1 * var(--rv-gutter))) e la
     griglia prende tutta la larghezza dello schermo: la colonna misura
     50,29px a 360 e 44,57px a 320, e smette di rispettare i 44px sotto i
     316px di schermo. Testata, legenda e nota restano allineate alla colonna
     con il loro padding, come fa .rv-listing-head--bleed con le sue
     etichette. Per questo il calendario sta DIRETTAMENTE in .rv-container:
     dentro una card (.rv-property__body, 16px di padding) resterebbero
     42,29px.
   - Lo stacco di 2px fra le celle è padding della cella con background-clip:
     content-box, non un gap della tabella: il bersaglio resta la cella
     intera, il riquadro colorato è quello che si vede. La griglia tiene da
     parte l'anello di fuoco (2px + 2px) ai due lati, così il fuoco della
     prima e dell'ultima colonna non finisce fuori dallo schermo.
   - Prezzo: .rv-calendar__price, facoltativo. Se c'è in una cella mettilo in
     tutte quelle del mese (anche vuoto): la riga resta regolare.
   ========================================================================== */
.rv-calendar {
  --rv-calendar-gap: var(--rv-space-0-5);
  --rv-calendar-cell: var(--rv-tap);
  display: flex;
  flex-direction: column;
  gap: var(--rv-space-3);
  min-inline-size: 0;
  /* Sconfina dal gutter: vedi «LARGHEZZA» qui sopra */
  margin-inline: calc(-1 * var(--rv-gutter));
}

.rv-calendar__head {
  display: flex;
  align-items: center;
  gap: var(--rv-space-2);
  padding-inline: var(--rv-gutter);
}

.rv-calendar__month {
  flex: 1 1 auto;
  min-inline-size: 0;
  margin: 0;
  font-size: var(--rv-fs-headline);
  font-weight: var(--rv-fw-semibold);
  line-height: var(--rv-lh-headline);
  text-align: center;
  color: var(--rv-text);
}

/* Oltre il primo o l'ultimo mese apribile il comando resta nel giro del Tab
   con aria-disabled: sparire gli toglierebbe il posto sotto il dito. */
.rv-calendar__nav[aria-disabled="true"] {
  --rv-icon-btn-fg: var(--rv-text-muted);
  cursor: default;
}

.rv-calendar__grid {
  inline-size: 100%;
  /* Posto per l'anello di fuoco della prima e dell'ultima colonna, che
     altrimenti finisce fuori dallo schermo. Il padding di una <table> vale
     solo con border-collapse: separate (CSS 2.1 §17.6.2); border-spacing
     resta a zero, perché lo stacco fra le celle lo fa .rv-calendar__day. */
  padding-inline: calc(var(--rv-focus-width) + var(--rv-focus-offset));
  table-layout: fixed;
  border-collapse: separate;
  border-spacing: 0;
}

.rv-calendar__weekday {
  padding: 0 0 var(--rv-space-1-5);
  font-size: var(--rv-fs-overline);
  font-weight: var(--rv-fw-semibold);
  line-height: var(--rv-lh-overline);
  letter-spacing: var(--rv-tracking-overline);
  text-transform: uppercase;
  color: var(--rv-text-muted);
}

.rv-calendar__cell {
  block-size: var(--rv-calendar-cell);
  padding: 0;
}

/* La cella intera è il bersaglio (≥44px); il padding di un mezzo gap con
   background-clip: content-box lascia i 2px di stacco senza rimpicciolirlo. */
.rv-calendar__day {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  inline-size: 100%;
  block-size: 100%;
  min-block-size: var(--rv-calendar-cell);
  padding: calc(var(--rv-calendar-gap) / 2);
  border: 0;
  border-radius: var(--rv-radius-sm);
  background-color: var(--rv-surface);
  background-clip: content-box;
  color: var(--rv-text);
  font-family: var(--rv-font-sans);
  -webkit-appearance: none;
  appearance: none;
  cursor: pointer;
  transition: background-color var(--rv-dur-fast) var(--rv-ease-out),
    color var(--rv-dur-fast) var(--rv-ease-out),
    transform var(--rv-dur-fast) var(--rv-ease-out);
}

.rv-calendar__num {
  font-size: var(--rv-fs-callout);
  line-height: 1;
}

.rv-calendar__price {
  margin-block-start: var(--rv-space-0-5);
  font-size: var(--rv-fs-tag);
  line-height: var(--rv-lh-tag);
  color: var(--rv-text-muted);
}

/* Notte occupata: fondo incassato E numero barrato, perché il fondo da solo
   è una differenza di tono, non un'informazione. */
.rv-calendar__day--busy {
  background-color: var(--rv-surface-sunken);
  color: var(--rv-text-muted);
}

.rv-calendar__day--busy .rv-calendar__num {
  text-decoration: line-through;
}

/* Mezza notte: chi parte libera la mattina (--departure, triangolo in alto a
   sinistra), chi arriva occupa la sera (--arrival, in basso a destra).
   Stop netti: nessuna sfumatura da interpretare. */
.rv-calendar__day--departure {
  background-image: linear-gradient(135deg, var(--rv-surface-sunken) 0 50%, transparent 50%);
}

.rv-calendar__day--arrival {
  background-image: linear-gradient(135deg, transparent 0 50%, var(--rv-surface-sunken) 50%);
}

/* Passata: spenta e senza fondo. Resta focalizzabile (aria-disabled), quindi
   chi naviga da tastiera la sente comunque. */
.rv-calendar__day--past,
.rv-calendar__day[aria-disabled="true"] {
  background-color: transparent;
  background-image: none;
  color: var(--rv-text-muted);
}

.rv-calendar__day[aria-disabled="true"] {
  cursor: default;
}

.rv-calendar__day--in-range {
  background-color: var(--rv-primary-soft);
  background-image: none;
  color: var(--rv-primary-soft-text);
}

.rv-calendar__day--chosen {
  background-color: var(--rv-primary);
  background-image: none;
  color: var(--rv-text-on-primary);
  font-weight: var(--rv-fw-semibold);
}

/* Sul verde il prezzo non può restare --rv-text-muted: prende il colore della
   cella, che è già misurato su quel fondo. */
.rv-calendar__day--in-range .rv-calendar__price,
.rv-calendar__day--chosen .rv-calendar__price {
  color: inherit;
}

/* Oggi: punto sotto il numero in currentColor, così si vede anche sul pieno
   verde di --chosen senza una seconda coppia di colori da verificare. */
.rv-calendar__day--today::after {
  content: "";
  position: absolute;
  inset-block-end: var(--rv-space-1);
  inset-inline: 0;
  inline-size: var(--rv-space-1);
  block-size: var(--rv-space-1);
  margin-inline: auto;
  border-radius: var(--rv-radius-pill);
  background-color: currentColor;
}

/* Il fuoco esce di 4px dalla cella e finirebbe sotto il fondo della vicina */
.rv-calendar__day:focus-visible {
  z-index: 1;
}

.rv-calendar__day:active {
  transform: scale(var(--rv-press-scale));
}

.rv-calendar__day[aria-disabled="true"]:active {
  transform: none;
}

@media (hover: hover) {
  .rv-calendar__day:hover {
    background-color: var(--rv-surface-hover);
  }

  .rv-calendar__day--in-range:hover {
    background-color: var(--rv-primary-soft);
  }

  .rv-calendar__day--chosen:hover {
    background-color: var(--rv-primary-hover);
  }

  .rv-calendar__day[aria-disabled="true"]:hover {
    background-color: transparent;
  }
}

/* --- Legenda e nota: allineate alla colonna, non al bordo dello schermo --- */
.rv-calendar__legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--rv-space-2) var(--rv-space-4);
  margin: 0;
  padding-inline: var(--rv-gutter);
  list-style: none;
}

.rv-calendar__key {
  display: flex;
  align-items: center;
  gap: var(--rv-space-1-5);
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text-secondary);
}

.rv-calendar__swatch {
  flex: none;
  inline-size: var(--rv-space-4);
  block-size: var(--rv-space-4);
  border-radius: var(--rv-radius-xs);
  background-color: var(--rv-surface);
  box-shadow: inset 0 0 0 var(--rv-border-width) var(--rv-border);
}

.rv-calendar__swatch--busy {
  background-color: var(--rv-surface-sunken);
}

.rv-calendar__swatch--half {
  background-image: linear-gradient(135deg, transparent 0 50%, var(--rv-surface-sunken) 50%);
}

.rv-calendar__hint {
  margin: 0;
  padding-inline: var(--rv-gutter);
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text-muted);
}

/* Sola lettura: niente da toccare. Come in .rv-route--readonly il
   modificatore lo impone anche se un template ha lasciato dei <button>. */
.rv-calendar--readonly .rv-calendar__grid {
  pointer-events: none;
}

/* Contrasto elevato di Windows: i fondi e la diagonale spariscono. Restano il
   numero barrato e la parola nascosta di ogni cella; l'intervallo scelto, che
   non ha un segno suo, prende il contorno. */
@media (forced-colors: active) {
  .rv-calendar__day--chosen,
  .rv-calendar__day--in-range {
    border: var(--rv-border-width) solid CanvasText;
  }
}


/* ==========================================================================
   19. RAIL CON PUNTATORE FINE — scrollbar sottile
   --------------------------------------------------------------------------
   Con il mouse la rotella verticale fa scorrere la pagina, non il rail, e
   non ci sono frecce: senza scrollbar le voci fuori schermo non si
   raggiungono (Telegram Desktop, Web, macOS). Solo con hover + puntatore
   fine; al tocco la scrollbar resta nascosta (regole in cima al file).
   Stessa barra di .rv-scroll-x in base.css: 6px, pollice arrotondato
   --rv-border-strong (--rv-text-muted all'hover), binario trasparente e
   rientrato del gutter, così la barra si allinea al contenuto.
   Due strade per la stessa barra:
   - Chromium e Safari: ::-webkit-scrollbar. Chromium dalla 121 lo ignora se
     scrollbar-width o scrollbar-color non sono «auto» (disegnerebbe la barra
     nativa, con le frecce su Windows): per questo lì scrollbar-width torna auto;
   - Firefox: scrollbar-width thin + scrollbar-color, solo dove
     ::-webkit-scrollbar non esiste.
   Distanza dalle voci: le tessere hanno già 12px sotto (spazio per l'ombra),
   la galleria prende 8px. Il carosello di card tiene i suoi 24px (l'ombra di
   hover, profonda di notte, non va tagliata) ma toglie il margine negativo:
   il distacco della pagina riparte sotto la barra (card · 24 · barra ·
   distacco), invece di lasciarla incollata al blocco successivo.
   ========================================================================== */
@media (hover: hover) and (pointer: fine) {
  .rv-tile-rail,
  .rv-card-rail,
  .rv-gallery--rail {
    scrollbar-width: auto;
  }

  @supports not selector(::-webkit-scrollbar) {
    .rv-tile-rail,
    .rv-card-rail,
    .rv-gallery--rail {
      scrollbar-width: thin;
      scrollbar-color: var(--rv-border-strong) transparent;
    }
  }

  .rv-card-rail {
    margin-block-end: 0;
  }

  .rv-gallery--rail {
    padding-block-end: var(--rv-space-2);
  }

  .rv-tile-rail::-webkit-scrollbar,
  .rv-card-rail::-webkit-scrollbar,
  .rv-gallery--rail::-webkit-scrollbar {
    display: block;
    height: 6px;
  }

  .rv-tile-rail::-webkit-scrollbar-track,
  .rv-card-rail::-webkit-scrollbar-track,
  .rv-gallery--rail::-webkit-scrollbar-track {
    margin: 0 var(--rv-gutter);
    background-color: transparent;
  }

  .rv-tile-rail::-webkit-scrollbar-thumb,
  .rv-card-rail::-webkit-scrollbar-thumb,
  .rv-gallery--rail::-webkit-scrollbar-thumb {
    border-radius: var(--rv-radius-pill);
    background-color: var(--rv-border-strong);
  }

  .rv-tile-rail::-webkit-scrollbar-thumb:hover,
  .rv-card-rail::-webkit-scrollbar-thumb:hover,
  .rv-gallery--rail::-webkit-scrollbar-thumb:hover {
    background-color: var(--rv-text-muted);
  }
}


/* ─── src/components/navigation.css ─────────────────────────────── */
/* ==========================================================================
   Riviera · components/navigation.css
   Navigazione e stati: .rv-app, .rv-appbar, .rv-hero, .rv-tabbar, .rv-sheet
   (--tall), .rv-dialog, .rv-toast, .rv-empty (--success), .rv-error,
   .rv-notice, .rv-skeleton, .rv-spinner, .rv-dots, .rv-steps, .rv-sticky,
   .rv-chat-bar (--closed), .rv-mainbutton-spacer, .rv-sim-mainbutton, .rv-viewer

   Regole del file
   - Solo token semantici (--rv-bg, --rv-text, --rv-primary, …) e le scale di
     tokens.css. Niente colori nei blocchi tema: il tema lo fanno i token.
   - Specificità piatta: una classe, al massimo classe + stato/modificatore.
   - Stati con attributi quando esistono ([aria-current], [aria-busy],
     [disabled]); altrimenti .is-* (.is-closing, .is-leaving, .is-stuck).
   - Hover solo con @media (hover: hover); pressione = scale(--rv-press-scale).
   - Con prefers-reduced-motion (e con html[data-tg-performance="low"])
     base.css porta a 0 transizioni e animazioni: sheet, dialog e toast
     compaiono senza movimento, lo shimmer si ferma, .rv-spinner e gli
     spinner di [aria-busy="true"] continuano a girare (attesa in corso).
   - Testo sopra una foto: --rv-on-image (+ --rv-on-image-scrim come alone),
     uguali nei due temi perché la foto è la stessa.
   - Le variabili locali dei componenti (--rv-toast-tone, --rv-spinner-size…)
     si dichiarano sull'elemento stesso, così si risolvono nel tema giusto.
   - color-mix() solo dentro @supports (color: color-mix(in srgb, red, blue)),
     sempre dopo un ripiego a token pieno. Una dichiarazione con var() non
     «ricade» su quella precedente dove color-mix() non esiste (WebView
     vecchie: iOS < 16.2, Chromium < 111): diventa il valore iniziale, cioè
     niente sfumatura, niente fondo, spinner invisibile.
   - Verde come TESTO (azione del toast): --rv-link / --rv-link-hover, AA su
     surface-raised e surface-hover in entrambi i temi. --rv-primary resta
     per i pieni: di notte come testo su surface-raised fa 4,04:1, sotto AA.

   Indice
    1. .rv-app                 contenitore dell'app
    2. .rv-appbar              barra in alto (marchio, titolo, azioni)
    3. .rv-hero                apertura della home
    4. .rv-tabbar              navigazione in basso, a tutta larghezza
    5. .rv-sheet               bottom sheet
   5b. .rv-sheet--tall         pannello a tutta altezza (elenchi che si riempiono)
   5c. .rv-sheet--full         il foglio prende tutto lo schermo (passaggi, non dettagli)
    6. .rv-dialog              finestra di conferma
    7. .rv-toast               avviso breve
    8. .rv-empty / .rv-error   stato vuoto, errore con riprova
   8b. .rv-empty--success      stato di successo
   8c. .rv-notice              avviso in linea (neutro, info, warning, success, danger)
    9. .rv-skeleton            segnaposto di caricamento
   10. .rv-spinner             attesa
   11. .rv-dots                paginazione del carosello
  11b. .rv-steps               avanzamento di una procedura a passi
   12. .rv-sticky              barra che resta in alto
  12b. .rv-chat-bar            barra di scrittura della chat, fissa in fondo
   13. .rv-mainbutton-spacer   spazio per il MainButton di Telegram
   14. .rv-sim-mainbutton      MainButton simulato (solo demo nel browser)
  14b. .rv-viewer              visore foto a tutto schermo su <dialog>
   15. Animazioni
   ========================================================================== */


/* ==========================================================================
   1. .rv-app — contenitore dell'app
   Rispetta le aree sicure (dispositivo + controlli di Telegram) e, con
   --tabbar, lascia in fondo lo spazio della tab bar flottante.

   <body>
     <div class="rv-app rv-app--tabbar">
       <header class="rv-appbar rv-appbar--transparent">…</header>
       <section class="rv-hero">…</section>
       <main class="rv-container">…</main>
       <nav class="rv-tabbar" aria-label="Разделы">…</nav>
     </div>
   ========================================================================== */
.rv-app {
  position: relative;               /* riferimento per l'appbar trasparente */
  display: flow-root;
  min-block-size: var(--tg-viewport-stable-height, 100vh);
  padding-block: var(--rv-safe-top) calc(var(--rv-safe-bottom) + var(--rv-space-6));
  padding-inline: var(--rv-safe-left) var(--rv-safe-right);
  overflow-x: clip;                 /* clip e non hidden: .rv-sticky continua a funzionare */
  background-color: var(--rv-bg);
  color: var(--rv-text);
}

/* dvh dentro var() senza supporto renderebbe la dichiarazione «unset»: va in @supports. */
@supports (height: 100dvh) {
  .rv-app {
    min-block-size: var(--tg-viewport-stable-height, 100dvh);
  }
}

/* Schermata radice con tab bar: la barra (area sicura compresa) + respiro */
.rv-app--tabbar {
  padding-block-end: calc(var(--rv-safe-bottom) + var(--rv-tabbar-h) + var(--rv-space-6));
}

/* Tastiera aperta: la tab bar non c'è (§4) e con lei se ne va il motivo di
   questo spazio, che resterebbe un buco morto in fondo mentre si scrive.
   Torna il fondo di .rv-app. La classe la mette js/riviera-telegram.js:
   senza lo script non compare mai e qui non cambia niente. */
html.rv-keyboard-open .rv-app--tabbar {
  padding-block-end: calc(var(--rv-safe-bottom) + var(--rv-space-6));
}

/* Schermata di chat: la barra di scrittura è fissa in fondo, quindi l'ultimo
   messaggio della conversazione deve fermarsi sopra di lei. Si conta la
   barra a riposo (--rv-chat-bar-h): quando il campo cresce, sotto di lui
   c'è solo la fine della conversazione e chi scrive la sta già guardando. */
.rv-app--chat-bar {
  padding-block-end: calc(var(--rv-safe-bottom) + var(--rv-chat-bar-h) + var(--rv-space-6));
}


/* ==========================================================================
   1b. Fasce libere per il focus (WCAG 2.4.11, focus mai coperto)
   base.css mette su html lo scroll-padding con due variabili; qui si alzano
   quando qualcosa di fisso o appiccicoso copre il bordo dello schermo, così
   Tab e scrollIntoView() lasciano l'elemento a fuoco fuori dalle barre.
   Misure reali, più --rv-space-3 di respiro per l'anello di focus:
   - tab bar attaccata al fondo: area sicura + --rv-tabbar-h (64px sopra il
     fondo senza area sicura → 76px);
   - MainButton simulato: area sicura + padding sopra e sotto (--rv-space-2)
     + bottone da --rv-mainbutton-h (66px → 78px). Dentro Telegram il bottone
     vero sta fuori dalla webview e la regola non scatta;
   - barra di scrittura .rv-chat-bar: area sicura + --rv-chat-bar-h (60px: il
     campo a una riga da 44 + 8 sopra e sotto → 72px). Il campo che cresce non
     conta: sopra la barra c'è solo la fine della conversazione, e mentre si
     scrive il focus è nel campo;
   - barra .rv-sticky: area sicura + --rv-sticky-h (60px: riga da 44 + 8 sopra
     e sotto). Una barra più alta ridefinisce --rv-sticky-h su :root.
   Due strade, stessi valori: html:has(…) dove :has() esiste; per le WebView
   senza :has() le classi rv-has-* che js/riviera-telegram.js mette su <html>
   (RivieraTelegram.refreshLayout()). Regole separate: un selettore con :has()
   invaliderebbe tutta la lista. Le regole :has() pesano (0,2,1), quelle con
   la classe (0,1,1); dove :has() esiste le classi, se ci sono, danno lo
   stesso valore. In entrambe le strade la tab bar (più alta) viene dopo il
   MainButton: se una pagina li mostrasse insieme, vince lei.
   Le copie statiche delle demo accendono solo la versione :has(): al più un
   po' di scroll-padding in più nella pagina della styleguide, innocuo.
   Con la tastiera aperta (html.rv-keyboard-open) la tab bar sparisce, quindi
   non copre più niente: le sue due regole si spengono con :not(.rv-keyboard-open)
   e la fascia libera torna quella di base. Si spegne la regola invece di
   aggiungerne una che la sovrascriva, perché quella con :has() pesa (0,2,1) e
   vincerebbe su una classe sola. La barra di scrittura .rv-chat-bar e il
   MainButton simulato, che con la tastiera restano, tengono la loro fascia.
   ========================================================================== */
html:where(:not(.rv-in-telegram)):has(:is(.rv-sim-mainbutton, [data-rv-sim="main"]):not([hidden])) {
  --rv-scroll-pad-bottom: calc(var(--rv-safe-bottom) + 2 * var(--rv-space-2) + var(--rv-mainbutton-h, 50px) + var(--rv-space-3));
}

html.rv-has-mainbutton-sim {
  --rv-scroll-pad-bottom: calc(var(--rv-safe-bottom) + 2 * var(--rv-space-2) + var(--rv-mainbutton-h, 50px) + var(--rv-space-3));
}

html:has(.rv-chat-bar:not([hidden])) {
  --rv-scroll-pad-bottom: calc(var(--rv-safe-bottom) + var(--rv-chat-bar-h) + var(--rv-space-3));
}

html.rv-has-chat-bar {
  --rv-scroll-pad-bottom: calc(var(--rv-safe-bottom) + var(--rv-chat-bar-h) + var(--rv-space-3));
}

html:not(.rv-keyboard-open):has(.rv-tabbar:not([hidden])) {
  --rv-scroll-pad-bottom: calc(var(--rv-safe-bottom) + var(--rv-tabbar-h) + var(--rv-space-3));
}

html.rv-has-tabbar:not(.rv-keyboard-open) {
  --rv-scroll-pad-bottom: calc(var(--rv-safe-bottom) + var(--rv-tabbar-h) + var(--rv-space-3));
}

html:has(.rv-sticky:not([hidden])) {
  --rv-scroll-pad-top: calc(var(--rv-safe-top) + var(--rv-sticky-h) + var(--rv-space-3));
}

html.rv-has-sticky {
  --rv-scroll-pad-top: calc(var(--rv-safe-top) + var(--rv-sticky-h) + var(--rv-space-3));
}


/* ==========================================================================
   2. .rv-appbar — barra in alto
   Leggera: il client Telegram ha già la sua intestazione. Niente CTA qui.
   Le azioni sono bottoni di actions.css (.rv-icon-btn, .rv-avatar).
   Il padding destro è ridotto di 6px: il bersaglio da 44px del cerchio da
   32px (avatar) allinea il bordo visibile al gutter di 16px.

   Home (marchio + avatar):
   <header class="rv-appbar">
     <a class="rv-appbar__brand" href="#/">
       <img class="rv-appbar__logo" src="assets/logo-palma.svg" alt="">
       <span class="rv-appbar__wordmark">Наши в Сан-Ремо</span>
     </a>
     <div class="rv-appbar__actions">
       <button class="rv-appbar__avatar" type="button" aria-label="Настройки">
         <span class="rv-avatar rv-avatar--sm rv-avatar--sabbia" aria-hidden="true">АМ</span>
       </button>
     </div>
   </header>

   Sopra l'hero: aggiungi .rv-appbar--transparent (va dentro .rv-app, prima
   dell'hero; è posizionata in assoluto e ha un velo di leggibilità).

   Sottopagina (titolo centrato; a sinistra il BackButton nativo o un
   .rv-icon-btn nel browser; slot vuoti ammessi, tengono il titolo al centro):
   <header class="rv-appbar rv-appbar--title">
     <div class="rv-appbar__start">…</div>
     <div class="rv-appbar__heading">
       <h1 class="rv-appbar__title">Дизайн и ремонт квартир</h1>
       <p class="rv-appbar__subtitle">Иван Петров · 12 отзывов</p>
     </div>
     <div class="rv-appbar__end">…</div>
   </header>
   ========================================================================== */
.rv-appbar {
  position: relative;
  z-index: var(--rv-z-sticky);
  display: flex;
  align-items: center;
  gap: var(--rv-space-2);
  min-block-size: var(--rv-appbar-h);
  padding-inline: var(--rv-gutter) calc(var(--rv-gutter) - var(--rv-space-1-5));
  color: var(--rv-text);
}

.rv-appbar__brand {
  display: inline-flex;
  align-items: center;
  gap: var(--rv-space-2);
  min-inline-size: 0;
  min-block-size: var(--rv-tap);
  margin-inline-end: auto;
  border-radius: var(--rv-radius-sm);
  color: var(--rv-heading);
  text-decoration: none;
  transition: transform var(--rv-dur-fast) var(--rv-ease-out);
}

.rv-appbar__brand:active {
  transform: scale(var(--rv-press-scale));
}

.rv-appbar__logo {
  flex: none;
  inline-size: 28px;
  block-size: 28px;
}

/* Wordmark: Playfair regolare come nel mockup, su una riga */
.rv-appbar__wordmark {
  min-inline-size: 0;
  overflow: hidden;
  font-family: var(--rv-font-display);
  font-size: var(--rv-fs-title-3);
  font-weight: var(--rv-fw-regular);
  line-height: var(--rv-lh-title-3);
  letter-spacing: var(--rv-tracking-title);
  white-space: nowrap;
  text-overflow: ellipsis;
}

.rv-appbar__actions {
  display: flex;
  flex: none;
  align-items: center;
  gap: var(--rv-space-1);
}

/* Bersaglio da 44px attorno all'avatar (Настройки) */
.rv-appbar__avatar {
  display: grid;
  flex: none;
  place-items: center;
  inline-size: var(--rv-tap);
  block-size: var(--rv-tap);
  border-radius: var(--rv-radius-pill);
  transition: transform var(--rv-dur-fast) var(--rv-ease-out);
}

.rv-appbar__avatar:active {
  transform: scale(var(--rv-press-scale));
}

/* --- Variante trasparente sopra l'hero --- */
.rv-appbar--transparent {
  position: absolute;
  inset-block-start: var(--rv-safe-top);
  inset-inline: 0;
}

/* Velo che scende dal colore del fondo: garantisce il contrasto del
   wordmark in entrambi i temi (crema di giorno, verde-carbone di notte).
   La maschera lo tiene a sinistra, sotto il marchio: a destra l'immagine
   resta piena (l'avatar ha già il suo fondo).
   Qui NON si usa --rv-on-image-scrim: il wordmark è l'inchiostro del marchio
   (--rv-heading, come nell'appbar normale) e l'hero deve sciogliersi nel
   crema, non scurirsi. Il velo scuro serve al testo crema su foto (la firma). */
.rv-appbar--transparent::before {
  content: "";
  position: absolute;
  inset-block: calc(-1 * var(--rv-safe-top)) calc(-1 * var(--rv-space-6));
  inset-inline: 0;
  z-index: -1;
  background-image: linear-gradient(to bottom, var(--rv-image-fade), transparent);   /* ripiego lineare */
  -webkit-mask-image: linear-gradient(to right, black 50%, transparent 92%);
  mask-image: linear-gradient(to right, black 50%, transparent 92%);   /* maschera: conta solo l'opacità */
  pointer-events: none;
}

@supports (color: color-mix(in srgb, red, blue)) {
  .rv-appbar--transparent::before {
    background-image: linear-gradient(
      to bottom,
      color-mix(in srgb, var(--rv-image-fade) 88%, transparent) 0%,
      color-mix(in srgb, var(--rv-image-fade) 70%, transparent) 45%,
      color-mix(in srgb, var(--rv-image-fade) 28%, transparent) 75%,
      transparent 100%
    );
  }
}

/* --- Variante con titolo centrato (sottopagine) --- */
.rv-appbar--title {
  display: grid;
  grid-template-columns: minmax(max-content, 1fr) minmax(0, auto) minmax(max-content, 1fr);
  padding-inline: var(--rv-space-1-5);   /* icona da 24 in un bersaglio da 44: il tratto cade a 16px */
}

.rv-appbar__start,
.rv-appbar__end {
  display: flex;
  align-items: center;
  gap: var(--rv-space-1);
  min-inline-size: var(--rv-tap);
}

.rv-appbar__start {
  justify-self: start;
}

.rv-appbar__end {
  justify-self: end;
}

.rv-appbar__heading {
  display: grid;
  min-inline-size: 0;
  padding-block: var(--rv-space-1);
  text-align: center;
}

.rv-appbar__title {
  overflow: hidden;
  font-family: var(--rv-font-display);
  font-size: var(--rv-fs-title-3);
  font-weight: var(--rv-fw-medium);
  line-height: var(--rv-lh-title-3);
  color: var(--rv-heading);
  white-space: nowrap;
  text-overflow: ellipsis;
}

.rv-appbar__subtitle {
  overflow: hidden;
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text-muted);
  white-space: nowrap;
  text-overflow: ellipsis;
}


/* ==========================================================================
   3. .rv-hero — apertura della home
   Immagine a tutta larghezza che sfuma nel fondo, firma a mano sull'immagine;
   sotto, sul crema: eyebrow, titolo con corsivo verde, pennellata, lede.
   Dentro .rv-app l'immagine sale sotto l'area sicura superiore.

   <section class="rv-hero" aria-labelledby="hero-title">
     <div class="rv-hero__media">
       <img class="rv-hero__image" src="assets/illustrazioni/riviera-hero.svg" alt="">
       <p class="rv-hero__signature" aria-hidden="true">San Remo Italia
         <span class="rv-icon rv-icon--heart rv-hero__heart"></span></p>
     </div>
     <div class="rv-hero__body">
       <p class="rv-hero__eyebrow">Люди и дело · русскоязычное сообщество</p>
       <h1 class="rv-hero__title" id="hero-title">Свои специалисты.<br>
         <em class="rv-accent-italic">Рядом с вами.</em></h1>
       <img class="rv-hero__stroke" src="assets/pennellata.svg" alt="">
       <p class="rv-hero__lede">От небольших забот до больших планов&nbsp;— найдите…</p>
     </div>
   </section>
   ========================================================================== */
.rv-hero {
  position: relative;
  margin-block-start: calc(-1 * var(--rv-safe-top));
}

.rv-hero__media {
  position: relative;
  container-type: inline-size;       /* la firma si misura sulla larghezza dell'immagine */
  overflow: hidden;
  /* Altezza proporzionale alla larghezza (60%: 234px a 390, massimo 320px)
     + area sicura, senza unità vw: funziona anche dentro una cornice più
     stretta della finestra. La percentuale del padding si riferisce alla
     larghezza dell'hero, che è quella del media. */
  padding-block-start: min(calc(60% + var(--rv-safe-top)), calc(320px + var(--rv-safe-top)));
  background-color: var(--rv-image-fade);
}

.rv-hero__image {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  object-position: 62% 60%;          /* il soggetto dell'illustrazione sta a destra */
  filter: var(--rv-image-dim);
}

/* Sfumatura nel fondo: curva morbida (più stop = niente gradino), nessun
   bordo netto; il mare si scioglie nel fondo come nel mockup. È già densa
   (~3/4) dove comincia la fascia chiara del parapetto (~87%), così di giorno
   il bordo mare/parapetto non si legge come una riga. */
.rv-hero__media::after {
  content: "";
  position: absolute;
  inset: 0;
  /* ripiego lineare senza color-mix(): mai un bordo netto in fondo */
  background-image: linear-gradient(to bottom, transparent 71%, var(--rv-image-fade) 95%);
  pointer-events: none;
}

@supports (color: color-mix(in srgb, red, blue)) {
  .rv-hero__media::after {
    background-image: linear-gradient(
      to bottom,
      transparent 71%,
      color-mix(in srgb, var(--rv-image-fade) 12%, transparent) 76%,
      color-mix(in srgb, var(--rv-image-fade) 40%, transparent) 81%,
      color-mix(in srgb, var(--rv-image-fade) 76%, transparent) 86%,
      color-mix(in srgb, var(--rv-image-fade) 93%, transparent) 90.5%,
      var(--rv-image-fade) 95%
    );
  }
}

/* Fascia chiara in fondo all'illustrazione (il parapetto, da ~87%): di giorno
   è crema su crema, di notte resterebbe una banda grigia sotto il velo.
   Secondo strato in «darken» col colore del fondo: ogni pixel diventa il più
   scuro fra immagine e fondo. Di giorno il fondo crema è più chiaro di quasi
   tutta l'immagine, quindi non cambia nulla; di notte il verde-carbone
   vince e la banda sparisce. Un solo gradiente per i due temi, nessun colore
   nei blocchi tema. Sopra la sfumatura, sotto la firma (z-index e ordine). */
.rv-hero__media::before {
  content: "";
  position: absolute;
  z-index: 1;
  inset: 0;
  background-image: linear-gradient(to bottom, transparent 79%, var(--rv-image-fade) 89%);
  mix-blend-mode: darken;
  pointer-events: none;
}

/* Firma a mano: decorativa, sul mare calmo a sinistra dell'illustrazione
   (riviera-hero.svg), non sulle facciate chiare del paese: misurata a 360,
   390 e 430px, il crema --rv-on-image sui pixel del mare sotto i glifi ha
   mediana 5,7:1 (sulle facciate era 2,1:1). Posizione in percentuale del
   media, che segue l'illustrazione in scala; il corpo scala con la larghezza
   del media (24px da 390px in su, 22px a 360, mai sotto 20), così il cuore
   (icona: il glifo ♡ non è nei font) resta ~8px a sinistra della barca a vela
   a ogni larghezza e lontano dalle palme. Senza unità cq resta a 24px: la
   clamp() con var() sta in @supports, altrimenti un motore senza cqi la
   renderebbe «unset» (15px) invece di ricadere sulla dichiarazione di base.
   Colore uguale nei due temi (la foto non cambia); un alone morbido di
   --rv-on-image-scrim la stacca dai riflessi del sole.
   drop-shadow e non text-shadow: vale anche per il cuore, che è una maschera. */
.rv-hero__signature {
  position: absolute;
  z-index: 1;
  inset-inline-start: 3%;
  inset-block-end: 26%;
  font-family: var(--rv-font-script);
  font-size: var(--rv-fs-script);
  font-weight: var(--rv-fw-regular);
  line-height: 1;
  color: var(--rv-on-image);
  filter: drop-shadow(0 1px 2px var(--rv-on-image-scrim));   /* ripiego senza color-mix() */
  white-space: nowrap;
  transform: rotate(-6deg);
  transform-origin: 50% 50%;
  pointer-events: none;
  user-select: none;
}

@supports (width: 1cqi) {
  .rv-hero__signature {
    font-size: clamp(20px, 6.15cqi, var(--rv-fs-script));
  }
}

@supports (color: color-mix(in srgb, red, blue)) {
  .rv-hero__signature {
    filter:
      drop-shadow(0 1px 1.5px color-mix(in srgb, var(--rv-on-image-scrim) 50%, transparent))
      drop-shadow(0 0 8px color-mix(in srgb, var(--rv-on-image-scrim) 40%, transparent));
  }
}

/* Il glifo ♡ non è nei sottoinsiemi del font: si usa l'icona .rv-icon--heart */
.rv-hero__heart {
  --rv-icon-size: 0.72em;
  margin-inline-start: 0.16em;
  vertical-align: 0.04em;
}

.rv-hero__body {
  position: relative;
  z-index: 1;
  max-inline-size: calc(var(--rv-content-max) + 2 * var(--rv-gutter));
  margin-inline: auto;
  padding-block-start: var(--rv-space-2);
  padding-inline: var(--rv-gutter);
}

.rv-hero__eyebrow {
  margin-block-end: var(--rv-space-3);
  font-size: var(--rv-fs-overline);
  font-weight: var(--rv-fw-semibold);
  line-height: var(--rv-lh-overline);
  letter-spacing: var(--rv-tracking-overline);
  text-transform: uppercase;
  color: var(--rv-accent-text);
  text-wrap: balance;
}

.rv-hero__title {
  font-family: var(--rv-font-display);
  font-size: var(--rv-fs-display);
  font-weight: var(--rv-fw-medium);
  line-height: var(--rv-lh-display);
  letter-spacing: var(--rv-tracking-display);
  color: var(--rv-heading);
  text-wrap: balance;
}

/* Pennellata ottone sotto il titolo: sfuma da sola alle estremità */
.rv-hero__stroke {
  display: block;
  inline-size: min(210px, 62%);
  block-size: 12px;
  margin-block-start: var(--rv-space-2);
  margin-inline-start: -2px;          /* il tratto parte sfumato: compensazione ottica */
}

.rv-hero__lede {
  max-inline-size: 34em;
  margin-block-start: var(--rv-space-4);
  font-size: var(--rv-fs-body-lg);
  line-height: var(--rv-lh-body-lg);
  color: var(--rv-text-secondary);
  text-wrap: pretty;
}


/* ==========================================================================
   4. .rv-tabbar — navigazione in basso, a tutta larghezza
   Attaccata ai tre bordi, senza angoli arrotondati, con il contenuto della
   pagina che le scorre sotto sfocato (stesso schema di .rv-sticky, §8: colore
   pieno come ripiego, appena trasparente dove ci sono sia color-mix() sia la
   sfocatura). L'area sicura del fondo sta dentro la barra, non sotto: sotto la
   riga dell'iPhone non resta una striscia di fondo pagina.
   Da 4 a 5 voci, solo sulle schermate radice. Voce attiva = aria-current="page":
   pill primary-soft dietro l'icona + etichetta nel colore del pill (forma e
   colore, non solo colore). Il pill sta sull'icona e non sull'intera voce:
   così le etichette russe lunghe non restano strette dentro una capsula e
   «Недвижимость» sta intera anche a 360px.
   Distribuzione: colonne uguali finché c'è posto (flex: 1 1 0, gap 0), ma
   nessuna voce scende sotto la propria etichetta + --rv-tabbar-label-gap
   (padding laterale della voce, metà per lato): fra due etichette vicine
   restano sempre almeno 12px, e le voci corte si prendono lo spazio che
   avanza. Così le icone restano il più possibile equidistanti (ritmo della
   barra) senza che «Специалисты», «Барахолка» e «Недвижимость» si tocchino.
   Colonne rigide (grid 1fr) non vanno: a 360px una colonna è più stretta di
   «Недвижимость»; ripartire l'avanzo sulle etichette (flex-basis auto)
   schiaccerebbe insieme le icone di «Чаты» e «Статьи».
   Il pill non conta nella larghezza (margini negativi: conta l'icona da
   24px); il bersaglio resta ≥44px grazie alla larghezza minima dell'etichetta
   (un min-inline-size sulla voce annullerebbe il minimo automatico).
   Barra stretta (≤376px di contenuto: telefoni da 360–375) etichette a 10,5px
   e pill da 44px; sotto i 346px (telefoni da 320) pill da 40px, spaziatura
   delle lettere più stretta e spazio minimo fra le etichette di 4px.
   Etichette mai sotto 10,5px, mai troncate.
   Le due soglie sono misurate con CINQUE voci, cioè il caso peggiore: con
   quattro ogni voce ha una ventina di pixel in più e le etichette grandi
   starebbero anche a 375. Restano dove sono di proposito — sbagliare in
   prudenza qui costa un'etichetta un po' più piccola, sbagliare nell'altro
   verso costa «Недвижимость» tagliata su un telefono che non ho in mano.
   Con la tastiera aperta la barra sparisce (regola qui sotto) e con lei il
   padding di .rv-app--tabbar (§1) e la fascia libera dello scroll (§1b).

   <nav class="rv-tabbar" aria-label="Разделы">
     <a class="rv-tabbar__item" href="#/specialists" aria-current="page">
       <span class="rv-tabbar__icon"><span class="rv-icon rv-icon--search" aria-hidden="true"></span></span>
       <span class="rv-tabbar__label">Специалисты</span>
     </a>
     <a class="rv-tabbar__item" href="#/chats">
       <span class="rv-tabbar__icon"><span class="rv-icon rv-icon--chats" aria-hidden="true"></span>
         <span class="rv-badge rv-tabbar__badge">17<span class="rv-visually-hidden"> непрочитанных</span></span></span>
       <span class="rv-tabbar__label">Чаты</span>
     </a>
     …
   </nav>
   ========================================================================== */
.rv-tabbar {
  --rv-tabbar-pill-w: 48px;
  --rv-tabbar-label-gap: 12px;
  position: fixed;
  inset-inline: 0;
  inset-block-end: 0;
  z-index: var(--rv-z-tabbar);
  container-type: inline-size;        /* le etichette si adattano alla larghezza della barra */
  display: flex;
  align-items: stretch;
  gap: 0;                             /* lo spazio fra le etichette è il padding delle voci */
  /* L'area sicura sta DENTRO la barra: il colore arriva fino al bordo dello schermo e sotto la riga
     dell'iPhone non resta una striscia di fondo pagina. L'altezza cresce di conseguenza, così le voci
     restano alte --rv-tabbar-h come prima. */
  min-block-size: calc(var(--rv-tabbar-h) + var(--rv-safe-bottom));
  padding-block: var(--rv-space-1-5) calc(var(--rv-space-1-5) + var(--rv-safe-bottom));
  /* Sul telefono sono i 4px che staccano la prima e l'ultima voce dal bordo; su uno schermo largo il
     secondo termine vince e centra la fila su --rv-content-max, senza che la barra smetta di essere a
     tutta larghezza. La percentuale si misura sulla viewport, che è il blocco contenitore di un fixed. */
  padding-inline:
    max(calc(var(--rv-safe-left) + var(--rv-space-1)), calc((100% - var(--rv-content-max)) / 2))
    max(calc(var(--rv-safe-right) + var(--rv-space-1)), calc((100% - var(--rv-content-max)) / 2));
  /* Pieno: è il ripiego dove non c'è color-mix() o non c'è la sfocatura (e dove la sfocatura la spegne
     `data-tg-performance="low"` in base.css). La versione appena trasparente sta nell'@supports. */
  --rv-tabbar-bg: var(--rv-surface);
  background-color: var(--rv-tabbar-bg);
  -webkit-backdrop-filter: saturate(1.2) blur(12px);
  backdrop-filter: saturate(1.2) blur(12px);
  /* Un capello, non un'ombra: attaccata al fondo la barra non «galleggia» più, e su un fondo pagina
     piatto — che qui è la norma — senza questa riga il suo bordo superiore sparirebbe. */
  box-shadow: 0 calc(-1 * var(--rv-border-width)) 0 var(--rv-border);
  transition:
    opacity var(--rv-dur-fast) var(--rv-ease-out),
    transform var(--rv-dur-fast) var(--rv-ease-out),
    visibility 0s linear 0s;          /* tornando, si vede subito */
}

/* Appena trasparente solo dove ci sono sia color-mix() sia la sfocatura, come .rv-sticky (§8). */
@supports (color: color-mix(in srgb, red, blue)) and ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .rv-tabbar {
    --rv-tabbar-bg: color-mix(in srgb, var(--rv-surface) 82%, transparent);
  }
}

/* Tastiera aperta: la tab bar se ne va.
   Dentro Telegram su iPhone la tastiera non copre la barra: WKWebView
   riaggancia gli elementi fixed alla viewport visuale e la barra si stacca
   dal fondo per piazzarsi SOPRA la tastiera, in mezzo allo schermo. Mentre
   si scrive delle cinque voci non serve nessuna, e quella striscia mangia
   una riga e mezzo di risultati proprio quando servono.
   Sparisce per davvero: visibility: hidden la toglie anche dal giro di Tab e
   dallo screen reader (opacity da sola no). Il contenuto non si muove — la
   barra è fissa, non occupa posto nel flusso — e la dissolvenza sta in
   --rv-dur-fast, azzerata da prefers-reduced-motion (base.css).
   La classe la mette js/riviera-telegram.js misurando la viewport (regola e
   soglie nel commento «Tastiera» di quel file): senza lo script non compare
   mai e la barra si comporta come prima.
   .rv-chat-bar non la guarda: è il campo in cui si sta scrivendo e sopra la
   tastiera ci deve stare. Il MainButton simulato neanche: imita il MainButton
   nativo, che il client tiene sopra la tastiera. */
html.rv-keyboard-open .rv-tabbar {
  visibility: hidden;
  opacity: 0;
  transform: translateY(100%);        /* attaccata al fondo: la sua altezza basta a portarla fuori */
  transition-delay: 0s, 0s, var(--rv-dur-fast);   /* prima la dissolvenza, poi via */
}

.rv-tabbar__item {
  --rv-tabbar-pill: transparent;
  position: relative;
  display: flex;
  flex: 1 1 0;                        /* colonne uguali; mai sotto etichetta + spazio */
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--rv-space-1);
  min-block-size: calc(var(--rv-tabbar-h) - 2 * var(--rv-space-1-5));
  padding-block: var(--rv-space-0-5);
  padding-inline: calc(var(--rv-tabbar-label-gap) / 2);
  border-radius: var(--rv-radius-lg);   /* si vede al tocco e sull'anello di focus, non a riposo */
  color: var(--rv-text-muted);
  text-align: center;
  text-decoration: none;
  transition:
    color var(--rv-dur-fast) var(--rv-ease-out),
    transform var(--rv-dur-fast) var(--rv-ease-out);
}

.rv-tabbar__item:active {
  transform: scale(var(--rv-press-scale));
}

.rv-tabbar__item:focus-visible {
  outline-offset: 0;
}

/* Pill dell'icona: 48×30 (44 e 40 sulle barre strette), colore dal
   --rv-tabbar-pill della voce. I margini negativi lo riducono, per il calcolo
   delle larghezze, all'icona da 24px: resta centrato e può sporgere di un
   paio di pixel da una voce corta (si vede solo sulla voce attiva). */
.rv-tabbar__icon {
  position: relative;
  display: grid;
  flex: none;
  place-items: center;
  inline-size: var(--rv-tabbar-pill-w);
  block-size: 30px;
  margin-inline: calc((24px - var(--rv-tabbar-pill-w)) / 2);
  border-radius: var(--rv-radius-pill);
  background-color: var(--rv-tabbar-pill);
  transition: background-color var(--rv-dur-fast) var(--rv-ease-out);
}

/* Etichetta 11px, peso costante: cambiarlo al cambio di voce farebbe
   scivolare di un pixel le voci già alla larghezza minima */
.rv-tabbar__label {
  font-size: var(--rv-fs-overline);
  font-weight: var(--rv-fw-medium);
  line-height: var(--rv-lh-tag);
  letter-spacing: -0.005em;
  white-space: nowrap;
  min-inline-size: calc(var(--rv-tap) - var(--rv-tabbar-label-gap));   /* voce ≥ 44px anche con «Чаты» */
}

/* Posiziona un .rv-badge (actions.css) sull'angolo alto destro dell'icona,
   con un contorno del colore che sta sotto (superficie o pill attivo) */
.rv-tabbar__badge {
  position: absolute;
  inset-block-start: -4px;
  inset-inline-start: calc(50% + 3px);
  /* Il contorno prende il colore di cio' che sta sotto: il pill quando c'e', senno' la barra stessa —
     che e' appena trasparente, quindi un contorno pieno lascerebbe un alone attorno al pallino. */
  box-shadow: 0 0 0 2px var(--rv-tabbar-cutout, var(--rv-tabbar-bg));
}

@media (hover: hover) {
  .rv-tabbar__item:hover {
    --rv-tabbar-pill: var(--rv-surface-hover);
    --rv-tabbar-cutout: var(--rv-surface-hover);
    color: var(--rv-text);
  }
}

.rv-tabbar__item[aria-current="page"] {
  --rv-tabbar-pill: var(--rv-primary-soft);
  --rv-tabbar-cutout: var(--rv-primary-soft);
  color: var(--rv-primary-soft-text);
}

/* Barra stretta (telefoni da 360–375px): etichette a 10,5px (il minimo) e
   pill da 44px, così fra «Специалисты», «Барахолка» e «Недвижимость» resta
   aria visibile. La variabile del pill si ridefinisce sulle voci: la query
   interroga la barra, quindi vale solo per i suoi discendenti. */
@container (max-width: 376px) {
  .rv-tabbar__item {
    --rv-tabbar-pill-w: 44px;
  }

  .rv-tabbar__label {
    font-size: var(--rv-fs-tag);
    letter-spacing: -0.01em;
  }
}

/* Barra strettissima (sotto i 330px, telefoni da 320): pill da 40px e
   spazio minimo fra le etichette ridotto, altrimenti la barra non basta */
@container (max-width: 346px) {
  .rv-tabbar__item {
    --rv-tabbar-pill-w: 40px;
    --rv-tabbar-label-gap: 4px;
  }

  .rv-tabbar__label {
    letter-spacing: -0.02em;
  }
}


/* ==========================================================================
   5. .rv-sheet — bottom sheet
   Il contenitore è il velo; il pannello sale dal basso. Funziona con un
   <div role="dialog" aria-modal="true"> (mostrato/nascosto con [hidden]) o
   con <dialog> aperto da showModal() (Esc e focus gestiti dal browser).
   Per chiudere con animazione: aggiungi .is-closing, attendi animationend,
   poi nascondi. Per le azioni primarie delle sottopagine preferisci il
   MainButton nativo; nel piede dello sheet va l'azione dello sheet stesso.

   <div class="rv-sheet" role="dialog" aria-modal="true" aria-labelledby="f-title">
     <div class="rv-sheet__panel">
       <div class="rv-sheet__handle" aria-hidden="true"></div>
       <header class="rv-sheet__head">
         <h2 class="rv-sheet__title" id="f-title">Фильтры</h2>
         <button class="rv-icon-btn rv-icon-btn--ghost" type="button" aria-label="Закрыть">…</button>
       </header>
       <div class="rv-sheet__body">
         <section class="rv-sheet__section">
           <h3 class="rv-sheet__label">Категория</h3> …
         </section>
       </div>
       <footer class="rv-sheet__foot">
         <button class="rv-btn rv-btn--ghost">Сбросить</button>
         <button class="rv-btn rv-btn--primary">Показать 24 специалиста</button>
       </footer>
     </div>
   </div>
   ========================================================================== */
.rv-sheet {
  position: fixed;
  inset: 0;
  z-index: var(--rv-z-sheet);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  inline-size: auto;                  /* reset delle regole UA di <dialog> */
  block-size: auto;
  max-inline-size: none;
  max-block-size: none;
  margin: 0;
  padding: calc(var(--rv-safe-top) + var(--rv-space-10)) 0 0;
  border: 0;
  overflow: hidden;
  overscroll-behavior: contain;
  background-color: var(--rv-scrim);
  color: var(--rv-text);
  animation: rv-overlay-in var(--rv-dur-base) var(--rv-ease-out) both;
}

/* <dialog> chiuso resta nascosto (le regole d'autore battono quelle UA) */
.rv-sheet:where(dialog:not([open])) {
  display: none;
}

.rv-sheet::backdrop {
  background: transparent;            /* il velo è il contenitore stesso */
}

.rv-sheet__panel {
  position: relative;
  display: flex;
  flex-direction: column;
  inline-size: 100%;
  max-inline-size: calc(var(--rv-content-max) + 2 * var(--rv-gutter));
  max-block-size: 100%;
  margin-inline: auto;
  overflow: hidden;
  border-start-start-radius: var(--rv-radius-xl);
  border-start-end-radius: var(--rv-radius-xl);
  background-color: var(--rv-surface-raised);
  box-shadow: var(--rv-shadow-lg);
  animation: rv-sheet-rise var(--rv-dur-slow) var(--rv-ease-out) both;
}

.rv-sheet__handle {
  flex: none;
  align-self: center;
  inline-size: 36px;
  block-size: 5px;
  margin-block-start: var(--rv-space-2);
  border-radius: var(--rv-radius-pill);
  background-color: var(--rv-border-control);   /* ≈ border-strong al 55% su surface-raised, senza color-mix() */
}

.rv-sheet__head {
  display: flex;
  flex: none;
  align-items: center;
  gap: var(--rv-space-2);
  min-block-size: var(--rv-appbar-h);
  padding-block: var(--rv-space-1);
  padding-inline: var(--rv-gutter) var(--rv-space-1-5);
}

.rv-sheet__title {
  flex: 1 1 auto;
  min-inline-size: 0;
  font-family: var(--rv-font-display);
  font-size: var(--rv-fs-title-2);
  font-weight: var(--rv-fw-medium);
  line-height: var(--rv-lh-title-2);
  letter-spacing: var(--rv-tracking-title);
  color: var(--rv-heading);
  text-wrap: balance;
}

.rv-sheet__body {
  flex: 1 1 auto;
  min-block-size: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding-block: 0 var(--rv-space-4);
  padding-inline: var(--rv-gutter);
}

/* Senza piede, il corpo si prende anche l'area sicura in basso */
.rv-sheet__body:last-child {
  padding-block-end: calc(var(--rv-safe-bottom) + var(--rv-space-6));
}

.rv-sheet__section {
  padding-block: var(--rv-space-4);
}

.rv-sheet__section:first-child {
  padding-block-start: var(--rv-space-1);
}

.rv-sheet__section + .rv-sheet__section {
  border-block-start: var(--rv-border-width) solid var(--rv-border);
}

.rv-sheet__label {
  margin-block-end: var(--rv-space-3);
  font-size: var(--rv-fs-body);
  font-weight: var(--rv-fw-semibold);
  line-height: var(--rv-lh-headline);
  color: var(--rv-text);
}

.rv-sheet__foot {
  display: flex;
  flex: none;
  align-items: center;
  gap: var(--rv-space-2);
  padding-block: var(--rv-space-3) calc(var(--rv-safe-bottom) + var(--rv-space-3));
  padding-inline: var(--rv-gutter);
  box-shadow: 0 calc(-1 * var(--rv-border-width)) 0 var(--rv-border);
}

/* L'ultima azione (la principale) prende lo spazio che resta */
.rv-sheet__foot > :last-child {
  flex: 1 1 auto;
}

.rv-sheet.is-closing {
  animation-name: rv-overlay-out;
}

.is-closing > .rv-sheet__panel {
  animation: rv-sheet-sink var(--rv-dur-base) var(--rv-ease-in-out) both;
}

/* --------------------------------------------------------------------------
   5b. .rv-sheet--tall — pannello a tutta altezza
   Per gli sheet il cui corpo si riempie mentre è aperto: scelta dell'indirizzo
   con i risultati che arrivano, elenco che si filtra mentre si scrive. Senza
   questo il pannello cresce da sé e il foglio «salta» sotto le dita a ogni
   risposta del server. Con --tall l'altezza è decisa subito (tutto lo spazio
   sotto l'area sicura) e a crescere è solo --rv-sheet__body, che scorre.

   <div class="rv-sheet rv-sheet--tall" role="dialog" aria-modal="true" aria-labelledby="addr-title">
     <div class="rv-sheet__panel">
       <div class="rv-sheet__handle" aria-hidden="true"></div>
       <header class="rv-sheet__head">
         <h2 class="rv-sheet__title" id="addr-title">Откуда едем</h2>
         <button class="rv-icon-btn rv-icon-btn--ghost" type="button" aria-label="Закрыть">…</button>
       </header>
       <div class="rv-sheet__body">…ricerca compatta + .rv-list dei risultati…</div>
     </div>
   </div>
   -------------------------------------------------------------------------- */
.rv-sheet--tall .rv-sheet__panel {
  block-size: 100%;
}

.rv-sheet--tall .rv-sheet__body {
  flex: 1 1 auto;
  min-block-size: 0;
}


/* --------------------------------------------------------------------------
   5c. .rv-sheet--full — il foglio prende tutto lo schermo
   Per i fogli che non sono un dettaglio ma un PASSAGGIO: la scelta di un
   indirizzo con i suggerimenti che arrivano, un modulo dentro un modulo. Lì la
   striscia di pagina che si intravede sopra non informa di niente — la
   schermata sotto e' comunque inutilizzabile finche' il foglio e' aperto — e in
   cambio toglie una riga di risultati su uno schermo gia' stretto.

   Si perde anche l'arrotondamento in alto: un pannello con gli angoli tondi
   attaccato al bordo dello schermo sembra un errore di calcolo, non una scelta.
   Il velo resta nel markup ma non si vede piu': serve solo a raccogliere il
   tocco fuori dal pannello, che qui non esiste. Chi usa --full dia percio' al
   foglio un modo esplicito di uscire (un'azione in fondo), oltre a Esc e al
   BackButton: il tocco fuori non e' piu' disponibile.

   <div class="rv-sheet rv-sheet--tall rv-sheet--full" role="dialog" aria-modal="true">
   -------------------------------------------------------------------------- */
.rv-sheet--full {
  padding-block-start: 0;
}

.rv-sheet--full .rv-sheet__panel {
  max-inline-size: none;
  block-size: 100%;
  border-start-start-radius: 0;
  border-start-end-radius: 0;
  box-shadow: none;
}

/* Il pannello arriva al bordo: l'area sicura in alto la tiene lui, non il velo. */
.rv-sheet--full .rv-sheet__head {
  padding-block-start: calc(var(--rv-safe-top) + var(--rv-space-2));
}

/* La maniglia e' il segno di «si tira giu' per chiudere»: a tutto schermo non si tira niente. */
.rv-sheet--full .rv-sheet__handle {
  display: none;
}


/* ==========================================================================
   6. .rv-dialog — finestra di conferma
   Per conferme semplici esiste anche il popup nativo (RivieraTelegram.confirm);
   .rv-dialog serve quando servono icona, testo lungo o più azioni.
   Azioni impilate di default (le etichette russe sono lunghe); --row per
   due etichette brevi. --danger colora l'icona per le azioni distruttive.

   <div class="rv-dialog" role="alertdialog" aria-modal="true"
        aria-labelledby="d-title" aria-describedby="d-text">
     <div class="rv-dialog__panel">
       <span class="rv-dialog__icon"><span class="rv-icon rv-icon--phone" aria-hidden="true"></span></span>
       <h2 class="rv-dialog__title" id="d-title">Поделиться номером?</h2>
       <p class="rv-dialog__text" id="d-text">Иван увидит ваш телефон…</p>
       <div class="rv-dialog__actions">
         <button class="rv-btn rv-btn--primary rv-btn--block">Поделиться</button>
         <button class="rv-btn rv-btn--ghost rv-btn--block">Не сейчас</button>
       </div>
     </div>
   </div>
   ========================================================================== */
.rv-dialog {
  position: fixed;
  inset: 0;
  z-index: var(--rv-z-sheet);
  display: grid;
  place-items: center;
  inline-size: auto;                  /* reset delle regole UA di <dialog> */
  block-size: auto;
  max-inline-size: none;
  max-block-size: none;
  margin: 0;
  padding:
    calc(var(--rv-safe-top) + var(--rv-space-6))
    calc(var(--rv-safe-right) + var(--rv-gutter))
    calc(var(--rv-safe-bottom) + var(--rv-space-6))
    calc(var(--rv-safe-left) + var(--rv-gutter));
  border: 0;
  overscroll-behavior: contain;
  background-color: var(--rv-scrim);
  color: var(--rv-text);
  animation: rv-overlay-in var(--rv-dur-base) var(--rv-ease-out) both;
}

.rv-dialog:where(dialog:not([open])) {
  display: none;
}

.rv-dialog::backdrop {
  background: transparent;
}

.rv-dialog__panel {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  inline-size: 100%;
  max-inline-size: 340px;
  max-block-size: 100%;
  overflow-y: auto;
  padding: var(--rv-space-8) var(--rv-space-5) var(--rv-space-5);
  border-radius: var(--rv-radius-xl);
  background-color: var(--rv-surface-raised);
  color: var(--rv-text);
  box-shadow: var(--rv-shadow-lg);
  text-align: center;
  animation: rv-dialog-pop var(--rv-dur-base) var(--rv-ease-out) both;
}

.rv-dialog__icon {
  --rv-dialog-tone: var(--rv-primary-soft-text);
  --rv-dialog-tone-soft: var(--rv-primary-soft);
  display: grid;
  flex: none;
  place-items: center;
  inline-size: 56px;
  block-size: 56px;
  margin-block-end: var(--rv-space-4);
  border-radius: var(--rv-radius-pill);
  background-color: var(--rv-dialog-tone-soft);
  color: var(--rv-dialog-tone);
}

.rv-dialog--danger .rv-dialog__icon {
  --rv-dialog-tone: var(--rv-danger);
  --rv-dialog-tone-soft: var(--rv-danger-soft);
}

.rv-dialog__title {
  font-family: var(--rv-font-display);
  font-size: var(--rv-fs-title-2);
  font-weight: var(--rv-fw-medium);
  line-height: var(--rv-lh-title-2);
  letter-spacing: var(--rv-tracking-title);
  color: var(--rv-heading);
  text-wrap: balance;
}

.rv-dialog__text {
  margin-block-start: var(--rv-space-2);
  font-size: var(--rv-fs-body);
  line-height: var(--rv-lh-body);
  color: var(--rv-text-secondary);
  text-wrap: pretty;
}

.rv-dialog__actions {
  display: grid;
  align-self: stretch;
  gap: var(--rv-space-2);
  margin-block-start: var(--rv-space-6);
}

.rv-dialog__actions--row {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

/* Chiusura facoltativa in alto a destra (per dialog informativi) */
.rv-dialog__close {
  position: absolute;
  inset-block-start: var(--rv-space-2);
  inset-inline-end: var(--rv-space-2);
}

.rv-dialog.is-closing {
  animation-name: rv-overlay-out;
}

.is-closing > .rv-dialog__panel {
  animation: rv-dialog-out var(--rv-dur-fast) var(--rv-ease-in-out) both;
}


/* ==========================================================================
   7. .rv-toast — avviso breve
   Superficie sollevata in fondo allo schermo, icona di tono, testo fino a
   tre righe, un'azione facoltativa. Toni: --success (predefinito),
   --favorite, --danger, --info. Sopra la tab bar: --above-tabbar (con la
   tastiera aperta torna in basso: la tab bar non c'è).
   Uscita: aggiungi .is-leaving, poi rimuovi all'animationend.

   <div class="rv-toast rv-toast--favorite rv-toast--above-tabbar" role="status" aria-live="polite">
     <span class="rv-icon rv-icon--heart-fill rv-toast__icon" aria-hidden="true"></span>
     <p class="rv-toast__text">Сохранено в избранное</p>
     <button class="rv-toast__action" type="button">Открыть</button>
   </div>
   ========================================================================== */
.rv-toast {
  --rv-toast-tone: var(--rv-success);
  position: fixed;
  inset-inline: calc(var(--rv-safe-left) + var(--rv-gutter)) calc(var(--rv-safe-right) + var(--rv-gutter));
  inset-block-end: calc(var(--rv-safe-bottom) + var(--rv-space-4));
  z-index: var(--rv-z-toast);
  display: flex;
  align-items: center;
  gap: var(--rv-space-3);
  max-inline-size: 440px;
  min-block-size: 52px;
  margin-inline: auto;
  padding-block: var(--rv-space-1-5);
  padding-inline: var(--rv-space-4);
  border-radius: var(--rv-radius-lg);
  background-color: var(--rv-surface-raised);
  color: var(--rv-text);
  box-shadow: var(--rv-shadow-lg);
  animation: rv-toast-rise var(--rv-dur-base) var(--rv-ease-out) both;
}

.rv-toast--favorite { --rv-toast-tone: var(--rv-favorite); }
.rv-toast--danger { --rv-toast-tone: var(--rv-danger); }
.rv-toast--info { --rv-toast-tone: var(--rv-info); }

.rv-toast--above-tabbar {
  /* La barra e' attaccata al fondo e si porta dentro l'area sicura: il distacco che c'era quando
     galleggiava non va piu' contato, senno' il toast resta sospeso a mezz'aria sopra di lei. */
  inset-block-end: calc(var(--rv-safe-bottom) + var(--rv-tabbar-h) + var(--rv-space-3));
}

/* Tastiera aperta: la tab bar non c'è (§4), quindi non c'è niente sopra cui
   stare e il toast torna alla sua altezza normale. Stessa idea del padding di
   .rv-app--tabbar (§1): sparita la barra, sparisce lo spazio tenuto per lei. */
html.rv-keyboard-open .rv-toast--above-tabbar {
  inset-block-end: calc(var(--rv-safe-bottom) + var(--rv-space-4));
}

.rv-toast__icon {
  flex: none;
  color: var(--rv-toast-tone);
}

.rv-toast__text {
  flex: 1 1 auto;
  min-inline-size: 0;
  padding-block: var(--rv-space-1-5);
  font-size: var(--rv-fs-callout);
  font-weight: var(--rv-fw-medium);
  line-height: var(--rv-lh-callout);
  text-wrap: pretty;
}

.rv-toast__action {
  flex: none;
  min-block-size: var(--rv-tap);
  margin-inline-end: calc(-1 * var(--rv-space-2));
  padding-inline: var(--rv-space-3);
  border-radius: var(--rv-radius-pill);
  font-size: var(--rv-fs-callout);
  font-weight: var(--rv-fw-semibold);
  color: var(--rv-link);   /* testo verde su surface-raised e surface-hover: AA in entrambi i temi */
  white-space: nowrap;
  transition:
    background-color var(--rv-dur-fast) var(--rv-ease-out),
    transform var(--rv-dur-fast) var(--rv-ease-out);
}

.rv-toast__action:active {
  background-color: var(--rv-surface-hover);
  color: var(--rv-link-hover);
  transform: scale(var(--rv-press-scale));
}

@media (hover: hover) {
  .rv-toast__action:hover {
    background-color: var(--rv-surface-hover);
    color: var(--rv-link-hover);
  }
}

.rv-toast.is-leaving {
  animation: rv-toast-sink var(--rv-dur-fast) var(--rv-ease-in-out) both;
}


/* ==========================================================================
   8. .rv-empty / .rv-error — stato vuoto, errore con riprova
   Stessa impaginazione: illustrazione (o icona in cerchio), titolo serif,
   testo che spiega cosa fare, azioni. L'errore ha anche __detail (codice,
   ora) e la variante --inline per un blocco dentro una schermata piena.

   <div class="rv-empty">
     <img class="rv-empty__art" src="assets/illustrazioni/vuoto.svg" alt="">
     <h2 class="rv-empty__title">Ничего не нашли</h2>
     <p class="rv-empty__text">Попробуйте изменить фильтры…</p>
     <div class="rv-empty__actions">
       <button class="rv-btn rv-btn--primary">Сбросить фильтры</button>
     </div>
   </div>

   <div class="rv-error" role="alert">
     <img class="rv-error__art" src="assets/illustrazioni/errore.svg" alt="">
     <h2 class="rv-error__title">Не получилось загрузить</h2>
     <p class="rv-error__text">Проверьте интернет и попробуйте ещё раз.</p>
     <div class="rv-error__actions"><button class="rv-btn rv-btn--outline">Повторить</button></div>
     <p class="rv-error__detail">Код ошибки 503 · 18:42</p>
   </div>

   <div class="rv-error rv-error--inline" role="alert">
     <span class="rv-error__icon"><span class="rv-icon rv-icon--alert-circle" aria-hidden="true"></span></span>
     <p class="rv-error__text">Не удалось загрузить отзывы.</p>
     <button class="rv-btn rv-btn--ghost rv-btn--sm">Повторить</button>
   </div>
   ========================================================================== */
.rv-empty,
.rv-error {
  display: flex;
  flex-direction: column;
  align-items: center;
  max-inline-size: 400px;
  margin-inline: auto;
  padding: var(--rv-space-10) var(--rv-gutter);
  text-align: center;
}

/* Le illustrazioni 240×180 hanno già aria in basso: margine piccolo */
.rv-empty__art,
.rv-error__art {
  inline-size: min(184px, 64%);
  block-size: auto;
  margin-block-end: var(--rv-space-2);
}

.rv-empty__icon,
.rv-error__icon {
  display: grid;
  flex: none;
  place-items: center;
  inline-size: 64px;
  block-size: 64px;
  margin-block-end: var(--rv-space-4);
  border-radius: var(--rv-radius-pill);
  background-color: var(--rv-primary-soft);
  color: var(--rv-primary-soft-text);
}

.rv-error__icon {
  background-color: var(--rv-danger-soft);
  color: var(--rv-danger);
}

.rv-empty__title,
.rv-error__title {
  font-family: var(--rv-font-display);
  font-size: var(--rv-fs-title-2);
  font-weight: var(--rv-fw-medium);
  line-height: var(--rv-lh-title-2);
  letter-spacing: var(--rv-tracking-title);
  color: var(--rv-heading);
  text-wrap: balance;
}

.rv-empty__text,
.rv-error__text {
  max-inline-size: 30em;
  margin-block-start: var(--rv-space-2);
  font-size: var(--rv-fs-body);
  line-height: var(--rv-lh-body);
  color: var(--rv-text-secondary);
  text-wrap: pretty;
}

.rv-empty__actions,
.rv-error__actions {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--rv-space-2);
  margin-block-start: var(--rv-space-6);
}

.rv-error__detail {
  margin-block-start: var(--rv-space-4);
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text-muted);
}

/* Compatto: dentro una sezione o una lista */
.rv-empty--compact {
  padding-block: var(--rv-space-6);
}

.rv-empty--compact .rv-empty__art {
  inline-size: min(128px, 50%);
  margin-block-end: var(--rv-space-3);
}

.rv-empty--compact .rv-empty__icon {
  inline-size: 56px;
  block-size: 56px;
  margin-block-end: var(--rv-space-3);
}

.rv-empty--compact .rv-empty__title {
  font-size: var(--rv-fs-title-3);
  line-height: var(--rv-lh-title-3);
}

.rv-empty--compact .rv-empty__actions {
  margin-block-start: var(--rv-space-4);
}

/* Errore in linea: riga su fondo tenue, l'azione a destra */
.rv-error--inline {
  flex-direction: row;
  gap: var(--rv-space-3);
  max-inline-size: none;
  padding-block: var(--rv-space-2);
  padding-inline: var(--rv-space-4) var(--rv-space-2);
  border-radius: var(--rv-radius-md);
  background-color: var(--rv-danger-soft);
  text-align: start;
}

.rv-error--inline .rv-error__icon {
  inline-size: 24px;
  block-size: 24px;
  margin: 0;
  background-color: transparent;
}

.rv-error--inline .rv-error__text {
  flex: 1 1 auto;
  min-inline-size: 0;
  margin: 0;
  padding-block: var(--rv-space-1-5);
  font-size: var(--rv-fs-callout);
  line-height: var(--rv-lh-callout);
  color: var(--rv-text);
}


/* --------------------------------------------------------------------------
   8b. .rv-empty--success — stato di successo sobrio
   Dopo un invio che chiude un flusso («Профиль отправлен на проверку»): la
   stessa impaginazione di .rv-empty, icona su verde tenue con un anello
   sottile staccato dal disco (un sigillo, non un'esplosione di colore).
   Niente illustrazione: vuoto.svg ed errore.svg hanno altri significati.

   <div class="rv-empty rv-empty--success" role="status">
     <span class="rv-empty__icon"><span class="rv-icon rv-icon--check rv-icon--xl" aria-hidden="true"></span></span>
     <h1 class="rv-empty__title" tabindex="-1">Профиль отправлен на&nbsp;проверку</h1>
     <p class="rv-empty__text">Модератор посмотрит его в&nbsp;течение дня. Мы&nbsp;напишем вам в&nbsp;Telegram, когда профиль появится в&nbsp;каталоге.</p>
     <div class="rv-empty__actions">
       <a class="rv-btn rv-btn--primary" href="…">Вернуться в&nbsp;каталог</a>
     </div>
   </div>
   Il significato non è solo nel verde: spunta e titolo lo dicono. role="status"
   quando lo stato sostituisce il modulo appena inviato (o focus sul titolo con
   tabindex="-1"). L'anello è staccato dal disco con il colore del fondo:
   su sheet o dialog dichiara --rv-empty-cutout: var(--rv-surface-raised).
   Funziona anche con --compact. Cosa succede dopo: .rv-notice o .rv-trust--list
   sotto lo stato, non dentro.
   -------------------------------------------------------------------------- */
.rv-empty--success .rv-empty__icon {
  margin-block-end: calc(var(--rv-space-4) + 7px);   /* + anello */
  background-color: var(--rv-success-soft);
  color: var(--rv-success);
  box-shadow:
    0 0 0 6px var(--rv-empty-cutout, var(--rv-bg)),
    0 0 0 7px var(--rv-success);
}

.rv-empty--compact.rv-empty--success .rv-empty__icon {
  margin-block-end: calc(var(--rv-space-3) + 7px);
}


/* --------------------------------------------------------------------------
   8c. .rv-notice — avviso in linea
   Informazione che resta nella pagina (non se ne va come il toast, non blocca
   come il dialog): modifiche in attesa di verifica, motivo di un rifiuto,
   «что дальше», profilo nascosto. Icona + titolo + testo + azione facoltativa.
   Toni: base neutra (salvia), --info, --warning, --success, --danger.

   <div class="rv-notice rv-notice--warning">
     <span class="rv-notice__icon"><span class="rv-icon rv-icon--clock" aria-hidden="true"></span></span>
     <div class="rv-notice__body">
       <p class="rv-notice__title">Изменения на&nbsp;проверке</p>
       <p class="rv-notice__text">Пока модератор их&nbsp;смотрит, в&nbsp;каталоге видна прежняя версия профиля.</p>
       <div class="rv-notice__actions">                                        (facoltativo)
         <a class="rv-link" href="…">Открыть анкету<span class="rv-icon rv-icon--arrow-right rv-link__icon" aria-hidden="true"></span></a>
       </div>
     </div>
   </div>

   Senza titolo basta __text (prende il colore del testo). Icone consigliate
   per tono: info → info, warning → clock o alert-circle, success →
   check-circle, danger → alert-circle, neutro → info o eye.
   Mai solo colore: il tono lo dicono anche l'icona e il titolo. Se il tono
   da solo porta un'informazione che il testo non dice, aggiungi all'inizio
   del titolo un <span class="rv-visually-hidden">Внимание: </span>.
   Ruoli: avviso presente al caricamento = nessun ruolo; comparso dopo
   un'azione = role="status" (--danger che blocca un invio: role="alert").
   Colori: icona --rv-<tono> su --rv-<tono>-soft (AA anche come testo);
   titolo --rv-text, testo --rv-text-secondary, azione .rv-link (--rv-link):
   AA sui cinque fondi in entrambi i temi.
   -------------------------------------------------------------------------- */
.rv-notice {
  --rv-notice-tone: var(--rv-text-secondary);
  --rv-notice-bg: var(--rv-bg-muted);
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  column-gap: var(--rv-space-3);
  padding: var(--rv-space-3) var(--rv-space-4);
  border-radius: var(--rv-radius-md);
  background-color: var(--rv-notice-bg);
  color: var(--rv-text);
  text-align: start;
}

.rv-notice--info { --rv-notice-tone: var(--rv-info); --rv-notice-bg: var(--rv-info-soft); }
.rv-notice--warning { --rv-notice-tone: var(--rv-warning); --rv-notice-bg: var(--rv-warning-soft); }
.rv-notice--success { --rv-notice-tone: var(--rv-success); --rv-notice-bg: var(--rv-success-soft); }
.rv-notice--danger { --rv-notice-tone: var(--rv-danger); --rv-notice-bg: var(--rv-danger-soft); }

/* Icona da 20px centrata sulla prima riga (titolo 15 × 1,35 ≈ testo 14 × 1,45 ≈ 20px) */
.rv-notice__icon {
  --rv-icon-size: 20px;
  display: flex;
  color: var(--rv-notice-tone);
}

.rv-notice__body {
  display: grid;
  gap: var(--rv-space-0-5);
  min-inline-size: 0;
}

.rv-notice__title {
  font-size: var(--rv-fs-body);
  font-weight: var(--rv-fw-semibold);
  line-height: 1.35;
  color: var(--rv-text);
  overflow-wrap: break-word;
}

.rv-notice__text {
  font-size: var(--rv-fs-callout);
  line-height: var(--rv-lh-callout);
  color: var(--rv-text-secondary);
  text-wrap: pretty;
  overflow-wrap: break-word;
}

/* Solo testo, senza titolo: pieno */
.rv-notice__text:first-child {
  color: var(--rv-text);
}

/* Azioni: .rv-link (24px fra righe di link che vanno a capo) o .rv-btn--sm */
.rv-notice__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--rv-space-6) var(--rv-space-5);
  margin-block-start: var(--rv-space-2);
}

/* Contrasto elevato di Windows: i fondi spariscono, resta un bordo */
@media (forced-colors: active) {
  .rv-notice {
    border: 1px solid CanvasText;
  }
}


/* ==========================================================================
   9. .rv-skeleton — segnaposto di caricamento
   Ossa calde (--rv-skeleton) con un riflesso che scorre (--rv-skeleton-glint):
   di giorno beige con riflesso crema, di notte verde-carbone con riflesso che
   SCHIARISCE (un riflesso più scuro dell'osso sembrerebbe un'ombra).
   Con moto ridotto o dispositivo lento il riflesso si ferma e resta l'osso.
   Forme: --text (riga di testo), --heading (riga di titolo), --circle,
   --media (4:3), --pill (bottone da 44px), --fill (riempie un contenitore
   posizionato: il media di una card, una miniatura; ne prende gli angoli).
   Larghezza con inline-size.
   Il contenitore annuncia il caricamento; le ossa sono nascoste ai lettori.

   <div class="rv-listing__media"><span class="rv-skeleton rv-skeleton--fill"></span></div>

   <div aria-busy="true">
     <span class="rv-visually-hidden">Загружаем специалистов…</span>
     <div aria-hidden="true">
       <span class="rv-skeleton rv-skeleton--media"></span>
       <span class="rv-skeleton rv-skeleton--heading" style="inline-size: 72%"></span>
       <span class="rv-skeleton rv-skeleton--text" style="inline-size: 48%"></span>
     </div>
   </div>
   ========================================================================== */
.rv-skeleton {
  display: block;
  flex: none;
  inline-size: 100%;
  block-size: var(--rv-space-4);
  border-radius: var(--rv-radius-xs);
  background-color: var(--rv-skeleton);
  background-image: linear-gradient(
    100deg,
    var(--rv-skeleton) 38%,
    var(--rv-skeleton-glint) 50%,
    var(--rv-skeleton) 62%
  );
  background-repeat: no-repeat;
  background-size: 300% 100%;
  background-position: 100% 0;
  animation: rv-skeleton-shimmer 1.6s var(--rv-ease-in-out) infinite;
}

/* Riga di testo: osso da 12px in una riga da ~22px (body) */
.rv-skeleton--text {
  block-size: 12px;
  margin-block: 5px;
  border-radius: var(--rv-radius-pill);
}

/* Riga di titolo (headline 17px) */
.rv-skeleton--heading {
  block-size: 16px;
  margin-block: 3px;
  border-radius: var(--rv-radius-pill);
}

.rv-skeleton--circle {
  inline-size: 40px;
  block-size: auto;
  aspect-ratio: 1;
  border-radius: var(--rv-radius-pill);
}

.rv-skeleton--media {
  block-size: auto;
  aspect-ratio: 4 / 3;
  border-radius: 0;
}

.rv-skeleton--pill {
  block-size: var(--rv-tap);
  border-radius: var(--rv-radius-pill);
}

/* Riempie il contenitore posizionato (i media delle card lo sono già) */
.rv-skeleton--fill {
  position: absolute;
  inset: 0;
  inline-size: auto;
  block-size: auto;
  border-radius: inherit;
}


/* ==========================================================================
   10. .rv-spinner — attesa
   Arco che sfuma con testa arrotondata; prende currentColor (nei bottoni
   eredita il colore del testo). --primary per l'attesa di pagina.
   Esente dal moto ridotto: comunica un'attesa in corso.

   <span class="rv-spinner rv-spinner--primary" role="status">
     <span class="rv-visually-hidden">Загрузка…</span>
   </span>
   ========================================================================== */
.rv-spinner {
  --rv-spinner-size: 24px;
  --rv-spinner-stroke: 2.5px;
  position: relative;
  display: inline-block;
  flex: none;
  inline-size: var(--rv-spinner-size);
  block-size: var(--rv-spinner-size);
  border-radius: var(--rv-radius-pill);
  vertical-align: middle;
  background-image: conic-gradient(transparent 0 30%, currentColor);   /* ripiego: arco senza binario */
  /* maschera ad anello: conta solo l'opacità, non il colore */
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - var(--rv-spinner-stroke) - 0.5px), black calc(100% - var(--rv-spinner-stroke)));
  mask: radial-gradient(farthest-side, transparent calc(100% - var(--rv-spinner-stroke) - 0.5px), black calc(100% - var(--rv-spinner-stroke)));
  animation: rv-spinner-spin 0.85s linear infinite;
}

@supports (color: color-mix(in srgb, red, blue)) {
  .rv-spinner {
    background-image: conic-gradient(
      color-mix(in srgb, currentColor 14%, transparent) 0 30%,
      currentColor
    );
  }
}

/* Testa arrotondata in cima all'arco */
.rv-spinner::after {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: calc(50% - var(--rv-spinner-stroke) / 2);
  inline-size: var(--rv-spinner-stroke);
  block-size: var(--rv-spinner-stroke);
  border-radius: var(--rv-radius-pill);
  background-color: currentColor;
}

.rv-spinner--sm {
  --rv-spinner-size: 16px;
  --rv-spinner-stroke: 2px;
}

.rv-spinner--lg {
  --rv-spinner-size: 32px;
  --rv-spinner-stroke: 3px;
}

.rv-spinner--primary {
  color: var(--rv-primary);
}


/* ==========================================================================
   11. .rv-dots — paginazione del carosello
   Indicatore, non controllo: si scorre col dito. La posizione corrente è
   un trattino più lungo (forma, non solo colore). Fino a ~7 punti; oltre,
   meglio un contatore «3 из 12».

   <div class="rv-dots" aria-hidden="true">
     <span class="rv-dots__dot" aria-current="true"></span>
     <span class="rv-dots__dot"></span>
     <span class="rv-dots__dot"></span>
   </div>
   ========================================================================== */
.rv-dots {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--rv-space-1-5);
  min-block-size: var(--rv-space-5);
}

.rv-dots__dot {
  flex: none;
  inline-size: 6px;
  block-size: 6px;
  border-radius: var(--rv-radius-pill);
  background-color: var(--rv-border-strong);
  transition:
    inline-size var(--rv-dur-base) var(--rv-ease-out),
    background-color var(--rv-dur-base) var(--rv-ease-out);
}

.rv-dots__dot[aria-current="true"],
.rv-dots__dot.is-active {
  inline-size: 18px;
  background-color: var(--rv-text);
}


/* ==========================================================================
   11b. .rv-steps — avanzamento di una procedura a passi
   Una riga di N barrette sottili che si dividono lo spazio in parti uguali:
   i passi fatti e quello corrente in --rv-primary, i futuri in --rv-border.
   Sotto, la didascalia che dice a parole dove siamo e come si chiama il passo.

   L'informazione VERA sta nella didascalia: il tracciato è decorativo
   (aria-hidden sul contenitore) e la didascalia è anche ciò che la schermata
   annuncia ai lettori di schermo (titolo della vista o region con aria-live
   quando il passo cambia senza cambiare pagina). Niente role="progressbar":
   il valore è già detto a parole, e un progressbar lo ripeterebbe in
   percentuale, che qui non significa nulla.

   <div class="rv-steps">
     <div class="rv-steps__track" aria-hidden="true">
       <span class="rv-steps__bar is-done"></span>
       <span class="rv-steps__bar is-current"></span>
       <span class="rv-steps__bar"></span>
       <span class="rv-steps__bar"></span>
     </div>
     <p class="rv-steps__caption">Шаг 2 из&nbsp;4 · Когда ехать</p>
   </div>

   A 360px la didascalia va a capo da sola: le barrette stanno in una riga a
   parte, quindi non si spostano né si accorciano.
   ========================================================================== */
.rv-steps {
  display: flex;
  flex-direction: column;
  gap: var(--rv-space-2);
}

.rv-steps__track {
  display: flex;
  align-items: center;
  gap: var(--rv-space-1);
}

.rv-steps__bar {
  flex: 1 1 0;
  min-inline-size: 0;
  block-size: 4px;
  border-radius: var(--rv-radius-pill);
  background-color: var(--rv-border);
  transition: background-color var(--rv-dur-base) var(--rv-ease-out);
}

/* Fatti e corrente: stesso pieno. La differenza fra «fatto» e «qui siamo» la
   dice la didascalia, non una terza tinta che nessuno saprebbe leggere. */
.rv-steps__bar.is-done,
.rv-steps__bar.is-current {
  background-color: var(--rv-primary);
}

.rv-steps__caption {
  font-size: var(--rv-fs-caption);
  line-height: var(--rv-lh-caption);
  color: var(--rv-text-secondary);
  overflow-wrap: break-word;
}


/* ==========================================================================
   12. .rv-sticky — barra che resta in alto
   Fondo del colore della pagina, appena trasparente, con sfocatura leggera.
   .is-stuck (da un IntersectionObserver su una sentinella) aggiunge il filo
   inferiore solo quando la barra è davvero attaccata. Va dentro un
   contenitore senza overflow: hidden (.rv-app usa clip, va bene).

   <div class="rv-sticky">
     <div class="rv-sticky__row">…ricerca compatta, filtri…</div>
   </div>

   <div class="rv-sticky rv-sticky--flush">
     <header class="rv-appbar rv-appbar--title">…</header>
   </div>
   ========================================================================== */
.rv-sticky {
  position: sticky;
  inset-block-start: var(--rv-safe-top);
  z-index: var(--rv-z-sticky);
  padding-block: var(--rv-space-2);
  padding-inline: var(--rv-gutter);
  background-color: var(--rv-bg);   /* pieno: ripiego senza color-mix() o senza sfocatura */
  -webkit-backdrop-filter: saturate(1.2) blur(12px);
  backdrop-filter: saturate(1.2) blur(12px);
  transition: box-shadow var(--rv-dur-base) var(--rv-ease-out);
}

/* Copre la striscia dell'area sicura sopra la barra quando è attaccata */
.rv-sticky::before {
  content: "";
  position: absolute;
  inset-inline: 0;
  inset-block-end: 100%;
  block-size: var(--rv-safe-top);
  background-color: inherit;
}

/* Appena trasparente solo dove ci sono sia color-mix() sia la sfocatura */
@supports (color: color-mix(in srgb, red, blue)) and ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .rv-sticky {
    background-color: color-mix(in srgb, var(--rv-bg) 90%, transparent);
  }
}

.rv-sticky.is-stuck {
  box-shadow: 0 var(--rv-border-width) 0 var(--rv-border);
}

/* Senza padding: per contenuti che hanno già il proprio, come un
   .rv-appbar (resta alta 56px) o una rail .rv-scroll-x che arriva ai bordi
   (in quel caso dai alla rail un padding-block) */
.rv-sticky--flush {
  padding: 0;
}

.rv-sticky__row {
  display: flex;
  align-items: center;
  gap: var(--rv-space-2);
  min-block-size: var(--rv-tap);
}


/* ==========================================================================
   12b. .rv-chat-bar — barra di scrittura della chat, fissa in fondo
   Campo che cresce con le righe fino a un tetto e poi scorre, bottone
   «invia» tondo a destra. La conversazione è .rv-chat (content.css).

   <div class="rv-app rv-app--chat-bar">
     <main class="rv-container">
       <div class="rv-chat" role="log" aria-label="Переписка с водителем">…</div>
     </main>
   </div>
   <form class="rv-chat-bar" action="…" method="post">
     <label class="rv-visually-hidden" for="chat-msg">Сообщение водителю</label>
     <textarea class="rv-textarea rv-chat-bar__input" id="chat-msg" name="text" rows="1"
               placeholder="Сообщение…" enterkeyhint="send"></textarea>
     <button class="rv-icon-btn rv-chat-bar__send" type="submit" aria-label="Отправить сообщение">
       <span class="rv-icon rv-icon--send" aria-hidden="true"></span>
     </button>
   </form>

   Chat chiusa (--closed): al posto del campo una frase che dice perché.
   <div class="rv-chat-bar rv-chat-bar--closed">
     <p class="rv-chat-bar__note"><span class="rv-icon rv-icon--lock" aria-hidden="true"></span>Чат закрыт: поездка завершена</p>
   </div>

   NOME DEL CAMPO — è la seconda e ultima eccezione del sistema alla label
   visibile (l'altra è .rv-search--compact): l'etichetta è la conversazione
   che sta subito sopra, e una scritta «Сообщение» fra l'ultima bolla e il
   campo ruberebbe la riga senza dire niente di nuovo. In cambio il campo ha
   sempre una <label> VERA in .rv-visually-hidden, che nomina l'interlocutore
   («Сообщение водителю», non «Сообщение»); niente aria-label sopra, che la
   coprirebbe senza aggiungere nulla. Il bottone ha il suo aria-label in
   russo. Regola completa in README §10.

   AREA SICURA — si usa --rv-safe-bottom di tokens.css, che è già il massimo
   fra env(safe-area-inset-bottom) e le aree del client Telegram: mai env()
   nudo, che dentro Telegram vale meno del vero. Stessa forma di
   .rv-sim-mainbutton, così le due barre in fondo si comportano uguale.

   ALTEZZA — a riposo --rv-chat-bar-h (8 + campo da 44 + 8). È la misura che
   usano .rv-app--chat-bar (spazio in fondo al contenuto) e lo scroll-padding
   del blocco 1b. Il campo che cresce alza solo la barra, non quelle misure.

   CAMPO CHE CRESCE — il campo è un .rv-textarea (16px: sotto, iOS ingrandisce
   la pagina al focus) e riusa field-sizing: content di forms.css, con
   min-block-size di una riga e max-block-size di cinque. Le righe di qui
   arrivano dopo forms.css nell'ordine di @import di riviera.css: stessa
   specificità, vince l'ordine. Dove field-sizing manca (iOS, Chromium < 123)
   il campo resta a una riga e scorre: nulla sparisce. Se una pagina vuole
   farlo crescere lo stesso, il contratto è scrivere el.style.blockSize:
   tetto e scorrimento restano quelli del CSS.

   TASTIERA iOS — dentro Telegram la webview si accorcia con la tastiera e la
   barra sale da sé; in Safari e WKWebView fuori da Telegram la viewport di
   layout non si accorcia e una barra fissa può restare dietro la tastiera.
   È un limite noto, da chiudere su un iPhone vero (README §12): qui non si
   inseguono i visualViewport con JavaScript, che romperebbe lo scorrimento.

   ALLINEAMENTO — align-items: flex-end tiene il bottone in basso mentre il
   campo cresce verso l'alto.
   ========================================================================== */
.rv-chat-bar {
  position: fixed;
  inset-inline: 0;
  inset-block-end: 0;
  z-index: var(--rv-z-tabbar);
  display: flex;
  align-items: flex-end;
  gap: var(--rv-space-2);
  padding-block: var(--rv-space-2) calc(var(--rv-safe-bottom) + var(--rv-space-2));
  padding-inline: calc(var(--rv-safe-left) + var(--rv-gutter)) calc(var(--rv-safe-right) + var(--rv-gutter));
  background-color: var(--rv-bg);
  box-shadow: 0 calc(-1 * var(--rv-border-width)) 0 var(--rv-border);
}

.rv-chat-bar__input {
  flex: 1 1 auto;
  min-inline-size: 0;
  min-block-size: var(--rv-tap);   /* 9 + 24 (una riga a 16px) + 9 + 2 di bordi */
  max-block-size: 140px;           /* cinque righe, poi scorre */
  padding-block: 9px;
  border-radius: var(--rv-radius-lg);
  resize: none;                    /* la maniglia litiga con la barra fissa */
  overflow-y: auto;
}

.rv-chat-bar__send {
  --rv-icon-btn-bg: var(--rv-primary);
  --rv-icon-btn-bg-hover: var(--rv-primary-hover);
  --rv-icon-btn-fg: var(--rv-text-on-primary);
  flex: none;
}

/* Campo vuoto: il bottone si spegne del tutto (fondo incassato + icona
   muted), altrimenti un cerchio verde prometterebbe un invio che non parte.
   Viene dopo .rv-icon-btn:disabled di actions.css, che spegne il solo colore. */
.rv-chat-bar__send:disabled,
.rv-chat-bar__send[aria-disabled="true"] {
  --rv-icon-btn-bg: var(--rv-surface-sunken);
  --rv-icon-btn-fg: var(--rv-text-muted);
}

.rv-chat-bar--closed {
  justify-content: center;
}

/* La frase che sostituisce il campo. Se la barra è già chiusa al caricamento
   non prende nessun ruolo; se si chiude durante la visita (la corsa finisce
   mentre la chat è aperta) la pagina le mette role="status", come .rv-notice. */
.rv-chat-bar__note {
  --rv-icon-size: 18px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--rv-space-2);
  min-block-size: var(--rv-tap);
  color: var(--rv-text-muted);
  font-size: var(--rv-fs-callout);
  line-height: var(--rv-lh-callout);
  text-align: center;
  overflow-wrap: break-word;
}

.rv-chat-bar__note > .rv-icon {
  flex: none;
}

/* Contrasto elevato di Windows: senza fondo la barra si confonderebbe con la
   conversazione, e il bottone pieno perderebbe il suo cerchio */
@media (forced-colors: active) {
  .rv-chat-bar {
    border-block-start: 1px solid CanvasText;
  }

  .rv-chat-bar__send {
    border: 1px solid CanvasText;
  }
}


/* ==========================================================================
   13. .rv-mainbutton-spacer — spazio per il MainButton di Telegram
   In fondo al contenuto delle sottopagine che mostrano il MainButton
   (BottomButton): nel browser l'ultimo elemento non finisce sotto il bottone
   simulato. Dentro Telegram il bottone vero sta FUORI dalla webview (la
   viewport si accorcia da sola): lo spazio non serve e sparisce.

   <main class="rv-container">… <div class="rv-mainbutton-spacer"></div></main>
   ========================================================================== */
.rv-mainbutton-spacer {
  display: block;
  flex: none;
  block-size: calc(var(--rv-mainbutton-h, 50px) + var(--rv-space-3));
  pointer-events: none;
}

.rv-in-telegram .rv-mainbutton-spacer {
  display: none;
}


/* ==========================================================================
   14. .rv-sim-mainbutton — MainButton simulato (SOLO demo nel browser)
   Riproduce la barra nativa: fondo = --rv-bg (come bottomBar), bottone
   --rv-primary con testo --rv-text-on-primary. js/riviera-telegram.js lo
   pilota fuori da Telegram: [hidden], testo in [data-rv-sim-label],
   caricamento = aria-busy="true", disattivo = disabled.
   Dentro Telegram è nascosto (.rv-in-telegram, messa su <html> dallo script):
   il bottone vero lo disegna il client, fuori dalla webview.

   <div class="rv-sim-mainbutton" hidden>
     <button class="rv-sim-mainbutton__button" type="button">
       <span data-rv-sim-label>Написать в Telegram</span>
     </button>
   </div>
   ========================================================================== */
.rv-sim-mainbutton {
  position: fixed;
  inset-inline: 0;
  inset-block-end: 0;
  z-index: var(--rv-z-tabbar);
  padding-block: var(--rv-space-2) calc(var(--rv-safe-bottom) + var(--rv-space-2));
  padding-inline: calc(var(--rv-safe-left) + var(--rv-gutter)) calc(var(--rv-safe-right) + var(--rv-gutter));
  background-color: var(--rv-bg);
}

.rv-in-telegram .rv-sim-mainbutton {
  display: none;
}

.rv-sim-mainbutton__button {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--rv-space-2);
  inline-size: 100%;
  max-inline-size: var(--rv-content-max);
  min-block-size: var(--rv-mainbutton-h, 50px);
  margin-inline: auto;
  padding-inline: var(--rv-space-4);
  border-radius: var(--rv-radius-sm);
  background-color: var(--rv-primary);
  color: var(--rv-text-on-primary);
  font-size: var(--rv-fs-headline);
  font-weight: var(--rv-fw-semibold);
  line-height: var(--rv-lh-headline);
  text-align: center;
  transition:
    background-color var(--rv-dur-fast) var(--rv-ease-out),
    transform var(--rv-dur-fast) var(--rv-ease-out);
}

.rv-sim-mainbutton__button > * {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

@media (hover: hover) {
  .rv-sim-mainbutton__button:hover {
    background-color: var(--rv-primary-hover);
  }
}

.rv-sim-mainbutton__button:active {
  background-color: var(--rv-primary-pressed);
  transform: scale(var(--rv-press-scale));
}

.rv-sim-mainbutton__button:disabled {
  background-color: var(--rv-surface-sunken);
  color: var(--rv-text-muted);
  transform: none;
}

/* Caricamento: il testo resta nel flusso (larghezza stabile) e nell'albero di
   accessibilità (il bottone conserva il nome), ma non si vede: trasparente
   come in .rv-btn[aria-busy], non visibility:hidden. Il colore si toglie ai
   figli e non al bottone: lo spinner qui sotto usa currentColor. */
.rv-sim-mainbutton__button[aria-busy="true"] {
  cursor: progress;
}

.rv-sim-mainbutton__button[aria-busy="true"] > * {
  color: transparent;
}

.rv-sim-mainbutton__button[aria-busy="true"]::after {
  content: "";
  position: absolute;
  inset-block-start: calc(50% - 11px);
  inset-inline-start: calc(50% - 11px);
  inline-size: 22px;
  block-size: 22px;
  border-radius: var(--rv-radius-pill);
  background-image: conic-gradient(transparent 0 30%, currentColor);   /* ripiego senza color-mix() */
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 2.5px), black calc(100% - 2px));
  mask: radial-gradient(farthest-side, transparent calc(100% - 2.5px), black calc(100% - 2px));
  animation: rv-spinner-spin 0.85s linear infinite;
}

@supports (color: color-mix(in srgb, red, blue)) {
  .rv-sim-mainbutton__button[aria-busy="true"]::after {
    background-image: conic-gradient(color-mix(in srgb, currentColor 18%, transparent) 0 30%, currentColor);
  }
}


/* ==========================================================================
   14b. .rv-viewer — visore foto a tutto schermo
   Un <dialog> aperto con showModal(): Esc, focus intrappolato e ritorno del
   focus li dà il browser. Foto intere (contain) su fondo pagina, una per
   schermata, scorrimento orizzontale con snap (swipe nativo), contatore
   «2 / 12» fra le frecce in basso (sotto il pollice), chiusura in alto.

   <dialog class="rv-viewer" aria-labelledby="viewer-title">
     <header class="rv-viewer__head">
       <h2 class="rv-viewer__title" id="viewer-title">Работы Ивана</h2>
       <button class="rv-icon-btn" type="button" aria-label="Закрыть" data-viewer-close>
         <span class="rv-icon rv-icon--close" aria-hidden="true"></span>
       </button>
     </header>
     <div class="rv-viewer__track" role="region" aria-label="Фото, листайте влево и вправо" tabindex="0">
       <figure class="rv-viewer__slide">
         <img class="rv-viewer__img" src="…" alt="Ванная после ремонта" width="1200" height="900">
       </figure> …
     </div>
     <footer class="rv-viewer__foot">
       <button class="rv-icon-btn rv-icon-btn--surface" type="button" aria-label="Предыдущее фото" data-viewer-prev>
         <span class="rv-icon rv-icon--chevron-left" aria-hidden="true"></span>
       </button>
       <p class="rv-viewer__count rv-num" aria-live="polite"><span class="rv-visually-hidden">Фото </span>2<span aria-hidden="true"> / </span><span class="rv-visually-hidden"> из </span>12</p>
       <button class="rv-icon-btn rv-icon-btn--surface" type="button" aria-label="Следующее фото" data-viewer-next>
         <span class="rv-icon rv-icon--chevron-right" aria-hidden="true"></span>
       </button>
     </footer>
   </dialog>

   Contratto per lo script della pagina (il CSS non conta le foto):
   - apertura: dialog.showModal(), poi slide.scrollIntoView({ inline: "center",
     block: "nearest", behavior: "instant" }) sulla foto toccata;
   - al termine dello scorrimento (evento scroll + requestAnimationFrame, o
     scrollend) indice = Math.round(track.scrollLeft / track.clientWidth),
     aggiorna il contatore e disabled delle frecce agli estremi;
   - frecce e tasti ←/→ (keydown sul dialog, con preventDefault):
     track.scrollBy({ left: ±track.clientWidth }); lo smooth lo toglie da solo
     il moto ridotto (base.css porta scroll-behavior ad auto);
   - chiusura: .is-closing, animationend, dialog.close(). Dentro Telegram
     BackButton chiude il visore finché è aperto.
   Aree sicure: testata e piede ne tengono conto; le foto restano fra le due
   barre. Una sola foto: niente piede (o frecce disabled).
   Notte in entrambi i temi, se serve: data-theme="dark" sul dialog.
   ========================================================================== */
.rv-viewer {
  position: fixed;
  inset: 0;
  z-index: var(--rv-z-sheet);
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  inline-size: auto;                  /* reset delle regole UA di <dialog> */
  block-size: auto;
  max-inline-size: none;
  max-block-size: none;
  margin: 0;
  padding: 0;
  border: 0;
  overflow: hidden;
  overscroll-behavior: contain;
  background-color: var(--rv-bg);
  color: var(--rv-text);
  animation: rv-viewer-in var(--rv-dur-base) var(--rv-ease-out) both;
}

.rv-viewer:where(dialog:not([open])) {
  display: none;
}

.rv-viewer::backdrop {
  background: transparent;            /* il fondo è il dialog stesso */
}

.rv-viewer.is-closing {
  animation: rv-viewer-out var(--rv-dur-fast) var(--rv-ease-in-out) both;
}

/* Testata: titolo a sinistra, chiusura a destra; sotto la barra di stato */
.rv-viewer__head,
.rv-viewer__foot {
  display: flex;
  align-items: center;
  gap: var(--rv-space-2);
  min-block-size: var(--rv-appbar-h);
}

.rv-viewer__head {
  padding-block-start: var(--rv-safe-top);
  padding-inline: calc(var(--rv-safe-left) + var(--rv-gutter)) calc(var(--rv-safe-right) + var(--rv-space-1-5));
}

.rv-viewer__title {
  flex: 1 1 auto;
  min-inline-size: 0;
  overflow: hidden;
  font-family: var(--rv-font-display);
  font-size: var(--rv-fs-title-3);
  font-weight: var(--rv-fw-medium);
  line-height: var(--rv-lh-title-3);
  color: var(--rv-heading);
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* Piede: frecce ai lati (bersagli da 44px sotto i pollici), contatore al centro */
.rv-viewer__foot {
  justify-content: space-between;
  padding-block: var(--rv-space-2) calc(var(--rv-safe-bottom) + var(--rv-space-2));
  padding-inline: calc(var(--rv-safe-left) + var(--rv-gutter)) calc(var(--rv-safe-right) + var(--rv-gutter));
}

.rv-viewer__count {
  flex: 1 1 auto;
  font-size: var(--rv-fs-body);
  font-weight: var(--rv-fw-semibold);
  line-height: var(--rv-lh-body);
  color: var(--rv-text);
  font-variant-numeric: tabular-nums lining-nums;
  text-align: center;
  white-space: nowrap;
}

/* Fila scorrevole: una foto per schermata, snap obbligatorio che non salta
   foto con uno swipe veloce. Scrollbar nascosta anche col mouse: ci sono le
   frecce e i tasti. */
.rv-viewer__track {
  display: flex;
  min-block-size: 0;
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

.rv-viewer__track::-webkit-scrollbar {
  display: none;
}

/* Anello dentro la fila: fuori sarebbe tagliato dal dialog */
.rv-viewer__track:focus-visible {
  outline-offset: calc(-1 * var(--rv-focus-width));
}

.rv-viewer__slide {
  display: flex;
  flex: 0 0 100%;
  align-items: center;
  justify-content: center;
  min-inline-size: 0;
  margin: 0;
  padding-block: var(--rv-space-2);
  padding-inline: calc(var(--rv-safe-left) + var(--rv-space-2)) calc(var(--rv-safe-right) + var(--rv-space-2));
  scroll-snap-align: center;
  scroll-snap-stop: always;
}

/* Foto intera: il box riempie la fascia, l'immagine ci sta dentro senza tagli */
.rv-viewer__img {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  min-block-size: 0;
  object-fit: contain;
  filter: var(--rv-image-dim);
  -webkit-user-select: none;
  user-select: none;
  -webkit-user-drag: none;
}


/* ==========================================================================
   15. Animazioni
   Solo trasformazioni e opacità (niente layout). Con moto ridotto base.css
   ne porta la durata a ~0: lo stato finale arriva subito.
   ========================================================================== */
@keyframes rv-overlay-in {
  from { background-color: transparent; }
}

@keyframes rv-overlay-out {
  to { background-color: transparent; }
}

@keyframes rv-sheet-rise {
  from { transform: translateY(100%); }
}

@keyframes rv-sheet-sink {
  to { transform: translateY(100%); }
}

@keyframes rv-dialog-pop {
  from {
    opacity: 0;
    transform: translateY(8px) scale(0.98);
  }
}

@keyframes rv-dialog-out {
  to {
    opacity: 0;
    transform: scale(0.98);
  }
}

@keyframes rv-toast-rise {
  from {
    opacity: 0;
    transform: translateY(12px);
  }
}

@keyframes rv-toast-sink {
  to {
    opacity: 0;
    transform: translateY(8px);
  }
}

@keyframes rv-viewer-in {
  from { opacity: 0; }
}

@keyframes rv-viewer-out {
  to { opacity: 0; }
}

@keyframes rv-skeleton-shimmer {
  from { background-position: 100% 0; }
  to { background-position: 0 0; }
}

@keyframes rv-spinner-spin {
  to { transform: rotate(1turn); }
}
