/* Sìntonia Console — il pannello.
 *
 * Continua `mockup/console.html` (approvato): stessa barra, stesse schede, stesse
 * schede-riquadro. Qui c'è solo ciò che il prodotto usa davvero — il selettore
 * «vedi come» del mockup era un attrezzo del disegno e non esiste, perché quello
 * che si vede dipende da chi è entrato (§26.1).
 *
 * Le misure del testo sono in rem, mai in px: la misura del mockup divisa
 * per 16 (13px = 0.8125rem), e `--scala-testo` in tema.css dice quanto vale
 * un rem. È la leva che ingrandisce il testo di tutte le schermate insieme.
 */

* {
  box-sizing: border-box;
}
body {
  margin: 0;
  /* Il cassetto e lo scrim stanno fuori da `.app` (§22.1-stile, per lo stesso
     motivo della barra laterale dell'istanza: `position:fixed` con un
     antenato senza `transform` restano ancorati alla finestra). `.app`
     porta colore e carattere ai suoi discendenti, ma il cassetto non è uno
     di questi — senza questa riga eredita il nero e il serif di serie del
     browser invece del tema. */
  color: var(--ink);
  font-family: var(--font-ui);
}

.app {
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: 0.875rem;
  line-height: 1.45;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  -webkit-font-smoothing: antialiased;
}

.topbar {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 10px 16px;
  border-bottom: 1px solid var(--line);
  background: var(--panel);
  position: sticky;
  top: 0;
  z-index: 30;
}
.crumb {
  font-size: 0.75rem;
  color: var(--ink-3);
}
.crumb b {
  color: var(--ink);
  font-weight: 500;
  font-size: 0.875rem;
  display: block;
  line-height: 1.2;
}
.spacer {
  margin-left: auto;
}

.btn {
  font: inherit;
  font-size: 0.78125rem;
  color: var(--ink-2);
  background: var(--panel-2);
  border: 1px solid var(--line);
  padding: 6px 12px;
  border-radius: var(--r-sm);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  white-space: nowrap;
}
.btn:hover {
  color: var(--ink);
  border-color: var(--accent-dim);
}
.btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
.btn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.btn.primary:hover {
  filter: brightness(1.08);
}
.btn.small {
  font-size: 0.71875rem;
  padding: 4px 9px;
}
/* Le azioni che non si disfano, come il mockup le disegna: tinta e bordo del
   pericolo, non un riempimento rosso. Un pulsante rosso pieno in mezzo a una
   tabella si preme per sbaglio proprio perché è quello che si vede. */
.btn.pericolo {
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 32%, var(--line));
}
.btn.pericolo:hover {
  border-color: color-mix(in srgb, var(--danger) 55%, var(--line));
  background: color-mix(in srgb, var(--danger) 8%, var(--panel-2));
}
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

/* I campi di un modulo, e le tendine per prime.
   La regola sta **sull'elemento** e non su una classe, come nei mockup
   (`console.html` e `console-impostazioni.html` la scrivono così): una tendina
   senza classe è la norma, non l'eccezione. Finché l'unica regola era
   `select.org`, tutte le altre — quelle dei cassetti, quelle delle
   impostazioni, e `select class="testo"` che chiedeva una regola scritta per
   gli `input` e quindi non prendeva niente — uscivano con l'aspetto nativo del
   browser: fondo grigio in mezzo a un pannello scuro e carattere più piccolo
   dei campi accanto. Il freno di `max-width`, poi, viveva nella stessa regola,
   e ogni modulo lo doveva disfare a mano con `style="max-width:none"`: un
   rimedio ripetuto sei volte dice che la regola stava dalla parte sbagliata.
   `input.testo` resta nell'elenco perché quasi nessuno di quei campi scrive
   `type="text"`, e senza attributo il selettore per tipo non li prende. */
input.testo,
input[type="text"],
input[type="password"],
input[type="email"],
input[type="number"],
input[type="search"],
input[type="url"],
input[type="date"],
input[type="datetime-local"],
select,
textarea {
  font: inherit;
  font-size: 0.8125rem;
  color: var(--ink);
  background-color: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 8px 10px;
  width: 100%;
}
/* La freccia la disegniamo noi: `appearance:none` perché del fondo e del bordo
   di una tendina nativa Safari su macOS non fa niente, e la console la si
   guarda anche da un Mac. Sono due gradienti e non un'icona perché un'icona si
   porterebbe dietro il proprio colore scritto a mano dentro il data URI, che è
   esattamente la cosa che §22.2 vieta: così invece la punta è `--ink-2` e
   segue il tema come tutto il resto. */
select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 28px;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--ink-2) 50%),
    linear-gradient(135deg, var(--ink-2) 50%, transparent 50%);
  background-position: right 16px center, right 11px center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
/* `.org` è la tendina che sta in una barra di controlli — il selettore del
   rivenditore in cima — e non in un modulo: si misura come i pulsanti che ha
   accanto, non come i campi di una scheda. */
select.org {
  font-size: 0.78125rem;
  padding: 6px 26px 6px 10px;
  max-width: 250px;
}
input:focus,
select:focus,
textarea:focus {
  border-color: var(--accent);
  outline: none;
}
input:disabled,
select:disabled,
textarea:disabled {
  opacity: 0.65;
  cursor: not-allowed;
}

/* Il tasto «mostra» dei campi mascherati — chiavi, gettoni, password. È lo stesso
   della pagina di accesso (`ingresso.css`, `console-accesso.html`), e sta dentro
   il campo invece che accanto perché una chiave OVH incollata riempie il campo
   fino al bordo: un tasto di fianco spingerebbe il campo fuori dalla colonna.
   Il guscio lo mette il pannello da sé su ogni `input[type=password]`, quindi il
   padding a destra vale per definizione ogni volta che la classe c'è. */
.conmostra {
  position: relative;
}
.conmostra input {
  padding-right: 74px;
}
.mostra {
  position: absolute;
  right: 5px;
  top: 50%;
  transform: translateY(-50%);
  font: inherit;
  font-size: 0.6875rem;
  color: var(--ink-3);
  background: none;
  border: 0;
  cursor: pointer;
  padding: 5px 7px;
  border-radius: var(--r-sm);
}
.mostra:hover {
  color: var(--ink-2);
  background: var(--panel-3);
}

