  :root{
    /* ── Brand verde: UN VERDE SOLO, quello del logo ─────────────────────────
       #003C0F è il verde di RACCOSTA (brand/marchio.js, VERDE.logo) ed è la
       tinta di riferimento: hue 135. --green e --green2 sono lo STESSO valore,
       e non è una svista: la distinzione fra «verde d'identità» (scuro) e
       «verde d'azione» (smeraldo) è stata TOLTA di proposito, perché dava due
       verdi che non erano nemmeno parenti — 151 contro 146÷148 di hue — e il
       secondo non era il colore dell'azienda. Resta UN gradino, --green3, per
       gli hover e per le superfici che devono staccarsi dal pieno.
       ⚠️ Ogni verde del foglio appartiene a hue 135, barra laterale compresa.
       Un verde nuovo che non passi da qui si riconosce a occhio perché tira al
       ciano: prima di scriverne uno, guarda se un token esiste già.
       ⚠️ --green è ANCHE lo sfondo pieno di ~20 controlli attivi con testo
       bianco: in tema scuro quelli passano da --green-solid (elenco più sotto),
       o diventano bianco su verde chiaro. ── */
    --green:#003C0F; --green2:#003C0F; --green3:#005C17; --soft:#EAF2EB; --soft-bd:#C6DCCB;
    --tan:#9a6f2c; --tan2:#b98a3e;
    /* Verde «tutto a posto»: il gemello di --amber-* (banner di sincronizzazione
       in app.1, card stato backup in app.5). Erano USATI in 9 punti e non
       definiti da nessuna parte, quindi cadevano da sempre sul ripiego letterale
       #ecfdf5/#a7f3d0/#065f46 — un'altra famiglia di verde, per giunta chiara
       anche in tema scuro. */
    --green-bg:#E7F3EA; --green-bd:#BDD9C4; --green-tx:#0A4A18;
    /* Anello di messa a fuoco: era scritto a mano in QUATTRO rgba diversi
       (42,135,73 · 47,107,67 · 28,138,78 · 63,138,89), cioè quattro colori per
       lo stesso gesto. Uno solo, sulla tinta del logo. */
    --ring:rgba(0,60,15,.14);
    /* ── Barra laterale ───────────────────────────────────────────────────────
       È verde scura in ENTRAMBI i temi, quindi i suoi colori vivono qui e non
       nel blocco scuro. Erano quattordici valori scritti a mano a hue 140÷148:
       con l'app a 135 la barra sarebbe rimasta l'unico elemento smeraldo, ed è
       la superficie più grande dello schermo.
       ⚠️ I livelli di testo sono QUATTRO perché la gerarchia (nome utente >
       ruolo) porta informazione. Il quarto era #7fa389 e stava a 4,33:1
       sull'estremo chiaro del gradiente, cioè sotto AA: ora 5,22:1.
       ⚠️ Il pieno verde --green su questo fondo dà 1,02:1 — sparirebbe. Nella
       barra i controlli attivi si segnano col bianco trasparente, non col
       verde: se ci metti un .btn.primary non lo vedrà nessuno. */
    --side-bg1:#163B20; --side-bg2:#0D2B15;
    --side-tx:#DEE8E0; --side-tx2:#C2D6C7; --side-tx3:#9FC1A7; --side-tx4:#8DB095;
    --side-acc:#32D25A; --side-acc2:#80E098; --side-hi:#EBFFF0; --side-menu:#15321C;
    --side-tan:#D4A557;  /* il sottotitolo del marchio: --tan2 dava 3,91:1 */
    /* ── Neutri freddi (famiglia slate): superfici pulite e ariose ── */
    --bg:#eef2f6; --panel:#ffffff; --card:#ffffff; --sidebar:#f1f5f9;
    --line:#e9eef3; --border:#e2e8f0; --line2:#cdd7e1;
    --ink:#0f2438; --muted:#64748b; --muted2:#94a3b8;
    --sans:'Hanken Grotesk',-apple-system,Arial,sans-serif;
    --serif:'Fraunces',Georgia,serif;
    --amber-bg:#fbf2dd; --amber-tx:#8a6516; --amber-bd:#e6cf9a;
    --red:#c0392b; --red2:#a93226;
    --yellow:#e3a712; --yellow2:#c9920a; --yellow-tx:#3d2c00;
    --content-pad:24px; --appbar-h:58px;
    /* ── Scala raggi coerente ── */
    --r-sm:9px; --r:13px; --r-lg:18px;
    /* ── Elevation: ombre morbide stratificate, tono freddo ── */
    --sh-sm:0 1px 2px rgba(15,36,56,.05),0 1px 1px rgba(15,36,56,.04);
    --sh:0 1px 2px rgba(15,36,56,.05),0 6px 18px -6px rgba(15,36,56,.12);
    --sh-lg:0 12px 38px -10px rgba(15,36,56,.22);
    /* ── Colori di stato semantici (accento + fondo tenue + testo carico) ── */
    --st-bozza:#94a3b8; --st-bozza-bg:#eef1f5; --st-bozza-tx:#5b6878;
    --st-lav:#e0a020; --st-lav-bg:#fdf3da; --st-lav-tx:#92670c;
    --st-pronta:#3b82f6; --st-pronta-bg:#e8f1fe; --st-pronta-tx:#1d4ed8;
    /* «Consegnata» era a hue 147: l'unico verde semantico rimasto smeraldo
       accanto a un brand a 135. Riallineata la sola TINTA, saturazione e
       luminosità invariate — il contrasto testo/fondo passa da 4,79 a 4,82. */
    --st-cons:#1F9E3F; --st-cons-bg:#E5F5E9; --st-cons-tx:#157A2E;
    --st-late:#ef4444; --st-late-bg:#fdecec; --st-late-tx:#c0392b;
    /* catena di lavorazione: il viola della pastiglia 🔗, ora in variabili così
       il tema scuro si governa in un punto solo (era scritto a mano in due). */
    --catena-bg:#f0eaff; --catena-tx:#5b3ea8; --catena-bd:#ddd0f3;
    /* ── Motion design tokens (curve + durate condivise per coerenza) ── */
    --ease:cubic-bezier(.4,0,.2,1);            /* standard, in/out bilanciato */
    --ease-out:cubic-bezier(.16,1,.3,1);        /* decelerazione: ingressi/aperture */
    --ease-spring:cubic-bezier(.34,1.4,.5,1);   /* leggero overshoot: pressioni/FAB */
    /* ⚠️ LA CURVA DI USCITA MANCAVA, e con lei mancavano le uscite (v20): in
       tutto il foglio non c'era un solo keyframe che portasse qualcosa FUORI —
       tredici animazioni, tutte d'ingresso o rotazioni. Una cosa che se ne va
       accelera (parte piano e sparisce svelta): è l'inverso di --ease-out, e
       usare quella per un'uscita fa sembrare che l'elemento si incolli.
       ⚠️ Le uscite sono più BREVI degli ingressi: chi guarda ha già deciso, e
       ogni millisecondo in più è attesa. Da qui --d-exit, non --d.
       ⚠️ --ease-spring resta per la sola PRESSIONE dei comandi: supera il
       valore d'arrivo del 40%, e su un foglio che sale sono 43px oltre il
       bordo — la fascia vuota in fondo all'iPhone, già pagata. */
    --ease-in:cubic-bezier(.4,0,.85,.85);
    --d-fast:.13s; --d:.2s; --d-slow:.3s; --d-exit:.18s;
    /* ── Alias storici: erano USATI in 19 punti (Calendario consegne compreso)
       ma non definiti da nessuna parte, quindi quelle regole cadevano sul
       fallback e ignoravano il tema scuro. Definirli qui li riallinea tutti. ── */
    --accent:var(--tan); --text:var(--ink); --fg:var(--ink);
    --brand:var(--green); --amber-ink:var(--amber-tx);
    /* ── Spazio in fondo all'elenco: UNA sola somma, scritta in un solo punto.
       Prima era un padding-bottom riscritto da quattro regole diverse e vinceva
       quella più specifica (html.ios-standalone), annullando la riserva del dock:
       i chip coprivano l'ultima riga proprio in PWA installata. Ora ogni
       elemento che galleggia in basso dichiara il SUO ingombro qui. ── */
    --pb-fondo:24px;    /* aria sotto l'ultima riga (qui stava la FAB 📷, ora nel menu) */
    --pb-dock:0px;      /* riquadro «Da riprendere»: lo scrive renderDock */
    --pb-tabbar:0px;    /* barra di navigazione in basso, quando attiva */
  }
  *{margin:0;padding:0;box-sizing:border-box;}
  /* Il DOCUMENTO non scorre e non rimbalza: lo scroll/rimbalzo vive dentro
     .content (l'elenco). Così le barre in alto restano assolutamente ferme. */
  html{height:100%;overflow:hidden;overscroll-behavior:none;background:var(--bg);}
  body{font-family:var(--sans);background:var(--bg);color:var(--ink);font-size:15px;line-height:1.5;-webkit-font-smoothing:antialiased;height:100%;overflow:hidden;}
  .tnum,.kpi .n,.stab td,.stab th,.barrow .v,.rnum{font-variant-numeric:tabular-nums;}
  a{color:inherit;text-decoration:none;}

  /* Scroll "a contenitore": la finestra NON scorre; scorre solo .content.
     Così le barre in alto (appbar + filtri) restano SEMPRE ferme e solo
     l'elenco scorre/rimbalza (niente più rubber-band dell'intera pagina con
     le barre che ballano su iOS).
     ALTEZZA: usiamo 100vh, NON 100dvh. Siccome la finestra non scorre mai, su
     iOS Safari (scheda browser) la toolbar in basso non riceve il segnale di
     scroll e NON si accorcia: con 100dvh l'altezza restava "incastrata" sul
     valore piccolo e sotto l'app spuntava una FASCIA GRIGIA fissa (lo sfondo
     --bg del body). 100vh = viewport "grande" = schermo intero: l'app riempie
     tutto, l'elenco arriva fino in fondo e la fascia sparisce. Quando la
     toolbar di Safari è estesa può coprire un filo il fondo dell'elenco,
     recuperato dal padding-bottom di .content. In PWA installata (standalone)
     non c'è toolbar: vedi il blocco @media più sotto. */
  .app{display:flex;height:100vh;overflow:hidden;}
  /* PWA INSTALLATA (standalone): qui NON c'è la toolbar dinamica di Safari.
     In standalone <html> e <body> a height:100% riempiono GIÀ lo schermo reale
     (safe-area incluse grazie a viewport-fit=cover), quindi ancoriamo l'app a
     quell'altezza con height:100%. È più affidabile sia di 100vh/100dvh (su iOS
     risultano più CORTI dello schermo reale → fascia grigia in basso) sia di
     position:fixed+inset:0 (su alcuni iOS il fixed resta dimensionato sul
     viewport "piccolo" e la fascia grigia restava comunque). Riserviamo SOLO la
     sottile striscia della safe-area inferiore (home indicator) come
     padding-bottom: il contenuto resta appena SOPRA la barra dei gesti. */
  @media all and (display-mode: standalone){
    /* ⚠️ NIENTE `padding-bottom` qui — vedi la nota estesa più sotto, accanto a
       `.app{height:var(--app-h,100vh)}`. Il padding accorcia il contenuto del
       guscio, la barra in basso (che vive DENTRO `.main`) si ferma prima del
       bordo e sotto restano scoperti i px della safe-area, colorati del fondo
       pagina: è la fascia grigia. La home bar la riservano già `.tabbar`,
       `.content`, `.dock` e `.totop`, ognuno per conto proprio.
       ⚠️ Questa regola è la SECONDA copia dello stesso padding: l'altra stava
       su `html.ios-standalone .app`. Toglierne una sola non serve a niente —
       i due blocchi si applicano allo stesso dispositivo, per due segnali
       diversi, e ne basta uno per riprodurre il difetto. */
    .app{height:100%;}
    /* In PWA non c'è la toolbar di Safari da scavalcare: i ~80px di padding
       inferiore dell'elenco sono spazio sprecato. Li riduciamo così le schede
       arrivano fino in fondo. Specificità .app .content per vincere sulla regola
       mobile .content senza spostarla. */
    /* niente override del fondo: la somma --pb-fondo/--pb-dock/--pb-tabbar vale anche qui,
       ed è ciò che tiene l'ultima riga fuori da sotto la FAB e dal dock. */
  }
  /* PWA iOS installata rilevata via navigator.standalone (vedi viewport-fix.js):
     più affidabile del media query CSS sopra, che su alcune versioni di iOS NON
     combacia. --app-h = window.innerHeight + safe-area-top: su iOS standalone
     innerHeight SOTTO-CONTA dell'inset superiore (Dynamic Island), così .app
     restava più corto e lasciava la fascia in basso. Sommando l'inset top il
     guscio copre TUTTO lo schermo; padding-bottom:safe-area tiene il contenuto
     sopra l'home indicator (che è dentro la web view, in fondo). La specificità
     (html.ios-standalone .app) batte .app{height:100vh} a prescindere
     dall'ordine. */
  /* Solo a SCHERMO: in stampa un'altezza fissa su html/body romperebbe la catena
     che dà a .pg il suo `min-height:100%` (vedi @media print), e il piè di
     pagina non resterebbe a filo del fondo foglio. Nella PWA installata queste
     regole erano attive anche in stampa. */
  @media screen{
    html.ios-standalone,html.ios-standalone body{height:var(--app-h,100%);}
    /* ⚠️ L'altezza del guscio NON è più gated sul rilevamento della PWA: quel
       rilevamento non è affidabile (su alcune versioni di iOS `navigator.
       standalone` è falso E il media query non combacia, quindi l'app non sa
       di essere installata mentre lo è). Finché era gated, in quel caso `.app`
       ricadeva su `height:100vh` — corto di un notch nella web view standalone
       — e sotto la barra di navigazione restava la fascia grigia: è il difetto
       storico, e nessuna correzione del CALCOLO poteva risolverlo, perché il
       valore calcolato non veniva nemmeno letto. Ora `--app-h` la misura una
       sonda ancorata al viewport (viewport-fix.js) ed è corretta in ogni
       contesto, quindi vale sempre; il fallback `100vh` copre il no-JS.
       Il padding-bottom resta invece riservato alla PWA installata: solo lì
       l'home indicator è DENTRO la web view. */
    .app{height:var(--app-h,100vh);}
    /* ⚠️ NIENTE `padding-bottom:env(safe-area-inset-bottom)` su `.app`, ed è la
       causa vera della fascia grigia in fondo alla PWA installata — quella che
       tre giri di correzioni all'ALTEZZA non hanno toccato, perché l'altezza
       era già giusta. Misurato sul telefono dell'utente con la diagnostica:
       `--app-h` 874, `.app` 874 (arriva al fondo), ma `.main` 840 e barra in
       basso che finisce a 840, con 34px scoperti sotto = esattamente
       l'inset. Il padding accorciava il contenuto di `.app`, la `.tabbar` —
       che è `absolute; bottom:0` DENTRO `.main` — si fermava lì sopra, e quei
       34px restavano colorati del fondo pagina invece che della barra.
       Era anche un DOPPIO conteggio: la `.tabbar` riserva già la home bar col
       proprio `padding-bottom: calc(6px + env(safe-area-inset-bottom))`, e
       `.content` la conta nella sua somma `--pb-*`. Gli altri elementi che
       galleggiano in basso (`.dock`, `.totop`) si ancorano al viewport con il
       loro `env()`: nessuno dipendeva da questo padding. */
    /* idem: vedi la nota nel blocco display-mode:standalone qui sopra. */
  }
  /* Diagnostica schermo (Impostazioni): coppie etichetta/valore incolonnate.
     I numeri in tabular-nums così le cifre stanno in colonna e si confrontano
     a colpo d'occhio; il valore è selezionabile per copiarlo a mano. */
  .diaggrid{display:grid;grid-template-columns:1fr auto;gap:2px 14px;font-size:12.5px;
    background:var(--sidebar);border:1px solid var(--border);border-radius:9px;padding:9px 11px;}
  .diagk{color:var(--muted);}
  .diagv{font-weight:800;color:var(--ink);font-variant-numeric:tabular-nums;text-align:right;
    -webkit-user-select:text;user-select:text;}
  /* ── BARRA LATERALE ──────────────────────────────────────────────────────
     Colori tutti da --side-* (definiti in :root, insieme al perché): era il
     pezzo di gestionale col maggior numero di verdi scritti a mano, a hue
     140÷148, e con l'app portata alla tinta del logo sarebbe rimasta l'unica
     superficie smeraldo — la più grande dello schermo, per giunta.
     ⚠️ Non è ridefinita nel blocco del tema scuro: è verde scura in entrambi,
     ed è per questo che i suoi token stanno in :root e non lì. */
  .sidebar{width:240px;flex:0 0 240px;background:linear-gradient(180deg,var(--side-bg1),var(--side-bg2) 94%);color:var(--side-tx);position:fixed;top:0;left:0;bottom:0;display:flex;flex-direction:column;padding:20px 14px;z-index:30;will-change:transform;transform:translateZ(0);overflow-y:auto;overscroll-behavior:contain;}
  /* Testata: MARCHIO a sinistra e nome accanto (v19.39). Prima era il logo disteso
     in un riquadro largo quanto la barra, col nome sotto: 187px di testata, cioè
     due voci e mezzo di menu mangiate prima ancora di cominciare. Ora 75px.
     ⚠️ Il riquadro bianco NON è decorativo: il marchio è verde e sul fondo verde
     della barra sparirebbe. L'alternativa sarebbe una versione in negativo, cioè
     un secondo marchio da mantenere — scartata apposta (vedi brand/README.md). */
  .side-brand{padding:4px 6px 14px;border-bottom:1px solid rgba(255,255,255,.1);margin-bottom:14px;display:flex;align-items:center;gap:11px;}
  .side-brand .logo{width:52px;height:52px;flex:0 0 52px;border-radius:11px;background:#fff;display:flex;align-items:center;justify-content:center;padding:6px;box-sizing:border-box;}
  .side-brand .logo img{width:100%;height:100%;display:block;object-fit:contain;}
  .side-brand .txt{min-width:0;}
  .side-brand .bt{font-family:var(--serif);font-weight:600;font-size:17px;line-height:1.05;color:#fff;letter-spacing:.3px;display:block;}
  .side-brand .bs{font-size:9px;letter-spacing:2.6px;text-transform:uppercase;color:var(--side-tan);font-weight:800;margin-top:4px;display:block;}
  /* ══════════════════════════════════════════════════════════════════
     IL TRATTO DELLE ICONE — si scrive QUI, una volta sola.

     ⚠️ Questa regola vale ANCHE per le 25 icone scritte a mano dentro
     l'index (nav, tabbar, ricerca): gli attributi di presentazione SVG
     (`stroke-width="2"`) hanno specificità ZERO, quindi il CSS li
     scavalca sempre. È il motivo per cui passare tutto il gestionale al
     tratto arrotondato non ha richiesto di riscrivere quel markup — e
     il motivo per cui scrivere `stroke-width` dentro `app.0-icone.js`
     non avrebbe alcun effetto.

     1.8 e non 2: a 16px — la misura delle icone di riga — il tratto 2
     impasta le forme chiuse (il cestino, la botte, il calendario), e le
     terminazioni a spigolo vivo mettono una punta secca in un'interfaccia
     dove ogni angolo è arrotondato.
     ══════════════════════════════════════════════════════════════════ */
  .ic,.nav a svg,.tb-item svg,.search svg,.gsb-ic svg,.nav-toggle svg{
    stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;
  }
  /* Le icone sono `currentColor`: dentro un pulsante verde diventano
     bianche, dentro un badge rosso diventano rosse, in tema scuro seguono
     il testo. Non dare mai un colore fisso a una `.ic`. */
  /* ⚠️ LA MISURA È IN `em`, NON IN PIXEL — e non è un vezzo tipografico.
     Un'emoji è un CARATTERE: cresce col `font-size` di chi la contiene, e
     in decine di punti l'app contava su questo (`.pvb-ic{font-size:19px}`,
     i chip a 11px, i titoli a 17px). Un'icona a 16px fissi dentro quei
     contenitori è più piccola del glifo che sostituisce, e dove l'altezza
     della riga la dettava il glifo il pulsante si ACCORCIA: nella barra
     del dettaglio «Stato» finiva 3px più basso del primario accanto.
     Con `1em` l'icona occupa ESATTAMENTE il box del glifo che sostituisce,
     e nessuna delle misure pagate care va rifatta. ⚠️ 1em e non 1.15: con
     un filo di margine lo stesso pulsante «Stato» passava a 55,3px contro
     i 52,5 del primario — sfondava dall'altra parte. Il `width` scritto da
     `ico()` resta come ripiego se questa regola non arriva. */
  .ic{flex:0 0 auto;width:1em;height:1em;vertical-align:-.15em;}

  .nav{display:flex;flex-direction:column;gap:3px;flex:1;overflow-y:auto;padding-bottom:8px;scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.18) transparent;}
  .nav a{display:flex;align-items:center;gap:11px;padding:11px 12px;border-radius:11px;color:var(--side-tx2);font-weight:600;font-size:14px;cursor:pointer;transition:background var(--d-fast),color var(--d-fast);position:relative;}
  .nav a svg{width:18px;height:18px;opacity:.7;flex:0 0 auto;}
  .nav a:hover{background:rgba(255,255,255,.06);color:#fff;}
  .nav a:hover svg{opacity:.95;}
  .nav a.active{background:rgba(40,164,71,.22);color:#fff;font-weight:700;}
  .nav a.active svg{opacity:1;color:var(--side-acc2);}
  .nav a.active::before{content:"";position:absolute;left:-14px;top:9px;bottom:9px;width:4px;border-radius:0 4px 4px 0;background:var(--side-acc);}
  /* ⚠️ I CONTEGGI SONO NUMERI NUDI (v19.94), non ventitré pastiglie grigie
     uguali: sono la stessa forma di `.mn-n` nella pagina Menu del telefono, e
     la pastiglia resta a ciò che chiede attenzione. Con 23 righe la pastiglia
     su tutto rendeva invisibile proprio quella che conta. */
  .nav .cnt{margin-left:auto;font-size:12px;font-weight:800;color:var(--side-tx4);
    font-variant-numeric:tabular-nums;}
  .nav a.active .cnt{color:var(--side-hi);}
  /* ⚠️ `.hot` è una CLASSE e non più uno stile inline sul markup: `menuGruppi`
     la legge per riportare il rosso anche nella pagina Menu del telefono. */
  .nav .cnt.hot{background:var(--red);color:#fff;border-radius:999px;min-width:19px;height:19px;
    display:flex;align-items:center;justify-content:center;padding:0 5px;font-size:11px;}
  .nav .cnt.hot:empty{display:none;}
  .nav a.active .cnt.hot{color:#fff;}

  /* ── I GRUPPI SI RICHIUDONO (v19.94) — logica in app.9g ──────────────
     Il separatore diventa un comando. ⚠️ Chiuso si usa `.nav-hid` e NON
     l'attributo `hidden`: quello lo legge `menuGruppi` per saltare le voci
     riservate, e col collasso via attributo la pagina Menu del telefono
     avrebbe perso tredici voci in silenzio. */
  .nav a.nav-hid{display:none;}
  .navsep{display:flex;align-items:center;gap:7px;cursor:pointer;user-select:none;
    -webkit-tap-highlight-color:transparent;}
  .navsep .nsep-t{flex:0 0 auto;}
  /* ⚠️ `--side-tx4` e non `--side-tx3`: il conteggio è un dato di servizio e
     `--side-tx4` è il livello previsto per quello (5,22:1 sul gradiente). */
  .navsep .nsep-n{font-size:10px;font-weight:700;letter-spacing:0;color:var(--side-tx4);
    font-variant-numeric:tabular-nums;}
  .navsep .nsep-cv{margin-left:auto;font-size:13px;line-height:1;color:var(--side-tx4);
    transition:transform var(--d-fast) var(--ease);}
  .navsep.chiuso .nsep-cv{transform:rotate(-90deg);}
  @media(hover:hover){ .navsep:hover{color:var(--side-tx2);} .navsep:hover .nsep-cv{color:var(--side-tx2);} }
  .navsep:focus-visible{outline:2px solid var(--side-acc);outline-offset:2px;border-radius:8px;}
  /* riordinando, il separatore torna un'etichetta: le voci non si nascondono */
  .nav.reordering .navsep{cursor:default;}
  .nav.reordering .navsep .nsep-cv{display:none;}
  /* ── Riordino voci menu (bottone "Modifica ordine menu") ───────────── */
  .nav.reordering a[data-view]{cursor:grab;touch-action:none;-webkit-user-select:none;user-select:none;background:rgba(255,255,255,.05);}
  .nav.reordering a[data-view]::before{content:"⠿";margin-right:2px;font-size:15px;opacity:.7;flex:0 0 auto;letter-spacing:-2px;}
  .nav.reordering a[data-view].active{background:rgba(255,255,255,.05);}
  .nav.reordering a[data-view] .cnt{display:none;}
  .nav.reordering .navsep{opacity:.45;}
  .nav a.nav-dragging{opacity:.55;background:rgba(255,255,255,.16)!important;outline:2px dashed rgba(255,255,255,.4);outline-offset:-2px;}
  #nav-reorder-btn.on{background:rgba(255,255,255,.16)!important;color:#fff!important;}
  .side-foot{margin-top:auto;padding-top:14px;border-top:1px solid rgba(255,255,255,.1);font-size:11.5px;color:var(--side-tx3);}
  /* Chip account compatta + menu a scomparsa (sostituisce le voci impilate) */
  .side-acct{position:relative;}
  .acct-row{display:flex;align-items:stretch;gap:6px;}
  /* ⚠️ LA RIGA ACCOUNT HA LA FORMA DEL MENU (v19.94): avatar tondo con
     l'iniziale, nome sopra, ruolo sotto — come `.mn-acct`/`.mn-av` sul
     telefono. Prima l'avatar era 24px e il nome 13px in una pastiglia
     stretta: sul computer il nome vero di chi è connesso non si leggeva da
     nessuna parte. Il markup non cambia, lo riempie già `applyUserUI`. */
  .acct-chip{display:flex;align-items:center;gap:10px;flex:1;min-width:0;border:1px solid rgba(255,255,255,.16);background:rgba(255,255,255,.05);color:var(--side-tx);border-radius:12px;padding:9px 10px;cursor:pointer;font:inherit;font-size:12.5px;font-weight:700;}
  .acct-chip:hover{background:rgba(255,255,255,.12);}
  /* Avatar a cerchietto con l'iniziale dell'utente (colore neutro: lo stato è solo nel pallino) */
  /* ⚠️ Fondo `--side-acc` con testo SCURO: il verde chiaro della barra su
     testo chiaro darebbe 1,x:1. È la stessa coppia della pillola attiva. */
  .acct-av{flex:none;width:34px;height:34px;border-radius:50%;display:flex;align-items:center;justify-content:center;background:var(--side-acc);color:#0D2B15;font-size:14px;font-weight:800;line-height:1;text-transform:uppercase;}
  .acct-logout{flex:none;width:38px;display:flex;align-items:center;justify-content:center;border:1px solid rgba(255,255,255,.16);background:rgba(255,255,255,.05);color:var(--side-tx);border-radius:10px;cursor:pointer;font-size:14px;line-height:1;}
  .acct-logout:hover{background:rgba(231,76,60,.18);border-color:rgba(231,76,60,.45);}
  .acct-text{flex:1;min-width:0;display:flex;flex-direction:column;line-height:1.18;text-align:left;}
  .acct-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13.5px;font-weight:800;letter-spacing:-.01em;}
  .acct-role{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px;font-weight:650;color:var(--side-tx3);margin-top:1px;}
  .acct-role:empty{display:none;}
  .acct-dot{width:9px;height:9px;border-radius:50%;background:var(--side-acc);flex:none;box-shadow:0 0 0 2px rgba(255,255,255,.07);}
  .acct-caret{flex:none;font-size:14px;color:var(--side-tx3);line-height:1;letter-spacing:1px;}
  .acct-menu{position:absolute;left:0;right:0;bottom:calc(100% + 6px);background:var(--side-menu);border:1px solid rgba(255,255,255,.16);border-radius:12px;padding:8px;box-shadow:0 14px 34px rgba(0,0,0,.45);z-index:60;animation:acctMenuIn var(--d-fast,.16s) var(--ease,ease);}
  @keyframes acctMenuIn{from{opacity:0;transform:translateY(6px);}to{opacity:1;transform:none;}}
  .acct-status{padding:4px 6px 8px;line-height:1.4;}
  .acct-status .lk{display:block;word-break:break-word;}
  .acct-status .savestamp{margin-top:3px;color:var(--side-tx4);font-size:10.5px;}
  .acct-status .savestamp:empty{display:none;}
  .acct-menu-sep{height:1px;background:rgba(255,255,255,.1);margin:2px 0 6px;}
  .acct-item{display:block;width:100%;text-align:left;border:none;background:none;color:var(--side-tx);border-radius:8px;padding:8px;cursor:pointer;font:inherit;font-size:12px;font-weight:600;}
  .acct-item:hover{background:rgba(255,255,255,.1);}
  .acct-item.danger{color:#ffb4ab;}
  .acct-item.danger:hover{background:rgba(231,76,60,.16);}
  .side-foot .ver{margin-top:10px;color:var(--side-tx4);font-size:10.5px;text-align:center;}

  /* `position:relative` è il riferimento del pulsante «torna in cima», che da
     v18.6 c'è anche su desktop. `contain:layout` lo faceva già da sé, ma è una
     conseguenza laterale: dichiararlo evita che togliendo il contain un giorno
     il pulsante scappi ad ancorarsi alla finestra. */
  .main{flex:1;margin-left:240px;min-width:0;min-height:0;display:flex;flex-direction:column;overflow:hidden;contain:layout style;position:relative;}
  /* Barra superiore: elemento fisso in cima a .main, FUORI dallo scroller (.content). */
  .appbar{position:relative;flex:0 0 auto;z-index:20;background:#fff;border-bottom:1px solid var(--line);padding:13px 24px;display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;}
  .appbar h1{font-size:23px;font-weight:800;letter-spacing:-.4px;color:var(--ink);}
  /* ⚠️ IL CONTEGGIO SOTTO IL TITOLO (v19.93). Non aveva NESSUNA regola fuori
     dal blocco ≤640px, quindi sul computer ereditava i 15px e il colore pieno
     del corpo: era il sottotitolo più grande dell'app — più grande di quello
     del telefono, dove lo stesso testo sta a 12px — e faceva concorrenza al
     titolo invece di accompagnarlo.
     ⚠️ `--g-ink2` e NON `--muted`, per la stessa ragione già scritta nella
     regola gemella del telefono: dalla v19.81 l'appbar è in VETRO anche sul
     computer, e sopra il vetro il grigio tenue scende sotto la soglia. */
  #app-sub{font-size:12.5px;font-weight:600;color:var(--g-ink2);line-height:1.25;}
  #app-sub .late{color:var(--st-late-tx);font-weight:800;}
  #app-sub:empty{display:none;}
  .appbar-actions{display:flex;align-items:center;gap:9px;flex-wrap:wrap;}
  /* ricerca globale nell'appbar (variante «barra elastica»): occupa lo spazio
     fra il titolo della pagina e «+ Nuova scheda». Si digita qui e al primo
     carattere il testo passa nella finestra Ctrl+K (wiring in app.6). */
  .appbar h1{flex:0 0 auto;}
  .gswrap{flex:1 1 auto;min-width:0;display:flex;}
  .gsbar{flex:1;display:flex;align-items:center;gap:8px;min-width:0;height:40px;padding:0 10px;
    border:1px solid var(--line2);background:var(--panel);border-radius:11px;}
  .gsbar:focus-within{border-color:var(--green);box-shadow:0 0 0 3px var(--soft);}
  .gsbar .gsb-ic{flex:0 0 auto;font-size:15px;opacity:.75;line-height:1;}
  .gsbar input{flex:1;min-width:0;border:none;background:transparent;outline:none;font:inherit;font-size:14px;font-weight:600;color:var(--ink);}
  .gsbar input::placeholder{color:var(--muted);font-weight:600;}
  /* Campo dentro la finestra di ricerca (Ctrl+K). Stava inline nell'index con
     15px: un inline batte qualunque regola, quindi la correzione dei 16px in
     fondo al file non lo raggiungeva e su iPhone la pagina si ingrandiva. */
  #gs-input{font-size:15px;}
  .gsbar .gsb-kbd{flex:0 0 auto;border:1px solid var(--line2);border-radius:6px;padding:2px 6px;
    font-size:11px;font-weight:800;color:var(--muted2);background:var(--soft);}
  /* hamburger: visibile solo su mobile, nascosto su desktop */
  .nav-toggle{display:none;flex:0 0 auto;align-items:center;justify-content:center;width:42px;height:42px;border:1px solid var(--line2);background:var(--panel);color:var(--green);border-radius:10px;cursor:pointer;padding:0;margin-right:2px;}
  .nav-toggle svg{width:22px;height:22px;}
  .nav-toggle:active{background:var(--soft);}
  /* Marchio nell'appbar: spento di base, acceso solo col drawer (≤860px). */
  .mk-appbar{display:none;flex:0 0 34px;width:34px;height:34px;border-radius:9px;background:#fff;
    border:1px solid var(--line2);align-items:center;justify-content:center;padding:3px;margin-right:2px;box-sizing:border-box;}
  .mk-appbar img{width:100%;height:100%;display:block;object-fit:contain;}
  /* sfondo scuro dietro al drawer su mobile — dissolvenza in/out (pointer-events
     disattiva i click da chiuso; durante lo swipe l'opacity è guidata inline). */
  .nav-backdrop{display:block;position:fixed;inset:0;background:rgba(15,25,18,.45);z-index:29;opacity:0;pointer-events:none;transition:opacity var(--d-slow) var(--ease);}
  .app.nav-open .nav-backdrop{opacity:1;pointer-events:auto;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);}
  /* Durante lo swipe il drawer deve seguire il dito 1:1: niente transizione
     (la riattiviamo al rilascio per lo "snap"). */
  .app.nav-dragging .sidebar{transition:none!important;}
  .app.nav-dragging .nav-backdrop{transition:none!important;}
  /* ⚠️ NIENTE SFOCATURA MENTRE IL DITO TRASCINA — è il lampeggio segnalato
     dall'utente (v19.56): «aprendo la barra laterale con uno swipe alcuni
     elementi sotto lampeggiano, soprattutto il bottone metri reali».
     Durante il gesto l'opacity del velo è scritta INLINE a ogni frame; un
     elemento con `backdrop-filter` la cui opacity cambia frame per frame
     obbliga il browser a ricomporre tutto il fondo, e gli elementi che stanno
     già su un layer proprio — i pieni verdi con ombra, come «metri reali» —
     sfarfallano. La v19.55 aveva alzato il velo da 2 a 18px di sfocatura,
     peggiorando una situazione già fragile.
     Il vetro resta a gesto finito, dove l'opacity è ferma: si spegne solo
     mentre si trascina, che è anche quando non lo si guarda. */
  .app.nav-dragging .nav-backdrop{
    -webkit-backdrop-filter:none!important;backdrop-filter:none!important;
    /* senza la sfocatura il velo deve scurire di più, o il drawer che entra
       si stacca male dal contenuto (il blocco Liquid Glass lo porta a .28
       proprio contando sul blur) */
    background:rgba(9,20,30,.45)!important;
  }
  /* Un layer proprio e stabile per il velo: senza, ogni variazione di opacity
     lo fa ridipingere insieme a ciò che ha sotto. */
  .nav-backdrop{will-change:opacity;transform:translateZ(0);}
  /* L'UNICO scroller verticale dell'area di lavoro: scorre e rimbalza solo
     questo, le barre sopra restano ferme. overscroll-behavior:contain evita
     il "scroll chaining"; il momentum iOS è nativo. */
  .content{--ctop:18px;flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;scrollbar-gutter:stable;
    padding:0 24px calc(var(--pb-fondo) + var(--pb-dock) + var(--pb-tabbar) + env(safe-area-inset-bottom));contain:layout style;}
  /* L'aria sopra il contenuto vive sulle SEZIONI, non sullo scroller: così la
     barra dei filtri (sticky) non resta confinata sotto il padding-top del
     contenitore e si aggancia a FILO dell'appbar (niente più spazietto in cui
     si vedevano le schede scorrere). Le viste senza barra mantengono l'aria. */
  .content>section{padding-top:var(--ctop);}
  .content>section>.stickyhead:first-child,
  .content>section>.toolbar:first-child{margin-top:calc(-1 * var(--ctop));}
  /* Contenitore FISSO della barra filtri estratta dalla vista attiva: sta in
     .main, fuori da .content, quindi non scorre e non rimbalza mai. La barra
     ospitata torna statica (non più sticky) e riempie i lati come faceva
     dentro .content (stesso padding orizzontale → bleed identico). */
  .subbar{flex:0 0 auto;background:var(--bg);z-index:16;}
  .subbar[hidden]{display:none;}
  .subbar>.stickyhead,.subbar>.toolbar{position:static;margin:0;top:auto;}
  .bkp-banner{display:none;align-items:center;gap:12px;flex-wrap:wrap;margin:16px 24px 0;padding:12px 16px;background:var(--amber-bg);border:1px solid var(--amber-bd);border-left:4px solid var(--tan);border-radius:12px;color:var(--amber-tx);font-size:13.5px;}
  .bkp-banner.show{display:flex;}
  .bkp-banner .bkp-ico{font-size:17px;line-height:1;}
  .bkp-banner .bkp-txt{flex:1;min-width:180px;font-weight:600;}
  .bkp-banner .bkp-actions{display:flex;gap:8px;}
  .bkp-banner button{font:inherit;font-weight:700;font-size:12.5px;border-radius:8px;padding:7px 12px;cursor:pointer;border:1px solid transparent;}
  .bkp-banner .bkp-now{background:var(--tan);border-color:var(--tan);color:#1b1206;}
  .bkp-banner .bkp-now:hover{background:var(--tan2);}
  .bkp-banner .bkp-later{background:transparent;border-color:var(--amber-bd);color:var(--amber-tx);}
  .bkp-banner .bkp-later:hover{background:rgba(0,0,0,.04);}
  /* ── Fascia «ricarica»: versione non più compatibile col server ───────────
     Gemella del promemoria backup, ma con UN solo pulsante: qui non esiste un
     «più tardi» che risolva qualcosa — finché non si ricarica, niente arriva al
     server (app.1 → appDaRicaricare, variante C approvata).
     ⚠️ Il filetto di sinistra è --amber-tx e NON --tan come nel gemello:
     --tan non è ridefinito nel blocco tema scuro (la trappola già pagata da
     --red), quindi al buio resterebbe il marrone chiaro del tema chiaro.
     Il pulsante è il .btn.primary di sempre: verde pieno, già coperto
     dall'elenco dei «pieni verdi» che in tema scuro passano da --green-solid. */
  .upd-banner{display:none;align-items:center;gap:12px;flex-wrap:wrap;margin:16px 24px 0;padding:12px 16px;background:var(--amber-bg);border:1px solid var(--amber-bd);border-left:4px solid var(--amber-tx);border-radius:12px;color:var(--amber-tx);font-size:13.5px;}
  .upd-banner.show{display:flex;}
  .upd-banner .upd-ico{display:flex;align-items:center;}
  .upd-banner .upd-txt{flex:1;min-width:180px;font-weight:700;}
  /* ⚠️ Media query SUBITO DOPO la regola base, non nel blocco ≤640px più avanti
     nel file: là, a parità di specificità, vincerebbe l'ultima scritta. */
  @media(max-width:640px){
    .upd-banner{margin:11px 13px 0;padding:10px 12px;font-size:13px;gap:9px;}
    .upd-banner .upd-txt{min-width:140px;}
  }

  .btn{font:inherit;font-weight:700;border:none;border-radius:var(--r-sm);padding:10px 15px;cursor:pointer;display:inline-flex;align-items:center;gap:7px;white-space:nowrap;-webkit-tap-highlight-color:transparent;transition:background var(--d-fast) var(--ease),border-color var(--d-fast) var(--ease),color var(--d-fast) var(--ease),transform var(--d-fast) var(--ease),box-shadow var(--d-fast) var(--ease);}
  .btn.primary{background:var(--green2);color:#fff;box-shadow:0 6px 16px -6px rgba(0,60,15,.28);}
  /* ⚠️ IL «+» DI «NUOVA SCHEDA» È DISEGNATO, NON SCRITTO (v19.53).
     Come lettera finiva 2,885px SOTTO il centro del pulsante tondo, e la
     causa non era il CSS: il «+» di Hanken Grotesk è alto esattamente quanto
     una «x» (0,5 em di inchiostro, zero discendenti) e il suo asse ottico sta
     0,25 em sopra la linea di base, mentre `align-items:center` centra la
     RIGA DI TESTO, che sotto la linea di base riserva lo spazio dei
     discendenti. Il glifo scivolava in quello spazio. Controprova misurata:
     con Arial lo scarto va a −0,23px senza toccare una riga di CSS.
     ⚠️ Due barrette non dipendono da NESSUNA metrica tipografica: la
     centratura regge il cambio di carattere e regge anche l'attimo in cui il
     webfont non è ancora arrivato e disegna il ripiego di sistema — che è il
     caso in cui una correzione «alza il glifo di 0,11 em» sbaglierebbe di
     segno. Residuo misurato: 0,03px.
     ⚠️ Le misure sono in % della scatola, non in px: così il segno scala col
     `font-size` del contesto (26px nel tondo del telefono, 15 accanto
     all'etichetta sul computer) senza numeri cuciti su una larghezza. */
  #new-btn .np-plus{display:inline-block;position:relative;flex:0 0 auto;
    width:.654em;height:.654em;}
  #new-btn .np-plus::before,#new-btn .np-plus::after{content:"";position:absolute;
    top:50%;left:50%;transform:translate(-50%,-50%);background:currentColor;border-radius:1px;}
  #new-btn .np-plus::before{width:100%;height:15%;min-height:2px;}
  #new-btn .np-plus::after{width:15%;height:100%;min-width:2px;}
  .btn.primary:hover{background:var(--green3);}
  .btn.ghost{background:var(--panel);color:var(--green);border:1px solid var(--border);box-shadow:var(--sh-sm);}
  .btn.ghost:hover{border-color:var(--green2);background:var(--soft);}
  .btn.sm{padding:6px 10px;font-size:12.5px;border-radius:8px;}
  .btn.tan{background:var(--tan);color:#fff;}
  .btn.tan:hover{background:#86601f;}
  /* eliminazione: quadratino rosso pieno con cestino, dimensione fissa (non si allarga mai) */
  .btn.del{flex:0 0 auto;width:38px;min-width:38px;height:38px;min-height:38px;padding:0;
    background:var(--red);color:#fff;border:none;border-radius:8px;
    display:inline-flex;align-items:center;justify-content:center;gap:0;}
  .btn.del:hover{background:var(--red2);}
  .btn.del .ico{width:18px;height:18px;}
  /* eliminazione definitiva: pulsante rosso pieno con testo */
  .btn.danger{background:var(--red);color:#fff;border:none;}
  .btn.danger:hover{background:var(--red2);}
  /* archivia: pulsante giallo ben marcato */
  .btn.arch{background:var(--yellow);color:var(--yellow-tx);border:none;}
  .btn.arch:hover{background:var(--yellow2);}
  .chip{font:inherit;font-weight:700;border-radius:999px;padding:8px 14px;cursor:pointer;display:inline-flex;align-items:center;gap:8px;font-size:13px;border:1px solid var(--amber-bd);background:var(--amber-bg);color:var(--amber-tx);}
  .chip.rem{border-color:#e8b9a0;background:#fbe9df;color:#9c4a23;}
  .chip .ldot{width:8px;height:8px;border-radius:50%;background:currentColor;opacity:.7;}
  .chip b{font-size:15px;}

  .toolbar{display:flex;gap:10px;flex-wrap:wrap;align-items:center;position:sticky;top:0;z-index:15;background:var(--bg);margin:0 calc(-1*var(--content-pad)) 14px;padding:12px var(--content-pad);border-bottom:1px solid var(--line);will-change:transform;transform:translateZ(0);}
  /* Intestazione fissa che racchiude toolbar + pulsanti rapidi + barra cliente */
  .stickyhead{position:sticky;top:0;z-index:15;background:var(--bg);margin:0 calc(-1*var(--content-pad)) 14px;padding:10px var(--content-pad) 12px;border-bottom:1px solid var(--line);will-change:transform;transform:translateZ(0);}
  /* La toolbar interna eredita transform:translateZ(0) da .toolbar, che creerebbe
     uno stacking-context intrappolando i popup dei filtri (.ms-pop) SOTTO ai campi
     data dei .qbtns che seguono nel DOM. Da statica non serve: lo annulliamo così
     il popup (z-index alto) galleggia sopra a tutto il contenuto sottostante. */
  .stickyhead .toolbar{position:static;margin:0;padding:0;border:none;background:none;transform:none;will-change:auto;}
  .stickyhead .qbtns{margin:12px 0 0 !important;}
  .stickyhead .clientbar{margin:10px 0 0 !important;}
  .stickyhead .dash-controls{margin:0;}
  .stickyhead .dash-controls .qbtns{margin:0 !important;}
  .search{flex:1;min-width:200px;position:relative;}
  /* ⚠️ Dentro una BARRA la ricerca cede prima di mandare a capo un comando: il
     `min-width` è la misura che il browser usa per decidere dove spezzare la
     riga, non quella a cui il campo finisce (cresce comunque con `flex:1`).
     A 200 la barra di Gestione lotti andava a capo per QUATTRO pixel, e il
     campo restava a 322 su una riga da 992 — cioè si preferiva un campo largo
     a un comando in vista. A 170 sta tutto su una riga a 1280 e il campo si
     assesta a 196: sulle pagine larghe non cambia nulla, perché lì il
     `min-width` non tocca mai. */
  .toolbar .search{min-width:170px;}
  .search input{width:100%;padding:10px 14px 10px 38px;border:1px solid var(--line2);border-radius:10px;background:var(--panel);font:inherit;}
  .search svg{position:absolute;left:12px;top:50%;transform:translateY(-50%);opacity:.45;}
  select.filter{padding:10px 12px;border:1px solid var(--line2);border-radius:10px;background:var(--panel);font:inherit;cursor:pointer;}
  .clientbar{display:none;align-items:center;gap:10px;background:var(--soft);border:1px solid var(--soft-bd);border-radius:10px;padding:8px 14px;margin-bottom:12px;font-weight:700;color:var(--green);}
  .clientbar.show{display:flex;}
  .clientbar .x{margin-left:auto;cursor:pointer;background:none;border:none;font-size:20px;color:var(--green);}

  .listwrap{background:var(--panel);border:1px solid var(--border);border-radius:var(--r-lg);overflow:hidden;box-shadow:var(--sh);}
  .listhead{display:flex;align-items:center;justify-content:space-between;padding:15px 20px;border-bottom:1px solid var(--border);background:linear-gradient(180deg,#fcfdfe,#f7fafc);}
  .listhead .lt{font-weight:800;font-size:14px;letter-spacing:-.1px;color:var(--ink);}
  .listhead .lc{font-size:12.5px;color:var(--muted);font-weight:600;font-variant-numeric:tabular-nums;}
  .row{display:grid;grid-template-columns:118px 1fr auto;gap:18px;align-items:start;padding:15px 20px 15px 22px;border-bottom:1px solid var(--line);cursor:pointer;box-shadow:inset 4px 0 0 var(--corsia,transparent);transition:background var(--d-fast) var(--ease),box-shadow var(--d-fast) var(--ease);}
  .row:last-child{border-bottom:none;}
  @media(hover:hover){ .row:hover{background:#f8fafc;} }
  .row:active{background:#f1f4f6;}
  /* ── ACCENTO DI STATO A SINISTRA ──────────────────────────────────────────
     Lo stato della lavorazione si capisce a colpo d'occhio. È un `box-shadow`
     inset, e per questo la classe di stato dichiara SOLO il colore in
     `--corsia`: l'ombra la compone il contesto (riga piatta qui, card sul
     telefono e sul computer), che ci somma la sua ombra di rilievo.
     ⚠️ Scritte com'erano — con `box-shadow` per intero — le sei classi (0,2,0)
     scavalcavano la regola della card (0,1,0) e quella del tema scuro,
     producendo DUE difetti misurati e vissuti per mesi: in tema CHIARO le
     righe con uno stato perdevano l'ombra della card (cioè tutte tranne le
     anagrafiche), e in tema SCURO la corsia diventava TRASPARENTE — su
     computer e telefono — quindi il colore che dice a che punto è la
     lavorazione spariva dall'elenco. Con la variabile la guerra di specificità
     non esiste più, e uno stato nuovo ha bisogno del solo `--corsia`. */
  .row.rst-bozza{--corsia:var(--st-bozza);}
  .row.rst-lav{--corsia:var(--st-lav);}
  .row.rst-pronta{--corsia:var(--st-pronta);}
  .row.rst-cons{--corsia:var(--st-cons);}
  .row.rst-trasf{--corsia:#8b5cf6;}
  .row.rst-late{--corsia:var(--st-late);background:#fffafa;}
  @media(hover:hover){ .row.rst-late:hover{background:#fff4f4;} }
  .rnum{font-weight:800;color:var(--green2);font-size:14px;letter-spacing:.2px;padding-top:1px;}
  .rnum .d{display:block;font-size:11.5px;color:var(--muted2);font-weight:600;margin-top:3px;}
  .rmain{min-width:0;display:flex;flex-direction:column;gap:7px;}
  .rtop{display:flex;align-items:center;gap:9px;flex-wrap:wrap;}
  .rart{font-weight:800;font-size:16px;letter-spacing:-.2px;color:var(--ink);}
  .rcol{font-weight:700;color:var(--green2);}
  /* sottotitolo (terzista · tipo · rifinizione) — secondario, attenuato */
  .rsub{font-size:12.5px;color:var(--muted);}
  .rsub b{color:var(--ink);font-weight:700;}
  .rsub .rmq{font-weight:700;color:var(--ink);font-variant-numeric:tabular-nums;}
  .rsub .rmq.ok{color:var(--st-cons-tx);font-weight:800;}
  /* riga COMPATTA: la usano ora le sole schede «appena consegnate» pinnate in
     cima all'elenco Schede. Le Consegnate sono passate alla riga piena in
     v19.33 (una grafica sola per tutti gli elenchi di schede). */
  .row.rcompact{padding-top:11px;padding-bottom:11px;}
  .row.rcompact .rmain{gap:5px;}
  .row.rcompact .rsub{line-height:1.4;}
  /* Schede appena consegnate dalla lista attiva: riga barrata pinnata in cima
     (come le "Appena segnate" del Programma), finché non si rientra nella pagina */
  .sched-donehead{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:6px 4px 8px;}
  .row.row-done{opacity:.8;cursor:default;background:var(--st-cons-bg);}
  .row.row-done .rnum,.row.row-done .rart{text-decoration:line-through;text-decoration-color:var(--muted2);}
  /* Storico del cliente in coda alla lista attiva (filtro cliente attivo):
     intestazioni richiudibili Consegnate/Archiviate — vedi _cliSecHTML in app.3 */
  /* L'intestazione è una FASCIA a tutta larghezza, non una card: le righe della
     lista si separano con un border-bottom e nessun margine, quindi una card
     staccata romperebbe la continuità dell'elenco. */
  .clisec-head{display:flex;align-items:center;gap:9px;width:100%;text-align:left;cursor:pointer;
    background:var(--sidebar);border:none;border-top:1px solid var(--line2);border-bottom:1px solid var(--border);
    padding:11px 20px;font-family:inherit;font-size:12px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--ink);}
  @media(hover:hover){ .clisec-head:hover{background:var(--soft);} }
  /* Colori scelti col metro del test dark-theme: sul fondo --sidebar della
     fascia, --muted2 stava a 2,34:1 e --muted a 4,34:1 in tema scuro (--muted
     passa altrove perché lì il fondo è --card, più scuro). */
  .clisec-caret{color:currentColor;font-size:11px;width:10px;}
  .clisec-n{font-weight:800;letter-spacing:0;color:var(--ink);background:var(--card);border:1px solid var(--border);border-radius:999px;padding:1px 9px;font-variant-numeric:tabular-nums;}
  .clisec-load{font-weight:600;letter-spacing:0;text-transform:none;color:var(--green);}
  .clisec-empty,.clisec-none{color:var(--ink);font-size:13px;padding:14px 20px;}
  @media(max-width:640px){ .clisec-head{padding:10px 13px;} .clisec-empty,.clisec-none{padding:12px 13px;} }
  /* griglia info ORDINATA e allineata: micro-etichetta sopra + valore — più scannerizzabile */
  .rmeta-grid{display:flex;flex-wrap:wrap;gap:7px 8px;margin-top:1px;}
  .rcell{display:flex;flex-direction:column;gap:2px;background:var(--sidebar);border:1px solid var(--border);border-radius:9px;padding:6px 11px;min-width:94px;}
  .rcell .rk{font-size:9px;letter-spacing:.5px;text-transform:uppercase;color:var(--muted2);font-weight:800;}
  .rcell .rv{font-size:13px;font-weight:700;color:var(--ink);font-variant-numeric:tabular-nums;line-height:1.25;}
  /* Metri REALI: --st-cons-tx (il verde di «consegnato/confermato»), non
     --green2, che su fondo bianco sta a 4,38:1 — sotto AA. Stesso verde nella
     riga del Programma lavorazioni: la stessa cosa si dice allo stesso modo. */
  .rcell .rv.ok{color:var(--st-cons-tx);font-weight:800;}
  .rcell .rv.miss{color:var(--st-late);}
  .rcell.wide{flex:1 1 100%;min-width:0;}
  .pill{font-size:10px;letter-spacing:.6px;text-transform:uppercase;font-weight:800;padding:3px 9px;border-radius:999px;border:1px solid var(--line2);color:var(--muted);background:#fff;}
  .pill.dest{color:var(--green);background:var(--soft);border-color:var(--soft-bd);}
  .pill.stock{color:#7a5912;background:var(--amber-bg);border-color:var(--amber-bd);}
  /* Scheda ripartita su più clienti */
  .pill.multicli{color:var(--st-pronta-tx);background:var(--st-pronta-bg);border-color:transparent;}
  /* Mix avanzato di crust nell'elenco: pastiglia (proporzioni a colpo d'occhio)
     + chip di dettaglio dentro la cella Crust, che è già a tutta riga. Tinta
     ambra come tutto ciò che riguarda il grezzo (riquadro Crust dell'anteprima,
     note crust): non introduce un colore nuovo nell'elenco. */
  .pill.mixpill{color:var(--amber-tx);background:var(--amber-bg);border-color:var(--amber-bd);
    font-variant-numeric:tabular-nums;text-transform:none;letter-spacing:.2px;}
  .mixline{display:flex;flex-wrap:wrap;gap:5px 6px;margin-top:5px;}
  .mixchip{font-size:11.5px;font-weight:700;color:var(--amber-tx);background:var(--amber-bg);
    border:1px solid var(--amber-bd);border-radius:7px;padding:2px 8px;font-variant-numeric:tabular-nums;
    display:inline-flex;align-items:baseline;gap:4px;line-height:1.35;}
  .mixchip b{font-weight:800;color:var(--ink);}
  .mixchip b.nop{color:var(--amber-tx);font-weight:700;}
  .mixchip .sp{font-weight:600;opacity:.85;}
  .mixchip.warn{color:var(--st-late-tx);background:var(--st-late-bg);border-color:#f3c2c2;}
  .rmeta{font-size:12.5px;color:var(--muted);}
  .rmeta b{color:var(--ink);font-weight:700;}
  .rqty,.rcli{font-size:12.5px;color:var(--muted);}
  .rqty{font-variant-numeric:tabular-nums;}
  .rcli b{color:var(--ink);font-weight:700;}
  .rwarn{display:flex;gap:14px;flex-wrap:wrap;font-size:12px;font-weight:800;margin-top:1px;}
  /* ⚠️ `.rcrust` è rimasta la riga di servizio delle sole TINTURE e RASATURE
     («tintura · collegata · RF26126»). Le schede non ce l'hanno più: «tinto in
     botte», «rasato» e le note interne sono uscite da tutti gli elenchi in
     v19.96 — con loro sono cadute `.rcrust b`, `.rcrust .cnote` e l'intera
     famiglia `.rnote`, che nessun markup produceva più. */
  .rcrust{font-size:12px;color:var(--muted);display:flex;gap:7px;flex-wrap:wrap;align-items:baseline;}
  /* link di interconnessione tintura↔rifinizione (clic dentro una riga/anteprima) */
  .tlink{color:var(--green);font-weight:800;cursor:pointer;text-decoration:none;border-bottom:1px dashed var(--green2);padding-bottom:1px;}
  .tlink:hover{color:var(--green2);border-bottom-style:solid;}
  .rcrust .ctag.tdone{color:var(--green);background:var(--soft);border-color:var(--soft-bd);}
  .rcrust .ctag{font-size:9.5px;letter-spacing:.6px;text-transform:uppercase;font-weight:800;color:var(--tan);background:var(--amber-bg);border:1px solid var(--amber-bd);border-radius:6px;padding:2px 6px;flex-shrink:0;min-width:104px;text-align:center;box-sizing:border-box;}
  .badge-stato{display:inline-flex;align-items:center;gap:6px;font-size:11px;letter-spacing:.1px;font-weight:800;padding:5px 11px 5px 9px;border-radius:999px;white-space:nowrap;}
  .badge-stato::before{content:"";width:7px;height:7px;border-radius:50%;background:currentColor;flex:0 0 auto;}
  .st-bozza{background:var(--st-bozza-bg);color:var(--st-bozza-tx);}
  .st-lav{background:var(--st-lav-bg);color:var(--st-lav-tx);}
  .st-pronta{background:var(--st-pronta-bg);color:var(--st-pronta-tx);}
  .st-cons{background:var(--st-cons-bg);color:var(--st-cons-tx);}
  .st-trasf{background:#ede9fe;color:#6d28d9;}
  /* banner "Trasferita alla lavorazione successiva" nell'anteprima scheda */
  .pvtrasf{margin:-4px 0 14px;padding:11px 14px;border-radius:12px;background:#f4f1fc;border:1px solid #ddd3f5;}
  .pvtrasf-h{font-size:13px;font-weight:800;color:#6d28d9;}
  .pvtrasf-b{font-size:12.5px;color:var(--muted);margin-top:5px;line-height:1.5;}
  /* chip rapide di stato (in alto nei filtri, es. pagina Tinture) */
  .statechips{display:flex;gap:8px;flex-wrap:wrap;align-items:center;padding:8px 2px 14px;}
  .statechips:empty{display:none;}
  .statechip{font-size:12.5px;font-weight:800;padding:7px 14px;border-radius:999px;border:1.5px solid transparent;background:var(--sidebar);color:var(--muted);cursor:pointer;display:inline-flex;align-items:center;gap:7px;line-height:1;}
  .statechip b{font-weight:800;opacity:.85;}
  @media(hover:hover){ .statechip:hover{filter:brightness(.97);} }
  .statechip:active{transform:scale(.97);}
  .statechip.on{border-color:currentColor;box-shadow:0 0 0 3px rgba(0,0,0,.045);}
  .statechip.st-bozza{background:var(--st-bozza-bg);color:var(--st-bozza-tx);}
  .statechip.st-lav{background:var(--st-lav-bg);color:var(--st-lav-tx);}
  .statechip.st-pronta{background:var(--st-pronta-bg);color:var(--st-pronta-tx);}
  .statechip.st-cons{background:var(--st-cons-bg);color:var(--st-cons-tx);}
  .statechip.st-trasf{background:#ede9fe;color:#6d28d9;}
  /* timeline storico stati (anteprima scheda) */
  .tl{display:flex;flex-direction:column;gap:2px;margin-top:4px;}
  .tl-row{display:flex;align-items:center;gap:10px;padding:5px 2px;position:relative;}
  .tl-row:not(:last-child)::after{content:"";position:absolute;left:5px;top:22px;bottom:-2px;width:2px;background:var(--border);}
  .tl-dot{width:12px;height:12px;border-radius:50%;flex:0 0 auto;background:var(--muted);z-index:1;}
  .tl-dot.st-bozza{background:var(--st-bozza-tx);} .tl-dot.st-lav{background:var(--st-lav-tx);}
  .tl-dot.st-pronta{background:var(--st-pronta-tx);} .tl-dot.st-cons{background:var(--st-cons-tx);}
  .tl-st{font-weight:700;min-width:120px;} .tl-dt{color:var(--muted);font-size:13px;font-weight:600;}
  .tl-by{color:var(--muted);font-size:12px;font-weight:600;font-style:italic;}
  .tl-gap{margin-left:auto;font-size:12px;font-weight:700;color:var(--muted);background:var(--sidebar);border:1px solid var(--border);border-radius:999px;padding:2px 9px;}
  .tl-tot{margin-top:8px;padding-top:8px;border-top:1px solid var(--border);font-size:13px;color:var(--muted);}
  /* ===== pagina Impostazioni ===== */
  .set-card{margin-bottom:16px;}
  .set-card h4{margin:0 0 4px;}
  .set-row{display:flex;align-items:center;justify-content:space-between;gap:18px;padding:14px 0;border-bottom:1px solid var(--border);}
  .set-row:last-child{border-bottom:none;padding-bottom:2px;}
  .set-info{flex:1 1 auto;min-width:0;}
  .set-info .set-label{font-weight:700;font-size:14.5px;color:var(--ink);}
  .set-info .set-help{color:var(--muted);font-size:12.5px;line-height:1.45;margin-top:3px;}
  .set-ctrl{flex:0 0 auto;display:flex;align-items:center;gap:8px;}
  .set-num{width:84px;font:inherit;font-weight:700;text-align:right;padding:8px 10px;border:1px solid var(--line2);border-radius:9px;background:var(--panel);color:var(--ink);}
  .set-num:focus{outline:none;border-color:var(--green2);box-shadow:0 0 0 3px var(--soft);}
  .set-unit{color:var(--muted);font-size:13px;font-weight:700;white-space:nowrap;}
  .set-badge{font-size:12px;font-weight:800;padding:5px 11px;border-radius:999px;white-space:nowrap;}
  .set-badge--ok{background:var(--soft);color:var(--green);}
  .set-badge--off{background:var(--amber-bg);color:var(--amber-tx);}
  .set-fields{display:grid;grid-template-columns:1fr 1fr;gap:12px 16px;}
  /* ── Chiusure aziendali: form a calendario ─────────────────────────────
     Sostituisce la casella di testo «una data per riga». Tre pezzi: aggiunta
     per PERIODO (le ferie sono un intervallo), elenco dei periodi, griglia del
     mese per correggere il singolo giorno. */
  .set-clo{margin-top:12px;border-top:1px solid var(--line);padding-top:11px;}
  .clo-head{font-size:13.5px;font-weight:800;color:var(--ink);}
  .clo-add{display:flex;gap:9px;align-items:flex-end;flex-wrap:wrap;margin-bottom:8px;}
  .clo-f{display:flex;flex-direction:column;gap:4px;min-width:150px;}
  .clo-f>span{font-size:10.5px;letter-spacing:.6px;text-transform:uppercase;color:var(--muted);font-weight:700;}
  .clo-f>span i{text-transform:none;letter-spacing:0;font-style:normal;color:var(--muted2);font-weight:600;}
  .clo-f input{font:inherit;font-size:16px;border:1px solid var(--line2);border-radius:9px;padding:8px 10px;
    background:var(--panel);color:var(--ink);width:100%;}
  .clo-err{font-size:12.5px;font-weight:700;color:var(--st-late-tx);background:var(--st-late-bg);
    border:1px solid rgba(192,57,43,.28);border-radius:8px;padding:6px 9px;margin-bottom:8px;}
  .clo-empty{font-size:12.5px;color:var(--muted);padding:6px 0 2px;}
  .clo-list{display:flex;flex-direction:column;gap:6px;margin-bottom:10px;}
  /* Fondo --panel, non --sidebar: la nota «18 giorni · 12 lavorativi» è testo
     --muted, che sul grigio scende a 4,34:1 (sotto AA, colto dal test). Stesso
     pattern di .ordr-item, il gemello nell'editor commesse: a distinguere la
     riga è il BORDO. */
  .clo-item{display:flex;align-items:center;gap:10px;background:var(--panel);border:1px solid var(--border);
    border-radius:10px;padding:8px 10px 8px 12px;}
  .clo-item-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px;}
  .clo-item-main b{font-size:14px;font-weight:800;}
  .clo-meta{font-size:11.5px;color:var(--muted);font-weight:650;font-variant-numeric:tabular-nums;}
  .clo-x{border:1px solid var(--line2);background:var(--panel);color:var(--muted);border-radius:8px;
    width:30px;height:30px;font-size:17px;line-height:1;cursor:pointer;flex:none;}
  @media(hover:hover){ .clo-x:hover{border-color:var(--red);color:var(--red);} }
  .clo-cal{border:1px solid var(--border);border-radius:12px;padding:10px;background:var(--panel);max-width:330px;}
  .clo-calhead{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:7px;}
  .clo-caltitle{font-size:13.5px;font-weight:800;text-transform:capitalize;}
  .clo-nav{border:1px solid var(--line2);background:var(--panel);color:var(--ink);border-radius:8px;
    width:30px;height:30px;font-size:16px;line-height:1;cursor:pointer;}
  @media(hover:hover){ .clo-nav:hover{border-color:var(--green2);color:var(--green);} }
  .clo-dow,.clo-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:3px;}
  /* --muted, non --muted2: quest'ultimo dà 2,56:1 in chiaro e 4,44:1 in scuro. */
  .clo-dow span{font-size:10px;text-transform:uppercase;letter-spacing:.5px;color:var(--muted);
    font-weight:800;text-align:center;padding-bottom:3px;}
  .clo-d{border:1px solid transparent;background:transparent;color:var(--ink);border-radius:8px;
    height:34px;font:inherit;font-size:13px;font-weight:700;cursor:pointer;font-variant-numeric:tabular-nums;}
  .clo-d.empty{cursor:default;}
  .clo-d.we{color:var(--muted);}
  .clo-d.today{border-color:var(--line2);}
  /* Giorno chiuso: pieno verde con testo bianco. In tema scuro --green è il
     verde CHIARO (è il colore di TESTO): come sfondo va riportato a
     --green-solid, come gli altri pieni verdi del gestionale. */
  .clo-d.on{background:var(--green);color:#fff;border-color:var(--green);}
  @media(hover:hover){ .clo-d:not(.empty):hover{border-color:var(--green2);} }
  .clo-calhint{font-size:11.5px;color:var(--muted);margin-top:7px;}
  @media(max-width:640px){ .clo-add{gap:8px;} .clo-f{min-width:0;flex:1 1 140px;} .clo-cal{max-width:none;} }
  .set-field{display:flex;flex-direction:column;gap:5px;}
  .set-field--full{grid-column:1/-1;}
  .set-field span{font-size:11px;letter-spacing:.6px;text-transform:uppercase;color:var(--muted);font-weight:800;}
  .set-field input{font:inherit;font-weight:600;padding:9px 11px;border:1px solid var(--line2);border-radius:9px;background:var(--panel);color:var(--ink);width:100%;}
  .set-field input:focus{outline:none;border-color:var(--green2);box-shadow:0 0 0 3px var(--soft);}
  .set-field textarea{font:inherit;font-weight:600;padding:9px 11px;border:1px solid var(--line2);border-radius:9px;background:var(--panel);color:var(--ink);width:100%;resize:vertical;min-height:46px;}
  .set-field textarea:focus{outline:none;border-color:var(--green2);box-shadow:0 0 0 3px var(--soft);}
  .set-select{font:inherit;font-weight:700;padding:8px 11px;border:1px solid var(--line2);border-radius:9px;background:var(--panel);color:var(--ink);cursor:pointer;max-width:230px;}
  .set-select:focus{outline:none;border-color:var(--green2);box-shadow:0 0 0 3px var(--soft);}
  @media (max-width:640px){ .set-row{flex-direction:column;align-items:stretch;gap:10px;} .set-ctrl{justify-content:flex-start;} .set-fields{grid-template-columns:1fr;} .set-select{max-width:none;width:100%;} }
  /* ── Impostazioni per REPARTI (v19.30) ────────────────────────────────────
     Un reparto per volta: sul computer la colonnina a sinistra, sul telefono un
     elenco in cui si entra e da cui si torna col «‹». Tutti i reparti restano
     nel DOM (quelli chiusi sono `hidden`), quindi il wiring per id non cambia:
     vedi la nota in renderSettings. */
  .setpage{max-width:1000px;}
  .setlayout{display:grid;gap:16px;}
  @media (min-width:760px){ .setlayout{grid-template-columns:216px minmax(0,1fr);align-items:start;} }
  /* ⚠️ LA COLONNINA HA LA FORMA DELLE RIGHE DEL MENU (v19.93) — stessa scelta
     già fatta sul telefono in v19.79, portata qui per la stessa ragione: erano
     due elenchi che dicono la stessa cosa in due modi (là righe da 60px con
     l'icona in un tondino verde, qui bottoncini da 43px con l'icona nuda da
     20px). I numeri vengono da `.mn-row`/`.mn-ic`: tondino 34px raggio 11 su
     `--soft`, riga da 52px, testo 14,5px. Non si può riusare `.mnlist` tale e
     quale — quel blocco è dentro `@media(max-width:640px)` e qui siamo sul
     computer — ma i valori sono gli stessi, e se cambiano là vanno cambiati
     qui: `test/impostazioni.test.js` li confronta. */
  .setnav{background:var(--panel);border:0;border-radius:var(--gr-sm);padding:6px;
    box-shadow:var(--sh-sm),inset 0 0 0 .5px rgba(15,36,56,.10);
    display:none;position:sticky;top:0;}
  html[data-theme="dark"] .setnav{box-shadow:var(--sh-sm),inset 0 0 0 .5px rgba(255,255,255,.09);}
  @media (min-width:760px){ .setnav{display:block;} }
  .setnav button{display:flex;align-items:center;gap:12px;width:100%;text-align:left;font:inherit;font-size:14.5px;
    font-weight:700;letter-spacing:-.01em;color:var(--ink);background:none;border:none;padding:9px 10px;
    min-height:52px;border-radius:14px;cursor:pointer;}
  .setnav button .ico{width:34px;height:34px;min-width:34px;border-radius:11px;background:var(--soft);
    color:var(--green);display:flex;align-items:center;justify-content:center;flex:0 0 auto;}
  .setnav button .ico .ic,.setnav button .ico svg{width:19px;height:19px;}
  /* ⚠️ La freccina su `--muted` e non `--muted2` (2,56:1): è la trappola già
     pagata dalla stessa freccina nell'elenco del telefono. */
  .setnav button .ch{margin-left:auto;color:var(--muted);font-weight:800;font-size:17px;line-height:1;flex:0 0 auto;}
  .setnav button.on{background:var(--soft);color:var(--green);}
  .setnav button.on .ch{color:var(--green);}
  @media(hover:hover){ .setnav button:not(.on):hover{background:var(--sidebar);} }
  /* Elenco dei reparti sul telefono. ⚠️ La FORMA la dà `.mnlist` — è la stessa
     lista della pagina Menu (v19.79) — e qui resta solo il gating: sul computer
     i reparti sono la colonnina `.setnav` e questo elenco non esiste.
     Le vecchie regole di riga (.replist button/.ico/.rl-t/.chev) sono state
     TOLTE: convivendo con `.mnlist` si sommavano ai suoi padding e la riga
     tornava a 80,6px invece di 70,6 — cioè il guadagno spariva senza che
     niente lo segnalasse. Il markup non ha più quelle classi, tranne `.rl-s`
     (il sottotitolo del reparto), stilata accanto a `.replist.mnlist`. */
  @media (min-width:760px){ .replist{display:none;} }
  /* L'icona del reparto, la STESSA in tutti e tre i punti (colonnina, titolo,
     fascia della ricerca) dalla v19.93 — vedi la nota su `SET_REPARTI`. */
  .repico{display:inline-flex;align-items:center;justify-content:center;color:var(--green);vertical-align:-3px;}
  .repico svg{width:19px;height:19px;}
  .setrep-band .repico svg{width:14px;height:14px;}
  .rep-title{font-size:19px;font-weight:800;letter-spacing:-.2px;display:flex;align-items:center;gap:8px;margin-bottom:2px;}
  .rep-sub{font-size:13px;color:var(--ink);margin-bottom:14px;}
  /* Il «‹» quadrato dentro la pagina è stato RIMOSSO in v19.79: l'indietro è
     uno solo, quello dell'appbar (vedi la pila di navigazione in app.9g). Stava
     a 162px sotto di lui e quasi sulla stessa colonna — due frecce incolonnate
     per lo stesso gesto. Non reintrodurlo senza chiedere. */
  /* L'intestazione di reparto DENTRO il contenuto serve solo alla ricerca, che
     mostra tutti i reparti insieme: fuori da lì il titolo è già in .rep-title. */
  .setrep-band{display:none;font-size:11px;letter-spacing:1.3px;text-transform:uppercase;font-weight:800;color:var(--green);
    margin:20px 0 -4px;}
  /* ⚠️ `:not([hidden])`: la ricerca nasconde le intestazioni dei reparti senza
     risultati con l'attributo hidden, e senza questo la regola lo scavalcherebbe
     (display:block batte il display:none implicito di [hidden]). */
  .setpage.cerca .setrep-band:not([hidden]){display:block;}
  .setpage.cerca .rep-title,.setpage.cerca .rep-sub{display:none;}
  .setsearch{display:flex;align-items:center;gap:9px;background:var(--panel);border:1px solid var(--border);
    border-radius:11px;padding:8px 13px;box-shadow:var(--sh-sm);margin-bottom:13px;}
  .setsearch svg{flex:0 0 auto;color:var(--muted);}
  /* 16px: sotto quella soglia iPhone ingrandisce la pagina al primo tocco */
  .setsearch input{flex:1;min-width:0;font:inherit;font-size:16px;font-weight:600;border:none;background:none;
    color:var(--ink);outline:none;}
  .setsearch:focus-within{border-color:var(--green2);box-shadow:0 0 0 3px var(--soft);}
  .set-qx{font:inherit;font-size:14px;font-weight:800;color:var(--muted);background:none;border:none;cursor:pointer;
    padding:2px 4px;border-radius:6px;flex:0 0 auto;}
  @media(hover:hover){ .set-qx:hover{color:var(--ink);} }
  .set-qout{font-size:12.5px;font-weight:700;color:var(--ink);margin:-6px 0 12px;}
  /* «solo qui»: vale su questo dispositivo e non si sincronizza. Sono 12 righe
     su 34 — l'eccezione — quindi le sincronizzate non portano niente. */
  .set-scope{font-size:10px;font-weight:800;letter-spacing:.4px;border-radius:999px;padding:2px 8px;white-space:nowrap;
    background:var(--st-bozza-bg);color:var(--st-bozza-tx);margin-left:8px;vertical-align:middle;text-transform:none;}
  .set-h4act{display:flex;align-items:center;gap:8px;flex:0 0 auto;}
  .set-h4act:empty{display:none;}
  /* ⚠️ Era `.anagvuota` nuda (0,1,0) e non ha MAI funzionato: `.btn.ghost`
     (0,2,0) le vinceva sopra, quindi «Svuota» del cestino anagrafiche — che
     elimina definitivamente — si presentava verde come un comando qualsiasi.
     La usa anche «Sposta nel cestino…» delle schede vecchie: contornata di
     rosso, non il pieno di `.btn.danger`, che è riservato al pulsante della
     conferma, dove l'azione si compie davvero. */
  .btn.ghost.anagvuota{color:var(--st-late-tx);border-color:var(--st-late-tx);}
  .ractions{display:flex;gap:6px;align-items:center;flex-wrap:wrap;justify-content:flex-end;}
  /* righe schede attive: stato in cima, poi avanzamento + icone compatte (impilate) */
  .ractions.rcol{flex-direction:column;align-items:flex-end;gap:9px;flex-wrap:nowrap;}
  .rstate-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap;justify-content:flex-end;}
  .ract-line{display:flex;align-items:center;gap:8px;flex-wrap:wrap;justify-content:flex-end;}
  .btn.adv{background:var(--soft);color:var(--green);border:1px solid var(--soft-bd);border-radius:999px;font-weight:800;font-size:12px;padding:7px 13px;box-shadow:none;}
  .btn.adv:hover{background:var(--soft);border-color:var(--green2);}
  .ract-icons{display:inline-flex;align-items:center;gap:2px;background:var(--sidebar);border:1px solid var(--border);border-radius:11px;padding:3px;}
  .rib{border:none;background:transparent;color:var(--muted);cursor:pointer;width:32px;height:30px;border-radius:8px;display:inline-flex;align-items:center;justify-content:center;-webkit-tap-highlight-color:transparent;transition:background var(--d-fast) var(--ease),color var(--d-fast) var(--ease);}
  .rib svg{width:17px;height:17px;}
  @media(hover:hover){ .rib:hover{background:#fff;color:var(--green);box-shadow:var(--sh-sm);} }
  .rib.pdf:hover{color:var(--green2);}
  .rib.del:hover{background:#fff;color:var(--st-late);}
  .rib:active{transform:scale(.9);}
  .empty{text-align:center;padding:56px 20px;color:var(--muted);}
  .empty h3{color:var(--ink);font-size:20px;margin-bottom:8px;font-weight:800;}

  /* panel/form */
  /* Backdrop pannelli: fade in/out (resta in DOM, pointer-events lo disattiva da
     chiuso → click-through garantito su mobile). */
  .overlay{position:fixed;inset:0;background:rgba(15,25,18,.45);z-index:40;opacity:0;pointer-events:none;transition:opacity var(--d) var(--ease);}
  .overlay.open{opacity:1;pointer-events:auto;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);}
  /* Pannello editor (scheda / tintura): finestra centrata e ampia per avere
     più campi a vista. ⤢ porta a tutto schermo. */
  .panel{position:fixed;inset:0;margin:auto;width:min(1180px,96vw);height:min(94vh,940px);max-width:96vw;background:var(--bg);z-index:50;border-radius:16px;box-shadow:0 30px 80px rgba(0,0,0,.34);display:flex;flex-direction:column;overflow:hidden;opacity:0;transform:translateY(14px) scale(.985);pointer-events:none;will-change:transform,opacity;transition:opacity var(--d) var(--ease-out),transform var(--d) var(--ease-out);}
  .panel.open{opacity:1;transform:none;pointer-events:auto;}
  .panel.full{width:100vw;height:100vh;max-width:100vw;border-radius:0;}
  .panel.min{height:300px;top:auto;bottom:0;width:484px;max-width:96vw;border-top-left-radius:14px;border-top-right-radius:14px;overflow:hidden;}
  .panel.min .panel-body{opacity:.55;}
  .panel-head{display:flex;align-items:center;justify-content:space-between;padding:11px 16px;border-bottom:1px solid var(--line);background:var(--panel);gap:10px;}
  .panel-head h2{font-size:16px;font-weight:800;}
  .panel-head .ph-tools{display:flex;gap:6px;align-items:center;}
  .panel-head .ph-tools button{background:none;border:1px solid var(--line2);border-radius:8px;width:32px;height:30px;cursor:pointer;color:var(--muted);font-size:15px;display:inline-flex;align-items:center;justify-content:center;}
  .panel-head .ph-tools button:hover{border-color:var(--green2);color:var(--green);}
  .panel-body{padding:10px 16px 14px;overflow-y:auto;overflow-x:hidden;flex:1;}
  .panel-foot{padding:10px 16px;border-top:1px solid var(--line);display:flex;gap:9px;justify-content:flex-end;flex-wrap:wrap;background:var(--panel);}
  /* Consegna prevista scelta a mano: il campo e la sua nota passano alla
     famiglia TAN, che nel gestionale segnala già «valore interno / bloccato»
     (note-lucchetto, campi n/d) — nessuna tinta nuova da imparare. */
  /* ⚠️ Il testo usa --amber-tx, NON --tan: --tan/--tan2 NON sono ridefiniti nel
     blocco tema scuro, quindi resterebbero il marrone chiaro-tema (#9a6f2c) sul
     fondo --amber-bg che in scuro diventa #37300f — marrone su marrone. La
     coppia --amber-bg/--amber-tx è invece bilanciata in entrambi i temi. */
  #fl-dataprev.manual{border-color:var(--amber-bd);box-shadow:0 0 0 3px rgba(154,111,44,.18);}
  .lotto-saldo-hint.manual{display:flex;align-items:center;gap:8px;flex-wrap:wrap;
    color:var(--amber-tx);background:var(--amber-bg);border:1px solid var(--amber-bd);border-radius:8px;padding:6px 9px;}
  .lotto-saldo-hint.manual b{color:var(--amber-tx);}
  /* Il testo secondario NON passa a --muted: su --amber-bg dà 4,27:1 (sotto AA,
     colto da dark-theme.test.js). Dentro un riquadro monocromatico la gerarchia
     la fa il PESO, non un secondo colore. */
  .lotto-saldo-hint.manual .dp-auto{color:var(--amber-tx);font-weight:600;opacity:.9;}
  /* Avviso «data prima dell'apertura»: classe PROPRIA, non `.bad` (che è del
     saldo lotto ed è testo semplice: passarla a flex ne cambierebbe il
     rendering in un punto che non c'entra nulla con questa modifica). */
  .lotto-saldo-hint.dp-warn{display:flex;align-items:center;gap:8px;flex-wrap:wrap;
    color:var(--st-late-tx);background:var(--st-late-bg);border:1px solid var(--st-late-bd,rgba(192,57,43,.28));border-radius:8px;padding:6px 9px;}
  .lotto-saldo-hint.dp-warn b{color:var(--st-late-tx);}
  /* Marcatori «data scelta a mano»: pastiglia dell'elenco schede, segno 📌 nel
     Programma, cella accesa nell'anteprima. Stessa famiglia tan del campo. */
  .pill.dppill{background:var(--amber-bg);color:var(--amber-tx);border:1px solid var(--amber-bd);font-weight:700;}
  .prog-days .dp-pin{font-size:11px;font-weight:800;background:var(--amber-bg);color:var(--amber-tx);
    border:1px solid var(--amber-bd);border-radius:99px;padding:1px 7px;white-space:nowrap;}
  .pvf.dp{background:var(--amber-bg);border-color:var(--amber-bd);}
  /* `.pvf.dp .v .pvsub` e non `.pvf.dp .pvsub`: la regola generica
     `.pvf .v .pvsub{color:var(--muted)}` sta PIÙ AVANTI nel file e a parità di
     specificità vincerebbe lei, lasciando il grigio su fondo ambra (4,27:1). */
  .pvf.dp .l,.pvf.dp .v,.pvf.dp .v .pvsub{color:var(--amber-tx);}
  .pvdp-mot{font-size:12.5px;font-weight:650;color:var(--amber-tx);margin-top:4px;font-style:italic;}
  /* Chiudi / Riduci a icona delle finestre: quadratino con bordo, così si
     vedono (prima erano due glifi nudi sul fondo bianco) e hanno un'area di
     tocco vera. Verde per «riduci», rosso per «chiudi»: colori solo al
     passaggio del mouse / pressione, a riposo restano neutri. */
  .x{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;padding:0;flex:0 0 auto;
    background:var(--panel);border:1px solid var(--line2);border-radius:10px;
    font-size:22px;line-height:1;cursor:pointer;color:var(--muted);
    transition:background var(--d-fast) ease,border-color var(--d-fast) ease,color var(--d-fast) ease,transform var(--d-fast) ease;}
  .x:hover{background:var(--soft);border-color:var(--green);color:var(--green);}
  .x:active{transform:scale(.93);}
  .x.x-close:hover{background:rgba(192,73,43,.10);border-color:#c0492b;color:#c0492b;}
  .x.x-min{font-size:24px;}
  /* la × della barra cliente resta il glifo nudo di prima (sta dentro una
     pillola, un quadratino lì sarebbe fuori scala) */
  .clientbar .x{width:auto;height:auto;border:none;background:none;border-radius:0;}
  /* minmax(0,1fr): senza il min 0 le colonne 1fr non scendono sotto il
     min-content dei campi (gli input hanno una larghezza intrinseca), così su
     mobile il form sforava oltre lo schermo e veniva tagliato. */
  .fgrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px 16px;}
  /* Pannello ampio: tre colonne su schermi larghi per maggiore visione */
  @media(min-width:1080px){ .fgrid{grid-template-columns:repeat(3,minmax(0,1fr));} }
  .field{display:flex;flex-direction:column;gap:2px;}
  .field.full{grid-column:1/-1;}
  /* Metri quadri (stima/reali) sempre su griglia 2 colonne allineata, indipendente
     dalle 2/3 colonne del form: stima a sinistra, reali a destra. */
  .fgrid .mqgrid{grid-column:1/-1;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px 16px;align-items:start;}
  /* Campo Commessa in scheda: riepilogo avanzamento sotto al menu + selettore
     riga a chip (colore/articolo/quantità + stato della riga). */
  .fl-ordine-hint{font-size:12px;color:var(--muted);margin-top:6px;}
  .fl-ordine-note{color:var(--muted);}
  .ord-sum{display:flex;flex-direction:column;gap:6px;}
  .ord-sum-bar{height:6px;border-radius:99px;background:var(--line);overflow:hidden;}
  .ord-sum-bar>span{display:block;height:100%;background:var(--green2);border-radius:99px;/* la transizione sta sulla regola scaleX più avanti: qui la width non cambia più */}
  .ord-sum-txt{font-size:12px;color:var(--muted);}
  .ord-sum-txt b{color:var(--ink);}
  .ord-sum-rit{color:#c0492b;font-weight:700;}
  .ord-rigapick{display:flex;flex-direction:column;gap:8px;margin-top:2px;}
  .ord-rigachip{display:flex;align-items:center;gap:11px;width:100%;text-align:left;background:var(--panel);border:1px solid var(--line2);border-radius:10px;padding:9px 12px;cursor:pointer;transition:border-color var(--d-fast) var(--ease),background var(--d-fast) var(--ease);}
  .ord-rigachip:hover{border-color:var(--green2);}
  .ord-rigachip.on{border-color:var(--green2);background:var(--soft);}
  .ord-rigachip.covered{box-shadow:inset 3px 0 0 var(--soft-bd);}
  /* Voce GIÀ PRESA da un'altra lavorazione: il filetto verde tenue di .covered
     non si notava, e sceglierla fa partire la domanda «scollego quella?».
     Ambra = il colore che il gestionale usa già per «in lavorazione». */
  .ord-rigachip.occupata{box-shadow:inset 3px 0 0 var(--st-lav);}
  .orc-lock{font-size:10.5px;font-weight:800;color:var(--st-lav-tx);background:var(--st-lav-bg);
    border-radius:999px;padding:2px 8px;margin-left:6px;white-space:nowrap;}
  .ord-rigachip .orc-sw{flex:0 0 auto;width:15px;height:15px;border-radius:5px;border:2px solid var(--line2);}
  .ord-rigachip.on .orc-sw{border-color:var(--green2);background:var(--green2);box-shadow:inset 0 0 0 2px var(--panel);}
  .ord-rigachip .orc-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px;}
  .ord-rigachip .orc-c{font-weight:700;font-size:13.5px;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
  .ord-rigachip .orc-s{font-size:11.5px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
  .ord-rigachip .orc-state{flex:0 0 auto;font-size:11px;font-weight:700;color:var(--muted);white-space:nowrap;}
  .ord-rigachip .orc-dot{display:inline-block;width:7px;height:7px;border-radius:99px;margin-right:5px;vertical-align:middle;}
  .ord-rigachip .orc-todo{background:var(--muted2);}
  .ord-rigachip .orc-work{background:#d99a2b;}
  .ord-rigachip .orc-done{background:var(--green2);}
  .field label{font-size:10.5px;letter-spacing:.6px;text-transform:uppercase;color:var(--muted);font-weight:700;}
  /* gli input riempiono il campo e NON impongono una larghezza minima propria
     (min-width:0 + box-sizing), così le colonne della griglia possono ridursi
     sui telefoni senza far sforare il form. */
  .field input,.field select,.field textarea{width:100%;box-sizing:border-box;min-width:0;padding:7px 10px;border:1px solid var(--line2);border-radius:8px;background:var(--panel);font:inherit;color:var(--ink);}
  .field input:disabled,.field.locked input,.field.locked select,.field.locked textarea{background:#f1f3f5;color:#9aa6b2;}
  .field textarea{resize:vertical;min-height:42px;}
  /* Promemoria costo tintura/rasatura: scheda proveniente da tintura/rasatura con
     costo €/m² ancora vuoto. Nota discreta ma visibile sotto il campo. */
  .cost-remind{display:block;margin-top:4px;font-size:11px;font-weight:700;line-height:1.3;color:#8a5a00;background:#fff5e0;border:1px solid #f0d9a0;border-radius:6px;padding:4px 8px;}
  .cost-remind[hidden]{display:none;}
  /* Nota informativa sotto un campo costo (catena: crust incluso nella precedente,
     hint col costo attuale della lavorazione precedente). */
  .cost-note{display:block;margin-top:4px;font-size:11px;font-weight:600;line-height:1.35;color:var(--muted);}
  /* Nota del prezzo concordato in commessa: verde come il prezzo stesso ovunque
     compaia — e leggibile, dove il grigio --muted resta appena sotto AA. */
  .cost-note.ordhint{color:var(--green);}
  .cost-note[hidden]{display:none;}
  /* nota di conversione "prezzo a pelle → €/m²" (tintura/rasatura + scheda) */
  .cost-conv{display:block;margin-top:4px;font-size:11px;font-weight:600;line-height:1.4;color:var(--tan);background:var(--amber-bg);border:1px solid var(--amber-bd);border-radius:6px;padding:4px 8px;}
  .cost-conv[hidden]{display:none;}
  .cost-conv b{color:var(--amber-tx);font-weight:800;}
  /* segmento €/m² ↔ €/pelle nella label del campo costo */
  .cost-unit-seg{display:inline-flex;gap:0;margin-left:8px;border:1px solid var(--line2);border-radius:7px;overflow:hidden;vertical-align:middle;}
  .cost-unit-seg button{border:none;background:var(--card);color:var(--muted);font:inherit;font-size:11px;font-weight:800;text-transform:none;letter-spacing:0;padding:3px 9px;cursor:pointer;}
  .cost-unit-seg button+button{border-left:1px solid var(--line2);}
  .cost-unit-seg button.on{background:var(--green3);color:#fff;}
  input.derived{background:var(--bg);color:var(--muted);font-style:italic;}
  /* riga "incluso nella lavorazione precedente" nel riquadro margine */
  .mincl{color:var(--muted);font-weight:600;font-size:12px;}
  .sec-title{font-size:10.5px;letter-spacing:1px;text-transform:uppercase;color:var(--green);font-weight:800;margin:10px 0 5px;grid-column:1/-1;border-top:1px solid var(--line);padding-top:8px;}
  .sec-title.first{border-top:none;padding-top:0;margin-top:0;}
  .predict{grid-column:1/-1;background:var(--soft);border:1px solid var(--soft-bd);border-radius:9px;padding:7px 11px;font-size:12.5px;color:var(--green);font-weight:600;}
  .predict b{font-weight:800;}
  .chkrow{grid-column:1/-1;display:flex;gap:10px;flex-wrap:wrap;}
  .chkbox{display:inline-flex;align-items:center;gap:8px;padding:7px 11px;border:1px solid var(--line2);border-radius:9px;background:var(--panel);cursor:pointer;font-size:13px;font-weight:600;user-select:none;}
  .chkbox input{width:16px;height:16px;accent-color:var(--green2);}
  .chkbox.on{background:var(--soft);border-color:var(--green2);color:var(--green);}
  /* Toggle «n/d» (non applicabile) per campo: nell'etichetta, a destra. Spento il
     campo diventa grigio/tratteggiato e in stampa esce «—». */
  .flabrow{display:flex;align-items:baseline;justify-content:space-between;gap:8px;}
  .flabrow>label:first-child{flex:1 1 auto;min-width:0;}
  .na-toggle{flex:0 0 auto;display:inline-flex;align-items:center;gap:5px;font-size:10.5px;letter-spacing:.3px;text-transform:none;color:#8a9490;font-weight:800;cursor:pointer;user-select:none;}
  .na-toggle input{width:14px;height:14px;accent-color:#b98a3e;margin:0;cursor:pointer;}
  .field.na .na-toggle{color:#b98a3e;}
  .na-toggle.disabled{opacity:.35;pointer-events:none;}
  .field.na input,.field.na select{background:#f2f3f1;color:#aeb4ae;border-style:dashed;}
  /* Lavorazioni: ELENCO verticale compatto, ordinato alfabeticamente di default.
     Righe sottili a tutta larghezza dentro un unico contenitore bordato; i
     campi-dettaglio di una voce spuntata vanno a capo rientrati sotto al nome
     (Variante B). Il drag (⠿) personalizza l'ordine nella scheda corrente ma
     NON viene salvato: ogni nuova scheda riparte dall'ordine alfabetico. */
  .lavgrid{grid-column:1/-1;display:flex;flex-direction:column;border:1px solid var(--line2);border-radius:11px;overflow:hidden;background:var(--panel);}
  /* Barra riepilogo "Selezionate" in cima alla griglia: una pillola per ogni
     lavorazione spuntata — ⠿ trascina per riordinare (l'ordine delle chip è
     l'ordine di salvataggio/stampa; le finali restano in fondo), ✎/tap sul
     nome apre il popover dei campi dettaglio, ✕ deseleziona. */
  .lavselbar{display:flex;flex-wrap:wrap;align-items:center;gap:7px;padding:9px 12px;border-bottom:1px solid var(--line);background:var(--soft);}
  .lavselbar-l{font-size:10.5px;letter-spacing:.5px;text-transform:uppercase;color:var(--green);font-weight:800;margin-right:2px;white-space:nowrap;}
  .lavselbar-none{font-size:12.5px;color:var(--muted);font-style:italic;}
  .lavselchip{display:inline-flex;align-items:center;gap:5px;background:var(--panel);border:1px solid var(--soft-bd);color:var(--green);font-size:13.5px;font-weight:700;border-radius:999px;padding:5px 7px 5px 7px;white-space:nowrap;}
  .lavselchip.fin{padding-left:12px;}
  .lavselchip.dragging{opacity:.6;box-shadow:0 6px 18px rgba(0,0,0,.18);}
  .lavselgrip{cursor:grab;color:var(--muted2);font-size:13px;letter-spacing:-2px;line-height:1;user-select:none;touch-action:none;padding:3px 1px 3px 3px;}
  .lavselgrip:active{cursor:grabbing;}
  .lavseltxt.haspop{cursor:pointer;text-decoration:underline dotted var(--green2);text-underline-offset:3px;}
  .lavselsum{font-size:11px;font-weight:700;color:var(--green2);max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  .lavselx{border:none;background:none;cursor:pointer;color:var(--green2);font-size:14px;line-height:1;width:20px;height:20px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;padding:0;flex-shrink:0;}
  .lavselx:hover{background:var(--soft-bd);color:var(--green);}
  .lavseled{font-size:12px;}
  /* Popover modifica rapida campi dettaglio (ancorato alla chip, sopra il pannello) */
  .lavchip-pop{position:fixed;z-index:9500;background:var(--panel);border:1px solid var(--line2);border-radius:11px;box-shadow:var(--sh-lg);padding:12px;display:flex;flex-direction:column;gap:8px;min-width:210px;max-width:min(320px,92vw);}
  .lavchip-pop-t{font-size:11px;letter-spacing:.5px;text-transform:uppercase;font-weight:800;color:var(--green);}
  .lavchip-pop-f{display:flex;align-items:center;justify-content:space-between;gap:10px;font-size:12px;font-weight:700;color:var(--muted);}
  .lavchip-pop-f input{width:120px;padding:6px 9px;border:1px solid var(--line2);border-radius:7px;font:inherit;font-size:13px;background:var(--panel);color:var(--ink);}
  .lavchip-pop-f input:focus{outline:none;border-color:var(--green2);box-shadow:0 0 0 3px var(--ring);}
  .lavchip-pop-ok{align-self:flex-end;}
  .lavitem{background:var(--panel);border-bottom:1px solid var(--line);}
  .lavitem:last-of-type{border-bottom:none;}
  .lavhead{display:flex;align-items:center;min-height:38px;}
  .lavchk{flex:1;display:flex;align-items:center;gap:9px;padding:7px 11px;cursor:pointer;font-size:13.5px;font-weight:600;user-select:none;}
  .lavchk input{width:16px;height:16px;accent-color:var(--green2);flex-shrink:0;}
  .lavitem.on{background:var(--soft);}
  .lavitem.on .lavchk{color:var(--green);}
  .lavtools{display:flex;gap:1px;padding-right:7px;opacity:.55;}
  .lavtool{border:none;background:transparent;cursor:pointer;font-size:13px;color:var(--muted);padding:5px 7px;border-radius:7px;line-height:1;}
  .lavtool:hover{background:rgba(0,0,0,.06);color:var(--ink);}
  .lavgrip{cursor:grab;color:var(--muted2);font-size:15px;padding:0 2px 0 10px;user-select:none;line-height:1;letter-spacing:-2px;}
  .lavgrip:active{cursor:grabbing;}
  .lavitem.dragging{opacity:.55;background:var(--soft);box-shadow:0 6px 18px rgba(0,0,0,.12);}
  /* dettagli a capo, rientrati sotto al nome (allineati oltre la spunta) */
  .lavsub{display:none;flex-wrap:wrap;gap:8px 14px;padding:0 12px 10px 36px;}
  .lavitem.on .lavsub.show{display:flex;}
  /* campo dettaglio — etichetta e input in linea, compatti */
  .lavfield{display:inline-flex;align-items:center;gap:7px;min-width:0;}
  .lavfield-tog{display:inline-flex;align-items:center;gap:5px;font-size:10.5px;letter-spacing:.5px;text-transform:uppercase;color:var(--muted);font-weight:700;cursor:pointer;user-select:none;white-space:nowrap;}
  .lavfield-tog input[type=checkbox]{width:12px;height:12px;accent-color:var(--green2);margin:0;cursor:pointer;flex-shrink:0;}
  /* input del campo — compatto, larghezza contenuta */
  .lavfield input[data-lavfield],.lavfield select[data-lavfield]{padding:5px 9px;border:1px solid var(--line2);border-radius:7px;background:var(--panel);font:inherit;color:var(--ink);font-size:12.5px;width:96px;box-sizing:border-box;}
  .lavfield input[data-lavfield]:focus{outline:none;border-color:var(--green2);box-shadow:0 0 0 3px var(--ring);}
  .lavfield input[data-lavfield].field-off{opacity:.45;background:var(--soft);cursor:not-allowed;}
  .lavadd{border:none;border-top:1px solid var(--line);background:var(--soft);color:var(--green);font-weight:800;font-size:12.5px;padding:9px 12px;cursor:pointer;text-align:left;width:100%;}
  .lavadd:hover{background:var(--soft);}
  .lavef-row{display:flex;align-items:center;gap:8px;margin-bottom:8px;}
  .lavef-row input{flex:1;padding:8px 11px;border:1px solid var(--line2);border-radius:8px;font:inherit;font-size:14px;}
  .lavef-rm{border:none;background:transparent;cursor:pointer;color:var(--muted);font-size:16px;padding:4px 8px;border-radius:7px;}
  .lavef-rm:hover{background:rgba(192,73,43,.12);color:#c0492b;}
  .dlg-err{color:#c0492b;font-size:13px;font-weight:700;margin-top:8px;}
  /* Pop-up "scegli copia" alla stampa (terzista / interna / entrambe) */
  .pdfpick-opts{display:flex;flex-direction:column;gap:9px;}
  .pdfpick-opts .btn{width:100%;justify-content:center;padding:12px;font-size:14px;}
  /* Riga selezionata da tastiera (navigazione con le frecce) */
  .row.kbd-sel,.arow.kbd-sel,.prog-row.kbd-sel{outline:2px solid var(--green2);outline-offset:-2px;background:var(--soft)!important;}
  tr.clk.kbd-sel>td{background:var(--soft);}
  /* Pannello aiuto scorciatoie da tastiera */
  .kbdhelp-wrap{position:fixed;inset:0;z-index:95;display:flex;align-items:center;justify-content:center;background:rgba(15,25,18,.5);padding:20px;opacity:0;pointer-events:none;transition:opacity var(--d) var(--ease);}
  .kbdhelp-wrap.open{opacity:1;pointer-events:auto;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);}
  .kbdhelp-box{background:#fff;border-radius:var(--r-lg);max-width:440px;width:100%;max-height:calc(100vh - 40px);overflow:auto;overscroll-behavior:contain;box-shadow:var(--sh-lg);transform:translateY(14px) scale(.97);transition:transform var(--d) var(--ease-out);}
  .kbdhelp-wrap.open .kbdhelp-box{transform:none;}
  .kbdhelp-head{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--border);position:sticky;top:0;background:#fff;}
  .kbdhelp-head h3{font-size:17px;font-weight:800;color:var(--ink);}
  .kbdhelp-body{padding:8px 20px;}
  .kbdhelp-row{display:flex;align-items:center;gap:14px;padding:9px 0;border-bottom:1px solid var(--line);}
  .kbdhelp-row:last-child{border-bottom:none;}
  .kbdhelp-row kbd{flex:0 0 auto;min-width:92px;text-align:center;font-family:var(--sans);font-size:12.5px;font-weight:800;color:var(--ink);background:var(--sidebar);border:1px solid var(--line2);border-bottom-width:2px;border-radius:7px;padding:5px 9px;}
  .kbdhelp-row span{font-size:13.5px;color:var(--muted);font-weight:600;}
  .kbdhelp-foot{padding:12px 20px;border-top:1px solid var(--border);font-size:12px;color:var(--muted2);background:var(--sidebar);}
  .navsep{font-size:10px;letter-spacing:1.2px;text-transform:uppercase;color:rgba(255,255,255,.45);font-weight:800;padding:14px 14px 6px;margin-top:6px;border-top:1px solid rgba(255,255,255,.12);}
  .arow{display:grid;grid-template-columns:1fr auto;gap:12px;align-items:center;padding:14px 20px;border-bottom:1px solid var(--line);transition:background var(--d-fast) var(--ease);}
  .arow:last-child{border-bottom:none;}
  @media(hover:hover){ .arow:hover{background:#f8fafc;} }
  .arow .an{font-weight:800;font-size:15.5px;color:var(--ink);letter-spacing:-.1px;}
  .arow .am{font-size:12.5px;color:var(--muted);margin-top:3px;display:flex;gap:7px;flex-wrap:wrap;align-items:baseline;}
  .arow .am b{color:var(--green2);font-weight:700;}
  .achip{font-size:11px;font-weight:700;color:var(--green);background:var(--soft);border:1px solid var(--soft-bd);border-radius:7px;padding:2px 8px;}
  .aedit-fields .field{margin-bottom:9px;}
  .aedit-row2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:11px;}

  /* dashboard */
  .dash-controls{display:flex;gap:10px;align-items:center;margin-bottom:16px;flex-wrap:wrap;}
  .rep-launch{display:inline-flex;gap:6px;align-items:center;margin-left:auto;}
  .rep-launch input[type=month]{border:1px solid var(--line2);border-radius:8px;padding:5px 8px;font:inherit;font-size:13px;background:var(--card,#fff);color:inherit;}
  /* Commesse — «scheda con corsia di stato» (variante A approvata, v17.7).
     L'urgenza si legge PRIMA del testo: corsia colorata sul fianco sinistro,
     scadenza in pastiglia, percentuale grande. Prima erano tutte card bianche
     identiche, ordinate per data, e per sapere quale scottava bisognava
     leggerle tutte. */
  .ordcard{position:relative;overflow:hidden;background:var(--card,#fff);border:1px solid var(--line2);border-radius:13px;
    padding:15px 16px 6px 19px;margin-bottom:16px;box-shadow:var(--sh-sm);transition:box-shadow var(--d-fast),border-color var(--d-fast);}
  /* La corsia è un ::before e non un border-left: il bordo colorato avrebbe
     spostato il contenuto di 4px rispetto alle card senza urgenza. */
  .ordcard::before{content:"";position:absolute;left:0;top:0;bottom:0;width:4px;background:var(--line2);}
  .ordcard.u-late::before{background:var(--st-late);}
  .ordcard.u-soon::before{background:var(--st-lav);}
  .ordcard.u-ok::before{background:var(--st-cons);}
  .ordcard.u-done::before,.ordcard.u-none::before{background:var(--line2);}
  .ordcard.ord-chiuso{opacity:.72;}
  .ordtop{display:flex;align-items:flex-start;gap:14px;}
  .ordid{flex:1;min-width:0;}
  /* Quando scade, a parole: «scaduta da 3 gg», «consegna domani», «fra 21 gg».
     La data sotto resta per chi la vuole esatta. */
  .ordwhen{flex:0 0 auto;text-align:right;font-size:11.5px;color:var(--muted);line-height:1.3;}
  .ordwhen b{display:block;font-size:12.5px;font-weight:800;border-radius:999px;padding:3px 11px;
    background:var(--st-bozza-bg);color:var(--st-bozza-tx);}
  .ordwhen.u-late b{background:var(--st-late-bg);color:var(--st-late-tx);}
  .ordwhen.u-soon b{background:var(--amber-bg);color:var(--amber-tx);}
  .ordwhen.u-ok b{background:var(--st-cons-bg);color:var(--st-cons-tx);}
  .ordwhen span{display:block;margin-top:3px;}
  .ordbig{flex:0 0 auto;text-align:right;min-width:66px;}
  .ordbig .n{display:block;font-family:var(--serif);font-size:27px;font-weight:600;line-height:1;letter-spacing:-.5px;color:var(--ink);}
  .ordbig .n i{font-style:normal;font-size:15px;color:var(--muted);}
  .ordbig .l{display:block;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--muted);margin-top:3px;}
  .ordcard-more{flex:0 0 auto;color:var(--muted);}
  /* I tre numeri che contano, ognuno con la sua etichetta: prima erano una riga
     grigia unica, dove valore e conteggi pesavano uguale. */
  .ordstats{display:flex;gap:26px;margin:13px 0 4px;padding:10px 0;border-top:1px solid var(--line);border-bottom:1px solid var(--line);}
  .ordstats>div{display:flex;flex-direction:column;min-width:0;}
  .ordstats b{font-size:15px;font-weight:800;color:var(--ink);line-height:1.1;}
  .ordstats>div>span{font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.4px;color:var(--muted);margin-top:2px;}
  /* Etichetta lunga/corta: un'etichetta non è ritagliabile in CSS, quindi si
     scrivono entrambe e se ne nasconde una (stesso schema della ricerca
     globale). Sempre SCOPED: `.sm` nuda colpirebbe ogni `.btn.sm` dell'app. */
  .ordstats .sm{display:none;}
  /* Chip di riepilogo/filtro, gemelli di quelli del Programma lavorazioni. */
  .ordchips{display:flex;gap:8px;flex-wrap:wrap;margin:2px 0 16px;}
  .ordchip-urg{display:inline-flex;align-items:baseline;gap:6px;font:inherit;font-size:12.5px;font-weight:700;color:var(--muted);
    background:var(--card,#fff);border:1px solid var(--line2);border-radius:999px;padding:7px 14px;cursor:pointer;
    transition:background var(--d-fast) var(--ease),border-color var(--d-fast) var(--ease);}
  .ordchip-urg b{font-size:15px;font-weight:800;color:var(--ink);}
  .ordchip-urg.late{border-color:#e7b7af;color:var(--st-late-tx);} .ordchip-urg.late b{color:var(--st-late-tx);}
  .ordchip-urg.soon{border-color:var(--amber-bd);color:var(--amber-tx);} .ordchip-urg.soon b{color:var(--amber-tx);}
  .ordchip-urg.ok{border-color:var(--soft-bd);} .ordchip-urg.ok b{color:var(--green);}
  .ordchip-urg.on{background:var(--sidebar);border-width:2px;padding:6px 13px;}
  /* «in tempo» e «completate» non hanno un colore proprio: restano su --muted,
     che sul fondo del chip selezionato si ferma a 4,34:1. Da selezionati il
     testo passa a --ink (il bordo continua a portare il colore). */
  .ordchip-urg.ok.on,.ordchip-urg.done.on{color:var(--ink);}
  .ordchip-urg .sm{display:none;}
  @media(hover:hover){ .ordchip-urg:hover{background:var(--sidebar);} }
  /* Menu «⋯»: le azioni escono dalla riga (erano fino a 18 per commessa) e
     restano tutte, a un tocco. Sul telefono diventa un foglio in fondo. */
  .ordmenu{position:fixed;z-index:90;min-width:212px;max-width:min(320px,calc(100vw - 16px));
    background:var(--panel);border:1px solid var(--line2);border-radius:12px;box-shadow:var(--sh-lg);padding:6px;
    /* ⚠️ CRESCE DAL PULSANTE CHE L'HA APERTO (v20). Appariva di colpo, mentre
       le tendine dei filtri — che sono la stessa cosa — hanno `dlgpop` da
       sempre. `transform-origin` lo scrive `_ordOpenMenu` in base al lato in
       cui il menu si è ancorato, così l'origine del movimento coincide col
       comando: aprendo verso l'alto deve crescere dal basso, non dall'alto. */
    animation:dlgpop var(--d-fast) var(--ease-out);
    transform-origin:var(--menu-orig,top right);}
  .ordmenu button{display:block;width:100%;text-align:left;font:inherit;font-size:13.5px;font-weight:600;color:var(--ink);
    background:none;border:0;border-radius:8px;padding:9px 10px;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
  .ordmenu button.del{color:var(--st-late-tx);}
  .ordmenu .sep{height:1px;background:var(--line);margin:5px 4px;}
  @media(hover:hover){ .ordmenu button:hover{background:var(--sidebar);} }
  /* Intestazione del menu: serve SOLO quando il menu è un foglio dal basso
     (telefono), dove copre la riga che hai appena toccato e va detto di quale
     record si tratta. Sul computer il menu si apre attaccato al pulsante e
     l'intestazione sarebbe una riga sprecata. */
  .ordmenu .pst-head{display:none;}
  @media(max-width:640px){
    .ordcard{padding:12px 12px 2px 15px;margin-bottom:12px;}
    /* Testata su due righe: numero e scadenza sopra, cliente e percentuale
       sotto. Il «⋯» resta in alto a destra, dove si cerca. */
    .ordtop{display:grid;grid-template-columns:minmax(0,1fr) auto auto;gap:2px 8px;align-items:center;}
    .ordid{grid-area:1/1/3/2;}
    .ordwhen{grid-area:1/2/2/3;justify-self:end;}
    .ordwhen b{font-size:11.5px;padding:2px 9px;} .ordwhen span{display:none;}
    .ordbig{grid-area:2/2/3/3;justify-self:end;display:flex;align-items:baseline;gap:5px;min-width:0;}
    .ordbig .n{font-size:19px;} .ordbig .l{display:none;}
    .ordcard-more{grid-area:1/3/3/4;align-self:center;}
    /* Le tre misure su UNA riga (come la .rmeta-grid delle schede su mobile),
       non tre blocchi impilati. */
    .ordid{min-width:0;}
    /* Data e note: sul telefono facevano andare a capo la testata. Da quando il
       cliente è salito nel titolo (v19.8) la riga di servizio contiene solo
       loro, quindi si spegne tutta invece del solo `.ordsub-x`. */
    .ordsub{display:none;}
    .ordstats{gap:12px;margin:9px 0 1px;padding:7px 0;}
    .ordstats>div{flex-direction:row;align-items:baseline;gap:4px;white-space:nowrap;}
    .ordstats b{font-size:12.5px;}
    .ordstats>div>span{font-size:11px;font-weight:600;text-transform:none;letter-spacing:0;margin:0;}
    .ordstats .lg{display:none;} .ordstats .sm{display:inline;}
    .ordchips{gap:6px;margin-bottom:12px;}
    .ordchip-urg{padding:6px 11px;font-size:12px;}
    .ordchip-urg .lg{display:none;} .ordchip-urg .sm{display:inline;}
    /* Il menu diventa un foglio in fondo allo schermo: un popup ancorato a un
       pulsante da 30px, sul telefono, si apre sempre nel posto sbagliato.
       Gli !important battono la posizione inline scritta da _ordOpenMenu. */
    .ordmenu{left:8px!important;right:8px!important;top:auto!important;
      bottom:calc(12px + env(safe-area-inset-bottom));width:auto;max-width:none;border-radius:16px;padding:8px;
      /* ⚠️ Qui il menu È un foglio dal basso, e un foglio SALE: `dlgpop` lo
         farebbe gonfiare dal centro come un popup ancorato, cioè racconterebbe
         un gesto che non è quello. Stessa curva dei fogli veri. */
      animation:ordSheetIn var(--d-slow) var(--ease-out)!important;transform-origin:bottom center;}
    @keyframes ordSheetIn{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
    .ordmenu button{padding:13px 12px;font-size:15px;}
    .ordmenu .pst-head{display:block;font-size:15px;font-weight:850;color:var(--ink);padding:8px 12px 4px;}
    .ordmenu .pst-head small{display:block;font-size:11.5px;font-weight:700;color:var(--muted);
      text-transform:uppercase;letter-spacing:.04em;margin-top:2px;}
  }
  .ordpick-colore{font-size:15.5px;font-weight:800;color:var(--green,#003C0F);line-height:1.15;}
  /* Catena di lavorazione (tracciabilità) nell'anteprima */
  .cl-block .cl-step{display:flex;align-items:center;gap:10px;padding:8px 10px;border:1px solid var(--line2,#e6e9ec);border-radius:11px;background:var(--card,#fff);cursor:pointer;transition:background var(--d-fast),border-color var(--d-fast);}
  .cl-block .cl-step:hover{background:var(--sidebar,#f1f5f9);}
  .cl-block .cl-step.cl-cur{border-color:var(--green,#003C0F);border-width:2px;background:var(--sidebar,#f1f5f9);}
  .cl-ico{font-size:16px;flex:0 0 auto;width:22px;text-align:center;}
  .cl-main{flex:1;min-width:0;}
  .cl-num{font-weight:800;font-size:13.5px;}
  .cl-here{font-size:10px;font-weight:800;color:var(--green,#003C0F);background:var(--card,#fff);border:1px solid var(--soft-bd);border-radius:999px;padding:1px 7px;margin-left:4px;text-transform:uppercase;letter-spacing:.4px;}
  .cl-lab{font-size:12px;color:var(--muted);margin-top:1px;}
  .cl-arrow{text-align:center;color:var(--muted);font-size:14px;line-height:1;margin:3px 0;}
  #fl-rifprec-box{display:flex;align-items:center;gap:8px;background:var(--sidebar,#f1f5f9);border:1px solid var(--line2,#cdd7e1);border-radius:10px;padding:8px 12px;}
  #fl-rifprec-box .rifprec-ico{font-size:16px;color:var(--green,#003C0F);}
  #fl-rifprec-box .rifprec-txt{flex:1;font-size:13px;}
  /* Testata della card: il CLIENTE è il titolo, il numero gli sta accanto in
     pastiglia (variante B approvata, v19.8). Prima il numero era il titolo nero
     in grassetto e il nome la riga grigia sotto, da 12,5px — ma una commessa si
     cerca per nome, non per numero.
     ⚠️ `min-width:0` sul nome: dentro un flex il minimo automatico è il
     CONTENUTO, quindi un cliente lungo allargherebbe la testata (e su telefono
     la riga) invece di troncare con i puntini. */
  .ordcli-row{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap;min-width:0;}
  .ordcli{min-width:0;font-size:18px;font-weight:800;color:var(--ink);line-height:1.15;letter-spacing:-.2px;
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
  /* ⚠️ La misura per il telefono sta QUI, in una media query subito dopo la
     regola base, e non nel blocco ≤640px più in alto nel file: lì ha la stessa
     specificità ma viene prima, e vincerebbe il 18px (stesso inciampo del dock
     e del modale schede-crust). Più piccolo entrano ~5 caratteri in più, cioè
     un nome medio sta in riga con la pastiglia invece di mandarla a capo. */
  /* Sul telefono la `.ordid` è larga ~190px: un nome oltre i ~13 caratteri manda
     la pastiglia a capo, e la testata cresce. È il costo dichiarato della
     variante — il nome INTERO vale più di 11px — ma lo spazio fra le due righe
     si stringe, così si paga il meno possibile. */
  @media(max-width:640px){ .ordcli{font-size:16.5px;} .ordcli-row{row-gap:1px;} }
  /* La pastiglia è un PULSANTE vero: sul telefono la card non è cliccabile per
     intero, quindi è l'unico modo di aprire l'anteprima — ed è l'appiglio da
     tastiera, che una card cliccabile non offre. */
  .ordnum-pill{flex:0 0 auto;font:inherit;font-size:11.5px;font-weight:800;letter-spacing:.3px;color:var(--green);
    background:var(--soft);border:1px solid var(--soft-bd);border-radius:999px;padding:2px 10px;cursor:pointer;
    transition:background var(--d-fast) var(--ease),border-color var(--d-fast) var(--ease);}
  @media(hover:hover){ .ordnum-pill:hover{background:var(--card,#fff);border-color:var(--green);} }
  .ordriga-prezzo{color:var(--green);font-weight:700;}
  .ord-valore{color:var(--green);font-weight:700;}
  .ord-valore-p{color:var(--muted);font-weight:600;}
  /* Tutta la card apre l'anteprima (v19.8), righe dei colori comprese — e dalla
     v19.9 SUL TELEFONO ANCHE (richiesta dell'utente): lì il bersaglio era la
     sola pastiglia del numero, 66×23px, alta la metà dei 44px comodi al pollice.
     Due segnali distinti, perché i due dispositivi ne danno di diversi:
     - col mouse, al passaggio: bordo verde e ombra alzata, come le righe schede;
     - col dito, alla pressione: la card rientra di un soffio e prende lo stesso
       bordo verde dell'hover. Sul telefono l'hover non esiste, quindi senza
       questo non ci sarebbe NULLA fra il dito e la finestra che si apre, e
       un'apertura che tarda un istante sembrerebbe un tocco perso.
     ⚠️ La classe `.ordcard-press` la mette e la toglie il JS (app.9c) e non è
     un `:active`: su iOS `:active` non scatta in modo affidabile su un elemento
     che non è un pulsante, ed è comunque da togliere anche quando il gesto
     diventa uno scorrimento e nessun click arriva.
     ⚠️ Il segnale è FORMA (scala + bordo + ombra), non una campitura: col fondo
     a `--sidebar` le etichette della fascia numeri, che sono `--muted`, sarebbero
     scese a 4,34:1 — la solita trappola del grigio sui fondi tenui. */
  .ordcard-clic{cursor:pointer;
    transition:border-color var(--d-fast) var(--ease),box-shadow var(--d-fast) var(--ease),
      transform var(--d-fast) var(--ease);}
  @media(hover:hover){ .ordcard-clic:hover{border-color:var(--green2);box-shadow:var(--sh);} }
  .ordcard-clic.ordcard-press{transform:scale(.985);border-color:var(--green2);box-shadow:var(--sh);}
  @media (prefers-reduced-motion: reduce){
    .ordcard-clic{transition:none;}
    .ordcard-clic.ordcard-press{transform:none;}
  }
  .ordsub{font-size:12.5px;color:var(--muted);margin-top:2px;}
  .ordbar{height:8px;border-radius:999px;background:var(--line2,#e6e9ec);overflow:hidden;margin:11px 0 8px;}
  .ordbar-fill{height:100%;border-radius:999px;}
  .ordmeta{display:flex;gap:14px;align-items:center;font-size:12.5px;color:var(--muted);flex-wrap:wrap;}
  .ordmeta b{color:var(--text,#0f2438);}
  .ordmeta .ordpct{margin-left:auto;font-weight:800;color:var(--brand,#003C0F);}
  .pill.ordpill{background:#eef3ff;color:#2b4d8a;border:1px solid #d3ddf3;font-weight:700;}
  html[data-theme="dark"] .pill.ordpill{background:#1e2a40;color:#9db8e8;border-color:#33456a;}
  .pill.catpill{background:var(--catena-bg);color:var(--catena-tx);border:1px solid var(--catena-bd);font-weight:700;}
  /* Editor commessa: form aggiungi-riga + lista righe.
     Il dialogo è più largo dei 640px comuni perché il form riga ha sei campi
     (colore, articolo, m², pelli, prezzo, consegna): a 640 i due campi di testo
     restavano sotto i 100px e la griglia sbordava. */
  #ordedit-wrap .dlgbox{max-width:880px;}
  .ordr-add{background:var(--sidebar,#f1f5f9);border:1px solid var(--line2,#cdd7e1);border-radius:11px;padding:10px;margin-bottom:8px;}
  /* ⚠️ .ordr-add-grid è CONDIVISA con il listino dell'editor cliente (4 campi):
     il form riga della commessa, che ne ha 6, si distingue con .rigacom — dando
     6 colonne alla classe base i due campi del listino cliente finivano a
     larghezza zero e la griglia sbordava dal dialogo. Le celle hanno min-width:0
     perché un <input> ha una larghezza minima intrinseca che, in una colonna fr,
     allarga la griglia oltre il contenitore invece di stringersi. */
  .ordr-add-grid{display:grid;grid-template-columns:1.4fr 1.4fr 90px 80px;gap:8px;margin-bottom:8px;}
  .ordr-add-grid.rigacom{grid-template-columns:1.1fr 1.1fr 76px 70px 92px 128px;}
  .ordr-add-grid>div{min-width:0;}
  .ordr-add-grid input{width:100%;box-sizing:border-box;min-width:0;}
  .ordr-lab{display:block;font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--muted);margin-bottom:3px;}
  .ordr-empty{font-size:12.5px;color:var(--muted);padding:8px 2px;}
  .ordr-item{padding:8px 10px;border:1px solid var(--line2,#e6e9ec);border-radius:9px;margin-bottom:6px;background:var(--card,#fff);}
  .ordr-item.open{border-color:var(--amber-bd,#e6cf9a);}
  .ordr-item-head{display:flex;align-items:center;justify-content:space-between;gap:10px;}
  .ordr-item-main{min-width:0;font-size:13.5px;}
  .ordr-item-acts{display:flex;gap:4px;flex-shrink:0;flex-wrap:wrap;justify-content:flex-end;}
  /* Tipo di grezzo della voce: NON è un campo della commessa, lo dichiara
     l'articolo (ordRigaGrezzo, app.9c). Pastiglia solo qui, nell'elenco
     dell'editor dove la riga è affollata; nella tabella della pagina Commesse
     sta in chiaro come gli altri dati. Ambra = dato interno, come il resto
     della famiglia --amber-* (ridefinita nel tema scuro, a differenza di --tan). */
  .grezzo-chip{display:inline-flex;align-items:center;gap:4px;font-size:11px;font-weight:800;border-radius:999px;padding:1px 8px;
    background:var(--amber-bg);color:var(--amber-tx);border:1px solid var(--amber-bd);white-space:nowrap;vertical-align:middle;}
  /* Nota sotto il campo Articolo del form riga: in ambra quando l'articolo non
     dichiara il grezzo (lì la scheda resterà libera). */
  .cost-note.miss{color:var(--amber-tx);font-weight:700;}
  /* Editor consegne inline per articolo */
  .ordr-item-cons{border-top:1px dashed var(--line2,#e6e9ec);margin-top:8px;padding-top:8px;}
  .ordr-cons-list{margin-bottom:6px;}
  .ordr-cons-empty{font-size:12px;color:var(--muted);padding:2px 0 6px;}
  .ordr-cons-add{display:grid;grid-template-columns:1fr 84px 74px auto;gap:8px;margin-bottom:6px;align-items:start;}
  .ordr-cons-add>input,.ordr-cons-add .datefield-wrap input{width:100%;box-sizing:border-box;font:inherit;font-size:13px;border:1px solid var(--line2);border-radius:7px;padding:6px 8px;background:var(--panel);color:var(--ink);}
  .ordr-cons-add .datefield-wrap{display:block;}
  .ordr-cons-add .datefield-wrap input{padding-right:28px;}
  .ordr-cons-row{display:flex;align-items:center;gap:8px;font-size:12.5px;padding:3px 0;}
  .ordr-cons-row .occ-q{font-weight:700;}
  .ordr-cons-row .occ-d{color:var(--muted);}
  .ordr-cons-sum{font-size:11.5px;color:var(--muted);}
  .ordr-cons-sum.warn{color:#8a6516;font-weight:700;}
  @media(max-width:640px){ .ordr-cons-add{grid-template-columns:1fr 1fr;} }
  .ord-group-h{display:flex;align-items:center;gap:8px;font-size:12px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:var(--muted);margin:26px 0 12px;padding-bottom:6px;border-bottom:2px solid var(--line2,#e6e9ec);}
  .ord-group-h:first-child{margin-top:6px;}
  .ord-group-h span{background:var(--line2,#cdd7e1);color:var(--text,#0f2438);border-radius:999px;padding:1px 9px;font-size:11px;}
  .ord-group-h.ord-group-done{margin-top:22px;color:var(--green);border-color:var(--soft-bd);}
  /* Le VOCI della commessa sono una tabella incolonnata, non più due blocchi
     alle estremità: quantità, prezzo, valore, consegna, avanzamento e stato
     cadono sempre nello stesso punto, così si leggono in verticale da una riga
     all'altra. Stesso markup nella pagina e nell'anteprima della commessa.
     ⚠️ LE COLONNE SONO DELLA GRIGLIA MADRE, NON DELLA SINGOLA VOCE (v19.101).
     Fino alla v19.100 `display:grid` stava sulla sola `.ordriga`, quindi OGNI
     voce era una griglia a sé: l'ultima colonna sono i PULSANTI, che cambiano
     larghezza secondo quanti sono (182px con «＋ Crea», 113 senza), e la
     differenza se la mangiavano le due colonne elastiche. Misurato
     nell'anteprima: la colonna «Quantità» cominciava a 80px su due voci e a 149
     sulle altre due — 69px di scarto nella stessa tabella — e quella del colore
     oscillava fra 68 e 137px. L'intestazione c'era già e non allineava nulla,
     perché non condivideva le colonne con le righe: a incolonnare è `subgrid`.
     ⚠️ Il ripiego senza subgrid NON è la vecchia griglia per-riga ma le stesse
     colonne con le AZIONI a larghezza fissa (184px = il caso più largo, tre
     pulsanti): incolonnato uguale, al prezzo di un po' d'aria sulle voci con
     due soli pulsanti. Se il browser non conosce `subgrid`, quindi, si degrada
     bene invece di tornare al difetto. */
  .ord-righe{margin-top:2px;}
  .ordriga-head,.ordriga{display:grid;
    grid-template-columns:minmax(0,2fr) 74px 84px 92px 62px minmax(80px,.7fr) 116px 184px;
    column-gap:10px;align-items:center;}
  @supports (grid-template-columns:subgrid){
    .ord-righe{display:grid;
      grid-template-columns:minmax(0,2fr) 74px 84px 92px 62px minmax(80px,.7fr) 116px auto;
      column-gap:10px;}
    .ordriga-head,.ordriga{grid-column:1/-1;grid-template-columns:subgrid;}
  }
  .ordriga-head{font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:.5px;color:var(--muted);padding:6px 0 5px;}
  .ordriga-head span:nth-child(2),.ordriga-head span:nth-child(3),
  .ordriga-head span:nth-child(4){text-align:right;}
  .ordriga-head span:nth-child(5){text-align:center;}
  /* Prezzo concordato e valore della voce (m² × prezzo). Il valore non si vedeva
     da nessuna parte: della commessa c'era solo il totale, e per sapere quanto
     pesa un colore bisognava farlo a mente.
     ⚠️ Il prezzo è `--ink`, NON `--muted`: nell'anteprima questa tabella sta
     dentro il riquadro ambra, dove il grigio tenue scende a 4,27:1 — sotto AA
     (la stessa trappola già pagata da `.ordmeta` dentro `.pvord`). A separarlo
     dal valore è il PESO, non un colore sbiadito. */
  .ordriga-cpz{font-size:12.5px;font-weight:600;color:var(--ink);text-align:right;white-space:nowrap;}
  .ordriga-cval{font-size:12.5px;font-weight:800;color:var(--green);text-align:right;white-space:nowrap;}
  .ordriga{padding:9px 0;border-top:1px solid var(--line);}
  .ordriga-c1{min-width:0;}
  .ordriga-col{font-weight:800;font-size:13.5px;line-height:1.2;}
  .ordriga-sub{font-size:11.5px;color:var(--muted);margin-top:1px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  .ordriga-c2{font-size:12.5px;font-weight:700;color:var(--ink);text-align:right;}
  .ordriga-c3{text-align:center;}
  .ordriga-c4{display:flex;align-items:center;gap:8px;}
  .ordriga-c6{display:flex;gap:5px;justify-content:flex-end;}
  /* La barra prende il colore dello STATO della lavorazione (gli stessi delle
     schede): prima era sempre lo stesso verde, quindi riempiva senza dire. */
  .ordriga-bar{flex:1;min-width:44px;height:6px;border-radius:999px;background:var(--line2,#e6e9ec);overflow:hidden;}
  .ordriga-bar i{display:block;height:100%;border-radius:999px;background:var(--st-lav);transition:width var(--d-slow) var(--ease-out);}
  .ordriga-bar.st-done i{background:var(--st-cons);}
  .ordriga-bar.st-ready i{background:var(--st-pronta);}
  .ordriga-bar.st-todo i{background:var(--line2);}
  .ordriga-pct{font-size:11.5px;font-weight:800;color:var(--muted);min-width:32px;text-align:right;}
  .ordriga-st{display:inline-block;font-size:11px;font-weight:800;border-radius:999px;padding:2px 9px;white-space:nowrap;
    background:var(--st-bozza-bg);color:var(--st-bozza-tx);}
  .ordriga-st.st-work{background:var(--st-lav-bg);color:var(--st-lav-tx);}
  .ordriga-st.st-ready{background:var(--st-pronta-bg);color:var(--st-pronta-tx);}
  .ordriga-st.st-done{background:var(--st-cons-bg);color:var(--st-cons-tx);}
  .ordriga-date{display:inline-block;font-size:11.5px;font-weight:800;border-radius:7px;padding:2px 7px;
    border:1px solid var(--line2);color:var(--muted);white-space:nowrap;}
  .ordriga-date.late{color:var(--st-late-tx);border-color:#e7b7af;background:var(--st-late-bg);}
  .ordriga-date.soon{color:var(--amber-tx);border-color:var(--amber-bd);background:var(--amber-bg);}
  .ordriga-date.ok{color:var(--st-cons-tx);border-color:var(--soft-bd);background:var(--st-cons-bg);}
  .ordriga-nodate{font-size:12px;color:var(--muted);}
  .ordriga-more{color:var(--muted);}
  /* Fascia stretta ma ancora tabellare (1180–1399px): le colonne fisse si
     stringono di un filo e le azioni scendono a 170px, o al nome del colore non
     resta niente. Misurato a 1180px: senza questa riga la colonna del colore va
     a ~38px nella pagina Commesse — la card è più STRETTA della finestra
     dell'anteprima (barra laterale + padding), quindi è lei a dettare la
     soglia, non l'anteprima. */
  @media(min-width:1180px) and (max-width:1399px){
    .ord-righe,.ordriga-head,.ordriga{
      grid-template-columns:minmax(0,2fr) 66px 78px 86px 56px minmax(72px,.7fr) 104px 170px;
      column-gap:8px;}
    @supports (grid-template-columns:subgrid){
      .ord-righe{grid-template-columns:minmax(0,2fr) 66px 78px 86px 56px minmax(72px,.7fr) 104px auto;}
      .ordriga-head,.ordriga{grid-template-columns:subgrid;}
    }
  }
  /* ⚠️ LA SOGLIA DELLA TABELLA È 1180px, NON 640 (v19.101) — e il numero è
     MISURATO, non scelto: a otto colonne servono ~900px di tabella perché al
     colore restino ~150px (428 di colonne fisse + 170÷184 di pulsanti + le
     distanze + gli 80 minimi della barra), e la tabella è la finestra meno ~255
     nell'anteprima (rail 189 + padding) o meno ~340 nella pagina Commesse
     (barra laterale + padding della card). Sotto, la voce si IMPILA — la stessa
     forma del telefono, che è anche il motivo per cui la soglia è una sola.
     Comprimere e basta non bastava: erano già stretti i 641–720px quando le
     colonne erano sei (quella media query è stata rimossa).
     ⚠️ Queste regole stanno QUI, subito dopo quelle base, e NON nel grande
     blocco ≤640px più avanti nel file: là a parità di specificità vincerebbe
     l'ultima scritta e il layout impilato si riprenderebbe anche il computer
     (stesso inciampo già pagato dal dock e dal modale schede-crust). */
  @media(max-width:1179px){
    /* Quattro righe, sempre allineate:
       colore | quantità · barra | prezzo · consegna | valore · stato | azioni */
    .ordriga-head{display:none;}
    .ordriga{grid-template-columns:minmax(0,1fr) auto;gap:7px 10px;padding:9px 0;}
    .ordriga-c1{grid-column:1;grid-row:1;}
    .ordriga-col{font-size:13px;} .ordriga-sub{font-size:11px;}
    .ordriga-c2{grid-column:2;grid-row:1;align-self:center;font-size:12px;}
    .ordriga-c4{grid-column:1;grid-row:2;align-self:center;}
    .ordriga-cpz{grid-column:2;grid-row:2;align-self:center;justify-self:end;font-size:11.5px;}
    .ordriga-c3{grid-column:1;grid-row:3;align-self:center;}
    .ordriga-cval{grid-column:2;grid-row:3;align-self:center;justify-self:end;}
    .ordriga-c3 .ordriga-nodate{display:none;}
    .ordriga-c5{grid-column:1;grid-row:4;}
    .ordriga-c6{grid-column:2;grid-row:4;justify-self:end;}
  }
  @supports (grid-template-columns:subgrid){
    /* la griglia madre vale solo dove c'è la tabella: impilate, le voci tornano
       a essere ognuna la propria griglia a due colonne */
    @media(max-width:1179px){ .ord-righe{display:block;} }
  }
  .ordriga-empty,.ord-libere{font-size:12.5px;color:var(--muted);padding:6px 0;}
  .ord-libere{margin-top:6px;border-top:1px solid var(--line2,#e6e9ec);padding-top:8px;}
  .ord-ampl{margin-top:8px;border-top:1px solid var(--line2,#e6e9ec);padding-top:8px;}
  .ord-ampl-h{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.6px;color:#8a6516;margin-bottom:5px;}
  .ord-ampl-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:12.5px;color:var(--ink);padding:3px 0;}
  .ord-ampl-tag{font-weight:800;color:#8a6516;background:var(--amber-bg,#fdf6e6);border:1px solid var(--amber-bd,#e6cf9a);border-radius:999px;padding:0 8px;font-size:11px;}
  .ord-ampl-q{font-weight:700;}
  .ord-ampl-riga,.ord-ampl-st,.ord-ampl-d{color:var(--muted);font-size:12px;}
  .ord-scad{display:inline-block;font-size:11px;font-weight:700;border-radius:999px;padding:0 8px;border:1px solid var(--line2,#e6e9ec);color:var(--muted);white-space:nowrap;}
  .ord-scad.soon{color:#8a6516;border-color:var(--amber-bd,#e6cf9a);background:var(--amber-bg,#fdf6e6);}
  .ord-scad.late{color:#b23b2e;border-color:#e7b7af;background:#fdeeec;}
  .ord-scad.done{color:var(--green);border-color:var(--soft-bd);background:var(--soft);}
  .ord-consegna{color:var(--green);font-weight:700;}
  .ordr-scad{font-size:11px;font-weight:700;color:#8a6516;}
  .ordriga-cons{display:flex;flex-wrap:wrap;gap:5px;margin-top:5px;width:100%;}

  .ordchip{display:inline-flex;align-items:center;font-size:11px;font-weight:700;padding:2px 8px;border-radius:999px;border:1px solid transparent;white-space:nowrap;}
  .ordchip-work{background:#fff5e6;color:#a6641b;border-color:#f0d5a8;}
  .ordchip-ready{background:#e7f0fb;color:#1f5aa8;border-color:#c3d8f2;}
  .ordchip-done{background:var(--soft);color:var(--green);border-color:var(--soft-bd);}
  .ordchip-todo{background:var(--line2,#cdd7e1);color:var(--muted);}
  html[data-theme="dark"] .ordchip-work{background:#3a2e19;color:#e6b877;border-color:#5a4620;}
  html[data-theme="dark"] .ordchip-ready{background:#1e2c3f;color:#8fbaf0;border-color:#2f4a6b;}
  html[data-theme="dark"] .ordchip-done{background:var(--soft);color:var(--green);border-color:var(--soft-bd);}
  .metric-toggle{display:inline-flex;border:1px solid var(--line2);border-radius:9px;overflow:hidden;}
  .metric-toggle button{font:inherit;font-weight:700;border:none;background:var(--panel);color:var(--muted);padding:9px 16px;cursor:pointer;}
  .metric-toggle button.on{background:var(--green);color:#fff;}
  .qbtns{display:flex;gap:6px;flex-wrap:wrap;align-items:center;}
  .qbtns button{font:inherit;font-weight:700;font-size:12.5px;border:1px solid var(--line2);background:var(--panel);color:var(--ink);border-radius:8px;padding:8px 11px;cursor:pointer;}
  .qbtns button.on{background:var(--green);color:#fff;border-color:var(--green);}
  .kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:14px;margin-bottom:8px;}
  .kpi{background:var(--card);border:1px solid var(--border);border-radius:var(--r);padding:16px 18px;box-shadow:var(--sh);position:relative;overflow:hidden;}
  .kpi::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:linear-gradient(180deg,var(--green2),var(--green3));}
  .kpi .n{font-family:var(--serif);font-size:30px;font-weight:600;color:var(--ink);line-height:1;letter-spacing:-.6px;}
  .kpi .l{font-size:10.5px;letter-spacing:.7px;text-transform:uppercase;color:var(--muted);font-weight:700;margin-top:8px;}
  .card{background:var(--card);border:1px solid var(--border);border-radius:var(--r-lg);padding:18px 20px;margin-top:16px;box-shadow:var(--sh-sm);}
  .card h4{font-size:12px;letter-spacing:1.1px;text-transform:uppercase;color:var(--green);font-weight:800;margin-bottom:13px;display:flex;align-items:center;justify-content:space-between;gap:8px;}
  .card h4 .tag{font-size:10px;color:var(--muted);font-weight:700;letter-spacing:.3px;text-transform:none;}
  .grid2c>*{min-width:0;} /* senza questo una tabella larga sfonda la colonna e fa scorrere di lato TUTTA la pagina */
  .grid2c{display:grid;grid-template-columns:1fr 1fr;gap:16px;}
  @media(max-width:1000px){.grid2c{grid-template-columns:1fr;}}
  .stab{width:100%;border-collapse:collapse;font-size:13.5px;}
  .stab th,.stab td{padding:9px 10px;text-align:right;border-bottom:1px solid var(--line);}
  .stab th:first-child,.stab td.tz{text-align:left;font-weight:700;}
  .stab thead th{font-size:10.5px;letter-spacing:.3px;text-transform:uppercase;color:var(--muted);font-weight:700;}
  .stab td.tot{font-weight:800;color:var(--green2);}
  .stab tfoot td{font-weight:800;border-top:2px solid var(--green2);border-bottom:none;}
  .stab tr:last-child td{border-bottom:none;}
  .stab tr.clk{cursor:pointer;}
  .stab tr.clk:hover{background:#f8fafc;}
  .up{color:var(--green);font-weight:800;}.down{color:#c0492b;font-weight:800;}.flat{color:var(--muted);font-weight:700;}
  .barlist{display:flex;flex-direction:column;gap:8px;}
  .barrow{display:grid;grid-template-columns:140px 1fr 76px;gap:12px;align-items:center;font-size:13px;}
  .barrow>span:first-child{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:600;}
  .bartrack{background:var(--sidebar);border:1px solid var(--border);border-radius:6px;height:16px;overflow:hidden;}
  .barfill{height:100%;background:linear-gradient(90deg,var(--green3),var(--green2));border-radius:6px;
    /* come le barre delle commesse: si riempie con scaleX (compositor) e non
       con la larghezza (layout a ogni fotogramma). Chi la disegna scrive --p. */
    width:100%;transform-origin:left center;transform:scaleX(var(--p,0));
    transition:transform var(--d-slow) var(--ease-out);}
  .barrow .v{text-align:right;font-weight:800;color:var(--green2);}
  .hint{font-size:12px;color:var(--muted);margin-top:10px;}
  .chip-x{font:inherit;font-weight:800;font-size:16px;line-height:1;border:1px solid var(--line2);background:var(--panel);color:var(--muted);border-radius:999px;width:30px;height:30px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;}
  .chip-x:hover{border-color:#c0492b;color:#c0492b;}
  .rreal-missing{color:#c0492b;font-weight:800;}
  /* Dialoghi: il wrapper resta in DOM (pointer-events lo disattiva da chiuso,
     come il pannello scheda) così entra E esce in dissolvenza via CSS, senza
     ritocchi JS. pointer-events:none garantisce il click-through su mobile
     (visibility:hidden + backdrop-filter veniva intercettato da iOS Safari). */
  .dlgwrap{position:fixed;inset:0;z-index:80;display:flex;align-items:center;justify-content:center;background:rgba(15,25,18,.5);padding:20px;opacity:0;pointer-events:none;transition:opacity var(--d) var(--ease);}
  #lavedit-wrap{z-index:90;}
  /* L'editor commessa apre altri editor (cliente/articolo/colore) e il popup di
     conferma: sta SOTTO di loro così non li copre. La conferma/avviso (#dlgwrap)
     è sempre in cima a tutto.
     ⚠️ Stessa ragione per il carico fattura, che apre l'editor Tipo di crust
     («＋ Crea un tipo nuovo»): a parità di z-index vince chi sta DOPO nel DOM, e
     #fattura-edit-wrap ci sta — quindi il dialogo del crust nuovo si apriva
     DIETRO quello da cui era stato chiesto. */
  #ordedit-wrap,#fattura-edit-wrap{z-index:74;}
  #dlgwrap{z-index:100050;}
  .dlgwrap.open{opacity:1;pointer-events:auto;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);}
  .dlgbox{background:#fff;border-radius:var(--r-lg);max-width:640px;width:100%;min-height:auto;max-height:calc(100vh - 40px);display:flex;flex-direction:column;box-shadow:var(--sh-lg);overflow:hidden;transform:translateY(14px) scale(.97);opacity:0;transition:transform var(--d) var(--ease-out),opacity var(--d-fast) var(--ease);}
  .dlgwrap.open .dlgbox{transform:none;opacity:1;}
  @keyframes dlgpop{from{transform:translateY(10px) scale(.98);opacity:0}to{transform:none;opacity:1}}
  .dlghead{padding:18px 22px 0;position:relative;}
  .dlghead h3{font-weight:800;font-size:18px;color:var(--green);letter-spacing:-.2px;}
  .dlg-x{position:absolute;top:12px;right:14px;width:34px;height:34px;border:none;background:var(--sidebar,#f1f5f9);color:var(--muted);border-radius:9px;font-size:17px;font-weight:700;cursor:pointer;display:flex;align-items:center;justify-content:center;line-height:1;}
  .dlg-x:hover{background:var(--line2,#cdd7e1);color:var(--text,#0f2438);}
  .dlgbody{padding:9px 22px 18px;color:var(--ink);white-space:pre-line;line-height:1.55;font-size:14.5px;overflow-y:auto;flex:1 1 auto;}
  .dlghead{flex:0 0 auto;}
  /* ── Anteprima cliente (dialog) ── */
  .cliprev{white-space:normal;}
  .cliprev-anag{display:flex;flex-wrap:wrap;gap:8px 22px;margin:2px 0 12px;}
  .cliprev-af{display:flex;flex-direction:column;gap:1px;}
  .cliprev-af .rk,.cliprev-rif .rk,.cliprev-note .rk{font-size:11px;text-transform:uppercase;letter-spacing:.03em;color:var(--muted);font-weight:700;}
  .cliprev-af .rv{font-weight:600;}
  .cliprev-note{margin:0 0 12px;font-size:13.5px;color:var(--ink);}
  .cliprev-rif{margin:0 0 4px;font-size:13.5px;}
  .cliprev-kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;margin:4px 0 14px;}
  .cliprev-kpi{background:var(--panel,#fff);border:1px solid var(--border);border-radius:12px;padding:10px 6px;text-align:center;}
  .cliprev-kpi .n{font-size:19px;font-weight:800;color:var(--green);line-height:1.1;}
  .cliprev-kpi .l{font-size:11px;color:var(--muted);margin-top:3px;font-weight:600;}
  .cliprev-sec{font-weight:800;margin:14px 0 8px;padding-top:12px;border-top:2px solid var(--border);}
  .cliprev-listino{width:100%;}
  @media (max-width:640px){ .cliprev-kpis{grid-template-columns:repeat(2,1fr);} }
  .dlgfoot{flex:0 0 auto;}
  .dlgfoot{padding:0 22px 18px;display:flex;gap:9px;justify-content:flex-end;}
  /* Terza via di un dialogo a tre uscite (es. «Aggiungi comunque» quando una
     voce di commessa è già presa): staccata dalle due normali e in piccolo, si
     trova solo se la si cerca. */
  .dlgfoot .btn.alt{margin-right:auto;}
  [hidden]{display:none !important;}

  /* preview modal */
  .pvwrap{position:fixed;inset:0;z-index:60;display:flex;align-items:center;justify-content:center;background:rgba(15,25,18,.5);padding:20px;opacity:0;pointer-events:none;transition:opacity var(--d) var(--ease);}
  .pvwrap.open{opacity:1;pointer-events:auto;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);}
  /* Anatomia a tre zone (Opzione A): testata fissa, .pvmain = corpo scrollabile
     (.pvscroll) + rail comandi fisso a destra; su mobile il rail sparisce e
     compare la barra .pvbar in basso (vedi @media 640). Il contenitore che
     scorre è .pvscroll — _openPreviewShell (app.4) ne azzera lo scrollTop. */
  .pvbox{background:var(--panel);border-radius:var(--r-lg);max-width:920px;width:100%;max-height:90vh;overflow:hidden;position:relative;display:flex;flex-direction:column;box-shadow:var(--sh-lg);transform:translateY(14px) scale(.97);opacity:0;transition:transform var(--d) var(--ease-out),opacity var(--d-fast) var(--ease);}
  .pvwrap.open .pvbox{transform:none;opacity:1;}
  /* L'anteprima COMMESSA è più larga delle altre: la sua tabella ha otto
     colonne e a 920px ne restavano 666, dove il colore scendeva a 68px e il
     prezzo veniva tagliato (v19.101). La classe la mette `openOrdPreview` e la
     toglie `_openPreviewShell` a ogni apertura.
     ⚠️ Dentro una media query da 1180px in su, non nuda: sotto quella soglia la
     tabella è impilata e non serve un pixel in più — e soprattutto la regola
     mobile `.pvbox{max-width:100%}` ha specificità MINORE di `.pvbox.pvwide`,
     quindi una versione nuda si porterebbe i 1180px anche sul telefono. */
  @media(min-width:1180px){ .pvbox.pvwide{max-width:1180px;} }
  .pvhead{display:flex;align-items:center;justify-content:space-between;padding:11px 14px 11px 20px;border-bottom:1px solid var(--border);background:var(--panel);flex:0 0 auto;}
  .pvhead h3{font-weight:800;font-size:17px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
  /* «← Indietro» della navigazione fra anteprime (catena rasatura→tintura→
     rifinizioni): compare solo quando si è arrivati qui da un'altra anteprima */
  .pvhl{display:flex;align-items:center;gap:10px;min-width:0;}
  .pvhr{display:flex;align-items:center;gap:8px;flex:0 0 auto;}
  .pvback{display:inline-flex;align-items:center;gap:5px;flex:0 0 auto;max-width:190px;padding:6px 11px;border:1px solid var(--line2);border-radius:999px;background:var(--panel);color:var(--green);font:inherit;font-weight:800;font-size:12.5px;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
  @media(hover:hover){ .pvback:hover{background:var(--soft);} }
  .pvback[hidden]{display:none;}
  .pvmain{display:flex;flex:1;min-height:0;}
  .pvscroll{flex:1;min-width:0;overflow-y:auto;}
  .pvbody{padding:18px;}
  /* rail comandi (desktop/tablet): gruppi in ordine fisso Stato → Documenti →
     Azioni → (spazio) → distruttive in fondo, tutti sempre visibili */
  .pvrail{flex:0 0 188px;width:188px;border-left:1px solid var(--border);background:var(--sidebar);padding:12px 12px 14px;display:flex;flex-direction:column;gap:6px;overflow-y:auto;}
  .pvrail-l{font-size:9.5px;letter-spacing:1px;text-transform:uppercase;color:var(--muted2);font-weight:800;margin:9px 2px 1px;}
  .pvrail-l:first-child{margin-top:0;}
  .pvrail .btn{width:100%;justify-content:flex-start;padding:9px 11px;font-size:13px;}
  .pvrail-spacer{flex:1 0 12px;}
  .pvrail-lock{font-size:11.5px;font-weight:700;color:var(--muted);background:var(--panel);border:1px dashed var(--line2);border-radius:8px;padding:8px 10px;line-height:1.4;}
  .pvact-ic{flex:0 0 18px;width:18px;text-align:center;}
  /* pill di stato nel rail: verticali, larghezza piena (stessa grafica .pvst-btn) */
  .pvrail .pvstate{margin:0;flex-direction:column;align-items:stretch;gap:6px;}
  .pvrail .pvstate-l{display:none;}
  .pvrail .pvstate-btns{flex-direction:column;}
  .pvrail .pvst-btn{text-align:left;padding:8px 13px;}
  /* azioni distruttive: rosse ma in variante outline (isolate, non urlate) */
  .pvrail .btn.danger,.pvsheet-sec .btn.danger{background:transparent;color:var(--red);border:1px solid rgba(192,57,43,.4);box-shadow:none;}
  .pvrail .btn.danger:hover,.pvsheet-sec .btn.danger:hover{background:rgba(192,57,43,.09);}
  /* barra azioni mobile (nascosta su desktop) + fogli Stato/Altro dal basso */
  .pvbar{display:none;}
  .pvsheet-dim{position:absolute;inset:0;z-index:5;background:rgba(15,25,18,.35);opacity:0;pointer-events:none;transition:opacity var(--d-fast) var(--ease);}
  .pvsheet-dim.open{opacity:1;pointer-events:auto;}
  .pvsheet{position:absolute;left:0;right:0;bottom:0;z-index:6;background:var(--panel);border-radius:16px 16px 0 0;box-shadow:0 -12px 34px rgba(0,0,0,.28);padding:8px 14px calc(12px + env(safe-area-inset-bottom));transform:translateY(105%);transition:transform var(--d) var(--ease-out);max-height:72%;overflow-y:auto;pointer-events:none;}
  .pvsheet.open{transform:none;pointer-events:auto;}
  .pvsheet-grab{width:38px;height:4px;border-radius:99px;background:var(--line2);margin:4px auto 8px;}
  .pvsheet-sec{display:none;flex-direction:column;gap:7px;}
  .pvsheet-sec.show{display:flex;}
  .pvsheet-sec .btn{width:100%;justify-content:flex-start;}
  .pvsheet-sec .pvstate{margin:0;flex-direction:column;align-items:stretch;gap:8px;}
  .pvsheet-sec .pvstate-btns{flex-direction:column;gap:8px;}
  .pvsheet-sec .pvst-btn{font-size:14px;padding:11px 15px;text-align:left;}
  .pvsheet-div{border-top:1px solid var(--line);margin:6px 0;}
  .pvgrid{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:14px;}
  .pvf{background:var(--sidebar);border:1px solid var(--border);border-radius:var(--r-sm);padding:10px 13px;}
  .pvf .l{font-size:10px;letter-spacing:.8px;text-transform:uppercase;color:var(--muted);font-weight:700;}
  .pvf .v{font-size:16px;font-weight:800;margin-top:3px;color:var(--ink);}
  /* ⚠️ LE CELLE DEL DETTAGLIO SONO CARD ANCHE SUL COMPUTER (v19.93) — il
     gemello desktop della regola `.pvgrid .pvf` del blocco ≤640px, che qui era
     rimasta: là raggio 16 su fondo bianco senza bordo, qui raggio 9 su fondo
     grigio col bordo. Stessa cella, due grammatiche.
     ⚠️ Scoped a `.pvgrid` e non alla `.pvf` nuda, come fa il gemello: la
     regola base serve anche fuori dalla griglia, e cambiarla lì cambierebbe
     pure il telefono, dove le due sono già a posto.
     ⚠️ La tipografia NON si allinea: il valore resta 16px (là 15,5) perché
     tutta la riga del computer è più grande. A divergere era la FORMA. */
  @media(min-width:641px){
    .pvgrid .pvf{background:var(--panel);border:0;border-radius:16px;
      box-shadow:0 1px 2px rgba(15,36,56,.05),0 4px 12px -8px rgba(15,36,56,.2);}
    html[data-theme="dark"] .pvgrid .pvf{box-shadow:0 1px 2px rgba(0,0,0,.45);}
  }
  .pvhero{background:linear-gradient(135deg,var(--side-bg1),var(--side-bg2));color:#fff;border-radius:var(--r);padding:14px 18px;margin-bottom:14px;box-shadow:var(--sh-sm);}
  .pvhero .l{font-size:11px;letter-spacing:2px;text-transform:uppercase;color:var(--side-tx3);font-weight:800;}
  .pvhero .v{font-size:26px;font-weight:800;margin-top:4px;}
  .pvlav{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px;}
  .pvlav span{background:var(--soft);border:1px solid var(--soft-bd);color:var(--green);border-radius:8px;padding:6px 11px;font-size:13px;font-weight:700;}
  .pvf.full{grid-column:1/-1;}
  .pvf .v .pvsub{font-size:12px;color:var(--muted);font-weight:600;}
  /* Colore crust dell'anteprima: sotto il colore attuale, il grezzo di partenza.
     Dentro `.pvcrust` il fondo è ambra, dove `--muted` scende sotto AA (stessa
     trappola già pagata da `.pvcrust-h`): il testo prende `--amber-tx`. */
  .pvcrust .pvf2 .v .pvsub{display:block;font-size:11.5px;color:var(--amber-tx);font-weight:700;margin-top:1px;}
  /* Switch rapido di stato nelle anteprime (rifinizioni/tinture/rasature) */
  .pvstate{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:-4px 0 14px;}
  .pvstate-l{font-size:10px;letter-spacing:.8px;text-transform:uppercase;color:var(--muted);font-weight:800;}
  .pvstate-btns{display:flex;gap:6px;flex-wrap:wrap;}
  .pvst-btn{background:var(--soft);color:var(--green);border:1px solid var(--soft-bd);border-radius:999px;font-weight:800;font-size:12px;padding:6px 13px;cursor:pointer;transition:background var(--d-fast),border-color var(--d-fast),color var(--d-fast);}
  .pvst-btn:hover:not(:disabled){background:var(--soft);border-color:var(--green2);}
  .pvst-btn.on{background:var(--green2);color:#fff;border-color:var(--green2);cursor:default;}
  /* ── Data consegna modificabile: è una CELLA della griglia ─────────────────
     Vedi `dataConsegnaCell` (app.4) per il perché. In breve: era una riga a sé
     (`.pvdc`) con l'etichetta A FIANCO, senza contenitore, campo a raggio 8 —
     le celle sono a 16 e nella scala del guscio l'8 non c'è — valore a 13px
     contro i 15,5 di ogni altro dato e la pastiglia delle pastiglie di STATO
     come pulsante. Ora è la cella che l'app usa già ovunque, e il campo È il
     valore: stessa tipografia degli altri, il tratteggio e l'icona calendario
     dicono che si tocca.
     ⚠️ La larghezza sta sul `.datefield-wrap`, non sull'input: il wrapper lo
     crea `attachCalendar` ed è `inline-block`, quindi con la larghezza
     sull'input (che è `width:100%` per la regola generale dei campi data) il
     wrapper collasserebbe a zero.
     ⚠️ `flex-wrap` e `.date-err-msg{flex:1 0 100%}` non sono di comodo:
     `attachCalendar` inserisce il messaggio d'errore come FRATELLO del
     wrapper, cioè dentro questa `.v` — senza, «Data non valida» finirebbe in
     riga accanto al campo, e con il Salva a vista sarebbe fuori dalla cella. */
  .pvf.dcell .v{display:flex;align-items:center;gap:9px;flex-wrap:wrap;}
  .pvf.dcell .datefield-wrap{width:128px;flex:0 0 auto;}
  /* ⚠️ `font:inherit` e BASTA: la tipografia la prende dal `.v` che lo ospita,
     che è 16px sul computer e 15,5 su telefono (`.pvgrid .pvf .v` nel blocco
     ≤640px). Fissandola a un numero, il campo restava più piccolo degli altri
     valori proprio sul computer — cioè l'opposto di ciò che serve qui. */
  .pvf.dcell .dc-in{font:inherit;color:var(--ink);
    background:transparent;border:0;border-bottom:1.5px dashed var(--line2);border-radius:0;
    padding:0 24px 2px 0;font-variant-numeric:tabular-nums;}
  .pvf.dcell .dc-in:focus{outline:none;border-bottom-color:var(--green);border-bottom-style:solid;}
  .pvf.dcell .dc-in.date-err{border-bottom-color:var(--st-late);border-bottom-style:solid;}
  /* ⚠️ L'icona qui NON è decorazione: insieme al tratteggio è l'unica cosa che
     dice che il dato si tocca. Il `--muted2` di `.cal-ico` sta a 2,34:1 — in
     un campo bordato è un dettaglio, in una cella che sembra di sola lettura è
     il segnale stesso a sparire. Verde: è il colore dell'azione, 12,7:1 in
     chiaro e 11,4 in scuro. */
  .pvf.dcell .cal-ico{right:1px;color:var(--green);}
  .pvf.dcell .dc-save{margin-left:auto;font-size:12.5px;padding:7px 13px;border-radius:8px;}
  .pvf.dcell .date-err-msg{flex:1 0 100%;margin-top:1px;}
  /* Ripartizione su più clienti (anteprima scheda): chi prende cosa e quanto rende */
  .pvrip{border:1px solid var(--border);border-radius:10px;padding:12px 14px;margin-bottom:14px;background:var(--card);}
  .pvrip-h{font-size:10px;letter-spacing:1px;text-transform:uppercase;color:var(--green);font-weight:800;margin-bottom:9px;display:flex;align-items:center;gap:7px;flex-wrap:wrap;}
  .pvrip-h .tag{font-size:9px;letter-spacing:.5px;font-weight:800;color:var(--muted);background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:1px 6px;text-transform:none;}
  .rip-bar{display:flex;height:9px;border-radius:5px;overflow:hidden;background:var(--bg);margin-bottom:10px;}
  .rip-bar i{display:block;height:100%;}
  .rip-scroll{overflow-x:auto;}
  .rip-tab{width:100%;border-collapse:collapse;font-size:13.5px;}
  .rip-tab th{font-size:9.5px;letter-spacing:.6px;text-transform:uppercase;color:var(--muted);font-weight:800;text-align:left;padding:0 8px 6px;border-bottom:1px solid var(--line);}
  .rip-tab td{padding:7px 8px;border-bottom:1px solid var(--line);vertical-align:top;}
  .rip-tab tr:last-child td{border-bottom:0;}
  .rip-tab .n{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap;}
  .rip-tab td:first-child{font-weight:700;}
  .rip-tot td{font-weight:800;border-top:1px solid var(--line2);background:var(--bg);}
  .rip-dot{display:inline-block;width:9px;height:9px;border-radius:3px;margin-right:7px;}
  .rip-sub{font-size:11.5px;font-weight:600;color:var(--muted);margin-top:2px;}
  .rip-mut{color:var(--muted);font-weight:600;}
  .rip-pos{color:var(--green);} .rip-neg{color:#c0492b;}
  .rip-note{font-size:11.5px;color:var(--muted);margin-top:9px;}
  .pvcrust{background:var(--amber-bg);border:1px solid var(--amber-bd);border-radius:10px;padding:12px 14px;margin-bottom:14px;}
  /* ⚠️ --amber-tx, NON --tan: --tan non è ridefinito nel blocco tema scuro e su
     --amber-bg (che in scuro diventa #37300f) dava marrone su marrone, 2,94:1 —
     trovato campionando .pvcrust-h in dark-theme.test.js. Vale per TUTTI i box
     dell'anteprima che riusano questa intestazione (crust, integrazioni, riprese). */
  .pvcrust-h{font-size:10px;letter-spacing:1px;text-transform:uppercase;color:var(--amber-tx);font-weight:800;margin-bottom:9px;display:flex;align-items:center;gap:7px;}
  .pvcrust-h .tag{font-size:9px;letter-spacing:.5px;font-weight:800;color:var(--amber-tx);background:#fff;border:1px solid var(--amber-bd);border-radius:6px;padding:1px 6px;text-transform:none;}
  .pvcrust-grid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:9px;}
  .pvf2 .l{font-size:9.5px;letter-spacing:.6px;text-transform:uppercase;color:var(--amber-tx);font-weight:700;}
  .pvf2 .v{font-size:15px;font-weight:800;margin-top:2px;color:var(--ink);word-break:break-word;}
  .pvcrust-note{margin-top:10px;border-top:1px solid var(--amber-bd);padding-top:9px;}
  .pvcrust-note .l{font-size:9.5px;letter-spacing:.6px;text-transform:uppercase;color:var(--amber-tx);font-weight:700;}
  .pvcrust-note .v{font-size:14px;font-weight:600;margin-top:3px;color:var(--ink);white-space:pre-wrap;}
  /* Mix avanzato dentro il riquadro Crust dell'anteprima: barra proporzionale +
     una riga per variante. I segmenti sono gradazioni dello STESSO ambra del
     riquadro (i colori li passa MIX_COL in app.4): la barra multicolore è di
     «Ripartizione clienti» e le due ripartizioni non devono somigliarsi. */
  .pvmix .mixbar{display:flex;height:9px;border-radius:5px;overflow:hidden;background:var(--panel);
    border:1px solid var(--amber-bd);margin:7px 0 10px;}
  .pvmix .mixbar i{display:block;height:100%;}
  .pvmix .mixrows{display:flex;flex-direction:column;gap:7px;}
  .pvmix .mixrow{display:grid;grid-template-columns:12px 1fr auto;gap:9px;align-items:center;font-size:13.5px;}
  .pvmix .mixdot{width:9px;height:9px;border-radius:3px;}
  .pvmix .mixnome{font-weight:700;color:var(--ink);min-width:0;}
  .pvmix .mixnome .sp{font-weight:600;color:var(--amber-tx);}
  .pvmix .mixsub{display:block;font-size:11.5px;font-weight:600;color:var(--amber-tx);margin-top:1px;}
  /* Prezzo per variante: quello di listino del Tipo si legge come gli altri dati,
     quello scritto a mano sulla riga porta il ✎ ed è più marcato — sono due cose
     diverse e a colpo d'occhio devono distinguersi. */
  /* Switch €/m² ↔ €/piede²: il crust si compra a piede quadro, quindi la fascia
     si legge nell'unità della fattura. Sta nell'intestazione del blocco, che
     diventa una riga a due estremi. */
  .pvmix .mixhead{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;}
  .mixunit-seg{display:inline-flex;background:var(--panel);border:1px solid var(--amber-bd);border-radius:8px;padding:2px;gap:2px;flex:0 0 auto;}
  .mixunit-seg button{border:none;background:none;font:inherit;font-size:11.5px;font-weight:700;color:var(--amber-tx);
    padding:3px 10px;border-radius:6px;cursor:pointer;white-space:nowrap;text-transform:none;letter-spacing:0;}
  .mixunit-seg button.on{background:var(--green);color:#fff;}
  @media(hover:hover){ .mixunit-seg button:not(.on):hover{background:var(--amber-bg);} }
  .pvmix .mixprice{font-variant-numeric:tabular-nums;}
  .pvmix .mixprice.man{font-weight:800;color:var(--ink);}
  .pvmix .mixprice.none{font-style:italic;opacity:.85;}
  .pvmix .mixtotr{font-variant-numeric:tabular-nums;font-weight:700;color:var(--ink);}
  .pvmix .mixpct{font-weight:800;font-variant-numeric:tabular-nums;color:var(--ink);white-space:nowrap;}
  .pvmix .mixtot{margin-top:9px;padding-top:8px;border-top:1px dashed var(--amber-bd);font-size:12px;font-weight:700;
    color:var(--amber-tx);display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap;}
  /* chip «rasato» accanto allo spessore: lo spessore è quello ottenuto dalla
     rasatura collegata, non del grezzo comprato */
  .pvcrust .pvras{font-size:10px;letter-spacing:.4px;font-weight:800;color:var(--green);background:var(--soft);
    border:1px solid var(--soft-bd);border-radius:6px;padding:1px 6px;white-space:nowrap;vertical-align:middle;}
  /* Integrazioni ordine (anteprima scheda): storico + azioni per voce */
  .pvint-row{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;padding:7px 0;border-bottom:1px dashed var(--amber-bd);}
  .pvint-row:last-of-type{border-bottom:0;}
  .pvint-main{font-size:13.5px;color:var(--ink);}
  .pvint-q{font-weight:800;}
  .pvint-note{font-size:12px;color:var(--muted);margin-top:2px;}
  .pvint-int{color:var(--amber-ink,#8a6516);font-style:italic;}
  .pvint-ord{color:#8a6516;font-weight:700;}
  .pvint-motivo{display:inline-block;font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.4px;color:#8a6516;background:var(--amber-bg,#fdf6e6);border:1px solid var(--amber-bd,#e6cf9a);border-radius:999px;padding:0 8px;}
  .pvint-acts{display:flex;gap:6px;flex:0 0 auto;}
  .pvint-acts .btn{padding:4px 10px;font-size:13px;}
  .pvint-empty{font-size:12.5px;color:var(--muted);}
  /* Riprese parziali (rientri dal terzista): stesso impianto del box
     integrazioni ma in verde — l'ambra resta all'ampliamento d'ordine, così a
     colpo d'occhio non si confondono due registri diversi. */
  /* Blocco commessa nell'anteprima scheda: tutto il riquadro apre l'anteprima
     della commessa (il «←» dell'intestazione riporta alla scheda). */
  .pvord{cursor:pointer;}
  .pvord-go{margin-left:auto;font-size:10px;font-weight:800;color:var(--green);letter-spacing:.5px;}
  @media(hover:hover){ .pvord:hover{border-color:var(--green3);} }
  /* Metri della commessa: stessa griglia delle celle del riquadro Crust, con un
     filetto sopra che la stacca dalla riga del cliente. ⚠️ Il totale usa
     --green e NON --green2: quest'ultimo come TESTO su un fondo tenue resta
     sotto AA (3,5:1 su --amber-bg) — è la trappola già pagata dal Programma e
     dal calcolatore prezzi. Su ≤540px le tre celle si impilano: a schermo
     stretto stavano a 60px l'una e i numeri andavano a capo dentro la cella. */
  .ordmqgrid{margin-top:10px;padding-top:9px;border-top:1px solid var(--amber-bd);}
  .ordmqgrid .pvf2 .v{white-space:nowrap;}
  .ordmqgrid .pvf2.tot .v{color:var(--green);}
  .ordmq-fuori{margin-top:8px;font-size:12px;font-weight:600;color:var(--amber-tx);line-height:1.4;}
  /* Variante compatta: quando la barra doppia racconta già consegnato e da
     ricevere, delle celle resterebbe solo «Ordinati» — una cella sola in una
     griglia da tre lascerebbe due buchi, quindi diventa una riga di testo. */
  .ordmq-line{margin-top:10px;padding-top:9px;border-top:1px solid var(--amber-bd);font-size:12.5px;font-weight:700;color:var(--amber-tx);}
  .ordmq-line b{color:var(--ink);font-size:13.5px;}
  /* ⚠️ `.ordmeta` è --muted, che sul fondo ambra del riquadro commessa sta a
     3,35:1 (la stessa trappola di .pvcrust-h, mai misurata qui finché non c'è
     stato un campione): dentro .pvord passa ad --amber-tx. */
  .pvord .ordmeta{color:var(--amber-tx);}
  .pvord .ordmeta-sub{font-size:11.5px;}
  /* Barra dei metri della voce: due segmenti sovrapposti — chiaro = rientrato
     dal terzista, pieno = consegnato al cliente (il consegnato è un
     sottoinsieme del rientrato, quindi si disegna SOPRA e parte dallo stesso
     bordo). Sono colori scritti a mano perché devono restare distinguibili dal
     binario --line2 in entrambi i temi. */
  .ordbar.duobar{position:relative;}
  .ordbar.duobar i{position:absolute;left:0;top:0;height:100%;border-radius:999px;}
  .ordbar.duobar .rip{background:#86C195;}
  .ordbar.duobar .cons{background:var(--green2);}
  html[data-theme="dark"] .ordbar.duobar .rip{background:#2F6A3E;}
  html[data-theme="dark"] .ordbar.duobar .cons{background:var(--green2);}
  .barleg{gap:6px 14px;}
  .barleg .k{display:inline-flex;align-items:center;gap:5px;font-weight:700;}
  .barleg .sw{width:11px;height:11px;border-radius:3px;display:inline-block;flex:0 0 auto;}
  .barleg .sw.rip{background:#86C195;}
  .barleg .sw.cons{background:var(--green2);}
  html[data-theme="dark"] .barleg .sw.rip{background:#2F6A3E;}
  html[data-theme="dark"] .barleg .sw.cons{background:var(--green2);}
  @media(max-width:540px){ .ordmqgrid{grid-template-columns:1fr 1fr;} }
  .pvripr{background:var(--soft);border-color:var(--soft-bd);}
  .pvripr .pvcrust-h{color:var(--green);}
  .pvripr .pvcrust-h .tag{color:var(--green2);border-color:var(--soft-bd);}
  .rpr-sum{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;background:var(--panel);border:1px solid var(--soft-bd);border-radius:9px;padding:8px 11px;font-size:13px;font-weight:700;}
  .rpr-sum b{color:var(--green2);}
  .rpr-manca{color:var(--st-lav-tx);background:var(--st-lav-bg);border:1px solid var(--amber-bd);border-radius:999px;padding:2px 9px;font-size:12px;}
  .rpr-ok{color:var(--st-cons-tx);background:var(--st-cons-bg);border:1px solid var(--soft-bd);border-radius:999px;padding:2px 9px;font-size:12px;}
  .rpr-bar{height:9px;border-radius:999px;background:var(--panel);border:1px solid var(--soft-bd);overflow:hidden;display:flex;margin:9px 0 4px;}
  .rpr-bar i{display:block;height:100%;}
  /* ⚠️ CON UN VERDE SOLO, IL VERDE NON BASTA PIÙ A SEPARARLI.
     I tre segmenti sono un rientro per volta e la loro divisione è il dato:
     quanti carichi sono tornati, e di che misura. Con --green2 e --green3 due
     segmenti adiacenti stanno a 1,54:1 in chiaro e 1,09 in scuro, sotto il 3:1
     che una forma informativa richiede — ma erano già indistinguibili PRIMA
     (1,35 e 1,14), quindi non è una regressione: è un difetto che la palette
     rende impossibile da ignorare. La separazione passa da un SECONDO CANALE,
     un filetto del colore del binario fra l'uno e l'altro.
     ⚠️ È un `box-shadow:inset`, non un `border-left`: con `box-sizing:border-box`
     un bordo rientrerebbe nella width in %, e su un rientro piccolo si
     mangerebbe il segmento che deve separare. */
  .rpr-bar i.s1{background:var(--green2);} .rpr-bar i.s2{background:var(--green3);} .rpr-bar i.s3{background:var(--green2);}
  .rpr-bar i + i{box-shadow:inset 2px 0 0 var(--panel);}
  /* Riga «metri reali ↔ somma dei rientri»: nota quando sono allineati, pulsante
     quando l'allineamento automatico non è (ancora) stato applicato. */
  .rpr-real{margin:8px 0 2px;font-size:12.5px;color:var(--muted);}
  .rpr-real b{color:var(--green2);}
  .rpr-real .btn{padding:5px 11px;font-size:12.5px;}
  .rpr-row{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;padding:7px 0;border-bottom:1px dashed var(--soft-bd);}
  .rpr-row:last-of-type{border-bottom:0;}
  .rpr-main{font-size:13.5px;color:var(--ink);}
  .rpr-q{font-weight:800;color:var(--green2);}
  .rpr-note{font-size:12px;color:var(--muted);margin-top:2px;}
  .rpr-acts{display:flex;gap:6px;flex:0 0 auto;}
  .rpr-acts .btn{padding:4px 10px;font-size:13px;}
  .rpr-empty{font-size:12.5px;color:var(--muted);}
  .rpr-dlgsum{background:var(--soft);border:1px solid var(--soft-bd);border-radius:9px;padding:8px 11px;font-size:13px;font-weight:700;margin-bottom:12px;}
  .rpr-dlgsum b{color:var(--green2);}
  /* chip nell'elenco schede */
  .pill.rippill{background:var(--soft);color:var(--green2);border:1px solid var(--soft-bd);font-weight:700;}
  .pill.rippill.done{background:var(--st-cons-bg);color:var(--st-cons-tx);border-color:var(--soft-bd);}
  html[data-theme="dark"] .pill.rippill{background:var(--soft);color:var(--green);border-color:var(--soft-bd);}
  html[data-theme="dark"] .pill.rippill.done{background:var(--st-cons-bg);color:var(--green);border-color:var(--soft-bd);}
  html[data-theme="dark"] .rpr-sum{background:var(--card);}
  /* su fondo verde scuro il ghost ereditava un verde troppo simile: illeggibile */
  html[data-theme="dark"] .pvripr .btn.ghost{background:var(--panel);color:var(--ink);border-color:var(--soft-bd);}
  html[data-theme="dark"] .pvripr .btn.ghost.danger{color:var(--red);}
  html[data-theme="dark"] .pvripr .pvcrust-h,
  html[data-theme="dark"] .rpr-sum b,
  html[data-theme="dark"] .rpr-real b,
  html[data-theme="dark"] .rpr-q{color:var(--green);}

  @media(max-width:860px){
    /* hamburger visibile e barra superiore sotto al notch */
    .nav-toggle{display:inline-flex;}
    /* col drawer chiuso il marchio non si vedrebbe mai: qui sta accanto all'hamburger */
    .mk-appbar{display:flex;}
    .appbar{padding-top:calc(13px + env(safe-area-inset-top));}
    /* la sidebar diventa un drawer che scorre da sinistra */
    .sidebar{transform:translateX(-100%);transition:transform var(--d-slow) cubic-bezier(.4,0,.2,1);width:260px;flex:0 0 260px;max-width:86vw;box-shadow:14px 0 40px rgba(0,0,0,.22);
      padding-top:calc(20px + env(safe-area-inset-top));
      padding-left:calc(14px + env(safe-area-inset-left));
      padding-bottom:calc(20px + env(safe-area-inset-bottom));}
    .app.nav-open .sidebar{transform:translateX(0);}
    /* il contenuto occupa tutta la larghezza */
    .main{margin-left:0;}
    .content{--ctop:16px;padding-left:13px;padding-right:13px;}
    :root{--content-pad:13px;}
    /* righe e griglie a colonna singola */
    .row{grid-template-columns:1fr;gap:9px;}
    .ractions{justify-content:flex-start;}
    .fgrid,.grid2c{grid-template-columns:1fr;}
    /* Su mobile il pannello torna a tutto schermo.
       ⚠️ L'altezza NON si dichiara: la danno `inset:0` + `height:auto` su un
       elemento `position:fixed`, cioè top e bottom ancorati al viewport. È
       l'unico modo che non dipende da una MISURA — e ogni misura qui si è
       rivelata sbagliata da qualche parte: `100vh` restava corto nella PWA iOS
       installata (barra Salva sotto la fascia dei gesti), e `--app-h`
       (innerHeight + notch) sforava di un notch dove innerHeight era già
       giusto, mandando il piè di pagina con «Salva» fuori dallo schermo.
       Con l'ancoraggio il box è ESATTAMENTE il viewport in entrambi i casi.
       `--app-h` resta a `.app` (il guscio), dove serve a non lasciare la
       fascia grigia in fondo alla PWA installata: quello è un altro problema,
       e va tenuto separato da questo. */
    .panel{inset:0;margin:0;width:100vw;height:auto;max-height:none;max-width:100vw;border-radius:0;}
    .panel.min{width:100%;}
    /* intestazione del pannello a tutto schermo sotto la safe-area (notch/status
       bar iPhone): titolo e pulsanti ⤢/× non devono finire sotto l'orologio.
       Escluso il pannello minimizzato, che è ancorato in basso. */
    .panel:not(.min) .panel-head{padding-top:calc(11px + env(safe-area-inset-top));}
    /* footer del pannello a tutto schermo sopra la safe-area in basso (home
       indicator iPhone): i pulsanti Salva non devono finire sotto la barra. */
    .panel-foot{padding-bottom:calc(10px + env(safe-area-inset-bottom));}
    /* ⚠️ Il ⤢ RESTA anche sul telefono, ed è una decisione presa due volte.
       Era stato nascosto perché lì il pannello è già a tutto schermo e quel
       pulsante non ha nulla da commutare — vero in teoria, ma era anche la via
       d'uscita di chi si trovava il piè di pagina fuori schermo: toglierlo ha
       peggiorato le cose per l'utente prima che l'ancoraggio qui sopra
       risolvesse la causa. Un comando che non serve costa una riga in una
       testata; toglierlo a chi ci si appoggia costa il salvataggio di una
       scheda. Non rimuoverlo di nuovo senza chiedere. */
    /* tabelle statistiche: scorrono in orizzontale invece di essere tagliate */
    .stab{display:block;overflow-x:auto;-webkit-overflow-scrolling:touch;white-space:nowrap;}
    /* quando il drawer è aperto blocca lo scroll di fondo */
    body.nav-locked{overflow:hidden;}
  }

  /* ===== Ottimizzazioni mobile / iPhone ===== */
  @media(max-width:640px){
    /* .rigacom va ripetuta: ha specificità maggiore della classe base e
       altrimenti resterebbe a 6 colonne anche sul telefono. */
    .ordr-add-grid,.ordr-add-grid.rigacom{grid-template-columns:1fr 1fr;}
    /* Le voci della commessa hanno il loro blocco mobile accanto alla regola
       base (v17.7): qui restavano le regole del vecchio layout a due estremità
       (.ordriga-state/-chips/-acts), classi che non esistono più. */
    /* iOS NON deve fare zoom automatico al focus: font >= 16px sugli input */
    .field input,.field select,.field textarea,.search input,select.filter,
    input,select,textarea{font-size:16px;}
    /* ⚠️ La riga sopra ha specificità (0,0,1) e cinque famiglie di campi la
       battono più avanti nel file riabbassando il carattere sotto la soglia:
       toccandoli iPhone ingrandiva la pagina e NON tornava indietro. La
       correzione NON può stare qui — a parità di specificità vince chi è
       scritto dopo — ed è in fondo al file, cerca «soglia iOS dei 16px». */
    /* target touch più comodi */
    .btn{min-height:44px;}
    .btn.sm{min-height:38px;}
    /* safe-area: la sidebar in alto sotto la status bar dell'iPhone */
    .sidebar{padding-top:calc(10px + env(safe-area-inset-top));
      padding-left:calc(14px + env(safe-area-inset-left));
      padding-right:calc(14px + env(safe-area-inset-right));}
    /* il fondo lo calcola la regola base di .content dalle variabili --pb-* */
    /* ── LA BARRA DI PAGINA STA NELLO SCHERMO (v19.77) ────────────────────
       Regola: sotto i 640px la barra ha al massimo QUATTRO comandi —
       ricerca · ⚙ Filtri · UN pulsante verde · ⋯ — e nessuno di loro può
       finire oltre il bordo destro. Chi ne aggiunge uno lo mette nel foglio
       «⋯» (_barraAzioniSetup, app.9g), non in coda alla riga.
       ⚠️ LO SBORDAMENTO SI MISURA SUL BORDO DESTRO DELLA BARRA, NON SUL
       DOCUMENTO: la .toolbar ha overflow-x:auto e scorre da sé, quindi
       document.scrollWidth resta uguale a clientWidth e un controllo che
       guarda lì passa verde col pulsante fuori schermo. È la ragione per cui
       lo stesso difetto è stato segnalato tre volte (Tinture/Rasature,
       Commesse, Gestione lotti) sopravvivendo a ogni misura di barra. */
    /* Va A CAPO invece di scorrere: è la rete di sicurezza. Un comando in più
       di quelli previsti fa crescere la barra — che si VEDE — invece di
       spingerne uno fuori campo, che non si vede. Con quattro comandi al
       massimo il capo scatta solo a 320px. I popup dei filtri sono ancorati
       in position:fixed via JS, quindi l'overflow non li taglia comunque. */
    .toolbar{flex-wrap:wrap;overflow-x:auto;gap:8px;scrollbar-width:none;-webkit-overflow-scrolling:touch;}
    .toolbar::-webkit-scrollbar{display:none;}
    .toolbar>*{flex:0 0 auto;}
    /* ⚠️ LA RICERCA DEVE POTER CEDERE. Era `flex:1 0 165px;min-width:150px`:
       cresceva ma non si stringeva di un pixel, quindi tutto ciò che le stava
       accanto veniva spinto FUORI invece di stringersi con lei — la prima
       delle due cause strutturali del difetto.
       ⚠️ Il `min-width:0` sull'input è load-bearing: `.search input` ha
       `flex:1;min-width:120px`, e senza azzerarlo il testo del campo esce dal
       riquadro e finisce SOPRA il pulsante «Filtri» appena la barra stringe. */
    .toolbar .search{flex:1 1 96px;min-width:96px;}
    .toolbar .search input{min-width:0;}
    /* «Azzera filtri» → sola icona ⨉ su mobile per non rubare spazio */
    .btn.reset .rlbl{display:none;}
    /* La coda del nome sul pulsante verde di pagina: «＋ Nuovo cliente» resta
       intero sul computer, su telefono è «＋ Nuovo» col nome nel suggerimento
       (stesso patto di .rlbl e di #new-btn .np-lbl). */
    .btn .albl{display:none;}
    /* Con la coda spenta il pulsante non ha più bisogno di tutta la sua aria:
       nella barra stretta questi 4px per lato sono un carattere in più di
       ricerca. Vale solo per il verde di pagina, non per i .btn in generale. */
    .toolbar>.btn.primary{padding-left:13px;padding-right:13px;}
    /* Il «⋯» delle azioni di pagina: quadratino, come quello delle righe */
    .barra-piu{flex:0 0 auto;min-width:44px;padding:0 12px;font-size:19px;line-height:1;letter-spacing:1px;}
    /* Ciò che è passato nel foglio «⋯». ⚠️ `_rowSheetBtns` riconosce le voci
       proprio perché non sono renderizzate: se un domani questa regola cambia
       in `visibility` o `opacity`, il foglio si svuota in silenzio. */
    .toolbar>.nel-piu{display:none !important;}
    /* pulsanti rapidi (quick filter): una riga scorrevole invece di andare a capo */
    .qbtns{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch;}
    .qbtns::-webkit-scrollbar{display:none;}
    .qbtns>*{flex:0 0 auto;}
    /* spazi più compatti per mostrare prima la lista (aria sulle sezioni) */
    .content{--ctop:12px;}
    .stickyhead{padding-top:8px;padding-bottom:10px;}
    .stickyhead .qbtns{margin-top:8px !important;}
    .card{margin-top:12px;padding:14px 14px;}
    .panel-body{padding:10px 13px 14px;}
    .fgrid{gap:6px 14px;}
    .listhead{padding:12px 14px;}
    /* liste (schede/tinture/rasature/archivio) più dense: meno spazio per
       riga, più righe a vista senza scorrere */
    .row{padding:10px 13px;}
    .rmain{gap:5px;}
    .rart{font-size:14.5px;}
    .rnum{font-size:13px;}
    .rcell{min-width:78px;padding:5px 9px;}
    .rmeta-grid{gap:6px 7px;}
    .rmeta,.rqty,.rcli,.rsub,.rcrust{font-size:12px;}
    /* preferenza "Vista liste" in Impostazioni → Aspetto: un giro di vite
       ulteriore per chi vuole ancora più righe a schermo. Locale al dispositivo. */
    html[data-listdensity="compact"] .row{padding:7px 11px;}
    html[data-listdensity="compact"] .rmain{gap:3px;}
    html[data-listdensity="compact"] .rcell{padding:4px 8px;min-width:68px;}
    html[data-listdensity="compact"] .rmeta-grid{gap:4px 6px;}
    html[data-listdensity="compact"] .card{padding:11px 12px;}
    /* il suggerimento "Tab ⇥ filtro" è inutile su touch e alza la barra ricerca */
    .tabhint{display:none;}
    /* barra superiore più compatta: titolo e chip occupano meno righe */
    .appbar{padding:9px 13px;padding-top:calc(9px + env(safe-area-inset-top));gap:8px;}
    .appbar h1{font-size:18px;}
    .chip{padding:7px 11px;font-size:12px;}
    .chip b{font-size:13px;}
    /* testata schede tutta su una riga: chip compatti (pallino + numero) e
       bottone "Nuova scheda" ridotto a "+" quadrato, per dare spazio alla lista */
    .appbar{flex-wrap:nowrap;justify-content:flex-start;}
    .appbar h1{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
    .appbar-actions{flex-wrap:nowrap;flex:0 0 auto;gap:6px;}
    .chip .chip-lbl{display:none;}
    .chip{padding:7px 10px;gap:5px;}
    .chip .ldot{width:9px;height:9px;opacity:.85;}
    .chip b{font-size:14px;}
    /* la barra di ricerca prende la riga al posto del titolo (lo spazio non c'è
       per entrambi, e il titolo è già dato dalla voce di menu attiva) */
    .appbar h1{display:none;}
    .gsbar{height:44px;}
    .gsbar .gsb-kbd{display:none;}
    #new-btn .np-lbl{display:none;}
    #new-btn{padding:0;width:44px;min-width:44px;height:44px;min-height:44px;display:inline-flex;align-items:center;justify-content:center;border-radius:11px;}
    #new-btn .np-plus{font-size:26px;font-weight:700;line-height:1;}
    /* griglia colori crust a colonna singola */
    .colc-grid{grid-template-columns:1fr;}
    .colc-card-acts .btn{flex:1 1 auto;}
    /* azioni delle righe: bottoni compatti, a larghezza naturale, che vanno
       a capo quando serve — tutti visibili, niente scorrimento orizzontale */
    .ractions{flex-wrap:wrap;gap:6px;}
    .ractions>*{flex:0 0 auto;}
    /* ECCEZIONE alla riga sopra per il blocco a colonna (.rcol): lì i figli non
       sono i pulsanti ma le due FASCE (.rstate-row badge / .ract-line pulsanti).
       Con flex:0 0 auto la fascia dei pulsanti resta larga quanto il contenuto
       (max-content) e NON va a capo: da 4 pulsanti in su sbordava lo schermo e
       la pagina scorreva di lato (il "galleggiamento" dello scroll). A tutta
       larghezza e con min-width:0 i pulsanti vanno a capo dentro la riga. */
    .ractions.rcol>.rstate-row,.ractions.rcol>.ract-line{flex:0 1 auto;width:100%;min-width:0;}
    /* editor rapido dei metri reali: prende la riga intera, i campi vanno a capo */
    .ract-line>.qreal{flex:1 1 100%;min-width:0;}
    .ractions .btn.sm{padding:7px 11px;}
    /* dialoghi a tutto schermo con safe-area */
    .dlgwrap{padding:0;align-items:stretch;}
    .dlgbox{max-width:100%;width:100%;max-height:100%;height:100%;border-radius:0;
      padding-top:env(safe-area-inset-top);padding-bottom:env(safe-area-inset-bottom);}
    /* modale etichetta: padding ridotto e safe-area in basso */
    #label-modal{padding:12px 10px calc(14px + env(safe-area-inset-bottom)) !important;}
    /* overlay schede-crust e movimentazioni lotto a tutto schermo */
    /* Classe ripetuta per alzare la specificità: la regola BASE di .csm-box sta
       più avanti nel file (~riga 1863) e, a parità di specificità, vinceva lei —
       annullando il tutto-schermo su mobile anche dentro questa media query. */
    .csm-box.csm-box{margin:0;border-radius:0;max-height:100vh;height:100vh;}
    /* testata movimentazioni: solo icone (etichetta/modifica) per stare su una riga */
    .lmov-act-lbl{display:none;}
    .lmov-acts .btn{padding:8px 11px;}
    /* safe-area sopra (notch/status bar iPhone) e sotto: l'intestazione non
       deve finire sotto l'orologio/batteria. */
    .csm-box{padding-top:env(safe-area-inset-top);padding-bottom:env(safe-area-inset-bottom);}
    /* anteprima a TUTTO SCHERMO con barra azioni in basso (zona del pollice,
       come le app iOS): il rail desktop sparisce, compaiono .pvbar + i fogli
       Stato/Altro. Testata sotto la status bar, barra sopra la home-bar. */
    .pvwrap{padding:0;}
    .pvbox{max-width:100%;width:100%;height:100%;max-height:100%;border-radius:0;}
    /* ⚠️ ENTRA ED ESCE DAL BASSO (v20). Il dettaglio ha la maniglia e si butta
       giù col dito, ma quando lo lasciavi RISALIVA mentre sfumava: senza
       `.open` il box tornava al suo stato di riposo, che è quello del
       computer (`translateY(14px) scale(.97)`, cioè una finestra che si
       gonfia dal centro). Si spingeva in basso una cosa che se ne andava
       verso l'alto. Ora lo stato di riposo su telefono è «fuori dallo schermo,
       sotto», come per i tre pannelli editor (v19.51): il gesto e
       l'animazione raccontano lo stesso movimento.
       ⚠️ `transform:none` da aperto, MAI `translateY(0)`: sono la stessa
       posizione ma non lo stesso valore calcolato, e un transform diverso da
       `none` rende il box il contenitore di riferimento dei `position:fixed`
       — cioè sposta le tendine. È la trappola già pagata dal pannello. */
    .pvbox{transform:translateY(100%);opacity:1;
      transition:transform var(--d-slow) var(--ease-out),opacity var(--d-fast) var(--ease);}
    .pvwrap.open .pvbox{transform:none;opacity:1;}
    .pvhead{padding-top:calc(11px + env(safe-area-inset-top));}
    .pvback{max-width:118px;padding:5px 9px;font-size:11.5px;}
    /* area di tocco piena su iPhone */
    .x{width:42px;height:42px;}
    .pvrail{display:none;}
    .pvbar{display:flex;flex:0 0 auto;border-top:1px solid var(--border);background:var(--panel);padding:6px 8px calc(6px + env(safe-area-inset-bottom));gap:4px;}
    .pvb-btn{flex:1;display:flex;flex-direction:column;align-items:center;gap:3px;padding:7px 2px;border:none;background:none;font:inherit;font-weight:800;font-size:11px;color:var(--green);border-radius:11px;cursor:pointer;-webkit-tap-highlight-color:transparent;}
    .pvb-btn .pvb-ic{font-size:19px;line-height:1;}
    /* Il primario si distingue col FONDO e non con la misura: la geometria la
       eredita da .pvb-btn, così resta identico agli altri tre (v19.73). In tema
       scuro passa da --green-solid come gli altri pieni verdi.
       ⚠️ Era `--green2` su `--soft`, e fino alla v19.52 quello era 3,5:1 —
       sotto il minimo, ed è la ragione per cui la v19.46 ci mise il pieno.
       MISURATO OGGI quel rischio NON esiste più: la palette «un verde solo»
       (v19.53) ha portato --green2 allo stesso #003C0F di --green, quindi il
       tenue darebbe 12:1. Il pieno resta perché è l'unica cosa che distingue
       questo pulsante dagli altri tre, non più per leggibilità. */
    .pvb-btn.pri{background:var(--green);color:#fff;}
    /* Distruttiva promossa in barra (danger con bar:true — oggi il solo
       «🗑 Elimina» dell'anteprima commessa, che non ha più il foglio «⋯»).
       ⚠️ --st-late-tx e NON --red: quest'ultimo non è ridefinito nel blocco
       tema scuro (stessa trappola di --tan) e resterebbe #c0392b sul fondo
       scuro della barra. È la stessa scelta già fatta per la voce «Elimina»
       del menu delle commesse. */
    .pvb-btn.dang{color:var(--st-late-tx);}
  }

  /* ===== themed native selects & date inputs ===== */
  select.filter,.field select,.lavsub select,.lavfield select[data-lavfield]{
    -webkit-appearance:none;-moz-appearance:none;appearance:none;
    background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23003C0F' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'><path d='M6 9l6 6 6-6'/></svg>");
    background-repeat:no-repeat;background-position:right 11px center;padding-right:32px;}
  /* in tema scuro la freccia col verde chiaro: sullo stesso URI cambia solo lo stroke */
  html[data-theme="dark"] select.filter,html[data-theme="dark"] .field select,
  html[data-theme="dark"] .lavsub select,html[data-theme="dark"] .lavfield select[data-lavfield]{
    background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%238EE1A3' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'><path d='M6 9l6 6 6-6'/></svg>");}
  select.filter:focus,.field input:focus,.field select:focus,.field textarea:focus,.search input:focus,.lavfield select[data-lavfield]:focus{
    outline:none;border-color:var(--green2);box-shadow:0 0 0 3px var(--ring);}
  /* hide native date UI – we use a themed calendar popover */
  input[type=date]{position:relative;}
  input[type=date]::-webkit-calendar-picker-indicator{opacity:0;cursor:pointer;}
  .forn-chip{display:inline-flex;align-items:center;gap:5px;background:var(--soft);border:1px solid var(--line2);border-radius:20px;padding:3px 10px 3px 11px;font-size:12.5px;font-weight:600;color:var(--green);white-space:nowrap;}
  .forn-chip button{background:none;border:none;cursor:pointer;color:var(--muted2);font-size:13px;padding:0;line-height:1;display:flex;align-items:center;}
  .forn-chip button:hover{color:#c0392b;}
  .datefield-wrap{position:relative;display:inline-block;}
  .field .datefield-wrap{display:block;}
  .qbtns .datefield-wrap{width:138px;}
  /* input data: eredita lo stile dei campi vicini, con spazio per l'icona */
  .datefield-wrap input{width:100%;box-sizing:border-box;padding-right:30px;}
  .qbtns .datefield-wrap input{font:inherit;font-size:12.5px;border:1px solid var(--line2);border-radius:8px;padding:7px 30px 7px 9px;background:var(--panel);color:var(--ink);}
  .cal-ico{position:absolute;right:7px;top:50%;transform:translateY(-50%);color:var(--muted2);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;padding:1px;border-radius:5px;transition:color var(--d-fast),background var(--d-fast);}
  .cal-ico:hover{color:var(--green2);background:var(--soft);}
  .cal-ico svg{display:block;}
  /* ⚠️ Era il letterale #b4452f — la trappola già pagata da --red, --tan e
     compagnia: un colore scritto a mano non lo tocca il blocco del tema scuro,
     e su fondo scuro dava 3,14:1. È il messaggio che dice PERCHÉ la data non è
     stata accettata: se non si legge, il campo non si può correggere. Il token
     --st-late-tx è l'unico rosso che il tema scuro ridefinisce, e in chiaro
     (#c0392b) è a occhio lo stesso rosso di prima. */
  .date-err-msg{color:var(--st-late-tx);font-size:10.5px;letter-spacing:.2px;margin-top:3px;font-weight:600;line-height:1.3;}
  .dt{position:relative;}
  .dt .dt-ico{position:absolute;right:10px;top:50%;transform:translateY(-50%);pointer-events:none;color:var(--green2);display:inline-flex;}

  /* ===== custom multi-select ===== */
  .ms{position:relative;}
  .ms-btn{font:inherit;font-weight:600;border:1px solid var(--line2);background:var(--panel);color:var(--ink);border-radius:var(--r-sm);padding:10px 30px 10px 12px;cursor:pointer;display:inline-flex;align-items:center;gap:8px;min-height:40px;white-space:nowrap;position:relative;text-align:left;box-shadow:var(--sh-sm);}
  .ms-btn:hover{border-color:var(--green2);}
  .ms-btn:focus-visible{outline:none;border-color:var(--green2);box-shadow:0 0 0 3px var(--soft);}
  .ms.open .ms-btn{border-color:var(--green2);box-shadow:0 0 0 3px var(--ring);}
  .ms-btn::after{content:"";position:absolute;right:11px;top:50%;width:8px;height:8px;border-right:2px solid var(--green2);border-bottom:2px solid var(--green2);transform:translateY(-65%) rotate(45deg);transition:background var(--d-fast) var(--ease),border-color var(--d-fast) var(--ease),color var(--d-fast) var(--ease),transform var(--d-fast) var(--ease);}
  .ms.open .ms-btn::after{transform:translateY(-30%) rotate(225deg);}
  .ms-btn .ms-cap{color:var(--muted);font-weight:700;}
  .ms-btn.has .ms-cap{color:var(--ink);}
  .ms-btn .ms-count{background:var(--green);color:#fff;font-size:11px;font-weight:800;border-radius:999px;padding:1px 8px;}
  .ms-pop{position:absolute;z-index:70;top:calc(100% + 6px);left:0;min-width:230px;max-width:320px;background:var(--panel);border:1px solid var(--line2);border-radius:12px;box-shadow:0 18px 44px rgba(15,30,20,.20);padding:8px;display:none;max-height:340px;overflow:auto;}
  .ms.open .ms-pop{display:block;animation:dlgpop .14s ease;}
  .ms-search{width:100%;border:1px solid var(--line2);border-radius:8px;padding:8px 10px;font:inherit;font-size:13px;margin-bottom:6px;}
  .ms-opt{display:flex;align-items:center;gap:9px;padding:8px 9px;border-radius:8px;cursor:pointer;font-size:13.5px;font-weight:600;user-select:none;}
  .ms-opt:hover{background:var(--soft);}
  .ms-opt.kbd-hl{background:var(--green2);color:#fff;}
  .ms-opt.kbd-hl.on{color:#fff;}
  .ms-opt input{width:16px;height:16px;accent-color:var(--green2);flex:0 0 auto;}
  .ms-opt.on{color:var(--green);}
  .ms-empty{padding:10px;color:var(--muted);font-size:13px;text-align:center;}
  /* Filtro crust BLOCCATO finché non si sceglie il fornitore. È il parente di
     `.ms.locked` (le tendine in sola lettura dei pannelli), con due differenze
     volute: resta TOCCABILE — `toggleMS` risponde col motivo, e su telefono non
     c'è il suggerimento al passaggio del mouse — e i colori vengono da variabili,
     quindi il tema scuro è coperto.
     ⚠️ La freccina è un `::after` in position:absolute CON I BORDI: per farne un
     lucchetto non basta cambiare `content`, vanno azzerati bordi e dimensioni o
     restano due tratti verdi sopra il glifo. */
  /* ⚠️ Il fondo resta --panel e l'etichetta il --muted di sempre: sul grigio
     --sidebar il --muted scende a 4,34:1 (la stessa trappola già pagata da
     .prog-terzhead) e --muted2 a 2,34. Che sia spento lo dicono il bordo
     TRATTEGGIATO e il lucchetto, non un testo sbiadito da leggere peggio. */
  .ms.crustlock .ms-btn{border-style:dashed;border-color:var(--line2);cursor:default;box-shadow:none;}
  .ms.crustlock .ms-btn::after{content:"🔒";border:0;width:auto;height:auto;right:10px;
    transform:translateY(-50%);font-size:11px;opacity:.7;}
  .ms.crustlock .ms-pop{display:none!important;}
  /* Una tendina lunga scorre invece di uscire dallo schermo — con UN SOLO
     contenitore che scorre: il riquadro è una colonna flex, la lista prende lo
     spazio che avanza (`min-height:0`, o un flex item non si comprime sotto il
     proprio contenuto) e ricerca, piede e «＋ Nuovo…» restano fermi ai bordi.
     ⚠️ Due scroller annidati NON funzionano: dare alla lista un `max-height` suo
     dentro un riquadro che scorre a sua volta faceva sì che la rotella muovesse
     solo la lista, e — con `overscroll-behavior:contain`, che è lì apposta per
     non trascinare la pagina sotto — arrivata in fondo non passava il gesto al
     riquadro. Misurato: riquadro alto 338px con 440 di contenuto, `scrollTop`
     fermo a 0 e il piede «Seleziona tutti / Nessuno» fuori dal riquadro,
     irraggiungibile; con poche voci e poco spazio sotto al pulsante la rotella
     non faceva proprio nulla. Difetto introdotto in v19.4 e corretto in v19.5.
     ⚠️ `:has()` non è un vezzo: `.ms.cb` (il combobox dei campi testo) NON ha
     una `.ms-list` — le voci sono figlie diritte del riquadro — e con
     `overflow:hidden` resterebbe tagliato senza scorrere. Se un browser non
     conoscesse `:has()` la regola cade INTERA e si torna al riquadro che scorre
     da sé, che è il comportamento di sempre: il ripiego è sicuro. */
  .ms-pop:has(.ms-list){flex-direction:column;overflow:hidden;}
  .ms.open .ms-pop:has(.ms-list){display:flex;}
  .ms-pop .ms-search,.ms-pop .ms-foot,.ms-pop>.ms-qadd{flex:0 0 auto;}
  .ms-pop .ms-list{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;}
  /* «Azzera filtri» è la sola ⨉ anche sul computer (scelta dell'utente): con
     cinque tendine in barra la scritta manda a capo la riga appena si filtra
     (misurato a 1280px: 103px invece di 49). Il `title` resta, quindi al
     passaggio del mouse la descrizione c'è lo stesso. */
  #subbar .toolbar .btn.reset .rlbl{display:none;}
  .ms-foot{display:flex;justify-content:space-between;gap:8px;border-top:1px solid var(--line);margin-top:6px;padding-top:7px;}
  /* ⚠️ --green, non --green2: quest'ultimo come TESTO su bianco dà 4,38:1, sotto
     AA. È la stessa trappola già pagata da .gs-scope.on e dalle voci di menu del
     Programma; qui è emersa campionando il piede del pannello «🧱 Crust». */
  .ms-foot button{font:inherit;font-weight:700;font-size:12px;border:none;background:none;color:var(--green);cursor:pointer;padding:4px 6px;border-radius:6px;}
  .ms-foot button:hover{background:var(--soft);}
  .ss{display:block;position:relative;}
  .ss .ms-btn{width:100%;font-weight:600;}
  .ss .ms-btn .ms-cap{color:var(--ink);font-weight:600;}
  .ss .ms-pop{min-width:100%;max-width:none;}
  .ss .ms-opt{font-weight:600;}
  .ss .ms-opt.on{background:var(--soft);color:var(--green);font-weight:800;}
  .ms.cb{display:block;position:relative;}
  .ms.cb .ms-pop{min-width:100%;max-height:230px;overflow-y:auto;}
  .ms.cb.open .ms-pop{display:block;animation:dlgpop .12s ease;}
  .chip.chip-pronta{border-color:#93c5fd;background:#eff6ff;color:#1d4ed8;}
  .chip.chip-pronta .ldot{background:#1d4ed8;}
  .ms.managed.disabled .ms-btn{opacity:.45;cursor:not-allowed;background:#f1f3f5;color:#9aa6b2;pointer-events:none;}
  /* tendina bloccata dal lock del pannello (Consegnata / tintura-rasatura Trasferito):
     classe ortogonale a .disabled (chain-lock) così sbloccando non si tocca il chain. */
  .ms.locked .ms-btn{opacity:.55;cursor:not-allowed;background:#f1f3f5;color:#9aa6b2;pointer-events:none;}
  .ms.managed .ms-btn{width:100%;font-weight:600;}
  .ms.managed .ms-btn .ms-cap{color:var(--muted);font-weight:600;}
  .ms.managed .ms-btn.has .ms-cap{color:var(--ink);}
  .ms.managed .ms-pop{min-width:100%;max-height:300px;}
  .dlgbody .ms-pop{max-height:300px;}
  .ms-qadd{display:flex;align-items:center;gap:7px;padding:8px 9px;border-top:1px solid var(--line);color:var(--green);font-weight:800;font-size:13px;cursor:pointer;border-radius:0 0 8px 8px;}
  .ms-qadd:hover{background:var(--soft);}
  .ms-qadd.disabled{color:var(--muted);cursor:not-allowed;opacity:.6;}
  .ms-qadd.disabled:hover{background:transparent;}
  .ms-qadd-why{font-weight:600;font-size:11px;opacity:.85;}

  /* ===== search with filter tags ===== */
  .search{display:flex;align-items:center;flex-wrap:wrap;gap:6px;padding:6px 10px 6px 36px;border:1px solid var(--line2);border-radius:var(--r);background:var(--panel);box-shadow:var(--sh-sm);}
  .search input{padding:6px 4px;border:none;background:transparent;box-shadow:none;flex:1;min-width:120px;}
  .search input:focus{outline:none;box-shadow:none;}
  .search:focus-within{border-color:var(--green2);box-shadow:0 0 0 3px var(--ring);}
  /* Contenitore dei chip filtro dentro la barra di ricerca: non era mai stato
     definito, quindi i .stag erano figli di uno span senza stile e perdevano il
     gap del flex della .search, restando attaccati fra loro. `display:contents`
     lo rende trasparente al layout: i chip tornano figli flex diretti. */
  .tagbox{display:contents;}
  .stag{display:inline-flex;align-items:center;gap:6px;background:var(--soft);border:1px solid var(--soft-bd);color:var(--green);font-size:12.5px;font-weight:700;border-radius:999px;padding:3px 6px 3px 11px;white-space:nowrap;}
  .stag b{font-weight:800;}
  .stag .sx{border:none;background:none;cursor:pointer;color:var(--green2);font-size:15px;line-height:1;width:18px;height:18px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;}
  .stag .sx:hover{background:var(--soft-bd);color:var(--green);}
  .tabhint{font-size:11px;color:var(--muted2);font-weight:600;white-space:nowrap;}
  /* La ✕ che svuota la barra di ricerca (aggiunta a runtime da wireSearchClear).
     Stessa forma della .gs-clear della ricerca globale, ma qui SVUOTA e basta.
     Compare solo con `.has-clear`, cioè quando c'è del testo o un filtro Tab:
     a barra vuota sarebbe un pulsante che non fa niente. Su telefono il
     quadratino sale a 30px, che è il minimo per un polpastrello. */
  .s-clear{display:none;flex:0 0 auto;border:none;background:transparent;color:var(--muted);cursor:pointer;
    font-size:19px;line-height:1;padding:0 2px 2px;border-radius:6px;}
  .search.has-clear .s-clear{display:inline-flex;align-items:center;justify-content:center;min-width:24px;}
  @media(hover:hover){ .s-clear:hover{color:var(--ink);} }
  @media(max-width:640px){ .search.has-clear .s-clear{min-width:30px;min-height:30px;padding:0;} }

  /* ===== reset-filters button ===== */
  .sort-btn.active{background:var(--green);color:#fff;border-color:var(--green);font-weight:800;}
  .btn.reset{background:var(--panel);color:var(--muted);border:1px solid var(--line2);}
  .btn.reset:hover{border-color:#c0492b;color:#c0492b;}
  .btn.reset.hot{border-color:var(--green2);color:var(--green);}
  /* Toolbar snella: «Azzera filtri» compare solo quando c'è davvero un filtro da azzerare */
  .btn.reset:not(.hot){display:none;}

  /* ===== stock-service highlight (clienti) ===== */
  tr.stockrow{background:linear-gradient(0deg,var(--amber-bg),var(--amber-bg));}
  tr.stockrow:hover{background:#f6ead0 !important;}
  tr.stockrow td.tz{color:#7a5912;}
  .stockbadge{display:inline-flex;align-items:center;gap:6px;font-size:10px;letter-spacing:.6px;text-transform:uppercase;font-weight:800;color:#7a5912;background:#fff;border:1px solid var(--amber-bd);border-radius:999px;padding:2px 8px;margin-left:8px;}
  .stockbadge::before{content:"◆";font-size:8px;}

  /* ===== trash / archive rows ===== */
  .metab{font-size:12px;color:var(--muted);font-weight:600;}
  .metab b{color:var(--ink);}
  .trleft{font-size:11.5px;color:#c0492b;font-weight:700;}
  .trleft.warn{color:#c0492b;}
  /* "fermo da X giorni": tempo che una scheda passa In lavorazione */
  .fermo{display:inline-flex;align-items:center;font-size:10.5px;font-weight:800;letter-spacing:.3px;padding:3px 9px;border-radius:999px;background:#eef2f4;color:#5b6b7a;white-space:nowrap;}
  .fermo.late{background:#fbe9e7;color:#c0492b;}
  .fermo.warn{background:#fef3c7;color:#b45309;}
  /* ----- calcolatore margine nel form scheda ----- */
  /* ⚠️ IL BIANCO DEV'ESSERE UN TOKEN, non `#fff` (v19.64). Scritto a mano non
     aveva nessun override nel blocco tema scuro, quindi al buio la card partiva
     BIANCA in alto — e proprio lì sta il numero del margine, che in scuro è
     verde chiaro: 1,43:1 misurato campionando i pixel. Il dato per cui la card
     esiste era praticamente invisibile. `--panel` si inverte da solo.
     La regola NON è scoped ai 640px di proposito: la card è condivisa con le
     tre anteprime e col calcolatore prezzi, e il difetto c'era ovunque. */
  .calc-card{border:1.5px solid var(--line2);border-radius:14px;background:linear-gradient(180deg,var(--panel),var(--soft));overflow:hidden;box-shadow:0 1px 2px rgba(20,40,30,.04);}
  .margine-box{padding:13px 15px;font-size:13px;}
  .margine-box.empty{color:var(--muted);font-style:italic;font-weight:500;padding:14px 15px;}
  /* hero: utile €/m² + chip margine (sul prezzo) e ricarico (sul costo) */
  .margine-box .mhero{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;padding-bottom:9px;}
  .margine-box .mhero-lab{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);}
  .margine-box .mhero-val{font-family:var(--serif);font-size:30px;font-weight:600;line-height:1;color:var(--ink);font-variant-numeric:tabular-nums;}
  .margine-box .mhero-val small{font-family:var(--sans);font-size:14px;font-weight:700;color:var(--muted);margin-left:2px;}
  .margine-box .mhero-chips{margin-left:auto;display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end;align-self:center;}
  .margine-box .mchip{font-size:12px;font-weight:800;padding:3px 9px;border-radius:999px;background:#fff;border:1px solid var(--line2);font-variant-numeric:tabular-nums;white-space:nowrap;color:var(--muted);}
  .margine-box .mchip-rik{color:var(--tan);border-color:var(--amber-bd);background:var(--amber-bg);}
  .margine-box.pos .mhero-val{color:var(--green3);}
  .margine-box.pos .mchip-marg{color:var(--green3);border-color:var(--soft-bd);background:var(--soft);}
  .margine-box.neg .mhero-val{color:var(--red);}
  .margine-box.neg .mchip-marg{color:var(--red);border-color:#f1c0b6;background:#fdf3f1;}
  /* didascalia discreta: differenza margine vs ricarico */
  .margine-box .mnote{font-size:11px;font-weight:600;color:var(--muted);line-height:1.45;padding-bottom:10px;margin-bottom:10px;border-bottom:1px dashed var(--line2);}
  .margine-box .mnote b{color:var(--ink);font-weight:800;}
  .margine-box .mlines{display:flex;flex-direction:column;gap:1px;}
  .margine-box .mrow{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;padding:3px 0;}
  .margine-box .mrow .ml{display:flex;flex-direction:column;gap:1px;color:var(--muted);font-weight:600;}
  .margine-box .mrow .ml .msub{font-size:11px;font-weight:600;color:var(--muted2);}
  .margine-box .mrow .mv{color:var(--ink);font-weight:700;font-variant-numeric:tabular-nums;white-space:nowrap;}
  .margine-box .mmiss{color:var(--muted2);font-style:italic;font-weight:600;}
  .sdog-badge{display:inline-block;font-size:10.5px;font-weight:800;letter-spacing:.02em;color:var(--amber-tx);background:var(--amber-bg);border:1px solid var(--amber-bd);border-radius:999px;padding:1px 7px;margin-left:5px;vertical-align:middle;text-transform:uppercase;}
  /* Provvigione agente nella box margine: riga in tono "tan/ambra". */
  .margine-box .mrow.prov .ml{color:var(--tan);font-weight:800;}
  .margine-box .mrow.prov .mv{color:var(--tan);}
  .prov-badge{display:inline-block;font-size:10.5px;font-weight:800;letter-spacing:.02em;color:var(--tan);background:var(--amber-bg);border:1px solid var(--amber-bd);border-radius:999px;padding:1px 7px;margin-left:5px;vertical-align:middle;text-transform:uppercase;}
  /* Chip provvigione nell'elenco agenti (verde brand). */
  .prov-chip{display:inline-block;font-size:11px;font-weight:800;color:var(--green);background:var(--soft);border:1px solid var(--soft-bd);border-radius:999px;padding:1px 9px;margin-left:6px;vertical-align:middle;font-variant-numeric:tabular-nums;}
  /* Contatti cliccabili (rubrica operativa): chip chiama/WhatsApp/scrivi. */
  .cchips{display:flex;flex-wrap:wrap;gap:6px;margin-top:5px;}
  .cchip{display:inline-flex;align-items:center;gap:4px;font-size:12px;font-weight:600;color:var(--ink);background:var(--soft);border:1px solid var(--soft-bd);border-radius:999px;padding:3px 10px;text-decoration:none;line-height:1.3;transition:background var(--d-fast),border-color var(--d-fast);}
  .cchip:hover{background:var(--card);border-color:var(--accent);}
  .cchip.wa{color:#128c4b;border-color:rgba(18,140,75,.35);background:rgba(18,140,75,.08);}
  .cchip.wa:hover{background:rgba(18,140,75,.16);border-color:#128c4b;}
  /* Campo percentuale con suffisso % (provvigione). */
  .pct-field{display:flex;align-items:stretch;border:1px solid var(--line2);border-radius:8px;overflow:hidden;background:var(--panel);}
  .pct-field:focus-within{border-color:var(--green3);box-shadow:0 0 0 3px var(--ring);}
  .pct-field input{flex:1;min-width:0;width:auto;border:none;border-radius:0;text-align:right;font-weight:700;font-variant-numeric:tabular-nums;box-shadow:none;}
  .pct-field input:focus{outline:none;box-shadow:none;}
  .pct-field .pct-sfx{display:flex;align-items:center;padding:0 12px;background:var(--sidebar);color:var(--muted);font-weight:800;border-left:1px solid var(--line2);}
  .margine-box .mtot{margin-top:10px;padding-top:9px;border-top:1px solid var(--line2);}
  .margine-box .mtot-h{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.04em;color:var(--muted);margin-bottom:3px;}
  .margine-box .mtot-note{margin-top:9px;padding-top:9px;border-top:1px dashed var(--line2);font-size:12px;color:var(--muted);font-style:italic;}
  .margine-box .mrow.tot{margin-top:4px;padding-top:7px;border-top:1px solid var(--line2);font-size:15px;}
  .margine-box.pos .mrow.tot .mv{color:var(--green3);}
  .margine-box.neg .mrow.tot .mv{color:var(--red);}
  /* card "Listino lavorazioni · TERZISTA" nella box margine (dettaglio per voce) */
  .margine-box .lav-card{background:var(--bg);border:1px solid var(--line2);border-radius:10px;padding:10px 11px;margin-bottom:10px;}
  .margine-box .lav-card-h{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:7px;}
  .margine-box .lav-card-h .t{font-size:10.5px;font-weight:800;text-transform:uppercase;letter-spacing:.04em;color:var(--muted);}
  .margine-box .lav-tbl{width:100%;border-collapse:collapse;font-size:12.5px;}
  .margine-box .lav-tbl td{padding:3px 0;}
  .margine-box .lav-tbl td.n{color:var(--muted);font-weight:600;}
  .margine-box .lav-tbl td.v{text-align:right;color:var(--ink);font-weight:700;font-variant-numeric:tabular-nums;white-space:nowrap;}
  .margine-box .lav-tbl tr.sum td{border-top:1px solid var(--line2);padding-top:6px;font-weight:800;}
  .margine-box .lav-tbl tr.sum td.n{color:var(--ink);}
  .margine-box.pos .lav-tbl tr.sum td.v{color:var(--green3);}
  /* strumenti del calcolatore (sdoganamento + obiettivo margine) */
  .calc-tools{display:flex;flex-wrap:wrap;align-items:flex-end;gap:14px;padding:12px 15px;background:#fff;border-top:1px solid var(--line);}
  .calc-tool{display:flex;flex-direction:column;gap:4px;}
  .calc-tool label{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.04em;color:var(--muted);}
  .calc-inp{display:inline-flex;align-items:center;gap:4px;border:1.5px solid var(--line2);border-radius:9px;background:#fff;padding:0 9px;width:108px;transition:border-color var(--d-fast),box-shadow var(--d-fast);}
  .calc-inp:focus-within{border-color:var(--green3);box-shadow:0 0 0 3px var(--ring);}
  .calc-inp input{border:0;outline:0;width:100%;padding:7px 0;font-size:14px;font-weight:700;background:transparent;color:var(--ink);font-variant-numeric:tabular-nums;-moz-appearance:textfield;}
  .calc-inp input::-webkit-outer-spin-button,.calc-inp input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0;}
  .calc-suf{font-size:13px;font-weight:800;color:var(--muted);}
  .calc-hint{font-size:11px;font-weight:600;color:var(--muted2);}
  .sdog-reset{border:0;background:none;padding:0;margin:0;font:inherit;font-weight:800;color:var(--green3);cursor:pointer;text-decoration:underline;}
  #fl-tmarg-out b{color:var(--green3);}
  .calc-apply{font-size:12.5px;padding:8px 13px;white-space:nowrap;}
  .calc-apply:disabled{opacity:.45;cursor:not-allowed;}
  @media(max-width:640px){
    .margine-box .mhero-val{font-size:26px;}
    .calc-tools{gap:11px;}
    .calc-tool{flex:1 1 130px;}
    .calc-inp{width:100%;}
    .calc-apply{flex:1 1 100%;}
  }

  /* ===== themed calendar popover ===== */
  .calpop{position:fixed;z-index:90;width:280px;background:var(--panel);border:1px solid var(--line2);border-radius:14px;box-shadow:0 22px 54px rgba(15,30,20,.26);padding:12px;display:none;}
  .calpop.open{display:block;animation:dlgpop .14s ease;}
  .cal-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;gap:6px;}
  .cal-head .cal-my{font-weight:800;color:var(--green);font-size:14.5px;text-transform:capitalize;}
  .cal-nav{border:1px solid var(--line2);background:var(--panel);color:var(--green2);width:30px;height:30px;border-radius:8px;cursor:pointer;font-size:15px;display:inline-flex;align-items:center;justify-content:center;}
  .cal-nav:hover{border-color:var(--green2);background:var(--soft);}
  .cal-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:2px;}
  .cal-dow{font-size:10px;letter-spacing:.4px;text-transform:uppercase;color:var(--muted2);font-weight:800;text-align:center;padding:4px 0;}
  .cal-day{font:inherit;font-size:13px;font-weight:600;border:none;background:none;color:var(--ink);height:32px;border-radius:8px;cursor:pointer;}
  .cal-day:hover{background:var(--soft);}
  .cal-day.muted{color:var(--muted2);}
  .cal-day.today{box-shadow:inset 0 0 0 1.5px var(--green2);color:var(--green);font-weight:800;}
  .cal-day.sel{background:var(--green);color:#fff;font-weight:800;}
  .cal-foot{display:flex;justify-content:space-between;margin-top:8px;border-top:1px solid var(--line);padding-top:8px;}
  .cal-foot button{font:inherit;font-weight:700;font-size:12px;border:none;background:none;color:var(--green2);cursor:pointer;padding:4px 6px;border-radius:6px;}
  .cal-foot button:hover{background:var(--soft);}

  /* quick real-meters inline editor on list rows */
  .qreal{display:flex;align-items:center;gap:6px;flex-wrap:wrap;background:var(--soft);border:1px solid var(--soft-bd);border-radius:9px;padding:6px 8px;}
  /* background/color espliciti: senza, in tema scuro il campo restava bianco
     col testo chiaro di --ink → si scriveva "in bianco su bianco". */
  .qreal input{width:84px;padding:6px 8px;border:1px solid var(--line2);border-radius:7px;font:inherit;font-size:12.5px;font-variant-numeric:tabular-nums;background-color:var(--panel);color:var(--ink);}
  .qreal label{font-size:11px;color:var(--green);font-weight:700;}
  .btn.qr{background:var(--green2);color:#fff;}
  .btn.qr:hover{background:var(--green3);}
  /* Barra «Metri reali» dell'anteprima scheda (realePreviewBlock, app.4): stessa
     famiglia grafica dell'editor inline .qreal, ma con etichetta SOPRA il campo
     perché qui c'è larghezza e i campi possono essere uno per cliente. */
  .pvreal{background:var(--soft);border:1px solid var(--soft-bd);border-radius:var(--r);padding:11px 14px 12px;margin-bottom:14px;}
  .pvreal-h{display:flex;align-items:center;justify-content:space-between;gap:10px;font-size:10px;letter-spacing:.9px;text-transform:uppercase;font-weight:800;color:var(--green);margin-bottom:8px;}
  .pvreal-h .st{font-size:10px;letter-spacing:.4px;text-transform:none;color:var(--st-lav-tx);background:var(--st-lav-bg);border:1px solid var(--amber-bd);border-radius:999px;padding:2px 8px;}
  .pvreal-row{display:flex;align-items:flex-end;gap:10px;flex-wrap:wrap;}
  .pvreal-f{display:flex;flex-direction:column;gap:3px;}
  .pvreal-f .l{font-size:10.5px;font-weight:800;color:var(--green);letter-spacing:.3px;}
  .pvreal-f input{width:120px;padding:9px 11px;border:1px solid var(--line2);border-radius:9px;font:inherit;font-size:15px;font-weight:800;text-align:right;font-variant-numeric:tabular-nums;background:var(--panel);color:var(--ink);}
  .pvreal-hint{font-size:11.5px;color:var(--muted);margin-top:7px;}
  /* Metri reali già registrati: campi in sola lettura finché non si sblocca
     (protezione delle sovrascritture accidentali — vedi app.3). Il campo resta
     ben LEGGIBILE (è il dato definitivo), non "spento" come un disabled. */
  .qreal input.ro,.pvreal-f input.ro,.mcli-in.ro,#panel input.ro{background:var(--soft);border-style:dashed;cursor:default;}
  .btn.qr-unlock{align-self:flex-end;}
  /* Metri ereditati dalla lavorazione finale della catena: al posto dello
     sblocco una nota che dice dove si cambiano. Viola come la pastiglia 🔗
     della catena, così il legame si riconosce a colpo d'occhio; il testo è
     --ink e non --muted, che sul fondo viola tenue scende sotto AA. */
  .qr-fromchain{grid-column:1/-1;display:flex;flex-wrap:wrap;gap:4px;align-items:baseline;
    font-size:11.5px;line-height:1.4;color:var(--ink);background:var(--catena-bg);
    border:1px solid var(--catena-bd);border-radius:8px;padding:6px 9px;}
  .qr-fromchain b{color:var(--catena-tx);font-weight:800;}
  .btn.off,.btn:disabled{opacity:.45;cursor:not-allowed;}
  .pvreal-h .lk{font-size:10px;letter-spacing:.4px;text-transform:none;color:var(--muted);background:var(--panel);border:1px solid var(--line2);border-radius:999px;padding:2px 8px;}
  /* Storico delle modifiche ai metri reali + ↩ Ripristina */
  .pvreal-log{margin-top:9px;padding-top:8px;border-top:1px dashed var(--soft-bd);}
  .pvreal-lh{font-size:10px;letter-spacing:.7px;text-transform:uppercase;font-weight:800;color:var(--muted);margin-bottom:4px;}
  .pvreal-lrow{display:flex;gap:8px;flex-wrap:wrap;font-size:11.5px;color:var(--ink);padding:2px 0;}
  .pvreal-lrow .d{color:var(--muted);font-variant-numeric:tabular-nums;min-width:96px;}
  .pvreal-lrow .c{flex:1 1 auto;}
  /* «↩ da RF26129»: da quale lavorazione della catena è arrivato il valore.
     --green e non --green2, che su fondo chiaro sta a 4,38:1 (sotto AA). */
  .pvreal-lrow .src{margin-left:7px;color:var(--green);font-weight:700;white-space:nowrap;}
  #pv-real-undo{margin-top:6px;}
  /* Nota di blocco dei metri reali nell'editor scheda (sotto la .mqgrid) */
  .lotto-saldo-hint.real-lock{display:flex;align-items:center;gap:8px;flex-wrap:wrap;}
  @media (max-width:640px){
    /* su iPhone i campi vanno a piena larghezza: due caselle affiancate da 120px
       più il Salva non ci stanno senza scorrimento laterale */
    .pvreal-f{flex:1 1 100%;}
    .pvreal-f input{width:100%;}
    .pvreal-row>.btn.qr{flex:1 1 100%;justify-content:center;}
  }


  /* ===== PRINT SCHEDA ===== */
  #print-root{display:none;}
  @media print{
    /* PDF copia (A4): attivo solo SENZA body.print-label e NON nel documento
       autonomo dell'etichetta (body.label-doc), che ha un suo layout A5. Senza
       l'esclusione di .label-doc, questa regola nasconderebbe l'etichetta in
       stampa (non c'è #print-root nel documento blob) → foglio bianco. */
    body:not(.print-label):not(.label-doc)>*:not(#print-root){display:none !important;}
    body:not(.print-label):not(.label-doc) #print-root{display:block !important;}
    /* margine di pagina 7mm = zona sicura non stampabile (Canon/iPhone tagliano
       gli ultimi mm ai bordi: con margin:0 il piè di pagina finiva nel margine
       fisico della stampante e spariva). L'area di contenuto resta identica
       perché i 7mm sono tolti dal padding interno di .pg. */
    @page{size:A4;margin:7mm;}
    /* CAUSA PRINCIPALE del footer tagliato: fuori da @media print html,body hanno
       height:100%;overflow:hidden (scroll-containment dello schermo). In stampa
       quel vincolo NON va lasciato: bloccherebbe l'altezza a UNA schermata e
       taglierebbe tutto ciò che sta più in basso (il piè di pagina!) e la 2ª
       copia. In stampa serve flusso naturale multi-pagina. */
    /* larghezza di layout = larghezza del foglio. Senza, iOS/AirPrint impagina
       su una larghezza maggiore della nostra pagina e poi riduce tutto per
       farcelo stare: il foglio esce più piccolo, con margini bianchi molto più
       larghi che da desktop. Fissandola, la stampa riempie la pagina anche lì
       (su desktop coincide già con i 196mm dell'area utile: nessun effetto). */
    /* `!important` sul fondo: boot-preload.js tinge html/body di verde scuro
       con uno STILE INLINE (serve a evitare il lampo bianco all'avvio su
       iOS) e hideBootGate lo ripulisce solo a boot/login conclusi. Un
       inline batte qualunque regola senza !important: stampando prima che
       il boot finisca uscirebbe un foglio interamente verde. */
    html,body{background:#fff !important;margin:0;padding:0;height:auto;overflow:visible;}
    body:not(.print-label):not(.label-doc){width:196mm;}   /* solo i documenti A4: l'etichetta A5 ha la sua larghezza */
    /* height:100% (NON overflow:hidden — è quello che tagliava il footer) serve
       a dare a .pg un riferimento DEFINITO per il suo min-height:100%, cioè
       l'AREA UTILE REALE della pagina di stampa: è così che il piè di pagina
       finisce in fondo al foglio come su un documento normale, adattandosi da
       sé ai margini che il sistema impone (vedi il commento su .pg).
       Il contenuto che eccede continua a scorrere sui fogli successivi:
       overflow:visible garantisce che nulla venga tagliato (verificato in
       headless: 2 copie → 2 pagine, report lungo → 3 pagine, etichetta A5
       invariata). L'etichetta è esclusa: ha un suo layout A5. */
    html{height:100%;}
    body:not(.print-label):not(.label-doc){height:100%;}
    #print-root{height:100%;}
  }
  #print-root *{margin:0;padding:0;box-sizing:border-box;}
  #print-root{font-family:'Helvetica Neue',Arial,sans-serif;color:#1d211e;-webkit-print-color-adjust:exact;print-color-adjust:exact;}

  /* ══ PALETTE DEI DOCUMENTI (PDF scheda, PDF commessa, report, etichetta A5) ══
     I documenti hanno una palette TUTTA LORO, scritta in letterali e non nei
     token: nessuna var() la raggiunge, ed è voluto — vedi la trappola in fondo.
     Dopo l'allineamento al marchio (agosto 2026) è la stessa TINTA dello
     schermo, hue 135, quella del logo RACCOSTA (brand/marchio.js, VERDE.logo).
     Prima era una famiglia sua, smeraldo: #163a22 e #2f6b43 stavano a hue 140,
     i grigi di corredo fra 127 e 150. Sul foglio non stonava, ma accanto a una
     stampa del logo si vedeva che i due verdi non erano parenti.

     La scala — ogni voce è hue 135 esatta, e i contrasti sono su BIANCO:
       #003C0F  12,70:1  verde pieno: numeri, titoli, bordi forti, badge
       #005C17   8,26:1  verde secondario: etichette accento, filetti, chip
       #1A6B2E   6,59:1  verde vivo: avanzamento (lo stesso di _ordStateColor)
       #252D27  14,16:1  testo delle tabelle
       #38443B  10,20:1  testo di corpo
       #56665A   6,10:1  testo di servizio (meta, provenienze)
       #637768   4,81:1  etichette piccole maiuscole e note tenui

     ⚠️ Sul foglio bianco il verde dev'essere SCURO e pieno: il verde d'azione
     dello schermo, più chiaro, in stampa perde corpo. Per questo il secondario
     è #005C17 e non una gradazione più tenue.

     ⚠️ NIENTE CAMPITURE. I fondi verde chiaro sono stati tolti da tutti i
     documenti per risparmiare inchiostro (scelta dell'utente): a delimitare
     sono i bordi. Restano campiti solo il crema di .pintbox, i badge ambra,
     .pchst.cur e il binario grigio delle barre. Un fondo bianco qui dentro non
     è una dimenticanza da colmare.

     ⚠️ Il #637768 delle etichette piccole è il PAVIMENTO della scala, non un
     grigio a piacere: il colore che sostituisce (#7c857e) stava a 3,81:1 ed è
     il più diffuso di tutti i documenti — .pbl, .plab, .pql, .pkick, le th
     delle tabelle. Un valore più chiaro riporta sotto AA metà del foglio.

     ⚠️ NON scrivere var(--green) qui dentro senza sapere che cosa fai: in tema
     scuro quella variabile diventa CHIARA. Dentro @media print è innocua (il
     blocco scuro è @media screen, quindi in stampa la variabile torna al verde
     scuro — è il caso del report, vedi il commento in app.5); ma #print-root e
     #label-modal-area stanno nel DOM dell'app anche a VIDEO, e lì una var()
     segue il tema mentre il fondo del documento resta bianco. Nel dubbio,
     letterale + il perché. */
  /* Report mensile: documento che SCORRE su più pagine (niente .pg a altezza fissa). */
  #print-root .repdoc{padding:2mm 3mm;}
  #print-root .rep-head{display:flex;justify-content:space-between;align-items:flex-start;border-bottom:2px solid #005C17;padding-bottom:10px;margin-bottom:14px;gap:16px;}
  #print-root .rep-az{font-size:11px;line-height:1.5;color:#38443B;}
  #print-root .rep-tt{text-align:right;}
  #print-root .rep-k{font-size:10px;letter-spacing:3px;text-transform:uppercase;color:#637768;font-weight:700;}
  #print-root .rep-mese{font-size:26px;font-weight:800;color:#003C0F;line-height:1.1;}
  #print-root .rep-gen{font-size:10px;color:#637768;margin-top:2px;}
  #print-root .rep-kpis{display:grid;grid-template-columns:repeat(7,1fr);gap:7px;margin-bottom:14px;}
  #print-root .rep-kpi{background:#fff;border:1px solid #e4e8e3;border-radius:9px;padding:8px 6px;text-align:center;}
  #print-root .rep-kpi .rn{font-size:15px;font-weight:800;color:#1d211e;}
  #print-root .rep-kpi .rl{font-size:8.5px;letter-spacing:.6px;text-transform:uppercase;color:#637768;font-weight:700;margin-top:3px;}
  #print-root .rep-warn{font-size:10px;color:#8a6516;background:#fbf2dd;border:1px solid #e6cf9a;border-radius:7px;padding:6px 10px;margin-bottom:12px;}
  /* La sezione può spezzarsi fra due pagine: con break-inside:avoid una sezione
     più alta del foglio veniva spostata INTERA alla pagina dopo, lasciando mezza
     pagina bianca e allungando il report di un foglio. Si tiene invece unito solo
     ciò che conta: il titolo con l'inizio della sua tabella, e l'intestazione di
     colonna ripetuta su ogni pagina. */
  #print-root .rep-sec{margin-bottom:14px;break-inside:auto;page-break-inside:auto;}
  #print-root .rep-sec h3{font-size:12px;letter-spacing:1.5px;text-transform:uppercase;color:#003C0F;font-weight:800;margin-bottom:6px;break-after:avoid;page-break-after:avoid;}
  #print-root .rep-tab thead{display:table-header-group;}
  #print-root .rep-tab tr{break-inside:avoid;page-break-inside:avoid;}
  #print-root .rep-note{font-size:9px;letter-spacing:.5px;text-transform:none;color:#637768;font-weight:600;}
  #print-root .rep-tab{width:100%;border-collapse:collapse;font-size:10.5px;}
  #print-root .rep-tab th{text-align:left;font-size:9px;letter-spacing:.5px;text-transform:uppercase;color:#637768;font-weight:700;border-bottom:1px solid #cfd6cf;padding:4px 6px;}
  #print-root .rep-tab td{padding:4px 6px;border-bottom:1px solid #eef1ee;}
  #print-root .rep-tab .rr{text-align:right;white-space:nowrap;}
  /* Modulo PDF Commessa (ordine cliente) */
  #print-root .commdoc{padding:2mm 3mm;}
  #print-root .comm-head{display:flex;justify-content:space-between;align-items:center;gap:16px;border-bottom:2.5px solid #003C0F;padding-bottom:10px;margin-bottom:12px;}
  #print-root .comm-logo{height:88px;width:auto;}
  #print-root .comm-az{font-size:10.5px;line-height:1.5;color:#38443B;}
  #print-root .comm-head-r{text-align:right;flex:0 0 auto;}
  #print-root .comm-kick{font-size:10px;letter-spacing:2.5px;text-transform:uppercase;color:#637768;font-weight:700;}
  #print-root .comm-num{font-size:27px;font-weight:800;color:#003C0F;line-height:1.05;margin-top:2px;}
  #print-root .comm-gen{font-size:10px;color:#637768;margin-top:2px;}
  #print-root .comm-cliente{display:flex;justify-content:space-between;align-items:flex-start;gap:16px;background:#fff;border:1px solid #e4e8e3;border-radius:11px;padding:11px 15px;margin-bottom:12px;}
  #print-root .comm-lab{font-size:9px;letter-spacing:1px;text-transform:uppercase;color:#637768;font-weight:700;}
  #print-root .comm-cliname{font-size:20px;font-weight:800;color:#1d211e;line-height:1.1;margin-top:2px;}
  #print-root .comm-climeta{font-size:10.5px;color:#56665A;margin-top:3px;}
  #print-root .comm-cliente-r{display:flex;gap:18px;text-align:right;}
  #print-root .comm-mini .comm-val{font-size:14px;font-weight:800;color:#1d211e;margin-top:2px;}
  #print-root .comm-stato-done{color:#1A6B2E !important;}
  #print-root .comm-stato-work{color:#b06a00 !important;}
  #print-root .comm-kpis{grid-template-columns:repeat(5,1fr) !important;}
  #print-root .comm-bar{height:11px;border-radius:999px;background:#e4e8e3;overflow:hidden;margin:2px 0 4px;}
  #print-root .comm-bar-fill{height:100%;border-radius:999px;}
  #print-root .comm-tab td{vertical-align:middle;}
  #print-root .comm-tab .comm-col{font-weight:800;color:#003C0F;font-size:11.5px;}
  #print-root .comm-avz{text-align:right;white-space:nowrap;width:78px;}
  #print-root .comm-rowbar{display:inline-block;width:48px;height:6px;border-radius:999px;background:#e4e8e3;overflow:hidden;vertical-align:middle;margin-right:5px;}
  #print-root .comm-rowbar-fill{height:100%;border-radius:999px;}
  #print-root .comm-avz span{font-size:9.5px;font-weight:800;}
  #print-root .comm-chain-row td{border-bottom:1px solid #eef1ee;padding-top:2px;}
  #print-root .comm-chlab{font-size:9px;font-weight:800;letter-spacing:.5px;text-transform:uppercase;color:#637768;}
  #print-root .comm-chstep{font-size:9.5px;color:#38443B;}
  #print-root .comm-chstep b{color:#003C0F;}
  #print-root .comm-chterz{color:#005C17;font-weight:700;}
  #print-root .comm-chst{color:#637768;font-style:italic;}
  #print-root .comm-charr{color:#637768;margin:0 5px;font-weight:800;}
  #print-root .comm-note{font-size:11px;color:#38443B;white-space:pre-wrap;line-height:1.5;}
  #print-root .comm-foot{display:flex;justify-content:space-between;align-items:flex-end;gap:16px;margin-top:22px;padding-top:9px;border-top:2px solid #005C17;font-size:10px;color:#637768;}
  #print-root .comm-foot-az{font-size:10.5px;line-height:1.5;color:#38443B;}
  #print-root .comm-foot-r{text-align:right;}
  #print-root .comm-sign{color:#38443B;margin-top:6px;}
  /* ALTEZZA DEL FOGLIO — NON rimetterla fissa (283mm): era la causa vera del
     "da iPhone esce su due pagine". Su iOS/AirPrint i margini di stampa li
     impone il sistema e sono più larghi dei nostri 7mm, quindi l'area utile
     reale scende ben sotto i 283mm: un box alto esattamente 283mm non ci entra
     e il browser lo spezza a cavallo di due fogli, anche con mezza pagina di
     contenuto. Da desktop non si vedeva perché lì i margini sono i nostri.
     Il minimo è `max(262mm, 100%)`, cioè il più grande fra un valore fisso di
     sicurezza e l'AREA UTILE REALE della pagina di stampa (il 100% risale la
     catena html/body/#print-root, tutti a height:100% in @media print):
     - da desktop l'area utile è 283mm → il foglio la riempie e il piè di pagina
       va in fondo come su un documento normale (finisce a 11mm dal bordo carta:
       i 7mm di @page + i 4mm di padding di .pg);
     - se il sistema impone margini più larghi dei nostri 7mm (iOS/AirPrint),
       il 100% VALE QUEI margini, quindi il foglio si adatta e NON viene mai
       spezzato in due: verificato con page.pdf a margini di 7/10/12/15/20/25mm
       (una copia = una pagina sempre, footer a 11→28mm dal bordo).
     - i 262mm fissi restano come pavimento di sicurezza: se un browser
       risolvesse quel 100% sul viewport dello SCHERMO invece che sulla pagina
       (è l'errore che fa `100vh` su iOS Safari, vedi sotto), il comportamento
       torna esattamente quello — già provato su stampa reale — di prima.
     **`100vh` è stato provato e NON va**: su Chrome vale l'area utile reale
     della pagina (verificato con page.pdf a margini da 7 a 25mm), ma su iOS
     Safari vale il viewport dello SCHERMO (~223mm su iPhone), quindi il foglio
     finiva a due terzi di pagina col piè di pagina a mezz'aria — verificato su
     stampa reale. Da qui il `max()` con il pavimento fisso.
     Se il contenuto eccede, la copia scorre sul foglio successivo invece di
     essere tagliata.
     Il salto pagina resta SOLO fra una copia e l'altra (break-before sulla 2ª
     .pg): nessun break dopo l'ultima → niente foglio vuoto in coda. */
  /* Il padding INFERIORE (1.5mm) è quanto resta fra il piè di pagina e il bordo
     dell'area utile: sommato ai 7mm di @page il footer esce a ~8.5mm dal bordo
     carta. È il minimo sensato — sotto i 7mm c'è la zona che le stampanti reali
     non riescono a imaging (con @page margin:0 il footer veniva tagliato), e
     quei 7mm NON vanno ridotti per abbassarlo ancora. */
  #print-root .pg{width:196mm;height:auto;min-height:max(262mm,100%);overflow:visible;padding:4mm 6mm 1.5mm;display:flex;flex-direction:column;break-inside:auto;page-break-inside:auto;}
  #print-root .pg + .pg{break-before:page;page-break-before:always;}
  #print-root .ph{display:flex;align-items:center;justify-content:space-between;padding-bottom:8px;}
  #print-root .plogo{height:96px;}
  #print-root .pkick{font-size:11px;letter-spacing:3px;text-transform:uppercase;color:#637768;font-weight:700;text-align:right;}
  #print-root .pnum{font-size:30px;font-weight:800;color:#003C0F;letter-spacing:.5px;margin-top:3px;text-align:right;}
  #print-root .pcommessa{font-size:11px;font-weight:800;letter-spacing:1px;text-transform:uppercase;color:#005C17;text-align:right;margin-top:2px;}
  #print-root .pchain{font-size:10px;font-weight:700;letter-spacing:.5px;text-transform:none;color:#637768;}
  #print-root .prule{height:0;border-top:2px solid #005C17;border-radius:0;margin:6px 0 14px;}
  #print-root .g4{display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:8px;margin-bottom:9px;}
  #print-root .g3{display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px;margin-bottom:9px;}
  #print-root .g2{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:9px;}
  #print-root .pcard{background:#fff;border:1px solid #e4e8e3;border-radius:11px;padding:10px 13px;}
  #print-root .plab{font-size:9.5px;letter-spacing:1px;text-transform:uppercase;color:#637768;font-weight:700;white-space:normal;line-height:1.25;}
  #print-root .pval{font-size:17px;font-weight:800;margin-top:5px;color:#1d211e;}
  #print-root .pval.green{color:#003C0F;}
  #print-root .pval.sm{font-size:14px;}
  #print-root .pdest{display:inline-block;margin-top:6px;font-size:9.5px;letter-spacing:1px;text-transform:uppercase;font-weight:800;color:#003C0F;background:#fff;border:1px solid #C6DCCB;padding:3px 9px;border-radius:999px;}
  /* chip "rasato" accanto allo spessore (solo copia interna): segnala che lo
     spessore è quello ottenuto dalla rasatura collegata, non del grezzo. */
  #print-root .prasrow{margin-top:6px;}
  #print-root .raschip{display:inline-flex;align-items:center;gap:5px;font-size:9.5px;letter-spacing:.8px;text-transform:uppercase;font-weight:800;color:#005C17;background:#fff;border:1px solid #C6DCCB;padding:3px 9px;border-radius:999px;}
  #print-root .chero{background:#fff;border:2px solid #003C0F;border-radius:12px;padding:10px 22px;margin-bottom:9px;}
  #print-root .chlab{font-size:12px;letter-spacing:3px;text-transform:uppercase;color:#005C17;font-weight:800;}
  #print-root .chval{font-size:34px;font-weight:800;color:#003C0F;line-height:1.05;margin-top:4px;}
  #print-root .ppanel{border:1px solid #e4e8e3;border-left:6px solid #003C0F;border-radius:12px;padding:12px 18px 12px 20px;margin-bottom:9px;}
  #print-root .ppanel h3{font-size:12px;letter-spacing:2.5px;text-transform:uppercase;color:#003C0F;font-weight:800;margin-bottom:10px;}
  #print-root .plav-wrap{display:flex;gap:12px;align-items:stretch;margin-bottom:9px;}
  #print-root .plav-col{flex:1 1 56%;margin-bottom:0;}
  #print-root .pqty-col{flex:1 1 44%;display:flex;}
  #print-root .plav{display:flex;flex-direction:column;gap:9px;}
  #print-root .plav-item{display:flex;align-items:baseline;gap:9px;font-size:16.5px;font-weight:700;color:#1d211e;}
  #print-root .plav-item small{font-weight:700;color:#005C17;font-size:13px;margin-left:2px;}
  #print-root .plav-n{flex:0 0 auto;min-width:22px;font-weight:800;color:#003C0F;font-size:16.5px;}
  #print-root .plav-t{flex:1;}
  #print-root .plav-none{font-size:16px;color:#637768;font-style:italic;}
  #print-root .pqty{flex:1;display:flex;flex-direction:column;gap:10px;margin-bottom:0;}
  #print-root .ptile{background:#fff;border:1px solid #dfe7da;border-radius:11px;padding:13px 18px;display:flex;align-items:center;justify-content:space-between;gap:16px;white-space:nowrap;overflow:hidden;}
  #print-root .ptnum{font-size:28px;font-weight:800;color:#003C0F;line-height:1;white-space:nowrap;}
  #print-root .ptunit{font-size:14px;color:#637768;font-weight:700;margin-left:6px;white-space:nowrap;}
  #print-root .ptdual{display:flex;align-items:flex-end;gap:14px;}
  #print-root .ptdual .ptnum{font-size:20px;white-space:nowrap;}
  #print-root .ptcol{display:flex;flex-direction:column;gap:3px;}
  #print-root .ptsub{font-size:9px;letter-spacing:1px;text-transform:uppercase;color:#637768;font-weight:700;}
  #print-root .ptsep{width:1px;align-self:stretch;background:#C6DCCB;margin-bottom:2px;}
  /* Tracciabilità lavorazione (SOLO copia interna): pannello verticale con
     tutte le fasi della catena (rasatura → tintura → rifinizioni). Compatto:
     ogni copia .pg deve restare in UNA pagina A4. */
  #print-root .pchainbox{border:1px solid #e4e8e3;border-left:6px solid #6b3fa0;border-radius:12px;padding:8px 16px 9px 18px;margin-bottom:9px;}
  #print-root .pchainbox h3{font-size:11px;letter-spacing:2.5px;text-transform:uppercase;color:#6b3fa0;font-weight:800;margin-bottom:5px;}
  #print-root .pchrow{display:flex;align-items:baseline;gap:9px;font-size:11.5px;padding:2.5px 0;border-bottom:1px dashed #eef1ee;}
  #print-root .pchrow:last-child{border-bottom:0;}
  #print-root .pchico{flex:0 0 auto;font-size:10.5px;}
  #print-root .pchnum{font-weight:800;color:#003C0F;min-width:64px;white-space:nowrap;}
  #print-root .pchrow.cur .pchnum{color:#005C17;}
  #print-root .pchlab{flex:1;color:#38443B;}
  #print-root .pchlab b{color:#003C0F;}
  #print-root .pchst{font-size:8.5px;letter-spacing:.8px;text-transform:uppercase;font-weight:800;color:#637768;white-space:nowrap;}
  #print-root .pchst.cur{color:#fff;background:#003C0F;border-radius:999px;padding:2px 9px;}
  /* Ripartizione su più clienti: SOLO copia interna (il terzista riceve i totali). */
  #print-root .pripbox{border:1px solid #e4e8e3;border-left:6px solid #1A6B2E;border-radius:12px;padding:8px 16px 9px 18px;margin-bottom:9px;}
  #print-root .pripbox h3{font-size:11px;letter-spacing:2.5px;text-transform:uppercase;color:#005C17;font-weight:800;margin-bottom:5px;}
  #print-root .priptab{width:100%;border-collapse:collapse;font-size:11.5px;}
  #print-root .priptab th{font-size:8.5px;letter-spacing:.8px;text-transform:uppercase;color:#637768;font-weight:800;text-align:left;padding:0 6px 3px;border-bottom:1px solid #dfe4df;}
  #print-root .priptab td{padding:3px 6px;border-bottom:1px dashed #eef1ee;color:#252D27;}
  #print-root .priptab td:first-child{font-weight:800;color:#003C0F;}
  #print-root .priptab .n{text-align:right;white-space:nowrap;font-variant-numeric:tabular-nums;}
  #print-root .priptab tr.pript td{border-bottom:0;border-top:1px solid #cfd8d1;font-weight:800;}
  #print-root .pripref{display:block;font-size:9px;font-weight:600;color:#637768;letter-spacing:.2px;}
  /* Composizione crust (mix): SOLO copia interna, come selezione e spessore —
     sono dati d'acquisto nostri, non istruzioni per il rifinitore. Filetto tan a
     sinistra (verde = clienti, ambra = integrazioni, viola = catena, tan =
     materiale) e fondo BIANCO: niente campiture nei PDF. Nessuna colonna €/m²:
     i costi d'acquisto non si stampano (scelta dell'utente). */
  #print-root .pmixbox{border:1px solid #e4e8e3;border-left:6px solid #9a6f2c;border-radius:12px;
    padding:8px 16px 9px 18px;margin-bottom:9px;background:#fff;}
  #print-root .pmixbox h3{font-size:11px;letter-spacing:2.5px;text-transform:uppercase;color:#8a6516;font-weight:800;margin-bottom:5px;}
  #print-root .pmixbar{display:flex;height:7px;border-radius:4px;overflow:hidden;border:1px solid #e0d3b4;margin:4px 0 6px;}
  #print-root .pmixbar i{display:block;height:100%;}
  #print-root .pmixtab{width:100%;border-collapse:collapse;font-size:11.5px;}
  #print-root .pmixtab th{font-size:8.5px;letter-spacing:.8px;text-transform:uppercase;color:#637768;font-weight:800;
    text-align:left;padding:0 6px 3px;border-bottom:1px solid #dfe4df;}
  #print-root .pmixtab td{padding:3px 6px;border-bottom:1px dashed #eef1ee;color:#252D27;}
  #print-root .pmixtab td:first-child{font-weight:800;color:#003C0F;}
  #print-root .pmixtab .n{text-align:right;white-space:nowrap;font-variant-numeric:tabular-nums;}
  #print-root .pmixtab tr.t td{border-bottom:0;border-top:1px solid #cfd8d1;font-weight:800;}
  #print-root .pmixnote{font-size:9.5px;color:#8a6516;margin-top:3px;line-height:1.3;}
  /* Nota in fascia: la cella Selezione porta la variante del campo «Variante»,
     che con un mix da sola direbbe una composizione che non è quella vera. */
  #print-root .pbmix{display:block;font-size:9px;font-weight:700;color:#8a6516;margin-top:3px;line-height:1.25;}
  /* Integrazioni ordine: sotto-nota «di cui …» nei tile quantità + box storico
     (entrambe le copie: il terzista deve vedere cosa è stato aggiunto). */
  #print-root .ptint{font-size:9px;font-weight:800;color:#9a6f2c;margin-top:3px;letter-spacing:.4px;white-space:nowrap;}
  #print-root .pintbox{border:1px solid #e6cf9a;background:#fdf9ef;border-radius:12px;padding:8px 16px 9px 18px;margin-bottom:9px;}
  #print-root .pintbox h3{font-size:11px;letter-spacing:2.5px;text-transform:uppercase;color:#9a6f2c;font-weight:800;margin-bottom:5px;}
  #print-root .pintrow{display:flex;align-items:baseline;gap:12px;font-size:11.5px;padding:2px 0;}
  #print-root .pintrow b{color:#8a6516;white-space:nowrap;}
  #print-root .pintnote{color:#637768;font-style:italic;}
  #print-root .pintint{color:#8a6516;font-weight:700;font-style:normal;}
  #print-root .pintmot{font-size:9px;font-weight:800;text-transform:uppercase;letter-spacing:.5px;color:#8a6516;border:1px solid #e6cf9a;border-radius:999px;padding:0 6px;white-space:nowrap;}
  #print-root .pintsum{font-size:8.5px;color:#9a6f2c;font-weight:700;margin-top:4px;letter-spacing:.4px;text-transform:uppercase;}
  #print-root .pnote{border:1px solid #e4e8e3;border-left:6px solid #b98a3e;border-radius:12px;padding:11px 18px 12px 20px;margin-bottom:9px;min-height:13mm;}
  #print-root .pnote h3{font-size:12px;letter-spacing:2.5px;text-transform:uppercase;color:#9a6f2c;font-weight:800;margin-bottom:8px;}
  #print-root .pnote p{font-size:17px;line-height:1.55;font-weight:700;white-space:pre-wrap;}
  #print-root .pnote-crust{border-left-color:#005C17;min-height:auto;}
  #print-root .pnote-crust h3{color:#005C17;}
  #print-root .pfoot{margin-top:auto;border-top:1px solid #e4e8e3;padding-top:10px;display:flex;justify-content:space-between;align-items:flex-end;}
  #print-root .pco{font-size:11px;line-height:1.5;color:#637768;}
  #print-root .pco b{color:#1d211e;font-size:12.5px;}
  #print-root .pbadge{font-size:11px;letter-spacing:2px;text-transform:uppercase;font-weight:800;color:#003C0F;background:#fff;border:1.5px solid #003C0F;padding:6px 13px;border-radius:999px;}
  #print-root .pbadge.tan{color:#9a6f2c;background:#fff;border-color:#9a6f2c;}
  #print-root .ph-r{display:flex;align-items:center;gap:14px;}

  /* ── Scheda di rifinizione, layout a fasce (.pg2) ──────────────────────
     Le tre righe dati diventano fasce continue divise da filetti: la stessa
     informazione delle vecchie .pcard in ~11mm invece di 26, che è ciò che
     rende stampabile una scheda con integrazioni + ripartizione + catena.
     Tutte le classi qui sono NUOVE e vivono sotto .pg2: i PDF di tintura,
     rasatura e il foglio integrazione riusano .pcard/.plab/.pval/.g4 e NON
     devono cambiare aspetto. */
  #print-root .pg2 .plogo{height:76px;}
  #print-root .pg2 .ph{padding-bottom:5px;}
  #print-root .pg2 .pnum{font-size:26px;}
  #print-root .pg2 .pkick{font-size:10px;}
  #print-root .pg2 .prule{margin:4px 0 8px;}
  #print-root .pband{display:grid;border:1px solid #e0e6df;border-radius:10px;background:#fff;overflow:hidden;margin-bottom:6px;}
  #print-root .pbc{padding:6px 12px 7px;border-left:1px solid #e0e6df;min-width:0;}
  #print-root .pbc:first-child{border-left:0;}
  #print-root .pbl{font-size:8px;letter-spacing:.9px;text-transform:uppercase;color:#637768;font-weight:800;line-height:1.2;}
  #print-root .pbv{font-size:13.5px;font-weight:800;color:#1d211e;margin-top:2px;line-height:1.2;overflow-wrap:anywhere;}
  #print-root .pbc.green .pbv{color:#003C0F;font-size:15px;}
  /* Consegna prevista (solo copia interna): il 📌 segnala che la data è stata
     decisa, non calcolata; il motivo va a capo sotto, piccolo. Niente
     campitura — sui PDF i fondi colorati sono stati tolti apposta. */
  #print-root .pbpin{font-size:10px;}
  #print-root .pbmot{font-size:8.5px;font-weight:700;color:#8a6516;line-height:1.25;margin-top:1px;overflow-wrap:anywhere;}
  /* Grezzo di partenza sotto il colore crust — SOLO copia interna (vedi pdfBandRows) */
  #print-root .pbfrom{font-size:8.5px;font-weight:700;color:#56665A;line-height:1.2;margin-top:1px;overflow-wrap:anywhere;}
  #print-root .pbc.lotto{background:#fff;}
  #print-root .pbc.lotto .pbv{color:#003C0F;font-variant-numeric:tabular-nums;}
  /* cliente: cornice, non campitura — si vede al primo colpo d'occhio senza
     bruciare inchiostro. Il padding è ridotto quanto il bordo aggiunge, così
     la cella resta alta come le altre e la fascia non si alza. */
  #print-root .pbc.acc{background:#fff;border:1.8px solid #003C0F;border-radius:8px;padding:4.2px 10.2px 5.2px;}
  #print-root .pbc.acc .pbl{color:#005C17;}
  #print-root .pbc.acc .pbv{color:#003C0F;font-size:15px;}
  #print-root .pbdest,#print-root .pbras{display:inline-block;margin-top:3px;font-size:8px;letter-spacing:.8px;text-transform:uppercase;font-weight:800;color:#003C0F;background:#fff;border:1px solid #C6DCCB;padding:1px 6px;border-radius:999px;}
  /* il chip "rasato" sta su una riga sua sotto il valore (ogni campo nuovo
     comincia a capo), su una riga sola: la sua colonna è allargata da
     pdfBandRows quanto basta. Il margine negativo compensa bordo + padding
     della pillola, così il TESTO del chip cade esattamente sotto il valore
     invece di sembrare rientrato. */
  #print-root .pbc .pbras{white-space:nowrap;margin-left:-7px;}
  /* banda colore: stessa evidenza di prima in metà altezza */
  #print-root .pcolband{display:flex;align-items:center;gap:16px;border:2px solid #003C0F;border-radius:10px;padding:6px 16px;margin-bottom:6px;}
  #print-root .pcolband .chlab{font-size:9px;letter-spacing:2.4px;flex:0 0 auto;}
  #print-root .pcolband .chval{font-size:27px;margin-top:0;flex:1;line-height:1.1;}
  #print-root .pcolband .chbadge{flex:0 0 auto;}
  /* quantità in striscia orizzontale (prima erano una colonna mezza vuota) */
  #print-root .pqstrip{display:grid;gap:6px;margin-bottom:6px;}
  #print-root .pqc{background:#fff;border:1px solid #dfe7da;border-radius:9px;padding:6px 13px;display:flex;align-items:center;justify-content:space-between;gap:10px;}
  #print-root .pql{font-size:8.5px;letter-spacing:.9px;text-transform:uppercase;color:#637768;font-weight:800;}
  #print-root .pqint{font-size:8px;font-weight:800;color:#9a6f2c;margin-top:1px;}
  #print-root .pqn{font-size:22px;font-weight:800;color:#003C0F;white-space:nowrap;}
  #print-root .pqn.sm{font-size:16px;}
  #print-root .pqdual{display:flex;align-items:flex-end;gap:10px;}
  #print-root .pqsub{font-size:7.5px;letter-spacing:.9px;text-transform:uppercase;color:#637768;font-weight:800;}
  #print-root .pqsep{width:1px;align-self:stretch;background:#C6DCCB;}
  /* corpo a due colonne: si attiva solo quando ci sono i pannelli */
  #print-root .pbody2{display:grid;grid-template-columns:1.15fr 1fr;gap:8px;align-items:start;}
  #print-root .pcol2{min-width:0;}
  /* nella colonna stretta le celle quantità vanno impilate: affiancate, i
     numeri (nowrap) uscirebbero dal bordo destro */
  #print-root .pbody2 .pqstrip{grid-template-columns:1fr !important;}
  #print-root .pg2 .ppanel{padding:8px 14px 9px 16px;margin-bottom:6px;}
  #print-root .pg2 .ppanel h3{font-size:10.5px;letter-spacing:2px;margin-bottom:6px;}
  #print-root .pg2 .plav{display:flex;flex-direction:column;gap:5px;}
  #print-root .pg2 .plav.lav2{display:block;column-count:2;column-gap:18px;}
  #print-root .pg2 .plav-item{font-size:14px;gap:7px;break-inside:avoid;margin-bottom:5px;}
  #print-root .pg2 .plav-item small{font-size:11px;}
  #print-root .pg2 .plav-n{font-size:14px;min-width:17px;}
  #print-root .pbody2 .plav{column-count:1 !important;}
  #print-root .pg2 .pnote{padding:7px 14px 8px 16px;margin-bottom:6px;min-height:0;}
  #print-root .pg2 .pnote h3{font-size:10.5px;letter-spacing:2px;margin-bottom:4px;}
  #print-root .pg2 .pnote p{font-size:13.5px;line-height:1.4;}
  #print-root .pg2 .pintbox,#print-root .pg2 .pripbox,#print-root .pg2 .pchainbox,#print-root .pg2 .pmixbox{padding:6px 14px 7px 16px;margin-bottom:6px;}
  #print-root .pg2 .pintbox h3,#print-root .pg2 .pripbox h3,#print-root .pg2 .pchainbox h3,#print-root .pg2 .pmixbox h3{font-size:10px;margin-bottom:3px;}
  /* ogni campo dell'integrazione su una riga sua, con l'etichetta a sinistra
     incolonnata: in fila separati da puntini si leggevano come un blocco unico */
  #print-root .pg2 .pintrow{display:block;font-size:10.5px;padding:2px 0 3px;line-height:1.4;border-top:1px dashed #ecdfc2;}
  #print-root .pg2 .pintrow:first-of-type{border-top:0;}
  #print-root .pg2 .pinth{display:flex;align-items:baseline;gap:7px;flex-wrap:wrap;margin-bottom:1px;}
  #print-root .pg2 .pintf{display:grid;grid-template-columns:62px 1fr;gap:7px;align-items:baseline;}
  #print-root .pg2 .pintk{font-size:8px;letter-spacing:.7px;text-transform:uppercase;color:#a98c52;font-weight:800;padding-top:1px;}
  #print-root .pg2 .pintv{color:#38443B;}
  #print-root .pg2 .pintv.pintint{color:#8a6516;font-weight:700;}
  #print-root .pg2 .pintmot{padding:0 5px;}
  #print-root .pg2 .priptab,#print-root .pg2 .pmixtab{font-size:10.5px;}
  #print-root .pbody2 .priptab th,#print-root .pbody2 .priptab td,#print-root .pbody2 .pmixtab th,#print-root .pbody2 .pmixtab td{padding:2px 4px;}
  /* tracciabilità: un campo per riga, stesso schema del box integrazioni */
  #print-root .pg2 .pchrow{display:block;font-size:10.5px;padding:2px 0 3px;}
  #print-root .pg2 .pchh{display:flex;align-items:baseline;gap:6px;flex-wrap:wrap;}
  #print-root .pg2 .pchf{display:grid;grid-template-columns:62px 1fr;gap:7px;align-items:baseline;}
  #print-root .pg2 .pchk{font-size:8px;letter-spacing:.7px;text-transform:uppercase;color:#9b86c0;font-weight:800;padding-top:1px;}
  #print-root .pg2 .pchv{color:#38443B;}
  #print-root .pg2 .pfoot{padding-top:7px;}
  #print-root .pg2 .pco{font-size:10px;}

  /* ── Copia TERZISTA: il foglio di reparto ─────────────────────────────
     Porta due fasce invece di tre (via selezione e spessore, dati d'acquisto
     nostri — vedi pdfBandRows) e nessuno dei pannelli riservati (cliente,
     ripartizione, tracciabilità, QR): su una scheda normale restavano ~12cm
     di foglio bianco. Non si comprime — di spazio ne avanza — si SPENDE su
     ciò che il rifinitore legge in piedi al banco: valori delle fasce,
     colore, quantità e lavorazioni più grandi, e il bianco che resta va al
     riquadro Note, dove annota a mano difetti e pelli scartate.
     Nessuna di queste regole tocca la copia interna (.pg-int), che resta
     densa perché deve reggere ripartizione + integrazioni + catena. */
  #print-root .pg-terz .pbc{padding:8px 13px 9px;}
  #print-root .pg-terz .pbl{font-size:8.5px;}
  #print-root .pg-terz .pbv{font-size:16px;}
  #print-root .pg-terz .pbc.green .pbv{font-size:18px;}
  #print-root .pg-terz .pbdest{font-size:8.5px;margin-top:4px;}
  #print-root .pg-terz .pcolband{padding:11px 18px;}
  #print-root .pg-terz .pcolband .chval{font-size:34px;}
  #print-root .pg-terz .pqc{padding:10px 16px;}
  #print-root .pg-terz .pqn{font-size:28px;}
  #print-root .pg-terz .pql{font-size:9px;}
  #print-root .pg-terz .ppanel{padding:10px 16px 11px 18px;}
  #print-root .pg-terz .plav-item{font-size:16px;margin-bottom:7px;}
  #print-root .pg-terz .plav-n{font-size:16px;min-width:19px;}
  #print-root .pg-terz .plav-item small{font-size:12.5px;}
  #print-root .pg-terz .pnote p{font-size:15px;line-height:1.5;}
  /* Le note si allargano nel bianco che avanza — è lì che il rifinitore
     annota — ma con un tetto: presa TUTTA la mezza pagina, il riquadro con
     dentro una riga di testo sembra un errore di impaginazione. `flex:1 0
     auto` (non `1 1`) perché su una scheda carica non debba stringersi sul
     proprio testo. Vale solo per la .pnote figlia DIRETTA di .pg: col corpo a
     due colonne (scheda con integrazioni) sta dentro .pcol2 e non cresce. */
  #print-root .pg-terz > .pnote{flex:1 0 auto;max-height:70mm;}

  /* ── Etichetta stampabile (mezza A4 / A5) ───────────────────────── */
  .label-a5{background:#fff;border:1.5px solid #c8d0da;border-radius:6px;overflow:hidden;font-family:-apple-system,Arial,sans-serif;color:#1b2430;font-size:13px;box-shadow:0 8px 32px rgba(0,0,0,.18);}
  .label-hdr{background:#003C0F;color:#fff;padding:12px 18px;display:flex;align-items:center;justify-content:space-between;gap:12px;}
  .label-hdr .label-company{font-size:13px;font-weight:800;letter-spacing:.4px;}
  .label-hdr .label-doctype{font-size:9px;letter-spacing:2.5px;text-transform:uppercase;opacity:.72;font-weight:700;text-align:right;}
  .label-top{display:flex;align-items:flex-start;gap:18px;padding:14px 18px 12px;border-bottom:2px solid #003C0F;background:#f8faf8;}
  .label-meta{flex:1;min-width:0;padding-top:4px;}
  .label-num{font-size:32px;font-weight:900;letter-spacing:-1.5px;color:#003C0F;line-height:1;margin-bottom:6px;}
  .label-metarow{font-size:12.5px;color:#6e7d8c;margin-bottom:3px;}
  .label-metarow b{color:#1b2430;}
  .label-colflow{display:flex;align-items:center;gap:10px;padding:10px 18px;background:#f3f6f3;border-bottom:1px solid #dce5dc;}
  .lcf-from{font-size:13px;font-weight:700;color:#9aa6b2;text-transform:uppercase;letter-spacing:.5px;}
  .lcf-arrow{font-size:18px;color:#9aa6b2;font-weight:800;}
  /* ⚠️ Il colore ottenuto era l'UNICO punto dei documenti scritto con
     var(--green): qui non va, ed è diverso dal caso del report (dove la
     variabile è dentro @media print e quindi vale sempre il verde chiaro-tema).
     L'etichetta si costruisce in `#label-modal-area`, che sta nel DOM dell'app
     anche a video: in tema scuro var(--green) diventa il verde CHIARO e finiva
     sul fondo #f3f6f3 fisso di .label-colflow, cioè illeggibile. Letterale. */
  .lcf-to{font-size:18px;font-weight:900;color:#003C0F;text-transform:uppercase;letter-spacing:.5px;}
  .label-body{padding:0 18px 6px;}
  .lb-row{display:flex;align-items:baseline;gap:10px;border-bottom:1px solid #f0f3f5;padding:5px 0;}
  .lb-k{font-size:9.5px;font-weight:800;text-transform:uppercase;letter-spacing:.9px;color:#9aa6b2;width:120px;flex-shrink:0;padding-top:2px;}
  .lb-v{flex:1;font-size:13px;font-weight:700;color:#1b2430;word-break:break-word;}
  .lb-note-row{align-items:flex-start;}
  .lb-note-v{font-size:12px;font-weight:600;white-space:pre-wrap;}
  .lb-sep{height:1px;background:#d0d8e4;margin:4px 0;}
  .lb-badge{font-size:9px;background:#f0f2f5;color:#9aa6b2;padding:1px 6px;border-radius:99px;font-weight:700;text-transform:uppercase;letter-spacing:.4px;margin-left:7px;vertical-align:middle;}
  .label-url{padding:7px 18px 0;font-size:10px;color:#c0c8d4;word-break:break-all;}
  .label-foot{background:#f5f7fa;padding:7px 18px;font-size:10px;color:#9aa6b2;border-top:1px solid #e4e8ec;text-align:center;margin-top:8px;}

  /* ── Stampa etichetta su A5 — attiva SOLO con body.print-label, così le
        copie PDF terzista/interna restano su A4 (regola @page sopra). ──── */
  @media print{
    body.print-label>*:not(#label-modal){display:none!important;}
    body.print-label #print-root{display:none!important;}
    body.print-label #label-modal{display:block!important;position:static!important;background:none!important;padding:0!important;overflow:visible!important;}
    body.print-label #label-modal .label-modal-controls,
    body.print-label #label-modal .label-modal-hint{display:none!important;}
    body.print-label #label-modal>div{padding:0!important;max-width:none!important;}
    body.print-label #label-modal-area{display:block!important;}
    body.print-label #label-modal-area .label-a5{border:none!important;border-radius:0!important;box-shadow:none!important;width:100%!important;font-size:14px!important;page:label-page;break-after:page;}
    body.print-label #label-modal-area .label-a5:last-child{break-after:auto;}
    body.print-label #label-modal-area .label-num{font-size:36px!important;}
  }
  @page label-page{size:A5 portrait;margin:8mm;}

  /* ── Colori crust: griglia card ───────────────────────────────── */
  .colc-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:14px;margin-top:10px;}
  .colc-fgroup{margin-top:22px;}
  .colc-fgroup:first-child{margin-top:6px;}
  .colc-fgroup-head{display:flex;align-items:center;gap:9px;font-size:14px;font-weight:800;color:var(--green);padding:7px 2px;border-bottom:2px solid var(--green2);}
  .colc-fgroup-head--none{color:var(--muted);border-bottom-color:var(--border);}
  .colc-fgroup-count{margin-left:auto;background:var(--green);color:#fff;border-radius:999px;padding:1px 10px;font-size:12px;font-weight:800;}
  .colc-card{background:var(--card);border:1.5px solid var(--border);border-radius:12px;padding:16px;display:flex;flex-direction:column;gap:8px;transition:box-shadow var(--d-fast);}
  .colc-card:hover{box-shadow:0 4px 18px rgba(0,0,0,.1);}
  .colc-card-name{font-size:17px;font-weight:900;color:var(--green);letter-spacing:.3px;line-height:1.1;}
  .colc-card-forn{font-size:12px;color:var(--muted);font-weight:600;line-height:1.4;}
  .colc-card-forn b{color:var(--ink);}
  .colc-card-lots{font-size:11.5px;color:var(--green);font-weight:700;background:var(--soft);border:none;border-radius:6px;padding:4px 9px;align-self:flex-start;cursor:pointer;text-align:left;transition:background var(--d-fast);display:flex;flex-direction:column;gap:1px;}
  .colc-card-split{font-size:10.5px;font-weight:600;color:var(--muted);letter-spacing:0;}
  button.colc-card-lots:hover{background:var(--soft-bd);}
  .colc-card-lots--empty{color:var(--muted);background:transparent;padding:2px 0;cursor:default;font-style:italic;}
  .colc-card-acts{display:flex;gap:6px;margin-top:4px;flex-wrap:wrap;align-items:center;}
  /* Azioni rapide (icona) nelle card crust: duplica / elimina */
  .colc-card-iact{width:32px;height:32px;display:inline-flex;align-items:center;justify-content:center;border:1px solid var(--line2);background:var(--panel);color:var(--muted);border-radius:8px;cursor:pointer;font-size:14px;line-height:1;transition:background var(--d-fast) var(--ease),border-color var(--d-fast) var(--ease),color var(--d-fast) var(--ease),transform var(--d-fast) var(--ease);}
  .colc-card-iact:hover{border-color:var(--green2);color:var(--ink);}
  .colc-card-iact--del:hover{border-color:var(--red);color:var(--red);background:#fbeaea;}
  /* Interruttore vista Griglia / Elenco (pagina Gestione crust) */
  .colc-view-seg{display:inline-flex;background:var(--panel);border:1px solid var(--line2);border-radius:var(--r-sm);padding:3px;gap:3px;box-shadow:var(--sh-sm);}
  .colc-view-seg button{border:none;background:none;font:inherit;font-size:13px;font-weight:700;color:var(--muted);padding:8px 13px;border-radius:7px;cursor:pointer;white-space:nowrap;transition:background var(--d-fast) var(--ease),border-color var(--d-fast) var(--ease),color var(--d-fast) var(--ease),transform var(--d-fast) var(--ease);}
  .colc-view-seg button.on{background:var(--green);color:#fff;}
  /* Vista elenco: tabella compatta con campi allineati */
  .colc-lgroup{margin-top:22px;} .colc-lgroup:first-child{margin-top:6px;}
  .colc-ltable-wrap{overflow-x:auto;margin-top:8px;border:1px solid var(--border);border-radius:12px;background:var(--card);-webkit-overflow-scrolling:touch;}
  .colc-ltable{border-collapse:collapse;width:100%;min-width:820px;font-size:13px;}
  .colc-ltable thead th{background:var(--soft);color:var(--muted);font-size:10.5px;text-transform:uppercase;letter-spacing:.5px;font-weight:800;text-align:left;padding:9px 12px;border-bottom:1px solid var(--border);white-space:nowrap;}
  .colc-ltable th.colc-lnum,.colc-ltable td.colc-lnum{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap;}
  .colc-ltable tbody tr{border-bottom:1px solid var(--line);transition:background var(--d-fast);}
  .colc-ltable tbody tr:last-child{border-bottom:none;}
  @media(hover:hover){ .colc-ltable tbody tr:hover{background:var(--soft);} }
  .colc-ltable td{padding:8px 12px;vertical-align:middle;}
  .colc-lname{font-weight:800;color:var(--ink);white-space:nowrap;}
  .colc-lmuted{color:var(--muted2);}
  .colc-lcorso{display:inline-block;min-width:22px;text-align:center;background:var(--soft);color:var(--green);border-radius:6px;padding:1px 7px;font-weight:800;}
  .colc-lacts{text-align:right;white-space:nowrap;}
  .colc-iact{width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center;border:1px solid var(--line2);background:var(--panel);color:var(--muted);border-radius:8px;cursor:pointer;font-size:13px;line-height:1;margin-left:3px;transition:background var(--d-fast) var(--ease),border-color var(--d-fast) var(--ease),color var(--d-fast) var(--ease),transform var(--d-fast) var(--ease);}
  .colc-iact:hover{border-color:var(--green2);color:var(--ink);}
  .colc-iact--del:hover{border-color:var(--red);color:var(--red);background:#fbeaea;}
  /* Pagina Selezioni & Spessori */
  .ssp-block{margin-top:20px;} .ssp-block:first-child{margin-top:4px;}
  .ssp-head{display:flex;align-items:center;gap:9px;font-size:14px;font-weight:800;color:var(--ink);padding:7px 2px;border-bottom:2px solid var(--green2);margin-bottom:12px;}
  .ssp-count{margin-left:auto;background:var(--green);color:#fff;border-radius:999px;padding:1px 10px;font-size:12px;font-weight:800;}
  .ssp-chips{display:flex;flex-wrap:wrap;gap:8px;}
  .ssp-chip{display:inline-flex;align-items:center;gap:6px;background:var(--card);border:1.5px solid var(--border);border-radius:10px;padding:6px 8px 6px 12px;font-size:13px;font-weight:700;color:var(--ink);}
  .ssp-btn{width:26px;height:26px;display:inline-flex;align-items:center;justify-content:center;border:1px solid var(--line2);background:var(--panel);color:var(--muted);border-radius:7px;cursor:pointer;font-size:12px;line-height:1;transition:background var(--d-fast) var(--ease),border-color var(--d-fast) var(--ease),color var(--d-fast) var(--ease),transform var(--d-fast) var(--ease);}
  .ssp-btn:hover{border-color:var(--green2);color:var(--ink);}
  .ssp-del:hover{border-color:var(--red);color:var(--red);background:#fbeaea;}
  .ssp-empty{color:var(--muted);font-style:italic;font-size:13px;}
  /* Valori non più in elenco: eccezione da sanare, quindi ambra (come le altre
     segnalazioni del gestionale) e in cima alla pagina. */
  .ssp-orfbox{margin-top:4px;border:1.5px solid #e8c07a;background:#fdf7ec;border-radius:12px;padding:12px 14px 14px;}
  .ssp-orfbox .ssp-head{border-bottom-color:#e0ad55;margin-bottom:8px;padding-top:0;}
  .ssp-orfbox .ssp-count{background:#8f5f0d;}
  .ssp-warn{font-size:12.5px;color:#7a5a1c;line-height:1.45;margin-bottom:11px;}
  .ssp-chip.orf{border-color:#e0ad55;background:#fff;}
  .ssp-orfn{font-size:11.5px;font-weight:700;color:#6f5112;background:#f6e7c9;border-radius:999px;padding:1px 8px;}

  /* ── Overlay "schede che usano un crust" (risultato scansione QR) ── */
  .csm-box{background:#fff;width:100%;max-width:600px;margin:24px auto;border-radius:14px;display:flex;flex-direction:column;max-height:calc(100vh - 48px);box-shadow:0 24px 60px rgba(0,0,0,.3);overflow:hidden;}
  .csm-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:15px 18px;border-bottom:1px solid var(--border);background:#fff;flex:0 0 auto;}
  .csm-head h3{margin:0;font-size:16px;font-weight:800;color:var(--green);line-height:1.3;}
  /* azioni del lotto nella testata fissa della modale movimentazioni (logica
     no-scroll: i comandi non stanno mai dentro il contenuto che scorre) */
  .lmov-acts{display:flex;gap:7px;flex:0 0 auto;align-items:center;flex-wrap:wrap;justify-content:flex-end;}
  /* scorciatoie In corso/Complete nella testata fissa della modale schede-crust */
  .csm-jumps{display:flex;gap:6px;margin-top:6px;flex-wrap:wrap;}
  .csm-jumps:empty{display:none;}
  .csm-jump{font:inherit;font-size:11.5px;font-weight:800;border-radius:999px;padding:4px 11px;border:1px solid var(--soft-bd);background:var(--soft);color:var(--green2);cursor:pointer;}
  .csm-jump:hover{border-color:var(--green2);}
  .csm-list{overflow-y:auto;padding:10px 14px 16px;-webkit-overflow-scrolling:touch;}
  .csm-row{display:flex;align-items:center;gap:10px;padding:12px 13px;border:1px solid var(--border);border-radius:11px;margin-top:9px;cursor:pointer;transition:background var(--d-fast);}
  .csm-row:first-child{margin-top:4px;}
  .csm-row:active{background:var(--sidebar);}
  @media(hover:hover){ .csm-row:hover{background:var(--sidebar);} }
  .csm-row-main{flex:1;min-width:0;}
  .csm-row-top{display:flex;align-items:center;gap:8px;flex-wrap:wrap;}
  .csm-num{font-weight:800;color:var(--green);font-size:15px;}
  .csm-row-sub{font-size:13.5px;font-weight:700;margin-top:4px;color:var(--ink);}
  .csm-row-lavs{font-size:12px;color:#1e56a0;margin-top:3px;font-weight:600;}
  .csm-row-meta{font-size:11.5px;color:var(--muted);margin-top:3px;}
  .csm-arrow{font-size:24px;color:var(--muted);flex:0 0 auto;line-height:1;}
  .csm-section{margin-top:14px;}
  .csm-section:first-child{margin-top:0;}
  .csm-section-head{display:flex;align-items:center;gap:8px;font-size:12px;font-weight:800;text-transform:uppercase;letter-spacing:.04em;padding:6px 4px;border-bottom:2px solid var(--border);}
  .csm-section-head--open{color:#1e56a0;border-bottom-color:rgba(30,86,160,.25);}
  .csm-section-head--done{color:var(--green);border-bottom-color:var(--soft-bd);}
  .csm-section-count{margin-left:auto;background:var(--sidebar);border:1px solid var(--border);border-radius:999px;padding:1px 9px;font-size:11px;font-weight:800;color:var(--ink);letter-spacing:0;}
  .csm-subgroup{margin-top:10px;}
  .csm-subgroup-head{display:flex;align-items:center;gap:8px;font-size:12px;font-weight:700;padding:5px 9px;background:var(--sidebar);border:1px solid var(--border);border-radius:8px;margin-bottom:2px;}
  .csm-sg-forn{color:var(--ink);font-weight:800;}
  .csm-sg-tipo{color:var(--muted);font-weight:700;background:#fff;border:1px solid var(--border);border-radius:999px;padding:0 8px;font-size:11px;}

  /* ===== Banner "Installa l'app" (solo iPhone/iPad Safari) ===== */
  #ios-install-banner{
    position:fixed; left:12px; right:12px;
    bottom:calc(14px + env(safe-area-inset-bottom,0px));
    z-index:10000;
    background:var(--green); color:#fff;
    border-radius:16px; padding:14px 16px 16px;
    box-shadow:0 10px 34px rgba(0,0,0,.34);
    font-family:inherit;
    transform:translateY(140%); opacity:0;
    transition:transform var(--d-slow) var(--ease-out), opacity var(--d-slow) ease;
  }
  #ios-install-banner.iib-in{ transform:translateY(0); opacity:1; }
  #ios-install-banner .iib-row{ display:flex; align-items:center; gap:12px; }
  #ios-install-banner .iib-icon{
    width:46px; height:46px; border-radius:11px; flex:0 0 auto;
    box-shadow:0 2px 8px rgba(0,0,0,.25);
  }
  #ios-install-banner .iib-text{ display:flex; flex-direction:column; gap:3px; line-height:1.3; }
  #ios-install-banner .iib-text strong{ font-size:15px; font-weight:800; }
  #ios-install-banner .iib-text span{ font-size:13px; color:#dbe9df; }
  #ios-install-banner .iib-share{
    display:inline-flex; align-items:center; justify-content:center;
    width:20px; height:20px; vertical-align:-5px; color:#fff;
  }
  #ios-install-banner .iib-share svg{ width:16px; height:16px; }
  #ios-install-banner .iib-close{
    position:absolute; top:8px; right:10px;
    background:none; border:none; color:#BCD2C1;
    font-size:24px; line-height:1; cursor:pointer; padding:2px 6px;
  }
  #ios-install-banner .iib-close:active{ color:#fff; }
  #ios-install-banner .iib-arrow{
    display:flex; justify-content:center; color:#9FE0AF;
    margin-top:6px; animation:iib-bounce 1.3s ease-in-out infinite;
  }
  #ios-install-banner .iib-arrow svg{ width:26px; height:26px; }
  @keyframes iib-bounce{
    0%,100%{ transform:translateY(0); }
    50%{ transform:translateY(5px); }
  }
  /* ══════════ Ricerca globale (Ctrl+K) ══════════
     Righe multi-informazione + evidenziazione + navigazione da tastiera.
     Hover/attivo via classe CSS (la CSP blocca gli handler inline). */
  /* La barra è in DUE zone: le categorie scorrono di lato (sono tante e su
     telefono non ci stanno), il pulsante di ordinamento resta fisso a destra —
     dentro l'area che scorre finirebbe fuori schermo proprio dove serve. */
  .gs-scopebar{ display:flex;align-items:center;gap:8px;padding:8px 14px;border-bottom:1px solid var(--border);position:relative; }
  .gs-scopebar:empty{ display:none; }
  .gs-scopes{ display:flex;align-items:center;gap:6px;flex:1;min-width:0;overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none; }
  .gs-scopes::-webkit-scrollbar{ display:none; }
  .gs-scope{ flex-shrink:0;font-size:12px;font-weight:700;color:var(--muted);background:var(--soft);border:1px solid transparent;border-radius:999px;padding:4px 12px;cursor:pointer;white-space:nowrap;transition:background var(--d-fast),color var(--d-fast); }
  .gs-scope:hover{ color:var(--ink); }
  /* Fondo pieno = --green (verde di marca), non --green2: bianco su --green2
     dà 4,38:1, sotto il minimo di leggibilità. Vale anche per il chip Archivio. */
  .gs-scope.on{ color:#fff;background:var(--green);border-color:var(--green); }
  .gs-scope.arch{ background:transparent;border-color:var(--line2); }
  .gs-scope.arch.on{ background:var(--green);border-color:var(--green);color:#fff; }
  .gs-sep{ flex:0 0 auto;width:1px;height:18px;background:var(--line2);margin:0 2px; }
  /* Ordinamento: pulsante + menu (il menu è figlio della barra, che è
     position:relative e NON ritaglia — l'area che ritaglia è .gs-scopes) */
  .gs-sortbtn{ flex:0 0 auto;display:inline-flex;align-items:center;gap:6px;font:inherit;font-size:12px;font-weight:700;
    color:var(--ink);background:var(--panel);border:1px solid var(--line2);border-radius:999px;padding:5px 11px;cursor:pointer;white-space:nowrap; }
  .gs-sortbtn .cv{ font-size:9px;opacity:.65; }
  /* sul telefono l'etichetta si accorcia: a schermo stretto ogni pixel tolto al
     pulsante è un pixel in più per le categorie che scorrono accanto */
  .gs-sortbtn .sm{ display:none; }
  @media(max-width:640px){ .gs-sortbtn .lg{ display:none; } .gs-sortbtn .sm{ display:inline; } }
  @media(hover:hover){ .gs-sortbtn:hover{ border-color:var(--green2);color:var(--green2); } }
  .gs-sortmenu{ position:absolute;top:calc(100% + 6px);right:14px;z-index:20;width:250px;max-width:calc(100% - 28px);
    background:var(--card);border:1px solid var(--line2);border-radius:12px;box-shadow:var(--sh-lg);padding:5px;
    animation:dlgpop var(--d-fast) var(--ease-out);transform-origin:top right; }
  .gs-sortmenu[hidden]{ display:none; }
  .gs-smh{ font-size:10px;font-weight:800;letter-spacing:1.1px;text-transform:uppercase;color:var(--muted2);padding:8px 10px 4px; }
  .gs-smi{ display:flex;width:100%;align-items:center;gap:9px;padding:9px 10px;border:0;background:transparent;border-radius:8px;
    font:inherit;font-size:13.5px;font-weight:600;color:var(--ink);cursor:pointer;text-align:left; }
  .gs-smi .tick{ width:14px;flex:0 0 auto;color:var(--green2);font-weight:800; }
  .gs-smi.on{ background:var(--soft); }
  @media(hover:hover){ .gs-smi:hover{ background:var(--soft); } }
  .gs-smn{ font-size:11px;line-height:1.4;color:var(--muted);padding:7px 10px 8px;border-top:1px solid var(--line);margin-top:4px; }
  .gs-group{ margin-bottom:4px; }
  .gs-gtitle{ font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:1.2px;color:var(--muted);padding:8px 16px 6px;display:flex;align-items:center;gap:8px; }
  .gs-gtitle .gs-count{ background:var(--soft);color:var(--muted);border-radius:999px;padding:1px 7px;font-size:10px;font-weight:800;letter-spacing:.5px;font-variant-numeric:tabular-nums; }
  /* Il contatore dice il totale VERO e, se è più di quanto si vede, porta
     nella categoria: prima scriveva quanti risultati erano MOSTRATI, quindi
     un gruppo troncato non lo dichiarava affatto. */
  .gs-gtitle .gs-count.link{ color:var(--green);border:1px solid var(--line2);cursor:pointer; }
  @media(hover:hover){ .gs-gtitle .gs-count.link:hover{ background:var(--card);border-color:var(--green2); } }
  .gs-archnote{ cursor:pointer; }
  @media(hover:hover){ .gs-archnote:hover{ color:var(--ink); } }
  /* ⚠️ I risultati NON hanno più un'altezza propria (v19.11): la ricerca è una
     pagina e a scorrere è la .content, come in ogni altro elenco. Prima erano in
     una finestra e servivano due `max-height` (min(72vh, 100vh-200px), e 420px
     su telefono) per non farla uscire dallo schermo — un'altezza rimasta qui
     ritaglierebbe l'elenco a metà pagina, con il bianco sotto.
     Storico: erano 420px INLINE nell'index, cioè 4 righe a vista su qualunque
     monitor; poi legati allo schermo; ora è lo schermo. */
  #gs-results{ padding:4px 0; }
  /* La barra della ricerca vive nella .stickyhead della pagina, quindi la riga
     delle categorie non ha più il bordo della finestra sotto di sé ma sopra. */
  #view-ricerca .gs-scopebar{ border-bottom:none;border-top:1px solid var(--border);padding-left:2px;padding-right:2px; }
  /* La ← accanto al campo: su telefono è l'unica uscita a portata di pollice
     (la tastiera non ha ESC e la ✕ è dentro il campo). Sul computer resta ESC. */
  .gs-back{ flex:0 0 auto;font-size:16px;line-height:1;padding:9px 13px; }
  @media(min-width:641px){ .gs-back{ display:none; } }
  /* La ✕ del campo CHIUDE la ricerca e torna indietro — non svuota soltanto:
     cancellare il testo a mano lascia la pagina aperta sui recenti, o riscrivere
     una ricerca sarebbe impossibile (il titolo lo dice). */
  .gs-clear{ flex:0 0 auto;border:none;background:transparent;color:var(--muted);cursor:pointer;
    font-size:19px;line-height:1;padding:0 2px 2px;border-radius:6px; }
  @media(hover:hover){ .gs-clear:hover{ color:var(--ink); } }
  /* Dentro la pagina Ricerca la barra dell'appbar sparisce: sarebbe un SECONDO
     campo di ricerca a due dita dal primo, e non si capirebbe in quale scrivere.
     Resta la porta d'ingresso da tutte le altre pagine. Sul telefono la sua
     scomparsa lascia il posto al titolo «Ricerca», che dice dove sei. */
  .app.in-ricerca .gswrap{ display:none; }
  /* Stessa ragione dentro le IMPOSTAZIONI, che hanno la loro «Cerca
     un'impostazione…»: erano due campi a 73px l'uno dall'altro che fanno cose
     diverse. La regola c'era già dalla v19.79 ma stava dentro il blocco
     ≤640px, quindi valeva solo sul telefono — qui vale a ogni larghezza
     (v19.93). La ricerca globale resta su Ctrl+K e su ogni altra pagina. */
  .app.in-settings .gswrap{ display:none; }
  /* ⚠️ Su ≤640px il titolo dell'appbar è nascosto per far posto alla barra di
     ricerca (regola più su nel file): sparita la barra, senza questa riga
     l'appbar resterebbe VUOTA col solo «+». Sta qui, dopo quel blocco, così
     vince — stesso inciampo del dock e del titolo delle commesse. */
  .app.in-ricerca .appbar h1{ display:block; }
  .gs-item{ display:flex;align-items:flex-start;gap:12px;padding:9px 16px;cursor:pointer;border-radius:8px;transition:background var(--d-fast); }
  .gs-item:hover,.gs-item.gs-active{ background:var(--soft); }
  /* ⚠️ IL RISULTATO È LA CARD DELL'ELENCO (v19.97) — vedi _gsCard in app.8b.
     Il `.gs-item` resta il guscio cliccabile (lo cercano `_gsWireItems` e la
     navigazione da tastiera) ma qui NON deve imporre la sua geometria: dentro
     c'è una `.row`/`.arow` che deve venire identica a quella dell'elenco.
     Senza `display:block` la card diventa un FLEX ITEM e si dimensiona sul
     contenuto: misurato a 1440px, **538,6px invece di 1105** — metà larghezza.
     ⚠️ Su iPhone il difetto NON si vede (la riga è già a tutta larghezza): si
     misura sul computer, o una verifica passa verde davanti al difetto.
     ⚠️ E il fondo va a `none`: un `--soft` dietro una card opaca si vedrebbe
     solo nel padding, come un alone. L'evidenziazione della riga attiva la
     danno l'anello e l'ombra sulla card, come l'hover degli elenchi. */
  .gs-item.gsrow{ display:block;padding:0 16px 9px;background:none;border-radius:0; }
  .gs-item.gsrow>.row,.gs-item.gsrow>.arow,.gs-item.gsrow>.ordcard{ transition:box-shadow var(--d-fast),transform var(--d-fast); }
  .gs-item.gsrow.gs-active>.row,.gs-item.gsrow.gs-active>.arow,.gs-item.gsrow.gs-active>.ordcard{ box-shadow:var(--sh),0 0 0 2px var(--soft-bd); }
  @media(hover:hover){ .gs-item.gsrow:hover>.row,.gs-item.gsrow:hover>.arow,.gs-item.gsrow:hover>.ordcard{ box-shadow:var(--sh),0 0 0 2px var(--soft-bd); } }
  /* la corsia di stato è un box-shadow inset: va RIMESSA insieme all'anello, o
     l'anello la cancella proprio sulla riga che si sta guardando */
  .gs-item.gsrow.gs-active>.row.rst-bozza,.gs-item.gsrow:hover>.row.rst-bozza{ box-shadow:inset 4px 0 0 var(--st-bozza),var(--sh),0 0 0 2px var(--soft-bd); }
  .gs-item.gsrow.gs-active>.row.rst-lav,.gs-item.gsrow:hover>.row.rst-lav{ box-shadow:inset 4px 0 0 var(--st-lav),var(--sh),0 0 0 2px var(--soft-bd); }
  .gs-item.gsrow.gs-active>.row.rst-pronta,.gs-item.gsrow:hover>.row.rst-pronta{ box-shadow:inset 4px 0 0 var(--st-pronta),var(--sh),0 0 0 2px var(--soft-bd); }
  .gs-item.gsrow.gs-active>.row.rst-cons,.gs-item.gsrow:hover>.row.rst-cons{ box-shadow:inset 4px 0 0 var(--st-cons),var(--sh),0 0 0 2px var(--soft-bd); }
  .gs-item.gsrow.gs-active>.row.rst-trasf,.gs-item.gsrow:hover>.row.rst-trasf{ box-shadow:inset 4px 0 0 #8b5cf6,var(--sh),0 0 0 2px var(--soft-bd); }
  .gs-item.gsrow.gs-active>.row.rst-late,.gs-item.gsrow:hover>.row.rst-late{ box-shadow:inset 4px 0 0 var(--st-late),var(--sh),0 0 0 2px var(--soft-bd); }
  /* la pastiglia «esatta» sopra la card, non dentro: la card è la riga vera */
  .gs-exact{ padding:0 0 5px 4px; }
  /* ⚠️ LA CARD COMMESSA NELLA RICERCA (v19.102) è la card VERA della pagina
     Commesse senza la tabella delle voci (`_gsCardOrd`, app.8b), quindi due
     cose che là servivano qui vanno spente: il `border-bottom` della
     `.ordstats`, che separava la fascia dei numeri DALLA TABELLA e senza di lei
     resta un filetto appeso in fondo alla card; e il `margin-bottom` che nella
     pagina distanzia una card dall'altra, qui già dato dal padding del
     `.gs-item`. Senza la prima si vede una riga che non separa niente; senza la
     seconda i risultati commessa stanno 25px l'uno dall'altro contro i 9 di
     tutti gli altri. Il padding di fondo torna simmetrico: nella pagina è 6px
     perché sotto comincia la tabella. */
  .gs-item.gsrow>.ordcard{ margin-bottom:0;padding-bottom:13px; }
  .gs-item.gsrow>.ordcard .ordstats{ border-bottom:0;margin-bottom:0;padding-bottom:0; }
  .gs-item.gsrow>.ordcard .gs-why{ margin-top:9px; }
  .gs-ic{ font-size:18px;flex-shrink:0;line-height:1.35; }
  .gs-body{ min-width:0;flex:1; }
  .gs-title{ font-size:14px;font-weight:600;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis; }
  .gs-sub{ font-size:12px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:1px; }
  /* ⚠️ La DATA non deve sparire mai. Su iPhone la sottoriga chiede 318÷346px e
     ne ha 296 (254 con la dimensione testo grande), quindi l'ellissi mangiava
     la coda — cioè proprio il dato che dice QUANDO. Qui la riga si divide in
     due: `.mm` è ciò che può stringersi (cliente · terzista), `.dd` è ciò che
     non cede mai (stato e data). A troncare finisce il nome del terzista,
     l'unico della riga che si ritrova comunque nel Programma, nell'anteprima e
     nella scheda. Provato a 390 e 402px, con e senza testo grande: qualunque
     altra soluzione (data abbreviata, data spostata in testa) o non basta col
     testo grande o sposta il taglio sul chip dello stato. */
  .gs-sub.fx{ display:flex;align-items:baseline;overflow:hidden; }
  .gs-sub.fx .mm{ min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap; }
  /* ⚠️ `padding-left`, non lo spazio nel testo: all'inizio di un elemento flex
     lo spazio bianco viene collassato via, e si leggeva «RASATURE SUD· DA
     RASARE» tutto attaccato. */
  .gs-sub.fx .dd{ flex:0 0 auto;white-space:nowrap;padding-left:4px; }
  .gs-meta{ font-size:11.5px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:2px;opacity:.92; }
  .gs-why{ font-size:11.5px;color:var(--muted);margin-top:2px;font-style:italic;white-space:nowrap;overflow:hidden;text-overflow:ellipsis; }
  .gs-col{ font-weight:700;color:var(--green2); }
  .gs-st{ display:inline-block;vertical-align:middle;font-size:10px;letter-spacing:.4px;text-transform:uppercase;font-weight:800;padding:1px 7px;border-radius:999px; }
  .gs-tag{ display:inline-block;vertical-align:middle;font-size:9.5px;letter-spacing:.4px;text-transform:uppercase;font-weight:800;padding:1px 6px;border-radius:6px;background:var(--soft);color:var(--muted); }
  #gs-results mark{ background:var(--st-lav-bg);color:var(--st-lav-tx);border-radius:3px;padding:0 1px;font-weight:800; }
  .gs-more{ padding:8px 16px 10px;font-size:12.5px;font-weight:700;color:var(--green2);cursor:pointer;border-radius:8px; }
  .gs-more:hover,.gs-more.gs-active{ background:var(--soft); }
  .gs-empty{ text-align:center;color:var(--muted);padding:24px;font-size:13px; }
  .gs-chips{ display:flex;flex-wrap:wrap;gap:6px;padding:2px 16px 8px; }
  .gs-chip{ font-size:12px;font-weight:600;color:var(--ink);background:var(--soft);border:1px solid var(--border);border-radius:999px;padding:4px 11px;cursor:pointer; }
  @media(hover:hover){ .gs-chip:hover{ background:var(--card);border-color:var(--green2); } }
  .gs-note{ font-size:11.5px;color:var(--muted);padding:8px 16px;display:flex;align-items:center;gap:8px; }
  .gs-spin{ width:12px;height:12px;flex-shrink:0;border:2px solid var(--border);border-top-color:var(--green2);border-radius:50%;animation:gs-spin .7s linear infinite; }
  @keyframes gs-spin{ to{ transform:rotate(360deg); } }
  /* Quando un pannello/overlay è aperto, l'area di lavoro sotto non scorre.
     Lo scroller è .content (non più la finestra), quindi blocco lui.
     --sbw compensa la scrollbar che sparisce, evitando il salto del layout. */
  body.scroll-locked .content,body.nav-locked .content{ overflow:hidden; }
  /* ══ Dock «Da riprendere» (finestre ridotte a icona — app.9f-dock.js) ══
     Chip flottanti in basso a SINISTRA dell'area contenuto: il basso-destra è
     della FAB 📷. Sparisce quando c'è un overlay aperto (come la FAB), e
     `body.has-dock` alza il fondo di .content così i chip non coprono
     l'ultima riga dell'elenco. */
  .dock{position:fixed;left:260px;bottom:20px;z-index:8900;
    display:flex;align-items:center;gap:8px;max-width:min(760px,calc(100vw - 260px - 110px));
    overflow-x:auto;overscroll-behavior:contain;scrollbar-width:none;padding:2px;}
  .dock::-webkit-scrollbar{display:none;}
  .dock[hidden]{display:none;}
  body.scroll-locked .dock,body.nav-locked .dock{display:none !important;}
  .dock .dk-lab{flex:0 0 auto;font-size:10.5px;font-weight:800;letter-spacing:.8px;text-transform:uppercase;
    color:var(--muted2);background:var(--panel);border:1px solid var(--line2);border-radius:999px;
    padding:5px 10px;box-shadow:0 3px 12px rgba(0,0,0,.10);}
  .dkchip{flex:0 0 auto;display:inline-flex;align-items:center;gap:7px;max-width:240px;height:36px;padding:0 6px 0 11px;
    border:1px solid var(--line2);background:var(--panel);border-radius:999px;box-shadow:0 3px 12px rgba(0,0,0,.10);
    font-weight:700;font-size:13px;color:var(--ink);cursor:pointer;white-space:nowrap;}
  @media(hover:hover){ .dkchip:hover{border-color:var(--green);} }
  .dkchip .dki{flex:0 0 auto;font-size:14px;line-height:1;}
  .dkchip .dkt{overflow:hidden;text-overflow:ellipsis;}
  .dkchip .dks{color:var(--muted);font-weight:600;}
  .dkchip .dkx{flex:0 0 auto;width:20px;height:20px;border-radius:50%;border:none;background:transparent;
    color:var(--muted2);font-size:14px;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0;}
  @media(hover:hover){ .dkchip .dkx:hover{background:var(--soft);color:var(--ink);} }
  .dkchip.q{background:var(--soft);border-color:var(--line2);}
  @keyframes dkIn{from{opacity:0;transform:translateY(10px) scale(.9)}to{opacity:1;transform:none}}
  .dkchip.new{animation:dkIn .3s var(--ease-spring,var(--ease-spring)) both;border-color:var(--green);}
  body.has-dock{--pb-dock:112px;} /* valore di riserva: renderDock lo riscrive con l'altezza vera */
  /* Mobile: la sidebar è un drawer, quindi il dock parte da sinistra; a destra
     lascia libera la FAB 📷 e sta sopra la home-bar dell'iPhone.
     ⚠️ Questa media query DEVE stare qui, dopo la regola base di `.dock`: nel
     blocco ≤640px più in alto nel file veniva annullata dal `left:260px` che
     segue (stessa specificità, vince l'ultima) — è lo stesso inciampo già visto
     col modale schede-crust. */
  @media(max-width:640px){
    .dock{left:12px;right:84px;max-width:none;bottom:calc(18px + env(safe-area-inset-bottom));}
    .dock .dk-lab{display:none;}
    /* target di tocco: 36px il chip e 20px la × erano sotto la soglia dei 44 */
    .dkchip{height:44px;padding:0 4px 0 13px;}
    .dkchip .dkx{width:32px;height:32px;font-size:16px;border-left:1px solid var(--line2);border-radius:0 999px 999px 0;}
    /* su mobile l'inset è già nella somma di .content */
  }
  /* ══════════════════════════════════════════════════════════════════
     Fluidità dello scroll (tutto il gestionale)
     ──────────────────────────────────────────────────────────────────
     1) overscroll-behavior:contain → lo scroll resta confinato al
        contenitore attivo (niente "scroll chaining" sulla pagina dietro).
     2) -webkit-overflow-scrolling:touch → momentum/inerzia nativa su
        iOS in OGNI contenitore scrollabile (non solo toolbar/liste mobile).
     Elenco unico di TUTTI gli scroller dell'app, così nessuno resta indietro. */
  .panel-body,.dlgbody,.csm-list,.pvscroll,.pvrail,.pvsheet,.sidebar,.nav,.ms-pop,.kbdhelp-box,
  #gs-results{
    overscroll-behavior:contain;
    -webkit-overflow-scrolling:touch;
  }
  /* NB: niente content-visibility:auto sulle righe degli elenchi.
     Implicherebbe contain:paint → uno stacking context per ogni riga, che su
     iOS Safari, durante lo scroll, viene compositato SOPRA la barra dei filtri
     sticky (le schede "finivano sopra" la toolbar). La fluidità su mobile è già
     garantita dal momentum nativo + overscroll-behavior qui sopra. */
  /* Dropdown "managed" disabilitato (es. cliente con Stock service attivo) */
  .ms.managed.disabled{opacity:.55;pointer-events:none;}
  .ms.managed.disabled .ms-btn{background:#f1f3f5;color:#9aa6b2;}
  /* Lotti tracciati nelle card Colori crust */
  .colc-card-lotti{font-size:12px;color:var(--muted);margin-top:4px;display:flex;flex-wrap:wrap;gap:5px;align-items:center;}
  .lotto-chip{display:inline-block;background:var(--soft);color:var(--green);border:1px solid var(--line2);border-radius:6px;padding:1px 7px;font-size:11.5px;font-weight:700;}
  /* Lista lotti gestibili dentro la card del Tipo crust (logica magazzino) */
  .colc-lotti{display:flex;flex-direction:column;gap:5px;margin-top:5px;}
  .colc-lotto{display:flex;align-items:center;justify-content:space-between;gap:8px;background:var(--soft);border:1px solid var(--line2);border-radius:8px;padding:5px 6px 5px 10px;}
  .colc-lotto-main{display:flex;flex-direction:column;gap:1px;min-width:0;}
  .colc-lotto-num{font-size:12.5px;font-weight:800;color:var(--green);letter-spacing:.3px;}
  .colc-lotto-meta{font-size:11px;color:var(--muted);font-weight:600;overflow:hidden;text-overflow:ellipsis;}
  .colc-lotto-acts{display:flex;gap:2px;flex:0 0 auto;}
  /* ── Pagina "Gestione lotti": gruppo per fornitore → sotto-gruppo per tipo ── */
  .glotti-tipo{margin-top:12px;}
  .glotti-tipo:first-child{margin-top:6px;}
  .glotti-tipohead{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;padding:4px 2px 6px;}
  .glotti-tipohead .gt-name{font-size:13.5px;font-weight:800;color:var(--green);letter-spacing:.2px;}
  .glotti-tipohead .gt-var{font-size:11.5px;font-weight:700;color:var(--muted);}
  .glotti-tipohead .gt-count{margin-left:auto;font-size:11px;color:var(--muted);font-weight:700;}
  .glotti-lotto-tipo{display:none;}
  /* Card Tipo crust: nota lotti (i lotti veri si gestiscono in Gestione lotti) */
  .colc-card-lotnote{font-size:11.5px;color:var(--muted);font-weight:600;margin-top:2px;line-height:1.4;}
  .colc-card-lotnote b{color:var(--green);}
  /* Riga lotto in Gestione lotti: N. lotto cliccabile + colonne crust impiegato/saldo */
  .lotto-row{flex-wrap:wrap;row-gap:7px;}
  .lotto-row .colc-lotto-main{flex:1 1 150px;}
  .lotto-num-btn{display:inline-flex;align-items:center;gap:5px;align-self:flex-start;background:var(--soft);border:1px solid var(--line2);border-radius:7px;padding:3px 9px;font-size:12.5px;font-weight:800;color:var(--green);letter-spacing:.3px;cursor:pointer;transition:background var(--d-fast);-webkit-tap-highlight-color:transparent;}
  .lotto-num-btn:hover{background:var(--soft-bd);}
  .lotto-num-ic{font-size:11px;opacity:.75;}
  /* Bottone 📋 copia N. lotto (accanto al numero, in Gestione lotti e nel modal movimentazioni) */
  .lotto-num-line{display:flex;align-items:center;gap:2px;}
  .lotto-copy-btn{font-size:13px;padding:3px 6px;color:var(--muted);}
  .lotto-copy-btn--ok{color:var(--green);font-weight:800;}
  /* Saldo lotto in scheda: hint sotto il campo N° lotto + saldo per-opzione in tendina */
  .lotto-saldo-hint{font-size:11.5px;color:var(--muted);font-weight:600;margin-top:4px;line-height:1.4;}
  .lotto-saldo-hint b{color:var(--green);}
  .lotto-saldo-hint.bad, .lotto-saldo-hint.bad b{color:#c0492b;}
  .ms.managed .ms-opt{display:flex;align-items:center;}
  .ms-opt-hint{margin-left:auto;padding-left:10px;font-size:11px;font-weight:700;color:var(--muted);white-space:nowrap;}
  .ms-opt-hint.bad{color:#c0492b;}
  .lotto-stats{display:grid;grid-template-columns:repeat(3,minmax(70px,1fr));gap:6px;flex:1 1 230px;max-width:340px;}
  .lst{display:flex;flex-direction:column;align-items:flex-end;gap:1px;background:#fff;border:1px solid var(--line2);border-radius:7px;padding:3px 8px;}
  .lst-k{font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.03em;color:var(--muted);}
  .lst-v{font-size:12.5px;font-weight:800;color:var(--ink);white-space:nowrap;}
  .lst-v--open{color:#1e56a0;}
  .lst-v--done{color:var(--green);}
  .lst-v--bal{color:var(--green);}
  .lst-v--neg{color:#b3261e;}
  .lst-p{font-size:10px;font-weight:700;color:var(--muted);white-space:nowrap;line-height:1.2;margin-top:1px;}
  .lst-p--neg{color:#b3261e;}
  /* Modal movimentazioni lotto: sottotitolo + banda riepilogo saldo */
  .lmov-sub{font-size:12px;color:var(--muted);font-weight:600;margin-top:2px;overflow:hidden;text-overflow:ellipsis;}
  .lmov-summary{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;padding:12px 14px 4px;flex:0 0 auto;}
  .lmv-card{background:var(--sidebar);border:1px solid var(--border);border-radius:10px;padding:8px 10px;display:flex;flex-direction:column;gap:2px;}
  .lmv-card--bal{background:var(--soft);border-color:var(--soft-bd);}
  /* senza questo override il saldo del lotto in tema scuro era 1,06:1 — c'era ma non si vedeva */
  html[data-theme="dark"] .lmv-card--bal{background:var(--soft);border-color:var(--soft-bd);}
  .lmv-k{font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:.03em;color:var(--muted);}
  .lmv-v{font-size:16px;font-weight:900;color:var(--ink);line-height:1.1;white-space:nowrap;}
  .lmv-sub{font-size:10.5px;font-weight:700;color:var(--muted);}
  .lmv-open{color:#1e56a0;}
  .lmv-done{color:var(--green);}
  .lmv-pos{color:var(--green);}
  .lmv-neg{color:#b3261e;}
  .lmv-rowmq{font-size:12px;font-weight:800;color:var(--green);margin-left:auto;}
  .lmv-rowpz{font-size:11px;font-weight:700;color:var(--muted);margin-left:7px;}
  /* Mobile: colonne crust su tutta la riga sotto il N. lotto; riepilogo saldo 2×2.
     Definito DOPO le regole base così l'override vince (stessa specificità). */
  @media(max-width:640px){
    .lotto-stats{flex:1 1 100%;max-width:none;}
    .lmov-summary{grid-template-columns:repeat(2,1fr);}
    .prog-row{flex-wrap:wrap;}
    /* ONDA 3 — la riga del Programma misurava 240px: quattro righe di testo (due
       troncate a metà) e quattro pulsanti su due file. Ora: due righe di testo,
       il badge della situazione in alto a destra e i comandi dietro due
       quadratini impilati («⟳» stato e «⋯» resto).
       Prima queste regole facevano l'opposto — pulsanti a tutta riga in griglia
       2×2 — perché con quattro azioni a vista non c'era altro modo. */
    .prog-row{position:relative;padding:11px 12px 11px 13px;gap:8px;margin-bottom:7px;}
    /* ⚠️ Il padding-top scavalca il BADGE, che è absolute in alto a destra
       (top:9px, alto 27 → finisce a 25px dentro questa colonna). Coi due
       quadratini impilati la colonna arriva fin lassù e il chip di stato
       finiva SOTTO il badge «in ritardo di N gg». */
    .prog-row>.ractions.rcol{width:auto;flex-direction:row;align-items:flex-end;gap:7px;margin-top:0;flex:0 0 auto;padding-top:25px;}
    .prog-row>.ractions.rcol>.rstate-row{position:absolute;top:9px;right:11px;justify-content:flex-end;max-width:58%;}
    /* ⚠️ IL CHIP DI STATO SI IMPILA SOPRA IL «⋯», non gli si affianca.
       Affiancato (chip con la scritta) il blocco azioni passava da 40 a 126px
       e la .prog-main perdeva altrettanto: misurato a 390px — l'iPhone più
       diffuso — la riga dei giorni «18/10 gg lavorati · prevista 24/07/2026»
       veniva TAGLIATA e il nome dell'articolo passava da intero a troncato,
       cioè si riapriva il difetto corretto due volte in v18.2/v18.5. Col solo
       glifo affiancato (87px) il taglio della riga dei giorni restava a 360 e
       375px. Impilati, i due quadratini stanno in colonna dentro gli stessi
       40px di prima (align-items:center, non stretch: con stretch la colonna
       si allarga al contenuto) e articolo, riga di servizio e riga dei giorni
       NON perdono un pixel a nessuna larghezza.
       Il prezzo è in ALTEZZA e sta tutto qui: due quadratini più il salto
       sopra il badge non stanno nei ~97px della .prog-main, quindi la riga
       passa da 119,3 a 128px (misurato da 320 a 430). È il costo del secondo
       comando in riga su telefono, scelto dall'utente. */
    .prog-row>.ractions.rcol>.ract-line{display:flex;flex-direction:column;gap:5px;align-items:center;}
    .prog-row>.ractions.rcol>.ract-line>.btn.adv{white-space:nowrap;padding:8px 12px;}
    /* Il chip perde la scritta e diventa un quadratino come il «⋯»: il nome
       dello stato lo dice il foglio che si apre, con numero della scheda e
       stato attuale in cima. 36px invece di 38: due quadratini più il salto
       sopra il badge fanno crescere la riga, e questi 4px la tengono a 128px
       (il limite pinnato è 132). */
    .prog-row .pst-btn{width:40px;height:36px;padding:0;font-size:17px;border-radius:10px;}
    .prog-row .pst-lbl{display:none;}
    /* le secondarie vivono nel foglio: restano nel DOM e il foglio le aziona */
    .prog-row .prog-secondary{display:none;}
    .prog-row .prog-more{display:inline-flex;width:40px;height:36px;align-items:center;justify-content:center;
      font-size:18px;font-weight:800;letter-spacing:1px;color:var(--muted);border:1px solid var(--line2);
      background:var(--panel);border-radius:10px;}
    /* ⚠️ IL COLORE RIFINITO SPARIVA — non riservare spazio al badge qui.
       La prima riga si riservava 150px a destra per il badge «in ritardo di N gg»,
       ma quel badge è in position:absolute SOPRA i pulsanti (.ract-line, ~124px),
       non sopra il testo: i pulsanti stanno già nel flusso e hanno già ristretto
       la .prog-main a 169px. Lo spazio veniva quindi scontato DUE VOLTE e ai tre
       dati ne restavano 18,9 — l'articolo spariva del tutto e il colore si
       riduceva a una lettera («🎨 T…»); sotto i 360px scendeva a zero. Misurato
       in headless: la .prog-main finisce a 194px, il badge comincia a 225 — non
       si toccano, bastano gli 8px d'aria. Se un domani i pulsanti in riga si
       riducono, la main si allarga e ALLORA il badge va evitato davvero: è il
       caso in cui questo padding va ricalcolato, non prima. */
    .prog-row .prog-top{padding-right:8px;flex-wrap:wrap;row-gap:4px;overflow:hidden;}
    .prog-row .prog-art{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;}
    /* Numero e articolo restano in riga (flex-basis:0 sull'articolo, così NON
       manda a capo: col wrap attivo il browser decide l'a-capo sulla dimensione
       ideale, non sul min-width, e un articolo lungo si porterebbe via una riga
       intera). Il chip colore va SEMPRE a capo, spinto giù dallo pseudo-elemento
       che fa da interruzione di riga: lì ha tutta la larghezza (140px invece di
       0) e il badge non gli sta sopra, perché è più in alto. */
    .prog-row .prog-num{order:1;flex:0 0 auto;}
    /* ⚠️ Lo spazio del badge si riserva QUI, sull'ultimo elemento della PRIMA
       riga, non con un padding sulla .prog-top: quello varrebbe anche per la
       seconda riga e si riprenderebbe al chip colore la larghezza appena data.
       Serve da quando «→ Pronta» è passato nel foglio «⋯» (v18.3): con i soli
       40px del ⋯ la riga arriva sotto il badge, che prima non raggiungeva. La
       misura è COSTANTE a ogni larghezza — badge e fine riga sono entrambi
       ancorati a destra — ed è ~66px col badge più lungo di oggi («in ritardo
       di 128 gg»), qui arrotondati per sicurezza. */
    .prog-row .prog-art{order:1;flex:1 1 0;margin-right:78px;}
    .prog-row .prog-top.has-col::after{content:"";flex:0 0 100%;height:0;order:2;}
    .prog-row .prog-col{order:3;flex:0 1 auto;font-size:12.5px;padding:2px 10px;
      min-width:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
    /* Due righe di testo invece di quattro: quelle che restano si leggono
       tutte, invece di andare a capo a metà parola. */
    .prog-row .prog-meta,.prog-row .prog-days{font-size:12px;margin-top:1px;
      white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
    /* I figli della .prog-main stanno a tutta riga: da quando la barra del tempo
       è stata rimossa (v18.5) non c'è più niente da affiancare, e una riga
       aggiunta in futuro non deve finire per sbaglio accanto a un'altra. */
    .prog-row .prog-main{min-width:0;flex:1;display:flex;flex-wrap:wrap;align-items:center;column-gap:8px;}
    .prog-row .prog-main>*{flex:0 0 100%;min-width:0;}
    /* ⚠️ La forma breve va accesa con TRE classi: la regola base che la spegne
       sta più avanti nel file (sezione «Programma lavorazioni») e a parità di
       specificità vincerebbe lei. Stessa trappola del modale schede-crust.
       L'orologio ⏱ e la parola «prevista» restano invece a vista come sul
       computer: con la barra tolta la riga è tutta libera e ci stanno. */
    .prog-row .prog-days .pd-sm{display:inline;}
    .prog-days .pd-lg{display:none;}
    /* la riga risponde al tocco, come quelle delle schede */
    .prog-row:active{background:var(--soft);}
    html[data-theme="dark"] .prog-row:active{background:#212d3a;}
    /* editor rapido dei metri reali: a tutta riga quando si apre */
    .prog-row .qreal{flex:1 1 100%;min-width:0;}
    /* i tre chip di riepilogo andavano a capo (67px invece di 34) */
    .prog-sum{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px;margin-bottom:11px;}
    /* numero sopra, etichetta sotto: in orizzontale «In scadenza» andava a capo
       dentro il chip e il blocco tornava a due righe (88px invece di 44) */
    .prog-sumchip{flex-direction:column;justify-content:center;gap:1px;padding:7px 4px;min-height:46px;
      border-radius:12px;text-align:center;font-size:10.5px;line-height:1.25;white-space:nowrap;
      overflow:hidden;text-overflow:ellipsis;}
    .prog-sumchip b{font-size:16px;line-height:1.1;}
    .prog-sumchip .psc-ic{display:none;}
    .prog-sumchip .psc-lbl{overflow:hidden;text-overflow:ellipsis;max-width:100%;}
    /* barra strumenti: riepilogo a tutta riga, poi ordina + raggruppa affiancati */
    .prog-tools{gap:7px;margin-bottom:10px;}
    .prog-tools .prog-sum{flex:1 1 100%;}
    .prog-sortwrap{margin-left:0;flex:1 1 auto;}
    .prog-sortbtn{width:100%;padding:8px 10px;}
    .prog-sortmenu{right:auto;left:0;min-width:0;width:100%;}
    .prog-seg{flex:1 1 auto;}
    .prog-seg button{flex:1;padding:7px 8px;font-size:12px;}
    /* gruppo terzista: nome e conteggi su due righe, contatti sotto */
    .prog-terzhead{gap:6px 9px;padding:9px 11px;}
    .pth-name{font-size:14.5px;} .pth-stat{font-size:11.5px;flex:1 1 100%;}
    .pth-sp{display:none;}
    /* consegne al cliente: il pulsante va sotto, la data resta a sinistra */
    .cons-row{flex-wrap:wrap;padding:10px 12px;gap:9px;}
    .cons-row>.ractions.rcol{width:auto;flex-direction:row;align-items:center;gap:7px;margin-top:0;
      flex:1 1 100%;justify-content:space-between;}
    .cons-row>.ractions.rcol>.rstate-row,.cons-row>.ractions.rcol>.ract-line{flex:0 1 auto;width:auto;min-width:0;}
  }
  .iconbtn{border:none;background:transparent;cursor:pointer;font-size:15px;line-height:1;padding:5px 6px;border-radius:7px;transition:background var(--d-fast);-webkit-tap-highlight-color:transparent;}
  .iconbtn:hover{background:#0000000f;}
  .iconbtn--del:hover{background:#f8d7d7;}
  /* Tag su card Tipo crust (selezione / spessore / destinazione) */
  .colc-card-tags{display:flex;flex-wrap:wrap;gap:6px;margin-top:2px;}
  .colc-tag{display:inline-block;background:#eef2f6;color:#3a4a5a;border:1px solid #d7e0ea;border-radius:6px;padding:2px 9px;font-size:11.5px;font-weight:700;}
  /* ⚠️ `.colc-tag--cost` stava 500 righe più in SU, fra le regole di Gestione
     crust: stessa specificità di `.colc-tag` ma prima nel file, quindi il chip
     del costo non è MAI stato verde — vinceva il grigio della regola base.
     Sta qui, accanto alle sorelle, perché è l'ordine a decidere. */
  .colc-tag--sel,.colc-tag--cost{background:var(--soft);color:var(--green);border-color:var(--soft-bd);}
  .colc-tag--dest{background:#fbf2dd;color:#8a6516;border-color:#e6cf9a;}
  /* Fornitori: chip ID LOTTO */
  .forn-idlotto{display:inline-block;font-size:11px;font-weight:800;background:var(--soft);color:var(--green);border:1px solid var(--soft-bd);border-radius:6px;padding:1px 7px;margin-left:6px;letter-spacing:.5px;vertical-align:middle;}
  .forn-idlotto--missing{background:#fbeaea;color:#b3402e;border-color:#ecc3bc;}
  /* Fornitori: riga ID LOTTO con lucchetto */
  .idlotto-row{display:flex;gap:8px;align-items:stretch;}
  .idlotto-row input{flex:1;letter-spacing:2px;font-weight:800;}
  .idlotto-row input.locked{background:#f1f3f5;color:#6e7d8c;cursor:not-allowed;}
  #fe-idlotto-lock{min-width:46px;font-size:18px;padding:0 10px;}
  /* Carica lotto: box anteprima N. LOTTO */
  /* ── Carico di più lotti da UNA fattura (openFatturaLotti, app.2) ──────────
     L'elenco dei colori del fornitore con le quantità accanto. È l'unico punto
     dell'app in cui si compilano decine di campi in colonna, quindi:
     ⚠️ l'elenco scorre da solo (max-height) mentre intestazione, riepilogo e
        pulsanti restano fermi — la stessa regola dei comandi mai dentro il
        contenuto scrollabile che vale per anteprime e modali;
     ⚠️ le colonne sono FISSE (non `auto`), o le righe ballerebbero di larghezza
        a seconda della lunghezza del nome del colore. */
  .fattbox{max-width:880px;}
  .fatt-bar{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin:0 0 8px;}
  .fatt-bar .search{flex:1 1 200px;min-width:150px;margin:0;}
  .fatt-rows{border:1px solid var(--border);border-radius:11px;overflow-y:auto;max-height:min(46vh,340px);background:var(--panel);}
  .fatt-row{display:grid;grid-template-columns:22px minmax(0,1fr) 78px 92px 92px;gap:9px;align-items:center;
            padding:7px 11px;border-bottom:1px solid var(--line);margin:0;cursor:pointer;}
  .fatt-row:last-child{border-bottom:0;}
  /* ⚠️ Fondo --panel, NON --sidebar: il --muted sul grigio scende a 4,34:1 (la
     trappola già pagata da .prog-terzhead e dalle tendine bloccate). A staccare
     l'intestazione dalle righe basta il filetto più marcato. */
  .fatt-head{background:var(--panel);font-size:10.5px;letter-spacing:.05em;text-transform:uppercase;
             color:var(--muted);font-weight:800;position:sticky;top:0;z-index:1;cursor:default;
             border-bottom:1px solid var(--line2);}
  .fatt-head .fu{text-align:left;}
  /* Riga spuntata: sul fondo --soft il grigio della variante scende a 4,14:1,
     quindi lì passa al verde di testo (stessa regola dei chip su fondo tenue). */
  .fatt-row:has(.fatt-chk:checked){background:var(--soft);}
  .fatt-row:has(.fatt-chk:checked) .gt-var{color:var(--green);}
  .fatt-chk{width:17px;height:17px;accent-color:var(--green2);margin:0;cursor:pointer;}
  .fatt-nm{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13.5px;}
  .fatt-nm b{font-weight:800;}
  .fatt-nm .gt-var{font-size:11.5px;font-weight:700;color:var(--muted);}
  .fatt-nl{display:inline-block;margin-left:6px;font-size:10.5px;font-weight:800;color:var(--muted);
           background:var(--sidebar);border:1px solid var(--border);border-radius:999px;padding:0 6px;}
  .fatt-in{width:100%;min-width:0;border:1px solid var(--line2);border-radius:7px;padding:5px 8px;
           font-size:13px;font-variant-numeric:tabular-nums;background:var(--panel);color:var(--ink);}
  .fatt-foot-actions{margin-top:9px;}
  .fatt-sum{margin-right:auto;font-size:12.5px;color:var(--muted);font-weight:600;}
  @media(max-width:640px){
    /* Su telefono le tre quantità vanno sotto il nome: cinque colonne in 320px
       darebbero campi da 40px. Il nome tiene la riga sua, i campi una griglia. */
    .fatt-row{grid-template-columns:22px minmax(0,1fr);row-gap:6px;}
    .fatt-head{display:none;}
    .fatt-in{grid-column:2;}
    .fatt-rows{max-height:min(42vh,300px);}
  }
  .numerolotto-box{background:var(--soft);border:1px dashed var(--line2);border-radius:10px;padding:10px 14px;margin:4px 0 12px;display:flex;flex-direction:column;gap:3px;}
  /* ⚠️ --green e non --muted: il grigio su questo fondo verde tenue dà 4,14:1,
     sotto AA (difetto preesistente, emerso misurando il dialogo «Carica fattura»
     — vale anche per il carico del lotto singolo, che usa la stessa fascia). */
  .numerolotto-box .nl-id{font-size:11.5px;color:var(--green);font-weight:600;}
  .numerolotto-box .nl-result{font-size:14px;color:var(--ink);}
  .numerolotto-box .nl-result b{font-size:20px;font-weight:900;color:var(--green);letter-spacing:.5px;}
  /* Scheda: mix avanzato selezioni crust */
  #fl-crustmix{margin-top:8px;border:1px solid var(--line);border-radius:10px;padding:12px;background:var(--soft);}
  .mix-hint{font-size:12px;color:var(--muted);margin-bottom:10px;line-height:1.45;}
  /* Nota nei pannelli tintura/rasatura: dichiara in anticipo che al salvataggio
     le schede collegate si riallineano, così l'automatismo non è una sorpresa.
     Testo su --green e NON --green2, che su --soft scende a 3,7:1. */
  .orig-propaga{font-size:12.5px;line-height:1.45;font-weight:600;color:var(--green);
    background:var(--soft);border:1px solid var(--soft-bd);border-radius:8px;padding:7px 10px;margin:0 0 9px;}
  .orig-propaga b{font-weight:800;}
  /* avviso "campo bloccato" (es. N° lotto finché mancano fornitore/colore/variante) */
  .lock-hint{margin-top:6px;font-size:12px;font-weight:600;color:#9a6f2c;background:var(--amber-bg);border:1px solid var(--amber-bd);border-radius:8px;padding:6px 9px;line-height:1.35;}
  /* ⚠️ `display:block` NON è decorativo: l'elemento porta anche la classe
     `.field`, che è `display:flex;flex-direction:column`. In un contenitore così
     ogni frammento di testo diventa una RIGA: le tre parole in grassetto
     spezzavano la frase in sette pezzi incolonnati e la nota occupava 153px su
     un monitor grande (170 su iPhone) per un testo che ne chiede 36. Se aggiungi
     un'altra nota testuale con la classe `.field`, dichiarale il display. */
  .crustlock-note{display:block;font-size:12.5px;font-weight:600;color:#2f5d8a;background:#eef4fb;border:1px solid #cddff2;border-radius:9px;padding:8px 11px;line-height:1.4;}
  html[data-theme="dark"] .crustlock-note{color:#a9c8ea;background:#16202b;border-color:#2b3d4f;}
  /* Nota «articolo dalla commessa · tipo di grezzo dall'articolo»: stessa nota
     azzurra, ma con i pulsanti («🔓 Sblocca», «↺ Allinea») sulla stessa riga —
     quindi qui il display torna flex, in ORIZZONTALE. La classe composta ha
     specificità maggiore di .crustlock-note, così vince sul display:block
     qualunque sia l'ordine nel file. Il testo è in uno <span> unico: non si
     ricade nel difetto delle righe spezzate spiegato qui sopra. */
  .crustlock-note.ordlock-note{display:flex;flex-direction:row;align-items:center;gap:10px;flex-wrap:wrap;}
  /* ⚠️ `flex:1 1 0` e non `1 1 auto`: con la base «auto» il flex parte dalla
     larghezza del TESTO, che su iPhone eccede la riga e manda a capo il
     pulsante — 38px di altezza in più per una nota informativa. Con base 0 il
     pulsante resta accanto e a mandare a capo è il testo, dentro il suo spazio. */
  .ordlock-note>span{flex:1 1 0;min-width:140px;}
  .ordlock-note .btn{flex:0 0 auto;}
  /* ⚠️ --amber-tx e non --tan: --tan non è ridefinito nel tema scuro e darebbe
     marrone su fondo scuro (la trappola già pagata dai marcatori consegna). */
  .ordlock-div{color:var(--amber-tx);font-weight:700;}
  /* Grezzo scelto a mano: è una DICHIARAZIONE, non un avviso — niente ambra
     dell'allarme. ⚠️ E niente --muted «per attenuarlo»: sul fondo azzurro della
     nota scende a 4,30:1, sotto il minimo leggibile (la stessa trappola del
     grigio sui fondi colorati già pagata in Programma e nel calcolatore).
     A distinguerlo bastano il 📌 e il grassetto. */
  .ordlock-man{font-weight:600;}
  /* Box "Suggeriti dai tuoi dati" (motore locale) */
  .lav-suggest{background:var(--card,#fff);border:1px solid var(--soft-bd);border-radius:11px;padding:9px 11px 10px;margin-bottom:2px;}
  .lav-suggest .lsug-head{font-size:12px;font-weight:800;color:var(--green,#003C0F);text-transform:uppercase;letter-spacing:.3px;display:flex;align-items:center;gap:5px;}
  .lav-suggest .lsug-ico{font-size:14px;}
  .lav-suggest .lsug-sub{font-weight:600;color:var(--muted);text-transform:none;letter-spacing:0;font-size:11.5px;}
  .lav-suggest .lsug-chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px;}
  .lav-suggest .lsug-chip{display:inline-flex;align-items:center;gap:5px;font-size:12.5px;font-weight:700;color:var(--green);background:var(--panel);border:1px solid var(--soft-bd);border-radius:999px;padding:4px 11px;cursor:pointer;transition:background var(--d-fast),border-color var(--d-fast);}
  .lav-suggest .lsug-chip:hover{background:var(--soft);border-color:var(--green3);}
  .lav-suggest .lsug-pct{font-size:10.5px;font-weight:800;color:var(--green);background:var(--soft);border-radius:999px;padding:0 5px;}
  .lav-suggest .lsug-line{margin-top:7px;font-size:12px;color:var(--muted);display:flex;flex-wrap:wrap;align-items:center;gap:6px;}
  .lav-suggest .lsug-lab{font-weight:700;}
  .lav-suggest .lsug-mini{font-size:12px;font-weight:700;color:var(--fg,#0f2438);background:var(--panel);border:1px solid var(--line2,#e6e9ec);border-radius:999px;padding:2px 9px;cursor:pointer;}
  .lav-suggest .lsug-mini:hover{border-color:var(--green3);background:var(--soft);}
  html[data-theme="dark"] .lav-suggest{background:var(--soft);border-color:var(--soft-bd);}
  html[data-theme="dark"] .lav-suggest .lsug-chip,html[data-theme="dark"] .lav-suggest .lsug-mini{background:var(--panel);border-color:var(--soft-bd);color:var(--green);}
  html[data-theme="dark"] .lav-suggest .lsug-chip:hover,html[data-theme="dark"] .lav-suggest .lsug-mini:hover{background:var(--soft);}
  html[data-theme="dark"] .lav-suggest .lsug-pct{background:var(--soft);color:var(--green);}
  /* Barra modalità/unità del mix */
  .mix-head{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-bottom:8px;}
  .mix-modeseg{display:inline-flex;background:var(--panel);border:1px solid var(--line2);border-radius:8px;padding:2px;gap:2px;}
  .mix-modeseg button{border:none;background:none;font:inherit;font-size:12px;font-weight:700;color:var(--muted);padding:5px 12px;border-radius:6px;cursor:pointer;white-space:nowrap;}
  .mix-modeseg button.on{background:var(--green);color:#fff;}
  /* Etichetta dei segmenti: in modalità Quantità convivono l'unità delle
     quantità e quella del COSTO, quindi il secondo va nominato. */
  .mix-seggrp{display:inline-flex;align-items:center;gap:6px;}
  .mix-seglab{font-size:11px;font-weight:800;letter-spacing:.4px;text-transform:uppercase;color:var(--muted);}
  .mix-costseg button{padding:5px 10px;}
  #fl-crustmix-toggle:disabled,#fl-crustmix-toggle.is-locked{opacity:.55;cursor:not-allowed;}
  /* Riga mix: selezione · spessore · quantità/% · costo €/m² · rimuovi */
  .mix-row{display:grid;grid-template-columns:1.3fr 1fr .7fr .7fr auto;gap:8px;align-items:center;margin-bottom:8px;}
  .mix-row input{width:100%;box-sizing:border-box;padding:8px 10px;border:1px solid var(--line2);border-radius:8px;font:inherit;background:var(--panel);color:var(--ink);}
  /* Tendine selezione/spessore del mix nello stile "managed" del gestionale */
  .mix-row .mix-ms{min-width:0;}
  .mix-row .mix-ms .ms-btn{width:100%;min-height:40px;padding:9px 26px 9px 11px;}
  .mix-row .mix-x{cursor:pointer;border:none;background:#fbeaea;color:#b3402e;border-radius:8px;font-size:18px;font-weight:900;line-height:1;padding:6px 12px;align-self:stretch;}
  .mix-foot{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:4px;}
  .mix-total{font-size:13px;font-weight:800;color:var(--green);}
  .mix-total--bad{color:#b3402e;}
  @media(max-width:640px){ .mix-row{grid-template-columns:1fr 1fr;} .mix-row .mix-x{grid-column:2;justify-self:end;} }
  /* ── Ripartizione su PIÙ CLIENTI (scheda) ──────────────────────────────────
     Righe incolonnate come la .mqgrid: cliente | stima | reali | quota | €/m².
     Su mobile la riga si impila (niente scorrimento laterale). */
  .mcli{border:1px solid var(--border);border-radius:12px;background:var(--panel);padding:10px;}
  .mcli-cols,.mcli-row{display:grid;grid-template-columns:minmax(0,1.6fr) .85fr .85fr .6fr .85fr auto;gap:8px;align-items:center;}
  .mcli-cols{font-size:10.5px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);padding:0 2px 6px;border-bottom:1px solid var(--line);}
  .mcli-cols .n,.mcli-row .mcli-in,.mcli-row .mcli-quota{text-align:right;}
  .mcli-row{padding:7px 0;border-bottom:1px solid var(--line);}
  .mcli-row:last-of-type{border-bottom:0;}
  .mcli-in{width:100%;box-sizing:border-box;padding:8px 10px;border:1px solid var(--line2);border-radius:8px;font:inherit;background:var(--panel);color:var(--ink);font-variant-numeric:tabular-nums;}
  .mcli-quota{font-size:13px;font-weight:800;color:var(--muted);font-variant-numeric:tabular-nums;}
  .mcli-cli{min-width:0;}
  .mcli-cli .ms-btn{width:100%;min-height:40px;padding:9px 26px 9px 11px;}
  .mcli-cli.disabled{pointer-events:none;opacity:.6;}
  .mcli-stock{display:inline-block;font-size:12.5px;font-weight:800;color:var(--muted);background:var(--bg);border:1px solid var(--line2);border-radius:8px;padding:9px 11px;width:100%;box-sizing:border-box;}
  .mcli-acts{display:flex;gap:4px;}
  .mcli-x{cursor:pointer;border:none;background:var(--bg);color:var(--muted);border-radius:8px;font-size:16px;font-weight:900;line-height:1;padding:8px 10px;}
  .mcli-x:hover{background:var(--soft);color:var(--green);}
  .mcli-row .mcli-acts .mcli-x:last-child{background:#fbeaea;color:#b3402e;}
  .mcli-more{display:grid;grid-template-columns:auto .7fr auto minmax(0,1fr) minmax(0,1fr);gap:8px;align-items:center;padding:0 0 10px;border-bottom:1px solid var(--line);}
  .mcli-mlab{font-size:10.5px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);}
  .mcli-tot{font-size:12.5px;color:var(--muted);padding:9px 2px 0;}
  .mcli-tot b{color:var(--ink);font-variant-numeric:tabular-nums;}
  .mcli-warn{display:inline-block;margin-left:8px;color:#b3402e;font-weight:700;}
  .mcli-bar{display:none;height:9px;border-radius:5px;overflow:hidden;background:var(--bg);margin-top:8px;}
  .mcli-bar.on{display:flex;}
  .mcli-bar i{display:block;height:100%;}
  .mcli-foot{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px;}
  .mcli-foot .mcli-add{border-style:dashed;}
  @media(max-width:820px){
    .mcli-cols{display:none;}
    .mcli-row{grid-template-columns:minmax(0,1fr) minmax(0,1fr);row-gap:6px;padding:10px 0;}
    .mcli-row .mcli-cli{grid-column:1/-1;}
    .mcli-row .mcli-quota{text-align:left;}
    .mcli-row .mcli-acts{grid-column:2;justify-self:end;}
    .mcli-in{text-align:left;}
    .mcli-more{grid-template-columns:auto minmax(0,1fr);}
    .mcli-more .wide{grid-column:1/-1;}
  }
  /* Etichetta crust: nome colore PROTAGONISTA con il QR accanto, a destra.
     La dimensione font del nome è impostata inline (in base alla lunghezza). */
  /* Nome colore crust: SEMPRE su una sola riga. La dimensione viene adattata via
     JS (fitLabelCrustName / print-label.js) alla larghezza disponibile, così non
     va mai a capo né su mobile né in stampa. nowrap+overflow è la rete di sicurezza. */
  .label-crustname{font-weight:900;color:#003C0F;line-height:.92;letter-spacing:-1px;white-space:nowrap;overflow:hidden;text-overflow:clip;}
  .label-hero{display:flex;align-items:center;gap:16px;padding:14px 18px 12px;border-bottom:2px solid #003C0F;background:#f8faf8;}
  .label-hero-main{flex:1;min-width:0;overflow:hidden;}
  /* Ricetta colore: badge nel PDF terzista — staccato sotto il nome colore e più
     grande. I selettori coprono ANCHE .pcolband: col passaggio della scheda al
     layout a fasce il badge si è spostato lì, e restando le regole agganciate
     alla sola .chero (usata ancora da tintura/rasatura) stampava come testo
     nudo, senza cornice né colore — cioè non si notava. */
  .chero .chbadge,#print-root .pcolband .chbadge{display:inline-block;padding:5px 13px;border-radius:8px;font-size:15px;font-weight:800;}
  .chero .chbadge{margin-top:10px;}   /* staccato sotto il nome; nella .pcolband (flex, allineata al centro) non serve */
  .chero .chbadge.new,#print-root .pcolband .chbadge.new{background:#fbf2dd;color:#8a6516;border:1px solid #e6cf9a;}
  .chero .chbadge.done,#print-root .pcolband .chbadge.done{background:#EAF2EB;color:#005C17;border:1px solid #C6DCCB;}
  /* Ricetta colore: badge a schermo (elenco schede + anteprima) */
  .ricbadge{display:inline-block;font-weight:800;font-size:11.5px;padding:1px 8px;border-radius:7px;white-space:nowrap;vertical-align:middle;}
  .ricbadge.new{background:#fbf2dd;color:#8a6516;border:1px solid #e6cf9a;}
  .ricbadge.done{background:var(--soft);color:var(--green);border:1px solid var(--soft-bd);}
  /* ===== Programma lavorazioni ===== */
  .prog-section{margin-bottom:18px;}
  .prog-section-head{font-size:11px;letter-spacing:1.2px;text-transform:uppercase;font-weight:800;margin-bottom:8px;display:flex;align-items:center;gap:8px;}
  .prog-head-late{color:#c0392b;}
  .prog-head-soon{color:#8a6516;}
  .prog-head-ok{color:var(--muted);}
  .prog-count{background:var(--soft);color:var(--green);border-radius:20px;padding:1px 9px;font-size:11px;font-weight:800;}
  .prog-row{display:flex;align-items:center;gap:12px;background:var(--panel);border:1px solid var(--border);border-left-width:4px;border-radius:var(--r);padding:12px 15px;margin-bottom:8px;cursor:pointer;box-shadow:var(--sh-sm);transition:border-color var(--d-fast),box-shadow var(--d-fast);}
  @media(hover:hover){ .prog-row:hover{box-shadow:var(--sh);} }
  .prog-late{border-left-color:var(--st-late);}
  .prog-soon{border-left-color:var(--st-lav);}
  .prog-ok{border-left-color:var(--st-cons);}
  .prog-main{flex:1;min-width:0;}
  .prog-top{display:flex;align-items:baseline;gap:9px;}
  .prog-num{font-weight:800;color:var(--ink);}
  .prog-art{font-weight:600;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
  .prog-col{flex-shrink:0;font-weight:800;color:var(--green);background:var(--soft);border:1px solid var(--soft-bd);border-radius:8px;padding:2px 11px;font-size:14.5px;letter-spacing:.1px;white-space:nowrap;}
  .prog-meta{font-size:12.5px;color:var(--muted);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
  /* Per CHI si lavora e QUANTA merce c'è: riga PROPRIA sotto quella del
     terzista (v19.33). ⚠️ Non spostare cliente e quantità dentro .prog-meta
     «per risparmiare una riga»: misurato, chiedono 322÷370px dove su iPhone ce
     ne sono 247÷317 e la riga viene tagliata a ogni larghezza — stesso difetto
     per cui colore crust e n° lotto sono usciti di lì in v18.5. */
  .prog-cliline{font-size:12.5px;color:var(--muted);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.45;}
  .prog-cliline .prog-cli{color:var(--ink);font-weight:800;}
  /* I metri REALI sono il dato definitivo (margini, saldo lotto, statistiche):
     si accendono in verde col ✓, come la cella «Metri²» dell'elenco schede. Il
     verde è --st-cons-tx e NON --green2, che su fondo bianco sta a 4,38:1,
     sotto AA (misurato). */
  .prog-cliline .prog-mq{font-weight:700;color:var(--ink);font-variant-numeric:tabular-nums;}
  .prog-cliline .prog-mq.ok{color:var(--st-cons-tx);font-weight:800;}
  /* Sotto i 360px (iPhone SE 1ª gen) la riga chiede 227px e ne ha 207: qualcosa
     deve cadere, e cadono le PELLI — così sopravvivono cliente e metri, che
     sono i due dati per cui la riga esiste. A quella larghezza troncano da
     sempre anche la riga del terzista e quella dei giorni: 320px resta
     l'unica larghezza dichiarata scoperta, non è una regressione di qui. */
  @media(max-width:359px){ .prog-cliline .prog-pz{display:none;} }
  .prog-days{font-size:12.5px;color:var(--ink);margin-top:3px;font-weight:600;}
  /* Forma breve dei giorni («18/10 gg lavorati»): serve solo su telefono, dove
     la riga completa non ci starebbe. Qui la si spegne (media query ≤640px). */
  .prog-days .pd-sm{display:none;}
  /* Quanto è già rientrato dal terzista (riprese parziali): serve mentre
     controlli, quindi sta sulla riga e non solo dentro la scheda. */
  .prog-rip{font-size:12.5px;color:var(--st-lav-tx);margin-top:3px;font-weight:600;}
  .prog-rip b{font-weight:800;}
  .prog-rip.done{color:var(--st-cons-tx);}
  /* La BARRA DEL TEMPO consumato (.prog-tbar/.ptb-*) è stata RIMOSSA in v18.5
     su richiesta dell'utente: ripeteva in forma grafica ciò che la riga dei
     giorni dice già in cifre. Non reintrodurla senza chiedere. */
  /* ── Barra strumenti: riepilogo + ordinamento + raggruppamento ─────────── */
  .prog-tools{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:12px;}
  .prog-tools .prog-sum{margin-bottom:0;}
  .prog-sortwrap{position:relative;margin-left:auto;}
  .prog-sortbtn{font:inherit;font-size:12.5px;font-weight:750;border:1px solid var(--line2);
    background:var(--panel);color:var(--ink);border-radius:999px;padding:7px 14px;cursor:pointer;white-space:nowrap;}
  .prog-sortbtn.open{border-color:var(--green2);color:var(--green);}
  .prog-sortmenu{position:absolute;top:calc(100% + 6px);right:0;z-index:60;min-width:230px;
    background:var(--panel);border:1px solid var(--line2);border-radius:var(--r-sm);
    box-shadow:var(--sh-lg);padding:5px;
    /* cresce dal pulsante, come .ms-pop e .ordmenu (v20: appariva di colpo) */
    animation:dlgpop var(--d-fast) var(--ease-out);transform-origin:top right;}
  .psm-item{display:block;width:100%;text-align:left;font:inherit;font-size:13.5px;font-weight:650;
    background:transparent;border:0;border-radius:7px;padding:8px 11px;color:var(--ink);cursor:pointer;}
  @media(hover:hover){ .psm-item:hover{background:var(--soft);} }
  .psm-item.on{background:var(--soft);color:var(--green);font-weight:850;}
  .prog-seg{display:inline-flex;background:var(--panel);border:1px solid var(--line2);
    border-radius:999px;padding:3px;gap:2px;}
  .prog-seg button{font:inherit;font-size:12.5px;font-weight:750;color:var(--muted);
    background:transparent;border:0;border-radius:999px;padding:6px 13px;cursor:pointer;white-space:nowrap;}
  .prog-seg button.on{background:var(--green);color:#fff;}
  /* ── Intestazione di gruppo per terzista ──────────────────────────────── */
  /* Fondo BIANCO, non var(--sidebar): il riepilogo è testo --muted, che sul
     grigio della barra laterale scende a 4,34:1 (sotto la soglia AA, colto da
     dark-theme.test.js). Su --panel sale a 4,76:1 ed è lo stesso trattamento
     di .prog-meta, che compare in ogni riga qui sotto. */
  .prog-terzhead{display:flex;align-items:center;gap:11px;flex-wrap:wrap;background:var(--panel);
    border:1px solid var(--line2);border-radius:var(--r);padding:9px 14px;margin:14px 0 8px;}
  .prog-terzhead:first-child{margin-top:0;}
  .pth-name{font-weight:850;font-size:15.5px;}
  .pth-stat{font-size:12.5px;color:var(--muted);font-weight:650;}
  .pth-sp{flex:1;min-width:4px;}
  .prog-terzhead .cchips{margin:0;}
  /* ── Consegne AL CLIENTE (ex Calendario) ──────────────────────────────── */
  .prog-head-cons{color:var(--st-pronta-tx);}
  .cons-row{display:flex;align-items:center;gap:12px;background:var(--panel);border:1px solid var(--border);
    border-left:4px solid var(--st-pronta);border-radius:var(--r);padding:11px 14px;margin-bottom:8px;
    box-shadow:var(--sh-sm);cursor:pointer;}
  .cons-row.late{border-left-color:var(--st-late);}
  @media(hover:hover){ .cons-row:hover{box-shadow:var(--sh);} }
  .cons-row:active{transform:scale(.995);}
  .cons-date{font-weight:850;font-size:15px;text-align:center;min-width:46px;line-height:1.1;flex-shrink:0;}
  .cons-date span{display:block;font-size:10.5px;font-weight:750;color:var(--muted);
    text-transform:uppercase;letter-spacing:.6px;}
  .cons-main{flex:1;min-width:0;}
  .cons-top{font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
  .cons-meta{font-size:12.5px;color:var(--muted);margin-top:2px;
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
  /* Fra 641 e 1199px il foglio «⋯» NON esiste (è gated su mobile), quindi TUTTI
     i pulsanti di riga restano a vista: con cinque (→ Pronta · 📅 Consegna ·
     📦 Ripresa · ＋ metri reali · → Consegnata) il blocco azioni chiede 547px
     invece di 422 e spinge il testo a capo — misurato in headless, la riga
     cresceva da 203 a 390px. Rimpicciolirli lì li rimette su una riga sola. */
  @media(max-width:1199px){
    .prog-row .ract-line .btn{font-size:11.5px;padding:6px 10px;}
    .prog-row .ract-line .btn.sm{font-size:11px;padding:5px 9px;}
  }
  /* Blocco azioni a destra: stesse classi delle schede di rifinizione
     (.ractions.rcol = colonna, badge sopra in .rstate-row, pulsanti sotto in
     .ract-line) così badge e pulsanti non si sovrappongono mai. */
  .prog-row>.ractions.rcol{flex-shrink:0;}
  /* Comando di stato «⟳ Stato ▾» (v19.2): una sola pastiglia al posto dei due
     pulsanti → Pronta / → Consegnata, che stavano agli estremi del blocco con
     tre comandi in mezzo. Misurato: il blocco azioni passa da 514 a 402px.
     Il colore è quello di .btn.adv (verde tenue): è un avanzamento di stato,
     non un'azione neutra come il «⋯». Il menu è il condiviso .ordmenu. */
  .pst-wrap{position:relative;display:inline-flex;}
  .pst-btn{font:inherit;font-size:12.5px;font-weight:750;line-height:1;white-space:nowrap;cursor:pointer;
    display:inline-flex;align-items:center;justify-content:center;gap:5px;
    border:1px solid var(--soft-bd);background:var(--soft);color:var(--green);
    border-radius:9px;padding:8px 12px;-webkit-tap-highlight-color:transparent;
    transition:background var(--d-fast) var(--ease),border-color var(--d-fast) var(--ease);}
  @media(hover:hover){ .pst-btn:hover{border-color:var(--green2);} }
  .pst-btn:active{transform:scale(.97);}
  .prog-badge{flex-shrink:0;font-size:12px;font-weight:800;padding:5px 11px;border-radius:8px;white-space:nowrap;}
  .prog-badge-late{background:var(--st-late-bg);color:var(--st-late-tx);}
  .prog-badge-soon{background:var(--st-lav-bg);color:var(--st-lav-tx);}
  .prog-badge-ok{background:var(--st-cons-bg);color:var(--st-cons-tx);}
  /* Riepilogo Programma: chip cliccabili In ritardo / In scadenza / In tempo */
  .prog-sum{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px;}
  .prog-sumchip{font:inherit;font-size:12.5px;font-weight:800;padding:7px 14px;border-radius:999px;border:1.5px solid transparent;background:var(--sidebar);color:var(--muted);cursor:pointer;display:inline-flex;align-items:center;gap:7px;line-height:1;}
  .prog-sumchip b{font-size:13.5px;}
  .prog-sumchip:hover{filter:brightness(.97);}
  .prog-sumchip.on{border-color:currentColor;box-shadow:0 0 0 3px rgba(0,0,0,.045);}
  .prog-sumchip.late{background:var(--st-late-bg);color:var(--st-late-tx);}
  .prog-sumchip.soon{background:var(--st-lav-bg);color:var(--st-lav-tx);}
  .prog-sumchip.ok{background:var(--st-cons-bg);color:var(--st-cons-tx);}
  /* Sezioni comprimibili (In tempo / Tinture-Rasature) */
  .prog-collapse{width:100%;font:inherit;text-align:left;background:var(--panel);border:1px solid var(--line2);border-radius:11px;padding:11px 14px;cursor:pointer;display:flex;align-items:center;gap:9px;font-size:12.5px;font-weight:800;color:var(--ink);-webkit-tap-highlight-color:transparent;transition:background var(--d-fast),border-color var(--d-fast);}
  .prog-collapse:hover{background:var(--soft);}
  .prog-collapse .pc-chev{color:var(--muted);font-size:11px;flex:0 0 auto;}
  .prog-collapse>span:nth-child(2){flex:0 1 auto;}
  .prog-collapse .prog-count{margin-left:auto;}
  .prog-collapse-body{margin-top:10px;}
  /* Sezione «Trasferite in rifinizione» delle pagine Tinture e Rasature: riusa
     il .prog-collapse del Programma, staccato SOTTO l'elenco delle attive. Il
     .listwrap interno tiene i suoi bordi anche su telefono (dove quello della
     lista principale li perde): è proprio lo stacco a dire che è un'altra cosa. */
  #tint-trasf,#ras-trasf{margin-top:16px;}
  #tint-trasf .prog-section,#ras-trasf .prog-section{margin-bottom:0;}
  #tint-trasf .prog-collapse .prog-count,#ras-trasf .prog-collapse .prog-count{font-variant-numeric:tabular-nums;}
  /* Riga "✓ Consegnata" che resta finché non aggiorni la pagina */
  .prog-row.prog-done{opacity:.75;border-left-color:var(--st-cons)!important;background:var(--st-cons-bg);cursor:default;}
  .prog-row.prog-done .prog-num,.prog-row.prog-done .prog-art{text-decoration:line-through;text-decoration-color:var(--muted2);}
  .prog-donebadge{font-size:12px;font-weight:800;border-radius:8px;padding:5px 11px;background:var(--st-cons);color:#fff;white-space:nowrap;display:inline-flex;gap:5px;align-items:center;}
  .prog-donenote{font-size:11px;font-weight:600;color:var(--muted);text-transform:none;letter-spacing:0;}

  /* ══════════════════════════════════════════════════════════════════
     ANIMAZIONI & MICRO-INTERAZIONI (fluidità UI, mobile + desktop)
     Tutto su transform/opacity (GPU), durate brevi, curve condivise.
     Il blocco prefers-reduced-motion in fondo neutralizza tutto.
     ══════════════════════════════════════════════════════════════════ */

  /* ── Transizione tra le viste: ogni cambio pagina entra in dissolvenza ──
     Le <section> passano da display:none (hidden) a visibile: l'animazione
     si ri-esegue ad ogni showView senza bisogno di JS. */
  /* ⚠️ LA CORSA È IN PERCENTUALE, NON IN PIXEL (v20.1), E VALE 22% (v20.2).
     La pagina nuova entra con una direzione invece di sfumare sul posto, e la
     uscente fa il movimento speculare — la accompagna `_uscitaVista` (app.5),
     che la tiene a vista staccandola dal flusso.
     Due giri per arrivarci, ed è la parte da ricordare: la prima stesura usava
     14px FISSI, che su un monitor si vedono e su un iPhone da 390 sono il 3,6%
     dello schermo, cioè niente; la seconda li ha portati in percentuale ma al
     7%, e l'utente ha risposto «è cambiato pochissimo». Il motivo è che le due
     pagine si INCROCIANO IN TRASPARENZA — la uscente resta sopra e sfuma
     mentre la nuova appare sotto — e con una corsa breve fra due elenchi che si
     somigliano l'occhio legge una dissolvenza, non una direzione: sotto una
     certa ampiezza l'effetto non esiste, non è che si vede poco.
     Il 22% è stato scelto su un banco a grandezza reale (quattro modi provati
     col dito sul telefono); il tetto in pixel evita che su un monitor largo la
     pagina attraversi mezza scrivania. Misure: 86px su iPhone e 120 sul
     computer per l'ingresso, 55 e 76 per l'uscita — più contenuta, perché
     quella pagina sta già sfumando.
     ⚠️ Col segno negativo il limite si scrive `max()`, non `min()`: fra -55 e
     -76 il «più piccolo» è -76, cioè il contrario di quello che serve.
     ⚠️ TRANSFORM E OPACITÀ HANNO DURATE DIVERSE (--d-slow contro --d), e sono
     due animazioni separate apposta: l'opacità arriva prima, quindi la pagina
     è già leggibile mentre finisce di posarsi. Con una corsa di 86px e un
     unico keyframe si leggerebbe l'ultimo tratto ancora in trasparenza. */
  @keyframes viewIn{from{transform:translateX(min(22%,120px))}to{transform:none}}
  @keyframes viewInFade{from{opacity:0}to{opacity:1}}
  @keyframes viewOut{from{opacity:1;transform:none}to{opacity:0;transform:translateX(max(-14%,-76px))}}
  section[id^="view-"]:not([hidden]){
    animation:viewIn var(--d-slow) var(--ease-out), viewInFade var(--d) var(--ease-out);}
  /* ⚠️ `top/left/right`, NON `inset:0`: il contenitore di riferimento è
     `.content` (che è `contain:layout`), e il suo padding box è alto quanto
     TUTTO il contenuto scorrevole — con `bottom:0` la pagina uscente si
     stirerebbe per l'intera lunghezza dell'elenco. Qui tiene la sua altezza
     naturale, e lo scorrimento è comunque già stato riportato in cima. */
  /* ⚠️ Curva `--ease`, NON `--ease-in`. Su un'uscita che dura due decimi e si
     sposta di venti pixel, una curva che accelera tiene l'elemento quasi fermo
     per i primi due terzi: misurato, la pagina uscente faceva 4,7px prima di
     sparire — cioè il movimento non esisteva. `--ease-in` resta giusta dove
     una cosa se ne va DAVVERO (una riga che scorre fuori dall'elenco), non
     dove arretra di poco sfumando. */
  section[id^="view-"].view-esce{
    position:absolute;top:0;left:0;right:0;pointer-events:none;z-index:1;overflow:hidden;
    animation:viewOut var(--d) var(--ease) forwards;}
  /* ⚠️ IL RITAGLIO LATERALE È OBBLIGATORIO, ed è il prezzo della corsa ampia
     (v20.2). `.content` ha `overflow-y:auto` e nessun `overflow-x`, quindi
     quest'ultimo è computato `auto`: una pagina che parte 86px a destra è
     contenuto oltre il bordo, e il browser tira su la barra di scorrimento
     ORIZZONTALE per la durata del movimento — misurati 67px su iPhone e 96 sul
     computer. `clip` ritaglia senza creare un contenitore di scorrimento
     (`hidden` lo creerebbe, restando scorrevole da JS).
     ⚠️ La classe la mette e la toglie `_ritagliaDuranteCambio` (app.5): a
     riposo NON si tocca l'overflow, o una tabella larga dentro una pagina
     diventerebbe irraggiungibile. Dove `clip` non è supportato la
     dichiarazione cade e si torna al comportamento di prima: ripiego innocuo. */
  .content.in-transito{overflow-x:clip;}
  @media (prefers-reduced-motion:reduce){
    section[id^="view-"].view-esce{display:none!important;}
  }

  /* ── UNA CASCATA SOLA per l'ingresso delle righe (v20) ────────────────────
     La classe `.view-anim` la mette showView sulla <section> quando si entra
     in una pagina; `.list-in` la mette render() su #list quando l'elenco si
     ricostruisce davvero (un chip di stato, un filtro). Sono due momenti
     diversi ma è lo STESSO movimento, e fino alla v19.100 erano due sistemi
     paralleli: `listRowIn` (16 righe, ritardi fino a 380ms, tutte le
     larghezze) e `rigaIn` (12 righe, 242ms, solo ≤640px), con keyframe,
     durate e scale di ritardo diverse.
     ⚠️ Si SOVRAPPONEVANO: entro gli 800ms dall'ingresso in pagina #list ha
     `.list-in` e la sezione ha `.view-anim`, e vinceva `listRowIn` per
     specificità — quindi sul telefono si pagava il codice della cascata
     mobile e si vedeva quella pensata per il computer.
     Ora il contenitore è uno dei due, indifferentemente, e la regola è una.
     ⚠️ I ritardi qui e `LISTA_CASCATA_MS` in app.3 devono restare allineati:
     quella costante è il momento in cui la classe viene tolta. */
  @keyframes rigaIn{from{opacity:0;transform:translateY(9px)}to{opacity:1;transform:none}}
  :is(.view-anim,.list-in) :is(.row,.arow,.prog-row,.colc-card,.kpi,.card,.glotti-tipo):nth-child(-n+12){
    animation:rigaIn var(--d-slow) var(--ease-out) backwards;}
  :is(.view-anim,.list-in) :is(.row,.arow,.prog-row,.colc-card,.kpi,.card,.glotti-tipo):nth-child(1){animation-delay:0ms}
  :is(.view-anim,.list-in) :is(.row,.arow,.prog-row,.colc-card,.kpi,.card,.glotti-tipo):nth-child(2){animation-delay:22ms}
  :is(.view-anim,.list-in) :is(.row,.arow,.prog-row,.colc-card,.kpi,.card,.glotti-tipo):nth-child(3){animation-delay:44ms}
  :is(.view-anim,.list-in) :is(.row,.arow,.prog-row,.colc-card,.kpi,.card,.glotti-tipo):nth-child(4){animation-delay:66ms}
  :is(.view-anim,.list-in) :is(.row,.arow,.prog-row,.colc-card,.kpi,.card,.glotti-tipo):nth-child(5){animation-delay:88ms}
  :is(.view-anim,.list-in) :is(.row,.arow,.prog-row,.colc-card,.kpi,.card,.glotti-tipo):nth-child(6){animation-delay:110ms}
  :is(.view-anim,.list-in) :is(.row,.arow,.prog-row,.colc-card,.kpi,.card,.glotti-tipo):nth-child(7){animation-delay:132ms}
  :is(.view-anim,.list-in) :is(.row,.arow,.prog-row,.colc-card,.kpi,.card,.glotti-tipo):nth-child(8){animation-delay:154ms}
  :is(.view-anim,.list-in) :is(.row,.arow,.prog-row,.colc-card,.kpi,.card,.glotti-tipo):nth-child(9){animation-delay:176ms}
  :is(.view-anim,.list-in) :is(.row,.arow,.prog-row,.colc-card,.kpi,.card,.glotti-tipo):nth-child(10){animation-delay:198ms}
  :is(.view-anim,.list-in) :is(.row,.arow,.prog-row,.colc-card,.kpi,.card,.glotti-tipo):nth-child(11){animation-delay:220ms}
  :is(.view-anim,.list-in) :is(.row,.arow,.prog-row,.colc-card,.kpi,.card,.glotti-tipo):nth-child(12){animation-delay:242ms}

  /* ── Ingressi generici ── */
  @keyframes ovIn{from{opacity:0}to{opacity:1}}
  @keyframes popIn{from{opacity:0;transform:translateY(12px) scale(.97)}to{opacity:1;transform:none}}

  /* ── Press feedback (tap/click): leggera compressione su ciò che è cliccabile ── */
  .btn:active,.chip:active,.qbtns button:active,.metric-toggle button:active,
  .ms-btn:active,.cal-nav:active,.cal-day:active,.side-foot button:active,
  .lotto-num-btn:active,.colc-card-lots:active,.chip-x:active,
  .ms-foot button:active,.cal-foot button:active,.lavtool:active,.lavadd:active,
  .nav a:active{transform:scale(.96);}
  .btn:active{transform:scale(.95);}
  /* niente compressione mentre si trascina una voce di menu */
  .nav.reordering a:active{transform:none;}

  /* ── Bottoni rapidi / toggle / chip: transizioni morbide ── */
  .chip,.qbtns button,.metric-toggle button,.chip-x,.x,.ms-foot button,
  .cal-foot button,.lavtool,.lavadd,.side-foot button,.forn-chip button,
  .cal-nav,.cal-day,.csm-head .btn,.bkp-banner button{
    transition:background var(--d-fast) var(--ease),border-color var(--d-fast) var(--ease),color var(--d-fast) var(--ease),transform var(--d-fast) var(--ease),box-shadow var(--d-fast) var(--ease);}
  .x:hover{color:var(--green2);} .chip:hover{filter:brightness(.99);}

  /* ── Toggle/checkbox e righe lavorazione: cambio stato fluido ── */
  .chkbox,.lavitem,.ms-opt,.lavchk{transition:background var(--d-fast) var(--ease),border-color var(--d-fast) var(--ease),color var(--d-fast) var(--ease);}

  /* ── LE SUPERFICI CHE SCATTAVANO (v20) ────────────────────────────────
     Quattordici comandi avevano un effetto al passaggio del mouse ma nessuna
     transizione — misurato a 1440px: cambiavano colore in un fotogramma,
     mentre il comando accanto (spesso nella stessa barra) sfumava. I casi
     più visti: le voci dei menu contestuali, l'indice del modulo sul
     computer, i pulsanti dei reparti nelle Impostazioni, il «← Indietro» del
     dettaglio, la ✕ dei dialoghi — che stava accanto a un `.x` che invece
     transiva. Una riga sola, la stessa transizione di tutto il resto. */
  .pvback,.prog-seg button,.prog-sumchip,.setnav button,.ordmenu button,
  .psm-item,.gs-smi,.dkchip,.sezlist .mn-row,.mnlist .mn-row,.ph-tools button,
  .dlg-x,.acct-dot,.bkp-banner,.ordnum-pill,.gs-scope,.gs-sortbtn{
    transition:background var(--d-fast) var(--ease),border-color var(--d-fast) var(--ease),
      color var(--d-fast) var(--ease),box-shadow var(--d-fast) var(--ease),
      transform var(--d-fast) var(--ease);}

  /* ── Campi form: focus-ring in dissolvenza (bordo + alone) ── */
  .field input,.field select,.field textarea,.search input,select.filter,
  .ms-btn,.qreal input,.lavef-row input,.mix-row input,.mix-row select,
  .lavfield input[data-lavfield],.lavfield select[data-lavfield]{
    transition:border-color var(--d-fast) var(--ease),box-shadow var(--d-fast) var(--ease),background var(--d-fast) var(--ease);}
  .search:focus-within svg{color:var(--green2);opacity:.7;transition:color var(--d-fast),opacity var(--d-fast);}

  /* ── Card/righe interattive: lieve sollevamento (solo desktop hover-capace) ── */
  .iconbtn{transition:background var(--d-fast) var(--ease),transform var(--d-fast) var(--ease);}
  .iconbtn:active{transform:scale(.9);}
  .colc-card{transition:box-shadow var(--d) var(--ease),transform var(--d) var(--ease),border-color var(--d) var(--ease);}
  .prog-row{transition:border-color var(--d-fast) var(--ease),box-shadow var(--d) var(--ease),transform var(--d) var(--ease);}
  .csm-row{transition:background var(--d-fast) var(--ease),transform var(--d-fast) var(--ease),box-shadow var(--d) var(--ease);}
  .arow,.stab tr.clk{transition:background var(--d-fast) var(--ease);}
  @media(hover:hover){
    .colc-card:hover{transform:translateY(-2px);box-shadow:0 10px 26px rgba(15,40,22,.10);border-color:var(--soft-bd);}
    .prog-row:hover{transform:translateY(-1px);}
    .csm-row:hover{transform:translateY(-1px);box-shadow:0 6px 18px rgba(15,40,22,.08);}
    .kpi{transition:transform var(--d) var(--ease),box-shadow var(--d) var(--ease);}
    .kpi:hover{transform:translateY(-2px);box-shadow:0 10px 24px rgba(15,40,22,.08);}
    .gs-item{transition:background var(--d-fast) var(--ease);}
  }

  /* ── Modali a display inline (gestiti in JS): fade del backdrop + pop del box ──
     display:none→flex riavvia l'animazione ad ogni apertura. */
  #gs-overlay,#label-modal,#crust-schede-modal,#lotto-mov-modal{
    animation:ovIn var(--d) var(--ease);}
  #crust-schede-modal .csm-box,#lotto-mov-modal .csm-box,
  #gs-overlay>div,#label-modal>div{animation:popIn var(--d-slow) var(--ease-out);}
  /* ── …E ADESSO ESCONO ANCHE (v20) ─────────────────────────────────────
     Entravano con grazia e sparivano nel fotogramma, perché la chiusura è un
     `style.display='none'` secco. La classe `.va-via` la mette `chiudiModale`
     (app.1) e la toglie insieme al display: l'uscita è più breve
     dell'ingresso, come vuole la scala (--d-exit). */
  #label-modal.va-via,#crust-schede-modal.va-via,#lotto-mov-modal.va-via{
    animation:ovOut var(--d-exit) var(--ease-in) forwards;}
  #label-modal.va-via>div,#crust-schede-modal.va-via .csm-box,
  #lotto-mov-modal.va-via .csm-box{animation:popOut var(--d-exit) var(--ease-in) forwards;}
  @keyframes ovOut{from{opacity:1}to{opacity:0}}
  @keyframes popOut{from{opacity:1;transform:none}to{opacity:0;transform:translateY(8px) scale(.985)}}

  /* ── Nessun lampo grigio di sistema al tocco ──────────────────────
     iOS/Android disegnano un rettangolo grigio su ogni elemento toccabile.
     Era disattivato su 6 elementi (.btn, .rib, .pvb-btn…) e attivo su tutti
     gli altri: righe, chip, voci di menu, celle. Qui vale per tutto ciò che
     si tocca; il feedback vero lo danno gli stati :active qui sotto. */
  a,button,[role="button"],[data-open],.row,.arow,.prog-row,.statechip,.ms-btn,.ms-opt,
  .csm-row,.gs-item,.gs-chip,.gs-scope,.dkchip,.pvst-btn,.ord-rigachip,.colc-card,
  .ssp-btn,.chip,.cal-day{-webkit-tap-highlight-color:transparent;}
  /* Risposta al tocco sulle righe: prima il tocco non dava alcun segnale
     (l'unico effetto era l'hover, che su telefono non esiste). */
  /* ⚠️ `.995` era SOTTO LA SOGLIA PERCETTIVA (v20): su una riga alta 107px
     sono mezzo pixel, cioè nessun segnale. Il telefono aveva già il suo
     (`.978`, nel blocco ≤640px); questo vale sul computer, dove la pressione
     è più breve e basta meno. */
  .row:active,.prog-row:active,.arow:active{transform:scale(.985);}
  .statechip:active,.dkchip:active{transform:scale(.96);}
  .row,.arow{transition:background var(--d-fast) var(--ease),box-shadow var(--d-fast) var(--ease),transform var(--d-fast) var(--ease);}
  .statechip{transition:background var(--d-fast) var(--ease),color var(--d-fast) var(--ease),border-color var(--d-fast) var(--ease),transform var(--d-fast) var(--ease);}

  /* ── LA RIGA CHE LASCIA L'ELENCO (v20) ────────────────────────────────
     Archiviare o cestinare faceva sparire la riga nello STESSO fotogramma, e
     le righe sotto saltavano su: proprio nel momento in cui si vuole la
     conferma che l'azione è andata a segno, l'app non dava nessun segnale —
     su tre righe simili non si capiva nemmeno quale fosse andata via.
     Esce verso DESTRA, cioè nella direzione in cui è stata mandata via, e lo
     spazio si richiude sopra di lei.
     ⚠️ ECCEZIONE DICHIARATA alla regola «solo transform e opacity»: il
     `margin-bottom` è una proprietà di layout. Vale per UN elemento e per
     180ms, ed è l'unico modo di far collassare lo spazio in un contenitore
     flex; l'alternativa — lasciare che le righe sotto risalgano di scatto a
     fine animazione — rimetterebbe il salto che si sta togliendo.
     ⚠️ `--via-h` la scrive `uscitaRiga` (app.3) misurando la riga: comprende
     il `gap` del contenitore, tranne sull'ultima riga, che gap non ne ha. */
  /* La riga appena salvata si accende e si spegne da sola. ⚠️ L'anello va
     nello STESSO box-shadow della corsia di stato, o salvando una scheda il
     colore del suo stato sparirebbe per un secondo e mezzo. */
  .row.row-fatta{animation:rigaFatta 1.5s var(--ease-out);}
  @keyframes rigaFatta{
    0%{box-shadow:inset 4px 0 0 var(--corsia,transparent),0 0 0 2px var(--st-cons),var(--sh-sm)}
    100%{box-shadow:inset 4px 0 0 var(--corsia,transparent),0 0 0 0 rgba(0,0,0,0),var(--sh-sm)}
  }
  @media (prefers-reduced-motion:reduce){ .row.row-fatta{animation:none;} }

  .row.row-via,.arow.row-via{
    /* ⚠️ `animation:none` NON è di troppo: se la riga sta ancora entrando con
       la cascata, i valori dell'ANIMAZIONE vincono su qualunque regola normale
       (è la loro precedenza nella cascata CSS) e l'uscita non si vedrebbe —
       succede archiviando entro mezzo secondo dall'ingresso in pagina. */
    animation:none!important;
    opacity:0;transform:translateX(34%) scale(.97);pointer-events:none;
    margin-bottom:calc(-1 * var(--via-h,0px));
    transition:opacity var(--d-exit) linear,
      transform var(--d-exit) var(--ease-in),
      margin-bottom var(--d-exit) var(--ease);
  }
  @media (prefers-reduced-motion:reduce){
    .row.row-via,.arow.row-via{transition:none!important;}
  }

  /* ── LA FRECCINA RUOTA, NON VIENE SCAMBIATA (v20) ─────────────────────
     In cinque punti il chevron era un CARATTERE sostituito da JS (▸ ↔ ▾):
     saltava anche dove il corpo si apriva morbido, cioè due segnali che
     raccontavano cose diverse nello stesso gesto. Ora il glifo è sempre ▸ e a
     girare è l'elemento; chi apre e chiude commuta la classe `.giu`.
     ⚠️ Serve `display:inline-block`: su un `<span>` in flusso il transform
     non si applica. */
  .pc-chev,.clisec-caret,.fold-chev{
    display:inline-block;transition:transform var(--d) var(--ease);}
  .pc-chev.giu,.clisec-caret.giu,.fold-chev.giu{transform:rotate(90deg);}
  @media (prefers-reduced-motion:reduce){
    .pc-chev,.clisec-caret,.fold-chev{transition:none!important;}
  }

  /* ── LA SPIA DEL SALVATAGGIO PULSA MENTRE È IN VOLO (v20) ─────────────
     Il pallino accanto al nome utente saltava da un colore all'altro e
     l'orario cambiava di scatto: fra il tocco su «Salva» e la conferma non
     c'era nessun segnale, e su rete lenta sembrava che non fosse successo
     niente. La classe la mette `updateAcctDot` (app.1) quando c'è almeno un
     salvataggio in volo. */
  .acct-dot.salvando{animation:pulsaSpia 1.1s var(--ease) infinite;}
  @keyframes pulsaSpia{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.45;transform:scale(.78)}}
  @media (prefers-reduced-motion:reduce){ .acct-dot.salvando{animation:none;} }

  /* Mentre si applica il tema nuovo, nessuna transizione: vedi
     `_temaSenzaAnimazioni` in app.8. Gemella di `.bar-nofx`, che fa la stessa
     cosa quando il guscio deve MISURARSI. */
  html.tema-cambio *,html.tema-cambio *::before,html.tema-cambio *::after{
    transition:none!important;animation-duration:.001ms!important;}

  /* ── Spinner riutilizzabile (stati di caricamento) ── */
  @keyframes spin{to{transform:rotate(360deg)}}
  .spin{animation:spin .8s linear infinite;}

  /* ══════════════════════════════════════════════════════════════════
     Rispetto delle preferenze utente: riduce/annulla il movimento.
     ══════════════════════════════════════════════════════════════════ */
  @media (prefers-reduced-motion: reduce){
    *,*::before,*::after{
      animation-duration:.001ms !important;
      animation-iteration-count:1 !important;
      transition-duration:.001ms !important;
      scroll-behavior:auto !important;
    }
  }

  /* ===== Tema scuro (pagina Impostazioni → Aspetto) ===========================
     Scoped a @media screen così la stampa (PDF/etichette) resta SEMPRE chiara.
     Quasi tutte le superfici usano già var(--panel/--card/--bg): qui ridefiniamo
     il palette e correggiamo le poche superfici con bianco hardcoded. La sidebar
     è verde scuro anche in chiaro, quindi resta coerente. */
  @media screen {
    html[data-theme="dark"]{
      --bg:#0f1820; --panel:#172230; --card:#172230; --sidebar:#111c27;
      --line:#22303d; --border:#283643; --line2:#384654;
      --ink:#e7eef5; --muted:#9bb0c2; --muted2:#74899c;
      --soft:#16301D; --soft-bd:#1F3A26;
      --amber-bg:#37300f; --amber-tx:#e7c87a; --amber-bd:#5a4d1d;
      --st-bozza-bg:#222d38; --st-bozza-tx:#9fb0c0;
      --st-lav-bg:#382e10; --st-lav-tx:#e8c06c;
      --st-pronta-bg:#16273f; --st-pronta-tx:#7fb2f7;
      --st-cons-bg:#12301A; --st-cons-tx:#5DCB79;
      --st-late-bg:#3a1d1d; --st-late-tx:#f08a8a;
      --catena-bg:#281e40; --catena-tx:#b79de8; --catena-bd:#463366;
      --sh-sm:0 1px 2px rgba(0,0,0,.45); --sh:0 2px 10px rgba(0,0,0,.5); --sh-lg:0 18px 48px rgba(0,0,0,.62);
      /* Il verde di TESTO: in chiaro è #003C0F (il verde del logo, pensato per
         il fondo bianco). Restava tale anche qui, dando ~1,3:1 su .btn.ghost,
         .pill.dest, .tlink e su un centinaio di altri selettori — praticamente
         invisibile. La versione chiara sale a ~9:1 sul fondo scuro, e come in
         chiaro --green e --green2 sono lo STESSO valore: un verde solo.
         ⚠️ --green è usato ANCHE come sfondo pieno con testo bianco (~20
         selettori, elencati più sotto): quelli vanno riportati al verde scuro
         con --green-solid, o diventano bianco su verde chiaro. */
      --green:#8EE1A3; --green2:#8EE1A3; --green3:#B5E3C1;
      --green-solid:#003C0F;   /* sfondo pieno dei controlli attivi: bianco sopra = 12,70:1 */
      /* Gemelli di --amber-*: vedi la nota in :root. */
      --green-bg:#16301D; --green-bd:#2C4E36; --green-tx:#8EE1A3;
      /* L'anello di messa a fuoco scuro su fondo scuro non si vedeva: qui è chiaro. */
      --ring:rgba(142,225,163,.22);
    }
    /* I "pieni verdi" (controllo attivo, contatore, giorno selezionato…) tengono
       testo bianco: qui lo sfondo torna scuro — 12,70:1 — invece di seguire il
       nuovo --green chiaro.
       ⚠️ SEI di questi mancavano all'elenco e davano bianco su verde chiaro:
       misurati 1,74÷2,21:1, cioè testo invisibile. Sono gli ultimi sei della
       lista. Se aggiungi un fondo verde con testo bianco, aggiungilo QUI:
       è l'unico modo di accorgersene senza aprire il tema scuro a mano. */
    html[data-theme="dark"] .metric-toggle button.on,
    html[data-theme="dark"] .qbtns button.on,
    html[data-theme="dark"] .ms-btn .ms-count,
    html[data-theme="dark"] .sort-btn.active,
    html[data-theme="dark"] .cal-day.sel,
    html[data-theme="dark"] .colc-fgroup-count,
    html[data-theme="dark"] .colc-view-seg button.on,
    html[data-theme="dark"] .gs-scope.on,
    html[data-theme="dark"] .ssp-count,
    html[data-theme="dark"] .mix-modeseg button.on,
    html[data-theme="dark"] .mixunit-seg button.on,
    html[data-theme="dark"] .clo-d.on,
    html[data-theme="dark"] .totop,
    html[data-theme="dark"] .pvb-btn.pri,
    html[data-theme="dark"] .prog-seg button.on,
    html[data-theme="dark"] .cost-unit-seg button.on,
    html[data-theme="dark"] .btn.qr,
    html[data-theme="dark"] .pvst-btn.on,
    html[data-theme="dark"] .ms-opt.kbd-hl,
    html[data-theme="dark"] #ios-install-banner,
    html[data-theme="dark"] .mn-av,
    html[data-theme="dark"] .filtbtn.hot{ background:var(--green-solid); color:#fff;
      /* ⚠️ L'ANELLO NON È DECORAZIONE. Il pieno #003C0F contro la card --panel
         (#172230) sta a 1,26:1: il testo bianco dentro si legge benissimo
         (12,70:1) ma la FORMA del controllo non si stacca dal riquadro che lo
         ospita, e WCAG 1.4.11 ne chiede 3 per i componenti. Era già così prima
         (1,37 col vecchio verde), ma con un verde solo il pieno diventa
         l'unico modo di dire «questo è attivo»: l'anello lo rende visibile
         (6,61:1 fra --green2 e --green-solid).
         ⚠️ È un `box-shadow:inset`, NON un `border`: un bordo vero
         allargherebbe la scatola di 2px e sposterebbe venti controlli — fra
         cui il tondo del «torna in cima» e i contatori dentro le tendine. */
      box-shadow:inset 0 0 0 1px var(--green2); }
    html[data-theme="dark"] .appbar{ background:var(--panel); }
    html[data-theme="dark"] .dlgbox,
    html[data-theme="dark"] .pvbox,
    html[data-theme="dark"] .kbdhelp-box,
    html[data-theme="dark"] .kbdhelp-head{ background:var(--panel); }
    html[data-theme="dark"] .pill{ background:var(--panel); }
    /* Superfici col bianco scritto a mano: senza override restavano bianche con
       il testo chiaro sopra (1,17:1). La modale schede-per-crust e quella delle
       movimentazioni lotto erano del tutto illeggibili. */
    html[data-theme="dark"] .csm-box,
    html[data-theme="dark"] .csm-head,
    html[data-theme="dark"] .calc-tools,
    html[data-theme="dark"] .calc-inp,
    html[data-theme="dark"] .lst,
    html[data-theme="dark"] .margine-box .mchip,
    html[data-theme="dark"] .csm-sg-tipo,
    html[data-theme="dark"] .pvcrust-h .tag{ background:var(--panel); }
    /* .stockbadge ha ANCHE il colore del testo scritto a mano (#7a5912, ambra
       scuro per il fondo bianco): va schiarito, o resta 2,5:1 sul fondo scuro. */
    html[data-theme="dark"] .stockbadge{ background:var(--panel); color:var(--amber-tx); }
    /* Box "valori non più in elenco": fondo crema + testo ambra scuro, entrambi
       scritti a mano per il tema chiaro — sul fondo scuro sarebbe illeggibile. */
    html[data-theme="dark"] .ssp-orfbox{ background:#2a2318; border-color:#5c4a26; }
    html[data-theme="dark"] .ssp-warn{ color:var(--amber-tx); }
    html[data-theme="dark"] .ssp-chip.orf{ background:var(--card); border-color:#5c4a26; }
    html[data-theme="dark"] .ssp-orfn{ background:#3d3320; color:var(--amber-tx); }
    html[data-theme="dark"] .lav-suggest .lsug-chip{ background:var(--panel); color:var(--green); border-color:var(--border); }
    html[data-theme="dark"] .field input:disabled,
    html[data-theme="dark"] .field select:disabled,
    html[data-theme="dark"] .field textarea:disabled,
    html[data-theme="dark"] .field.locked input,
    html[data-theme="dark"] .field.locked select,
    html[data-theme="dark"] .field.locked textarea{ background:#1f2937; color:#6b7c8e; }
    html[data-theme="dark"] .row.rst-late{ background:#2a1a1a; }
    @media(hover:hover){ html[data-theme="dark"] .row.rst-late:hover{ background:#321e1e; } }
    /* intestazione liste, hover di righe/tabelle e pulsanti ghost */
    html[data-theme="dark"] .listhead{ background:linear-gradient(180deg,#1a2532,#141d28); }
    @media(hover:hover){ html[data-theme="dark"] .row:hover,
      html[data-theme="dark"] .arow:hover,
      html[data-theme="dark"] .stab tr.clk:hover{ background:#1d2835; } }
    html[data-theme="dark"] .row:active{ background:#212d3a; }
    html[data-theme="dark"] .btn.ghost:hover{ background:#1d2835; }
    html[data-theme="dark"] tr.stockrow:hover{ background:#2a2410 !important; }
    /* chip/badge con tinte chiare hardcoded → tinte scure leggibili */
    html[data-theme="dark"] .fermo{ background:var(--st-bozza-bg); color:var(--muted); }
    html[data-theme="dark"] .fermo.late{ background:var(--st-late-bg); color:var(--st-late-tx); }
    html[data-theme="dark"] .fermo.warn{ background:var(--st-lav-bg); color:var(--st-lav-tx); }
    html[data-theme="dark"] .chip.chip-pronta{ background:var(--st-pronta-bg); color:var(--st-pronta-tx); border-color:transparent; }
    html[data-theme="dark"] .chip.rem{ background:#3a1d1d; color:#f0a890; border-color:transparent; }
    html[data-theme="dark"] .cost-remind{ background:#2e2410; color:#e8c778; border-color:#4a3a18; }
    html[data-theme="dark"] .st-trasf{ background:#2e2350; color:#c4b5fd; }
    html[data-theme="dark"] .pvtrasf{ background:#211b3a; border-color:#3b2f66; }
    html[data-theme="dark"] .pvtrasf-h{ color:#c4b5fd; }
    html[data-theme="dark"] .margine-box.pos .mchip-marg{ background:var(--st-cons-bg); border-color:transparent; }
    html[data-theme="dark"] .margine-box.neg .mchip-marg{ background:var(--st-late-bg); border-color:transparent; }
    html[data-theme="dark"] .ms.managed.disabled .ms-btn,
    html[data-theme="dark"] .ms.locked .ms-btn{ background:#1f2937; color:#6b7c8e; }
    /* Metri reali: --green è il verde SCURO del logo e in chiaro sta bene sul
       bianco, ma qui va ridefinito o le etichette spariscono su --soft scuro;
       il bordo di .qreal era un letterale chiaro su fondo scuro (ora --soft-bd,
       quindi segue il tema da sé). */
    html[data-theme="dark"] .qreal{ border-color:var(--soft-bd); }
    html[data-theme="dark"] .qreal label,
    html[data-theme="dark"] .pvreal-h,
    html[data-theme="dark"] .pvreal-f .l{ color:var(--green); }
    /* dimensione testo (Aspetto): ingrandisce proporzionalmente l'area contenuti */
    html[data-textsize="large"] .content{ zoom:1.12; }
  }

  /* ── Tooltip bottoni-icona (app.tooltip.js) — bollicina scura compatta, tema-aware ── */
  .app-tip{
    position:fixed; left:-9999px; top:0; z-index:99999; pointer-events:none;
    max-width:260px; padding:6px 10px; border-radius:8px;
    background:#1e2f42; color:#f4f7fb;
    font:600 12.5px/1.35 var(--sans); letter-spacing:.1px;
    box-shadow:0 8px 24px -6px rgba(15,36,56,.45), 0 2px 6px rgba(15,36,56,.28);
    opacity:0; transform:translateY(3px);
    transition:opacity var(--d-fast) var(--ease), transform var(--d-fast) var(--ease);
    white-space:normal; word-break:break-word;
  }
  .app-tip.ready{ visibility:visible; }
  .app-tip.show{ opacity:1; transform:translateY(0); }
  .app-tip-arrow{
    position:absolute; left:12px; bottom:-5px; width:10px; height:10px;
    background:#1e2f42; transform:rotate(45deg); border-radius:2px;
    box-shadow:2px 2px 4px rgba(15,36,56,.14);
  }
  .app-tip.below .app-tip-arrow{ bottom:auto; top:-5px; box-shadow:-2px -2px 4px rgba(15,36,56,.14); }
  html[data-theme="dark"] .app-tip{
    background:#26374e; color:#eaf1fa;
    border:1px solid rgba(255,255,255,.12);
    box-shadow:0 10px 30px -8px rgba(0,0,0,.6);
  }
  html[data-theme="dark"] .app-tip-arrow{ background:#26374e; box-shadow:none; }
  @media (prefers-reduced-motion: reduce){
    .app-tip{ transition:none; transform:none; }
    .app-tip.show{ transform:none; }
  }


  /* ══════════════════════════════════════════════════════════════════
     GUSCIO MOBILE — foglio filtri + barra che si ritrae (app.9g)
     ──────────────────────────────────────────────────────────────────
     Tutto quanto segue vale SOLO sotto i 640px: su desktop la barra dei
     filtri resta esattamente com'era. */
  /* ⚠️ IL VELO DURA QUANTO IL FOGLIO (v20). Era `--d` (200ms) contro i 300 del
     foglio e i 300 dell'arretramento dello sfondo: tre durate diverse nello
     stesso gesto, col velo che finiva 100ms prima e lasciava l'ultimo tratto
     del foglio a salire su uno sfondo già schiarito. Un gesto, un tempo. */
  .filtsheet-dim{position:fixed;inset:0;z-index:78;background:rgba(9,20,30,.42);opacity:0;pointer-events:none;
    transition:opacity var(--d-slow) var(--ease);}
  .filtsheet-dim.open{opacity:1;pointer-events:auto;}
  .filtsheet{position:fixed;left:0;right:0;bottom:0;z-index:79;background:var(--panel);
    border-radius:18px 18px 0 0;box-shadow:0 -12px 40px rgba(9,20,30,.28);
    transform:translateY(102%);transition:transform var(--d-slow) var(--ease-out);
    max-height:82vh;display:flex;flex-direction:column;pointer-events:none;}
  .filtsheet.open{transform:none;pointer-events:auto;}
  .filtsheet-grab{width:38px;height:4px;border-radius:99px;background:var(--line2);margin:9px auto 4px;flex:0 0 auto;}
  .filtsheet-head{display:flex;align-items:center;gap:10px;padding:4px 14px 10px;border-bottom:1px solid var(--line);flex:0 0 auto;}
  .filtsheet-head b{font-size:16px;font-weight:800;flex:1;}
  .filtsheet-body{padding:14px;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;flex:1;
    display:flex;flex-direction:column;gap:12px;align-items:stretch;}
  /* i filtri, qui dentro, prendono tutta la riga: nel foglio c'è spazio */
  .filtsheet-body>.ms{width:100%;}
  .filtsheet-body>.ms .ms-btn{width:100%;justify-content:space-between;min-height:46px;}
  .filtsheet-body>.qbtns{flex-wrap:wrap;overflow:visible;gap:8px;}
  .filtsheet-body>.qbtns>*{flex:0 0 auto;}
  .filtsheet-body>.btn.reset{display:none;} /* c'è già «Azzera» nell'intestazione */
  .filtsheet-foot{padding:10px 14px calc(12px + env(safe-area-inset-bottom));border-top:1px solid var(--line);flex:0 0 auto;}
  .filtsheet-foot .btn{width:100%;justify-content:center;min-height:48px;font-size:15px;}
  body.filtsheet-on{overflow:hidden;}
  /* il pulsante che apre il foglio, con quanti filtri sono attivi */
  .filtbtn{flex:0 0 auto;gap:6px;}
  .filtbtn.hot{background:var(--green);color:#fff;border-color:var(--green);}
  .filtbtn .filtbtn-n{background:var(--panel);color:var(--green);border-radius:999px;
    font-size:11.5px;font-weight:800;padding:1px 7px;line-height:1.5;}
  .filtbtn:not(.hot) .filtbtn-n{display:none;}

  /* ══════════════════════════════════════════════════════════════════
     Barre che si ritirano scorrendo — SENZA balzi di scroll
     ──────────────────────────────────────────────────────────────────
     ⚠️ È ARCHITETTURA, non un dettaglio di stile. Su mobile appbar, barra
     filtri e barra in basso ESCONO DAL FLUSSO e diventano un overlay sopra
     un contenitore di scorrimento ad altezza fissa; lo spazio che occupano
     è riservato dai padding di .content (--chrome-h sopra, --pb-tabbar
     sotto), che NON cambiano MAI.
     A RITIRARSI però sono le sole DUE barre in alto: quella in basso resta
     ferma (v16.8) — vale a dire che l'overlay serve a entrambe le cose, a
     ritirarsi senza balzi e a stare ferme senza rubare spazio.
     Perché: finché una barra sta nel flusso, nasconderla ACCORCIA .content,
     e accorciare il contenitore di scorrimento sposta il contenuto rispetto
     allo schermo — 117px di scatto in un fotogramma. L'utente lo ha
     segnalato due volte («scorre troppo», «fa scorrere tutto più su
     velocemente») e nessuna soglia lo può evitare: è geometria.
     In overlay nascondere è solo un `transform`: il contenuto non si muove
     di un pixel e si SCOPRE quello che stava sotto la barra — che è anche
     il modo in cui si guadagna spazio. È il comportamento delle app native.
     ⚠️ Non legare i padding allo stato ritirato «per recuperare altro
     spazio»: rimetterebbe i balzi esattamente com'erano.

     Il movimento segue il dito 1:1 (variante B approvata dall'utente), quindi
     ogni fotogramma deve costare SOLO compositing: si animano `transform` e
     `opacity`, mai una proprietà che ricalcoli il layout. Da qui due scelte:
     • il subbar sale fino a filo della safe-area e NON oltre, invece di salire
       a top:0 crescendo di padding per non finire sotto l'orologio — quel
       padding era un reflow per fotogramma;
     • la fascia del notch la copre `.main::before`, ferma e sempre presente
       (a barre visibili sta dietro l'appbar e non si vede).
     Il tratto che il subbar percorre — altezza appbar meno safe-area — È il
     guadagno netto di spazio, ed è quanto vale l'intero gesto. */
  @media(max-width:640px){
    .main{position:relative;}
    /* fascia sotto l'orologio: resta piena mentre l'appbar se ne va */
    .main::before{content:"";position:absolute;top:0;left:0;right:0;z-index:15;
      height:var(--safe-top,env(safe-area-inset-top));background:var(--bg);pointer-events:none;}
    .appbar{position:absolute;top:0;left:0;right:0;z-index:20;
      transition:transform var(--d) var(--ease),opacity var(--d-fast) var(--ease);will-change:transform;}
    /* ⚠️ IL -1px NON È UN RITOCCO ESTETICO: chiude la MICRORIGA in cui si
       vedeva la lista scorrere (segnalata dall'utente, corretta in v19.54).
       `--appbar-h` è `Math.round()` dell'altezza vera: quando quella è, per
       dire, 170,6px l'arrotondamento la porta a 171 e il subbar comincia
       mezzo pixel SOTTO la fine dell'appbar — una fessura sottilissima in cui
       passa il contenuto che scorre. Lo stesso capita a barre ritirate, dove
       il subbar si ferma esattamente a filo della fascia del notch
       (`.main::before`, alta `--safe-top`, anch'essa arrotondata): due
       arrotondamenti indipendenti che si incontrano.
       Sovrapponendo il subbar di 1px la fessura non può esistere in nessuno
       stato, e `--chrome-h` non cambia — quindi il contenuto NON si sposta:
       resta semplicemente 1px in più coperto, che non si vede.
       ⚠️ Col vetro a .42 (v19.54) la fessura si noterebbe molto più di prima:
       si vede attraverso una barra chiara, non contro un fondo pieno.
       Il presidio è `mobile-css.test.js` → «nessuna fessura fra le barre». */
    .subbar{position:absolute;left:0;right:0;top:var(--appbar-h,calc(58px + env(safe-area-inset-top)));
      margin-top:-1px;padding-top:1px;
      z-index:16;transition:transform var(--d) var(--ease);will-change:transform;}
    /* ⚠️ La barra in BASSO non partecipa al gesto (scelta dell'utente, v16.8):
       resta ferma e sempre a vista mentre quelle in alto si ritirano. È il
       comando di navigazione — deve stare dov'è, come nelle app native — e
       sparisce solo quando sotto non scorre niente (pannello o menu aperti).
       Sta comunque in overlay come le altre: lo spazio che occupa è riservato
       da --pb-tabbar, quindi il contenuto non si muove in nessun caso. */
    .tabbar{position:absolute;left:0;right:0;bottom:0;}
    /* Mentre il dito è sullo schermo le barre seguono lo scorrimento senza
       transizione: animarle mentre le si trascina le farebbe arrancare. La
       transizione torna al rilascio, per l'aggancio. */
    .app.bar-drag .appbar,.app.bar-drag .subbar{transition:none;}
    /* spazio riservato in cima: misurato da app.9g a barre VISIBILI e mai più
       toccato (il fondo lo dichiarano già --pb-fondo/--pb-dock/--pb-tabbar). */
    .content{padding-top:var(--chrome-h,calc(58px + env(safe-area-inset-top)));
      /* scrollIntoView & c. devono fermarsi SOTTO le barre, non dietro */
      scroll-padding-top:var(--chrome-h,calc(58px + env(safe-area-inset-top)));}
    /* Una .stickyhead/.toolbar rimasta dentro .content (di norma showView la
       sposta nel subbar, ma una vista può averne una seconda o annidata) si
       aggancerebbe a top:0, cioè DIETRO le barre in overlay: si aggancia
       invece a filo del loro bordo inferiore. */
    .content .stickyhead,.content .toolbar{top:var(--chrome-h,calc(58px + env(safe-area-inset-top)));}
    /* Stato agganciato «ritirate». Il subbar si ferma a filo della safe-area:
       --bar-shift = altezza appbar − safe-area, la stessa corsa che percorre
       durante il trascinamento. */
    .app.bar-hidden .appbar{transform:translateY(-100%);opacity:0;pointer-events:none;}
    .app.bar-hidden .subbar{transform:translateY(calc(-1 * var(--bar-shift,58px)));}

    /* ── DOPPIO COMPORTAMENTO DELLA TESTATA (v19.52) ──────────────────
       Prima era tutto-o-niente: o la testata intera (112px con titolo
       grande, conteggio e barra di ricerca) o niente. Ora, come in Mail di
       iOS, scorrendo un poco si CONTRAE — titolo piccolo col conteggio di
       fianco, il «+» e l'hamburger dove sono — e solo scorrendo con
       decisione se ne va del tutto. Misurato: 112 → 56px, cioè mezza riga
       di elenco recuperata già al primo tocco di scorrimento.

       ⚠️ `--chrome-h` NON cambia. È lo spazio che `.content` riserva in
       cima, misurato a barre INTERE e mai più toccato: è l'invariante per
       cui il contenuto non si sposta di un pixel quando le barre si
       muovono (segnalata due volte dall'utente come «scorre troppo»).
       Contrarre la testata non lascia un buco: il contenuto che si vede
       sotto è quello che il gesto di scorrimento ha già portato lì.

       ⚠️ Il subbar NON si riaggancia al nuovo `top` — si SPOSTA con un
       `transform`, come già fa per ritirarsi. Cambiare `top` non è
       animabile e farebbe un salto; e soprattutto `--bar-shift` resta
       quello di sempre, quindi lo stato «ritirate» è identico a prima. */
    .app.bar-compact .appbar{padding-top:calc(5px + env(safe-area-inset-top));padding-bottom:5px;row-gap:0;}
    .app.bar-compact .appbar .ab-tit{flex-direction:row;align-items:baseline;gap:8px;}
    .app.bar-compact .appbar h1{font-size:17px;}
    .app.bar-compact #app-sub{font-size:11.5px;}
    .app.bar-compact .gswrap{display:none;}
    .app.bar-compact .mk-appbar{width:28px;height:28px;}
    /* ⚠️ IL RIPIEGO È 60px, NON 0 — è la fessura segnalata dall'utente
       (v19.54). Contraendosi, l'appbar si accorcia di ~60px; il subbar la
       insegue con questo transform. Col ripiego a 0 bastava che
       `--bar-compact` non fosse scritta perché il subbar restasse dov'era e
       fra le due barre si aprisse una fascia di 59px con la lista che scorre
       a vista. Succedeva davvero: ruotando il telefono in orizzontale
       `_measureChromeInner` CANCELLA le variabili del guscio (larghezza fuori
       da mobile), e tornando in verticale la classe `bar-compact` era ancora
       lì mentre la variabile non c'era più.
       Ora la classe viene tolta insieme alle variabili (app.9g) E il ripiego
       copre: 60px di troppo fanno sovrapporre il subbar all'appbar, che non
       si vede; 60px di meno lasciano un buco, che si vede eccome.
       Presidio: `mobile-css.test.js` → «nessuna fessura fra le barre». */
    .app.bar-compact .subbar{transform:translateY(calc(-1 * var(--bar-compact,60px)));}
    /* ritirate: comanda la corsa piena, la contrazione è già dentro */
    .app.bar-compact.bar-hidden .subbar{transform:translateY(calc(-1 * var(--bar-shift,58px)));}
    .appbar{transition:transform var(--d) var(--ease),opacity var(--d-fast) var(--ease),
      padding var(--d) var(--ease);}
    .appbar h1{transition:font-size var(--d) var(--ease);}
    /* Misura della testata contratta a transizioni spente (vedi _measureChrome):
       serve nello stesso frame, altrimenti si legge un'altezza a metà strada. */
    .app.bar-nofx .appbar,.app.bar-nofx .appbar h1,.app.bar-nofx .subbar{transition:none!important;}
  }
  @media (prefers-reduced-motion: reduce){
    .subbar,.tabbar{transition:none!important;}
    .filtsheet,.filtsheet-dim,.appbar,.totop{transition:none!important;}
  }

  @media(max-width:640px){
    /* I chip di stato andavano a capo su DUE righe (87px invece di 51): i 5 chip
       sommano 458px contro i 360 disponibili. Come .toolbar e .qbtns qui accanto,
       diventano una riga sola che scorre. */
    .statechips{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch;
      padding:6px 2px 9px;scroll-snap-type:x proximity;}
    .statechips::-webkit-scrollbar{display:none;}
    .statechips>*{flex:0 0 auto;scroll-snap-align:start;}
    /* La testata «Schede attive» ripeteva il nome della voce di menu attiva e
       costava 46px. Sparisce SOLO sulle liste-record: dove la testata contiene
       comandi (cestino) o separa elenchi diversi nella stessa pagina resta. */
    #view-schede>.listwrap>.listhead,
    #view-consegnate>.listwrap>.listhead,
    #view-tinture>.listwrap>.listhead,
    #view-rasature>.listwrap>.listhead,
    #view-archivio>.listwrap>.listhead{display:none;}
    /* con la testata via, la lista tocca i bordi: 26px di larghezza in più */
    #view-schede>.listwrap,#view-consegnate>.listwrap,
    #view-tinture>.listwrap,#view-rasature>.listwrap,
    #view-archivio>.listwrap{border-left:0;border-right:0;border-radius:0;box-shadow:none;
      margin-left:calc(-1 * var(--content-pad));margin-right:calc(-1 * var(--content-pad));}
    .content>section{padding-top:8px;}
  }

  /* ══════════════════════════════════════════════════════════════════
     ONDA 2 — densità delle righe su schermo stretto
     ──────────────────────────────────────────────────────────────────
     Una riga «In lavorazione» misurava 350px: 40 di numero+data su righe
     proprie, 94 di celle bordate per quattro valori che stanno in una riga di
     testo, 125 di pulsanti. A schermo ne entrava UNA. Qui la riga scende
     sotto i 110px senza perdere un solo dato: cambia come sono disposti.
     Tutto in CSS, così i render restano quelli di prima. */
  @media(max-width:640px){
    /* 1) Numero, data e tipo su UNA riga invece che tre */
    .row>.rnum{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;font-size:12.5px;}
    .row>.rnum .d{margin:0;font-size:11.5px;}
    .row>.rnum .rtipo{margin:0;font-size:11.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;}
    /* 2) Le celle bordate diventano una riga di testo continua: stessi valori,
          un settimo dello spazio. L'etichetta sparisce perché l'unità la dice
          già («248 pz», «412,5 m²») e il resto è cliente e crust. */
    .row .rmeta-grid{display:block;font-size:12px;line-height:1.5;color:var(--muted);}
    .row .rmeta-grid .rcell{display:inline;background:none;border:0;border-radius:0;padding:0;min-width:0;}
    .row .rmeta-grid .rcell .rk{display:none;}
    /* ⚠️ IL CLIENTE si stacca dagli altri valori (richiesta dell'utente).
       Con le celle rese come riga di testo continua i quattro valori avevano
       tutti lo stesso peso e lo stesso colore — «248 pz · 412,5 m² · ROSSI SPA
       · CONCERIA IL PONTE» — e il nome, che è il dato che si cerca per primo
       scorrendo l'elenco, spariva in mezzo ai numeri. Qui la gerarchia la fa la
       sola tipografia: gli altri valori passano a grigio 600, il cliente resta
       scuro e più grande. Niente pastiglie e nessun elemento nuovo: l'altezza
       della riga non cambia di un pixel (variante C, scelta su anteprima). */
    .row .rmeta-grid .rcell .rv{display:inline;font-size:12px;font-weight:600;color:var(--muted);}
    .row .rmeta-grid .rcell .rv.cli{font-size:13px;font-weight:800;color:var(--ink);}
    /* «da inserire» resta ROSSO: la regola generica qui sopra ha specificità 4 e
       batterebbe sia `.rcell .rv.miss` (3, più in alto nel file) sia la vecchia
       `.rcell.miss .rv`, che per giunta non ha mai colpito niente — la classe
       `miss` la mette rcell() sulla `.rv`, non sulla cella. */
    .row .rmeta-grid .rcell .rv.miss{color:var(--st-late-tx);}
    /* …e per la stessa ragione i metri REALI restano VERDI: la regola generica
       li spegneva a grigio proprio sul telefono, dove la riga è più fitta e il
       colpo d'occhio serve di più (v19.33). */
    .row .rmeta-grid .rcell .rv.ok{color:var(--st-cons-tx);font-weight:800;}
    .row .rmeta-grid .rcell+.rcell::before{content:" · ";color:var(--muted2);font-weight:400;}
    /* riga COMPATTA (Consegnate, «appena consegnate»): lì il cliente non sta in
       una cella ma nella riga di servizio, e si stacca allo stesso modo */
    .row.rcompact .rsub .rcli-nm{color:var(--ink);font-weight:800;font-size:12.5px;}
    /* 3) Titolo e chip: il nome dell'ARTICOLO non si tronca più (richiesta
          dell'utente). Era `flex-wrap:nowrap` + ellissi sul titolo, per non
          spendere i 23px di una riga di chip in più: ma un articolo lungo
          spariva a metà («SCAMOSCIATO DOPPIA FACCIA MERI…»), e con due chip
          accanto restava tagliato anche a 390px.
          ⚠️ Le due regole vanno INSIEME: lasciando andare a capo il solo
          titolo, senza permettere ai chip di scendere, un articolo di
          cinquanta caratteri si spezza nella colonna stretta che resta — otto
          righe, 152px di sola testata — e resta comunque tagliato (misurato).
          Con il wrap sulla `.rtop` il costo si paga solo dove serve: le righe
          con articolo corto restano a 181,8px come oggi, crescono solo quelle
          lunghe (211,8 e 231,3px). Su cinque schede l'elenco passa da 908 a
          988px, contro i 1.073 della variante «articolo sempre su riga sua». */
    .row .rtop{flex-wrap:wrap;overflow:hidden;gap:7px;}
    .row .rtop .rart{min-width:0;overflow:visible;text-overflow:clip;white-space:normal;}
    .row .rtop>.pill,.row .rtop>.ricbadge,.row .rtop>.rcol{flex:0 0 auto;}
    /* ⚠️ Del MIX AVANZATO non resta traccia nella riga (richiesta dell'utente,
       variante «via tutto» scelta su anteprima): sparisce la pastiglia 🧩 —
       prima nascosta nella sola riga piena, ora anche nella compatta — E i chip
       col dettaglio delle varianti dentro la cella Crust. Quei chip erano il
       pezzo più caro dell'elenco: **+26,5px** su una scheda con due varianti e
       **+53px** con tre (misurato sull'app vera a 390px), cioè una riga che da
       181,8px arrivava a 234,8. Le quote per esteso restano nell'anteprima e
       sulla copia interna del PDF. ⚠️ Conseguenza da conoscere: sul telefono
       una scheda con crust misto è ora indistinguibile da una con crust
       singolo — il mix si scopre aprendola. È stata una scelta esplicita, con
       l'alternativa «resta la sola pastiglia» scartata: non rimetterli senza
       chiedere. L'etichetta «Crust · mix di N varianti» non si vede comunque
       (su mobile le `.rk` sono nascoste). */
    .row .rtop>.mixpill{display:none;}
    .row .rmeta-grid .mixline{display:none;}
    /* ⚠️ La pastiglia della COMMESSA sparisce dalla riga (richiesta dell'utente):
       «📦 ORD-12 · TESTA DI MORO» è la più lunga della fila e su 390px si
       mangiava l'articolo e il colore. Il dato non si perde — l'anteprima gli
       dedica un riquadro intero (numero, avanzamento, metri ordinati/consegnati)
       e la copia interna del PDF lo stampa. Vale su TUTTE le liste del telefono,
       compatta compresa: la commessa si guarda aprendo la scheda, non scorrendo. */
    .row .rtop>.ordpill{display:none;}
    /* ⚠️ Stessa sorte per la pastiglia della CONSEGNA PREVISTA scelta a mano
       (richiesta dell'utente): «📅 03/09» in riga dice una data senza dire di
       che cosa, e la scheda che ce l'ha è una minoranza. Il dato resta dov'è
       leggibile per intero — riga del Programma, anteprima (col motivo) e
       copia interna del PDF. */
    .row .rtop>.dppill{display:none;}
    /* 4) Le icone diventano UN pulsante «⋯» che apre il foglio azioni
          (app.9g). Restano nel DOM, nascoste: il foglio le aziona con .click()
          così i gestori delegati non cambiano di una riga. */
    .ract-icons{position:relative;width:44px;height:38px;min-width:44px;padding:0;border-radius:10px;
      border:1px solid var(--line2);background:var(--panel);display:inline-flex;align-items:center;justify-content:center;
      cursor:pointer;transition:background var(--d-fast) var(--ease),transform var(--d-fast) var(--ease);}
    .ract-icons::after{content:"⋯";font-size:19px;font-weight:800;line-height:1;color:var(--muted);letter-spacing:1px;}
    .ract-icons:active{transform:scale(.94);background:var(--sidebar);}
    .ract-icons>.rib{display:none!important;}
    /* ⚠️ Anche «→ Pronta» e «→ Consegnata» entrano nel foglio (richiesta
       dell'utente), come nel Programma lavorazioni: erano tre comandi in riga
       oltre al gruppo di icone, e quello che CHIUDE la lavorazione si toccava
       scorrendo l'elenco.
       Il selettore è mirato a `[data-stato]`: «↩ Ripristina» dell'Archivio è
       anch'esso un `.btn.adv` e deve restare a vista. Il foglio NON ha un elenco
       proprio di voci — prende ciò che la riga nasconde (openRowSheet, app.9g),
       quindi questa regola e il foglio non possono divergere. */
    .row>.ractions .ract-line>.btn.adv[data-stato]{display:none;}
    /* ⚠️ E dalla v19.58 anche il CHIP DEI METRI REALI (richiesta dell'utente).
       In v18.7 era rimasto in riga apposta — «si usa tutti i giorni, un tocco
       invece di due» — quindi è un cambio di idea dichiarato, non una svista:
       nella riga resta il SOLO «⋯».
       ⚠️ Vale su `.row`, cioè schede E consegnate: è la stessa riga e lo stesso
       menu, e nelle consegnate il chip serve a correggere i metri dopo — nel
       foglio ci arriva uguale.
       ⚠️ NON tocca il Programma (là l'attributo è `data-progqreal` e il chip sta
       in `.prog-secondary`) né la barra dell'anteprima (`#pv-real`): sono gli
       altri due punti d'ingresso rapidi, e restano come sono.
       ⚠️ Solo ≤640px, come tutto questo blocco: sul computer il foglio «⋯» non
       esiste, quindi là il chip deve restare in riga o sparirebbe e basta. */
    .row>.ractions .ract-line>.btn.qr[data-qreal]{display:none;}
    /* ⚠️ E dalla v20.2 anche il pulsante RIFINIZIONE delle righe TINTURA e
       RASATURA (richiesta dell'utente), nei DUE rami in cui esiste: «＋
       Rifinizione» quando manca e «↗ RF26126» quando c'è. Sono lo stesso slot
       della stessa `.ract-line`, mutuamente esclusivi: nasconderne uno solo
       darebbe alla riga due geometrie diverse a seconda del suo stato.
       ⚠️ NON ERA SOLO INGOMBRO, COPRIVA IL TESTO — ed è il motivo per cui la
       richiesta è arrivata con una schermata. Dalla v19.99 la `.ract-line` è
       ancorata in angolo (`position:absolute`), e la riserva di 54px che le fa
       spazio è scritta per la sola `.rmeta-grid`, che è delle SCHEDE: tinture e
       rasature hanno `.rmeta`/`.rcrust` e non la prendono, quindi il pulsante
       ci galleggiava sopra. Misurato a 390px: «＋ Rifinizione» (103px) copriva
       82,3px della riga «tintura · nessuna rifinizione collegata» e fino a
       100,6 di quella del terzista; «↗ RF26126» (94,6px) ne copriva 77,3. Il
       testo non era troncato — era SOTTO il pulsante: `elementFromPoint` sulla
       sua coda ritornava il bottone. Col solo «⋯» (44px all'estrema destra) la
       copertura va a 0 da 390px in su.
       ⚠️ LA RISERVA DI 54px SU `.rmeta`/`.rcrust` È STATA PROVATA E SCARTATA,
       non dimenticata: azzera anche i 3,3÷6,6px che restano coperti a 360px —
       l'unica larghezza ancora scoperta, dove il testo sfiora il «⋯» — ma per
       farlo manda a capo la riga di servizio, e l'elenco di tre righe cresce di
       +18px a 390, +36 a 320 e **+95 a 360**. Novantacinque pixel per tre.
       ⚠️ Il selettore elenca i CONTENITORI delle due liste (comprese le
       sezioni «Trasferite in rifinizione») invece di colpire `.btn.adv` in
       generale: «↩ Ripristina» dell'Archivio è anch'esso un `.btn.adv` e resta
       a vista (vedi il punto su `[data-stato]`). Il `data-gorif` dentro
       `.rcrust` è un `<a class="tlink">`, quindi non è toccato.
       ⚠️ Il foglio NON ha un elenco proprio di voci: prende ciò che la riga
       nasconde (`_rowSheetBtns`, app.9g), quindi questa regola e il foglio non
       possono divergere — e i due pulsanti portano un `data-sheet-lbl`, perché
       in colonna con Modifica/Stampa/Elimina «Rifinizione» e «RF26126» da soli
       non dicono se la creano o la aprono. */
    #list-tint .row>.ractions .ract-line>.btn.adv,
    #tint-trasf .row>.ractions .ract-line>.btn.adv,
    #list-ras .row>.ractions .ract-line>.btn.adv,
    #ras-trasf .row>.ractions .ract-line>.btn.adv{display:none;}
    /* 5) Il badge di stato si prendeva una fascia tutta sua sopra i pulsanti
          (27px + spaziatura). Va in alto a destra, dove lo si cerca, sopra il
          titolo: è ancorato alla riga, non alla colonna delle azioni. */
    .row{position:relative;}
    .row>.ractions.rcol>.rstate-row{position:absolute;top:10px;right:12px;width:auto;justify-content:flex-end;
      max-width:56%;flex-wrap:nowrap;gap:5px;}
    /* ⚠️ IL BADGE DEI GIORNI PERDE LA SUA CODA (v19.50) — correzione di un
       difetto misurato, non una scelta grafica. Il badge di stato e quello dei
       giorni sono in `position:absolute` in alto a destra e la riga del numero
       riserva loro 185px con un padding FISSO; ma «In lavorazione» (104,6) più
       «⚠️ 16 gg · oltre stima» (126) chiedono **237px**, quindi il badge
       finiva SOPRA la data e la copriva per 20,8px a 390px — su ogni scheda in
       lavorazione da un po', cioè proprio quelle che si guardano.
       Senza la coda («· oltre stima», «· in lavorazione») la coppia scende a
       173px e ci sta: il numero e la data restano interi a ogni larghezza.
       Non è un dato perso — lo dicono già il ⚠️ contro il ⏳, il colore del
       badge e la corsia rossa della riga; la frase intera resta nel `title` e
       il Programma lavorazioni la scrive per esteso.
       ⚠️ La variante che accorpava numero e comandi per accorciare la riga è
       stata MISURATA due volte e non entra: quei cinque pezzi (numero+data,
       badge di stato, badge dei giorni, chip dei metri reali, «⋯») chiedono
       ~520px dove a 390px ce ne sono 336. Per farceli stare bisognerebbe
       troncare il terzista o mettere i badge nel flusso, che costa 7px in più
       su ogni riga tranne una. Non rifarla senza chiedere. */
    .fermo .fb-lbl{display:none;}
    /* lo spazio lo lascia SOLO la prima riga, quella accanto al badge: al
       titolo e ai dati serve tutta la larghezza */
    .row>.rnum{padding-right:185px;flex-wrap:nowrap;overflow:hidden;}
    /* il tipo animale ripete quello che dicono articolo e rifinizione qui sotto */
    .row>.rnum .rtipo{display:none;}
    /* i dati stanno su due righe al massimo, poi troncano */
    .row .rmeta-grid{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}
    /* 6) Riga più raccolta */
    .row{padding:11px 12px 12px 14px;gap:7px;}
    .rmain{gap:4px;}
    .ract-line{gap:6px;align-items:center;}
    .ractions.rcol{gap:6px;}
    .rstate-row{gap:6px;}
    /* 7) IL «⋯» ESCE DAL FLUSSO E VA NELL'ANGOLO (v19.99, richiesta
          dell'utente approvata su anteprima fra tre varianti).
          In fondo alla riga c'era una fascia da 38px + i 7 che la staccavano —
          45 su 164, il 27% — con dentro il SOLO «⋯»: un quadratino di 44×38 e
          292px di vuoto accanto. Non è sempre stato così: lì stavano anche
          «→ Pronta»/«→ Consegnata» (usciti in v18.7) e il chip dei metri reali
          (uscito in v19.58), e nessuno dei due giri ha poi ripreso la fascia
          rimasta vuota. Misurato sull'app vera: la riga passa da 164,3 a
          119,3px a 390px, identica da 320 a 430, e l'elenco di cinque schede
          da 886,8 a 661,8. Nessun dato esce dalla riga e nessun comando si
          perde: il «⋯» resta 44×38 e apre lo stesso foglio.
       ⚠️ `inset:0` NON è un modo prolisso di scrivere «in fondo a destra»: è
          ciò che tiene fermo il BADGE DI STATO. `.rstate-row` è a sua volta
          `absolute` e si riferisce all'antenato posizionato più vicino, che
          finché `.ractions` era statica era la `.row`; rendendo `.ractions`
          `absolute` con right/bottom, il badge le si aggancia e scivola in
          fondo, dove copre il cliente (visto in prova, non dedotto). Con
          `inset:0` il contenitore coincide col padding box della riga, quindi
          il `top:10px;right:12px` del punto 5 vale esattamente come prima.
       ⚠️ Il `right:12px` è lo STESSO del badge: sono i due elementi ancorati a
          destra e vanno incolonnati — a `right:0` il «⋯» finiva a filo del
          bordo, 12px più in là del badge sopra di lui (richiesta esplicita
          dell'utente: «che siano ben messi dentro la riga»).
       ⚠️ `pointer-events` sul contenitore, o il velo trasparente steso su
          tutta la riga si mangerebbe il tocco che apre l'anteprima.
       ⚠️ L'ARCHIVIO È ESCLUSO col `:not(:has(…[data-unarch]))`: lì la
          `.ract-line` porta anche «↩ Ripristina», che resta a vista per scelta
          (vedi il punto sul selettore mirato a `[data-stato]`) — in angolo i
          due comandi insieme sono ~160px e finirebbero sopra i dati. Se
          `:has()` non c'è, cade tutta la regola e si torna alla riga di prima:
          il ripiego è lo stato di oggi, non un rotto. Il CESTINO non c'entra:
          la sua `.ractions` non ha la classe `rcol` e non è mai toccata. */
    .row>.ractions.rcol:not(:has(.ract-line>.btn[data-unarch])){
      position:absolute;inset:0;display:block;gap:0;pointer-events:none;}
    .row>.ractions.rcol:not(:has(.ract-line>.btn[data-unarch]))>.ract-line{
      position:absolute;right:12px;bottom:10px;width:auto;pointer-events:auto;}
    /* I dati si fermano prima del «⋯»: 44 del pulsante + 10 d'aria.
       ⚠️ La riserva vale SOLO dove il «⋯» è davvero finito in angolo, e per
          questo la condizione ripete `:has(>.ractions.rcol)`: il CESTINO ha
          una `.ractions` senza `rcol` (due pulsanti testuali a vista, niente
          «⋯»), quindi il suo blocco azioni resta nel flusso — ma senza questa
          riga si prendeva lo stesso i 54px di riserva, la riga dei dati andava
          a capo e il cestino CRESCEVA di 18px. Colto confrontando prima/dopo
          su tutti e quattro i modi, non si vedeva guardando le schede. */
    .row:has(>.ractions.rcol):not(:has(.ract-line>.btn[data-unarch])) .rmeta-grid{padding-right:54px;}
  }
  /* Il foglio azioni di riga riusa il vestito del foglio filtri */
  .rowsheet-body{gap:0;padding:6px 8px calc(12px + env(safe-area-inset-bottom));}
  .rowsheet-act{display:flex;align-items:center;gap:13px;width:100%;font:inherit;font-size:15px;font-weight:700;
    color:var(--ink);background:none;border:0;border-radius:12px;padding:14px 12px;cursor:pointer;text-align:left;
    -webkit-tap-highlight-color:transparent;transition:background var(--d-fast) var(--ease),transform var(--d-fast) var(--ease);}
  .rowsheet-act+.rowsheet-act{border-top:1px solid var(--line);border-radius:0;}
  .rowsheet-act:active{background:var(--sidebar);transform:scale(.995);}
  .rowsheet-act.danger{color:var(--red);}
  .rowsheet-act .rsa-ico{width:26px;display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;
    font-size:17px;line-height:1;overflow:hidden;}
  .rowsheet-act .rsa-ico svg{width:19px;height:19px;}
  /* l'etichetta prende lo spazio che resta e va a capo lì, non nella colonna */
  .rowsheet-act .rsa-lbl{flex:1 1 auto;min-width:0;}
  /* L'azione che CHIUDE la lavorazione resta ultima e staccata: si tocca per
     ultima e non per sbaglio mentre si cerca quella sopra. Niente rosso — la
     `danger` è un'altra cosa (distruttiva); questa è solo definitiva. */
  .rowsheet-act.final{margin-top:7px;}
  .rowsheet-act+.rowsheet-act.final{border-top:1px solid var(--line2);}
  /* Un ordinamento/una vista già attiva: nel foglio lo stato si vede col
     segno di spunta, perché il `.on` della barra qui non arriva. */
  .rowsheet-act.on{color:var(--green);}
  .rowsheet-act.on::after{content:"✓";margin-left:auto;font-size:15px;flex:0 0 auto;}
  .rowsheet-act:disabled{opacity:.45;cursor:default;}
  .rowsheet-act:disabled:active{background:none;transform:none;}

  /* «Vista liste: Compatta» ora mantiene la promessa: prima toglieva 16px su
     350 (il 4,6%), cioè nulla. Con la riga già compatta, questo è il giro di
     vite ulteriore per chi vuole più righe possibile a schermo. */
  @media(max-width:640px){
    html[data-listdensity="compact"] .row{padding:8px 11px 9px 13px;gap:5px;}
    html[data-listdensity="compact"] .row>.rnum .rtipo{display:none;}
    html[data-listdensity="compact"] .row .rmeta-grid{font-size:11.5px;}
    html[data-listdensity="compact"] .row .rmeta-grid .rcell .rv{font-size:11.5px;}
    html[data-listdensity="compact"] .rart{font-size:13.5px;}
    html[data-listdensity="compact"] .prog-row{padding:8px 11px;margin-bottom:6px;}
  }

  /* ── ⚠️ NIENTE content-visibility:auto sulle righe degli elenchi ────
     Provato e RIMOSSO due volte, per due motivi diversi e indipendenti:
     1) implica contain:paint → uno stacking context per riga, che su iOS
        Safari durante lo scroll viene compositato SOPRA la barra dei filtri
        sticky (le schede «finivano sopra» la toolbar);
     2) contain-intrinsic-size è UN'ALTEZZA SOLA per tutte le righe, ma una
        riga vera va da ~120 a ~200px secondo stato, chip e ripartizione:
        scorrendo, il browser scopre l'altezza reale, corregge il totale e la
        posizione scatta — a dito fermo la pagina sembra scappare.
     La velocità di disegno si prende invece costruendo l'innerHTML della sola
     lista a video (_listaVisibile in app.3), che non ha nessuno dei due
     effetti collaterali. Vedi anche la nota gemella sopra, vicino a
     overscroll-behavior. */

  /* ── Tabelle su schermo stretto: la prima colonna resta ancorata ────
     Le tabelle di Statistiche e Clienti sono larghe 644-843px in 334 di
     schermo: scorrendo di lato per leggere margine e percentuale si perdeva
     di vista DI CHI si stava leggendo il dato. Ancorando la prima colonna il
     nome resta sempre a sinistra. Nascondere le colonne "meno importanti"
     sarebbe stato più aggressivo ma la classe .stab è condivisa da una
     quindicina di tabelle con ordini di colonna diversi: si sarebbero persi
     dati veri. */
  @media(max-width:640px){
    .stab th:first-child,.stab td:first-child{position:sticky;left:0;z-index:1;
      background:var(--card);box-shadow:1px 0 0 var(--line);max-width:140px;
      overflow:hidden;text-overflow:ellipsis;}
    .stab thead th:first-child{background:linear-gradient(180deg,#fcfdfe,#f7fafc);}
    html[data-theme="dark"] .stab thead th:first-child{background:#1a2532;}
    .stab{font-size:13px;}
    .stab th,.stab td{padding:8px 9px;}
  }

  /* ── Barra di navigazione in basso (solo telefono) ─────────────────
     Le voci le sceglie l'utente in Impostazioni; la quinta è sempre «Menu»,
     che apre il menu completo (v19.77: si chiamava «Altro», ma ha preso il
     posto dell'hamburger e «Altro» è la convenzione iOS per ciò che AVANZA
     da una tab bar — qui invece c'è il menu intero). Lo spazio in fondo alla lista lo dichiara la
     barra stessa scrivendo --pb-tabbar, così non si somma due volte con FAB e
     dock (vedi il calcolo unico sul padding di .content). */
  .tabbar{display:none;}
  @media(max-width:640px){
    .tabbar{display:grid;flex:0 0 auto;background:var(--panel);border-top:1px solid var(--line);
      padding:5px 4px calc(6px + env(safe-area-inset-bottom));gap:2px;z-index:18;}
    .tb-item{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;
      position:relative;font:inherit;font-size:10px;font-weight:750;color:var(--muted);
      background:none;border:0;border-radius:11px;padding:6px 2px 4px;cursor:pointer;min-height:48px;
      -webkit-tap-highlight-color:transparent;
      transition:color var(--d-fast) var(--ease),transform var(--d-fast) var(--ease);}
    .tb-item svg{width:22px;height:22px;stroke-width:1.9;flex:0 0 auto;}
    .tb-item .tb-lbl{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
    /* ⚠️ Era --green2: sul VETRO della barra, con una riga «in ritardo» che
       scorre sotto, il composito scende a 4,24:1 — sotto il minimo di 4,5
       (misurato). --green (verde profondo in chiaro, chiaro in scuro) regge in
       entrambi i temi anche col fondo peggiore. È la stessa trappola dei verdi
       tenui già corretta in una decina di punti. */
    .tb-item.on{color:var(--green);}
    .tb-item:active{transform:scale(.94);}
    .tb-badge{position:absolute;top:2px;left:calc(50% + 4px);min-width:16px;height:16px;padding:0 4px;
      border-radius:999px;background:var(--red);color:#fff;font-size:9.5px;font-weight:800;
      display:flex;align-items:center;justify-content:center;line-height:1;}
    /* con la barra in basso, il dock sale sopra di essa */
    body.has-tabbar .dock{bottom:calc(12px + var(--pb-tabbar,0px) + env(safe-area-inset-bottom));}
    /* mentre un pannello è aperto la barra non deve galleggiare sopra */
    body.scroll-locked .tabbar,body.nav-locked .tabbar{opacity:0;pointer-events:none;}
    .tabbar{transition:opacity var(--d-fast) var(--ease);}
  }

  /* ══ «↑ Torna in cima» (app.9g) ══════════════════════════════════════
     Compare dopo TOP_SHOW_PX di scorrimento e sparisce da sé risalendo. Dalla
     v18.6 è un TONDINO con la sola freccia in basso a DESTRA — dove stava la
     FAB 📷 fino alla v16.9 — e c'è anche sul COMPUTER, dove prima era spento.

     ⚠️ Qui c'era un commento MALFORMATO — una chiusura di commento di troppo a
     metà blocco. Tutto ciò che la seguiva finiva nel prelude della regola dopo,
     che diventava un selettore non valido e veniva scartata: la
     `.totop{display:none}` non è mai arrivata al browser, senza effetti
     visibili solo perché su desktop il pulsante non veniva nemmeno creato.
     Riscrivendo questo blocco l'errore è stato rifatto identico (una chiusura
     dentro il testo, mentre si spiegava proprio quella) e ha ingoiato la regola
     base: non nominare quella sequenza qui dentro nemmeno fra apici.

     ⚠️ Non somma più --pb-dock: i chip «Da riprendere» arrivano al massimo a
     84px dal bordo destro (left:12px;right:84px) e il pulsante ne occupa 56,
     quindi gli stanno ACCANTO senza toccarli (misurato a 320 e 390px). Al
     centro invece li copriva, ed è per quello che doveva alzarsi.
     ⚠️ Su desktop i 24px dal bordo tengono conto della barra di scorrimento
     della .content (15px in Chromium): sotto quella misura ci finisce sopra. */
  .totop{position:absolute;right:24px;z-index:17;
    bottom:calc(20px + env(safe-area-inset-bottom));
    width:40px;height:40px;padding:0;
    display:inline-flex;align-items:center;justify-content:center;
    font:inherit;font-size:19px;font-weight:800;line-height:1;
    background:var(--green);color:#fff;border:0;border-radius:50%;
    box-shadow:var(--sh-lg);cursor:pointer;opacity:0;pointer-events:none;
    transform:translateY(10px);
    touch-action:manipulation;-webkit-tap-highlight-color:transparent;
    transition:opacity var(--d-fast) var(--ease),transform var(--d) var(--ease-out),
               filter var(--d-fast) var(--ease);}
  .totop.vis{opacity:1;transform:translateY(0);pointer-events:auto;}
  .totop:active{transform:translateY(0) scale(.94);}
  /* ⚠️ L'anello di messa a fuoco ha una regola SUA: quella condivisa più avanti
     nel file impone border-radius:9px e squadrerebbe il tondino. */
  .totop:focus-visible{outline:2px solid var(--green2);outline-offset:3px;border-radius:50%;}
  /* schiarita al passaggio del mouse invece di un colore diverso: il fondo
     cambia col tema (in scuro passa da --green-solid) e un secondo verde
     scritto a mano andrebbe riverificato in entrambi */
  @media(hover:hover){ .totop:hover{filter:brightness(1.18);} }
  /* con un pannello o il menu aperti sotto non scorre niente: sparisce come
     la barra in basso */
  body.scroll-locked .totop,body.nav-locked .totop{opacity:0;pointer-events:none;}
  @media(max-width:640px){
    /* 44px è la misura minima comoda al pollice su iOS; col mouse non serve */
    .totop{right:12px;width:44px;height:44px;font-size:20px;
      bottom:calc(14px + var(--pb-tabbar,0px) + env(safe-area-inset-bottom));}
  }

  /* Impostazioni → scelta delle pagine della barra in basso */
  .tabpick-list{display:flex;flex-wrap:wrap;gap:7px;}
  .tabpick-chip{font:inherit;font-size:13px;font-weight:700;color:var(--ink2);background:var(--panel);
    border:1px solid var(--line2);border-radius:999px;padding:8px 13px;cursor:pointer;display:inline-flex;
    align-items:center;gap:7px;min-height:40px;-webkit-tap-highlight-color:transparent;
    transition:background var(--d-fast) var(--ease),border-color var(--d-fast) var(--ease),color var(--d-fast) var(--ease);}
  .tabpick-chip.on{background:var(--green-solid,var(--green2));border-color:var(--green2);color:#fff;}
  .tabpick-chip.full{opacity:.45;}
  .tabpick-chip:active{transform:scale(.97);}
  .tabpick-n{background:rgba(255,255,255,.24);border-radius:999px;width:19px;height:19px;
    display:inline-flex;align-items:center;justify-content:center;font-size:11px;font-weight:800;}
  @media(hover:hover){ .tabpick-chip:not(.full):hover{border-color:var(--green2);color:var(--green);} .tabpick-chip.on:hover{color:#fff;} }

  /* ── Statistiche su telefono ────────────────────────────────────────
     Le tessere KPI non avevano nessun adattamento: la sola card «Costi e
     margini» ne conteneva 632px. I grafici SVG avevano min-width:540px in uno
     schermo da 364, quindi si vedeva il 62% del grafico e le etichette
     uscivano a 7,8px. */
  @media(max-width:640px){
    .kpis{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;margin-bottom:6px;}
    .kpi{padding:11px 12px;border-radius:12px;}
    .kpi .n{font-size:21px;letter-spacing:-.3px;}
    .kpi .l{font-size:9.5px;letter-spacing:.4px;margin-top:5px;}
    .kpi::before{width:2px;}
    /* il grafico si adatta alla larghezza invece di essere tagliato: meno
       tacche e testo un filo più grande, così le date si leggono */
    .card svg[style*="min-width"]{min-width:0!important;}
    .card svg text{font-size:11px;}
    /* «Report mese» è una funzione da scrivania: fuori dalla barra fissa */
    .rep-launch{width:100%;justify-content:flex-start;margin-top:6px;}
    .stab{font-variant-numeric:tabular-nums;}
  }

  /* ── Le dieci pagine di anagrafica su telefono ──────────────────────
     Stesso trattamento per tutte: riga compatta, contatti a icona, azioni
     raggiungibili. Su Fornitori la riga passa da 181 a ~70px. */
  @media(max-width:640px){
    .arow{padding:10px 12px;gap:8px;}
    .arow .an{font-size:14.5px;}
    .arow .am{font-size:12px;margin-top:2px;}
    /* i contatti restano toccabili (40px) ma senza il testo per esteso, che
       mandava i chip su due righe su ogni record */
    .cchips{gap:6px;margin-top:5px;}
    .cchip{padding:0;width:40px;height:40px;justify-content:center;border-radius:11px;font-size:16px;}
    .cchip .cchip-t{display:none;}
  }

  /* ── Editor a fisarmonica ───────────────────────────────────────────
     Il contenitore di sezione è `display:contents`: i campi restano item della
     stessa griglia di prima (nessuna regola di layout cambia) e si possono
     nascondere in blocco. */
  .fsec-body{display:contents;}
  .fsec-body[hidden]{display:none;}
  .sec-title.fsec-head{display:flex;align-items:baseline;gap:7px;cursor:default;}
  .sec-title .fsec-chev{display:none;}
  .sec-title .fsec-sum{margin-left:auto;font-size:10.5px;font-weight:700;color:var(--muted);
    text-transform:none;letter-spacing:0;}
  @media(max-width:640px){
    .sec-title.fsec-head{cursor:pointer;-webkit-tap-highlight-color:transparent;user-select:none;
      padding:11px 2px 9px;margin:0;border-top:1px solid var(--line);min-height:44px;align-items:center;}
    .sec-title.fsec-head:focus-visible{outline:2px solid var(--green2);outline-offset:2px;border-radius:6px;}
    .sec-title .fsec-chev{display:inline;color:var(--muted);font-size:11px;}
    .sec-title.fsec-head.closed{color:var(--ink2);}
    /* la sezione aperta si distingue a colpo d'occhio da quelle chiuse */
    .sec-title.fsec-head:not(.closed){color:var(--green);}
  }

  /* ── Anteprima record su telefono ──────────────────────────────────
     La griglia dei campi identificativi costava 334px in tessere bordate per
     mostrare coppie etichetta/valore che stanno in un elenco a filetti in 197.
     Stessa informazione, stesso ordine: cambia la cornice. */
  @media(max-width:640px){
    /* ⚠️ Il dettaglio era una LISTA a colonna unica, etichetta a sinistra e
       valore a destra: su iPhone diventava lunga 2,71 schermate (misurato), e
       per arrivare al margine bisognava scorrere tre volte. Ora è la griglia a
       DUE colonne dell'anteprima approvata: riquadri separati, etichetta sopra
       e valore sotto, come nelle app native. A parità di campi la pagina passa
       a ~1,4 schermate. I campi lunghi (quantità, lotto, note) restano a tutta
       riga con `.pvf.full`, che qui non si tocca. */
    .pvgrid{display:grid;grid-template-columns:1fr 1fr;gap:9px;margin-bottom:11px;
      border:0;border-radius:0;overflow:visible;}
    .pvgrid .pvf{background:var(--panel);border:0;border-radius:16px;padding:10px 12px;
      display:block;box-shadow:0 1px 2px rgba(15,36,56,.05),0 4px 12px -8px rgba(15,36,56,.2);}
    html[data-theme="dark"] .pvgrid .pvf{box-shadow:0 1px 2px rgba(0,0,0,.45);}
    .pvgrid .pvf + .pvf{border-top:0;}
    .pvgrid .pvf .l{font-size:10px;letter-spacing:.1em;text-transform:uppercase;margin:0 0 3px;}
    .pvgrid .pvf .v{font-size:15.5px;text-align:left;min-width:0;overflow-wrap:anywhere;margin:0;}
    /* testata più bassa: 112px erano notch + due quadratini da 42 + padding */
    /* ⚠️ La safe-area superiore va RIPETUTA qui: questa regola sta dopo quella
       che la applica (riga ~1288) e con `padding` in forma breve la
       azzererebbe — titolo e × dell'anteprima finivano sotto l'orologio e la
       batteria dell'iPhone, quindi la × non si riusciva a premere. */
    .pvhead{padding:calc(8px + env(safe-area-inset-top)) 10px 8px 14px;}
    .pvhead h3{font-size:15px;}
    .pvbox .x{width:38px;height:38px;}
    /* i box informativi si stringono */
    .pvcrust{padding:10px 12px;margin-bottom:10px;}
  }

  /* Trascinamento dell'anteprima: la maniglia dice che si può fare */
  @media(max-width:640px){
    .pvbox{position:relative;}
    /* ⚠️ NIENTE `position` QUI. La maniglia è un `::before` e le basta un
       antenato posizionato: glielo dà già il blocco «il contenuto scorre sotto
       le barre delle finestre» (v19.54), che rende la testata `absolute`.
       Dichiarare `position:relative` in questo punto è stato un difetto vero:
       questo selettore (0,2,0) BATTE il `.pvhead{position:absolute}` di quel
       blocco (0,1,0) benché stia PRIMA nel file, quindi la testata restava nel
       FLUSSO mentre lo scroller le riservava comunque il padding — lo stesso
       spazio contato due volte, cioè 139px di fascia bianca sotto la testata
       dell'anteprima (misurati a 390px con un notch da 59). */
    .pvbox .pvhead{touch-action:pan-x;}
    .pvbox .pvhead::before{content:"";position:absolute;top:calc(4px + env(safe-area-inset-top));left:50%;transform:translateX(-50%);
      width:36px;height:4px;border-radius:99px;background:var(--line2);}
    .pvbox .pvhead{padding-top:calc(14px + env(safe-area-inset-top));}
  }

  /* ══════════════════════════════════════════════════════════════════
     I PANNELLI COME FOGLIO DAL BASSO (v19.51)
     ──────────────────────────────────────────────────────────────────
     Maniglia, angoli superiori arrotondati e chiusura buttandoli giù
     (`_wireSheetDrag` in app.9g). Gemelli dell'anteprima qui sopra.

     ⚠️ LA GEOMETRIA NON SI TOCCA. Su telefono `.panel` è `inset:0` con
     `height:auto;max-height:none`, e ci si è arrivati dopo tre giri di
     correzioni (v19.19–21): ogni ALTEZZA provata era sbagliata da qualche
     parte — `100vh` restava corta nella PWA installata, `--app-h` sforava
     dove `innerHeight` non sotto-conta, e «Salva» finiva 59px sotto il bordo.
     Qui cambiano SOLO gli angoli e una maniglia disegnata; il foglio resta
     ancorato ai quattro lati. Il mock a `max-height:80%` che si intravedeva
     nell'anteprima NON è stato realizzato apposta: su un modulo da quaranta
     campi toglierebbe un quinto dell'altezza utile, ed è la geometria che è
     costata di più in questo progetto (scelta dell'utente: «tenendo
     l'ancoraggio attuale»).

     ⚠️ Gli angoli arrotondati scoprono il velo scuro `.overlay` che sta
     sotto — è quello a dare l'aria di foglio appoggiato, senza spostare
     nulla di un pixel.
     ⚠️ Il `padding-top` della maniglia si SOMMA alla safe-area, che qui è
     load-bearing: senza, titolo e ✕ finiscono sotto l'orologio (v19.44). */
  @media(max-width:640px){
    .panel:not(.min){border-radius:22px 22px 0 0;}
    .panel:not(.min) .panel-head{touch-action:pan-x;position:relative;
      padding-top:calc(17px + env(safe-area-inset-top));border-radius:22px 22px 0 0;}
    .panel:not(.min) .panel-head::before{content:"";position:absolute;left:50%;transform:translateX(-50%);
      top:calc(6px + env(safe-area-inset-top));width:36px;height:4px;border-radius:99px;background:var(--line2);}
    /* a tutto schermo il foglio riempie tutto: gli angoli tornano squadrati */
    .panel.full{border-radius:0;}
    .panel.full .panel-head{border-radius:0;}
  }

  /* ── Movimento e tocco (onda 4) ─────────────────────────────────────
     touch-action:manipulation toglie l'attesa di ~300ms con cui il browser
     verifica se il tocco è un doppio-tap da zoom: senza, ogni pulsante
     risponde con un ritardo percepibile. Vale per tutto ciò che si tocca;
     lo scorrimento resta libero perché non è dichiarato "none". */
  a,button,[role="button"],.row,.arow,.prog-row,.statechip,.ms-btn,.ms-opt,.csm-row,
  .gs-item,.gs-chip,.dkchip,.pvst-btn,.ord-rigachip,.colc-card,.chip,.rib,.x,
  .tb-item,.filtbtn,.rowsheet-act,.fsec-head,.cchip{touch-action:manipulation;}

  /* Il tema scuro va dichiarato anche al browser: senza `color-scheme` restano
     chiare le barre di scorrimento, i campi nativi (date, select) e i menu di
     sistema — le uniche isole bianche in una pagina scura. */
  html[data-theme="dark"]{color-scheme:dark;}
  html[data-theme="light"]{color-scheme:light;}

  /* Feedback di pressione dove mancava del tutto */
  .pvb-btn:active,.ms-btn:active,.rib:active,.iconbtn:active{transform:scale(.94);}
  .pvb-btn,.ms-btn,.iconbtn{transition:background var(--d-fast) var(--ease),border-color var(--d-fast) var(--ease),transform var(--d-fast) var(--ease);}

  /* Le sezioni comprimibili si aprivano e chiudevano di scatto (hidden). Con
     grid-template-rows 0fr→1fr l'altezza si anima senza doverla misurare in JS
     e senza far ricalcolare il layout a ogni frame. Il contenuto resta nel DOM,
     quindi il vincolo del Programma (le righe appena segnate non devono
     sparire) è rispettato: non si ridisegna niente. */
  .prog-collapse-body,.fold-body{display:grid;grid-template-rows:1fr;
    transition:grid-template-rows var(--d) var(--ease-out),opacity var(--d-fast) var(--ease);}
  .prog-collapse-body>*,.fold-body>*{min-height:0;}
  .prog-collapse-body[hidden],.fold-body[hidden]{display:grid;grid-template-rows:0fr;opacity:0;
    pointer-events:none;overflow:hidden;}

  /* Il fuoco da tastiera deve vedersi su TUTTO ciò che si può attivare: senza,
     chi naviga con Tab non sa dov'è. Un solo anello, uguale ovunque. */
  .btn:focus-visible,.rib:focus-visible,.x:focus-visible,.chip:focus-visible,
  .statechip:focus-visible,.tb-item:focus-visible,.rowsheet-act:focus-visible,
  .filtbtn:focus-visible,.helpcard-x:focus-visible,.ract-icons:focus-visible,
  .prog-sumchip:focus-visible,.tabpick-chip:focus-visible,.cchip:focus-visible,
  /* ⚠️ v20 — tredici famiglie di comandi non erano in questo elenco, cioè
     navigando col Tab non si vedeva dove si era: le 23 voci del menu, le voci
     dei menu contestuali, i reparti delle Impostazioni, l'indice del modulo
     sul computer, le pastiglie di stato del dettaglio, il «← Indietro», le
     opzioni delle tendine, la ✕ dei dialoghi, gli strumenti di testata e le
     righe di tabella cliccabili. Se aggiungi un comando, aggiungilo QUI: è
     l'unico posto dove l'anello è definito. */
  .nav a:focus-visible,.ordmenu button:focus-visible,.psm-item:focus-visible,
  .gs-smi:focus-visible,.setnav button:focus-visible,.sezlist .mn-row:focus-visible,
  .mnlist .mn-row:focus-visible,.ordnum-pill:focus-visible,.pvst-btn:focus-visible,
  .pvback:focus-visible,.ms-opt:focus-visible,.dlg-x:focus-visible,
  .ph-tools button:focus-visible,.stab tr.clk:focus-visible,.ordcard:focus-visible{
    outline:2px solid var(--green2);outline-offset:2px;border-radius:9px;}
  /* dentro la barra laterale l'anello verde scuro non si vedrebbe: là il
     contrasto lo dà il verde chiaro degli accenti (vedi --side-acc). */
  .nav a:focus-visible{outline-color:var(--side-acc);}

  /* Card di analisi richiudibili (Scorte per tipo, Costo crust nel tempo) */
  .card.foldable>h4{cursor:pointer;display:flex;align-items:baseline;gap:7px;flex-wrap:wrap;
    -webkit-tap-highlight-color:transparent;user-select:none;}
  .card.foldable>h4 .fold-chev{color:var(--muted);font-size:12px;flex:0 0 auto;
    transition:color var(--d-fast) var(--ease);}
  .card.foldable>h4:focus-visible{outline:2px solid var(--green2);outline-offset:3px;border-radius:6px;}
  @media(hover:hover){ .card.foldable>h4:hover .fold-chev{color:var(--green);} }
  .card.foldable.folded{padding-bottom:14px;}
  .card.foldable.folded>h4{margin-bottom:0;}

  /* Note di pagina richiudibili (Articoli, Lavorazioni, crust, lotti, Sel&Sp) */
  .helpcard{position:relative;padding-right:44px;}
  .helpcard-x{position:absolute;top:6px;right:8px;width:30px;height:30px;border-radius:9px;border:0;
    background:transparent;color:inherit;opacity:.55;font-size:20px;line-height:1;cursor:pointer;
    display:flex;align-items:center;justify-content:center;transition:opacity var(--d-fast) var(--ease);}
  .helpcard-x:active{transform:scale(.92);}
  @media(hover:hover){ .helpcard-x:hover{opacity:1;} }
  @media(max-width:640px){
    .helpcard{font-size:12.5px!important;padding:11px 40px 11px 12px;line-height:1.45;}
  }

  /* ══════════════════════════════════════════════════════════════════
     CALCOLATORE PREZZI (pagina #view-calcprezzi + finestra rapida .qcalc)
     ──────────────────────────────────────────────────────────────────
     Due colonne: a sinistra i numeri che scrivi, a destra la risposta —
     la stessa card margine delle schede (.margine-box, riusata tale e
     quale) più il confronto fra prezzi. Sotto i 900px si impila, con la
     risposta PRIMA dei campi: su telefono l'ordine di lettura è «quanto
     margino» e poi «ritocco il prezzo». */
  .cpgrid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.05fr);gap:16px;align-items:start;}
  .cpcol{display:grid;gap:14px;align-content:start;min-width:0;}
  .cpgrid .fgrid.cpf2{grid-template-columns:1fr 1fr;}
  .cpgrid .fgrid.cpf3{grid-template-columns:repeat(3,1fr);}
  .cpfoot{display:flex;justify-content:flex-end;}
  .cpacts{display:flex;gap:8px;flex-wrap:wrap;margin-top:11px;}
  /* Tabella scenari: righe cliccabili (portano il prezzo nel campo), quindi
     devono sembrare toccabili — cursore, stato hover e focus da tastiera. */
  .cps-wrap{overflow-x:auto;}
  table.cps{width:100%;border-collapse:collapse;font-size:13px;font-variant-numeric:tabular-nums;min-width:340px;}
  table.cps th{text-align:right;font-size:10px;font-weight:800;letter-spacing:.05em;text-transform:uppercase;
    color:var(--muted);padding:0 0 7px;border-bottom:1px solid var(--border);white-space:nowrap;}
  table.cps th:first-child,table.cps td:first-child{text-align:left;}
  table.cps td{padding:7px 0;border-bottom:1px solid var(--line);text-align:right;font-weight:600;color:var(--ink);white-space:nowrap;}
  table.cps tbody tr{cursor:pointer;}
  table.cps tbody tr:focus-visible{outline:2px solid var(--green3);outline-offset:-2px;}
  table.cps td.cps-p{font-weight:800;padding-left:8px;}
  table.cps td.cps-tot{font-weight:800;}
  .cps-lab{display:inline-block;margin-left:7px;font-size:9.5px;font-weight:800;letter-spacing:.05em;
    text-transform:uppercase;color:var(--muted);vertical-align:middle;}
  /* L'etichetta prende il colore della SUA riga: il grigio tenue va bene sul
     bianco, ma sul fondo verde della riga in uso scende a 4,1:1 (testo piccolo
     e maiuscolo, quindi sotto AA) e sull'ambra dell'obiettivo pure. */
  table.cps tr.cps-corrente .cps-lab{color:var(--green);}
  table.cps tr.cps-obiettivo .cps-lab{color:var(--amber-tx);}
  /* Le tre righe notevoli si riconoscono dal fondo, non solo dall'etichetta:
     il prezzo in uso (verde), l'obiettivo (ambra) e il pareggio (spento). */
  table.cps tr.cps-corrente td{background:var(--soft);}
  table.cps tr.cps-corrente td.cps-p{box-shadow:inset 3px 0 0 var(--green3);}
  table.cps tr.cps-obiettivo td{background:var(--amber-bg);}
  table.cps tr.cps-obiettivo td.cps-p{box-shadow:inset 3px 0 0 var(--amber-bd);}
  table.cps tr.cps-pareggio td{color:var(--muted);}
  /* ⚠️ La specificità deve battere `table.cps td` (0,1,1), o il colore
     semantico non si vede: una classe sola perderebbe. */
  table.cps td.cps-pos{color:var(--green);}
  table.cps td.cps-neg{color:var(--red);}
  table.cps td.cps-warn{color:var(--amber-tx);}
  /* Finestra rapida: stessa matematica, spazio ridotto. */
  .qcalc .dlghead{display:flex;align-items:center;gap:10px;}
  .qcalc .qc-kbd{margin-left:auto;font-size:10.5px;font-weight:700;color:var(--muted);
    border:1px solid var(--border);border-radius:6px;padding:2px 7px;white-space:nowrap;}
  .qcalc .qc-cost{margin-top:10px;background:var(--sidebar);border:1px solid var(--border);border-radius:9px;
    padding:8px 10px;font-size:12.5px;font-weight:600;color:var(--ink);font-variant-numeric:tabular-nums;}
  .qcalc .qc-cost b{color:var(--ink);font-weight:800;}
  .qcalc table.cps td{padding:5px 0;}
  /* In finestra la card margine mostra solo il RISULTATO: il numero grande e il
     totale in €. Le voci di costo una per una (.mlines) e la nota lordo/netto
     (.mnote) sono già riassunte nella riga «Costo pieno» qui sopra, e su uno
     schermo di telefono spingevano la tabella dei prezzi fuori dalla finestra —
     cioè proprio la cosa per cui la si apre. Il dettaglio sta un tocco più in
     là, in «Apri completo». */
  /* ⚠️ Si nascondono BLOCCHI INTERI, mai singole righe dentro un blocco: fra un
     `.mrow` e l'altro il markup lascia nodi di testo (gli a-capo del template) e
     con le righe a display:none quelli restano, formando righe vuote — 164px di
     bianco dentro la card, misurati. Il totale in € torna nel riepilogo qui
     sopra, scritto da openQuickCalc. */
  .qcalc .margine-box .mnote,.qcalc .margine-box .mlines,.qcalc .margine-box .mtot{display:none;}
  /* Stessa ragione, un giro più in là: anche fra i blocchi nascosti restano gli
     a-capo del template, che ereditano il line-height del contenitore e lasciano
     una striscia vuota sotto i chip. Azzerato sul contenitore, ridato ai figli. */
  .qcalc .margine-box{line-height:0;}
  .qcalc .margine-box .mhero{line-height:1.4;}
  .qcalc .margine-box .mhero{padding-bottom:6px;}
  .qcalc .margine-box .mhero-val{font-size:26px;}
  .qcalc .dlgfoot{flex-wrap:wrap;}
  @media(max-width:900px){
    .cpgrid{grid-template-columns:1fr;}
    /* la risposta sale sopra i campi: è quello che si guarda */
    .cpgrid .cpcol:last-child{order:-1;}
    .cpgrid .fgrid.cpf3{grid-template-columns:1fr 1fr;}
  }
  @media(max-width:640px){
    .cpgrid .fgrid.cpf2,.cpgrid .fgrid.cpf3{grid-template-columns:1fr;}
    .cpacts .btn{flex:1 1 auto;}
  }


  /* ══════════════════════════════════════════════════════════════════════
     LIQUID GLASS — il materiale delle superfici che galleggiano (v19.54)
     ──────────────────────────────────────────────────────────────────────
     Anteprima approvata dall'utente (agosto 2026, seconda passata). Il vetro
     sta SOLO sulle superfici che galleggiano — testate, barre comandi, fogli,
     menu, chip flottanti — e MAI sotto i dati: le righe restano piene e
     opache, o il contrasto dei numeri scenderebbe sotto AA su un fondo che
     cambia mentre scorri. Vale anche per il CORPO delle finestre: in vetro
     vanno la testata e il piede, non il contenuto in mezzo.
     ⚠️ Funziona perché le barre sono GIÀ un overlay in `position:absolute`
     sopra `.content` (vedi il blocco «barre che si ritirano»): il contenuto
     scorre di sotto e c'è qualcosa da rifrangere. Se un domani le barre
     tornassero nel flusso, il vetro non avrebbe più senso — e nemmeno lo
     spazio riservato da --chrome-h. È la stessa ragione per cui, nella
     v19.54, testata e piede del dettaglio e degli editor sono diventati
     anch'essi overlay: senza contenuto che ci scorra sotto, il vetro è un
     fondo pieno un po' sporco.
     ⚠️ Solo ≤640px, come tutto il restyling: su computer e iPad la cornice
     resta quella di sempre.

     LA RICETTA (misurata, non scelta a occhio). Fino alla v19.53 il fill era
     .62 con blur 26 e il commento diceva che «sotto il 55% il testo non tiene
     AA». Sui dati veri del gestionale non è così: il contenuto è tutto card
     bianche su fondo chiaro, quindi il fondo composito sotto le barre resta
     quasi bianco anche col vetro molto più leggero. Misurato scorrendo
     l'elenco in sei posizioni, col contenuto delle barre nascosto per
     campionare il composito puro e non i glifi:

         fill  blur   titolo   sottotitolo(*)  verde attivo
         .62   26px   9,53:1      5,32:1          7,67:1     (v19.53)
         .42   40px   8,24:1      4,60:1          6,63:1     ← questa
         .26   52px   6,89:1      3,85:1 ✗        5,54:1

     (*) col sottotitolo corretto, vedi il difetto qui sotto.
     ⚠️ `brightness()` è il pezzo che mancava: appiattisce il contenuto sotto
     ed è ciò che permette di essere trasparenti restando leggibili. Senza,
     a .42 il titolo scenderebbe sensibilmente. In tema scuro va SOTTO 1
     (schiarire un fondo scuro lo renderebbe lattiginoso).
     ⚠️ Ritoccare il solo blur non serve: provato a parità di fill (26→30px)
     dà una schermata indistinguibile, 9,77 contro 9,53. Il salto lo fa
     l'opacità.
     ══════════════════════════════════════════════════════════════════════ */
  :root{
    --g-fill:rgba(255,255,255,.42);      /* barre: vedi la tabella qui sopra */
    --g-fill-hi:rgba(255,255,255,.56);   /* variante più carica: fogli, menu, campi */
    --g-blur:40px;
    --g-sat:180%;
    --g-bright:1.12;                     /* < 1 in tema scuro */
    --g-spec:rgba(255,255,255,.92);      /* riflesso in alto = spessore */
    --g-spec-lo:rgba(255,255,255,.30);
    --g-edge:rgba(15,36,56,.10);
    --g-drop:0 8px 26px -8px rgba(15,36,56,.24),0 1px 3px rgba(15,36,56,.09);
    /* L'anello di luce del bordo: chiaro dove la luce entra, spento dove
       esce. È ciò che distingue il Liquid Glass dalla sfocatura opaca, e
       fino alla v19.53 c'era su UNA superficie sola (la capsula in basso). */
    --g-ring-hi:rgba(255,255,255,.98);
    --g-ring-lo:rgba(255,255,255,.14);
    /* ⚠️ IL TESTO DI SERVIZIO SOPRA IL VETRO NON È --muted (difetto v19.54).
       «6 attive · 2 in ritardo» sotto il titolo, e il segnaposto delle barre
       di ricerca, stavano a 2,87:1 in chiaro e 4,11:1 in scuro contro il
       minimo di 4,5 — misurato sul composito, non su un fondo solido, ed è
       per questo che `dark-theme.test.js` non lo vedeva: campiona su fondi
       pieni. Non è un difetto introdotto qui, c'era dalla v19.42.
       Su un fondo che cambia mentre scorri, il testo di servizio va su un
       grigio CARICO. 5,32:1 in chiaro, 6,10:1 in scuro. */
    --g-ink2:#334d63;
    /* ── Scala dei raggi del guscio mobile ──────────────────────────────
       A schermo su iPhone convivevano ELEVEN raggi diversi (9 · 11 · 12 · 13
       · 14 · 16 · 18 · 20 · 22 · 28 · 999) e otto di essi non erano nella
       scala dichiarata --r-sm/--r/--r-lg. Questi cinque valori la
       sostituiscono sotto i 640px. --gr-xl è pensato per essere concentrico
       con l'angolo dello schermo di un iPhone. */
    --gr-xs:12px; --gr-sm:18px; --gr:22px; --gr-lg:28px; --gr-xl:38px;
  }
  @media(max-width:640px){
    /* Il vetro ha bisogno di COLORE sotto per vedersi: senza, è grigio su
       grigio. Non è un gradiente decorativo — sono i due colori della merce
       (il verde del brand, il cuoio del crust) diluiti quasi a zero. */
    .main::after{
      content:'';position:absolute;inset:0;pointer-events:none;z-index:0;
      background:
        radial-gradient(420px 300px at -12% -4%, rgba(0,60,15,.10), transparent 70%),
        radial-gradient(380px 320px at 112% 22%, rgba(154,111,44,.11), transparent 70%);
    }
    /* ⚠️ La ricetta del vetro si scrive UNA volta sola, qui: ogni superficie
       che la vuole si aggiunge a questo elenco o a quello di `.vetro-hi` più
       sotto. Prima era ricopiata in cinque punti con la saturazione scritta a
       mano, ed è così che si finisce con due vetri diversi nello stesso
       schermo. */
    .appbar,#subbar,.tabbar{
      background:var(--g-fill);
      -webkit-backdrop-filter:blur(var(--g-blur)) saturate(var(--g-sat)) brightness(var(--g-bright));
      backdrop-filter:blur(var(--g-blur)) saturate(var(--g-sat)) brightness(var(--g-bright));
    }
    .appbar{
      border-bottom:0;
      /* l'anello di luce in alto dichiara lo spessore; l'ombra sotto stacca
         la barra dal contenuto che le scorre sotto */
      box-shadow:inset 0 -1px 0 var(--g-edge), inset 0 1px 0 var(--g-ring-hi),
                 0 6px 22px -14px rgba(15,36,56,.34);
    }
    #subbar{
      box-shadow:inset 0 -1px 0 var(--g-edge),
                 0 10px 26px -16px rgba(15,36,56,.30);
      border-radius:0 0 var(--gr-xl) var(--gr-xl);
    }
    /* La barra in basso GALLEGGIA: si stacca dai bordi e diventa una capsula,
       così il vetro si vede su tre lati invece che solo sopra. Lo spazio che
       occupa resta quello dichiarato da --pb-tabbar (lo scrive renderTabbar),
       quindi il contenuto non si sposta di un pixel. */
    /* ⚠️ La barra RESTA a filo del fondo (left/right/bottom a 0). Nell'anteprima
       galleggiava staccata dai bordi — più vicino a iOS 26 — ma quella forma
       viola l'invariante «la barra arriva al fondo», che è costata tre giri di
       correzioni e ha una card di diagnostica dedicata: staccandola, sotto
       restano scoperti i px della safe-area, ed è esattamente il sintomo della
       fascia grigia. Il vetro si vede lo stesso, perché il contenuto scorre già
       sotto la barra (è un overlay). La variante flottante si può provare, ma
       va verificata su un iPhone VERO: Chromium non riproduce quella quirk.
       Il presidio è `mobile-css.test.js` → «PWA installata: la barra in basso
       arriva al fondo». */
    .tabbar{
      border-top:0;border-radius:24px 24px 0 0;
      background:var(--g-fill-hi);
      box-shadow:inset 0 1px 0 var(--g-spec), inset 0 0 0 .5px var(--g-edge),
                 0 -6px 22px -10px rgba(15,36,56,.22);
    }
    /* ⚠️ L'anello di luce sul bordo — ciò che distingue il Liquid Glass dalla
       vecchia sfocatura opaca — è un ::before mascherato ad anello. Sulla
       .tabbar il ::before NON è libero: renderTabbar ci scrive dentro le voci,
       ma sono figli veri, non pseudo-elementi, quindi non c'è collisione. */
    .tabbar::before{
      content:'';position:absolute;inset:0;border-radius:inherit;pointer-events:none;padding:1.4px;
      background:linear-gradient(100deg,
        rgba(255,255,255,.95) 0%, rgba(255,255,255,.30) 34%,
        rgba(255,255,255,.72) 62%, rgba(255,255,255,.20) 100%);
      -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
      -webkit-mask-composite:xor;
      mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
      mask-composite:exclude;
    }
    /* ── TUTTE le superfici che galleggiano, in un elenco solo ────────────
       Fogli, menu, testate e piedi delle finestre, chip flottanti. Fino alla
       v19.53 il vetro si fermava alle prime quattro: si apriva una scheda e
       l'effetto spariva. Le altre sono arrivate con la copertura totale
       approvata dall'utente (v19.54).
       ⚠️ Il CORPO delle finestre resta pieno: in vetro vanno la testata e il
       piede — quelli che galleggiano sopra il contenuto — non il contenuto. */
    .filtsheet,.rowsheet,.ordmenu,.pst-menu,
    .pvhead,#pv-bar,.pvsheet,
    #panel .panel-head,#panel .panel-foot,
    #tpanel .panel-head,#tpanel .panel-foot,
    #rpanel .panel-head,#rpanel .panel-foot,
    .dkchip,.dock .dk-lab{
      background:var(--g-fill-hi);
      -webkit-backdrop-filter:blur(var(--g-blur)) saturate(var(--g-sat)) brightness(var(--g-bright));
      backdrop-filter:blur(var(--g-blur)) saturate(var(--g-sat)) brightness(var(--g-bright));
      box-shadow:inset 0 1px 0 var(--g-spec), var(--g-drop);
    }
    /* ⚠️ I MENU E IL CALENDARIO RESTANO OPACHI (v19.91, segnalato dall'utente).
       Il vetro va sulle superfici che si guardano ATTRAVERSO — barre, fogli,
       chip — non su quelle in cui si LEGGE. Un menu del modulo galleggia sopra
       il testo dei campi (nero, 15px, a due dita di distanza) e col fill al 56%
       si leggevano due testi sovrapposti: nella tendina «Scelte» la voce
       «I – II» cadeva esattamente su «pronta o da creare». È la stessa regola
       già dichiarata poche righe più su per le righe di elenco — sotto i dati
       il vetro non va mai — solo applicata a ciò che ha dati DENTRO.
       Forma e ombre restano quelle del vetro: cambia solo il fondo. */
    .ms-pop,.calpop{
      background:var(--panel);
      box-shadow:inset 0 1px 0 var(--g-spec), var(--g-drop);
    }
    /* ── L'ANELLO DI LUCE — lo spessore del vetro ─────────────────────────
       Un ::before mascherato ad anello, con la luce che entra da un lato e si
       spegne dall'altro. Fino alla v19.53 c'era su UNA superficie sola.
       ⚠️ Si applica a elementi che hanno già `position` diversa da static e
       che NON usano il ::before per altro. La `.tabbar` lo ha (ma è
       trasparente dalla v19.44: l'anello vero sta sulla `.tb-cap`), i fogli e
       i menu no, quindi è libero. */
    .filtsheet::before,.rowsheet::before,.ordmenu::before,.pst-menu::before,
    .ms-pop::before,.calpop::before,.pvsheet::before{
      content:'';position:absolute;inset:0;border-radius:inherit;pointer-events:none;padding:1.2px;
      background:conic-gradient(from 200deg,
        var(--g-ring-hi) 0deg, var(--g-ring-lo) 62deg, var(--g-ring-lo) 148deg,
        var(--g-ring-hi) 214deg, var(--g-ring-lo) 286deg, var(--g-ring-lo) 340deg);
      -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
      -webkit-mask-composite:xor;
      mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
      mask-composite:exclude;
      z-index:1;
    }
    /* ⚠️ Nessuno di questi elementi riceve qui una `position`: la hanno già
       tutti (fixed i fogli e il calendario, fixed-o-absolute i menu e le
       tendine, che il JS posiziona caso per caso). Forzarla romperebbe
       `positionMSPop` e il portale nel <body>. */

    /* ── CONCENTRICITÀ: raggio interno = esterno − padding ────────────────
       iOS 26 ci insiste, ed è la differenza fra un elemento che «appartiene»
       al suo contenitore e uno che ci galleggia dentro storto.
       ⚠️ La capsula della barra in basso ha padding 8px: 28 − 8 = 20px, non
       gli 11px che aveva (mancavano nove pixel). */
    .filtsheet,.rowsheet{ border-radius:var(--gr-xl) var(--gr-xl) 0 0; }
    .pvsheet{ border-radius:var(--gr-xl) var(--gr-xl) 0 0; }
    .ordmenu,.pst-menu{ border-radius:var(--gr-lg); }
    .ms-pop,.calpop{ border-radius:var(--gr); }
    .filtbtn,.ms-btn,.search{ border-radius:var(--gr-sm); }
    .row,.arow{ border-radius:var(--gr); }
    /* la capsula in basso: 28 − 8 di padding = 20px sulla voce */
    .tb-cap{ border-radius:var(--gr-lg); }
    .tb-item{ border-radius:20px; position:relative; }
    /* ── La PILLOLA della voce attiva ─────────────────────────────────────
       Fino alla v19.53 la voce selezionata cambiava solo colore, e sopra un
       fondo di vetro — che cambia mentre scorri — il colore da solo si nota
       poco. In iOS 26 la voce attiva sta dentro una pastiglia.
       ⚠️ `z-index:-1` la manda dietro icona ed etichetta senza toccare il
       markup di renderTabbar (le voci sono figli veri, non pseudo).

       ⚠️ QUATTRO NUMERI, UNA SOLA IDEA: 4px D'ARIA SU TUTTO IL CONTORNO
       (v19.87, variante C approvata dall'utente su anteprima fra tre). La
       capsula ha padding 8 sopra, 9 sotto, 6 ai lati, quindi:
        • `top:-4px`    → 8 − 4 = 4px d'aria sopra
        • `bottom:-5px` → 9 − 5 = 4px sotto (il padding è asimmetrico: la
          pillola si centra nella CAPSULA, non nella voce)
        • `width:calc(100% + 4px)` → sporge 2px per lato dalla voce, quindi
          sulla prima e sull'ultima restano 6 − 2 = 4px dal bordo della barra
        • `border-radius:24px` = 28 − 4, la concentricità che vale per tutto
          il guscio (raggio interno = esterno meno l'aria). Il 20px di
          `.tb-item` qui sopra invece RESTA giusto: è un altro box, rientrato
          di 8px (il padding), non di 4.
       Se tocchi uno di questi quattro, ricalcola gli altri tre: sono la
       stessa misura scritta in quattro posti.

       ⚠️ LA LARGHEZZA SEGUE LA VOCE, E NON PUÒ TORNARE FISSA. Dalla v19.63 al
       19.85 era `min(74px, calc(100% + 6px))`: sotto i 376px di schermo
       comandava il secondo termine (aria costante, simmetrico), sopra restava
       il tetto dei 74 mentre la voce continuava a crescere — quindi l'aria
       LATERALE cresceva con lei e quella verticale no. Misurato sulla prima
       voce con cinque voci in barra: 4,4px a sinistra su 390, **5,59 su 402**,
       **8,4 su 430** contro i 3 di sopra e sotto; e con tre sole voci in barra
       (le voci le sceglie l'utente in Impostazioni) si arrivava a **30px**.
       È il difetto che l'utente ha fotografato: «l'alone verde non è
       perfettamente simmetrico con la barra». Una larghezza fissa non può
       essere simmetrica su una voce elastica — non è un valore da correggere,
       è il `min()` da togliere.
       ⚠️ Conseguenza dichiarata: la pillola non ha più UNA misura, ma quella
       della sua voce + 4 (79,2 × 59 su 402, 82,8 su 430). Il giro di misure
       della v19.63 — «troppo stretto», «troppo largo», «troppo schiacciata»,
       da cui i 74 × 61 — resta valido come ordine di grandezza: a 402 siamo a
       79,2 × 59, cioè lì attorno.
       ⚠️ Il SOFFITTO in altezza è 66px (8 sopra + 9 sotto + i 50 della voce):
       oltre, la pillola esce dalla capsula. A 59 ne restano 4 e 4.

       Presidiato da mobile-css.test.js, che misura l'aria lungo TUTTO il
       contorno con una funzione di distanza con segno — guardando i soli lati
       (o l'ingombro dei rettangoli) i numeri tornano anche quando le curve
       divergono, perché la differenza sta nell'ARCO. */
    .tb-item.on::before{
      content:'';position:absolute;
      top:-4px;bottom:-5px;left:50%;right:auto;transform:translateX(-50%);
      width:calc(100% + 4px);border-radius:24px;z-index:-1;
      background:var(--soft);box-shadow:inset 0 0 0 .5px var(--soft-bd);
    }
  }
  /* Tema scuro: il vetro si scurisce, il riflesso si smorza. Senza questo
     blocco resterebbe il vetro chiaro e le barre diventerebbero lattiginose. */
  @media screen{
    html[data-theme="dark"]{
      --g-fill:rgba(26,38,52,.46);
      --g-fill-hi:rgba(30,44,58,.60);
      /* ⚠️ Al buio `brightness` va SOTTO 1: schiarire un fondo scuro lo
         renderebbe lattiginoso, cioè l'opposto di ciò che serve. Misurato,
         il titolo resta a 7,09:1 contro i 4,5 richiesti. */
      --g-bright:.80;
      --g-spec:rgba(255,255,255,.24);
      --g-spec-lo:rgba(255,255,255,.06);
      --g-edge:rgba(255,255,255,.10);
      --g-drop:0 10px 30px -8px rgba(0,0,0,.66),0 1px 3px rgba(0,0,0,.5);
      --g-ring-hi:rgba(255,255,255,.42);
      --g-ring-lo:rgba(255,255,255,.05);
      --g-ink2:#c3d2df;
    }
    @media(max-width:640px){
      /* ⚠️ In tema scuro l'appbar ha già `background:var(--panel)` (blocco dark,
         più su nel file): specificità (0,2,1) contro la (0,1,0) del vetro, quindi
         vinceva lei e la barra restava OPACA — il vetro si vedeva solo in chiaro.
         Qui si ripristina il fondo semitrasparente con la stessa specificità. */
      html[data-theme="dark"] .appbar{ background:var(--g-fill); }
      html[data-theme="dark"] .main::after{
        background:
          radial-gradient(420px 300px at -12% -4%, rgba(142,225,163,.11), transparent 70%),
          radial-gradient(380px 320px at 112% 22%, rgba(231,200,122,.07), transparent 70%);
      }
      html[data-theme="dark"] .tabbar::before{
        background:linear-gradient(100deg,
          rgba(255,255,255,.40) 0%, rgba(255,255,255,.10) 34%,
          rgba(255,255,255,.30) 62%, rgba(255,255,255,.07) 100%);
      }
      /* ⚠️ Stessa trappola dell'appbar: testata e piede degli editor, la
         testata del dettaglio e le tendine hanno già un `background` proprio
         nel blocco scuro, con specificità maggiore del vetro. Senza queste
         righe restano OPACHI e il vetro si vede solo in chiaro. */
      html[data-theme="dark"] #panel .panel-head,
      html[data-theme="dark"] #panel .panel-foot,
      html[data-theme="dark"] #tpanel .panel-head,
      html[data-theme="dark"] #tpanel .panel-foot,
      html[data-theme="dark"] #rpanel .panel-head,
      html[data-theme="dark"] #rpanel .panel-foot,
      html[data-theme="dark"] .pvhead,
      html[data-theme="dark"] #pv-bar,
      html[data-theme="dark"] .pvsheet,
      html[data-theme="dark"] .dkchip{ background:var(--g-fill-hi); }
      /* ⚠️ menu e calendario NON sono in questo elenco: restano opachi in
         entrambi i temi (vedi la nota sul vetro poco più su). Al buio il
         difetto è lo stesso — testo chiaro del menu sopra testo chiaro del
         modulo — e `--panel` è già il fondo scuro giusto. */
      /* la pillola della voce attiva: il verde tenue del tema chiaro al buio
         sarebbe una macchia scura, serve il verde chiaro trasparente */
      html[data-theme="dark"] .tb-item.on::before{
        background:rgba(142,225,163,.14);
        box-shadow:inset 0 0 0 .5px rgba(142,225,163,.24);
      }
    }
  }

  /* ══════════════════════════════════════════════════════════════════════
     IL MODULO A LISTE RAGGRUPPATE (v19.59) — variante C approvata
     ──────────────────────────────────────────────────────────────────────
     Il guscio della finestra era già da app (foglio, maniglia, testata e piede
     in vetro col modulo che ci scorre sotto), ma DENTRO erano rimasti i campi
     di sempre: etichetta maiuscoletta grigia sopra, riquadro bordato sotto,
     uno per uno su fondo grigio, con raggi da 8px — fuori dalla scala del
     guscio. Nelle app di iPhone un modulo è una LISTA RAGGRUPPATA: card
     bianche con dentro le righe e separatori sottili.

     VARIANTE C: riga compatta per ciò che si SCEGLIE, campo intero per ciò
     che si DIGITA. In una riga «etichetta a sinistra, valore a destra» il
     valore non è più un campo largo — per le tendine è perfetto (sono già
     così), per i numeri e le date sarebbe una colonna stretta.
     Misurato sui 14 campi delle prime tre sezioni: 1.329 → 1.117px.

     ⚠️ VALE SU TUTTI E TRE I PANNELLI senza una riga in più: scheda, tintura
     e rasatura condividono `.fgrid > .field` (44 · 22 · 21 campi, di cui
     14 · 9 · 10 di scelta). Se aggiungi un pannello con la stessa struttura,
     lo eredita.
     ⚠️ IL MARKUP NON SI TOCCA. La distinzione è `:has(> .ms)`, cioè «questo
     campo ha una tendina»: `enhanceManaged` mette l'input DENTRO l'host `.ms`
     e `enhanceSelect` glielo mette ACCANTO, ma in entrambi i casi la `.ms` è
     figlia del `.field`. Senza `:has()` la regola cade e resta il modulo
     impilato di prima — cioè il ripiego è lo stato precedente, non un rotto.
     ⚠️ La card è `.fsec-body`, che ESISTE GIÀ (la crea `wireFormSections` in
     app.9g per rendere le sezioni richiudibili): qui si limita a smettere di
     essere `display:contents` e a vestirsi. Niente contenitore nuovo.
     ⚠️ `:not(.full)` NON è un dettaglio: un campo dichiarato a tutta larghezza
     lo è perché ha bisogno di spazio — «Commessa» porta un'etichetta lunga
     («solo le commesse aperte del cliente…») che in riga sbordava di 50px,
     misurati. Un campo `.full` non è mai un candidato per la riga compatta.
     ⚠️ Restano fuori di proposito: le DATE (hanno una nota sotto e un
     messaggio d'errore, che in una riga non ci starebbero), le caselle di
     spunta, e tutto ciò che è complesso — `.mqgrid`, mix crust, ripartizione
     clienti, lavorazioni, note. La `.mqgrid` in particolare NON si tocca: è
     la griglia allineata stima|reali, una preferenza già dichiarata.
     ══════════════════════════════════════════════════════════════════════ */
  @media(max-width:640px){
    /* la sezione diventa una card: era `display:contents`, quindi i campi
       cadevano dritti nella .fgrid e non c'era niente da vestire */
    #panel .fsec-body,#tpanel .fsec-body,#rpanel .fsec-body{
      display:block;background:var(--panel);border-radius:var(--gr);
      box-shadow:var(--sh-sm);overflow:hidden;margin-bottom:4px;
    }
    #panel .fsec-body[hidden],#tpanel .fsec-body[hidden],#rpanel .fsec-body[hidden]{display:none;}
    /* l'intestazione esce dalla card e diventa un'etichetta di gruppo */
    #panel .sec-title.fsec-head,#tpanel .sec-title.fsec-head,#rpanel .sec-title.fsec-head{
      border-top:0;padding:14px 13px 6px;margin:0;color:var(--muted);
      font-size:11px;letter-spacing:.08em;
    }
    #panel .sec-title.fsec-head:not(.closed),
    #tpanel .sec-title.fsec-head:not(.closed),
    #rpanel .sec-title.fsec-head:not(.closed){color:var(--muted);}
    /* i campi dentro la card: niente margine, un separatore sottile che parte
       dopo l'etichetta (come nelle liste di iOS) */
    /* ⚠️ IL PADDING È MISURATO, non scelto per estetica. Fra un campo e l'altro
       la `.fgrid` lasciava 6px di gap; dentro la card il gap non c'è più e al
       suo posto c'è questo padding, che si paga su OGNI campo — trenta volte.
       A 10/11px il modulo si ALLUNGAVA del 3÷6% (misurato) nonostante le righe
       compatte: la card costava più di quanto le righe risparmiassero. A 7/8
       si torna in pareggio (−1% · +2% · +1%). Se lo alzi, rimisura. */
    #panel .fsec-body > .field,#tpanel .fsec-body > .field,#rpanel .fsec-body > .field{
      padding:7px 14px 8px;margin:0;position:relative;gap:3px;
    }
    #panel .fsec-body > .field + .field::before,
    #tpanel .fsec-body > .field + .field::before,
    #rpanel .fsec-body > .field + .field::before{
      content:'';position:absolute;left:14px;right:0;top:0;height:1px;background:var(--line);
    }
    /* i campi che si DIGITANO: l'input perde il bordo e diventa un incasso,
       che dentro una card bianca legge meglio di un riquadro nel riquadro.
       ⚠️ Le DATE e la griglia dei METRI QUADRI vanno elencate a parte: le prime
       hanno l'input dentro `.datefield-wrap` (ce lo mette `attachCalendar`, per
       il glifo del calendario), la seconda tiene i suoi campi dentro `.mqgrid`
       — in entrambi i casi non sono figli DIRETTI del `.field` e restavano
       riquadrate accanto a campi incassati, nella stessa card (visto: «Data»
       col bordo e «N° scheda» senza, una sotto l'altra).
       ⚠️ Restano fuori le tendine (`.ms`, che hanno la loro forma) e il
       calcolatore `.calc-inp`, che ha una palette scura sua. */
    #panel .fsec-body .field > input,#panel .fsec-body .field > textarea,
    #panel .fsec-body .field .datefield-wrap > input,
    #tpanel .fsec-body .field > input,#tpanel .fsec-body .field > textarea,
    #tpanel .fsec-body .field .datefield-wrap > input,
    #rpanel .fsec-body .field > input,#rpanel .fsec-body .field > textarea,
    #rpanel .fsec-body .field .datefield-wrap > input{
      border-color:transparent;background:var(--sidebar);border-radius:12px;
    }
    /* ⚠️ IL CAMPO BLOCCATO DEVE RESTARE RICONOSCIBILE (v19.64). La regola qui
       sopra ha specificità (1,2,1) e batte `.field input:disabled` (0,2,1):
       dalla v19.59 un campo spento aveva ESATTAMENTE lo stesso colore di uno
       acceso — misurato, rgb(241,245,249) in entrambi i casi. È il segnale su
       cui si regge `setLock` per le schede Consegnata e Trasferita e per il
       «🔓 Forza modifica»: senza, il pannello sembra scrivibile e non lo è. */
    #panel .fsec-body .field > input:disabled,
    #panel .fsec-body .field > textarea:disabled,
    #panel .fsec-body .field .datefield-wrap > input:disabled,
    #panel .fsec-body .field.locked > input,
    #tpanel .fsec-body .field > input:disabled,
    #tpanel .fsec-body .field > textarea:disabled,
    #tpanel .fsec-body .field .datefield-wrap > input:disabled,
    #tpanel .fsec-body .field.locked > input,
    #rpanel .fsec-body .field > input:disabled,
    #rpanel .fsec-body .field > textarea:disabled,
    #rpanel .fsec-body .field .datefield-wrap > input:disabled,
    #rpanel .fsec-body .field.locked > input{
      background:var(--line);color:var(--muted2,var(--muted));
      border:1px dashed var(--line2);
    }
    /* ── i campi che si SCELGONO diventano una riga ─────────────────────── */
    #panel .fsec-body > .field:has(> .ms),
    #tpanel .fsec-body > .field:has(> .ms),
    #rpanel .fsec-body > .field:has(> .ms){
      flex-direction:row;align-items:center;flex-wrap:wrap;
      column-gap:12px;row-gap:2px;min-height:46px;
    }
    #panel .fsec-body > .field:has(> .ms) > label,
    #panel .fsec-body > .field:has(> .ms) > .flabrow,
    #tpanel .fsec-body > .field:has(> .ms) > label,
    #tpanel .fsec-body > .field:has(> .ms) > .flabrow,
    #rpanel .fsec-body > .field:has(> .ms) > label,
    #rpanel .fsec-body > .field:has(> .ms) > .flabrow{
      flex:0 0 auto;max-width:100%;
    }
    #panel .fsec-body > .field:has(> .ms) > label,
    #panel .fsec-body > .field:has(> .ms) > .flabrow > label:first-child,
    #tpanel .fsec-body > .field:has(> .ms) > label,
    #tpanel .fsec-body > .field:has(> .ms) > .flabrow > label:first-child,
    #rpanel .fsec-body > .field:has(> .ms) > label,
    #rpanel .fsec-body > .field:has(> .ms) > .flabrow > label:first-child{
      font-size:14.5px;font-weight:600;letter-spacing:0;
      text-transform:none;color:var(--ink);
    }
    /* la tendina si stringe a destra e perde il riquadro: resta il valore col
       «›», come in una lista di iOS. Il popup si ancora al pulsante via JS
       (positionMSPop), quindi segue la larghezza nuova da solo. */
    #panel .fsec-body > .field:has(> .ms) > .ms,
    #tpanel .fsec-body > .field:has(> .ms) > .ms,
    #rpanel .fsec-body > .field:has(> .ms) > .ms{
      margin-left:auto;min-width:0;flex:1 1 90px;max-width:62%;
    }
    /* ⚠️ Il riquadro si toglie SOLO alla tendina viva: `.crustlock` (crust
       ereditato dalla catena) e `.locked`/`.disabled` dicono di essere spente
       col BORDO TRATTEGGIATO e col lucchetto, e una tendina senza riquadro non
       avrebbe più niente da tratteggiare — resterebbe un valore come tutti gli
       altri. Lì il controllo resta scatolato, appoggiato a destra. */
    #panel .fsec-body > .field:has(> .ms) > .ms:not(.crustlock):not(.locked):not(.disabled) .ms-btn,
    #tpanel .fsec-body > .field:has(> .ms) > .ms:not(.crustlock):not(.locked):not(.disabled) .ms-btn,
    #rpanel .fsec-body > .field:has(> .ms) > .ms:not(.crustlock):not(.locked):not(.disabled) .ms-btn{
      border-color:transparent;background:transparent;box-shadow:none;
      justify-content:flex-end;text-align:right;min-height:0;
      font-weight:700;color:var(--ink);
      /* ⚠️ Il padding destro NON è aria: la freccina è un `::after` in
         `position:absolute` a `right:11px`, quindi con `padding:0` il valore
         le finisce SOTTO (visto: «Es. CENTANNI» col ∨ sopra la N). Qui la
         freccina si sposta a filo e il testo le lascia i suoi 17px. */
      padding:0 17px 0 0;max-width:100%;overflow:hidden;
    }
    #panel .fsec-body > .field:has(> .ms) > .ms:not(.crustlock):not(.locked):not(.disabled) .ms-btn::after,
    #tpanel .fsec-body > .field:has(> .ms) > .ms:not(.crustlock):not(.locked):not(.disabled) .ms-btn::after,
    #rpanel .fsec-body > .field:has(> .ms) > .ms:not(.crustlock):not(.locked):not(.disabled) .ms-btn::after{right:2px;}
    /* un valore lungo tronca invece di sbordare (il pulsante è `nowrap`, quindi
       serve il `min-width:0` sul figlio o non si comprime) */
    #panel .fsec-body > .field:has(> .ms) > .ms .ms-btn .ms-cap,
    #tpanel .fsec-body > .field:has(> .ms) > .ms .ms-btn .ms-cap,
    #rpanel .fsec-body > .field:has(> .ms) > .ms .ms-btn .ms-cap{
      min-width:0;overflow:hidden;text-overflow:ellipsis;
    }
    /* ⚠️ IL POPUP VA ANCORATO A DESTRA. La regola base è `left:0` con
       `min-width:230px`: da una tendina larga 120px appoggiata al bordo destro
       si aprirebbe VERSO DESTRA e uscirebbe dallo schermo (misurato: 470px su
       390). Ancorandolo a destra cresce verso l'interno e ci sta sempre.
       Il JS non c'entra: fuori da un `.dlgwrap` azzera gli stili inline
       (`pop.style.left=''`), quindi qui comanda il CSS. */
    #panel .fsec-body > .field:has(> .ms) > .ms .ms-pop,
    #tpanel .fsec-body > .field:has(> .ms) > .ms .ms-pop,
    #rpanel .fsec-body > .field:has(> .ms) > .ms .ms-pop{
      left:auto;right:0;min-width:min(300px,calc(100vw - 44px));max-width:calc(100vw - 44px);
    }
    /* ⚠️ Le note e i messaggi sotto un campo-riga tornano a tutta larghezza,
       o si infilerebbero nella colonna stretta del valore. */
    #panel .fsec-body > .field:has(> .ms) > .hint,
    #panel .fsec-body > .field:has(> .ms) > small,
    #panel .fsec-body > .field:has(> .ms) > .fl-ordine-hint,
    #panel .fsec-body > .field:has(> .ms) > .lotto-saldo-hint,
    #tpanel .fsec-body > .field:has(> .ms) > .hint,
    #tpanel .fsec-body > .field:has(> .ms) > small,
    #tpanel .fsec-body > .field:has(> .ms) > .fl-ordine-hint,
    #tpanel .fsec-body > .field:has(> .ms) > .lotto-saldo-hint,
    #rpanel .fsec-body > .field:has(> .ms) > .hint,
    #rpanel .fsec-body > .field:has(> .ms) > small,
    #rpanel .fsec-body > .field:has(> .ms) > .fl-ordine-hint,
    #rpanel .fsec-body > .field:has(> .ms) > .lotto-saldo-hint{flex:1 0 100%;text-align:left;}
  }

  /* ══════════════════════════════════════════════════════════════════════
     IL FOGLIO «COSA VUOI CREARE?» (v19.57)
     ──────────────────────────────────────────────────────────────────────
     Variante A approvata su anteprima: le quattro voci in elenco, tutte con
     lo stesso peso — icona, nome, a cosa serve e il numero che verrà.
     È lo stesso foglio dei filtri e delle azioni (`.filtsheet`), quindi
     maniglia, angoli, vetro, velo e arretramento arrivano da lì: qui c'è
     solo la forma delle voci. Il markup lo costruisce `openCreaSheet`
     (app.9g).
     ⚠️ Le voci sono OPACHE (`--panel`) sopra il vetro del foglio: sono la
     cosa da leggere e da toccare, e su un fondo che cambia il testo di
     servizio scenderebbe sotto AA. È la stessa regola delle righe di elenco.
     ══════════════════════════════════════════════════════════════════════ */
  .crea-voce{
    display:flex;align-items:center;gap:12px;width:100%;text-align:left;border:0;cursor:pointer;
    background:var(--panel);border-radius:var(--gr,18px);padding:11px 13px;margin-bottom:7px;
    font:inherit;color:var(--ink);box-shadow:var(--sh-sm);
    transition:transform var(--d,.2s) var(--ease-spring);
    -webkit-tap-highlight-color:transparent;
  }
  .crea-voce:active{transform:scale(.978);}
  .crea-voce:focus-visible{outline:2px solid var(--green);outline-offset:2px;}
  .crea-voce .cv-ic{
    width:38px;height:38px;min-width:38px;border-radius:12px;background:var(--soft);color:var(--green);
    display:flex;align-items:center;justify-content:center;
  }
  .crea-voce .cv-ic svg{width:20px;height:20px;}
  .crea-voce .cv-tt{flex:1;min-width:0;}
  .crea-voce .cv-tt b{display:block;font-size:14.5px;font-weight:800;letter-spacing:-.01em;}
  .crea-voce .cv-tt span{display:block;font-size:11.5px;color:var(--muted);font-weight:600;line-height:1.3;}
  .crea-voce .cv-num{font-size:11px;font-weight:800;color:var(--muted2);letter-spacing:.03em;flex:0 0 auto;}
  .creasheet .filtsheet-body{padding-top:2px;}
  @media(hover:hover){ .crea-voce:hover{background:var(--soft);} }

  /* ══════════════════════════════════════════════════════════════════════
     IL CONTENUTO SCORRE SOTTO LE BARRE DELLE FINESTRE (v19.54)
     ──────────────────────────────────────────────────────────────────────
     Perché il vetro di testata e piede si veda, sotto ci deve passare
     qualcosa: altrimenti è un fondo pieno un po' sporco. È la stessa
     architettura delle barre dell'elenco (overlay + spazio riservato da un
     padding), applicata al dettaglio e ai tre editor.
     ⚠️ LO SPAZIO LO RISERVA UN PADDING SUL CONTENITORE CHE SCORRE, e le due
     misure le scrive il JS (`_misuraFinestraChrome` in app.9g) in
     --pvh-h/--pvb-h e --pnh-h/--pnf-h, come già fa `_measureChrome` per
     --chrome-h. I valori nel `var(…, …)` qui sotto sono il RIPIEGO: se il JS
     non gira, il contenuto parte comunque sotto la testata invece di
     finirci dietro.
     ⚠️ NON si tocca la geometria del pannello: `.panel` resta `inset:0` con
     height auto — l'altezza è costata tre giri di correzioni (v19.19÷21) e la
     regola è «ancorare, non misurare». Qui cambia solo dove sta la testata
     dentro quel box, non quanto è grande il box.
     ══════════════════════════════════════════════════════════════════════ */
  @media(max-width:640px){
    .pvbox{ position:relative; }
    /* ⚠️ `.pvbox .pvhead` e non `.pvhead` nudo: la maniglia di trascinamento
       (blocco v19.51, più IN ALTO nel file) usa proprio quel selettore, e con
       la specificità pari a uno solo vince chi sta dopo — cioè questa. Con
       `.pvhead` semplice bastava una riga `.pvbox .pvhead{position:…}` scritta
       ovunque prima per rimettere la testata nel flusso, e allora il suo
       ingombro si sommava al padding qui sotto: 139px di bianco. */
    .pvbox .pvhead{
      position:absolute;top:0;left:0;right:0;z-index:7;
      border-bottom:0;
      box-shadow:inset 0 -1px 0 var(--g-edge), inset 0 1px 0 var(--g-ring-hi);
    }
    #pv-bar{
      position:absolute;bottom:0;left:0;right:0;z-index:7;
      border-top:0;
      box-shadow:inset 0 1px 0 var(--g-ring-hi), 0 -8px 26px -14px rgba(15,36,56,.30);
    }
    /* ⚠️ I FOGLI «Stato»/«⋯» SI FERMANO SOPRA LA BARRA, non a bottom:0.
       Qui #pv-bar è absolute con z-index 7, cioè SOPRA il foglio (z-index 6):
       ancorato al fondo, il foglio si infilava sotto la barra e ne perdeva gli
       ultimi ~99px. E siccome è più corto del suo max-height NON scorre, quindi
       l'ultima voce era IRRAGGIUNGIBILE, non «da scorrere». Misurato a 390×844
       con notch 59/34: «Integrazione» a 754→798 con la barra che comincia a
       746, e nel foglio «Stato» spariva «Consegnata» su OGNI scheda — cioè dal
       telefono una scheda non si poteva segnare consegnata dall'anteprima.
       Fino alla v19.54 la barra stava nel FLUSSO e il foglio la copriva: la
       regressione nasce lì, insieme al vetro. Stesso pavimento di .pvscroll
       (una sola idea di «quanto è alta la barra»), e il padding di fondo perde
       la safe-area, che ora è dentro la barra sotto: sommandola resterebbe una
       fascia vuota. Fra 641 e 860px la barra è ancora nel flusso e il foglio la
       copre come sempre — lì non c'è niente da correggere. */
    .pvsheet{
      bottom:max(var(--pvb-h,0px), calc(66px + env(safe-area-inset-bottom)));
      padding-bottom:12px;
    }
    /* ⚠️ `max()` e non un semplice fallback: il valore fisso è il PAVIMENTO,
       non il ripiego. La misura arriva un frame dopo l'apertura (la testata
       cresce quando compare il «← Indietro», la barra quando si riempie di
       pulsanti), e in quel frame un `var(--pvh-h, …)` userebbe la misura
       VECCHIA — più bassa — mandando le prime righe dietro la testata.
       Col max() vince sempre il maggiore fra misura e pavimento. */
    /* ⚠️ IL PADDING VA SULLO SCROLLER (.pvscroll), NON sul contenitore flex
       che lo ospita (.pvmain). Messo su `.pvmain` lo spazio resta FERMO in
       cima: il contenuto comincia sotto la testata ma non ci passa mai
       sotto, e si vede una fascia vuota che non scorre — cioè si paga
       l'ingombro senza avere l'effetto. Sullo scroller, invece, il padding
       scorre col contenuto: è così che si ottiene «il contenuto passa sotto
       la barra», ed è quello che fa anche `.content` con --chrome-h. */
    .pvscroll{
      padding-top:max(var(--pvh-h,0px), calc(62px + env(safe-area-inset-top)));
      padding-bottom:max(var(--pvb-h,0px), calc(66px + env(safe-area-inset-bottom)));
      /* uno scrollIntoView finirebbe dietro la testata */
      scroll-padding-top:max(var(--pvh-h,0px), calc(62px + env(safe-area-inset-top)));
    }

    #panel,#tpanel,#rpanel{ position:fixed; }
    #panel .panel-head,#tpanel .panel-head,#rpanel .panel-head{
      position:absolute;top:0;left:0;right:0;z-index:7;
      border-bottom:0;
      box-shadow:inset 0 -1px 0 var(--g-edge), inset 0 1px 0 var(--g-ring-hi);
    }
    #panel .panel-foot,#tpanel .panel-foot,#rpanel .panel-foot{
      position:absolute;bottom:0;left:0;right:0;z-index:7;
      border-top:0;
      box-shadow:inset 0 1px 0 var(--g-ring-hi), 0 -8px 26px -14px rgba(15,36,56,.30);
    }
    #panel .panel-body,#tpanel .panel-body,#rpanel .panel-body{
      padding-top:calc(max(var(--pnh-h,0px), calc(60px + env(safe-area-inset-top))) + 12px);
      padding-bottom:calc(max(var(--pnf-h,0px), calc(66px + env(safe-area-inset-bottom))) + 12px);
      scroll-padding-top:8px;
    }
  }

  /* ══════════════════════════════════════════════════════════════════════
     IL PULSANTE PIENO IN TEMA SCURO — difetto trovato misurando (v19.42)
     `.btn.primary` è `--green2` con testo bianco. In tema scuro --green2
     diventa CHIARO (oggi #8EE1A3) e il bianco sopra scende a 2,21:1, contro il
     minimo di 4,5 — misurato sull'app in esecuzione, su «+ Nuova scheda»
     dell'appbar (quindi su OGNI pagina) e sui «＋ Aggiungi».
     È la stessa trappola dei «pieni verdi» già corretta per altri dieci
     selettori con --green-solid; `.btn.primary` non era nell'elenco e
     dark-theme.test.js campionava altri elementi.
     Qui il pulsante tiene il verde chiaro e passa a testo SCURO (8,4:1):
     su fondo scuro si vede molto meglio di un verde profondo.
     ══════════════════════════════════════════════════════════════════════ */
  @media screen{
    html[data-theme="dark"] .btn.primary{ color:#08120A; }
    html[data-theme="dark"] .btn.primary:hover{ background:var(--green3); color:#08120A; }
  }


  /* ══════════════════════════════════════════════════════════════════════
     QUANDO SALE UN FOGLIO, SOTTO IL CONTENUTO ARRETRA (v19.54)
     ──────────────────────────────────────────────────────────────────────
     Anteprima approvata dall'utente. Erano due cose sbagliate insieme:
     • i veli stavano a `blur(2px)`, cioè quasi niente — dietro il foglio
       filtri si continuavano a leggere «Schede», «6 attive» e i chip di
       stato, che competevano col foglio;
     • la vista sotto non si muoveva, quindi non si capiva che è una PILA di
       superfici e non una sostituzione di pagina.
     Ora il velo sfoca davvero e la vista rimpicciolisce del 4% arrotondandosi
     su una cornice scura, come in iOS.
     ⚠️ NON tocca nessuna invariante del guscio: le barre restano un overlay,
     `--chrome-h` non cambia, la `.tabbar` resta ancorata al fondo. Misurato:
     `.main` passa da 390 a 365px e torna, e i fogli non si muovono perché
     sono figli di <body>, non di `.main` (`_filtSheetEls` li appende lì).
     ⚠️ La cornice DEVE essere scura, o l'arretramento non si vede: ai bordi
     comparirebbe il grigio chiaro della pagina e sembrerebbe solo un salto.
     ⚠️ L'interruttore è `body.filtsheet-on`, che ESISTE GIÀ: la mettono e la
     tolgono `openFilterSheet`/`closeFilterSheet` e `openRowSheet`/
     `closeRowSheet` (app.9g). Agganciarsi a quella invece di introdurre una
     classe nuova significa zero JS da aggiungere e nessun punto in cui
     dimenticarsene — e un terzo foglio che un domani la usasse erediterebbe
     l'arretramento da solo, che è quello che si vuole.
     ⚠️ NON usare `scroll-locked`: si attiva anche per le finestre a tutto
     schermo (editor, dettaglio), dove l'arretramento non si vedrebbe e si
     pagherebbe una compositazione per niente.
     ⚠️ Il menu «⋯» delle commesse e il foglio del dettaglio restano fuori di
     proposito: un menu contestuale non fa arretrare la vista nemmeno in iOS,
     e il foglio del dettaglio sta DENTRO una finestra che copre già tutto.
     ══════════════════════════════════════════════════════════════════════ */
  @media(max-width:640px){
    .filtsheet-dim,.pvsheet-dim,
    .overlay.open,.dlgwrap.open,.pvwrap.open,.kbdhelp-wrap.open,
    .app.nav-open .nav-backdrop{
      -webkit-backdrop-filter:blur(18px) saturate(120%) brightness(.94);
      backdrop-filter:blur(18px) saturate(120%) brightness(.94);
      background:rgba(9,20,30,.28);
    }
    body.filtsheet-on .app{ background:#0b1620; }
    /* ⚠️ IL RAGGIO NON SI ANIMA (v20). Prima i quattro `border-radius`
       transivano insieme alla scala: è paint a ogni fotogramma sulla
       superficie che porta l'elenco — frame peggiore misurato 53,7ms. E
       l'effetto era ASIMMETRICO: la regola del raggio vive solo dentro
       `body.filtsheet-on`, quindi alla chiusura spariva subito e gli angoli
       tornavano squadrati di scatto mentre la scala stava ancora animando.
       Ora il raggio cambia senza transizione — istantaneo in entrambe le
       direzioni, quindi simmetrico — e l'unica cosa che si muove è la scala.
       ⚠️ `overflow:hidden` resta dentro la classe e NON va portato sulla
       regola base: a riposo ritaglierebbe tutto ciò che sporge da `.main`. */
    .main{ transition:transform var(--d-slow,.3s) var(--ease-spring); }
    body.filtsheet-on .main{ transform:scale(.96); border-radius:24px; overflow:hidden; }
  }
  @media (prefers-reduced-motion:reduce){
    body.filtsheet-on .main{ transform:none!important;border-radius:0!important; }
    body.filtsheet-on .app{ background:var(--bg)!important; }
  }

  /* ══════════════════════════════════════════════════════════════════════
     MOVIMENTO (v19.42) — il massimo che resta a 60 fps
     ──────────────────────────────────────────────────────────────────────
     Si animano SOLO `transform` e `opacity`: sono le due proprietà che il
     telefono gestisce sul compositor, senza rifare layout né paint. Tutto
     il resto (larghezze, colori, ombre) costa a ogni fotogramma.
     Misurato in Chromium su schermo iPhone: 56 fps con 60 righe che entrano
     insieme e la CPU rallentata 6×.
     Le curve e le durate sono quelle che il file ha già (--ease-spring,
     --d-fast/--d/--d-slow): non se ne inventano di nuove.
     ⚠️ Niente animazioni mentre il dito scorre: il blocco «barre che si
     ritirano» spegne apposta le transizioni durante il trascinamento.
     ══════════════════════════════════════════════════════════════════════ */
  @media(max-width:640px){
    /* 1 · pressione: il segnale che il tocco è arrivato (su iPhone non c'è
       il passaggio del mouse a darlo). Solo transform, quindi gratis. */
    .row,.prog-row,.ordcard,.card,.tb-item,.rib,.btn{
      transition:transform var(--d,.2s) var(--ease-spring);
    }
    .row:active,.prog-row:active,.ordcard:active,.tb-item:active{transform:scale(.978);}
    .rib:active,.btn:active{transform:scale(.94);}
    /* 2 · la cascata d'ingresso delle righe NON sta più qui: era un secondo
       sistema parallelo a `.view-anim`, con un keyframe e una scala di ritardi
       tutti suoi, e i due si sovrapponevano. Ora è una regola sola, valida a
       ogni larghezza — vedi «UNA CASCATA SOLA» più su nel file. */
    /* 3 · la barra in basso: l'icona attiva sale di due pixel e cresce
       appena. È il movimento più piccolo di tutti e si sente più degli altri,
       perché lo fai cento volte al giorno. */
    .tb-item svg{transition:transform var(--d-slow,.3s) var(--ease-spring);}
    .tb-item.on svg{transform:translateY(-2px) scale(1.12);}
  }

  /* 4 · le barre di avanzamento si riempivano animando la LARGHEZZA: layout
     a ogni fotogramma, la più cara di tutte. Stesso effetto con scaleX, che
     sta sul compositor. ⚠️ La percentuale ora è `transform:scaleX(k)` con
     k da 0 a 1 — chi le riempie da JS scrive `--p`, non `width`. */
  .ordbar-fill,.ordriga-bar i,.ord-sum-bar>span{
    width:100%!important;transform-origin:left center;
    transform:scaleX(var(--p,0));
    transition:transform var(--d-slow,.3s) var(--ease-out);
  }
  @media (prefers-reduced-motion:reduce){
    :is(.view-anim,.list-in) :is(.row,.arow,.prog-row,.colc-card,.kpi,.card,.glotti-tipo){
      animation:none!important;opacity:1!important;transform:none!important;}
    .row:active,.prog-row:active,.ordcard:active,.tb-item:active,
    .rib:active,.btn:active{transform:none!important;}
    .tb-item.on svg{transform:none!important;}
    .ordbar-fill,.ordriga-bar i,.ord-sum-bar>span{transition:none!important;}
  }


  /* ══════════════════════════════════════════════════════════════════════
     LA FORMA DELL'ANTEPRIMA (v19.44) — righe a card, barra a capsula
     ──────────────────────────────────────────────────────────────────────
     Solo ≤640px. Non tocca `rowHTML`: il contenuto delle righe resta quello
     pinnato dai test (cliente in evidenza, metri reali accesi, articolo per
     intero, in riga solo il chip m² e il «⋯»). Cambia solo la forma.
     ══════════════════════════════════════════════════════════════════════ */
  @media(max-width:640px){
    /* L'elenco si stacca dai bordi e ogni riga diventa una card che galleggia:
       è ciò che permette al vetro delle barre di avere qualcosa da rifrangere
       oltre al fondo pagina. */
    #list,#list-cons,#list-arch,#list-trash,#list-tint,#list-ras,
    #tint-trasf,#ras-trasf,#cons-catena,#arch-catena{
      padding:0 13px;display:flex;flex-direction:column;gap:9px;
    }
    /* ⚠️ Qui era stato provato ad AFFIANCARE numero e contenuto (il numero sta
       su una riga sua, 19,8px, con la colonna vuota per due terzi). Misurato:
       la riga passa da 163,8 a 267,2px e sei articoli su sei si troncano —
       `.rmain` porta titolo, colore, terzista e quantità, e in mezza larghezza
       va tutto a capo. Non rifarlo: per accorciare la riga davvero bisogna
       riorganizzare `rowHTML`, non ridisporne i blocchi in CSS. */
    /* ⚠️ `var(--corsia,transparent)` e non `transparent`: la corsia di stato la
       dichiara la classe `.rst-*` (vedi la nota accanto a quelle regole) e qui
       si compone con l'ombra della card. Scrivendo `transparent` la corsia
       spariva in tema scuro, dove questo blocco viene DOPO le classi di stato. */
    .row,.arow{
      border-bottom:0;border-radius:20px;background:var(--panel);
      padding:12px 13px 12px 15px;
      box-shadow:inset 4px 0 0 var(--corsia,transparent), 0 1px 2px rgba(15,36,56,.05), 0 5px 16px -8px rgba(15,36,56,.18);
      overflow:hidden;
    }
    html[data-theme="dark"] .row,html[data-theme="dark"] .arow{
      box-shadow:inset 4px 0 0 var(--corsia,transparent), 0 1px 2px rgba(0,0,0,.5), 0 6px 18px -8px rgba(0,0,0,.6);
    }
    /* La corsia di stato è già un box-shadow inset: con gli angoli tondi va
       ridisegnata come pseudo-elemento, o il raggio la taglia male. */
    .row{position:relative;}
    /* ── LA RIGA RISPONDE COME LE ALTRE CARD (v20) ──────────────────────
       Dalla v19.93 la riga è una card, ma aveva ancora la risposta della
       vecchia striscia piatta: cambiava SOLO il colore di fondo, mentre
       `.colc-card`, `.kpi`, `.prog-row` e `.csm-row` si sollevano da sempre
       (vedi il blocco «ANIMAZIONI & MICRO-INTERAZIONI»). Curiosità che dice
       tutto: la STESSA riga, dentro la pagina Ricerca, l'ombra ce l'aveva già
       (`.gs-item.gsrow`). Ora è una grammatica sola.
       ⚠️ L'ombra va riscritta per intero perché deve conservare la corsia di
       stato, che vive nello stesso `box-shadow`: dichiararne una senza
       `inset 4px 0 0 var(--corsia)` spegnerebbe il colore dello stato al
       passaggio del mouse. È la trappola già pagata dalle classi `.rst-*`. */
    @media(hover:hover){
      .row:hover,.arow:hover{
        transform:translateY(-2px);
        box-shadow:inset 4px 0 0 var(--corsia,transparent), 0 2px 4px rgba(15,36,56,.06), 0 10px 24px -10px rgba(15,36,56,.28);
      }
      html[data-theme="dark"] .row:hover,html[data-theme="dark"] .arow:hover{
        box-shadow:inset 4px 0 0 var(--corsia,transparent), 0 2px 6px rgba(0,0,0,.55), 0 12px 28px -10px rgba(0,0,0,.7);
      }
    }
    /* le righe «appena consegnate» e quelle barrate restano riconoscibili */
    .row.row-done{opacity:.72;}
    /* niente separatore fra card: lo spazio lo fa il gap */
    .row + .row{border-top:0;}
    .listwrap{background:transparent;border:0;box-shadow:none;}
    .listhead{padding-left:13px;padding-right:13px;}

    /* ── LA BARRA IN BASSO A CAPSULA ─────────────────────────────────────
       ⚠️ La `.tabbar` RESTA ancorata al fondo (bottom:0, larghezza piena) e
       diventa TRASPARENTE: è un semplice contenitore. A galleggiare è la
       capsula figlia `.tb-cap`, che porta il vetro e i margini.
       Serve così perché l'invariante «la barra arriva al fondo» — tre giri di
       correzioni e una card di diagnostica — misura `.tabbar`: staccando LEI
       dal fondo riappare la fascia grigia della safe-area. Staccando la
       capsula, invece, il contenitore continua a toccare il bordo e il test
       resta verde, ma a schermo si vede la forma dell'anteprima. */
    .tabbar{
      background:transparent!important;-webkit-backdrop-filter:none!important;backdrop-filter:none!important;
      box-shadow:none!important;border-radius:0!important;
      padding:0 12px calc(9px + env(safe-area-inset-bottom));
      display:block!important;
    }
    .tabbar::before{display:none;}
    .tb-cap{
      display:grid;grid-auto-flow:column;grid-auto-columns:1fr;
      border-radius:28px;padding:8px 6px 9px;position:relative;
      background:var(--g-fill-hi);
      /* ⚠️ La ricetta passa dai token, come tutte le altre superfici: qui la
         saturazione era scritta a mano (190%) e mancava `brightness`, quindi
         la capsula restava l'unica superficie con un vetro diverso dalle
         altre — visibile affiancandola alla barra dei filtri. */
      -webkit-backdrop-filter:blur(var(--g-blur)) saturate(var(--g-sat)) brightness(var(--g-bright));
      backdrop-filter:blur(var(--g-blur)) saturate(var(--g-sat)) brightness(var(--g-bright));
      box-shadow:inset 0 1px 0 var(--g-spec), inset 0 -1px 0 var(--g-spec-lo),
                 inset 0 0 0 .5px var(--g-edge), var(--g-drop);
    }
    /* l'anello di luce che dichiara lo spessore del vetro */
    .tb-cap::before{
      content:'';position:absolute;inset:0;border-radius:inherit;pointer-events:none;padding:1.4px;
      background:conic-gradient(from 200deg,
        rgba(255,255,255,.95) 0deg, rgba(255,255,255,0) 60deg,
        rgba(255,255,255,0) 150deg, rgba(255,255,255,.72) 215deg,
        rgba(255,255,255,.18) 280deg, rgba(255,255,255,0) 340deg);
      -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
      -webkit-mask-composite:xor;
      mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
      mask-composite:exclude;
    }
    html[data-theme="dark"] .tb-cap::before{
      background:conic-gradient(from 200deg,
        rgba(255,255,255,.40) 0deg, rgba(255,255,255,0) 60deg,
        rgba(255,255,255,0) 150deg, rgba(255,255,255,.30) 215deg,
        rgba(255,255,255,.07) 280deg, rgba(255,255,255,0) 340deg);
    }
  }

  /* ══════════════════════════════════════════════════════════════════════
     GLI ELENCHI SONO CARD ANCHE SUL COMPUTER (v19.93) — variante C, scelta
     dall'utente su anteprima fra tre
     ──────────────────────────────────────────────────────────────────────
     Il gemello desktop del blocco qui sopra. Misurato prima: sul telefono la
     riga è una card (raggio 20, fondo bianco, ombra, staccata dalle altre di
     9px), sul computer era una STRISCIA PIATTA dentro un pannello unico —
     raggio 0, fondo trasparente, nessuno stacco — con i dati dentro riquadri
     grigi bordati. Due grammatiche per la superficie che si guarda tutto il
     giorno, su sei pagine più le anagrafiche.

     ⚠️ PERCHÉ UN BLOCCO A PARTE E NON UNA MEDIA QUERY TOLTA: le misure sono
     diverse (il telefono ha padding 12/13, il computer 15/20) e soprattutto
     TRE blocchi ≤640px più in alto nel file scrivono `.row{padding:…}`. Una
     regola non mediata scritta qui, dopo di loro, vincerebbe a parità di
     specificità e cambierebbe il telefono. Il perimetro `min-width:641px` è
     l'unico modo di aggiungere il computer senza toccare l'altro.
     ⚠️ Il raggio è 20px come sul telefono, non `--r-lg` (18): è la stessa
     riga, e un numero diverso riaprirebbe la divergenza che si sta chiudendo.
     ══════════════════════════════════════════════════════════════════════ */
  @media(min-width:641px){
    #list,#list-cons,#list-arch,#list-trash,#list-tint,#list-ras,
    #tint-trasf,#ras-trasf,#cons-catena,#arch-catena{
      display:flex;flex-direction:column;gap:10px;
    }
    /* ⚠️ `var(--corsia,transparent)`: vedi la nota gemella nel blocco ≤640px. */
    .row,.arow{
      border-bottom:0;border-radius:20px;background:var(--panel);position:relative;overflow:hidden;
      box-shadow:inset 4px 0 0 var(--corsia,transparent), 0 1px 2px rgba(15,36,56,.05), 0 5px 16px -8px rgba(15,36,56,.18);
    }
    html[data-theme="dark"] .row,html[data-theme="dark"] .arow{
      box-shadow:inset 4px 0 0 var(--corsia,transparent), 0 1px 2px rgba(0,0,0,.5), 0 6px 18px -8px rgba(0,0,0,.6);
    }
    /* niente separatore fra card: lo stacco lo fa il gap */
    .row + .row{border-top:0;}
    .row.row-done{opacity:.72;}
    /* ⚠️ `overflow:visible` è load-bearing: il pannello aveva `overflow:hidden`
       per tenere dentro i suoi angoli, e lasciandolo TAGLIA l'ombra delle card
       (le righe sembrano incollate al bordo, che è il difetto di partenza). */
    .listwrap{background:transparent;border:0;box-shadow:none;overflow:visible;}
    /* l'intestazione non è più il cappello di un pannello ma una didascalia
       sopra le card: niente fondo, niente filetto, allineata alle righe */
    /* ⚠️ `--hint` e NON `--muted` per il conteggio: tolto il fondo chiaro del
       pannello, l'intestazione si posa su `--bg`, dove `--muted` scende a
       4,23:1 — la trappola già pagata da `.rep-sub` e `.set-qout`. In tema
       scuro `--hint` ricade su `--muted`, che lì sta a 8:1. */
    .listhead{background:transparent;border-bottom:0;padding:2px 6px 10px;}
    .listhead .lc{color:var(--hint);}

    /* ── I DATI DIVENTANO UNA RIGA DI TESTO CONTINUA (variante C) ────────
       Le stesse regole del telefono (cercare «riga di testo continua» nel
       blocco ≤640px), con la sola tipografia riportata alla scala del
       computer: là il valore è 12px perché tutta la riga è più piccola, qui
       la base è 13. ⚠️ Le tre eccezioni di colore vanno RIPETUTE: la regola
       generica `.row .rmeta-grid .rcell .rv` ha specificità 4 e batte sia
       `.rcell .rv.ok`/`.miss` (3, più in alto) sia `.rcell .rv.cli`, quindi
       senza di loro i metri reali si spegnerebbero a grigio, «da inserire»
       perderebbe il rosso e il cliente non si distinguerebbe più. */
    .row .rmeta-grid{display:block;font-size:13px;line-height:1.5;color:var(--muted);}
    .row .rmeta-grid .rcell{display:inline;background:none;border:0;border-radius:0;padding:0;min-width:0;}
    .row .rmeta-grid .rcell .rk{display:none;}
    .row .rmeta-grid .rcell .rv{display:inline;font-size:13px;font-weight:600;color:var(--muted);}
    .row .rmeta-grid .rcell .rv.cli{font-size:14px;font-weight:800;color:var(--ink);}
    .row .rmeta-grid .rcell .rv.miss{color:var(--st-late-tx);}
    .row .rmeta-grid .rcell .rv.ok{color:var(--st-cons-tx);font-weight:800;}
    .row .rmeta-grid .rcell+.rcell::before{content:" · ";color:var(--muted2);font-weight:400;}
    /* riga COMPATTA (le «appena consegnate» pinnate in cima): lì il cliente
       non sta in una cella ma nella riga di servizio, e si stacca uguale */
    .row.rcompact .rsub .rcli-nm{color:var(--ink);font-weight:800;font-size:13.5px;}
  }


  /* ── Lo SWIPE sulle righe è stato RIMOSSO (v19.56, richiesta dell'utente).
     C'era dalla v19.44: trascinando una riga verso sinistra si scopriva un
     pannello (`.swact`) con l'azione più probabile per quello stato, e il
     tocco prolungato apriva il foglio «⋯».
     ⚠️ Non reintrodurlo senza chiedere. Vale ancora la ragione per cui era
     stato scartato la prima volta (nota nel blocco del foglio «⋯»): va in
     conflitto con lo swipe-indietro di iOS, e in una lista due delle azioni
     candidate cancellano senza annulla. Le stesse azioni restano tutte nel
     foglio «⋯», che è il modo dichiarato di raggiungerle.
     Rimossi con lui: `app.9i-swipe.js`, la voce in `index.html` e in
     `sw.js`, e le regole `.swact`/`.sw-drag`/`.sw-open`. La sola cosa
     rimasta è `#list…{position:relative}`, che serve anche ad altro. */
  @media(max-width:640px){
    #list,#list-cons,#list-arch,#list-trash,#list-tint,#list-ras{position:relative;}
  }


  /* ══════════════════════════════════════════════════════════════════════
     LA TESTATA DELL'ANTEPRIMA (v19.46) — titolo grande + conteggio
     ──────────────────────────────────────────────────────────────────────
     Nell'anteprima approvata la testata è: titolo grande con sotto «6 attive ·
     2 in ritardo», il «+» tondo a destra, e la ricerca globale sulla RIGA
     SOTTO, in vetro. Il titolo su mobile era nascosto (`display:none`) proprio
     per far posto alla ricerca sulla stessa riga: mandando la ricerca a capo
     tornano entrambi, ed è quello che il prototipo mostra.
     ⚠️ L'appbar ha già `flex-wrap:wrap`, quindi il ritorno a capo non richiede
     una struttura nuova: basta che la ricerca prenda tutta la larghezza.
     ⚠️ `--chrome-h` è MISURATA da app.9g, quindi l'altezza maggiore non va
     dichiarata da nessuna parte: si adatta da sola.
     ══════════════════════════════════════════════════════════════════════ */
  @media(max-width:640px){
    /* ⚠️ La safe-area va RIPETUTA: questa regola sta DOPO quella di riga ~1678
       che la applica, e scrivendo `padding` in forma breve l'avevo azzerata —
       titolo e pulsanti finivano sotto l'orologio, e la corsa della ritirata
       (`--bar-shift` = altezza appbar − safe-area) andava a ZERO, cioè le barre
       non liberavano più spazio. È la trappola già pagata da `.pvhead`. */
    /* ⚠️ `flex-wrap` torna a `wrap`: la regola mobile di riga ~1684 lo mette a
       `nowrap` per tenere tutto su una riga, e con quello un `flex-basis:100%`
       non manda a capo — COMPRIME, schiacciando il titolo a larghezza zero. */
    .appbar{padding:10px 14px 9px;padding-top:calc(10px + env(safe-area-inset-top));
      row-gap:8px;align-items:center;flex-wrap:wrap;}
    /* il titolo torna, grande come nell'anteprima, col conteggio sotto */
    .appbar h1{display:block;font-size:25px;font-weight:800;letter-spacing:-.024em;
      line-height:1.12;flex:1 1 auto;min-width:0;margin:0;
      overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
    /* ⚠️ `flex:1 1 0`, NON `1 1 auto`: con la base «auto» il blocco parte dalla
       larghezza del TESTO e non si comprime, così un titolo lungo («Programma
       lavorazioni») spinge il «+» a capo su una riga sua — 48px sprecati su
       ogni pagina dal nome lungo. È la stessa trappola di `.prog-art`. */
    .appbar .ab-tit{flex:1 1 0;min-width:0;display:flex;flex-direction:column;gap:1px;}
    /* ⚠️ --g-ink2 e NON --muted: sopra il vetro il grigio tenue scende a
       2,87:1. Vedi la nota accanto al token. */
    #app-sub{font-size:12px;font-weight:600;color:var(--g-ink2);line-height:1.2;
      overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
    #app-sub .late{color:var(--st-late-tx);font-weight:800;}
    #app-sub:empty{display:none;}
    /* la ricerca globale scende sulla riga sua, a tutta larghezza */
    .gswrap{flex:1 0 100%;order:5;}
    .gsbar{height:42px;border-radius:14px;}
    /* il «+» resta il pieno verde tondo dell'anteprima */
    #new-btn{border-radius:50%;width:40px;min-width:40px;height:40px;min-height:40px;
      align-self:center;}
    .nav-toggle{align-self:center;}
    .mk-appbar{align-self:center;}
  }
  /* Nella pagina Ricerca la barra dell'appbar sparisce già (regola esistente):
     lì il titolo deve restare da solo, senza il conteggio di un'altra pagina. */
  .app.in-ricerca #app-sub{display:none;}

  /* ── I CHIP DI STATO E LA RICERCA DI PAGINA: PIENI, NON IN VETRO (v20) ──
     ⚠️ QUI C'ERA IL VETRO, ed è stato TOLTO su scelta dell'utente approvata
     su anteprima. La ragione è misurata: chip e campo di ricerca stanno
     DENTRO `#subbar`, che è già una superficie in vetro, quindi erano SEI
     `backdrop-filter` annidati in un settimo — e mentre le barre si ritirano
     il transform sul subbar obbliga a ricampionarli tutti a ogni fotogramma.
     Area sfocata a riposo: 1,15 schermate; con il dettaglio aperto 3,3.
     Il vetro resta dov'è ARCHITETTURA — le due barre in alto, i fogli, i
     menu, le testate delle finestre: superfici sotto cui il contenuto scorre
     davvero. Su elementi piccoli non aggiunge nulla a vedersi e si paga
     sempre, anche da fermi. Non rimetterlo senza chiedere.
     Il chip ATTIVO resta il pieno verde di prima (regola base di
     `.statechip.on`), che è anche ciò che lo tiene leggibile. */
  @media(max-width:640px){
    .statechip{background:var(--panel);box-shadow:var(--sh-sm);}
    .search{background:var(--panel);box-shadow:var(--sh-sm);}
  }


  /* ══════════════════════════════════════════════════════════════════════
     LA BARRA AZIONI DEL DETTAGLIO — QUATTRO PULSANTI UGUALI (v19.73)
     ──────────────────────────────────────────────────────────────────────
     La v19.46 aveva fatto del primario un pulsante GRANDE e in riga («una
     azione primaria, i secondari attorno»): misurato, su iPhone 390 il PDF
     prendeva 194px dei 378 della barra — METÀ — contro i 52 di Stato,
     Modifica e Altro, cioè 3,3 volte la loro area. Su richiesta dell'utente
     (variante B approvata su anteprima fra tre) i quattro tornano IDENTICI:
     87,5 × 52,5px l'uno. Il primario resta riconoscibile col FONDO — la
     pastiglia verde piena, ridotta al suo quadratino — e non con la misura.

     ⚠️ QUI NON C'È PIÙ CSS, ed è il punto: la geometria giusta è quella che
     `.pvb-btn` ha già (colonna, flex:1, icona 19px, raggio 11). Bastava
     smettere di scavalcarla. Le due regole rimosse erano `.pvb-btn.pri`
     (riga + padding + min-height, che lo rendeva anche più BASSO degli altri:
     46 contro 52,5px di area toccabile) e `#pv-bar .pvb-btn:not(.pri){flex:0
     0 auto}` — quest'ultima è quella che davvero allargava il PDF: teneva i
     secondari a 52px fissi e gli lasciava tutto lo spazio libero. Togliendo
     solo la prima, il primario restava largo uguale.

     ⚠️ IL PIENO È L'UNICA COSA CHE DISTINGUE QUESTO PULSANTE, ora che la
     misura è uguale per tutti: togliendolo «per uniformare ancora» si ottiene
     una barra di quattro comandi indistinguibili — che è la variante A, mostrata
     in anteprima e NON scelta. La regola base più su nel file è stata portata
     da `--soft`/`--green2` a `--green` pieno + bianco, invece di essere
     scavalcata qui. ⚠️ Il commento della v19.46 diceva che il tenue era 3,5:1:
     era vero ALLORA, non più — la palette «un verde solo» (v19.53) ha portato
     --green2 allo stesso #003C0F di --green, quindi oggi darebbe 12:1. Se
     riapri questa scelta, il vincolo è l'evidenza, non il contrasto.
     ⚠️ E in tema scuro `html[data-theme="dark"] .pvb-btn.pri` (elenco dei
     «pieni verdi», specificità maggiore di una media query) vince comunque:
     con la variante B è ciò che serve, ma è la ragione per cui le varianti
     SENZA pieno, provate in anteprima, restavano non uniformi al buio mentre
     di giorno cambiavano — se un domani si toglie il fondo, va tolto di lì.
     ══════════════════════════════════════════════════════════════════════ */
  @media(max-width:640px){
    #pv-bar{gap:8px;align-items:center;}
  }


  /* ══════════════════════════════════════════════════════════════════════
     LA BARRA DI TINTURE E RASATURE — UNA RIGA SOLA (v19.54)
     ──────────────────────────────────────────────────────────────────────
     Qui non c'è CSS: la barra sta su una riga perché le sono stati tolti
     dei PEZZI. La nota resta perché i pezzi non tornino dentro uno per volta.

     Con dieci comandi — ricerca, tre filtri suoi, le quattro tendine del
     crust (v19.4), «Azzera», «⬇ CSV» e «＋ Nuova tintura» — la barra chiedeva
     1.482px (misurati sull'app vera, caso peggiore: filtro attivo e quindi
     «Azzera» a vista), cioè una finestra da 1.770px: a due righe (103px
     invece di 49) su qualunque portatile.

     ⚠️ IL CSV DA SOLO NON BASTAVA, ed è la misura da ricordare prima di
     riprovarci: vale 87px e porta la soglia a 1.683px — a 1.440 la barra
     resta a due righe esattamente com'era. Il taglio vero sono le SCRITTE:

        via il CSV (dalla barra)                    −87px
        «Botte/Terzista» → «Terzista»               −55
        «Colore ottenuto» → «Colore ott.»           −30
        «Crust di partenza» → «Crust»               −74
        «＋ Nuova tintura» → «＋ Nuova»              −53
        ──────────────────────────────────────────────
        1.482 → 1.183px, cioè una riga da 1.471px

     ⚠️ Il nome per esteso NON si perde: sta nel `titolo` delle MS
     (app.7/app.9 → `renderMSButton`), che diventa il suggerimento al
     passaggio del mouse. Sulle rasature è load-bearing, perché gli spessori
     sono due: «Spessore» è la misura finale, «Sp. crust» quella del grezzo.
     ⚠️ Il CSV non è sparito: è in Impostazioni → Dati e backup, accanto
     all'export delle schede.
     ⚠️ Il margine è sottile per costruzione: su una finestra da 1.366px, o
     non a tutto schermo, la barra torna a due righe. Aggiungere un filtro
     qui vuol dire riaprire questa scelta, non «solo un altro pulsante».
     ⚠️ Le due strade più larghe sono state misurate e NON scelte dall'utente:
     togliere le quattro tendine crust (una riga da 1.158px, ma si perdono
     filtri chiesti in v19.4) e mandare tutti i filtri nel pannello «Filtri»
     anche su computer (800px, un tocco in più per filtrare).
     ⚠️ Sul telefono non cambia nulla: sotto i 640px i filtri stanno già nel
     foglio «Filtri» (_filtSheetSetup) e la barra era già su una riga.
     ══════════════════════════════════════════════════════════════════════ */


  /* ⚠️ LA RIGA PIÙ CORTA (variante A) — provata in CSS e ANNULLATA (v19.48).
     L'idea era unire in una griglia le due righe quasi vuote — quella del
     numero (19,8px) e quella dei comandi (38px) — mettendo numero e data a
     sinistra e i comandi a destra. Misurato: la riga scende da 167,8 a 139px,
     MA la prima riga si sovraffolla e gli elementi si sovrappongono: lì
     finiscono SEI cose — numero, data, badge di stato, badge «oltre stima»,
     chip m² e «⋯» — che su 364px non ci stanno. Il prototipo ne mostrava
     quattro perché non aveva il badge dei giorni.
     ⚠️ RIPROVATA in v19.50 con le tre strade possibili — badge nel flusso,
     badge nella riga del terzista, comandi sovrapposti ai dati — e MISURATE
     tutte: nessuna sta in piedi. I cinque pezzi chiedono ~520px dove a 390px
     ce ne sono 336, e il prezzo è sempre lo stesso: troncare il terzista,
     oppure +7px su ogni riga tranne una. Il dettaglio in CLAUDE.md, sezione
     «Il badge di stato non copre più il numero». Non riproporla senza
     chiedere. */

  /* ══════════════════════════════════════════════════════════════════
     SEZIONI RICHIUDIBILI DELL'ANTEPRIMA (v19.49)
     ──────────────────────────────────────────────────────────────────
     Margine, QR, riprese e ampliamenti diventano righe da toccare: sono i
     quattro blocchi più alti del dettaglio e i meno guardati.
     ⚠️ Solo ≤640px: sul computer `pvSez` (app.4) ritorna il blocco nudo e non
     cambia un pixel — vedi il commento lì.
     ⚠️ L'intestazione della sezione SOSTITUISCE quella del blocco: senza il
     `display:none` qui sotto il titolo comparirebbe due volte.
     ⚠️ I blocchi portano `style="margin-top:14px"` INLINE: dentro il
     contenitore lo spazio lo dà già il contenitore, quindi va annullato — e
     un inline si annulla solo con `!important`. */
  .pvsez{margin-top:14px;border:1px solid var(--border);border-radius:14px;background:var(--panel);overflow:hidden;}
  .pvsez-h{display:flex;align-items:center;gap:9px;width:100%;padding:11px 13px;background:transparent;border:0;
    font:inherit;color:var(--ink);text-align:left;cursor:pointer;-webkit-tap-highlight-color:transparent;}
  .pvsez-t{font-size:13.5px;font-weight:800;}
  .pvsez-n{font-size:11.5px;font-weight:700;color:var(--green);margin-left:auto;}
  .pvsez-x{margin-left:auto;color:var(--ink);font-size:15px;font-weight:800;line-height:1;
    transition:transform var(--d) var(--ease-spring);}
  .pvsez-n + .pvsez-x{margin-left:6px;}
  .pvsez:not(.chiusa) .pvsez-x{transform:rotate(90deg);}
  .pvsez-b{padding:0 11px 11px;}
  .pvsez.chiusa .pvsez-b{display:none;}
  .pvsez-b > *{margin-top:0 !important;}
  .pvsez-b > .pvcrust > .pvcrust-h,
  .pvsez-b > .calc-card > .margine-box > .pvcrust-h{display:none;}

  /* ══════════════════════════════════════════════════════════════════════
     IL VETRO SI SPEGNE IN DUE CASI (v19.54)
     ──────────────────────────────────────────────────────────────────────
     1 · «Riduci trasparenza» — l'interruttore di accessibilità dell'iPhone
     (Impostazioni → Accessibilità → Display). Fino alla v19.53 non era
     gestito: chi lo attivava — spesso proprio chi fatica a leggere —
     continuava a vedere tutte le superfici traslucide.
     2 · Nessun `backdrop-filter` — oggi non capita su nessun browser di
     riferimento, ma con il fill sceso a .42 diventa load-bearing: senza
     sfocatura, una barra al 42% è testo su contenuto nudo.
     ⚠️ In entrambi i casi la superficie torna PIENA, non semi-piena: a metà
     strada si otterrebbe il peggio dei due.
     ⚠️ L'elenco è lo stesso delle superfici in vetro: se ne aggiungi una là,
     aggiungila anche qui — o resterà traslucida proprio a chi ha chiesto che
     non lo sia. Il presidio è `mobile-css.test.js`.
     ══════════════════════════════════════════════════════════════════════ */
  @media(max-width:640px){
    /* i selettori sono in una variabile sola per non divergere: appbar e
       subbar prendono --panel, tutto il resto pure. */
    @supports not ((-webkit-backdrop-filter:blur(1px)) or (backdrop-filter:blur(1px))){
      .appbar,#subbar,.tabbar,.tb-cap,
      .filtsheet,.rowsheet,.ordmenu,.pst-menu,
      .pvhead,#pv-bar,.pvsheet,
      #panel .panel-head,#panel .panel-foot,
      #tpanel .panel-head,#tpanel .panel-foot,
      #rpanel .panel-head,#rpanel .panel-foot,
      .ms-pop,.calpop,.dkchip,.dock .dk-lab,.statechip,.search{
        background:var(--panel);
      }
      .filtsheet-dim,.pvsheet-dim,.overlay.open,.dlgwrap.open,
      .pvwrap.open,.kbdhelp-wrap.open,.app.nav-open .nav-backdrop{
        background:rgba(9,20,30,.55);
      }
    }
  }
  @media (prefers-reduced-transparency: reduce){
    .appbar,#subbar,.tabbar,.tb-cap,
    .filtsheet,.rowsheet,.ordmenu,.pst-menu,
    .pvhead,#pv-bar,.pvsheet,
    #panel .panel-head,#panel .panel-foot,
    #tpanel .panel-head,#tpanel .panel-foot,
    #rpanel .panel-head,#rpanel .panel-foot,
    .ms-pop,.calpop,.dkchip,.dock .dk-lab,.statechip,.search{
      background:var(--panel)!important;
      -webkit-backdrop-filter:none!important;backdrop-filter:none!important;
    }
    .filtsheet-dim,.pvsheet-dim,.overlay.open,.dlgwrap.open,
    .pvwrap.open,.kbdhelp-wrap.open,.app.nav-open .nav-backdrop{
      background:rgba(9,20,30,.55)!important;
      -webkit-backdrop-filter:none!important;backdrop-filter:none!important;
    }
    /* senza trasparenza l'arretramento non ha più niente da spiegare */
    body.filtsheet-on .main{ transform:none!important;border-radius:0!important; }
    body.filtsheet-on .app{ background:var(--bg)!important; }
    /* il fondo colorato sotto le barre serviva solo a dare al vetro qualcosa
       da rifrangere */
    .main::after{ display:none!important; }
  }

  /* ══════════════════════════════════════════════════════════════════════
     IL FOGLIO PRENDE QUOTA (v19.65) — variante C, scelta dall'utente
     ──────────────────────────────────────────────────────────────────────
     Il modulo era già una lista raggruppata (v19.59) dentro un guscio già da
     app (v19.51/54), ma il pannello ENTRAVA come una finestra — sfumando e
     salendo di 14px — e le sue card stavano ferme, piatte e senza reazione al
     dito. Qui si lavora sul CONTENITORE e sul MOVIMENTO: non si tocca una sola
     etichetta, né i controlli, né il contenuto del piede, né la geometria.
     ⚠️ DUE PEZZI DI QUESTA VARIANTE C'ERANO GIÀ e non vanno rifatti: l'anello
     di luce su testata e piede (gli `inset 0 1px 0 var(--g-ring-hi)` del blocco
     Liquid Glass) e la pressione sui pulsanti e sui comandi della testata
     (`.btn:active` e `.x:active`, globali). Restano cinque interventi.
     ⚠️ Vale SOLO sotto i 640px: sopra, il pannello non cambia di un pixel.
     ══════════════════════════════════════════════════════════════════════ */
  @media(max-width:640px){
    /* ── 1 · IL PANNELLO SALE DAL BASSO ────────────────────────────────────
       ⚠️ IL `:not(.min)` NON BASTA, SERVE L'ORDINE. `.panel.open` (riga ~743)
       e `.panel:not(.min)` (riga ~4466) hanno la STESSA specificità (0,2,0):
       a parità vince l'ultima del file. Una regola di ingresso scritta più in
       alto verrebbe scavalcata e il pannello resterebbe fuori schermo mentre
       l'app lo crede aperto — chiuso a chiave, senza errori da nessuna parte.
       Per questo lo stato aperto si ridichiara QUI, in coda.
       ⚠️ L'USCITA È PIÙ RAPIDA DELL'ENTRATA (--d contro --d-slow): il velo
       `.overlay` sparisce subito, e un foglio che scende lentamente su uno
       sfondo già tornato chiaro sembra un ritardo, non un'animazione. */
    .panel:not(.min){
      transform:translateY(100%);
      transition:transform var(--d) var(--ease-out), opacity var(--d-fast) linear;
    }
    /* ⚠️ DA APERTO IL TRANSFORM DEV'ESSERE `none`, NON `translateY(0)`.
       Sono la stessa posizione ma non lo stesso valore calcolato: `translateY(0)`
       dà `matrix(1,0,0,1,0,0)`, e un antenato con un transform diverso da `none`
       diventa il contenitore di riferimento dei discendenti `position:fixed`.
       Scritto così ha RIPORTATO il difetto corretto il giorno prima: le tendine
       del modulo tornavano ancorate al pannello e ritagliate dalla card —
       misurato, 5 su 11 rotte e quattro invisibili del tutto. La transizione
       verso `none` funziona lo stesso: il browser lo interpola come identità. */
    /* ⚠️ `--ease-out`, NON `--ease-spring`. Il molleggio supera il valore finale
       di circa il 5%, e su una corsa alta quanto lo schermo sono ~43px: ma il
       foglio è ancorato `inset:0`, cioè il suo bordo inferiore È il bordo dello
       schermo — non c'è niente in cui sfondare. Misurato: top minimo −42,6px,
       una fascia della stessa altezza scoperta in fondo con la pagina velata a
       vista, e la testata che entra sotto la fascia di stato. È la famiglia di
       difetti già pagata tre volte con la fascia grigia in fondo. */
    .panel:not(.min).open{
      transform:none;
      transition:transform var(--d-slow) var(--ease-out), opacity var(--d-fast) linear;
    }

    /* ── 2 · LA SEZIONE COMPARE IN DISSOLVENZA ────────────────────────────
       ⚠️ QUI C'ERA UNA CASCATA SCAGLIONATA, ED È STATA TOLTA: non si vedeva
       mai e per giunta peggiorava il gesto principale del modulo. Su telefono
       `resetFormSections` (app.9g) lascia aperta la SOLA prima sezione, e un
       elemento `display:none` non anima — misurato, all'apertura del pannello
       le card visibili sono 1 su 8, quindi dei sei ritardi ne scattava uno.
       Peggio: l'animazione riparte a ogni passaggio nascosto→visibile, cioè
       ogni volta che si APRE una sezione, e col ritardo della sua posizione —
       misurato, il contenuto compariva dopo 175ms su un gesto che prima era
       istantaneo. Resta la dissolvenza, senza ritardo: la sezione che si apre
       compare invece di apparire di scatto.
       ⚠️ Anima SOLO l'opacità: è l'unica proprietà che non crea un contenitore
       di riferimento per i `position:fixed`, e con un `translateY` nei keyframe
       la card lo crea per tutta la durata, mandando fuori posto le tendine
       (misurato: popup a x=392 su uno schermo da 390). */
    @keyframes fsecQuota{ from{ opacity:0; } to{ opacity:1; } }
    .panel.open .fgrid > .fsec-body{ animation:fsecQuota var(--d-fast) var(--ease-out); }

    /* ── 3 · LE CARD HANNO UNA PROFONDITÀ ──────────────────────────────────
       Erano `--sh-sm`, cioè due ombre da 1px: appoggiate, non sollevate. Con
       `--sh` prendono quota, e l'anello a mezzo pixel dà loro un bordo che in
       tema scuro diventa chiaro da solo (`--g-edge` si inverte). */
    #panel .fsec-body,#tpanel .fsec-body,#rpanel .fsec-body{
      box-shadow:var(--sh), inset 0 0 0 .5px var(--g-edge);
    }

    /* ── 4 · LE RIGHE REAGISCONO AL DITO ───────────────────────────────────
       ⚠️ IL SEGNALE È UN FONDO, NON UNA SCALA. Un `transform` sulla riga la
       rende il contenitore di riferimento dei `position:fixed` che contiene, e
       la tendina di quella stessa riga è ancorata al viewport dalla v19.64:
       misurato, il popup nasceva 11px più in basso del dovuto e su un campo in
       fondo al modulo finiva fuori schermo per un quarto di secondo. È la terza
       volta che questo meccanismo morde in questo blocco — pannello, keyframe,
       e qui — e la riga è l'antenato che avevo dimenticato.
       ⚠️ Solo le righe che si TOCCANO davvero, cioè quelle con una tendina: un
       campo che si digita non deve reagire mentre ci scrivi. E non quando la
       tendina è bloccata: promettere un'apertura che non arriva è peggio che
       non reagire. */
    #panel .fsec-body > .field:has(> .ms),
    #tpanel .fsec-body > .field:has(> .ms),
    #rpanel .fsec-body > .field:has(> .ms){
      transition:background-color var(--d-fast) var(--ease-out);
    }
    #panel .fsec-body > .field:has(> .ms):active,
    #tpanel .fsec-body > .field:has(> .ms):active,
    #rpanel .fsec-body > .field:has(> .ms):active{
      background:var(--sidebar);
    }
    #panel .fsec-body > .field:has(> .ms.crustlock):active,
    #panel .fsec-body > .field:has(> .ms.locked):active,
    #panel .fsec-body > .field:has(> .ms.disabled):active,
    #tpanel .fsec-body > .field:has(> .ms.crustlock):active,
    #tpanel .fsec-body > .field:has(> .ms.locked):active,
    #tpanel .fsec-body > .field:has(> .ms.disabled):active,
    #rpanel .fsec-body > .field:has(> .ms.crustlock):active,
    #rpanel .fsec-body > .field:has(> .ms.locked):active,
    #rpanel .fsec-body > .field:has(> .ms.disabled):active{
      background:transparent;
    }

    /* ── 5 · LA SEZIONE APERTA SI RICONOSCE ────────────────────────────────
       ⚠️ Oggi APERTA E CHIUSA SONO IDENTICHE, e non per una svista di colore:
       `#panel .sec-title.fsec-head` (1,2,0) batte `.sec-title.fsec-head.closed`
       (0,3,0), quindi quella regola non arriva mai — e per giunta usa `--ink2`,
       che nel file NON È DEFINITA. Qui si ridichiara con la specificità giusta.
       ⚠️ La differenza è di TINTA, non di intensità: l'intestazione sta sul
       fondo `--bg`, dove `--muted` scende a 4,23:1 (sotto AA, trappola già
       pagata). Chiusa `--ink`, aperta `--green3`: passano entrambe.
       ⚠️ Il chevron resta il `<span class="fsec-chev">` che il JS riempie di
       ▾/▸: non si tocca il markup, si azzera il testo e si disegna col bordo,
       così ruota invece di cambiare glifo. */
    #panel .sec-title.fsec-head.closed,
    #tpanel .sec-title.fsec-head.closed,
    #rpanel .sec-title.fsec-head.closed{ color:var(--ink); }
    #panel .sec-title.fsec-head:not(.closed),
    #tpanel .sec-title.fsec-head:not(.closed),
    #rpanel .sec-title.fsec-head:not(.closed){ color:var(--green3); }
    #panel .sec-title .fsec-chev,
    #tpanel .sec-title .fsec-chev,
    #rpanel .sec-title .fsec-chev{
      font-size:0;width:10px;height:10px;position:relative;display:inline-block;flex:0 0 auto;
      /* ⚠️ senza questo resta il `--muted` della regola generica (riga ~4395) e
         la freccia di una sezione aperta è grigia mentre il titolo è verde:
         il disegno è in `currentColor`, ma `currentColor` va ereditato */
      color:inherit;
    }
    #panel .sec-title .fsec-chev::before,
    #tpanel .sec-title .fsec-chev::before,
    #rpanel .sec-title .fsec-chev::before{
      content:'';position:absolute;left:1px;top:1px;width:6px;height:6px;
      border-right:1.8px solid currentColor;border-bottom:1.8px solid currentColor;
      transform:rotate(45deg);transform-origin:60% 60%;
      transition:transform var(--d-fast) var(--ease-out);
    }
    #panel .sec-title.fsec-head.closed .fsec-chev::before,
    #tpanel .sec-title.fsec-head.closed .fsec-chev::before,
    #rpanel .sec-title.fsec-head.closed .fsec-chev::before{ transform:rotate(-45deg); }
  }
  /* Chi ha chiesto meno movimento non lo riceve: il foglio compare senza
     salire, le card senza cascata, il chevron senza rotazione. */
  @media(max-width:640px) and (prefers-reduced-motion:reduce){
    .panel:not(.min),.panel:not(.min).open{ transition:none; }
    .panel.open .fgrid > .fsec-body{ animation:none; }
    #panel .sec-title .fsec-chev::before,
    #tpanel .sec-title .fsec-chev::before,
    #rpanel .sec-title .fsec-chev::before{ transition:none; }
    #panel .fsec-body > .field:has(> .ms),
    #tpanel .fsec-body > .field:has(> .ms),
    #rpanel .fsec-body > .field:has(> .ms){ transition:none; }
  }

  /* ══════════════════════════════════════════════════════════════════════
     NIENTE RIQUADRO DENTRO IL RIQUADRO (v19.68)
     ──────────────────────────────────────────────────────────────────────
     Dalla v19.59 la sezione del modulo È una card. I tre componenti che ci
     stanno dentro — la griglia lavorazioni, il mix avanzato e la tabella
     multi-cliente — portano però ancora il loro riquadro di quando la sezione
     era solo un gruppo di campi su fondo grigio: un bordo dentro un bordo, e
     per la lavgrid perfino un fondo bianco dentro un fondo bianco, dove
     l'unica cosa che la delimitava era quel filetto.
     Restano le separazioni INTERNE (i filetti fra una voce e l'altra), che
     sono quelle che servono a leggere. Il mix tiene il suo fondo `--soft`:
     è una composizione a parte, non un elenco di righe.
     ⚠️ Solo sotto i 640px: sopra, la sezione è `display:contents` e la card
     non esiste, quindi lì il riquadro è l'unica cosa che delimita il blocco.
     ══════════════════════════════════════════════════════════════════════ */
  @media(max-width:640px){
    #panel .fsec-body .lavgrid,
    #tpanel .fsec-body .lavgrid,
    #rpanel .fsec-body .lavgrid,
    #panel .fsec-body .mcli{ border:0; border-radius:0; background:transparent; }
    #panel .fsec-body #fl-crustmix,
    #tpanel .fsec-body [id$="-crustmix"],
    #rpanel .fsec-body [id$="-crustmix"]{ border:0; border-radius:0; }
    /* senza il riquadro la prima e l'ultima voce restano attaccate ai campi
       sopra e sotto: le si stacca con gli stessi filetti che dividono le voci */
    #panel .fsec-body .lavgrid,
    #tpanel .fsec-body .lavgrid,
    #rpanel .fsec-body .lavgrid,
    #panel .fsec-body .mcli{ border-top:1px solid var(--line); border-bottom:1px solid var(--line); }
  }

  /* ══════════════════════════════════════════════════════════════════════
     I SUFFISSI DELLE ETICHETTE SONO UNA CLASSE, NON UNO STILE INLINE (v19.68)
     ──────────────────────────────────────────────────────────────────────
     Cinquantasette etichette dei moduli portano dentro una spiegazione —
     «Botte / Terzista · chi esegue la tintura», «Fornitore crust · riservato,
     solo copia interna» — scritta come `<span style="…">` con quattro colori
     letterali diversi. Ora sono tre classi che dicono COSA sono:
       .fhint       neutro (39)  una precisazione
       .fhint.int   ambra (11)   «resta in azienda»: non va al terzista, o è
                                 un costo che viene da un'altra lavorazione
       .fhint.key   verde  (5)   il dato che identifica il record
     ⚠️ L'ambra era `#b98a3e`, che su fondo bianco sta a 3,11:1 — sotto AA, ed
     era testo a tutti gli effetti. Passando a `--amber-tx` si corregge un
     difetto che c'era da prima e che nessuno stava guardando, perché il
     letterale non lo raggiungeva nessuna misura sui token.
     ⚠️ IL «·» NON STA PIÙ NEL TESTO ma è un `::before`: serve solo quando il
     suffisso è IN LINEA con l'etichetta. Sui campi di scelta va a capo, e lì
     un puntino a inizio riga sarebbe orfano.
     ══════════════════════════════════════════════════════════════════════ */
  /* ⚠️ `--hint` E NON `--muted`, ed è un difetto PREESISTENTE che si vede solo
     ora che il colore passa da un token. Sul computer la sezione è
     `display:contents`, quindi il fondo di quei suffissi è `--bg` e non
     `--panel`: lì `--muted` sta a **4,23:1**, sotto AA — la trappola già
     dichiarata altrove in questo file, che qui riguarda 39 etichette. Serve
     un grigio appena più scuro, che regga anche sul fondo più chiaro
     (4,75:1 su --bg, 5,35 su --panel). In tema scuro `--muted` è già a 8:1 e
     non c'è niente da correggere. */
  :root{ --hint:#5C6C82; }
  @media screen{
    html[data-theme="dark"]{ --hint:var(--muted); }
  }
  .fhint{ text-transform:none; letter-spacing:normal; color:var(--hint); font-weight:600; }
  .fhint::before{ content:'· '; }
  .fhint.int{ color:var(--amber-tx); font-weight:700; }
  .fhint.key{ color:var(--green); font-weight:700; }

  /* ══════════════════════════════════════════════════════════════════════
     UNA TIPOGRAFIA SOLA PER LE ETICHETTE DEL MODULO (v19.68)
     ──────────────────────────────────────────────────────────────────────
     Non era una scelta fra due stili: c'erano già TUTTI E DUE. Da quando i
     campi con tendina sono righe compatte (v19.59) la loro etichetta è
     14,5px tonda e nera, mentre quella dei campi che si digitano è rimasta il
     maiuscoletto grigio da 10,5 — nella scheda 14 contro 23, e capita che si
     tocchino dentro la stessa card («CONSEGNA PREVISTA» sopra «Terzista»).
     ⚠️ Vince la forma delle righe compatte, non il maiuscoletto: su una riga
     «etichetta a sinistra, valore a destra» un grigio da 10,5px accanto a un
     valore nero da 14,5 è sbilanciato, ed è lontano dalle liste di iPhone a
     cui il modulo si è avvicinato. Costa +84px sulla scheda (misurato).
     ⚠️ `:not(.chkbox)`: l'etichetta di una CASELLA non è l'etichetta di un
     campo — è il testo del comando — e ha la sua misura. Ma va riportata a
     quella misura anche quando è figlia diretta di `.field`, o `.field label`
     la scrive in maiuscoletto: era il caso di «nascondi i metri quadri», e
     un'etichetta di casella in maiuscoletto era una delle sei eccezioni che
     rendevano il modulo disomogeneo.
     ══════════════════════════════════════════════════════════════════════ */
  @media(max-width:640px){
    #panel .fsec-body > .field > label:not(.chkbox),
    #tpanel .fsec-body > .field > label:not(.chkbox),
    #rpanel .fsec-body > .field > label:not(.chkbox),
    #panel .fsec-body > .field > .flabrow > label:first-child,
    #tpanel .fsec-body > .field > .flabrow > label:first-child,
    #rpanel .fsec-body > .field > .flabrow > label:first-child,
    /* ⚠️ i m² stanno in una sotto-griglia loro (`.mqgrid`, l'allineamento
       stima|reali) e NON sono figli diretti di `.fsec-body`: dimenticandoli
       restavano le due sole etichette maiuscolette del modulo, che è
       esattamente la disomogeneità che si sta togliendo. */
    #panel .fsec-body .mqgrid > .field > label,
    #tpanel .fsec-body .mqgrid > .field > label,
    #rpanel .fsec-body .mqgrid > .field > label{
      font-size:14.5px; font-weight:600; text-transform:none;
      letter-spacing:0; color:var(--ink);
    }
    #panel .fsec-body > .field > label.chkbox,
    #tpanel .fsec-body > .field > label.chkbox,
    #rpanel .fsec-body > .field > label.chkbox{
      font-size:13.5px; font-weight:600; text-transform:none;
      letter-spacing:0; color:var(--ink);
    }
    /* la spiegazione resta di servizio: più piccola e più leggera del nome */
    #panel .fsec-body .field .fhint,
    #tpanel .fsec-body .field .fhint,
    #rpanel .fsec-body .field .fhint{ font-size:12.5px; font-weight:500; }

    /* ── LA SPIEGAZIONE VA A CAPO SUI SOLI CAMPI DI SCELTA ──────────────
       È la parte portante della «variante B». Su un campo di scelta la
       spiegazione dentro l'etichetta la fa diventare troppo lunga, la tendina
       non ci sta in riga e va a capo da sola allineata a destra: mandandola
       sotto, il campo si ACCORCIA di 10px E torna una riga compatta (misurato
       sulla tintura: da 5 righe compatte su 9 a 9 su 9).
       ⚠️ NON SU TUTTI I CAMPI. Su quelli che si DIGITANO non c'è nessuna riga
       da recuperare e la spiegazione a capo costa e basta: la scheda passava
       da +64 a +175px e la tintura da +51 a +95, cioè quasi il triplo per la
       sola coerenza. Il perimetro è `:has(> .ms)`, lo stesso della riga
       compatta: dove la riga si guadagna, la spiegazione scende. */
    #panel .fsec-body > .field:has(> .ms) .fhint,
    #tpanel .fsec-body > .field:has(> .ms) .fhint,
    #rpanel .fsec-body > .field:has(> .ms) .fhint{
      display:block; margin-top:1px; line-height:1.3; font-size:12px;
    }
    #panel .fsec-body > .field:has(> .ms) .fhint::before,
    #tpanel .fsec-body > .field:has(> .ms) .fhint::before,
    #rpanel .fsec-body > .field:has(> .ms) .fhint::before{ content:none; }
  }

  /* ══════════════════════════════════════════════════════════════════════
     LE CASELLE SONO DISEGNATE, NON QUELLE DI SISTEMA (v19.68)
     ──────────────────────────────────────────────────────────────────────
     Erano le caselle native del browser tinte con `accent-color`. In Chromium
     vengono decenti, ma su iPhone le disegna iOS e il loro aspetto non è
     misurabile da qui: disegnarle è l'unico modo per sapere come sono davvero
     e per averle uguali su telefono e su computer.
     UNA forma sola e cinque misure, che è l'inventario COMPLETO delle caselle
     dell'app: le sei del modulo (`.chkbox`, 22px), le diciassette della
     griglia lavorazioni (`.lavchk`, 20px), i quattro «n/d» (`.na-toggle`,
     15px, ambra perché quello spegne un campo e non è un'opzione fra altre),
     i campi dettaglio della lavorazione (`.lavfield-tog`, 13px) e le due di
     selezione in elenco (`.gl-selchk` dei lotti, `.fatt-chk` del carico
     fattura, 17px).
     ⚠️ L'elenco va tenuto COMPLETO: bastava dimenticarne una — è successo con
     quella di «ORE» dentro la griglia lavorazioni — per avere una casella di
     sistema verde brillante accanto a quelle disegnate verde scuro, nella
     stessa schermata. Se aggiungi una casella, aggiungila qui.
     ⚠️ Le 137 caselle di `GUIDA.html` NON sono coperte ed è giusto così:
     quel file non carica `app.css` e ha una palette sua.
     ⚠️ VALE OVUNQUE, non solo sotto i 640px: il motivo per cui si disegnano è
     proprio non dipendere da come ogni browser rende il suo controllo.
     ⚠️ LE REGOLE ORIGINALI (righe ~879, ~886, ~924) RESTANO e non vanno
     tolte: sono il ripiego dove `appearance:none` non si applica — lì torna
     la casella di sistema con il suo `accent-color`, che è il comportamento
     di prima e non una casella invisibile.
     ⚠️ Il segno di spunta è DISEGNATO, non è un carattere: non dipende dal
     font e non può cadere su un ripiego che lo renda con un glifo diverso.
     ⚠️⚠️ IL SEGNO PENDEVA IN BASSO A DESTRA, e la causa non era nei numeri ma
     in una riga di reset che non arriva (corretto in v19.82, segnalato
     dall'utente sulla casella «Come già fatto»). Il `left`/`top` del segno
     erano scritti a mano dando per scontato `box-sizing:border-box` — ma il
     reset `*{box-sizing:border-box}` (riga 90) NON raggiunge gli
     pseudo-elementi, perché il selettore universale non li seleziona. Il
     rettangolo che ruotando fa il segno era quindi più grande del previsto di
     quanto sono spessi i suoi bordi, e il segno scivolava di **1,50px a destra
     e 1,77px in basso** su un quadratino di 22px (misurato ingrandendo 10×).
     Lo scarto verticale vale esattamente `braccio-corto / 2√2`, e tornava al
     centesimo su tutte e cinque le misure.
     Ora il segno si àncora al centro PER COSTRUZIONE (`left/top:50%` +
     `translate(-50%,-50%)`, dentro `--chk-pos`): non ci sono più coordinate da
     ricalcolare quando una misura cambia. `test/modulo-liste.test.js` misura
     la centratura sui pixel — a occhio mezzo pixel non si giudica.
     ⚠️ CONTRASTO — in tema scuro `--green` è CHIARO, quindi la spunta bianca
     ci sparirebbe sopra (è la trappola già pagata da `.btn.primary` in v19.42,
     dove la correzione fu la stessa): il segno prende `--chk-ink`, che è
     bianco in chiaro e scuro in scuro. Il fondo resta `--green` in entrambi.
     ⚠️⚠️ IL BORDO DELLA CASELLA SPENTA È `--muted`, NON `--line2`, e questo è
     un difetto che si INTRODUCE disegnandole: finché la casella era di
     sistema, il suo contorno lo disegnava il browser: passando a un bordo
     nostro, `--line2` sul fondo della card dà **1,46:1** (misurato) contro il
     3:1 che WCAG 1.4.11 chiede a un comando — cioè una casella spenta quasi
     invisibile. Nessun altro grigio dell'app sta nella fascia utile: `--line`
     è 1,17, `--muted2` 2,56, e il primo che passa è `--muted` (4,17÷4,76 in
     chiaro, 6,36÷7,17 in scuro). Se un giorno il bordo torna più tenue,
     la casella spenta smette di esistere a schermo.
     ══════════════════════════════════════════════════════════════════════ */
  :root{ --chk-ink:#fff; }
  @media screen{
    html[data-theme="dark"]{ --chk-ink:#0B1620; }
  }
  .chkbox input[type=checkbox],
  .lavchk input[type=checkbox],
  .na-toggle input[type=checkbox],
  .lavfield-tog input[type=checkbox],
  input[type=checkbox].gl-selchk,
  input[type=checkbox].fatt-chk{
    appearance:none; -webkit-appearance:none; margin:0; flex:0 0 auto;
    position:relative; cursor:pointer;
    background:var(--panel); border:1.5px solid var(--muted); border-radius:7px;
    transition:background var(--d-fast) var(--ease-out),
               border-color var(--d-fast) var(--ease-out);
  }
  .chkbox input[type=checkbox]::after,
  .lavchk input[type=checkbox]::after,
  .na-toggle input[type=checkbox]::after,
  .lavfield-tog input[type=checkbox]::after,
  input[type=checkbox].gl-selchk::after,
  input[type=checkbox].fatt-chk::after{
    content:''; position:absolute; box-sizing:content-box;
    left:50%; top:50%;
    width:var(--chk-w); height:var(--chk-h);
    border:solid var(--chk-ink); border-width:0 var(--chk-s) var(--chk-s) 0;
    /* al centro del quadratino, meno lo scarto che la rotazione introduce */
    --chk-pos:translate(-50%,-50%) translateY(calc(-1 * var(--chk-w) / 2.8284)) rotate(45deg);
    transform:var(--chk-pos) scale(.35); opacity:0;
    transition:transform var(--d-fast) var(--ease-out), opacity var(--d-fast) linear;
  }
  .chkbox input[type=checkbox]:checked,
  .lavchk input[type=checkbox]:checked,
  .lavfield-tog input[type=checkbox]:checked,
  input[type=checkbox].gl-selchk:checked,
  input[type=checkbox].fatt-chk:checked{ background:var(--green); border-color:var(--green); }
  .chkbox input[type=checkbox]:checked::after,
  .lavchk input[type=checkbox]:checked::after,
  .na-toggle input[type=checkbox]:checked::after,
  .lavfield-tog input[type=checkbox]:checked::after,
  input[type=checkbox].gl-selchk:checked::after,
  input[type=checkbox].fatt-chk:checked::after{ transform:var(--chk-pos) scale(1); opacity:1; }
  /* Le cinque misure. `--chk-w`/`--chk-h`/`--chk-s` sono i due bracci e lo
     spessore del segno; `--chk-lg` è quanto il segno viene largo una volta
     ruotato — cioè la misura che serve alla maschera qui sotto, tenuta uguale
     a quella del ripiego perché le due forme abbiano lo stesso peso. */
  .chkbox input[type=checkbox]{ width:22px; height:22px;
    --chk-w:5px; --chk-h:10px; --chk-s:2.2px; --chk-lg:13.7px; }
  .lavchk input[type=checkbox]{ width:20px; height:20px; border-radius:6.5px;
    --chk-w:4.6px; --chk-h:9.2px; --chk-s:2.2px; --chk-lg:12.9px; }
  input[type=checkbox].gl-selchk,
  input[type=checkbox].fatt-chk{ width:17px; height:17px; border-radius:5.5px;
    --chk-w:4px; --chk-h:8px; --chk-s:2px; --chk-lg:11.3px; }
  .na-toggle input[type=checkbox]{ width:15px; height:15px; border-radius:5px; border-width:1.4px;
    --chk-w:3.4px; --chk-h:7px; --chk-s:1.8px; --chk-lg:9.9px; }
  .na-toggle input[type=checkbox]:checked{ background:var(--amber-tx); border-color:var(--amber-tx); }
  .lavfield-tog input[type=checkbox]{ width:13px; height:13px; border-radius:4.5px; border-width:1.3px;
    --chk-w:3px; --chk-h:6.2px; --chk-s:1.7px; --chk-lg:8.9px; }
  .chkbox input[type=checkbox]:focus-visible,
  .lavchk input[type=checkbox]:focus-visible,
  .na-toggle input[type=checkbox]:focus-visible,
  .lavfield-tog input[type=checkbox]:focus-visible,
  input[type=checkbox].gl-selchk:focus-visible,
  input[type=checkbox].fatt-chk:focus-visible{ outline:2px solid var(--green3); outline-offset:2px; }

  /* ── il segno è LA SPUNTA DEL REGISTRO ICONE (v19.82) ──────────────────
     Nella riga «Come già fatto» di spunte ce ne sono DUE — quella della
     casella e quella dell'etichetta, a un centimetro l'una dall'altra — e
     fino a qui erano disegni diversi: la prima due bordi squadrati, la
     seconda il tratto arrotondato di `ico('spunta')`. Ora combaciano: la
     maschera porta lo STESSO path di `app.0-icone.js`, col `linecap` tondo
     della classe `.ic`. È il seguito della v19.80 («un linguaggio solo per le
     icone»), che si era fermata alle icone vere e non aveva toccato le
     caselle.
     ⚠️ IL RIPIEGO È IL BORDO QUI SOPRA, e per questo la regola sta dentro un
     `@supports`: dove la maschera non c'è resta il segno disegnato — centrato
     com'è ora — e non un quadratino pieno, che è ciò che si vedrebbe
     applicando il `background` senza la maschera che lo ritaglia.
     ⚠️ Il viewBox è stretto sulla BBOX del tratto (`2.5 4.5 19 14`: il path
     più metà spessore per via del cappuccio tondo), così il segno riempie il
     riquadro e centrarlo non chiede altri numeri. Il rapporto 14/19 tiene
     l'altezza in proporzione: non scriverla a mano.
     ⚠️ Il colore resta `--chk-ink` — è un `background` ritagliato, non un
     `fill` — quindi la trappola del tema scuro è coperta come prima.
     ⚠️ `--chk-pos` perde la rotazione (la maschera è già dritta): la regola
     `:checked` qui sopra non va ripetuta, ci passa attraverso. */
  @supports (mask-image:linear-gradient(#000,#000)) or (-webkit-mask-image:linear-gradient(#000,#000)){
    .chkbox input[type=checkbox]::after,
    .lavchk input[type=checkbox]::after,
    .na-toggle input[type=checkbox]::after,
    .lavfield-tog input[type=checkbox]::after,
    input[type=checkbox].gl-selchk::after,
    input[type=checkbox].fatt-chk::after{
      width:var(--chk-lg); height:calc(var(--chk-lg) * 14 / 19);
      border:0; background:var(--chk-ink);
      -webkit-mask:var(--chk-mask) center/contain no-repeat;
      mask:var(--chk-mask) center/contain no-repeat;
      --chk-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='2.5 4.5 19 14'%3E%3Cpath d='M20 6L9 17l-5-5' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
      --chk-pos:translate(-50%,-50%);
    }
  }

  /* ── l'interruttore, per la casella che sta da sola ───────────────────
     ⚠️ Il binario SPENTO è pieno `--muted` e non il fondo del pannello: col
     fondo chiaro il pallino bianco ci sparirebbe dentro e da spento non si
     vedrebbe niente. Vale la stessa misura del bordo qui sopra — `--line2`
     resta a 1,46:1 e non basta. A distinguere acceso da spento è la POSIZIONE
     del pallino prima ancora del colore.
     ⚠️ IL PALLINO NON È BIANCO, È `--chk-ink` — lo stesso token della spunta,
     e per la stessa ragione: in tema scuro il binario spento è `--muted`, che
     lì è CHIARO (#9bb0c2), e un pallino bianco sopra sta a 2,24:1 (misurato),
     sotto il 3:1 che WCAG chiede a una forma. Col token diventa scuro in tema
     scuro: 7,17:1 da spento e 10,46:1 sul verde da acceso. */
  .chkbox.sw input[type=checkbox]{
    width:46px; height:27px; border-radius:14px;
    background:var(--muted); border-color:var(--muted);
  }
  .chkbox.sw input[type=checkbox]::after{
    left:2.5px; top:2.5px; width:20px; height:20px;
    border:0; border-radius:50%; background:var(--chk-ink); opacity:1;
    transform:none; box-shadow:0 1px 3px rgba(15,36,56,.35);
    transition:transform var(--d) var(--ease-spring);
    /* ⚠️ il pallino NON va ritagliato dalla maschera della spunta: senza
       questa riga l'interruttore mostrerebbe un visto al posto del pallino */
    -webkit-mask:none; mask:none;
  }
  /* ⚠️ IL BINARIO ACCESO DEVE DIVENTARE VERDE, e serve una regola sua: la
     `.chkbox input[type=checkbox]:checked{background:var(--green)}` più su nel
     file ha la STESSA specificità (0,3,1) della `.chkbox.sw` qui sopra, e a
     parità vince chi è scritto dopo — cioè il `--muted` dell'interruttore
     spento. Risultato: l'unico interruttore del gestionale restava grigio
     anche da acceso, e l'unico segno che fosse attivo era la posizione del
     pallino. Segnalato dall'utente (v19.95). */
  .chkbox.sw input[type=checkbox]:checked{ background:var(--green); border-color:var(--green); }
  .chkbox.sw input[type=checkbox]:checked::after{ transform:translateX(19px); }

  @media (prefers-reduced-motion:reduce){
    .chkbox input[type=checkbox],.lavchk input[type=checkbox],.na-toggle input[type=checkbox],
    .lavfield-tog input[type=checkbox],input[type=checkbox].gl-selchk,input[type=checkbox].fatt-chk,
    .chkbox input[type=checkbox]::after,.lavchk input[type=checkbox]::after,
    .na-toggle input[type=checkbox]::after,.lavfield-tog input[type=checkbox]::after,
    input[type=checkbox].gl-selchk::after,input[type=checkbox].fatt-chk::after{ transition:none; }
  }

  /* ══════════════════════════════════════════════════════════════════════
     IL PIEDE DI TINTURA E RASATURA RESTA AL SOLO «SALVA» (v19.68)
     ──────────────────────────────────────────────────────────────────────
     In modifica quel piede porta CINQUE pulsanti — Annulla · PDF terzista ·
     PDF copia interna · ＋ Crea rifinizione collegata · Salva — che su iPhone
     non stanno in fila e vanno su TRE righe con dei buchi in mezzo: 206px
     misurati, un quarto dello schermo, contro i 99 del pannello scheda.
     ⚠️ NON era solo ingombro: fino alla v19.67 lo spazio che il modulo si
     riserva in fondo lo misurava il SOLO `#panel` (vedi `_misuraFinestraChrome`
     in app.9g), quindi ne restavano 112 contro un piede di 206 e gli ULTIMI
     89px DEL MODULO finivano sotto il piede senza poterli raggiungere — lo
     scorrimento era già finito. Quel difetto è corretto a parte, nel JS: qui
     si toglie la causa per cui si era manifestato.
     Le quattro azioni si leggono nel «⋯» della testata, che riusa il foglio
     delle righe di elenco — `_rowSheetBtns` prende i pulsanti VERI del piede,
     nascosti e non rimossi, quindi non c'è un secondo wiring da allineare.
     ⚠️ Il criterio è «nel foglio finisce ciò che il piede NON mostra»: il
     Salva resta a vista e per questo NON compare fra le voci.
     ⚠️ Il pannello SCHEDA non si tocca: il suo piede è di tre pulsanti e sta
     già in una riga sola (98,5px). «Salva bozza» è un'azione che si cerca lì.
     ══════════════════════════════════════════════════════════════════════ */
  .panel-head .ph-tools button.ph-more{ display:none; }
  @media(max-width:640px){
    #tpanel .panel-foot .btn.ghost,
    #rpanel .panel-foot .btn.ghost{ display:none !important; }
    #tpanel .panel-head .ph-tools button.ph-more,
    #rpanel .panel-head .ph-tools button.ph-more{
      display:inline-flex; font-size:19px; line-height:1; padding-bottom:5px;
    }
  }

  /* ══════════════════════════════════════════════════════════════════
     IL MENU ESCE DAL CASSETTO: la pagina «Menu» (v19.76)
     ──────────────────────────────────────────────────────────────────
     Su telefono l'hamburger e il cassetto laterale spariscono: tutto il
     gestionale sta sotto «☰ Menu», che è già in basso a destra, e da lì
     si torna col «‹ Menu» come nelle app di iPhone.

     Perché: il menu ha 24 voci ed è alto 1.199px in uno schermo da 844,
     quindi aprendolo se ne vedevano 12 e TUTTE quelle di Gestione e
     Sistema stavano sotto il bordo; il pannello copriva 260px su 390
     (il 67% dello schermo) e si apriva dall'angolo in alto a sinistra,
     il punto più lontano dal pollice; e i modi di aprirlo erano due —
     l'hamburger e lo scorrimento dal bordo — nessuno dei due dichiarato.

     ⚠️ Il cassetto NON è stato rimosso: sotto i 640px è irraggiungibile,
     da 641 a 860 resta esattamente com'era (lì la barra in basso non
     esiste, e togliere l'hamburger lascerebbe senza menu il telefono
     ruotato e l'iPad). Ed è l'elemento da cui `renderMenu` legge nomi,
     icone, conteggi e ORDINE: una seconda copia divergerebbe.
     ══════════════════════════════════════════════════════════════════ */
  .ab-back{display:none;}
  @media(max-width:640px){
    /* niente più cassetto da aprire, quindi niente hamburger — e con lui se
       ne va il pulsante che gli stava accanto solo perché era chiuso.
       ⚠️ Eccezione: con la barra in basso SPENTA (elenco vuoto in Impostazioni)
       il pulsante «Altro» non esiste, quindi l'hamburger torna: è l'unica via
       che resterebbe al menu. La classe la scrive renderTabbar. */
    .nav-toggle{display:none!important;}
    .app.senza-tabbar .nav-toggle{display:inline-flex!important;}

    .menupage{display:flex;flex-direction:column;gap:16px;padding-bottom:8px;}

    /* La riga account: quello che nel cassetto stava in fondo, qui in cima */
    .mn-acct{display:flex;align-items:center;gap:12px;width:100%;text-align:left;border:0;cursor:pointer;
      background:var(--panel);border-radius:var(--gr-lg);padding:12px 13px;box-shadow:var(--sh-sm);
      font:inherit;color:var(--ink);-webkit-tap-highlight-color:transparent;
      transition:transform var(--d) var(--ease-spring);}
    .mn-acct:active{transform:scale(.978);}
    .mn-av{width:46px;height:46px;min-width:46px;border-radius:50%;background:var(--green);color:#fff;
      display:flex;align-items:center;justify-content:center;font-weight:800;font-size:18px;flex:0 0 auto;}
    .mn-acct-t{flex:1;min-width:0;}
    .mn-acct-t b{display:block;font-size:16px;font-weight:800;letter-spacing:-.01em;
      overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
    .mn-acct-t span{display:block;font-size:12px;color:var(--muted);font-weight:650;margin-top:1px;
      overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
    .mn-dot{width:8px;height:8px;border-radius:50%;background:var(--st-cons);flex:0 0 auto;}

    .mn-band{font-size:11px;letter-spacing:1.3px;text-transform:uppercase;font-weight:800;
      color:var(--green);margin:2px 0 -8px 6px;}

    .mnlist{background:var(--panel);border-radius:var(--gr-lg);box-shadow:var(--sh-sm);overflow:hidden;}
    .mn-row{display:flex;align-items:center;gap:12px;width:100%;text-align:left;border:0;background:none;
      cursor:pointer;font:inherit;color:var(--ink);padding:9px 12px;min-height:52px;
      -webkit-tap-highlight-color:transparent;}
    /* il filetto parte dopo l'icona, come negli elenchi di iOS */
    .mn-row+.mn-row{background-image:linear-gradient(var(--line),var(--line));
      background-size:calc(100% - 58px) 1px;background-position:58px 0;background-repeat:no-repeat;}
    .mn-row:active{background-color:var(--sidebar);}
    .mn-ic{width:34px;height:34px;min-width:34px;border-radius:11px;background:var(--soft);color:var(--green);
      display:flex;align-items:center;justify-content:center;flex:0 0 auto;}
    .mn-ic svg{width:19px;height:19px;stroke-width:1.9;}
    .mn-t{flex:1;min-width:0;font-size:14.5px;font-weight:700;letter-spacing:-.01em;
      overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
    /* ⚠️ Conteggio e freccina su --muted e NON --muted2 (2,56:1): è la trappola
       già pagata dalla freccina dell'elenco delle Impostazioni. */
    .mn-n{font-size:12px;font-weight:800;color:var(--muted);flex:0 0 auto;font-variant-numeric:tabular-nums;}
    .mn-n.hot{background:var(--red);color:#fff;border-radius:999px;min-width:19px;height:19px;
      display:flex;align-items:center;justify-content:center;padding:0 5px;font-size:11px;}
    .mn-chev{color:var(--muted);font-weight:800;font-size:17px;line-height:1;flex:0 0 auto;margin-left:-2px;}
    /* ⚠️ --ink e non --muted: qui il fondo è --bg (non --panel), dove --muted
       scende a 4,23:1 — la trappola di .rep-sub e .set-qout. */
    .mn-ver{text-align:center;font-size:11.5px;color:var(--ink);opacity:.72;font-weight:650;padding:2px 0 4px;}

    /* ── Le IMPOSTAZIONI prendono la forma del Menu (v19.79) ──────────
       L'elenco dei reparti è una .mnlist come quella del Menu; qui si aggiunge
       solo la seconda riga (il sottotitolo del reparto), che le voci del Menu
       non hanno. Le righe passano da 84,5 a 70,6px — i cinque reparti da 422 a
       321px — perché il testo si stringe accanto all'icona invece di andare a
       capo sotto. */
    /* ⚠️ `.sezlist` è l'elenco delle sezioni del MODULO (v19.90): stessa
       forma, stesse regole — si aggiunge al selettore, non si ricopia. */
    .replist.mnlist .mn-row,.sezlist.mnlist .mn-row{min-height:60px;align-items:center;}
    .replist.mnlist .mn-t,.sezlist.mnlist .mn-t{white-space:normal;overflow:visible;}
    .replist.mnlist .mn-t b,.sezlist.mnlist .mn-t b{display:block;font-size:14.5px;font-weight:700;letter-spacing:-.01em;}
    .replist.mnlist .rl-s,.sezlist.mnlist .rl-s{display:block;font-size:11.5px;font-weight:600;color:var(--muted);
      line-height:1.3;margin-top:1px;}
    /* il riepilogo di una sezione può essere lungo («Pigmentato DEEP BLUE ·
       NATURALE · BILBAO»): tronca invece di far crescere la riga */
    .sezlist.mnlist .rl-s{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
    /* Dentro un reparto il nome lo dice l'APPBAR: il titolo in pagina sarebbe
       il secondo, e il «‹» che ci stava dentro è sparito del tutto. */
    #view-settings .rep-title{display:none;}
    /* ⚠️ La regola «una sola ricerca per pagina» (`.app.in-settings .gswrap`)
       stava QUI ed è stata spostata fuori dal blocco, accanto alla gemella
       `.app.in-ricerca`: dalla v19.93 vale a ogni larghezza, perché sul
       computer i due campi erano a 73px l'uno dall'altro esattamente come qui
       a 59. Non rimetterla dentro: la si duplicherebbe. */

    /* ── Il «‹ Menu»: stessa posizione dell'hamburger, gesto opposto ── */
    .ab-back .abb-chev{font-size:30px;font-weight:500;line-height:.72;margin-top:-4px;}
    /* col ritorno a vista il marchio sarebbe un terzo elemento in fila */
    .app.has-back .mk-appbar{display:none;}
  }

  /* ══════════════════════════════════════════════════════════════════
     L'INDIETRO VALE ANCHE SUL COMPUTER (v19.81) — variante A approvata
     ──────────────────────────────────────────────────────────────────
     Il pulsante e la pila esistevano già dalla v19.79, ma il CSS che li
     mostra stava dentro il blocco ≤640px: sul computer `navPush` girava
     e nessuno vedeva niente.

     ⚠️ SUL COMPUTER LA PILA HA MENO DA FARE, ed è giusto così: il menu è
     sempre a vista (quindi non c'è un «‹ Menu» da cui tornare) e i reparti
     delle Impostazioni hanno la loro colonnina, che dice già dove sei —
     `setApriReparto` resta infatti gated su `_setWide()`. Restano i salti
     LATERALI, quelli che cambiano pagina portandosi dietro uno stato: la
     Ricerca e il «Vedi schede» di un cliente, che filtra l'elenco e ti
     lascia lì senza un modo di tornare.

     ⚠️ Il chevron è più piccolo che su telefono (24 contro 30): là il
     pulsante prende il posto dell'hamburger ed è il bersaglio del pollice,
     qui sta accanto a un titolo e un glifo grosso lo sovrasterebbe.
     ══════════════════════════════════════════════════════════════════ */
  .ab-back{display:none;align-items:center;gap:3px;flex:0 0 auto;background:none;border:0;
    padding:2px 6px 2px 0;font:inherit;font-size:16px;font-weight:700;color:var(--green);
    cursor:pointer;max-width:180px;min-height:40px;-webkit-tap-highlight-color:transparent;}
  .ab-back .abb-chev{font-size:24px;font-weight:500;line-height:.75;margin-top:-3px;}
  .ab-back .abb-lbl{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  .ab-back:active{opacity:.5;}
  .app.has-back .ab-back{display:inline-flex;}
  @media(hover:hover){ .ab-back:hover{color:var(--green3);} }

  /* ══════════════════════════════════════════════════════════════════
     LE BARRE IN ALTO SONO UN OVERLAY ANCHE SUL COMPUTER (v19.81)
     ──────────────────────────────────────────────────────────────────
     ⚠️ QUESTO BLOCCO NACQUE PER IL VETRO, CHE NON C'È PIÙ (v20.4, blocco
     qui sotto): l'overlay è rimasto, ed è il motivo per cui le righe
     spariscono sotto la barra invece di fermarsi. Costa zero — la
     misura sta sotto — quindi resta.

     Fino alla v19.80 il Liquid Glass era tutto sotto i 640px, e non per
     pigrizia: sul computer appbar e barra dei filtri stavano NEL FLUSSO
     (`.appbar{position:relative}`, `#subbar{position:static}` dentro una
     `.main` in colonna), quindi sotto di loro non passava niente e il
     vetro sarebbe stato un fondo bianco un po' sporco — una sfocatura di
     nulla. È la stessa nota che il blocco mobile porta da sempre: «il
     vetro funziona PERCHÉ le barre sono già un overlay».

     Qui si toglie proprio quel vincolo: le due barre in alto diventano un
     overlay anche sul computer, esattamente come sul telefono, e lo spazio
     che occupano lo riserva `.content` con `--chrome-h`.

     ⚠️ NIENTE SI SPOSTA, ed è la sola cosa da non rompere. Lo spazio
     riservato è ESATTAMENTE l'altezza delle barre: il contenuto comincia
     dove cominciava prima, solo che ora ci scorre SOTTO invece di
     fermarsi.
     ⚠️ IL RIPIEGO È L'APPBAR SOLA, non appbar+subbar — stessa scelta del
     telefono, e per una ragione misurata: un ripiego generoso (229px, cioè
     le due barre di una pagina piena) diventa una FASCIA VUOTA permanente
     dovunque il JS non giri o il subbar sia più basso; sbagliare in difetto
     costa invece un solo frame, finché `_measureChrome` non scrive il
     valore vero. Nella pagina Ricerca il subbar è alto 119 e non 171: col
     ripiego grosso si perdevano due righe di risultati.

     ⚠️ SUL COMPUTER LE BARRE NON SI RITIRANO E NON SI CONTRAGGONO: il
     gesto (`bar-hidden`, `bar-compact`, `--bar-shift`) resta una cosa del
     telefono, dove lo spazio manca. Qui l'overlay non serve a un gesto:
     serve a far scorrere il contenuto sotto le barre.
     ══════════════════════════════════════════════════════════════════ */
  /* ══════════════════════════════════════════════════════════════════
     ⚠️ E QUI IL VETRO SULLE BARRE È STATO TOLTO (v20.4) — non rimetterlo.
     ──────────────────────────────────────────────────────────────────
     L'OVERLAY RESTA (è quanto sopra: il contenuto scorre sotto le barre e
     lo spazio lo riserva `--chrome-h`); cade la sola TRASLUCIDITÀ, perché
     su uno schermo largo non si riesce a pagarla.

     Il costo di `backdrop-filter` si paga per pixel sfocato e a ogni
     fotogramma in cui sotto si muove qualcosa — cioè per tutta la durata
     di ogni scorsa, di ogni cambio pagina, di ogni cascata. Misurato
     sull'app vera, 60 schede in elenco, processore rallentato 4×, durata
     dei fotogrammi su 140 consecutivi:

              schermo 1440        schermo 1920
       vetro   39,6 ms · 131 scatti su 140   48,3 ms · 135 su 140
       pieno   16,7 ms ·   1 scatto          16,7 ms ·   1

     ⚠️ IL RAGGIO RIDOTTO NON BASTA, ed è la misura che ha deciso la
     scelta: a blur 14 il portatile rientra (25,9 ms · 9 scatti) ma il
     monitor da scrivania resta indietro (35,1 ms · 128 scatti su 140),
     perché l'area da sfocare cresce con la larghezza — 296.000 px² a
     1440, 415.000 a 1920. Non è una costante da tarare: è una superficie.
     ⚠️ Quando il vetro fu misurato (v19.42) il banco era il TELEFONO, dove
     l'area è tre volte più piccola e il costo risultò davvero trascurabile
     («47 fps con, 43 senza»). Quel numero non si trasporta sul computer.
     ⚠️ Ci si guadagna anche in leggibilità: attraverso il vetro si leggeva
     il testo delle righe che scorrono — dietro i chip di stato compariva
     un secondo testo fantasma, più marcato in tema scuro.
     SUL TELEFONO IL VETRO RESTA (blocco ≤640px): lì è stato misurato sul
     posto, l'area è piccola e le barre si ritirano scorrendo.
     Le altre superfici del computer che galleggiano — menu, dock, testate
     delle finestre — lo tengono: si aprono sopra contenuto FERMO, quindi
     la sfocatura si calcola una volta sola.
     ══════════════════════════════════════════════════════════════════ */
  @media(min-width:641px){
    /* I due colori della merce — il verde del marchio, il cuoio del crust —
       diluiti quasi a zero. Nascono come fondo per il vetro, e restano
       perché il fondo pagina li porta comunque; sotto le barre, che ora
       sono piene, non si vedono più. Misurato: toglierli non cambia un
       fotogramma, quindi la scelta è solo di colore. */
    .main::after{content:'';position:absolute;inset:0;pointer-events:none;z-index:0;
      background:
        radial-gradient(560px 380px at -6% -8%, rgba(0,60,15,.09), transparent 70%),
        radial-gradient(520px 400px at 106% 14%, rgba(154,111,44,.10), transparent 70%);}

    .appbar{position:absolute;top:0;left:0;right:0;z-index:20;border-bottom:0;
      background:var(--bg);
      box-shadow:inset 0 -1px 0 var(--g-edge), inset 0 1px 0 var(--g-ring-hi),
                 0 6px 22px -14px rgba(15,36,56,.30);}
    /* ⚠️ Il -1px chiude la MICRORIGA fra le due barre: `--appbar-h` è un
       valore arrotondato, e mezzo pixel di scarto lascia passare il
       contenuto che scorre. Stessa correzione della v19.54 su telefono.
       Con le barre piene il difetto si vedrebbe ancora di più: sarebbe una
       riga di testo NITIDO in mezzo a due superfici opache. */
    #subbar{position:absolute;left:0;right:0;top:var(--appbar-h,58px);z-index:16;
      margin-top:-1px;padding-top:1px;background:var(--bg);
      box-shadow:inset 0 -1px 0 var(--g-edge),0 10px 26px -18px rgba(15,36,56,.26);}
    /* la testata dei filtri sta DENTRO il subbar: un suo fondo pieno di
       tinta diversa lo taglierebbe in due */
    #subbar .stickyhead{background:transparent;border-bottom:0;}

    /* Lo spazio in cima: misurato, mai animato.
       ⚠️ `will-change:scroll-position` — e NON `transform` né `translateZ(0)`,
       che è la promozione che verrebbe in mente per prima. Sono equivalenti
       per il browser (entrambe danno all'elenco un piano suo, così scorrere
       diventa spostare invece di ridisegnare), ma un transform — anche
       dichiarato solo come intenzione — rende `.content` il CONTENITORE DI
       RIFERIMENTO di ogni `position:fixed` che sta dentro: le tendine dei
       filtri, ancorate in fixed da `positionMSPop`, finirebbero fuori posto.
       È la trappola già pagata in v19.65 sul foglio del modulo e in v19.101
       sull'anteprima commessa. `scroll-position` non è fra le proprietà che
       creano quel contenitore, quindi promuove senza effetti collaterali.
       Misurato con le barre già piene: 24 → 16,7 ms per fotogramma. */
    .content{padding-top:var(--chrome-h,var(--appbar-h,84px));
      scroll-padding-top:var(--chrome-h,var(--appbar-h,84px));
      will-change:scroll-position;}
    /* Una .stickyhead/.toolbar rimasta dentro .content si aggancerebbe a
       top:0, cioè DIETRO le barre: si aggancia a filo del loro bordo. */
    .content .stickyhead,.content .toolbar{top:var(--chrome-h,var(--appbar-h,84px));}

    /* ⚠️ In tema scuro l'appbar ha già `background:var(--panel)` con
       specificità maggiore (0,2,1 contro 0,1,0): l'override esplicito
       serviva a farle vedere il vetro e serve ancora, perché ora il subbar
       deve prendere la STESSA tinta dell'appbar — senza, le due barre
       sarebbero di due colori diversi. */
    html[data-theme="dark"] .appbar,
    html[data-theme="dark"] #subbar{background:var(--panel);}
    html[data-theme="dark"] #subbar .stickyhead{background:transparent;}
  }
  /* In STAMPA le barre non esistono: l'overlay non deve lasciare spazio
     riservato in cima al foglio. */
  @media print{
    .appbar,#subbar{position:static;}
    .content{padding-top:0;}
  }

  /* ══════════════════════════════════════════════════════════════════
     …E SULLE SUPERFICI CHE GIÀ GALLEGGIANO (v19.81, parte A)
     ──────────────────────────────────────────────────────────────────
     La variante approvata era «A + le barre»: le barre sono il blocco
     qui sopra, questa è la A. Sono superfici che stanno GIÀ sopra il
     contenuto — menu a tendina, dock, «torna in cima», testate e piedi
     delle finestre — quindi il vetro è vero senza toccare la struttura
     della pagina.

     ⚠️ NON la barra laterale: `.content` ha `margin-left:240px`, cioè il
     contenuto non le passa MAI sotto. Lì il vetro non rifrangerebbe
     niente, e per giunta la barra è un pieno verde scuro che è il
     riferimento cromatico di tutta l'app.
     ⚠️ NON il corpo delle finestre né le righe: sotto i dati il vetro
     non va mai, o il contrasto dei numeri cambia mentre scorri.
     ══════════════════════════════════════════════════════════════════ */
  @media(min-width:641px){
    /* menu a tendina dei filtri e delle righe: galleggiano sul contenuto */
    .ms-pop,.ordmenu,.rowsheet,.gs-sortmenu,#prog-sortmenu{
      background:var(--g-fill-hi);
      -webkit-backdrop-filter:blur(var(--g-blur)) saturate(var(--g-sat)) brightness(var(--g-bright));
      backdrop-filter:blur(var(--g-blur)) saturate(var(--g-sat)) brightness(var(--g-bright));
      box-shadow:var(--g-drop), inset 0 1px 0 var(--g-ring-hi);
    }
    /* chip flottanti: dock «Da riprendere» e «torna in cima» */
    .dock .dkchip,.totop{
      background:var(--g-fill-hi);
      -webkit-backdrop-filter:blur(var(--g-blur)) saturate(var(--g-sat)) brightness(var(--g-bright));
      backdrop-filter:blur(var(--g-blur)) saturate(var(--g-sat)) brightness(var(--g-bright));
    }
    /* ⚠️ Il «torna in cima» è un PIENO verde con la freccia bianca (vedi la
       sua nota): il vetro lo renderebbe illeggibile. Resta pieno. */
    .totop{background:var(--green-solid);-webkit-backdrop-filter:none;backdrop-filter:none;}

    /* Testate e piedi delle FINESTRE: il contenuto ci scorre sotto, come
       già sul telefono dalla v19.54. Le misure le scrive
       `_misuraFinestraChrome` sulla finestra stessa, non su body. */
    .pvbox .pvhead{position:absolute;top:0;left:0;right:0;z-index:7;
      background:var(--g-fill-hi);border-bottom:0;
      -webkit-backdrop-filter:blur(var(--g-blur)) saturate(var(--g-sat)) brightness(var(--g-bright));
      backdrop-filter:blur(var(--g-blur)) saturate(var(--g-sat)) brightness(var(--g-bright));
      box-shadow:inset 0 -1px 0 var(--g-edge), inset 0 1px 0 var(--g-ring-hi);}
    /* ⚠️ Il padding va sullo SCROLLER, non sul contenitore flex che lo
       ospita: su `.pvmain` lo spazio resta FERMO in cima e si paga
       l'ingombro senza avere l'effetto (nota della v19.54). */
    .pvbox .pvmain{position:relative;}
    .pvbox .pvscroll{padding-top:max(var(--pvh-h,62px),62px);}
    .pvbox #pv-rail{padding-top:max(var(--pvh-h,62px),62px);}

    html[data-theme="dark"] .ms-pop,html[data-theme="dark"] .ordmenu,
    html[data-theme="dark"] .rowsheet,html[data-theme="dark"] .gs-sortmenu,
    html[data-theme="dark"] #prog-sortmenu,
    html[data-theme="dark"] .dock .dkchip,
    html[data-theme="dark"] .pvbox .pvhead{background:var(--g-fill-hi);}
  }
  @media(min-width:641px) and (prefers-reduced-transparency: reduce){
    .ms-pop,.ordmenu,.rowsheet,.gs-sortmenu,#prog-sortmenu,
    .dock .dkchip,.pvbox .pvhead{background:var(--panel);
      -webkit-backdrop-filter:none;backdrop-filter:none;}
  }
  @supports not ((-webkit-backdrop-filter:blur(1px)) or (backdrop-filter:blur(1px))){
    @media(min-width:641px){
      .ms-pop,.ordmenu,.rowsheet,.gs-sortmenu,#prog-sortmenu,
      .dock .dkchip,.pvbox .pvhead{background:var(--panel);}
    }
  }

  /* ══════════════════════════════════════════════════════════════════
     IL MODULO È UN ELENCO A DUE LIVELLI (v19.90) — vedi app.9g
     ──────────────────────────────────────────────────────────────────
     Prima schermata: l'elenco delle sezioni (`.sezlist`, che è la STESSA
     `.mnlist` del Menu e delle Impostazioni — non una copia). Toccandone
     una si entra: restano visibili solo i campi di quella sezione, il
     nome passa nell'intestazione della finestra e a sinistra compare il
     «‹». In fondo, «Avanti: <la prossima> ›».

     ⚠️ SUL COMPUTER QUESTE TRE COSE NON ESISTONO: la regola base qui
     sotto le spegne a ogni larghezza e solo la media query le riaccende.
     Le sezioni restano tutte aperte come sono sempre state.
     ⚠️ Niente `!important`: `#panel.liv-elenco .fsec-body` (1,2,0) batte
     da sé `#panel .fsec-body` (1,1,0) del modulo a liste, e la riga che
     riaccende la sezione aperta aggiunge `.sez-on` (1,3,0). Se un domani
     serve un `!important` qui, vuol dire che una regola sta litigando —
     si guarda quella, non si alza il volume.
     ══════════════════════════════════════════════════════════════════ */
  .sezlist,.sez-avanti,.sez-back{display:none;}

  @media(max-width:640px){
    #panel.liv-elenco .sezlist,#tpanel.liv-elenco .sezlist,#rpanel.liv-elenco .sezlist{
      display:block;margin-bottom:2px;}
    /* primo livello: il modulo è tutto lì, ma non si vede nessun campo */
    #panel.liv-elenco .sec-title.fsec-head,#panel.liv-elenco .fsec-body,
    #tpanel.liv-elenco .sec-title.fsec-head,#tpanel.liv-elenco .fsec-body,
    #rpanel.liv-elenco .sec-title.fsec-head,#rpanel.liv-elenco .fsec-body{display:none;}
    /* secondo livello: solo la sezione in cui si è entrati. L'intestazione
       resta nascosta — il nome lo dice l'intestazione della finestra, e
       ripeterlo due volte a 40px di distanza era il difetto già corretto
       nelle Impostazioni (`.rep-title`). */
    #panel.liv-sez .sec-title.fsec-head,#panel.liv-sez .fsec-body,
    #tpanel.liv-sez .sec-title.fsec-head,#tpanel.liv-sez .fsec-body,
    #rpanel.liv-sez .sec-title.fsec-head,#rpanel.liv-sez .fsec-body{display:none;}
    #panel.liv-sez .fsec-body.sez-on,#tpanel.liv-sez .fsec-body.sez-on,
    #rpanel.liv-sez .fsec-body.sez-on{display:block;}

    /* le righe dell'elenco: due righe di testo come i reparti delle
       Impostazioni (la regola sta accanto a quella, cercare `.replist`) */
    .sezlist.mnlist .rl-s.vuota{color:var(--hint);}
    /* ⚠️ Ambra su `--panel`: è un promemoria, non un errore. Il rosso qui
       direbbe «hai sbagliato» a chi ha semplicemente il modulo aperto da
       dieci secondi. */
    .sez-manca{flex:0 0 auto;font-size:10.5px;font-weight:800;letter-spacing:.02em;
      background:var(--amber-bg);color:var(--amber-tx);border:1px solid var(--amber-bd);
      border-radius:999px;padding:3px 8px;white-space:nowrap;}

    /* il «‹» della testata: stesso gesto del «‹ Menu» dell'appbar */
    .panel .sez-back{display:flex;align-items:center;gap:4px;flex:0 0 auto;
      background:none;border:0;padding:0 2px 0 0;margin-right:-2px;cursor:pointer;
      font:inherit;font-size:15px;font-weight:700;color:var(--green);
      -webkit-tap-highlight-color:transparent;}
    .panel .sez-back[hidden]{display:none;}
    .panel .sez-back .ic{width:18px;height:18px;}
    .panel .sez-back:active{opacity:.6;}
    /* col «‹» accanto, un nome lungo («Destinazione commerciale») spingerebbe
       fuori la × invece di troncare: il minimo automatico di un flex item è
       il CONTENUTO, va azzerato a mano. */
    .panel.liv-sez .panel-head h2{min-width:0;overflow:hidden;text-overflow:ellipsis;
      white-space:nowrap;flex:1 1 auto;}

    /* «Avanti: Quantità ›» — concatena le sezioni senza tornare all'elenco */
    .panel .sez-avanti{display:flex;align-items:center;justify-content:space-between;gap:10px;
      width:100%;background:var(--panel);border:0;border-radius:var(--gr);
      padding:14px 13px;margin-top:6px;box-shadow:var(--sh-sm);cursor:pointer;
      font:inherit;font-size:14.5px;font-weight:700;color:var(--green);
      -webkit-tap-highlight-color:transparent;}
    .panel .sez-avanti[hidden]{display:none;}
    .panel .sez-avanti .ic{width:18px;height:18px;}
    .panel .sez-avanti:active{background:var(--sidebar);}
  }

  /* ══════════════════════════════════════════════════════════════════════
     …E SUL COMPUTER: L'INDICE A SINISTRA (v19.93) — variante B approvata
     ──────────────────────────────────────────────────────────────────────
     La traduzione desktop degli stessi due livelli: invece di entrare e
     uscire, l'indice resta a vista in una colonna e la sezione si apre
     accanto — la forma che le Impostazioni usano già qui. La nota lunga sul
     perché sta accanto a `sezDesktop` in app.9g.

     ⚠️ Gated a `min-width:641px`: `liv-idx` la mette e la toglie il JS, ma se
     un giorno restasse addosso passando a schermo stretto il telefono non se
     ne accorgerebbe comunque. Le regole del telefono stanno PRIMA nel file e
     hanno la stessa specificità, quindi senza il gate vincerebbero queste.
     ══════════════════════════════════════════════════════════════════════ */
  @media(min-width:641px){
    /* ⚠️ Le RIGHE sono esplicite, non lasciate all'auto-placement: l'indice, la
       sezione e «Avanti» hanno tutti e tre una colonna dichiarata, e senza
       `grid-row` il browser mette «Avanti» nella riga SUCCESSIVA della colonna
       2 — cioè sotto l'INDICE, non sotto la card. Con l'indice alto 430px e la
       sezione 290 restavano 155px di vuoto in mezzo (misurato). */
    #panel.liv-idx .fgrid,#tpanel.liv-idx .fgrid,#rpanel.liv-idx .fgrid{
      grid-template-columns:252px minmax(0,1fr);gap:16px;align-items:start;align-content:start;
      /* ⚠️ La TERZA riga esiste per assorbire: l'indice spanna tutte le righe,
         e quando è più alto di card+«Avanti» il browser distribuisce l'eccesso
         sulle righe che spanna — con due sole righe finiva nella PRIMA, e
         «Avanti» si staccava dalla card di 64÷124px secondo la sezione
         (misurato). Una traccia flessibile in fondo se lo prende tutto. */
      grid-template-rows:auto auto 1fr;
    }
    /* la card ha bisogno di un fondo su cui posarsi: il pannello era un unico
       foglio bianco, e una card bianca su bianco non è una card */
    .panel.liv-idx .panel-body{background:var(--bg);}

    /* ── L'INDICE ────────────────────────────────────────────────────────
       Stessa forma della colonnina delle Impostazioni (che a sua volta è
       quella delle righe del Menu): tondino 34px, riga da 52, testo 14,5.
       ⚠️ `.mnlist`/`.mn-row` sono dichiarate dentro `@media(max-width:640px)`
       e qui non arrivano: i valori vanno ripetuti, e se cambiano là vanno
       cambiati qui. `test/modulo-indice.test.js` li confronta. */
    .panel.liv-idx .sezlist{
      display:block;grid-column:1;grid-row:1/span 3;position:sticky;top:0;z-index:2;
      background:var(--panel);border-radius:var(--gr-sm);padding:6px;
      box-shadow:var(--sh-sm),inset 0 0 0 .5px rgba(15,36,56,.10);
    }
    html[data-theme="dark"] .panel.liv-idx .sezlist{box-shadow:var(--sh-sm),inset 0 0 0 .5px rgba(255,255,255,.09);}
    .panel.liv-idx .sezlist .mn-row{
      display:flex;align-items:center;gap:11px;width:100%;text-align:left;border:0;background:none;
      font:inherit;font-size:14px;font-weight:700;letter-spacing:-.01em;color:var(--ink);
      padding:8px 9px;min-height:52px;border-radius:14px;cursor:pointer;
    }
    .panel.liv-idx .sezlist .mn-row.on{background:var(--soft);color:var(--green);}
    @media(hover:hover){ .panel.liv-idx .sezlist .mn-row:not(.on):hover{background:var(--sidebar);} }
    .panel.liv-idx .sezlist .mn-ic{
      width:32px;height:32px;min-width:32px;border-radius:11px;background:var(--soft);color:var(--green);
      display:flex;align-items:center;justify-content:center;flex:0 0 auto;
    }
    .panel.liv-idx .sezlist .mn-ic .ic{width:18px;height:18px;}
    .panel.liv-idx .sezlist .mn-t{flex:1;min-width:0;}
    /* ⚠️ Il nome VA A CAPO, non tronca (come sul telefono): a 252px con la
       pastiglia accanto, «Destinazione commerciale» e «Materiale & prodotto» si
       leggevano «Destinazione …» — cioè l'indice non diceva più dove si va. */
    .panel.liv-idx .sezlist .mn-t b{display:block;font-size:14px;font-weight:700;line-height:1.22;}
    /* il riepilogo di una sezione può essere lungo: tronca invece di far
       crescere la riga, come sul telefono */
    .panel.liv-idx .sezlist .rl-s{display:block;font-size:11.5px;font-weight:600;color:var(--muted);
      line-height:1.3;margin-top:1px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
    .panel.liv-idx .sezlist .rl-s.vuota{color:var(--hint);}
    /* ⚠️ Sulla riga ATTIVA il fondo è `--soft` e lì `--muted` scende a 4,17:1
       (misurato): il riepilogo passa al verde della riga, che su quel fondo
       sta sopra 10:1. È la trappola dei grigi tenui sui fondi colorati, già
       pagata da `.prog-terzhead` e da mezza dozzina di altri punti. */
    .panel.liv-idx .sezlist .mn-row.on .rl-s,
    .panel.liv-idx .sezlist .mn-row.on .rl-s.vuota{color:var(--green3);}
    /* ⚠️ Ambra e non rosso: è un promemoria, non un errore — chi ha il modulo
       aperto da dieci secondi non ha sbagliato niente. */
    .panel.liv-idx .sezlist .sez-manca{
      flex:0 0 auto;font-size:10px;font-weight:800;letter-spacing:.02em;
      background:var(--amber-bg);color:var(--amber-tx);border:1px solid var(--amber-bd);
      border-radius:999px;padding:2px 7px;white-space:nowrap;
    }
    .panel.liv-idx .sezlist .mn-chev{display:none;}

    /* ── LA SEZIONE APERTA ───────────────────────────────────────────────
       ⚠️ La regola che regge tutto: nella card ogni figlio prende la riga
       INTERA e solo un `.field` semplice sta in colonna. Le sezioni non
       contengono solo campi — ci sono `.chkrow`, `.mqgrid`, le note-lucchetto,
       il mix crust, la ripartizione clienti, la griglia lavorazioni — e senza
       questo default si sarebbero schiacciate in mezza colonna. */
    .panel.liv-idx .sec-title.fsec-head{display:none;}
    .panel.liv-idx .fsec-body{display:none;}
    .panel.liv-idx .fsec-body.sez-on{
      display:grid;grid-column:2;grid-row:1;grid-template-columns:repeat(2,minmax(0,1fr));gap:2px 18px;
      align-content:start;background:var(--panel);border-radius:var(--gr-sm);padding:12px 16px 14px;
      box-shadow:var(--sh-sm),inset 0 0 0 .5px rgba(15,36,56,.10);
    }
    html[data-theme="dark"] .panel.liv-idx .fsec-body.sez-on{box-shadow:var(--sh-sm),inset 0 0 0 .5px rgba(255,255,255,.09);}
    .panel.liv-idx .fsec-body.sez-on > *{grid-column:1/-1;}
    .panel.liv-idx .fsec-body.sez-on > .field{grid-column:auto;}
    .panel.liv-idx .fsec-body.sez-on > .field.full{grid-column:1/-1;}
    .panel.liv-idx .fsec-body.sez-on > .field{padding:8px 0 9px;margin:0;gap:3px;}

    /* ── I CAMPI DENTRO LA CARD ──────────────────────────────────────────
       Le stesse due mosse del telefono: UNA tipografia per le etichette
       (14,5px tonda scura, non il maiuscoletto grigio da 10,5 — nella scheda
       erano 23 campi contro 14, e capitava che si toccassero) e l'input che
       perde il bordo e diventa un incasso, che dentro una card bianca legge
       meglio di un riquadro nel riquadro.
       ⚠️ `:not(.chkbox)`: l'etichetta di una CASELLA è il testo del comando e
       ha la sua misura.
       ⚠️ Le DATE vanno elencate a parte: `attachCalendar` mette il loro input
       dentro `.datefield-wrap`, quindi non è figlio DIRETTO del `.field` e
       resterebbe riquadrato accanto a campi incassati, nella stessa card. */
    .panel.liv-idx .fsec-body > .field > label:not(.chkbox),
    .panel.liv-idx .fsec-body > .field > .flabrow > label:first-child,
    /* ⚠️ I m² stanno nella loro sotto-griglia (`.mqgrid`, l'allineamento
       stima|reali) e NON sono figli diretti della sezione: dimenticandoli
       restavano le due sole etichette maiuscolette del modulo — la stessa
       trappola già pagata sul telefono, e si vede subito perché «Pelli (pz)»
       e «METRI QUADRI — STIMA» finiscono una sotto l'altra. */
    .panel.liv-idx .fsec-body .mqgrid > .field > label{
      font-size:14.5px;font-weight:600;letter-spacing:normal;text-transform:none;color:var(--ink);
      /* ⚠️ INTERLINEA INTERA, non ereditata: 14,5px × 1,5 fa 21,75px e porta
         tutto ciò che sta sotto su posizioni frazionarie — la casella «Come già
         fatto» finiva a 691,125px e il suo VISTO pendeva di 0,54px, oltre il
         mezzo pixel di tolleranza (colto da «il visto sta al centro della
         casella»). Con 20px le righe cadono su pixel interi. */
      line-height:20px;
    }
    /* l'etichetta di una CASELLA è il testo del comando, e ha la sua misura —
       ma va tolta dal maiuscoletto lo stesso, o «nascondi i metri quadri…»
       resta l'unica riga in maiuscolo della card */
    /* ⚠️ Qui si tocca SOLO il maiuscoletto, non la misura: `.chkbox` è già
       13px/600 e portarla a 13,5 dava una riga alta 20,25px — un mezzo pixel
       che spostava la casella su una posizione frazionaria e faceva pendere il
       VISTO di 0,54px (colto da «il visto sta al centro della casella»). Il
       maiuscolo invece arriva da `.field label`, che ha specificità maggiore di
       `.chkbox`, e va spento o «COME GIÀ FATTO» resta l'unica riga in
       maiuscolo della card. */
    .panel.liv-idx .fsec-body > .field > label.chkbox{
      letter-spacing:normal;text-transform:none;color:var(--ink);
    }
    /* la spiegazione resta di servizio: più piccola e più leggera del nome */
    .panel.liv-idx .fsec-body .field .fhint{font-size:12.5px;font-weight:500;}
    .panel.liv-idx .fsec-body .field > input,
    .panel.liv-idx .fsec-body .field > textarea,
    .panel.liv-idx .fsec-body .field > select,
    .panel.liv-idx .fsec-body .field .datefield-wrap > input,
    .panel.liv-idx .fsec-body .field > .ms > .ms-btn{
      border-color:transparent;background:var(--sidebar);border-radius:12px;
    }
    /* ⚠️ IL CAMPO BLOCCATO DEVE RESTARE RICONOSCIBILE: la regola qui sopra ha
       specificità maggiore di `.field input:disabled`, e senza questa un campo
       spento avrebbe lo STESSO colore di uno acceso — è il segnale su cui si
       reggono `setLock` (Consegnata, Trasferita) e «🔓 Forza modifica». Stessa
       trappola già pagata sul telefono in v19.64. */
    .panel.liv-idx .fsec-body .field > input:disabled,
    .panel.liv-idx .fsec-body .field > textarea:disabled,
    .panel.liv-idx .fsec-body .field > select:disabled,
    .panel.liv-idx .fsec-body .field .datefield-wrap > input:disabled,
    .panel.liv-idx .fsec-body .field.locked > input{
      background:var(--line);color:var(--muted2,var(--muted));border:1px dashed var(--line2);
    }

    /* «Avanti: Quantità ›» — concatena le sezioni senza passare dall'indice */
    .panel.liv-idx .sez-avanti{
      display:flex;grid-column:2;grid-row:2;align-items:center;justify-content:space-between;gap:10px;
      background:var(--panel);border:0;border-radius:var(--gr-sm);padding:13px 16px;margin-top:2px;
      box-shadow:var(--sh-sm),inset 0 0 0 .5px rgba(15,36,56,.10);cursor:pointer;
      font:inherit;font-size:14px;font-weight:700;color:var(--green);
    }
    .panel.liv-idx .sez-avanti[hidden]{display:none;}
    .panel.liv-idx .sez-avanti .ic{width:17px;height:17px;}
    @media(hover:hover){ .panel.liv-idx .sez-avanti:hover{background:var(--soft);} }
  }

  /* ── Spazio archivio (Impostazioni → Dati e backup) ──────────────────────
     Il misuratore dello spazio sul database. La barra è una sola riga divisa
     in quattro segmenti (backup · schede · anagrafiche · sistema): la
     ripartizione è il dato che conta, perché tre quarti dell'occupato è
     l'overhead fisso di Supabase e senza vederlo separato sembra che il
     gestionale pesi cinque volte quello che pesa.
     ⚠️ La nota ha fondo --sidebar, dove --muted scende a 4,34:1 (la trappola
     già pagata da .prog-terzhead): il testo è --hint, non --muted.
     ⚠️ Le tre pastiglie usano coppie fondo/testo che il tema scuro RIDEFINISCE
     (--green-*, --amber-*, --st-late-*). Con un --red o un --tan qui si
     avrebbe rosso su rosso al buio, com'è già successo altrove. ── */
  /* Il misuratore NON è più un riquadro dentro la card (era una card dentro
     la card): è la prima cosa che la card mostra. Con lui se ne sono andate
     .spz-box/.spz-h/.spz-bar/.spz-leg — la barra a segmenti e la sua legenda,
     sostituite da .spz-lim (usato/libero) e .spz-rip (le righe coi numeri). */
  .spz{display:flex;flex-direction:column;gap:12px;}
  .spz-load{font-size:13px;color:var(--hint);padding:4px 0;}
  .spz-top{display:flex;justify-content:space-between;align-items:baseline;gap:10px;flex-wrap:wrap;}
  .spz-big{font-size:25px;font-weight:800;letter-spacing:-.02em;color:var(--ink);
    font-variant-numeric:tabular-nums;}
  .spz-big .of{font-size:14px;font-weight:600;color:var(--muted);letter-spacing:0;}
  .spz-pct{font-size:12.5px;font-weight:700;border-radius:999px;padding:3px 10px;white-space:nowrap;}
  .spz-pct.ok{color:var(--green-tx);background:var(--green-bg);border:1px solid var(--green-bd);}
  .spz-pct.warn{color:var(--amber-tx);background:var(--amber-bg);border:1px solid var(--amber-bd);}
  .spz-pct.alta{color:var(--st-late-tx);background:var(--st-late-bg);border:1px solid var(--st-late);}
  .spz-note{font-size:13px;line-height:1.5;color:var(--hint);background:var(--panel);
    border:1px solid var(--border);border-radius:10px;padding:10px 12px;}
  .spz-note b{color:var(--ink);font-weight:700;}
  .spz-note.warn{background:var(--amber-bg);border-color:var(--amber-bd);color:var(--amber-tx);font-weight:600;}
  .spz-note.warn b{color:var(--amber-tx);}
  .spz-prov{color:var(--muted);}
  /* L'esito delle due manopole della retention («Fino a 40 copie · puoi tornare
     indietro di 30 giorni»). Sta sotto le righe «Copie recenti» e «Storico
     giornaliero», che dalla v20.11 sono due righe normali della pagina — la
     vecchia frase spezzata attorno ai campi (.bkeep) non esiste più: la parola
     «copie» non compariva mai accanto ai numeri.
     ⚠️ Il testo è --hint e non --muted: la .set-help nuda su fondo --panel
     regge, ma questa riga porta l'unica sintesi di due impostazioni e va letta,
     non intravista. */
  .set-help.bkeep-esito{color:var(--hint);}
  .set-help.bkeep-esito b{color:var(--ink);font-weight:700;}

  /* ══ Pagina «Salvataggio e backup» ═══════════════════════════════════════
     v20.14 — riordino approvato dall'utente su anteprima (variante A fra tre).
     Prima la card «Stato database» era CINQUE fasce a tutta riga con la stessa
     forma — posizione, sicurezza, copia esterna, sveglia, spazio — e nessuna
     gerarchia: 187px su 577 di card sul computer (il 32%), 240 su 709 su
     iPhone. La più «gridata», in ambra, era la posizione del database, cioè
     l'unico dato che non cambia mai.
     Ora: il misuratore in cima, i controlli a posto in pastiglia, e la fascia
     colorata resta SOLO a ciò che chiede attenzione.
     ⚠️ Il testo di servizio su fondo --sidebar va in --hint e non in --muted,
     che lì scende a 4,34:1 (la trappola già pagata da .prog-terzhead). ── */
  .bk-online{display:flex;align-items:center;gap:12px;margin-bottom:14px;}
  #bk-dot{width:13px;height:13px;border-radius:50%;background:#f87171;flex:0 0 auto;
    box-shadow:0 0 0 3px rgba(248,113,113,.2);}
  #bk-conn-txt{font-weight:700;font-size:15px;color:var(--ink);}
  /* La barra del riempimento: DUE sole parti, usato e libero, col libero
     scritto dentro. La barra a segmenti di prima calcolava le percentuali sui
     14,1 MB USATI mentre sopra c'era scritto «di 500 MB», quindi il grigio si
     leggeva come spazio libero — ed era invece «Sistema». Sui 500 MB veri i
     dati varrebbero 30px su 1067 di barra: ecco perché la ripartizione è
     passata alle righe qui sotto, dove i numeri si leggono. */
  .spz-lim{position:relative;height:22px;border-radius:999px;background:var(--sidebar);
    border:1px solid var(--border);overflow:hidden;display:flex;align-items:center;}
  .spz-lim .uso{height:100%;background:var(--green);min-width:3px;}
  .spz-lim .libero{position:absolute;right:10px;font-size:11.5px;font-weight:700;
    color:var(--hint);font-variant-numeric:tabular-nums;}
  .spz-lim.warn .uso{background:var(--st-lav);}
  .spz-lim.alta .uso{background:var(--st-late);}
  .spz-sub{font-size:11px;color:var(--muted);font-weight:700;text-transform:uppercase;
    letter-spacing:.8px;}
  .spz-rip{display:flex;flex-direction:column;}
  .spz-rip .r{display:flex;align-items:center;gap:9px;padding:7px 0;border-top:1px solid var(--line);
    font-size:13px;font-variant-numeric:tabular-nums;}
  .spz-rip .r:first-child{border-top:0;}
  .spz-rip em{width:9px;height:9px;border-radius:3px;font-style:normal;flex:0 0 auto;}
  .spz-rip .nm{flex:1;min-width:0;color:var(--ink);font-weight:600;}
  .spz-rip .nm i{font-style:normal;color:var(--muted);font-weight:500;}
  .spz-rip .vv{font-weight:700;color:var(--ink);}
  .spz-rip .pp{color:var(--muted);width:42px;text-align:right;}
  /* Controlli: pastiglia per ciò che va bene, fascia per ciò che non va. */
  .bk-ctrl{display:flex;flex-direction:column;gap:8px;margin-top:15px;}
  .bk-chips{display:flex;flex-wrap:wrap;gap:7px;}
  .bk-chip{display:inline-flex;align-items:center;gap:6px;font-size:12.5px;font-weight:700;
    border-radius:999px;padding:5px 11px;background:var(--soft);border:1px solid var(--soft-bd);
    color:var(--green);}
  .bk-chip .ic{width:14px;height:14px;}
  .bk-fascia{border-radius:9px;padding:10px 13px;font-size:12.5px;line-height:1.45;
    background:var(--amber-bg);border:1px solid var(--amber-bd);color:var(--amber-tx);}
  .bk-fascia b{font-weight:700;}
  .bk-serv{margin-top:12px;padding-top:10px;border-top:1px solid var(--line);font-size:12px;
    color:var(--muted);display:flex;gap:6px 16px;flex-wrap:wrap;font-variant-numeric:tabular-nums;}
  .bk-serv b{color:var(--hint);font-weight:700;}
  /* Le altre card della pagina: erano stili scritti a mano nell'HTML, con
     intestazioni grigie invece del verde standard e riquadri a colori fissi
     che in tema scuro restavano chiari. */
  .bk-riga{display:flex;align-items:flex-start;gap:13px;margin-bottom:14px;}
  .bk-riga:last-child{margin-bottom:0;}
  .bk-riga-ic{color:var(--green);display:flex;padding-top:1px;flex:0 0 auto;}
  .bk-riga-ic .ic{width:19px;height:19px;}
  .bk-riga-t{font-weight:700;font-size:14px;color:var(--ink);margin-bottom:3px;}
  .bk-riga-d{font-size:12.5px;color:var(--muted);line-height:1.5;}
  .bk-riga-n{font-size:11.5px;color:var(--hint);margin-top:7px;line-height:1.45;}
  .bk-azioni{display:flex;gap:9px;flex-wrap:wrap;align-items:center;margin-top:10px;}
  .bk-fb{align-self:center;font-size:12.5px;color:var(--green);font-weight:600;
    display:inline-flex;align-items:center;gap:5px;}
  .bk-sep{height:1px;background:var(--border);margin:14px 0;}
  .bk-avviso{border-radius:9px;padding:10px 13px;font-size:12.5px;line-height:1.55;margin-top:10px;}
  .bk-avviso.ambra{background:var(--amber-bg);border:1px solid var(--amber-bd);color:var(--amber-tx);}
  .bk-avviso.rosso{background:var(--st-late-bg);border:1px solid var(--st-late);color:var(--st-late-tx);}
  .bk-avviso .ic{width:15px;height:15px;vertical-align:-3px;}
  .bk-h4act{display:flex;gap:8px;align-items:center;text-transform:none;letter-spacing:0;}
  .bk-h4act .tag{background:var(--sidebar);border:1px solid var(--border);border-radius:20px;
    padding:3px 11px;font-size:12px;font-weight:700;color:var(--ink);}
  .bk-list{max-height:280px;overflow-y:auto;overscroll-behavior:contain;
    border:1px solid var(--border);border-radius:9px;padding:4px 0;}
  .bk-list-vuota{text-align:center;padding:18px;color:var(--hint);font-size:13px;font-style:italic;}
  /* ⚠️ --st-late-tx e non un rosso letterale: il tema scuro ridefinisce solo i
     token (è la trappola già pagata da --red). */
  .bk-list-vuota.err{color:var(--st-late-tx);font-style:normal;}
  .bk-row{display:flex;align-items:center;gap:10px;padding:9px 13px;border-bottom:1px solid var(--border);}
  .bk-row:last-child{border-bottom:0;}
  .bk-row-ic{color:var(--green);display:flex;flex:0 0 auto;}
  .bk-row-txt{flex:1;min-width:0;}
  .bk-row-t{font-weight:600;font-size:13.5px;color:var(--ink);}
  .bk-row-s{font-size:11.5px;color:var(--muted);margin-top:1px;word-break:break-all;}
  .bk-row-new{font-size:11px;background:var(--soft);color:var(--green);border-radius:4px;
    padding:1px 6px;margin-left:4px;font-weight:700;}
  .bk-row .btn{white-space:nowrap;font-size:12px;}
  .bk-verify{margin-top:10px;border-radius:9px;padding:10px 13px;font-size:12.5px;line-height:1.5;}
  /* Le due manopole della retention, in riga con le parole che le legano.
     ⚠️ I campi sono stretti (48px): con la larghezza piena della .set-num la
     riga andava a capo su telefono spezzando la frase a metà. */
  .bkeep{display:flex;align-items:center;gap:7px;flex-wrap:wrap;justify-content:flex-end;
    font-size:13px;color:var(--hint);}
  .bkeep .set-num{width:48px;text-align:center;}

  /* ══ SCORRERE NON DEVE ACCENDERE L'HOVER (v20.15) ══════════════════════
     Segnalato dall'utente come «lo scroll lagga», e non erano le animazioni:
     scorrendo, il puntatore sta FERMO ed è l'elenco a passargli sotto, quindi
     ogni riga riceve :hover a turno, fa partire la sua transizione di fondo e
     ombra e obbliga a ridipingere — decine di volte al secondo, su una
     superficie grande quanto la finestra. Misurato con 60 schede e processore
     rallentato 4×, fotogrammi oltre i 32ms durante una scorsa:
        Programma  40 su 91 (p95 50,1ms)   →   2 su 89 (p95 16,8ms)
        Schede     27 su 117 (p95 50ms)    →   6 su 91 (p95 33,3ms)
     Il rimedio è togliere i DISCENDENTI dai puntatori per la durata del gesto:
     niente hover da calcolare, niente ridisegno. La classe la mette e la toglie
     il gestore di scorrimento in app.9g (`HOVER_QUIETE`).
     ⚠️ Gated su `hover:hover` e `pointer:fine`: su telefono l'hover non esiste
     e il tocco che ferma l'inerzia non va toccato per nessun motivo.
     ⚠️ La perde `.content`, NON i suoi discendenti — il contenitore deve
     restare il bersaglio del gesto — e il «torna in cima» è figlio di `.main`,
     quindi resta cliccabile mentre si scorre.
     ⚠️ Non è una sostituzione dell'hover: appena il dito o la rotella si
     fermano (140ms) torna tutto come prima, riga sotto il cursore compresa. */
  @media (hover:hover) and (pointer:fine){
    .content.sta-scorrendo *{pointer-events:none;}
  }

/* ══════════════════════════════════════════════════════════════════
     SOGLIA iOS DEI 16px — deve restare l'ULTIMO blocco del file
     ──────────────────────────────────────────────────────────────────
     Sotto i 16px iPhone ingrandisce la pagina al focus e NON la riporta
     indietro: il modulo resta zoomato e bisogna pizzicare per rientrare.
     La regola generale (`input,select,textarea{font-size:16px}` nel blocco
     ≤640px) ha specificità (0,0,1) e veniva battuta da otto famiglie di
     campi dichiarate PIÙ AVANTI nel file con la stessa specificità o
     maggiore. A parità di specificità vince chi è scritto dopo: per questo
     la correzione sta qui in fondo e non accanto alla regola generale.
     Se aggiungi un campo con un font più piccolo, aggiungilo a questo elenco.
     Dove i 16px allargherebbero troppo il campo si compensa con padding e
     larghezza — MAI riabbassando il carattere.
     Le DUE ricerche globali (la barra nell'appbar e il campo della finestra
     che si apre al primo carattere) vanno tenute insieme: si toccano una
     dopo l'altra, e bastava che una sola fosse sotto soglia perché la pagina
     restasse ingrandita. I campi generati in JS con stile inline — quelli di
     accesso — non li raggiunge nessuna regola di qui: stanno a 16px alla
     fonte, in app.6. */
  @media(max-width:640px){
    .qreal input,.pvreal-f input,.calc-inp input,
    .lavfield input[data-lavfield],.lavfield select[data-lavfield],
    .mcli-in,.mix-row input,.mix-row select,.fatt-in,
    .gsbar input,#gs-input,.rep-launch input[type=month],
    .qbtns .datefield-wrap input{font-size:16px;}
    .qreal input{width:104px;padding:7px 9px;}
    .lavfield input[data-lavfield],.lavfield select[data-lavfield]{width:116px;padding:6px 8px;}
    .calc-inp input{padding:6px 8px;}
    .qbtns .datefield-wrap{width:152px;}
    .qbtns .datefield-wrap input{padding:8px 30px 8px 10px;}
    .mix-row input,.mix-row select{padding:7px 8px;}
    .mcli-in{padding:7px 8px;}
    .fatt-in{padding:6px 8px;}
    .rep-launch input[type=month]{padding:7px 8px;}
  }
