/* Le regole del disegno. I valori che usano stanno tutti in tokens.css, e qui non ne compare
   nessuno che non sia strutturale.

   Il criterio del foglio: **l'HTML resta leggibile a mano**. Una classe dice a che cosa serve un
   elemento — `.card`, `.btn`, `.pill--error` — non come è fatto. Il mock-up fa il contrario, perché
   doveva solo mostrare l'idea e veniva riemesso intero a ogni giro; qui l'HTML lo scrive e lo
   corregge una persona, e uno stile in linea è una decisione presa in un posto dove nessuno la
   cerca. Dove le due cose non coincidono vince la semplicità, non la somiglianza. */


/* ─── Il carattere ────────────────────────────────────────────────────────────────────────────
   Ospitato qui e non preso da un CDN: SeamBox gira anche su computer senza Internet, e un banco
   che dipende dalla rete per disegnarsi è un banco che un giorno si presenta bianco.

   Un solo file per sottoinsieme, variabile da 400 a 800: quaranta chilobyte in tutto contro i
   cinque file che servirebbero a pesi fissi. `latin` e `latin-ext` coprono le sei lingue in cui i
   nomi sono tradotti — italiano, inglese, tedesco, francese, spagnolo, portoghese. */

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

@font-face {
    font-family: "Manrope";
    font-style: normal;
    font-weight: 400 800;
    font-display: swap;
    src: url("../fonts/manrope-latin-ext.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
                   U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
                   U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}


/* ─── Fondamenta ──────────────────────────────────────────────────────────────────────────────
   Le cifre a larghezza fissa valgono su tutta l'applicazione, non solo sulle tabelle: è uno
   strumento di misura, e una colonna di numeri che balla a ogni aggiornamento si legge male anche
   quando è in mezzo a una frase. */

*, *::before, *::after { box-sizing: border-box; }

/* **L'applicazione è una cornice, non un documento**: occupa la finestra e non la fa scorrere mai.
   A scorrere è la sola area di lavoro (`.content`), ed è la condizione perché barra laterale, barra
   superiore, albero e comandi della scheda restino dove sono — `position: sticky` si aggancia
   all'antenato che scorre, e finché a scorrere era la finestra non aveva presa su niente. */
html { height: 100%; overflow: hidden; }

body {
    height: 100%;
    overflow: hidden;
    margin: 0;
    background: var(--page-bg);
    color: var(--text-primary);
    font-family: var(--font-ui);
    font-size: var(--text-body);
    font-variant-numeric: tabular-nums;
    line-height: 1.45;
}

h1, h2, h3 { margin: 0; font-weight: 800; line-height: 1.2; }
h1 { font-size: 21px; }
h2 { font-size: 17px; }
h3 { font-size: var(--text-title, 15px); }

p { margin: 0 0 0.8em; }
p:last-child { margin-bottom: 0; }

a { color: inherit; }

/* Le caselle di spunta e i selettori prendono l'inchiostro del testo, non il blu di sistema. Il blu
   in questa applicazione ha un significato solo — l'anello del comando di cattura, che richiama il
   pulsante fisico del banco — e una casella spuntata non è quello. Il verde nemmeno: è dell'azione
   principale, una per schermata. */
input[type="checkbox"],
input[type="radio"] {
    accent-color: var(--text-primary);
}

/* Il contorno di messa a fuoco non si toglie mai: al banco si lavora anche da tastiera, e un
   pannello che non dice dove sei è un pannello in cui ci si perde. */
:focus-visible {
    outline: 2px solid var(--action);
    outline-offset: 2px;
}

/* **Sulla testata delle procedure l'anello è bianco**: è verde scuro come l'azione, e un anello verde
   sulla ✕ o sulle briciole dava un contrasto di 1,3 — chi lavora da tastiera non vedeva dove fosse. È il
   caso «verde su verde» che il redesign rendeva possibile (07 §91.8). */
.procedure__head :focus-visible { outline-color: var(--brand-ink); }

/* Le icone prendono la misura dal registro e il colore dal contesto: nel markup un `<svg>` non
   porta né `width` né `stroke`, e la stessa icona serve al banco e alla scrivania. L'elenco è
   esplicito perché le figure vere — la sezione con le quote — hanno misure loro e non vanno toccate. */
.sidebar__item > svg,
.usermenu__item > svg,
.theme-switch__option > svg,
.btn > svg,
.tree-tools__item > svg,
.pill > svg {
    width: var(--icon-size);
    height: var(--icon-size);
    flex: none;
}

/* `hidden` è un attributo nativo e vince su qualunque classe.

   La regola dell'user-agent — `[hidden] { display: none }` — ha specificità zero, quindi **qualunque
   nostra classe che dichiari un `display` la batte**: `.field { display: block }` faceva ricomparire
   un campo che il server aveva nascosto, e il difetto non si vedeva né nei test né nell'HTML, perché
   l'attributo c'era davvero. Si vedeva solo guardando la schermata.

   Vale per tutto il meccanismo di `reveal.js` (*07* §19.6): ciò che si governa è **reso sempre** e
   nascosto con `hidden`, e se una classe può scavalcarlo il meccanismo funziona su una tabella e non
   su un campo, in silenzio. L'`!important` è quindi la dichiarazione giusta e non una scorciatoia:
   nascosto vuol dire nascosto, e nessuna classe deve poter dire il contrario. */

[hidden] {
    display: none !important;
}

.visually-hidden {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}


/* ─── L'impianto ──────────────────────────────────────────────────────────────────────────────
   Barra laterale fissa a sinistra, e a destra una colonna che scorre: la barra superiore resta,
   il contenuto scorre sotto di essa. */

.app {
    display: grid;
    grid-template-columns: 248px 1fr;
    height: 100%;
}

/* `min-height: 0` accanto a `min-width: 0`, e per la stessa ragione: questa è una cella di griglia,
   e il suo minimo automatico è l'altezza del proprio contenuto — che **batte** `height: 100%`. Senza,
   la colonna cresce con la scheda, si porta dietro l'area che dovrebbe scorrere, e a scorrere finisce
   per essere la finestra. Da lì discende che niente resta appiccicato, in nessuna schermata. */
.workspace {
    display: grid;
    grid-template-rows: auto 1fr;
    min-width: 0;
    min-height: 0;
    height: 100%;
}

/* **`min-height: 0` è ciò che fa scorrere questo invece della finestra.** Una traccia `1fr` non si
   comprime sotto il proprio contenuto se non glielo si dice: senza, l'area cresce con la scheda,
   `overflow: auto` non ha niente da tagliare, e a scorrere finisce per essere la pagina intera. Da
   cui una conseguenza che si vede in ogni schermata e sembra tutt'altro difetto: barra laterale,
   barra superiore, albero e comandi della scheda **se ne vanno scorrendo**, benché siano dichiarati
   appiccicati — `position: sticky` si aggancia all'antenato che scorre, e se quello non scorre non
   c'è niente a cui agganciarsi. */
.content {
    padding: var(--pad-l);
    overflow: auto;
    min-height: 0;
}

/* La colonna di lettura, per le schermate che non hanno una tabella da stendere. Non sta su
   `.content` perché Ricette e Archivio la larghezza la vogliono tutta: si mette sulla pagina che la
   chiede, non su tutte. */
.column {
    max-width: var(--content-max);
    margin-inline: auto;
}


/* ─── La barra laterale ───────────────────────────────────────────────────────────────────────
   Quattro voci soltanto — Home, Ricette, Archivio, Impostazioni — e in fondo la scheda utente.

   Le voci che il ruolo non permette **si mostrano disabilitate, non si nascondono**, con la
   ragione al passaggio del mouse: la schermata resta la stessa per tutti, e chi non può fare una
   cosa vede che esiste e perché non gli è concessa. */

/* Quattro righe: il marchio, la postazione, le voci — che prendono lo spazio che avanza — e in
   fondo l'utente. La postazione è arrivata il 26/08 e la riga va dichiarata: una cella non prevista
   finisce in una traccia implicita, e il riquadro si stira a riempirla. */
/* La colonna è dichiarata: senza, un disegno più largo della barra — compreso il bordo trasparente di
   un logo — allargava la traccia implicita, e le voci finivano tagliate a destra. */
.sidebar {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto auto 1fr auto;
    border-right: 1px solid var(--border);
    background: var(--sidebar-bg);
    color: var(--sidebar-ink);
    overflow: hidden;
}

/* **I due loghi del redesign, e il numero di compilazione** (07 §91.6). Il marchio di chi fa
   l'apparato prima, il nome dell'applicazione sotto e più piccolo — 160 px su 251: più grande diventava
   la cosa più forte della barra, e largo quanto le voci era invadente — e il numero si vede solo quando
   lo si cerca.

   **I PNG sono ricavati per differenza dal loro fondo**, e la barra ha il fondo del logo di Imeta: quello
   di SeamBox differisce di quattro-cinque livelli, e l'alone non si vede nemmeno a scala 2. I margini
   negativi recuperano il bordo trasparente dei file, misurato, così il disegno comincia dove cominciano
   le voci sotto. */
.sidebar__brand {
    display: grid;
    justify-items: start;
    gap: 10px;
    padding: 22px 20px 18px;
}

.sidebar__brand > img { display: block; }

/* **Al buio i loghi sono in negativo** (07 §91.10). Sono scuri e ricavati da un fondo chiaro: invertiti, danno il
   negativo esatto della loro composizione su quel fondo, e la rotazione della tinta riporta i verdi a essere
   verdi. Il cubo di Imeta ne esce con le facce scambiate di luminosità: è provvisorio, finché non arrivano i
   loghi ufficiali per i due temi. */
:root[data-theme="dark"] .sidebar__brand > img { filter: invert(1) hue-rotate(180deg); }
.brand__owner { margin: -7px 0 0 -8px; }
.brand__app { width: 160px; height: auto; margin: -7px 0 -8px -9px; }

/* Cifre a passo fisso perché è un numero che si confronta a occhio fra due schermate, e con le
   proporzionali le colonne non si allineano.

   **Le due righe sono volute.** Il numero è più largo della barra e andrebbe a capo comunque: senza
   una regola lasciava la parola «Build» orfana da sola sopra, che è il modo di far sembrare rotta
   una cosa che funziona. Così invece la parola è un'etichetta e il numero è il valore — e siccome la
   mette il foglio di stile, chi seleziona il numero per copiarlo non se la porta dietro. */
.brand__build {
    font-size: 11px;
    font-weight: 400;
    letter-spacing: 0;
    color: var(--sidebar-ink-muted);
    font-variant-numeric: tabular-nums;
    overflow-wrap: anywhere;
    line-height: 1.35;
}

.brand__build::before {
    content: "Build";
    display: block;
    font-size: 9px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    opacity: 0.75;
}

/* La postazione e la spia del suo box. Sotto il marchio perché è il primo fatto che riguarda dove
   si sta lavorando, e il resto della barra sono i posti dove si può andare. */
/* Lo stacco cresce scendendo — intestazione, poi postazione, poi le voci — perché sono tre cose di
   natura diversa e non un elenco: dove si lavora non è un posto dove si va. L'aria fa il lavoro che
   altrove farebbe un filetto, che qui competerebbe con quello giallo della voce attiva. */
.station {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0 var(--pad-m) calc(var(--gap) * 2);
    padding: 8px 10px;
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    background: var(--card-bg);
    font-size: 13px;
    font-weight: 700;
}

/* **Il verde acceso, e solo qui**: è la spia, e dal redesign (07 §91.8) sta su una pastiglia bianca con
   il filetto — il bianco velato di prima era pensato per la barra verde, e sulla barra chiara spariva. Spenta è un grigio neutro — non rossa: il box che non risponde è una
   condizione normale a inizio turno, non un guasto da segnalare in rosso. **Un grigio vero**, il neutro
   degli stati, dal 07/10 (S.82): fino ad allora era l'inchiostro della barra al 35%, che sul verde
   diventava un verde scuro. */
.station__dot {
    width: 9px;
    height: 9px;
    flex: none;
    border-radius: 50%;
    background: var(--status-pending-ink);
}

.station__dot--on { background: #1FA34A; opacity: 1; box-shadow: 0 0 0 3px rgb(31 163 74 / 22%); }

.station__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.sidebar__nav {
    margin: 0;
    padding: 0 10px;
    list-style: none;
}

/* **Le voci sono più grandi del registro della scrivania** (07 §91.4): la barra serve al banco e alla
   scrivania insieme, e chi è in piedi la legge da più lontano. Testo, riga e icona crescono insieme. */
.sidebar__item {
    --icon-size: 20px;
    display: flex;
    align-items: center;
    gap: 12px;
    min-height: 48px;
    margin-bottom: 2px;
    padding: 11px 14px;
    /* Il filetto giallo esiste su ogni voce ma è trasparente: così l'etichetta non si sposta di
       tre pixel quando la voce diventa attiva. */
    border-left: 3px solid transparent;
    border-radius: var(--radius-btn);
    color: var(--sidebar-ink);
    font-size: 16px;
    font-weight: 600;
    text-decoration: none;
}

.sidebar__item:hover {
    background: color-mix(in srgb, var(--sidebar-ink) 6%, transparent);
}

/* Il giallo dice soltanto «sei qui». Non significa mai attenzione, né stato. Il rilievo è lo stesso
   della cella e della scheda della cronologia su cui si lavora: la selezione ha un modo solo. */
.sidebar__item[aria-current] {
    background: var(--sidebar-active-bg);
    border-left-color: var(--sidebar-filet);
    box-shadow: var(--selection-relief), 0 0 0 1px var(--border);
    color: var(--sidebar-active-ink);
}

.sidebar__item[aria-disabled="true"] {
    color: var(--sidebar-ink-muted);
    cursor: not-allowed;
    opacity: 0.55;
    pointer-events: none;
}

/* La scheda utente, e il menu che apre. È un `<details>`: si apre e si chiude senza una riga di
   JavaScript, e con la tastiera funziona da sé. */

.usermenu { position: relative; }

.sidebar__user {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: var(--gap) var(--pad-m);
    border-top: 1px solid color-mix(in srgb, var(--sidebar-ink) 15%, transparent);
    cursor: pointer;
    list-style: none;
}

.sidebar__user::-webkit-details-marker { display: none; }
.sidebar__user:hover { background: color-mix(in srgb, var(--sidebar-active-bg) 10%, transparent); }

/* Il pannello esce dalla barra e sale: in fondo alla colonna non ci sarebbe posto sotto. */
.usermenu__panel {
    position: absolute;
    bottom: calc(100% - var(--gap));
    left: 10px;
    z-index: 60;
    width: 232px;
    padding: 10px;
    border: 1px solid var(--border);
    border-radius: var(--radius-panel);
    background: var(--card-bg);
    box-shadow: var(--shadow-raised);
    color: var(--text-primary);
}

.usermenu__item {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    min-height: var(--tap-target);
    padding: 0 10px;
    border: 0;
    border-radius: var(--radius-btn);
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    text-decoration: none;
    /* Un comando non va a capo dentro sé stesso: «Nascondi ricetta» spezzato su due righe si legge
       come due comandi, e la riga che lo contiene si allarga di conseguenza. Se lo spazio non basta,
       va a capo la riga — non la parola. */
    white-space: nowrap;
    cursor: pointer;
}

.usermenu__item:hover { background: var(--border-softer); }

.usermenu__section {
    margin: 8px 0 6px;
    padding: 0 10px;
    color: var(--text-muted);
    font-size: var(--text-label);
    font-weight: 700;
    letter-spacing: var(--text-label-spacing);
    text-transform: var(--text-label-transform);
}

.usermenu__rule { margin: 8px 0; border: 0; border-top: 1px solid var(--border); }

.sidebar__avatar {
    display: grid;
    place-items: center;
    width: 32px; height: 32px;
    flex: none;
    border-radius: var(--radius-pill);
    background: var(--brand-surface);
    color: var(--brand-ink);
    font-size: 13px;
    font-weight: 700;
}

.sidebar__who { display: grid; min-width: 0; }
.sidebar__name { font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sidebar__role { color: var(--sidebar-ink-muted); font-size: var(--text-label); }


/* ─── La soglia ───────────────────────────────────────────────────────────────────────────────
   L'accesso, e le poche schermate che si vedono prima di entrare. Non hanno barra laterale — non
   c'è ancora nessuno a cui disegnarla — quindi vivono su un layout loro, centrate sulla pagina. */

.gate {
    display: grid;
    place-items: center;
    min-height: 100%;
    padding: var(--pad-l);
}

/* ─── La scheda del marchio ───────────────────────────────────────────────────────────────────
   L'accesso, lo splash che lo segue e le Informazioni dal menu utente: un oggetto solo, con la stessa
   forma e la stessa misura nei tre momenti (07 §91.5). In cima la lattina del redesign, sotto il logo
   di Imeta in una colonna che ha il colore della barra laterale, e al centro il nome con ciò che serve
   in quel momento.

   **L'altezza è un minimo, non una misura**: all'accesso, con il suggerimento delle credenziali di
   fabbrica e un rifiuto, il contenuto supera i 480 px, e una scheda che taglia «Entra» è peggio di una
   scheda che cresce. Negli altri due momenti resta a 480. */

.brand-card {
    display: grid;
    grid-template-columns: 240px 1fr;
    grid-template-rows: 116px 1fr;
    width: min(760px, calc(100vw - 32px));
    min-height: 480px;
    overflow: hidden;
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-raised);
    color: var(--text-primary);
    text-align: left;
}

/* La lattina sta a destra come nella testata da cui viene, e le si tolgono gli ultimi 13 px: il ritaglio
   si porta dietro, quasi trasparente, l'angolo del riquadro del calendario che le stava accanto. */
.brand-card__photo {
    grid-column: 1 / -1;
    border-bottom: 1px solid var(--border);
    background: var(--brand-card-main) url("../img/brand/can.png") right -13px top / 653px 116px no-repeat;
}

.brand-card__owner {
    padding: 28px;
    border-right: 1px solid var(--border);
    background: var(--brand-card-owner);
}

/* I due loghi hanno un margine trasparente loro, misurato sui file: lo si recupera, così il disegno
   comincia dove comincia il testo che gli sta sotto. */
.brand-card__owner > img { display: block; margin: -7px 0 0 -8px; }

.brand-card__main {
    display: grid;
    align-content: start;
    gap: 14px;
    padding: 28px 32px;
    background: var(--brand-card-main);
}

.brand-card__main > img { display: block; margin: -11px 0 -4px -14px; }

/* Le distanze le dà la griglia: i margini propri — dei paragrafi, e quello che `.notice` aggiunge a ciò
   che la segue — le raddoppiavano. */
.brand-card__main > :not(img) { margin: 0; }

.brand-card__form { display: grid; gap: 14px; }
.brand-card__form .field { margin: 0; }

.brand-card__greeting { margin: 0; font-size: 18px; font-weight: 700; }

.brand-card__facts {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 6px 16px;
    margin: 0;
}

.brand-card__facts dt { color: var(--text-secondary); }
.brand-card__facts dd { margin: 0; font-variant-numeric: tabular-nums; }

/* Su un telefono la colonna del logo va sopra: affiancata lascerebbe ai campi meno di metà schermo. */
@media (max-width: 640px) {
    .brand-card { grid-template-columns: 1fr; grid-template-rows: 116px auto 1fr; }
    .brand-card__owner { border-right: 0; border-bottom: 1px solid var(--border); }
}

/* **Lo splash**: la scheda sopra la prima pagina dopo l'accesso, per un secondo e mezzo, poi si dissolve.
   Si chiude **anche senza JavaScript**: l'animazione la rende invisibile e la toglie dai clic. Con
   JavaScript un clic o un tasto la chiudono subito (`js/splash.js`), perché chi entra al cambio turno
   non deve aspettare. */
.splash {
    position: fixed;
    inset: 0;
    z-index: 1000;
    display: grid;
    place-items: center;
    background: rgb(0 0 0 / .38);
    animation: splash-out 2s forwards;
}

.splash--gone { display: none; }

@keyframes splash-out {
    0%, 75% { opacity: 1; visibility: visible; }
    100%    { opacity: 0; visibility: hidden; }
}

/* Chi ha chiesto meno movimento la vede comparire e sparire, senza dissolvenza. */
@media (prefers-reduced-motion: reduce) {
    .splash { animation: splash-out 1.5s steps(1, end) forwards; }
}

/* **Le Informazioni sono la stessa scheda in un `<dialog>`**: il dialogo si spoglia del proprio riquadro
   e porta solo la croce, sopra la lattina. */
.dialog--brand {
    width: auto;
    min-width: 0;
    border: 0;
    border-radius: var(--radius-card);
    background: none;
    box-shadow: none;
}

.dialog--brand .drawer__close {
    position: absolute;
    top: 10px;
    right: 10px;
    background: rgb(255 255 255 / .85);
}

/* ─── La barra superiore ──────────────────────────────────────────────────────────────────────
   Porta il titolo della schermata e lo stato del box. Lo stato sta qui e non in un riquadro della
   Home perché è informazione che serve dovunque, e un riquadro la mostrerebbe solo lì. */

.topbar {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--gap);

    /* **L'altezza è sua, non del contenuto.** Senza questo minimo la barra la decidono i comandi che
       porta, e sono tre altezze diverse in sei schermate — 55 px dove non ce ne sono (Linea), 58
       sulla Home, 65 dove ci sono: passando da una scheda all'altra *tutto* ciò che sta sotto scende
       o sale, e sull'albero delle Ricette sono i 10 px che il committente ha segnalato (S.11).

       Il minimo è quello della barra con i comandi — un bersaglio più i due margini, più il filetto,
       perché il box è `border-box` — e si scrive con i token invece che in pixel: al banco crescono
       insieme e la barra li segue. Dove i comandi non ci sono resta un po' di vuoto in più, ed è il
       prezzo dichiarato dell'uniformità. */
    min-height: calc(var(--tap-target) + var(--gap) * 2 + 1px);
    padding: var(--gap) var(--pad-l);
    border-bottom: 1px solid var(--border);
    background: var(--card-bg);
}

/* Le tre zone. La prima e la terza si dividono lo spazio, il centro resta alla fascia degli esiti —
   che è in overlay e non sposta niente comparendo. I comandi non vanno a capo: se lo spazio manca,
   a stringersi sono le briciole, che si possono troncare senza perdere il gesto. */
.topbar__title { flex: 1; min-width: 0; }

.topbar__actions {
    display: flex;
    align-items: center;
    gap: var(--tap-gap);
    flex: 0 0 auto;
}

/* ─── Le briciole ─────────────────────────────────────────────────────────────────────────────
   Dicono che cosa si sta guardando, e da lì che cosa faranno i comandi che stanno a destra nella
   stessa barra. L'ultima voce è dove si è già e non è un collegamento: premerla per sbaglio
   ricaricherebbe la pagina buttando via ciò che si stava battendo.

   Si tronca la voce lunga, non la catena: il nome dell'oggetto può essere lungo quanto si vuole, e
   l'ultima voce — quella che dice che cosa si salva — non deve mai sparire. */

.crumbs {
    display: flex;
    align-items: baseline;
    gap: 8px;
    min-width: 0;
    font-size: 15px;
}

.crumbs__step,
.crumbs__here {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.crumbs__step { color: var(--text-secondary); text-decoration: none; }
.crumbs__step:hover { text-decoration: underline; }
.crumbs__sep { color: var(--text-muted); flex: none; }

.crumbs__here { font-weight: 700; color: var(--text-primary); }

/* Un'intestazione di scheda, ora che i comandi non ci stanno più: il titolo e le pastiglie di stato.
   Non è appiccicata — a restare in vista sono i comandi, che stanno nella barra superiore. */
.card__head {
    display: flex;
    align-items: center;
    gap: var(--gap);
    flex-wrap: wrap;
    padding: 12px var(--pad-m);
    border-bottom: 1px solid var(--border-soft);
}

.card__head .card__title { margin: 0; }

/* **Una testata senza margini né riga**, per una scheda che ha già il suo: il titolo e i suoi comandi sulla stessa riga,
   allineati con i titoli delle schede sorelle (14/10). `card__head` dentro una scheda col suo margine rientrava di venti
   pixel, e nel wizard dell'importazione «Utenze» non stava sotto «Operatori». */
.card__heading {
    display: flex;
    align-items: center;
    gap: var(--gap);
    flex-wrap: wrap;
}

.card__heading .card__title { margin: 0; }


/* ─── Riquadri ────────────────────────────────────────────────────────────────────────────────*/

.card {
    padding: var(--pad-m);
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    background: var(--card-bg);
    box-shadow: var(--shadow);
}

.card__title { margin-bottom: 4px; }
/* **La riga si legge fino a una certa lunghezza, e poi non si legge più.** Su una schermata larga
   questi paragrafi correvano da bordo a bordo — centoventisette caratteri sulla Home, dove il limite
   oltre il quale l'occhio perde il capo della riga successiva sta intorno ai settanta. Il tetto è in
   `ch`, cioè nella larghezza del carattere: cresce con il testo quando si passa alla scala del
   banco. */
.card__note {
    max-width: 74ch;
    color: var(--text-muted);
    font-size: var(--text-secondary-size);
}

/* Il saluto in cima alla Home. Il nome grande e sotto, più tenue, ruolo e data: chi apre
   l'applicazione deve capire in un colpo d'occhio con che utenza la sta usando. */

.greeting__hello { margin-bottom: 2px; }

.greeting__who {
    color: var(--text-secondary);
    font-size: var(--text-secondary-size);
}


.card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: var(--gap);
}

/* Le quattro porte dei calibri: **quattro colonne uguali, o due**, mai tre più una.
   La griglia elastica qui sopra, alla larghezza della scheda, ne metteva tre in fila e una sola
   sotto — e quattro elementi omologhi che si dispongono così si leggono come «tre più un'altra
   cosa». Sono quattro prese identiche su una scatola, e la forma deve dirlo. */
.ports {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: var(--gap);
}

@media (max-width: 1100px) {
    .ports { grid-template-columns: repeat(2, 1fr); }
}

/* **Un margine solo, non due** (S.77): il riquadro di una porta sta dentro un altro riquadro, e i margini di
   riquadro e corpo si sommavano a quaranta pixel per lato. Su 236 ne restavano 156, e «Spessore aggraffatura»
   — 165 — si leggeva «Spessore aggra». Con un margine solo la tendina ne ha 186: ci stanno tutti i nomi del
   catalogo, misurati alle due risoluzioni. */
.ports > .card { padding: 12px; }
.ports > .card > .card__head,
.ports > .card > .card__body { padding-left: 0; padding-right: 0; }

/* Assegnare una misura a una porta: **impilati, non affiancati**. Un riquadro di porta è largo
   poco più di duecento pixel, e affiancando tendina e comando alla tendina non resta niente — si
   riduce a due caratteri e non si legge più che cosa si sta per scegliere. È lo stesso difetto
   dell'etichetta tagliata dentro una `select`, che non produce overflow e quindi nessuna misura
   automatica lo trova: si vede guardando. */
.ports__assign {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.ports__assign select { width: 100%; }

/* **La vista dal vivo della scheda Connessione** (13/10): piccola, nella terza colonna della riga della
   telecamera, e con lo stesso fondo e lo stesso «per intero» del palco. Sotto, le righe che dicono dove si
   rompe la catena: vuote finché va. I conti del server si vedono solo se il browser, dopo tre secondi, non
   mostra ancora niente — lo dice `live-check.js` sul riquadro. */
.live-check { min-width: 0; }

.live-check__image {
    display: block;
    width: 100%;
    aspect-ratio: 4 / 3;
    object-fit: contain;
    border-radius: var(--radius-panel);
    background: #15171a;
}

.live-check__browser,
.live-check__trouble,
.live-check__traffic {
    margin: 6px 0 0;
    color: var(--status-error-ink);
    overflow-wrap: anywhere;
}

.live-check__browser:empty { display: none; }
.live-check__traffic { display: none; color: var(--text-muted); }
.live-check[data-live-late]:not([data-live-shown]) .live-check__traffic { display: block; }



/* ─── Schede ──────────────────────────────────────────────────────────────────────────────────
   Le schede sono navigazione, non stato: ognuna è un indirizzo suo, quindi si può mettere fra i
   preferiti e il tasto «indietro» funziona. Quelle che non si possono aprire restano visibili e
   disabilitate con il motivo. */

.tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    width: fit-content;
    max-width: 100%;
    margin-bottom: var(--gap);
    padding: 6px;
    border: 1px solid var(--border);
    border-radius: var(--radius-panel);
    background: var(--card-bg);
}

.tabs__item {
    display: inline-flex;
    align-items: center;
    min-height: var(--tap-target);
    padding: 0 16px;
    border-radius: var(--radius-btn);
    color: var(--text-muted);
    font-weight: 600;
    text-decoration: none;
}

.tabs__item:hover { background: var(--border-softer); }

/* **Il verde pieno non sta qui.** Una scheda scelta dice «sei qui», che è la stessa cosa che dicono
   la voce di barra laterale e il nodo aperto dell'albero: stesso fondo e stesso filetto giallo. Il
   verde è dell'azione principale della schermata — che nelle Impostazioni è «Salva» — e finché lo
   portava la navigazione erano due, con quella che conta resa in secondario. */
.tabs__item[aria-current] {
    background: var(--nav-active-bg);
    color: var(--text-primary);
    font-weight: 700;
    box-shadow: inset 0 -3px 0 0 var(--step-accent);
}

.tabs__item[aria-disabled="true"] {
    color: var(--text-faint);
    cursor: not-allowed;
    opacity: 0.55;
}

/* ─── Comandi ─────────────────────────────────────────────────────────────────────────────────
   Il verde pieno è l'azione principale di una schermata, **una sola per schermata**. Tutto il
   resto è secondario o neutro: gli stati non sono mai pulsanti, sono pastiglie. */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    min-height: var(--tap-target);
    padding: 0 16px;
    border: 1px solid var(--input-border);
    border-radius: var(--radius-btn);
    background: var(--card-bg);
    color: var(--text-primary);
    font: inherit;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
}