.chip {
  font-size: 0.6875rem;
  padding: 3px 8px;
  border-radius: 999px;
  border: 1px solid var(--line);
  color: var(--ink-2);
  background: var(--panel-2);
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
#versione-console {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}
.chip .dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}
.chip.ok {
  color: var(--ok);
  border-color: color-mix(in srgb, var(--ok) 32%, var(--line));
}
.chip.warn {
  color: var(--warn);
  border-color: color-mix(in srgb, var(--warn) 32%, var(--line));
}
.chip.bad {
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 32%, var(--line));
}
.chip.ai {
  color: var(--ai);
  border-color: color-mix(in srgb, var(--ai) 32%, var(--line));
}

.tabs {
  display: flex;
  gap: 2px;
  padding: 0 16px;
  border-bottom: 1px solid var(--line);
  background: var(--panel);
  overflow-x: auto;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar {
  display: none;
}
.tab {
  font: inherit;
  font-size: 0.8125rem;
  color: var(--ink-2);
  background: none;
  border: 0;
  cursor: pointer;
  padding: 10px 13px;
  border-bottom: 2px solid transparent;
  white-space: nowrap;
}
.tab:hover {
  color: var(--ink);
}
.tab[aria-selected="true"] {
  color: var(--ink);
  border-bottom-color: var(--accent);
}

.panel {
  padding: 16px 20px 32px;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.panel[hidden] {
  display: none;
}
.cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 340px;
  gap: 16px;
  align-items: start;
}
@media (max-width: 1180px) {
  .cols {
    grid-template-columns: minmax(0, 1fr);
  }
}

.card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--glow);
  overflow: hidden;
}
.card-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 15px;
  border-bottom: 1px solid var(--line-soft);
  flex-wrap: wrap;
}
.card-title {
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.card-body {
  padding: 15px;
  display: flex;
  flex-direction: column;
  gap: 13px;
}
.help {
  font-size: 0.71875rem;
  color: var(--ink-3);
  line-height: 1.5;
}
.help b {
  color: var(--ink-2);
  font-weight: 500;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.field label {
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 11px;
}
@media (max-width: 560px) {
  .row {
    grid-template-columns: 1fr;
  }
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.78125rem;
}
th {
  text-align: left;
  font-size: 0.625rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-weight: 500;
  padding: 9px 12px;
  border-bottom: 1px solid var(--line-soft);
  white-space: nowrap;
}
td {
  padding: 9px 12px;
  border-bottom: 1px solid var(--line-soft);
  white-space: nowrap;
}
td.num {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
}
.table-scroll {
  overflow-x: auto;
}
/* La nota sotto l'esito di una mail: `td` è nowrap, e un errore SMTP lungo
   uscirebbe di lato. Qui si va a capo; il resto della riga resta su una linea. */
.esito-mail {
  color: var(--danger);
  font-size: 0.71875rem;
  margin-top: 2px;
  white-space: normal;
  max-width: 28ch;
}
#imp-invii td.help {
  white-space: normal;
}

.tiles {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 11px;
}
@media (max-width: 820px) {
  .tiles {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
.tile {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 11px 13px;
}
.tile-label {
  font-size: 0.625rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.tile-val {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-size: 1.5625rem;
  font-weight: 500;
  line-height: 1.15;
  letter-spacing: -0.02em;
  margin-top: 2px;
}
.tile-foot {
  font-size: 0.65625rem;
  color: var(--ink-3);
  margin-top: 2px;
}

.today {
  display: flex;
  flex-direction: column;
}
.today-row {
  display: grid;
  grid-template-columns: 4px 1fr auto;
  gap: 12px;
  align-items: center;
  padding: 11px 15px;
  border-bottom: 1px solid var(--line-soft);
}
.today-row .bar {
  width: 3px;
  height: 30px;
  border-radius: 2px;
  background: var(--rowcol);
}
.today-what {
  font-size: 0.8125rem;
  font-weight: 500;
}
.today-who {
  font-size: 0.71875rem;
  color: var(--ink-3);
}
.mono {
  font-family: var(--font-num);
}
code {
  font-family: var(--font-num);
  font-size: 0.75rem;
  background: var(--panel-3);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 1px 5px;
}

/* Gli stati scomodi, che ogni schermata deve avere (CLAUDE.md): vuoto, errore,
   e «non ancora». Il terzo è quello che qui serve di più: metà delle schede
   arrivano con un lotto che non è ancora stato fatto, e dirlo è meglio che
   mostrare una tabella finta. */
.vuoto,
.non-ancora {
  padding: 26px 18px;
  text-align: center;
  color: var(--ink-3);
  font-size: 0.78125rem;
  line-height: 1.6;
}
.vuoto b,
.non-ancora b {
  color: var(--ink-2);
  font-weight: 500;
  display: block;
  margin-bottom: 4px;
  font-size: 0.8125rem;
}
.non-ancora {
  border: 1px dashed var(--line);
  border-radius: var(--r-md);
  margin: 15px;
}

.avviso {
  display: flex;
  gap: 9px;
  align-items: flex-start;
  padding: 10px 12px;
  border-radius: var(--r-md);
  font-size: 0.78125rem;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--ink-2);
}
.avviso[data-tipo="guasto"] {
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 34%, var(--line));
  background: color-mix(in srgb, var(--danger) 8%, var(--panel-2));
}
.avviso[data-tipo="attesa"] {
  color: var(--warn);
  border-color: color-mix(in srgb, var(--warn) 34%, var(--line));
  background: color-mix(in srgb, var(--warn) 8%, var(--panel-2));
}

/* Il comando da incollare, e le fasi dell'installazione (§26.6). */
.comando {
  background: var(--panel-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 13px 14px;
  font-family: var(--font-num);
  font-size: 0.78125rem;
  line-height: 1.6;
  color: var(--ink);
  overflow-x: auto;
  white-space: pre-wrap;
  word-break: break-all;
}
.requisiti {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.requisito {
  display: grid;
  grid-template-columns: 16px 1fr;
  gap: 9px;
  align-items: baseline;
}
.requisito .segno {
  color: var(--ok);
}
.requisito b {
  font-size: 0.78125rem;
  font-weight: 500;
  color: var(--ink);
}
/* Fasi verificabili, non una barra che scorre: ognuna dice come è andata. */
.fasi {
  display: flex;
  flex-direction: column;
}
.fase {
  display: grid;
  grid-template-columns: 18px 1fr auto;
  gap: 11px;
  align-items: center;
  padding: 8px 0;
  font-size: 0.8125rem;
  border-bottom: 1px solid var(--line-soft);
}
.fase .pallino {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: var(--panel-2);
}
.fase[data-esito="fatta"] .pallino {
  background: var(--ok);
  border-color: var(--ok);
}
.fase[data-esito="in_corso"] .pallino {
  background: var(--accent);
  border-color: var(--accent);
}
.fase[data-esito="fallita"] .pallino {
  background: var(--danger);
  border-color: var(--danger);
}
.fase[data-esito="saltata"] .pallino {
  background: var(--warn);
  border-color: var(--warn);
}
/* Una fase che su questa strada non c'era: pallino spento e riga in grigio, come una
   voce d'elenco che non riguarda questa installazione. L'ambra è per le fasi saltate
   che **chiedono qualcosa** — il DNS da puntare a mano, Plesk da configurare —, e usare
   la stessa tinta per «non serve» vorrebbe dire chiamare in causa chi guarda per niente. */
.fase[data-non-serve] .pallino {
  background: var(--panel-3);
  border-color: var(--line);
}
.fase[data-non-serve] {
  opacity: 0.72;
}
/* Anche una fase saltata **da prima** — quando la ragione la scrivevano in `errore` —
   non si legge in rosso: il rosso è la riga per cui si rimedia a un guasto, e una fase
   saltata non è un guasto. */
.fase[data-esito="saltata"] .errore {
  color: var(--ink-3);
}
.fase[data-esito="attesa"] {
  opacity: 0.5;
}
.fase .errore {
  grid-column: 2 / 4;
  font-size: 0.71875rem;
  color: var(--danger);
  line-height: 1.5;
  padding-bottom: 4px;
}
/* Cosa è successo quando la fase **è andata**: il mockup approvato le mostra sotto
   la riga, in grigio. Stessa posizione dell'errore e colore diverso — l'occhio deve
   distinguere «ecco cosa ho fatto» da «ecco cosa non ha funzionato» senza leggere. */
.fase .dettaglio {
  grid-column: 2 / 4;
  font-size: 0.71875rem;
  line-height: 1.5;
  padding-bottom: 4px;
}
.impronta {
  font-family: var(--font-num);
  font-size: 0.875rem;
  letter-spacing: 0.1em;
  color: var(--ink);
  background: var(--panel-3);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 8px 10px;
  text-align: center;
}

/* Il cassetto: creare un rivenditore, un cliente, un accesso. Come nel mockup,
   e come nel pannello dell'istanza — Esc lo chiude. */
.scrim {
  position: fixed;
  inset: 0;
  background: rgba(4, 7, 13, 0.55);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.24s;
  z-index: 55;
}
.scrim[data-open="true"] {
  opacity: 1;
  pointer-events: auto;
}
.drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: 460px;
  max-width: 96vw;
  background: var(--panel);
  border-left: 1px solid var(--line);
  box-shadow: -30px 0 60px -30px rgba(0, 0, 0, 0.7);
  transform: translateX(100%);
  transition: transform 0.26s cubic-bezier(0.4, 0, 0.2, 1);
  z-index: 60;
  display: flex;
  flex-direction: column;
}
.drawer[data-open="true"] {
  transform: none;
}
.drawer-head {
  padding: 14px 16px;
  border-bottom: 1px solid var(--line-soft);
  display: flex;
  gap: 10px;
  align-items: flex-start;
}
.drawer-body {
  padding: 16px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 15px;
}
.drawer-title {
  font-size: 0.875rem;
  font-weight: 500;
}

/* **Un figlio del cassetto non si stringe.** `.drawer-body` è una colonna flex, e in
   flex-column `flex-shrink` vale 1: quando il modulo cresce, la carta dei requisiti
   viene schiacciata e le ultime righe sparivano *dentro* la carta — con la carta che
   sembrava finita e non lo era. Visto nel mockup guardando uno screenshot. */
.drawer-body > * {
  flex: none;
}

/* ── il modulo di creazione: le due strade (§26.4) ───────────────────────────
   Gli spazi sono del contenitore e non di ogni campo: prima ogni riga portava il
   suo `margin-top` scritto a mano, e aggiungerne una voleva dire ricordarselo. */
.modulo {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.etich {
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
}
/* Carte e non un menu a tendina: **la scelta cambia tutto quello che viene dopo** —
   chi va in cloud non vede né requisiti né comando — e una scelta con quelle
   conseguenze si legge, non si apre. */
.scelte {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.scelta {
  display: grid;
  grid-template-columns: 15px 1fr;
  gap: 11px;
  align-items: start;
  padding: 12px 13px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--panel-2);
  cursor: pointer;
}
.scelta:hover {
  border-color: var(--accent-dim);
}
.scelta:has(input:checked) {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 9%, var(--panel-2));
}
/* Una strada che non si può prendere resta a schermo e dice perché: toglierla
   lascerebbe chi la cercava a chiedersi se ha sbagliato pagina. */
.scelta:has(input:disabled) {
  opacity: 0.55;
  cursor: not-allowed;
}
.scelta input {
  width: auto;
  margin: 3px 0 0;
  accent-color: var(--accent);
}
.scelta .t {
  display: flex;
  gap: 8px;
  align-items: baseline;
  flex-wrap: wrap;
  font-size: 0.8125rem;
  color: var(--ink);
}
.scelta .t b {
  font-weight: 500;
}
.scelta .help {
  margin-top: 4px;
}
.scelta .prezzo {
  font-family: var(--font-num);
  color: var(--ink-2);
}
/* «Crea istanza» comincia dal prodotto: quattro schede, nessuna già scelta. Il
   tondo della radio non si vede — è la scheda intera a dire che è scelta — ma
   resta nel documento, così frecce, Tab e lettori di schermo funzionano come
   con una radio qualunque. */
.prodotti {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
  margin-top: 8px;
}
@media (max-width: 460px) {
  .prodotti {
    grid-template-columns: 1fr;
  }
}
.prodotto {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
  padding: 16px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--panel-2);
  cursor: pointer;
  transition: border-color 0.15s, background-color 0.15s, transform 0.15s;
}
.prodotto input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
.prodotto:hover {
  border-color: var(--accent-dim);
  transform: translateY(-1px);
}
.prodotto:has(input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.prodotto:has(input:checked) {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 9%, var(--panel-2));
}
.prodotto:has(input:disabled) {
  opacity: 0.55;
  cursor: not-allowed;
  transform: none;
  border-color: var(--line);
}
.prodotto .segno,
.prodotto-scelto .segno {
  display: grid;
  place-items: center;
  flex: none;
  width: 40px;
  height: 40px;
  margin-bottom: 4px;
  border-radius: 10px;
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, var(--panel-3));
}
.prodotto .segno svg,
.prodotto-scelto .segno svg {
  width: 22px;
  height: 22px;
}
.prodotto .nome,
.prodotto-scelto .nome {
  display: flex;
  gap: 8px;
  align-items: center;
  font-size: 1rem;
  font-weight: 500;
  color: var(--ink);
}
.prodotto .cosa,
.prodotto-scelto .cosa {
  font-size: 0.8125rem;
  color: var(--ink-2);
}
.prodotto ul {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 6px 0 0;
  padding: 10px 0 0;
  list-style: none;
  border-top: 1px solid var(--line-soft);
  font-size: 0.75rem;
  color: var(--ink-3);
}
.prodotto li::before {
  content: "·";
  margin-right: 6px;
  color: var(--accent);
}
/* Su schermo stretto una scheda per riga, e l'icona a sinistra: quattro schede alte
   come quelle larghe riempirebbero due schermate prima del modulo. */
@media (max-width: 460px) {
  .prodotto {
    display: grid;
    grid-template-columns: 40px minmax(0, 1fr);
    column-gap: 12px;
    row-gap: 2px;
    padding: 12px 14px;
  }
  .prodotto .segno {
    grid-row: 1 / span 3;
    margin: 0;
  }
  .prodotto ul {
    grid-column: 2;
    margin-top: 4px;
    padding-top: 6px;
  }
}
@media (prefers-reduced-motion: reduce) {
  .prodotto,
  .prodotto:hover {
    transition: none;
    transform: none;
  }
}
/* Scelto il prodotto, le schede diventano una riga: si vede cosa si sta creando, e
   «Cambia» le riapre senza perdere quello che si è scritto. */
.prodotto-scelto {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border: 1px solid var(--accent);
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--accent) 9%, var(--panel-2));
}
.prodotto-scelto[hidden] {
  display: none;
}
.prodotto-scelto .segno {
  width: 34px;
  height: 34px;
  margin: 0;
  border-radius: 9px;
}
.prodotto-scelto > span:nth-child(2) {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.prodotto-scelto .btn {
  margin-left: auto;
}
/* Il nome e il suo suffisso attaccati: si scrive l'etichetta, si legge il nome intero. */
.gruppo {
  display: flex;
  align-items: stretch;
  min-width: 0;
}
.gruppo input {
  border-radius: var(--r-sm) 0 0 var(--r-sm);
  min-width: 0;
}
.gruppo .suff {
  font-family: var(--font-num);
  font-size: 0.78125rem;
  color: var(--ink-3);
  background: var(--panel-3);
  border: 1px solid var(--line);
  border-left: 0;
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  padding: 8px 10px;
  display: flex;
  align-items: center;
  white-space: nowrap;
}
/* Ciò che appare solo su una delle due strade, rientrato: si vede che dipende dalla
   scelta di sopra, e sparendo non lascia un buco in mezzo al modulo. */
.sotto {
  border-left: 2px solid var(--line);
  padding-left: 13px;
  display: flex;
  flex-direction: column;
  gap: 11px;
}
.sotto[hidden] {
  display: none;
}
.spunta {
  display: flex;
  gap: 9px;
  align-items: flex-start;
  font-size: 0.78125rem;
  color: var(--ink-2);
  cursor: pointer;
}
.spunta input {
  width: auto;
  margin: 2px 0 0;
  accent-color: var(--accent);
  flex: none;
}

/* ── il secondo livello di Impostazioni (§22.2) ──────────────────────────────
   Pastiglie e non una seconda fila di linguette: due file identiche una sotto
   l'altra sono due file di cui non si sa quale porta dove. */
.sezioni {
  display: flex;
  gap: 7px;
  flex-wrap: wrap;
}
.sez {
  font: inherit;
  font-size: 0.78125rem;
  color: var(--ink-2);
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 7px 14px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  white-space: nowrap;
}
.sez:hover {
  color: var(--ink);
  border-color: var(--accent-dim);
}
/* Tinta **e** peso: in tema chiaro `--panel-2` è quasi bianco e una tinta sola si
   vede appena, e «si vede appena» su una navigazione fa cliccare due volte per
   capire dove si è. */
.sez[aria-pressed="true"] {
  color: var(--ink);
  font-weight: 500;
  background: color-mix(in srgb, var(--accent) 20%, var(--panel-2));
  border-color: var(--accent);
}
/* Lo stato della sezione **sta nella navigazione**: aprendo la schermata si vede dove
   andare, non cosa leggere. Con un guaio non basta il colore — chi non distingue rosso
   e verde vedrebbe quattro pastiglie uguali — quindi il pallino guasto ha un anello. */
.sezioni .pallino {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex: none;
  background: var(--ink-3);
}
.sezioni .pallino.ok {
  background: var(--ok);
}
.sezioni .pallino.warn {
  background: var(--warn);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--warn) 22%, transparent);
}
.sezioni .pallino.bad {
  background: var(--danger);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 26%, transparent);
}
.sezione {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.sezione[hidden] {
  display: none;
}
/* **Perché queste due carte stanno insieme**, in una riga sola sopra di loro. Una
   sezione che raccoglie due schede senza dirlo è una cartella. */
.perche {
  margin: 0;
  font-size: 0.78125rem;
  color: var(--ink-3);
  line-height: 1.55;
  max-width: 78ch;
}
.perche b {
  color: var(--ink-2);
  font-weight: 500;
}
/* Il confine della schermata: si legge una volta, non ogni volta. */
.fuori {
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--panel-2);
}
.fuori > summary {
  cursor: pointer;
  padding: 11px 14px;
  font-size: 0.78125rem;
  color: var(--ink-2);
  list-style: none;
}
.fuori > summary::-webkit-details-marker {
  display: none;
}
.fuori > summary::before {
  content: "\203A";
  display: inline-block;
  margin-right: 9px;
  color: var(--ink-3);
  transition: transform 0.18s;
}
.fuori[open] > summary::before {
  transform: rotate(90deg);
}
.fuori > summary:hover {
  color: var(--ink);
}
.fuori-corpo {
  padding: 0 14px 14px 30px;
  display: flex;
  flex-direction: column;
  gap: 9px;
}
@media (min-width: 1101px) {
  /* La spalla resta in vista mentre si scorrono le due carte: con una carta sola in
     una colonna da 320 px, senza questo metà schermata è vuota dove si guarda. */
  .cols > .stack:last-child {
    position: sticky;
    top: 0;
  }
}

