/*
  Customer-facing menu, two levels: a home screen of large section tiles, and
  the dish list of the section that was tapped. Layout follows the food-delivery
  app convention the owner pointed at — photo-led tiles, then each dish as a
  full-width photo with its text underneath, separated by hair lines rather
  than boxed cards.

  Strictly monochrome — see tokens.css. The dish photos are the only colour on
  the page, which is the whole idea.
*/

* { box-sizing: border-box; }

/* L'altezza delle due barre in cima — la striscia con logo e nome sulla
   schermata iniziale, e la barra «‹ Menù» dentro una sezione — è **una sola
   misura**, dichiarata qui.

   Prima erano due numeri indipendenti: 68 px la striscia (logo da 52 più 8+8 di
   respiro) e 60 px la barra (pulsante da 44 più lo stesso respiro). Otto pixel
   di differenza, che passando da una schermata all'altra si vedono come un
   salto. Segnalato dal titolare. Con una misura sola non possono più divergere:
   chi domani cambia l'una cambia l'altra senza doverselo ricordare.

   Sta in components.css e non in tokens.css perché quest'ultimo è condiviso col
   pannello del titolare (§ 11.1), e lì questa misura non significa niente.

   Dal 18/09/2026 le fasce che seguono questa misura sono **tre**, non due: si è
   aggiunta la striscia del carrello in fondo (`.cart-bar`), che prima aveva
   numeri suoi ed era alta 48 px contro i 68 delle altre. Stesso difetto di
   allora, stesso rimedio — e stessa segnalazione, di nuovo dal titolare. Chi
   cambia questo valore cambia anche il fondo della pagina: lo spazio riservato
   `body.has-cart-bar` è legato a lui apposta, e non a un numero copiato. */
:root { --alt-barra: 68px; }

/* .restaurant-logo below sets display, which would silently beat the hidden
   attribute and leave a broken image on the menu of a venue with no logo. */
[hidden] { display: none !important; }

/* L'anello del fuoco. Sui bottoni neri (sfondo e anello di default dello
   stesso colore) l'anello predefinito di Chromium non si vede affatto: chi
   naviga da tastiera non sa mai dove si trova. Il doppio anello bianco+nero
   si vede sia sui bottoni neri sia sugli sfondi bianchi del resto della
   pagina, senza introdurre nessun colore nuovo nel menù monocromo. */
:focus-visible {
  outline: 2px solid #ffffff;
  outline-offset: 2px;
  box-shadow: 0 0 0 4px #111111;
}

html, body {
  margin: 0;
  padding: 0;
  background: var(--color-background);
  color: var(--color-text);
  font-family: var(--font-family);
  font-size: 16px;
  line-height: 1.45;
  -webkit-text-size-adjust: 100%;
  -webkit-font-smoothing: antialiased;
  /* Una parola sola, scritta dal titolare senza spazi
     ("Pappardellealcinghialeconfunghiporcini…", un indirizzo, un hashtag), non
     ha nessun punto in cui andare a capo: il suo blocco resta largo quanto la
     parola e **la pagina intera diventa più larga dello schermo**. Su un
     telefono non si vede come un piatto sbilenco — Chrome rimpicciolisce tutto
     il menù per farci stare la parola, e da lì in poi la barra del carrello e
     il cassetto (che si centrano sulla finestra, non sul testo) escono per
     metà dal bordo destro. Qui la regola è dichiarata una volta su `body` e
     scende per eredità su ogni testo che arriva dal titolare — nome del
     locale, nome della sezione, nome e descrizione del piatto, etichetta del
     tavolo, riga del carrello — perché il testo che può essere lungo non è
     uno solo, ed elencarli a uno a uno vuol dire dimenticarne uno il giorno
     che se ne aggiunge un altro. */
  overflow-wrap: break-word;
}

body {
  padding-bottom: max(var(--space-6), env(safe-area-inset-bottom));
  overflow-x: hidden;
}

/* Lo spazio che la striscia del carrello si tiene in fondo allo schermo.
 *
 * Fino al 18/09/2026 serviva a **limitare il danno**: la barra galleggiava, e
 * questo spazio impediva almeno che coprisse l'ultimo "Aggiungi" o il link
 * privacy una volta arrivati in fondo. A metà scorrimento non poteva farci
 * niente, ed è lì che nasceva il difetto.
 *
 * Adesso la striscia è appoggiata, e questo spazio è **la sua altezza**: il
 * menù finisce dove comincia lei. Non è più un rimedio, è una misura — e va
 * verificata a occhio sul telefono, non calcolata: se resta corta, il link
 * dell'informativa privacy, che per legge dev'essere raggiungibile, finisce
 * sotto la striscia. La classe la mette updateCartBar() solo quando la
 * striscia è davvero visibile. */
body.has-cart-bar .menu-content {
  /* Legato a `--alt-barra`, non a un numero scritto a mano: la striscia ha
     preso l'altezza della testata, e se un giorno quel token cambia questo
     spazio lo segue da solo. I 12px in più sono aria, non superstizione: senza,
     l'ultimo elemento tocca il bordo della striscia. Il valore precedente era
     64px, cioè **meno** dell'altezza nuova: lasciato com'era, il link
     dell'informativa privacy sarebbe finito sotto la striscia. */
  padding-bottom: calc(var(--alt-barra) + 12px + env(safe-area-inset-bottom, 0px));
}

/* --- Testata ---------------------------------------------------------- */

.menu-header {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: nowrap;
  padding: var(--space-4) var(--space-4) var(--space-5);
  max-width: 720px;
  margin: 0 auto;
}

/* Riserva in CSS lo spazio che il logo occuperà, appena arriva da JS:
   misurato nell'audit SEO (allora con un logo da 64 px), porta il CLS della
   home da 0,086 a 0,002. Solo quando il logo c'è — la classe la mette
   renderRestaurantHeader() — perché senza logo la testata è già alta 72px e
   non si sposta: il min-height aggiungerebbe 56px di testata vuota a ogni
   locale senza logo. box-sizing è border-box globale, quindi 88 (il logo tondo
   di `.restaurant-logo`) + 16 (padding sopra) + 24 (padding sotto) = 128px,
   l'altezza esatta della testata col logo. Le tre misure seguono
   `.restaurant-logo` e il padding di `.menu-header` qui sopra: se una cambia,
   il conto va rifatto o lo spazio riservato torna sbagliato e il salto
   ricompare. Con la striscia accesa questa regola non conta più:
   `body.striscia-* .menu-header` è più specifica e impone `--alt-barra`. */
.menu-header.has-logo {
  min-height: calc(88px + var(--space-4) + var(--space-5));
}

/* Il nome sta **fra** il logo e la pastiglia del tavolo, e prende tutto lo
   spazio che avanza: centrato lì dentro resta centrato anche quando il tavolo
   non c'è. `min-width: 0` è la solita difesa dei testi del titolare dentro un
   flex (§ 5.4): senza, un nome scritto tutto attaccato allarga la pagina. */
.restaurant-name {
  flex: 1 1 auto;
  text-align: center;
  font-size: 1.75rem;
  font-weight: 800;
  letter-spacing: -0.025em;
  line-height: 1.15;
  margin: 0;
  min-width: 0;
  overflow-wrap: break-word;
}

/* --- Contenuto -------------------------------------------------------- */

.menu-content {
  padding: 0 var(--space-4);
  max-width: 720px;
  margin: 0 auto;
}

/* Section header: a full-bleed photo band with the section name laid over it.
   The dark gradient is what keeps white text readable on a bright food photo —
   without it the name disappears over pale dishes. */
.category-header {
  position: relative;
  margin: 0 calc(-1 * var(--space-4)) var(--space-4);
  padding: var(--space-5) var(--space-4);
  min-height: 56px;
  display: flex;
  align-items: flex-end;
  overflow: hidden;
}

.category-header.has-photo {
  min-height: 150px;
  border-radius: 0;
}

.category-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
}