.btn:hover:not(:disabled) { border-color: var(--text-secondary); }

.btn:disabled,
.btn[aria-disabled="true"] {
    background: var(--disabled-bg);
    color: var(--text-faint);
    cursor: not-allowed;
    opacity: 0.7;
}

.btn--primary {
    border-color: var(--action);
    background: var(--action);
    color: var(--action-ink);
}

.btn--primary:hover:not(:disabled) {
    border-color: var(--action);
    background: color-mix(in srgb, var(--action) 88%, black);
}

.btn--danger { border-color: var(--status-error-ink); color: var(--status-error-ink); }

.btn--quiet { border-color: transparent; background: none; }

.btn--wide { width: 100%; }


/* ─── Pastiglie di stato ──────────────────────────────────────────────────────────────────────
   Cinque stati, e nessuno di essi è un pulsante: una pastiglia si legge, non si preme. */

.pill {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 5px 12px 5px 10px;
    border-radius: var(--radius-pill);
    background: var(--chip-bg);
    color: var(--chip-ink);
    font-size: var(--text-secondary-size);
    font-weight: 600;
    white-space: nowrap;
}

.pill--ok           { background: var(--status-ok-bg);          color: var(--status-ok-ink); }
.pill--warning      { background: var(--status-warning-bg);     color: var(--status-warning-ink); }
.pill--error        { background: var(--status-error-bg);       color: var(--status-error-ink); }
.pill--pending      { background: var(--status-pending-bg);     color: var(--status-pending-ink); }
.pill--unjudged     { background: var(--status-unjudged-bg);    color: var(--status-unjudged-ink); }
.pill--implausible  { background: var(--status-implausible-bg); color: var(--status-implausible-ink); }

.pill__dot {
    width: 10px; height: 10px;
    flex: none;
    border-radius: 50%;
    background: currentColor;
}


/* ─── Avvisi ──────────────────────────────────────────────────────────────────────────────────
   Una fascia che resta finché la condizione dura. Non si chiude con una crocetta: un avviso che si
   può togliere senza risolvere il problema è un avviso che non viene letto la seconda volta. */

.notice {
    display: flex;
    align-items: center;
    gap: var(--gap);
    padding: 12px var(--pad-m);
    border: 1px solid var(--notice-border);
    border-radius: var(--radius-panel);
    background: var(--notice-bg);
    color: var(--status-warning-ink);
    font-size: var(--text-secondary-size);
}

.notice__text { flex: 1; max-width: 78ch; color: var(--text-primary); }

/* **Una condizione che è andata bene non ha il colore dell'attenzione** (14/10): l'esito riuscito dell'importazione
   stava nell'ambra della nota, e si leggeva come un avviso. Prende i colori di `pill--ok`, che fuori dall'area di lavoro
   dicono già «valida» e «attiva». */
.notice--done { border-color: transparent; background: var(--status-ok-bg); color: var(--status-ok-ink); }

/* Il lucchetto dell'avviso della password, dal redesign. */
.notice__tile {
    display: grid;
    place-items: center;
    flex: none;
    width: 40px;
    height: 40px;
    border-radius: var(--radius-btn);
    background: var(--notice-icon-bg);
    color: var(--notice-icon-ink);
}

.notice__tile > svg { width: 22px; height: 22px; }

.notice + * { margin-top: var(--gap); }

/* La nota che avvisa del salvataggio che il gesto si porta dietro.
   **È una regola di stile e non una riga di codice**: `guard.js` dichiara sul documento se la scheda
   ha modifiche non salvate, e da lì la nota compare da sé nei pannelli che la portano. Sta **in fondo
   al pannello, accanto ai comandi**, perché è una postilla sul gesto che si sta per compiere e non
   una premessa — e perché nel pannello della definizione eviterebbe due fasce colorate in fila.

   Non è gialla, benché avvisi: **il giallo dice soltanto «sei qui»**, ed è l'unica cosa che significa
   in tutta l'applicazione. Questa è una condizione che dura finché il pannello è aperto, cioè
   esattamente ciò per cui `.notice` esiste. */
.notice--carry { display: none; margin-bottom: var(--gap); }

:root[data-unsaved] .notice--carry { display: flex; }


/* ─── La fascia dei messaggi ──────────────────────────────────────────────────────────────────
   Un solo meccanismo per tutti gli esiti di un gesto — «Linea creata» e «La linea è bloccata» —
   perché due meccanismi per eventi simili si imparano due volte.

   **Non consuma area di lavoro, e non copre nulla.** Sta sospesa sulla barra superiore, che è
   cornice: comparendo o sparendo non sposta di un pixel ciò che sta sotto, e non finisce davanti al
   contenuto né alle schede di navigazione. È in alto perché il messaggio in basso a destra del
   mock-up non lo guarda nessuno — è rimasto lì diciassette versioni senza essere notato.

   Conferma e rifiuto si dissolvono da sé dopo cinque secondi (07 §69.4); il rifiuto ha anche la
   croce, per toglierlo prima. `.notice` è un'altra cosa e resta: quella dichiara una condizione che
   dura, non l'esito di un gesto. */

.flash-slot {
    position: absolute;
    inset-inline: 0;
    top: 50%;
    transform: translateY(-50%);
    z-index: 50;
    display: flex;
    justify-content: center;
    pointer-events: none;
}

.flash {
    pointer-events: auto;
    display: flex;
    align-items: center;
    gap: 12px;
    max-width: min(640px, 100%);
    padding: 12px var(--pad-m);
    border-radius: var(--radius-pill);
    box-shadow: var(--shadow-raised);
    font-size: var(--text-secondary-size);
    font-weight: 600;
    transition: opacity .28s ease, transform .28s ease;
}

/* L'inchiostro della pagina come fondo: si inverte da sé in tutti e tre i temi, e soprattutto non è
   verde. Nell'area di lavoro il verde significa «dentro tolleranza» e non si presta ad altro. */
.flash--done {
    background: var(--text-primary);
    color: var(--card-bg);
}

.flash--refused {
    background: var(--status-error-bg);
    color: var(--status-error-ink);
    border: 1px solid currentColor;
}

.flash__text { flex: 1; }

.flash__close {
    flex: none;
    border: none;
    background: none;
    padding: 0;
    color: inherit;
    font: inherit;
    line-height: 1;
    cursor: pointer;
    opacity: .7;
}

.flash__close:hover { opacity: 1; }

.flash--leaving {
    opacity: 0;
    transform: translateY(-8px);
}

@media (prefers-reduced-motion: reduce) {
    .flash { transition: none; }
}


/* ─── La testata di una scheda con i suoi comandi di sezione ──────────────────────────────────
   «+ Misura», «+ Chiave», le pastiglie dei gruppi: comandi **secondari**, che stanno accanto a ciò
   su cui agiscono. I comandi principali dal 26/08 stanno nella barra superiore (*03* §A.13 regola 1),
   e questa testata è ciò che resta.

   **Non è appiccicata, e prima lo era.** Restando ferma mentre la tabella le passava sotto si
   leggeva sovrapposta alle righe: un'intestazione che scavalca il contenuto costa più di quanto
   renda, perché ciò che serve avere sempre sott'occhio è già in alto. Scorre con la sua tabella, e
   se esce di scena pazienza. */

.card--barred { padding: 0; overflow: visible; }

.card__bar {
    display: flex;
    align-items: center;
    gap: var(--gap);
    flex-wrap: wrap;
    padding: 12px var(--pad-m);
    border-bottom: 1px solid var(--border-soft);
    border-radius: var(--radius-card) var(--radius-card) 0 0;
    background: var(--card-bg);
}

.card__bar .card__title { margin: 0; }

.card__body { padding: var(--pad-m); }


/* ─── Campi ───────────────────────────────────────────────────────────────────────────────────*/

.field { display: block; margin-bottom: var(--gap); }

.field__label {
    display: block;
    margin-bottom: 6px;
    color: var(--text-muted);
    font-size: var(--text-secondary-size);
}

.field__hint  { margin-top: 6px; color: var(--text-faint);      font-size: var(--text-label); }
.field__error { margin-top: 6px; color: var(--status-error-ink); font-size: var(--text-label); }

/* Un modulo si legge in colonna e stretto: righe lunghe quanto la finestra costringono l'occhio a
   tornare indietro fra un'etichetta e il suo campo. */
/* La testata di una scheda quando i campi sono più di tre.

   `.form-column` li incolonna dentro 420 px, che è la larghezza giusta per leggere ma sbagliata per
   quattro campi corti: la scheda cominciava con mezzo schermo vuoto a destra. Qui si dispongono da
   soli e il riquadro si riempie. Sotto i 180 px per campo tornano in colonna da sé. */
.field-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: var(--gap);
    align-items: start;
}

/* Campi che sono **un gruppo solo** e vanno letti insieme: il minimo, l'obiettivo, il massimo.
   Restano in riga anche quando si stringono, perché mandarne uno a capo li fa leggere come due
   gruppi — ed è esattamente il rilievo del committente sul pannello «Nuova misura» (09 §6.3).
   `.field-grid` non va bene qui: il suo `auto-fit` va a capo appena i 180 px non ci stanno.

   Sotto i 420 px si impilano tutti: tre campi numerici in 130 px l'uno si leggono peggio di tre
   righe, e a quel punto la lettura in ordine crescente resta comunque, dall'alto in basso. */
.field-row {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--gap);
    align-items: start;
}

@media (max-width: 420px) {
    .field-row { grid-template-columns: 1fr; }
}

/* Un campo con la spunta **a fianco** dell'etichetta e non sopra.

   La classe era usata da tre voci delle Impostazioni fin da quando quella scheda è nata, e **non è
   mai esistita nel foglio di stile**: le caselle stavano sopra i loro nomi, incolonnate come se
   fossero campi di testo. Trovata guardando la scheda al passo 6-bis della fetta 7 — nessun test
   poteva vederla, e l'HTML era corretto. */
/* **La scheda dell'apparato occupa due colonne della riga** (*07* §87.4): la voce dice il MAC e la presa, e accanto
   ci stanno il comando che la fa lampeggiare e, mentre lampeggia, la sua pastiglia. In una colonna sola il MAC si
   leggeva a metà. */
.field--board { grid-column: span 2; min-width: 0; }

.field__line {
    display: flex;
    align-items: center;
    gap: var(--gap);
}

.field__line select { flex: 1; min-width: 0; }

.field__line .btn,
.field__line .pill { flex: none; }

/* **Il comando accanto a un campo è alto quanto il campo** (14/10): «Fai lampeggiare» accanto alla tendina della scheda
   era più basso di sette pixel, e due cose affiancate di altezze diverse si leggono come sbagliate. La pastiglia no:
   è uno stato, non un comando. */
.field__line .btn { align-self: stretch; }

.field--inline {
    display: grid;
    grid-template-columns: auto 1fr;
    column-gap: 10px;
    align-items: center;
}

.field--inline input[type="checkbox"] { width: auto; min-height: 0; justify-self: start; }

/* La nota va sotto il nome, non sotto la casella: incolonnata con ciò che spiega. */
.field--inline .field__hint { grid-column: 2; }

/* La fascia delle pastiglie **premibili**, sopra una tabella.

   `.chip` non è `.pill`, e la differenza non è cosmetica: una pastiglia di stato dice come stanno le
   cose e non si preme mai, questa è un comando. Si vedono diverse — la `.chip` ha un bordo, che è il
   segno con cui tutta l'applicazione dice «questo si preme». */
.chip-bar {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

.chip-bar__label {
    font-size: var(--text-label);
    font-weight: 700;
    letter-spacing: var(--text-label-spacing);
    text-transform: var(--text-label-transform);
    color: var(--text-faint);
    margin-right: 2px;
}

.chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 12px;
    border: 1px solid var(--input-border);
    border-radius: var(--radius-pill);
    background: var(--chip-bg);
    color: var(--chip-ink);
    font: inherit;
    font-size: var(--text-label);
    font-weight: 600;
    cursor: pointer;
}

.chip:hover:not(:disabled) {
    border-color: var(--text-secondary);
    color: var(--text-primary);
}

.chip:disabled {
    opacity: .5;
    cursor: not-allowed;
}

/* Il comando dentro una riga di tabella. Sta sotto il bersaglio tattile di 32 px, e va bene: è il
   registro gestionale, dove chi guarda è seduto con il mouse. Al banco non esistono tabelle con
   comandi di riga. */
.row-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 1px solid var(--input-border);
    border-radius: var(--radius-btn);
    background: var(--card-bg);
    color: var(--text-secondary);
    cursor: pointer;
}

.row-btn > svg {
    width: 15px;
    height: 15px;
}

.row-btn:hover:not(:disabled) { background: var(--chip-bg); }

/* **Spento si deve vedere**, ed era l'unica famiglia di comandi a non dirlo: `.btn`, `.chip`,
   `.grip` e `.tree-tools__item` hanno tutte il loro stato, il comando di riga no — restava a
   opacità piena, con il rosso del cestino intatto e il cursore a mano, e per giunta si illuminava
   al passaggio. Chi lo premeva vedeva comparire solo il `title`, ed è il rilievo S.63 e S.65:
   «togliere il messaggio e rendere l'icona non abilitata». Il messaggio resta dov'è — un motivo
   costante sta nel tooltip (*07* §45.4) — quello che mancava è questa metà.

   Il valore è quello di `.tree-tools__item:disabled`, che è l'omologo più vicino: stesso mestiere,
   un comando dentro una riga. */
.row-btn:disabled {
    opacity: .45;
    cursor: not-allowed;
}

.row-btn--danger { color: var(--status-error-ink); }

/* I comandi di una riga, affiancati. Non è `.row`: quella va a capo, e in una cella stretta — che è
   dove i comandi di riga stanno sempre — due pulsanti da 30 px si impilavano uno sull'altro,
   raddoppiando l'altezza della riga. */
.row-actions {
    display: flex;
    flex-wrap: nowrap;
    gap: 4px;
}

.row-btn--danger:hover:not(:disabled) { border-color: var(--status-error-ink); }

/* Come si ottiene una misura: il calibro, accanto al segno delle calcolate. Sta in una cella sua e
   non accanto al nome perché è una proprietà della riga, non del nome, e incolonnata si legge senza
   scorrere l'elenco. */
.type-icon {
    width: var(--icon-size);
    height: var(--icon-size);
    color: var(--text-secondary);
    display: block;
}

/* Il segno delle grandezze che si calcolano invece di misurarsi. Una sola lettera, perché sta in
   una cella accanto al nome e una pastiglia lì ruberebbe la riga. */
.derived {
    font-style: italic;
    font-weight: 700;
    color: var(--text-secondary);
    text-decoration: none;
    cursor: help;
}

.form-column { max-width: 420px; }

input[type="text"],
input[type="password"],
input[type="number"],
input[type="search"],
input[type="date"],
select,
textarea {
    width: 100%;
    min-height: var(--tap-target);
    padding: 8px 12px;
    border: 1px solid var(--input-border);
    border-radius: var(--radius-btn);
    background: var(--input-bg);
    color: var(--text-primary);
    font: inherit;
    font-variant-numeric: inherit;
}

input:disabled, select:disabled, textarea:disabled {
    background: var(--disabled-bg);
    color: var(--text-faint);
}


/* ─── Tabelle ─────────────────────────────────────────────────────────────────────────────────*/

/* **Il corpo delle tabelle è del registro** (07 §91.9): 18 alla scrivania, 16 al banco. Vale per tutte, non
   solo per `.table`, perché il tabellone dell'Archivio e il riepilogo hanno classi loro, e prima ereditavano
   il corpo di base senza che nessuno l'avesse deciso. */
table { font-size: var(--text-table); }

.table { width: 100%; border-collapse: collapse; }

.table th {
    padding: 8px 12px;
    border-bottom: 1px solid var(--border);
    color: var(--text-muted);
    font-size: var(--text-label);
    font-weight: 700;
    letter-spacing: var(--text-label-spacing);
    text-align: left;
    text-transform: var(--text-label-transform);
}