/* La consegna di una password: si vede una volta sola, e la schermata lo dice. */
.consegna {
  border: 1px solid color-mix(in srgb, var(--ok) 34%, var(--line));
  background: color-mix(in srgb, var(--ok) 7%, var(--panel-2));
  border-radius: var(--r-md);
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.consegna .valore {
  font-family: var(--font-num);
  font-size: 0.9375rem;
  color: var(--ink);
  background: var(--panel-3);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 9px 10px;
  word-break: break-all;
}

/* Il segreto del secondo fattore: a gruppi di quattro, perché si copia a mano. */
.segreto {
  font-family: var(--font-num);
  font-size: 0.9375rem;
  letter-spacing: 0.12em;
  color: var(--ink);
  background: var(--panel-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 11px 12px;
  word-break: break-all;
  line-height: 1.7;
  text-align: center;
}
.codici {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 7px;
  font-family: var(--font-num);
  font-size: 0.8125rem;
}
.codici span {
  background: var(--panel-3);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 7px 9px;
  text-align: center;
  color: var(--ink);
}

/* La scheda «Questa console»: versione, cosa arriva, com'è andata l'ultima volta.
   `.fp` viene dal mockup ed è il riquadro scuro che porta un dato tecnico; qui
   serve a distinguere ciò che la macchina dice da ciò che la schermata spiega. */
.fp {
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 12px 13px;
  font-size: 0.78125rem;
  line-height: 1.7;
}
.fp .k {
  font-family: var(--font-num);
  font-size: 0.8125rem;
  color: var(--ink);
  letter-spacing: 0.04em;
}
/* Cosa arriva nel clone, una riga per prodotto: si accende solo quella che chiede
   un'azione, cioè la console. */
.arrivi {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: 10px;
}
.arrivo {
  display: grid;
  grid-template-columns: 86px 30px minmax(0, 1fr);
  gap: 8px;
  align-items: baseline;
  padding: 5px 8px;
  border-radius: var(--r-sm);
  color: var(--ink-3);
}
.arrivo[data-acceso="si"] {
  background: color-mix(in srgb, var(--warn) 9%, transparent);
  color: var(--ink-2);
}
.arrivo-chi {
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.arrivo-quanti {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  text-align: right;
}
.arrivo-cosa {
  font-size: 0.75rem;
  line-height: 1.5;
}
.arrivo-titolo {
  font-family: var(--font-ui);
  font-size: 0.625rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-top: 6px;
}
@media (max-width: 520px) {
  .arrivo {
    grid-template-columns: minmax(0, 1fr) auto;
  }
  .arrivo-cosa {
    grid-column: 1 / -1;
  }
}
.commit-in-arrivo {
  display: flex;
  flex-direction: column;
  gap: 5px;
  font-size: 0.75rem;
  color: var(--ink-2);
  max-height: 190px;
  overflow-y: auto;
}
.changelog-voci {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.changelog-voci details {
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 8px 10px;
  background: var(--panel-2);
}
.changelog-voci summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  font-size: 0.8125rem;
}
.changelog-voci summary::-webkit-details-marker { display: none; }
.changelog-voci summary::before {
  content: "▸";
  color: var(--ink-3);
  font-size: 0.6875rem;
}
.changelog-voci details[open] summary::before { content: "▾"; }
.azioni-console {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
/* Il racconto di un aggiornamento fallito esce **in chiaro** (§26.10): «errore 3»
   non lo legge nessuno, e questo testo è ciò che si incolla a chi può rimediare. */
.racconto {
  font-family: var(--font-num);
  font-size: 0.71875rem;
  line-height: 1.6;
  white-space: pre-wrap;
  word-break: break-word;
  background: var(--panel-3);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 9px 10px;
  margin: 8px 0;
  max-height: 220px;
  overflow-y: auto;
}
.help[data-esito="guasto"] b {
  color: var(--danger);
}
.help[data-esito="ok"] b {
  color: var(--ok);
}

@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important;
  }
}

/* Impostazioni (§26.15) */
.stack { display: flex; flex-direction: column; gap: 16px; }
.row3 {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr;
  gap: 12px;
}
@media (max-width: 900px) {
  .row3 { grid-template-columns: 1fr; }
}
.banner {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--panel-2);
  font-size: 0.78125rem;
  line-height: 1.45;
}
.banner[data-tipo="ok"] {
  color: var(--ok);
  border-color: color-mix(in srgb, var(--ok) 34%, var(--line));
}
.banner[data-tipo="warn"] {
  color: var(--warn);
  border-color: color-mix(in srgb, var(--warn) 34%, var(--line));
}
.banner[data-tipo="bad"] {
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 34%, var(--line));
}
.azioni {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
}
.anteprima {
  font-size: 0.8125rem;
  color: var(--ink);
  padding: 8px 10px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  min-height: 36px;
}
.anteprima.mono, .mono { font-family: var(--font-num); }
.segreto-riga {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 6px 10px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  min-height: 36px;
}
/* Un segreto mostrato è lungo — una consumer key di OVH è trentadue caratteri — e
   dentro una riga di stato o un aiuto va **spezzato**, non lasciato spingere i tasti
   fuori dalla scheda. Vale per il valore, non per «••••ab12», che è corto per sempre. */