.category-header.has-photo::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(0, 0, 0, 0.15) 0%, rgba(0, 0, 0, 0.72) 100%);
  z-index: 1;
}

.category-title {
  position: relative;
  z-index: 2;
  font-size: 1.5rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  margin: 0;
  /* Il titolo è dentro una fascia `display: flex`, e un figlio di un flex non
     scende sotto la larghezza della sua parola più lunga se non glielo si dice:
     con `overflow-wrap` da solo il nome di una sezione scritto tutto attaccato
     resta largo 600 px, e siccome la fascia ha `overflow: hidden` viene
     **tagliato** invece che andare a capo. Le stesse due righe stanno su
     `.restaurant-name` e `.section-tile-name`, per lo stesso motivo. */
  min-width: 0;
  overflow-wrap: break-word;
}

.category-header.has-photo .category-title {
  color: #ffffff;
  font-size: 1.85rem;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}

/* --- Logo del locale --------------------------------------------------- */

/* Accanto al nome, non sopra: insieme formano l'insegna del locale, e messi in
   riga occupano l'altezza di uno solo — sul telefono quello spazio in meno è
   una fila di piatti in più visibile senza scorrere.

   **Tondo e non rettangolare.** Il ritaglio circolare con `object-fit: cover`
   tiene solo il centro dell'immagine: una foto storta, o scattata allo schermo
   di un computer, smette di mostrare i bordi. `aspect-ratio` insieme a
   `flex: 0 0 auto` serve perché in una riga flex il cerchio non si schiacci in
   un ovale quando accanto c'è un nome lungo. */
.restaurant-logo {
  display: block;
  flex: 0 0 auto;
  width: 88px;
  height: 88px;
  aspect-ratio: 1 / 1;
  border-radius: 50%;
  object-fit: cover;
  margin: 0;
}

/* --- Sfondo scelto dal titolare --------------------------------------- */

/* Un solo strato fisso dietro tutte e due le viste: lo sfondo non scorre col
   contenuto e non cambia passando da un livello all'altro, che è ciò che
   chiedeva il titolare — lo stesso sfondo dall'inizio alla fine del menù.
   Fisso e non "background-attachment: fixed", che su iOS non funziona. */
.menu-background {
  position: fixed;
  inset: 0;
  z-index: -1;
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
}

/* Il velo fra la foto e il testo. Senza, su una foto scura o affollata i prezzi
   diventano illeggibili: il titolare sceglie lo sfondo guardando il proprio
   telefono, non quello del cliente al tavolo con meno luce. */
.menu-background::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--color-veil);
}

/* Il bianco della pagina coprirebbe la foto: viene dipinto sopra agli strati a
   z-index negativo, non sotto. Va tolto solo quando uno sfondo c'è davvero. */
body.has-background { background: transparent; }

/* Col fondo bianco il piatto è separato dal successivo da una linea. Sopra una
   foto quella linea non basta più: il testo va posato su una scheda piena,
   altrimenti nome e prezzo finiscono direttamente sull'immagine. */
body.has-background .product-card,
body.has-background .menu-empty {
  background: var(--color-surface);
  border-radius: var(--radius-card);
  border-bottom: 0;
  padding: var(--space-3);
  margin-bottom: var(--space-4);
}

/* Una sezione senza foto propria: il titolo va posato anche lui su una scheda.
   Le rientranze negative che la fanno arrivare ai bordi vanno tolte, o gli
   angoli arrotondati finirebbero fuori dallo schermo. */
body.has-background .category-header:not(.has-photo) {
  background: var(--color-surface);
  border-radius: var(--radius-card);
  margin-left: 0;
  margin-right: 0;
}

/* La barra "‹ Menù" è appiccicata in alto e il contenuto le passa sotto:
   trasparente lascerebbe leggere due testi sovrapposti. */
body.has-background .section-topbar {
  background: var(--color-surface-sticky);
}

/* --- Filtro sullo sfondo e colore del testo ---------------------------- */
/*
  Due impostazioni scelte dal titolare nel pannello, che arrivano dentro
  `/api/menu` come due parole e qui diventano due classi sul `body`.

  Prima erano un valore fisso: un velo bianco al 62% uguale per tutti. Su una
  foto scura non bastava e i prezzi restavano illeggibili; su una foto chiara
  spegneva la foto senza che servisse. Era una scelta che solo chi guarda il
  proprio menù può fare.

  Il filtro ha **due strade** perché sotto ha due fondali diversi: con una foto
  agisce sul velo sopra la foto, senza foto agisce sul fondo della pagina.
  Altrimenti scegliere «forte» in un locale che non ha messo nessuno sfondo non
  farebbe assolutamente niente, e il titolare crederebbe che l'impostazione sia
  rotta.
*/

body.filtro-nessuno .menu-background::after { background: transparent; }

body.filtro-leggero .menu-background::after {
  background: linear-gradient(180deg, rgba(0, 0, 0, 0.22) 0%, rgba(0, 0, 0, 0.18) 100%);
}

body.filtro-forte .menu-background::after {
  background: linear-gradient(180deg, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0.45) 100%);
}

/* Senza foto il filtro agisce sul fondo. `:not(.has-background)` perché quando
   la foto c'è il fondo dev'essere trasparente, o coprirebbe la foto. */