.table td {
    height: var(--row-height);
    padding: 6px 12px;
    border-bottom: 1px solid var(--border-soft);
}

.table tbody tr:hover { background: var(--border-softer); }

/* Una colonna che si stringe sul proprio contenuto, per lasciare al nome tutta la larghezza che
   avanza. Senza, sette colonne di spunta si spartiscono la tabella in parti uguali e i nomi vanno a
   capo a metà. */
.table th.tight, .table td.tight {
    width: 1%;
    white-space: nowrap;
    padding-inline: 10px;
}

.table .numeric { text-align: right; }


/* ─── Il selettore del tema ───────────────────────────────────────────────────────────────────
   Vive dentro il menu utente. Nel mock-up era una pastiglia fissa in basso a destra, che serviva a
   mettere in evidenza i tre temi davanti al committente ma sta fra i piedi in esercizio.

   La scelta si registra **per browser**, non per utente: il tema è una proprietà dello schermo —
   la luce del locale, il pannello — non della persona, e al banco lo stesso browser passa fra i
   turni. Legarlo all'utente farebbe cambiare aspetto al pannello a ogni cambio turno. */

.theme-switch {
    display: flex;
    gap: 4px;
    padding: 0 10px;
}

.theme-switch__option {
    display: grid;
    place-items: center;
    flex: 1;
    min-height: var(--tap-target);
    border: 1px solid var(--input-border);
    border-radius: var(--radius-btn);
    background: var(--card-bg);
    color: var(--text-muted);
    cursor: pointer;
}

.theme-switch__option[aria-pressed="true"] {
    border-color: var(--action);
    background: var(--action);
    color: var(--action-ink);
}


/* ─── Utilità, e sono poche di proposito ──────────────────────────────────────────────────────
   Un sistema di utilità generico non compra niente dove non c'è niente di generico da fare: qui
   stanno le tre che servono davvero a impaginare, e ogni altra cosa si scrive come classe che dice
   il suo mestiere. */

.stack { display: grid; gap: var(--gap); }
.row   { display: flex; align-items: center; gap: var(--tap-gap); flex-wrap: wrap; }

/* Una casella di spunta e la sua frase restano appaiate: a mandare a capo dev'essere la frase, non
   la coppia — altrimenti la spunta finisce sopra il testo che spiega che cosa spunta. */
.row > input[type="checkbox"] { flex: none; }
.row > input[type="checkbox"] + span { flex: 1; min-width: 0; }
.spacer { flex: 1; }

.muted { color: var(--text-muted); }
/* Un dato solo, messo in evidenza: il nome della ricetta attiva sulla Home. **Non è una lettura** —
   quelle hanno il loro riquadro più sotto, con etichetta e stato — e finché ha condiviso la classe
   con loro si portava dietro fondo, contorno e rientro, cioè si leggeva come un campo disabilitato
   invece che come il titolo che è. */
.figure { font-size: var(--text-reading); font-weight: 700; }

/* ─── L'impianto a due colonne ────────────────────────────────────────────────────────────────
   Un elenco a sinistra e la scheda di ciò che vi è selezionato a destra. È la forma della sezione
   Ricette, e la colonna dell'elenco resta ferma mentre la scheda scorre: chi configura passa da un
   nodo all'altro di continuo, e un albero che scivola via costringe a ritrovare il segno ogni
   volta.

   380 px, allargata dai 320 iniziali il 24/08. Il mock-up ne dà 440, ma li ottiene staccando il
   pannello dal bordo sinistro — spazio speso in margine — e a destra le sue schede finiscono per
   scorrere in orizzontale. Allineato a sinistra, 380 dà alla barra dei comandi le due parole di
   etichetta che le servono e lascia alla scheda sessanta pixel più del mock-up. */
.split {
    display: grid;
    grid-template-columns: 380px minmax(0, 1fr);
    gap: var(--gap);
    align-items: start;
}

/* Un figlio di griglia si allarga per il proprio contenuto se non gli si dice il contrario, e un
   contenuto che scorre non scorre più: la tabella dei parametri usciva dal riquadro portandosi via
   le ultime due colonne. Vale per la colonna e per ciò che vi si impila dentro. */
.split > *,
.stack > * {
    min-width: 0;
}

/* Sotto una certa larghezza le due colonne si impilano: il banco ha monitor modesti, e 380 px di
   albero più una scheda stretta non stanno in orizzontale. */
@media (max-width: 960px) {
    .split { grid-template-columns: minmax(0, 1fr); }
}

/* ─── L'albero ────────────────────────────────────────────────────────────────────────────────*/
/* Il pannello resta appiccicato mentre la scheda di destra scorre, ed è la ragione per cui esiste
   l'impianto a due colonne: chi configura passa da un nodo all'altro di continuo.

   A scorrere sono **soltanto i nodi**: la barra dei comandi e l'interruttore restano fermi in testa.
   È la regola del 03 §A.13 — i comandi operativi non devono mai richiedere di scorrere per essere
   trovati — applicata al pannello invece che alla scheda. Il mock-up li fa scorrere via insieme
   all'albero, e su una linea con molti oggetti spariscono. */
/* Resta fermo mentre la scheda scorre: è la ragione per cui l'impianto è a due colonne, e da quando
   i comandi stanno nella barra superiore è anche ciò che dice **su che cosa** agiranno.
   L'albero lungo scorre **dentro di sé**: senza, le ultime linee finirebbero sotto il bordo dello
   schermo e non ci sarebbe modo di raggiungerle, perché la pagina non scorre più. */
.tree-panel {
    position: sticky;
    top: var(--gap);
    display: flex;
    flex-direction: column;
    min-height: 0;
    max-height: calc(100vh - 2 * var(--gap));
}

.tree-panel .tree {
    overflow: auto;
    min-height: 0;
}

/* L'interruttore di ciò che è nascosto. Un collegamento e non un pulsante: pesato come i quattro
   comandi qui sopra si prenderebbe l'attenzione che spetta a loro, ed è un interruttore di vista,
   non un gesto sui dati. */
/* L'interruttore dei nascosti. Era un collegamento sottolineato e il committente non l'ha trovato:
   un interruttore si vede come oggetto, prima che come testo, e dice il proprio stato senza che si
   debba leggere l'etichetta (09 §6.2).

   **Acceso è scuro, non verde.** Il pieno del marchio spetta all'azione principale, una per
   schermata, e in ogni caso non entra nell'area di lavoro; qui basta il contrasto fra spento e
   acceso. */

.tree-switch {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: var(--tap-gap);
    padding: 6px 8px;
    border-radius: var(--radius-btn);
    color: var(--text-muted);
    font-size: var(--text-secondary-size);
    font-weight: 600;
    text-decoration: none;
}

.tree-switch:hover { background: var(--border-softer); color: var(--text-primary); }

/* **Lo stesso interruttore, anche quando è un pulsante** (14/10): nel wizard dell'importazione cambia stato senza
   cambiare pagina, ed è un `<button>` — con il fondo e il bordo di sistema, e diverso da tutti gli altri. */
button.tree-switch { border: 0; background: none; font-family: inherit; cursor: pointer; }

/* In una testata l'interruttore sta sulla riga del titolo: il margine sotto, che nell'albero lo separa dai nodi, lì lo
   alzava di quattro pixel rispetto al titolo e ai comandi accanto (14/10). */
.card__head .tree-switch,
.card__heading .tree-switch { margin-bottom: 0; }

.tree-switch__track {
    flex: 0 0 auto;
    width: 30px;
    height: 17px;
    padding: 2px;
    border: 1px solid var(--input-border);
    border-radius: 999px;
    background: var(--chip-bg);
    transition: background-color 120ms ease, border-color 120ms ease;
}

.tree-switch__knob {
    display: block;
    width: 11px;
    height: 11px;
    border-radius: 50%;
    background: var(--text-secondary);
    transition: transform 120ms ease, background-color 120ms ease;
}

.tree-switch[aria-checked="true"] { color: var(--text-primary); }

.tree-switch[aria-checked="true"] .tree-switch__track {
    border-color: var(--text-primary);
    background: var(--text-primary);
}

.tree-switch[aria-checked="true"] .tree-switch__knob {
    background: var(--card-bg);
    transform: translateX(13px);
}

/* Chi ha chiesto di non vedere le animazioni non le vede nemmeno qui: lo stato resta leggibile
   perché lo dicono il colore e la posizione, non il movimento fra i due. */
@media (prefers-reduced-motion: reduce) {
    .tree-switch__track,
    .tree-switch__knob { transition: none; }
}

/* La barra dei comandi dell'albero. Quattro caselle di uguale larghezza, icona sopra ed etichetta
   sotto: si colpiscono a occhio senza leggerle, e restano nello stesso posto passando da un nodo
   all'altro. Quelli che non si applicano restano visibili e spenti, con il motivo sul titolo. */

.tree-tools {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 6px;
    margin-bottom: var(--gap);
}

/* Il modulo di «Elimina» non deve diventare una colonna della griglia in più della sua casella. */
.tree-tools > form { display: contents; }

.tree-tools__item {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    min-height: 64px;
    padding: 8px 4px;
    border: 1px solid var(--input-border);
    border-radius: var(--radius-btn);
    background: var(--card-bg);
    color: var(--text-primary);
    font: inherit;
    font-size: var(--text-label);
    font-weight: 600;
    cursor: pointer;
}

.tree-tools__item:hover:not(:disabled) { background: var(--chip-bg); }

.tree-tools__item:disabled {
    opacity: .45;
    cursor: not-allowed;
}

.tree-tools__item--danger:not(:disabled) {
    border-color: var(--status-error-ink);
    color: var(--status-error-ink);
}

/* L'etichetta va su due righe perché il verbo da solo non basta: due comandi che dicono entrambi
   «Aggiungi» non si distinguono. Il secondo pezzo è quello che cambia con la selezione, e si vede
   che è il secondo perché è più leggero. */
.tree-tools__verb {
    font-weight: 700;
    line-height: 1.15;
}

.tree-tools__what {
    font-weight: 500;
    line-height: 1.15;
    color: var(--text-secondary);
}

.tree-tools__item:disabled .tree-tools__what { color: inherit; }

.tree {
    display: flex;
    flex-direction: column;
    overflow: auto;
    min-height: 0;
    gap: 2px;
}

/* Una voce dell'albero è un collegamento, non un elemento cliccabile finto: si apre in una scheda
   nuova, si copia, e la tastiera ci arriva da sola. */
.tree__item {
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: var(--tap-target);
    padding: 6px 10px;
    border-radius: var(--radius-btn);
    color: var(--text-primary);
    text-decoration: none;
    font-size: var(--text-body);
}
.tree__item:hover { background: var(--chip-bg); }

/* Il nodo aperto porta lo stesso filetto giallo della barra laterale, perché dice la stessa cosa:
   sei qui. Il verde resta all'azione principale, e nell'albero non ce n'è nessuna. */
.tree__item[aria-current] {
    background: var(--nav-active-bg);
    font-weight: 600;
    box-shadow: inset 3px 0 0 0 var(--step-accent);
}

/* I tre livelli si distinguono per rientro e per peso, non per colore: verde e rosso qui non
   significano niente, e prenderebbero il posto di ciò che invece significano nell'area di lavoro.

   **Il rientro viene dall'annidamento e non più da un margine per classe.** Da quando i rami si
   comprimono, un ramo è un elemento vero e i suoi figli stanno dentro di lui: due fonti di rientro —
   la struttura e il margine — divergerebbero al primo livello aggiunto. */
.tree__item--line    { font-weight: 600; }
.tree__item--recipe  { font-size: var(--text-secondary-size); }

/* Un ramo: la sua riga, e i figli che si possono chiudere. */
.tree__node {
    display: flex;
    align-items: center;
    gap: 2px;
}

.tree__node > .tree__item { flex: 1; min-width: 0; }

.tree__children {
    display: flex;
    flex-direction: column;
    gap: 2px;
    /* Allineato al nome del padre, non al suo chevron: è il nome che l'occhio segue scendendo. */
    margin-left: 13px;
    padding-left: 9px;
    border-left: 1px solid var(--border);
}

.tree__branch[data-closed] > .tree__children { display: none; }

/* Le ricette non hanno figli, quindi non hanno chevron — ma il **segnaposto** ce l'hanno, come ogni
   altra riga. Fino al 28/08 non l'avevano, con la motivazione che «lo spazio glielo rende il rientro
   del loro contenitore»: i conti non tornavano, perché il rientro vale 22 px e il chevron 20, e
   l'icona della ricetta finiva sotto quella dell'oggetto. Si leggeva come una sorella. */
.tree__twist {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 20px;
    height: 20px;
    padding: 0;
    border: 0;
    border-radius: var(--radius-btn);
    background: none;
    color: var(--text-secondary);
    cursor: pointer;
}

.tree__twist > svg { width: 13px; height: 13px; }

.tree__twist:hover { background: var(--chip-bg); color: var(--text-primary); }

/* Aperto il chevron punta in giù, chiuso a destra. La rotazione è sull'aperto e non sul chiuso
   perché il disegno nasce a destra: così un browser che non applicasse la trasformazione mostrerebbe
   comunque la freccia nel verso giusto per un ramo chiuso, che è il caso in cui serve saperlo. */
.tree__twist[aria-expanded="true"] > svg { transform: rotate(90deg); }

@media (prefers-reduced-motion: no-preference) {
    .tree__twist > svg { transition: transform 120ms ease; }
}

/* Un ramo che non ha niente sotto non ha comando: tiene solo il posto, perché i nomi restino
   incolonnati. Non è un comando spento — non c'è nessun comando da spegnere. */
.tree__twist--none { cursor: default; }

.tree__item > svg {
    width: var(--icon-size);
    height: var(--icon-size);
    flex: none;
    color: var(--text-secondary);
}

/* Il nome è l'unica cosa che può eccedere: si taglia lui invece di allargare la colonna. */
.tree__name {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Il lucchetto nell'albero è la sola icona a destra, e non porta etichetta: lo spazio non c'è, e
   l'etichetta «Bloccata» sta in testa alla scheda, dove serve davvero. */
.tree__lock {
    flex: none;
    color: var(--text-muted);
}

.tree__empty {
    padding: 10px;
    color: var(--text-secondary);
    font-size: var(--text-secondary-size);
}

/* ─── Il pannello dei filtri ──────────────────────────────────────────────────────────────────
   Un pannello a scomparsa sul lato destro, che contiene tutti i filtri di un elenco. È un
   `<details>` come il menu utente: si apre e si chiude senza una riga di JavaScript, e resta aperto
   mentre si affina la ricerca. Nasce qui per il Registro e serve identico all'Archivio (03 §A.10),
   che lo troverà già fatto. */
.drawer {
    position: relative;
}

/* Il comando chiuso porta il numero di filtri attivi: senza, ci si ritrova con una lista ristretta
   senza sapere perché. */
.drawer > summary {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    min-height: var(--tap-target);
    padding: 0 16px;
    border: 1px solid var(--input-border);
    border-radius: var(--radius-btn);
    background: var(--card-bg);
    font-weight: 600;
    cursor: pointer;
    list-style: none;
}
.drawer > summary::-webkit-details-marker { display: none; }

.drawer__count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 22px;
    height: 22px;
    padding: 0 6px;
    border-radius: var(--radius-pill);
    background: var(--chip-bg);
    color: var(--chip-ink);
    font-size: var(--text-label);
}

/* Il pannello copre il bordo destro per tutta l'altezza dell'area di lavoro. Non è un modale: la
   lista resta visibile a sinistra e si aggiorna mentre si compila, che è il punto. */
.drawer__panel {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: 70;
    width: min(380px, 92vw);
    padding: var(--pad-l);
    overflow-y: auto;
    background: var(--card-bg);
    border-left: 1px solid var(--border);
    box-shadow: var(--shadow-raised);
}

.drawer__head {
    display: flex;
    align-items: center;
    gap: var(--tap-gap);
    margin-bottom: var(--gap);
}

.drawer__title {
    margin: 0;
    font-size: var(--text-step);
    font-weight: 700;
}

/* La croce chiude il pannello. Senza, l'unico modo sarebbe premere di nuovo il comando che l'ha
   aperto — e il pannello glielo copre. */
.drawer__close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--tap-target);
    height: var(--tap-target);
    margin-left: auto;
    border: 0;
    border-radius: var(--radius-btn);
    background: none;
    color: var(--text-secondary);
    cursor: pointer;
}
.drawer__close:hover { background: var(--chip-bg); }
.drawer__close > svg { width: var(--icon-size); height: var(--icon-size); }

/* ─── Il pannello di dialogo ──────────────────────────────────────────────────────────────────
   Creare, duplicare, eliminare: gesti brevi che non meritano di lasciare la schermata. Il committente
   ha approvato esplicitamente il meccanismo del pannello in overlay, ed è la forma da conservare.

   È un `<dialog>` nativo, non un riquadro finto: il fondale, la cattura del fuoco, il tasto Esc e la
   pila di sovrapposizione li fa il browser. Quel che aggiungiamo — `js/dialog.js` — è soltanto
   aprirlo e chiuderlo. */

.dialog {
    width: min(420px, calc(100vw - 32px));

    /* **I comandi del piede non vanno a capo, e il dialogo si allarga quanto serve a farli stare**
       (01/10): a 420 px tre comandi si spezzavano a metà — «Tieni in / sospeso», «Avvia lo / stesso».
       `min-content` misura il piede con i comandi interi, e il testo del corpo si adatta. */
    min-width: min-content;
    max-width: calc(100vw - 32px);
    padding: 0;
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    background: var(--card-bg);
    color: var(--text-primary);
    box-shadow: var(--shadow-raised);

    /* **Il comando di conferma non deve mai richiedere di scorrere per essere trovato**, che è la
       regola del *03* §A.13 n° 1 applicata al dialogo. Un pannello con molti campi — quello di una
       chiave ne ha nove — cresceva oltre lo schermo e portava «Conferma» fuori vista: restava
       raggiungibile scorrendo, e sembrava che non ci fosse. Il pannello si ferma all'altezza della
       finestra, il **corpo** scorre, testa e piede stanno fermi. */
    max-height: calc(100vh - 2 * var(--pad-m));
}

/* **Il pannello di una chiave è largo, e i suoi campi vanno a coppie.** Il committente l'ha visto
   «troppo stretto e verticale» l'8/09: a 420 px cinque campi diventano una colonna alta, e per
   arrivare in fondo si scorre. Non è una misura scelta a occhio — è quella che fa stare due campi
   affiancati con l'etichetta intera sopra ciascuno.

   Resta `min()` con il margine della finestra: su uno schermo stretto torna largo quanto c'è, che è
   la stessa regola del pannello normale. */
.dialog--wide {
    width: min(680px, calc(100vw - 32px));
}

/* Due campi affiancati invece dei tre di `.field-row`. Stessa griglia, stesso comportamento sotto i
   420 px — si impilano — e nessuna regola nuova sul contenuto. */
.field-row--pair {
    grid-template-columns: repeat(2, 1fr);
}

@media (max-width: 560px) {
    .field-row--pair { grid-template-columns: 1fr; }
}

/* Una spunta **dentro** un campo con la sua etichetta sopra, invece che accanto come in
   `.field--inline`. Serve dove la spunta sta in una coppia insieme a una tendina: allineare
   un'etichetta di campo a una spunta senza etichetta lascia la seconda colonna che galleggia. */
.field__check {
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: 2.25rem;
}

.field__check input[type="checkbox"] {
    flex: none;
}

/* `flex` solo da aperto: un `<dialog>` chiuso è `display: none`, e dichiararlo qui lo renderebbe
   visibile sempre — con nove pannelli impilati sotto la tabella. */
.dialog[open] {
    display: flex;
    flex-direction: column;
}

.dialog::backdrop {
    background: rgb(0 0 0 / .38);
}

.dialog__head {
    flex: none;
    display: flex;
    align-items: center;
    gap: var(--gap);
    padding: var(--pad-m) var(--pad-m) 0;
}

.dialog__title { flex: 1; margin: 0; }

.dialog__body {
    display: grid;
    gap: var(--gap);
    padding: var(--pad-m);

    /* Scorre il corpo, non il pannello: `min-height: 0` perché un elemento di flex non si stringe
       sotto il proprio contenuto se non glielo si dice, e senza quello l'`overflow` non morde. */
    min-height: 0;
    overflow-y: auto;
}

/* Un elenco dentro il corpo: la distanza fra i blocchi la dà già il corpo, e i margini dell'elenco la
   raddoppiavano. */
.dialog__list { margin: 0; padding-left: 20px; }

.dialog__body > p:has(+ .dialog__list) { margin-bottom: 0; }

.dialog__list + p { margin-top: 0; }

.dialog__foot {
    flex: none;
    display: flex;
    white-space: nowrap;
    align-items: center;
    justify-content: flex-end;
    gap: var(--tap-gap);
    padding: 0 var(--pad-m) var(--pad-m);
}

/* Il modulo dentro al dialogo non è una colonna stretta dentro una colonna stretta. */
.dialog .form-column { max-width: none; }


/* ─── Paginazione ─────────────────────────────────────────────────────────────────────────────
   Precedente, successiva e la posizione. Non si numerano le pagine: su un registro che cresce
   sarebbero centinaia di collegamenti, e nessuno salta alla pagina quarantatré — si restringe. */
.pager {
    display: flex;
    align-items: center;
    gap: var(--tap-gap);
    margin-top: var(--gap);
}
.pager__position {
    color: var(--text-secondary);
    font-size: var(--text-secondary-size);
}

/* Un comando disabilitato non si preme e si vede che non si preme. Vale la stessa forma della barra
   laterale: opacità e nessun puntatore, mai la scomparsa — un comando che sparisce fa cercare
   qualcosa che non c'è più. */
.btn[aria-disabled="true"] {
    opacity: 0.5;
    pointer-events: none;
}

/* ─── Tabelle larghe ──────────────────────────────────────────────────────────────────────────
   Una tabella con sette campi numerici non sta su un monitor da banco. Scorre da sé invece di
   stringere le colonne: un numero tagliato a metà è peggio di uno da raggiungere scorrendo. */
/* Il punto interrogativo accanto a un'intestazione di colonna: la spiegazione sta nel suggerimento.

   Sostituisce la legenda sotto la tabella, che occupava sempre spazio per un testo che si legge una
   volta o due e poi si ricorda — e che con termine a sinistra e prosa a destra si impastava. Il
   colore è quello secondario e non l'inchiostro pieno: è aiuto, non contenuto della tabella. */
.help {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 15px;
    height: 15px;
    margin-left: 4px;
    color: var(--text-muted);
    cursor: help;
    vertical-align: middle;
}

.help > svg { width: 100%; height: 100%; }

.help:hover,
.help:focus-visible { color: var(--text-primary); }