.segreto-riga .mono,
.help .mono {
  word-break: break-all;
}
.btn.danger {
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 40%, var(--line));
}
.btn.danger:hover {
  color: var(--danger);
  border-color: var(--danger);
}
.chip.ai {
  color: var(--ai);
  border-color: color-mix(in srgb, var(--ai) 32%, var(--line));
}
.chip .dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}

/* ── gateway delle sveglie (lotto 31) ─────────────────────────────────────────
 *
 * **La pagina risponde con un disegno, non con un tema.** La prima versione
 * spiegava a parole quello che il disegno può mostrare: tre riquadri di verdetto
 * seguiti da mezza pagina di prosa, due tabelle di spiegazioni, e i numeri sepolti
 * in mezzo. Chi apre questa scheda ha una domanda sola e urgente — «perché l'app di
 * quel cliente non squilla» — e la risposta deve stare in un colpo d'occhio.
 *
 * Da cui la catena: gateway → chiave della piattaforma → telefoni. È una catena
 * perché è una catena: basta un anello rotto e non squilla niente, e **dove si
 * rompe si vede invece di leggerlo**. Le spiegazioni non sono sparite — sono
 * dentro i «Perché?», che si aprono quando servono e non occupano la schermata
 * quando non servono.
 *
 * Nessun colore scritto a mano: i tre stati prendono i ruoli del tema (§22.2),
 * che è anche il motivo per cui `ci/tema.py` esiste.
 */