body.filtro-leggero:not(.has-background) { background: #d9d9d9; }
body.filtro-forte:not(.has-background) { background: #5f5f5f; }

/* Il colore del testo si cambia **sui token**, non sui singoli elementi: in
   questo foglio le regole che scrivono `color: var(--color-text)` sono undici e
   quelle col grigio di contorno altre otto. Cambiarle una per una vuol dire
   dimenticarne una il giorno che se ne aggiunge la ventesima. */
body.testo-bianco {
  --color-text: #ffffff;
  --color-text-muted: rgba(255, 255, 255, 0.78);
  --color-border: rgba(255, 255, 255, 0.38);
  --color-border-strong: #ffffff;
  /* Le superfici si invertono **insieme** al testo. Una scheda piatto bianca
     con sopra un testo bianco è invisibile, ed è esattamente quello che
     succede a cambiare il colore del testo senza toccare queste due. */
  --color-surface: rgba(0, 0, 0, 0.42);
  --color-surface-sticky: rgba(0, 0, 0, 0.62);
  --color-surface-muted: rgba(255, 255, 255, 0.16);
  /* E i pulsanti pieni: bianchi con la scritta scura, o «Aggiungi» sparirebbe
     nel fondo scuro. */
  --color-accent: #ffffff;
  --color-accent-contrast: #111111;
}

/* Testo bianco e nessuna foto: il fondo dev'essere scuro comunque, o si
   scriverebbe bianco su bianco. Quanto, lo decide il livello del filtro. */
body.testo-bianco.filtro-nessuno:not(.has-background) { background: #3a3a3a; }
body.testo-bianco.filtro-leggero:not(.has-background) { background: #2e2e2e; }
body.testo-bianco.filtro-forte:not(.has-background) { background: #1c1c1c; }


/* --- La striscia piena in cima ----------------------------------------- */
/*
  Idea del titolare, ispirata all'intestazione di Instagram: una fascia a tinta
  **piena** — bianca o nera, mai trasparente — che tiene insieme logo, nome del
  locale e tavolo, e resta ferma mentre il cliente scorre. Nasce da un problema
  vero: sopra una foto di sfondo scura il nome del locale quasi spariva.

  L'insegna sta fuori dalle due viste (`index.html`) proprio per questo: al
  livello 2 non esisteva affatto, e una striscia che sparisce entrando in una
  sezione non è una striscia.

  `z-index: 20` la mette sopra il contenuto e sopra la barra «‹ Menù» (che è a
  10), e **sotto** il carrello e i suoi veli (1000 in su). Fra 10 e 1000 non c'è
  nient'altro: la fascia era libera.
*/
body.striscia-bianca .menu-header,
body.striscia-nera .menu-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 20;
  max-width: none;
  margin: 0;
  padding: var(--space-2) var(--space-4);
  /* Il notch: la striscia tocca il bordo alto, quindi si ingrassa quanto serve.
     Stessa tecnica della barra «‹ Menù», che è l'unico altro elemento del menù
     attaccato al bordo superiore. */
  padding-top: max(var(--space-2), env(safe-area-inset-top));
  gap: var(--space-3);
  min-height: var(--alt-barra);
}

/* Tinte piene e opache: niente `rgba`, o si vedrebbe la foto di sfondo sotto e
   non sarebbe più una striscia. */
body.striscia-bianca .menu-header { background: #ffffff; }
body.striscia-nera .menu-header { background: #111111; }

/* Il testo dentro la striscia non segue il colore scelto per il menù: segue la
   **striscia**, o su fondo nero resterebbe nero. È l'unico punto del menù dove
   il colore del testo non è quello del titolare, e il motivo è che qui il fondo
   non è la sua foto: è una tinta che ha scelto lui stesso. */
body.striscia-bianca .menu-header .restaurant-name,
body.striscia-bianca .menu-header .table-notice-nome { color: #111111; }
body.striscia-bianca .menu-header .table-notice-tavolo { border-color: #111111; color: #111111; }

body.striscia-nera .menu-header .restaurant-name,
body.striscia-nera .menu-header .table-notice-nome { color: #ffffff; }
body.striscia-nera .menu-header .table-notice-tavolo { border-color: #ffffff; color: #ffffff; }

/* Il nome del locale, quando la striscia è accesa, scala con la larghezza
 * dello schermo invece di restare fisso a 1.75rem: con la pastiglia che
 * adesso non cede più spazio (vedi sopra), un nome lungo su un telefono
 * stretto è l'unico elemento che può stringersi. Niente taglio con i puntini
 * e niente `overflow: hidden`: stessa regola del nome del tavolo (§ "il nome
 * non si taglia mai") — va a capo quanto serve, anche su tre righe. Non è la
 * testata a farsi più alta senza controllo: è aggiornaAltezzaBarra() in
 * app.js, con un ResizeObserver sulla striscia vera, a tenere lo spazio
 * riservato sotto (`--alt-barra`) uguale alla sua altezza reale, cosicché il
 * contenuto non è mai coperto qualunque sia il numero di righe. Solo qui,
 * con la striscia "nessuna" la testata non è `position: fixed` e cresce nel
 * flusso normale senza bisogno di scalare. */
body.striscia-bianca .menu-header .restaurant-name,
body.striscia-nera .menu-header .restaurant-name {
  font-size: clamp(1rem, 5vw, 1.75rem);
}

/* Il logo si rimpicciolisce: 88 px in una fascia ferma la renderebbero alta un
   quinto di schermo, tolto per sempre ai piatti. */
body.striscia-bianca .restaurant-logo,
body.striscia-nera .restaurant-logo {
  width: 52px;
  height: 52px;
}

/* Lo spazio che la striscia occupa va restituito al contenuto, o la prima riga
   del menù nasce già coperta. Stessa misura per la barra «‹ Menù», che altrimenti
   si appiccicherebbe **sotto** la striscia sovrapponendocisi. */
body.striscia-bianca,
body.striscia-nera { padding-top: var(--alt-barra); }

body.striscia-bianca .section-topbar,
body.striscia-nera .section-topbar { top: var(--alt-barra); }

/* --- La vista sezione: barra in cima e scheda piatto ------------------- */
/*
  Dentro una sezione il cliente non ha bisogno dell'insegna: sa già dove si
  trova. Ha bisogno di **tornare indietro**. Quindi l'insegna sparisce e il suo
  posto in cima, a battuta, lo prende la barra «‹ Menù» — chiesto dal titolare
  segnando con una X la striscia col logo.

  Una scelta sola nel pannello (`sezione_aspetto`: bianca o nera) vale **insieme**
  per la barra e per la scheda del piatto, e il testo è sempre l'opposto del
  fondo. È la coppia a doversi leggere: separarle vorrebbe dire permettere nero
  su nero. Il titolare l'ha detto così: «come la matematica, non si può
  sbagliare».
*/

/* L'insegna sparisce dentro una sezione — anche quando la striscia è accesa. */
body.dentro-sezione .menu-header { display: none; }

/* La barra prende il suo posto: in cima a battuta, tinta **piena**, e sopra il
   contenuto. Sostituisce il `top` che le dava la striscia dell'insegna, che qui
   non c'è più. */
body.dentro-sezione .section-topbar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 20;
  border-bottom: 0;
  padding: var(--space-2) var(--space-4);
  padding-top: max(var(--space-2), env(safe-area-inset-top));
  /* La stessa misura della striscia con logo e nome: passando da una schermata
     all'altra la barra non deve cambiare altezza. */
  min-height: var(--alt-barra);
}

/* Lo spazio che la barra fissa occupa va restituito — **esattamente** quello, né
   uno in più: la foto della sezione deve cominciare attaccata alla barra. */
body.dentro-sezione { padding-top: var(--alt-barra); }

/* Il respiro che il contenuto aveva sotto la barra non serve più: adesso lì
   sotto c'è subito la fascia con la foto della sezione, e il titolare l'ha
   chiesta attaccata. */
body.dentro-sezione #view-section .menu-content { padding-top: 0; }

/* Il pulsante, più grande e più marcato di prima (era 1.05rem/700). */
body.dentro-sezione .back-button {
  font-size: 1.35rem;
  font-weight: 800;
  padding: 0;
}

/* Le due tinte. Il testo è **sempre l'opposto del fondo**, e questo vale anche
   per la pastiglia del tavolo che sta nella stessa barra. */
body.sezione-bianca.dentro-sezione .section-topbar { background: #ffffff; }
body.sezione-bianca.dentro-sezione .back-button { color: #111111; }
body.sezione-bianca.dentro-sezione .section-topbar .table-notice-tavolo,
body.sezione-bianca.dentro-sezione .section-topbar .table-notice-nome {
  border-color: #111111;
  color: #111111;
}

body.sezione-nera.dentro-sezione .section-topbar { background: #111111; }
body.sezione-nera.dentro-sezione .back-button { color: #ffffff; }
body.sezione-nera.dentro-sezione .section-topbar .table-notice-tavolo,
body.sezione-nera.dentro-sezione .section-topbar .table-notice-nome {
  border-color: #ffffff;
  color: #ffffff;
}

/* La scheda del piatto segue la stessa coppia. `--color-text-muted` è la
   descrizione, `--color-border` le linee: si spostano insieme al resto, o
   resterebbero del contrasto sbagliato. */
body.sezione-bianca.dentro-sezione .product-card,
body.sezione-bianca.dentro-sezione .menu-empty {
  background: #ffffff;
  color: #111111;
  --color-text: #111111;
  --color-text-muted: #5a5a5a;
  --color-border: #e6e6e6;
  --color-surface: #ffffff;
  --color-surface-muted: #f2f2f2;
  --color-accent: #111111;
  --color-accent-contrast: #ffffff;
}

body.sezione-nera.dentro-sezione .product-card,
body.sezione-nera.dentro-sezione .menu-empty {
  background: #111111;
  color: #ffffff;
  --color-text: #ffffff;
  --color-text-muted: rgba(255, 255, 255, 0.72);
  --color-border: rgba(255, 255, 255, 0.28);
  --color-surface: rgba(255, 255, 255, 0.10);
  --color-surface-muted: rgba(255, 255, 255, 0.14);
  --color-accent: #ffffff;
  --color-accent-contrast: #111111;
}

/* Le schede hanno gli angoli arrotondati e un po' d'aria anche senza foto di
   sfondo: con un fondo pieno proprio, la linea di separazione non serve più. */
body.dentro-sezione .product-card,
body.dentro-sezione .menu-empty {
  border-radius: var(--radius-card);
  border-bottom: 0;
  padding: var(--space-3);
  margin-bottom: var(--space-4);
}

/* --- La riga del tavolo dentro l'intestazione -------------------------- */
/*
  Quando il tavolo è valido, `collocaAvvisoTavolo()` sposta la riga dentro
  l'intestazione (livello 1, accanto al nome del locale) o dentro la barra
  «‹ Menù» (livello 2). Deve vedersi in tutte e due, § 5.1: chi inquadra per
  sbaglio il QR del tavolo accanto non ha nessun altro modo di accorgersene.

  Qui perde la forma di scheda che aveva — era `--radius-card`, lo stesso raggio
  delle schede piatto, per dire un'etichetta — e diventa una pastiglia.
*/

.section-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}

/* La pastiglia non si restringe mai sotto la sua larghezza naturale.
 *
 * Foto del titolare, 27/09/2026, «Osteria Quattro Venti» / «Tavolo 1» a
 * 412px: con `flex: 0 1 auto` la pastiglia poteva cedere spazio al nome del
 * locale, e "Tavolo 1" andava a capo ("Tavolo" / "1") — il bordo arrotondato
 * (pensato per una pillola di una riga) si allungava in un ovale storto.
 * `flex: 0 0 auto` (flex-shrink: 0) le vieta di restringersi: se lo spazio
 * non basta è il nome del locale a cedere (vedi .restaurant-name qui sotto),
 * mai la pastiglia. Non deve accadere mai più. */
.menu-header .table-notice,
.section-topbar .table-notice {
  width: auto;
  max-width: 55%;
  margin: 0;
  padding: 0;
  flex: 0 0 auto;
}

.menu-header .table-notice-tavolo,
.section-topbar .table-notice-tavolo {
  display: inline-flex;
  align-items: center;
  gap: 0;
  padding: 3px var(--space-3);
  border: 2px solid var(--color-text);
  border-radius: var(--radius-chip);
  background: none;
  color: var(--color-text);
  /* Era 0.82rem, e col testo nero risultava sottile e poco visibile: il nero su
     una foto non "buca" come il bianco, che si stacca da solo. Alzata a 1rem,
     cioè la misura del testo normale della pagina. */
  font-size: 1rem;
}

/* «Stai ordinando per» sparisce alla vista ma **resta nel documento**: accanto
   al nome del locale quella frase non serve, ma a chi ascolta la pagina con un
   lettore di schermo «Tavolo 1» da solo non dice cosa sia. Non si toglie da
   app.js perché nell'avviso a larghezza piena (tavolo cancellato) serve
   ancora. */
.menu-header .table-notice-tavolo > span,
.section-topbar .table-notice-tavolo > span {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Il nome del tavolo **non si taglia mai** con i tre puntini: il titolare può
   chiamarlo «Dehors 3», e «Dehors…» a un cliente seduto al «Dehors 5» dice la
   cosa sbagliata. Va a capo e la pastiglia si alza, come ovunque altrove. */
.menu-header .table-notice-nome,
.section-topbar .table-notice-nome {
  font-weight: 800;
  color: var(--color-text);
  min-width: 0;
  overflow-wrap: break-word;
}

/* --- Riquadri delle sezioni (schermata iniziale) ---------------------- */

/* One column on a phone: a section tile is the main thing on the screen and
   deserves the full width, the same way a delivery app opens on big
   photo cards rather than a cramped two-up grid. */
.section-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
  padding-top: var(--space-2);
}

.section-tile {
  position: relative;
  display: flex;
  align-items: flex-end;
  width: 100%;
  min-height: 132px;
  padding: var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-card);
  background: var(--color-surface-muted);
  overflow: hidden;
  cursor: pointer;
  text-align: left;
  font-family: inherit;
  -webkit-tap-highlight-color: transparent;
}

/* A section without a photo must not leave a hole in the grid: same size,
   plain grey, name in black. */
.section-tile:not(.has-photo) .section-tile-name {
  color: var(--color-text);
}

/* `border: 0` e non `border-color: transparent`, che è quello che c'era prima e
   lasciava un filo chiaro tutt'attorno alla foto.

   Il motivo: un bordo trasparente **non sparisce**, occupa ancora il suo pixel, e
   lì sotto si vede lo sfondo dell'elemento — di serie lo sfondo si disegna fino
   al bordo esterno. Il grigio `--color-surface-muted` del riquadro spuntava
   quindi come un anello di 1 px, e la foto (`inset: 0`) si ferma al bordo
   interno e non lo copriva.

   Il riquadro **senza** foto tiene il suo bordo: sta su `.section-tile`, e
   questa regola vale solo per `.has-photo`. Serve a non farlo sembrare un buco
   nella griglia. */
.section-tile.has-photo {
  min-height: 160px;
  border: 0;
}

.section-tile-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
}

/* The dark gradient is what keeps white text readable on a bright food photo —
   without it the section name disappears over pale dishes. */
.section-tile.has-photo::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(0, 0, 0, 0.15) 0%, rgba(0, 0, 0, 0.72) 100%);
  z-index: 1;
}

.section-tile-name {
  position: relative;
  z-index: 2;
  font-size: 1.5rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  line-height: 1.2;
  overflow-wrap: break-word;
  min-width: 0;
}

.section-tile.has-photo .section-tile-name {
  color: #ffffff;
  font-size: 1.75rem;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}

/* ATTENZIONE ALL'ORDINE: questo blocco deve stare **dopo** le due regole qui
   sopra (`.category-header.has-photo .category-title` e
   `.section-tile.has-photo .section-tile-name`), non prima.

   Scritto prima, il colore nero passava — perché `body.testo-nero` aggiunge una
   classe e quindi pesa di più — ma `text-shadow` no: la regola più in basso lo
   riscriveva comunque, e restava l'ombra **nera e sfocata** del testo bianco,
   visibile come una sbavatura attorno alle lettere nere. Un difetto che a
   schermo intero non si nota e ingrandendo salta all'occhio. */
/* I nomi delle sezioni — sui riquadri e nella fascia in cima a una sezione —
   stanno **sopra una foto**, non sul fondo della pagina. Per questo erano
   scritti in bianco a mano (righe `.category-header.has-photo .category-title` e
   `.section-tile.has-photo .section-tile-name`), con sotto una sfumatura scura
   che li tiene leggibili anche su un piatto chiarissimo.

   Scegliendo «nero» restavano bianchi: il titolare cambiava *il colore del
   testo* e due titoli su tre non cambiavano. Ma cambiare solo il colore non
   basta — nero sopra una sfumatura scura è illeggibile, che è esattamente il
   motivo per cui quella sfumatura esiste. **Si ribalta la coppia**: testo nero e
   sfumatura chiara, cioè lo stesso mestiere di prima fatto al contrario.

   La `.product-card` non è in questo elenco apposta: il testo di un piatto sta
   su una scheda piena, non sulla foto, e segue già il token. */
body.testo-nero .category-header.has-photo .category-title,
body.testo-nero .section-tile.has-photo .section-tile-name {
  color: var(--color-text);
  /* **Nessuna ombra e nessun contorno: solo il colore.** Il titolare ha chiesto
     di poter scegliere il colore del testo, e il colore è tutto quello che
     questa regola cambia.

     Qui sono già passate due versioni sbagliate, e vanno dichiarate morte
     invece di sparire in silenzio (§ 6.2): prima un velo bianco su tutto il
     riquadro, che slavava le foto del cibo; poi un contorno bianco attorno alle
     lettere, prima sfocato e poi netto. Tutte e due risolvevano un problema di
     leggibilità **che non era stato chiesto di risolvere**, e per farlo
     aggiungevano al menù una cosa che nessuno aveva domandato.

     L'ombra scura ereditata dalla regola del testo bianco va tolta lo stesso
     (`none`): serviva a staccare il bianco dalla foto, e sotto una scritta nera
     è solo una sbavatura.

     Conseguenza da conoscere: sopra una foto scura il nero si legge male. È una
     scelta del titolare, che nel pannello vede l'anteprima prima di salvare. */
  text-shadow: none;
}

/* La sfumatura sopra la foto **sparisce** col testo nero: esiste per rendere
   leggibile il bianco, e col nero non serve più. La foto resta intatta. */
body.testo-nero .category-header.has-photo::after,
body.testo-nero .section-tile.has-photo::after {
  background: transparent;
}

/* Il riquadro premuto si **scurisce**, non diventa trasparente.
   `opacity` rendeva translucido tutto l'elemento — foto compresa — e sotto si
   vedeva lo sfondo del locale: tenendo il dito sul riquadro per scorrere, quello
   spariva a metà. Segnalato dal titolare con una foto, sulla sezione «DRINK
   SPECIALI». `brightness` tocca solo i colori e non l'opacità, quindi la foto
   resta piena e il riquadro non lascia passare niente. */
.section-tile:active {
  filter: brightness(0.9);
}

/* --- Barra "torna al menù" -------------------------------------------- */

.section-topbar {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--color-background);
  border-bottom: 1px solid var(--color-border);
  padding: var(--space-2) var(--space-3);
  padding-top: max(var(--space-2), env(safe-area-inset-top));
}

.back-button {
  min-height: var(--tap-target-min);
  display: inline-flex;
  align-items: center;
  padding: 0 var(--space-3);
  border: 0;
  background: none;
  color: var(--color-text);
  font-family: inherit;
  font-size: 1.05rem;
  font-weight: 700;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.back-button:active { opacity: 0.6; }

#view-section .menu-content { padding-top: var(--space-3); }

/* --- Piatto ----------------------------------------------------------- */

/* Photo on top at full width, text underneath: roughly double the previous
   thumbnail-in-a-row size, which is what the owner asked for so the food
   carries the page. */
.product-card {
  display: block;
  padding: 0 0 var(--space-5);
  margin-bottom: var(--space-5);
  border-bottom: 1px solid var(--color-border);
}

.product-card:last-child {
  border-bottom: 0;
  margin-bottom: 0;
}

.product-photo-wrap {
  width: 100%;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-card);
  overflow: hidden;
  background: var(--color-surface-muted);
  margin-bottom: var(--space-3);
}

.product-photo-wrap img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.product-info {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.product-name {
  font-weight: 700;
  font-size: 1.25rem;
  line-height: 1.25;
  margin: 0;
}

.product-description {
  font-size: 1rem;
  color: var(--color-text-muted);
  margin: 0;
}

.product-price {
  margin: var(--space-2) 0 0;
  font-weight: 800;
  font-size: 1.2rem;
  color: var(--color-text);
}

/* --- Caricamento ------------------------------------------------------ */

.skeleton-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding-top: var(--space-5);
}

.skeleton-card {
  height: 230px;
  border-radius: var(--radius-photo);
  background: linear-gradient(90deg, var(--color-surface-muted) 25%, #e8e8e8 37%, var(--color-surface-muted) 63%);
  background-size: 400% 100%;
  animation: skeleton-shimmer 1.4s ease infinite;
}

@keyframes skeleton-shimmer {
  0% { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}

@media (prefers-reduced-motion: reduce) {
  .skeleton-card { animation: none; }
}

/* --- Messaggi --------------------------------------------------------- */

.error-banner {
  margin: var(--space-4);
  padding: var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-card);
  background: var(--color-surface-muted);
  color: var(--color-text);
  font-size: 0.95rem;
  text-align: center;
}

/* "Riprova" dentro la fascia d'errore: senza, l'unica via d'uscita è ricaricare
   la pagina, e chi è al tavolo col telefono in mano non sa che l'indirizzo
   giusto è ancora quello del QR appena inquadrato. */
.retry-btn {
  display: block;
  margin: var(--space-3) auto 0;
  min-height: var(--tap-target-min);
  padding: 0 var(--space-5);
  border: 0;
  border-radius: var(--radius-chip);
  background: var(--color-accent);
  color: var(--color-accent-contrast);
  font-family: inherit;
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.retry-btn:active { opacity: 0.85; }

/* --- La striscia del tavolo ------------------------------------------- */

/* In cima a tutte e due le viste. Dice a che tavolo sta mandando questa pagina
   — chi inquadra per sbaglio il QR del tavolo accanto non ha nessun altro modo
   di accorgersene — oppure che quel codice non è più collegato a nessun tavolo,
   e in quel caso lo dice **prima** che il carrello sia pieno. */
.table-notice {
  width: calc(100% - 2 * var(--space-4));
  max-width: 720px;
  margin: var(--space-3) auto 0;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-card);
  font-size: 0.95rem;
}

/* Il tavolo c'è: una riga tranquilla, che si legge e non disturba. */
.table-notice-tavolo {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  color: var(--color-text-muted);
}
.table-notice-nome {
  font-weight: 800;
  color: var(--color-text);
  /* Il nome del tavolo lo scrive il titolare, e la striscia è `display: flex`:
     senza questo, "Tavolodellaverandasulgiardinointerno12" tiene la riga larga
     quanto sé stesso e la striscia esce dal bordo destro dello schermo. */
  min-width: 0;
}

/* Il tavolo non c'è più: qui bisogna che si veda, perché è la ragione per cui
   non compare nessun "Aggiungi". Fondo pieno e non rosso — il menù del cliente
   resta monocromo (§ 5.3) — che su bianco è il contrasto più forte che c'è. */
.table-notice-avviso {
  background: var(--color-accent);
  color: var(--color-accent-contrast);
}
.table-notice-titolo {
  display: block;
  font-weight: 800;
  margin-bottom: 2px;
}

.menu-empty {
  color: var(--color-text-muted);
  text-align: center;
  padding: var(--space-6) var(--space-4);
}

@media (min-width: 640px) {
  .menu-content { padding: 0 var(--space-5); }
  .menu-header { padding: var(--space-6) var(--space-5) var(--space-4); }
  .category-header { margin-left: calc(-1 * var(--space-5)); margin-right: calc(-1 * var(--space-5)); }
  /* On a wider screen two tiles per row stop each one stretching to an
     awkward letterbox. */
  .section-grid { grid-template-columns: 1fr 1fr; gap: var(--space-4); }
}

/* --- Ordinazione al tavolo (carrello) ---------------------------------
   Compare solo quando il titolare ha attivato gli ordini e il cliente è
   arrivato dal QR di un tavolo. Costruito sui token esistenti: il menù cliente
   non ha una classe .btn (è solo nel pannello), quindi qui sono classi proprie. */

.product-action {
  margin-top: var(--space-3);
}

/* Prezzo e comando affiancati (18/09/2026). Impilati, il comando scendeva di
   una riga intera e arrivava all'altezza della barra del carrello: è il difetto
   che il titolare ha fotografato. Qui il margine del comando va azzerato, o
   dentro la riga si siederebbe più in basso del prezzo. */
.riga-azione {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-top: var(--space-2);
}
.riga-azione .product-price { margin: 0; }
.riga-azione .product-action { margin-top: 0; }

.add-btn {
  min-height: var(--tap-target-min);
  padding: 0 var(--space-5);
  border: 0;
  border-radius: var(--radius-chip);
  background: var(--color-accent);
  color: var(--color-accent-contrast);
  font-family: inherit;
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.add-btn:active { opacity: 0.85; }

.qty-stepper {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-1) var(--space-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-chip);
}

/* Squadrato **solo nella scheda** (18/09/2026): lì convive con la striscia del
   carrello, e due pillole con lo stesso angolo tondo si leggono come lo stesso
   oggetto. Dentro il cassetto il regolatore resta tondo com'era: là non c'è
   niente con cui confondersi, e cambiarlo sarebbe una modifica non chiesta. */
.product-action .qty-stepper {
  border-radius: var(--radius-control);
}
.product-action .qty-btn {
  border-radius: 8px;
}
.qty-btn {
  width: var(--tap-target-min);
  height: var(--tap-target-min);
  border: 0;
  border-radius: 50%;
  background: var(--color-surface-muted);
  color: var(--color-text);
  font-size: 1.4rem;
  line-height: 1;
  font-weight: 700;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.qty-btn:active { opacity: 0.7; }
/* Spenti mentre l'ordine o il pagamento sta partendo (spegniPulsantiRighe in
   app.js): da quel momento il totale che si legge è quello che si paga. Stessa
   sbiadita del pulsante d'invio spento, nessun colore nuovo — quello che deve
   dire «aspetta» è il pulsante grande, non queste due rotelle. */
.qty-btn:disabled { opacity: 0.6; cursor: default; }
.qty-val {
  min-width: 20px;
  text-align: center;
  font-weight: 800;
  font-size: 1.1rem;
  font-variant-numeric: tabular-nums;
}

/* La striscia del carrello, appoggiata in fondo allo schermo su entrambi i
   livelli.
 *
 * Fino al 18/09/2026 era una **pillola nera che galleggiava** sopra il menù, e
 * il difetto è stato visto su un telefono vero: a metà scorrimento ci passava
 * sotto qualunque cosa, e quello che ci finiva era il comando + / − della
 * scheda — cioè proprio il pezzo che il cliente deve toccare. Peggiorava perché
 * pillola e selettore hanno lo stesso angolo tondo: non si leggeva «una barra
 * sopra», si leggeva «due pezzi che si scontrano».
 *
 * Adesso **non galleggia più: si appoggia**. È una striscia piena a tutta
 * larghezza, e sotto di lei non passa niente, perché lo spazio che occupa è
 * riservato nel flusso della pagina (`body.has-cart-bar`, più sotto). Non è una
 * sovrapposizione resa rara: è una sovrapposizione resa impossibile.
 *
 * Il fondo bianco al posto del nero è una richiesta del titolare: la scritta
 * sta sulla striscia stessa invece che dentro una pillola. Il pieno nero però
 * era anche ciò che diceva «toccami», e togliendolo la striscia si leggeva come
 * una riga di servizio: per questo l'icona del carrello e la freccia «›» —
 * senza di loro nessuno capisce che tutta la striscia è un pulsante. */
.cart-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1000;
  width: 100%;
  display: flex;
  align-items: center;
  /* Gli **stessi numeri della testata** (§ la striscia piena in cima), non
     numeri suoi: stesso passo, stesso margine laterale, stessa altezza minima.
     Le due fasce incorniciano la pagina, e due cornici che misurano diverso si
     vedono anche senza saperle misurare. `--alt-barra` è il token che la
     testata e la barra «‹ Menù» usano già: qui si riusa, non si reinventa. */
  gap: var(--space-3);
  min-height: var(--alt-barra);
  padding: var(--space-2) var(--space-4);
  /* In basso vale per il bordo inferiore quello che in cima vale per il notch:
     la striscia tocca il bordo, quindi si ingrassa quanto serve. */
  padding-bottom: max(var(--space-2), env(safe-area-inset-bottom));
  border: 0;
  border-top: 1px solid var(--color-border);
  border-radius: 0;
  background: var(--color-surface);
  color: var(--color-text);
  font-family: inherit;
  /* 1rem, la misura del testo normale della pagina — la stessa della pillola
     del tavolo, e per lo stesso motivo scritto lì. 1,05rem era un numero mio,
     e un carattere che non sta sulla scala si nota senza sapere perché. */
  font-size: 1rem;
  font-weight: 700;
  text-align: left;
  cursor: pointer;
  /* L'ombra verso l'alto dice che è una superficie su cui il menù si ferma, e
     non una riga stampata in fondo alla pagina. Il solo bordo non bastava:
     bianco su bianco spariva. */
  box-shadow: 0 -8px 16px rgba(0, 0, 0, 0.06);
}
.cart-bar:active { background: var(--color-surface-muted); }

/* La striscia segue la scelta del titolare, come la testata (18/09/2026).
 *
 * Prima era bianca e basta: con la testata impostata su **nera** la pagina si
 * ritrovava nera sopra e bianca sotto, cioè due cornici che si contraddicono.
 * Quella scelta il titolare la fa in una schermata sola — «Tema e stile» del
 * pannello — e deve valere per tutte e due le fasce, o non è una scelta: è
 * metà di una scelta.
 *
 * Vale qui la stessa regola già scritta per la testata: **il testo dentro una
 * fascia segue la fascia, non il colore scelto per il menù**, o su fondo nero
 * resterebbe nero. L'icona eredita `currentColor`, quindi basta il colore del
 * testo; la pallina si inverte, e il suo anello prende la tinta della fascia
 * perché serve a staccarla dall'icona, non a essere bianca. */
body.striscia-nera .cart-bar {
  background: #111111;
  color: #ffffff;
  border-top-color: #111111;
}
body.striscia-nera .cart-bar:active { background: #2a2a2a; }
body.striscia-nera .cart-count {
  background: #ffffff;
  color: #111111;
  border-color: #111111;
}
body.striscia-nera .cart-bar::after { color: rgba(255, 255, 255, 0.72); }

/* Con «nessuna» in cima non c'è nessuna fascia piena: la striscia del carrello
   resta bianca ed è l'unico piano solido della pagina. È una scelta, non una
   dimenticanza — sopra una foto di sfondo serve un fondo pieno, o il totale
   diventa illeggibile proprio mentre il cliente decide se ordinare. */
body.striscia-bianca .cart-bar,
body.striscia-nessuna .cart-bar {
  background: var(--color-surface);
  color: var(--color-text);
}

.cart-bar-left { display: flex; align-items: center; gap: var(--space-2); }

/* L'icona porta la pallina del conteggio appiccicata sopra. La pallina sporge,
   quindi serve `overflow: visible`: senza, il bordo della striscia le mangia un
   pezzo e si legge come una mezzaluna. L'anello bianco la stacca dall'icona. */
.cart-icona {
  position: relative;
  display: inline-flex;
  flex: none;
  overflow: visible;
}
/* La pallina sta nell'angolo in alto a destra, che nel disegno del carrello è
   **vuoto**: il cestello comincia più in basso e la maniglia sta a sinistra.
   Era 19px su un'icona di 23 e copriva il carrello invece di contarlo: chi
   guardava vedeva una pallina con due ruote sotto. Adesso è 16 su un'icona di
   30, e il carrello si vede per intero. */
.cart-count {
  position: absolute;
  top: -3px;
  right: -4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  border-radius: var(--radius-chip);
  background: var(--color-accent);
  color: var(--color-accent-contrast);
  font-size: 0.66rem;
  font-weight: 800;
  line-height: 1;
  border: 2px solid var(--color-surface);
}
.cart-bar-right { margin-left: auto; font-variant-numeric: tabular-nums; }
.cart-bar::after {
  content: "›";
  font-size: 1.45rem;
  font-weight: 400;
  line-height: 1;
  color: var(--color-text-muted);
  margin-left: var(--space-1);
  margin-top: -3px;
}

/* Velo dietro al carrello e alla conferma. */
.cart-overlay {
  position: fixed;
  inset: 0;
  z-index: 1001;
  background: rgba(0, 0, 0, 0.55);
}

/* Il pannello del carrello che sale dal basso. */
.cart-drawer {
  position: fixed;
  left: 50%;
  bottom: 0;
  transform: translateX(-50%);
  z-index: 1002;
  width: 100%;
  max-width: 520px;
  max-height: 85vh;
  overflow-y: auto;
  padding: var(--space-5);
  padding-bottom: max(var(--space-5), env(safe-area-inset-bottom));
  background: var(--color-surface);
  color: var(--color-text);
  border-radius: var(--radius-card) var(--radius-card) 0 0;
  box-shadow: 0 -10px 30px rgba(0, 0, 0, 0.25);
}
.drawer-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-4);
}
.drawer-title { font-size: 1.3rem; font-weight: 800; margin: 0; }
.drawer-close {
  width: var(--tap-target-min);
  height: var(--tap-target-min);
  border: 0;
  background: none;
  color: var(--color-text-muted);
  font-size: 1.8rem;
  line-height: 1;
  cursor: pointer;
}

/* L'errore d'invio: sta **sopra al pulsante**, in fondo al cassetto, non in
   cima. In cima restava fuori dallo schermo e l'unica cosa che il cliente
   vedeva dopo aver toccato "Invia ordine" era il pulsante che tornava normale:
   si crede di aver mandato l'ordine e si aspetta un piatto che nessuno sta
   cucinando. Il margine è sopra, non sotto, perché adesso ha sotto il
   pulsante. */
.error-inline {
  background: var(--color-danger-surface);
  color: var(--color-danger);
  padding: var(--space-3);
  border-radius: var(--radius-control);
  font-size: 0.95rem;
  margin: var(--space-3) 0 0;
}

/* Il tavolo, nell'ultima schermata prima di "Invia ordine". */
.cart-table {
  margin: 0 0 var(--space-3);
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--color-text-muted);
}

.cart-items { display: flex; flex-direction: column; gap: var(--space-3); }
.cart-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--color-border);
}
.cart-item-info { min-width: 0; }
.cart-item-title { font-weight: 700; }
.cart-item-line { font-size: 0.95rem; color: var(--color-text-muted); margin-top: 2px; }

.cart-note-field { display: block; margin: var(--space-4) 0; }
.cart-note-label {
  display: block;
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--color-text-muted);
  margin-bottom: var(--space-2);
}
.cart-note-field textarea {
  width: 100%;
  padding: var(--space-3);
  font-family: inherit;
  font-size: 1rem;
  color: var(--color-text);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-control);
  resize: vertical;
}

.cart-total-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-top: var(--space-3);
  border-top: 2px solid var(--color-border);
  font-weight: 800;
  font-size: 1.15rem;
}

.submit-order-btn {
  display: block; /* i due pulsanti si impilano senza lo spazio fantasma che
                     due inline-block larghi 100% si lasciano fra le righe */
  width: 100%;
  min-height: 52px;
  margin-top: var(--space-4);
  border: 0;
  border-radius: var(--radius-chip);
  background: var(--color-accent);
  color: var(--color-accent-contrast);
  font-family: inherit;
  font-size: 1.1rem;
  font-weight: 800;
  cursor: pointer;
}
.submit-order-btn:disabled { opacity: 0.6; cursor: default; }

/* Il secondo pulsante quando si può scegliere: «Invia e paga al banco».
   Stesso raggio, stessa altezza e stessa larghezza del primario — impilati, mai
   affiancati, o a 320 px due pulsanti con dentro quelle frasi si spezzerebbero
   a metà parola. Cambia solo il riempimento: bordo e testo del colore
   dell'accento su fondo trasparente, così si vede quale dei due è la strada
   principale senza introdurre un secondo colore nel menù monocromo (§ 5.3).
   Trasparente e non bianco: sotto può esserci la foto di sfondo del locale.
   Il bordo sta **dentro** la misura (box-sizing: border-box in cima al file),
   quindi i due pulsanti restano alti uguali. */
.submit-order-alt {
  background: transparent;
  color: var(--color-accent);
  border: 2px solid var(--color-accent);
  font-size: 1rem;
}

/* Le due righe sopra ai pulsanti. Grigie e piccole: sono spiegazioni, non
   avvisi — quello che deve farsi vedere è il pulsante. */
.pay-note,
.pay-prova {
  margin: var(--space-3) 0 0;
  font-size: 0.9rem;
  color: var(--color-text-muted);
}

/* «Pagato con carta: € 12,50» nel riquadro di conferma: in nero pieno come il
   numero dell'ordine, perché è l'altra cosa da dire se poi si chiama qualcuno. */
.success-pagamento {
  margin: 0 0 var(--space-3);
  font-weight: 800;
  color: var(--color-text);
}

/* Conferma "Ordine inviato". */
.success-modal {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 1003;
  width: calc(100% - 2 * var(--space-5));
  max-width: 380px;
  padding: var(--space-6) var(--space-5);
  text-align: center;
  background: var(--color-surface);
  color: var(--color-text);
  border-radius: var(--radius-card);
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
}
.success-icon {
  width: 64px;
  height: 64px;
  margin: 0 auto var(--space-4);
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--color-accent);
  color: var(--color-accent-contrast);
  font-size: 2rem;
  font-weight: 800;
}
.success-title { font-size: 1.3rem; font-weight: 800; margin: 0 0 var(--space-2); }
/* Il numero dell'ordine e il tavolo: sono le due cose da dire se poi si chiama
   qualcuno, quindi in nero pieno e non nel grigio del testo di contorno. */