/* Il promemoria in fondo a un riquadro: una riga sola, che si legge sempre e non si chiude.

   Non è `.card__note`, che sta **sopra** e spiega la tabella prima di leggerla; questo sta **sotto**
   e ricorda una cosa che riguarda un'altra schermata — le misure esterne si chiedono per prime nella
   Misura. Chi prepara le ricette quasi mai è chi poi misura, e l'ordine della pagina suggerisce il
   contrario di quel che succede al banco. */
.card__foot {
    margin: var(--gap) 0 0;
    color: var(--text-secondary);
    font-size: var(--text-secondary-size);
}

.table-scroll {
    overflow-x: auto;
}
.table-scroll .table {
    min-width: 680px;
}

/* I campi si stringono a ciò che devono contenere: sette colonne di numeri su un monitor da banco
   non perdonano lo spazio sprecato.

   **Le frecce di incremento c'erano e le avevamo tolte**, per guadagnare una quindicina di pixel a
   campo con la scusa che «un limite si batte, non si raggiunge a passi». Il committente le ha
   chieste indietro (*09* §6.1): al banco si corregge un limite di un centesimo molto più spesso di
   quanto lo si riscriva, e con le mani sporche una freccia è più facile di una cifra. Il passo lo dà
   `ValueDisplay.StepOf`, cioè l'ultima cifra mostrata per quella grandezza.

   I pixel si ripagano allargando il campo: la tabella scorre già dentro il suo riquadro. */
.table-scroll input[type="number"] {
    width: 9ch;
    padding: 0 4px 0 8px;
    text-align: right;
}
.table-scroll .table th,
.table-scroll .table td {
    padding-right: 4px;
}

/* Una scelta a tendina dentro una tabella prende la larghezza della colonna, e la colonna si stringe
   a favore di quella dei nomi: «a ogni controllo» usciva come «a ogni contr». Si dichiara quanto le
   serve — la voce più lunga fra quelle che ospita — e sono le altre colonne a cedere.

   Attenzione a come si verifica: una `select` troppo stretta **non** produce overflow, il browser
   tronca l'etichetta con i puntini. Confrontare scrollWidth e clientWidth dà zero anche quando il
   testo è tagliato: bisogna guardarla. */
.table-scroll select {
    min-width: 17ch;
}

/* E un campo di testo si dichiara allo stesso modo, per una ragione che alla tendina non capita: la
   sua larghezza intrinseca è **zero**. In una colonna `tight` — che vale `width: 1%` — il `width:
   100%` dei campi lo fa collassare sullo spazio del padding, e nel Valore di una chiave ci stavano
   tre caratteri. La tendina accanto si vedeva intera, ed è la ragione per cui il difetto sembrava di
   quella cella sola: una `select` la sua larghezza ce l'ha, e regge la colonna da sé. */
.table-scroll input[type="text"] {
    min-width: 14ch;
}

/* La maniglia di trascinamento, e ciò che il trascinamento fa vedere.

   Ha preso il posto delle due frecce di ordinamento il 24/08: il 03 riga 408 chiede il trascinamento,
   e le frecce erano state messe per aggirare un problema — riordinare senza salvare perdeva i numeri
   appena battuti — che si risolve invece facendo arrivare l'ordine insieme al «Salva».

   È un `<button>` vero e non un'icona decorativa, perché la tastiera ci deve arrivare: Alt e le
   frecce fanno la stessa cosa. */
.grip-cell {
    width: 1%;
    padding-right: 0;
}

.grip {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 28px;
    padding: 0;
    border: 1px solid transparent;
    border-radius: var(--radius-btn);
    background: none;
    color: var(--text-faint);
    cursor: grab;
}

.grip:hover:not(:disabled) { background: var(--chip-bg); }

.grip:active:not(:disabled) { cursor: grabbing; }

/* Una maniglia che non si può prendere resta al suo posto e dice perché, ma non deve somigliare a
   una viva: senza questa regola le due erano indistinguibili, e una tabella di sole righe non
   spostabili sembrava spostabile per intero. Il motivo è sul `title`. */
.grip:disabled {
    opacity: 0.35;
    cursor: default;
}

.grip > svg {
    width: 12px;
    height: 16px;
}

/* La riga in movimento resta visibile ma si stacca dal foglio: sparendo si perderebbe il segno di
   dove la si sta portando. */
.is-dragging {
    opacity: .45;
    background: var(--chip-bg);
}

.is-dragging td { border-color: transparent; }

/* Il posto dell'icona del parametro, finché i dodici profili non saranno disegnati.
   
   È **spazio riservato e basta**, senza bordo: con il riquadro tratteggiato che aveva prima si
   leggeva come una casella di spunta vuota, e nella scheda della Ricetta — dove le caselle di spunta
   ci sono davvero, e accendono la misura — chi guardava le vedeva tutte spente. Un
   segnaposto che comunica qualcosa a chi sviluppa e una cosa falsa a chi lavora è peggio del
   niente. */
.param-icon {
    display: inline-block;
    flex: none;
    width: var(--icon-size);
    height: var(--icon-size);
}


/* ═══ LA CORNICE DI UNA PROCEDURA (03 §A.4) ═══════════════════════════════════════════════════
   Non è della sola Calibrazione: le posizioni non cambiano mai da un passo all'altro, e la Misura
   riuserà queste stesse regole. Porta `register-bench`, quindi ciò che sta dentro cresce da sé —
   corpi, righe, bersagli — senza che una singola misura vada ripetuta qui. */

.procedure {
    position: fixed;
    inset: 0;
    z-index: 60;
    display: flex;
    padding: 28px;

    /* Il fondo scurito **deve intercettare i clic** perché non raggiungano l'applicazione
       sottostante: senza, si preme un comando della Home credendo di premerne uno della procedura. */
    background: rgb(0 0 0 / 45%);
}

/* Il pannello copre quasi tutto lasciando un margine, e **non si chiude cliccando fuori**: chiuderlo
   per sbaglio a metà di una procedura fa ricominciare da capo chi aveva le mani sporche. */
.procedure__panel {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
    overflow: hidden;
    border-radius: var(--radius-panel);
    background: var(--card-bg);
    box-shadow: var(--shadow-raised);
}

/* `position` perché la fascia degli esiti vi sta sospesa sopra, come nella barra superiore. */
.procedure__head {
    position: relative;
    display: flex;
    align-items: flex-start;
    gap: 28px;
    padding: 14px 20px;
    background: var(--brand-surface-raised);
    color: var(--brand-ink);
}

/* Titolo e croce restano in alto: con una ricetta lunga le briciole occupano più righe, e allineare
   al centro farebbe scendere il titolo insieme a loro. */
.procedure__head > .steps { align-self: center; }

.procedure__what { flex: 0 0 auto; }
.procedure__title { margin: 0; font-size: 20px; font-weight: 700; }
.procedure__where { margin: 2px 0 0; font-size: var(--text-secondary-size); color: var(--brand-ink-muted); }

.procedure__close {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: var(--tap-target);
    min-height: var(--tap-target);
    border: 0;
    border-radius: 50%;
    background: rgb(255 255 255 / 12%);
    color: var(--brand-ink);
    font-size: 18px;
    cursor: pointer;
}

.procedure__close:hover { background: rgb(255 255 255 / 22%); }

/* Il contenuto scorre; la barra dei comandi no. */
.procedure__body {
    flex: 1;
    min-height: 0;
    overflow: auto;
    padding: 20px;
}

/* «Indietro» e «Avanti» stanno sempre in fondo e **non si raggiungono mai con uno scroll**. Muovono
   fra i passi, mai fra le celle. */
.procedure__foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--tap-gap);
    padding: 14px 20px;
    border-top: 1px solid var(--border-soft);
    background: var(--card-bg);
}


/* ─── La scelta della ricetta, primo passo di «Misura su un'altra ricetta» ────────────────────
   Una riga per ricetta, con sotto il ramo da cui viene: le versioni di un oggetto si chiamano tutte
   allo stesso modo meno il numero, e il nome da solo non basta a riconoscerle.

   Righe e non pastiglie: la scelta è di una sola, e la riga larga è il bersaglio grande che serve a
   chi la tocca con il dito. */

/* ─── Il selettore della ricetta ──────────────────────────────────────────────────────────────
   Tre colonne a cascata più una di informazioni, com'è nel disegno e come il committente ha ridetto
   (S.41). Era un elenco piatto di tutte le ricette dell'installazione: in un impianto vero sono
   decine, e la gerarchia con cui chi lavora pensa al prodotto stava in una riga piccola sotto ogni
   nome invece che nella forma della schermata.

   **La quarta colonna è più larga delle altre**: le tre elencano nomi, la quarta ha da dire teste,
   campioni, tagli, modalità, misure e chiavi. A colonne uguali le prime sprecavano spazio e l'ultima
   andava a capo su ogni riga. */
.picker {
    display: grid;
    grid-template-columns: repeat(3, minmax(140px, 200px)) minmax(260px, 380px);
    gap: var(--gap);
    align-items: start;
    justify-content: start;
}

.picker__column {
    display: flex;
    flex-direction: column;
    gap: 8px;
    min-width: 0;
}

/* Le colonne scorrono ognuna per conto suo: sei linee e quaranta ricette non devono allungare il
   pannello, che è fisso, né far scorrere la procedura intera. */
.picker__list {
    display: flex;
    flex-direction: column;
    gap: var(--tap-gap);
    max-height: 44vh;
    overflow-y: auto;
    margin: 0;
    padding: 0;
    list-style: none;
}

.picker__title {
    margin: 0;
    color: var(--text-muted);
    font-size: var(--text-label);
    font-weight: 700;
    letter-spacing: var(--text-label-spacing);
    text-transform: var(--text-label-transform);
}

.picker__empty {
    margin: 0;
    color: var(--text-faint);
    font-size: var(--text-secondary-size);
}

.picker__item {
    display: block;
    width: 100%;
    min-height: var(--tap-target);
    padding: 10px 14px;
    border: 1px solid var(--input-border);
    border-radius: var(--radius-btn);
    background: var(--card-bg);
    color: var(--text-primary);
    font: inherit;
    font-weight: 600;
    text-align: left;
    cursor: pointer;
}

.picker__item:hover { border-color: var(--text-secondary); }

/* La voce su cui si è fermi: **il filetto giallo del «sei qui»**, lo stesso della barra laterale e
   del nodo aperto dell'albero. Non il verde pieno, che spetta all'azione principale — e qui l'azione
   principale è «Avvia la misura», in fondo. */
.picker__item--here {
    border-color: var(--step-accent);
    box-shadow: inset 3px 0 0 var(--step-accent);
}

/* La scheda della ricetta scelta. */
.brief { display: flex; flex-direction: column; gap: 10px; }

.brief__name { font-size: 17px; font-weight: 800; }

.brief__where { margin: -8px 0 0; color: var(--text-muted); font-size: var(--text-secondary-size); }

.brief__facts {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 6px;
    margin: 0;
}

/* Gli stessi fatti in una scheda larga: in fila, ciascuno largo quanto il suo valore. Le tre colonne della
   scheda della ricetta, stese su una scheda da mille pixel, li allontanavano fino a non leggerli come un
   gruppo — è la forma della pagina finta dell'importazione (07 §74). */
.brief__facts--inline { display: flex; flex-wrap: wrap; gap: 6px 28px; }

.brief__facts dt { color: var(--text-muted); font-size: var(--text-label); }
.brief__facts dd {
    margin: 0;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
}

.brief__count { margin: 0; color: var(--text-secondary); font-size: var(--text-secondary-size); }

.brief__group { display: flex; flex-direction: column; gap: 4px; }

.brief__label {
    margin: 0;
    color: var(--text-muted);
    font-size: var(--text-label);
    font-weight: 700;
}

.brief__note { margin: 0; color: var(--text-secondary); font-size: var(--text-secondary-size); }

.brief__list {
    margin: 0;
    padding-left: 18px;
    color: var(--text-primary);
    font-size: var(--text-secondary-size);
}


/* ─── Il passo delle chiavi ───────────────────────────────────────────────────────────────────
   **Campi larghi al più 420 px, ma disposti a riempire il pannello.** Il disegno li mette in una
   colonna sola, e con le tre chiavi che mostra ci sta; una ricetta nasce però raccogliendo le otto
   di sistema, e otto campi in colonna misurano mille pixel in un corpo che ne ha settecento — il
   blocco di riferimento finiva sotto il bordo e per vederlo si doveva scorrere. Al banco si scorre
   con le mani sporche, mentre lo spazio a destra c'era tutto. È la stessa ragione per cui
   `.field-grid` esiste.

   I campi sono **più alti del solito**: qui si compila in piedi, con il dito, e l'altezza da
   scrivania — che vale per chi configura una ricetta seduto — è il bersaglio sbagliato. */

.keys__intro { margin-bottom: 20px; }

.keys__hint {
    margin: 4px 0 0;
    color: var(--text-muted);
    font-size: var(--text-secondary-size);
}

.keys__fields {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 420px));
    gap: 4px var(--gap);
    align-items: start;
}

/* Il testo lungo prende due celle dove ci stanno: un'annotazione in una colonna da 260 px si scrive
   guardando tre parole per riga. */
.keys__field--long { grid-column: span 2; }

.keys__field input,
.keys__field select,
.keys__field textarea { width: 100%; }

.keys__field input:not([type="checkbox"]),
.keys__field select { min-height: 44px; }

/* L'unità sta **accanto** al numero e non dentro l'etichetta: si legge nel momento in cui si batte,
   che è quando serve sapere se sono millimetri o pollici. */
.keys__number { display: flex; align-items: center; gap: var(--tap-gap); }

.keys__number input { flex: 1; min-width: 0; }

.keys__unit {
    flex: 0 0 auto;
    color: var(--text-muted);
    font-size: var(--text-secondary-size);
}

/* Ciò che si legge soltanto, separato da un filetto da ciò che si compila: la differenza fra i due
   blocchi dev'essere visibile prima di leggere le parole. */
.keys__reference {
    margin-top: 24px;
    padding-top: 16px;
    border-top: 1px solid var(--border-soft);
}

.keys__reference-title {
    margin: 0;
    font-size: var(--text-secondary-size);
    color: var(--text-muted);
}

/* Il valore che viene dalla ricetta: **la forma di un campo, senza esserlo**. Sta nella stessa
   griglia e alla stessa altezza dei campi che si compilano, così le due metà del passo si leggono
   come una cosa sola (S.25); il fondo spento e l'assenza di contorno dicono che non si tocca, senza
   fingere un controllo disabilitato che qualcuno proverebbe ad accendere. */
.keys__given {
    display: flex;
    align-items: center;
    min-height: var(--tap-target);
    padding: 8px 12px;
    border-radius: var(--radius-btn);
    background: var(--disabled-bg);
    color: var(--text-primary);
    font-weight: 600;
}

/* Perché «Avanti» è spento, **scritto** e non appeso al passaggio del mouse: su uno schermo tattile
   il `title` non compare mai (07 §23.10). Sta accanto al comando che spiega, non altrove: un motivo
   lontano dal comando è un motivo che si legge dopo averlo premuto invano. */
.procedure__forward {
    display: flex;
    align-items: center;
    gap: var(--tap-gap);
}

.procedure__missing {
    margin: 0;
    color: var(--text-muted);
    font-size: var(--text-secondary-size);
}


/* ─── Il passo di una misura esterna ──────────────────────────────────────────────────────────
   **Il layout non è qui**: questo passo usa la griglia del palco — `.stage__work`,
   `.stage__values`, `.stage__history` — come ogni altro passo di ogni procedura. Fino al 14/09 ne
   aveva una sua, `.external__*`, che ricopiava i numeri dell'altra con il commento «larga quanto
   quella»: due dichiarazioni che dicono di somigliarsi invece di essere la stessa, e infatti il
   tondo blu era finito in tre posizioni diverse su cinque schermate (*07* §46).

   Qui restano solo le cose che sono **di questo passo** e non della cornice. */

.external__head { display: flex; align-items: center; gap: var(--gap); }

/* Il disegno della grandezza, più grande che in tabella: qui è il soggetto del passo, non una
   colonna che aiuta a scorrere l'elenco. */
.param-icon--large { width: calc(var(--param-icon-w) * 1.6); height: calc(var(--param-icon-h) * 1.6); }

.external__unit { color: var(--text-muted); font-weight: 500; }

.external__limits {
    margin: 2px 0 0;
    color: var(--text-muted);
    font-size: var(--text-secondary-size);
    font-variant-numeric: tabular-nums;
}

/* ── La griglia delle unità ──
   Celle larghe uguali che si dispongono da sole: con una 6×2×3 sono trentasei, e devono starci
   senza scorrimento orizzontale. Ogni cella ha due bersagli — la coordinata sceglie, il campo
   riempie — e tutti e due grandi abbastanza per un dito.

   **I due bersagli seguono il registro, e fino al 10/09 non lo facevano**: erano 32 e 44 pixel
   scritti a mano, mentre la procedura gira in `register-bench`, dove un bersaglio è 48. Era la
   causa del rilievo del committente dell'8/09 — *«celle più grandi»* — ed è **il posto dove si
   scrive a mano quando il calibro non c'è**, cioè quello in cui un dito sbaglia di più. Un registro
   si applica per intero o l'elemento stona con ciò che ha attorno (*03* §A.14): cresciuti i due
   bersagli, la larghezza minima cresce con loro, nella stessa proporzione. */

.cells {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    /* Lo spazio dell'anello di selezione, che sta fuori dalla cella: senza, sulla prima riga e sulla
       colonna di bordo lo taglia lo scorrimento. */
    padding: 5px;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(144px, 1fr));
    grid-auto-rows: min-content;
    gap: var(--tap-gap);
}

.cell {
    display: flex;
    flex-direction: column;
    border: 1.5px dashed var(--border-soft);
    border-radius: var(--radius-btn);
    background: transparent;
    overflow: hidden;
}

/* Presa: il tratteggio diventa continuo. È la differenza che si legge da lontano, prima del colore. */
.cell.is--ok,
.cell.is--warning,
.cell.is--error,
.cell.is--implausible,
.cell.is--unjudged    { border-style: solid; background: var(--card-bg); }

.cell.is--ok          { border-color: var(--status-ok-ink); }
.cell.is--warning     { border-color: var(--status-warning-mark); }
.cell.is--error       { border-color: var(--status-error-ink); }
.cell.is--implausible { border-color: var(--status-implausible-ink); }

/* **Senza limiti la cella è presa e non giudicata** (07 §69.2): bordo pieno come le altre prese — senza,
   sembrerebbe una cella ancora da misurare — ma grigio, e nessun segno. */
.cell.is--unjudged    { border-color: var(--status-unjudged-ink); }

/* **Attiva: giallo, che qui come altrove dice soltanto «sei qui»** — non è un terzo stato del
   giudizio, e infatti lascia intatto il colore del bordo che dice com'è andata.

   **Lo stato sta dentro, la selezione sta fuori** (07 §53), ed è la regola che rende vera quella
   frase: fino al 15/09 l'anello era disegnato *sopra* il bordo — `outline-offset: -3px` — e su una
   cella selezionata il colore del giudizio spariva, lasciando il solo segno a dirlo. Due celle con
   lo stesso valore in attenzione si mostravano una ambra e una gialla.

   Il passo è 8 px, quindi i 5 che l'anello occupa fuori ne lasciano 3 alla vicina; il contenitore
   ha il suo margine perché la prima riga non se lo faccia tagliare dallo scorrimento. */
.cell--here {
    /* Sopra le vicine: dipinte dopo, coprirebbero il suo rilievo. */
    position: relative;
    z-index: 1;
    outline: 3px solid var(--step-accent);
    outline-offset: 2px;
    box-shadow: var(--selection-relief);
}

.cell__pick {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 4px;
    min-height: var(--tap-target);
    padding: 8px 10px 0;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    cursor: pointer;
}

.cell__coord {
    font-size: var(--text-label);
    font-weight: 700;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}

.cell__mark { font-size: var(--text-secondary-size); font-weight: 700; }

/* **Il segno di stato, disegnato.** Prende la misura del testo che ha attorno — `1em` — così sta
   nelle celle, nelle briciole e nelle cronologie senza una regola per posto, e `currentColor` gli dà
   il colore dello stato che il contenitore ha già. Erano caratteri, e li disegnava il sistema
   operativo (*07* §48.2). */
.mark-icon {
    width: 1em;
    height: 1em;
    vertical-align: -0.14em;
}

.cell.is--ok          .cell__mark { color: var(--status-ok-ink); }
.cell.is--warning     .cell__mark { color: var(--status-warning-mark); }
.cell.is--error       .cell__mark { color: var(--status-error-ink); }
.cell.is--implausible .cell__mark { color: var(--status-implausible-ink); }

/* Il campo non ha bordo suo: il bordo è quello della cella, e due riquadri annidati farebbero
   sembrare la cella un contenitore invece che il bersaglio. */