/* La riga sola che dice com'è finita. Se qualcuno legge una cosa sola, è questa. */
.gw-testata {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}
.gw-sentenza {
  font-size: 1.0625rem;
  font-weight: 600;
  color: var(--ink);
  line-height: 1.3;
}
.gw-sentenza[data-stato="no"] { color: var(--danger); }
.gw-sentenza[data-stato="warn"] { color: var(--warn); }
.gw-sentenza[data-stato="ok"] { color: var(--ok); }

/* La catena. Non è un riquadro: i riquadri impilati sono due per vista (§22.2), e
   qui servono per l'elenco dei centralini e per i conteggi. */
.gw-catena {
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--panel);
  padding: 15px;
}
.gw-schema {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 44px minmax(0, 1.35fr);
  align-items: center;
  gap: 0;
}
.gw-corsie {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
/* La forcella che collega il gateway alle due piattaforme. Si tinge di rosso
   quando il gateway è giù, perché è lì che la catena si interrompe. */
.gw-forcella {
  width: 44px;
  height: 100%;
  min-height: 96px;
  fill: none;
  stroke: var(--line);
  stroke-width: 1.5;
  vector-effect: non-scaling-stroke;
}
.gw-catena[data-su="no"] .gw-forcella { stroke: color-mix(in srgb, var(--danger) 55%, var(--line)); }
@media (max-width: 720px) {
  .gw-schema { grid-template-columns: minmax(0, 1fr); gap: 10px; }
  .gw-forcella { display: none; }
}

.verdetto {
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 11px 13px;
  background: var(--panel-2);
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.verdetto[data-stato="ok"] { border-color: color-mix(in srgb, var(--ok) 30%, var(--line)); }
.verdetto[data-stato="no"] { border-color: color-mix(in srgb, var(--danger) 30%, var(--line)); }
.verdetto[data-stato="warn"] { border-color: color-mix(in srgb, var(--warn) 30%, var(--line)); }
.verdetto .vt {
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--ink-3);
}
.verdetto .vs {
  font-size: 1rem;
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: 8px;
}
.verdetto .vs::before {
  content: "";
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--ink-3);
  flex: none;
}
.verdetto[data-stato="ok"] .vs { color: var(--ok); }
.verdetto[data-stato="ok"] .vs::before { background: var(--ok); }
.verdetto[data-stato="no"] .vs { color: var(--danger); }
.verdetto[data-stato="no"] .vs::before { background: var(--danger); }
.verdetto[data-stato="warn"] .vs { color: var(--warn); }
.verdetto[data-stato="warn"] .vs::before { background: var(--warn); }
.verdetto .vd { margin: 0; }