.success-detail {
  margin: 0 0 var(--space-2);
  font-weight: 700;
  color: var(--color-text);
}
.success-text { color: var(--color-text-muted); margin: 0 0 var(--space-5); }
.success-close-btn {
  min-height: var(--tap-target-min);
  padding: 0 var(--space-6);
  border: 0;
  border-radius: var(--radius-chip);
  background: var(--color-accent);
  color: var(--color-accent-contrast);
  font-family: inherit;
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
}

/* --------------------------------------------- allergeni sulla scheda piatto

   La riga degli allergeni del regolamento UE 1169/2011, sotto al nome del
   piatto e **sopra al prezzo**: chi ha un'allergia deve incontrarla mentre
   legge il piatto, non dopo aver già deciso.

   Sono voci in rilievo leggero, non pastiglie colorate. Il menù è bianco e nero
   apposta perché le uniche cose colorate siano le foto del cibo (vedi § 5.3):
   quattordici pastiglie rosse spegnerebbero proprio quell'effetto, su ogni
   scheda, per un'informazione che deve essere chiara ma non gridare. Chi la
   cerca la trova subito perché ha un'etichetta scritta davanti; chi non ne ha
   bisogno la legge come parte della descrizione. */

.product-allergeni {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-1) var(--space-2);
  margin: var(--space-2) 0 0;
  /* L'unica informazione obbligatoria per legge della scheda era anche la più
     piccola: 12,8px grigi contro descrizione 16, prezzo 19,2, nome 20 —
     illeggibile al tavolo con poca luce (finding V1). A 320px la riga può
     andare su due righe: va bene, flex-wrap è qui apposta. */
  font-size: 0.875rem;
  line-height: 1.45;
  color: var(--color-text-muted);
}