.cell__value {
    width: 100%;
    min-height: var(--tap-target);
    padding: 0 10px 8px;
    border: 0;
    background: none;
    color: var(--text-primary);
    font-size: 20px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.cell__value:focus { outline: 2px solid var(--step-accent); outline-offset: -2px; }

/* ── Andamento e statistiche ── */

.trend {
    flex: 0 0 auto;
    padding: 16px;
    border-radius: var(--radius-panel);
    background: var(--border-softer);
}

.trend__title { margin: 0 0 12px; font-size: var(--text-secondary-size); font-weight: 700; }

/* Il grafico è fatto di barre alte una percentuale: nessuna libreria, e le linee dei limiti sono
   due righe assolute sopra di esse. */
/* **Lo scorrimento sta qui, e la fascia delle etichette pure.** Il `padding-bottom` è lo spazio in
   cui le sigle `1.1.1` vivono senza far parte del disegno: essendo dentro il box, l'`overflow` non
   le taglia. */
.chart {
    position: relative;

    /* Il pixel in cima **non è un margine estetico**: la linea del limite superiore sta a
       `bottom: 100%`, cioè appoggiata al bordo di sopra del disegno, e sborda dell'unico pixel della
       propria altezza. Senza questa riga lo scorrimento qui sotto la taglia e **il limite superiore
       sparisce** — visto al secondo scatto, dopo averlo introdotto io con l'`overflow`. */
    padding: 1px 4px 24px;

    /* Lo scorrimento sta qui e non sulle barre: le etichette vivono nel `padding-bottom`, che
       appartiene a questo box, e da lì nessun overflow le taglia. */
    overflow-x: auto;
}

/* **Il rettangolo del disegno, e ce n'è uno solo.** Linee e colonne si misurano entrambe da questo
   fondo — le prime con `bottom: %`, le seconde con `height: %` — e coincidono per costruzione invece
   che per attenzione.

   Fino al 15/09 non esisteva: le linee si ancoravano a `.chart` e le colonne partivano da sopra la
   propria etichetta, che nella colonnina verticale faceva da piedistallo. Ventiquattro pixel su
   novantasei, sistematici, e il grafico mostrava una lettura **dentro tolleranza sopra la soglia di
   attenzione** — è il rilievo S.53, che sembrava una questione di colore (*07* §49).

   `min-width: 100%` perché le linee coprano tutta la larghezza anche quando le colonne scorrono: a
   trentasei letture il contenuto è più largo del riquadro, e una linea lunga quanto la sola parte
   visibile si staccherebbe scorrendo. */
.chart__plot {
    position: relative;
    height: 96px;
    min-width: 100%;
}

.chart__bars {
    display: flex;
    align-items: flex-end;
    gap: var(--tap-gap);
    height: 100%;
}

/* **L'altezza piena non è decorazione**: `height: %` su una barra dentro un contenitore alto quanto
   il suo contenuto si risolve a zero, e il grafico si vedeva come una fila di trattini in fondo. */
/* **La larghezza minima la detta l'etichetta, che non è più lei a darla.** Finché stava nel flusso
   allargava la colonna da sé; tolta di lì, tre sigle da `1.1.1` su colonne da 18 px si accavallavano
   in `1.1.12.1.13.1.1` — visto al primo scatto. `3.4em` tiene il caso peggiore reale, che è la terna
   a una cifra per lato di una 6×2×3; con teste o tagli a due cifre le sigle si avvicinano, e allora
   la strada è stringere il passo, non allargare all'infinito. */
.chart__bar {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-end;
    flex: 0 0 auto;
    min-width: 3.4em;
    height: 100%;
}

.chart__stem { width: 18px; min-height: 2px; border-radius: 5px 5px 0 0; background: var(--text-faint); }

.chart__stem.is--ok          { background: var(--status-ok-ink); }
.chart__stem.is--warning     { background: var(--status-warning-mark); }
.chart__stem.is--error       { background: var(--status-error-ink); }
.chart__stem.is--implausible { background: var(--status-implausible-ink); }

/* **Fuori dal flusso della colonna**, o torna a farle da piedistallo. Sta nella fascia che `.chart`
   si tiene sotto il disegno, centrata sulla propria barra. */
.chart__key {
    position: absolute;
    top: calc(100% + 4px);
    left: 50%;
    transform: translateX(-50%);
    font-size: var(--text-label);
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.chart__limit,
.chart__warning,
.chart__target {
    position: absolute;
    left: 0;
    right: 0;
    height: 1px;
}

.chart__limit  { background: var(--status-error-ink); opacity: 0.55; }
.chart__target { background: var(--text-faint); }

/* Le soglie di attenzione, che nel grafico non c'erano: si vedevano le due linee rosse dei limiti e
   nient'altro, e una lettura in attenzione appariva dentro la zona buona (rilievo del committente del
   05/09). Tratteggiate e non piene, perché non sono un confine ma un avviso: superarle non scarta il
   pezzo, dice di guardarlo. */
.chart__warning {
    background: repeating-linear-gradient(
        to right,
        var(--status-warning-mark) 0 6px,
        transparent 6px 12px);
    opacity: 0.75;
}

.chart__empty { margin: 0; color: var(--text-muted); font-size: var(--text-secondary-size); }

.stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
    gap: 10px;
    margin: 14px 0 0;
}

.stats__item dt { color: var(--text-muted); font-size: var(--text-label); font-weight: 600; }

.stats__item dd {
    margin: 2px 0 0;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

/* La barra dell'intervallo: dove cade l'ultima lettura fra i due limiti. */
.interval { margin-top: 14px; }

/* **La lettura si legge sopra la sua barra**, e ha preso il posto della frase «ultima lettura fra X
   e Y»: quella diceva a parole ciò che la linea disegna, e i due numeri che citava adesso stanno agli
   estremi, dove sono le posizioni che descrivono (rilievo del committente del 05/09). */
.interval__reading {
    margin-bottom: 6px;
    font-size: 17px;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
}

.interval__reading.is--ok          { color: var(--status-ok-ink); }
.interval__reading.is--warning     { color: var(--status-warning-ink); }
.interval__reading.is--error       { color: var(--status-error-ink); }
.interval__reading.is--implausible { color: var(--status-implausible-ink); }

.interval__ends {
    display: flex;
    justify-content: space-between;
    margin-top: 4px;
    color: var(--text-muted);
    font-size: var(--text-label);
    font-variant-numeric: tabular-nums;
}

.interval__track {
    position: relative;
    height: 6px;
    border-radius: 999px;
    background: var(--border-soft);
}

.interval__target {
    position: absolute;
    top: -2px;
    width: 1px;
    height: 10px;
    background: var(--text-faint);
}

/* Le soglie di attenzione sulla linea: tacche gialle, più corte dei limiti che sono gli estremi
   della barra stessa. Prima non c'erano, e una lettura in attenzione stava in mezzo al grigio. */
.interval__warning {
    position: absolute;
    top: -2px;
    width: 2px;
    height: 10px;
    border-radius: 1px;
    background: var(--status-warning-mark);
}

/* **I limiti di errore, agli estremi** (S.51). Stessa forma delle tacche di attenzione e colore
   diverso, perché dicono la stessa categoria di cosa: da qui in là si è fuori. Stanno **dentro** la
   linea e non a cavallo del suo bordo — a metà fuori si leggevano come una sbavatura del tracciato,
   e agli estremi arrotondati non seguivano la curva. */
.interval__limit {
    position: absolute;
    top: -2px;
    width: 2px;
    height: 10px;
    border-radius: 1px;
    background: var(--status-error-ink);
}

.interval__limit--low  { left: 0; }
.interval__limit--high { right: 0; }

/* Un triangolo, non un tondo: la forma dice «qui» anche a chi il colore non lo distingue.
   **Più grande di prima**: a sei pixel spariva fra le tacche, e il committente ha chiesto un
   marcatore visibile. */
.interval__mark {
    position: absolute;
    top: -7px;
    transform: translateX(-50%);
    width: 0;
    height: 0;
    border-left: 7px solid transparent;
    border-right: 7px solid transparent;
    border-top: 10px solid var(--text-faint);
    filter: drop-shadow(0 0 2px var(--card-bg));
}

.interval__mark.is--ok          { border-top-color: var(--status-ok-ink); }
.interval__mark.is--warning     { border-top-color: var(--status-warning-mark); }
.interval__mark.is--error       { border-top-color: var(--status-error-ink); }
.interval__mark.is--implausible { border-top-color: var(--status-implausible-ink); }

/* **Il registro stretto**, per il passo della telecamera (07 §54): la stessa linea con gli stessi
   segni, dentro l'altezza di una riga. Sparisce ciò che la riga dice già — la lettura e i due
   estremi, tolti nel markup e non qui, perché un numero nascosto con il CSS lo legge lo stesso chi
   ascolta la pagina — e resta il solo tracciato, un filo più basso.

   **Il triangolo non rimpicciolisce.** È il portatore dell'informazione, e a sei pixel spariva già
   fra le tacche nel registro largo: stringere la riga non è una ragione per riaprire un difetto
   appena chiuso. Quel che si stringe è lo spazio attorno. */
.interval--compact { margin-top: 6px; grid-column: 1 / -1; }
.interval--compact .interval__track { height: 4px; }

/* ── La cronologia ──
   **Si riempie dall'alto**, e l'ultima resta sempre in vista.

   Le due metà di quella frase si ottengono in due modi diversi, e per un giorno abbiamo avuto solo
   la seconda: `column-reverse` con le schede rese dalla più recente ancorava il fondo per
   costruzione, ma faceva **crescere la colonna verso l'alto** — la prima cattura compariva in basso
   e le successive salivano, che non è come si legge un elenco che si allunga.

   Adesso l'ordine è naturale e a tenere in vista l'ultima è `roll.js`, cioè il comportamento che
   `data-roll` prometteva senza averlo. *La strada ovvia in CSS, `justify-content: flex-end`, resta
   quella sbagliata: con quattordici letture la lista smetteva di scorrere e le prime otto restavano
   802 px sopra il bordo, irraggiungibili anche con la rotella — misurato.* */

.history {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    gap: var(--tap-gap);
    overflow-y: auto;
    /* Come per `.cells`: l'anello della selezione sta fuori dalla scheda e va lasciato passare. */
    padding: 5px;
}

.history__item {
    /* Posizionata perché il segno d'esito ci sta sopra, in un angolo. */
    position: relative;
    flex: 0 0 auto;
    display: grid;
    gap: 2px;
    padding: 10px 12px;
    border: 0;
    border-radius: var(--radius-btn);
    background: var(--card-bg);
    box-shadow: inset 0 0 0 var(--ring, 1px) var(--judgement, var(--border-soft));
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

/* **Il contorno dice com'è andata**, non solo il numero dentro (rilievo del committente del 05/09).
   Al banco la cronologia si guarda di sfuggita e da lontano: una cifra colorata alta diciassette
   pixel non si legge, un bordo sì. */
/* **Il filetto si scrive una volta sola**, nella regola della scheda, e qui si danno solo il colore e lo spessore:
   così la scheda selezionata può **comporre** il suo rilievo con il filetto, invece di cancellarlo con un'ombra
   sola (S.50). Senza giudizio resta il pixel neutro di partenza. */
.history__item.is--ok,
.history__item.is--warning,
.history__item.is--error,
.history__item.is--implausible,
.history__item.is--unjudged    { --ring: 2px; }

.history__item.is--ok          { --judgement: var(--status-ok-ink); }
.history__item.is--warning     { --judgement: var(--status-warning-mark); }
.history__item.is--error       { --judgement: var(--status-error-ink); }
.history__item.is--implausible { --judgement: var(--status-implausible-ink); }
.history__item.is--unjudged    { --judgement: var(--status-unjudged-ink); }

/* **Lo stato sta dentro, la selezione sta fuori**, come nella griglia (07 §53): il filetto del
   giudizio resta il suo `box-shadow` interno, e il «sei qui» è un anello **attorno** alla scheda.
   Fino al 15/09 vinceva sul giudizio e lo cancellava — era scritto qui sopra come una scelta, ma
   costava l'unica cosa che quella scheda dice da lontano.

   È un `outline` e non un sesto `box-shadow` perché la griglia usa quello: due omologhi resi con lo
   stesso mezzo si cambiano in un posto solo. */
.history__item--here {
    z-index: 1;
    outline: 3px solid var(--step-accent);
    outline-offset: 2px;
    box-shadow: inset 0 0 0 var(--ring, 1px) var(--judgement, var(--border-soft)), var(--selection-relief);
}

.history__coord { font-size: var(--text-label); font-weight: 700; color: var(--text-muted); }

.history__value { font-size: 17px; font-weight: 800; font-variant-numeric: tabular-nums; }

.history__value.is--ok          { color: var(--status-ok-ink); }
.history__value.is--warning     { color: var(--status-warning-ink); }
.history__value.is--error       { color: var(--status-error-ink); }
.history__value.is--implausible { color: var(--status-implausible-ink); }

.history__where { font-size: var(--text-label); color: var(--text-muted); }

/* Il segno dell'esito accanto al nome del passo, nelle briciole. */
.steps__mark { margin-left: 6px; font-weight: 700; }

.steps__mark.is--ok          { color: var(--status-ok-ink); }
.steps__mark.is--warning     { color: var(--status-warning-mark); }
.steps__mark.is--error       { color: var(--status-error-ink); }
.steps__mark.is--implausible { color: var(--status-implausible-ink); }

/* **Il conteggio di un passo lasciato a metà** — «2/4». Non è uno stato di tolleranza e non ne
   prende i colori: quelli dicono *com'è andata*, e qui non è ancora andata. Ha le cifre a larghezza
   fissa perché non ballino cambiando da 9 a 10, e si distingue dall'esito **per forma**: due numeri
   con una barra, non un simbolo.

   **L'inchiostro è quello della barra, non della pagina**: la barra dei passi è verde scuro in tutti e
   due i temi, e il grigio del testo secondario è pensato per un fondo chiaro. Nel tema industriale,
   che c'era fino al 09/10, era quasi nero e il «2/8» non si leggeva per niente (visto il 01/10). */
.steps__partial {
    margin-left: 6px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--brand-ink-muted);
}


/* ─── Il passo della telecamera ───────────────────────────────────────────────────────────────
   Il palco è quello della Calibrazione e non si ridisegna: quel che si aggiunge è il conto delle
   sezioni fatte, i punti sull'immagine e le grandezze che se ne leggono. */

.camera { display: flex; flex-direction: column; gap: 12px; height: 100%; min-height: 0; }

.camera__progress { display: flex; align-items: center; gap: var(--gap); }


.camera__count { font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }

.camera__tools {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--tap-gap);
    margin-top: var(--tap-gap);
}

/* Le grandezze lette sull'immagine, incolonnate accanto ad essa. */
/* ── Le letture della telecamera ──
   **Impacchettate, una riga per grandezza**: nome a sinistra, valore a destra, e sotto la barra che
   dice dove cade fra i limiti. Erano riquadri alti centocinquanta pixel in colonna, e con sette
   grandezze quella colonna misurava il doppio del pannello: il tondo blu finiva sotto il bordo dello
   schermo, che è il rilievo del committente.

   La forma viene dall'apparato in esercizio, dove le stesse sette grandezze stanno in un palmo di
   spazio senza diventare illeggibili. Qui non c'è il grafico dell'andamento — nelle misure esterne
   sta in fondo e serve a vedere come va la fila; in questo passo lo spazio è della telecamera. */

.readings { display: grid; gap: 6px; margin: 0; }

.reading {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: baseline;
    gap: 4px 12px;
    padding: 9px 12px;
    border-radius: var(--radius-btn);
    background: var(--card-bg);
    box-shadow: inset 0 0 0 1px var(--border-soft);
}

.reading__label {
    margin: 0;
    color: var(--text-secondary);
    font-size: var(--text-secondary-size);
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.reading__value {
    margin: 0;
    font-size: 19px;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.reading__unit { margin-left: 3px; font-size: var(--text-label); font-weight: 600; }

.reading.is--ok          .reading__value { color: var(--status-ok-ink); }
.reading.is--warning     .reading__value { color: var(--status-warning-ink); }
.reading.is--error       .reading__value { color: var(--status-error-ink); }
.reading.is--implausible .reading__value { color: var(--status-implausible-ink); }

/* *Qui stavano `.reading__bar`, `.reading__span` e `.reading__mark`*: una barretta con un pallino,
   che faceva il mestiere dell'intervallo con un disegno tutto suo. Dal 15/09 la riga ospita il
   componente vero nel suo registro stretto (07 §54), e quelle tre regole non hanno più clienti. */

/* ── I punti sull'immagine ──
   Le misure sono in **pixel del fotogramma**, e gli spessori li scrive `points.js` a ogni ridisegno:
   qui stanno solo i colori e ciò che non dipende dall'ingrandimento. */

/* **`overflow: visible` è ciò che permette a un punto di esistere fuori dal fotogramma.** Un `<svg>`
   di suo ritaglia al proprio `viewBox`, e il viewBox qui **è** il sensore: una maniglia oltre il
   bordo non veniva tagliata, non veniva disegnata — e con l'aggraffatura accostata al bordo quella
   correzione non si poteva più fare. A ritagliare resta `.stage__frame`, che è il riquadro vero
   (*07* §47). */
.points {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    overflow: visible;
}

/* ─── Le quote sulla sezione ──────────────────────────────────────────────────────────────────
   Una quota è fatta come su un disegno tecnico: due linee di richiamo che escono dai punti, una
   linea fra loro con le tacche agli estremi, e in mezzo il nome della grandezza con quanto misura.
   È la forma con cui l'apparato in esercizio le mostra, ed è quella che chi misura riconosce.

   **Le quote escono dalla sezione invece di posarcisi sopra**: una scritta sul metallo copre proprio
   il bordo che si sta accostando, e i richiami servono a portarla via tenendo il legame con il punto.

   Il colore lo porta il disegno della grandezza (`params.css`); qui c'è solo come si comporta. */

.point__quote { opacity: 0.95; }

/* Il richiamo è più tenue della quota: dice «vengo di là», non è ciò che si legge. */
.quote__lead { opacity: 0.5; }
.quote__span,
.quote__tick { opacity: 0.95; }

/* La piastrina che tiene la scritta staccata dal metallo. Scura e non opaca del tutto: sotto c'è la
   fotografia, e coprirla del tutto vorrebbe dire nascondere ciò che si sta guardando.

   **È anche la presa dell'etichetta** (S.36 del 05/09): si trascina per spostarla dove non dà
   fastidio. Si afferra questa e non il testo — è più grande, e il testo si deve poter leggere mentre
   lo si sposta. */
.quote__plate {
    fill: rgb(12 14 16 / 82%);
    cursor: grab;
}

.quote__plate--held { cursor: grabbing; fill: rgb(12 14 16 / 92%); }

.quote__label {
    fill: #fff;
    font-family: inherit;
    font-weight: 700;
    paint-order: stroke;
    pointer-events: none;
}

/* La maniglia è **vuota, con un crocino**: piena coprirebbe il bordo che si sta accostando, ed è
   sul bordo che si misura. L'alone scuro la tiene leggibile anche dove la sezione è chiara. */
.point__halo { fill: none; stroke: rgb(0 0 0 / 55%); }
.point__grip { fill: none; }
.point__cross { opacity: 0.9; }

/* Grande da prendere, invisibile da guardare: al banco si lavora con i guanti. */
.point__target { fill: transparent; cursor: grab; touch-action: none; }
.point__target--held { cursor: grabbing; }

/* La cronologia con le miniature sta in orizzontale sotto l'immagine, perché la colonna di destra
   qui è occupata dalle grandezze. */
.history--wide {
    flex: 0 0 auto;
    flex-direction: row-reverse;
    justify-content: flex-end;
    overflow-x: auto;
    overflow-y: hidden;
    max-height: 120px;
}

.history--wide .history__item { flex: 0 0 auto; width: 132px; }

.history__thumb {
    width: 100%;
    height: 64px;
    object-fit: cover;
    border-radius: var(--radius-btn);
    background: #000;
}


/* ─── Le misure in sospeso, sulla Home ────────────────────────────────────────────────────────
   Il riquadro compare solo quando ce n'è almeno una: il resto del tempo sarebbe una riga che dice
   «niente», e quelle si imparano a saltare. */

.suspended {
    display: flex;
    flex-direction: column;
    gap: var(--tap-gap);
    margin: 0;
    padding: 0;
    list-style: none;
}

.suspended__item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--gap);
    padding: 10px 0;
    border-top: 1px solid var(--border-soft);
}

.suspended__item:first-child { border-top: none; }

.suspended__what { margin: 0; font-weight: 600; }

.suspended__commands { display: flex; flex: none; align-items: center; gap: var(--tap-gap); }
.suspended__commands > form { margin: 0; }

.suspended__more { justify-self: start; margin-top: 4px; }

/* ─── Le tessere della Home ───────────────────────────────────────────────────────────────────
   Dal redesign (07 §91.7): a sinistra di ogni riquadro un'icona del nostro insieme su un fondo verde
   pallido. Il riquadro diventa una griglia a due colonne e tutto ciò che portava va nella seconda.
   **La tessera è una sola, da 88 px, in tutti i riquadri** (14/10, Gianluca): fino ad allora quello che si consulta
   e basta l'aveva da 64, per dire il suo genere, e impilato sotto gli altri due si leggeva come un'icona sbagliata —
   più piccola, e con il titolo più a sinistra. */

.card--tiled {
    display: grid;
    grid-template-columns: 88px minmax(0, 1fr);
    column-gap: 24px;
    align-items: start;
}

.card--tiled > .card__tile { grid-row: 1 / span 9; }
.card--tiled > :not(.card__tile) { grid-column: 2; }

.card__tile {
    display: grid;
    place-items: center;
    width: 88px;
    height: 88px;
    border-radius: 18px;
    background: var(--tile-bg);
    color: var(--tile-ink);
}

.card__tile > svg { width: 40px; height: 40px; stroke-width: 1.8; }

/* Il numero accanto al titolo: quante ce ne sono in tutto, anche quelle che il riquadro non mostra. */
.card__count { margin-left: 10px; vertical-align: 2px; font-weight: 600; }

/* Il ▶ davanti al testo dei comandi che avviano o riprendono una procedura. Più piccolo dell'icona di
   un comando, perché accompagna la parola e non la sostituisce. */
.btn > .btn__start { width: 12px; height: 12px; }

/* L'elenco delle misure in sospeso: largo abbastanza per riga e due comandi del registro del banco. */
.dialog--list { width: min(880px, calc(100vw - 32px)); }

/* ─── Le briciole dei passi ───────────────────────────────────────────────────────────────────
   A un solo livello e a lunghezza variabile. Portano lo stato di ciascun passo, ed è il motivo per
   cui sono grandi: in una procedura sono ciò che dice all'operatore dove si trova.

   Il giallo dice «sei qui», e nient'altro: non è un secondo colore di stato.

   **Vanno a capo, e non si stringono né scorrono.** Una ricetta nasce con tutte le misure accese, e
   una misura esterna è un passo: con nove esterne i passi sono undici, e in riga occupavano 1632 px
   dentro un pannello di 1444 — misurato — con gli ultimi tre, «Telecamera» e «Riepilogo» compresi,
   **oltre il bordo e irraggiungibili**. Scorrere in orizzontale è il rimedio peggiore al banco, dove
   si lavora con le mani sporche: su tre righe si vedono tutti e si toccano tutti. */

/* **Una riga sola, che scorre.** Andando a capo la fila si spezzava — sette passi ne facevano tre
   righe, e una ricetta con dodici misure esterne ne farebbe di più — e la seconda riga cominciava
   con un trattino sospeso sotto la prima briciola. Un fregio che cambia altezza fa anche saltare in
   su e in giù tutto ciò che gli sta sotto, a ogni passo.

   Chi non ha JavaScript la scorre con il dito; `steps.js` porta in vista quella corrente. */
.steps {
    display: flex;
    align-items: center;
    flex-wrap: nowrap;
    gap: 6px 10px;
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: var(--text-step);
    overflow-x: auto;
    scrollbar-width: none;

    /* **Il taglio si vede.** Scorrendo, la briciola al bordo restava mozzata a metà parola e niente
       diceva che ce n'erano altre: la sfumatura lo dice senza aggiungere un comando. Il rientro è
       largo quanto la sfumatura, così la prima briciola non ci finisce sotto quando non si è
       ancora scorso. */
    padding-inline: 14px;
    mask-image: linear-gradient(90deg,
        transparent 0, #000 14px, #000 calc(100% - 14px), transparent 100%);
}

.steps::-webkit-scrollbar { display: none; }

/* La briciola non si stringe per far stare le altre: leggerne mezza è peggio che scorrere. */
.steps__item { flex: 0 0 auto; }

/* Portata in vista, resta staccata dal bordo invece di appoggiarcisi. */
.steps__item[aria-current] { scroll-margin-inline: 24px; }

.steps__item {
    display: inline-flex;
    align-items: center;
    color: var(--brand-ink-muted);
    white-space: nowrap;
}

/* Il passo, che sia un comando o no, ha la stessa forma: chi può tornarci lo scopre passandoci
   sopra, non perché quello raggiungibile sia disegnato in un altro modo. Il bersaglio è largo
   quanto la briciola, perché al banco si tocca con il dito. */
.steps__go {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin: 0;
    padding: 4px 6px;
    border: 0;
    border-radius: var(--radius-btn);
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
}

button.steps__go { cursor: pointer; }

button.steps__go:hover,
button.steps__go:focus-visible { background: rgb(255 255 255 / 12%); }

/* Un passo dove non si è ancora stati resta leggibile ma non invita: il motivo sta nel titolo. */
.steps__go[aria-disabled="true"] { opacity: 0.55; }

/* Il trattino fra un passo e l'altro: dice che è una sequenza e non un elenco. */
.steps__item + .steps__item::before {
    content: "";
    width: 26px;
    height: 1px;
    margin-right: 2px;
    background: currentcolor;
    opacity: 0.5;
}

.steps__dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: currentcolor;
}

.steps__item--done  { color: var(--brand-ink); }
.steps__item--here  { color: var(--step-accent); font-weight: 700; }
.steps__item--ahead { opacity: 0.65; }


/* ─── Il palco di un passo: la griglia di ogni procedura ──────────────────────────────────────
   **Tre colonne, sempre le stesse, in ogni passo di ogni procedura** (03 §A.15). Da sinistra:

     `.stage__work`     l'area di lavoro — la telecamera dove c'è, altrimenti ciò su cui si lavora:
                        nelle misure esterne la griglia delle unità e sotto l'andamento;
     `.stage__values`   la colonna dei valori — che cosa si legge, e in fondo i comandi del passo;
     `.stage__history`  la colonna della cronologia — che cosa è stato preso, e in fondo il tondo.

   **Le tre colonne si dichiarano sempre, anche vuote.** È la regola che tiene fermo tutto il resto:
   la Calibrazione non ha cronologia, e fino al 14/09 non dichiarava la terza colonna — il tondo
   scivolava di 188 px rispetto alla Misura, cioè di una colonna intera, ed è il rilievo S.46 del
   committente. Collassare una colonna vuota sposta ciò che le sta accanto: il vuoto qui **è**
   struttura.

   **Il tondo non si centra e non si misura: sta in una cella.** Prima era `align-self: center` in
   una colonna elastica in tre passi e `padding-right: 72px` in una colonna rigida nel quarto, con
   un commento che dichiarava 104 px dal bordo mentre il valore scritto ne produceva 120 — perché
   era stato dimensionato sull'anello (72) invece che sul bersaglio (96). Tre posizioni diverse su
   cinque schermate, e nessun test poteva vederle (*07* §46). */

.procedure__stage {
    display: flex;
    gap: 24px;
    align-items: stretch;
    height: 100%;
    min-height: 0;

    /* Il palco è il **contenitore di query** da cui il riquadro dell'immagine ricava la propria
       larghezza: `100cqh` è la sua altezza, e senza questa riga il riquadro non ne ha una da cui
       partire. Porta con sé `contain: size`, quindi da qui in avanti il palco non cresce più con
       il proprio contenuto — oggi non lo fa comunque, perché l'altezza gliela dà il pannello. */
    container-type: size;
}

/* **L'area di lavoro impila**: nelle misure esterne porta il nome della grandezza, sotto la griglia
   delle unità, sotto ancora l'andamento. È una **colonna**, ed è il verso di partenza perché è quello
   che vale dove l'area non è una fotografia.

   Prende lo spazio che avanza: celle e grafico se ne servono davvero, e più sono larghi meglio si
   leggono. */
.stage__work {
    display: flex;
    flex-direction: column;
    gap: 16px;

    /* **La stessa larghezza in ogni passo, e la detta la telecamera.** È l'unica area con un vincolo
       fisico — il fotogramma è 4:3 e la sua larghezza discende dall'altezza del palco — quindi è lei
       a fissare la misura e gli altri passi la seguono, non il contrario. Nelle misure esterne le
       celle e l'andamento stanno dentro lo stesso rettangolo in cui altrove sta l'immagine.

       I trenta pixel in più sono la linguetta delle luci, che vive **dentro** quest'area: senza,
       l'area della telecamera sarebbe più larga delle altre di quel tanto, ed è il salto di
       cinquantanove pixel che il committente interno ha visto passando da un passo all'altro. */
    flex: 0 1 auto;
    width: calc(100cqh * 4 / 3 + var(--lights-tab));
    min-height: 0;
}

/* **Con la telecamera l'area è una riga** — le luci accanto al riquadro — e soprattutto **non
   cresce**: la larghezza del riquadro la detta la propria altezza, quindi lo spazio in più non le
   serve e resterebbe lì come vuoto fra l'immagine e i valori. Erano i 126 px di aria che si vedevano
   nella prima stesura del 14/09. Qui l'area prende quello che le serve e basta; a raccogliere ciò
   che avanza è la colonna dei valori, con la regola qui sotto.

   **640 px è il suo fondo**, e sotto non si scende: più stretta di così i punti da trascinare si
   avvicinano fra loro sotto la soglia del bersaglio, e la sezione si misura solo ingrandendo. È anche
   il punto in cui si riaprirebbe dal basso il rilievo S.3/S.33/S.35 — «l'area dell'immagine è troppo
   piccola» — che stiamo chiudendo dall'alto. Oltre questo minimo non si comprime più niente: lì
   comincia il responsive, e le colonne andranno sotto (08 §4.9). */
.stage__work--camera {
    flex-direction: row;
    align-items: stretch;
    gap: 0;
    height: 100%;
    min-width: 640px;
}

/* **Il riquadro è 4:3, come il sensore.** Il 03 §A.8 lo vuole quadrato, e qui si diverge di
   proposito: le telecamere sono 4:3 — 1440 × 1080 e 2048 × 1536 — quindi un riquadro quadrato mostra l'immagine con due
   bande nere sopra e sotto e butta via un quinto dell'area proprio nella schermata dove l'immagine
   **è** il soggetto. Sarebbe onorare la lettera della regola tradendo il rilievo che l'ha generata,
   cioè «l'immagine è troppo piccola in Calibrazione». */
/* La larghezza si scrive **due volte**, e non è una svista. `aspect-ratio` dichiara l'intento ed è
   ciò che regge se un domani il palco cambia; il `calc` sull'altezza del contenitore è ciò che
   funziona davvero **in WebKit**, dove la larghezza di un elemento flex si calcola prima di
   conoscerne l'altezza. Con il solo rapporto, lì dentro non c'è niente da misurare — il contenuto
   è tutto in `position: absolute` per via dell'ingrandimento — il riquadro esce largo zero, e con
   `overflow: hidden` spariscono insieme immagine, fondo e pastiglie: la schermata sembra senza
   telecamera mentre i fotogrammi arrivano regolarmente. */
/* **Il tetto è ciò che permette al palco di cedere.** `calc` dà al riquadro la larghezza che
   l'altezza gli concede; `max-width` gli impedisce di superare la colonna quando la colonna si
   stringe. Senza, il riquadro resta largo e sfonda: l'area non cede e a pagare sono le altre due.
   Quando il tetto morde, il rapporto 4:3 del **riquadro** salta — ma non quello dell'immagine, che
   ha `object-fit: contain` qui sotto e compare con le bande ai lati. Deformata non lo è mai. */
.stage__frame {
    position: relative;
    height: 100%;
    aspect-ratio: 4 / 3;
    width: calc(100cqh * 4 / 3);
    max-width: 100%;
    overflow: hidden;
    border-radius: var(--radius-panel);
    background: #15171a;
}

/* Il fotogramma è 4:3 e il riquadro è quadrato: si mostra **per intero**, con le bande sopra e
   sotto. Ritagliarlo toglierebbe campo proprio a chi sta cercando di inquadrare. */
.stage__live {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
}

.stage__badge {
    position: absolute;
    left: 12px;
    padding: 5px 10px;
    border-radius: var(--radius-pill);
    background: rgb(0 0 0 / 62%);
    color: #fff;
    font-size: var(--text-label);
    font-weight: 600;
}

.stage__badge--top { top: 12px; }

/* **Uno strumento dell'immagine, non un comando della schermata.** Azzerare l'ingrandimento agisce
   su ciò che si sta guardando, e stava in colonna in mezzo ai comandi che agiscono sulla
   calibrazione: due mestieri diversi nello stesso posto. Ora sta nell'angolo opposto al livello di
   ingrandimento — quello lo dice, questo lo rimette — e la mano lo trova senza uscire dall'immagine.

   Vale anche per la Misura, dove l'immagine è la stessa cosa e lo strumento pure. */
.stage__tool {
    position: absolute;
    right: 12px;
    bottom: 12px;
    padding: 5px 10px;
    border: 0;
    border-radius: var(--radius-pill);
    background: rgb(0 0 0 / 62%);
    color: #fff;
    font-family: inherit;
    font-size: var(--text-label);
    font-weight: 600;
    cursor: pointer;
}

.stage__tool:hover,
.stage__tool:focus-visible { background: rgb(0 0 0 / 80%); }

/* Senza ingrandimento non c'è niente da rimettere a posto: resta visibile e spento, con il motivo,
   perché sparire farebbe cercare un comando che c'è. */
.stage__frame:not(.stage__frame--zoomed) .stage__tool {
    opacity: 0.45;
    pointer-events: none;
}

/* ── La colonna dei valori: che cosa si legge, e in fondo i comandi del passo ──
   **300 px è il suo minimo**: la larghezza che serve alla riga più larga dell'elenco grandezze —
   «Spessore aggraffatura» a 15px/600, più il valore a 19px/800, più l'unità e i margini: 293 px
   misurati sui glifi di Manrope. È anche la sua larghezza dove l'area di lavoro sa usare lo spazio
   che avanza, cioè nelle misure esterne; dove non sa usarlo — con la telecamera — se lo prende
   questa colonna, per la regola qui sotto.

   Il numero lo detta **un dato**, non una frase. Fino al 14/09 lo dettava l'etichetta «Rimetti i
   punti trovati automaticamente», che con i suoi 356 px era più larga di qualunque misura: un
   minimo di layout tenuto in ostaggio da una parola, che cambia ogni volta che si riscrive il
   comando. L'etichetta è stata accorciata, e il minimo è tornato al dato (*07* §46). */
.stage__values {
    /* Cresce con il residuo e **non scende sotto i 300**: `flex-shrink: 0` con la base al minimo è
       ciò che mette in ordine la cedevolezza senza doverla scrivere altrove. Stringendo il pannello
       il residuo cala da sé e questa colonna torna verso il minimo; da lì in poi cedono le altre
       due, ciascuna fino al proprio fondo. */
    flex: 1 0 300px;
    display: flex;
    flex-direction: column;
    gap: var(--tap-gap);
    min-height: 0;
}


/* **Ciò che cresce con i dati non deve stirarsi oltre il leggibile.** La colonna è elastica e sul
   banco del committente arriva a 641 px (07 §48.1): senza un tetto, una riga con nome a sinistra e
   valore a destra diventa un ponte, e l'occhio deve attraversarlo per appaiare due cose che stanno
   sulla stessa riga. **`.reading` mancava**, ed era l'unica delle quattro a portare due estremi. */
.stage__values .field,
.stage__values .btn,
.stage__values .readout,
.stage__values .reading { max-width: 460px; }

/* **I comandi si appoggiano al fondo e crescono verso l'alto.** Non hanno un'altezza fissa e non
   devono averla: il passo Telecamera può averne tre — «Riprendi da X», «Rifai lo scatto», «Rimetti
   i punti automatici» — e la Calibrazione uno solo. A restare fermo è il **fondo**, ed è l'unica
   cosa che conta: il tondo che gli sta a destra è ancorato allo stesso fondo, e quello si preme
   senza guardare. Le cime si muovono, e va bene: i comandi si leggono. */
.stage__commands {
    margin-top: auto;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--tap-gap);
}