/* «Perché?»: la spiegazione c'è, ma sta ferma finché non la si chiede. È il posto
   dove finisce la prosa che prima stava aperta in mezzo alla schermata. */
.spiega {
  font-size: 0.71875rem;
  color: var(--ink-3);
}
.spiega > summary {
  cursor: pointer;
  color: var(--ink-3);
  list-style: none;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 0;
  border-bottom: 1px dotted var(--line);
}
.spiega > summary::-webkit-details-marker { display: none; }
.spiega > summary::after { content: "›"; transition: transform .15s; display: inline-block; }
.spiega[open] > summary::after { transform: rotate(90deg); }
.spiega > summary:hover { color: var(--ink-2); }
.spiega .dentro {
  padding-top: 7px;
  line-height: 1.55;
  color: var(--ink-3);
  max-width: 62ch;
}
.spiega .dentro b { color: var(--ink-2); font-weight: 500; }
.spiega .dentro p { margin: 0 0 7px; }
.spiega .dentro p:last-child { margin-bottom: 0; }

/* I conteggi. Una barra invece di una tabella: le proporzioni si guardano, non
   si calcolano leggendo quattro numeri incolonnati. */
.gw-totale {
  display: flex;
  align-items: baseline;
  gap: 7px;
  color: var(--ink-3);
  font-size: 0.75rem;
}
.gw-totale b {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-size: 1.5rem;
  font-weight: 600;
  color: var(--ink);
}
.gw-barra {
  display: flex;
  height: 10px;
  border-radius: 999px;
  overflow: hidden;
  background: var(--panel-3);
  border: 1px solid var(--line);
}
.gw-barra .fetta { min-width: 2px; }
.gw-barra .fetta[data-esito="consegnata"] { background: var(--ok); }
.gw-barra .fetta[data-esito="rifiutata"] { background: var(--danger); }
.gw-barra .fetta[data-esito="non_partita"] { background: var(--warn); }
.gw-barra .fetta[data-esito="telefono_sparito"] { background: var(--ink-3); }
.gw-barra[data-vuota="si"] { background: var(--panel-3); }

.gw-legenda { display: flex; flex-direction: column; gap: 1px; }
.gw-voce {
  display: grid;
  grid-template-columns: 10px minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 9px;
  padding: 5px 0;
  border-bottom: 1px solid var(--line-soft);
  font-size: 0.78125rem;
}
.gw-voce:last-child { border-bottom: 0; }
.gw-voce .pallino {
  width: 8px;
  height: 8px;
  border-radius: 2px;
  background: var(--ink-3);
  transform: translateY(-1px);
}
.gw-voce[data-esito="consegnata"] .pallino { background: var(--ok); }
.gw-voce[data-esito="rifiutata"] .pallino { background: var(--danger); }
.gw-voce[data-esito="non_partita"] .pallino { background: var(--warn); }
.gw-voce .che { color: var(--ink-2); }
.gw-voce .che small { display: block; color: var(--ink-3); font-size: 0.6875rem; }
.gw-voce .quante {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.gw-voce[data-zero="si"] { opacity: .5; }

/* Le richieste fermate alla porta: tre contatori, grigi a zero e rossi appena
   contano qualcosa. Un numero che si accende si nota; una riga di tabella no. */
.gw-porta {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}
.gw-contatore {
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--panel-2);
  padding: 8px 9px;
  text-align: center;
}
.gw-contatore .n {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-size: 1.125rem;
  font-weight: 600;
  color: var(--ink-3);
}
.gw-contatore .l { font-size: 0.65625rem; color: var(--ink-3); line-height: 1.25; margin-top: 2px; }
.gw-contatore[data-acceso="si"] {
  border-color: color-mix(in srgb, var(--danger) 40%, var(--line));
  background: color-mix(in srgb, var(--danger) 8%, var(--panel-2));
}
.gw-contatore[data-acceso="si"] .n { color: var(--danger); }
.gw-contatore[data-acceso="si"] .l { color: var(--ink-2); }

