Files
SteUP_Dotnet/SteUp.Shared/wwwroot/css/components.css
T
MarcoE 2d7e3c591e Dai una gerarchia ai pesi tipografici
Quasi tutto il testo dell'app stava fra 600 e 800: etichette, valori, indirizzi,
conteggi, voci di elenco. Un grassetto ovunque non segnala niente, e la pagina
Profilo era il caso peggiore — ogni riga gridava come un titolo.

La scala ora ha quattro gradini, documentati nei token:
  400  corpo, metadati, valori descrittivi
  500  etichette di campo, voci di lista, testo dei controlli
  600  stati brevi (badge, voce attiva), nomi in una riga di elenco
  700  titoli, uno solo per blocco

Sopra il 700 non si sale: l'800 dei titoli di card e dell'avatar e' sceso a 700.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 12:46:28 +02:00

1126 lines
31 KiB
CSS

/* =============================================================================
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;
}
/* Il titolo puo' andare su due righe prima di troncare: il nome del punto
vendita e' l'ancora del compito, tagliarlo a meta' non aiuta nessuno. */
.appbar__title {
font-size: var(--headline-sm-size);
line-height: var(--headline-sm-height);
font-weight: 700;
letter-spacing: -0.02em;
color: var(--ink);
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.appbar__subtitle {
font-size: var(--label-md-size);
line-height: 1.3;
font-weight: 500;
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: 600;
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: 700;
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: 400;
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: 400;
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: 400;
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: 500;
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: 400;
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: 500;
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: 500;
}
.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);
padding: var(--space-xs) var(--space-md) var(--space-sm);
padding-bottom: calc(var(--space-sm) + env(safe-area-inset-bottom, 0px));
}
}
/* -----------------------------------------------------------------------------
§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: 400;
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: 400;
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: 700;
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: 500;
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: 500;
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: 600;
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: 600;
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: 400;
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: 500;
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); }