/* ── La colonna della cronologia: che cosa è stato preso, e in fondo il tondo ──
   **160 px è il fondo**, e lo detta la miniatura della sezione: sotto, l'anteprima smette di dire
   quale cattura è. Il tondo da solo ne chiederebbe 128 — 96 di bersaglio più 16 per lato — e il
   valore più largo che ci sta dentro, «100,00 %» a 17px/800 con i suoi margini, ne chiede 119.
   Si parte da 200 e si cede fino a 160: è l'unica delle tre colonne che ha corsa vera. */
.stage__history {
    flex: 0 1 200px;
    min-width: 160px;
    display: flex;
    flex-direction: column;
    gap: var(--gap);
    min-height: 0;
}

/* La cronologia dentro il suo riquadro: **fondo scostato e bordo**, così si legge come un
   contenitore che scorre per conto suo e non come una colonna di schede appoggiate al bianco. Finisce
   sopra l'anello — è la richiesta — invece di scendere fino in fondo. */
.history-box {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 10px;
    border-radius: var(--radius-panel);
    background: var(--border-softer);
    box-shadow: inset 0 0 0 1px var(--border-soft);
}

/* Il riquadro ha un nome, perché con il fondo scostato diventa un contenitore e un contenitore senza
   nome fa chiedere che cosa contenga. «Catture» è la parola del glossario per il gesto che le produce. */
.history__title {
    margin: 0;
    color: var(--text-muted);
    font-size: var(--text-label);
    font-weight: 700;
    letter-spacing: var(--text-label-spacing);
    text-transform: var(--text-label-transform);
    text-align: center;
}

/* **La cella del tondo: in fondo alla colonna, centrata dentro di essa.** Nessun numero da
   azzeccare — né un centro relativo a una colonna elastica né una distanza dal bordo — perché
   entrambi si sono già rotti: il primo cambiando la larghezza della colonna, il secondo quando
   l'anello è passato da 72 a 96 px e il margine è rimasto quello di prima.

   `margin-top: auto` serve dove la cronologia non c'è: nella Calibrazione questa colonna è vuota
   sopra, e senza l'ancoraggio il tondo salirebbe in cima. Al banco la mano lo cerca sempre nello
   stesso punto: è l'unico comando che si preme senza guardare. */
.stage__capture {
    flex: 0 0 auto;
    margin-top: auto;
    display: flex;
    justify-content: center;
    /* **Al fondo va l'anello, non il pulsante** (14/10): il pulsante è alto 96 e l'anello che si vede 72, centrato, quindi
       ancorando il pulsante l'anello finiva dodici pixel sopra il fondo dei comandi accanto, e i due si leggevano
       leggermente disallineati. Il margine negativo restituisce quei dodici pixel trasparenti. */
    margin-bottom: -12px;
}

.stage__title { margin: 0; font-size: 20px; font-weight: 700; }
.stage__note  { margin: 0; color: var(--text-secondary); font-size: var(--text-secondary-size); }
.stage__note--quiet { color: var(--text-muted); }


/* ─── I tre canali di luce ────────────────────────────────────────────────────────────────────
   Sull'orlo dell'immagine, perché si regolano una volta e restano, mentre l'immagine si guarda
   sempre. Sono dell'apparato e non della telecamera. */

.lights {
    flex: 0 0 auto;
    display: flex;
    align-items: stretch;
    border-radius: var(--radius-panel) 0 0 var(--radius-panel);
    background: var(--border-soft);
}

/* La linguetta verticale: si vede sempre, e da chiusa costa trenta pixel di larghezza. **Il numero
   sta in `--lights-tab`** e non qui, perché lo legge anche la larghezza dell'area di lavoro: sono la
   stessa misura, e due copie divergono alla prima volta che si tocca la linguetta. */
.lights__tab {
    display: flex;
    align-items: center;
    justify-content: center;
    width: var(--lights-tab);
    writing-mode: vertical-rl;
    font-size: var(--text-label);
    font-weight: 700;
    letter-spacing: 0.08em;
    color: var(--text-secondary);
    cursor: pointer;
    list-style: none;
}

.lights__tab::-webkit-details-marker { display: none; }

.lights__body {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 18px;
    width: 150px;
    padding: 16px 14px 16px 0;
}

.lights__channel { display: block; }
.lights__name { display: block; margin-bottom: 6px; font-size: var(--text-label); font-weight: 600; color: var(--text-secondary); }

/* **Il verde del marchio non entra nell'area di lavoro**, dove verde e rosso significano già dentro
   e fuori tolleranza. Un cursore di luce non è uno stato: resta neutro. */
.lights input[type="range"] { width: 100%; accent-color: var(--text-muted); }

/* **Stato a sinistra, comandi a destra**, nei riquadri della Home da cui si avvia qualcosa. È la
   forma del mock-up — lì il riquadro è un flex con `space-between` — e la ragione è l'ordine in cui
   si legge: prima com'è messo il banco, poi cosa si può fare.

   Sotto i 900 px la colonna dei comandi va a capo e prende tutta la larghezza: su uno schermo stretto
   due colonne strizzate sono peggio di due righe. */
.card__split {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--pad-m);
    flex-wrap: wrap;
}

.card__state { flex: 1 1 340px; min-width: 0; }

/* **Impilati e larghi uguali**, e la larghezza uguale non è un vezzo: tre comandi di larghezza
   diversa in colonna fanno una scaletta, e la scaletta si legge come una gerarchia che non c'è. La
   gerarchia vera la porta il **peso** — il verde pieno all'azione principale, gli altri bianchi — che
   resta leggibile a parità di misura.

   `align-items: stretch` è il valore di partenza in colonna: i pulsanti prendono la larghezza del
   contenitore, che è quella del più largo fra loro. */
.card__deck {
    display: flex;
    flex-direction: column;
    gap: 10px;
    flex: 0 0 auto;
    /* **Una larghezza comune, come minimo** (14/10): le pile dei riquadri stanno una sotto l'altra, e prendendo
       ciascuna la larghezza del suo pulsante più lungo avevano i bordi sinistri sfalsati di una quarantina di pixel. Un
       minimo e non una misura fissa, perché una lingua più lunga deve poterle allargare. */
    min-width: 248px;
}

.card__deck .btn { width: 100%; }

/* Le note che accompagnano i comandi restano sotto la colonna, allineate con essa. */
.card__deck .card__note { margin: 2px 0 0; text-align: right; }

@media (max-width: 900px) {
    .card__deck { flex: 1 1 100%; min-width: 0; }
    .card__deck .card__note { text-align: left; }
}

/* **Le luci a immagine ferma: si vedono, non si toccano.** Il pannello resta al suo posto perché
   sparendo sposterebbe di lato l'immagine — il rilievo del committente dell'8/09 su tutte e due le
   procedure — e la linguetta si smorza per dire che adesso non serve. Il motivo è scritto dentro,
   non affidato al titolo: su uno schermo tattile il suggerimento non compare mai. */
.lights--off .lights__tab { opacity: 0.55; }

.lights__reason {
    margin: 0 0 var(--gap);
    font-size: var(--text-label);
    line-height: 1.4;
    color: var(--text-secondary);
}

.lights--off input[type="range"] { cursor: not-allowed; }


/* ─── Le due quote sull'immagine ──────────────────────────────────────────────────────────────
   Le unità sono i pixel del fotogramma: le coordinate che tornano al server sono già quelle
   dell'immagine. I colori sono quelli dell'apparato in esercizio, così la mano che li conosce li
   ritrova — e sono gli unici due colori dell'area di lavoro, dove il verde del marchio non entra. */

.quotes {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    touch-action: none;

    /* Come per `.points`: una piastrina che cade oltre il bordo del fotogramma si disegna lo stesso,
       e la si raggiunge allontanandosi o scorrendo. Prima spariva. */
    overflow: visible;
}

/* **Gli spessori li scrive `quotes.js`**, insieme a raggi e corpo del carattere e per la stessa
   ragione: sono misure di *schermo*, e qui non ci sarebbe modo di tenerle tali. `non-scaling-stroke`
   sembra fatto apposta e non basta — annulla la scala del `viewBox`, ma il `transform` CSS di un
   antenato, che è il nostro ingrandimento, se lo porta dietro lo stesso: a 6× il riquadro veniva
   spesso dodici pixel, e su un bordo che si accosta al sotto-pixel la riga copre ciò che si misura. */
.quote__box  { fill: none; }
.quote__axis { opacity: 0.85; }
.quote__label { font-weight: 700; paint-order: stroke; stroke: rgb(0 0 0 / 75%); }

/* **La maniglia non è piena.** Copriva il bordo del disco, cioè la cosa che si sta accostando: ora
   è un anello e sotto si vede l'immagine, con un crocino che dice quale punto è stato preso invece
   di lasciarlo indovinare al centro di una pastiglia.

   L'anello scuro sotto è lo stesso rimedio del contorno delle scritte: senza, la maniglia gialla
   sparisce proprio sul bordo chiaro del calibratore, che è dove finisce sempre. */
.quote__halo { fill: none; stroke: rgb(0 0 0 / 60%); }
.quote__grip { fill: none; }
.quote__cross { stroke-linecap: round; }

/* Il bersaglio è più grande di ciò che si vede: al banco si lavora in piedi e con i guanti. */
.quote__target { fill: transparent; cursor: grab; }
.quote__target--held { cursor: grabbing; }

/* Le due tinte stanno nei token perché servono in due posti: qui, e nel disegno dell'esito. */
.quote-name--x { color: var(--quote-x-ink); }
.quote-name--y { color: var(--quote-y-ink); }


/* ─── I valori letti mentre si trascina ───────────────────────────────────────────────────────
   Cifre a larghezza fissa: un numero che si aggiorna trascinando non deve ballare in orizzontale
   mentre lo si guarda. */

.readout {
    padding: 14px 16px;
    border-radius: var(--radius-card);
    background: var(--chip-bg);
}

.readout__title { margin: 0 0 8px; font-size: var(--text-label); font-weight: 700; color: var(--text-secondary); text-transform: var(--text-label-transform); letter-spacing: var(--text-label-spacing); }
.readout__row   { display: flex; align-items: baseline; justify-content: space-between; margin: 4px 0; }
.readout__axis  { font-weight: 600; }
.readout__value { font-size: var(--text-reading); font-weight: 700; font-variant-numeric: tabular-nums; }
.readout__note  { margin: 8px 0 0; font-size: var(--text-label); color: var(--text-muted); }


/* ─── L'esito ─────────────────────────────────────────────────────────────────────────────────
   Le soglie si nominano nel testo: un colore da solo lascerebbe indovinare da dove viene il
   giudizio. */

/* `.outcome` veste la **colonna dei valori** del palco, non più un riquadro suo: dal 16/09 il passo
   dell'esito ha le tre colonne come gli altri due (03 §A.15), e i numeri stanno dove negli altri passi
   stanno i valori. */
.outcome { display: flex; flex-direction: column; gap: var(--gap); overflow-y: auto; }

/* Il verdetto sta sopra i numeri: si legge per primo, e i numeri spiegano il perché.

   **Ed è la pastiglia più grande dell'applicazione**, deliberatamente: il committente chiede «un box
   verde/rosso per dire ko/ok» (S.9), e quella che c'era era della stessa taglia delle quattro che
   dicono se un singolo criterio sta nella sua soglia. Fra sei pastiglie identiche, quella che porta
   l'esito non si distingueva da quelle che portano un dettaglio.

   *Resta una pastiglia e non diventa un riquadro: in questa applicazione uno stato si legge e non si
   preme, e un rettangolo pieno con un bordo si legge come un comando.* */