/* L'elenco dei centralini: un pallino invece della colonna «Stato». */
.gw-nome { display: inline-flex; align-items: center; gap: 8px; }
.gw-nome .stato {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ok);
  flex: none;
}
.gw-nome[data-attiva="no"] .stato { background: var(--ink-3); }
.gw-esito {
  font-size: 0.6875rem;
  padding: 2px 7px;
  border-radius: 999px;
  border: 1px solid var(--line);
  color: var(--ink-2);
  background: var(--panel-2);
  white-space: nowrap;
}
.gw-esito[data-esito="consegnata"] {
  color: var(--ok);
  border-color: color-mix(in srgb, var(--ok) 32%, var(--line));
}
.gw-esito[data-esito="rifiutata"] {
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 32%, var(--line));
}
.gw-esito[data-esito="non_partita"] {
  color: var(--warn);
  border-color: color-mix(in srgb, var(--warn) 32%, var(--line));
}

/* Il piede: dove sta il gateway, e cosa non si vede da qui. Sta in fondo e non in
   una scheda perché è contesto, non è la risposta — ma **si vede anche a gateway
   giù**, che è proprio quando l'indirizzo serve. */
.gw-piede {
  display: flex;
  align-items: flex-start;
  gap: 20px;
  flex-wrap: wrap;
  padding: 11px 14px;
  border: 1px dashed var(--line);
  border-radius: var(--r-md);
  font-size: 0.75rem;
  color: var(--ink-3);
}
.gw-piede .etichetta {
  font-size: 0.625rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-right: 6px;
}
.gw-piede a { color: var(--accent); text-decoration: none; margin-left: 9px; }
.gw-piede a:hover { text-decoration: underline; }

.elenco-no { display: flex; flex-direction: column; gap: 8px; margin: 8px 0 0; }
.elenco-no > div {
  display: flex;
  gap: 9px;
  align-items: flex-start;
  font-size: 0.75rem;
  color: var(--ink-3);
}
.elenco-no > div > span { color: var(--danger); flex: none; font-weight: 600; }
.elenco-no b { color: var(--ink-2); font-weight: 500; }

/* ── il rilascio: tre tappe in fila ───────────────────────────────────────────
 *
 * La pagina Aggiornamenti raccontava in prosa una cosa che è **un percorso**: il
 * codice diventa una versione, la versione va in Beta, e da Beta passa a Stabile.
 * Tre riquadri in fila con una freccia in mezzo lo dicono senza scriverlo, e
 * soprattutto dicono **dove si è fermi**: la tappa che ha qualcosa da fare è
 * quella accesa.
 *
 * Ogni tappa porta il numero che conta — la versione — e, per i due canali, quante
 * macchine ci stanno sopra: è la domanda che prima non aveva risposta da nessuna
 * parte («quante istanze sono in beta?»), e sta nel riquadro del canale perché è
 * lì che si guarda prima di promuovere.
 */
.tappe {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 22px minmax(0, 1fr) 22px minmax(0, 1fr);
  align-items: stretch;
  gap: 0;
}
@media (max-width: 900px) {
  /* Sotto i 900 px le frecce diventano verticali: tre riquadri stretti in fila
     spezzano le versioni a metà, ed è proprio il numero che si va a leggere. */
  .tappe { grid-template-columns: minmax(0, 1fr); }
  .tappa-freccia { height: 22px; width: 100%; }
}
.tappa {
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 13px 14px;
  display: flex;
  flex-direction: column;
  gap: 9px;
  min-width: 0;
}
/* La tappa che aspetta qualcosa. Un bordo e un filo di fondo: non un colore
   pieno, che a tre riquadri affiancati diventa un semaforo. */