.allergeni-etichetta {
  font-weight: 600;
  /* Non va mai a capo da sola staccandosi dal primo allergene: una riga con
     scritto solo "Allergeni:" e l'elenco altrove si legge male. */
  white-space: nowrap;
}

.allergene-voce {
  padding: 1px var(--space-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-chip);
  background: var(--color-surface);
}

/* Il piede della pagina con l'informativa privacy.
   Il margine in basso è generoso apposta: quando si ordina dal QR di un
   tavolo, la barra del carrello è fissa in fondo allo schermo e coprirebbe il
   link.
   Un collegamento obbligatorio per legge, nascosto sotto un pulsante, è come
   non averlo messo. */
.menu-footer {
  padding: 28px 16px calc(96px + env(safe-area-inset-bottom, 0px));
  text-align: center;
}

.privacy-link {
  color: var(--color-text-muted, #6b6b6b);
  font-size: 0.9rem;
  text-decoration: underline;
  /* Area toccabile piena anche se il testo è piccolo: sotto i 44px un dito
     manca il bersaglio, e questo è un link che qualcuno deve poter aprire. */
  display: inline-block;
  padding: 12px 16px;
  min-height: 44px;
}
.allergene-voce { white-space: nowrap; }
.allergene-voce-ignoto { font-style: italic; white-space: normal; }

/* "Cosa comprende": le note di legge del piatto, per esteso, dietro a un tocco.
   Sono il testo dell'Allegato II — dieci su quattordici ripetono il nome
   ("pesce e prodotti a base di pesce") e due sono lunghe tre righe: in chiaro
   su ogni scheda diventerebbero mezzo schermo di caratteri piccoli che nessuno
   legge. Qui ci sono intere, senza puntini di sospensione, a un dito di
   distanza. */
.allergeni-blocco { display: block; }

.allergeni-dettaglio {
  margin-top: var(--space-1);
  font-size: 0.8rem;
  color: var(--color-text-muted);
}

/* Il triangolino di serie è minuscolo: qui il bersaglio da toccare è tutta la
   riga, alta quanto un bottone. */
.allergeni-dettaglio-titolo {
  display: inline-block;
  padding: var(--space-1) 0;
  min-height: 24px;
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.allergeni-note {
  margin: var(--space-1) 0 0;
  padding-left: var(--space-4);
  line-height: 1.4;
}
.allergeni-note li { margin-bottom: var(--space-1); }
.allergeni-note strong { color: var(--color-text); }

/* Gli allergeni che **non ci sono**.

   Prima un piatto senza allergeni compilati non mostrava nessuna riga, e per
   chi ha un'allergia "niente scritto" si legge "sicuro": la pagina bianca era
   la stessa per «non ne contiene» e per «il titolare non l'ha compilato». Qui
   quella riga esiste, e si vede: fondo pieno, testo nero e non il grigio delle
   note, una barra nera a sinistra che la stacca dalla descrizione. Resta
   monocroma perché il rosso nel menù del cliente non si usa (§ 5.3) — e perché
   questo non è un errore del programma, è un'informazione che manca. */
.product-allergeni-mancanti {
  display: block;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-border-strong);
  border-radius: var(--radius-control);
  background: var(--color-surface-muted);
  color: var(--color-text);
  /* Stessa dimensione della riga con gli allergeni presenti: è la stessa
     informazione di legge, solo nel caso "manca". */
  font-size: 0.875rem;
}
.product-allergeni-mancanti .allergeni-etichetta {
  font-weight: 800;
  /* Qui il nowrap dell'etichetta normale farebbe uscire la frase dal riquadro
     sugli schermi stretti: questa è una frase, non un'etichetta corta. */
  white-space: normal;
}
.allergeni-invito { margin-left: var(--space-1); }

/* ==========================================================================
   FASCIA DELLA SEZIONE — la foto si dissolve verso il basso
   --------------------------------------------------------------------------
   Il titolare: «sembra una fascia che va da parte a parte». La larghezza piena
   gli va bene, il taglio netto in fondo no. Qui la larghezza NON si tocca:
   sparisce solo la linea di taglio, perché gli ultimi pixel della foto
   diventano via via trasparenti e lasciano vedere quello che c'è sotto.

   Due strati che sfumano insieme:
   1. una maschera (`mask-image`) sull'INTERA fascia, così la foto e la
      sfumatura scura che rende leggibile il titolo si dissolvono allo stesso
      modo: se sfumasse solo la foto resterebbe una banda scura sospesa;
   2. la sfumatura scura riscritta perché arrivi a zero da sola in fondo. È
      anche la rete di sicurezza: dove la maschera non funzionasse, resterebbe
      il bordo della foto ma MAI la riga scura che c'è oggi.

   Perché la maschera sta sulla fascia e non sull'immagine: la barra «‹ Menù»
   è un fratello della fascia, non un figlio, quindi la maschera non la tocca.

   NIENTE COLORI FISSI QUI DENTRO: la dissolvenza scopre lo sfondo, non lo
   dipinge. Per questo funziona identica con `sezione-bianca` e con
   `sezione-nera`, con la foto del locale e senza.
   ========================================================================== */

#view-section .category-header.has-photo {
  /* Quanto è alta la dissolvenza, e quanto respiro c'è fra il fondo del
     titolo e il punto in cui comincia. Cambiando questi due numeri si
     riaccorda tutto il blocco: le sfumature qui sotto sono scritte in
     funzione loro. */
  --dissolvenza: 64px;
  --aria-titolo: var(--space-3);

  /* I MARGINI NEGATIVI NON SI TOCCANO, di proposito: la fascia deve restare
     da bordo a bordo com'è oggi. Questo blocco non scrive `margin` proprio
     per non riscrivere la riga dentro `@media (min-width: 640px)` che porta i
     margini a `--space-5` — una regola dentro una media query non pesa di
     più, e stando questo blocco in fondo al foglio la vincerebbe, facendo
     rientrare la fascia di 8 px sugli schermi larghi. */

  /* Il titolo si alza di tutta la dissolvenza più un po' d'aria, così non
     finisce mai nella parte dissolta.
     Non allunga la fascia: `box-sizing: border-box` (riga 12) e
     `min-height: 150px` — con un titolo su una riga il contenuto misura
     24 + 43 + 76 = 143 px, sotto i 150, quindi la fascia resta alta
     esattamente quanto adesso e NIENTE si sposta più in basso. */
  padding-bottom: calc(var(--dissolvenza) + var(--aria-titolo));

  /* Nero pieno = si vede, trasparente = non si vede. Opaca fino a
     `--dissolvenza` dal fondo (cioè fino a sotto il titolo), poi giù con tre
     tappe intermedie: una rampa dritta a due sole tappe si vede "a bande" su
     una foto con un cielo o una parete in tinta unita. */
  -webkit-mask-image: linear-gradient(
    180deg,
    #000 0%,
    #000 calc(100% - var(--dissolvenza)),
    rgba(0, 0, 0, 0.92) calc(100% - var(--dissolvenza) * 0.80),
    rgba(0, 0, 0, 0.62) calc(100% - var(--dissolvenza) * 0.58),
    rgba(0, 0, 0, 0.26) calc(100% - var(--dissolvenza) * 0.30),
    rgba(0, 0, 0, 0) 100%
  );
  mask-image: linear-gradient(
    180deg,
    #000 0%,
    #000 calc(100% - var(--dissolvenza)),
    rgba(0, 0, 0, 0.92) calc(100% - var(--dissolvenza) * 0.80),
    rgba(0, 0, 0, 0.62) calc(100% - var(--dissolvenza) * 0.58),
    rgba(0, 0, 0, 0.26) calc(100% - var(--dissolvenza) * 0.30),
    rgba(0, 0, 0, 0) 100%
  );

  /* La maschera deve coprire la fascia una volta sola e per intero: senza
     queste righe alcuni motori la ripetono o la dimensionano da soli e la
     sfumatura compare due volte. `alpha` dice di leggere la trasparenza del
     gradiente e non la sua luminosità: letta al contrario, il nero
     nasconderebbe invece di mostrare. */
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  mask-mode: alpha;
}