.outcome__verdict { margin: 0; }

.outcome__verdict .pill {
    padding: 8px 18px;
    font-size: var(--text-title);
    font-weight: 700;
    border-radius: 999px;
}

.outcome__verdict .pill__dot { width: 10px; height: 10px; }

/* **La colonna di lavoro senza telecamera**: qui non c'è fotogramma né linguetta delle luci, e il
   disegno del calibratore ne prende il posto. Si dimensiona sull'altezza del palco come farebbe un
   fotogramma — è ciò che lo tiene largo quanto l'immagine degli altri due passi invece dei 260÷420 px
   della colonna di destra in cui stava prima.

   *Il `- var(--lights-tab)` non c'è, ed è la differenza con `--camera`: quei trenta pixel sono la
   linguetta, che qui non esiste.* */
.stage__work--diagram {
    justify-content: center;
    width: calc(100cqh * 4 / 3);
    max-width: 100%;
    min-height: 0;
}

/* **Il disegno si dimensiona sull'altezza, non sulla larghezza**, ed è la stessa regola del riquadro
   della telecamera. Con `width: 100%` e l'altezza lasciata al rapporto, nella colonna di lavoro — che
   qui è larga 905 px — il bersaglio veniva alto 700 dentro un palco di 679 e sbordava di sopra: sullo
   schermo del committente usciva dal pannello.

   La misura la dà il flex (`flex: 1 1 0` più `min-height: 0`, che è ciò che permette a un elemento
   flex di restringersi sotto il proprio contenuto), e a tenere le proporzioni dentro quel box ci pensa
   `preserveAspectRatio`, che per un SVG è predefinito.

   **E c'è un tetto, che non è timidezza.** Le scritte dentro l'SVG sono dichiarate sulla griglia del
   `viewBox`: a tutta colonna il fattore di scala arriva a tre, e un 13 diventa **39 px veri** — il
   bersaglio si leggeva come un manifesto e i suoi due numeri pesavano più del verdetto. È lo stesso
   difetto dei segni di stato del 15/09 visto dal verso opposto: *una misura dichiarata su una griglia
   non è una misura sullo schermo*. Il committente ha chiesto «più grande» rispetto ai 260÷420 px in
   cui stava prima, non tutta la colonna. */
.stage__work--diagram .outcome__figure {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 16px;
    width: 100%;
    max-width: 520px;
    height: 100%;
    min-height: 0;
    margin-inline: auto;
}

/* **Il disegno non si allunga a riempire**, e le sue divisioni gli restano sotto. Con `flex: 1 1 0`
   l'SVG prendeva tutta l'altezza della colonna e ci disegnava dentro il bersaglio centrato — che è
   corretto per lui e sbagliato per la composizione: il disegno rimpiccioliva e le due divisioni
   finivano in fondo alla colonna, staccate da ciò che spiegano, con un palmo di vuoto in mezzo che
   non aspettava niente.

   Adesso il blocco *disegno più divisioni* è un'unità sola, centrata nella colonna: il vuoto resta,
   ma tutto attorno invece che dentro. */
.stage__work--diagram .outcome__figure svg {
    flex: 0 1 auto;
    min-height: 0;
    width: 100%;
    max-height: 100%;
    height: auto;
}

/* Le divisioni stanno con il disegno e non a bandiera sul bordo della colonna: sono la sua didascalia,
   e una didascalia allineata altrove si legge come un blocco a sé. */
.stage__work--diagram .outcome__working { align-items: center; text-align: center; }

/* ─── Un criterio dell'esito ───────────────────────────────────────────────────────────────────
   **Sei numeri, una sola forma.** Erano due tabelle a tre colonne larghe 860 px, che nella colonna dei
   valori non ci stanno; e soprattutto rendevano in tre modi diversi cose che fanno lo stesso mestiere
   — chi aveva la pastiglia, chi il solo colore del numero, chi una frase in prosa staccata in fondo
   alla schermata. È la regola degli omologhi, ed è ciò che il committente ha segnalato tre volte
   senza che lo chiamassimo così (S.9, S.47, S.48).

   Il nome e il valore sulla prima riga, ciò che li spiega sotto: regge a 300 px come a 641, che è la
   larghezza che questa colonna prende sullo schermo del committente. */
.criterion {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 2px 12px;
    padding: 10px 0;
    border-top: 1px solid var(--border);
}

.criterion:first-of-type { border-top: 0; }

.criterion__name { color: var(--text-muted); }

.criterion__value {
    text-align: right;
    font-size: 19px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* **Il rosso è del solo criterio che decide.** Gli altri tre fuori soglia prendono l'ambra: avvisano,
   e dire in rosso «non si può usare» di un avviso sarebbe falso. */
.criterion__value--out { color: var(--status-error-ink); }
.criterion__value--none { color: var(--text-secondary); font-weight: 600; }

.criterion__unit {
    margin-left: 3px;
    font-size: var(--text-label);
    font-weight: 600;
    color: var(--text-secondary);
}

/* Occupa tutte e due le colonne: è la riga di sotto, dove sta la pastiglia con la soglia e la frase
   che la spiega. */
.criterion__aside {
    grid-column: 1 / -1;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    font-size: var(--text-label);
    color: var(--text-secondary);
}

/* **Quello che decide si distingue**, e non con un colore: il colore qui è già impegnato dal
   giudizio. Un filetto a sinistra e il nome in evidenza bastano a dire che questa riga non è come le
   altre tre — senza romperne la forma, che è ciò che le rende leggibili insieme. */
.criterion--decisive {
    padding-left: 12px;
    border-left: 3px solid var(--text-muted);
}

.criterion--decisive .criterion__name { color: var(--text); font-weight: 600; }

/* Lo stacco fra i due gruppi: sopra ciò che si misura, sotto ciò che si giudica. */
.outcome__group--judged { margin-top: 18px; }

.outcome__group {
    margin: 0 0 6px;
    font-size: var(--text-label);
    font-weight: 700;
    letter-spacing: var(--text-label-spacing);
    text-transform: var(--text-label-transform);
    color: var(--text-secondary);
}

/* **La tabella delle letture è uscita il 16/09**, e con essa la collisione di nomi che ci viveva
   dentro: si chiamava `readings` come le letture della telecamera — una tabella qui, una lista di
   righe là — e un commento rimasto aperto si era mangiato per settimane la regola che colora di rosso
   un valore fuori soglia. Al suo posto c'è `.criterion`, che rende i sei numeri **tutti allo stesso
   modo** e sta nella colonna dei valori del palco.

   *Il difetto del commento non chiuso non l'ha segnalato nessuno: il verdetto in cima diceva comunque
   «Non riuscita», quindi il rosso mancante non lasciava buchi visibili. Si è visto leggendo il foglio
   di stile, non guardando la schermata.* */

/* Il quadratino colorato lega il criterio al suo asse nel disegno accanto, e sono gli stessi due
   colori delle maniglie che l'operatore ha appena trascinato. */
.criterion--x .criterion__name::before,
.criterion--y .criterion__name::before {
    content: "";
    display: inline-block;
    width: 10px; height: 10px;
    margin-right: 8px;
    border-radius: 2px;
}
.criterion--x .criterion__name::before { background: var(--quote-x-ink); }
.criterion--y .criterion__name::before { background: var(--quote-y-ink); }

/* Il disegno del riscontro. */
.outcome__figure { margin: 0; }
.outcome__figure svg { display: block; width: 100%; height: auto; }

.target__disc { fill: var(--chip-bg); stroke: var(--border); }
.target__axis { stroke-width: 1.5; fill: none; }
.target__axis--x { stroke: var(--quote-x-ink); }
.target__axis--y { stroke: var(--quote-y-ink); }
.target__value { font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums; }
.target__value--x { fill: var(--quote-x-ink); }
.target__value--y { fill: var(--quote-y-ink); }
/* **Queste due misure sono in unità del `viewBox`, non in pixel**, e vanno lette moltiplicate per il
   fattore di scala del disegno: con il tetto di 520 px vale circa 1,73, quindi il 9 qui sotto si rende
   a ~15,6 px veri e il 12 del valore a ~21. Gli 11 di prima, a 420 px, facevano 15,4: la didascalia è
   sempre stata sopra il minimo di 13 px del 03 §A.13, e portarla a 13 *nel viewBox* l'avrebbe resa a
   22,5 — cioè grande quanto il numero che accompagna. */
.target__caption { font-size: 9px; fill: var(--text-secondary); }

/* La divisione, che è la cosa che spiega da dove escono i due numeri. */
.outcome__working {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-top: 14px;
    padding-top: 14px;
    border-top: 1px solid var(--border);
}
.outcome__sum {
    font-size: var(--text-secondary-size);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}
.outcome__sum--x { color: var(--quote-x-ink); }
.outcome__sum--y { color: var(--quote-y-ink); }

/* Un avviso dentro l'area di lavoro, non la fascia degli esiti: quello dichiara una condizione che
   dura, e infatti non si chiude. */
.notice--inline { margin: 0; }


/* ─── Ingrandimento e spostamento dell'immagine ───────────────────────────────────────────────
   Si trasforma un contenitore e non l'immagine, così il disegno delle quote resta incollato ai
   pixel che descrive. L'ingrandimento è della schermata: non entra in nessun numero conservato. */

/* **Niente `will-change: transform` qui, ed è la correzione di un difetto misurato.** Quella
   dichiarazione prometteva fluidità e in cambio faceva promuovere il contenitore a livello
   composito: il browser lo disegnava **una volta sola** alla scala di partenza e poi ne ingrandiva
   l'immagine sulla scheda grafica, invece di ridisegnarlo. A 8× sgranavano insieme la fotografia —
   dove è giusto, sono i pixel veri — **e le quote**, che sono vettoriali e dovrebbero restare nette:
   maniglie con l'alone e linee morbide, cioè il contrario di ciò che serve per accostare un punto
   a un bordo.

   Le due misure che hanno deciso: con la dichiarazione il browser costruisce **otto** livelli
   compositi, senza **cinque**; e la fluidità è **identica**, 120 fotogrammi al secondo in tutti e
   due i casi. Non comprava niente e costava la nitidezza. Non rimetterla. */
.stage__viewport {
    position: absolute;
    inset: 0;
    transform-origin: 0 0;
}

/* **Si può spostare sempre**, anche a 100%, perché il margine attorno al fotogramma si raggiunge
   da lì: il cursore lo dice sempre. La classe `--zoomed` non governa più il cursore ma il solo
   «Reset zoom», e vuol dire «c'è qualcosa da rimettere a posto» — ingrandimento **o** scorrimento. */
.stage__frame { cursor: grab; }
.stage__frame:active { cursor: grabbing; }

.stage__badge--bottom { bottom: 12px; }


/* ─── Il comando di cattura ───────────────────────────────────────────────────────────────────
   **L'anello blu è l'eco del pulsante fisico del banco**, ed è l'unico posto dell'applicazione in
   cui il blu compare: chi lavora al banco preme quello, e qui ritrova la stessa forma. Non è uno
   stato e non è un'azione primaria — quella è verde e sta in fondo — è la mano che riconosce. */

/* Quello vero è un pulsante d'acciaio con una **corona luminosa blu** attorno a un tondo di metallo
   spazzolato, e la luce si spande sulla mano che lo preme. Qui non si imita il metallo — sarebbe
   skeuomorfismo, e su uno schermo opaco al banco verrebbe pure male — si prende la sola cosa che la
   mano riconosce da un metro di distanza: **un anello di luce blu attorno a un tondo neutro**. */
/* **Sta per conto suo, e al centro.** In fila con i campi si leggeva come un comando qualunque, e
   invece è il gesto della schermata: lo stacco sopra lo toglie dall'elenco dei dati e il centro gli dà
   il peso che ha. Si centra nella fascia dei campi e non nella colonna, o su uno schermo largo
   scivolerebbe a destra di tutto quello a cui si riferisce. La fascia non si preme: si preme il tondo. */
/* **`.capture-slot` è uscito il 14/09**, e con esso l'idea che il tondo e «Rifai lo scatto» siano
   un gruppo solo. Non lo sono: il primo è il gesto di cattura e sta nella colonna della cronologia,
   il secondo è un comando del passo e sta con gli altri comandi nella colonna dei valori. Tenerli
   insieme era ciò che costringeva il tondo a centrarsi dentro una colonna elastica, cioè a stare in
   un posto diverso in ogni passo. Adesso stanno in due celle della griglia (*07* §46). */

/* Quel che c'era prima di questa calibrazione: si legge una volta, e non compete con niente. */
.stage__previous {
    margin: 0;
    max-width: 460px;
    color: var(--text-muted);
    font-size: var(--text-secondary-size);
}

.capture {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 96px;
    height: 96px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    cursor: pointer;
}

.capture__ring {
    position: relative;
    width: 72px;
    height: 72px;
    border-radius: 50%;

    /* Il tondo interno: neutro e appena rilevato, come il metallo tornito senza esserlo. */
    background: radial-gradient(circle at 38% 32%, var(--input-bg), var(--chip-bg) 72%);

    /* La corona, e la luce che le si spande attorno. */
    border: 7px solid var(--capture-ring);
    box-shadow:
        0 0 12px 2px color-mix(in srgb, var(--capture-ring) 55%, transparent),
        0 0 30px 6px color-mix(in srgb, var(--capture-ring) 22%, transparent),
        inset 0 0 8px color-mix(in srgb, var(--capture-ring) 30%, transparent);
    transition: box-shadow 120ms ease, transform 120ms ease;
}

/* Avvicinandosi, la corona si accende di più: è come si comporta quella vera quando la si arma. */
.capture:hover .capture__ring {
    box-shadow:
        0 0 16px 3px color-mix(in srgb, var(--capture-ring) 70%, transparent),
        0 0 42px 10px color-mix(in srgb, var(--capture-ring) 30%, transparent),
        inset 0 0 8px color-mix(in srgb, var(--capture-ring) 35%, transparent);
}

/* Premuto, rientra: la corsa del pulsante vero è corta e secca. */
.capture:active .capture__ring {
    transform: scale(0.94);
    box-shadow:
        0 0 8px 1px color-mix(in srgb, var(--capture-ring) 60%, transparent),
        inset 0 0 14px color-mix(in srgb, var(--capture-ring) 45%, transparent);
}

.capture:focus-visible { outline: 3px solid var(--capture-ring); outline-offset: 6px; }

/* Righe accese e righe spente, nelle tabelle dove una spunta decide se la riga vale.

   La spunta da sola non basta: su diciassette righe il committente non distingueva a colpo d'occhio
   ciò che aveva scelto da ciò che aveva lasciato (09 §6.4). Si lavora sui due lati — il nome di una
   riga accesa pesa di più, una riga spenta si scrive con meno contrasto — perché rafforzare solo il
   primo lascia il grigio uniforme, e attenuare solo il secondo fa sembrare rotta mezza tabella.

   **Le righe spente non spariscono e non si toccano di meno**: restano leggibili e i loro comandi
   restano premibili, perché è da lì che si accende quel che manca. Il contrasto scende quanto basta
   a mettere in secondo piano, non tanto da nascondere. */

/* Lo stato lo legge il CSS **dalla spunta stessa**, non da una classe resa dal server. Così è in
   diretta per costruzione — spuntare una riga la accende sotto le dita, e accenderle tutte con la
   spunta di testa le accende tutte — senza una riga di JavaScript che tenga allineate le classi.
   `:has()` colpisce solo le righe che una spunta ce l'hanno, quindi le tabelle senza — i limiti
   dell'Oggetto — non ne sono toccate. */

.table tr:has([data-row-toggle]:checked) .row-name { font-weight: 600; }

.table tr:has([data-row-toggle]:not(:checked)) { color: var(--text-secondary); }

/* I campi di una riga spenta seguono la riga, ma la spunta no: è l'unico comando che la riaccende,
   e attenuarlo la renderebbe più difficile da trovare proprio quando la si cerca. */
.table tr:has([data-row-toggle]:not(:checked)) input:not([type="checkbox"]),
.table tr:has([data-row-toggle]:not(:checked)) select {
    color: var(--text-secondary);
}

/* La legenda che spiega le colonne di una tabella, sotto la tabella.

   Sta sotto e non nelle intestazioni perché nelle intestazioni non ci starebbe: «Si corregge nel
   Report» è già lungo per una colonna stretta, e la frase che lo spiega è tre volte tanto. Sotto si
   legge una volta per tutte le righe (07 §18.1).

   È una `<dl>` e non un elenco puntato: sono coppie termine-spiegazione, ed è esattamente il
   mestiere di quel tag. Chi legge con la voce sente il legame. */

.legend {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 4px var(--gap);
    margin: 0;
    color: var(--text-muted);
    font-size: var(--text-secondary-size);
}

.legend dt {
    font-weight: 600;
    color: var(--text-secondary);
}

.legend dd { margin: 0; }

/* Sotto i 560 px il termine va sopra la sua spiegazione: due colonne dove la prima è lunga quanto
   «Si corregge nel Report» lascerebbero alla seconda una gola di poche parole per riga. */

/* ═══ IL RIEPILOGO DEL CONTROLLO (03 §A.5) ══════════════════════════════════════════════════════

   La tabella più larga dell'applicazione: fino a trentasei righe per una dozzina di colonne, che è
   la dimensione vera osservata e non un limite teorico (02 §9.3). **A scorrere è la tabella dentro
   il suo involucro**, non la colonna che la contiene: se scorresse la colonna, la testata e i
   comandi del pannello uscirebbero dallo schermo insieme ai numeri.

   Nessun colore è scritto qui: gli stati vengono dai token, che sono l'unico posto dove si cambiano
   il giorno che le tinte del giudizio andranno virate. */

.summary { display: flex; flex-direction: column; gap: 16px; min-width: 0; }

/* **La tabella scorre, l'andamento resta dov'è.** È la regola «ciò che cresce con i dati non spinge
   fuori ciò che è fisso» (03 §A.14): le righe sono Teste × Campioni × Tagli — con dodici teste
   settantadue — e spingevano il grafico giù, fuori dallo schermo. Chi arrivava in fondo alla tabella
   non lo trovava più.

   **Ancorato in basso, come nel passo delle misure esterne**: chi passa da un passo all'altro ritrova
   le stesse cose negli stessi posti, che è la regola degli omologhi. Il riquadro del riepilogo prende
   l'altezza del corpo della procedura, la tabella si prende quel che avanza e scorre dentro di sé, e
   sotto restano il grafico e le statistiche. */
.summary { height: 100%; }

.summary .table-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
}

.summary .trend { flex: none; }

/* Il corpo della procedura non scorre più quando ospita il riepilogo: a scorrere è la tabella dentro
   di sé, e due scorrimenti annidati sono il modo più sicuro di perdere il grafico che si voleva
   tenere fermo. */
.procedure__body:has(> .summary) { overflow: hidden; }

/* **Il campo del valore scende solo qui.** `.cell__value` veste anche la griglia delle celle del
   passo esterne, che resta nel registro «banco» perché lì il numero si guarda di sfuggita mentre si
   misura: cambiarlo alla radice porterebbe via anche quello. Nel riepilogo si legge da vicino e si
   correggono valori, quindi prende il corpo del registro — restano il peso e le cifre a larghezza
   fissa, che sono ciò che rende una colonna di numeri leggibile incolonnata. */
.summary .cell__value { font-size: var(--text-title); }

.summary__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 12px;
}

.summary__title { margin: 0; font-size: 19px; font-weight: 800; }

/* L'intestazione di colonna porta tre cose in verticale — disegno, nome, unità — e deve restare
   stretta: dodici colonne larghe quanto il loro nome per esteso non ci starebbero.

   **Le tre cose si allineano fra colonne, non dentro la propria cella.** Con `vertical-align: bottom`
   ogni intestazione si appoggiava al fondo e risaliva per conto suo: un nome su due righe spingeva il
   *suo* disegno più in alto di quello accanto, e i nomi cominciavano a quote diverse. Il
   disallineamento cresce con la lunghezza dei nomi, quindi si vede solo con le grandezze vere —
   «Profondità spalletta» accanto a «Grinze».

   Adesso si appoggiano in **cima**: il disegno sta in una fascia di altezza fissa, il nome comincia
   subito sotto e quindi alla stessa quota per tutte, e il vuoto che avanza cade **in fondo**, fra
   l'intestazione e la prima riga, dove non sposta niente. È ciò che il committente ha chiesto il
   16/09.

   *Non con `display: flex` sulla cella: toglie alla `th` il comportamento di cella e la riga
   d'intestazione si impila in verticale — provato, e le sei colonne finivano una sotto l'altra.* */
.summary__table thead th { vertical-align: top; }

.summary__table thead th.numeric { padding-bottom: 12px; }

/* La fascia del disegno c'è **anche dove il disegno manca**, o le colonne senza icona alzerebbero il
   proprio nome dell'altezza di un'icona rispetto alle vicine. */
.summary__table thead th.numeric .param-icon {
    display: block;
    height: var(--param-icon-h);
    margin-left: auto;
}

.summary__table thead th.numeric:not(:has(.param-icon))::before,
.summary__table thead th:first-child::before {
    content: "";
    display: block;
    height: var(--param-icon-h);
}

/* La stessa fascia vale per la prima colonna (14/10): «Unità» stava sulla riga dei disegni, venti pixel sopra i nomi
   delle grandezze accanto. */

.summary__name { display: block; font-weight: 700; }

.summary__unit { display: block; font-size: var(--text-label); color: var(--text-muted); }

/* **Le intestazioni con i nomi delle grandezze restano a 13**, un gradino sotto le altre etichette (07 §91.9):
   sono loro a decidere la larghezza delle colonne, e a 14 il riepilogo della Misura — che non deve scorrere,
   S.58 — scorreva di lato per 45 px alla risoluzione del committente. A 13 torna intero anche con le celle a
   18. Valgono uguali nella scheda Misure di Visualizza, che usa le stesse intestazioni. */
th:has(> .summary__name) { --text-label: 13px; }

/* I numeri incolonnati: cifre a larghezza fissa, o le colonne ballano a ogni tasto battuto. */
.summary__table td.numeric { font-variant-numeric: tabular-nums; white-space: nowrap; }

.summary__table td.numeric.is--ok          { color: var(--status-ok-ink); }
.summary__table td.numeric.is--warning     { color: var(--status-warning-ink); }
.summary__table td.numeric.is--error       { color: var(--status-error-ink); }
.summary__table td.numeric.is--implausible { color: var(--status-implausible-ink); }

/* **L'esito non si tiene attaccato al bordo destro**, ed è stato provato: con dodici grandezze la
   colonna sticky galleggia *sopra* quelle che le scorrono sotto, e la pastiglia si legge in mezzo
   ai numeri di un'altra misura. Peggio del problema che risolveva. Resta in fondo, dove il disegno
   la mette, e la si raggiunge scorrendo come le altre — ma **con le parole corte**, perché era la
   loro lunghezza a farla uscire. */