.tappa[data-attesa="si"] {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
  background: color-mix(in srgb, var(--accent) 7%, var(--panel-2));
}
.tappa[data-vuota="si"] .tappa-versione { color: var(--ink-3); }
.tappa-testa {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.tappa-nome {
  font-size: 0.625rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.tappa-versione {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-size: 1.625rem;
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: -0.02em;
  overflow-wrap: anywhere;
}
.tappa-sotto {
  font-size: 0.6875rem;
  color: var(--ink-3);
  line-height: 1.5;
}
.tappa-azioni {
  display: flex;
  gap: 7px;
  flex-wrap: wrap;
  margin-top: auto;
  padding-top: 2px;
}
.tappa-freccia {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ink-3);
  font-size: 1.25rem;
  line-height: 1;
  user-select: none;
}
.tappa-freccia[data-pronta="si"] { color: var(--accent); }

/* Quante macchine ci sono sopra. Due numeri e non uno: «dieci in beta» non dice
   niente se sono tutte ferme alla versione di prima. */
.tappa-conta {
  display: flex;
  align-items: baseline;
  gap: 8px;
}
.tappa-conta .n {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-size: 1.125rem;
  color: var(--ink);
  line-height: 1;
}
.tappa-conta .l { font-size: 0.6875rem; color: var(--ink-3); }
.tappa-barra {
  height: 4px;
  border-radius: 2px;
  background: var(--panel-3);
  overflow: hidden;
  display: flex;
}
.tappa-barra .fetta { background: var(--ok); }
.tappa-barra[data-vuota="si"] { opacity: .45; }

/* L'elenco «chi sta dove»: una riga per centralino, col canale che si sposta da
   qui. Non è la scheda Istanze in piccolo — qui si guarda una colonna sola. */
.canale-righe { display: flex; flex-direction: column; }
.canale-riga {
  display: flex;
  /* **Va a capo**, e serve stretto: nome, pastiglie, lo scambio del canale e
     «Aggiorna» in fila su 360 px uscivano dalla scheda, con il pulsante fuori dallo
     schermo e nessuna barra per raggiungerlo. */
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  padding: 8px 0;
  border-bottom: 1px solid var(--line-soft);
  font-size: 0.78125rem;
}
.canale-riga:last-child { border-bottom: 0; }
.canale-riga .chi { min-width: 0; flex: 1; }
.canale-riga .chi b { font-weight: 500; color: var(--ink); display: block; }
.canale-riga .chi small {
  color: var(--ink-3);
  font-size: 0.6875rem;
  font-family: var(--font-num);
}
.canale-riga .btn { flex: none; }
/* Stretto, il nome si prende la sua riga: i comandi vanno tutti sotto, in fila fra
   loro, invece di stringere il nome fino a spezzarlo in tre. */
@media (max-width: 560px) {
  .canale-riga .chi { flex-basis: 100%; }
}

/* Le due pastiglie del canale, una accanto all'altra: si vede su quale sta e si
   sposta con un clic, senza aprire niente. */
.scambio { display: inline-flex; border: 1px solid var(--line); border-radius: 999px; padding: 2px; gap: 2px; }
.scambio button {
  font: inherit;
  font-size: 0.6875rem;
  border: 0;
  background: transparent;
  color: var(--ink-3);
  padding: 3px 10px;
  border-radius: 999px;
  cursor: pointer;
}
.scambio button:hover:not([disabled]) { color: var(--ink); }
.scambio button[aria-pressed="true"] {
  background: var(--accent-dim);
  color: var(--ink);
}
.scambio button[disabled] { cursor: default; }

/* ── la Suite: Mail e Chat accanto ai centralini ──────────────────────────────
   Continua `mockup/console/console-suite.html`. Solo token del tema. */

.simboli {
  position: absolute;
}
/* Il prodotto: un'etichetta squadrata e non una pastiglia tonda, perché non è uno
   stato. Niente colore: i colori della console dicono «va bene / guarda / guasto». */
.prod {
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  padding: 2px 7px;
  border-radius: 4px;
  border: 1px solid var(--line);
  color: var(--ink);
  background: var(--panel-3);
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.prod svg {
  width: 12px;
  height: 12px;
  flex: none;
  color: var(--ink-2);
}
.prod.tenue {
  color: var(--ink-3);
}
.chip.spento {
  color: var(--ink-3);
}
.sez .n {
  font-family: var(--font-num);
  color: var(--ink-3);
}
.sez:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
td .sotto-riga {
  display: block;
  font-size: 0.6875rem;
  color: var(--ink-3);
  white-space: normal;
}
td.tenue,
.tenue {
  color: var(--ink-3);
}
td.lontano {
  color: var(--warn);
}
td.muto {
  color: var(--danger);
}
tbody tr[data-riga] {
  cursor: pointer;
}
.tile-val.bad {
  color: var(--danger);
}
.tile-val.warn {
  color: var(--warn);
}
.tile-val.parola {
  font-size: 1.25rem;
}
.tile-val.ok {
  color: var(--ok);
}

/* barre di occupazione dei server */
.uso {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 120px;
}
.uso-num {
  font-family: var(--font-num);
  font-size: 0.71875rem;
  color: var(--ink-2);
}
.uso-num.warn {
  color: var(--warn);
}
.barra {
  height: 5px;
  border-radius: 3px;
  background: var(--panel-3);
  overflow: hidden;
}
.barra > span {
  display: block;
  height: 100%;
  background: var(--ok);
  border-radius: 3px;
}
.barra.warn > span {
  background: var(--warn);
}
.barra.ignoto > span {
  background: var(--neutro);
  opacity: 0.5;
}

/* righe chiave/valore */
.kvs {
  gap: 0;
}
.kv {
  display: flex;
  gap: 10px;
  font-size: 0.78125rem;
  padding: 6px 0;
  border-bottom: 1px solid var(--line-soft);
  flex-wrap: wrap;
}
.kv:last-child {
  border-bottom: 0;
}
.kv .k {
  color: var(--ink-3);
}
.kv .v {
  margin-left: auto;
  font-family: var(--font-num);
  text-align: right;
  overflow-wrap: anywhere;
  min-width: 0;
}
.kv .v.ok {
  color: var(--ok);
}
.kv .v.warn {
  color: var(--warn);
}
.kv .v.bad {
  color: var(--danger);
}

/* il dettaglio di un'istanza */
.dettaglio-testa {
  display: flex;
  gap: 12px;
  align-items: center;
  flex-wrap: wrap;
}
.dettaglio-testa h1 {
  margin: 0;
  font-size: 1.125rem;
  font-weight: 500;
  overflow-wrap: anywhere;
  min-width: 0;
}
.dettaglio-testa .sotto-titolo {
  font-size: 0.75rem;
  color: var(--ink-3);
  flex-basis: 100%;
}
.vista {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
}
.vista[hidden] {
  display: none;
}
.strumenti {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
}
.strumenti input[type="search"] {
  max-width: 260px;
}
.strumenti select {
  max-width: 200px;
  width: auto;
}
.esito-campo {
  font-size: 0.71875rem;
}
.esito-campo.ok {
  color: var(--ok);
}
.esito-campo.bad {
  color: var(--danger);
}
.banner b {
  font-weight: 500;
}
.banner .testo {
  color: var(--ink-2);
}
.banner[hidden] {
  display: none;
}
.scelte.griglia {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (max-width: 460px) {
  .scelte.griglia {
    grid-template-columns: minmax(0, 1fr);
  }
}
#scelta-prodotto .etich,
#solo-suite .etich {
  display: block;
  margin-bottom: 7px;
}
#scelta-prodotto[hidden],
#solo-suite[hidden],
#solo-voce[hidden] {
  display: none;
}
#solo-suite [hidden],
[data-panel="istanza"] [hidden],
[data-panel="server"] [hidden],
.sezione[data-sez="suite"] [hidden],
.sezione[data-sez="accesso"] [hidden] {
  display: none;
}

/* S13b: l'esito di un'azione che aspetta il server (Backup adesso, la prova della
   posta) e l'esportazione dei dati. Una riga: il chip dice lo stato, il testo il
   perché; l'impronta va a capo dove serve, non allarga la pagina a 390 px. */
.riga-esito {
  display: flex;
  gap: 8px;
  align-items: baseline;
  flex-wrap: wrap;
  font-size: 0.75rem;
  color: var(--ink-2);
  line-height: 1.5;
}
.riga-esito .testo {
  flex: 1 1 12rem;
  min-width: 0;
  overflow-wrap: anywhere;
}
.esportazione {
  display: flex;
  flex-direction: column;
  gap: 8px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--panel-2);
  padding: 10px 12px;
}
.esportazione .etich {
  font-size: 0.6875rem;
  color: var(--ink-3);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.esportazione .sha {
  font-family: var(--font-num);
  font-size: 0.6875rem;
  color: var(--ink-3);
  overflow-wrap: anywhere;
}
a.btn {
  text-decoration: none;
}
.esito[data-tipo="ok"] {
  color: var(--ok);
}
.esito[data-tipo="bad"] {
  color: var(--danger);
}
.campo-prova {
  max-width: 15rem;
}