/* La sfumatura che tiene leggibile il titolo bianco. Prima andava dritta da
   0.15 a 0.72 ed era più scura proprio in fondo: è quella la "riga" che il
   titolare vede. Adesso è più scura in mezzo — dove ADESSO sta il titolo — e
   torna a zero prima del bordo, quindi non c'è più niente che finisca di
   colpo. Le tappe sono ancorate al fondo in pixel, non in percentuale, così
   restano al loro posto anche se un nome lungo manda il titolo a capo e la
   fascia diventa più alta. */
#view-section .category-header.has-photo::after {
  background: linear-gradient(
    180deg,
    rgba(0, 0, 0, 0.12) 0%,
    rgba(0, 0, 0, 0.42) 35%,
    rgba(0, 0, 0, 0.78) calc(100% - var(--dissolvenza) - 28px),
    rgba(0, 0, 0, 0.78) calc(100% - var(--dissolvenza) - 4px),
    rgba(0, 0, 0, 0.42) calc(100% - var(--dissolvenza) * 0.70),
    rgba(0, 0, 0, 0.12) calc(100% - var(--dissolvenza) * 0.28),
    rgba(0, 0, 0, 0) 100%
  );
}

/* Col testo nero la sfumatura scura non ci deve essere: è la regola che il
   titolare ha già preteso una volta (nero sopra una sfumatura scura è
   illeggibile, e il velo bianco e il contorno attorno alle lettere sono due
   versioni già dichiarate morte). Va riscritta qui perché questo blocco sta
   in fondo al foglio e altrimenti la sua sfumatura tornerebbe.
   La dissolvenza in basso resta lo stesso: la fa la maschera, non questa
   sfumatura, quindi il bordo netto sparisce anche col testo nero. */
body.testo-nero #view-section .category-header.has-photo::after {
  background: transparent;
}