/* ============================================================================= SteUP — Componenti condivisi (STILE_UI §6) Mattoni riusabili. Le schermate compongono questi, non inventano stili. ========================================================================== */ /* ----------------------------------------------------------------------------- §5 AppBar — alta, titolo a sinistra, fusa con la pagina -------------------------------------------------------------------------- */ .appbar { flex: 0 0 auto; display: flex; align-items: center; gap: var(--space-xs); min-height: var(--appbar-height); padding: var(--space-xs) var(--space-md); padding-top: calc(var(--space-xs) + env(safe-area-inset-top, 0px)); background-color: var(--workspace); } .appbar__lead { display: flex; align-items: center; flex: 0 0 auto; } .appbar__titles { flex: 1 1 auto; min-width: 0; } .appbar__title { font-size: var(--headline-sm-size); line-height: var(--headline-sm-height); font-weight: 700; letter-spacing: -0.02em; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .appbar__subtitle { font-size: var(--label-md-size); line-height: 1.3; font-weight: 600; color: var(--ink-muted); margin-top: var(--space-2); } .appbar__actions { display: flex; align-items: center; gap: var(--space-4); flex: 0 0 auto; } /* Titolo compatto per i form a schermo pieno, dove il compito e' gia' noto. */ .appbar--compact { min-height: 64px; } .appbar--compact .appbar__title { font-size: var(--title-lg-size); line-height: var(--title-lg-height); } @media (min-width: 720px) { .appbar { padding-inline: 28px; } } /* ----------------------------------------------------------------------------- §6.2 Panel — la superficie base di tutto -------------------------------------------------------------------------- */ .panel { background-color: var(--surface); border-radius: var(--radius-surface); padding: var(--space-md); overflow: hidden; } .panel--flush { padding: 0; } .panel--lg { padding: var(--space-18); } .panel--info { background-color: var(--info-surface); } .panel--muted { background-color: var(--surface-muted); } .panel--danger { background-color: var(--danger-surface); } /* Pannello interamente toccabile. */ .panel--tappable { cursor: pointer; -webkit-tap-highlight-color: transparent; transition: transform var(--duration-fast) var(--ease-out), background-color var(--duration-fast) var(--ease-out); } .panel--tappable:active { transform: scale(0.985); background-color: var(--surface-muted); } @media (prefers-reduced-motion: reduce) { .panel--tappable:active { transform: none; } } /* ----------------------------------------------------------------------------- §6.3 SectionHeader -------------------------------------------------------------------------- */ .section-header { display: flex; align-items: flex-start; gap: var(--space-sm); margin-bottom: var(--space-sm); } .section-header__text { flex: 1 1 auto; min-width: 0; } .section-header__title { font-size: var(--title-lg-size); line-height: var(--title-lg-height); font-weight: 700; letter-spacing: -0.01em; color: var(--ink); } .section-header__desc { margin-top: var(--space-4); font-size: var(--body-md-size); line-height: var(--body-md-height); color: var(--ink-muted); } .section-header__aside { flex: 0 0 auto; } /* ----------------------------------------------------------------------------- §6.4 Badge — stato in pillola Mai il solo colore: sempre icona + etichetta. -------------------------------------------------------------------------- */ .badge { display: inline-flex; align-items: center; gap: 5px; padding: 5px 9px; border-radius: var(--radius-pill); font-size: var(--label-sm-size); line-height: 1.2; font-weight: 700; white-space: nowrap; } .badge__icon { font-size: 15px; line-height: 1; flex: 0 0 auto; } .badge--danger { background-color: var(--danger-surface); color: var(--danger-ink); } .badge--warning { background-color: var(--warning-surface); color: var(--warning-ink); } .badge--success { background-color: var(--success-surface); color: var(--success-ink); } .badge--info { background-color: var(--info-surface); color: var(--info-ink); } .badge--neutral { background-color: var(--neutral-surface); color: var(--neutral-ink); } .badge-row { display: flex; flex-wrap: wrap; gap: var(--space-xs); } /* ----------------------------------------------------------------------------- §6.5 Card di elenco -------------------------------------------------------------------------- */ .list-card { position: relative; background-color: var(--surface); border-radius: var(--radius-surface); padding: var(--space-md); overflow: hidden; } .list-card__head { display: flex; align-items: flex-start; gap: var(--space-xs); } .list-card__ident { flex: 1 1 auto; min-width: 0; } /* Il titolo e' l'entita' (il punto vendita), non il tipo di record. */ .list-card__title { font-size: var(--title-lg-size); line-height: 1.15; font-weight: 800; letter-spacing: -0.01em; color: var(--ink); margin-top: var(--space-2); } .list-card__meta { margin-top: var(--space-6); font-size: var(--body-md-size); line-height: var(--body-md-height); font-weight: 600; color: var(--ink-muted); } .list-card__badges { margin-top: var(--space-sm); } .list-card__footer { display: flex; align-items: center; gap: var(--space-6); margin-top: var(--space-sm); font-size: var(--body-md-size); font-weight: 600; color: var(--ink-muted); } .list-card__footer .icon { font-size: 16px; } /* Barra azioni interna: affiancate, in colonna quando la card e' stretta. */ .list-card__actions { display: flex; gap: var(--space-xs); margin-top: var(--space-md); } .list-card__actions > * { flex: 1 1 0; min-width: 0; } @container list-card (max-width: 350px) { .list-card__actions { flex-direction: column; } } .list-card { container-type: inline-size; container-name: list-card; } .card-list { display: flex; flex-direction: column; gap: var(--space-10); } /* ----------------------------------------------------------------------------- §6.6 Pannello di riepilogo -------------------------------------------------------------------------- */ .summary__head { display: flex; align-items: flex-start; gap: var(--space-sm); } .summary__title { font-size: var(--title-lg-size); line-height: var(--title-lg-height); font-weight: 700; letter-spacing: -0.01em; color: var(--ink); } .summary__subtitle { margin-top: var(--space-2); font-size: var(--body-md-size); font-weight: 600; color: var(--ink-muted); } .summary__metrics { display: flex; gap: var(--space-sm); margin-top: var(--space-md); } .summary__metric { flex: 1 1 0; min-width: 0; } .summary__metric-icon { font-size: 20px; line-height: 1; color: var(--primary-dark); } .summary__metric-value { margin-top: var(--space-4); font-size: var(--title-md-size); line-height: var(--title-md-height); font-weight: 700; color: var(--ink); } .summary__metric-label { font-size: var(--label-sm-size); line-height: 1.3; font-weight: 600; color: var(--ink-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .summary__progress-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-xs); margin-top: var(--space-md); } .summary__progress-count { font-size: var(--body-md-size); font-weight: 600; color: var(--ink-muted); } .summary__progress-pct { font-size: var(--headline-sm-size); line-height: 1; font-weight: 700; letter-spacing: -0.02em; color: var(--primary-dark); } .progress-track { height: 8px; border-radius: var(--radius-compact); background-color: rgba(0, 35, 57, 0.12); overflow: hidden; margin-top: var(--space-xs); } /* Si anima con scaleX invece che con width: la larghezza rifa' il layout a ogni frame, la trasformazione no. La percentuale arriva come --progress. */ .progress-fill { height: 100%; width: 100%; border-radius: var(--radius-compact); background-color: var(--primary); transform: scaleX(var(--progress, 0)); transform-origin: left center; transition: transform var(--duration-slow) var(--ease-out); } /* ----------------------------------------------------------------------------- §6.7 Chip e filtri -------------------------------------------------------------------------- */ .chip-row { display: flex; gap: var(--space-xs); align-items: center; min-height: var(--touch-min); overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch; padding-inline: var(--space-md); margin-inline: calc(var(--space-md) * -1); } .chip-row::-webkit-scrollbar { display: none; } .chip { display: inline-flex; align-items: center; gap: var(--space-6); min-height: 36px; padding: var(--space-xs) var(--space-sm); border-radius: var(--radius-pill); border: 1px solid var(--divider); background-color: var(--surface-muted); color: var(--ink-muted); font-size: var(--label-lg-size); font-weight: 600; white-space: nowrap; transition: background-color var(--duration-fast) var(--ease-out), border-color var(--duration-fast) var(--ease-out), color var(--duration-fast) var(--ease-out); } .chip[aria-pressed="true"], .chip--selected { background-color: var(--info-surface); border-color: var(--primary); color: var(--primary-dark); } /* ----------------------------------------------------------------------------- §6.8 Campo di ricerca — completamente stondato, senza bordo a riposo -------------------------------------------------------------------------- */ .search-field { display: flex; align-items: center; gap: var(--space-xs); min-height: var(--touch-min); padding: var(--space-14) var(--space-18); border-radius: var(--radius-pill); background-color: var(--surface-muted); border: 1.5px solid transparent; transition: border-color var(--duration-fast) var(--ease-out); } .search-field:focus-within { border-color: var(--primary); } .search-field__icon { color: var(--ink-muted); font-size: 20px; flex: 0 0 auto; } .search-field input { flex: 1 1 auto; min-width: 0; border: none; background: none; outline: none; font: inherit; font-size: var(--body-lg-size); color: var(--ink); } .search-field input::placeholder { color: var(--ink-muted); } /* ----------------------------------------------------------------------------- §6.11 Feedback inline — messaggi persistenti dentro la pagina -------------------------------------------------------------------------- */ .feedback { display: flex; align-items: flex-start; gap: var(--space-10); padding: var(--space-sm); border-radius: var(--radius-compact); font-size: var(--body-lg-size); line-height: 1.4; font-weight: 600; } .feedback__icon { font-size: 20px; line-height: 1.2; flex: 0 0 auto; } .feedback--danger { background-color: var(--danger-surface); color: var(--danger-ink); } .feedback--warning { background-color: var(--warning-surface); color: var(--warning-ink); } .feedback--success { background-color: var(--success-surface); color: var(--success-ink); } .feedback--info { background-color: var(--info-surface); color: var(--info-ink); } /* ----------------------------------------------------------------------------- §6.12 Stato vuoto — insegna la mossa successiva, non dice "niente qui" -------------------------------------------------------------------------- */ .empty-state { display: flex; flex-direction: column; align-items: center; text-align: center; max-width: 420px; margin-inline: auto; padding: 28px; } .empty-state__icon { font-size: 44px; line-height: 1; color: var(--ink-muted); } .empty-state__title { margin-top: var(--space-14); font-size: var(--title-md-size); line-height: var(--title-md-height); font-weight: 700; color: var(--ink); } .empty-state__text { margin-top: var(--space-6); font-size: var(--body-lg-size); line-height: 1.45; color: var(--ink-muted); } .empty-state__action { margin-top: var(--space-18); } /* ----------------------------------------------------------------------------- §6.13 Barra azioni fissa -------------------------------------------------------------------------- */ .action-bar { flex: 0 0 auto; display: flex; gap: var(--space-sm); padding: var(--space-sm) var(--space-md) var(--space-md); padding-bottom: calc(var(--space-md) + env(safe-area-inset-bottom, 0px)); background-color: var(--surface); border-top: 1px solid var(--divider); } .action-bar > * { flex: 1 1 0; min-width: 0; } @media (max-width: 390px) { .action-bar { flex-direction: column; gap: var(--space-xs); } } /* ----------------------------------------------------------------------------- §6.15 Navigazione — barra flottante in basso -------------------------------------------------------------------------- */ .navbar { position: fixed; left: var(--space-md); right: var(--space-md); bottom: calc(var(--space-sm) + env(safe-area-inset-bottom, 0px)); z-index: var(--z-navbar); display: flex; align-items: stretch; height: var(--navbar-height); border-radius: 24px; background-color: var(--surface); border: 1px solid var(--divider); overflow: hidden; transition: transform var(--duration-base) var(--ease-out), opacity var(--duration-base) var(--ease-out); } .navbar--hidden { transform: translateY(calc(100% + var(--space-lg))); opacity: 0; pointer-events: none; } .navbar__item { flex: 1 1 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-2); min-width: var(--touch-min); color: var(--ink-muted); text-decoration: none; background: none; border: none; font-family: inherit; transition: color var(--duration-fast) var(--ease-out); } .navbar__icon { display: flex; align-items: center; justify-content: center; width: 56px; height: 30px; border-radius: var(--radius-pill); font-size: 20px; transition: background-color var(--duration-base) var(--ease-out); } .navbar__label { font-size: var(--label-md-size); line-height: 1.2; font-weight: 600; } /* Icona piena e pastiglia di selezione sulla voce attiva. */ .navbar__item.active { color: var(--primary-dark); } .navbar__item.active .navbar__icon { background-color: var(--info-surface); } .navbar__item:active .navbar__icon { transform: scale(0.94); } /* Azione primaria della schermata, dentro la barra e a portata di pollice. */ .navbar__fab { flex: 0 0 auto; display: flex; align-items: center; justify-content: center; width: 56px; height: 44px; margin: 12px var(--space-sm) 12px 0; border: none; border-radius: var(--radius-control); background-color: var(--primary); color: var(--on-primary); font-size: 26px; transition: transform var(--duration-fast) var(--ease-out), background-color var(--duration-fast) var(--ease-out); } .navbar__fab:active { transform: scale(0.94); background-color: var(--primary-pressed); } .navbar__fab:disabled { background-color: var(--divider); color: var(--ink-muted); } @media (prefers-reduced-motion: reduce) { .navbar__item:active .navbar__icon, .navbar__fab:active { transform: none; } } /* Da 720px la navigazione diventa una colonna laterale (rail). */ @media (min-width: 720px) { .navbar { left: var(--space-md); right: auto; top: calc(var(--space-md) + env(safe-area-inset-top, 0px)); bottom: calc(var(--space-md) + env(safe-area-inset-bottom, 0px)); height: auto; width: 96px; flex-direction: column; justify-content: flex-start; gap: var(--space-xs); padding: var(--space-sm) 0; } .navbar__item { flex: 0 0 auto; padding-block: var(--space-xs); } .navbar__fab { order: -1; width: 56px; height: 48px; margin: var(--space-xs) auto var(--space-sm); } .app-shell { padding-left: calc(96px + var(--space-lg)); } .page-body { padding-bottom: calc(var(--space-lg) + env(safe-area-inset-bottom, 0px)); } } /* ----------------------------------------------------------------------------- §6.14 Bottom sheet Sale dal basso, con la maniglia di trascinamento e un'unica linea di struttura sotto la testata. -------------------------------------------------------------------------- */ .sheet-scrim { position: fixed; inset: 0; z-index: var(--z-modal); background-color: rgba(0, 12, 20, 0.45); animation: scrim-in var(--duration-base) var(--ease-out); } .sheet { position: fixed; left: 0; right: 0; bottom: 0; z-index: var(--z-modal); display: flex; flex-direction: column; max-height: 78dvh; padding-bottom: env(safe-area-inset-bottom, 0px); background-color: var(--surface); border-radius: var(--radius-surface) var(--radius-surface) 0 0; animation: sheet-in var(--duration-slow) var(--ease-out); } .sheet__handle { width: 36px; height: 4px; margin: var(--space-sm) auto var(--space-xs); border-radius: var(--radius-pill); background-color: var(--divider); flex: 0 0 auto; } .sheet__head { display: flex; align-items: flex-start; gap: var(--space-xs); padding: 0 var(--space-xs) var(--space-sm) var(--space-20); flex: 0 0 auto; } .sheet__titles { flex: 1 1 auto; min-width: 0; } .sheet__title { font-size: var(--title-lg-size); line-height: var(--title-lg-height); font-weight: 700; letter-spacing: -0.01em; color: var(--ink); } .sheet__subtitle { margin-top: var(--space-2); font-size: var(--body-md-size); font-weight: 600; color: var(--ink-muted); } .sheet__divider { height: 1px; background-color: var(--divider); flex: 0 0 auto; } .sheet__body { flex: 1 1 auto; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; padding: var(--space-md); scrollbar-width: none; } .sheet__body::-webkit-scrollbar { display: none; } .sheet--tall { height: 92dvh; max-height: 92dvh; } @keyframes sheet-in { from { transform: translateY(100%); } to { transform: none; } } @keyframes scrim-in { from { opacity: 0; } to { opacity: 1; } } @media (prefers-reduced-motion: reduce) { .sheet { animation: scrim-in var(--duration-fast) var(--ease-out); } } /* ----------------------------------------------------------------------------- §6.16 Liste di navigazione (profilo, impostazioni) -------------------------------------------------------------------------- */ .nav-list__item { display: flex; align-items: center; gap: var(--space-sm); width: 100%; min-height: var(--touch-min); padding: var(--space-sm) 0; background: none; border: none; font-family: inherit; font-size: var(--body-lg-size); font-weight: 600; color: var(--ink); text-align: left; } .nav-list__item + .nav-list__item { border-top: 1px solid var(--divider); } .nav-list__icon { font-size: 22px; color: var(--ink-muted); flex: 0 0 auto; } .nav-list__label { flex: 1 1 auto; min-width: 0; } .nav-list__chevron { font-size: 20px; color: var(--ink-muted); flex: 0 0 auto; } .nav-list__item--danger { color: var(--danger-ink); } .nav-list__item--danger .nav-list__icon { color: var(--danger-ink); } /* Intestazione profilo */ .profile-head { display: flex; align-items: center; gap: var(--space-md); } .profile-avatar { display: flex; align-items: center; justify-content: center; width: 60px; height: 60px; border-radius: var(--radius-pill); background-color: var(--info-surface); color: var(--primary-dark); font-size: 22px; font-weight: 800; flex: 0 0 auto; } /* ----------------------------------------------------------------------------- §6.17 Campo composito (tappabile) -------------------------------------------------------------------------- */ .field-tap { display: block; width: 100%; padding: var(--space-14); border: none; border-radius: var(--radius-control); background-color: var(--surface-muted); font-family: inherit; text-align: left; transition: transform var(--duration-fast) var(--ease-out); } .field-tap:active { transform: scale(0.99); } .field-tap__label { font-size: var(--label-md-size); line-height: 1.25; font-weight: 600; color: var(--ink-muted); } .field-tap__value { display: flex; align-items: center; gap: 7px; margin-top: 5px; font-size: var(--body-lg-size); font-weight: 600; color: var(--ink); overflow: hidden; } .field-tap__value span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .field-tap__icon { font-size: 20px; color: var(--ink-muted); flex: 0 0 auto; } /* ----------------------------------------------------------------------------- Barra di stato connessione — la firma visiva di "Offline e' la verita'" -------------------------------------------------------------------------- */ .connection { position: fixed; top: 0; left: 0; right: 0; z-index: var(--z-connection); display: flex; align-items: center; justify-content: center; gap: var(--space-6); padding: var(--space-6) var(--space-md); padding-top: calc(var(--space-6) + env(safe-area-inset-top, 0px)); font-size: var(--label-md-size); font-weight: 700; color: #ffffff; transform: translateY(-100%); transition: transform var(--duration-slow) var(--ease-out); } .connection--show { transform: translateY(0); } .connection--ok { background-color: var(--success-solid); } .connection--down { background-color: var(--warning-solid); } .connection--offline { background-color: var(--danger-solid); } /* ----------------------------------------------------------------------------- §6.10 Dialog — composizione verticale allineata a sinistra -------------------------------------------------------------------------- */ .dialog-icon { display: flex; align-items: center; justify-content: center; width: 48px; height: 48px; border-radius: var(--radius-pill); font-size: 26px; margin-bottom: var(--space-md); } .dialog-icon--danger { background-color: var(--danger-surface); color: var(--danger-ink); } .dialog-icon--warning { background-color: var(--warning-surface); color: var(--warning-ink); } .dialog-icon--success { background-color: var(--success-surface); color: var(--success-ink); } .dialog-icon--info { background-color: var(--info-surface); color: var(--info-ink); } .dialog-title { font-size: var(--title-md-size); line-height: var(--title-md-height); font-weight: 700; color: var(--ink); } .dialog-message { margin-top: var(--space-xs); font-size: var(--body-lg-size); line-height: 1.45; color: var(--ink-muted); } /* Il dettaglio tecnico non e' il messaggio: sta sotto, in secondo piano, per chi deve riferirlo all'assistenza. */ .dialog-detail { margin-top: var(--space-sm); padding: var(--space-xs) var(--space-10); border-radius: var(--radius-compact); background-color: var(--surface-muted); font-size: var(--label-md-size); line-height: 1.4; color: var(--ink-muted); word-break: break-word; user-select: text; -webkit-user-select: text; } /* ----------------------------------------------------------------------------- §7 Accesso — variante derivata, non un tema a parte Cambia il registro, non la struttura: l'accento cromatico sparisce e l'attenzione va al marchio. Il corallo torna dentro l'app, dove indica le azioni. -------------------------------------------------------------------------- */ .onboarding { display: flex; align-items: center; justify-content: center; min-height: 100dvh; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: var(--space-lg); padding-top: calc(var(--space-lg) + env(safe-area-inset-top, 0px)); padding-bottom: calc(var(--space-lg) + env(safe-area-inset-bottom, 0px)); background-color: var(--surface-muted); } .onboarding__panel { width: 100%; max-width: 440px; display: flex; flex-direction: column; } .onboarding__logo { height: 128px; object-fit: contain; margin-inline: auto; } .onboarding__fields { display: flex; flex-direction: column; gap: var(--space-md); margin-top: var(--space-xl); } .onboarding__field { background-color: var(--surface); border: 1px solid var(--divider); border-radius: var(--radius-control); padding: var(--space-xs) var(--space-md); transition: border-color var(--duration-fast) var(--ease-out), box-shadow var(--duration-fast) var(--ease-out); } .onboarding__field:focus-within { border-color: var(--primary-dark); box-shadow: inset 0 0 0 1px var(--primary-dark); } .onboarding__error { margin-top: var(--space-lg); text-align: center; font-size: var(--body-lg-size); font-weight: 600; color: var(--danger-ink); } /* Bottone a pillola, alto 52: e' l'unico gesto della schermata. */ .onboarding__submit { display: flex; align-items: center; justify-content: center; gap: var(--space-xs); width: 100%; min-height: 52px; margin-top: var(--space-sm); border: none; border-radius: var(--radius-pill); background-color: var(--primary-dark); color: #ffffff; font-family: inherit; font-size: var(--body-lg-size); font-weight: 700; transition: transform var(--duration-fast) var(--ease-out), background-color var(--duration-fast) var(--ease-out); } .onboarding__submit:active { transform: scale(0.98); } .onboarding__submit:disabled { background-color: var(--ink-muted); } /* Il caricamento prende il posto dell'icona, stessa dimensione: il bottone non cambia forma mentre lavora. */ .onboarding__spinner { width: 18px; height: 18px; border-radius: 50%; border: 2px solid rgba(255, 255, 255, 0.35); border-top-color: #ffffff; animation: boot-spin 0.7s linear infinite; } @keyframes boot-spin { to { transform: rotate(360deg); } } .onboarding__footer { display: flex; align-items: center; justify-content: center; gap: var(--space-xs); margin-top: var(--space-20); font-size: var(--label-md-size); font-weight: 600; color: var(--ink-muted); } .onboarding__footer img { height: 18px; } @media (prefers-reduced-motion: reduce) { .onboarding__submit:active { transform: none; } .onboarding__spinner { animation-duration: 2s; } } /* ----------------------------------------------------------------------------- Caricamento — scheletri, non spinner in mezzo al contenuto -------------------------------------------------------------------------- */ .skeleton { border-radius: var(--radius-compact); background-color: var(--surface-muted); position: relative; overflow: hidden; } .skeleton::after { content: ""; position: absolute; inset: 0; background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.75), transparent); animation: skeleton-sweep 1.4s var(--ease-in-out) infinite; } @keyframes skeleton-sweep { from { transform: translateX(-100%); } to { transform: translateX(100%); } } @media (prefers-reduced-motion: reduce) { .skeleton::after { animation: none; } } /* ----------------------------------------------------------------------------- Overlay di lavoro in corso Copre solo quando l'operazione e' bloccante (invio al server): altrove il contenuto si aggiorna in silenzio, senza sostituirsi con uno spinner. -------------------------------------------------------------------------- */ .overlay { position: fixed; inset: 0; z-index: var(--z-overlay); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-md); padding: var(--space-xl); background-color: rgba(233, 237, 242, 0.92); animation: scrim-in var(--duration-fast) var(--ease-out); } .overlay__message { max-width: 320px; text-align: center; font-size: var(--body-lg-size); line-height: 1.4; font-weight: 600; color: var(--ink); } .spinner { width: 34px; height: 34px; border-radius: 50%; border: 3px solid rgba(0, 35, 57, 0.15); border-top-color: var(--primary); animation: boot-spin 0.7s linear infinite; } .spinner--lg { width: 44px; height: 44px; border-width: 4px; } /* Spunta di conferma: si disegna, non lampeggia. E' il riscontro del momento piu' importante dell'app, quando il lavoro arriva al server. */ .checkmark { width: 56px; height: 56px; border-radius: 50%; background-color: var(--success-surface); display: flex; align-items: center; justify-content: center; animation: check-pop var(--duration-slow) var(--ease-out); } .checkmark svg { width: 30px; height: 30px; } .checkmark path { stroke: var(--success-ink); stroke-width: 3.5; stroke-linecap: round; stroke-linejoin: round; fill: none; stroke-dasharray: 32; stroke-dashoffset: 32; animation: check-draw 420ms var(--ease-out) 80ms forwards; } @keyframes check-pop { from { transform: scale(0.85); opacity: 0; } to { transform: none; opacity: 1; } } @keyframes check-draw { to { stroke-dashoffset: 0; } } @media (prefers-reduced-motion: reduce) { .spinner { animation-duration: 2s; } .checkmark { animation: none; } .checkmark path { animation: none; stroke-dashoffset: 0; } } .skeleton--title { height: 22px; width: 60%; } .skeleton--line { height: 15px; width: 100%; } .skeleton--line-short { height: 15px; width: 40%; } .skeleton--badge { height: 24px; width: 110px; border-radius: var(--radius-pill); }