/* ============================================================
   I colori dell'app.
   Navy #020c2e e bordeaux #820e25 sono campionati dal logo e non si toccano:
   quello che cambia è il contorno, da grigio-azzurro freddo a neutri caldi.
   Regola unica: il resto del foglio usa SOLO i token semantici
   (--bg, --ink, --action…), mai i gradini delle scale.
   Cambiare tema = cambiare la mappa, non il foglio di stile.
   ============================================================ */
  :root{
    /* L'app dei clienti è **sempre chiara**, qualunque tema abbia il telefono.
       Ci abbiamo provato a seguire il tema di sistema (`light dark`): sul telefono di chi tiene il
       modo scuro il menu del pranzo diventava una schermata notturna, e non è quello che deve
       sembrare un bar a mezzogiorno. Il modo scuro resta, ma dove serve davvero: nel pannello del
       bar, che sta acceso tutto il giorno dietro il banco (vedi la mappa antracite più sotto). */
    color-scheme: light;

    /* ---------- SCALA NAVY — identità del logo ---------- */
    --navy-50:#eff4fa;  --navy-100:#e1e8f4; --navy-200:#cbd7ea; --navy-300:#afbfd9;
    --navy-400:#8c9fc1; --navy-500:#697da5; --navy-600:#485d88; --navy-700:#293e6a;
    --navy-800:#10224c; --navy-900:#020c2e; /* navy del logo, invariato */
    --navy-950:#000319; /* fondo pagina in modo scuro: un gradino sotto il logo */

    /* ---------- SCALA BORDEAUX — identità dell'accento ---------- */
    --bord-50:#fef0ed;  --bord-100:#fce1dd; --bord-200:#f6c6c1; --bord-300:#ea9f9a;
    --bord-400:#d6706f; --bord-500:#bb464c; --bord-600:#a02836;
    --bord-700:#820e25; /* accento storico, invariato */
    --bord-800:#5e041a; --bord-900:#3f0412;

    /* ---------- SCALA ANTRACITE — solo per il pannello del bar ----------
       Grigio neutro appena freddo: non è il navy del logo (che resta l'identità dell'header) e
       non è nero pieno, che dietro un banco illuminato fa da specchio. */
    --ant-50:#f4f5f7;   --ant-100:#e8eaee; --ant-200:#d3d7dd; --ant-300:#b2b8c1;
    --ant-350:#9aa1ab;  --ant-400:#8b929d; --ant-500:#6b727d; --ant-600:#4e545e;
    --ant-700:#3a3f47;  --ant-800:#282c33; --ant-900:#1d2026; --ant-950:#141619;

    /* ---------- SCALA NEUTRI CALDI — avorio, panna, tabacco ----------
       Resta definita: la usano ancora il velo sopra le foto e le superfici navy. Non è più però
       la carta della pagina — vedi la scala `carta` qui sotto. */
    --warm-50:#fefbf7;  --warm-100:#faf5ed; --warm-200:#f4ede1; --warm-300:#e7ded0;
    --warm-400:#d6caba; --warm-500:#b4a796; --warm-600:#918474; --warm-700:#736759;
    --warm-800:#554a3e; --warm-900:#362d25;

    /* ---------- SCALE ORGANICHE — TASK-039, il tema del mockup ----------
       Vengono dal design system del mockup di Andrea (`_ds/organic-…`): crema, terracotta, oliva.
       Non sono una tinta in più sopra le altre: **sostituiscono** navy e bordeaux come mappa
       dell'app cliente. Il pannello del bar non le vede mai (ha la sua mappa antracite più sotto).

       Le tre scale sono generate in OKLCH su una sola scala di luminosità, quindi lo stesso
       gradino di due scale diverse pesa uguale: `--terra-600` e `--oliva-600` si scambiano senza
       che l'interfaccia cambi contrasto. È la proprietà che rende sicuro remappare un token.

       I gradini che l'app usa davvero come testo (`700`, `800`) sono più scuri di quelli del
       mockup: là il terracotta `#c67139` fa da pulsante con sopra il crema, che sta a 3,0:1 e
       sotto la soglia AA per il testo. Il mockup può permetterselo, un'app che vende cibo no —
       e `tests/contrasti.py` lo fermerebbe comunque. Vedi TASK-039 per il perché per esteso. */
    --org-50:#fdf9f1;   --org-100:#f9f4ed; --org-150:#f5ead8; --org-200:#ebddc5;
    --org-300:#dcd3c4;  --org-400:#c0b6a5; --org-500:#a19786; --org-600:#82796a;
    --org-700:#645c50;  --org-800:#474238; --org-900:#2e2b25; --org-950:#201e1d;

    --terra-50:#fff7f1;  --terra-100:#fff2eb; --terra-200:#ffe1d0; --terra-300:#ffc6a5;
    --terra-400:#f6a06b; --terra-500:#d67f48; --terra-600:#b2622d; --terra-650:#a05426;
    --terra-700:#8c491a; --terra-800:#643312; --terra-900:#402310;
    /* TASK-046 — il terracotta **esatto** del mockup (`--color-accent: #c67139`). Non sta sulla
       rampa: cade fra il 500 e il 600, ed è la tinta che si vede nelle schermate di Andrea.
       Si usa **solo dove sopra non ci va del testo**: pallini, anelli, barre di riempimento,
       bordi, il pieno delle pastiglie decorative. Col crema sopra sta a 3,6:1 e col bianco a
       3,61 — sotto la soglia AA del testo — quindi il pieno dei **pulsanti** resta `terra-650`,
       che è la stessa tinta un gradino più giù e passa a 5,3:1. Due token, un colore solo:
       quello che si guarda e quello su cui si legge. */
    --terra-mock:#c67139;

    --oliva-100:#f0fae1; --oliva-200:#e1eecc; --oliva-300:#ccdbb2; --oliva-400:#aebf92;
    --oliva-500:#8fa073; --oliva-600:#728157; --oliva-700:#56633f; --oliva-800:#3d472b;
    --oliva-900:#272e1b;

    /* ---------- SCALA CARTA — TASK-036/A, la carta nuova ----------
       Il tabacco dell'avorio (#f4ede1) è caldo del 6% di saturazione: su uno schermo d'ufficio a
       mezzogiorno legge "trattoria", non "pranzo di lavoro". Questa è la stessa carta con due terzi
       della saturazione tolti: resta un neutro appena caldo — si accorge di non essere grigio solo
       accanto al bianco — e guadagna il contrasto che serviva.

       Non è una tinta nuova nell'app: navy e bordeaux del logo **non si toccano**, e restano
       l'unico colore. Quello che cambia è che adesso hanno intorno del silenzio invece di un
       altro colore tenue. È tutto qui il "fresco": meno cose colorate, non più.

       Ogni gradino è più chiaro del corrispondente warm e più freddo: i due grigi profondi
       (700, 800) guadagnano più di un punto di rapporto sul testo tenue e sui bordi. */
    --carta-50:#ffffff;  --carta-100:#fbfbfa; --carta-200:#f2f2ef; --carta-300:#e6e5e0;
    --carta-400:#d2d1ca; --carta-500:#a8a69d; --carta-600:#7d7b72; --carta-700:#615f58;
    --carta-800:#45443e; --carta-900:#2b2a26;

    /* ---------- STATI ---------- */
    --ok-100:#e2f6e6;  --ok-200:#c5e9cd;  --ok-300:#92cfa1;  --ok-500:#45935e;
    --ok-600:#2e7747;  --ok-700:#215e36;  --ok-800:#133f23;  --ok-900:#0c2d17;
    --wa-100:#ffeddc;  --wa-200:#fcd6b4;  --wa-300:#edaf74;  --wa-500:#c37823;
    --wa-600:#a15e00;  --wa-700:#7f4900;  --wa-800:#563000;  --wa-900:#402200;
    --er-100:#ffebe7;  --er-200:#ffd2c9;  --er-300:#f99885;  --er-500:#c83c25;
    --er-600:#af2208;  --er-700:#901500;  --er-800:#640e02;  --er-900:#490a02;

    /* ========== MAPPA CHIARA (predefinita) — TASK-039, il tema del mockup ==========
       Una cosa qui è al contrario di prima, ed è voluta: **le schede sono più scure della
       pagina**, non più chiare. Sulla carta bianca la scheda si staccava alzandosi (ombra e
       bianco più bianco); sul crema si stacca **affondando**, com'è nel mockup. È la differenza
       fra un'interfaccia stampata su carta e una cosa posata su un tavolo di legno, e cambia
       come si legge la gerarchia: il fondo pagina resta il posto dove non c'è niente.

       Conseguenza da non dimenticare: `theme-color` nel `<head>` deve seguire `--surface`, che è
       quello che il telefono si trova sotto l'orologio. Sta in `layout_head()`. */
    /* TASK-047 — **la pagina degli ordini è bianca**, il pannello resta il crema del mockup.
       Richiesta esplicita di Andrea il 28/07: *"rendiamo più chiaro lo sfondo di ordini…
       portiamolo bianco"*. È il terzo passo di una storia che vale la pena tenere per intero,
       perché ogni passo ha corretto un errore del precedente:

         27/07 15:3x  «rendi lo sfondo più chiaro»  → `org-150` → `org-100` (#f9f4ed)
                      Errore: `--bg` è **condiviso**, e si schiarì anche il pannello.
         27/07 sera   «completa tutto lo stile del mockup»  → `org-150` (#f5ead8)
                      Giusto per il pannello, ma rimise il crema anche al cliente.
         28/07        «lo sfondo di ordini… bianco»  → **#ffffff qui, `org-150` in `.gapp`**

       La lezione, scritta perché non si ripaghi: **le due metà dell'app non hanno più la stessa
       pagina**, e il posto dove si separano è questo più la ridichiarazione dentro `.gapp`
       (`gestore.css`). Chi tocca `--bg` qui tocca **solo i clienti**.

       Le **schede non si muovono** (`org-200`): la regola di TASK-039 è che la scheda si stacca
       *affondando*, e sul bianco quel distacco cresce invece di ridursi — il crema delle schede
       diventa la cosa che si vede, la pagina il posto dove non c'è niente. Il crema non è sparito
       dall'app cliente: è passato dal fondo agli oggetti.

       `theme-color` **non cambia**: segue `--surface` (#ebddc5), che è l'header, cioè quello che
       il telefono si trova davvero sotto l'orologio. */
    --bg:#ffffff;                      /* pagina degli ordini: bianca (TASK-047) */
    --surface:var(--org-200);          /* schede e header: crema profondo */
    /* Su una pagina bianca «in rilievo» non si può più dire col colore: `org-50` (#fdf9f1) sarebbe
       **più scuro** della pagina, cioè un rilievo che affonda. Il commento di TASK-039 lo aveva
       previsto — *"se un domani la pagina salisse ancora, quello è il primo token che perde il suo
       mestiere"* — e quel giorno è oggi. Diventa bianco: il rilievo di campi, pastiglie e passo
       lo porta il **bordo**, che quei diciassette punti dichiarano già tutti
       (`border:1px solid var(--border-strong)`). Nel pannello resta `org-50`, vedi `gestore.css`. */
    --surface-raised:#ffffff;          /* campi e superfici in rilievo: li definisce il bordo */
    --ink:var(--org-950);              /* testo principale */
    --ink-2:var(--org-800);            /* testo secondario */
    --ink-muted:var(--org-700);        /* testo tenue */
    --border:var(--org-300);           /* righe divisorie (decorative) */
    --border-strong:var(--org-600);    /* bordi dei comandi */
    /* TASK-046 — il decorativo del mockup, per quello che si guarda e non si legge.
       `--ink-muted` invece **non** scende al grigio del mockup: là il testo tenue è
       `color-mix(ink 55%, transparent)`, cioè #807a71 sul crema, che sta a **3,57:1**. Sotto la
       soglia AA per il testo, e `tests/contrasti.py` lo fermerebbe. Il gradino sopra della rampa
       (`org-600`) sta a 3,61 e non basta nemmeno lui: fra i due non c'è niente di legittimo,
       quindi resta `org-700` (5,53:1). È l'unico posto in cui il mockup non si può copiare. */
    --deco:var(--terra-mock);          /* pallini, anelli, barre: mai testo sopra */
    /* Il pieno del pulsante è **terracotta 650**, non il `#c67139` del mockup: quello col crema
       sopra sta a 3,0:1. Questo tiene la stessa tinta e passa la soglia del testo. */
    --action:var(--terra-650);         /* pieno del pulsante */
    --action-hover:var(--terra-700);   /* pulsante premuto */
    /* TASK-046 — sopra il pulsante ci va **il crema della pagina**, non un quasi-bianco.
       Nel mockup è `color: var(--color-bg)` (#f5ead8): il pulsante non porta un colore nuovo,
       porta il fondo della pagina ritagliato dentro il pieno. Era `terra-50` (#fff7f1), che a
       occhio è bianco e faceva sembrare il pulsante un elemento importato da un altro sistema.
       Il conto regge: crema su `terra-650` fa **4,66:1**, sopra la soglia AA. */
    --action-ink:var(--org-150);       /* testo sopra il pulsante: il crema della pagina */
    --action-text:var(--terra-700);    /* terracotta usato come testo */
    --action-soft:var(--terra-100);    /* fondo tenue dei chip terracotta */
    --action-line:var(--terra-600);    /* bordo di "selezionato" */
    --action-badge:var(--terra-700);   /* pastiglia del contatore nel pulsante */
    --focus:var(--terra-700);          /* anello da tastiera */
    /* "Va bene" diventa **oliva**: è il secondo accento del mockup, e sul crema il verde di prima
       era l'unica tinta che non apparteneva a nessuna delle due famiglie. */
    --ok:var(--oliva-700);    --ok-soft:var(--oliva-100); --ok-line:var(--oliva-500);
    --warn:var(--wa-700);     --warn-soft:var(--wa-100); --warn-line:var(--wa-500);
    --danger:var(--er-700);   --danger-soft:var(--er-100); --danger-line:var(--er-500);
    --neutral:var(--org-800);          /* "esaurito" */
    --neutral-soft:var(--org-300);
    --disabled-bg:var(--org-300);    --disabled-ink:var(--org-800);
    --track:var(--org-300);          --track-active:var(--org-50);
    --scrim:rgba(46,43,37,.52);        /* velo dietro le finestre */

    /* Ombre: base neutra (org-900) e **più leggere**. Su una carta chiara l'ombra pesante è la
       cosa che fa sembrare vecchia un'interfaccia: la profondità la fanno i bordi e lo spazio. */
    --shadow:0 1px 2px rgba(46,43,37,.05), 0 6px 20px rgba(46,43,37,.07);
    --shadow-sm:0 1px 2px rgba(46,43,37,.08);
    --shadow-lg:0 -10px 40px rgba(46,43,37,.18);
    --glow-action:0 4px 14px rgba(160,84,38,.22);
    /* L'alone bordeaux in cima alla pagina era una tinta in più su una pagina che ne ha già due.
       Tolto: "niente elementi di distrazione" comincia dal fondo. */
    --page-wash:none;

    /* ---------- superfici profonde: la testata del tracking, il fondo delle miniature ----------
       Si chiamano ancora `--navy-*` perché è il nome che tutto il foglio usa da quattro giorni, e
       rinominare 60 occorrenze in tutta l'app **mentre un'altra sessione lavora sugli stessi
       file** è il modo migliore per farsi cancellare il lavoro da un conflitto. Il nome è un
       token, non un colore: qui dentro adesso c'è il **bruno profondo** del mockup (`terra-900`,
       che è il fondo della scheda "Bollini" e della testata dell'ordine).

       Il navy del logo non è sparito dall'app: è ancora dentro `logo-mark.png`, che è un PNG e
       non un token. Vedi la nota in TASK-039 — è una decisione di marchio, non di CSS. */
    --navy-surface:var(--terra-900);
    --navy-surface-2:var(--terra-800);
    --on-navy:var(--org-150);
    --on-navy-muted:var(--terra-200);
    --navy-line:var(--terra-500);      /* bordo dei comandi sulle superfici profonde */
    --navy-pill:var(--terra-800);
    --on-navy-pill:var(--terra-100);
    --on-navy-ok:var(--oliva-300);     /* pallino "ordini aperti" */
    --on-navy-warn:var(--terra-300);   /* pallino "ordini chiusi" */
    --thumb-base:var(--terra-900);     /* tinta piena dietro le miniature: la prova dei contrasti
                                          misura su questa, e cambia col tema */
    --thumb-bg:linear-gradient(150deg, var(--terra-800), var(--terra-900));
    --thumb-mono:var(--terra-500);     /* monogramma */
    --thumb-cap:var(--terra-200);      /* didascalia "foto" */
    --thumb-edge:var(--terra-500);

    /* ---------- I CARATTERI DEL MOCKUP — TASK-041 ----------
       Fino a qui c'era scritto "caratteri invariati": TASK-039 aveva portato i **colori** di
       Organic e lasciato apposta la coppia vecchia (Palatino nei titoli, sans di sistema nel
       corpo). Il risultato era un'app color mockup scritta con un'altra voce — ed è la cosa
       che si nota per prima, perché Caprasimo è l'unica voce da display del sistema
       (`_ds/organic-…/readme.md`: *"Caprasimo is the only display voice"*).

       Il pannello del bar li aveva già da TASK-040; `fonts.css` era scritto per servire tutti e
       due ("usabile anche dall'app cliente") ma nessuno l'aveva linkato lato cliente. Ora
       `render.php` lo linka, e questi due token puntano dove devono.

       La coppia di sistema resta **dietro**, non davanti: se il file del carattere non arriva
       (PWA su linea del bar ballerina) il menu resta leggibile invece di sparire. */
    --serif:'Caprasimo','Iowan Old Style','Palatino Linotype',Palatino,Georgia,serif;
    --sans:'Figtree',system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;

    /* ---------- LE ROTONDITÀ DEL MOCKUP — TASK-041 ----------
       Il mockup cliente usa **22 volte** `999px` e i riquadri a 26px; qui c'erano 16 e 12, cioè
       il raggio di TASK-036 che veniva da prima di Organic. Il pannello era già stato allineato
       (`--gr:26px`, `--gr-sm:18px` in `gestore.css`): questi sono gli stessi numeri, così le due
       metà dell'app hanno la stessa curva.

       `--r-pill` non è una terza misura: è il nome della pillola, per non scrivere 999px in
       trenta punti e per poterla spegnere in un posto solo se un giorno servisse. */
    --r:26px; --r-sm:18px; --r-pill:999px;

    /* TASK-036/A + TASK-031 — **l'unica altezza dei comandi**.
       L'audit di TASK-031 aveva trovato due difetti veri e uno solo era un colore: "Vai al menu"
       a 42 px invece di 44, e due pulsanti affiancati alti 49 e 44. Tutti e due venivano dalla
       stessa cosa: nessuno diceva quanto è alto un pulsante, e ognuno lo deduceva dal proprio
       padding. Da qui in avanti lo dice questa riga, e i comandi la ereditano. */
    --tap:48px;
    /* Il pavimento del tocco, per i comandi che stanno DENTRO una riga (Aggiungi, +/−, le icone
       del pannello): 48 px lì spezzerebbero la riga del piatto. 44 è il minimo delle linee guida,
       e sotto non scende niente. Due misure in tutto, tutte e due dichiarate qui. */
    --tap-min:44px;
  }

  /* ========== MAPPA ANTRACITE — solo il pannello del bar ==========
     Non segue il tema del telefono: è una scelta, non una preferenza di sistema. Il pannello sta
     acceso tutto il servizio dietro il banco, spesso su uno schermo fisso: lì lo scuro riposa e
     fa risaltare gli ordini.

     **Qui dentro non entrano né il navy né il bordeaux del logo.** È tutto antracite e scritte
     bianche: l'identità la porta la scritta del marchio nell'header, non il colore delle
     superfici. Comandi e accenti si distinguono per **peso e forma** — pieno chiaro contro
     contorno — invece che per tinta, che è come funzionano le interfacce monocromatiche.

     Restano a colori **solo gli stati**, che sono informazione e non decorazione: verde "pronto",
     arancio "in preparazione", grigio "annullato". Dietro il banco quei tre si leggono di
     traverso e da lontano; renderli tutti bianchi vorrebbe dire leggere ogni riga.

     Si accende con `data-tema="gestore"` sull'`<html>`, messo da `layout_head()`. ========== */
  :root[data-tema="gestore"]{
    color-scheme: dark;

    --bg:var(--ant-950);              /* pagina: antracite profondo, non nero */
    --surface:var(--ant-900);         /* schede */
    --surface-raised:var(--ant-800);  /* campi e superfici in rilievo */
    --ink:var(--ant-50);              /* testo principale */
    --ink-2:var(--ant-200);
    --ink-muted:var(--ant-400);       /* 5.20:1 sulle schede */
    --border:var(--ant-700);
    --border-strong:var(--ant-400);   /* al buio i bordi fanno il lavoro delle ombre — 4.43:1 */

    /* comandi: pieno chiaro con testo antracite. In un'interfaccia senza tinte è il contrasto
       a dire "questo si tocca", non il colore. */
    --action:var(--ant-100);
    --action-hover:var(--ant-50);
    --action-ink:var(--ant-950);      /* 15.27:1 dentro il pulsante */
    --action-text:var(--ant-100);     /* accenti e link: bianchi, distinti dal peso */
    --action-soft:var(--ant-800);
    --action-line:var(--ant-400);     /* bordo di "selezionato" — 5.20:1 */
    --action-badge:var(--ant-300);    /* pastiglia del contatore, dentro il pulsante chiaro */
    --focus:var(--ant-100);           /* anello da tastiera: bianco, si vede su tutto */

    /* stati: informazione, non tema */
    --ok:var(--ok-300);      --ok-soft:var(--ok-900);   --ok-line:var(--ok-500);
    --warn:var(--wa-300);    --warn-soft:var(--wa-900); --warn-line:var(--wa-500);
    /* "elimina" e "annulla": in monocromatico si distinguono col contorno e con l'icona,
       non col rosso. Il testo resta chiaro, il fondo è quello delle schede. */
    --danger:var(--ant-100); --danger-soft:var(--ant-800); --danger-line:var(--ant-300);
    --neutral:var(--ant-300);      --neutral-soft:var(--ant-800);
    --disabled-bg:var(--ant-800);  --disabled-ink:var(--ant-350);
    --track:var(--ant-950);        --track-active:var(--ant-600);
    --scrim:rgba(0,0,0,.72);       /* su fondo scuro il velo deve essere più fitto */

    /* --- superfici che nel tema dei clienti sono navy: qui diventano antracite --- */
    --navy-surface:var(--ant-900);    /* header del pannello */
    --navy-surface-2:var(--ant-950);
    --on-navy:var(--ant-50);
    --on-navy-muted:var(--ant-300);   /* "Esci" — 8.17:1 */
    --navy-line:var(--ant-500);       /* bordo di "Esci" — 3.37:1 */
    --navy-pill:var(--ant-800);
    --on-navy-pill:var(--ant-100);
    --thumb-base:var(--ant-900);      /* fondo delle miniature senza foto */
    --thumb-bg:linear-gradient(150deg, var(--ant-800), var(--ant-950));
    --thumb-mono:var(--ant-500);      /* monogramma — 3.37:1 */
    --thumb-cap:var(--ant-300);       /* didascalia "foto in arrivo" — 8.17:1 */
    --thumb-edge:var(--ant-600);

    /* al buio le ombre non si vedono: il lavoro lo fanno i bordi */
    --shadow:0 1px 2px rgba(0,0,0,.40), 0 8px 24px rgba(0,0,0,.45);
    --shadow-sm:0 1px 2px rgba(0,0,0,.45);
    --shadow-lg:0 -10px 40px rgba(0,0,0,.55);
    --glow-action:0 6px 18px rgba(0,0,0,.50);
    --page-wash:none;                 /* niente alone bordeaux: il fondo è pulito */
  }

  /* Visibile solo a chi legge lo schermo: serve per i titoli di struttura che a video
     sarebbero ridondanti ma senza i quali la pagina non ha gerarchia. */
  .visually-hidden{position:absolute; width:1px; height:1px; margin:-1px; padding:0;
    overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0}

  /* ============================================================
     Misure del layout. Le distanze usano clamp() invece di scalini a ogni
     larghezza; le colonne dichiarano solo la loro larghezza MINIMA e la griglia
     decide da sola quante ce ne stanno.
     In rem, non in px: se l'utente ingrandisce il testo di sistema, le colonne
     si allargano con lui invece di stringere le parole.
     ============================================================ */
  :root{
    --gutter: clamp(16px, 1rem + 1vw, 32px);    /* margine interno di main */
    --gap:    clamp(11px, .6rem + .5vw, 20px);  /* distanza fra le celle */

    --col-piatto:   15rem;   /* 240px — scheda piatto leggibile con la foto */
    /* 152px — la colonna del piatto **sul telefono** (TASK-037). Il numero non è estetico, è il
       solo che dà due colonne dove servono e una dove due non ci starebbero:
         390px → 358 di traccia, 2×152+12 = 316  → due colonne da 173
         360px → 328 di traccia, 2×152+11 = 315  → due colonne da 158
         320px → 288 di traccia, 315 non ci sta  → una colonna, e il pulsante respira
       Alzarlo sopra 158 perde la seconda colonna sugli Android stretti; abbassarlo sotto 139 la
       impone anche a 320px, dove "Aggiungi" e il prezzo finiscono uno sull'altro. */
    --col-piatto-s: 9.5rem;
    --col-ordine:   18rem;   /* 288px — un ordine senza andare a capo */
    --col-catalogo: 13rem;   /* 208px — cella di magazzino, foto riconoscibile */

    --shell-cliente:  88rem;   /* 1408px — oltre, un menu non guadagna niente */
    --shell-gestore: 108rem;   /* 1728px — e' un pannello di lavoro, puo' respirare */
    --aside-carrello: clamp(19rem, 24vw, 23rem);

    --misura: 62ch;            /* larghezza massima di lettura */

    /* Misurati da JavaScript a pagina caricata (l'header cambia altezza con la tacca del
       telefono, la tastiera con il dispositivo): qui restano i valori di ripiego, usati
       nell'istante prima che la misura arrivi e se JavaScript non parte. */
    --header-h: 0px;
    --cartbar-h: 84px;
    /* TASK-045 — **zero**: la barra in fondo non esiste più, le sue voci stanno nel pannello che
       apre il panino. Il token resta perché lo leggono ancora la quota del carrello e lo spazio in
       fondo alla pagina, e vale zero perché in fondo allo schermo non c'è più niente da scavalcare.
       Non è stato tolto del tutto per non riscrivere quei due conti con uno zero letterale dentro:
       se un giorno tornasse qualcosa in fondo, si cambia qui e basta. */
    --tabbar-h: 0px;
    /* Quanto spazio si prende il carrello che galleggia sopra la barra. Zero è il valore giusto
       di partenza — a carrello vuoto la barra non c'è — e lo alza `app.js` quando compare.
       Dichiararlo qui non è una formalità: senza, il foglio userebbe un token mai definito e
       `contrasti.py` lo direbbe, giustamente. */
    --cart-lift: 0px;
    /* altezza della barra delle sezioni: la misura app.js, ma serve un valore di partenza,
       altrimenti saltare a una sezione col JavaScript non ancora partito la lascia sotto la barra */
    --secnav-h: 0px;
    --kb: 0px;
    --gtabs-h: 3.4rem;         /* altezza delle schede appiccicate del pannello */
  }

  *{box-sizing:border-box}
  /* Il tocco deve rispondere subito e in modo nostro: il lampo grigio di Android non è dell'app,
     il doppio tocco rapido sul "+" non deve zoomare, e tenere premuto un comando non deve
     aprire il menu Copia/Incolla del sistema. */
  button, a, label.qpick, .filepick, .opt{
    -webkit-tap-highlight-color:transparent; touch-action:manipulation;
  }
  /* TASK-059 — **i comandi parlano la lingua dell'app**, e va detto una volta per tutte.
     Un `<button>`, un `<input>` e un `<select>` NON ereditano il carattere dal genitore: senza
     `font-family` vince il foglio di stile del browser, cioè il carattere di sistema. Non è
     teoria: le sette pastiglie del checkout — ritiro/consegna, asporto/tavolo,
     carta/buono/contanti, cioè **le sette decisioni di un ordine** — hanno parlato in Arial
     mentre tutto il resto era in Figtree. Nel codice non si vedeva (la regola `.opt` dichiarava
     corpo, peso e colore, sembrava completa) e sullo schermo si notava appena.
     Sta qui in cima e non sulle singole regole perché il difetto è di categoria, non di caso. */
  button, input, select, textarea, .opt{font-family:inherit}
  button, .opt{-webkit-user-select:none; user-select:none}
  button:active, .opt:active{transform:scale(.97)}
  @media (prefers-reduced-motion:reduce){ button:active, .opt:active{transform:none} }
  .btn:active, .cartbtn:active, .addbtn:active{filter:brightness(.94)}
  html,body{margin:0}
  html{scroll-padding-top:var(--header-h, 0px)}
  body{
    background:var(--bg); color:var(--ink); font-family:var(--sans);
    overscroll-behavior-y:contain;   /* blocca il "tira giù per aggiornare" di Android */
    -webkit-font-smoothing:antialiased; line-height:1.45;
    background-image:var(--page-wash);
    /* TASK-041 — Caprasimo ha **un peso solo** (400). Quaranta regole di questo foglio chiedono
       `font-weight:600/700` sui titoli e sui prezzi: senza questa riga il browser se lo fabbrica
       da sé ingrassando le aste, e su un display così tondo si vede subito che è finto. Qui si
       dice "non inventare pesi" una volta per tutte, invece di correggere quaranta regole.
       Figtree non ne risente: è variabile e i pesi 400→700 li ha davvero, quindi continua a
       usarli. E nemmeno il ripiego: Palatino e Georgia un corsivo e un neretto veri ce l'hanno,
       e la sintesi si accende solo quando non esistono. */
    font-synthesis-weight:none;
  }
  /* Con un foglio aperto la pagina sotto resta ferma. `position:fixed` sul body perderebbe
     il punto in cui si era: si blocca e si ripristina la posizione da JS. */
  body.fc-locked{position:fixed; left:0; right:0; width:100%; overflow:hidden}
  .app{
    max-width:468px; margin:0 auto; min-height:100dvh; background:var(--bg);
    display:flex; flex-direction:column; position:relative;
    box-shadow:0 0 0 1px var(--border), 0 30px 80px rgba(54,45,37,.10);
    /* in orizzontale la tacca della fotocamera sta di lato: niente contenuto sotto */
    padding-left:env(safe-area-inset-left, 0px); padding-right:env(safe-area-inset-right, 0px);
  }
  @media (min-width:520px){ .app{min-height:calc(100dvh - 32px); margin:16px auto; border-radius:22px; overflow:hidden} }

  /* ---- header ----
     Il padding alto somma la "safe area": sui telefoni con la tacca (o l'isola) per la fotocamera
     quello spazio è occupato dal sistema, e senza questo il logo ci finirebbe sotto. Dove la tacca
     non c'è, env() vale 0 e resta solo il margine normale — che teniamo generoso. */
  /* DEC-007 §1 — **la testata non è più appiccicata**. Era `position:sticky; top:0` e restava
     sopra la pagina tutto il tempo. Adesso scorre via come tutto il resto: sullo schermo del
     cliente non c'è più un solo pixel fisso, che è la richiesta presa alla lettera.
     Il padding alto somma la "safe area": sui telefoni con la tacca quello spazio è del sistema. */
  header{
    background:var(--surface); border-bottom:1px solid var(--border);
    padding:calc(20px + env(safe-area-inset-top, 0px)) 16px 12px;
    display:flex; flex-direction:column; gap:11px;
  }
  .bar{display:flex; align-items:center; justify-content:space-between; gap:12px; position:relative}
  /* Il pannello del bar ha ancora la sua testata appiccicata, e deve restare: lì si lavora, non
     si sceglie il pranzo, e la barra degli ordini serve sempre sotto mano. */
  header.ghead{position:sticky; top:0; z-index:20}
  .hdr-out{position:absolute; right:0; top:50%; transform:translateY(-50%); margin:0; line-height:0}
  .hdr-out button{border:0; background:transparent; color:var(--ink-muted); font:inherit; font-size:13px;
    font-weight:600; height:var(--tap-min); min-width:var(--tap-min); padding:0 10px; border-radius:var(--r-pill); cursor:pointer}
  .hdr-out button:active{color:var(--action-text)}
  /* TASK-043 — il marchio del mockup: cerchio pieno + nome in Caprasimo, allineati.
     Le stesse misure del pannello (`.gmono`/`.gbrand-nome` in `gestore.css`), perché è lo stesso
     marchio e non deve avere due tarature. `line-height:0` non c'è più: qui dentro adesso c'è del
     testo vero, e azzerarne l'interlinea lo taglierebbe. */
  /* TASK-059 — il marchio è anche il collegamento alla Home: `min-height` lo porta a 44 senza
     spostare niente, perché il contenuto (monogramma da 38) resta centrato dentro. */
  .logo-chip{background:transparent; padding:0; text-decoration:none; color:var(--ink);
    display:inline-flex; align-items:center; gap:10px; min-width:0; min-height:var(--tap-min)}
  .logo-mono{width:38px; height:38px; flex:none; border-radius:var(--r-pill);
    background:var(--action); color:var(--action-ink); display:grid; place-items:center;
    font-family:var(--serif); font-size:19px; line-height:1}
  .logo-nome{font-family:var(--serif); font-size:17px; line-height:1.1; white-space:nowrap;
    overflow:hidden; text-overflow:ellipsis}
  /* Sotto i 360px il nome per esteso spinge fuori l'ingranaggio: resta il cerchio, che è la parte
     che si riconosce. Il nome del bar è comunque scritto due righe più sotto, nella testata del menu. */
  @media (max-width:359.98px){ .logo-nome{display:none} }
  /* Il ripiego: se qualcuno rimette la targa (vedi la nota in `render.php`), continua a funzionare. */
  .logo-chip img{height:44px; width:auto; display:block; border-radius:8px}

  /* ---- TASK-036/B · i due comandi della testata ----
     Carrello a sinistra, ingranaggio a destra, marchio al centro. Sono le uniche due cose
     toccabili qui dentro, e stanno agli estremi: il centro è il marchio, che riporta al menu.
     `.hspace` tiene il posto quando uno dei due non c'è (schermata del codice, pagine pubbliche),
     così il marchio resta al centro invece di scivolare. */
  .hcart, .hgear, .hspace{width:var(--tap-min); height:var(--tap-min); flex:none}
  .hgear{display:grid; place-items:center; color:var(--ink-muted); border-radius:var(--r-pill); text-decoration:none}
  .hgear:active{color:var(--action-text); background:var(--action-soft)}
  .hcart{position:relative; display:grid; place-items:center; border:0; background:var(--action);
    color:var(--action-ink); border-radius:var(--r-pill); cursor:pointer; box-shadow:var(--shadow-sm)}
  .hcart[hidden]{display:none}
  .hcart-n{position:absolute; top:-4px; right:-4px; min-width:19px; height:19px; padding:0 5px;
    border-radius:999px; background:var(--ink); color:var(--surface); font-size:11px; font-weight:700;
    display:grid; place-items:center; border:2px solid var(--surface); font-variant-numeric:tabular-nums}

  .status{display:flex; align-items:center; gap:10px; justify-content:space-between}
  /* min-width:0 serve perché un nome lungo non spinga fuori la pillola degli orari */
  .status .greet{font-family:var(--serif); font-size:16px; color:var(--ink); min-width:0;
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
  .pill{display:inline-flex; align-items:center; gap:6px; font-size:12px; font-weight:600; padding:5px 10px; border-radius:999px; white-space:nowrap}
  .pill .dot{width:7px; height:7px; border-radius:50%}
  .pill.open{background:var(--ok-soft); color:var(--ok)} .pill.open .dot{background:var(--ok)}
  .pill.closed{background:var(--warn-soft); color:var(--warn)} .pill.closed .dot{background:var(--warn)}

  /* ---- scroll body ---- */
  /* Scorre il DOCUMENTO, non un contenitore interno: solo così Safari nasconde la barra
     dell'indirizzo e Android non si comporta da pagina bloccata. L'header resta sticky e
     continua a funzionare identico. Lo spazio in fondo è quello vero della barra carrello. */
  /* TASK-039 — lo spazio in fondo torna, e questa volta c'è davvero qualcosa da scavalcare: la
     barra in fondo, più la quota del carrello che le galleggia sopra quando c'è. Senza, l'ultimo
     piatto della sezione finisce sotto la barra e non lo si può toccare.
     `--cart-lift` la accende `app.js` insieme al carrello: a carrello vuoto la barra non c'è e
     riservarle spazio lascerebbe 70 px di vuoto sotto l'ultimo piatto. */
  main{flex:1; padding:18px 16px calc(28px + env(safe-area-inset-bottom, 0px))}
  main.nopad{padding-bottom:calc(28px + env(safe-area-inset-bottom, 0px))}
  /* Lo spazio in fondo lo chiede **il carrello**, l'unica cosa rimasta a galleggiare sopra la
     pagina (TASK-045: la barra in fondo non c'è più). A chiederlo è la sua presenza (`:has`), non
     una classe che qualcuno deve ricordarsi di mettere: le pagine senza carrello — la schermata
     del codice, le pagine di legge lette da fuori — non devono avere una striscia di vuoto in
     fondo per una barra che lì non c'è.
     Fuori dal `:has` resta comunque un po' d'aria: 28px, che è il respiro di fine pagina. */
  main{padding-bottom:calc(28px + env(safe-area-inset-bottom, 0px))}
  .app:has(.cartend:not(.vuoto)) main{
    padding-bottom:calc(28px + var(--tabbar-h, 0px) + var(--cart-lift, 0px) + env(safe-area-inset-bottom, 0px))}
  .fade{animation:fade .22s ease both}
  @keyframes fade{from{opacity:0; transform:translateY(4px)}to{opacity:1;transform:none}}
  @media (prefers-reduced-motion:reduce){ .fade{animation:none} }

  /* ---------- LA SCALA TIPOGRAFICA DEL MOCKUP — TASK-046 ----------
     Il design system Organic la dichiara per esteso (`_ds/organic-…/styles.css`):
       h1 42 · h2 32 · h3 25 · h4 20 · h5 16 · h6 13,
       `line-height:1.12`, `letter-spacing:-0.015em`, Caprasimo peso 400.
     L'app aveva preso da Organic i colori (TASK-039) e i caratteri (TASK-041), ma **non la
     scala**: i titoli erano tarati a occhio, senza tracking e con l'interlinea del corpo. Su un
     display tondo come Caprasimo l'interlinea larga è la cosa che fa sembrare un titolo una
     frase — è la differenza che si vede di più fra le due schermate affiancate.

     Sta in `:where()` di proposito: **specificità zero**, quindi qualunque regola più sotto che
     dichiari la sua interlinea o il suo tracking continua a vincere. Questo non riscrive niente,
     riempie i buchi. Le misure restano dove sono: qui si dice solo *come si compone* un titolo. */
  /* **Solo i titoli veri.** Misurato sul mockup: «La vetrina di oggi» (20px) porta -0.3px,
     «Bollini» (32px) -0.48px — ma «Lasagna al forno», «€ 7,50», «Aggiungi · € 3,20» e i nomi
     degli ordini hanno tracking `normal`. Nel design system il tracking sta su `h1..h6`, non su
     tutto quello che usa il carattere da display: un titolo si stringe, una riga di menu no.

     Il primo giro di questa regola comprendeva anche nomi e prezzi, e ha fatto un danno che vale
     la pena scrivere: **`letter-spacing` si eredita, e si eredita già risolto in pixel**. Un
     titolo da 25px che ne dichiara -0.015em passa ai figli -0.375px *fissi*, che su una scritta
     da 12px è quasi il triplo in proporzione. Si è visto rimisurando il pannello: i titoli delle
     colonne del kanban erano a -0.36px dove il mockup dice `normal`.

     L'interlinea invece resta larga: 1.12 è del design system e sui titoli non ha controindicazioni. */
  :where(.td-titolo, .bo-h1, .imp h1, .pt-titolo h1, .gate h1, .sheet h3, .fcbox h3,
         h1, h2, h3, h4){
    line-height:1.12; letter-spacing:-.015em;
  }
  /* Il testo da display **in linea** (nomi, prezzi, pulsanti) non si stringe, e qui si dice
     esplicitamente perche' altrimenti se lo prende in eredita' dal titolo che lo contiene.
     Vale anche per i titoli che fanno da **contenitore**: un `<h2>` messo lì per dare un nome a
     una zona a chi naviga a voce non ha una misura sua, resta ai 24px del browser, e regala ai
     figli il suo tracking in pixel. `.gcol-tit` del pannello era esattamente questo. */
  :where(.dish .name, .cnome, .cprezzo, .price, .sz-nome, .sol-nome, .trk-n, .pn-nome,
         .order .who, .qpick .nm, .drow .nm, .medit .nm, .tn-nome, .tr-nome,
         .btn, .addbtn, .cartbtn){ letter-spacing:normal; }

  /* Il titolo di sezione era una **micro-etichetta**: 13px, maiuscoletto, terracotta, tracking
     .14em. Nel mockup la stessa cosa («La vetrina di oggi», «Da riscattare») è un **h4 pieno**:
     20px di Caprasimo in inchiostro, tondo minuscolo, tracking negativo. È la differenza fra
     un'etichetta d'archivio e un titolo, e a colpo d'occhio è quella che cambia di più la pagina.
     Il filetto che tira a destra resta: è dell'app, non del mockup, e non contraddice niente. */
  h2.sec{font-family:var(--serif); font-weight:400; font-size:20px; letter-spacing:-.015em;
    line-height:1.12; text-transform:none; color:var(--ink); margin:24px 2px 12px;
    display:flex; align-items:center; gap:12px}
  h2.sec::after{content:""; flex:1; height:1px; background:var(--border)}
  h2.sec:first-child{margin-top:4px}
  /* con la barra delle sezioni appiccicata in alto, saltare a una sezione la lascerebbe
     nascosta sotto: `scroll-margin-top` è quanto spazio tenerle sopra */
  h2.sec{scroll-margin-top:calc(var(--header-h, 0px) + var(--secnav-h, 0px) + 12px)}
  h2.sec:focus{outline:none}

  /* ---- TASK-024 · la testata del giorno ----
     Un menu del pranzo è una giornata, non un catalogo: la prima cosa che si legge dice che
     giorno è, cosa c'è, e quanto tempo resta. L'orologio è l'unica cosa che si muove in tutta
     la schermata, e si muove solo quando manca poco: vedi `render_menu_testata()`. */
  .testata{margin:2px 0 16px}
  /* TASK-046 — la micro-etichetta sopra il titolo è il `.card-kicker` del design system:
     10px, `letter-spacing:.1em`, maiuscoletto, **in terracotta** e non in grigio. Era 12px/700
     con .12em: più grande, più grassa e senza tinta, cioè un titolino invece di un'etichetta.
     Il colore è `--action-text` (terra-700) e non il `#c67139` del mockup: quello a 10px sul
     crema sta a 3,6:1, sotto la soglia. Stessa famiglia, un gradino più giù, 5,4:1. */
  .td-giorno{font-size:10px; font-weight:400; letter-spacing:.1em; text-transform:uppercase;
    color:var(--action-text)}
  .td-titolo{font-family:var(--serif); font-size:29px; font-weight:400; line-height:1.12;
    color:var(--ink); margin:3px 0 0; letter-spacing:-.015em}
  .td-riga{display:flex; flex-wrap:wrap; align-items:baseline; gap:2px 16px; margin-top:7px;
    font-size:13px; color:var(--ink-muted)}
  .td-cosa{font-weight:600}
  /* Il puntino di separazione stava attaccato alla prima voce, e quando l'orologio andava a capo
     restava lì appeso in fondo alla riga da solo. Separa lo spazio, che a capo non lascia tracce. */
  .td-clock{display:inline-flex; align-items:center; gap:6px}
  .td-clock b{color:var(--ink-2); font-variant-numeric:tabular-nums}
  /* gli ultimi trenta minuti: i minuti veri. Gli ultimi dieci: il colore dell'avviso. */
  .td-clock.stretta{color:var(--ink-2); font-weight:600}
  .td-clock.stretta b{color:var(--ink)}
  .td-clock.ultima, .td-clock.chiuso{color:var(--warn); font-weight:600}
  .td-clock.ultima b{color:var(--warn)}
  .testata.chiusa .td-clock{flex:1 1 100%; background:var(--warn-soft); border-radius:var(--r-sm);
    padding:9px 11px; margin-top:3px; line-height:1.4; align-items:flex-start}

  /* ---- TASK-033 · il menu tematico si annuncia qui ----
     `DEC-007` §4: **struttura, non interruzione**. Un tema attivo non apre una fascia da chiudere
     — cambia il titolo del menu. Il costo in pixel è **zero**: «Menu del mercoledì» sta dove
     stava «Il menu di oggi», e la densità appena guadagnata con TASK-037 non viene restituita
     al primo annuncio.

     L'unica cosa che si aggiunge è il sopratitolo, ed è necessario: senza, un cliente che apre
     l'app e legge un nome diverso da quello di ieri non ha modo di capire perché. Prende il
     bordeaux del logo — l'unico accento dell'app — perché è l'unico punto della testata che deve
     farsi notare; il titolo resta del colore che ha sempre avuto. Alzare la voce due volte nella
     stessa testata sarebbe la fascia promozionale rientrata dalla finestra. */
  .td-occhiello{display:block; font-size:11.5px; font-weight:700; letter-spacing:.13em;
    text-transform:uppercase; color:var(--action-text); margin-top:5px}
  .td-titolo.td-tema{margin-top:1px}
  .td-sub{font-size:13.5px; line-height:1.45; color:var(--ink-2); margin:5px 0 0; max-width:52ch;
    text-wrap:pretty}

  /* ---- TASK-024 · le sezioni che restano a portata di pollice ----
     È quello che rende navigabile un menu lungo, e funziona anche senza una foto: per questo è
     la prima cosa costruita del restyle. Sotto le quattro sezioni non viene nemmeno disegnata
     (vedi `render_menu_sezioni()`): navigare fra tre voci già tutte visibili non è navigare. */
  /* DEC-007 §1b — la barra **resta**, ma non è più appiccicata: sta in cima al menu e scorre con
     lui. Le due risposte di Andrea (via tutto ciò che è fisso · una pagina che scorre con la barra
     delle sezioni) stanno insieme solo così. Le ancore continuano a funzionare identiche. */
  .secnav{display:flex; gap:7px;
    overflow-x:auto; scrollbar-width:none; scroll-behavior:smooth;
    padding:9px 16px; margin:0 -16px 12px; background:var(--bg);
    box-shadow:0 1px 0 var(--border)}
  .secnav::-webkit-scrollbar{display:none}
  /* Erano 38 px, e nessuno se n'era accorto: l'audit di TASK-031 aveva un buco dichiarato — il
     selettore non copriva le pastiglie delle sezioni. Le ha misurate il browser, non il CSS. */
  /* TASK-046 — la pastiglia spenta è **vuota**, non piena. Nel mockup i filtri sono contorno su
     niente, e solo quello acceso si riempie di terracotta: così l'acceso è l'unica cosa piena
     della fila e si trova senza leggere. Le nostre erano tutte piene di `--surface`, e l'acceso
     era una fra otto pastiglie colorate uguali di forma. Testo in inchiostro pieno e peso 400,
     come il mockup: erano `--ink-2` a 600, cioè più grigie e più grasse. */
  .secchip{flex:none; display:inline-flex; align-items:center; min-height:var(--tap-min); padding:0 15px;
    border-radius:999px; border:1px solid var(--border-strong); background:transparent;
    color:var(--ink); font-size:13px; font-weight:400; letter-spacing:.02em;
    text-decoration:none; white-space:nowrap;
    transition:background .14s ease, color .14s ease, border-color .14s ease}
  .secchip.on{background:var(--action); border-color:var(--action); color:var(--action-ink)}
  .secchip[hidden]{display:none}
  /* TASK-035 — «Menu» è la schermata d'ingresso, non una sezione di piatti. Il punto davanti la
     stacca dalle altre senza cambiarle forma: la barra resta una fila di pastiglie uguali, e non
     diventa due cose. */
  .secchip-menu::before{content:"•"; margin-right:7px; opacity:.55; font-size:15px; line-height:0}

  /* ---- TASK-035 · l'indice delle sezioni, sulla schermata Menu ----
     Risponde a «cosa c'è oggi» senza far leggere tredici nomi di piatto: quante cose per tipo, e
     il prezzo da cui si parte. Due colonne anche sul telefono, come le schede dei piatti: sei
     sezioni stanno in tre righe invece che in sei, e si vedono tutte senza scorrere. */
  .sezindex{display:grid; gap:var(--gap);
    grid-template-columns:repeat(auto-fill, minmax(min(9.5rem, 100%), 1fr))}
  /* TASK-039 — la pastiglia del mockup, con dentro l'informazione che il mockup non aveva.
     Il pallino colorato è l'unica cosa presa di peso: dà a ogni sezione una tinta che si
     riconosce prima di leggere il nome, e a scorrere la Home è quello che si guarda. */
  /* TASK-043 — pillola intera, com'è nel mockup («Cosa ti va»: `border-radius:999px`, pallino a
     sinistra, nome in Caprasimo). Erano 22px, cioè un rettangolo stondato: accanto alla barra
     delle sezioni — che le pastiglie ce l'ha da TASK-039 — si vedeva che erano due forme diverse
     per la stessa cosa. Il fianco cresce da 14 a 18px: dentro una pillola il testo che parte
     dal punto in cui la curva è ancora stretta sembra schiacciato contro il bordo. */
  .sezcard{display:flex; align-items:center; gap:11px; text-decoration:none;
    background:var(--surface); border:1px solid var(--border); border-radius:var(--r-pill);
    box-shadow:var(--shadow-sm); padding:12px 18px; min-height:var(--tap);
    transition:border-color .14s ease, box-shadow .14s ease}
  .sz-blob{width:26px; height:26px; border-radius:50%; flex:none; background:var(--tinta, var(--terra-300))}
  .sz-testo{display:flex; flex-direction:column; gap:1px; min-width:0}
  .sz-nome{font-family:var(--serif); font-size:16px; font-weight:600; color:var(--ink);
    line-height:1.18; letter-spacing:-.011em}
  .sz-meta{font-size:12px; color:var(--ink-muted)}
  .sz-da{white-space:nowrap}
  /* Sei tinte, assegnate per posizione: la stessa sezione ha lo stesso pallino a ogni apertura
     perché l'ordine delle categorie è quello del pannello (`categories.sort_order`), che non
     cambia da solo. Sono i gradini 300/400 delle tre scale, cioè valori uguali fra loro: sul
     crema nessuna delle sei pesa più delle altre. */
  .tinta-0{--tinta:var(--terra-300)}  .tinta-1{--tinta:var(--oliva-300)}
  .tinta-2{--tinta:var(--terra-400)}  .tinta-3{--tinta:var(--oliva-400)}
  .tinta-4{--tinta:var(--org-400)}    .tinta-5{--tinta:var(--terra-200)}
  /* TASK-060 — quando c'è qualcosa da dire, il pallino lo dice, e la tinta a rotazione si fa da
     parte. In questa app un pallino colorato significa già qualcosa (quello verde in testata dice
     «ordini aperti»): due grammatiche diverse nella stessa schermata insegnano a non fidarsi di
     nessuna delle due. Il nome della disponibilità sta in `aria-label`, perché un colore da solo
     non è un'informazione per chi non lo distingue. */
  .sezcard.sz-agli-ultimi .sz-blob{--tinta:var(--warn)}
  .sezcard.sz-finita{opacity:.62}
  .sezcard.sz-finita .sz-blob{--tinta:var(--disabled-bg); box-shadow:inset 0 0 0 2px var(--border-strong)}
  @media (hover:hover){
    .sezcard:hover{border-color:var(--action-line); box-shadow:0 2px 4px rgba(46,43,37,.08), 0 12px 28px rgba(46,43,37,.12)}
    .sezcard:hover .sz-nome{color:var(--action-text)}
  }
  @media (prefers-reduced-motion:reduce){ .sezcard{transition:none} }

  /* ---- TASK-039 · «Il tuo solito» ----
     La scommessa della Home: chi ordina il pranzo dall'ufficio ordina quasi sempre la stessa cosa.
     Sta su fondo oliva tenue e non terracotta perché **non è un comando del bar**: è una cosa del
     cliente, e il terracotta in questa app vuol dire "questo lo fa succedere l'app". */
  .solito{background:var(--oliva-100); border:1px solid var(--oliva-300); border-radius:var(--r);
    padding:14px; margin:0 0 16px; display:flex; flex-direction:column; gap:12px}
  .sol-top{display:flex; align-items:flex-start; gap:11px}
  .sol-ico{width:38px; height:38px; border-radius:50%; flex:none; display:grid; place-items:center;
    background:var(--oliva-200); color:var(--oliva-700)}
  .sol-txt{display:flex; flex-direction:column; gap:1px; min-width:0}
  .sol-kick{font-size:11.5px; font-weight:700; letter-spacing:.07em; text-transform:uppercase; color:var(--oliva-700)}
  .sol-nome{font-family:var(--serif); font-size:16px; font-weight:600; color:var(--ink); line-height:1.25}
  /* TASK-055 — le scelte che l'ordine in un tocco si porta dietro. Sta **sopra** i due pulsanti
     e non sotto: si legge prima di toccare, non dopo. */
  .sol-scelte{font-size:12px; line-height:1.4; margin:-4px 0 0 49px}
  /* Due comandi, non uno: «Ordina» prende lo spazio perché è quello che si usa ogni giorno,
     «Modifica» resta accanto per il giorno in cui si cambia idea. Su schermi molto stretti
     tornano uno sopra l'altro invece di stringersi sotto i 44px. */
  .sol-act{display:flex; gap:8px; flex-wrap:wrap}
  .sol-act .btn{flex:1 1 62%; min-width:11rem}
  .sol-act .btn.ghost{flex:0 1 auto; min-width:7.5rem}

  /* ---- TASK-039 · la vetrina a carosello ----
     Le foto grandi escono dall'elenco e vengono qui. È il pezzo che fa il lavoro che TASK-037
     aveva provato a fare stringendo le schede: chi vuole *vedere* guarda il carosello, chi vuole
     *scegliere in fretta* scorre l'elenco compatto. Due bisogni, due posti, invece di un
     compromesso solo che non accontentava nessuno dei due. */
  .sec-riga{display:flex; align-items:baseline; justify-content:space-between; gap:12px}
  .sec-riga h2.sec{flex:1}
  .sec-tutto{font-size:13px; white-space:nowrap; padding:6px 0}
  /* I margini negativi fanno arrivare il carosello ai bordi dello schermo: una fila che si ferma
     16 px prima del bordo sembra finita, e non si prova a scorrerla. Il padding li restituisce
     dentro, così la prima e l'ultima scheda non toccano il vetro. */
  /* `scroll-padding-inline` non è un di più: senza, la prima scheda finisce **incollata al bordo
     dello schermo**, e non per un errore di padding. Con `scroll-snap-type: mandatory` il browser
     accosta subito il primo elemento al bordo dello *scrollport*, che comincia dentro il padding:
     scorre di 16 px da solo e se li mangia. Misurato: `prima_scheda_left = 0` invece di 16.
     `scroll-padding` dice dove sono le posizioni di aggancio, e le rimette dove si vedono. */
  .carosello{display:flex; gap:12px; overflow-x:auto; scroll-snap-type:x mandatory;
    margin:0 -16px; padding:4px 16px 8px; scroll-padding-inline:16px;
    -webkit-overflow-scrolling:touch; scrollbar-width:none}
  .carosello::-webkit-scrollbar{display:none}
  .cvoce{flex:none; width:11.5rem; scroll-snap-align:start; text-decoration:none;
    display:flex; flex-direction:column; gap:2px}
  .cfoto{position:relative; display:block; aspect-ratio:4/3; border-radius:20px; overflow:hidden;
    background:var(--thumb-bg); background-color:var(--thumb-base)}
  .cfoto img{width:100%; height:100%; object-fit:cover; display:block}
  .cbadge{position:absolute; top:8px; left:8px; background:var(--bg); color:var(--ink);
    font-size:11px; font-weight:700; padding:4px 9px; border-radius:999px; box-shadow:var(--shadow-sm)}
  /* La formula: nome e prezzo con le misure del mockup (17px per tutti e due) e il prezzo
     **in inchiostro**, non in terracotta. Nel mockup nessun prezzo è colorato: il terracotta è
     riservato ai comandi, e usarlo anche sui numeri toglieva al pulsante la sua unicità. */
  .cnome{font-family:var(--serif); font-size:17px; font-weight:400; color:var(--ink);
    line-height:1.12; margin-top:8px}
  .cprezzo{font-family:var(--serif); font-size:17px; font-weight:400; color:var(--ink);
    font-variant-numeric:tabular-nums}
  @media (prefers-reduced-motion:reduce){ .secnav{scroll-behavior:auto} .secchip{transition:none} }

  /* ---- dish card ---- */
  .card{background:var(--surface); border:1px solid var(--border); border-radius:var(--r); box-shadow:var(--shadow); overflow:hidden}
  .dish{position:relative; display:grid; grid-template-columns:76px 1fr; gap:13px; padding:12px; align-items:stretch}
  /* Tutta la scheda apre la pagina del piatto (TASK-025): il collegamento è steso sopra, non
     attorno, così la griglia resta quella. I comandi del carrello gli passano davanti — toccare
     "Aggiungi" deve aggiungere, non aprire una pagina. */
  .dish-open{position:absolute; inset:0; z-index:1; border-radius:inherit}
  .dish .act, .dish .disp, .dish .stamp{position:relative; z-index:2}
  .dish.vetrina .disp, .dish .stamp{position:absolute}
  .dish:hover .name{color:var(--action-text)}
  .dish + .dish{border-top:1px solid var(--border)}
  /* `align-self:start` e `width:100%` non sono rifiniture, tengono insieme la scheda.
     Senza, la miniatura si allunga fino all'altezza della riga (`align-items:stretch` qui sopra)
     e `aspect-ratio` le fa seguire la larghezza: su una scheda alta 145px diventava un quadrato
     di 121px dentro una colonna di 76, e siccome è `position:relative` si disegnava SOPRA il
     nome del piatto, tagliandolo. Si vedeva su qualunque piatto con la descrizione su due righe.
     Trovato con Playwright il 26/07: le misure a video dicevano che era tutto a posto (il testo
     stava nella sua colonna), ma la fotografia della pagina mostrava mezzo nome sparito. */
  /* TASK-039: raggio 20px come nel mockup — la miniatura smette di essere un riquadro con gli
     angoli smussati e diventa una cosa posata sulla riga. È la stessa forma del pallino delle
     sezioni e delle schede del carosello: tre elementi diversi, una sola idea di angolo. */
  .thumb{position:relative; align-self:start; width:100%; border-radius:20px; overflow:hidden; background:var(--thumb-bg); aspect-ratio:1/1; display:grid; place-items:center; border:1px solid rgba(254,251,247,.06)}
  .thumb::before{content:attr(data-mono); font-family:var(--serif); font-weight:600; font-size:40px; color:rgba(254,251,247,.11); line-height:1}
  /* TASK-059 — 10px, non 8,5: sotto i 10 il maiuscoletto spaziato smette di essere testo e
     diventa una texture. Il contrasto era ed è a norma (`contrasti.py` misura --thumb-cap su
     --thumb-base): qui il problema era solo il corpo. */
  .thumb .cam{position:absolute; bottom:5px; left:0; right:0; text-align:center; font-size:10px; letter-spacing:.08em; text-transform:uppercase; color:var(--thumb-cap)}
  .thumb .edge{position:absolute; inset:5px; border:1px solid var(--thumb-edge); border-radius:7px; pointer-events:none}
  .thumb.has{background:var(--neutral-soft)}
  .thumb.has img{width:100%; height:100%; object-fit:cover; display:block}
  .thumb.has .edge{border-color:rgba(254,251,247,.35)}
  .dish .body{display:flex; flex-direction:column; min-width:0}
  /* Il nome del piatto è la voce della schermata, e stava largo: l'interlinea di un paragrafo e
     nessuna crenatura. Un serif da titolo a 17px con le lettere leggermente ravvicinate legge
     come il nome di un piatto su una carta, non come una riga di elenco — ed è metà del
     "moderno" che il restyle non aveva ancora dato (l'altra metà è il prezzo, qui sotto). */
  .dish .name{font-family:var(--serif); font-size:16px; font-weight:400; color:var(--ink);
    line-height:1.12}
  /* ---- la descrizione del piatto ----
     È la sola riga che dice cosa c'è dentro, e stava stretta: 12,5px con l'interlinea di un
     sottotitolo, stesa per tutta la larghezza della scheda e col rischio di lasciare una parola
     sola in fondo.
       · un gradino più grande e più aria fra le righe: si legge di scorsa, che è come si legge
         un menu, non parola per parola;
       · una misura massima: oltre le ~44 battute l'occhio perde il capo della riga dopo,
         e su un tablet la descrizione arrivava da un bordo all'altro;
       · `text-wrap:pretty` toglie la parola orfana in fondo (dove non c'è, non cambia niente);
       · `hyphens:none` perché "besciamella" spezzata a metà in un menu è peggio di una riga corta. */
  .dish .desc{font-size:13px; color:var(--ink-muted); margin-top:3px; line-height:1.45;
    max-width:44ch; text-wrap:pretty; hyphens:none; overflow-wrap:break-word}
  /* A ordini chiusi il pulsante spento porta scritto il motivo ("Apre alle 10:30") ed è più largo
     di "Aggiungi": senza wrap schiaccerebbe prezzo e porzioni sui telefoni stretti. */
  .dish .row{margin-top:auto; padding-top:8px; display:flex; align-items:center; justify-content:space-between; gap:8px; flex-wrap:wrap}
  .dish .act{margin-left:auto}
  .prezzo-e-scorte{display:flex; align-items:center; gap:9px}

  /* ============ TASK-024 · la scheda in vetrina ============
     L'idea copiata dalle grandi app di ordinazione è una sola, e non sono i colori: **la foto è
     il prodotto**. Si sceglie guardando, non leggendo. Da qui il resto — nome grande, prezzo
     forte, tutto il resto sottotono.
     Quando la foto non c'è, al suo posto non va un rettangolo grigio: va il piatto navy col
     monogramma, che è una cosa disegnata e non un buco. Ed è `menu_layout()` a decidere se
     questo registro ha senso oggi, guardando quante foto esistono davvero. */
  .dish.vetrina{grid-template-columns:1fr; gap:0; padding:0}
  .dish.vetrina + .dish.vetrina{border-top:1px solid var(--border)}
  .shot{position:relative; aspect-ratio:1/1; background:var(--thumb-bg); overflow:hidden;
    display:grid; place-items:center}
  .shot.has{background:var(--neutral-soft)}
  .shot img{width:100%; height:100%; object-fit:cover; display:block}
  .shot::before{content:attr(data-mono); font-family:var(--serif); font-weight:600; font-size:88px;
    line-height:1; color:rgba(254,251,247,.13)}
  .shot.has::before{content:none}
  .shot .edge{position:absolute; inset:9px; border:1px solid var(--thumb-edge); border-radius:9px;
    pointer-events:none}
  .shot.has .edge{border-color:rgba(254,251,247,.34)}
  .shot .cam{position:absolute; bottom:14px; left:0; right:0; text-align:center; font-size:11px;   /* TASK-059 */
    letter-spacing:.1em; text-transform:uppercase; color:var(--thumb-cap)}
  .dish.vetrina .body{padding:13px 14px 14px}
  .dish.vetrina .name{font-size:21px; line-height:1.15; letter-spacing:-.01em; text-wrap:balance}
  .dish.vetrina .desc{font-size:14px; margin-top:5px; line-height:1.5; color:var(--ink-2)}
  .dish.vetrina .alg{font-size:12px; margin-top:6px}
  .dish.vetrina .price{font-size:21px}
  .dish.vetrina .row{padding-top:12px}

  /* La disponibilità sta sulla foto e non fra le informazioni: è l'unica cosa della scheda che
     si legge senza leggere, ed è quella che fa ordinare adesso invece che alle 12:55. */
  .disp{position:absolute; left:10px; bottom:10px; z-index:2; font-size:11.5px; font-weight:700;
    padding:4px 10px; border-radius:999px; backdrop-filter:saturate(1.4) blur(6px);
    background:color-mix(in srgb, var(--surface) 88%, transparent); color:var(--ink-2);
    box-shadow:var(--shadow-sm)}
  .disp.low{background:var(--warn-soft); color:var(--warn)}
  .disp.no{background:var(--neutral-soft); color:var(--neutral)}
  .dish.riga .disp{display:none}   /* su 76px non ci sta, e lì la pastiglia resta accanto al prezzo */

  /* Il timbro "Esaurito" — la firma di questa schermata.
     Non è un adesivo: è il gesto vero del bar, che sulla lavagna tira una riga sul piatto finito
     quando la teglia è vuota. Serve a capire che è finito senza leggere niente, ed è l'unico
     punto in cui questo restyle alza la voce. Tutto il resto attorno sta zitto. */
  .dish.finito .shot img, .dish.finito .thumb.has img{filter:grayscale(.85) brightness(.72)}
  .dish.finito .body{opacity:.62}
  .stamp{position:absolute; top:50%; left:50%; z-index:3;
    transform:translate(-50%,-50%) rotate(-8deg);
    font-family:var(--serif); font-size:19px; font-weight:600; letter-spacing:.16em;
    text-transform:uppercase; color:var(--bord-300);
    border:2px solid var(--bord-300); border-radius:7px; padding:5px 15px;
    background:rgba(63,4,18,.30); white-space:nowrap}
  .stamp.mini{font-size:9px; letter-spacing:.08em; padding:2px 5px; border-width:1px; border-radius:4px}

  /* Da tablet in su la vetrina diventa davvero una vetrina: due o tre schede affiancate.
     Le colonne le dà già `.card` (griglia automatica); qui si accompagna solo la tipografia. */
  @media (min-width:560px){
    .dish.vetrina .name{font-size:19px}
    .shot::before{font-size:72px}
  }

  /* ============ TASK-037 · due colonne anche sul telefono ============
     La vetrina di TASK-024 ha risolto *come si sceglie* (guardando) e ha creato il problema
     opposto: **quanto si vede**. Misurato in produzione a 390px, una scheda con la foto era alta
     517px, cioè il **61% dello schermo**. Un piatto e mezzo per schermata; col menu vero da 13
     piatti, otto o nove schermate, e due primi non si vedono mai insieme — per confrontarli
     bisogna ricordarsi il primo mentre si scorre verso il secondo. È esattamente il contrario di
     *"il cliente non deve perdere tempo per leggere ma per vedere e aggiungere al carrello"*.

     La colonna singola non era una scelta: era il non aver esteso al telefono la griglia che da
     640px in su c'era già. Qui la si estende, con la misura giusta per lo schermo piccolo.

     Tre cose vanno tenute a mente leggendo questo blocco:

     · **Vale solo per `vetrina` e `misto`**, cioè dove le foto ci sono. Il registro `elenco` è
       già compatto (riga da 144px, 17% di schermo, cinque piatti a schermata) e resta la lista
       incorniciata con i divisori che è sempre stata: gridarlo l'avrebbe solo peggiorato.
     · **La riga compatta, dentro `misto`, prende la larghezza piena** (`grid-column:1/-1`).
       Schiacciarla in mezza colonna avrebbe messo una miniatura da 76px accanto a 80px di testo.
       Così invece il registro `misto` guadagna un ritmo leggibile: chi ha la foto sta in vetrina
       a due per riga, chi non ce l'ha passa in fascia piena. È la stessa gerarchia di prima —
       *quello che fotografi si prende la scheda grande* — detta con la larghezza invece che con
       l'altezza.
     · **Non si tocca niente sopra i 640px**: questo blocco è tutto dentro un `max-width`, e la
       regola dura del foglio ("niente sopra i 640 cambia come si vede il telefono") vale anche
       al contrario.

     Il prezzo che si paga è la descrizione tagliata a due righe. Non sparisce: la scheda intera
     è un collegamento alla pagina del piatto (TASK-025), che è il posto dove la descrizione sta
     per intero e senza fretta. Gli allergeni **non** si toccano: stanno sulla scheda perché
     l'art. 14 li vuole dove si compra (DEC-006), e sono l'unica cosa qui dentro che non si
     accorcia per far posto. ============================================================ */
  @media (max-width:639.98px){
    :is(.menu-vetrina, .menu-misto) .card{
      background:transparent; border:0; box-shadow:none; overflow:visible;
      display:grid; gap:var(--gap);
      /* il min() e' obbligatorio: senza, sotto la larghezza della traccia la griglia sfonda */
      grid-template-columns:repeat(auto-fill, minmax(min(var(--col-piatto-s), 100%), 1fr));
      /* `stretch` e non `start`: le due schede di una riga finiscono alla stessa altezza, e i
         due "Aggiungi" si allineano invece di sfalsarsi di una decina di pixel a seconda di
         quanto è lungo il nome del piatto. Non si vede nell'HTML — si vede in una schermata,
         ed è così che è saltato fuori. Perché funzioni serve la riga qui sotto: senza
         `auto 1fr` l'altezza in più andrebbe alla foto invece che al testo, e le due foto
         della stessa riga verrebbero di due formati diversi. */
      align-items:stretch;
    }
    :is(.menu-vetrina, .menu-misto) .card > .dish.vetrina{grid-template-rows:auto 1fr}
    :is(.menu-vetrina, .menu-misto) .card > .dish{
      background:var(--surface); border:1px solid var(--border);
      border-radius:var(--r); box-shadow:var(--shadow); overflow:hidden;
    }
    /* la cornice ce l'ha gia' ogni scheda: il divisore di lista qui e' una riga di troppo */
    :is(.menu-vetrina, .menu-misto) .card > .dish + .dish{border-top:0}
    /* la riga compatta non entra in mezza colonna: passa in fascia piena (vedi sopra) */
    :is(.menu-vetrina, .menu-misto) .card > .dish.riga{grid-column:1 / -1}

    /* ---- la scheda in vetrina, a mezza larghezza ----
       La foto passa da quadrata a 4/3. È il taglio che rende: su una colonna da 173px sono 43px
       di altezza risparmiati per scheda, e con `object-fit:cover` la foto non si deforma, si
       centra. Il quadrato pieno resta dov'è l'unica cosa sullo schermo: nella pagina del piatto. */
    .menu-vetrina .shot, .menu-misto .shot{aspect-ratio:4/3}
    .menu-vetrina .shot::before, .menu-misto .shot::before{font-size:56px}
    /* meno cornici: il filetto interno sulla foto è una rifinitura da scheda grande.
       A 173px diventa rumore, e toglierlo è metà del "più pulito" che si vede a occhio. */
    .menu-vetrina .shot .edge, .menu-misto .shot .edge{display:none}
    .menu-vetrina .shot .cam, .menu-misto .shot .cam{bottom:9px; font-size:10px}   /* TASK-059 */

    :is(.menu-vetrina, .menu-misto) .dish.vetrina .body{padding:11px 11px 12px}
    /* il nome scende da 21 a 15: a mezza larghezza 21px mandava a capo quasi ogni piatto.
       Due righe sono il tetto — "Insalata di farro con verdure grigliate" ci sta, e oltre
       il nome si legge sulla pagina del piatto.

       **Era 16px e con Caprasimo non bastava più** (TASK-041). Il display del mockup è più largo
       del Palatino che c'era prima: alla stessa misura mandava a capo schede che prima stavano
       su una riga, e siccome le due schede di una riga sono legate (`align-items:stretch`) ne
       bastava una per alzare tutta la fila. Misurato: 326 → 346px, cioè 41% di schermo contro il
       tetto di 40 che TASK-037 aveva conquistato scendendo da 61.

       15px non è una rinuncia: è la misura che il mockup usa davvero per i nomi in lista
       (`font-family:var(--font-heading);font-size:15px` sulle righe del carrello, dei premi e
       del riepilogo). Il tetto delle due righe resta dov'era — quello serve ai nomi lunghi. */
    :is(.menu-vetrina, .menu-misto) .dish.vetrina .name{
      font-size:15px; line-height:1.22; letter-spacing:-.014em;
      display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden}
    :is(.menu-vetrina, .menu-misto) .dish.vetrina .desc{
      font-size:12.5px; line-height:1.4; margin-top:3px;
      display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden}
    :is(.menu-vetrina, .menu-misto) .dish.vetrina .alg{font-size:11px; margin-top:5px}

    /* ---- prezzo e "Aggiungi" ----
       A mezza larghezza non ci stanno affiancati: 173px di colonna meno 22 di padding fanno 151,
       e "€ 5,50" più il pulsante ne chiedono ~145 nel caso buono e di più con la formula del
       prezzo a due cifre. Invece di stringere il pulsante — che è il comando principale della
       schermata e sotto i 44px il dito lo sbaglia — si mettono su due righe: il prezzo sopra,
       grande, e il pulsante sotto per tutta la larghezza della scheda. Il bersaglio da toccare
       diventa più grande, non più piccolo. */
    :is(.menu-vetrina, .menu-misto) .dish.vetrina .row{
      flex-direction:column; align-items:stretch; gap:8px; padding-top:9px; flex-wrap:nowrap}
    :is(.menu-vetrina, .menu-misto) .dish.vetrina .act{margin-left:0; width:100%}
    :is(.menu-vetrina, .menu-misto) .dish.vetrina .price{font-size:18px}
    :is(.menu-vetrina, .menu-misto) .dish.vetrina .addbtn{width:100%}
    :is(.menu-vetrina, .menu-misto) .dish.vetrina .step{width:100%; justify-content:space-between}
  }
  /* Il prezzo era 15px, cioè la stessa voce del testo attorno: in un menu è una delle due cose
     che si cercano (l'altra è la foto), e cercarla non deve costare niente. Sale di un gradino
     qui e di due in vetrina, dove c'è lo spazio per farlo. Non cambia colore e non prende un
     riquadro: si fa vedere con la dimensione, che è il modo che non aggiunge cose colorate. */
  /* TASK-046 — il prezzo parla con la voce da display, come nel mockup: Caprasimo a 15px, in
     inchiostro. Era Figtree 700 a 16: più grande e più grassa, cioè **due** modi di gridare la
     stessa cosa. Nel mockup il prezzo si distingue perché è nell'altro carattere, non perché è
     più grosso — e accanto al nome del piatto (Caprasimo 16) fa una coppia invece di una gara. */
  .price{font-family:var(--serif); font-weight:400; color:var(--ink);
    font-variant-numeric:tabular-nums; font-size:15px}
  .stock{font-size:11px; font-weight:700; padding:3px 8px; border-radius:999px; white-space:nowrap}
  .stock.ok{background:var(--ok-soft); color:var(--ok)} .stock.low{background:var(--warn-soft); color:var(--warn)} .stock.no{background:var(--neutral-soft); color:var(--neutral)}

  /* stepper */
  .step{display:inline-flex; align-items:center; gap:0; border:1px solid var(--border-strong); border-radius:999px; background:var(--surface-raised); overflow:hidden}
  /* 44px: sotto questa misura il dito sbaglia. Prima erano 30. */
  .step button{width:var(--tap-min); height:var(--tap-min); border:0; background:transparent; color:var(--action-text); font-size:18px; font-weight:600; cursor:pointer; display:grid; place-items:center; line-height:1}
  .step button:disabled{color:var(--disabled-ink); cursor:not-allowed}
  .step .q{min-width:24px; text-align:center; font-weight:700; font-variant-numeric:tabular-nums; font-size:14px}
  /* «Aggiungi» è un `.btn` a tutti gli effetti: stessa voce da display del mockup (Caprasimo 14). */
  .addbtn{border:0; background:var(--action); color:var(--action-ink);
    font-family:var(--serif); font-weight:400; font-size:14px; line-height:1.2;
    padding:8px 16px; min-height:var(--tap-min); display:inline-flex; align-items:center; justify-content:center; border-radius:999px; cursor:pointer; box-shadow:var(--shadow-sm)}
  .addbtn:disabled{background:var(--disabled-bg); color:var(--disabled-ink); cursor:not-allowed; box-shadow:none}
  /* TASK-039 — il "+" tondo del mockup, sulla riga compatta.
     Il bersaglio resta 44 px (`--tap-min`) in tutte e due le direzioni: un cerchio più piccolo
     sarebbe più elegante e si mancherebbe col pollice. Il nome accessibile lo scrive `renderActs`
     — chi naviga a voce sente "Aggiungi Lasagne al forno al carrello", non "più". */
  .addbtn.tondo{width:var(--tap-min); min-width:var(--tap-min); padding:0; font-size:19px;
    font-weight:400; line-height:1}
  /* Il contatore accanto al "+" dei piatti con varianti: quel pulsante non diventa mai +/− (non
     saprebbe a quale delle righe applicarsi), quindi "quante ne ho già preso" va detto qui. */
  .act-n{margin-left:7px; min-width:22px; height:22px; padding:0 6px; border-radius:999px;
    background:var(--action-soft); color:var(--action-text); font-size:12px; font-weight:700;
    display:inline-grid; place-items:center; font-variant-numeric:tabular-nums}

  .banner{display:flex; gap:10px; align-items:flex-start; background:var(--warn-soft); color:var(--warn); border:1px solid transparent; border-radius:var(--r); padding:12px 14px; font-size:13px; font-weight:600; margin-bottom:14px}
  .banner.ok{background:var(--ok-soft); color:var(--ok)}
  .banner[hidden]{display:none}

  /* ---- tracker ordine (cliente): stato, tempo stimato, avanzamento ---- */
  #tracker:not(:empty){display:flex; flex-direction:column; gap:10px; margin-bottom:16px}
  .trk{background:var(--surface); border:1px solid var(--border); border-radius:var(--r); box-shadow:var(--shadow); padding:14px}
  .trk.closed{box-shadow:none; border-style:dashed}
  /* TASK-039 — la testata scura del mockup, sull'ordine in corso e su quello soltanto.
     `overflow:hidden` sulla scheda e margini negativi sulla testata: la fascia arriva ai bordi
     invece di galleggiare dentro un riquadro con 14 px di crema tutt'intorno. */
  .trk.vivo{padding:0; overflow:hidden; border-color:var(--terra-800)}
  .trk.vivo > *:not(.trk-testa){padding-inline:14px}
  .trk.vivo > *:last-child{padding-bottom:14px}
  .trk-testa{padding:14px}
  .trk.vivo .trk-testa{background:var(--navy-surface); color:var(--on-navy)}
  .trk.vivo .trk-testa .trk-sub{color:var(--on-navy-muted)}
  .trk.vivo .trk-testa .trk-eta{background:rgba(255,255,255,.09); border-color:transparent; color:var(--on-navy)}
  .trk.vivo .trk-testa .trk-eta svg, .trk.vivo .trk-testa .trk-eta b{color:var(--terra-300)}
  .trk.vivo .trk-testa .trk-eta .muted{color:var(--on-navy-muted)}
  .trk.vivo .trk-steps{margin-top:16px}
  .trk-top{display:flex; justify-content:space-between; align-items:flex-start; gap:10px}
  .trk-n{font-family:var(--serif); font-size:16px; font-weight:600}
  .trk-sub{font-size:12px; color:var(--ink-muted); margin-top:1px}
  .trk-eta{display:flex; align-items:center; gap:8px; margin-top:11px; padding:9px 11px; border-radius:var(--r-sm);
    background:var(--action-soft); color:var(--ink-2); font-size:13.5px; font-weight:600}
  .trk-eta svg{color:var(--action-text); flex:none}
  .trk-eta b{font-variant-numeric:tabular-nums; color:var(--action-text)}
  .trk-eta .muted{font-weight:600}
  /* La stima calcolata da noi si distingue da un'ora promessa dal bar: fondo neutro invece che
     colorato. Sono due cose diverse e non devono sembrare la stessa — una è un impegno di chi
     cucina, l'altra è quello che dicono i pranzi passati. */
  .trk-eta.stima{background:var(--surface-raised); border:1px dashed var(--border-strong)}
  .trk-eta.stima svg{color:var(--ink-muted)}
  .trk-cancel{margin-top:10px; font-size:13px; color:var(--ink-muted)}
  .trk-steps{list-style:none; display:flex; margin:15px 0 0; padding:0}
  .trk-steps li{flex:1; position:relative; text-align:center; font-size:10.5px; font-weight:700; color:var(--ink-muted);
    letter-spacing:.01em; line-height:1.25}
  .trk-steps li::before{content:""; position:absolute; top:6px; left:-50%; width:100%; height:2px; background:var(--border)}
  .trk-steps li:first-child::before{display:none}
  .trk-steps .d{display:block; position:relative; z-index:1; width:14px; height:14px; margin:0 auto 7px;
    border-radius:50%; background:var(--surface); border:2px solid var(--border-strong)}
  .trk-steps li.done{color:var(--ink-2)}
  .trk-steps li.done::before, .trk-steps li.now::before{background:var(--action)}
  .trk-steps li.done .d{background:var(--action); border-color:var(--action)}
  .trk-steps li.now{color:var(--action-text)}
  .trk-steps li.now .d{border-color:var(--action); box-shadow:0 0 0 4px var(--action-soft)}
  @media (prefers-reduced-motion:no-preference){
    .trk-steps li.now .d{animation:beat 1.9s ease-in-out infinite}
    @keyframes beat{0%,100%{box-shadow:0 0 0 4px var(--action-soft)}50%{box-shadow:0 0 0 8px var(--action-soft)}}
  }
  .trk-items{display:flex; justify-content:space-between; align-items:baseline; gap:12px; margin-top:13px;
    padding-top:10px; border-top:1px solid var(--border); font-size:12.5px; color:var(--ink-muted)}
  .trk-items .price{font-size:14px; white-space:nowrap}

  /* ══════════════════════════════════════════════════════════════════════════
     IL PANNELLO DI NAVIGAZIONE — TASK-045, al posto della barra in fondo

     Qui c'era `.tabbar`: quattro voci in `position:fixed; bottom:0`. Su iPhone si staccava dal
     bordo dello schermo lasciando una striscia di vuoto sotto. TASK-044 ci ha provato due volte —
     misurando lo scarto fra i due viewport di iOS (peggiorò: la barra si spostava da sola) e poi
     prolungando il fondo della barra per rendere la striscia invisibile. Andrea: *"ci abbiamo
     provato in mille maniere anche in altri progetti ma non funziona"*.

     La decisione è sua: **si toglie il problema invece di mascherarlo**. Qui dentro non c'è più
     niente ancorato al fondo dello schermo, quindi non c'è un fondo da sbagliare. Il pannello si
     apre dal panino in testata, e la testata sta in cima — dove i due viewport di iOS coincidono
     sempre.
     ══════════════════════════════════════════════════════════════════════ */

  /* Il velo. `position:fixed; inset:0` è un'altra cosa dall'ancorarsi al fondo: copre tutto lo
     schermo, e se iOS sbaglia di qualche pixel il velo è appena più corto — non lascia un buco
     colorato in mezzo all'interfaccia. */
  .pn-velo{position:fixed; inset:0; z-index:120; background:var(--scrim);
    display:flex; justify-content:flex-end; animation:fcin .16s ease both}
  .pn-velo[hidden]{display:none}
  .pn-velo.out{animation:fcout .14s ease both}

  /* Entra da destra, come il comando che l'ha aperto. Larghezza a misura del contenuto, non
     dell'intero schermo: dietro si continua a vedere dove si era, che è metà del motivo per cui
     un pannello è meno spaesante di un cambio di pagina. */
  .pn{background:var(--bg); width:min(19rem, 86vw); height:100%;
    display:flex; flex-direction:column; gap:2px; overflow-y:auto; overscroll-behavior:contain;
    padding:calc(12px + env(safe-area-inset-top, 0px)) 12px calc(16px + env(safe-area-inset-bottom, 0px));
    box-shadow:var(--shadow-lg); animation:pnin .22s cubic-bezier(.2,.9,.3,1) both}
  .pn-velo.out .pn{animation:pnout .16s ease both}
  @keyframes pnin{from{transform:translateX(100%)}to{transform:none}}
  @keyframes pnout{to{transform:translateX(100%)}}
  @media (prefers-reduced-motion:reduce){
    .pn, .pn-velo, .pn-velo.out, .pn-velo.out .pn{animation:none}
  }

  .pn-testa{display:flex; align-items:center; justify-content:space-between; gap:8px;
    padding:2px 6px 10px}
  .pn-tit{font-size:11px; font-weight:700; letter-spacing:.14em; text-transform:uppercase;
    color:var(--ink-muted)}
  .pn-chiudi{width:var(--tap-min); height:var(--tap-min); border:0; background:transparent;
    color:var(--ink-2); border-radius:var(--r-pill); cursor:pointer; display:grid; place-items:center}
  .pn-chiudi:active{background:var(--action-soft); color:var(--action-text)}

  /* Ogni voce è una pillola, come tutto il resto dell'app dopo TASK-041. Alta almeno `--tap`:
     è la navigazione principale, e adesso ci si arriva con un tocco in più — quello che resta
     dev'essere comodo. */
  .pn-voce{display:flex; align-items:center; gap:12px; text-decoration:none; color:var(--ink);
    min-height:var(--tap); padding:9px 14px; border-radius:var(--r-pill);
    border:1px solid transparent}
  .pn-voce:active{background:var(--action-soft)}
  .pn-voce.on{background:var(--surface); border-color:var(--border); color:var(--action-text)}
  .pn-ico{flex:none; display:grid; place-items:center; color:var(--ink-muted)}
  .pn-voce.on .pn-ico{color:var(--action-text)}
  .pn-txt{display:flex; flex-direction:column; gap:1px; min-width:0}
  .pn-nome{font-family:var(--serif); font-size:16px; line-height:1.15}
  .pn-sotto{font-size:11.5px; color:var(--ink-muted); line-height:1.25;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap}

  /* Il panino: stesso bersaglio dell'ingranaggio che sostituisce. */
  .hpanino{border:0; background:transparent; cursor:pointer; color:var(--ink-muted)}
  .hpanino[aria-expanded="true"]{color:var(--action-text); background:var(--action-soft)}

  /* Il carrello: galleggia in fondo, e c'è solo quando ha qualcosa da dire.
     Da TASK-045 è **l'unica cosa fissa** dello schermo del cliente — la barra di navigazione non
     c'è più. Resta fissa perché DEC-007 §1b lo chiede per iscritto: da qualunque punto del menu si
     deve poter andare a pagare senza scorrere. Rispetto alla barra corre un rischio molto minore
     sullo scarto di iOS: è una pillola che galleggia con la pagina intorno, quindi qualche pixel
     più su o più giù è come dev'essere, non un buco.
     A carrello vuoto non compare — l'informazione "aggiungi un piatto per ordinare" ce l'ha già
     ogni scheda, sul suo pulsante, e ripeterla in fondo era una barra piena per non dire niente.
     A ordini chiusi invece resta: lì la frase è una notizia ("apre alle 10:30"), non un invito. */
  .cartend{position:fixed; z-index:30; left:0; right:0;
    bottom:calc(var(--tabbar-h, 0px) + env(safe-area-inset-bottom, 0px) + 14px);
    width:100%; max-width:468px; margin:0 auto; padding:0 14px;
    pointer-events:none}
  .cartend > *{pointer-events:auto}
  .cartend.vuoto{display:none}
  .cartbar{padding:12px 16px calc(14px + env(safe-area-inset-bottom))}
  .cartbtn{width:100%; border:0; border-radius:999px; background:var(--action); color:var(--action-ink); padding:14px 20px;
    font-family:var(--serif); font-weight:400; font-size:15px; line-height:1.2;
    cursor:pointer; display:flex; align-items:center; justify-content:space-between; gap:12px; box-shadow:var(--glow-action), var(--shadow)}
  .cartbtn .n{background:var(--action-badge); border-radius:999px; padding:2px 9px; font-size:12.5px}
  .cartbtn:disabled{background:var(--disabled-bg); color:var(--disabled-ink); box-shadow:var(--shadow-sm); cursor:not-allowed}
  .cartbtn .tot{font-variant-numeric:tabular-nums}
  /* La lista del carrello in colonna esiste solo sugli schermi larghi (vedi più giù): sul telefono
     resta nascosta, e dentro una barra che galleggia darebbe fastidio due volte. */
  .cartend .cartlist{display:none}

  /* ---- TASK-039 · BOLLINI ----
     La cartolina di cartone col timbro. I dieci cerchi rispondono a "quanti me ne mancano?"
     senza far fare una sottrazione a nessuno — che è tutto quello che questa schermata deve fare.
     Sta su fondo bruno profondo come nel mockup: è l'unica scheda dell'app che si prende un
     colore pieno, e le tocca perché è l'unica cosa che il cliente possiede invece di comprare. */
  /* Il titolo di pagina è l'`h2` del design system: **32px**. Nel mockup «Bollini» e «I tuoi
     ordini» sono tutti e due così, ed è la misura che dà alla schermata la sua testata. */
  .bo-h1{font-family:var(--serif); font-size:32px; font-weight:400; line-height:1.12;
    letter-spacing:-.015em; margin:2px 0 16px}
  .bo-card{background:var(--navy-surface); color:var(--on-navy); border-radius:var(--r);
    padding:20px; margin-bottom:8px; box-shadow:var(--shadow)}
  .bo-kick{font-size:11.5px; font-weight:700; letter-spacing:.07em; text-transform:uppercase;
    color:var(--on-navy-muted)}
  .bo-n{font-family:var(--serif); font-size:34px; font-weight:600; line-height:1.1; margin-top:2px;
    font-variant-numeric:tabular-nums}
  .bo-n small{font-size:17px; font-weight:600}
  .bo-hint{margin:6px 0 0; font-size:13px; color:var(--on-navy-muted)}
  /* I timbri si adattano al numero di caselle: dieci stanno in riga su 390 px, venticinque vanno
     a capo da sole. `flex-wrap` invece di una griglia a colonne fisse, così non c'è un numero
     massimo di premi scritto nel foglio di stile. */
  .bo-timbri{display:flex; flex-wrap:wrap; gap:8px; margin-top:18px}
  .bo-t{width:22px; height:22px; border-radius:50%; flex:none;
    border:2px solid var(--terra-500); background:transparent}
  .bo-t.on{background:var(--terra-400); border-color:var(--terra-400)}

  .bo-premi{display:flex; flex-direction:column; gap:10px}
  .bo-premio{display:flex; align-items:center; gap:12px; padding:11px 14px;
    border:1px solid var(--border); border-radius:22px; background:var(--surface)}
  .bo-pico{width:36px; height:36px; border-radius:50%; flex:none; display:grid; place-items:center;
    background:var(--action-soft); color:var(--action-text)}
  .bo-ptxt{flex:1; display:flex; flex-direction:column; min-width:0}
  .bo-ptxt b{font-family:var(--serif); font-size:15px; font-weight:600}
  .bo-ptxt small{font-size:12px; color:var(--ink-muted)}
  .bo-premio .btn{width:auto; flex:none}
  .bo-nota{margin-top:12px}

  .bo-mov{display:flex; flex-direction:column}
  .bo-riga{display:flex; align-items:center; justify-content:space-between; gap:12px;
    padding:11px 2px; border-bottom:1px solid var(--border)}
  .bo-riga:last-child{border-bottom:0}
  .bo-mtxt{display:flex; flex-direction:column; min-width:0}
  .bo-mtxt b{font-size:14px; font-weight:600}
  .bo-mtxt small{font-size:12px; color:var(--ink-muted)}
  .bo-delta{font-family:var(--serif); font-size:16px; font-weight:600; color:var(--ok);
    font-variant-numeric:tabular-nums; flex:none}
  .bo-delta.meno{color:var(--ink-muted)}

  /* ---- generic buttons / inputs ---- */

  /* iOS ingrandisce la pagina da solo appena si tocca un campo con testo sotto i 16px, e non torna
     più indietro da solo. 16px è la soglia: sotto non ci scende nessun campo in cui si scrive o si
     sceglie. Vale anche per i campi senza una regola propria, che altrimenti prenderebbero il
     13,3px di default del browser. La prova `campi-16px.py` fa fallire la suite se qualcuno riscende. */
  input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=hidden]),
  select, textarea{font-size:16px}

  /* ---- TASK-036/A — LO STANDARD DEI PULSANTI, in cinque righe ----
     Un pulsante è: alto `--tap`, centrato dentro di sé, e uguale che sia un `<button>` o una `<a>`.

     Le tre righe qui sotto chiudono i due difetti misurati da TASK-031, e li chiudono alla radice
     invece che uno per uno:
      · `min-height:var(--tap)` → "Vai al menu" non può più uscire a 42 px;
      · `display:inline-flex` su TUTTI, `<a>` compresi → un collegamento vestito da pulsante ha
        la stessa scatola di un pulsante vero. Era questa la causa: su una `<a>` inline il padding
        verticale non alza la riga, e la stessa classe usciva 42 px invece di 49;
      · `align-items:center` → due comandi affiancati sono alti uguale perché **partono** uguali,
        non perché qualcuno li ha pareggiati a mano.

     Chi vuole un comando più basso non abbassa l'altezza: usa `.btn.mini`, che resta comunque
     sopra i 44 px richiesti dal tocco. Non c'è una terza misura. */
  /* TASK-041 — pillola. `.addbtn` e `.cartbtn` lo erano già; questo, che è il comando di tutti i
     fogli e di tutte le schermate, era rimasto un rettangolo stondato. Nel mockup i comandi sono
     **tutti** pillole (22 elementi a 999px) e la regola del design system lo dice a parole:
     *"999px for buttons and inputs"*. Era la differenza che si vedeva di più dopo i caratteri. */
  /* TASK-046 — **i pulsanti parlano con la voce da display**. È la regola più sistemica del
     design system e l'unica che l'app non aveva mai preso:
         `.btn { font-family: var(--font-heading); font-weight: var(--font-heading-weight); font-size: 14px }`
     cioè Caprasimo 400 a 14px, non il grassetto del corpo. Tutti i pulsanti dei due mockup sono
     così: «Entra», «Accetta e prepara», «Metti in pausa», «+ Ordine al telefono». Erano Figtree
     700 a 14,5: stessa forma, voce diversa — ed è la voce che si riconosce prima della forma.

     Il peso resta scritto 400 e non "bold": Caprasimo ha **un peso solo**, e `font-synthesis-weight:none`
     (sul `body`) impedisce al browser di fabbricarne uno finto. Chiedere 700 qui non ingrasserebbe
     niente, ma direbbe il falso a chi legge il foglio. */
  .btn, a.btn{border:0; border-radius:var(--r-pill); padding:12px 22px;
    font-family:var(--serif); font-weight:400; font-size:14px; line-height:1.2;
    cursor:pointer; width:100%; min-height:var(--tap);
    display:inline-flex; align-items:center; justify-content:center; gap:8px; text-align:center;
    text-decoration:none; background:var(--action); color:var(--action-ink); box-shadow:var(--shadow-sm)}
  .btn.ghost{background:var(--surface); color:var(--ink); border:1px solid var(--border-strong); box-shadow:none}
  /* Un comando spento deve **sembrare** spento. Il pieno lo dice già (`--disabled-bg`), il
     contorno no: «Manca poco» sui premi dei bollini usciva col bordo di un pulsante vivo e
     invitava a toccarlo. Si vede solo guardando la schermata — nell'HTML è già `disabled`. */
  .btn:disabled, .btn.ghost:disabled{background:var(--disabled-bg); color:var(--disabled-ink);
    border-color:transparent; box-shadow:none; cursor:not-allowed}
  .btn.mini{width:auto; padding:9px 18px; min-height:var(--tap-min); font-size:13px; border-radius:var(--r-pill)}
  .link{background:none; border:0; color:var(--action-text); font:inherit; font-weight:600; cursor:pointer;
    padding:6px; min-height:var(--tap-min); display:inline-flex; align-items:center}
  label.fld{display:block; font-size:12px; font-weight:700; letter-spacing:.03em; text-transform:uppercase; color:var(--ink-muted); margin:0 0 6px 2px}
  /* Campi: pillola sui monoriga, riquadro tondo sul testo lungo. Il design system dice "999px
     for buttons and **inputs**" e il mockup infatti disegna il campo del telefono come una
     pillola piena — ma una `textarea` a pillola taglia gli angoli della prima e dell'ultima riga
     scritta, quindi lì resta `--r-sm`. È la stessa eccezione che fa il foglio del design system,
     che a `textarea.input` dà un'altezza minima e non la stringe. */
  input.txt, select.txt, textarea.txt{width:100%; border:1px solid var(--border-strong); background:var(--surface-raised); color:var(--ink); border-radius:var(--r-pill); padding:12px 17px; font:inherit; font-size:16px}
  textarea.txt{border-radius:var(--r-sm); padding-inline:14px}
  input.txt:focus, select.txt:focus, .step button:focus-visible, button:focus-visible{outline:2px solid var(--focus); outline-offset:2px}
  .choice{display:grid; grid-template-columns:1fr 1fr; gap:8px}
  .choice.three{grid-template-columns:1fr 1fr 1fr}
  .opt{border:1px solid var(--border-strong); background:var(--surface); border-radius:var(--r-pill); padding:12px 10px; cursor:pointer; text-align:center; font-size:13.5px; font-weight:600; color:var(--ink-2)}
  .opt small{display:block; font-weight:500; color:var(--ink-muted); font-size:11px; margin-top:2px}
  .opt[aria-pressed="true"]{border-color:var(--action-line); background:var(--action-soft); color:var(--ink)}

  .sheet{background:var(--surface); border:1px solid var(--border); border-radius:var(--r); box-shadow:var(--shadow); padding:16px; margin-bottom:14px}
  .sheet h3{font-family:var(--serif); font-size:19px; margin:0 0 4px}
  .sheet .sub{color:var(--ink-muted); font-size:13px; margin:0 0 14px}

  /* ---- TASK-036/D · i passaggi del checkout ----
     Il numero dice a che punto si è senza che ci sia scritto: si guarda, non si legge. Era la
     cosa che mancava a quattro riquadri tutti uguali, che per capirli andavano letti in fila. */
  .ck-h{display:flex; align-items:center; gap:10px; margin-bottom:12px}
  .ck-h h3{margin:0}
  .ck-n{flex:none; width:26px; height:26px; border-radius:50%; background:var(--action);
    color:var(--action-ink); display:grid; place-items:center; font-size:13px; font-weight:700;
    font-variant-numeric:tabular-nums}
  .ck-opt{font-family:var(--sans); font-size:12px; font-weight:600; color:var(--ink-muted);
    letter-spacing:0}
  .ck-step .totrow{margin-top:12px}
  .lineitem{display:flex; justify-content:space-between; gap:10px; padding:9px 0; font-size:14px; border-bottom:1px solid var(--border)}
  .lineitem:last-of-type{border-bottom:0}
  /* TASK-039 — le due righe dei conti quando c'è la consegna. Un totale unico che cresce di 2,50
     senza dire perché è il modo più veloce per far sembrare l'app disonesta. */
  .ck-riga{display:flex; justify-content:space-between; gap:10px; font-size:13.5px; padding:4px 0}

  /* ---- TASK-039 · le pastiglie dell'orario ----
     Erano un `<select>`. Una tendina nasconde le scelte finché non la si apre e su un telefono
     tira su il rullo del sistema sopra mezza schermata; gli orari sono pochi e corti, quindi ci
     stanno tutti in vista e si toccano una volta invece di tre. */
  .slotchips{display:flex; flex-wrap:wrap; gap:8px; margin-top:8px}
  /* `min-width` da DEC-015: le pastiglie di «in quanti siete» portano **una cifra sola** e con il
     solo padding restavano 40px. Gli orari («12:30») erano già oltre, quindi questa riga cambia
     solo le più piccole — che sono anche le uniche che rischiavano di essere scomode. */
  .slotchip{border:1px solid var(--border-strong); background:var(--surface-raised); color:var(--ink);
    border-radius:999px; padding:0 15px; min-height:var(--tap-min); min-width:var(--tap-min);
    justify-content:center; font:inherit; font-size:13.5px;
    font-weight:600; cursor:pointer; display:inline-flex; align-items:center;
    font-variant-numeric:tabular-nums}
  .slotchip[aria-pressed="true"]{background:var(--action); border-color:var(--action); color:var(--action-ink)}

  /* ---- TASK-039 · il foglio delle varianti ---- */
  .vgruppo{margin-top:16px}
  .vgruppo:first-child{margin-top:6px}
  .vtit{font-size:12px; font-weight:700; letter-spacing:.03em; text-transform:uppercase;
    color:var(--ink-muted); margin-bottom:8px}
  .vtit small{text-transform:none; letter-spacing:0; font-weight:600}
  .vopts{display:flex; flex-wrap:wrap; gap:8px}
  .vopt{border:1px solid var(--border-strong); background:var(--surface-raised); color:var(--ink);
    border-radius:999px; padding:0 15px; min-height:var(--tap-min); font:inherit; font-size:13.5px;
    font-weight:600; cursor:pointer; display:inline-flex; align-items:center}
  .vopt[aria-pressed="true"]{background:var(--action); border-color:var(--action); color:var(--action-ink)}
  .vqta{display:flex; align-items:center; justify-content:space-between; gap:12px;
    margin-top:20px; padding-top:14px; border-top:1px solid var(--border)}

  .lineitem .q{color:var(--action-text); font-weight:700}
  .totrow{display:flex; justify-content:space-between; align-items:baseline; margin-top:12px; padding-top:12px; border-top:1px solid var(--border)}
  .totrow .big{font-family:var(--serif); font-size:24px; font-weight:600; font-variant-numeric:tabular-nums}
  .stack{display:flex; flex-direction:column; gap:10px}

  /* confirm */
  .confirm{text-align:center; padding:24px 8px}
  .seal{width:74px; height:74px; border-radius:50%; margin:0 auto 16px; display:grid; place-items:center; background:var(--ok-soft); color:var(--ok)}
  .seal svg{width:38px; height:38px}
  .ordernum{font-family:var(--serif); font-size:15px; color:var(--ink-muted)}
  .recap{background:var(--surface); border:1px solid var(--border); border-radius:var(--r); box-shadow:var(--shadow); padding:16px; text-align:left; margin:18px 0}
  .recap .kv{display:flex; justify-content:space-between; padding:7px 0; font-size:14px; border-bottom:1px solid var(--border)}
  .recap .kv:last-child{border-bottom:0}
  .recap .kv .k{color:var(--ink-muted)}

  /* ---- gestore ---- */
  .gtabs{display:flex; gap:4px; background:var(--surface); border:1px solid var(--border-strong); border-radius:12px; padding:4px; margin-bottom:16px; position:sticky; top:var(--header-h, 0px); box-shadow:var(--shadow-sm); z-index:10}
  /* TASK-031/B — erano 35 px, ed è il comando più toccato di tutto il pannello: le cinque schede
     con cui chi sta dietro al banco cambia vista venti volte a servizio, spesso con le mani
     occupate. L'audit di TASK-031 non le aveva viste perché il suo selettore non copriva
     `.gtabs a` — era uno dei tre buchi che il task si era scritto da solo. Le ha misurate il
     browser. Ora ereditano `--tap-min` come tutto il resto. */
  .gtabs a{font-size:12.5px !important; padding:9px 4px !important; white-space:nowrap;
    min-height:var(--tap-min); display:inline-flex !important; align-items:center; justify-content:center}
  @media (max-width:420px){ .gtabs a{font-size:11.5px !important} }
  .gtabs button{flex:1; border:0; background:transparent; color:var(--ink-2); font:inherit; font-weight:600;
    font-size:13px; padding:9px; min-height:var(--tap-min); display:inline-flex; align-items:center;
    justify-content:center; gap:6px; border-radius:9px; cursor:pointer}
  .gtabs button[aria-pressed="true"]{background:var(--action); color:var(--action-ink)}
  .gcount{background:var(--action-soft); color:var(--action-text); border-radius:999px; padding:0 6px; font-size:11px; margin-left:5px}
  .gtabs button[aria-pressed="true"] .gcount{background:var(--action-badge); color:var(--action-ink)}

  .order{background:var(--surface); border:1px solid var(--border); border-radius:var(--r); box-shadow:var(--shadow); padding:13px 14px; margin-bottom:11px}
  .order .top{display:flex; justify-content:space-between; align-items:flex-start; gap:8px}
  .order .who{font-family:var(--serif); font-size:16px; font-weight:600}
  .order .meta{font-size:12px; color:var(--ink-muted); margin-top:1px}
  .order .items{font-size:13px; color:var(--ink-2); margin:9px 0; line-height:1.5}
  .chip{font-size:11px; font-weight:700; padding:3px 9px; border-radius:999px; white-space:nowrap}
  .chip.tag{background:var(--neutral-soft); color:var(--ink-2); font-weight:600}
  .st-nuovo{background:var(--action-soft); color:var(--action-text)}
  .st-in_preparazione{background:var(--warn-soft); color:var(--warn)}
  .st-pronto{background:var(--ok-soft); color:var(--ok)}
  .st-in_consegna{background:var(--warn-soft); color:var(--warn)}
  .st-ritirato, .st-consegnato{background:var(--ok-soft); color:var(--ok)}
  .st-annullato{background:var(--neutral-soft); color:var(--neutral)}
  .order .actions{display:flex; gap:8px; margin-top:11px; flex-wrap:wrap; align-items:center}
  .etapick{display:inline-flex; align-items:center; gap:5px; border:1px solid var(--border-strong); background:var(--surface-raised);
    border-radius:10px; padding:0 6px 0 10px; color:var(--ink-muted)}
  .etapick select{border:0; background:transparent; color:var(--ink); font:inherit; font-size:16px; font-weight:600;
    padding:9px 2px; cursor:pointer}
  .btn.danger{color:var(--danger); border-color:var(--danger-line)}
  .chip.tag svg{vertical-align:-.12em; margin-right:2px}
  .order .foot{display:flex; gap:6px; align-items:center; margin-top:9px; flex-wrap:wrap}

  /* ---- catalogo piatti + selezione rapida (gestore) ---- */
  .gthumb, .qthumb{position:relative; flex:none; display:block; border-radius:var(--r-sm); overflow:hidden;
    background:var(--thumb-bg); width:46px; height:46px}
  .qthumb{width:40px; height:40px}
  .gthumb img, .qthumb img{width:100%; height:100%; object-fit:cover; display:block}
  .gthumb.noimg::before, .qthumb.noimg::before{content:attr(data-mono); position:absolute; inset:0; display:grid;
    place-items:center; font-family:var(--serif); font-size:22px; color:var(--thumb-mono)}
  .qthumb.noimg::before{font-size:18px}

  .qgroup{font-size:10.5px; font-weight:700; letter-spacing:.12em; text-transform:uppercase; color:var(--action-text);
    margin:16px 0 4px; padding-top:10px; border-top:1px solid var(--border)}
  .qgroup:first-of-type{margin-top:8px; border-top:0; padding-top:0}

  .qpick{display:flex; align-items:center; gap:11px; padding:9px 0; border-bottom:1px solid var(--border); cursor:pointer}
  .qpick:last-of-type{border-bottom:0}
  .qpick input[type=checkbox]{flex:none; width:20px; height:20px; accent-color:var(--action); cursor:pointer}
  .qpick .qmeta{flex:1; min-width:0; display:flex; flex-direction:column}
  .qpick .nm{font-family:var(--serif); font-size:15px; font-weight:600; line-height:1.2}
  .qpick .ct{font-size:11.5px; color:var(--ink-muted); margin-top:1px}
  .qpick .qty{display:flex; align-items:center; gap:5px; flex:none}
  .qpick input.mini{width:58px}
  .qpick:has(input:checked){background:var(--action-soft); border-radius:var(--r-sm); padding-left:8px; padding-right:8px}

  .drow{display:flex; align-items:center; gap:11px; padding:10px 0; border-bottom:1px solid var(--border)}
  .drow:last-of-type{border-bottom:0}
  .drow .dmeta{flex:1; min-width:0}
  .drow .nm{font-family:var(--serif); font-size:15.5px; font-weight:600; line-height:1.2}
  .drow .ct{font-size:11.5px; color:var(--ink-muted); margin-top:2px}
  .drow .ct b{color:var(--warn); font-weight:700}
  .drow .dact{display:flex; gap:6px; flex:none; align-items:center}
  .drow .dact a.iconbtn{display:grid; place-items:center; text-decoration:none}
  /* TASK-064 — il nome del piatto è un **bersaglio**, non solo una scritta.
     Era alto **19px**: sotto i 44 il dito sbaglia, e su un tablet dietro al banco si tocca di
     corsa. Misurati 15 così nel catalogo, cioè uno per piatto, sulla schermata dove si va apposta
     per modificare.
     ⚠️ Primo tentativo, e non rifarlo: stendere il collegamento su tutta la riga con
     `::after{inset:0}` e tenere i comandi sopra con `z-index`. Sembra la soluzione elegante ed è
     sbagliata **qui**, perché a ≥900px `.drow` diventa una scheda con la foto a tutta larghezza:
     la riga passa da ~66px a **296px**, lo pseudo-elemento copre tutto e **«archivia» smette di
     essere raggiungibile** — verificato con `elementFromPoint`, non a occhio.
     Il rimedio giusto è il più noioso: il nome cresce fino alla soglia e basta. La matita da
     44×44 per aprire c'è già, quindi non serve rendere cliccabile mezzo schermo. */
  .drow .nm{display:inline-flex; align-items:center; min-height:var(--tap-min)}

  /* blocco foto del piatto: grande, con anteprima di quello che si è appena scelto */
  .photobox{border:1px solid var(--border-strong); border-radius:var(--r); padding:12px; background:var(--surface-raised)}
  .pbprev{margin-bottom:10px; border-radius:var(--r-sm); overflow:hidden; background:var(--neutral-soft); line-height:0}
  .pbprev img{width:100%; max-height:190px; object-fit:cover; display:block}
  .pbprev[hidden]{display:none}
  .pbhint{font-size:12px; color:var(--ink-muted); margin-top:9px; line-height:1.4; text-align:center}
  .rmphoto{display:flex; align-items:center; justify-content:center; gap:8px; margin-top:11px; font-size:13px;
    font-weight:600; color:var(--ink-muted); cursor:pointer}
  .rmphoto input{width:17px; height:17px; accent-color:var(--action)}
  .filepick{position:relative; display:flex; align-items:center; justify-content:center; gap:9px;
    border:1px dashed var(--action-line); background:var(--action-soft); color:var(--action-text); border-radius:12px;
    padding:15px 12px; font-size:14px; font-weight:700; cursor:pointer; text-align:center}
  .filepick input[type=file]{position:absolute; inset:0; width:100%; height:100%; opacity:0; cursor:pointer}
  .filepick:hover{background:var(--action); color:var(--action-ink); border-style:solid}
  .filepick.done{border-style:solid; border-color:var(--ok); background:var(--ok-soft); color:var(--ok)}
  .tlink{text-decoration:none; color:inherit; display:block}
  a.nm.tlink:hover{color:var(--action-text)}
  .iconbtn.danger{color:var(--danger); border-color:var(--danger-line)}

  .medit{display:grid; grid-template-columns:1fr auto; gap:10px; padding:12px 0; border-bottom:1px solid var(--border); align-items:center}
  /* Su telefono i comandi (prezzo, porzioni, tre pulsanti) mangiano tutta la riga e il nome del
     piatto si riduce a due lettere: sotto i 460px vanno a capo. */
  @media (max-width:459px){
    .medit{grid-template-columns:1fr; gap:9px}
    .medit .ctr{justify-content:flex-end}
    .medit input.mini{width:72px}
  }
  .medit .nm{font-family:var(--serif); font-size:15px; font-weight:600}
  .medit .ct{font-size:11px; color:var(--ink-muted); text-transform:uppercase; letter-spacing:.05em}
  .medit .ctr{display:flex; align-items:center; gap:8px}
  input.mini{width:64px; text-align:center; border:1px solid var(--border-strong); background:var(--surface-raised); color:var(--ink); border-radius:8px; padding:7px 4px; font:inherit; font-size:16px; font-variant-numeric:tabular-nums}
  .iconbtn{width:var(--tap-min); height:var(--tap-min); border:1px solid var(--border-strong); background:var(--surface-raised); border-radius:8px; cursor:pointer; color:var(--ink-2); display:grid; place-items:center; font-size:15px}
  /* ---- TASK-033 · i menu tematici nel pannello ----
     Ogni tema è una riga con tre zone in ordine di frequenza d'uso: **cosa è** (nome, quanti
     piatti), **cosa gli faccio oggi** (sostituisci / aggiungi), **come lo cambio** (componi,
     elimina). Il bar apre questa scheda al mattino per applicare, non per comporre: quello che
     si fa tutti i giorni sta prima e ha i pulsanti pieni, quello che si fa una volta sta dopo ed
     è in punta. */
  .temanow{display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap;
    background:var(--action-soft); border-radius:var(--r-sm); padding:11px 13px; margin:2px 0 12px}
  .tn-eti{display:block; font-size:11px; font-weight:700; letter-spacing:.1em; text-transform:uppercase;
    color:var(--action-text)}
  .tn-nome{display:block; font-family:var(--serif); font-size:17px; font-weight:600; color:var(--ink);
    margin-top:2px}
  .temarow{display:grid; gap:9px; padding:13px 0; border-bottom:1px solid var(--border)}
  .temarow:last-of-type{border-bottom:0}
  /* il tema aperto in composizione si riconosce senza cercarlo */
  .temarow.on{background:var(--surface-raised); border-radius:var(--r-sm);
    padding-inline:11px; margin-inline:-11px}
  .tr-nome{font-family:var(--serif); font-size:16px; font-weight:600; line-height:1.2}
  .tr-meta{font-size:11px; color:var(--ink-muted); text-transform:uppercase; letter-spacing:.05em; margin-top:2px}
  .tr-desc{font-size:13px; color:var(--ink-2); margin-top:4px; max-width:52ch; text-wrap:pretty}
  .tr-app, .tr-cmd{display:flex; align-items:center; gap:8px; flex-wrap:wrap}
  .tr-cmd form{display:contents}
  /* Da tablet in su le tre zone stanno sulla stessa riga: sul telefono no, perché i due modi di
     applicare finirebbero larghi 70px e si toccherebbero per sbaglio. */
  @media (min-width:620px){
    .temarow{grid-template-columns:1fr auto auto; align-items:center; gap:14px}
  }
  .temanew, .temaedit{display:grid; gap:9px; margin-top:14px; padding-top:14px; border-top:1px solid var(--border)}
  .temanew input, .temaedit input{border:1px solid var(--border-strong); background:var(--surface-raised);
    color:var(--ink); border-radius:8px; padding:11px 12px; font:inherit; font-size:16px; min-height:var(--tap-min)}
  .temaedit{margin-top:0; padding-top:0; border-top:0}
  @media (min-width:620px){
    .temanew{grid-template-columns:1fr 1fr auto; align-items:center}
    .temaedit{grid-template-columns:1fr 1fr auto; align-items:center}
  }

  .codeitem{display:flex; justify-content:space-between; align-items:center; gap:10px; padding:10px 0; border-bottom:1px solid var(--border)}
  .codeitem code{font-family:var(--serif); background:var(--action-soft); color:var(--action-text); padding:3px 9px; border-radius:7px; font-size:13px; font-weight:700; letter-spacing:.05em}
  .switch{display:flex; align-items:center; justify-content:space-between; gap:10px; padding:11px 0}
  .muted{color:var(--ink-muted); font-size:12.5px}
  .empty{text-align:center; color:var(--ink-muted); padding:40px 20px; font-size:14px}
  .empty .big{font-family:var(--serif); font-size:19px; color:var(--ink-2); margin-bottom:6px}

  /* ---- finestre dell'app (al posto di alert/confirm del browser) ---- */
  /* ---- Il velo d'attesa (TASK-028) ----------------------------------------------------
     Sta sopra a tutto, finestre comprese: mentre la pagina se ne sta andando, nulla di quello
     che c'è sotto deve poter essere toccato. È metà della richiesta ("evitare che l'utente
     schiacci qualcosa per sbaglio"); l'altra metà è dirgli che il comando è partito.
     `z-index` 200 contro il 100 delle finestre: un comando dato **dentro** una finestra
     (le conferme "Sì, procedi") deve poter mostrare il velo sopra la finestra stessa. */
  .velo{position:fixed; inset:0; z-index:200; background:var(--scrim);
    backdrop-filter:blur(3px); display:flex; align-items:center; justify-content:center;
    animation:fcin .18s ease both; touch-action:none}
  .velo[hidden]{display:none}
  /* Un corpo, non logo e testo appoggiati sullo sfocato: lì sotto passa il menu, e due righe
     senza fondo ci si perdono dentro. È la stessa scatola delle finestre dell'app, così il velo
     sembra una cosa dell'app e non un fermo immagine. */
  .velo-box{display:flex; flex-direction:column; align-items:center; gap:12px;
    padding:28px 32px; text-align:center; max-width:min(320px, calc(100vw - 48px));
    background:var(--surface); border:1px solid var(--border); border-radius:18px;
    box-shadow:var(--shadow-lg); animation:fcup .22s cubic-bezier(.2,.9,.3,1) both}
  .velo-logo{width:132px; height:auto; animation:velo-respiro 1.6s ease-in-out infinite}
  .velo-txt{font-size:14.5px; font-weight:600; color:var(--ink-2); letter-spacing:.01em; line-height:1.45}
  .velo-esci{margin-top:4px}
  .velo-esci[hidden]{display:none}
  /* Un respiro, non una rotellina: la rotellina promette una barra di avanzamento che non
     abbiamo, e a ogni giro sembra che ci stia mettendo di più. */
  @keyframes velo-respiro{0%,100%{opacity:.55; transform:scale(.97)} 50%{opacity:1; transform:scale(1)}}
  @media (prefers-reduced-motion:reduce){
    .velo, .velo-box{animation:none} .velo-logo{animation:none; opacity:.9}
  }

  .fcmodal{position:fixed; inset:0; z-index:100; background:var(--scrim); backdrop-filter:blur(2px);
    display:flex; align-items:flex-end; justify-content:center; animation:fcin .16s ease both}
  @media (min-width:520px){ .fcmodal{align-items:center} }
  .fcmodal.out{animation:fcout .14s ease both}
  @keyframes fcin{from{opacity:0}to{opacity:1}}
  @keyframes fcout{to{opacity:0}}
  .fcgrab{width:38px; height:4px; border-radius:999px; background:var(--border-strong);
    margin:-8px auto 12px; opacity:.6}
  .fcbox{background:var(--surface); width:100%; max-width:400px; border-radius:18px 18px 0 0; padding:22px 20px
    calc(20px + env(safe-area-inset-bottom)); box-shadow:var(--shadow-lg);
    max-height:calc(100dvh - env(safe-area-inset-top, 0px) - 28px - var(--kb, 0px)); overflow-y:auto; overscroll-behavior:contain;
    animation:fcup .2s cubic-bezier(.2,.9,.3,1) both}
  @media (min-width:520px){ .fcbox{border-radius:18px; margin:16px; animation:fczoom .18s ease both} }
  @keyframes fcup{from{transform:translateY(18px)}to{transform:none}}
  @keyframes fczoom{from{transform:scale(.97); opacity:.6}to{transform:none; opacity:1}}
  @media (prefers-reduced-motion:reduce){ .fcmodal,.fcbox{animation:none} }
  .fcbox h3{font-family:var(--serif); font-size:20px; margin:0 0 6px}
  .fcbox p{color:var(--ink-2); font-size:14px; line-height:1.5; margin:0 0 4px}
  .fcbox .fcta{width:100%; margin-top:12px; min-height:118px; resize:vertical; font-family:var(--sans); line-height:1.45}
  .fcact{display:flex; gap:9px; margin-top:18px}
  .fcact .btn{flex:1; padding:12px 14px}

  /* segnalazioni nel pannello */
  .fbitem{border:1px solid var(--border); border-radius:var(--r); padding:12px 13px; margin-bottom:10px; background:var(--surface-raised)}
  .fbitem.new{border-color:var(--action-line); background:var(--action-soft)}
  .fbtop{display:flex; justify-content:space-between; align-items:center; gap:10px; font-size:11.5px}
  .fbmsg{font-size:14px; line-height:1.5; color:var(--ink); margin:9px 0 2px; white-space:pre-wrap; word-break:break-word}
  .fbact{display:flex; justify-content:flex-end; margin-top:8px}

  /* access gate — è l'unica schermata senza header, quindi la safe area la gestisce da sé */
  .gate{padding:calc(46px + env(safe-area-inset-top, 0px)) 20px 34px; text-align:center;
    display:flex; flex-direction:column; align-items:center; gap:6px}
  .gate .plate{background:transparent; padding:0; margin-bottom:26px; line-height:0}
  .gate .plate img{height:74px; width:auto; max-width:100%; border-radius:10px; box-shadow:0 6px 24px rgba(54,45,37,.16)}
  .gate h1{font-family:var(--serif); font-size:24px; font-weight:600; margin:0}
  .gate p{color:var(--ink-muted); font-size:14px; margin:0 0 8px; max-width:30ch}
  .gate form{width:100%; max-width:280px; margin-top:14px; display:flex; flex-direction:column; gap:10px}
  .gate .hint{font-size:12px; color:var(--ink-muted); margin-top:2px}
  .gate .hint code{background:var(--action-soft); color:var(--action-text); padding:2px 7px; border-radius:6px; font-weight:700}
  .err{color:var(--danger); font-size:13px; font-weight:600; min-height:18px}
  .installnote{margin-top:26px; font-size:12px; color:var(--ink-muted); max-width:32ch; line-height:1.5}

  /* ---- correzioni difensive (valgono a ogni larghezza) ---- */
  /* Nessuna cella puo' sfondare il contenitore per un contenuto largo (un prezzo, un codice). */
  .choice > *, .medit > *, .order > *{min-width:0}
  /* A 320px le quattro schede del pannello con due contatori accesi non ci stanno:
     meglio far scorrere loro che tutta la pagina. */
  .gtabs{overflow-x:auto; scrollbar-width:none}
  .gtabs::-webkit-scrollbar{display:none}
  /* Il contorno del fuoco mancava su TUTTI i link: con la tastiera si navigava alla cieca. */
  a:focus-visible, .filepick:focus-within, [tabindex]:focus-visible{
    outline:2px solid var(--focus); outline-offset:2px; border-radius:6px;
  }
  /* Nessuna immagine puo' sfondare, mai. */
  img{max-width:100%}
  /* La lista del carrello esiste solo dove il carrello e' una colonna (vedi piu' sotto). */
  .cartlist{display:none}

  /* ---- passaggio del mouse: solo dove un puntatore esiste davvero ----
     Senza il guardiano, su telefono lo stato resta appiccicato dopo il tocco e
     sembra che qualcosa sia rimasto selezionato. */
  @media (hover:hover) and (pointer:fine){
    .dish{transition:box-shadow .15s ease, border-color .15s ease}
    .dish:hover{border-color:var(--action-line); box-shadow:0 2px 4px rgba(54,45,37,.08), 0 12px 28px rgba(54,45,37,.12)}
    .drow:hover, .order:hover{border-color:var(--action-line)}
    .qpick:hover{background:var(--neutral-soft); border-radius:var(--r-sm)}
    .btn:hover, .addbtn:hover{filter:brightness(1.07)}
    .iconbtn:hover{background:var(--action-soft); color:var(--action-text); border-color:var(--action-line)}
    .gtabs a:hover{background:var(--neutral-soft)}
    a.tlink:hover .gthumb{filter:brightness(1.06)}
  }
  @media (prefers-reduced-motion:reduce){ .dish{transition:none} }

  /* ============================================================
     DA QUI IN GIU': SCHERMI LARGHI.
     Regola dura: niente sopra i 640px puo' cambiare come si vede il telefono.
     I quattro numeri sono sempre commentati (s / m / l / xl) cosi' si trovano tutti
     con una ricerca di testo. `var()` non funziona dentro una media query: i numeri
     sono scritti a mano ogni volta, di proposito.
     ============================================================ */

  /* ---- s (640px): il menu va a colonne. La scheda diventa il piatto, non piu' la categoria. ---- */
  @media (min-width:640px){
    .app:not(.wide){max-width:min(46rem, 100% - 2rem)}
    /* Accesso a codice e pagina "installa": niente carrello, niente cartellone — restano strette.
       Il riconoscimento è "questa pagina ha un carrello?", e va tenuto agganciato a dove il
       carrello sta DAVVERO: da TASK-036/B non è più `.cartbar` figlia di `.app`, è `.cartend`
       dentro `main`. Con il selettore vecchio anche il menu risultava senza carrello e si
       stringeva a 34rem: a 1440 px diventava una colonna sola alta 7600 px, e il carrello finiva
       a quattro schermate di distanza. L'ha trovato `browser-restyle.js` misurando, non io. */
    .app:not(.wide):not(:has(.cartend)){max-width:min(34rem, 100% - 2rem)}
    .app:not(.wide) main{padding-inline:var(--gutter)}

    .card{
      background:transparent; border:0; box-shadow:none; overflow:visible;
      display:grid; gap:var(--gap);
      /* il min() e' obbligatorio: senza, sotto la larghezza della traccia la griglia sfonda */
      grid-template-columns:repeat(auto-fill, minmax(min(var(--col-piatto), 100%), 1fr));
    }
    .dish{
      background:var(--surface); border:1px solid var(--border);
      border-radius:var(--r); box-shadow:var(--shadow); overflow:hidden;
    }
    .dish + .dish{border-top:1px solid var(--border)}   /* non e' piu' un divisore di lista */
  }

  /* ---- l (1200px): il menu prende la larghezza che ha ----
     A 1440 px il menu restava dentro 46rem (736 px) e lasciava 700 px di pagina vuota: la stessa
     quantità di piatti diventava una colonna lunga il doppio del necessario. Non è solo brutto —
     è quello che rendeva il carrello lontano da metà elenco (DEC-007 §1b), perché la distanza da
     risalire è la metà dell'altezza della pagina.
     60rem tiene la scheda alla sua misura buona e ne mette una in più per riga: la pagina si
     accorcia di un terzo senza che niente diventi appiccicato. È la strada che rispetta la
     decisione invece di aggirarla con una barra fissa. */
  @media (min-width:1200px){
    .app:not(.wide):has(.cartend){max-width:min(60rem, 100% - 3rem)}
  }

  /* ---- m (900px): foto grande in alto, ma solo se lo schermo ha altezza.
         Il vincolo tiene fuori i telefoni girati (932x430), dove una scheda alta
         farebbe vedere un piatto e mezzo per schermata.

         Dal restyle di TASK-024 questa regola vale **solo per la riga compatta**: la vetrina
         nasce già così a ogni misura, e applicargliela sopra avrebbe rimesso in gioco il
         `4/3` sul `1/1` che le foto del bar hanno davvero. ---- */
  @media (min-width:900px) and (min-height:640px){
    .dish.riga{grid-template-columns:1fr; gap:0; padding:0}
    .dish.riga .thumb{aspect-ratio:4/3; border-radius:var(--r) var(--r) 0 0}
    .dish.riga .thumb::before{font-size:64px}
    .dish.riga .thumb .cam{font-size:10px; bottom:8px}
    .dish.riga .body{padding:12px 13px 13px}
    .dish.riga .name{font-size:18px}
    /* il taglio a due righe sta QUI dentro apposta: sul telefono la descrizione
       e' l'unica cosa che dice cosa c'e' nel piatto e non si taglia mai */
    .dish .desc{display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden}
  }

  /* ---- m (900px): il testo lungo non si stende per tutta la scocca ---- */
  @media (min-width:900px){
    .banner, .empty, .gate p, .installnote, .sheet .sub, .fbmsg, .trk-sub{max-width:var(--misura)}
    .empty, .gate p, .installnote{margin-inline:auto}
  }

  /* ---- l (1180px): il carrello esce dal fondo e diventa la colonna di destra.
         `:has(> .cartbar)` tiene fuori accesso e /installa, che un carrello non ce l'hanno. ---- */
  @media (min-width:1180px) and (min-height:600px){
    .app:not(.wide):has(> .cartbar){
      max-width:min(var(--shell-cliente), 100% - 2rem);
      display:grid;
      grid-template-columns:minmax(0,1fr) var(--aside-carrello);
      grid-template-rows:auto minmax(0,1fr);
      grid-template-areas:"testa testa" "menu carrello";
    }
    .app:not(.wide):has(> .cartbar) > header{grid-area:testa}
    .app:not(.wide):has(> .cartbar) > main{
      grid-area:menu;
      min-height:0;                  /* senza, la riga 1fr prende l'altezza del contenuto e non scorre nulla */
      padding-bottom:var(--gutter);  /* lo spazio serviva a schivare la barra: non c'e' piu' */
    }
    .app:not(.wide):has(> .cartbar) > .cartbar{
      grid-area:carrello; position:static; background:var(--surface);
      border-left:1px solid var(--border);
      padding:var(--gutter);
      padding-bottom:calc(var(--gutter) + env(safe-area-inset-bottom, 0px));
      display:flex; flex-direction:column; gap:12px; min-height:0;
    }
    .cartlist{display:flex; flex-direction:column; flex:1; min-height:0; overflow-y:auto}
    .cartlist .lineitem:first-child{padding-top:0}
    .cartlist .empty{padding:24px 8px; font-size:13px}
    .cartbtn{flex:none}
  }

  /* ---- m (900px): il checkout smette di essere un foglio e diventa una finestra.
         Il prefisso `body ` batte la regola in linea di render.php, che viene dopo. ---- */
  @media (min-width:900px){
    body .overlay .panel{
      max-width:56rem; border-radius:20px; padding:20px 20px 22px;
      display:grid; gap:0 var(--gap);
      grid-template-columns:minmax(0,22rem) minmax(0,1fr);
    }
    body .overlay .panel > .fcgrab{display:none}          /* qui non si trascina: si chiude col mouse */
    /* Cinque righe da TASK-036/D: i passaggi sono quattro più la riga di legge, e il riepilogo
       li affianca tutti. Con la nota senza una sua casella la griglia la piazzava a caso e il
       pulsante di conferma finiva sotto il riepilogo invece che sotto i passaggi. */
    body .overlay .panel > button.link{grid-area:1 / 1 / 2 / -1}
    body .overlay .panel > .ck-recap{grid-area:2 / 1 / 7 / 2; position:sticky; top:0; align-self:start}
    body .overlay .panel > .ck-come{grid-area:2 / 2 / 3 / 3}
    body .overlay .panel > .ck-pay{grid-area:3 / 2 / 4 / 3}
    body .overlay .panel > .ck-nota{grid-area:4 / 2 / 5 / 3}
    body .overlay .panel > .ck-legal{grid-area:5 / 2 / 6 / 3}
    body .overlay .panel > button.btn{grid-area:6 / 2 / 7 / 3; align-self:start}
    /* la conferma e' un'altra cosa: torna stretta e centrata */
    body .overlay .panel:has(.confirm){display:block; max-width:32rem}
  }

  /* ============================ PANNELLO DEL BAR ============================ */

  /* ---- m (900px): la scocca si allarga ---- */
  @media (min-width:900px){
    body .app.wide{max-width:min(64rem, 100% - 2rem)}
  }
  @media (min-width:1180px){
    body .app.wide{max-width:min(var(--shell-gestore), 100% - 2rem)}
  }
  /* ---- xl (1500px): via la cornice. Su un monitor da lavoro il pannello E' l'applicazione. ---- */
  @media (min-width:1500px){
    body .app.wide{max-width:none; margin:0; border-radius:0; box-shadow:none; min-height:100dvh}
    .app.wide > header.ghead, .app.wide > main{
      padding-inline:max(var(--gutter), (100% - var(--shell-gestore)) / 2);
    }
  }

  /* ---- m (900px): gli ordini smettono di essere una lista ---- */
  @media (min-width:900px){
    .app.wide > main.g-ordini > #glist{
      display:grid; gap:var(--gap); align-content:start;
      grid-template-columns:repeat(auto-fill, minmax(min(var(--col-ordine), 100%), 1fr));
    }
    .app.wide > main.g-ordini > #glist > .empty,
    .app.wide > main.g-ordini > #glist > .gdone{grid-column:1 / -1}
    .app.wide > main.g-ordini .order{margin-bottom:0}
    /* un ordine nuovo si deve vedere da tre metri, che e' la distanza del bancone */
    .order:has(.st-nuovo){border-color:var(--action-line); box-shadow:0 0 0 1px var(--action-line), var(--shadow)}
  }

  /* ---- m (900px): catalogo — il form dove si lavora a sinistra, il magazzino a destra ---- */
  @media (min-width:900px){
    .app.wide > main.g-cat{
      display:grid; gap:var(--gap); align-items:start;
      grid-template-columns:minmax(0,20rem) minmax(0,1fr);
    }
    .app.wide > main.g-cat > .install-hint,
    .app.wide > main.g-cat > .gtabs,
    .app.wide > main.g-cat > .banner{grid-column:1 / -1}
    .app.wide > main.g-cat > form.sheet{grid-column:1; position:sticky; top:calc(var(--gtabs-h) + 10px)}
    .app.wide > main.g-cat > .sheet{grid-column:2; margin-bottom:0}

    /* il magazzino diventa una griglia di celle con la foto grande: a 46px non si
       distingue una cotoletta da una parmigiana */
    .sheet:has(.drow){
      display:grid; gap:var(--gap); align-content:start;
      grid-template-columns:repeat(auto-fill, minmax(min(var(--col-catalogo), 100%), 1fr));
    }
    .sheet:has(.drow) > h3, .sheet:has(.drow) > .sub, .sheet:has(.drow) > .qgroup{
      grid-column:1 / -1; margin-block:0;
    }
    .drow{
      display:flex; flex-direction:column; align-items:stretch; gap:8px;
      padding:0 0 10px; border-bottom:0;
      border:1px solid var(--border); border-radius:var(--r-sm); overflow:hidden;
      background:var(--surface-raised);
    }
    .drow > a.tlink{display:block}
    .drow .gthumb{width:100%; height:auto; aspect-ratio:4/3; border-radius:0}
    .drow .gthumb.noimg::before{font-size:44px}
    .drow .dmeta{padding-inline:10px}
    .drow .dact{padding-inline:10px; justify-content:flex-end}
  }

  /* ---- l (1180px): "Oggi" e "Impostazioni" — riquadri che si impilano, non una griglia ----

     Questi due pannelli erano `display:grid`. Una grid dispone **per righe**: ogni riga è alta
     quanto il suo riquadro più alto, e con `align-items:start` gli altri non si allungano — sotto
     ai corti resta il vuoto fino alla riga successiva. Qui le altezze vanno da 117px
     ("Segnalazioni") a 842px ("Chi vende"), un fattore 7: misurato in Chromium, **742px di buco
     massimo e ~2000px di vuoto totale** in Impostazioni, 735px in "Oggi". Nessuna regolazione
     della grid può chiuderli — è quello che una grid deve fare.

     Multi-column invece riempie una colonna e passa alla successiva: un riquadro corto non lascia
     buco, quello dopo gli va subito sotto. `break-inside:avoid` impedisce che un riquadro venga
     spezzato a metà fra due colonne, e il distacco verticale torna a essere il margine (in
     multicol `gap` vale solo fra le colonne, non fra i blocchi).

     Il prezzo pagato, dichiarato: "Menu di oggi" perde `position:sticky`, che dentro multicol non
     funziona. Resta il `max-height` con scorrimento interno, così non domina la colonna. Un buco
     di 735px si vede sempre; la sticky serviva in un caso solo. Se un giorno la si rivuole, la
     strada è avvolgere le due colonne in HTML, non tornare alla grid. */
  @media (min-width:1180px){
    .app.wide > main.g-menu,
    .app.wide > main.g-set{
      columns:24rem; column-gap:var(--gap);
    }
    .app.wide > main.g-menu > .install-hint, .app.wide > main.g-menu > .gtabs,
    .app.wide > main.g-set  > .install-hint, .app.wide > main.g-set  > .gtabs{
      column-span:all;
    }
    .app.wide > main.g-menu > .sheet, .app.wide > main.g-menu > form.sheet,
    .app.wide > main.g-set  > .sheet, .app.wide > main.g-set  > form.sheet{
      break-inside:avoid; margin-bottom:var(--gap);
    }
    /* il menu di oggi può essere lunghissimo: resta contenuto e scorre dentro di sé */
    .app.wide > main.g-menu > .sheet:has(.medit){
      max-height:calc(100dvh - var(--gtabs-h) - 7rem); overflow-y:auto;
    }
  }
  /* ---- xl (1500px): a 1728px una colonna sola di piatti da spuntare e' una striscia sperduta ---- */
  @media (min-width:1500px){
    .app.wide > main.g-menu > form.sheet{
      display:grid; gap:0 var(--gap); align-content:start;
      grid-template-columns:repeat(auto-fill, minmax(min(17rem, 100%), 1fr));
    }
    .app.wide > main.g-menu > form.sheet > h3,
    .app.wide > main.g-menu > form.sheet > .sub,
    .app.wide > main.g-menu > form.sheet > .qgroup,
    .app.wide > main.g-menu > form.sheet > .btn{grid-column:1 / -1}
  }

  /* ---- pannello del bar: riepilogo, vista cucina, ordini chiusi ---- */
  .gsummary{position:relative}
  .gnums{display:grid; grid-template-columns:repeat(2,1fr); gap:10px; margin-top:4px}
  @media (min-width:560px){ .gnums{grid-template-columns:repeat(4,1fr)} }
  .gnums div{background:var(--surface-raised); border:1px solid var(--border); border-radius:var(--r-sm);
    padding:10px 12px; display:flex; flex-direction:column; gap:2px}
  .gnums b{font-family:var(--serif); font-size:20px; font-variant-numeric:tabular-nums; line-height:1.1}
  .gnums span{font-size:11.5px; color:var(--ink-muted); text-transform:uppercase; letter-spacing:.06em}
  .gsplit{display:flex; flex-wrap:wrap; gap:6px; margin-top:10px}
  .gsound{margin-top:12px}
  .gdet{margin-top:12px}
  .gdet summary, .gdone summary{cursor:pointer; font-size:13.5px; font-weight:600; color:var(--action-text);
    padding:10px 0; min-height:var(--tap-min); display:flex; align-items:center}
  .gtable{display:flex; flex-direction:column}
  .grow{display:grid; grid-template-columns:1fr auto auto; gap:10px; align-items:baseline;
    padding:7px 0; border-bottom:1px solid var(--border); font-size:13.5px}
  .grow:last-child{border-bottom:0}
  .grow .q{font-weight:700; color:var(--action-text); font-variant-numeric:tabular-nums}
  /* i tempi veri della giornata (TASK-029): la riga del caso peggiore è una nota sotto la sua
     tratta, non una voce a sé — sta rientrata e senza bordo, così si legge come un inciso */
  .gtempi .sub-riga{border-bottom:0; padding-top:0; font-size:12.5px; padding-left:12px}
  .gtempi.vuoto{margin-top:12px; font-size:13px; line-height:1.5}
  .gtempi.vuoto svg{vertical-align:-2px; margin-right:2px}
  /* ---- Il codice di ritiro (TASK-030) ----------------------------------------------------
     Lato cliente è la cosa da mostrare al banco: grande, cifre distanziate, leggibile da uno
     screenshot mandato al collega che va a ritirare. Lato bar è una riga di servizio: il codice
     da confrontare e la casella per battere quello che gli viene detto. */
  .ordcode{margin:0 0 16px; padding:14px 16px; border-radius:var(--r);
    background:var(--action-soft, var(--surface-raised)); border:1px solid var(--border);
    display:flex; flex-direction:column; gap:2px; align-items:center}
  .ordcode-lbl{font-size:11.5px; font-weight:700; text-transform:uppercase; letter-spacing:.08em; color:var(--ink-muted)}
  .ordcode-num{font-family:var(--serif); font-size:34px; line-height:1.1; letter-spacing:.16em;
    font-variant-numeric:tabular-nums; color:var(--action-text, var(--ink))}
  .ordcode .muted{font-size:12.5px}

  .ocode{display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-top:10px;
    padding-top:10px; border-top:1px solid var(--border); font-size:13px}
  .ocode-lbl{color:var(--ink-2); font-weight:600}
  .ocode-lbl b{font-variant-numeric:tabular-nums; letter-spacing:.08em; color:var(--ink)}
  .ocode-in{width:8.5em; flex:0 0 auto; font-variant-numeric:tabular-nums; letter-spacing:.06em}
  .ocode.fatto{color:var(--ok); font-weight:600}
  .ocode.fatto svg{vertical-align:-2px}
  /* "Al tavolo" è l'unico caso in cui al bar serve anche un posto a sedere: si distingue */
  .chip.tag.tavolo{background:var(--warn-soft); color:var(--warn); font-weight:700}
  .chip.tag.tavolo svg{vertical-align:-2px}

  .gout{margin-top:12px; padding:10px 12px; border-radius:var(--r-sm);
    background:var(--warn-soft); color:var(--warn); font-size:13px; font-weight:600}
  .gout .muted{color:var(--warn); opacity:.85; font-weight:500; margin-top:2px}
  .gdone{margin-top:14px}
  .gdone[open] summary{margin-bottom:8px}

  /* Vista cucina: una colonna per stato. Su telefono restano una sotto l'altra,
     sul tablet del bancone diventano tre colonne affiancate. */
  .kanban{display:grid; gap:var(--gap); grid-template-columns:1fr}
  @media (min-width:820px){ .kanban{grid-template-columns:repeat(3,minmax(0,1fr)); align-items:start} }
  .kcol{background:var(--surface); border:1px solid var(--border); border-radius:var(--r);
    box-shadow:var(--shadow); padding:12px}
  .ktitle{font-family:var(--serif); font-size:15px; margin:0 0 10px; display:flex;
    align-items:center; justify-content:space-between; gap:8px}
  .kcard{border:1px solid var(--border); border-radius:var(--r-sm); padding:10px 11px;
    margin-bottom:9px; background:var(--surface-raised)}
  .kcard:last-child{margin-bottom:0}
  .ktop{display:flex; align-items:center; gap:7px; flex-wrap:wrap; font-size:13.5px}
  .ktop b{font-family:var(--serif); font-size:15px}
  .kitems{font-size:13px; color:var(--ink-2); margin:7px 0 9px; line-height:1.45}
  .knote{font-size:12.5px; font-weight:600; color:var(--warn); background:var(--warn-soft);
    border-radius:var(--r-sm); padding:7px 9px; margin-bottom:9px}
  .kcol[data-col="nuovo"] .kcard{border-color:var(--action-line)}
  .kcard .btn.mini{width:100%}

  /* azione di annullamento dentro il tracker del cliente */
  .trk-act{margin-top:11px; font-size:13px}
  .trk-act .btn.mini{width:100%}
  .trk-act a{color:var(--action-text); font-weight:700}

  /* pallini della presentazione iniziale */
  .introdots{display:flex; gap:7px; justify-content:center; margin:16px 0 2px}
  .introdots span{width:7px; height:7px; border-radius:50%; background:var(--border-strong); opacity:.5}
  .introdots span.on{background:var(--action); opacity:1; width:20px; border-radius:999px}

  /* ---- etichette dei piatti e filtri del menu ---- */
  .labs{display:flex; flex-wrap:wrap; gap:5px; margin-top:5px}
  .lab{font-size:10.5px; font-weight:700; letter-spacing:.03em; padding:2px 7px; border-radius:999px;
    background:var(--ok-soft); color:var(--ok)}
  .lab-piccante{background:var(--warn-soft); color:var(--warn)}
  .lab-pesce{background:var(--neutral-soft); color:var(--neutral)}
  .filtri{display:flex; gap:7px; overflow-x:auto; padding:2px 0 10px; scrollbar-width:none}
  .filtri::-webkit-scrollbar{display:none}
  /* TASK-046 — stessa regola delle pastiglie delle sezioni, e per lo stesso motivo: nel mockup
     `pill(on)` è `bg: on ? accent : 'transparent'` con l'inchiostro pieno quando è spenta.
     Piene tutte, "acceso" doveva dirlo solo la tinta; vuote, lo dice la forma. */
  .filtro{flex:none; border:1px solid var(--border-strong); background:transparent; color:var(--ink);
    font:inherit; font-size:12.5px; font-weight:400; letter-spacing:.02em;
    padding:0 13px; min-height:var(--tap-min); border-radius:999px; cursor:pointer}
  .filtro .n{opacity:.6; font-weight:700}
  .filtro[aria-pressed="true"]{background:var(--action); border-color:var(--action); color:var(--action-ink)}
  .filtro[aria-pressed="true"] .n{opacity:.8}
  .filtri-nota{margin:-4px 0 12px}
  .dish[hidden], .card[hidden], h2.sec[hidden]{display:none}

  /* ---- allergeni: dichiarazione di legge, non un'etichetta (vedi src/legale.php) ----
     Sta sotto le etichette e sopra il prezzo: chi scorre il menu deve incontrarla senza cercarla,
     perché nella vendita a distanza l'informazione va data PRIMA dell'acquisto. Tono sobrio
     quando c'è, tono d'avviso quando manca — la differenza fra le due dev'essere visibile
     senza leggere. */
  .alg{font-size:11.5px; line-height:1.35; color:var(--ink-muted); margin-top:4px}
  .alg.manca{color:var(--warn); font-weight:600}
  .algrow{display:flex; flex-wrap:wrap; align-items:baseline; justify-content:space-between; gap:6px 12px;
    padding:11px 13px}
  .algrow + .algrow{border-top:1px solid var(--border)}
  .algname{font-family:var(--serif); font-size:15.5px; font-weight:600; color:var(--ink)}
  .algchips{display:flex; flex-wrap:wrap; gap:5px; justify-content:flex-end}
  .algchip{display:inline-flex; align-items:center; gap:5px; font-size:11px; font-weight:700;
    padding:3px 9px 3px 6px; border-radius:999px; background:var(--warn-soft); color:var(--warn)}
  /* L'elenco dei 14 è numerato per legge (è l'ordine dell'Allegato II) ma qui il numero non serve
     a nessuno: quello che serve è associare il simbolo alla frase, ed è la sola pagina dove i due
     stanno vicini. Da lista numerata a righe con l'icona. */
  .alglist{margin:0; padding:0; list-style:none; display:flex; flex-direction:column; gap:11px;
    font-size:13px; line-height:1.45; color:var(--ink-2)}
  .alglist li{display:flex; align-items:flex-start; gap:11px}
  .alglist b{color:var(--ink)}
  .alg-set{margin-top:12px}
  .alg-set .alg-conf{margin-bottom:0}

  /* ============ TASK-025 · la pagina di un piatto ============
     Nel menu il piatto sta stretto fra altri dodici. Qui è l'unica cosa sullo schermo.

     La testata è il punto della pagina: l'immagine non finisce con un taglio netto, **sfuma nel
     colore della pagina**, e il nome del piatto sta dentro la sfumatura. È lì che la foto smette
     di essere un riquadro e diventa l'intestazione. Senza foto funziona uguale: a sfumare è il
     piatto navy col monogramma, che è disegnato e non è un buco. */
  .app.piatto{background:var(--bg)}
  .pt-hero{position:relative; margin:0; aspect-ratio:1/1; max-height:62vh; overflow:hidden;
    background:var(--thumb-bg); display:grid; place-items:center}
  .pt-hero.has{background:var(--neutral-soft)}
  .pt-hero img{width:100%; height:100%; object-fit:cover; display:block}
  .pt-hero::before{content:attr(data-mono); font-family:var(--serif); font-weight:600;
    font-size:132px; line-height:1; color:rgba(254,251,247,.13)}
  .pt-hero.has::before{content:none}
  /* La sfumatura: trasparente per metà, poi il colore della pagina. I due passaggi in mezzo non
     sono decorazione — con una rampa sola il centro sembra un velo grigio steso sulla foto. */
  .pt-fade{position:absolute; inset:0; pointer-events:none;
    background:linear-gradient(to bottom,
      rgba(0,0,0,.34) 0, rgba(0,0,0,0) 26%,
      color-mix(in srgb, var(--bg) 0%, transparent) 46%,
      color-mix(in srgb, var(--bg) 62%, transparent) 76%,
      color-mix(in srgb, var(--bg) 94%, transparent) 92%,
      var(--bg) 100%)}
  .pt-titolo{position:absolute; left:0; right:0; bottom:0; z-index:2; padding:0 16px 14px}
  .pt-cat{display:block; font-size:11.5px; font-weight:700; letter-spacing:.14em;
    text-transform:uppercase; color:var(--action-text); margin-bottom:3px}
  .pt-titolo h1{font-family:var(--serif); font-size:31px; line-height:1.08; font-weight:600;
    color:var(--ink); margin:0; letter-spacing:-.015em; text-wrap:balance}
  /* Il ritorno al menu galleggia sulla foto, dove la sfumatura non è ancora arrivata: quindi
     porta con sé il proprio fondo scuro, altrimenti su una foto chiara sparisce. */
  /* TASK-059 — 44px, non 40: è il ritorno indietro della pagina, si tocca di corsa e galleggia
     su una foto, dove non c'è un bordo che aiuti a mirare. */
  .pt-back{position:absolute; z-index:3; top:calc(10px + env(safe-area-inset-top, 0px)); left:12px;
    width:var(--tap-min); height:var(--tap-min); border-radius:50%; display:grid; place-items:center;
    background:rgba(20,15,10,.46); color:var(--warm-50); text-decoration:none;
    backdrop-filter:blur(6px)}
  .pt-back:hover{background:rgba(20,15,10,.62)}
  .pt-hero .stamp{top:42%}

  .pt-body{padding-top:6px; padding-bottom:calc(var(--cartbar-h, 84px) + env(safe-area-inset-bottom, 0px) + 20px)}
  /* Qui la descrizione è un paragrafo, non una didascalia: ha lo spazio per esserlo. */
  .pt-desc{font-size:16px; line-height:1.55; color:var(--ink-2); margin:12px 0 0;
    max-width:46ch; text-wrap:pretty; hyphens:none}
  .pt-body .labs{margin-top:2px}
  .pt-prezzo{display:flex; align-items:center; gap:11px; margin-top:14px}
  .pt-prezzo .price{font-size:25px}
  .pt-blocco{margin-top:22px; background:var(--surface); border:1px solid var(--border);
    border-radius:var(--r); box-shadow:var(--shadow); padding:15px 16px}
  .pt-blocco h2{display:flex; align-items:center; gap:8px; font-family:var(--serif);
    font-size:16px; font-weight:600; color:var(--ink); margin:0 0 10px}
  .pt-blocco h2 svg{color:var(--action-text)}
  .pt-blocco p{font-size:14px; line-height:1.5; color:var(--ink-2); margin:0 0 10px}
  .pt-blocco p:last-of-type{margin-bottom:0}
  .pt-nota{font-size:12.5px !important; color:var(--ink-muted) !important; line-height:1.5 !important}
  /* TASK-060 — l'avviso «allergeni non dichiarati» è una **riga**, non un riquadro: fuori dalla
     scheda compete meno col piatto e si legge prima. Il filo a sinistra le dà il peso che serve
     senza darle la forma di una carta. */
  .pt-manca{color:var(--warn) !important; font-weight:600; font-size:13px !important;
    line-height:1.45 !important; margin:16px 0 0 !important; padding:0 0 0 12px;
    border-left:3px solid var(--warn); display:flex; flex-wrap:wrap; gap:5px; align-items:baseline}
  .pt-manca a{white-space:nowrap}
  /* L'unica riga di «Come e quando» che cambia una decisione: quando si chiude. Il resto è
     tornato dove sta di casa, in «Chi vende e condizioni». */
  .pt-quando{margin-top:20px; font-size:12.5px; color:var(--ink-muted); line-height:1.6}
  .pt-ok{color:var(--ok) !important; font-weight:600; display:flex; align-items:center; gap:7px}
  .pt-alg{list-style:none; margin:0 0 10px; padding:0; display:flex; flex-direction:column; gap:11px}
  .pt-alg li{display:flex; align-items:flex-start; gap:11px; font-size:13.5px; line-height:1.4}
  .pt-algi{flex:none; width:34px; height:34px; border-radius:10px; display:grid; place-items:center;
    background:var(--action-soft); color:var(--action-text); margin-top:-2px}
  .pt-algt{display:flex; flex-direction:column; min-width:0}
  .pt-alg b{color:var(--ink)}
  .pt-alg .pt-algt span{color:var(--ink-muted); font-size:12.5px; margin-top:1px}
  .pt-blocco .link{display:inline-block; margin-top:12px; font-size:13.5px}

  /* Il comando in fondo alla pagina del piatto. Era appiccicato («non se ne va mai»); da DEC-007 §1
     non lo è più — su questa pagina non serviva nemmeno, perché il piatto è uno solo e la pagina
     finisce due schermate sotto. Resta l'ultima cosa che si incontra, che è il punto. */
  .pt-barra{display:flex; gap:10px; align-items:center;
    margin-top:8px; padding:16px 16px calc(20px + env(safe-area-inset-bottom));
    border-top:1px solid var(--border); background:var(--surface)}
  .pt-barra .cartbtn{flex:1}
  .pt-qta{display:inline-flex; align-items:center; border:1px solid var(--border-strong);
    border-radius:999px; background:var(--surface-raised); overflow:hidden; flex:none}
  .pt-qta[hidden]{display:none}
  .pt-qta button{width:46px; height:50px; border:0; background:transparent; color:var(--action-text);
    font-size:20px; font-weight:600; cursor:pointer; display:grid; place-items:center; line-height:1}
  .pt-qta button:disabled{color:var(--disabled-ink); cursor:not-allowed}
  .pt-qta .q{min-width:26px; text-align:center; font-weight:700; font-variant-numeric:tabular-nums;
    font-size:16px}

  @media (min-width:760px){
    .app.piatto{max-width:min(46rem, 100% - 2rem)}
    .pt-hero{border-radius:0 0 var(--r) var(--r); max-height:44vh}
    .pt-titolo h1{font-size:34px}
  }

  /* ============================================================
     TASK-032 · GLI ALLERGENI COME SIMBOLI
     Erano una riga di testo su ogni scheda («Allergeni: glutine, latte, uova»): la frase più
     lunga del menu, ripetuta tredici volte, e l'unica che nessuno leggeva finché non serviva
     davvero. Adesso sono icone — e il testo non è sparito, è passato a chi legge con la voce
     (`.visually-hidden` dentro `.algi`).
     Gli altri due stati portano una parola con sé, perché "senza allergeni" e "da chiedere" sono
     dichiarazioni, non elenchi, e un simbolo da solo non le direbbe.
     ============================================================ */
  .algi{display:flex; align-items:center; gap:7px; flex-wrap:wrap; margin-top:6px;
    color:var(--ink-muted); min-height:20px}
  .alg-i{display:inline-flex; line-height:0}
  .algi-t{font-size:12px; font-weight:600; letter-spacing:.01em}
  .algi.nessuno{color:var(--ok)}
  .algi.manca{color:var(--warn)}
  /* Nel registro a vetrina la scheda è grande e i simboli possono respirare. */
  .dish.vetrina .algi{gap:9px; margin-top:8px}

  /* ============================================================
     TASK-034/A · LE IMPOSTAZIONI DEL CLIENTE
     Un elenco di righe tutte uguali, ognuna con la sua icona e la sua riga di spiegazione.
     Nessuna è più importante delle altre: è il posto dove si va a cercare, non dove si viene
     spinti — al contrario della fila di pastiglie che c'era prima sopra il menu.
     ============================================================ */
  .imp h1{font-family:var(--serif); font-size:32px; font-weight:400; margin:2px 0 18px;
    letter-spacing:-.01em}
  .back{display:inline-flex; align-items:center; gap:7px; min-height:var(--tap-min);
    color:var(--ink-muted); text-decoration:none; font-size:13.5px; font-weight:600; margin:-10px 0 2px}
  .back:active{color:var(--action-text)}
  .mlist{display:flex; flex-direction:column; margin:2px -4px -4px}
  .mrow{display:flex; align-items:center; gap:12px; padding:11px 4px; min-height:var(--tap);
    text-decoration:none; color:var(--ink); border-radius:12px}
  .mrow + .mrow{border-top:1px solid var(--border)}
  .mrow:active{background:var(--action-soft)}
  .mrow[hidden]{display:none}
  .mico{flex:none; width:34px; height:34px; border-radius:10px; display:grid; place-items:center;
    background:var(--action-soft); color:var(--action-text)}
  .mtxt{display:flex; flex-direction:column; min-width:0; flex:1}
  .mtxt b{font-size:14.5px; font-weight:600}
  .mtxt small{font-size:12.5px; color:var(--ink-muted); margin-top:1px}
  .mgo{flex:none; color:var(--border-strong); font-size:20px; line-height:1}
  .kv{display:flex; justify-content:space-between; align-items:baseline; gap:12px;
    padding:9px 0; font-size:14px; border-bottom:1px solid var(--border)}
  .kv span{color:var(--ink-muted); font-size:13px}
  .kv:last-of-type{border-bottom:0}
  .small{font-size:12.5px; line-height:1.5}
  .center{text-align:center}

  /* ---- TASK-036/B · la riga in cima alla testata del giorno ----
     "I miei ordini" è l'unica delle sette pastiglie che si cerca **mentre** si ordina: resta sul
     menu, ma appaiata al giorno, non in una fila di comandi tutti uguali. */
  .td-top{display:flex; align-items:center; justify-content:space-between; gap:12px}
  .td-mine{display:inline-flex; align-items:center; gap:6px; min-height:var(--tap-min);
    color:var(--ink-muted); text-decoration:none; font-size:12.5px; font-weight:600;
    white-space:nowrap; margin:-10px -6px -10px 0; padding:0 6px; border-radius:10px}
  .td-mine:active{color:var(--action-text); background:var(--action-soft)}

  /* ---- pagine di legge ---- */
  .legal .sheet + .sheet{margin-top:14px}
  .legal-ul{margin:0; padding-left:1.25em; display:flex; flex-direction:column; gap:9px;
    font-size:14px; line-height:1.5; color:var(--ink-2)}
  .legal-ul b{color:var(--ink)}
  .privrow{padding:11px 0; border-bottom:1px solid var(--border)}
  .privrow:last-child{border-bottom:0; padding-bottom:0}
  .privq{font-weight:700; font-size:14px; color:var(--ink); line-height:1.35}
  .privd{display:flex; flex-direction:column; gap:3px; margin-top:5px;
    font-size:12.5px; line-height:1.45; color:var(--ink-muted)}
  .privd b{color:var(--ink-2); font-weight:700}
  /* il richiamo sotto il pulsante di conferma: va letto, non deve gridare */
  /* TASK-056 — il conto del buono: quanto copre, quanto si integra, quanto avanza.
     Non è una nota a margine, è la riga che fa aggiungere il caffè invece di lasciare 50
     centesimi sul tavolo — quindi ha il peso di un'informazione, non di una postilla. */
  .ck-buono{margin-top:10px; padding:9px 12px; border-radius:var(--r-sm, 12px);
    background:var(--action-soft); color:var(--action-text); font-size:12.5px; line-height:1.45}
  .ck-buono b{color:var(--ink)}
  /* DEC-015 — «Sei al tavolo 7», per chi è entrato dal QR.
     Non è un comando e non è una scelta: è una constatazione, e il secondo passaggio del checkout
     per lui è tutto qui. Ha il peso di un'informazione data — stesso fondo del conto del buono,
     che è l'altra cosa in questa schermata che l'app dice invece di chiedere. */
  .ck-tavolo{padding:11px 13px; border-radius:var(--r-sm, 12px);
    background:var(--action-soft); color:var(--action-text); line-height:1.4}
  .ck-tavolo b{color:var(--ink); font-size:16px}
  .ck-legal{font-size:11.5px; line-height:1.5; margin:2px 2px 12px}
  /* TASK-059 — «condizioni di vendita» è il collegamento che l'art. 49 del Codice del Consumo
     obbliga a mettere qui, ed era alto 14px: il bersaglio più piccolo della schermata.
     Un obbligo che nessuno riesce a toccare è dichiarato, non assolto. Il rettangolo visibile
     resta quello del testo — è dentro un paragrafo, ingrassarlo spezzerebbe la riga — e l'area
     si estende sotto con un `::after`, che è il modo di allargare un bersaglio senza muovere
     il testo attorno. */
  .ck-legal a{position:relative; display:inline-block}
  .ck-legal a::after{content:""; position:absolute; left:0; right:0;
    top:50%; height:var(--tap-min); transform:translateY(-50%)}
  .ck-legal a{color:var(--action-text)}
  .legal-foot{padding:22px 16px calc(18px + env(safe-area-inset-bottom, 0px));
    border-top:1px solid var(--border); margin-top:8px; text-align:center}
  .legal-foot nav{display:flex; flex-wrap:wrap; gap:6px 18px; justify-content:center}
  .legal-foot a{font-size:12.5px; font-weight:600; color:var(--ink-muted); text-decoration:none}
  .legal-foot a:hover{color:var(--action-text)}
  .lf-who{font-size:11.5px; color:var(--ink-muted); margin-top:9px; opacity:.85}

  /* etichette nel modulo del piatto (gestore) */
  .labset{border:1px solid var(--border-strong); border-radius:12px; padding:12px; margin:0}
  .labset legend{padding:0 6px}
  .labgrid{display:grid; grid-template-columns:repeat(auto-fill, minmax(9rem,1fr)); gap:6px}
  .labopt{display:flex; align-items:center; gap:8px; min-height:var(--tap-min); font-size:13.5px; cursor:pointer}
  .labopt input{width:19px; height:19px; accent-color:var(--action)}

  /* nota del cliente sulla scheda ordine del bar: deve saltare all'occhio */
  .onote{margin:8px 0 0; padding:8px 10px; border-radius:var(--r-sm);
    background:var(--warn-soft); color:var(--warn); font-size:13px; font-weight:600}

  /* ---- ritocco automatico della foto: confronto prima/dopo ---- */
  .aibox{border:1px solid var(--border-strong); border-radius:12px; padding:12px; margin-top:12px}
  .aibox.pending{border-color:var(--action-line); background:var(--action-soft)}
  .aipair{display:grid; grid-template-columns:1fr 1fr; gap:10px; margin:4px 0 12px}
  .aipair figure{margin:0}
  .aipair img{width:100%; aspect-ratio:1/1; object-fit:cover; border-radius:var(--r-sm); display:block;
    border:1px solid var(--border)}
  .aipair figcaption{font-size:12px; font-weight:700; color:var(--ink-2); text-align:center; margin-top:5px}
  .aiact{display:flex; gap:8px; flex-wrap:wrap}
  .aiact .btn.mini{flex:1; min-width:9rem}

  /* ---- formule pranzo ---- */
  .combos{display:grid; gap:11px; margin-bottom:6px}
  @media (min-width:640px){ .combos{grid-template-columns:repeat(auto-fill, minmax(min(17rem,100%),1fr))} }
  .combo{background:var(--surface); border:1px solid var(--action-line); border-radius:var(--r);
    box-shadow:var(--shadow); padding:13px 14px; display:flex; flex-direction:column; gap:10px}
  .cname{font-family:var(--serif); font-size:17px; font-weight:600; line-height:1.2}
  .cdesc{font-size:12.5px; color:var(--ink-muted); margin-top:3px; line-height:1.4}
  .cfoot{display:flex; align-items:center; gap:9px; margin-top:auto; flex-wrap:wrap}
  .csave{font-size:11.5px; font-weight:700; padding:3px 9px; border-radius:999px;
    background:var(--ok-soft); color:var(--ok)}
  .cfoot .addbtn{margin-left:auto}
  .lineitem small{display:block; font-weight:500; margin-top:2px}