/* La riga di prosa che dichiara la modalità: è un'informazione che dura, non un avviso da chiudere,
   quindi non prende i colori dell'attenzione. */
.summary__mode {
    margin: 0;
    font-size: var(--text-secondary-size);
    font-weight: 600;
    color: var(--text-secondary);
}

/* La colonna della terna resta ferma mentre le misure scorrono: è la coordinata che dice di quale
   unità si sta leggendo la riga, e senza di essa una riga in mezzo non si sa più di chi sia. */
.summary__table tbody th {
    position: sticky;
    left: 0;
    z-index: 1;
    /* **Il fondo della scheda, e non `--surface`**, che fra i token non è mai esistito: la cella era
       trasparente, e scorrendo in orizzontale i numeri passavano sotto la terna. Trovato il 29/09
       costruendo l'Archivio, che usa questa stessa regola; vale anche per `.history__mark`. */
    background: var(--card-bg);
    font-variant-numeric: tabular-nums;
}

.trend__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 12px;
}

.trend__head .trend__title { margin: 0; }

/* **Il selettore va ristretto a mano**: la regola generale dà `width: 100%` a ogni `select`, che è
   giusto dentro un campo di modulo e sbagliato per un comando che sta in una testata accanto a un
   titolo — largo tutta la riga sembra il campo principale della schermata invece di una scelta di
   come si guarda. */
.trend__pick { width: auto; max-width: 26ch; }

/* Il segno d'esito su una scheda della cronologia: sta in alto a destra sulla miniatura, dove non
   copre la coordinata e si vede anche su una fotografia scura. */
.history__mark {
    position: absolute;
    top: 4px;
    right: 4px;
    font-size: var(--text-label);
    font-weight: 700;
    line-height: 1;
    padding: 2px 4px;
    border-radius: var(--radius-btn);
    background: var(--card-bg);
}

.history__mark.is--ok          { color: var(--status-ok-ink); }
.history__mark.is--warning     { color: var(--status-warning-mark); }
.history__mark.is--error       { color: var(--status-error-ink); }
.history__mark.is--implausible { color: var(--status-implausible-ink); }


/* ═══ L'AVANZAMENTO AUTOMATICO (03 §A.6) ════════════════════════════════════════════════════════

   La cronologia non cambia forma né posto: cambia l'ambito di ciò che la riempie. Quel che si
   aggiunge è il nome della misura su ogni scheda — la lista mescola passi diversi — e lo stacco fra
   un gruppo e l'altro. */

.history__measure {
    font-size: var(--text-label);
    font-weight: 700;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

/* Lo stacco fra due misure. **Porta il nome di ciò che comincia**, e non è un filetto muto: la lista
   si legge scorrendo, e un filetto costringe a interpretarlo — «finisce qualcosa» senza dire cosa.

   Sta in testa al suo gruppo e il filetto lo separa da quello di sopra, quindi lo spazio sta dalla
   parte del filetto: sotto il nome ce n'è appena, perché le schede che seguono gli appartengono.
   *Questa nota diceva il contrario fino al 21/09*, quando la colonna era resa con `column-reverse` e
   «sopra» e «sotto» erano scambiati rispetto al markup — il rovesciamento è finito, ed era metà
   della S.67. */
.history__break {
    margin: 6px 0 2px;
    padding-top: 8px;
    border-top: 1px solid var(--border-soft);
    font-size: var(--text-label);
    font-weight: 700;
    color: var(--text-faint);
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

/* Il primo apre la lista e non ha niente sopra da cui separare: il filetto resterebbe appeso al
   bordo della colonna. */
.history__break:first-child {
    margin-top: 0;
    padding-top: 0;
    border-top: none;
}

.history__empty {
    margin: 0;
    color: var(--text-muted);
    font-size: var(--text-secondary-size);
}

/* Le due modalità di lavoro, in Impostazioni · Misura: due riquadri con pari dignità, non una
   casella che ne spunta una. Chi sceglie deve leggere anche che cosa lascia. */
.modes {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 12px;
    margin: 0;
    padding: 0;
    border: 0;
}

.mode {
    display: grid;
    gap: 4px;
    padding: 14px;
    border-radius: var(--radius-panel);
    box-shadow: inset 0 0 0 2px var(--border-soft);
    cursor: pointer;
}

/* Il riquadro scelto si marca con **il bordo e il peso**, non con un pieno colorato: il verde è
   dell'azione principale e il giallo dice «sei qui» in una procedura, e questa non è né l'una né
   l'altra cosa. */
.mode:has(.mode__pick:checked) { box-shadow: inset 0 0 0 2px var(--text-primary); }

.mode__pick { justify-self: start; accent-color: var(--text-primary); }

.mode__name { font-size: var(--text-secondary-size); font-weight: 700; }

.mode__hint { font-size: var(--text-label); color: var(--text-secondary); }

@media (max-width: 560px) {
    .legend { grid-template-columns: 1fr; }
    .legend dd { margin-bottom: var(--tap-gap); }
}


/* ─── L'Archivio (07 §66) ─────────────────────────────────────────────────────────────────────
   La lista dei controlli e Visualizza. Registro «scrivania», che è quello dell'applicazione: qui si
   sta seduti e si consultano tabelle. */

/* Una riga si apre con un clic (03 §A.10), e il cursore lo dice. */
.archive__table tbody tr[data-href] { cursor: pointer; }
.archive__table tbody tr[data-href]:hover { background: var(--border-softer); }

/* La data porta il collegamento per chi non ha JavaScript, ma non si sottolinea: la riga intera si
   apre, e una sola cella sottolineata direbbe che solo lei lo fa. */
.archive__open { color: inherit; text-decoration: none; }

/* Un controllo nascosto si vede solo con l'interruttore acceso, e lì deve distinguersi da quelli in
   evidenza: più tenue, non barrato — non è cancellato. Vale per ogni tabella, e allo stesso modo per una
   riga che non entra: nel wizard dell'importazione, un controllo già importato o un'utenza lasciata fuori. */
.table tbody tr.is-hidden td { color: var(--text-muted); }

/* L'intestazione di Visualizza: che cosa si guarda a sinistra, com'è andato a destra. */
.archive__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
}

.archive__title { margin: 0; font-size: 19px; font-weight: 800; }

.archive__head .card__note { margin: 4px 0 0; }

/* Le schede e il selettore della testa sulla stessa riga (07 §66.3): la testa restringe ciò che le
   schede mostrano, e sta dove l'occhio va a scegliere che cosa guardare. */
.archive__bar {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}

.archive__bar .tabs { margin-bottom: 0; }

/* I selettori di una barra stanno in fila: nel Dettaglio a confronto sono due — parametro e seconda
   serie — e impilati alzavano la barra di una riga. */
.archive__head-pick { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 20px; margin: 0; }
.archive__head-pick .field { margin: 0; }

/* **Il confine fra due controlli** (07 §66.4): una riga piena, più alta delle altre, con un filetto
   sopra che la stacca dal controllo precedente. Non prende il verde né il giallo — non è un'azione e
   non è «sei qui» — ma il fondo delle intestazioni, perché è un'intestazione: quella del blocco. */
.sheet__break th {
    position: static;
    padding-top: 14px;
    padding-bottom: 10px;
    background: var(--border);
    border-top: 2px solid var(--text-muted);
    text-align: left;
    font-weight: 700;
    /* È una cella d'intestazione, ma porta un nome e una pastiglia, non un'etichetta di colonna: il
       maiuscoletto delle intestazioni li scriveva «S. VERDI» e «CONFORME». */
    font-size: var(--text-body);
    text-transform: none;
    letter-spacing: normal;
    color: var(--text-primary);
}

.sheet__break th > * { margin-right: 14px; vertical-align: middle; }

/* Il chevron dell'albero, dentro una cella: `display: flex` lo toglierebbe dalla riga di testo e
   andrebbe a capo da solo. */
.sheet .tree__twist { display: inline-flex; vertical-align: middle; }

.sheet__break th > .tree__twist { margin-right: 6px; }

/* **Una condizione sulla chiave** (07 §66.14): chiave e valore uno sotto l'altro come gli altri campi
   del pannello, e la ✕ che la toglie accanto, in alto. */
.key-condition {
    display: grid;
    grid-template-columns: 1fr auto;
    column-gap: 8px;
    align-items: end;
}

.key-condition > .field { grid-column: 1; }

.key-condition > [data-remove-key] { grid-column: 2; grid-row: 1 / span 2; align-self: center; }

/* Compresso, di un controllo resta la riga che lo nomina (07 §66.14). */
.sheet tbody[data-closed] > tr:not(.sheet__break) { display: none; }

.sheet__when {
    color: var(--text-secondary);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

tbody th .sheet__when { margin-right: 6px; font-size: var(--text-label); }

/* La miniatura sta nella riga senza alzarla: è un indice (03 §A.13 regola 3), la fotografia vera si
   apre toccandola. */
.sheet__photo { width: 1%; padding-left: 0; }

.sheet__thumb {
    display: block;
    width: 48px;
    height: 32px;
    object-fit: cover;
    border-radius: var(--radius-btn);
    background: #000;
}

/* Le due forme delle Statistiche sono **schede piccole**, non un componente nuovo: scegliere come
   guardare gli stessi dati è lo stesso gesto che scegliere che cosa guardare (07 §66.2). */
.tabs--compact { padding: 4px; }
.tabs--compact .tabs__item { min-height: 28px; padding: 0 12px; font-size: var(--text-secondary-size); }

/* ─── L'istogramma — Pages/Shared/_Histogram.cshtml ──────────────────────────────────────────
   Le righe hanno i colori del grafico dell'andamento, che è il suo compagno di schermata: rosso tenue
   per i limiti, ambra tratteggiata per l'attenzione, grigio per l'obiettivo. Le classi sono in
   inchiostro neutro: a giudicare sono le righe, non il colore delle barre. */
/* Sotto il disegno due fasce: i confini delle classi, e sotto ancora i valori dei limiti. */
.histogram { position: relative; padding-bottom: 42px; }

.histogram__plot { display: block; width: 100%; height: 160px; overflow: visible; }

.histogram__bin { fill: var(--text-faint); opacity: 0.45; }

.histogram__curve {
    fill: none;
    stroke: var(--text-secondary);
    stroke-width: 2;
    vector-effect: non-scaling-stroke;
}

.histogram__limit,
.histogram__warning,
.histogram__target { stroke-width: 1.5; vector-effect: non-scaling-stroke; }

.histogram__limit   { stroke: var(--status-error-ink); opacity: 0.7; }
.histogram__warning { stroke: var(--status-warning-mark); stroke-dasharray: 6 6; opacity: 0.85; }
.histogram__target  { stroke: var(--text-faint); }

.histogram__keys { position: absolute; left: 0; right: 0; bottom: 0; height: 18px; }

.histogram__key {
    position: absolute;
    transform: translateX(-50%);
    font-size: var(--text-label);
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.histogram__key--limit { color: var(--status-error-ink); }

.histogram__edges { position: absolute; left: 0; right: 0; bottom: 20px; height: 18px; border-top: 1px solid var(--border-soft); }

.histogram__edge {
    position: absolute;
    top: 2px;
    transform: translateX(-50%);
    font-size: var(--text-label);
    color: var(--text-faint);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* **La seconda serie di un confronto**, con lo stesso colore nei tre grafici del Dettaglio (07 §66.15):
   barre, curva, steli e violino. La prima serie resta com'è. */
.histogram__bin--compare { fill: var(--series-compare); opacity: 0.5; }

.histogram__curve--compare { stroke: var(--series-compare); }

.chart__pair { display: flex; align-items: flex-end; gap: 3px; height: 100%; }

.chart__pair > .chart__stem { width: 12px; }

.chart__stem--compare { background: var(--series-compare); }

.violins__item--compare .violins__shape { fill: var(--series-compare); stroke: var(--series-compare); opacity: 0.45; }

/* La legenda delle due serie, una volta sola in cima al Dettaglio: vale per tutti e tre i grafici. */
.series-keys { display: flex; flex-wrap: wrap; gap: 8px 24px; }

.series-key {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: var(--text-secondary-size);
    color: var(--text-secondary);
    font-variant-numeric: tabular-nums;
}

.series-key::before {
    content: "";
    width: 14px;
    height: 14px;
    border-radius: 3px;
    background: var(--text-faint);
    opacity: 0.6;
}

.series-key--compare::before { background: var(--series-compare); opacity: 0.8; }

/* ─── La fila di violini — Pages/Shared/_Violins.cshtml ──────────────────────────────────────
   Gli stessi colori dell'istogramma, che è suo fratello: righe rosse tenui per i limiti, ambra
   tratteggiata per l'attenzione, grigio per l'obiettivo, sagome in inchiostro neutro. Ogni violino è
   una colonna di pari larghezza; con tante colonne la fila scorre dentro il suo riquadro. */
/* Le colonne hanno un tetto: con due parametri un violino largo mezzo schermo è una macchia, e la
   forma si legge meglio stretta. La fila allora si centra; con molte colonne si stringono fino al
   minimo, e oltre scorre. */
.violins {
    position: relative;
    display: flex;
    align-items: stretch;
    justify-content: center;
    gap: 0;
    overflow-x: auto;

    /* **In verticale non si scorre mai**, e va detto: con `overflow-x` diverso da `visible` anche l'altro
       asse diventa `auto`, e l'etichetta sotto il violino — una riga da 1,2 volte il carattere — è alta
       qualche decimo di pixel più di quanto il riquadro arrotondi. Mezzo pixel di eccedenza, e su
       Windows, dove le barre di scorrimento non si nascondono, una barra verticale accanto a ogni fila
       di violini (*09* §14). */
    overflow-y: hidden;
}

.violins__keys { position: relative; flex: none; width: 4.5em; height: 200px; }

.violins__key {
    position: absolute;
    right: 6px;
    transform: translateY(50%);
    font-size: var(--text-label);
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.violins__key--limit { color: var(--status-error-ink); }

.violins__item {
    display: flex;
    flex-direction: column;
    align-items: center;
    flex: 1 1 0;
    min-width: 56px;
    max-width: 120px;
    color: inherit;
    text-decoration: none;
}

a.violins__item[href]:hover .violins__shape { opacity: 0.6; }

.violins__plot { display: block; width: 100%; height: 200px; overflow: visible; }

.violins__shape { fill: var(--text-faint); opacity: 0.4; stroke: var(--text-secondary); stroke-width: 1; vector-effect: non-scaling-stroke; }

.violins__span { stroke: var(--text-secondary); stroke-width: 1; stroke-dasharray: 2 3; vector-effect: non-scaling-stroke; }

.violins__tick { stroke: var(--text-primary); stroke-width: 1.5; vector-effect: non-scaling-stroke; }

.violins__limit,
.violins__warning,
.violins__target { stroke-width: 1.5; vector-effect: non-scaling-stroke; }

.violins__limit   { stroke: var(--status-error-ink); opacity: 0.7; }
.violins__warning { stroke: var(--status-warning-mark); stroke-dasharray: 6 6; opacity: 0.85; }
.violins__target  { stroke: var(--text-faint); stroke-dasharray: 3 4; }

.violins__label {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    margin-top: 8px;
    padding: 0 2px;
    font-size: var(--text-label);
    line-height: 1.2;
    text-align: center;
    color: var(--text-secondary);
}

/* Uno stato sotto il nome, non una spiegazione: «senza limiti». */
.violins__state { color: var(--text-muted); }

/* Il nome del parametro nel Riepilogo porta al Dettaglio: un collegamento che si legge come il nome,
   con l'icona accanto come nelle intestazioni del tabellone. */
.archive__stats-name {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    vertical-align: middle;
    color: inherit;
    text-decoration: none;
}

.archive__stats-name:hover { text-decoration: underline; }

/* **Il nome prende lo spazio che avanza**, così la colonna dell'unità sta accanto ai numeri (*09* C.1):
   con due teste i numeri stavano in fondo a destra e l'unità in coda al nome, a un metro di distanza. */
.archive__stats th:first-child,
.heads-grid th:first-child { width: 100%; }

/* E le intestazioni dei numeri su una riga: stretta dal nome, «Sotto il limite» andava a capo tre volte. */
.archive__stats thead th,
.heads-grid thead th { white-space: nowrap; }

/* «media» sotto «Testa N» è una nota, non un'intestazione: niente maiuscole. */
.heads-grid thead .summary__unit { text-transform: none; letter-spacing: 0; font-weight: 400; }

/* **La griglia parametro × testa** (07 §66.15): le celle sono quelle del tabellone — numero e segno,
   con il colore del giudizio — e tutta la cella porta al Dettaglio di quella testa. La riga del
   parametro dei violini qui sotto si distingue con il fondo delle intestazioni: non è «sei qui», che è
   giallo, ma la riga che si sta guardando più sotto. */
/* Il nome del parametro è un'intestazione di riga, ma si legge come il nome nel Riepilogo: il
   maiuscoletto delle intestazioni di colonna lo scriveva «SPESSORE AGGRAFFATURA MM». */
.heads-grid th[scope="row"] {
    font-size: var(--text-body);
    font-weight: 400;
    text-transform: none;
    letter-spacing: normal;
    color: var(--text-primary);
    text-align: left;
    white-space: nowrap;
}

.heads-grid tr[aria-current] > * { background: var(--chip-bg); }

.heads-grid__head { color: inherit; text-decoration: none; white-space: nowrap; }

.heads-grid__head:hover,
.heads-grid__cell:hover { text-decoration: underline; }

.heads-grid__cell {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    color: inherit;
    text-decoration: none;
    font-variant-numeric: tabular-nums;
}

.heads-grid td.is--ok      { color: var(--status-ok-ink); }
.heads-grid td.is--warning { color: var(--status-warning-ink); }
.heads-grid td.is--error   { color: var(--status-error-ink); }
.heads-grid td.is--implausible { color: var(--status-implausible-ink); }

/* Più pastiglie affiancate, che non si separano andando a capo: fra gli ultimi controlli della Home la
   validazione accanto all'esito, nella scheda Connessione «Calibri non verificati» accanto allo stato
   della scheda. */
.pills { display: inline-flex; gap: 6px; flex: none; }

/* Una riga delle ultime misure apre il controllo (S.84), e si rende come una riga dell'Archivio: cursore e
   fondo al passaggio, nient'altro. Il fondo esce di poco dalla riga, o toccherebbe il testo. */
.recent__open {
    display: flex;
    flex: 1;
    align-items: center;
    justify-content: space-between;
    gap: var(--gap);
    margin: -10px -8px;
    padding: 10px 8px;
    border-radius: var(--radius-btn);
    color: inherit;
    text-decoration: none;
}

.recent__open:hover { background: var(--border-softer); }

/* Una sezione **ferma** — la fotografia dell'Archivio — non si trascina: le maniglie non promettono
   una presa che non c'è. */
.points[data-still] .point__target { cursor: default; }

/* La fotografia dell'Archivio in un dialogo: il riquadro è quello del passo Telecamera, ma la misura
   non gliela dà un palco — gliela dà la finestra. Largo quanto l'altezza concede al 4:3, e mai oltre
   lo schermo: alla risoluzione del committente (1920 × 928) il dialogo non deve scorrere. */
.dialog--photo {
    width: min(1280px, calc((100vh - 120px) * 4 / 3 + 2 * var(--pad-m)), calc(100vw - 32px));
}

.archive__photo {
    width: 100%;
    height: auto;
    aspect-ratio: 4 / 3;
}


/* ─── L'importazione ───────────────────────────────────────────────────────────────────────────
   Il wizard della pagina finta approvata (docs/mockup-importazione, 07 §74): queste sono le regole che il
   prodotto non aveva, e niente di più. Cornice, briciole, albero, tabelle e pastiglie sono quelli di sempre. */

/* L'albero delle Ricette ha per voci dei collegamenti; qui la voce mette a fuoco la scheda accanto, quindi è
   un pulsante — vestito come la voce, senza il bordo del pulsante. */
button.tree__item {
    width: 100%;
    border: 0;
    background: none;
    font: inherit;
    text-align: left;
    cursor: pointer;
}
button.tree__item[aria-current] { background: var(--nav-active-bg); }

/* La casella a tre stati sta prima del chevron: è il gesto principale di questo albero. */
.tree__check { flex: none; margin: 0 4px 0 2px; }

/* Il conteggio dei controlli, a destra del nome: è lo stato della scelta, non un'etichetta. */
.tree__count {
    margin-left: auto;
    padding-left: 8px;
    color: var(--text-muted);
    font-size: var(--text-label);
    font-weight: 400;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* L'albero resta fermo mentre la scheda accanto scorre, e scorre per conto suo. È più largo dei 380 delle
   Ricette: ogni riga porta anche casella, conteggio e, dove c'è, la pastiglia della collisione. Nel pannello
   a scorrere è il corpo, quindi l'albero si appiccica al suo bordo e non a quello della pagina. */
@media (min-width: 961px) {
    .import-split { grid-template-columns: 460px minmax(0, 1fr); }
}
.import-tree { top: 0; max-height: calc(100vh - 240px); }
.import-tree__all { margin-bottom: var(--tap-gap); padding: 0 8px; }

/* La scelta fra due destini — fondi o rinomina — una riga per possibilità. Il motivo per cui una non si può
   prendere sta sotto di lei, perché porta un dato che cambia: i due angoli. */
.choice { display: grid; gap: 10px; margin: 0; padding: 0; border: 0; }
.choice__item { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.choice__item input[type="text"], .choice__item select { width: auto; min-width: 260px; }
.choice__item input[type="text"] { width: 36ch; }
.choice__why { flex-basis: 100%; margin-left: 24px; color: var(--text-muted); font-size: var(--text-secondary-size); }

/* Tendine e campi dentro una riga di tabella o di scelta: larghi quanto il contenuto, non quanto la colonna. */
/* Il tetto c'è perché le voci crescono con i nomi dell'archivio: senza, una tendina «Associa a «…»» lunga schiaccia
   la colonna dei nomi e porta la tabella fuori dal riquadro (regola 15). La tendina taglia la voce da sé. */
.import-select { width: auto; max-width: 22rem; }
.import-name { width: 22rem; }
.import-key { flex-wrap: nowrap; gap: 8px; }
.import-note { font-size: var(--text-label); white-space: nowrap; }
.import-period input[type="date"] { width: auto; }
.import-period .field__label { margin: 0; }
.import-problems { margin: 0; padding-left: 1.2em; }

/* L'interruttore delle utenze è un pulsante, perché cambia una scelta invece di aprire una vista. */
button.tree-switch { margin-bottom: 0; border: 0; background: none; font: inherit; cursor: pointer; }

/* La barra dell'avanzamento. Il pieno **non è verde**: dice quanto manca, non che è andata bene. */
.progress {
    height: 10px;
    border-radius: 999px;
    background: var(--border-soft);
    overflow: hidden;
}
.progress__done { height: 100%; background: var(--text-secondary); }
