/* ============================================================
   instaurus-app.css — il layer `app`.
   ------------------------------------------------------------
   E' l'ULTIMO layer dichiarato (l'ordine sta in testa a
   instaurus-tokens.css), quindi quello che si scrive qui vince su
   tokens, base, componenti, runtime e tema SENZA usare !important.
   E' qui che va il CSS nuovo del restyle, una schermata alla volta.

   Per ora contiene solo le due correzioni trasversali della fase 1:
   l'indicatore di focus e la compensazione delle safe area di iPhone.
   Sono additive: non toccano nessuna regola esistente, e infatti la
   verifica a resa identica non registra differenze (nulla e' a fuoco,
   e su un browser senza notch tutte le env() valgono 0).
   ============================================================ */

@layer app {

  /* Dashboard superadmin: gerarchia compatta, operatività prima dei dettagli. */
  .fen-admin { max-width: 1500px; margin: 0 auto; }
  .fen-admin-head { display:flex; align-items:flex-start; justify-content:space-between; gap:16px; margin:2px 0 18px; }
  .fen-admin-head h1 { margin:0; font-family:var(--font-display); font-size:28px; font-weight:500; }
  .fen-admin-head p { margin:4px 0 0; color:var(--muted); font-size:13px; }
  .fen-admin-actions { display:flex; gap:8px; flex-wrap:wrap; justify-content:flex-end; }
  .fen-admin-kpis { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:10px; }
  .fen-admin-kpi { min-width:0; padding:15px; border:1px solid var(--line); border-radius:var(--radius); background:var(--surface); }
  .fen-admin-kpi span { display:block; color:var(--muted); font-size:11px; margin-bottom:8px; }
  .fen-admin-kpi b { display:block; color:var(--ink); font-size:24px; font-weight:600; letter-spacing:-.025em; overflow-wrap:anywhere; }
  .fen-admin-kpi small { display:block; color:var(--ok-text); font-size:11px; margin-top:5px; }
  .fen-admin-grid { display:grid; grid-template-columns:minmax(0,1.6fr) minmax(250px,.65fr); gap:14px; align-items:start; }
  .fen-admin-clienti { min-width:0; }
  .fen-admin-side { display:grid; gap:14px; }
  .fen-admin-side .card { margin-top:14px; }
  .fen-admin-search { padding:10px 16px; border-bottom:1px solid var(--line); background:var(--surface-2); }
  .fen-admin-search input { width:min(100%,320px); min-height:40px; padding:8px 11px; border:1px solid var(--line); border-radius:8px; background:var(--surface); color:var(--ink); font:inherit; }
  .fen-admin-row-actions { display:flex; align-items:center; gap:6px; }
  .fen-admin-row-actions select { min-height:36px; max-width:142px; border:1px solid var(--line); border-radius:8px; background:var(--surface); color:var(--ink); font:inherit; font-size:12px; padding:0 8px; }
  .fen-admin-alerts { padding:3px 16px; }
  .fen-admin-alert { display:grid; grid-template-columns:25px 1fr; gap:9px; padding:12px 0; border-bottom:1px solid var(--line); }
  .fen-admin-alert:last-child { border-bottom:0; }
  .fen-admin-alert > span { width:24px; height:24px; display:grid; place-items:center; border-radius:7px; color:var(--warn-text); background:var(--warn-soft); font-weight:700; }
  .fen-admin-alert b,.fen-admin-alert small { display:block; }
  .fen-admin-alert b { font-size:12.5px; font-weight:600; }
  .fen-admin-alert small { color:var(--muted); font-size:11px; margin-top:3px; }
  .fen-admin-alert-more,.fen-admin-ok { padding:13px 0; color:var(--muted); font-size:12px; }
  .fen-admin-ok { color:var(--ok-text); }
  .fen-admin-usage { padding:15px 16px; }
  .fen-admin-usage-row { margin-bottom:14px; }
  .fen-admin-usage-row > div { display:flex; justify-content:space-between; gap:10px; font-size:12px; margin-bottom:7px; }
  .fen-admin-usage-row i { display:block; height:7px; overflow:hidden; border-radius:999px; background:var(--surface-2); }
  .fen-admin-usage-row em { display:block; height:100%; border-radius:inherit; background:var(--action); }
  .fen-admin-usage-foot { display:grid; grid-template-columns:1fr auto; gap:4px 10px; padding-top:12px; border-top:1px solid var(--line); font-size:12px; }
  .fen-admin-usage-foot small { grid-column:1/-1; color:var(--muted); }
  .fen-admin-usage-foot b.neg { color:var(--err-text); }
  /* Ricarichi sugli import. La testata sta SOPRA la tabella e non dentro il
     sommario: il pulsante «Aggiorna» dentro un <summary> aprirebbe e chiuderebbe
     il riquadro a ogni clic, perche' il clic sul sommario e' il toggle. */
  .fen-ric-head { display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:10px; margin:0 0 10px; font-size:12px; color:var(--muted); }
  .fen-ric-riuso { display:inline-block; margin-top:3px; padding:1px 7px; border-radius:6px; background:var(--green-soft); color:var(--ok-text); font-size:10.5px; font-weight:700; }
  /* auto-fit e non un numero fisso di colonne: le cinque voci del totale su
     375px starebbero in colonne da 60 pixel, cioe' «€ 1.234,56» mandato a capo
     in mezzo alla cifra. */
  .fen-ric-tot { display:grid; grid-template-columns:repeat(auto-fit, minmax(130px, 1fr)); gap:10px 16px; margin-top:12px; padding-top:12px; border-top:1px solid var(--line); }
  .fen-ric-tot > div { display:flex; flex-direction:column; gap:2px; }
  .fen-ric-tot span { color:var(--muted); font-size:11px; }
  .fen-ric-tot b { font-size:15px; font-weight:700; }
  .fen-ric-tot b.pos { color:var(--ok-text); }
  .fen-ric-tot b.neg { color:var(--err-text); }
  .fen-ric-tot small { color:var(--muted); font-size:10.5px; }
  /* L'invito a ricaricare, quando il credito e' finito. Bersagli da 44px:
     lo legge chi e' in cantiere con i guanti, ed e' l'unica strada per
     tornare a lavorare. */
  .imp-ric-box { background:var(--surface); border-radius:var(--radius); padding:22px 24px; max-width:460px; width:100%; font-family:inherit; }
  .imp-ric-tit { font-weight:700; font-size:15px; color:var(--ink); margin-bottom:8px; }
  .imp-ric-txt { margin:0 0 10px; font-size:13px; line-height:1.5; color:var(--ink); }
  .imp-ric-taglie { display:flex; gap:8px; flex-wrap:wrap; margin-bottom:14px; }
  .imp-ric-taglia { min-height:44px; padding:0 16px; border:1px solid var(--line-strong); border-radius:var(--radius-sm); background:var(--surface); color:var(--ink); font:inherit; font-size:14px; font-weight:600; cursor:pointer; }
  .imp-ric-taglia.scelta { border-color:var(--action); background:var(--action); color:var(--action-on); }
  .imp-ric-azioni { display:flex; gap:8px; justify-content:flex-end; flex-wrap:wrap; }
  .imp-ric-btn { min-height:44px; padding:0 16px; border:1px solid var(--line-strong); border-radius:var(--radius-sm); background:var(--surface); color:var(--ink); font:inherit; font-size:13px; font-weight:600; cursor:pointer; }
  .imp-ric-btn.primario { border-color:var(--action); background:var(--action); color:var(--action-on); }
  .imp-ric-nota { margin:12px 0 0; font-size:11px; line-height:1.45; color:var(--muted); }
  @media (hover: hover) {
    .imp-ric-taglia:hover, .imp-ric-btn:hover { border-color:var(--action); }
  }
  /* L'anteprima del listino import: non e' un avviso, e' il risultato del
     numero che si sta digitando. Fondo tenue e non un bordo colorato, che
     lo farebbe leggere come un errore. */
  .fen-sim { margin-top:10px; padding:10px 12px; border-radius:10px; background:var(--surface-2); font-size:12px; line-height:1.55; }
  .fen-sim small { display:block; margin-top:5px; color:var(--muted); font-size:10.5px; }
  .fen-sim-pos { color:var(--ok-text); font-weight:700; }
  .fen-sim-neg { color:var(--err-text); font-weight:700; }
  .fen-sim-warn { color:var(--warn-text); font-weight:700; }
  details.fen-admin-secondary { overflow:hidden; }
  /* Il <small> del sommario spiega il titolo che ha accanto, quindi gli sta
     accanto. Con `margin-left:auto` finiva all'estremo destro della riga: sulla
     panoramica erano ottocento pixel fra «Utenze non piu' in uso» e «Nessun dato
     e' stato cancellato», e a quella distanza si leggono come due cose diverse.
     `flex-wrap` perche' un titolo lungo e una spiegazione lunga adesso possono
     non starci: meglio due righe che una riga che deborda. */
  .fen-admin-secondary summary { display:flex; align-items:center; flex-wrap:wrap; gap:8px; min-height:48px; padding:0 16px; cursor:pointer; font-weight:600; list-style-position:inside; }
  .fen-admin-secondary summary small { min-width:0; color:var(--muted); font-weight:400; font-size:11px; }
  .fen-admin-secondary-body { padding:0 14px 14px; }
  .fen-training-intro { display:grid; grid-template-columns:minmax(0,1fr) auto; gap:4px 16px; align-items:center; margin:0 0 14px; padding:14px; border:1px solid var(--line); border-radius:var(--radius); background:var(--surface-2); }
  .fen-training-intro b { color:var(--ink); font-size:14px; }
  .fen-training-intro p { grid-column:1; margin:0; color:var(--muted); font-size:12px; line-height:1.5; }
  .fen-training-intro .ibtn { grid-column:2; grid-row:1 / span 2; min-height:44px; }
  .fen-training #platform-training-dropzone { margin-bottom:14px; }
  .fen-admin-map-head { display:flex; justify-content:flex-end; align-items:center; padding:0 14px 10px; }

  /* Pagina Nova della console. Il corpo delle due schede aperte stava a 14px
     scritti inline: fuori dalla griglia 8px, e un valore in piu' da inseguire in
     una stringa di JS. Sedici, in una classe. */
  .nc-body { padding:var(--sp-4, 16px); }
  .nc-hero-act { margin-top:var(--sp-3, 12px); }
  /* Le tre zone hanno un titolo proprio, e lo spazio PRIMA di un titolo dev'essere
     maggiore di quello fra le schede che raggruppa: se sono uguali, il
     raggruppamento non si legge e tornano nove blocchi in fila. */
  .content > .dash-section-title { margin-top:var(--sp-6, 32px); }
  .content > .dash-hero + .dash-section-title { margin-top:0; }
  /* `.dash-section-title` spinge il <small> all'estremo opposto della riga:
     sulla dashboard, dove le colonne sono strette, funziona; qui la colonna e'
     larga tutto il contenuto e a 1440 fra il titolo e la sua spiegazione
     restavano settecento pixel di niente — si leggevano come due cose diverse.
     La classe e' propria di questa pagina: la dashboard resta com'e'.

     Due classi e non una: `workbench.css` sta nello STESSO layer `app` ed e'
     caricato DOPO (index.html: prima instaurus-app.css, poi workbench.css),
     quindi a parita' di specificita' vincerebbe lui. `!important` qui e'
     vietato — si alza la specificita', che e' la risposta giusta alla domanda
     «perche' non vince». */
  .dash-section-title.nc-sec { justify-content:flex-start; gap:var(--sp-3, 12px); }
  .dash-section-title.nc-sec small { color:var(--ink-muted); }

  /* ------------------------------------------------------------------
     Archivio cataloghi: un riquadro per ditta, i materiali dentro.
     Stessa forma delle card-azione del banco di lavoro (.dash-actions
     button), perche' il gesto e' lo stesso: un bersaglio grande da
     colpire con un dito solo.

     --ditta-tinta arriva dal DATABASE, non da questo foglio: e' il
     colore del marchio del fornitore. Ha SEMPRE un ripiego su un token
     di ruolo, perche' una var() che non risolve non ripiega — annulla
     la dichiarazione, e il filetto sparirebbe. E resta un riempimento:
     nessun testo ci va sopra, visto che quel colore arriva da un PDF di
     cui non sappiamo niente e in uno dei due temi potrebbe essere
     illeggibile.
     ------------------------------------------------------------------ */
  .catarc-body { padding:0 14px 14px; }
  .catarc-stato { padding:14px 0; color:var(--muted); font-size:12.5px; }
  /* auto-fit, non auto-fill: auto-fill tiene in piedi le colonne vuote, e a
     1920 con quattro ditte restavano mille pixel di niente sulla destra — la
     stessa trappola gia' costata alla testata della landing.
     Il massimo resta 1fr e NON un valore fisso: con un massimo definito il
     numero di tracce si calcola su QUELLO, e a 700px un tetto di 360px dava
     una colonna sola dove ce ne stavano due. Visto in fotografia, non dedotto. */
  .catarc-tiles { display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:8px; }
  .catarc-tile {
    min-height:72px; display:grid; grid-template-columns:44px minmax(0,1fr); grid-template-rows:auto auto;
    column-gap:12px; align-items:center; padding:12px 14px; text-align:left; cursor:pointer;
    color:var(--ink); background:var(--surface); border:1px solid var(--line);
    border-left:3px solid var(--ditta-tinta,var(--action)); border-radius:var(--radius);
    font-family:var(--font-ui); box-shadow:var(--shadow-1);
    transition:border-color var(--dur-1), background var(--dur-1);
  }
  .catarc-tile > .catarc-seg {
    grid-row:1/3; display:grid; place-items:center; width:44px; height:30px; overflow:hidden;
    border-radius:var(--radius-xs); background:var(--ditta-tinta,var(--action)); color:var(--action-on);
  }
  .catarc-seg img { max-width:44px; max-height:30px; display:block; }
  .catarc-seg span { font-size:12.5px; font-weight:700; letter-spacing:.5px; }
  .catarc-tile b { align-self:end; font-size:13.5px; font-weight:600; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .catarc-tile small { align-self:start; color:var(--ink-muted); font-size:11.5px; }
  .catarc-tile.is-aperta { background:var(--surface-2); border-color:var(--line-strong); }
  .catarc-tile:focus-visible { outline:2px solid var(--focus); outline-offset:2px; }
  @media (hover:hover) {
    .catarc-tile:hover { border-color:var(--line-strong); background:var(--surface-2); }
  }

  .catarc-det { margin-top:14px; }
  .catarc-det:empty { margin-top:0; }
  .catarc-det-h { display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-bottom:10px; }
  .catarc-det-h b { font-family:var(--font-display); font-size:16px; font-weight:600; }
  .catarc-mut { color:var(--muted); font-size:12px; }
  .catarc-cerca {
    flex:1 1 200px; min-width:0; max-width:420px; min-height:44px; padding:9px 12px; font:inherit; font-size:16px;
    color:var(--ink); background:var(--surface); border:1px solid var(--line-strong); border-radius:var(--radius-sm);
  }
  .catarc-cerca:focus-visible { outline:2px solid var(--focus); outline-offset:1px; }
  .catarc-avviso {
    margin-bottom:10px; padding:9px 12px; font-size:12px; color:var(--warn-text);
    background:var(--warn-soft); border-radius:var(--radius-sm);
  }
  /* Le schede a scomparsa dei gruppi stanno dentro una card: niente ombra
     e niente margine sopra, o si vede una card dentro una card. */
  /* Bersagli da cantiere: 44px. Vale SOLO qui dentro — .ibtn e' di tutta
     l'app, e cambiarla da qui sarebbe un ritocco che nessuno ha chiesto. */
  .catarc-det-h .ibtn, .catarc-body .ibtn { min-height:44px; }
  .catarc-det .catarc-gr { margin:0 0 8px; box-shadow:none; }
  .catarc-det .catarc-gr:last-child { margin-bottom:0; }
  .catarc-gr summary .cnt { margin-left:0; }

  /* ------------------------------------------------------------------
     Passo «Materiali»: si apre fra il preventivo accettato e l'ordine.
     Righe alte e bersagli larghi: si compila in officina, non alla
     scrivania. I campi non scendono sotto i 16px sotto i 600px, o Safari
     iOS zooma la pagina e non torna piu' indietro.
     ------------------------------------------------------------------ */
  .mat-ovl {
    position:fixed; inset:0; z-index:100000; display:flex; align-items:center; justify-content:center;
    padding:14px; background:var(--velo); font-family:var(--font-ui);
  }
  .mat-card {
    display:flex; flex-direction:column; width:min(980px,100%); max-height:92vh;
    background:var(--surface); border:1px solid var(--line); border-radius:var(--radius-lg);
    box-shadow:var(--shadow-2); overflow:hidden;
  }
  .mat-head {
    display:flex; align-items:flex-start; gap:12px; padding:16px 18px;
    border-bottom:1px solid var(--line); background:var(--surface-2);
  }
  .mat-title { font-family:var(--font-display); font-size:18px; font-weight:600; color:var(--ink); }
  .mat-sub { margin-top:3px; font-size:12px; color:var(--ink-muted); max-width:64ch; line-height:1.5; }
  .mat-x {
    margin-left:auto; min-width:44px; min-height:44px; border:1px solid var(--line);
    background:var(--surface); color:var(--ink-muted); border-radius:var(--radius-xs); cursor:pointer;
  }
  .mat-body { flex:1 1 auto; min-height:0; overflow:auto; padding:16px 18px; }
  .mat-foot {
    display:flex; gap:10px; justify-content:flex-end; padding:14px 18px;
    border-top:1px solid var(--line); background:var(--surface-2);
  }

  .mat-tipo { margin-bottom:18px; padding:12px 14px; border:1px solid var(--line); border-radius:var(--radius); background:var(--surface-2); }
  .mat-tipo-h { display:flex; align-items:baseline; gap:10px; flex-wrap:wrap; margin-bottom:10px; }
  .mat-tipo-h b { font-size:14px; color:var(--ink); }
  .mat-mut { font-size:11.5px; color:var(--ink-muted); }
  .mat-vuoto { padding:8px 0 10px; font-size:12px; color:var(--ink-muted); }

  .mat-riga {
    display:flex; flex-wrap:wrap; align-items:center; gap:8px;
    padding:8px; margin-bottom:8px; background:var(--surface);
    border:1px solid var(--line); border-radius:var(--radius-sm);
  }
  .mat-riga select, .mat-riga input {
    min-height:44px; padding:8px 10px; font:inherit; font-size:16px; color:var(--ink);
    background:var(--surface); border:1px solid var(--line-strong); border-radius:var(--radius-xs);
  }
  .mat-riga .mat-art { flex:3 1 240px; min-width:0; }
  .mat-riga .mat-regola, .mat-riga .mat-per { flex:1 1 130px; min-width:0; }
  .mat-riga .mat-molt { flex:0 0 88px; width:88px; }
  .mat-riga select:focus-visible, .mat-riga input:focus-visible { outline:2px solid var(--focus); outline-offset:1px; }
  .mat-calc { flex:1 1 170px; display:flex; flex-direction:column; gap:1px; min-width:0; }
  .mat-calc b { font-size:15px; font-variant-numeric:tabular-nums; color:var(--ink); }
  .mat-calc small { font-size:11px; color:var(--ink-muted); }
  .mat-avv { color:var(--warn-text); }
  .mat-riga-avv { flex:1 1 100%; font-size:11.5px; }
  .mat-add { margin-top:2px; }
  .mat-tipo .ibtn, .mat-foot .ibtn, .mat-foot .btn-add { min-height:44px; }

  .mat-tot { margin-top:6px; padding:12px 14px; border:1px solid var(--line-strong); border-radius:var(--radius); background:var(--surface); }
  .mat-bann {
    margin:0 0 12px; padding:10px 12px; font-size:12.5px; line-height:1.5;
    color:var(--warn-text); background:var(--warn-soft); border-radius:var(--radius-sm);
  }
  .mat-tot .mat-bann { margin:12px 0 0; }
  .mat-bann-err { color:var(--err-text); background:var(--err-soft); }

  @media (hover:hover) {
    .mat-x:hover { color:var(--ink); border-color:var(--line-strong); }
  }
  @media (max-width:640px) {
    .mat-ovl { padding:0; }
    .mat-card { width:100%; max-height:100dvh; border-radius:0; border:0; }
    .mat-riga .mat-art, .mat-riga .mat-regola, .mat-riga .mat-per { flex:1 1 100%; }
    .mat-riga .mat-molt { flex:0 0 100px; }
    .mat-foot { flex-direction:column-reverse; }
    .mat-foot > * { width:100%; }
  }

  @media (max-width:640px) {
    .catarc-tiles { grid-template-columns:1fr; }
    .catarc-det-h { gap:8px; }
    .catarc-det-h b { flex:1 1 100%; }
  }

  @media (max-width:1100px) {
    .fen-admin-kpis { grid-template-columns:repeat(2,minmax(0,1fr)); }
    .fen-admin-grid { grid-template-columns:1fr; }
    .fen-admin-side { grid-template-columns:repeat(2,minmax(0,1fr)); }
  }
  @media (max-width:640px) {
    .fen-admin-head { display:block; }
    .fen-admin-actions { justify-content:flex-start; margin-top:14px; }
    .fen-admin-kpis,.fen-admin-side { grid-template-columns:1fr; }
    .fen-admin-kpi b { font-size:22px; }
    .fen-admin-secondary summary { flex-wrap:wrap; padding:10px 14px; }
    .fen-admin-secondary summary small { width:100%; margin-left:22px; }
    .fen-training-intro { grid-template-columns:1fr; }
    .fen-training-intro p,.fen-training-intro .ibtn { grid-column:1; grid-row:auto; }
  }

  /* ==========================================================
     FOCUS VISIBILE
     ----------------------------------------------------------
     Prima di questo blocco, in tutto il progetto :focus-visible
     compariva in DUE righe, tutte e due dentro Nova. Il token
     --focus-ring era definito e non usato da nessuno. Restavano
     senza alcun indicatore: ogni `button`, le voci di navigazione
     della barra laterale, .btn, .btn-add, .ibtn, .tiny, .logout,
     .hamb, tutto il preventivatore p2-*, e #loginForgot, che ha
     tabindex="0" e quindi si raggiunge da tastiera.
     In piu' c'erano 9 `outline:none`, alcuni senza rimpiazzo.

     Non e' una formalita' da spuntare: in cantiere si lavora con
     una mano sola e con la tastiera del telefono, e sapere dove si
     e' finiti col tab e' la differenza fra usare l'app e indovinare.

     :focus-visible e non :focus, cosi' l'anello compare a chi
     naviga da tastiera e non a ogni tocco del dito.
     ========================================================== */
  :focus-visible {
    outline: 2px solid var(--focus-ring, var(--action));
    outline-offset: 2px;
  }

  /* Sulle superfici scure l'anello non puo' essere --focus, che li'
     sparirebbe: serve --on-dark-accent, cioe' carta piena.
     Sono le stesse superfici che glass.css porta a inchiostro.
     Il minimo per un indicatore non testuale e' 3.00, e in cantiere si
     guarda lo schermo al sole: qui sotto il minimo non ci si va.
       carta #F2EFE9 su inchiostro #26262A .... 13.5  (ok)
       --focus #6F5316 su sabbia #EAE5DA ...... 5.72  (ok)
     NB: il FAB di Nova NON sta in questo elenco, anche se lui e' scuro:
     l'anello si disegna 2px FUORI dal bottone, quindi cade sul fondo
     della PAGINA, non sul bottone. Li' vale --focus. */
  /* .topbar e #sidebar SONO USCITE da questo elenco, e non per gusto: il
     commento qui sopra dice «le stesse superfici che glass.css porta a
     inchiostro», ed era vero. Non lo e' piu' da quando workbench.css le ha
     schiarite — #sidebar sta su --surface (bianco in Sabbia) e .topbar su
     --surface al 94%. Su quei fondi --accento-chiaro (#F8F4EC) fa 1,10:1:
     l'anello c'era, disegnato, e non si vedeva. Chi navigava col tab nella
     barra laterale non sapeva dove fosse finito.
     Tolte di qui ricadono sulla regola globale, cioe' --focus-ring: 6,17:1
     in Sabbia e 7,48:1 in Notturna, tutte e due sopra il 3,00 richiesto a un
     indicatore non testuale. Le altre cinque restano: quelle sono scure per
     davvero. */
  #login :focus-visible,
  .dash-hero :focus-visible,
  .nova-surface :focus-visible,
  #biolock :focus-visible,
  #pwreset :focus-visible {
    outline-color: var(--accento-chiaro, var(--on-dark-accent));
  }

  /* Campi di testo: l'anello si stacca di 1px per non impastarsi con il
     box-shadow che hanno gia' al :focus.
     DA SAPERE, perche' e' controintuitivo: su un campo di testo
     :focus-visible combacia SEMPRE, anche quando il fuoco arriva da un
     tocco o da un clic — e' l'euristica prevista dalla specifica, non un
     nostro errore. Quindi qui l'anello si vede anche al dito, al
     contrario dei bottoni. E' voluto tenerlo: per .righe input e per
     .irv-cell del modale catalogo l'anello e' l'UNICO indicatore
     decente, perche' li' il :focus cambia solo il colore del bordo. */
  input:focus-visible,
  select:focus-visible,
  textarea:focus-visible {
    outline-offset: 1px;
  }

  /* ==========================================================
     SAFE AREA (notch, Dynamic Island, home indicator)
     ----------------------------------------------------------
     Tutti e tre gli HTML dichiarano viewport-fit=cover, che estende
     la pagina sotto le zone di sistema di iPhone — ma
     env(safe-area-inset-*) non compariva NEMMENO UNA VOLTA in tutto
     il progetto. Risultato: la barra in alto finiva sotto la notch e
     il pulsante di Nova, fissato a 24px dal fondo, cadeva sotto la
     home indicator, che di px ne occupa 34.

     Tutte le regole qui sotto valgono 0 dove non c'e' notch, quindi
     su desktop e Android non cambia un pixel. Sono scritte con max()
     o come somma, mai come sostituzione, proprio per questo.
     ========================================================== */

  /* Barra superiore: cresce dell'altezza della notch invece di
     finirci sotto. L'altezza va aumentata insieme al padding, perche'
     con box-sizing:border-box il padding mangerebbe il contenuto. */
  .topbar {
    height: calc(56px + env(safe-area-inset-top));
    padding-top: env(safe-area-inset-top);
  }
  @media (max-width: 640px) {
    .topbar {
      height: auto;
      min-height: calc(54px + env(safe-area-inset-top));
      padding-top: calc(8px + env(safe-area-inset-top));
    }
  }

  /* Barra laterale: su mobile e' a tutta altezza e fissa, quindi il
     marchio in cima va sotto la notch e il fondo dell'elenco sotto la
     home indicator. */
  #sidebar {
    padding-top: env(safe-area-inset-top);
    padding-bottom: env(safe-area-inset-bottom);
  }

  /* Area di contenuto: un blocco in coda alto quanto il PALLINO DI NOVA,
     piu' la home indicator. Cosi' l'ultima riga di una tabella si riesce
     sempre a leggere e a toccare.
     LO SPAZIO C'ERA GIA' E ERA ALTO ZERO DOVE SERVIVA. Il commento diceva
     la cosa giusta ma riservava solo `env(safe-area-inset-bottom)`, che vale
     0 su desktop, su Android e su ogni iPhone in orizzontale — cioe' proprio
     dove il pallino e' piu' grande. #nova-fab e' position:fixed
     (nova-fab.js:52) e non occupa niente nel flusso: l'ultima riga degli
     elenchi Clienti e Preventivi gli finiva sotto e i suoi pulsanti non si
     raggiungevano piu', perche' la pagina era gia' in fondo. Un «Elimina»
     premuto li' apriva Nova (collaudo 2026-09-05, difetto D).
     La misura e' quella che il pallino si prende davvero: 24 di stacco + 72
     di disco, che sotto i 760px diventano 18 + 66 — la stessa soglia di
     nova-fab.js. Se un giorno cambia taglia, si cambia qui lo stesso numero.
     L'env() e' una SOMMA dentro un calc(), mai una sostituzione: su iPhone il
     pallino sale con la home indicator e lo spazio sale con lui.
     Fatto con ::after e non con padding-bottom perche' il padding cambia a
     ogni breakpoint (24/16/12px) e qui invece si aggiunge e basta, senza
     doverli riscrivere. */
  .content::after {
    content: "";
    display: block;
    height: calc(96px + env(safe-area-inset-bottom));
  }
  @media (max-width: 760px) {
    .content::after { height: calc(84px + env(safe-area-inset-bottom)); }
  }

  /* Pulsante di Nova e messaggi di conferma: sopra la home indicator. */
  #nova-fab {
    bottom: max(24px, calc(env(safe-area-inset-bottom) + 8px));
    right: max(24px, env(safe-area-inset-right));
  }
  @media (max-width: 760px) {
    #nova-fab {
      bottom: max(18px, calc(env(safe-area-inset-bottom) + 6px));
      right: max(18px, env(safe-area-inset-right));
    }
  }
  .toast {
    bottom: max(24px, calc(env(safe-area-inset-bottom) + 8px));
    right: max(24px, env(safe-area-inset-right));
  }
  /* I toast stanno in una colonna (#toast-stack): due nello stesso istante non
     si coprono piu'. La pila porta la safe area; il singolo toast, dentro la
     pila, torna nel flusso. L'errore resta finche' non lo chiudi. */
  #toast-stack {
    position: fixed;
    bottom: max(24px, calc(env(safe-area-inset-bottom) + 8px));
    right: max(24px, env(safe-area-inset-right));
    display: flex; flex-direction: column; align-items: flex-end; gap: 8px;
    z-index: 80; pointer-events: none;
  }
  #toast-stack .toast { position: static; bottom: auto; right: auto; pointer-events: auto; display: flex; align-items: center; gap: 8px; max-width: min(92vw, 420px); }
  #toast-stack .toast.toast-err { background: var(--err-soft); color: var(--err-text); border: 1px solid var(--err); }
  /* «avviso»: resta come l'errore, ma e' un'informazione (senza rete, dati del…). */
  #toast-stack .toast.toast-avviso { background: var(--warn-soft); color: var(--warn-text); border: 1px solid var(--warn); }
  #toast-stack .toast .toast-x { margin-left: 4px; min-width: 44px; min-height: 44px; background: transparent; border: 0; color: inherit; font-size: 22px; cursor: pointer; }
  /* Piede della barra laterale: il pulsante «operazioni non riuscite» compare
     solo quando ce ne sono. Testo nel colore d'errore, bersaglio 44px. */
  #sidebar .sb-foot-btn { margin-top: 6px; min-height: 44px; padding: 0 8px; border: 1px solid var(--err); border-radius: var(--radius-xs); background: var(--err-soft); color: var(--err-text); font: 600 12px/1 var(--font-ui); cursor: pointer; text-align: left; }
  /* La barra Backup / Ripristina / CSV e' un lavoro da scrivania: sotto i 760px
     traboccava dalla barra e schiacciava a zero il titolo della pagina. */
  @media (max-width: 760px) { .topbar #ord-trasv-bar { display: none; } }

  /* Overlay dei modali: il pannello non deve nascere sotto la notch. */
  .ovl {
    padding-top: max(24px, env(safe-area-inset-top));
    padding-bottom: max(24px, env(safe-area-inset-bottom));
  }
  /* 640px e NON 760px: e' il breakpoint dove index.html porta davvero
     .ovl a padding 12px. Averlo scritto 760 faceva perdere 12px di
     padding verticale ai modali fra 641 e 760px — tablet in verticale e
     finestre desktop strette. Quando si compensa una safe area bisogna
     ricopiare il breakpoint originale, non sceglierne uno vicino. */
  @media (max-width: 640px) {
    .ovl {
      padding-top: max(12px, env(safe-area-inset-top));
      padding-bottom: max(12px, env(safe-area-inset-bottom));
    }
  }

  /* Nova a tutto schermo su telefono: li' il pannello arriva ai bordi
     veri, quindi le safe area vanno messe sull'overlay. */
  @media (max-width: 560px) {
    .nova-ovl#nova-ovl {
      padding-top: env(safe-area-inset-top);
      padding-bottom: env(safe-area-inset-bottom);
    }
  }


  /* ==========================================================
     OVERLAY DI AUTENTICAZIONE (#biolock, #pwreset)
     ----------------------------------------------------------
     Fase 2. Erano 26 style= inline dentro supabase.js: nessuna
     regola poteva vincerci contro, perche' lo stile inline batte
     ogni foglio. Spostandoli qui diventano ridisegnabili, e per
     la prima volta si possono anche solo MISURARE.

     Le quattro correzioni fatte nel trasloco, tutte misurate:

     a) L'anello di focus. Sono superfici scure #26262A, ma non
        erano nell'elenco qui sopra: prendevano il blu, che su
        quel fondo fa 2.17 contro il minimo di 3.00 preteso per
        un indicatore. Ora prendono il periwinkle, 6.20.

     b) Le etichette dei campi erano #8B887F, cioe' 4.25 contro
        i 4.5 richiesti. Ora #939087, 4.72 - scelto apposta piu'
        smorto della nota esplicativa (#9C9990, 5.29), se no la
        gerarchia si appiattisce.

     c) #biolock non aveva overflow: il contenuto e' alto 383px e
        un iPhone in orizzontale ne ha ~320. Un elemento fixed
        senza overflow NON si scorre, quindi il tasto di sblocco
        finiva fuori e non lo si raggiungeva piu'. #pwreset ce
        l'aveva gia'; ora ce l'hanno tutti e due.
        E justify-content: safe center, non center: col semplice
        center il contenuto trabocca ANCHE da un contenitore
        scorribile, perche' il centraggio lo spinge sopra
        l'origine dello scorrimento. E' il difetto classico del
        flexbox centrato. Chi non conosce safe ignora la riga e
        resta al comportamento di prima.

     d) Zero env(safe-area-inset-*), con viewport-fit=cover
        dichiarato: su iPhone il link in fondo cadeva sotto la
        home indicator. La fase 1 aveva coperto .ovl, .nova-ovl,
        topbar, FAB e toast, ma non questi due, che nascono da JS.
     ========================================================== */
  .auth-ovl {
    position: fixed;
    inset: 0;
    z-index: 99999;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    justify-content: safe center;
    gap: 14px;
    overflow: auto;
    background: var(--shell);
    color: var(--on-dark-ink);
    font-family: var(--font-ui, system-ui, sans-serif);
    text-align: center;
    padding: max(32px, env(safe-area-inset-top))
             max(24px, env(safe-area-inset-right))
             max(32px, env(safe-area-inset-bottom))
             max(24px, env(safe-area-inset-left));
  }
  /* Il reset password sta SOPRA lo sblocco biometrico: si apre mentre
     quello e' ancora montato. */
  #pwreset.auth-ovl { z-index: 100000; gap: 11px; }

  .auth-titolo { font-size: 22px; font-weight: 600; letter-spacing: .14em; color: var(--on-dark-ink); }
  #pwreset .auth-titolo { font-size: 21px; }

  /* La linea di quota: due squadrette, due tratti e la dicitura in mezzo.
     E' lo stesso segno della schermata di accesso. */
  .auth-quota { display: flex; align-items: center; width: 100%; max-width: 230px; margin: 0 0 2px; }
  #pwreset .auth-quota { max-width: 260px; }
  .auth-quota-cap { width: 1.6px; height: 11px; background: var(--on-dark-accent); flex: none; }
  .auth-quota-linea { flex: 1; height: 1.4px; background: var(--on-dark-accent); }
  .auth-quota-testo {
    font-family: var(--font-mono, ui-monospace, monospace);
    font-size: 9.5px; letter-spacing: .16em; color: var(--on-dark-accent);
    padding: 0 10px; white-space: nowrap;
  }

  #biolock-msg { font-size: 13.5px; color: var(--on-dark-accent); max-width: 300px; line-height: 1.55; }
  .auth-nota { font-size: 13.5px; color: var(--on-dark-muted); max-width: 330px; line-height: 1.55; margin-bottom: 4px; }

  .auth-label {
    width: 100%; max-width: 320px; text-align: left;
    font-family: var(--font-mono, ui-monospace, monospace);
    font-size: 10.5px; letter-spacing: .1em; color: var(--on-dark-muted); text-transform: uppercase;
  }
  .auth-campo {
    width: 100%; max-width: 320px; padding: 13px 15px;
    border: 1.5px solid var(--on-dark-line); border-radius:var(--radius);
    background: var(--shell-2); color: var(--on-dark-ink); font-size: 14px; font-family: inherit;
  }
  #pwreset-msg { font-size: 12.5px; color: var(--on-dark-accent); max-width: 320px; line-height: 1.5; min-height: 17px; }

  /* I due bottoni avevano onmouseover/onmouseout inline: lo stato di
     passaggio del mouse e' materia di CSS, non di JS, e toglierli leva
     anche due gestori inline dalla superficie della CSP. */
  /* Carta piena su testo guscio, come i bottoni del pannello di Nova e per la
     stessa ragione: .auth-ovl e' SCURO in entrambi i temi, e --action li' non
     funziona. In Notturna --action e --on-dark-ink sono lo STESSO #F2EFE9:
     l'etichetta spariva dentro il bottone, 1,00:1 esatto. In Sabbia il testo si
     leggeva ma il bottone era inchiostro su inchiostro, 1,04:1 come forma. */
  .auth-btn {
    min-height: 52px; width: 100%; max-width: 320px; padding: 14px 22px;
    border: none; border-radius:var(--radius); background: var(--on-dark-ink); color: var(--shell);
    font-weight: 700; font-size: 15px; font-family: inherit;
    cursor: pointer; transition: background .14s;
  }
  @media (hover: hover) {
    .auth-btn:hover { background: var(--on-dark-muted); }
  }
  #pwreset .auth-btn { min-height: 50px; }

  .auth-btn-2 {
    width: 100%; max-width: 320px; min-height: 48px; padding: 13px 16px;
    background: var(--on-dark-soft); border: 1.5px solid var(--on-dark-line);
    border-radius:var(--radius); color: var(--on-dark-ink); font-weight: 500; font-size: 13.5px;
    font-family: inherit; cursor: pointer; transition: background .14s;
  }
  @media (hover: hover) {
    .auth-btn-2:hover { background: var(--on-dark-line); }
  }

  .auth-link {
    background: none; border: none; padding: 0; color: var(--on-dark-accent);
    font-size: 12px; font-family: inherit; text-decoration: underline;
    cursor: pointer; margin-top: 4px;
  }
  #pwreset .auth-link { margin-top: 2px; }


  /* ==========================================================
     FASE 3 - DENSITA' DA OFFICINA
     ----------------------------------------------------------
     Nessuna decorazione nuova: lo stesso linguaggio, piu' stretto
     dove si legge e piu' largo dove si tocca. Undici elenchi
     tabellari passano di qui, quindi ogni regola e' scritta per
     non rompere nulla altrove.

     QUATTRO TRAPPOLE, tutte trovate PRIMA di scrivere il codice e
     tutte evitate qui sotto:

     1. La shorthand `padding` sulla topbar CANCELLA la safe area.
        In questo stesso layer c'e' gia'
          .topbar{ height:calc(56px + env(safe-area-inset-top));
                   padding-top:env(safe-area-inset-top) }
        Il selettore col flag ha specificita' (0,2,1) contro (0,1,0)
        e sta nello stesso layer: vince. Scritto come shorthand
        riazzererebbe padding-top lasciando l'altezza aumentata, e
        su iPhone con Dynamic Island hamburger e bottoni tornerebbero
        DENTRO la zona di sistema - il difetto che la fase 1 aveva
        chiuso. Qui si tocca solo l'orizzontale, con le longhand.
        Vale per tutta la passata: mai una shorthand `padding` su
        .topbar, .ovl, #nova-fab, .toast.

     2. Il padding del contenuto su telefono ALLARGA invece di
        stringere. index.html porta .content a 16px sotto 760px e a
        12px sotto 640px, ma sta nel layer base: il layer app vince a
        ogni larghezza. Un 16px 20px secco avrebbe portato il margine
        orizzontale da 12 a 20px su un telefono da 360px, e con
        .kpis a repeat(auto-fit,minmax(160px,1fr)) i due riquadri
        affiancati sarebbero caduti in colonna: la dashboard
        RADDOPPIA in altezza, l'opposto di quello che si vuole.
        Percio' qui i tre scaglioni sono riscritti tutti e tre.

     3. La zebra si scontrava con l'evidenziazione al passaggio del
        mouse. .dt tr:hover td dipinge la CELLA, quindi vince sulla
        riga e il meccanismo regge - ma #FAF7F1 contro #FBF9F5 danno
        1.02 di stacco, cioe' su meta' delle righe l'evidenziazione
        sparirebbe. E #FBF9F5 e' gia' il fondo dell'intestazione.
        Qui la scala e' rifatta: carta #FFFFFF, riga pari #FAF8F3,
        passaggio #EFEAE0 (1.20 sulla carta, 1.13 sulla riga pari),
        intestazione #F2EFE9. Il testo resta sopra 12.5 ovunque.

     4. L'intestazione appiccicosa NON e' qui, ed e' una scelta.
        Le 24 tabelle stanno tutte dentro .tbl-wrap, che ha
        overflow-x:auto: per la specifica basta un asse non visible
        perche' l'altro diventi auto, quindi .tbl-wrap E' il
        contenitore di scorrimento a cui position:sticky si
        aggancia - e non ha altezza, quindi non scorre mai. La
        regola sarebbe stata INERTE, e in piu' il fondo bianco che
        serve a una th agganciata avrebbe cancellato la tinta che
        oggi la distingue. Farla davvero vuol dire dare a .tbl-wrap
        una max-height e passare a border-collapse:separate, perche'
        in collapse il bordo appartiene alla tabella e non viaggia
        con la cella. E' un cambio di scorrimento, non di densita':
        va fatto a parte. Il progetto ha gia' l'esempio giusto in
        prev2-import-catalogo.js, .irv-tbl-wrap con max-height:300px.

     E una cosa da sapere su .userbadge: su desktop e' il punto in cui
     si vede con quale account si e' entrati; su telefono NON era
     nascosto (la regola di index.html che lo credeva veniva scavalcata
     da workbench.css) e a 375px lasciava al titolo di pagina 26px
     (collaudo 2026-09-02). Dal 2026-09-03 sotto i 760px il badge lascia
     la barra e l'account sta nel cassetto (#sb-account), scritto dallo
     stesso aggiornaBadgeAccount().
     ========================================================== */

  /* --- contenuto: gli stessi tre scaglioni di index.html --- */
  .content { padding: 16px 20px; }
  @media (max-width: 760px) { .content { padding: 12px 16px; } }
  @media (max-width: 640px) { .content { padding: 12px; } }

  /* --- intestazione della dashboard --- */
  .dash-hero { padding: 16px 20px 14px; }
  .dash-hero h1 { font-size: 24px; margin: 0; }
  .dash-hero p { margin: 2px 0 0; }

  /* --- riquadri KPI: piu' bassi, il numero resta il protagonista --- */
  .kpis { gap: 10px; margin-top: 14px; }
  .kpi { padding: 10px 14px; }
  .kpi .num { font-size: 26px; line-height: 1.05; }
  .kpi .lab { font-size: 9.5px; margin-top: 2px; }

  /* --- schede --- */
  .card { margin-top: 14px; }
  .card-h { padding: 12px 16px; }
  .page-h { font-size: 17px; }

  /* --- ricerca: larga quanto serve, non quanto la scheda --- */
  .search { padding: 10px 16px; }
  .search input { max-width: 260px; min-height: 40px; }

  /* --- tabelle: righe piu' basse, zebra leggibile ---
     Scopate a table.dt e non a .dt: index.html:573 usa <div class="dt"> per la
     riga della data nell'intestazione della dashboard. Oggi non farebbe danno
     (ogni regola chiede th/td/tr, che un div non ha) ma la stessa classe per
     due cose diverse e' una trappola che va disinnescata, non scoperta. */
  table.dt th { padding: 8px 16px; background: var(--surface-2); }
  table.dt td { padding: 7px 16px; }
  table.dt tbody tr:nth-child(even) { background: var(--bg); }
  /* Sulla CELLA, come la regola che sostituisce: cosi' batte la zebra.
     E sotto @media (hover: hover), come vuole DESIGN-SYSTEM.md: al tocco
     :hover resta incollato dopo il tap, e questa evidenziazione e' piu'
     marcata della precedente, quindi si noterebbe di piu'. Dove il puntatore
     non c'e', la cella torna trasparente e lascia vedere la zebra della riga:
     e' anche il modo per neutralizzare la regola di base, che non e'
     protetta e continuerebbe a incollarsi. */
  @media (hover: hover) {
    table.dt tr:hover td { background: var(--action-soft); }
  }
  @media (hover: none) {
    table.dt tr:hover td { background: transparent; }
  }

  /* --- bersagli tattili: qui si va in senso opposto, piu' grandi ---
     In cantiere si tocca con le dita, spesso con i guanti. */
  .btn-add {
    min-height: 44px;
    padding: 0 18px;
    display: inline-flex;
    align-items: center;
  }
  .ibtn { min-height: 36px; min-width: 36px; }
  /* Tranne dentro l'editor delle righe del preventivo: li' la .ibtn e' la "x"
     che cancella una riga, non un'azione principale, e sta dentro una griglia
     di immissione dove servono PIU' righe a schermo, non meno. Misurato: col
     bersaglio a 36px la riga passa da 36,5 a 44,5px e il modale da 580 a 597.
     E' l'unico punto dove la densita' e l'ergonomia tirano in direzioni
     opposte, e qui vince la densita'. */
  .righe .ibtn { min-height: 0; min-width: 0; }
  /* La «x» che cancella una riga e quella che chiude il modale erano 25 e 21px:
     sotto il minimo dell'app (DESIGN-SYSTEM.md §5, 44px). La densita' si
     recupera sulle righe, non sul pulsante che butta via il lavoro
     (collaudo 2026-09-02, difetto 42). */
  .righe .x, .modal-h .x { min-width: 44px; min-height: 44px; display: inline-flex; align-items: center; justify-content: center; }
  .fg label .req { color: var(--err-text); font-weight: 700; margin-left: 2px; }
  .tiny,
  .logout { min-height: 36px; }
  .nav a { padding: 12px 14px; font-size: 14px; }

  /* --- barra in alto: SOLO l'orizzontale, vedi la trappola 1 --- */
  .topbar { padding-left: 14px; padding-right: 14px; }


  /* ==========================================================
     CICLO design-loop, giro 1 — cinque problemi, per impatto
     ----------------------------------------------------------
     Non sono difetti della passata di densita': sono marcatori che
     il guscio si porta dietro e che DESIGN-SYSTEM.md ora vieta. Il
     ciclo impone di guardare TUTTA la pagina, non il blocco appena
     scritto: spostare un marcatore non e' rimuoverlo.
     ========================================================== */

  /* 1. Il bottone primario era blu pieno, cioe' l'elemento piu' forte
        della pagina. Il blu e' l'unico colore di stato che il prodotto
        possiede (link, focus, riga attiva): se riempie il rettangolo
        piu' grande non segnala piu' niente. DESIGN-SYSTEM §2: grafite,
        con l'accento come filetto di 3px sul filo inferiore. E
        :active cambia massa, non posizione - il filetto va a 8px. */
  /* --shell e' il colore del GUSCIO (barra laterale, barra in alto): su una
     scheda chiara e' un bottone che non si stacca, e in Notturna e' perfino
     piu' scuro della scheda che lo contiene — 1,19:1 come forma, contro i 3:1
     che serve a un elemento non testuale. L'azione primaria e' --action, che
     fra i due temi si inverte da sola. */
  .btn-add,
  .btn-save {
    background: var(--action);
    color: var(--action-on);
    border-radius:var(--radius-sm);
    box-shadow: inset 0 -3px 0 var(--alone);
  }
  .btn-add:active,
  .btn-save:active {
    box-shadow: inset 0 -8px 0 var(--alone);
    transform: none;
  }

  /* 2. La voce attiva del menu' era una pillola blu piena CON alone
        colorato sotto: due marcatori vietati insieme (§8.5 e §8.9).
        E su fondo scuro l'accento dev'essere periwinkle, non blu, che
        li' fa 2.17 (§3.1). Il filetto a sinistra esiste gia' come
        ::before per il passaggio del mouse: qui si accende e basta. */
  .nav a.active {
    background: var(--on-dark-soft);
    color: var(--on-dark-ink);
    box-shadow: none;
    border-radius:var(--radius-sm);
  }
  .nav a.active::before {
    transform: scaleY(1);
    background: var(--on-dark-accent);
  }

  /* 3. Piu' grande era piu' PESANTE, l'inverso del principio della §2.
        .page-h stava a 800, che §8.6 nomina come la firma piu' visibile
        della pagina generata. E il mono chiesto al 700 non esiste - la
        famiglia ha 400/500/600 - quindi ripiegava in silenzio sul 600:
        meglio chiedere quello che c'e'. */
  .page-h { font-weight: 500; }
  .top-title { font-weight: 500; }
  .kpi .num { font-weight: 500; }
  table.dt th { font-weight: 600; }

  /* 4. Nove spaziature fuori dalla griglia 8px, tutte introdotte dalla
        passata di densita': 14, 10 e 7. La griglia e' 4·8·12·16·24. */
  .dash-hero { padding: 16px 20px; }
  .kpis { gap: 8px; margin-top: 16px; }
  .kpi { padding: 12px 16px; }
  .card { margin-top: 16px; }
  .search { padding: 12px 16px; }
  table.dt td { padding: 8px 16px; }
  .nav a { padding: 12px 16px; }

  /* GIRO 2 — un solo problema, e i marcatori sono calati da 5 a 1.
        "Esci" era rosso #C32A2A, che DESIGN-SYSTEM.md §3 assegna a
        "Errore / urgenza". Uscire non e' un errore: e' un'azione
        secondaria come "Ripristina dati". Finche' il bottone primario
        era blu pieno il rosso non si notava; calmato quello, e'
        diventato l'elemento piu' forte della pagina - un caso da
        manuale di gerarchia che si sposta quando si toglie qualcosa.
        Ora prende la stessa veste degli altri comandi della barra, e
        il rosso torna disponibile per quello che deve segnalare. */
  .logout {
    background: var(--on-dark-soft);
    border-color: var(--on-dark-line);
    color: var(--on-dark-ink);
  }
  @media (hover: hover) { .logout:hover { background: var(--on-dark-line); } }
  /* Sotto i 760px workbench.css nascondeva «Esci» insieme a «Ripristina
     dati»: sul telefono non esisteva NESSUN modo di uscire (collaudo del
     2026-09-02). Ora sparisce solo «Ripristina dati», ed «Esci» prende il
     bersaglio minimo dell'app: 44px, con i guanti. Specificita' a tre
     classi perche' workbench.css e' collegato dopo, nello stesso layer. */
  @media (max-width: 760px) {
    .topbar .top-right .logout {
      display: inline-flex;
      align-items: center;
      min-height: 44px;
      padding: 0 14px;
      font-size: 13px;
    }
    /* Il badge account lascia la barra al titolo (fase 11, 2026-09-03):
       tre classi, perche' workbench.css e' collegato dopo nello stesso layer. */
    .topbar .top-right .userbadge { display: none; }
    /* :not([hidden]) alza la specificita' sopra la regola base qui sotto,
       che viene DOPO nel foglio e altrimenti vincerebbe. */
    #sidebar .sb-account:not([hidden]) { display: flex; }
  }
  /* L'account nel cassetto: lo stesso disegno del badge, sotto il marchio.
     Su desktop non si mostra (il badge in barra basta). */
  #sidebar .sb-account {
    display: none;
    align-items: center;
    gap: 9px;
    margin: 4px 12px 10px;
    padding: 8px 10px;
    border: 1px solid var(--line);
    border-radius: var(--radius-xs);
    background: var(--surface-2);
    color: var(--ink);
  }
  #sidebar .sb-account[hidden] { display: none; }
  #sidebar .sb-account > span:last-child { display: flex; flex-direction: column; line-height: 1.15; min-width: 0; }
  #sidebar .sb-account small { color: var(--ink-muted); font-size: 9.5px; text-transform: uppercase; letter-spacing: .08em; }
  #sidebar .sb-account b { font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  #sidebar .sb-account .sb-account-tipo { color: var(--ink-muted); font-size: 11px; }

  /* 5. Testo di servizio a 9,5 e 10,5px: sotto la soglia dei 13px che
        la lente del cantiere chiama difetto, e sotto la misura che il
        sistema stesso fissa per questo ruolo (13px mono). Si legge con
        i guanti, al sole, con una mano. */
  .kpi .lab { font-size: 12px; letter-spacing: .12em; }
  table.dt th { font-size: 12px; letter-spacing: .08em; }


  /* ==========================================================
     IL MARCHIO — il toro di InsTaurus
     ----------------------------------------------------------
     E' una MASCHERA, non un'immagine: il colore lo mette
     `background-color: currentColor`, quindi il segno eredita il
     colore del testo che lo circonda e funziona identico su fondo
     chiaro, su fondo scuro e in stampa. Un <img> avrebbe richiesto
     un file per ogni tinta.

     TRE DISEGNI, NON UNO RIMPICCIOLITO. Sotto una certa dimensione un
     tratto sottile si chiude e il segno diventa una macchia: allora il
     tratto ingrassa e i dettagli se ne vanno, uno alla volta.

       >= 40px   tratto 24   profilo + occhi + CORNA
       20-39px   tratto 28   profilo + CORNA
       <= 19px   tratto 32   solo profilo

     Le corna sono la geometria che il prodotto spedisce ovunque — favicon,
     icone, cartolina social, Nova, barra della landing, e il render 3D
     della testata, che e' cotto nei .webp e non si rigenera. Fino ad
     Alabastro questi token erano l'unico posto con un altro animale:
     senza corna e con un muso rettangolare che non esisteva altrove.
     Le bande sono quelle VERE: il segno predefinito e' 20px e prende il
     medio, non il piccolo. Prima la banda del medio diceva 24-39 e la
     misura in uso ci cadeva fuori.

     Le tre varianti hanno lo stesso viewBox, quindi il segno non balla
     quando si passa da una all'altra.

     NB: qui il `#000` dentro le data-URI non e' vernice. Una maschera
     CSS usa il canale ALFA: conta dove c'e' disegno e dove no, non di
     che colore e'. Tokenizzarlo non avrebbe senso e romperebbe il segno.
     ========================================================== */
  .instaurus-mark {
    display: inline-block; flex: none;
    width: 20px; height: 20px;
    background-color: currentColor;
    -webkit-mask: var(--marchio-medio) center / contain no-repeat;
            mask: var(--marchio-medio) center / contain no-repeat;
  }
  .instaurus-mark.grande { width: 44px; height: 44px;
    -webkit-mask-image: var(--marchio-grande); mask-image: var(--marchio-grande); }
  .instaurus-mark.piccolo { width: 16px; height: 16px;
    -webkit-mask-image: var(--marchio-piccolo); mask-image: var(--marchio-piccolo); }

  /* ==========================================================
     IL LOGOTIPO
     ----------------------------------------------------------
     La T maiuscola in mezzo alla parola non e' un vezzo: separa
     "Ins" da "Taurus" senza uno spazio, e il peso leggero e la
     trasparenza sulla prima meta' fanno il resto. Il lettore vede
     "InsTAURUS" prima ancora di leggerlo.
     ========================================================== */
  /* IL LOGOTIPO E UNO SOLO, ed e quello della landing: Instrument Sans 600,
     maiuscolo, tracking 0.16em, con la T nel rosso di marca. Prima l app ne
     aveva un altro — Fraunces, con "Ins" a peso 300 e opacita 45% — quindi chi
     passava dalla landing al login vedeva due parole diverse per lo stesso
     nome. Le misure sono quelle di landing.html (.logo-testo e .logotipo). */
  .wordmark {
    font-family: var(--font-ui); font-weight: 600;
    text-transform: uppercase; letter-spacing: .16em;
    white-space: nowrap;
  }
  .wordmark__ins { font-weight: 600; opacity: 1; }
  /* Il tracking lascia spazio anche DOPO l ultima lettera: il margine negativo
     lo recupera, se no la parola non e centrata nel suo spazio — e a occhio si
     vede. E la stessa correzione di .logo-parola sulla landing.
     inline-block serve anche a ::first-letter, che vuole un contenitore di
     blocco: e il modo di colorare la T senza toccare il markup in quattro
     posti, due dei quali sono stringhe dentro supabase.js. */
  .wordmark__taurus { font-weight: 600; display: inline-block; margin-right: -.16em; }
  .wordmark__taurus::first-letter { color: var(--marchio-occhio); }
  /* Sul guscio scuro il rosso di marca farebbe 2,17:1 e sparirebbe. Non si
     rinuncia alla T: si usa il suo gemello chiaro, --marchio-occhio-chiaro,
     che sullo stesso fondo fa 6,47 e nel punto piu chiaro del gradiente 5,77.
     Il -webkit-text-fill-color va ridichiarato perche .sb-word lo forza a
     --on-dark-ink (glass.css) e vincerebbe sul color. */
  #sidebar .wordmark__taurus::first-letter,
  .auth-ovl .wordmark__taurus::first-letter {
    color: var(--marchio-occhio-chiaro);
    -webkit-text-fill-color: var(--marchio-occhio-chiaro);
  }

  /* ==========================================================
     SELETTORE DEL TEMA (Impostazioni > Aspetto)
     ----------------------------------------------------------
     Tre stati affiancati. Le regole che contano:

     1. LO STATO ATTIVO NON PASSA DAL SOLO COLORE. C'e' un segno di
        spunta e il testo in grassetto: chi non distingue le tinte
        vede lo stesso quale opzione e' scelta. Il fondo pieno e'
        conferma, non informazione.
     2. TASTIERA. E' un radiogroup vero: una sola tabulazione entra
        nel gruppo, poi le frecce spostano E scelgono. Il tabindex
        gira sull'opzione attiva (roving tabindex).
     3. BERSAGLI DA GUANTO. min-height 56px: si tocca con una mano
        sola, senza mirare.
     4. Su schermo stretto le tre opzioni si impilano invece di
        stringersi fino a tagliare le etichette.
     ========================================================== */
  .imp-tema { display: flex; gap: 8px; flex-wrap: wrap; }
  .imp-tema-op {
    flex: 1 1 150px; min-height: 56px;
    display: flex; align-items: center; gap: 10px;
    padding: 10px 14px; cursor: pointer; text-align: left;
    font: inherit; color: var(--ink);
    background: var(--surface);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    transition: background var(--dur-1) var(--ease-out),
                border-color var(--dur-1) var(--ease-out);
  }
  .imp-tema-op.attiva {
    background: var(--action); color: var(--action-on);
    border-color: var(--action);
  }
  .imp-tema-segno { width: 1em; flex: none; font-size: 15px; line-height: 1; }
  .imp-tema-testo { display: flex; flex-direction: column; gap: 1px; }
  .imp-tema-op b { font-size: 13.5px; font-weight: 600; }
  .imp-tema-nota { font-size: 11.5px; color: var(--ink-muted); }
  .imp-tema-op.attiva .imp-tema-nota { color: var(--action-on); opacity: .72; }

  /* :hover solo dove il puntatore esiste davvero: su touch resterebbe
     incollato dopo il tap, e l'opzione sembrerebbe scelta senza esserlo. */
  @media (hover: hover) {
    .imp-tema-op:hover { border-color: var(--action); }
    .imp-tema-op.attiva:hover { background: var(--action-strong); }
  }
  .imp-tema-op:focus-visible {
    outline: 2px solid var(--focus); outline-offset: 2px;
  }

  @media (max-width: 520px) {
    .imp-tema-op { flex: 1 1 100%; }
  }

  /* ================================================================
     IL KIT DI ALABASTRO — ricette, non componenti
     ----------------------------------------------------------------
     Il mockup porta quattordici @keyframes. Meta' esistono gia' e non
     si riscrivono: nova.js ha nova-respiro (= breathe), nova-eco-dentro
     e -fuori (= halo), nova-eq (= eq), nova-pop, nova-fade; landing.html
     disegna le quote da se' con IntersectionObserver (= dash) e fa
     scorrere il nastro con rAF (= marquee), che a 60fps costa meno di
     un'animazione CSS su una striscia lunga il doppio dello schermo.

     Qui stanno le tre che mancavano davvero, scritte come RICETTE:
     una classe da appiccicare, non un componente da montare.

     LE GUARDIE CI SONO GIA'. @layer guardie in instaurus-tokens.css
     spegne ogni animazione sotto prefers-reduced-motion, e arriva anche
     al CSS iniettato a runtime. Qui non si ripete: si evita solo di
     scrivere ricette il cui stato FINALE dipenda dall'animazione, se no
     con il movimento ridotto l'elemento resta a meta' strada. Per questo
     ognuna parte da uno stato gia' leggibile: nessuna scrive opacity:0 nel
     foglio, il nascondere e compito dello script che poi rivela.
     ================================================================ */

  /* --- comparsa: sale ed entra. Da abbinare a un IntersectionObserver
         che aggiunge .vista, come fa gia' landing.html con .comparsa. --- */
  .sale.vista  { animation: al-sale .5s var(--ease-out) both; }
  /* Lo stato di RIPOSO e visibile: il nascondere lo fa lo script, come a
     landing.html:1363. Senza JS si vede tutto. ANIMATIONS.md regola 7. */
  @keyframes al-sale { from { opacity: 0; transform: translateY(22px); }
                       to   { opacity: 1; transform: none; } }

  /* --- schiude: per le schede che compaiono in griglia. Piu' corta della
         precedente perche' ne compaiono tante insieme, e sommate diventano
         un'attesa invece di un gesto. --- */
  .schiude.vista { animation: al-schiude .38s var(--ease-out) both; }
  /* Stesso patto: nessun opacity:0 scritto nel foglio. */
  @keyframes al-schiude { from { opacity: 0; transform: scale(.94); }
                          to   { opacity: 1; transform: none; } }

  /* --- ondeggia: il respiro lento di un elemento decorativo (le lettere
         giganti della landing). Alternato, cosi' non torna mai di scatto
         al punto di partenza. --- */
  .ondeggia { animation: al-ondeggia 7s ease-in-out infinite alternate; }
  @keyframes al-ondeggia { from { transform: none; }
                           to   { transform: rotate(4deg) scale(1.06); } }

  /* --- I NUMERI CHE CONTANO — la ricetta c'e', l'uso e' una scelta.
     Alabastro fa salire le cifre da zero con @property + counter(). Sta
     scritta qui perche' funzioni quando serve, con DUE avvertenze che il
     mockup non paga:

     1) Il numero finisce in ::after, e il contenuto generato NON entra
        in modo affidabile nell'albero di accessibilita': un lettore di
        schermo puo' leggere una scheda "Preventivi" senza il suo numero.
        Chi la usa deve mettere il valore anche in aria-label.
     2) Senza @property (Safari sotto la 16.4) una custom property non e'
        interpolabile e l'animazione non parte. Qui, a differenza del
        mockup, --num vale GIA' --to: dove @property manca il numero si
        vede giusto e fermo, invece di restare a zero. E' esattamente il
        difetto che l'autore del mockup aveva incontrato ("i contatori
        mostrano 0") e che si evita partendo dalla fine, non dall'inizio.

     Uso:  <b class="conta" style="--to:42" aria-label="42"></b>          */
  .conta { --num: var(--to, 0); counter-reset: n var(--num); }
  .conta::after { content: counter(n); }
  .conta { animation: al-conta .9s .15s both var(--ease-out); }
  @keyframes al-conta { from { --num: 0; } to { --num: var(--to, 0); } }

  /* --- LA RIGA DEI FILTRI dell'elenco articoli (pagina Catalogo).
     Una riga sola: la ricerca libera, poi i menu che tagliano per fornitore,
     tipo e modello. Flex e non griglia perche' i menu SPARISCONO quando non
     offrono una scelta vera (un fornitore solo non e' un filtro): con una
     griglia a colonne fisse lascerebbero un buco, cosi' la riga si richiude.
     Nessuna media query: i menu vanno a capo da soli quando non ci stanno. --- */
  .catimp-filtri { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
  .catimp-filtri input { flex: 2 1 220px; min-width: 0; max-width: none; }
  .catimp-filtri select {
    flex: 1 1 150px; min-width: 0; min-height: 42px; padding: 9px 12px;
    border: 1.5px solid var(--line-strong); border-radius: var(--radius-sm);
    background: var(--surface); color: var(--ink); font: inherit;
  }
  .catimp-filtri select:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }
  /* «Togli i filtri»: compare solo a filtri accesi, e non si allarga come i
     menu — con i guanti serve un bersaglio fermo, non uno che cambia misura. */
  .catimp-filtri .catimp-reset {
    flex: 0 0 auto; min-height: 42px; padding: 9px 14px; cursor: pointer;
    border: 1.5px solid var(--line-strong); border-radius: var(--radius-sm);
    background: var(--surface-2); color: var(--ink); font: inherit;
  }
  @media (hover: hover) { .catimp-filtri .catimp-reset:hover { border-color: var(--action); } }
  .catimp-filtri .catimp-reset:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }


  /* ==========================================================
     IL MARCHIO E' UN TASTO, E IL BADGE DICE CHE ACCOUNT SEI
     ----------------------------------------------------------
     Due cose piccole nella stessa barra, quindi un blocco solo.

     Perche' i selettori portano #sidebar e .topbar: workbench.css
     e' anch'esso @layer app ed e' caricato DOPO questo foglio
     (index.html), quindi a parita' di specificita' vince lui e
     .sb-logo / .userbadge stanno proprio li'. L'id alza la
     specificita' quanto basta. La risposta NON e' !important.
     ========================================================== */

  /* .sb-logo e' diventata un <a>: qui si rimette solo cio' che
     l'<a> si porta dietro e che il marchio non vuole (la
     sottolineatura, la tinta di sistema), piu' gli stati.
     Misura e colore del segno restano di workbench.css.
     Bersaglio tattile: .sb-logo ha 22px di padding sopra e 18
     sotto attorno a un segno da 28px, cioe' ~68px di altezza
     sull'intera larghezza della barra. Sopra i 44px del minimo
     (DESIGN-SYSTEM §5): non serve allargare niente, serve solo
     che il bersaglio sia il contenitore e non il segno. */
  #sidebar .sb-home {
    text-decoration: none;
    color: inherit;
    border-radius: var(--radius-sm);
    /* Il fondo, e nient'altro. Il marchio e' l'ancora di
       orientamento della schermata: una che si sposta al
       passaggio del mouse smette di essere un'ancora
       (ANIMATIONS.md). Niente sollevamento, niente alone. */
    transition: background-color var(--dur-1) var(--ease-out);
  }
  @media (hover: hover) {
    #sidebar .sb-home:hover { background: var(--action-soft); }
  }
  #sidebar .sb-home:active { background: var(--line); }
  /* L'anello lo disegna la regola globale :focus-visible. Qui si
     porta solo DENTRO il riquadro: la testata ha un bordo
     inferiore e un anello disegnato 2px in fuori ci finirebbe
     sopra, sfrangiato. */
  #sidebar .sb-home:focus-visible { outline-offset: -3px; }

  /* IL NOME DELL'UTENTE ERA INVISIBILE, e va sistemato prima di scrivergli
     qualcosa sotto: un tipo di account illeggibile non serve a nessuno.
     Chi vinceva era glass.css:107 — `.userbadge b{ color:var(--on-dark-ink) }`
     in @layer tema — scritta quando il badge stava sul guscio SCURO, dove
     carta su inchiostro era la scelta giusta. workbench.css ha poi portato
     .userbadge su --surface-2, che in Sabbia e' #F6F1E8: il <b> restava
     #F8F4EC, cioe' 1,03:1. Il nome c'era, nel DOM, e semplicemente non si
     vedeva. Nessun controllo poteva accorgersene: contrasti.js verifica le
     coppie di TOKEN, non quello che il markup finisce per accostare.
     Non si corregge in glass.css (li' la regola e' giusta per il guscio scuro
     che quel foglio descrive) e nemmeno nel <style> di index.html, che sta in
     @layer base e perde comunque contro @layer tema. Si corregge QUI, in
     @layer app, che viene dopo tutti e due. `inherit` e non --ink: cosi' il
     nome segue il colore del badge qualunque esso diventi.
     --ink su --surface-2 fa 14,74:1 in Sabbia e 16,85:1 in Notturna. */
  .topbar .userbadge b { color: inherit; }

  /* Il gemello in topbar, per il telefono. Fino a 900px la barra
     laterale e' un cassetto chiuso: il marchio in .sb-logo esiste
     ma costa due tocchi. Qui il ritorno al sito e' a portata di
     pollice, accanto all'hamburger, dove il marchio sta di solito.
     Sopra i 900px la barra laterale e' fissa e mostra gia' il suo:
     due porte per la stessa stanza, accanto, sono rumore. */
  .topbar .top-home { display: none; }
  @media (max-width: 900px) {
    .topbar .top-home {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      /* 44px pieni, il minimo dell'app (DESIGN-SYSTEM §5). Il segno
         dentro ne occupa 24: il bersaglio e' il riquadro, non il toro.
         flex-shrink a 0 perche' la topbar e' gia' stretta e questo e'
         l'ultimo elemento che deve cedere. */
      width: 44px;
      height: 44px;
      flex: 0 0 auto;
      border-radius: var(--radius-sm);
      color: var(--ink);
      text-decoration: none;
      transition: background-color var(--dur-1) var(--ease-out);
    }
    .topbar .top-home .instaurus-mark { width: 24px; height: 24px; }
    @media (hover: hover) {
      .topbar .top-home:hover { background: var(--action-soft); }
    }
    .topbar .top-home:active { background: var(--line); }
  }

  /* IL TABLET VERTICALE VEDEVA IL MARCHIO MA NON CHI ERA.
     workbench.css:105 nasconde .userbadge fra 761 e 900px. Nasce col
     blocco che trasforma il menu in cassetto sui tablet verticali, ed
     era per restituire larghezza alla barra: a 768 con la sidebar
     fissa da 210px restavano 558px al gestionale.
     Il risultato pero' era che un telefono da 375px mostrava il tipo
     di account e un iPad da 768 — il doppio dello spazio — no. E li'
     restavano in barra «Ripristina dati» e «Esci», mentre a sparire
     era l'unica cosa che dice CHI sei.
     .topbar .userbadge e non .userbadge: workbench.css e' anch'esso
     @layer app e viene DOPO, quindi a parita' di specificita' vince
     lui. La risposta non e' !important.

     IL POSTO LO CEDE «Ripristina dati». Misurato a 768 con la barra
     trasversale gia' iniettata (si aggiunge DOPO l'accesso, e prima
     che arrivi le misure dicono il falso): col badge la topbar
     chiede 807px in 768, cioe' 39 della barra Backup/Ripristina/CSV
     finirebbero fuori schermo. Nascondendo «Ripristina dati» — 80px
     — si torna esatti a 768, debordo zero, e la barra resta intera.
     E' il candidato giusto a cedere: sotto i 760px e' GIA' nascosto
     da workbench.css:95, quindi qui non si inventa una regola nuova,
     si estende quella che c'e'; e ricarica il seed dimostrativo
     cancellando il lavoro fatto in locale, cioe' e' l'ultima cosa
     che serve sotto mano a chi e' in cantiere. «Esci» resta. */
  @media (min-width: 761px) and (max-width: 900px) {
    .topbar .userbadge { display: flex; }
    .topbar .top-right .tiny { display: none; }
  }

  /* La terza riga del badge: il TIPO di account, sotto il nome.
     --ink-muted su --surface-2 fa 5,28:1 in Sabbia e 7,16:1 in
     Notturna — si legge al sole senza rubare il primo posto al
     nome. Nessuna tinta propria per «Superadmin»: a distinguerlo
     e' la parola. L'oro e' Nova e solo Nova, e --warn / --err
     sono STATI, non identita': colorare di rosso un
     amministratore direbbe «qualcosa non va». */
  .topbar .userbadge-tipo {
    color: var(--ink-muted);
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: .06em;
    white-space: nowrap;
  }
  /* Prima che il ruolo sia noto la riga e' vuota: senza questa
     regola occuperebbe comunque la sua altezza e il badge
     sobbalzerebbe di 13px quando la RPC risponde. */
  .topbar .userbadge-tipo:empty { display: none; }

  /* La riga del tipo NON deve allargare il badge: la larghezza la
     decide il nome, come prima che questa riga esistesse. `width:0`
     la toglie dal calcolo della larghezza naturale della colonna;
     `min-width:100%` la riporta larga quanto la colonna intanto e'
     diventata. Cosi' sfuma con i puntini invece di spingere, e il
     testo intero resta nel title.

     FIN DOVE la barra e' stretta, e il confine e' misurato, non
     scelto: col tipo libero la topbar chiede 775px, e la sua
     larghezza e' quella della finestra meno i 210 della barra
     laterale. Sotto i ~985px non ci sta. Misurato: a 901 il badge
     passava da 121 a 182 e il debordo da 22 a 84; a 950 debordava
     di 35; a 1000 e a 1280 non debordava affatto.
     Il freno si ferma quindi a 1000px. Sopra, «Superadmin ·
     supporto» si legge per intero: troncarlo dove lo spazio c'e'
     e' peggio del problema che risolve — a 1280 si leggeva
     «Superad…» con 300px liberi accanto.
     Il caso che resta troncato e' uno solo: un amministratore che
     sta impersonando un cliente, su una finestra fra 900 e 1000px.
     E in quel momento c'e' gia' una fascia rossa a tutta larghezza
     che dice su quale cliente sta operando. */
  @media (max-width: 1000px) {
    .topbar .userbadge-tipo {
      width: 0;
      min-width: 100%;
      overflow: hidden;
      text-overflow: ellipsis;
    }
  }


  /* IL PIEDE DELLE RIGHE D'ORDINE DICE DI CHE COSA E' FATTO IL TOTALE.
     L'elenco stampa `TotaleOrdine` (manodopera e IVA comprese), la scheda
     stampava la sola somma degli articoli: due numeri diversi a un clic di
     distanza (difetto A3, 2026-09-05). Ora la scheda porta tutte e tre le
     grandezze, e quella in fondo e' la stessa che si legge nell'elenco.
     `.rtot` sta in @layer base dentro index.html; qui siamo in `app`, che e'
     dichiarato dopo, quindi non serve nessun !important. */
  /* Sotto il codice ordine: per chi si sta lavorando. In officina «#1» non e'
     un'informazione (difetto A6). */
  .dt .lav-per { margin-top: 3px; font-size: 11.5px; color: var(--ink-muted); }

  .rtot .rtot-r { display: flex; justify-content: space-between; gap: 12px; align-items: baseline; }
  .rtot .rtot-nota { font-weight: 600; font-size: 12px; color: var(--ink-muted); text-align: left; }
  .rtot .rtot-tot { margin-top: 4px; padding-top: 6px; border-top: 1px solid var(--line); font-size: 15px; }

  /* ==========================================================
     === TELEFONO === (blocco 1, 07/09/2026) — sotto i 760px
     ----------------------------------------------------------
     Il guscio del telefono: barra in basso a cinque voci, header compatto
     con il menu dell'account, FAB «Nuovo preventivo», archivio dei
     preventivi a schede. Il markup lo scrive app/guscio/telefono.js, e SOLO
     quando la media query combacia: sopra i 760px questo blocco non
     dipinge nulla e il DOM e' quello di sempre.

     Tre cose da sapere prima di toccarlo:
     1. Sta DOPO il blocco a 760px pinnato dai test (.logout, .userbadge,
        .sb-account): quelle regole non si toccano, qui si aggiunge.
     2. workbench.css e' nello stesso layer ed e' collegato dopo: a parita'
        di specificita' vince lui. Percio' i selettori portano #app, .topbar,
        .main: piu' specificita', mai !important.
     3. La barra e' l'ULTIMO figlio flex di .main, non un position:fixed:
        .content (l'unico contenitore di scorrimento) si accorcia da sola,
        la barra sparisce con #app.hidden, e la tastiera di iOS non la fa
        galleggiare a meta' schermo. La safe area e' una SOMMA
        (min-height + padding-bottom longhand), come vuole CLAUDE.md.
     I colori passano tutti dai token: sotto i 760px instaurus-tokens.css li
     ri-mappa sulla palette di evilinstaurus (blocco TELEFONO del layer
     tokens). Qui non c'e' un colore in cifre.
     ========================================================== */
  @media (max-width: 760px) {
    :root { --tel-barra-h: calc(56px + env(safe-area-inset-bottom)); }

    /* --- il guscio di prima sparisce: barra laterale, velo del cassetto,
           hamburger, FAB di Nova (che resta nel DOM: ensureFab lo ricreerebbe
           ogni 2 s se mancasse — si nasconde, non si rimuove) --- */
    #app #sidebar { display: none; }
    #app .backdrop { display: none; }
    .topbar .hamb { display: none; }
    #nova-fab { display: none; }
    .main { position: relative; }

    /* Lo spazio in fondo al contenuto era il piede del FAB di Nova (84px +
       safe area). La barra e' nel flusso e la safe area la porta lei: resta
       da scavalcare il FAB «Nuovo preventivo» (56 + 16 di aria + 12). */
    .content::after { height: 84px; }

    /* I toast salgono sopra barra e FAB, a tutta larghezza: in basso a destra
       finirebbero sotto il pollice. La regola base con max() resta com'e'. */
    #toast-stack { bottom: calc(var(--tel-barra-h) + 84px); left: 12px; right: 12px; align-items: stretch; }
    #toast-stack .toast { max-width: none; }

    /* La nebbia di evilinstaurus: due gradienti radiali sul fondo. In Notturna
       i token --mist-nebbia-* non esistono, il ripiego e' trasparente e resta
       il solo --bg: e' voluto. Due classi per battere workbench.css:51. */
    .main .content {
      background:
        radial-gradient(ellipse at 17% 60%, var(--mist-nebbia-1, transparent), transparent 60%),
        radial-gradient(ellipse at 80% 20%, var(--mist-nebbia-2, transparent), transparent 55%),
        var(--bg);
    }

    /* --- la barra in basso --- */
    .tel-barra {
      display: flex; align-items: stretch; flex: none;
      position: relative; z-index: 2;
      min-height: calc(56px + env(safe-area-inset-bottom));
      padding-bottom: env(safe-area-inset-bottom);
      padding-left: max(4px, env(safe-area-inset-left));
      padding-right: max(4px, env(safe-area-inset-right));
      /* Prima la carta piena, poi la translucida: dove color-mix() non
         esiste (iOS prima della 16.2) la seconda riga cade e resta la prima.
         Senza, la barra sarebbe TRASPARENTE sopra il contenuto. */
      background: var(--surface);
      background: color-mix(in srgb, var(--surface) 74%, transparent);
      -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
      border-top: 1px solid var(--line);
    }
    .tel-voce {
      flex: 1 1 0; min-width: 44px; min-height: 44px;
      display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
      padding-top: 6px; padding-bottom: 4px; padding-left: 2px; padding-right: 2px;
      background: none; border: 0; border-radius: 0;
      color: var(--ink-muted);
      font: 600 11px/1.2 var(--font-ui); letter-spacing: var(--tracking-ui);
      position: relative; cursor: pointer; -webkit-tap-highlight-color: transparent;
    }
    /* L'indicatore attivo e' un filetto navy in alto (--action, che in Notturna
       diventa carta): niente pillole piene, DESIGN-SYSTEM.md §8.5. */
    .tel-voce::before {
      content: ""; position: absolute; top: 0; left: 50%;
      width: 24px; height: 2px; background: var(--action); border-radius: 0 0 2px 2px;
      transform: translateX(-50%) scaleX(0);
      transition: transform var(--dur-1) var(--ease-out);
    }
    .tel-voce[aria-current="page"], .tel-voce[data-attiva] { color: var(--action); }
    .tel-voce[aria-current="page"]::before, .tel-voce[data-attiva]::before { transform: translateX(-50%) scaleX(1); }
    .tel-voce:active { background: var(--action-soft); }
    .tel-voce:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -2px; }
    .tel-voce-ic { display: grid; place-items: center; width: 24px; height: 24px; font-size: 18px; line-height: 1; }
    .tel-voce-ic svg { display: block; width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
    .tel-voce-ic .tel-ic-altro { fill: currentColor; stroke: none; }
    .tel-voce-eti { white-space: nowrap; }
    /* Nova: il segno oro su disco scuro, come sul FAB. L'oro nudo su #e8edf1
       farebbe 2,72 — sotto i 3:1 — e l'oro e' un riempimento, mai un tratto. */
    .tel-nova-disco { width: 28px; height: 28px; border-radius: 50%; background: var(--shell-alto); box-shadow: 0 0 0 1px var(--accent); color: var(--accent); }
    .tel-nova-disco .instaurus-mark { width: 16px; height: 16px; }
    .tel-voce-nova[data-spento] .tel-nova-disco { box-shadow: 0 0 0 1px var(--on-dark-line); color: var(--on-dark-muted); }

    /* --- i fogli («Altro», account): dialog dal basso, sopra .ovl (60) e
           sotto i toast (80) e Nova (1200) --- */
    .tel-velo { position: fixed; inset: 0; z-index: 70; background: var(--velo); }
    .tel-velo[hidden], .tel-foglio[hidden] { display: none; }
    .tel-foglio {
      position: fixed; left: 0; right: 0; bottom: 0; z-index: 71;
      max-height: 80vh;                              /* ripiego dove dvh non c'e' (iOS < 15.4) */
      max-height: min(80dvh, calc(100dvh - 56px));
      overflow: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
      background: var(--surface); color: var(--ink);
      border: 1px solid var(--line); border-bottom: 0;
      border-radius: var(--radius-lg) var(--radius-lg) 0 0;
      box-shadow: var(--shadow-2);
      padding-top: 8px;
      padding-left: max(12px, env(safe-area-inset-left));
      padding-right: max(12px, env(safe-area-inset-right));
      padding-bottom: max(12px, env(safe-area-inset-bottom));
      animation: tel-foglio-su var(--dur-2) var(--ease-out) both;
    }
    @keyframes tel-foglio-su { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
    .tel-foglio-testa { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 6px 4px 10px 8px; }
    .tel-occhiello { margin: 0 0 4px; font: 600 11px/1.2 var(--font-ui); letter-spacing: .12em; text-transform: uppercase; color: var(--ink-muted); }
    .tel-foglio-testa h2 { margin: 0; font: 500 var(--fs-600)/1.15 var(--font-ui); letter-spacing: -.5px; color: var(--ink); }
    .tel-foglio-x { flex: none; width: 44px; height: 44px; border: 0; border-radius: var(--radius-sm); background: var(--surface-2); color: var(--ink); font-size: 24px; line-height: 1; cursor: pointer; }
    .tel-foglio-lista { list-style: none; margin: 0; padding: 0; }
    .tel-foglio-voce {
      display: flex; align-items: center; gap: 14px; width: 100%; min-height: 56px;
      padding: 8px 8px; border: 0; border-bottom: 1px solid var(--line);
      background: none; color: var(--ink);
      font: 400 var(--fs-400)/1.25 var(--font-ui); text-align: left; cursor: pointer;
    }
    .tel-foglio-lista > li:last-child > .tel-foglio-voce { border-bottom: 0; }
    .tel-foglio-voce .tel-voce-ic { color: var(--ink-muted); }
    .tel-foglio-voce[aria-current="page"] { color: var(--action); }
    .tel-foglio-voce[aria-current="page"] .tel-voce-ic { color: var(--action); }
    .tel-foglio-testo { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
    .tel-foglio-testo b { font-weight: 600; font-size: var(--fs-400); }
    .tel-foglio-testo small { color: var(--ink-muted); font-size: var(--fs-300); }
    .tel-foglio-voce:active { background: var(--action-soft); }
    .tel-foglio-voce:focus-visible, .tel-foglio-x:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -2px; }
    @media (hover: hover) {
      .tel-foglio-voce:hover { background: var(--action-soft); }
      .tel-foglio-x:hover { background: var(--line); }
    }

    /* --- header compatto: il marchio (.top-home, gia' visibile fino a 900px),
           il titolo della sezione, l'avatar. «Nuovo preventivo» diventa il FAB
           in basso a destra; «Esci» e le funzioni di servizio stanno nel menu
           dell'account (la toolbar Backup/Ripristina/CSV, che ha un display
           inline, la spegne la guardia in instaurus-tokens.css). Tre classi
           dove serve battere workbench.css; height e min-height come longhand,
           mai la shorthand padding sulla .topbar. --- */
    .main > .topbar { height: calc(56px + env(safe-area-inset-top)); min-height: calc(56px + env(safe-area-inset-top)); }
    .topbar .top-right .top-create { display: none; }
    .topbar .top-right .logout { display: none; }
    .topbar .top-right { margin-left: auto; gap: 0; flex: 0 0 auto; }
    .topbar .top-heading { flex: 1 1 auto; min-width: 0; }
    .topbar .top-title { font: 500 17px/1.15 var(--font-ui); letter-spacing: -.4px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .tel-avatar {
      position: relative; width: 44px; height: 44px; flex: none;
      display: grid; place-items: center;
      border: 1px solid var(--line); border-radius: var(--radius-sm);
      background: var(--surface-2); color: var(--ink);
      font: 600 15px/1 var(--font-ui); cursor: pointer; -webkit-tap-highlight-color: transparent;
    }
    .tel-avatar > span { grid-area: 1 / 1; }
    .tel-avatar .tel-avatar-iniziale[hidden], .tel-avatar .user-avatar[hidden] { display: none; }
    .tel-avatar .user-avatar { width: 30px; height: 30px; background: transparent; }
    /* Il pallino della rete, come nel piede della barra laterale che qui non c'e'. */
    .tel-avatar-stato { position: absolute; right: -3px; bottom: -3px; width: 12px; height: 12px; border-radius: 50%; background: var(--ok); border: 2px solid var(--surface); }
    .tel-avatar[data-rete="off"] .tel-avatar-stato { background: var(--ink-muted); }
    /* Le modifiche in coda: lo stesso numero che il FAB di Nova portava sul telefono. */
    .tel-avatar[data-pendenti]:not([data-pendenti="0"])::after {
      content: attr(data-pendenti); position: absolute; top: -6px; right: -6px;
      min-width: 18px; height: 18px; padding-left: 4px; padding-right: 4px; border-radius: 9px;
      background: var(--err); color: var(--err-on); font: 600 10px/18px var(--font-ui); text-align: center;
    }
    .tel-avatar:active { background: var(--action-soft); }
    .tel-avatar:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
    @media (hover: hover) { .tel-avatar:hover { border-color: var(--line-strong); } }

    /* --- il FAB «Nuovo preventivo»: il tasto invio di evil (disco navy), sopra
           la barra, a destra sotto il pollice. Sparisce dentro l'editor: li'
           un altro «nuovo» sarebbe solo rumore sopra il modulo. --- */
    .tel-fab {
      position: absolute; z-index: 3;
      right: max(16px, env(safe-area-inset-right));
      bottom: calc(var(--tel-barra-h) + 16px);
      width: 56px; height: 56px; border: 0; border-radius: 50%;
      background: var(--action); color: var(--action-on);
      box-shadow: var(--shadow-2);
      font: 400 30px/1 var(--font-ui); display: grid; place-items: center;
      cursor: pointer; -webkit-tap-highlight-color: transparent;
      transition: transform var(--dur-1) var(--ease-out);
    }
    .tel-fab:active { transform: scale(.96); background: var(--action-strong); }
    .tel-fab:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 3px; }
    .main:has(#prev2-editor) .tel-fab { display: none; }

    /* --- il menu dell'account: chi sei, come sta la rete, le funzioni di
           servizio del ruolo, Esci per ultimo --- */
    .tel-account { display: flex; align-items: center; gap: 12px; padding: 4px 8px 12px; }
    .tel-account .user-avatar { width: 40px; height: 40px; flex: none; border-radius: var(--radius-sm); background: var(--surface-2); }
    .tel-account > span:last-child { display: flex; flex-direction: column; min-width: 0; }
    .tel-account b { font-weight: 600; font-size: var(--fs-500); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .tel-account small { color: var(--ink-muted); font-size: var(--fs-300); }
    .tel-stato { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; margin: 0 4px 8px; padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface-2); }
    .tel-stato-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--ok); flex: none; }
    .tel-stato[data-rete="off"] .tel-stato-dot { background: var(--ink-muted); }
    .tel-stato > span:not(.tel-stato-dot) { display: flex; flex-direction: column; flex: 1; min-width: 0; }
    .tel-stato b { font-size: var(--fs-300); font-weight: 600; }
    .tel-stato small { color: var(--ink-muted); font-size: 11px; }
    .tel-non-riuscite { flex: 1 1 100%; min-height: 44px; padding-left: 10px; padding-right: 10px; border: 1px solid var(--err); border-radius: var(--radius-xs); background: var(--err-soft); color: var(--err-text); font: 600 12px/1 var(--font-ui); text-align: left; cursor: pointer; }
    .tel-non-riuscite[hidden] { display: none; }
    .tel-csv > summary { list-style: none; }
    .tel-csv > summary::-webkit-details-marker { display: none; }
    .tel-csv > summary::after { content: "›"; margin-left: auto; color: var(--ink-muted); font-size: 22px; line-height: 1; transition: transform var(--dur-1) var(--ease-out); }
    .tel-csv[open] > summary::after { transform: rotate(90deg); }
    .tel-csv-lista { padding-left: 16px; border-bottom: 1px solid var(--line); }
    .tel-csv-lista .tel-foglio-voce { min-height: 48px; }
    .tel-foglio-esci { color: var(--err-text); }

    /* --- l'archivio dei preventivi a schede (il markup lo da' il disegnatore
           in app/guscio/telefono.js, chiamato da loadArchivioPreventivi). Non
           e' una .card: .card ha overflow:hidden, che renderebbe inerte la
           ricerca fissa, e preventivatore.css imbottisce di 18px ogni suo
           figlio. Il contenitore si toglie il padding di .content cosi' non
           dipende dal .no-pad che l'editor lascia dietro di se'. --- */
    .content:has(> #prev2-container) { padding-top: 0; padding-left: 0; padding-right: 0; }
    .tel-archivio { padding-left: 12px; padding-right: 12px; padding-bottom: 8px; }
    /* La ricerca resta in cima mentre si scorre: sticky contro .content, che
       e' il contenitore di scorrimento vero; stessa carta translucida delle
       barre, cosi' le schede le passano sotto. */
    .tel-cerca {
      position: sticky; top: 0; z-index: 1;
      display: flex; align-items: center; gap: 10px;
      margin-left: -12px; margin-right: -12px;
      padding: 8px 12px;
      background: var(--bg);                         /* ripiego dove color-mix() non c'e' */
      background: color-mix(in srgb, var(--bg) 88%, transparent);
      -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
    }
    .tel-cerca input {
      flex: 1 1 auto; min-width: 0; min-height: 44px;
      padding-left: 14px; padding-right: 14px;
      font: 400 16px/1.3 var(--font-ui); color: var(--ink);
      background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--radius);
    }
    .tel-cerca input:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }
    .tel-cerca-cnt { flex: none; min-width: 28px; text-align: center; font: 600 12px/1 var(--font-ui); letter-spacing: var(--tracking-ui); color: var(--ink-muted); }
    .tel-elenco { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
    /* La scheda e' il .panel di evil: carta, bordo tenue, raggio 20 (via
       --radius-lg), l'ombra quasi impercettibile (via --shadow-1). */
    .tel-scheda { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--shadow-1); overflow: hidden; }
    .tel-scheda-apri {
      display: flex; flex-direction: column; gap: 6px; width: 100%;
      padding: 14px 16px 10px; text-align: left;
      background: none; border: 0; color: var(--ink);
      font: 400 var(--fs-400)/1.35 var(--font-ui); cursor: pointer; -webkit-tap-highlight-color: transparent;
    }
    .tel-scheda-apri:active { background: var(--action-soft); }
    .tel-scheda-apri:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -3px; }
    /* La gerarchia la fa la scala: cliente 17, oggetto 15 in grigio a due
       righe, il pie' a 13 con le cifre tabellari. */
    .tel-scheda-testa { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
    .tel-scheda-cliente { min-width: 0; font-size: var(--fs-500); font-weight: 600; letter-spacing: -.2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .tel-scheda-testa .badge { flex: none; }
    .tel-scheda-oggetto { color: var(--ink-muted); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; overflow-wrap: anywhere; }
    .tel-scheda-piede { display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px; margin-top: 2px; font-size: var(--fs-300); color: var(--ink-muted); font-variant-numeric: tabular-nums; }
    .tel-scheda-piede i { font-style: normal; opacity: .6; }
    .tel-scheda-piede b { margin-left: auto; color: var(--ink); font-size: var(--fs-400); font-weight: 600; }
    .tel-scheda-azioni { display: flex; justify-content: flex-end; gap: 8px; padding: 0 10px 10px; }
    .tel-scheda-azioni .ibtn { min-height: 44px; min-width: 44px; padding-left: 14px; padding-right: 14px; font-size: 13px; }
    .tel-piede { display: flex; justify-content: center; min-height: 24px; padding-top: 14px; padding-bottom: 8px; }
    .tel-piede .tel-altri { min-height: 44px; padding-left: 18px; padding-right: 18px; }
    .tel-piede .tel-altri:disabled { opacity: .6; cursor: wait; }
  }
  /* === fine TELEFONO === */
}
/* fine @layer app */

/* @property va DICHIARATA FUORI dai layer: e' una at-rule di registrazione,
   non una regola di stile, e dentro un @layer i browser la ignorano. Senza
   registrazione --num non e' interpolabile e .conta mostra il numero fermo
   — che e' il ripiego voluto, non un guasto. */
@property --num { syntax: '<integer>'; initial-value: 0; inherits: false; }
